Annotation of embedaddon/nginx/src/core/ngx_output_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: #if 0
        !            14: #define NGX_SENDFILE_LIMIT  4096
        !            15: #endif
        !            16: 
        !            17: /*
        !            18:  * When DIRECTIO is enabled FreeBSD, Solaris, and MacOSX read directly
        !            19:  * to an application memory from a device if parameters are aligned
        !            20:  * to device sector boundary (512 bytes).  They fallback to usual read
        !            21:  * operation if the parameters are not aligned.
        !            22:  * Linux allows DIRECTIO only if the parameters are aligned to a filesystem
        !            23:  * sector boundary, otherwise it returns EINVAL.  The sector size is
        !            24:  * usually 512 bytes, however, on XFS it may be 4096 bytes.
        !            25:  */
        !            26: 
        !            27: #define NGX_NONE            1
        !            28: 
        !            29: 
        !            30: static ngx_inline ngx_int_t
        !            31:     ngx_output_chain_as_is(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf);
        !            32: static ngx_int_t ngx_output_chain_add_copy(ngx_pool_t *pool,
        !            33:     ngx_chain_t **chain, ngx_chain_t *in);
        !            34: static ngx_int_t ngx_output_chain_align_file_buf(ngx_output_chain_ctx_t *ctx,
        !            35:     off_t bsize);
        !            36: static ngx_int_t ngx_output_chain_get_buf(ngx_output_chain_ctx_t *ctx,
        !            37:     off_t bsize);
        !            38: static ngx_int_t ngx_output_chain_copy_buf(ngx_output_chain_ctx_t *ctx);
        !            39: 
        !            40: 
        !            41: ngx_int_t
        !            42: ngx_output_chain(ngx_output_chain_ctx_t *ctx, ngx_chain_t *in)
        !            43: {
        !            44:     off_t         bsize;
        !            45:     ngx_int_t     rc, last;
        !            46:     ngx_chain_t  *cl, *out, **last_out;
        !            47: 
        !            48:     if (ctx->in == NULL && ctx->busy == NULL) {
        !            49: 
        !            50:         /*
        !            51:          * the short path for the case when the ctx->in and ctx->busy chains
        !            52:          * are empty, the incoming chain is empty too or has the single buf
        !            53:          * that does not require the copy
        !            54:          */
        !            55: 
        !            56:         if (in == NULL) {
        !            57:             return ctx->output_filter(ctx->filter_ctx, in);
        !            58:         }
        !            59: 
        !            60:         if (in->next == NULL
        !            61: #if (NGX_SENDFILE_LIMIT)
        !            62:             && !(in->buf->in_file && in->buf->file_last > NGX_SENDFILE_LIMIT)
        !            63: #endif
        !            64:             && ngx_output_chain_as_is(ctx, in->buf))
        !            65:         {
        !            66:             return ctx->output_filter(ctx->filter_ctx, in);
        !            67:         }
        !            68:     }
        !            69: 
        !            70:     /* add the incoming buf to the chain ctx->in */
        !            71: 
        !            72:     if (in) {
        !            73:         if (ngx_output_chain_add_copy(ctx->pool, &ctx->in, in) == NGX_ERROR) {
        !            74:             return NGX_ERROR;
        !            75:         }
        !            76:     }
        !            77: 
        !            78:     out = NULL;
        !            79:     last_out = &out;
        !            80:     last = NGX_NONE;
        !            81: 
        !            82:     for ( ;; ) {
        !            83: 
        !            84: #if (NGX_HAVE_FILE_AIO)
        !            85:         if (ctx->aio) {
        !            86:             return NGX_AGAIN;
        !            87:         }
        !            88: #endif
        !            89: 
        !            90:         while (ctx->in) {
        !            91: 
        !            92:             /*
        !            93:              * cycle while there are the ctx->in bufs
        !            94:              * and there are the free output bufs to copy in
        !            95:              */
        !            96: 
        !            97:             bsize = ngx_buf_size(ctx->in->buf);
        !            98: 
        !            99:             if (bsize == 0 && !ngx_buf_special(ctx->in->buf)) {
        !           100: 
        !           101:                 ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0,
        !           102:                               "zero size buf in output "
        !           103:                               "t:%d r:%d f:%d %p %p-%p %p %O-%O",
        !           104:                               ctx->in->buf->temporary,
        !           105:                               ctx->in->buf->recycled,
        !           106:                               ctx->in->buf->in_file,
        !           107:                               ctx->in->buf->start,
        !           108:                               ctx->in->buf->pos,
        !           109:                               ctx->in->buf->last,
        !           110:                               ctx->in->buf->file,
        !           111:                               ctx->in->buf->file_pos,
        !           112:                               ctx->in->buf->file_last);
        !           113: 
        !           114:                 ngx_debug_point();
        !           115: 
        !           116:                 ctx->in = ctx->in->next;
        !           117: 
        !           118:                 continue;
        !           119:             }
        !           120: 
        !           121:             if (ngx_output_chain_as_is(ctx, ctx->in->buf)) {
        !           122: 
        !           123:                 /* move the chain link to the output chain */
        !           124: 
        !           125:                 cl = ctx->in;
        !           126:                 ctx->in = cl->next;
        !           127: 
        !           128:                 *last_out = cl;
        !           129:                 last_out = &cl->next;
        !           130:                 cl->next = NULL;
        !           131: 
        !           132:                 continue;
        !           133:             }
        !           134: 
        !           135:             if (ctx->buf == NULL) {
        !           136: 
        !           137:                 rc = ngx_output_chain_align_file_buf(ctx, bsize);
        !           138: 
        !           139:                 if (rc == NGX_ERROR) {
        !           140:                     return NGX_ERROR;
        !           141:                 }
        !           142: 
        !           143:                 if (rc != NGX_OK) {
        !           144: 
        !           145:                     if (ctx->free) {
        !           146: 
        !           147:                         /* get the free buf */
        !           148: 
        !           149:                         cl = ctx->free;
        !           150:                         ctx->buf = cl->buf;
        !           151:                         ctx->free = cl->next;
        !           152: 
        !           153:                         ngx_free_chain(ctx->pool, cl);
        !           154: 
        !           155:                     } else if (out || ctx->allocated == ctx->bufs.num) {
        !           156: 
        !           157:                         break;
        !           158: 
        !           159:                     } else if (ngx_output_chain_get_buf(ctx, bsize) != NGX_OK) {
        !           160:                         return NGX_ERROR;
        !           161:                     }
        !           162:                 }
        !           163:             }
        !           164: 
        !           165:             rc = ngx_output_chain_copy_buf(ctx);
        !           166: 
        !           167:             if (rc == NGX_ERROR) {
        !           168:                 return rc;
        !           169:             }
        !           170: 
        !           171:             if (rc == NGX_AGAIN) {
        !           172:                 if (out) {
        !           173:                     break;
        !           174:                 }
        !           175: 
        !           176:                 return rc;
        !           177:             }
        !           178: 
        !           179:             /* delete the completed buf from the ctx->in chain */
        !           180: 
        !           181:             if (ngx_buf_size(ctx->in->buf) == 0) {
        !           182:                 ctx->in = ctx->in->next;
        !           183:             }
        !           184: 
        !           185:             cl = ngx_alloc_chain_link(ctx->pool);
        !           186:             if (cl == NULL) {
        !           187:                 return NGX_ERROR;
        !           188:             }
        !           189: 
        !           190:             cl->buf = ctx->buf;
        !           191:             cl->next = NULL;
        !           192:             *last_out = cl;
        !           193:             last_out = &cl->next;
        !           194:             ctx->buf = NULL;
        !           195:         }
        !           196: 
        !           197:         if (out == NULL && last != NGX_NONE) {
        !           198: 
        !           199:             if (ctx->in) {
        !           200:                 return NGX_AGAIN;
        !           201:             }
        !           202: 
        !           203:             return last;
        !           204:         }
        !           205: 
        !           206:         last = ctx->output_filter(ctx->filter_ctx, out);
        !           207: 
        !           208:         if (last == NGX_ERROR || last == NGX_DONE) {
        !           209:             return last;
        !           210:         }
        !           211: 
        !           212:         ngx_chain_update_chains(ctx->pool, &ctx->free, &ctx->busy, &out,
        !           213:                                 ctx->tag);
        !           214:         last_out = &out;
        !           215:     }
        !           216: }
        !           217: 
        !           218: 
        !           219: static ngx_inline ngx_int_t
        !           220: ngx_output_chain_as_is(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf)
        !           221: {
        !           222:     ngx_uint_t  sendfile;
        !           223: 
        !           224:     if (ngx_buf_special(buf)) {
        !           225:         return 1;
        !           226:     }
        !           227: 
        !           228:     if (buf->in_file && buf->file->directio) {
        !           229:         return 0;
        !           230:     }
        !           231: 
        !           232:     sendfile = ctx->sendfile;
        !           233: 
        !           234: #if (NGX_SENDFILE_LIMIT)
        !           235: 
        !           236:     if (buf->in_file && buf->file_pos >= NGX_SENDFILE_LIMIT) {
        !           237:         sendfile = 0;
        !           238:     }
        !           239: 
        !           240: #endif
        !           241: 
        !           242:     if (!sendfile) {
        !           243: 
        !           244:         if (!ngx_buf_in_memory(buf)) {
        !           245:             return 0;
        !           246:         }
        !           247: 
        !           248:         buf->in_file = 0;
        !           249:     }
        !           250: 
        !           251:     if (ctx->need_in_memory && !ngx_buf_in_memory(buf)) {
        !           252:         return 0;
        !           253:     }
        !           254: 
        !           255:     if (ctx->need_in_temp && (buf->memory || buf->mmap)) {
        !           256:         return 0;
        !           257:     }
        !           258: 
        !           259:     return 1;
        !           260: }
        !           261: 
        !           262: 
        !           263: static ngx_int_t
        !           264: ngx_output_chain_add_copy(ngx_pool_t *pool, ngx_chain_t **chain,
        !           265:     ngx_chain_t *in)
        !           266: {
        !           267:     ngx_chain_t  *cl, **ll;
        !           268: #if (NGX_SENDFILE_LIMIT)
        !           269:     ngx_buf_t    *b, *buf;
        !           270: #endif
        !           271: 
        !           272:     ll = chain;
        !           273: 
        !           274:     for (cl = *chain; cl; cl = cl->next) {
        !           275:         ll = &cl->next;
        !           276:     }
        !           277: 
        !           278:     while (in) {
        !           279: 
        !           280:         cl = ngx_alloc_chain_link(pool);
        !           281:         if (cl == NULL) {
        !           282:             return NGX_ERROR;
        !           283:         }
        !           284: 
        !           285: #if (NGX_SENDFILE_LIMIT)
        !           286: 
        !           287:         buf = in->buf;
        !           288: 
        !           289:         if (buf->in_file
        !           290:             && buf->file_pos < NGX_SENDFILE_LIMIT
        !           291:             && buf->file_last > NGX_SENDFILE_LIMIT)
        !           292:         {
        !           293:             /* split a file buf on two bufs by the sendfile limit */
        !           294: 
        !           295:             b = ngx_calloc_buf(pool);
        !           296:             if (b == NULL) {
        !           297:                 return NGX_ERROR;
        !           298:             }
        !           299: 
        !           300:             ngx_memcpy(b, buf, sizeof(ngx_buf_t));
        !           301: 
        !           302:             if (ngx_buf_in_memory(buf)) {
        !           303:                 buf->pos += (ssize_t) (NGX_SENDFILE_LIMIT - buf->file_pos);
        !           304:                 b->last = buf->pos;
        !           305:             }
        !           306: 
        !           307:             buf->file_pos = NGX_SENDFILE_LIMIT;
        !           308:             b->file_last = NGX_SENDFILE_LIMIT;
        !           309: 
        !           310:             cl->buf = b;
        !           311: 
        !           312:         } else {
        !           313:             cl->buf = buf;
        !           314:             in = in->next;
        !           315:         }
        !           316: 
        !           317: #else
        !           318:         cl->buf = in->buf;
        !           319:         in = in->next;
        !           320: 
        !           321: #endif
        !           322: 
        !           323:         cl->next = NULL;
        !           324:         *ll = cl;
        !           325:         ll = &cl->next;
        !           326:     }
        !           327: 
        !           328:     return NGX_OK;
        !           329: }
        !           330: 
        !           331: 
        !           332: static ngx_int_t
        !           333: ngx_output_chain_align_file_buf(ngx_output_chain_ctx_t *ctx, off_t bsize)
        !           334: {
        !           335:     size_t      size;
        !           336:     ngx_buf_t  *in;
        !           337: 
        !           338:     in = ctx->in->buf;
        !           339: 
        !           340:     if (in->file == NULL || !in->file->directio) {
        !           341:         return NGX_DECLINED;
        !           342:     }
        !           343: 
        !           344:     ctx->directio = 1;
        !           345: 
        !           346:     size = (size_t) (in->file_pos - (in->file_pos & ~(ctx->alignment - 1)));
        !           347: 
        !           348:     if (size == 0) {
        !           349: 
        !           350:         if (bsize >= (off_t) ctx->bufs.size) {
        !           351:             return NGX_DECLINED;
        !           352:         }
        !           353: 
        !           354:         size = (size_t) bsize;
        !           355: 
        !           356:     } else {
        !           357:         size = (size_t) ctx->alignment - size;
        !           358: 
        !           359:         if ((off_t) size > bsize) {
        !           360:             size = (size_t) bsize;
        !           361:         }
        !           362:     }
        !           363: 
        !           364:     ctx->buf = ngx_create_temp_buf(ctx->pool, size);
        !           365:     if (ctx->buf == NULL) {
        !           366:         return NGX_ERROR;
        !           367:     }
        !           368: 
        !           369:     /*
        !           370:      * we do not set ctx->buf->tag, because we do not want
        !           371:      * to reuse the buf via ctx->free list
        !           372:      */
        !           373: 
        !           374: #if (NGX_HAVE_ALIGNED_DIRECTIO)
        !           375:     ctx->unaligned = 1;
        !           376: #endif
        !           377: 
        !           378:     return NGX_OK;
        !           379: }
        !           380: 
        !           381: 
        !           382: static ngx_int_t
        !           383: ngx_output_chain_get_buf(ngx_output_chain_ctx_t *ctx, off_t bsize)
        !           384: {
        !           385:     size_t       size;
        !           386:     ngx_buf_t   *b, *in;
        !           387:     ngx_uint_t   recycled;
        !           388: 
        !           389:     in = ctx->in->buf;
        !           390:     size = ctx->bufs.size;
        !           391:     recycled = 1;
        !           392: 
        !           393:     if (in->last_in_chain) {
        !           394: 
        !           395:         if (bsize < (off_t) size) {
        !           396: 
        !           397:             /*
        !           398:              * allocate a small temp buf for a small last buf
        !           399:              * or its small last part
        !           400:              */
        !           401: 
        !           402:             size = (size_t) bsize;
        !           403:             recycled = 0;
        !           404: 
        !           405:         } else if (!ctx->directio
        !           406:                    && ctx->bufs.num == 1
        !           407:                    && (bsize < (off_t) (size + size / 4)))
        !           408:         {
        !           409:             /*
        !           410:              * allocate a temp buf that equals to a last buf,
        !           411:              * if there is no directio, the last buf size is lesser
        !           412:              * than 1.25 of bufs.size and the temp buf is single
        !           413:              */
        !           414: 
        !           415:             size = (size_t) bsize;
        !           416:             recycled = 0;
        !           417:         }
        !           418:     }
        !           419: 
        !           420:     b = ngx_calloc_buf(ctx->pool);
        !           421:     if (b == NULL) {
        !           422:         return NGX_ERROR;
        !           423:     }
        !           424: 
        !           425:     if (ctx->directio) {
        !           426: 
        !           427:         /*
        !           428:          * allocate block aligned to a disk sector size to enable
        !           429:          * userland buffer direct usage conjunctly with directio
        !           430:          */
        !           431: 
        !           432:         b->start = ngx_pmemalign(ctx->pool, size, (size_t) ctx->alignment);
        !           433:         if (b->start == NULL) {
        !           434:             return NGX_ERROR;
        !           435:         }
        !           436: 
        !           437:     } else {
        !           438:         b->start = ngx_palloc(ctx->pool, size);
        !           439:         if (b->start == NULL) {
        !           440:             return NGX_ERROR;
        !           441:         }
        !           442:     }
        !           443: 
        !           444:     b->pos = b->start;
        !           445:     b->last = b->start;
        !           446:     b->end = b->last + size;
        !           447:     b->temporary = 1;
        !           448:     b->tag = ctx->tag;
        !           449:     b->recycled = recycled;
        !           450: 
        !           451:     ctx->buf = b;
        !           452:     ctx->allocated++;
        !           453: 
        !           454:     return NGX_OK;
        !           455: }
        !           456: 
        !           457: 
        !           458: static ngx_int_t
        !           459: ngx_output_chain_copy_buf(ngx_output_chain_ctx_t *ctx)
        !           460: {
        !           461:     off_t        size;
        !           462:     ssize_t      n;
        !           463:     ngx_buf_t   *src, *dst;
        !           464:     ngx_uint_t   sendfile;
        !           465: 
        !           466:     src = ctx->in->buf;
        !           467:     dst = ctx->buf;
        !           468: 
        !           469:     size = ngx_buf_size(src);
        !           470:     size = ngx_min(size, dst->end - dst->pos);
        !           471: 
        !           472:     sendfile = ctx->sendfile & !ctx->directio;
        !           473: 
        !           474: #if (NGX_SENDFILE_LIMIT)
        !           475: 
        !           476:     if (src->in_file && src->file_pos >= NGX_SENDFILE_LIMIT) {
        !           477:         sendfile = 0;
        !           478:     }
        !           479: 
        !           480: #endif
        !           481: 
        !           482:     if (ngx_buf_in_memory(src)) {
        !           483:         ngx_memcpy(dst->pos, src->pos, (size_t) size);
        !           484:         src->pos += (size_t) size;
        !           485:         dst->last += (size_t) size;
        !           486: 
        !           487:         if (src->in_file) {
        !           488: 
        !           489:             if (sendfile) {
        !           490:                 dst->in_file = 1;
        !           491:                 dst->file = src->file;
        !           492:                 dst->file_pos = src->file_pos;
        !           493:                 dst->file_last = src->file_pos + size;
        !           494: 
        !           495:             } else {
        !           496:                 dst->in_file = 0;
        !           497:             }
        !           498: 
        !           499:             src->file_pos += size;
        !           500: 
        !           501:         } else {
        !           502:             dst->in_file = 0;
        !           503:         }
        !           504: 
        !           505:         if (src->pos == src->last) {
        !           506:             dst->flush = src->flush;
        !           507:             dst->last_buf = src->last_buf;
        !           508:             dst->last_in_chain = src->last_in_chain;
        !           509:         }
        !           510: 
        !           511:     } else {
        !           512: 
        !           513: #if (NGX_HAVE_ALIGNED_DIRECTIO)
        !           514: 
        !           515:         if (ctx->unaligned) {
        !           516:             if (ngx_directio_off(src->file->fd) == NGX_FILE_ERROR) {
        !           517:                 ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, ngx_errno,
        !           518:                               ngx_directio_off_n " \"%s\" failed",
        !           519:                               src->file->name.data);
        !           520:             }
        !           521:         }
        !           522: 
        !           523: #endif
        !           524: 
        !           525: #if (NGX_HAVE_FILE_AIO)
        !           526: 
        !           527:         if (ctx->aio_handler) {
        !           528:             n = ngx_file_aio_read(src->file, dst->pos, (size_t) size,
        !           529:                                   src->file_pos, ctx->pool);
        !           530:             if (n == NGX_AGAIN) {
        !           531:                 ctx->aio_handler(ctx, src->file);
        !           532:                 return NGX_AGAIN;
        !           533:             }
        !           534: 
        !           535:         } else {
        !           536:             n = ngx_read_file(src->file, dst->pos, (size_t) size,
        !           537:                               src->file_pos);
        !           538:         }
        !           539: #else
        !           540: 
        !           541:         n = ngx_read_file(src->file, dst->pos, (size_t) size, src->file_pos);
        !           542: 
        !           543: #endif
        !           544: 
        !           545: #if (NGX_HAVE_ALIGNED_DIRECTIO)
        !           546: 
        !           547:         if (ctx->unaligned) {
        !           548:             ngx_err_t  err;
        !           549: 
        !           550:             err = ngx_errno;
        !           551: 
        !           552:             if (ngx_directio_on(src->file->fd) == NGX_FILE_ERROR) {
        !           553:                 ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, ngx_errno,
        !           554:                               ngx_directio_on_n " \"%s\" failed",
        !           555:                               src->file->name.data);
        !           556:             }
        !           557: 
        !           558:             ngx_set_errno(err);
        !           559: 
        !           560:             ctx->unaligned = 0;
        !           561:         }
        !           562: 
        !           563: #endif
        !           564: 
        !           565:         if (n == NGX_ERROR) {
        !           566:             return (ngx_int_t) n;
        !           567:         }
        !           568: 
        !           569:         if (n != size) {
        !           570:             ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0,
        !           571:                           ngx_read_file_n " read only %z of %O from \"%s\"",
        !           572:                           n, size, src->file->name.data);
        !           573:             return NGX_ERROR;
        !           574:         }
        !           575: 
        !           576:         dst->last += n;
        !           577: 
        !           578:         if (sendfile) {
        !           579:             dst->in_file = 1;
        !           580:             dst->file = src->file;
        !           581:             dst->file_pos = src->file_pos;
        !           582:             dst->file_last = src->file_pos + n;
        !           583: 
        !           584:         } else {
        !           585:             dst->in_file = 0;
        !           586:         }
        !           587: 
        !           588:         src->file_pos += n;
        !           589: 
        !           590:         if (src->file_pos == src->file_last) {
        !           591:             dst->flush = src->flush;
        !           592:             dst->last_buf = src->last_buf;
        !           593:             dst->last_in_chain = src->last_in_chain;
        !           594:         }
        !           595:     }
        !           596: 
        !           597:     return NGX_OK;
        !           598: }
        !           599: 
        !           600: 
        !           601: ngx_int_t
        !           602: ngx_chain_writer(void *data, ngx_chain_t *in)
        !           603: {
        !           604:     ngx_chain_writer_ctx_t *ctx = data;
        !           605: 
        !           606:     off_t              size;
        !           607:     ngx_chain_t       *cl;
        !           608:     ngx_connection_t  *c;
        !           609: 
        !           610:     c = ctx->connection;
        !           611: 
        !           612:     for (size = 0; in; in = in->next) {
        !           613: 
        !           614: #if 1
        !           615:         if (ngx_buf_size(in->buf) == 0 && !ngx_buf_special(in->buf)) {
        !           616:             ngx_debug_point();
        !           617:         }
        !           618: #endif
        !           619: 
        !           620:         size += ngx_buf_size(in->buf);
        !           621: 
        !           622:         ngx_log_debug2(NGX_LOG_DEBUG_CORE, c->log, 0,
        !           623:                        "chain writer buf fl:%d s:%uO",
        !           624:                        in->buf->flush, ngx_buf_size(in->buf));
        !           625: 
        !           626:         cl = ngx_alloc_chain_link(ctx->pool);
        !           627:         if (cl == NULL) {
        !           628:             return NGX_ERROR;
        !           629:         }
        !           630: 
        !           631:         cl->buf = in->buf;
        !           632:         cl->next = NULL;
        !           633:         *ctx->last = cl;
        !           634:         ctx->last = &cl->next;
        !           635:     }
        !           636: 
        !           637:     ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
        !           638:                    "chain writer in: %p", ctx->out);
        !           639: 
        !           640:     for (cl = ctx->out; cl; cl = cl->next) {
        !           641: 
        !           642: #if 1
        !           643:         if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) {
        !           644:             ngx_debug_point();
        !           645:         }
        !           646: 
        !           647: #endif
        !           648: 
        !           649:         size += ngx_buf_size(cl->buf);
        !           650:     }
        !           651: 
        !           652:     if (size == 0 && !c->buffered) {
        !           653:         return NGX_OK;
        !           654:     }
        !           655: 
        !           656:     ctx->out = c->send_chain(c, ctx->out, ctx->limit);
        !           657: 
        !           658:     ngx_log_debug1(NGX_LOG_DEBUG_CORE, c->log, 0,
        !           659:                    "chain writer out: %p", ctx->out);
        !           660: 
        !           661:     if (ctx->out == NGX_CHAIN_ERROR) {
        !           662:         return NGX_ERROR;
        !           663:     }
        !           664: 
        !           665:     if (ctx->out == NULL) {
        !           666:         ctx->last = &ctx->out;
        !           667: 
        !           668:         if (!c->buffered) {
        !           669:             return NGX_OK;
        !           670:         }
        !           671:     }
        !           672: 
        !           673:     return NGX_AGAIN;
        !           674: }

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