📄 sendrecv.c
字号:
if (apr_is_option_set(sock->netmask, APR_SO_TIMEOUT)) {
sock->netmask |= APR_INCOMPLETE_WRITE;
}
return arv;
}
else {
/* If the file got smaller mid-request, eventually the offset
* becomes equal to the new file size and the kernel returns 0.
* Make this an error so the caller knows to log something and
* exit.
*/
return APR_EOF;
}
}
/* Now write the footers */
if (hdtr->numtrailers > 0) {
apr_size_t trbytes;
arv = apr_socket_sendv(sock, hdtr->trailers, hdtr->numtrailers,
&trbytes);
nbytes += trbytes;
if (arv != APR_SUCCESS) {
*len = nbytes;
rv = errno;
apr_socket_opt_set(sock, APR_TCP_NOPUSH, 0);
return rv;
}
}
apr_socket_opt_set(sock, APR_TCP_NOPUSH, 0);
(*len) = nbytes;
return rv < 0 ? errno : APR_SUCCESS;
}
#elif defined(__FreeBSD__)
static int include_hdrs_in_length(void)
{
#ifdef HAVE_SYS_SYSCTL_H
/* this assumes:
* if the header exists, so does the sysctlbyname() syscall, and
* if the header doesn't exist, the kernel is really old
*/
/* see
* http://www.freebsd.org/cgi/cvsweb.cgi/src/sys/sys/param.h#rev1.61.2.29
* for kernel version number
*
* http://www.freebsd.org/cgi/cvsweb.cgi/src/sys/kern/uipc_syscalls.c#rev1.65.2.10
* for the sendfile patch
*/
#define KERNEL_WITH_SENDFILE_LENGTH_FIX 460001
typedef enum { UNKNOWN = 0,
NEW,
OLD
} api_e;
static api_e api;
int kernel_version;
apr_size_t kernel_version_size;
if (api != UNKNOWN) {
return (api == OLD);
}
kernel_version = 0; /* silence compiler warning */
kernel_version_size = sizeof(kernel_version);
if (sysctlbyname("kern.osreldate", &kernel_version,
&kernel_version_size, NULL, 0) == 0 &&
kernel_version < KERNEL_WITH_SENDFILE_LENGTH_FIX) {
api = OLD;
return 1;
}
/* size of kern.osreldate's output might change in the future
* causing the sysctlbyname to fail,
* but if it's the future, we should use the newer API
*/
api = NEW;
return 0;
#else
/* the build system's kernel is older than 3.4. Use the old API */
return 1;
#endif
}
/* Release 3.1 or greater */
apr_status_t apr_socket_sendfile(apr_socket_t * sock, apr_file_t * file,
apr_hdtr_t * hdtr, apr_off_t * offset,
apr_size_t * len, apr_int32_t flags)
{
off_t nbytes = 0;
int rv, i;
struct sf_hdtr headerstruct;
apr_size_t bytes_to_send = *len;
/* Ignore flags for now. */
flags = 0;
if (!hdtr) {
hdtr = &no_hdtr;
}
else if (hdtr->numheaders && include_hdrs_in_length()) {
/* On early versions of FreeBSD sendfile, the number of bytes to send
* must include the length of the headers. Don't look at the man page
* for this :( Instead, look at the the logic in
* src/sys/kern/uipc_syscalls::sendfile().
*
* This was fixed in the middle of 4.6-STABLE
*/
for (i = 0; i < hdtr->numheaders; i++) {
bytes_to_send += hdtr->headers[i].iov_len;
}
}
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 {
if (sock->netmask & APR_INCOMPLETE_WRITE) {
apr_status_t arv;
sock->netmask &= ~APR_INCOMPLETE_WRITE;
arv = apr_wait_for_io_or_timeout(NULL, sock, 0);
if (arv != APR_SUCCESS) {
*len = 0;
return arv;
}
}
if (bytes_to_send) {
/* We won't dare call sendfile() if we don't have
* header or file bytes to send because bytes_to_send == 0
* means send the whole file.
*/
rv = sendfile(file->filedes, /* file to be sent */
sock->socketdes, /* socket */
*offset, /* where in the file to start */
bytes_to_send, /* number of bytes to send */
&headerstruct, /* Headers/footers */
&nbytes, /* number of bytes written */
flags); /* undefined, set to 0 */
if (rv == -1) {
if (errno == EAGAIN) {
if (apr_is_option_set(sock->netmask, APR_SO_TIMEOUT)) {
sock->netmask |= APR_INCOMPLETE_WRITE;
}
/* FreeBSD's sendfile can return -1/EAGAIN even if it
* sent bytes. Sanitize the result so we get normal EAGAIN
* semantics w.r.t. bytes sent.
*/
if (nbytes) {
/* normal exit for a big file & non-blocking io */
(*len) = nbytes;
return APR_SUCCESS;
}
}
}
else { /* rv == 0 (or the kernel is broken) */
if (nbytes == 0) {
/* Most likely the file got smaller after the stat.
* Return an error so the caller can do the Right Thing.
*/
(*len) = nbytes;
return APR_EOF;
}
}
}
else {
/* just trailer bytes... use writev()
*/
rv = writev(sock->socketdes,
hdtr->trailers,
hdtr->numtrailers);
if (rv > 0) {
nbytes = rv;
rv = 0;
}
else {
nbytes = 0;
}
}
if (rv == -1 &&
errno == EAGAIN &&
apr_is_option_set(sock->netmask, APR_SO_TIMEOUT)) {
apr_status_t arv = apr_wait_for_io_or_timeout(NULL, sock, 0);
if (arv != APR_SUCCESS) {
*len = 0;
return arv;
}
}
} while (rv == -1 && (errno == EINTR || errno == EAGAIN));
(*len) = nbytes;
if (rv == -1) {
return errno;
}
return APR_SUCCESS;
}
#elif defined(__hpux) || defined(__hpux__)
/* HP cc in ANSI mode defines __hpux; gcc defines __hpux__ */
/* HP-UX Version 10.30 or greater
* (no worries, because we only get here if autoconfiguration found sendfile)
*/
/* ssize_t sendfile(int s, int fd, off_t offset, size_t nbytes,
* const struct iovec *hdtrl, int flags);
*
* nbytes is the number of bytes to send just from the file; as with FreeBSD,
* if nbytes == 0, the rest of the file (from offset) is sent
*/
apr_status_t apr_socket_sendfile(apr_socket_t *sock, apr_file_t *file,
apr_hdtr_t *hdtr, apr_off_t *offset,
apr_size_t *len, apr_int32_t flags)
{
int i;
apr_ssize_t rc;
apr_size_t nbytes = *len, headerlen, trailerlen;
struct iovec hdtrarray[2];
char *headerbuf, *trailerbuf;
if (!hdtr) {
hdtr = &no_hdtr;
}
/* Ignore flags for now. */
flags = 0;
/* HP-UX can only send one header iovec and one footer iovec; try to
* only allocate storage to combine input iovecs when we really have to
*/
switch(hdtr->numheaders) {
case 0:
hdtrarray[0].iov_base = NULL;
hdtrarray[0].iov_len = 0;
break;
case 1:
hdtrarray[0] = hdtr->headers[0];
break;
default:
headerlen = 0;
for (i = 0; i < hdtr->numheaders; i++) {
headerlen += hdtr->headers[i].iov_len;
}
/* XXX: BUHHH? wow, what a memory leak! */
headerbuf = hdtrarray[0].iov_base = apr_palloc(sock->cntxt, headerlen);
hdtrarray[0].iov_len = headerlen;
for (i = 0; i < hdtr->numheaders; i++) {
memcpy(headerbuf, hdtr->headers[i].iov_base,
hdtr->headers[i].iov_len);
headerbuf += hdtr->headers[i].iov_len;
}
}
switch(hdtr->numtrailers) {
case 0:
hdtrarray[1].iov_base = NULL;
hdtrarray[1].iov_len = 0;
break;
case 1:
hdtrarray[1] = hdtr->trailers[0];
break;
default:
trailerlen = 0;
for (i = 0; i < hdtr->numtrailers; i++) {
trailerlen += hdtr->trailers[i].iov_len;
}
/* XXX: BUHHH? wow, what a memory leak! */
trailerbuf = hdtrarray[1].iov_base = apr_palloc(sock->cntxt, trailerlen);
hdtrarray[1].iov_len = trailerlen;
for (i = 0; i < hdtr->numtrailers; i++) {
memcpy(trailerbuf, hdtr->trailers[i].iov_base,
hdtr->trailers[i].iov_len);
trailerbuf += hdtr->trailers[i].iov_len;
}
}
do {
if (nbytes) { /* any bytes to send from the file? */
rc = sendfile(sock->socketdes, /* socket */
file->filedes, /* file descriptor to send */
*offset, /* where in the file to start */
nbytes, /* number of bytes to send from file */
hdtrarray, /* Headers/footers */
flags); /* undefined, set to 0 */
}
else { /* we can't call sendfile() with no bytes to send from the file */
rc = writev(sock->socketdes, hdtrarray, 2);
}
} while (rc == -1 && errno == EINTR);
while (rc == -1 &&
(errno == EAGAIN || errno == EWOULDBLOCK) &&
apr_is_option_set(sock->netmask, APR_SO_TIMEOUT)) {
apr_status_t arv = apr_wait_for_io_or_timeout(NULL, sock, 0);
if (arv != APR_SUCCESS) {
*len = 0;
return arv;
}
else {
do {
if (nbytes) {
rc = sendfile(sock->socketdes, /* socket */
file->filedes, /* file descriptor to send */
*offset, /* where in the file to start */
nbytes, /* number of bytes to send from file */
hdtrarray, /* Headers/footers */
flags); /* undefined, set to 0 */
}
else { /* we can't call sendfile() with no bytes to send from the file */
rc = writev(sock->socketdes, hdtrarray, 2);
}
} while (rc == -1 && errno == EINTR);
}
}
if (rc == -1) {
*len = 0;
return errno;
}
/* Set len to the number of bytes written */
*len = rc;
return APR_SUCCESS;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -