summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrbb <rbb@13f79535-47bb-0310-9956-ffa450edef68>1999-12-04 21:48:34 +0000
committerrbb <rbb@13f79535-47bb-0310-9956-ffa450edef68>1999-12-04 21:48:34 +0000
commit7a0db41c17d7e2c82b61db5d72e8026eb61c2aec (patch)
treee49165720311ae731be48529cbc29ffff7b3f4a3
parent7b26f0f33b6f80d4449e415e4f5ac3d0c5ff4c3f (diff)
downloadlibapr-7a0db41c17d7e2c82b61db5d72e8026eb61c2aec.tar.gz
Add Sendfile to APR. This is not well tested, and a test case is needed.
I did some minor cleanup work, mainly to integrate sendfile into the APR autoconf stuff. Submitted by: John Zedlewski Reviewed by: Ryan Bloom git-svn-id: http://svn.apache.org/repos/asf/apr/apr/trunk@59503 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--acconfig.h1
-rw-r--r--configure.in3
-rw-r--r--include/apr.h.in1
-rw-r--r--include/apr_network_io.h16
-rw-r--r--include/arch/unix/networkio.h6
-rw-r--r--network_io/unix/networkio.h6
-rw-r--r--network_io/unix/sendrecv.c380
7 files changed, 413 insertions, 0 deletions
diff --git a/acconfig.h b/acconfig.h
index 42b618eb8..605fa464d 100644
--- a/acconfig.h
+++ b/acconfig.h
@@ -46,6 +46,7 @@
#undef SIZEOF_SSIZE_T
#undef APR_HAS_THREADS
+#undef APR_HAS_SENDFILE
@BOTTOM@
diff --git a/configure.in b/configure.in
index 2061ea112..d24049a6e 100644
--- a/configure.in
+++ b/configure.in
@@ -215,6 +215,7 @@ AC_CHECK_HEADERS(winsock.h)
AC_CHECK_HEADERS(arpa/inet.h)
AC_CHECK_HEADERS(netinet/in.h, netinet_inh="1", netinet_inh="0")
+AC_CHECK_HEADERS(netinet/tcp.h)
AC_CHECK_HEADERS(sys/file.h)
AC_CHECK_HEADERS(sys/ioctl.h)
@@ -261,8 +262,10 @@ dnl Checks for library functions.
AC_CHECK_FUNCS(pthread_sigmask)
AC_CHECK_FUNCS(strcasecmp stricmp poll setsid)
AC_CHECK_FUNCS(sigaction writev)
+AC_CHECK_FUNCS(sendfile, [ sendfile="#define APR_HAS_SENDFILE 1" ], [ sendfile="APR_HAS_SENDFILE 0" ])
AC_CHECK_FUNCS(getpass)
AC_CHECK_FUNC(_getch)
+AC_SUBST(sendfile)
AC_CHECK_FUNCS(gmtime_r localtime_r)
diff --git a/include/apr.h.in b/include/apr.h.in
index df66e519c..96fcbbb28 100644
--- a/include/apr.h.in
+++ b/include/apr.h.in
@@ -18,6 +18,7 @@
/* APR Feature Macros */
@threads@
+@sendfile@
@mmap@
/* Typedefs that APR needs. */
diff --git a/include/apr_network_io.h b/include/apr_network_io.h
index d5368d99d..9afd3bfc0 100644
--- a/include/apr_network_io.h
+++ b/include/apr_network_io.h
@@ -61,6 +61,7 @@
#endif
#include "apr_general.h"
+#include "apr_file_io.h"
#include "apr_errno.h"
#if APR_HAVE_NETINET_IN_H
#include <netinet/in.h>
@@ -103,6 +104,17 @@ typedef enum {APR_SHUTDOWN_READ, APR_SHUTDOWN_WRITE,
typedef struct socket_t ap_socket_t;
typedef struct pollfd_t ap_pollfd_t;
+typedef struct hdtr_t ap_hdtr_t;
+
+#ifdef APR_HAS_SENDFILE
+/* A structure to encapsulate headers and trailers for ap_sendfile */
+struct hdtr_t {
+ struct iovec* headers;
+ int numheaders;
+ struct iovec* trailers;
+ int numtrailers;
+};
+#endif
/* function definitions */
@@ -123,6 +135,10 @@ ap_status_t ap_set_socketdata(ap_socket_t *, void *, char *,
ap_status_t ap_send(ap_socket_t *, const char *, ap_ssize_t *);
ap_status_t ap_sendv(ap_socket_t *sock, const struct iovec *vec, ap_int32_t nvec, ap_int32_t *nbytes);
+#ifdef HAVE_SENDFILE
+ap_status_t ap_sendfile(ap_socket_t *sock, ap_file_t *file, ap_hdtr_t *hdtr, ap_off_t *offset,
+ ap_size_t *len, ap_int32_t flags);
+#endif
ap_status_t ap_recv(ap_socket_t *, char *, ap_ssize_t *);
ap_status_t ap_setsocketopt(ap_socket_t *, ap_int32_t, ap_int32_t);
diff --git a/include/arch/unix/networkio.h b/include/arch/unix/networkio.h
index 2cb64086f..d64a11d60 100644
--- a/include/arch/unix/networkio.h
+++ b/include/arch/unix/networkio.h
@@ -62,6 +62,12 @@
#include "apr_lib.h"
/* System headers the network I/O library needs */
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
#if HAVE_POLL_H
#include <poll.h>
#endif
diff --git a/network_io/unix/networkio.h b/network_io/unix/networkio.h
index 2cb64086f..d64a11d60 100644
--- a/network_io/unix/networkio.h
+++ b/network_io/unix/networkio.h
@@ -62,6 +62,12 @@
#include "apr_lib.h"
/* System headers the network I/O library needs */
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if HAVE_SYS_UIO_H
+#include <sys/uio.h>
+#endif
#if HAVE_POLL_H
#include <poll.h>
#endif
diff --git a/network_io/unix/sendrecv.c b/network_io/unix/sendrecv.c
index 1db9178bd..15d3c8d2d 100644
--- a/network_io/unix/sendrecv.c
+++ b/network_io/unix/sendrecv.c
@@ -57,6 +57,19 @@
#include "networkio.h"
+#ifdef HAVE_SENDFILE
+/* This file is needed to allow us access to the ap_file_t internals. */
+#include "../../file_io/unix/fileio.h"
+
+/* Glibc2.1.1 fails to define TCP_CORK. This is a bug that will be
+ *fixed in the next release. It should be 3
+ */
+#if !defined(TCP_CORK) && defined(__linux__)
+#define TCP_CORK 3
+#endif
+
+#endif /* HAVE_SENDFILE */
+
/* ***APRDOC********************************************************
* ap_status_t ap_send(ap_socket_t *, const char *, ap_ssize_t *, time_t)
* Send data over a network.
@@ -172,3 +185,370 @@ ap_status_t ap_recv(struct socket_t *sock, char *buf, ap_ssize_t *len)
return APR_SUCCESS;
}
+/* ***APRDOC********************************************************
+ * ap_status_t ap_sendv(ap_socket_t *, const struct iovec *, ap_int32_t, ap_int32_t *)
+ * Send multiple packets of data over a network.
+ * arg 1) The socket to send the data over.
+ * arg 2) The array of iovec structs containing the data to send
+ * arg 3) The number of iovec structs in the array
+ * arg 4) Receives the number of bytes actually written
+ * NOTE: This functions acts like a blocking write by default. To change
+ * this behavior, use ap_setsocketopt with the APR_SO_TIMEOUT option.
+ * The number of bytes actually sent is stored in argument 3.
+ */
+ap_status_t ap_sendv(struct socket_t * sock, const struct iovec * vec,
+ ap_int32_t nvec, ap_int32_t * nbytes)
+{
+ ssize_t rv;
+
+ do {
+ rv = writev(sock->socketdes, vec, nvec);
+ } while (rv == -1 && errno == EINTR);
+
+ if (rv == -1 && errno == EAGAIN && sock->timeout != 0) {
+ struct timeval *tv;
+ fd_set fdset;
+ int srv;
+
+ do {
+ FD_ZERO(&fdset);
+ FD_SET(sock->socketdes, &fdset);
+ if (sock->timeout < 0) {
+ tv = NULL;
+ }
+ else {
+ tv = ap_palloc(sock->cntxt, sizeof(struct timeval));
+ tv->tv_sec = sock->timeout;
+ tv->tv_usec = 0;
+ }
+ srv = select(FD_SETSIZE, NULL, &fdset, NULL, tv);
+ } while (srv == -1 && errno == EINTR);
+
+ if (srv == 0) {
+ (*nbytes) = -1;
+ return APR_TIMEUP;
+ }
+ else if (srv < 0) {
+ (*nbytes) = -1;
+ return errno;
+ }
+ else {
+ do {
+ rv = writev(sock->socketdes, vec, nvec);
+ } while (rv == -1 && errno == EINTR);
+ }
+ }
+ (*nbytes) = rv;
+ return APR_SUCCESS;
+}
+
+#if defined(HAVE_SENDFILE)
+/* ***APRDOC********************************************************
+ * ap_status_t ap_sendfile(ap_socket_t *, ap_file_t *, ap_hdtr_t *,
+ * ap_off_t *, ap_size_t *, ap_int32_t flags)
+ * Send a file from an open file descriptor to a socket, along with
+ * optional headers and trailers
+ * arg 1) The socket to which we're writing
+ * arg 2) The open file from which to read
+ * arg 3) A structure containing the headers and trailers to send
+ * arg 4) Offset into the file where we should begin writing
+ * arg 5) Number of bytes to send
+ * arg 6) OS-specific flags to pass to sendfile()
+ * NOTE: This functions acts like a blocking write by default. To change
+ * this behavior, use ap_setsocketopt with the APR_SO_TIMEOUT option.
+ * The number of bytes actually sent is stored in argument 4.
+ */
+
+ /* TODO: Verify that all platforms handle the fd the same way
+ * (i.e. not moving current file pointer)
+ * - Should flags be an int_32 or what?
+ */
+
+#if defined(__linux__) && defined(HAVE_WRITEV)
+ap_status_t ap_sendfile(ap_socket_t * sock, ap_file_t * file,
+ ap_hdtr_t * hdtr, ap_off_t * offset, ap_size_t * len,
+ ap_int32_t flags)
+{
+ off_t off = *offset;
+ int corkflag = 1;
+ int rv, nbytes = 0;
+
+ /* TCP_CORK keeps us from sending partial frames when we shouldn't */
+ rv = setsockopt(sock->socketdes, SOL_TCP, TCP_CORK,
+ (const void *) &corkflag, sizeof(corkflag));
+ if (rv == -1) {
+ return errno;
+ }
+
+ /* Now write the headers */
+ if (hdtr->numheaders > 0) {
+ ap_int32_t hdrbytes;
+ rv = ap_sendv(sock, hdtr->headers, hdtr->numheaders, &hdrbytes);
+ if (rv != APR_SUCCESS) {
+ return errno;
+ }
+ nbytes += hdrbytes;
+ }
+
+ do {
+ rv = sendfile(sock->socketdes, /* socket */
+ file->filedes, /* open file descriptor of the file to be sent */
+ &off, /* where in the file to start */
+ *len /* number of bytes to send */
+ );
+ } while (rv == -1 && errno == EINTR);
+
+ if (rv == -1 && errno == EAGAIN && sock->timeout != 0) {
+ struct timeval *tv;
+ fd_set fdset;
+ int srv;
+ do {
+ FD_ZERO(&fdset);
+ FD_SET(sock->socketdes, &fdset);
+ if (sock->timeout < 0) {
+ tv = NULL;
+ }
+ else {
+ tv = ap_palloc(sock->cntxt, sizeof(struct timeval));
+ tv->tv_sec = sock->timeout;
+ tv->tv_usec = 0;
+ }
+ srv = select(FD_SETSIZE, NULL, &fdset, NULL, tv);
+ } while (srv == -1 && errno == EINTR);
+
+ if (srv == 0) {
+ (*len) = -1;
+ return APR_TIMEUP;
+ }
+ else if (srv < 0) {
+ (*len) = -1;
+ return errno;
+ }
+ else {
+ do {
+ rv = sendfile(sock->socketdes, /* socket */
+ file->filedes, /* open file descriptor of the file to be sent */
+ &off, /* where in the file to start */
+ *len); /* number of bytes to send */
+ } while (rv == -1 && errno == EINTR);
+ }
+ }
+
+ if (rv == -1) {
+ return errno;
+ }
+
+ nbytes += rv;
+
+ /* Now write the footers */
+ if (hdtr->numtrailers > 0) {
+ ap_int32_t trbytes;
+ rv = ap_sendv(sock, hdtr->trailers, hdtr->numtrailers, &trbytes);
+ if (rv == -1) {
+ return errno;
+ }
+ nbytes += trbytes;
+ }
+
+ /* Uncork to send queued frames */
+ corkflag = 0;
+ rv = setsockopt(sock->socketdes, SOL_TCP, TCP_CORK,
+ (const void *) &corkflag, sizeof(corkflag));
+
+ (*len) = nbytes;
+ return APR_SUCCESS;
+}
+
+/* These are just demos of how the code for the other OSes.
+ * I haven't tested these, but they're right in terms of interface.
+ * I just wanted to see what types of vars would be required from other OSes.
+ */
+
+#elif defined(__FreeBSD__)
+/* Release 3.1 or greater */
+ap_status_t ap_sendfile(ap_socket_t * sock, ap_file_t * file,
+ ap_hdtr_t * hdtr, ap_off_t * offset, ap_size_t * len,
+ ap_int32_t flags)
+{
+ off_t nbytes;
+ int rv;
+ struct sf_hdtr headerstruct;
+
+ headerstruct.headers = hdtr->headers;
+ headerstruct.hdr_cnt = hdtr->numheaders;
+ headerstruct.trailers = hdtr->trailers;
+ headerstruct.trl_cnt = hdtr->numtrailers;
+
+
+ /* FreeBSD can send the headers/footers as part of the system call */
+ do {
+ rv = sendfile(file->filedes, /* open file descriptor of the file to be sent */
+ sock->socketdes, /* socket */
+ *offset, /* where in the file to start */
+ (size_t) * len, /* number of bytes to send */
+ &headerstruct, /* Headers/footers */
+ &nbytes, /* number of bytes written */
+ flags /* undefined, set to 0 */
+ );
+ } while (rv == -1 && errno == EINTR);
+
+ if (rv == -1 && errno == EAGAIN && sock->timeout != 0) {
+ struct timeval *tv;
+ fd_set fdset;
+ int srv;
+
+ do {
+ FD_ZERO(&fdset);
+ FD_SET(sock->socketdes, &fdset);
+ if (sock->timeout < 0) {
+ tv = NULL;
+ }
+ else {
+ tv = ap_palloc(sock->cntxt, sizeof(struct timeval));
+ tv->tv_sec = sock->timeout;
+ tv->tv_usec = 0;
+ }
+ srv = select(FD_SETSIZE, NULL, &fdset, NULL, tv);
+ } while (srv == -1 && errno == EINTR);
+
+ if (srv == 0) {
+ (*len) = -1;
+ return APR_TIMEUP;
+ }
+ else if (srv < 0) {
+ (*len) = -1;
+ return errno;
+ }
+ else {
+ do {
+ rv = sendfile(file->filedes, /* open file descriptor of the file to be sent */
+ sock->socketdes, /* socket */
+ *offset, /* where in the file to start */
+ (size_t) * len, /* number of bytes to send */
+ &headerstruct, /* Headers/footers */
+ &nbytes, /* number of bytes written */
+ flags /* undefined, set to 0 */
+ );
+ } while (rv == -1 && errno == EINTR);
+ }
+ }
+
+
+ if (rv == -1) {
+ return errno;
+ }
+
+ (*len) = nbytes;
+ return APR_SUCCESS;
+}
+
+#elif defined(__HPUX__)
+
+/* HP-UX Version 10.30 or greater */
+ap_status_t ap_sendfile(ap_socket_t * sock, ap_file_t * file,
+ ap_hdtr_t * hdtr, ap_off_t * offset, ap_size_t * len,
+ ap_int32_t flags)
+{
+ int i, ptr = 0;
+ size_t nbytes = 0, headerlen = 0, trailerlen = 0;
+ struct sf_hdtr headerstruct;
+ struct iovec hdtrarray[2];
+ void *headerbuf, *trailerbuf;
+
+
+ /* HP-UX can only send one header iovec and one footer iovec */
+
+ for (i = 0; i < hdtr->numheaders; i++) {
+ headerlen += hdtr->headers[i].iov_len;
+ }
+
+ headerbuf = ap_palloc(sock->cntxt, headerlen);
+
+ for (i = 0; i < hdtr->numheaders; i++) {
+ memcpy(headerbuf, hdtr->headers[i].iov_base + ptr,
+ hdtr->headers[i].iov_len);
+ ptr += hdtr->headers[i].iov_len;
+ }
+
+ for (i = 0; i < hdtr->numtrailers; i++) {
+ trailerlen += hdtr->headers[i].iov_len;
+ }
+
+ trailerbuf = ap_palloc(sock->cntxt, trailerlen);
+
+ for (i = 0; i < hdtr->numtrailers; i++) {
+ memcpy(trailerbuf, hdtr->trailers[i].iov_base + ptr,
+ hdtr->trailers[i].iov_len);
+ ptr += hdtr->trailers[i].iov_len;
+ }
+
+ hdtrarray[0].iov_base = headerbuf;
+ hdtrarray[0].iov_len = headerlen;
+ hdtrarray[1].iov_base = trailerbuf;
+ hdtrarray[1].iov_len = trailerlen;
+
+ do {
+ rv = sendfile(sock->socketdes, /* socket */
+ file->filedes, /* file descriptor to send */
+ *offset, /* where in the file to start */
+ nbytes, /* number of bytes to send */
+ hdtrarray, /* Headers/footers */
+ flags /* undefined, set to 0 */
+ );
+ } while (rv == -1 && errno == EINTR);
+
+ if (rv == -1 && errno == EAGAIN && sock->timeout != 0) {
+ struct timeval *tv;
+ fd_set fdset;
+ int srv;
+
+ do {
+ FD_ZERO(&fdset);
+ FD_SET(sock->socketdes, &fdset);
+ if (sock->timeout < 0) {
+ tv = NULL;
+ }
+ else {
+ tv = ap_palloc(sock->cntxt, sizeof(struct timeval));
+ tv->tv_sec = sock->timeout;
+ tv->tv_usec = 0;
+ }
+ srv = select(FD_SETSIZE, NULL, &fdset, NULL, tv);
+ } while (srv == -1 && errno == EINTR);
+
+ if (srv == 0) {
+ (*len) = -1;
+ return APR_TIMEUP;
+ }
+ else if (srv < 0) {
+ (*len) = -1;
+ return errno;
+ }
+ else {
+ do {
+ rv = sendfile(sock->socketdes, /* socket */
+ file->filedes, /* file descriptor to send */
+ *offset, /* where in the file to start */
+ nbytes, /* number of bytes to send */
+ hdtrarray, /* Headers/footers */
+ flags /* undefined, set to 0 */
+ );
+ } while (rv == -1 && errno == EINTR);
+ }
+ }
+
+
+ if (rv == -1) {
+ return errno;
+ }
+
+
+ /* Set len to the number of bytes written */
+ (*len) = rv;
+ return APR_SUCCESS;
+}
+#else
+/* TODO: Add AIX support */
+#endif /* __linux__, __FreeBSD__, __HPUX__ */
+#endif /* HAVE_SENDFILE */
+