Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.15.12 ]​[ nginx-1.16.0 ]​

0001 
0002 /*
0003  * Copyright (C) Igor Sysoev
0004  * Copyright (C) Nginx, Inc.
0005  */
0006 
0007 
0008 #include <ngx_config.h>
0009 #include <ngx_core.h>
0010 #include <ngx_event.h>
0011 
0012 
0013 /*
0014  * It seems that Darwin 9.4 (Mac OS X 1.5) sendfile() has the same
0015  * old bug as early FreeBSD sendfile() syscall:
0016  * http://bugs.freebsd.org/33771
0017  *
0018  * Besides sendfile() has another bug: if one calls sendfile()
0019  * with both a header and a trailer, then sendfile() ignores a file part
0020  * at all and sends only the header and the trailer together.
0021  * For this reason we send a trailer only if there is no a header.
0022  *
0023  * Although sendfile() allows to pass a header or a trailer,
0024  * it may send the header or the trailer and a part of the file
0025  * in different packets.  And FreeBSD workaround (TCP_NOPUSH option)
0026  * does not help.
0027  */
0028 
0029 
0030 ngx_chain_t *
0031 ngx_darwin_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
0032 {
0033     int              rc;
0034     off_t            send, prev_send, sent;
0035     off_t            file_size;
0036     ssize_t          n;
0037     ngx_uint_t       eintr;
0038     ngx_err_t        err;
0039     ngx_buf_t       *file;
0040     ngx_event_t     *wev;
0041     ngx_chain_t     *cl;
0042     ngx_iovec_t      header, trailer;
0043     struct sf_hdtr   hdtr;
0044     struct iovec     headers[NGX_IOVS_PREALLOCATE];
0045     struct iovec     trailers[NGX_IOVS_PREALLOCATE];
0046 
0047     wev = c->write;
0048 
0049     if (!wev->ready) {
0050         return in;
0051     }
0052 
0053 #if (NGX_HAVE_KQUEUE)
0054 
0055     if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) {
0056         (void) ngx_connection_error(c, wev->kq_errno,
0057                                "kevent() reported about an closed connection");
0058         wev->error = 1;
0059         return NGX_CHAIN_ERROR;
0060     }
0061 
0062 #endif
0063 
0064     /* the maximum limit size is the maximum size_t value - the page size */
0065 
0066     if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) {
0067         limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize;
0068     }
0069 
0070     send = 0;
0071 
0072     header.iovs = headers;
0073     header.nalloc = NGX_IOVS_PREALLOCATE;
0074 
0075     trailer.iovs = trailers;
0076     trailer.nalloc = NGX_IOVS_PREALLOCATE;
0077 
0078     for ( ;; ) {
0079         eintr = 0;
0080         prev_send = send;
0081 
0082         /* create the header iovec and coalesce the neighbouring bufs */
0083 
0084         cl = ngx_output_chain_to_iovec(&header, in, limit - send, c->log);
0085 
0086         if (cl == NGX_CHAIN_ERROR) {
0087             return NGX_CHAIN_ERROR;
0088         }
0089 
0090         send += header.size;
0091 
0092         if (cl && cl->buf->in_file && send < limit) {
0093             file = cl->buf;
0094 
0095             /* coalesce the neighbouring file bufs */
0096 
0097             file_size = ngx_chain_coalesce_file(&cl, limit - send);
0098 
0099             send += file_size;
0100 
0101             if (header.count == 0 && send < limit) {
0102 
0103                 /*
0104                  * create the trailer iovec and coalesce the neighbouring bufs
0105                  */
0106 
0107                 cl = ngx_output_chain_to_iovec(&trailer, cl, limit - send,
0108                                                c->log);
0109                 if (cl == NGX_CHAIN_ERROR) {
0110                     return NGX_CHAIN_ERROR;
0111                 }
0112 
0113                 send += trailer.size;
0114 
0115             } else {
0116                 trailer.count = 0;
0117             }
0118 
0119             /*
0120              * sendfile() returns EINVAL if sf_hdtr's count is 0,
0121              * but corresponding pointer is not NULL
0122              */
0123 
0124             hdtr.headers = header.count ? header.iovs : NULL;
0125             hdtr.hdr_cnt = header.count;
0126             hdtr.trailers = trailer.count ? trailer.iovs : NULL;
0127             hdtr.trl_cnt = trailer.count;
0128 
0129             sent = header.size + file_size;
0130 
0131             ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
0132                            "sendfile: @%O %O h:%uz",
0133                            file->file_pos, sent, header.size);
0134 
0135             rc = sendfile(file->file->fd, c->fd, file->file_pos,
0136                           &sent, &hdtr, 0);
0137 
0138             if (rc == -1) {
0139                 err = ngx_errno;
0140 
0141                 switch (err) {
0142                 case NGX_EAGAIN:
0143                     break;
0144 
0145                 case NGX_EINTR:
0146                     eintr = 1;
0147                     break;
0148 
0149                 default:
0150                     wev->error = 1;
0151                     (void) ngx_connection_error(c, err, "sendfile() failed");
0152                     return NGX_CHAIN_ERROR;
0153                 }
0154 
0155                 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, err,
0156                                "sendfile() sent only %O bytes", sent);
0157             }
0158 
0159             if (rc == 0 && sent == 0) {
0160 
0161                 /*
0162                  * if rc and sent equal to zero, then someone
0163                  * has truncated the file, so the offset became beyond
0164                  * the end of the file
0165                  */
0166 
0167                 ngx_log_error(NGX_LOG_ALERT, c->log, 0,
0168                               "sendfile() reported that \"%s\" was truncated",
0169                               file->file->name.data);
0170 
0171                 return NGX_CHAIN_ERROR;
0172             }
0173 
0174             ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
0175                            "sendfile: %d, @%O %O:%O",
0176                            rc, file->file_pos, sent, file_size + header.size);
0177 
0178         } else {
0179             n = ngx_writev(c, &header);
0180 
0181             if (n == NGX_ERROR) {
0182                 return NGX_CHAIN_ERROR;
0183             }
0184 
0185             sent = (n == NGX_AGAIN) ? 0 : n;
0186         }
0187 
0188         c->sent += sent;
0189 
0190         in = ngx_chain_update_sent(in, sent);
0191 
0192         if (eintr) {
0193             send = prev_send + sent;
0194             continue;
0195         }
0196 
0197         if (send - prev_send != sent) {
0198             wev->ready = 0;
0199             return in;
0200         }
0201 
0202         if (send >= limit || in == NULL) {
0203             return in;
0204         }
0205     }
0206 }