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;
    }
    
  • 相关阅读:
    【Azure 应用服务】在Azure App Service多实例的情况下,如何在应用中通过代码获取到实例名(Instance ID)呢?
    【Azure 应用服务】App Service For Windows 中如何设置代理实现前端静态文件和后端Java Spring Boot Jar包
    【Azure Developer】使用Azure Key Vault 的Key签名后,离线验证的一些参考资料
    【Azure Function】调试 VS Code Javascript Function本地不能运行,报错 Value cannot be null. (Parameter 'provider')问题
    【Azure 应用服务】App Service 使用Tomcat运行Java应用,如何设置前端网页缓存的相应参数呢(Xms512m Xmx1204m)?
    【Azure API 管理】APIM添加Logtoeventhub的策略后,一些相关APIM与Event Hub的问题
    【Azure API 管理】为调用APIM的请求启用Trace 调试APIM Policy的利器
    【Azure 事件中心】China Azure上是否有Kafka服务简答
    【Azure 应用服务】探索在Azure上设置禁止任何人访问App Service的默认域名(Default URL)
    【Azure 微服务】记一次错误的更新Service Fabric 证书而引发的集群崩溃而只能重建
  • 原文地址:https://www.cnblogs.com/jimodetiantang/p/9217482.html
Copyright © 2011-2022 走看看