Annotation of embedaddon/nginx/src/os/unix/ngx_darwin_sendfile_chain.c, revision 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>