1. ngx_http_top_header_filter
该链表主要是用于构造响应消息的消息报头。
ngx_http_top_header_filter 单链表有如下模块插入了操作:
- ngx_http_not_modified_filter_module: ngx_http_not_modified_header_filter
- ngx_http_headers_filter_module:ngx_http_headers_filter
- ngx_http_userid_filter_module: ngx_http_userid_filter
- ngx_http_charset_filter_module:ngx_http_charset_header_filter
- ngx_http_ssi_filter_module: ngx_http_ssi_header_filter
- ngx_http_gzip_filter_module: ngx_http_gzip_header_filter
- ngx_http_range_filter_module: ngx_http_range_header_filter
- ngx_http_chunked_filter_module: ngx_http_chunked_header_filter
- 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;
}
1.9.2 ngx_alloc_chain_link
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;
}