src/os/unix/ngx_darwin_sendfile_chain.c - nginx-1.7.10

Functions defined

Source code


  1. /*
  2. * Copyright (C) Igor Sysoev
  3. * Copyright (C) Nginx, Inc.
  4. */


  5. #include <ngx_config.h>
  6. #include <ngx_core.h>
  7. #include <ngx_event.h>


  8. /*
  9. * It seems that Darwin 9.4 (Mac OS X 1.5) sendfile() has the same
  10. * old bug as early FreeBSD sendfile() syscall:
  11. * http://bugs.freebsd.org/33771
  12. *
  13. * Besides sendfile() has another bug: if one calls sendfile()
  14. * with both a header and a trailer, then sendfile() ignores a file part
  15. * at all and sends only the header and the trailer together.
  16. * For this reason we send a trailer only if there is no a header.
  17. *
  18. * Although sendfile() allows to pass a header or a trailer,
  19. * it may send the header or the trailer and a part of the file
  20. * in different packets.  And FreeBSD workaround (TCP_NOPUSH option)
  21. * does not help.
  22. */


  23. ngx_chain_t *
  24. ngx_darwin_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
  25. {
  26.     int              rc;
  27.     off_t            send, prev_send, sent;
  28.     off_t            file_size;
  29.     ssize_t          n;
  30.     ngx_uint_t       eintr;
  31.     ngx_err_t        err;
  32.     ngx_buf_t       *file;
  33.     ngx_event_t     *wev;
  34.     ngx_chain_t     *cl;
  35.     ngx_iovec_t      header, trailer;
  36.     struct sf_hdtr   hdtr;
  37.     struct iovec     headers[NGX_IOVS_PREALLOCATE];
  38.     struct iovec     trailers[NGX_IOVS_PREALLOCATE];

  39.     wev = c->write;

  40.     if (!wev->ready) {
  41.         return in;
  42.     }

  43. #if (NGX_HAVE_KQUEUE)

  44.     if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) {
  45.         (void) ngx_connection_error(c, wev->kq_errno,
  46.                                "kevent() reported about an closed connection");
  47.         wev->error = 1;
  48.         return NGX_CHAIN_ERROR;
  49.     }

  50. #endif

  51.     /* the maximum limit size is the maximum size_t value - the page size */

  52.     if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) {
  53.         limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize;
  54.     }

  55.     send = 0;

  56.     header.iovs = headers;
  57.     header.nalloc = NGX_IOVS_PREALLOCATE;

  58.     trailer.iovs = trailers;
  59.     trailer.nalloc = NGX_IOVS_PREALLOCATE;

  60.     for ( ;; ) {
  61.         eintr = 0;
  62.         prev_send = send;

  63.         /* create the header iovec and coalesce the neighbouring bufs */

  64.         cl = ngx_output_chain_to_iovec(&header, in, limit - send, c->log);

  65.         if (cl == NGX_CHAIN_ERROR) {
  66.             return NGX_CHAIN_ERROR;
  67.         }

  68.         send += header.size;

  69.         if (cl && cl->buf->in_file && send < limit) {
  70.             file = cl->buf;

  71.             /* coalesce the neighbouring file bufs */

  72.             file_size = ngx_chain_coalesce_file(&cl, limit - send);

  73.             send += file_size;

  74.             if (header.count == 0) {

  75.                 /*
  76.                  * create the trailer iovec and coalesce the neighbouring bufs
  77.                  */

  78.                 cl = ngx_output_chain_to_iovec(&trailer, cl, limit - send,
  79.                                                c->log);
  80.                 if (cl == NGX_CHAIN_ERROR) {
  81.                     return NGX_CHAIN_ERROR;
  82.                 }

  83.                 send += trailer.size;

  84.             } else {
  85.                 trailer.count = 0;
  86.             }

  87.             /*
  88.              * sendfile() returns EINVAL if sf_hdtr's count is 0,
  89.              * but corresponding pointer is not NULL
  90.              */

  91.             hdtr.headers = header.count ? header.iovs : NULL;
  92.             hdtr.hdr_cnt = header.count;
  93.             hdtr.trailers = trailer.count ? trailer.iovs : NULL;
  94.             hdtr.trl_cnt = trailer.count;

  95.             sent = header.size + file_size;

  96.             ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
  97.                            "sendfile: @%O %O h:%uz",
  98.                            file->file_pos, sent, header.size);

  99.             rc = sendfile(file->file->fd, c->fd, file->file_pos,
  100.                           &sent, &hdtr, 0);

  101.             if (rc == -1) {
  102.                 err = ngx_errno;

  103.                 switch (err) {
  104.                 case NGX_EAGAIN:
  105.                     break;

  106.                 case NGX_EINTR:
  107.                     eintr = 1;
  108.                     break;

  109.                 default:
  110.                     wev->error = 1;
  111.                     (void) ngx_connection_error(c, err, "sendfile() failed");
  112.                     return NGX_CHAIN_ERROR;
  113.                 }

  114.                 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, err,
  115.                                "sendfile() sent only %O bytes", sent);
  116.             }

  117.             if (rc == 0 && sent == 0) {

  118.                 /*
  119.                  * if rc and sent equal to zero, then someone
  120.                  * has truncated the file, so the offset became beyond
  121.                  * the end of the file
  122.                  */

  123.                 ngx_log_error(NGX_LOG_ALERT, c->log, 0,
  124.                               "sendfile() reported that \"%s\" was truncated",
  125.                               file->file->name.data);

  126.                 return NGX_CHAIN_ERROR;
  127.             }

  128.             ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
  129.                            "sendfile: %d, @%O %O:%O",
  130.                            rc, file->file_pos, sent, file_size + header.size);

  131.         } else {
  132.             n = ngx_writev(c, &header);

  133.             if (n == NGX_ERROR) {
  134.                 return NGX_CHAIN_ERROR;
  135.             }

  136.             sent = (n == NGX_AGAIN) ? 0 : n;
  137.         }

  138.         c->sent += sent;

  139.         in = ngx_chain_update_sent(in, sent);

  140.         if (eintr) {
  141.             send = prev_send + sent;
  142.             continue;
  143.         }

  144.         if (send - prev_send != sent) {
  145.             wev->ready = 0;
  146.             return in;
  147.         }

  148.         if (send >= limit || in == NULL) {
  149.             return in;
  150.         }
  151.     }
  152. }