• Nginx-HTTP之ngx_http_top_body_filter


    1. ngx_http_top_body_filter

    该链表用于构造响应消息的响应正文.

    大致有以下模块在该链表中插入了自己的函数:

    1. ngx_http_range_filter_module: ngx_http_range_body_filter
    2. ngx_http_copy_filter_module: ngx_http_copy_filter
    3. ngx_http_headers_filter_module: ngx_http_trailers_filter
    4. ngx_http_charset_filter_module: ngx_http_charset_body_filter
    5. ngx_http_ssi_filter_module: ngx_http_ssi_body_filter
    6. ngx_http_postpone_filter_module: ngx_http_postpone_filter
    7. ngx_http_gzip_filter_module:ngx_http_gzip_body_filter
    8. ngx_http_chunked_filter_module:ngx_http_chunked_body_filter
    9. ngx_http_write_filter_module:ngx_http_write_filter

    1.1 ngx_http_range_body_filter

    static ngx_int_t
    ngx_http_range_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
    {
        ngx_http_range_filter_ctx_t  *ctx;
    
        if (in == NULL) {
            return ngx_http_next_body_filter(r, in);
        }
    
        /* 获取 ngx_http_range_body_filter_module 模块的上下文结构体 */
        ctx = ngx_http_get_module_ctx(r, ngx_http_range_body_filter_module);
    
        /* 若为 NULL 则执行链表的下一个函数 */
        if (ctx == NULL) {
            return ngx_http_next_body_filter(r, in);
        }
    
        if (ctx->ranges.nelts == 1) {
            return ngx_http_range_singlepart_body(r, ctx, in);
        }
    
        /*
         * multipart ranges are supported only if whole body is in a single buffer
         */
    
        if (ngx_buf_special(in->buf)) {
            return ngx_http_next_body_filter(r, in);
        }
    
        if (ngx_http_range_test_overlapped(r, ctx, in) != NGX_OK) {
            return NGX_ERROR;
        }
    
        return ngx_http_range_multipart_body(r, ctx, in);
    }
    

    1.2 ngx_http_copy_filter

    static ngx_int_t
    ngx_http_copy_filter(ngx_http_request_t *r, ngx_chain_t *in)
    {
        ngx_int_t                     rc;
        ngx_connection_t             *c;
        ngx_output_chain_ctx_t       *ctx;
        ngx_http_core_loc_conf_t     *clcf;
        ngx_http_copy_filter_conf_t  *conf;
    
        c = r->connection;
    
        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
                       "http copy filter: "%V?%V"", &r->uri, &r->args);
    
        /* 获取 ngx_http_copy_filter_module 模块的上下文结构体 */
        ctx = ngx_http_get_module_ctx(r, ngx_http_copy_filter_module);
    
        /* 若为 NULL,则分配 */
        if (ctx == NULL) {
            ctx = ngx_pcalloc(r->pool, sizeof(ngx_output_chain_ctx_t));
            if (ctx == NULL) {
                return NGX_ERROR;
            }
    
            /* 将该 ngx_http_copy_filter_module 模块的上下文结构体  
             * ngx_output_chain_ctx_t 存放到 ngx_http_request_t 结构体
             * 中的 ctx 数组中,下标为单前模块的 ctx_index */
            ngx_http_set_ctx(r, ctx, ngx_http_copy_filter_module);
    
            conf = ngx_http_get_module_loc_conf(r, ngx_http_copy_filter_module);
            clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
    
            /* 标志位,为 1 表示使用 sendfile 系统调用发送文件 */
            ctx->sendfile = c->sendfile;
            ctx->need_in_memory = r->main_filter_need_in_memory
                                  || r->filter_need_in_memory;
            ctx->need_in_temp = r->filter_need_temporary;
    
            ctx->alignment = clcf->directio_alignment;
    
            ctx->pool = r->pool;
            ctx->bufs = conf->bufs;
            ctx->tag = (ngx_buf_tag_t) &ngx_http_copy_filter_module;
    
            ctx->output_filter = (ngx_output_chain_filter_pt)
                                      ngx_http_next_body_filter;
            ctx->filter_ctx = r;
    
    #if (NGX_HAVE_FILE_AIO)
            if (ngx_file_aio && clcf->aio == NGX_HTTP_AIO_ON) {
                ctx->aio_handler = ngx_http_copy_aio_handler;
    #if (NGX_HAVE_AIO_SENDFILE)
                ctx->aio_preload = ngx_http_copy_aio_sendfile_preload;
    #endif
            }
    #endif
    
    #if (NGX_THREADS)
            if (clcf->aio == NGX_HTTP_AIO_THREADS) {
                ctx->thread_handler = ngx_http_copy_thread_handler;
            }
    #endif
    
            if (in && in->buf && ngx_buf_size(in->buf)) {
                r->request_output = 1;
            }
        }
    
    #if (NGX_HAVE_FILE_AIO || NGX_THREADS)
        ctx->aio = r->aio;
    #endif
    
        rc = ngx_output_chain(ctx, in);
    
        if (ctx->in == NULL) {
            r->buffered &= ~NGX_HTTP_COPY_BUFFERED;
    
        } else {
            r->buffered |= NGX_HTTP_COPY_BUFFERED;
        }
    
        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
                       "http copy filter: %i "%V?%V"", rc, &r->uri, &r->args);
    
        return rc;
    }
    

    1.2.1 ngx_output_chain

    ngx_int_t
    ngx_output_chain(ngx_output_chain_ctx_t *ctx, ngx_chain_t *in)
    {
        off_t         bsize;
        ngx_int_t     rc, last;
        ngx_chain_t  *cl, *out, **last_out;
    
        if (ctx->in == NULL && ctx->busy == NULL
    #if (NGX_HAVE_FILE_AIO || NGX_THREADS)
            && !ctx->aio
    #endif
           )
        {
            /*
             * the short path for the case when the ctx->in and ctx->busy chains
             * are empty, the incoming chain is empty too or has the single buf
             * that does not require the copy
             */
    
            if (in == NULL) {
                return ctx->output_filter(ctx->filter_ctx, in);
            }
    
            if (in->next == NULL
    #if (NGX_SENDFILE_LIMIT)
                && !(in->buf->in_file && in->buf->file_last > NGX_SENDFILE_LIMIT)
    #endif
                && ngx_output_chain_as_is(ctx, in->buf))
            {
                return ctx->output_filter(ctx->filter_ctx, in);
            }
        }
    
        /* add the incoming buf to the chain ctx->in */
    
        if (in) {
            if (ngx_output_chain_add_copy(ctx->pool, &ctx->in, in) == NGX_ERROR) {
                return NGX_ERROR;
            }
        }
    
        out = NULL;
        last_out = &out;
        last = NGX_NONE;
    
        for ( ;; ) {
    
    #if (NGX_HAVE_FILE_AIO || NGX_THREADS)
            if (ctx->aio) {
                return NGX_AGAIN;
            }
    #endif
    
            while (ctx->in) {
    
                /*
                 * cycle while there are the ctx->in bufs
                 * and there are the free output bufs to copy in
                 */
    
                bsize = ngx_buf_size(ctx->in->buf);
    
                if (bsize == 0 && !ngx_buf_special(ctx->in->buf)) {
    
                    ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0,
                                  "zero size buf in output "
                                  "t:%d r:%d f:%d %p %p-%p %p %O-%O",
                                  ctx->in->buf->temporary,
                                  ctx->in->buf->recycled,
                                  ctx->in->buf->in_file,
                                  ctx->in->buf->start,
                                  ctx->in->buf->pos,
                                  ctx->in->buf->last,
                                  ctx->in->buf->file,
                                  ctx->in->buf->file_pos,
                                  ctx->in->buf->file_last);
    
                    ngx_debug_point();
    
                    ctx->in = ctx->in->next;
    
                    continue;
                }
    
                if (ngx_output_chain_as_is(ctx, ctx->in->buf)) {
    
                    /* move the chain link to the output chain */
    
                    cl = ctx->in;
                    ctx->in = cl->next;
    
                    *last_out = cl;
                    last_out = &cl->next;
                    cl->next = NULL;
    
                    continue;
                }
    
                if (ctx->buf == NULL) {
    
                    rc = ngx_output_chain_align_file_buf(ctx, bsize);
    
                    if (rc == NGX_ERROR) {
                        return NGX_ERROR;
                    }
    
                    if (rc != NGX_OK) {
    
                        if (ctx->free) {
    
                            /* get the free buf */
    
                            cl = ctx->free;
                            ctx->buf = cl->buf;
                            ctx->free = cl->next;
    
                            ngx_free_chain(ctx->pool, cl);
    
                        } else if (out || ctx->allocated == ctx->bufs.num) {
    
                            break;
    
                        } else if (ngx_output_chain_get_buf(ctx, bsize) != NGX_OK) {
                            return NGX_ERROR;
                        }
                    }
                }
    
                rc = ngx_output_chain_copy_buf(ctx);
    
                if (rc == NGX_ERROR) {
                    return rc;
                }
    
                if (rc == NGX_AGAIN) {
                    if (out) {
                        break;
                    }
    
                    return rc;
                }
    
                /* delete the completed buf from the ctx->in chain */
    
                if (ngx_buf_size(ctx->in->buf) == 0) {
                    ctx->in = ctx->in->next;
                }
    
                cl = ngx_alloc_chain_link(ctx->pool);
                if (cl == NULL) {
                    return NGX_ERROR;
                }
    
                cl->buf = ctx->buf;
                cl->next = NULL;
                *last_out = cl;
                last_out = &cl->next;
                ctx->buf = NULL;
            }
    
            if (out == NULL && last != NGX_NONE) {
    
                if (ctx->in) {
                    return NGX_AGAIN;
                }
    
                return last;
            }
    
            last = ctx->output_filter(ctx->filter_ctx, out);
    
            if (last == NGX_ERROR || last == NGX_DONE) {
                return last;
            }
    
            ngx_chain_update_chains(ctx->pool, &ctx->free, &ctx->busy, &out,
                                    ctx->tag);
            last_out = &out;
        }
    }
    

    1.3 ngx_http_trailers_filter

    static ngx_int_t
    ngx_http_trailers_filter(ngx_http_request_t *r, ngx_chain_t *in)
    {
        ngx_str_t                 value;
        ngx_uint_t                i, safe_status;
        ngx_chain_t              *cl;
        ngx_table_elt_t          *t;
        ngx_http_header_val_t    *h;
        ngx_http_headers_conf_t  *conf;
    
        conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module);
    
        /* 若 trailers 为 NULL 或其他,则下一个 */
        if (in == NULL
            || conf->trailers == NULL
            || !r->expect_trailers
            || r->header_only)
        {
            return ngx_http_next_body_filter(r, in);
        }
    
        for (cl = in; cl; cl = cl->next) {
            if (cl->buf->last_buf) {
                break;
            }
        }
    
        if (cl == NULL) {
            return ngx_http_next_body_filter(r, in);
        }
    
        switch (r->headers_out.status) {
    
        case NGX_HTTP_OK:
        case NGX_HTTP_CREATED:
        case NGX_HTTP_NO_CONTENT:
        case NGX_HTTP_PARTIAL_CONTENT:
        case NGX_HTTP_MOVED_PERMANENTLY:
        case NGX_HTTP_MOVED_TEMPORARILY:
        case NGX_HTTP_SEE_OTHER:
        case NGX_HTTP_NOT_MODIFIED:
        case NGX_HTTP_TEMPORARY_REDIRECT:
        case NGX_HTTP_PERMANENT_REDIRECT:
            safe_status = 1;
            break;
    
        default:
            safe_status = 0;
            break;
        }
    
        h = conf->trailers->elts;
        for (i = 0; i < conf->trailers->nelts; i++) {
    
            if (!safe_status && !h[i].always) {
                continue;
            }
    
            if (ngx_http_complex_value(r, &h[i].value, &value) != NGX_OK) {
                return NGX_ERROR;
            }
    
            if (value.len) {
                t = ngx_list_push(&r->headers_out.trailers);
                if (t == NULL) {
                    return NGX_ERROR;
                }
    
                t->key = h[i].key;
                t->value = value;
                t->hash = 1;
            }
        }
    
        return ngx_http_next_body_filter(r, in);
    }
    

    1.4 ngx_http_charset_body_filter

    暂略

    1.5 ngx_http_ssi_body_filter

    暂略

    1.6 ngx_http_postpone_filter

    暂略

    1.7 ngx_http_gzip_body_filter

    暂略

    1.8 ngx_http_chunked_body_filter

    暂略

    1.9 ngx_http_write_filter

    ngx_int_t
    ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in)
    {
        off_t                      size, sent, nsent, limit;
        ngx_uint_t                 last, flush, sync;
        ngx_msec_t                 delay;
        ngx_chain_t               *cl, *ln, **ll, *chain;
        ngx_connection_t          *c;
        ngx_http_core_loc_conf_t  *clcf;
    
        c = r->connection;
    
        if (c->error) {
            return NGX_ERROR;
        }
    
        size = 0;
        flush = 0;
        sync = 0;
        last = 0;
        /* 指向 r->out 链表的第一个元素 */
        ll = &r->out;
    
        /* find the size, the flush point and the last link of the saved chain */
    
        /* 若此时缓存的是 body,则此时 r->out 链表中已经缓存响应消息的消息报头 */
        for (cl = r->out; cl; cl = cl->next) {
            ll = &cl->next;
    
            ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0,
                           "write old buf t:%d f:%d %p, pos %p, size: %z "
                           "file: %O, size: %O",
                           cl->buf->temporary, cl->buf->in_file,
                           cl->buf->start, cl->buf->pos,
                           cl->buf->last - cl->buf->pos,
                           cl->buf->file_pos,
                           cl->buf->file_last - cl->buf->file_pos);
    
    #if 1
            if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) {
                ngx_log_error(NGX_LOG_ALERT, c->log, 0,
                              "zero size buf in writer "
                              "t:%d r:%d f:%d %p %p-%p %p %O-%O",
                              cl->buf->temporary,
                              cl->buf->recycled,
                              cl->buf->in_file,
                              cl->buf->start,
                              cl->buf->pos,
                              cl->buf->last,
                              cl->buf->file,
                              cl->buf->file_pos,
                              cl->buf->file_last);
    
                ngx_debug_point();
                return NGX_ERROR;
            }
    #endif
    
            size += ngx_buf_size(cl->buf);
    
            if (cl->buf->flush || cl->buf->recycled) {
                flush = 1;
            }
    
            if (cl->buf->sync) {
                sync = 1;
            }
    
            if (cl->buf->last_buf) {
                last = 1;
            }
        }
    
        /* add the new chain to the existent one */
    
        for (ln = in; ln; ln = ln->next) {
            /* 生成一个 ngx_chain_t  */
            cl = ngx_alloc_chain_link(r->pool);
            if (cl == NULL) {
                return NGX_ERROR;
            }
    
            cl->buf = ln->buf;
            /* 将该新生成的 ngx_chain_t 添加到 r->out 链表尾部 */
            *ll = cl;
            ll = &cl->next;
    
            ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0,
                           "write new buf t:%d f:%d %p, pos %p, size: %z "
                           "file: %O, size: %O",
                           cl->buf->temporary, cl->buf->in_file,
                           cl->buf->start, cl->buf->pos,
                           cl->buf->last - cl->buf->pos,
                           cl->buf->file_pos,
                           cl->buf->file_last - cl->buf->file_pos);
    
    #if 1
            if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) {
                ngx_log_error(NGX_LOG_ALERT, c->log, 0,
                              "zero size buf in writer "
                              "t:%d r:%d f:%d %p %p-%p %p %O-%O",
                              cl->buf->temporary,
                              cl->buf->recycled,
                              cl->buf->in_file,
                              cl->buf->start,
                              cl->buf->pos,
                              cl->buf->last,
                              cl->buf->file,
                              cl->buf->file_pos,
                              cl->buf->file_last);
    
                ngx_debug_point();
                return NGX_ERROR;
            }
    #endif
    
            size += ngx_buf_size(cl->buf);
    
            if (cl->buf->flush || cl->buf->recycled) {
                flush = 1;
            }
    
            if (cl->buf->sync) {
                sync = 1;
            }
    
            /* 若 last_buf 为 1,表明这个最后一个 buf 了 */
            if (cl->buf->last_buf) {
                last = 1;
            }
        }
    
        *ll = NULL;
    
        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
                       "http write filter: l:%ui f:%ui s:%O", last, flush, size);
    
        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
    
        /*
         * avoid the output if there are no last buf, no flush point,
         * there are the incoming bufs and the size of all bufs
         * is smaller than "postpone_output" directive
         */
    
        if (!last && !flush && in && size < (off_t) clcf->postpone_output) {
            return NGX_OK;
        }
    
        if (c->write->delayed) {
            c->buffered |= NGX_HTTP_WRITE_BUFFERED;
            return NGX_AGAIN;
        }
    
        if (size == 0
            && !(c->buffered & NGX_LOWLEVEL_BUFFERED)
            && !(last && c->need_last_buf))
        {
            if (last || flush || sync) {
                for (cl = r->out; cl; /* void */) {
                    ln = cl;
                    cl = cl->next;
                    ngx_free_chain(r->pool, ln);
                }
    
                r->out = NULL;
                c->buffered &= ~NGX_HTTP_WRITE_BUFFERED;
    
                return NGX_OK;
            }
    
            ngx_log_error(NGX_LOG_ALERT, c->log, 0,
                          "the http output chain is empty");
    
            ngx_debug_point();
    
            return NGX_ERROR;
        }
    
        if (r->limit_rate) {
            if (r->limit_rate_after == 0) {
                r->limit_rate_after = clcf->limit_rate_after;
            }
    
            limit = (off_t) r->limit_rate * (ngx_time() - r->start_sec + 1)
                    - (c->sent - r->limit_rate_after);
    
            if (limit <= 0) {
                c->write->delayed = 1;
                delay = (ngx_msec_t) (- limit * 1000 / r->limit_rate + 1);
                ngx_add_timer(c->write, delay);
    
                c->buffered |= NGX_HTTP_WRITE_BUFFERED;
    
                return NGX_AGAIN;
            }
    
            if (clcf->sendfile_max_chunk
                && (off_t) clcf->sendfile_max_chunk < limit)
            {
                limit = clcf->sendfile_max_chunk;
            }
    
        } else {
            limit = clcf->sendfile_max_chunk;
        }
    
        /* 当前已经发送的字节数 */
        sent = c->sent;
    
        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
                       "http write filter limit %O", limit);
    
        /* 指向 ngx_linux_sendfile_chain 函数, 将响应消息发送给客户端 */
        chain = c->send_chain(c, r->out, limit);
    
        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
                       "http write filter %p", chain);
    
        if (chain == NGX_CHAIN_ERROR) {
            c->error = 1;
            return NGX_ERROR;
        }
    
        if (r->limit_rate) {
    
            nsent = c->sent;
    
            if (r->limit_rate_after) {
    
                sent -= r->limit_rate_after;
                if (sent < 0) {
                    sent = 0;
                }
    
                nsent -= r->limit_rate_after;
                if (nsent < 0) {
                    nsent = 0;
                }
            }
    
            delay = (ngx_msec_t) ((nsent - sent) * 1000 / r->limit_rate);
    
            if (delay > 0) {
                limit = 0;
                c->write->delayed = 1;
                ngx_add_timer(c->write, delay);
            }
        }
    
        if (limit
            && c->write->ready
            && c->sent - sent >= limit - (off_t) (2 * ngx_pagesize))
        {
            c->write->delayed = 1;
            ngx_add_timer(c->write, 1);
        }
    
        /* 将 r->out 链表中的元素(即 ngx_chain_t)插入到 r->pool->chain 链表中 */
        for (cl = r->out; cl && cl != chain; /* void */) {
            ln = cl;
            cl = cl->next;
            ngx_free_chain(r->pool, ln);
        }
    
        r->out = chain;
    
        if (chain) {
            c->buffered |= NGX_HTTP_WRITE_BUFFERED;
            return NGX_AGAIN;
        }
    
        c->buffered &= ~NGX_HTTP_WRITE_BUFFERED;
    
        if ((c->buffered & NGX_LOWLEVEL_BUFFERED) && r->postponed == NULL) {
            return NGX_AGAIN;
        }
    
        return NGX_OK;
    }
    

    1.9.1 c->send_chain: ngx_linux_sendfile_chain

    ngx_chain_t *
    ngx_linux_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
    {
        int            tcp_nodelay;
        off_t          send, prev_send;
        size_t         file_size, sent;
        ssize_t        n;
        ngx_err_t      err;
        ngx_buf_t     *file;
        ngx_event_t   *wev;
        ngx_chain_t   *cl;
        ngx_iovec_t    header;
        struct iovec   headers[NGX_IOVS_PREALLOCATE];
    
        /* 获取该连接的写事件 */
        wev = c->write;
    
        if (!wev->ready) {
            return in;
        }
    
    
        /* the maximum limit size is 2G-1 - the page size */
    
        if (limit == 0 || limit > (off_t) (NGX_SENDFILE_MAXSIZE - ngx_pagesize)) {
            limit = NGX_SENDFILE_MAXSIZE - ngx_pagesize;
        }
    
    
        send = 0;
    
        header.iovs = headers;
        header.nalloc = NGX_IOVS_PREALLOCATE;
    
        for ( ;; ) {
            prev_send = send;
    
            /* create the iovec and coalesce the neighbouring bufs */
    
            /* 将 in 中的数据(除在文件中的数据外)构造成 iovec */
            cl = ngx_output_chain_to_iovec(&header, in, limit - send, c->log);
    
            if (cl == NGX_CHAIN_ERROR) {
                return NGX_CHAIN_ERROR;
            }
    
            send += header.size;
    
            /* set TCP_CORK if there is a header before a file */
    
            if (c->tcp_nopush == NGX_TCP_NOPUSH_UNSET
                && header.count != 0
                && cl
                && cl->buf->in_file)
            {
                /* the TCP_CORK and TCP_NODELAY are mutually exclusive */
    
                if (c->tcp_nodelay == NGX_TCP_NODELAY_SET) {
    
                    tcp_nodelay = 0;
    
                    if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY,
                                   (const void *) &tcp_nodelay, sizeof(int)) == -1)
                    {
                        err = ngx_socket_errno;
    
                        /*
                         * there is a tiny chance to be interrupted, however,
                         * we continue a processing with the TCP_NODELAY
                         * and without the TCP_CORK
                         */
    
                        if (err != NGX_EINTR) {
                            wev->error = 1;
                            ngx_connection_error(c, err,
                                                 "setsockopt(TCP_NODELAY) failed");
                            return NGX_CHAIN_ERROR;
                        }
    
                    } else {
                        c->tcp_nodelay = NGX_TCP_NODELAY_UNSET;
    
                        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
                                       "no tcp_nodelay");
                    }
                }
    
                if (c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) {
    
                    if (ngx_tcp_nopush(c->fd) == NGX_ERROR) {
                        err = ngx_socket_errno;
    
                        /*
                         * there is a tiny chance to be interrupted, however,
                         * we continue a processing without the TCP_CORK
                         */
    
                        if (err != NGX_EINTR) {
                            wev->error = 1;
                            ngx_connection_error(c, err,
                                                 ngx_tcp_nopush_n " failed");
                            return NGX_CHAIN_ERROR;
                        }
    
                    } else {
                        c->tcp_nopush = NGX_TCP_NOPUSH_SET;
    
                        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
                                       "tcp_nopush");
                    }
                }
            }
    
            /* get the file buf */
    
            /* 这里开始获取文件中的数据 */
            if (header.count == 0 && cl && cl->buf->in_file && send < limit) {
                file = cl->buf;
    
                /* coalesce the neighbouring file bufs */
    
                /* 统计该文件的大小 */
                file_size = (size_t) ngx_chain_coalesce_file(&cl, limit - send);
    
                send += file_size;
    #if 1
                if (file_size == 0) {
                    ngx_debug_point();
                    return NGX_CHAIN_ERROR;
                }
    #endif
                
                /* 将该文件中的数据发送出去 */
                n = ngx_linux_sendfile(c, file, file_size);
    
                if (n == NGX_ERROR) {
                    return NGX_CHAIN_ERROR;
                }
    
                if (n == NGX_DONE) {
                    /* thread task posted */
                    return in;
                }
    
                sent = (n == NGX_AGAIN) ? 0 : n;
    
            } else {
                n = ngx_writev(c, &header);
    
                if (n == NGX_ERROR) {
                    return NGX_CHAIN_ERROR;
                }
    
                sent = (n == NGX_AGAIN) ? 0 : n;
            }
    
            /* 更新已经发送的字节数 */
            c->sent += sent;
    
            in = ngx_chain_update_sent(in, sent);
    
            if (n == NGX_AGAIN) {
                wev->ready = 0;
                return in;
            }
    
            if ((size_t) (send - prev_send) != sent) {
    
                /*
                 * sendfile() on Linux 4.3+ might be interrupted at any time,
                 * and provides no indication if it was interrupted or not,
                 * so we have to retry till an explicit EAGAIN
                 *
                 * sendfile() in threads can also report less bytes written
                 * than we are prepared to send now, since it was started in
                 * some point in the past, so we again have to retry
                 */
    
                send = prev_send + sent;
                continue;
            }
    
            if (send >= limit || in == NULL) {
                return in;
            }
        }
    }
    

    1.9.2 ngx_output_chain_to_iovec

    ngx_chain_t *
    ngx_output_chain_to_iovec(ngx_iovec_t *vec, ngx_chain_t *in, size_t limit,
        ngx_log_t *log)
    {
        size_t         total, size;
        u_char        *prev;
        ngx_uint_t     n;
        struct iovec  *iov;
    
        iov = NULL;
        prev = NULL;
        total = 0;
        n = 0;
    
        for ( /* void */ ; in && total < limit; in = in->next) {
    
            if (ngx_buf_special(in->buf)) {
                continue;
            }
    
            /* 若该buf中的数据是在文件中,则跳出该循环 */
            if (in->buf->in_file) {
                break;
            }
    
            if (!ngx_buf_in_memory(in->buf)) {
                ngx_log_error(NGX_LOG_ALERT, log, 0,
                              "bad buf in output chain "
                              "t:%d r:%d f:%d %p %p-%p %p %O-%O",
                              in->buf->temporary,
                              in->buf->recycled,
                              in->buf->in_file,
                              in->buf->start,
                              in->buf->pos,
                              in->buf->last,
                              in->buf->file,
                              in->buf->file_pos,
                              in->buf->file_last);
    
                ngx_debug_point();
    
                return NGX_CHAIN_ERROR;
            }
    
            size = in->buf->last - in->buf->pos;
    
            if (size > limit - total) {
                size = limit - total;
            }
    
            if (prev == in->buf->pos) {
                iov->iov_len += size;
    
            } else {
                if (n == vec->nalloc) {
                    break;
                }
    
                iov = &vec->iovs[n++];
    
                iov->iov_base = (void *) in->buf->pos;
                iov->iov_len = size;
            }
    
            prev = in->buf->pos + size;
            total += size;
        }
    
        vec->count = n;
        vec->size = total;
    
        return in;
    }
    

    1.9.3 ngx_writev

    ssize_t
    ngx_writev(ngx_connection_t *c, ngx_iovec_t *vec)
    {
        ssize_t    n;
        ngx_err_t  err;
    
    eintr:
    
        n = writev(c->fd, vec->iovs, vec->count);
    
        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
                       "writev: %z of %uz", n, vec->size);
    
        if (n == -1) {
            err = ngx_errno;
    
            switch (err) {
            case NGX_EAGAIN:
                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
                               "writev() not ready");
                return NGX_AGAIN;
    
            case NGX_EINTR:
                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
                               "writev() was interrupted");
                goto eintr;
    
            default:
                c->write->error = 1;
                ngx_connection_error(c, err, "writev() failed");
                return NGX_ERROR;
            }
        }
    
        return n;
    }
    

    1.9.4 ngx_chain_update_sent

    ngx_chain_t *
    ngx_chain_update_sent(ngx_chain_t *in, off_t sent)
    {
        off_t  size;
    
        for ( /* void */ ; in; in = in->next) {
    
            if (ngx_buf_special(in->buf)) {
                continue;
            }
    
            if (sent == 0) {
                break;
            }
    
            size = ngx_buf_size(in->buf);
    
            /* 若已将 buf 中的数据都发送出去 */
            if (sent >= size) {
                sent -= size;
    
                if (ngx_buf_in_memory(in->buf)) {
                    /* 则更新 pos 指针的位置 */
                    in->buf->pos = in->buf->last;
                }
    
                if (in->buf->in_file) {
                    in->buf->file_pos = in->buf->file_last;
                }
    
                continue;
            }
    
            if (ngx_buf_in_memory(in->buf)) {
                in->buf->pos += (size_t) sent;
            }
    
            if (in->buf->in_file) {
                in->buf->file_pos += sent;
            }
    
            break;
        }
    
        return in;
    }
    

    1.9.5 ngx_chain_coalesce_file

    off_t
    ngx_chain_coalesce_file(ngx_chain_t **in, off_t limit)
    {
        off_t         total, size, aligned, fprev;
        ngx_fd_t      fd;
        ngx_chain_t  *cl;
    
        total = 0;
    
        cl = *in;
        fd = cl->buf->file->fd;
    
        do {
            /* 计算当前需要发送的字节数 */
            size = cl->buf->file_last - cl->buf->file_pos;
    
            if (size > limit - total) {
                size = limit - total;
    
                aligned = (cl->buf->file_pos + size + ngx_pagesize - 1)
                           & ~((off_t) ngx_pagesize - 1);
    
                if (aligned <= cl->buf->file_last) {
                    size = aligned - cl->buf->file_pos;
                }
    
                total += size;
                break;
            }
    
            total += size;
            fprev = cl->buf->file_pos + size;
            cl = cl->next;
    
        } while (cl
                 && cl->buf->in_file
                 && total < limit
                 && fd == cl->buf->file->fd
                 && fprev == cl->buf->file_pos);
    
        *in = cl;
    
        return total;
    }
    

    1.9.6 ngx_linux_sendfile

    static ssize_t
    ngx_linux_sendfile(ngx_connection_t *c, ngx_buf_t *file, size_t size)
    {
    #if (NGX_HAVE_SENDFILE64)
        off_t      offset;
    #else
        int32_t    offset;
    #endif
        ssize_t    n;
        ngx_err_t  err;
    
    #if (NGX_THREADS)
    
        if (file->file->thread_handler) {
            return ngx_linux_sendfile_thread(c, file, size);
        }
    
    #endif
    
    #if (NGX_HAVE_SENDFILE64)
        offset = file->file_pos;
    #else
        offset = (int32_t) file->file_pos;
    #endif
    
    eintr:
    
        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
                       "sendfile: @%O %uz", file->file_pos, size);
    
        n = sendfile(c->fd, file->file->fd, &offset, size);
    
        if (n == -1) {
            err = ngx_errno;
    
            switch (err) {
            case NGX_EAGAIN:
                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
                               "sendfile() is not ready");
                return NGX_AGAIN;
    
            case NGX_EINTR:
                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
                               "sendfile() was interrupted");
                goto eintr;
    
            default:
                c->write->error = 1;
                ngx_connection_error(c, err, "sendfile() failed");
                return NGX_ERROR;
            }
        }
    
        if (n == 0) {
            /*
             * if sendfile returns zero, then someone has truncated the file,
             * so the offset became beyond the end of the file
             */
    
            ngx_log_error(NGX_LOG_ALERT, c->log, 0,
                          "sendfile() reported that "%s" was truncated at %O",
                          file->file->name.data, file->file_pos);
    
            return NGX_ERROR;
        }
    
        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, "sendfile: %z of %uz @%O",
                       n, size, file->file_pos);
    
        return n;
    }
    
  • 相关阅读:
    Metro-UI系统-1-tile标签
    linux 修改文件最大数
    linux 利用cat写入一段文件
    linux for 循环的小应用
    办公区公网Ip访问不到阿里云ECS
    Django 构建一个项目
    Django 基础篇
    iptables 生产环境下基础设置
    centos7 部署LNMP
    查看sqlserver被锁的表以及如何解锁
  • 原文地址:https://www.cnblogs.com/jimodetiantang/p/9218179.html
Copyright © 2020-2023  润新知