Annotation of embedaddon/nginx/src/os/unix/ngx_darwin_sendfile_chain.c, revision 1.1.1.1

1.1       misho       1: 
                      2: /*
                      3:  * Copyright (C) Igor Sysoev
                      4:  * Copyright (C) Nginx, Inc.
                      5:  */
                      6: 
                      7: 
                      8: #include <ngx_config.h>
                      9: #include <ngx_core.h>
                     10: #include <ngx_event.h>
                     11: 
                     12: 
                     13: /*
                     14:  * It seems that Darwin 9.4 (Mac OS X 1.5) sendfile() has the same
                     15:  * old bug as early FreeBSD sendfile() syscall:
                     16:  * http://www.freebsd.org/cgi/query-pr.cgi?pr=33771
                     17:  *
                     18:  * Besides sendfile() has another bug: if one calls sendfile()
                     19:  * with both a header and a trailer, then sendfile() ignores a file part
                     20:  * at all and sends only the header and the trailer together.
                     21:  * For this reason we send a trailer only if there is no a header.
                     22:  *
                     23:  * Although sendfile() allows to pass a header or a trailer,
                     24:  * it may send the header or the trailer and a part of the file
                     25:  * in different packets.  And FreeBSD workaround (TCP_NOPUSH option)
                     26:  * does not help.
                     27:  */
                     28: 
                     29: 
                     30: #if (IOV_MAX > 64)
                     31: #define NGX_HEADERS   64
                     32: #define NGX_TRAILERS  64
                     33: #else
                     34: #define NGX_HEADERS   IOV_MAX
                     35: #define NGX_TRAILERS  IOV_MAX
                     36: #endif
                     37: 
                     38: 
                     39: ngx_chain_t *
                     40: ngx_darwin_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
                     41: {
                     42:     int              rc;
                     43:     u_char          *prev;
                     44:     off_t            size, send, prev_send, aligned, sent, fprev;
                     45:     off_t            header_size, file_size;
                     46:     ngx_uint_t       eintr, complete;
                     47:     ngx_err_t        err;
                     48:     ngx_buf_t       *file;
                     49:     ngx_array_t      header, trailer;
                     50:     ngx_event_t     *wev;
                     51:     ngx_chain_t     *cl;
                     52:     struct sf_hdtr   hdtr;
                     53:     struct iovec    *iov, headers[NGX_HEADERS], trailers[NGX_TRAILERS];
                     54: 
                     55:     wev = c->write;
                     56: 
                     57:     if (!wev->ready) {
                     58:         return in;
                     59:     }
                     60: 
                     61: #if (NGX_HAVE_KQUEUE)
                     62: 
                     63:     if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) {
                     64:         (void) ngx_connection_error(c, wev->kq_errno,
                     65:                                "kevent() reported about an closed connection");
                     66:         wev->error = 1;
                     67:         return NGX_CHAIN_ERROR;
                     68:     }
                     69: 
                     70: #endif
                     71: 
                     72:     /* the maximum limit size is the maximum size_t value - the page size */
                     73: 
                     74:     if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) {
                     75:         limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize;
                     76:     }
                     77: 
                     78:     send = 0;
                     79: 
                     80:     header.elts = headers;
                     81:     header.size = sizeof(struct iovec);
                     82:     header.nalloc = NGX_HEADERS;
                     83:     header.pool = c->pool;
                     84: 
                     85:     trailer.elts = trailers;
                     86:     trailer.size = sizeof(struct iovec);
                     87:     trailer.nalloc = NGX_TRAILERS;
                     88:     trailer.pool = c->pool;
                     89: 
                     90:     for ( ;; ) {
                     91:         file = NULL;
                     92:         file_size = 0;
                     93:         header_size = 0;
                     94:         eintr = 0;
                     95:         complete = 0;
                     96:         prev_send = send;
                     97: 
                     98:         header.nelts = 0;
                     99:         trailer.nelts = 0;
                    100: 
                    101:         /* create the header iovec and coalesce the neighbouring bufs */
                    102: 
                    103:         prev = NULL;
                    104:         iov = NULL;
                    105: 
                    106:         for (cl = in; cl && send < limit; cl = cl->next) {
                    107: 
                    108:             if (ngx_buf_special(cl->buf)) {
                    109:                 continue;
                    110:             }
                    111: 
                    112:             if (!ngx_buf_in_memory_only(cl->buf)) {
                    113:                 break;
                    114:             }
                    115: 
                    116:             size = cl->buf->last - cl->buf->pos;
                    117: 
                    118:             if (send + size > limit) {
                    119:                 size = limit - send;
                    120:             }
                    121: 
                    122:             if (prev == cl->buf->pos) {
                    123:                 iov->iov_len += (size_t) size;
                    124: 
                    125:             } else {
                    126:                 if (header.nelts >= IOV_MAX) {
                    127:                     break;
                    128:                 }
                    129: 
                    130:                 iov = ngx_array_push(&header);
                    131:                 if (iov == NULL) {
                    132:                     return NGX_CHAIN_ERROR;
                    133:                 }
                    134: 
                    135:                 iov->iov_base = (void *) cl->buf->pos;
                    136:                 iov->iov_len = (size_t) size;
                    137:             }
                    138: 
                    139:             prev = cl->buf->pos + (size_t) size;
                    140:             header_size += size;
                    141:             send += size;
                    142:         }
                    143: 
                    144: 
                    145:         if (cl && cl->buf->in_file && send < limit) {
                    146:             file = cl->buf;
                    147: 
                    148:             /* coalesce the neighbouring file bufs */
                    149: 
                    150:             do {
                    151:                 size = cl->buf->file_last - cl->buf->file_pos;
                    152: 
                    153:                 if (send + size > limit) {
                    154:                     size = limit - send;
                    155: 
                    156:                     aligned = (cl->buf->file_pos + size + ngx_pagesize - 1)
                    157:                                & ~((off_t) ngx_pagesize - 1);
                    158: 
                    159:                     if (aligned <= cl->buf->file_last) {
                    160:                         size = aligned - cl->buf->file_pos;
                    161:                     }
                    162:                 }
                    163: 
                    164:                 file_size += size;
                    165:                 send += size;
                    166:                 fprev = cl->buf->file_pos + size;
                    167:                 cl = cl->next;
                    168: 
                    169:             } while (cl
                    170:                      && cl->buf->in_file
                    171:                      && send < limit
                    172:                      && file->file->fd == cl->buf->file->fd
                    173:                      && fprev == cl->buf->file_pos);
                    174:         }
                    175: 
                    176:         if (file && header.nelts == 0) {
                    177: 
                    178:             /* create the trailer iovec and coalesce the neighbouring bufs */
                    179: 
                    180:             prev = NULL;
                    181:             iov = NULL;
                    182: 
                    183:             while (cl && send < limit) {
                    184: 
                    185:                 if (ngx_buf_special(cl->buf)) {
                    186:                     cl = cl->next;
                    187:                     continue;
                    188:                 }
                    189: 
                    190:                 if (!ngx_buf_in_memory_only(cl->buf)) {
                    191:                     break;
                    192:                 }
                    193: 
                    194:                 size = cl->buf->last - cl->buf->pos;
                    195: 
                    196:                 if (send + size > limit) {
                    197:                     size = limit - send;
                    198:                 }
                    199: 
                    200:                 if (prev == cl->buf->pos) {
                    201:                     iov->iov_len += (size_t) size;
                    202: 
                    203:                 } else {
                    204:                     if (trailer.nelts >= IOV_MAX) {
                    205:                         break;
                    206:                     }
                    207: 
                    208:                     iov = ngx_array_push(&trailer);
                    209:                     if (iov == NULL) {
                    210:                         return NGX_CHAIN_ERROR;
                    211:                     }
                    212: 
                    213:                     iov->iov_base = (void *) cl->buf->pos;
                    214:                     iov->iov_len = (size_t) size;
                    215:                 }
                    216: 
                    217:                 prev = cl->buf->pos + (size_t) size;
                    218:                 send += size;
                    219:                 cl = cl->next;
                    220:             }
                    221:         }
                    222: 
                    223:         if (file) {
                    224: 
                    225:             /*
                    226:              * sendfile() returns EINVAL if sf_hdtr's count is 0,
                    227:              * but corresponding pointer is not NULL
                    228:              */
                    229: 
                    230:             hdtr.headers = header.nelts ? (struct iovec *) header.elts: NULL;
                    231:             hdtr.hdr_cnt = header.nelts;
                    232:             hdtr.trailers = trailer.nelts ? (struct iovec *) trailer.elts: NULL;
                    233:             hdtr.trl_cnt = trailer.nelts;
                    234: 
                    235:             sent = header_size + file_size;
                    236: 
                    237:             ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0,
                    238:                            "sendfile: @%O %O h:%O",
                    239:                            file->file_pos, sent, header_size);
                    240: 
                    241:             rc = sendfile(file->file->fd, c->fd, file->file_pos,
                    242:                           &sent, &hdtr, 0);
                    243: 
                    244:             if (rc == -1) {
                    245:                 err = ngx_errno;
                    246: 
                    247:                 switch (err) {
                    248:                 case NGX_EAGAIN:
                    249:                     break;
                    250: 
                    251:                 case NGX_EINTR:
                    252:                     eintr = 1;
                    253:                     break;
                    254: 
                    255:                 default:
                    256:                     wev->error = 1;
                    257:                     (void) ngx_connection_error(c, err, "sendfile() failed");
                    258:                     return NGX_CHAIN_ERROR;
                    259:                 }
                    260: 
                    261:                 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, err,
                    262:                                "sendfile() sent only %O bytes", sent);
                    263:             }
                    264: 
                    265:             if (rc == 0 && sent == 0) {
                    266: 
                    267:                 /*
                    268:                  * if rc and sent equal to zero, then someone
                    269:                  * has truncated the file, so the offset became beyond
                    270:                  * the end of the file
                    271:                  */
                    272: 
                    273:                 ngx_log_error(NGX_LOG_ALERT, c->log, 0,
                    274:                               "sendfile() reported that \"%s\" was truncated",
                    275:                               file->file->name.data);
                    276: 
                    277:                 return NGX_CHAIN_ERROR;
                    278:             }
                    279: 
                    280:             ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
                    281:                            "sendfile: %d, @%O %O:%O",
                    282:                            rc, file->file_pos, sent, file_size + header_size);
                    283: 
                    284:         } else {
                    285:             rc = writev(c->fd, header.elts, header.nelts);
                    286: 
                    287:             ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
                    288:                            "writev: %d of %uz", rc, send);
                    289: 
                    290:             if (rc == -1) {
                    291:                 err = ngx_errno;
                    292: 
                    293:                 switch (err) {
                    294:                 case NGX_EAGAIN:
                    295:                     break;
                    296: 
                    297:                 case NGX_EINTR:
                    298:                     eintr = 1;
                    299:                     break;
                    300: 
                    301:                 default:
                    302:                     wev->error = 1;
                    303:                     ngx_connection_error(c, err, "writev() failed");
                    304:                     return NGX_CHAIN_ERROR;
                    305:                 }
                    306: 
                    307:                 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
                    308:                                "writev() not ready");
                    309:             }
                    310: 
                    311:             sent = rc > 0 ? rc : 0;
                    312:         }
                    313: 
                    314:         if (send - prev_send == sent) {
                    315:             complete = 1;
                    316:         }
                    317: 
                    318:         c->sent += sent;
                    319: 
                    320:         for (cl = in; cl; cl = cl->next) {
                    321: 
                    322:             if (ngx_buf_special(cl->buf)) {
                    323:                 continue;
                    324:             }
                    325: 
                    326:             if (sent == 0) {
                    327:                 break;
                    328:             }
                    329: 
                    330:             size = ngx_buf_size(cl->buf);
                    331: 
                    332:             if (sent >= size) {
                    333:                 sent -= size;
                    334: 
                    335:                 if (ngx_buf_in_memory(cl->buf)) {
                    336:                     cl->buf->pos = cl->buf->last;
                    337:                 }
                    338: 
                    339:                 if (cl->buf->in_file) {
                    340:                     cl->buf->file_pos = cl->buf->file_last;
                    341:                 }
                    342: 
                    343:                 continue;
                    344:             }
                    345: 
                    346:             if (ngx_buf_in_memory(cl->buf)) {
                    347:                 cl->buf->pos += (size_t) sent;
                    348:             }
                    349: 
                    350:             if (cl->buf->in_file) {
                    351:                 cl->buf->file_pos += sent;
                    352:             }
                    353: 
                    354:             break;
                    355:         }
                    356: 
                    357:         if (eintr) {
                    358:             continue;
                    359:         }
                    360: 
                    361:         if (!complete) {
                    362:             wev->ready = 0;
                    363:             return cl;
                    364:         }
                    365: 
                    366:         if (send >= limit || cl == NULL) {
                    367:             return cl;
                    368:         }
                    369: 
                    370:         in = cl;
                    371:     }
                    372: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>