zoukankan      html  css  js  c++  java
  • Nginx-HTTP之ngx_http_top_header_filter

    1. ngx_http_top_header_filter

    该链表主要是用于构造响应消息的消息报头。

    ngx_http_top_header_filter 单链表有如下模块插入了操作:

    1. ngx_http_not_modified_filter_module: ngx_http_not_modified_header_filter
    2. ngx_http_headers_filter_module:ngx_http_headers_filter
    3. ngx_http_userid_filter_module: ngx_http_userid_filter
    4. ngx_http_charset_filter_module:ngx_http_charset_header_filter
    5. ngx_http_ssi_filter_module: ngx_http_ssi_header_filter
    6. ngx_http_gzip_filter_module: ngx_http_gzip_header_filter
    7. ngx_http_range_filter_module: ngx_http_range_header_filter
    8. ngx_http_chunked_filter_module: ngx_http_chunked_header_filter
    9. ngx_http_header_filter_module: ngx_http_header_filter

    1.1 ngx_http_not_modified_header_filter

    static ngx_int_t
    ngx_http_not_modified_header_filter(ngx_http_request_t *r)
    {
        if (r->headers_out.status != NGX_HTTP_OK
            || r != r->main
            || r->disable_not_modified)
        {
            return ngx_http_next_header_filter(r);
        }
    
        /* If-Modified-Since: 当客户端请求服务器判断自己缓存的内容是否变化时,可以设置
         * If-Modified-Since 请求头,如果服务器上的内容修改时间比这个头的时间值还新,
         * 那么将返回新的内容,否则不返回。这个与 If-Unmodified-Since 类似,是用来判断
         * 资源变化的另一种方式 */
        if (r->headers_in.if_unmodified_since
            && !ngx_http_test_if_unmodified(r))
        {
            return ngx_http_filter_finalize_request(r, NULL,
                                                    NGX_HTTP_PRECONDITION_FAILED);
        }
    
        /* If-Match: 与 ETag 一起用来判断已缓存的内容是否被修改过。比如,客户端在获取
         * 内容时会获取一个与内容相同的实体标签 ETag (内容变化会使 ETag 变化),下次
         * 再请求同样的内容时,会在请求头的 If-Match 中包含这个 ETag,然后发给可能存有
         * 更新内容的服务器。服务器将收到的 ETag 与本地目前的 ETag 进行比较,如果匹配
         * 返回所请求内容。这种方法在断点续传的时候使用较多 */
        if (r->headers_in.if_match
            && !ngx_http_test_if_match(r, r->headers_in.if_match, 0))
        {
            return ngx_http_filter_finalize_request(r, NULL,
                                                    NGX_HTTP_PRECONDITION_FAILED);
        }
     
        /* If-Modified-Since: 当客户端请求服务器判断自己缓存的内容是否变化时,可以设置
         * If-Modified-Since 头,如果服务器上的内容修改时间比这个头的时间值还要新,那么将
         * 返回新的内容,否则不返回。这个与 If-Unmodified-Since 类似,是用来判断资源变化的
         * 另一种方式。
         *
         * If-None-Match: 与 If-Match 类似,结果相反。如果 If-None-Match 中包含了 ETag 的值,
         * 服务器在进行比较后发现如果不匹配,则返回所请求的内容,否则不返回相关内容。这种
         * 方法在网页刷新的时候使用较多。
         */
        if (r->headers_in.if_modified_since || r->headers_in.if_none_match) {
    
            if (r->headers_in.if_modified_since
                && ngx_http_test_if_modified(r))
            {
                return ngx_http_next_header_filter(r);
            }
    
            if (r->headers_in.if_none_match
                && !ngx_http_test_if_match(r, r->headers_in.if_none_match, 1))
            {
                return ngx_http_next_header_filter(r);
            }
    
            /* not modified */
    
            r->headers_out.status = NGX_HTTP_NOT_MODIFIED;
            r->headers_out.status_line.len = 0;
            r->headers_out.content_type.len = 0;
            ngx_http_clear_content_length(r);
            ngx_http_clear_accept_ranges(r);
    
            if (r->headers_out.content_encoding) {
                r->headers_out.content_encoding->hash = 0;
                r->headers_out.content_encoding = NULL;
            }
    
            return ngx_http_next_header_filter(r);
        }
    
        return ngx_http_next_header_filter(r);
    }
    

    此时 ngx_http_next_header_filter 回调的函数为 ngx_http_headers_filter。

    1.2 ngx_http_headers_filter

    static ngx_int_t
    ngx_http_headers_filter(ngx_http_request_t *r)
    {
        ngx_str_t                 value;
        ngx_uint_t                i, safe_status;
        ngx_http_header_val_t    *h;
        ngx_http_headers_conf_t  *conf;
    
        if (r != r->main) {
            return ngx_http_next_header_filter(r);
        }
    
        /* 获取 ngx_http_headers_filter_module 模块与 location 相关的配置 */
        conf = ngx_http_get_module_loc_conf(r, ngx_http_headers_filter_module);
    
        /* 若对 expires、headers、trailers 都没有相应的配置,则执行
         * ngx_http_top_header_filter 链表的下一个函数 */
        if (conf->expires == NGX_HTTP_EXPIRES_OFF
            && conf->headers == NULL
            && conf->trailers == NULL)
        {
            return ngx_http_next_header_filter(r);
        }
    
        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;
        }
    
        if (conf->expires != NGX_HTTP_EXPIRES_OFF && safe_status) {
            if (ngx_http_set_expires(r, conf) != NGX_OK) {
                return NGX_ERROR;
            }
        }
    
        if (conf->headers) {
            h = conf->headers->elts;
            for (i = 0; i < conf->headers->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 (h[i].handler(r, &h[i], &value) != NGX_OK) {
                    return NGX_ERROR;
                }
            }
        }
    
        if (conf->trailers) {
            h = conf->trailers->elts;
            for (i = 0; i < conf->trailers->nelts; i++) {
    
                if (!safe_status && !h[i].always) {
                    continue;
                }
    
                r->expect_trailers = 1;
                break;
            }
        }
    
        return ngx_http_next_header_filter(r);
    }
    

    1.3 ngx_http_userid_filter

    static ngx_int_t
    ngx_http_userid_filter(ngx_http_request_t *r)
    {
        ngx_http_userid_ctx_t   *ctx;
        ngx_http_userid_conf_t  *conf;
    
        /* 若为子请求,直接执行 ngx_http_top_header_filter 链表的下一个函数  */
        if (r != r->main) {
            return ngx_http_next_header_filter(r);
        }
    
        /* 获取 ngx_http_userid_filter_module 模块 loc 级别的配置 */
        conf = ngx_http_get_module_loc_conf(r, ngx_http_userid_filter_module);
    
        /* 若没有使能该模块,则执行下一个函数 */
        if (conf->enable < NGX_HTTP_USERID_V1) {
            return ngx_http_next_header_filter(r);
        }
    
        ctx = ngx_http_userid_get_uid(r, conf);
    
        if (ctx == NULL) {
            return NGX_ERROR;
        }
    
        if (ngx_http_userid_set_uid(r, ctx, conf) == NGX_OK) {
            return ngx_http_next_header_filter(r);
        }
    
        return NGX_ERROR;
    }
    

    1.4 ngx_http_charset_header_filter

    static ngx_int_t
    ngx_http_charset_header_filter(ngx_http_request_t *r)
    {
        ngx_int_t                      charset, source_charset;
        ngx_str_t                      dst, src;
        ngx_http_charset_t            *charsets;
        ngx_http_charset_main_conf_t  *mcf;
    
        /* 若当前请求为父请求 */
        if (r == r->main) {
            charset = ngx_http_destination_charset(r, &dst);
    
        } else {
            charset = ngx_http_main_request_charset(r, &dst);
        }
    
        if (charset == NGX_ERROR) {
            return NGX_ERROR;
        }
    
        /* 执行下一个 */
        if (charset == NGX_DECLINED) {
            return ngx_http_next_header_filter(r);
        }
    
        /* charset: charset index or NGX_HTTP_NO_CHARSET */
    
        source_charset = ngx_http_source_charset(r, &src);
    
        if (source_charset == NGX_ERROR) {
            return NGX_ERROR;
        }
    
        /*
         * source_charset: charset index, NGX_HTTP_NO_CHARSET,
         *                 or NGX_HTTP_CHARSET_OFF
         */
    
        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                       "charset: "%V" > "%V"", &src, &dst);
    
        if (source_charset == NGX_HTTP_CHARSET_OFF) {
            ngx_http_set_charset(r, &dst);
    
            return ngx_http_next_header_filter(r);
        }
    
        if (charset == NGX_HTTP_NO_CHARSET
            || source_charset == NGX_HTTP_NO_CHARSET)
        {
            if (source_charset != charset
                || ngx_strncasecmp(dst.data, src.data, dst.len) != 0)
            {
                goto no_charset_map;
            }
    
            ngx_http_set_charset(r, &dst);
    
            return ngx_http_next_header_filter(r);
        }
    
        if (source_charset == charset) {
            r->headers_out.content_type.len = r->headers_out.content_type_len;
    
            ngx_http_set_charset(r, &dst);
    
            return ngx_http_next_header_filter(r);
        }
    
        /* source_charset != charset */
    
        if (r->headers_out.content_encoding
            && r->headers_out.content_encoding->value.len)
        {
            return ngx_http_next_header_filter(r);
        }
    
        mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module);
        charsets = mcf->charsets.elts;
    
        if (charsets[source_charset].tables == NULL
            || charsets[source_charset].tables[charset] == NULL)
        {
            goto no_charset_map;
        }
    
        r->headers_out.content_type.len = r->headers_out.content_type_len;
    
        ngx_http_set_charset(r, &dst);
    
        return ngx_http_charset_ctx(r, charsets, charset, source_charset);
    
    no_charset_map:
    
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "no "charset_map" between the charsets "%V" and "%V"",
                      &src, &dst);
    
        return ngx_http_next_header_filter(r);
    }
    

    1.4.1 ngx_http_destination_charset

    static ngx_int_t
    ngx_http_destination_charset(ngx_http_request_t *r, ngx_str_t *name)
    {
        ngx_int_t                      charset;
        ngx_http_charset_t            *charsets;
        ngx_http_variable_value_t     *vv;
        ngx_http_charset_loc_conf_t   *mlcf;
        ngx_http_charset_main_conf_t  *mcf;
    
        /* 若还没有设置当前响应的 Content-Type 头  */
        if (r->headers_out.content_type.len == 0) {
            return NGX_DECLINED;
        }
    
        if (r->headers_out.override_charset
            && r->headers_out.override_charset->len)
        {
            *name = *r->headers_out.override_charset;
    
            charset = ngx_http_get_charset(r, name);
    
            if (charset != NGX_HTTP_NO_CHARSET) {
                return charset;
            }
    
            ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                          "unknown charset "%V" to override", name);
    
            return NGX_DECLINED;
        }
    
        mlcf = ngx_http_get_module_loc_conf(r, ngx_http_charset_filter_module);
        charset = mlcf->charset;
    
        /* 若没有开启 charset */
        if (charset == NGX_HTTP_CHARSET_OFF) {
            return NGX_DECLINED;
        }
    
        if (r->headers_out.charset.len) {
            if (mlcf->override_charset == 0) {
                return NGX_DECLINED;
            }
    
        } else {
            if (ngx_http_test_content_type(r, &mlcf->types) == NULL) {
                return NGX_DECLINED;
            }
        }
    
        if (charset < NGX_HTTP_CHARSET_VAR) {
            mcf = ngx_http_get_module_main_conf(r, ngx_http_charset_filter_module);
            charsets = mcf->charsets.elts;
            *name = charsets[charset].name;
            return charset;
        }
    
        vv = ngx_http_get_indexed_variable(r, charset - NGX_HTTP_CHARSET_VAR);
    
        if (vv == NULL || vv->not_found) {
            return NGX_ERROR;
        }
    
        name->len = vv->len;
        name->data = vv->data;
    
        return ngx_http_get_charset(r, name);
    }
    

    1.5 ngx_http_ssi_header_filter

    static ngx_int_t
    ngx_http_ssi_header_filter(ngx_http_request_t *r)
    {
        ngx_http_ssi_ctx_t       *ctx;
        ngx_http_ssi_loc_conf_t  *slcf;
    
        /* 获取 ngx_http_ssi_filter_module 模块 loc 级别的配置 */
        slcf = ngx_http_get_module_loc_conf(r, ngx_http_ssi_filter_module);
    
        /* 若没有启用该模块 */
        if (!slcf->enable
            || r->headers_out.content_length_n == 0
            || ngx_http_test_content_type(r, &slcf->types) == NULL)
        {
            return ngx_http_next_header_filter(r);
        }
    
        ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_ssi_ctx_t));
        if (ctx == NULL) {
            return NGX_ERROR;
        }
    
        ngx_http_set_ctx(r, ctx, ngx_http_ssi_filter_module);
    
    
        ctx->value_len = slcf->value_len;
        ctx->last_out = &ctx->out;
    
        ctx->encoding = NGX_HTTP_SSI_ENTITY_ENCODING;
        ctx->output = 1;
    
        ctx->params.elts = ctx->params_array;
        ctx->params.size = sizeof(ngx_table_elt_t);
        ctx->params.nalloc = NGX_HTTP_SSI_PARAMS_N;
        ctx->params.pool = r->pool;
    
        ctx->timefmt = ngx_http_ssi_timefmt;
        ngx_str_set(&ctx->errmsg,
                    "[an error occurred while processing the directive]");
    
        r->filter_need_in_memory = 1;
    
        if (r == r->main) {
            ngx_http_clear_content_length(r);
            ngx_http_clear_accept_ranges(r);
    
            if (!slcf->last_modified) {
                ngx_http_clear_last_modified(r);
                ngx_http_clear_etag(r);
    
            } else {
                ngx_http_weak_etag(r);
            }
        }
    
        return ngx_http_next_header_filter(r);
    }
    

    1.6 ngx_http_gzip_header_filter

    static ngx_int_t
    ngx_http_gzip_header_filter(ngx_http_request_t *r)
    {
        ngx_table_elt_t       *h;
        ngx_http_gzip_ctx_t   *ctx;
        ngx_http_gzip_conf_t  *conf;
    
        conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
    
        /* 若没有使能该模块 */
        if (!conf->enable
            || (r->headers_out.status != NGX_HTTP_OK
                && r->headers_out.status != NGX_HTTP_FORBIDDEN
                && r->headers_out.status != NGX_HTTP_NOT_FOUND)
            || (r->headers_out.content_encoding
                && r->headers_out.content_encoding->value.len)
            || (r->headers_out.content_length_n != -1
                && r->headers_out.content_length_n < conf->min_length)
            || ngx_http_test_content_type(r, &conf->types) == NULL
            || r->header_only)
        {
            return ngx_http_next_header_filter(r);
        }
    
        r->gzip_vary = 1;
    
    #if (NGX_HTTP_DEGRADATION)
        {
        ngx_http_core_loc_conf_t  *clcf;
    
        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
    
        if (clcf->gzip_disable_degradation && ngx_http_degraded(r)) {
            return ngx_http_next_header_filter(r);
        }
        }
    #endif
    
        if (!r->gzip_tested) {
            if (ngx_http_gzip_ok(r) != NGX_OK) {
                return ngx_http_next_header_filter(r);
            }
    
        } else if (!r->gzip_ok) {
            return ngx_http_next_header_filter(r);
        }
    
        ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_gzip_ctx_t));
        if (ctx == NULL) {
            return NGX_ERROR;
        }
    
        ngx_http_set_ctx(r, ctx, ngx_http_gzip_filter_module);
    
        ctx->request = r;
        ctx->buffering = (conf->postpone_gzipping != 0);
    
        ngx_http_gzip_filter_memory(r, ctx);
    
        h = ngx_list_push(&r->headers_out.headers);
        if (h == NULL) {
            return NGX_ERROR;
        }
    
        h->hash = 1;
        ngx_str_set(&h->key, "Content-Encoding");
        ngx_str_set(&h->value, "gzip");
        r->headers_out.content_encoding = h;
    
        r->main_filter_need_in_memory = 1;
    
        ngx_http_clear_content_length(r);
        ngx_http_clear_accept_ranges(r);
        ngx_http_weak_etag(r);
    
        return ngx_http_next_header_filter(r);
    }
    

    1.7 ngx_http_range_header_filter

    static ngx_int_t
    ngx_http_range_header_filter(ngx_http_request_t *r)
    {
        time_t                        if_range_time;
        ngx_str_t                    *if_range, *etag;
        ngx_uint_t                    ranges;
        ngx_http_core_loc_conf_t     *clcf;
        ngx_http_range_filter_ctx_t  *ctx;
    
        if (r->http_version < NGX_HTTP_VERSION_10
            || r->headers_out.status != NGX_HTTP_OK
            || (r != r->main && !r->subrequest_ranges)
            || r->headers_out.content_length_n == -1
            || !r->allow_ranges)
        {
            return ngx_http_next_header_filter(r);
        }
    
        /* 获取 ngx_http_core_module 模块 loc 级别的配置 */
        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
    
        if (clcf->max_ranges == 0) {
            return ngx_http_next_header_filter(r);
        }
    
        if (r->headers_in.range == NULL
            || r->headers_in.range->value.len < 7
            || ngx_strncasecmp(r->headers_in.range->value.data,
                               (u_char *) "bytes=", 6)
               != 0)
        {
            goto next_filter;
        }
    
        if (r->headers_in.if_range) {
    
            if_range = &r->headers_in.if_range->value;
    
            if (if_range->len >= 2 && if_range->data[if_range->len - 1] == '"') {
    
                if (r->headers_out.etag == NULL) {
                    goto next_filter;
                }
    
                etag = &r->headers_out.etag->value;
    
                ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                               "http ir:%V etag:%V", if_range, etag);
    
                if (if_range->len != etag->len
                    || ngx_strncmp(if_range->data, etag->data, etag->len) != 0)
                {
                    goto next_filter;
                }
    
                goto parse;
            }
    
            if (r->headers_out.last_modified_time == (time_t) -1) {
                goto next_filter;
            }
    
            if_range_time = ngx_parse_http_time(if_range->data, if_range->len);
    
            ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                           "http ir:%T lm:%T",
                           if_range_time, r->headers_out.last_modified_time);
    
            if (if_range_time != r->headers_out.last_modified_time) {
                goto next_filter;
            }
        }
    
    parse:
    
        ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_range_filter_ctx_t));
        if (ctx == NULL) {
            return NGX_ERROR;
        }
    
        ctx->offset = r->headers_out.content_offset;
    
        ranges = r->single_range ? 1 : clcf->max_ranges;
    
        switch (ngx_http_range_parse(r, ctx, ranges)) {
    
        case NGX_OK:
            ngx_http_set_ctx(r, ctx, ngx_http_range_body_filter_module);
    
            r->headers_out.status = NGX_HTTP_PARTIAL_CONTENT;
            r->headers_out.status_line.len = 0;
    
            if (ctx->ranges.nelts == 1) {
                return ngx_http_range_singlepart_header(r, ctx);
            }
    
            return ngx_http_range_multipart_header(r, ctx);
    
        case NGX_HTTP_RANGE_NOT_SATISFIABLE:
            return ngx_http_range_not_satisfiable(r);
    
        case NGX_ERROR:
            return NGX_ERROR;
    
        default: /* NGX_DECLINED */
            break;
        }
    
    next_filter:
    
        /* Accept-Ranges: 可以请求网页实体的一个或多个子范围字段,
         * 值为 bytes 表示支持断点续传,为 none 表示不支持 */
        r->headers_out.accept_ranges = ngx_list_push(&r->headers_out.headers);
        if (r->headers_out.accept_ranges == NULL) {
            return NGX_ERROR;
        }
    
        r->headers_out.accept_ranges->hash = 1;
        ngx_str_set(&r->headers_out.accept_ranges->key, "Accept-Ranges");
        ngx_str_set(&r->headers_out.accept_ranges->value, "bytes");
    
        return ngx_http_next_header_filter(r);
    }
    

    1.8 ngx_http_chunked_header_filter

    static ngx_int_t
    ngx_http_chunked_header_filter(ngx_http_request_t *r)
    {
        ngx_http_core_loc_conf_t       *clcf;
        ngx_http_chunked_filter_ctx_t  *ctx;
    
        if (r->headers_out.status == NGX_HTTP_NOT_MODIFIED
            || r->headers_out.status == NGX_HTTP_NO_CONTENT
            || r->headers_out.status < NGX_HTTP_OK
            || r != r->main
            || r->method == NGX_HTTP_HEAD)
        {
            return ngx_http_next_header_filter(r);
        }
    
        if (r->headers_out.content_length_n == -1
            || r->expect_trailers)
        {
            clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
    
            if (r->http_version >= NGX_HTTP_VERSION_11
                && clcf->chunked_transfer_encoding)
            {
                if (r->expect_trailers) {
                    ngx_http_clear_content_length(r);
                }
    
                r->chunked = 1;
    
                ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_chunked_filter_ctx_t));
                if (ctx == NULL) {
                    return NGX_ERROR;
                }
    
                ngx_http_set_ctx(r, ctx, ngx_http_chunked_filter_module);
    
            } else if (r->headers_out.content_length_n == -1) {
                r->keepalive = 0;
            }
        }
    
        return ngx_http_next_header_filter(r);
    }
    

    1.9 ngx_http_header_filter

    static ngx_int_t
    ngx_http_header_filter(ngx_http_request_t *r)
    {
        u_char                    *p;
        size_t                     len;
        ngx_str_t                  host, *status_line;
        ngx_buf_t                 *b;
        ngx_uint_t                 status, i, port;
        ngx_chain_t                out;
        ngx_list_part_t           *part;
        ngx_table_elt_t           *header;
        ngx_connection_t          *c;
        ngx_http_core_loc_conf_t  *clcf;
        ngx_http_core_srv_conf_t  *cscf;
        u_char                     addr[NGX_SOCKADDR_STRLEN];
    
        /* 标志位,为 1 表示该 header 已经发送了 */
        if (r->header_sent) {
            return NGX_OK;
        }
    
        /* 设置该标志位为 1 */
        r->header_sent = 1;
    
        /* 若为子请求,则直接返回 */
        if (r != r->main) {
            return NGX_OK;
        }
    
        if (r->http_version < NGX_HTTP_VERSION_10) {
            return NGX_OK;
        }
    
        /* 若当前方法为 HEAD 则仅发送 header */
        if (r->method == NGX_HTTP_HEAD) {
            r->header_only = 1;
        }
    
        if (r->headers_out.last_modified_time != -1) {
            if (r->headers_out.status != NGX_HTTP_OK
                && r->headers_out.status != NGX_HTTP_PARTIAL_CONTENT
                && r->headers_out.status != NGX_HTTP_NOT_MODIFIED)
            {
                r->headers_out.last_modified_time = -1;
                r->headers_out.last_modified = NULL;
            }
        }
    
        /* 状态行的长度 */
        len = sizeof("HTTP/1.x ") - 1 + sizeof(CRLF) - 1
              /* the end of the header */
              + sizeof(CRLF) - 1;
    
        /* status line */
    
        if (r->headers_out.status_line.len) {
            len += r->headers_out.status_line.len;
            status_line = &r->headers_out.status_line;
    #if (NGX_SUPPRESS_WARN)
            status = 0;
    #endif
    
        } else {
    
            status = r->headers_out.status;
    
            if (status >= NGX_HTTP_OK
                && status < NGX_HTTP_LAST_2XX)
            {
                /* 2XX */
    
                /* 204:该响应没有响应正文 */
                if (status == NGX_HTTP_NO_CONTENT) {
                    r->header_only = 1;
                    ngx_str_null(&r->headers_out.content_type);
                    r->headers_out.last_modified_time = -1;
                    r->headers_out.last_modified = NULL;
                    r->headers_out.content_length = NULL;
                    r->headers_out.content_length_n = -1;
                }
    
                /* 计算该状态在 ngx_http_status_lines 数组中的索引 */
                status -= NGX_HTTP_OK;
                /* 从该数组中 status 索引处获取字符串形式的状态行 */
                status_line = &ngx_http_status_lines[status];
                len += ngx_http_status_lines[status].len;
    
            } else if (status >= NGX_HTTP_MOVED_PERMANENTLY
                       && status < NGX_HTTP_LAST_3XX)
            {
                /* 3XX */
    
                if (status == NGX_HTTP_NOT_MODIFIED) {
                    r->header_only = 1;
                }
    
                status = status - NGX_HTTP_MOVED_PERMANENTLY + NGX_HTTP_OFF_3XX;
                status_line = &ngx_http_status_lines[status];
                len += ngx_http_status_lines[status].len;
    
            } else if (status >= NGX_HTTP_BAD_REQUEST
                       && status < NGX_HTTP_LAST_4XX)
            {
                /* 4XX */
                status = status - NGX_HTTP_BAD_REQUEST
                                + NGX_HTTP_OFF_4XX;
    
                status_line = &ngx_http_status_lines[status];
                len += ngx_http_status_lines[status].len;
    
            } else if (status >= NGX_HTTP_INTERNAL_SERVER_ERROR
                       && status < NGX_HTTP_LAST_5XX)
            {
                /* 5XX */
                status = status - NGX_HTTP_INTERNAL_SERVER_ERROR
                                + NGX_HTTP_OFF_5XX;
    
                status_line = &ngx_http_status_lines[status];
                len += ngx_http_status_lines[status].len;
    
            } else {
                len += NGX_INT_T_LEN + 1 /* SP */;
                status_line = NULL;
            }
    
            if (status_line && status_line->len == 0) {
                status = r->headers_out.status;
                len += NGX_INT_T_LEN + 1 /* SP */;
                status_line = NULL;
            }
        }
    
        /* 获取 ngx_http_core_module 模块在 loc 级别的配置 */
        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
    
        if (r->headers_out.server == NULL) {
            if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {
                /* Server: web 服务器表明自己是什么版本及版本信息 */
                len += sizeof(ngx_http_server_full_string) - 1;
    
            } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {
                len += sizeof(ngx_http_server_build_string) - 1;
    
            } else {
                len += sizeof(ngx_http_server_string) - 1;
            }
        }
    
        if (r->headers_out.date == NULL) {
            /* Date: 发送该 HTTP 消息的时间 */
            len += sizeof("Date: Mon, 28 Sep 1970 06:00:00 GMT" CRLF) - 1;
        }
    
        if (r->headers_out.content_type.len) {
            len += sizeof("Content-Type: ") - 1
                   + r->headers_out.content_type.len + 2;
    
            if (r->headers_out.content_type_len == r->headers_out.content_type.len
                && r->headers_out.charset.len)
            {
                len += sizeof("; charset=") - 1 + r->headers_out.charset.len;
            }
        }
    
        if (r->headers_out.content_length == NULL
            && r->headers_out.content_length_n >= 0)
        {
            /* Content-Length: 实体内容的大小 */
            len += sizeof("Content-Length: ") - 1 + NGX_OFF_T_LEN + 2;
        }
    
        if (r->headers_out.last_modified == NULL
            && r->headers_out.last_modified_time != -1)
        {
            /* Last-Modified: 指定所访问内容的最后更新时间 */
            len += sizeof("Last-Modified: Mon, 28 Sep 1970 06:00:00 GMT" CRLF) - 1;
        }
    
        c = r->connection;
    
        if (r->headers_out.location
            && r->headers_out.location->value.len
            && r->headers_out.location->value.data[0] == '/'
            && clcf->absolute_redirect)
        {
            r->headers_out.location->hash = 0;
    
            if (clcf->server_name_in_redirect) {
                cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module);
                host = cscf->server_name;
    
            } else if (r->headers_in.server.len) {
                host = r->headers_in.server;
    
            } else {
                host.len = NGX_SOCKADDR_STRLEN;
                host.data = addr;
    
                if (ngx_connection_local_sockaddr(c, &host, 0) != NGX_OK) {
                    return NGX_ERROR;
                }
            }
    
            port = ngx_inet_get_port(c->local_sockaddr);
    
            len += sizeof("Location: https://") - 1
                   + host.len
                   + r->headers_out.location->value.len + 2;
    
            if (clcf->port_in_redirect) {
    
    #if (NGX_HTTP_SSL)
                if (c->ssl)
                    port = (port == 443) ? 0 : port;
                else
    #endif
                    port = (port == 80) ? 0 : port;
    
            } else {
                port = 0;
            }
    
            if (port) {
                len += sizeof(":65535") - 1;
            }
    
        } else {
            ngx_str_null(&host);
            port = 0;
        }
    
        if (r->chunked) {
            len += sizeof("Transfer-Encoding: chunked" CRLF) - 1;
        }
    
        if (r->headers_out.status == NGX_HTTP_SWITCHING_PROTOCOLS) {
            len += sizeof("Connection: upgrade" CRLF) - 1;
    
        } else if (r->keepalive) {
            len += sizeof("Connection: keep-alive" CRLF) - 1;
    
            /*
             * MSIE and Opera ignore the "Keep-Alive: timeout=<N>" header.
             * MSIE keeps the connection alive for about 60-65 seconds.
             * Opera keeps the connection alive very long.
             * Mozilla keeps the connection alive for N plus about 1-10 seconds.
             * Konqueror keeps the connection alive for about N seconds.
             */
    
            if (clcf->keepalive_header) {
                len += sizeof("Keep-Alive: timeout=") - 1 + NGX_TIME_T_LEN + 2;
            }
    
        } else {
            len += sizeof("Connection: close" CRLF) - 1;
        }
    
    #if (NGX_HTTP_GZIP)
        if (r->gzip_vary) {
            if (clcf->gzip_vary) {
                len += sizeof("Vary: Accept-Encoding" CRLF) - 1;
    
            } else {
                r->gzip_vary = 0;
            }
        }
    #endif
    
        /* 从 headers_out.headers 链表中取出第一个 数组元素 */
        part = &r->headers_out.headers.part;
        /* header 执行该数组 part 的第一个元素 */
        header = part->elts;
    
        /* 统计响应消息中响应头部的总大小 */
        for (i = 0; /* void */; i++) {
    
            if (i >= part->nelts) {
                if (part->next == NULL) {
                    break;
                }
    
                part = part->next;
                header = part->elts;
                i = 0;
            }
    
            if (header[i].hash == 0) {
                continue;
            }
    
            len += header[i].key.len + sizeof(": ") - 1 + header[i].value.len
                   + sizeof(CRLF) - 1;
        }
    
        /* 为该响应消息分配临时缓存 */
        b = ngx_create_temp_buf(r->pool, len);
        if (b == NULL) {
            return NGX_ERROR;
        }
    
        /* 响应消息分四部分:状态行、消息报头、空行和响应正文 */
        /* "HTTP/1.x " */
        b->last = ngx_cpymem(b->last, "HTTP/1.1 ", sizeof("HTTP/1.x ") - 1);
    
        /* status line */
        if (status_line) {
            b->last = ngx_copy(b->last, status_line->data, status_line->len);
    
        } else {
            b->last = ngx_sprintf(b->last, "%03ui ", status);
        }
        *b->last++ = CR; *b->last++ = LF;
    
        /* 下面依次向该缓存中写入消息报头 */
        if (r->headers_out.server == NULL) {
            if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_ON) {
                p = ngx_http_server_full_string;
                len = sizeof(ngx_http_server_full_string) - 1;
    
            } else if (clcf->server_tokens == NGX_HTTP_SERVER_TOKENS_BUILD) {
                p = ngx_http_server_build_string;
                len = sizeof(ngx_http_server_build_string) - 1;
    
            } else {
                p = ngx_http_server_string;
                len = sizeof(ngx_http_server_string) - 1;
            }
    
            b->last = ngx_cpymem(b->last, p, len);
        }
    
        if (r->headers_out.date == NULL) {
            b->last = ngx_cpymem(b->last, "Date: ", sizeof("Date: ") - 1);
            b->last = ngx_cpymem(b->last, ngx_cached_http_time.data,
                                 ngx_cached_http_time.len);
    
            *b->last++ = CR; *b->last++ = LF;
        }
    
        if (r->headers_out.content_type.len) {
            b->last = ngx_cpymem(b->last, "Content-Type: ",
                                 sizeof("Content-Type: ") - 1);
            p = b->last;
            b->last = ngx_copy(b->last, r->headers_out.content_type.data,
                               r->headers_out.content_type.len);
    
            if (r->headers_out.content_type_len == r->headers_out.content_type.len
                && r->headers_out.charset.len)
            {
                b->last = ngx_cpymem(b->last, "; charset=",
                                     sizeof("; charset=") - 1);
                b->last = ngx_copy(b->last, r->headers_out.charset.data,
                                   r->headers_out.charset.len);
    
                /* update r->headers_out.content_type for possible logging */
    
                r->headers_out.content_type.len = b->last - p;
                r->headers_out.content_type.data = p;
            }
    
            *b->last++ = CR; *b->last++ = LF;
        }
    
        if (r->headers_out.content_length == NULL
            && r->headers_out.content_length_n >= 0)
        {
            b->last = ngx_sprintf(b->last, "Content-Length: %O" CRLF,
                                  r->headers_out.content_length_n);
        }
    
        if (r->headers_out.last_modified == NULL
            && r->headers_out.last_modified_time != -1)
        {
            b->last = ngx_cpymem(b->last, "Last-Modified: ",
                                 sizeof("Last-Modified: ") - 1);
            b->last = ngx_http_time(b->last, r->headers_out.last_modified_time);
    
            *b->last++ = CR; *b->last++ = LF;
        }
    
        if (host.data) {
    
            p = b->last + sizeof("Location: ") - 1;
    
            b->last = ngx_cpymem(b->last, "Location: http",
                                 sizeof("Location: http") - 1);
    
    #if (NGX_HTTP_SSL)
            if (c->ssl) {
                *b->last++ ='s';
            }
    #endif
    
            *b->last++ = ':'; *b->last++ = '/'; *b->last++ = '/';
            b->last = ngx_copy(b->last, host.data, host.len);
    
            if (port) {
                b->last = ngx_sprintf(b->last, ":%ui", port);
            }
    
            b->last = ngx_copy(b->last, r->headers_out.location->value.data,
                               r->headers_out.location->value.len);
    
            /* update r->headers_out.location->value for possible logging */
    
            r->headers_out.location->value.len = b->last - p;
            r->headers_out.location->value.data = p;
            ngx_str_set(&r->headers_out.location->key, "Location");
    
            *b->last++ = CR; *b->last++ = LF;
        }
    
        if (r->chunked) {
            b->last = ngx_cpymem(b->last, "Transfer-Encoding: chunked" CRLF,
                                 sizeof("Transfer-Encoding: chunked" CRLF) - 1);
        }
    
        if (r->headers_out.status == NGX_HTTP_SWITCHING_PROTOCOLS) {
            b->last = ngx_cpymem(b->last, "Connection: upgrade" CRLF,
                                 sizeof("Connection: upgrade" CRLF) - 1);
    
        } else if (r->keepalive) {
            b->last = ngx_cpymem(b->last, "Connection: keep-alive" CRLF,
                                 sizeof("Connection: keep-alive" CRLF) - 1);
    
            if (clcf->keepalive_header) {
                b->last = ngx_sprintf(b->last, "Keep-Alive: timeout=%T" CRLF,
                                      clcf->keepalive_header);
            }
    
        } else {
            b->last = ngx_cpymem(b->last, "Connection: close" CRLF,
                                 sizeof("Connection: close" CRLF) - 1);
        }
    
    #if (NGX_HTTP_GZIP)
        if (r->gzip_vary) {
            b->last = ngx_cpymem(b->last, "Vary: Accept-Encoding" CRLF,
                                 sizeof("Vary: Accept-Encoding" CRLF) - 1);
        }
    #endif
    
        part = &r->headers_out.headers.part;
        header = part->elts;
    
        for (i = 0; /* void */; i++) {
    
            if (i >= part->nelts) {
                if (part->next == NULL) {
                    break;
                }
    
                part = part->next;
                header = part->elts;
                i = 0;
            }
    
            if (header[i].hash == 0) {
                continue;
            }
    
            b->last = ngx_copy(b->last, header[i].key.data, header[i].key.len);
            *b->last++ = ':'; *b->last++ = ' ';
    
            b->last = ngx_copy(b->last, header[i].value.data, header[i].value.len);
            *b->last++ = CR; *b->last++ = LF;
        }
    
        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
                       "%*s", (size_t) (b->last - b->pos), b->pos);
    
        /* the end of HTTP header */
        *b->last++ = CR; *b->last++ = LF;
    
        r->header_size = b->last - b->pos;
    
        if (r->header_only) {
            b->last_buf = 1;
        }
    
        out.buf = b;
        out.next = NULL;
    
        return ngx_http_write_filter(r, &out);
    }
    

    1.9.1 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;
        /* 指向该链表的头部 */
        ll = &r->out;
    
        /* find the size, the flush point and the last link of the saved chain */
    
        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;
            /* 将该 cl 添加到 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
    
            /* 计算该 cl->buf 中缓存的数据大小 */
            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;
            }
        }
    
        *ll = NULL;
    
        ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
                       "http write filter: l:%ui f:%ui s:%O", last, flush, size);
    
        /* 获取 ngx_http_core_module 模块 loc 级别的配置  */
        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);
    
        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);
        }
    
        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;
    }
    
    ngx_chain_t *
    ngx_alloc_chain_link(ngx_pool_t *pool)
    {
        ngx_chain_t  *cl;
    
        cl = pool->chain;
    
        if (cl) {
            pool->chain = cl->next;
            return cl;
        }
    
        /* 重新分配一个 ngx_chain_t */
        cl = ngx_palloc(pool, sizeof(ngx_chain_t));
        if (cl == NULL) {
            return NULL;
        }
    
        return cl;
    }
    
  • 相关阅读:
    计算机图形学
    2017.3.20
    史上最强型人养成秘籍: 90 天肥仔变型男实录
    Word 2013发布博客测试
    eeeeeeeeeee
    测试,使用word2013发布博客园博客
    Win7系统桌面便签怎么添加?
    开启两步验证的教程
    【Todo】 cygwin下emacs中M-x shell 中出现乱码
    emacs的LoadPath
  • 原文地址:https://www.cnblogs.com/jimodetiantang/p/9217482.html
Copyright © 2011-2022 走看看