diff options
author | rbb <rbb@13f79535-47bb-0310-9956-ffa450edef68> | 1999-12-04 21:48:34 +0000 |
---|---|---|
committer | rbb <rbb@13f79535-47bb-0310-9956-ffa450edef68> | 1999-12-04 21:48:34 +0000 |
commit | 7a0db41c17d7e2c82b61db5d72e8026eb61c2aec (patch) | |
tree | e49165720311ae731be48529cbc29ffff7b3f4a3 | |
parent | 7b26f0f33b6f80d4449e415e4f5ac3d0c5ff4c3f (diff) | |
download | libapr-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.h | 1 | ||||
-rw-r--r-- | configure.in | 3 | ||||
-rw-r--r-- | include/apr.h.in | 1 | ||||
-rw-r--r-- | include/apr_network_io.h | 16 | ||||
-rw-r--r-- | include/arch/unix/networkio.h | 6 | ||||
-rw-r--r-- | network_io/unix/networkio.h | 6 | ||||
-rw-r--r-- | network_io/unix/sendrecv.c | 380 |
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 */ + |