sendrecv.c revision 251875
1251875Speter/* Licensed to the Apache Software Foundation (ASF) under one or more 2251875Speter * contributor license agreements. See the NOTICE file distributed with 3251875Speter * this work for additional information regarding copyright ownership. 4251875Speter * The ASF licenses this file to You under the Apache License, Version 2.0 5251875Speter * (the "License"); you may not use this file except in compliance with 6251875Speter * the License. You may obtain a copy of the License at 7251875Speter * 8251875Speter * http://www.apache.org/licenses/LICENSE-2.0 9251875Speter * 10251875Speter * Unless required by applicable law or agreed to in writing, software 11251875Speter * distributed under the License is distributed on an "AS IS" BASIS, 12251875Speter * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13251875Speter * See the License for the specific language governing permissions and 14251875Speter * limitations under the License. 15251875Speter */ 16251875Speter 17251875Speter#include "apr_arch_networkio.h" 18251875Speter#include "apr_support.h" 19251875Speter 20251875Speter#if APR_HAS_SENDFILE 21251875Speter/* This file is needed to allow us access to the apr_file_t internals. */ 22251875Speter#include "apr_arch_file_io.h" 23251875Speter#endif /* APR_HAS_SENDFILE */ 24251875Speter 25251875Speter/* osreldate.h is only needed on FreeBSD for sendfile detection */ 26251875Speter#if defined(__FreeBSD__) 27251875Speter#include <osreldate.h> 28251875Speter#endif 29251875Speter 30251875Speterapr_status_t apr_socket_send(apr_socket_t *sock, const char *buf, 31251875Speter apr_size_t *len) 32251875Speter{ 33251875Speter apr_ssize_t rv; 34251875Speter 35251875Speter if (sock->options & APR_INCOMPLETE_WRITE) { 36251875Speter sock->options &= ~APR_INCOMPLETE_WRITE; 37251875Speter goto do_select; 38251875Speter } 39251875Speter 40251875Speter do { 41251875Speter rv = write(sock->socketdes, buf, (*len)); 42251875Speter } while (rv == -1 && errno == EINTR); 43251875Speter 44251875Speter while (rv == -1 && (errno == EAGAIN || errno == EWOULDBLOCK) 45251875Speter && (sock->timeout > 0)) { 46251875Speter apr_status_t arv; 47251875Speterdo_select: 48251875Speter arv = apr_wait_for_io_or_timeout(NULL, sock, 0); 49251875Speter if (arv != APR_SUCCESS) { 50251875Speter *len = 0; 51251875Speter return arv; 52251875Speter } 53251875Speter else { 54251875Speter do { 55251875Speter rv = write(sock->socketdes, buf, (*len)); 56251875Speter } while (rv == -1 && errno == EINTR); 57251875Speter } 58251875Speter } 59251875Speter if (rv == -1) { 60251875Speter *len = 0; 61251875Speter return errno; 62251875Speter } 63251875Speter if ((sock->timeout > 0) && (rv < *len)) { 64251875Speter sock->options |= APR_INCOMPLETE_WRITE; 65251875Speter } 66251875Speter (*len) = rv; 67251875Speter return APR_SUCCESS; 68251875Speter} 69251875Speter 70251875Speterapr_status_t apr_socket_recv(apr_socket_t *sock, char *buf, apr_size_t *len) 71251875Speter{ 72251875Speter apr_ssize_t rv; 73251875Speter apr_status_t arv; 74251875Speter 75251875Speter if (sock->options & APR_INCOMPLETE_READ) { 76251875Speter sock->options &= ~APR_INCOMPLETE_READ; 77251875Speter goto do_select; 78251875Speter } 79251875Speter 80251875Speter do { 81251875Speter rv = read(sock->socketdes, buf, (*len)); 82251875Speter } while (rv == -1 && errno == EINTR); 83251875Speter 84251875Speter while ((rv == -1) && (errno == EAGAIN || errno == EWOULDBLOCK) 85251875Speter && (sock->timeout > 0)) { 86251875Speterdo_select: 87251875Speter arv = apr_wait_for_io_or_timeout(NULL, sock, 1); 88251875Speter if (arv != APR_SUCCESS) { 89251875Speter *len = 0; 90251875Speter return arv; 91251875Speter } 92251875Speter else { 93251875Speter do { 94251875Speter rv = read(sock->socketdes, buf, (*len)); 95251875Speter } while (rv == -1 && errno == EINTR); 96251875Speter } 97251875Speter } 98251875Speter if (rv == -1) { 99251875Speter (*len) = 0; 100251875Speter return errno; 101251875Speter } 102251875Speter if ((sock->timeout > 0) && (rv < *len)) { 103251875Speter sock->options |= APR_INCOMPLETE_READ; 104251875Speter } 105251875Speter (*len) = rv; 106251875Speter if (rv == 0) { 107251875Speter return APR_EOF; 108251875Speter } 109251875Speter return APR_SUCCESS; 110251875Speter} 111251875Speter 112251875Speterapr_status_t apr_socket_sendto(apr_socket_t *sock, apr_sockaddr_t *where, 113251875Speter apr_int32_t flags, const char *buf, 114251875Speter apr_size_t *len) 115251875Speter{ 116251875Speter apr_ssize_t rv; 117251875Speter 118251875Speter do { 119251875Speter rv = sendto(sock->socketdes, buf, (*len), flags, 120251875Speter (const struct sockaddr*)&where->sa, 121251875Speter where->salen); 122251875Speter } while (rv == -1 && errno == EINTR); 123251875Speter 124251875Speter while ((rv == -1) && (errno == EAGAIN || errno == EWOULDBLOCK) 125251875Speter && (sock->timeout > 0)) { 126251875Speter apr_status_t arv = apr_wait_for_io_or_timeout(NULL, sock, 0); 127251875Speter if (arv != APR_SUCCESS) { 128251875Speter *len = 0; 129251875Speter return arv; 130251875Speter } else { 131251875Speter do { 132251875Speter rv = sendto(sock->socketdes, buf, (*len), flags, 133251875Speter (const struct sockaddr*)&where->sa, 134251875Speter where->salen); 135251875Speter } while (rv == -1 && errno == EINTR); 136251875Speter } 137251875Speter } 138251875Speter if (rv == -1) { 139251875Speter *len = 0; 140251875Speter return errno; 141251875Speter } 142251875Speter *len = rv; 143251875Speter return APR_SUCCESS; 144251875Speter} 145251875Speter 146251875Speterapr_status_t apr_socket_recvfrom(apr_sockaddr_t *from, apr_socket_t *sock, 147251875Speter apr_int32_t flags, char *buf, 148251875Speter apr_size_t *len) 149251875Speter{ 150251875Speter apr_ssize_t rv; 151251875Speter 152251875Speter from->salen = sizeof(from->sa); 153251875Speter 154251875Speter do { 155251875Speter rv = recvfrom(sock->socketdes, buf, (*len), flags, 156251875Speter (struct sockaddr*)&from->sa, &from->salen); 157251875Speter } while (rv == -1 && errno == EINTR); 158251875Speter 159251875Speter while ((rv == -1) && (errno == EAGAIN || errno == EWOULDBLOCK) 160251875Speter && (sock->timeout > 0)) { 161251875Speter apr_status_t arv = apr_wait_for_io_or_timeout(NULL, sock, 1); 162251875Speter if (arv != APR_SUCCESS) { 163251875Speter *len = 0; 164251875Speter return arv; 165251875Speter } else { 166251875Speter do { 167251875Speter rv = recvfrom(sock->socketdes, buf, (*len), flags, 168251875Speter (struct sockaddr*)&from->sa, &from->salen); 169251875Speter } while (rv == -1 && errno == EINTR); 170251875Speter } 171251875Speter } 172251875Speter if (rv == -1) { 173251875Speter (*len) = 0; 174251875Speter return errno; 175251875Speter } 176251875Speter 177251875Speter apr_sockaddr_vars_set(from, from->sa.sin.sin_family, ntohs(from->sa.sin.sin_port)); 178251875Speter 179251875Speter (*len) = rv; 180251875Speter if (rv == 0 && sock->type == SOCK_STREAM) { 181251875Speter return APR_EOF; 182251875Speter } 183251875Speter 184251875Speter return APR_SUCCESS; 185251875Speter} 186251875Speter 187251875Speterapr_status_t apr_socket_sendv(apr_socket_t * sock, const struct iovec *vec, 188251875Speter apr_int32_t nvec, apr_size_t *len) 189251875Speter{ 190251875Speter#ifdef HAVE_WRITEV 191251875Speter apr_ssize_t rv; 192251875Speter apr_size_t requested_len = 0; 193251875Speter apr_int32_t i; 194251875Speter 195251875Speter for (i = 0; i < nvec; i++) { 196251875Speter requested_len += vec[i].iov_len; 197251875Speter } 198251875Speter 199251875Speter if (sock->options & APR_INCOMPLETE_WRITE) { 200251875Speter sock->options &= ~APR_INCOMPLETE_WRITE; 201251875Speter goto do_select; 202251875Speter } 203251875Speter 204251875Speter do { 205251875Speter rv = writev(sock->socketdes, vec, nvec); 206251875Speter } while (rv == -1 && errno == EINTR); 207251875Speter 208251875Speter while ((rv == -1) && (errno == EAGAIN || errno == EWOULDBLOCK) 209251875Speter && (sock->timeout > 0)) { 210251875Speter apr_status_t arv; 211251875Speterdo_select: 212251875Speter arv = apr_wait_for_io_or_timeout(NULL, sock, 0); 213251875Speter if (arv != APR_SUCCESS) { 214251875Speter *len = 0; 215251875Speter return arv; 216251875Speter } 217251875Speter else { 218251875Speter do { 219251875Speter rv = writev(sock->socketdes, vec, nvec); 220251875Speter } while (rv == -1 && errno == EINTR); 221251875Speter } 222251875Speter } 223251875Speter if (rv == -1) { 224251875Speter *len = 0; 225251875Speter return errno; 226251875Speter } 227251875Speter if ((sock->timeout > 0) && (rv < requested_len)) { 228251875Speter sock->options |= APR_INCOMPLETE_WRITE; 229251875Speter } 230251875Speter (*len) = rv; 231251875Speter return APR_SUCCESS; 232251875Speter#else 233251875Speter *len = vec[0].iov_len; 234251875Speter return apr_socket_send(sock, vec[0].iov_base, len); 235251875Speter#endif 236251875Speter} 237251875Speter 238251875Speter#if APR_HAS_SENDFILE 239251875Speter 240251875Speter/* TODO: Verify that all platforms handle the fd the same way, 241251875Speter * i.e. that they don't move the file pointer. 242251875Speter */ 243251875Speter/* TODO: what should flags be? int_32? */ 244251875Speter 245251875Speter/* Define a structure to pass in when we have a NULL header value */ 246251875Speterstatic apr_hdtr_t no_hdtr; 247251875Speter 248251875Speter#if defined(__linux__) && defined(HAVE_WRITEV) 249251875Speter 250251875Speterapr_status_t apr_socket_sendfile(apr_socket_t *sock, apr_file_t *file, 251251875Speter apr_hdtr_t *hdtr, apr_off_t *offset, 252251875Speter apr_size_t *len, apr_int32_t flags) 253251875Speter{ 254251875Speter int rv, nbytes = 0, total_hdrbytes, i; 255251875Speter apr_status_t arv; 256251875Speter 257251875Speter#if APR_HAS_LARGE_FILES && defined(HAVE_SENDFILE64) 258251875Speter apr_off_t off = *offset; 259251875Speter#define sendfile sendfile64 260251875Speter 261251875Speter#elif APR_HAS_LARGE_FILES && SIZEOF_OFF_T == 4 262251875Speter /* 64-bit apr_off_t but no sendfile64(): fail if trying to send 263251875Speter * past the 2Gb limit. */ 264251875Speter off_t off; 265251875Speter 266251875Speter if ((apr_int64_t)*offset + *len > INT_MAX) { 267251875Speter return EINVAL; 268251875Speter } 269251875Speter 270251875Speter off = *offset; 271251875Speter 272251875Speter#else 273251875Speter off_t off = *offset; 274251875Speter 275251875Speter /* Multiple reports have shown sendfile failing with EINVAL if 276251875Speter * passed a >=2Gb count value on some 64-bit kernels. It won't 277251875Speter * noticably hurt performance to limit each call to <2Gb at a 278251875Speter * time, so avoid that issue here: */ 279251875Speter if (sizeof(off_t) == 8 && *len > INT_MAX) { 280251875Speter *len = INT_MAX; 281251875Speter } 282251875Speter#endif 283251875Speter 284251875Speter if (!hdtr) { 285251875Speter hdtr = &no_hdtr; 286251875Speter } 287251875Speter 288251875Speter /* Ignore flags for now. */ 289251875Speter flags = 0; 290251875Speter 291251875Speter if (hdtr->numheaders > 0) { 292251875Speter apr_size_t hdrbytes; 293251875Speter 294251875Speter /* cork before writing headers */ 295251875Speter rv = apr_socket_opt_set(sock, APR_TCP_NOPUSH, 1); 296251875Speter if (rv != APR_SUCCESS) { 297251875Speter return rv; 298251875Speter } 299251875Speter 300251875Speter /* Now write the headers */ 301251875Speter arv = apr_socket_sendv(sock, hdtr->headers, hdtr->numheaders, 302251875Speter &hdrbytes); 303251875Speter if (arv != APR_SUCCESS) { 304251875Speter *len = 0; 305251875Speter return errno; 306251875Speter } 307251875Speter nbytes += hdrbytes; 308251875Speter 309251875Speter /* If this was a partial write and we aren't doing timeouts, 310251875Speter * return now with the partial byte count; this is a non-blocking 311251875Speter * socket. 312251875Speter */ 313251875Speter total_hdrbytes = 0; 314251875Speter for (i = 0; i < hdtr->numheaders; i++) { 315251875Speter total_hdrbytes += hdtr->headers[i].iov_len; 316251875Speter } 317251875Speter if (hdrbytes < total_hdrbytes) { 318251875Speter *len = hdrbytes; 319251875Speter return apr_socket_opt_set(sock, APR_TCP_NOPUSH, 0); 320251875Speter } 321251875Speter } 322251875Speter 323251875Speter if (sock->options & APR_INCOMPLETE_WRITE) { 324251875Speter sock->options &= ~APR_INCOMPLETE_WRITE; 325251875Speter goto do_select; 326251875Speter } 327251875Speter 328251875Speter do { 329251875Speter rv = sendfile(sock->socketdes, /* socket */ 330251875Speter file->filedes, /* open file descriptor of the file to be sent */ 331251875Speter &off, /* where in the file to start */ 332251875Speter *len); /* number of bytes to send */ 333251875Speter } while (rv == -1 && errno == EINTR); 334251875Speter 335251875Speter while ((rv == -1) && (errno == EAGAIN || errno == EWOULDBLOCK) 336251875Speter && (sock->timeout > 0)) { 337251875Speterdo_select: 338251875Speter arv = apr_wait_for_io_or_timeout(NULL, sock, 0); 339251875Speter if (arv != APR_SUCCESS) { 340251875Speter *len = 0; 341251875Speter return arv; 342251875Speter } 343251875Speter else { 344251875Speter do { 345251875Speter rv = sendfile(sock->socketdes, /* socket */ 346251875Speter file->filedes, /* open file descriptor of the file to be sent */ 347251875Speter &off, /* where in the file to start */ 348251875Speter *len); /* number of bytes to send */ 349251875Speter } while (rv == -1 && errno == EINTR); 350251875Speter } 351251875Speter } 352251875Speter 353251875Speter if (rv == -1) { 354251875Speter *len = nbytes; 355251875Speter rv = errno; 356251875Speter apr_socket_opt_set(sock, APR_TCP_NOPUSH, 0); 357251875Speter return rv; 358251875Speter } 359251875Speter 360251875Speter nbytes += rv; 361251875Speter 362251875Speter if (rv < *len) { 363251875Speter *len = nbytes; 364251875Speter arv = apr_socket_opt_set(sock, APR_TCP_NOPUSH, 0); 365251875Speter if (rv > 0) { 366251875Speter 367251875Speter /* If this was a partial write, return now with the 368251875Speter * partial byte count; this is a non-blocking socket. 369251875Speter */ 370251875Speter 371251875Speter if (sock->timeout > 0) { 372251875Speter sock->options |= APR_INCOMPLETE_WRITE; 373251875Speter } 374251875Speter return arv; 375251875Speter } 376251875Speter else { 377251875Speter /* If the file got smaller mid-request, eventually the offset 378251875Speter * becomes equal to the new file size and the kernel returns 0. 379251875Speter * Make this an error so the caller knows to log something and 380251875Speter * exit. 381251875Speter */ 382251875Speter return APR_EOF; 383251875Speter } 384251875Speter } 385251875Speter 386251875Speter /* Now write the footers */ 387251875Speter if (hdtr->numtrailers > 0) { 388251875Speter apr_size_t trbytes; 389251875Speter arv = apr_socket_sendv(sock, hdtr->trailers, hdtr->numtrailers, 390251875Speter &trbytes); 391251875Speter nbytes += trbytes; 392251875Speter if (arv != APR_SUCCESS) { 393251875Speter *len = nbytes; 394251875Speter rv = errno; 395251875Speter apr_socket_opt_set(sock, APR_TCP_NOPUSH, 0); 396251875Speter return rv; 397251875Speter } 398251875Speter } 399251875Speter 400251875Speter apr_socket_opt_set(sock, APR_TCP_NOPUSH, 0); 401251875Speter 402251875Speter (*len) = nbytes; 403251875Speter return rv < 0 ? errno : APR_SUCCESS; 404251875Speter} 405251875Speter 406251875Speter#elif defined(DARWIN) 407251875Speter 408251875Speter/* OS/X Release 10.5 or greater */ 409251875Speterapr_status_t apr_socket_sendfile(apr_socket_t *sock, apr_file_t *file, 410251875Speter apr_hdtr_t *hdtr, apr_off_t *offset, 411251875Speter apr_size_t *len, apr_int32_t flags) 412251875Speter{ 413251875Speter apr_off_t nbytes = 0; 414251875Speter apr_off_t bytes_to_send = *len; 415251875Speter apr_off_t bytes_sent = 0; 416251875Speter apr_status_t arv; 417251875Speter int rv = 0; 418251875Speter 419251875Speter /* Ignore flags for now. */ 420251875Speter flags = 0; 421251875Speter 422251875Speter if (!hdtr) { 423251875Speter hdtr = &no_hdtr; 424251875Speter } 425251875Speter 426251875Speter /* OS X can send the headers/footers as part of the system call, 427251875Speter * but how it counts bytes isn't documented properly. We use 428251875Speter * apr_socket_sendv() instead. 429251875Speter */ 430251875Speter if (hdtr->numheaders > 0) { 431251875Speter apr_size_t hbytes; 432251875Speter int i; 433251875Speter 434251875Speter /* Now write the headers */ 435251875Speter arv = apr_socket_sendv(sock, hdtr->headers, hdtr->numheaders, 436251875Speter &hbytes); 437251875Speter if (arv != APR_SUCCESS) { 438251875Speter *len = 0; 439251875Speter return errno; 440251875Speter } 441251875Speter bytes_sent = hbytes; 442251875Speter 443251875Speter hbytes = 0; 444251875Speter for (i = 0; i < hdtr->numheaders; i++) { 445251875Speter hbytes += hdtr->headers[i].iov_len; 446251875Speter } 447251875Speter if (bytes_sent < hbytes) { 448251875Speter *len = bytes_sent; 449251875Speter return APR_SUCCESS; 450251875Speter } 451251875Speter } 452251875Speter 453251875Speter do { 454251875Speter if (!bytes_to_send) { 455251875Speter break; 456251875Speter } 457251875Speter if (sock->options & APR_INCOMPLETE_WRITE) { 458251875Speter apr_status_t arv; 459251875Speter sock->options &= ~APR_INCOMPLETE_WRITE; 460251875Speter arv = apr_wait_for_io_or_timeout(NULL, sock, 0); 461251875Speter if (arv != APR_SUCCESS) { 462251875Speter *len = 0; 463251875Speter return arv; 464251875Speter } 465251875Speter } 466251875Speter 467251875Speter nbytes = bytes_to_send; 468251875Speter rv = sendfile(file->filedes, /* file to be sent */ 469251875Speter sock->socketdes, /* socket */ 470251875Speter *offset, /* where in the file to start */ 471251875Speter &nbytes, /* number of bytes to write/written */ 472251875Speter NULL, /* Headers/footers */ 473251875Speter flags); /* undefined, set to 0 */ 474251875Speter 475251875Speter if (rv == -1) { 476251875Speter if (errno == EAGAIN) { 477251875Speter if (sock->timeout > 0) { 478251875Speter sock->options |= APR_INCOMPLETE_WRITE; 479251875Speter } 480251875Speter /* BSD's sendfile can return -1/EAGAIN even if it 481251875Speter * sent bytes. Sanitize the result so we get normal EAGAIN 482251875Speter * semantics w.r.t. bytes sent. 483251875Speter */ 484251875Speter if (nbytes) { 485251875Speter bytes_sent += nbytes; 486251875Speter /* normal exit for a big file & non-blocking io */ 487251875Speter (*len) = bytes_sent; 488251875Speter return APR_SUCCESS; 489251875Speter } 490251875Speter } 491251875Speter } 492251875Speter else { /* rv == 0 (or the kernel is broken) */ 493251875Speter bytes_sent += nbytes; 494251875Speter if (nbytes == 0) { 495251875Speter /* Most likely the file got smaller after the stat. 496251875Speter * Return an error so the caller can do the Right Thing. 497251875Speter */ 498251875Speter (*len) = bytes_sent; 499251875Speter return APR_EOF; 500251875Speter } 501251875Speter } 502251875Speter } while (rv == -1 && (errno == EINTR || errno == EAGAIN)); 503251875Speter 504251875Speter /* Now write the footers */ 505251875Speter if (hdtr->numtrailers > 0) { 506251875Speter apr_size_t tbytes; 507251875Speter arv = apr_socket_sendv(sock, hdtr->trailers, hdtr->numtrailers, 508251875Speter &tbytes); 509251875Speter bytes_sent += tbytes; 510251875Speter if (arv != APR_SUCCESS) { 511251875Speter *len = bytes_sent; 512251875Speter rv = errno; 513251875Speter return rv; 514251875Speter } 515251875Speter } 516251875Speter 517251875Speter (*len) = bytes_sent; 518251875Speter if (rv == -1) { 519251875Speter return errno; 520251875Speter } 521251875Speter return APR_SUCCESS; 522251875Speter} 523251875Speter 524251875Speter#elif defined(__FreeBSD__) || defined(__DragonFly__) 525251875Speter 526251875Speter/* Release 3.1 or greater */ 527251875Speterapr_status_t apr_socket_sendfile(apr_socket_t * sock, apr_file_t * file, 528251875Speter apr_hdtr_t * hdtr, apr_off_t * offset, 529251875Speter apr_size_t * len, apr_int32_t flags) 530251875Speter{ 531251875Speter off_t nbytes = 0; 532251875Speter int rv; 533251875Speter#if defined(__FreeBSD_version) && __FreeBSD_version < 460001 534251875Speter int i; 535251875Speter#endif 536251875Speter struct sf_hdtr headerstruct; 537251875Speter apr_size_t bytes_to_send = *len; 538251875Speter 539251875Speter /* Ignore flags for now. */ 540251875Speter flags = 0; 541251875Speter 542251875Speter if (!hdtr) { 543251875Speter hdtr = &no_hdtr; 544251875Speter } 545251875Speter 546251875Speter#if defined(__FreeBSD_version) && __FreeBSD_version < 460001 547251875Speter else if (hdtr->numheaders) { 548251875Speter 549251875Speter /* On early versions of FreeBSD sendfile, the number of bytes to send 550251875Speter * must include the length of the headers. Don't look at the man page 551251875Speter * for this :( Instead, look at the the logic in 552251875Speter * src/sys/kern/uipc_syscalls::sendfile(). 553251875Speter * 554251875Speter * This was fixed in the middle of 4.6-STABLE 555251875Speter */ 556251875Speter for (i = 0; i < hdtr->numheaders; i++) { 557251875Speter bytes_to_send += hdtr->headers[i].iov_len; 558251875Speter } 559251875Speter } 560251875Speter#endif 561251875Speter 562251875Speter headerstruct.headers = hdtr->headers; 563251875Speter headerstruct.hdr_cnt = hdtr->numheaders; 564251875Speter headerstruct.trailers = hdtr->trailers; 565251875Speter headerstruct.trl_cnt = hdtr->numtrailers; 566251875Speter 567251875Speter /* FreeBSD can send the headers/footers as part of the system call */ 568251875Speter do { 569251875Speter if (sock->options & APR_INCOMPLETE_WRITE) { 570251875Speter apr_status_t arv; 571251875Speter sock->options &= ~APR_INCOMPLETE_WRITE; 572251875Speter arv = apr_wait_for_io_or_timeout(NULL, sock, 0); 573251875Speter if (arv != APR_SUCCESS) { 574251875Speter *len = 0; 575251875Speter return arv; 576251875Speter } 577251875Speter } 578251875Speter if (bytes_to_send) { 579251875Speter /* We won't dare call sendfile() if we don't have 580251875Speter * header or file bytes to send because bytes_to_send == 0 581251875Speter * means send the whole file. 582251875Speter */ 583251875Speter rv = sendfile(file->filedes, /* file to be sent */ 584251875Speter sock->socketdes, /* socket */ 585251875Speter *offset, /* where in the file to start */ 586251875Speter bytes_to_send, /* number of bytes to send */ 587251875Speter &headerstruct, /* Headers/footers */ 588251875Speter &nbytes, /* number of bytes written */ 589251875Speter flags); /* undefined, set to 0 */ 590251875Speter 591251875Speter if (rv == -1) { 592251875Speter if (errno == EAGAIN) { 593251875Speter if (sock->timeout > 0) { 594251875Speter sock->options |= APR_INCOMPLETE_WRITE; 595251875Speter } 596251875Speter /* FreeBSD's sendfile can return -1/EAGAIN even if it 597251875Speter * sent bytes. Sanitize the result so we get normal EAGAIN 598251875Speter * semantics w.r.t. bytes sent. 599251875Speter */ 600251875Speter if (nbytes) { 601251875Speter /* normal exit for a big file & non-blocking io */ 602251875Speter (*len) = nbytes; 603251875Speter return APR_SUCCESS; 604251875Speter } 605251875Speter } 606251875Speter } 607251875Speter else { /* rv == 0 (or the kernel is broken) */ 608251875Speter if (nbytes == 0) { 609251875Speter /* Most likely the file got smaller after the stat. 610251875Speter * Return an error so the caller can do the Right Thing. 611251875Speter */ 612251875Speter (*len) = nbytes; 613251875Speter return APR_EOF; 614251875Speter } 615251875Speter } 616251875Speter } 617251875Speter else { 618251875Speter /* just trailer bytes... use writev() 619251875Speter */ 620251875Speter rv = writev(sock->socketdes, 621251875Speter hdtr->trailers, 622251875Speter hdtr->numtrailers); 623251875Speter if (rv > 0) { 624251875Speter nbytes = rv; 625251875Speter rv = 0; 626251875Speter } 627251875Speter else { 628251875Speter nbytes = 0; 629251875Speter } 630251875Speter } 631251875Speter if ((rv == -1) && (errno == EAGAIN) 632251875Speter && (sock->timeout > 0)) { 633251875Speter apr_status_t arv = apr_wait_for_io_or_timeout(NULL, sock, 0); 634251875Speter if (arv != APR_SUCCESS) { 635251875Speter *len = 0; 636251875Speter return arv; 637251875Speter } 638251875Speter } 639251875Speter } while (rv == -1 && (errno == EINTR || errno == EAGAIN)); 640251875Speter 641251875Speter (*len) = nbytes; 642251875Speter if (rv == -1) { 643251875Speter return errno; 644251875Speter } 645251875Speter return APR_SUCCESS; 646251875Speter} 647251875Speter 648251875Speter#elif defined(__hpux) || defined(__hpux__) 649251875Speter 650251875Speter/* HP cc in ANSI mode defines __hpux; gcc defines __hpux__ */ 651251875Speter 652251875Speter/* HP-UX Version 10.30 or greater 653251875Speter * (no worries, because we only get here if autoconfiguration found sendfile) 654251875Speter */ 655251875Speter 656251875Speter/* ssize_t sendfile(int s, int fd, off_t offset, size_t nbytes, 657251875Speter * const struct iovec *hdtrl, int flags); 658251875Speter * 659251875Speter * nbytes is the number of bytes to send just from the file; as with FreeBSD, 660251875Speter * if nbytes == 0, the rest of the file (from offset) is sent 661251875Speter */ 662251875Speter 663251875Speterapr_status_t apr_socket_sendfile(apr_socket_t *sock, apr_file_t *file, 664251875Speter apr_hdtr_t *hdtr, apr_off_t *offset, 665251875Speter apr_size_t *len, apr_int32_t flags) 666251875Speter{ 667251875Speter int i; 668251875Speter apr_ssize_t rc; 669251875Speter apr_size_t nbytes = *len, headerlen, trailerlen; 670251875Speter struct iovec hdtrarray[2]; 671251875Speter char *headerbuf, *trailerbuf; 672251875Speter 673251875Speter#if APR_HAS_LARGE_FILES && defined(HAVE_SENDFILE64) 674251875Speter /* later HP-UXes have a sendfile64() */ 675251875Speter#define sendfile sendfile64 676251875Speter apr_off_t off = *offset; 677251875Speter 678251875Speter#elif APR_HAS_LARGE_FILES && SIZEOF_OFF_T == 4 679251875Speter /* HP-UX 11.00 doesn't have a sendfile64(): fail if trying to send 680251875Speter * past the 2Gb limit */ 681251875Speter off_t off; 682251875Speter 683251875Speter if ((apr_int64_t)*offset + *len > INT_MAX) { 684251875Speter return EINVAL; 685251875Speter } 686251875Speter off = *offset; 687251875Speter#else 688251875Speter apr_off_t off = *offset; 689251875Speter#endif 690251875Speter 691251875Speter if (!hdtr) { 692251875Speter hdtr = &no_hdtr; 693251875Speter } 694251875Speter 695251875Speter /* Ignore flags for now. */ 696251875Speter flags = 0; 697251875Speter 698251875Speter /* HP-UX can only send one header iovec and one footer iovec; try to 699251875Speter * only allocate storage to combine input iovecs when we really have to 700251875Speter */ 701251875Speter 702251875Speter switch(hdtr->numheaders) { 703251875Speter case 0: 704251875Speter hdtrarray[0].iov_base = NULL; 705251875Speter hdtrarray[0].iov_len = 0; 706251875Speter break; 707251875Speter case 1: 708251875Speter hdtrarray[0] = hdtr->headers[0]; 709251875Speter break; 710251875Speter default: 711251875Speter headerlen = 0; 712251875Speter for (i = 0; i < hdtr->numheaders; i++) { 713251875Speter headerlen += hdtr->headers[i].iov_len; 714251875Speter } 715251875Speter 716251875Speter /* XXX: BUHHH? wow, what a memory leak! */ 717251875Speter headerbuf = hdtrarray[0].iov_base = apr_palloc(sock->pool, headerlen); 718251875Speter hdtrarray[0].iov_len = headerlen; 719251875Speter 720251875Speter for (i = 0; i < hdtr->numheaders; i++) { 721251875Speter memcpy(headerbuf, hdtr->headers[i].iov_base, 722251875Speter hdtr->headers[i].iov_len); 723251875Speter headerbuf += hdtr->headers[i].iov_len; 724251875Speter } 725251875Speter } 726251875Speter 727251875Speter switch(hdtr->numtrailers) { 728251875Speter case 0: 729251875Speter hdtrarray[1].iov_base = NULL; 730251875Speter hdtrarray[1].iov_len = 0; 731251875Speter break; 732251875Speter case 1: 733251875Speter hdtrarray[1] = hdtr->trailers[0]; 734251875Speter break; 735251875Speter default: 736251875Speter trailerlen = 0; 737251875Speter for (i = 0; i < hdtr->numtrailers; i++) { 738251875Speter trailerlen += hdtr->trailers[i].iov_len; 739251875Speter } 740251875Speter 741251875Speter /* XXX: BUHHH? wow, what a memory leak! */ 742251875Speter trailerbuf = hdtrarray[1].iov_base = apr_palloc(sock->pool, trailerlen); 743251875Speter hdtrarray[1].iov_len = trailerlen; 744251875Speter 745251875Speter for (i = 0; i < hdtr->numtrailers; i++) { 746251875Speter memcpy(trailerbuf, hdtr->trailers[i].iov_base, 747251875Speter hdtr->trailers[i].iov_len); 748251875Speter trailerbuf += hdtr->trailers[i].iov_len; 749251875Speter } 750251875Speter } 751251875Speter 752251875Speter do { 753251875Speter if (nbytes) { /* any bytes to send from the file? */ 754251875Speter rc = sendfile(sock->socketdes, /* socket */ 755251875Speter file->filedes, /* file descriptor to send */ 756251875Speter off, /* where in the file to start */ 757251875Speter nbytes, /* number of bytes to send from file */ 758251875Speter hdtrarray, /* Headers/footers */ 759251875Speter flags); /* undefined, set to 0 */ 760251875Speter } 761251875Speter else { /* we can't call sendfile() with no bytes to send from the file */ 762251875Speter rc = writev(sock->socketdes, hdtrarray, 2); 763251875Speter } 764251875Speter } while (rc == -1 && errno == EINTR); 765251875Speter 766251875Speter while ((rc == -1) && (errno == EAGAIN || errno == EWOULDBLOCK) 767251875Speter && (sock->timeout > 0)) { 768251875Speter apr_status_t arv = apr_wait_for_io_or_timeout(NULL, sock, 0); 769251875Speter 770251875Speter if (arv != APR_SUCCESS) { 771251875Speter *len = 0; 772251875Speter return arv; 773251875Speter } 774251875Speter else { 775251875Speter do { 776251875Speter if (nbytes) { 777251875Speter rc = sendfile(sock->socketdes, /* socket */ 778251875Speter file->filedes, /* file descriptor to send */ 779251875Speter off, /* where in the file to start */ 780251875Speter nbytes, /* number of bytes to send from file */ 781251875Speter hdtrarray, /* Headers/footers */ 782251875Speter flags); /* undefined, set to 0 */ 783251875Speter } 784251875Speter else { /* we can't call sendfile() with no bytes to send from the file */ 785251875Speter rc = writev(sock->socketdes, hdtrarray, 2); 786251875Speter } 787251875Speter } while (rc == -1 && errno == EINTR); 788251875Speter } 789251875Speter } 790251875Speter 791251875Speter if (rc == -1) { 792251875Speter *len = 0; 793251875Speter return errno; 794251875Speter } 795251875Speter 796251875Speter /* Set len to the number of bytes written */ 797251875Speter *len = rc; 798251875Speter return APR_SUCCESS; 799251875Speter} 800251875Speter#elif defined(_AIX) || defined(__MVS__) 801251875Speter/* AIX and OS/390 have the same send_file() interface. 802251875Speter * 803251875Speter * subtle differences: 804251875Speter * AIX doesn't update the file ptr but OS/390 does 805251875Speter * 806251875Speter * availability (correctly determined by autoconf): 807251875Speter * 808251875Speter * AIX - version 4.3.2 with APAR IX85388, or version 4.3.3 and above 809251875Speter * OS/390 - V2R7 and above 810251875Speter */ 811251875Speterapr_status_t apr_socket_sendfile(apr_socket_t * sock, apr_file_t * file, 812251875Speter apr_hdtr_t * hdtr, apr_off_t * offset, 813251875Speter apr_size_t * len, apr_int32_t flags) 814251875Speter{ 815251875Speter int i, ptr, rv = 0; 816251875Speter void * hbuf=NULL, * tbuf=NULL; 817251875Speter apr_status_t arv; 818251875Speter struct sf_parms parms; 819251875Speter 820251875Speter if (!hdtr) { 821251875Speter hdtr = &no_hdtr; 822251875Speter } 823251875Speter 824251875Speter /* Ignore flags for now. */ 825251875Speter flags = 0; 826251875Speter 827251875Speter /* word to the wise: by default, AIX stores files sent by send_file() 828251875Speter * in the network buffer cache... there are supposedly scenarios 829251875Speter * where the most recent copy of the file won't be sent, but I can't 830251875Speter * recreate the potential problem, perhaps because of the way we 831251875Speter * use send_file()... if you suspect such a problem, try turning 832251875Speter * on the SF_SYNC_CACHE flag 833251875Speter */ 834251875Speter 835251875Speter /* AIX can also send the headers/footers as part of the system call */ 836251875Speter parms.header_length = 0; 837251875Speter if (hdtr && hdtr->numheaders) { 838251875Speter if (hdtr->numheaders == 1) { 839251875Speter parms.header_data = hdtr->headers[0].iov_base; 840251875Speter parms.header_length = hdtr->headers[0].iov_len; 841251875Speter } 842251875Speter else { 843251875Speter for (i = 0; i < hdtr->numheaders; i++) { 844251875Speter parms.header_length += hdtr->headers[i].iov_len; 845251875Speter } 846251875Speter#if 0 847251875Speter /* Keepalives make apr_palloc a bad idea */ 848251875Speter hbuf = malloc(parms.header_length); 849251875Speter#else 850251875Speter /* but headers are small, so maybe we can hold on to the 851251875Speter * memory for the life of the socket... 852251875Speter */ 853251875Speter hbuf = apr_palloc(sock->pool, parms.header_length); 854251875Speter#endif 855251875Speter ptr = 0; 856251875Speter for (i = 0; i < hdtr->numheaders; i++) { 857251875Speter memcpy((char *)hbuf + ptr, hdtr->headers[i].iov_base, 858251875Speter hdtr->headers[i].iov_len); 859251875Speter ptr += hdtr->headers[i].iov_len; 860251875Speter } 861251875Speter parms.header_data = hbuf; 862251875Speter } 863251875Speter } 864251875Speter else parms.header_data = NULL; 865251875Speter parms.trailer_length = 0; 866251875Speter if (hdtr && hdtr->numtrailers) { 867251875Speter if (hdtr->numtrailers == 1) { 868251875Speter parms.trailer_data = hdtr->trailers[0].iov_base; 869251875Speter parms.trailer_length = hdtr->trailers[0].iov_len; 870251875Speter } 871251875Speter else { 872251875Speter for (i = 0; i < hdtr->numtrailers; i++) { 873251875Speter parms.trailer_length += hdtr->trailers[i].iov_len; 874251875Speter } 875251875Speter#if 0 876251875Speter /* Keepalives make apr_palloc a bad idea */ 877251875Speter tbuf = malloc(parms.trailer_length); 878251875Speter#else 879251875Speter tbuf = apr_palloc(sock->pool, parms.trailer_length); 880251875Speter#endif 881251875Speter ptr = 0; 882251875Speter for (i = 0; i < hdtr->numtrailers; i++) { 883251875Speter memcpy((char *)tbuf + ptr, hdtr->trailers[i].iov_base, 884251875Speter hdtr->trailers[i].iov_len); 885251875Speter ptr += hdtr->trailers[i].iov_len; 886251875Speter } 887251875Speter parms.trailer_data = tbuf; 888251875Speter } 889251875Speter } 890251875Speter else { 891251875Speter parms.trailer_data = NULL; 892251875Speter } 893251875Speter 894251875Speter /* Whew! Headers and trailers set up. Now for the file data */ 895251875Speter 896251875Speter parms.file_descriptor = file->filedes; 897251875Speter parms.file_offset = *offset; 898251875Speter parms.file_bytes = *len; 899251875Speter 900251875Speter /* O.K. All set up now. Let's go to town */ 901251875Speter 902251875Speter if (sock->options & APR_INCOMPLETE_WRITE) { 903251875Speter sock->options &= ~APR_INCOMPLETE_WRITE; 904251875Speter goto do_select; 905251875Speter } 906251875Speter 907251875Speter do { 908251875Speter rv = send_file(&(sock->socketdes), /* socket */ 909251875Speter &(parms), /* all data */ 910251875Speter flags); /* flags */ 911251875Speter } while (rv == -1 && errno == EINTR); 912251875Speter 913251875Speter while ((rv == -1) && (errno == EAGAIN || errno == EWOULDBLOCK) 914251875Speter && (sock->timeout > 0)) { 915251875Speterdo_select: 916251875Speter arv = apr_wait_for_io_or_timeout(NULL, sock, 0); 917251875Speter if (arv != APR_SUCCESS) { 918251875Speter *len = 0; 919251875Speter return arv; 920251875Speter } 921251875Speter else { 922251875Speter do { 923251875Speter rv = send_file(&(sock->socketdes), /* socket */ 924251875Speter &(parms), /* all data */ 925251875Speter flags); /* flags */ 926251875Speter } while (rv == -1 && errno == EINTR); 927251875Speter } 928251875Speter } 929251875Speter 930251875Speter (*len) = parms.bytes_sent; 931251875Speter 932251875Speter#if 0 933251875Speter /* Clean up after ourselves */ 934251875Speter if(hbuf) free(hbuf); 935251875Speter if(tbuf) free(tbuf); 936251875Speter#endif 937251875Speter 938251875Speter if (rv == -1) { 939251875Speter return errno; 940251875Speter } 941251875Speter 942251875Speter if ((sock->timeout > 0) 943251875Speter && (parms.bytes_sent 944251875Speter < (parms.file_bytes + parms.header_length + parms.trailer_length))) { 945251875Speter sock->options |= APR_INCOMPLETE_WRITE; 946251875Speter } 947251875Speter 948251875Speter return APR_SUCCESS; 949251875Speter} 950251875Speter#elif defined(__osf__) && defined (__alpha) 951251875Speter/* Tru64's sendfile implementation doesn't work, and we need to make sure that 952251875Speter * we don't use it until it is fixed. If it is used as it is now, it will 953251875Speter * hang the machine and the only way to fix it is a reboot. 954251875Speter */ 955251875Speter#elif defined(HAVE_SENDFILEV) 956251875Speter/* Solaris 8's sendfilev() interface 957251875Speter * 958251875Speter * SFV_FD_SELF refers to our memory space. 959251875Speter * 960251875Speter * Required Sparc patches (or newer): 961251875Speter * 111297-01, 108528-09, 109472-06, 109234-03, 108995-02, 111295-01, 109025-03, 962251875Speter * 108991-13 963251875Speter * Required x86 patches (or newer): 964251875Speter * 111298-01, 108529-09, 109473-06, 109235-04, 108996-02, 111296-01, 109026-04, 965251875Speter * 108992-13 966251875Speter */ 967251875Speter 968251875Speter#if APR_HAS_LARGE_FILES && defined(HAVE_SENDFILEV64) 969251875Speter#define sendfilevec_t sendfilevec64_t 970251875Speter#define sendfilev sendfilev64 971251875Speter#endif 972251875Speter 973251875Speterapr_status_t apr_socket_sendfile(apr_socket_t *sock, apr_file_t *file, 974251875Speter apr_hdtr_t *hdtr, apr_off_t *offset, 975251875Speter apr_size_t *len, apr_int32_t flags) 976251875Speter{ 977251875Speter apr_status_t rv, arv; 978251875Speter apr_size_t nbytes; 979251875Speter sendfilevec_t *sfv; 980251875Speter int vecs, curvec, i, repeat; 981251875Speter apr_size_t requested_len = 0; 982251875Speter 983251875Speter if (!hdtr) { 984251875Speter hdtr = &no_hdtr; 985251875Speter } 986251875Speter 987251875Speter /* Ignore flags for now. */ 988251875Speter flags = 0; 989251875Speter 990251875Speter /* Calculate how much space we need. */ 991251875Speter vecs = hdtr->numheaders + hdtr->numtrailers + 1; 992251875Speter sfv = apr_palloc(sock->pool, sizeof(sendfilevec_t) * vecs); 993251875Speter 994251875Speter curvec = 0; 995251875Speter 996251875Speter /* Add the headers */ 997251875Speter for (i = 0; i < hdtr->numheaders; i++, curvec++) { 998251875Speter sfv[curvec].sfv_fd = SFV_FD_SELF; 999251875Speter sfv[curvec].sfv_flag = 0; 1000251875Speter /* Cast to unsigned long to prevent sign extension of the 1001251875Speter * pointer value for the LFS case; see PR 39463. */ 1002251875Speter sfv[curvec].sfv_off = (unsigned long)hdtr->headers[i].iov_base; 1003251875Speter sfv[curvec].sfv_len = hdtr->headers[i].iov_len; 1004251875Speter requested_len += sfv[curvec].sfv_len; 1005251875Speter } 1006251875Speter 1007251875Speter /* If the len is 0, we skip the file. */ 1008251875Speter if (*len) 1009251875Speter { 1010251875Speter sfv[curvec].sfv_fd = file->filedes; 1011251875Speter sfv[curvec].sfv_flag = 0; 1012251875Speter sfv[curvec].sfv_off = *offset; 1013251875Speter sfv[curvec].sfv_len = *len; 1014251875Speter requested_len += sfv[curvec].sfv_len; 1015251875Speter 1016251875Speter curvec++; 1017251875Speter } 1018251875Speter else { 1019251875Speter vecs--; 1020251875Speter } 1021251875Speter 1022251875Speter /* Add the footers */ 1023251875Speter for (i = 0; i < hdtr->numtrailers; i++, curvec++) { 1024251875Speter sfv[curvec].sfv_fd = SFV_FD_SELF; 1025251875Speter sfv[curvec].sfv_flag = 0; 1026251875Speter sfv[curvec].sfv_off = (unsigned long)hdtr->trailers[i].iov_base; 1027251875Speter sfv[curvec].sfv_len = hdtr->trailers[i].iov_len; 1028251875Speter requested_len += sfv[curvec].sfv_len; 1029251875Speter } 1030251875Speter 1031251875Speter /* If the last write couldn't send all the requested data, 1032251875Speter * wait for the socket to become writable before proceeding 1033251875Speter */ 1034251875Speter if (sock->options & APR_INCOMPLETE_WRITE) { 1035251875Speter sock->options &= ~APR_INCOMPLETE_WRITE; 1036251875Speter arv = apr_wait_for_io_or_timeout(NULL, sock, 0); 1037251875Speter if (arv != APR_SUCCESS) { 1038251875Speter *len = 0; 1039251875Speter return arv; 1040251875Speter } 1041251875Speter } 1042251875Speter 1043251875Speter /* Actually do the sendfilev 1044251875Speter * 1045251875Speter * Solaris may return -1/EAGAIN even if it sent bytes on a non-block sock. 1046251875Speter * 1047251875Speter * If no bytes were originally sent (nbytes == 0) and we are on a TIMEOUT 1048251875Speter * socket (which as far as the OS is concerned is a non-blocking socket), 1049251875Speter * we want to retry after waiting for the other side to read the data (as 1050251875Speter * determined by poll). Once it is clear to send, we want to retry 1051251875Speter * sending the sendfilevec_t once more. 1052251875Speter */ 1053251875Speter arv = 0; 1054251875Speter do { 1055251875Speter /* Clear out the repeat */ 1056251875Speter repeat = 0; 1057251875Speter 1058251875Speter /* socket, vecs, number of vecs, bytes written */ 1059251875Speter rv = sendfilev(sock->socketdes, sfv, vecs, &nbytes); 1060251875Speter 1061251875Speter if (rv == -1 && errno == EAGAIN) { 1062251875Speter if (nbytes) { 1063251875Speter rv = 0; 1064251875Speter } 1065251875Speter else if (!arv && (sock->timeout > 0)) { 1066251875Speter apr_status_t t = apr_wait_for_io_or_timeout(NULL, sock, 0); 1067251875Speter 1068251875Speter if (t != APR_SUCCESS) { 1069251875Speter *len = 0; 1070251875Speter return t; 1071251875Speter } 1072251875Speter 1073251875Speter arv = 1; 1074251875Speter repeat = 1; 1075251875Speter } 1076251875Speter } 1077251875Speter } while ((rv == -1 && errno == EINTR) || repeat); 1078251875Speter 1079251875Speter if (rv == -1) { 1080251875Speter *len = 0; 1081251875Speter return errno; 1082251875Speter } 1083251875Speter 1084251875Speter /* Update how much we sent */ 1085251875Speter *len = nbytes; 1086251875Speter 1087251875Speter if (nbytes == 0) { 1088251875Speter /* Most likely the file got smaller after the stat. 1089251875Speter * Return an error so the caller can do the Right Thing. 1090251875Speter */ 1091251875Speter return APR_EOF; 1092251875Speter } 1093251875Speter 1094251875Speter if ((sock->timeout > 0) && (*len < requested_len)) { 1095251875Speter sock->options |= APR_INCOMPLETE_WRITE; 1096251875Speter } 1097251875Speter return APR_SUCCESS; 1098251875Speter} 1099251875Speter#else 1100251875Speter#error APR has detected sendfile on your system, but nobody has written a 1101251875Speter#error version of it for APR yet. To get past this, either write 1102251875Speter#error apr_socket_sendfile or change APR_HAS_SENDFILE in apr.h to 0. 1103251875Speter#endif /* __linux__, __FreeBSD__, __DragonFly__, __HPUX__, _AIX, __MVS__, 1104251875Speter Tru64/OSF1 */ 1105251875Speter 1106251875Speter#endif /* APR_HAS_SENDFILE */ 1107