HTTP 请求 HEADER 的处理与解析 -- ngx_http_process_request_headers

2015-10-11 23:46:53   最后更新: 2015-10-11 23:46:53   访问数量:1012




在 HTTP 请求行正确处理完成后,针对 HTTP/1.0 及以上版本紧接着要做的就是请求 HEADER 的处理与解析了

// static void ngx_http_process_request_headers(ngx_event_t *rev) // HTTP 请求 HEADER 的处理与解析 {{{ static void ngx_http_process_request_headers(ngx_event_t *rev) { u_char *p; size_t len; ssize_t n; ngx_int_t rc, rv; ngx_table_elt_t *h; ngx_connection_t *c; ngx_http_header_t *hh; ngx_http_request_t *r; ngx_http_core_srv_conf_t *cscf; ngx_http_core_main_conf_t *cmcf; c = rev->data; r = c->data; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http process request header line"); if (rev->timedout) { ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out"); c->timedout = 1; ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT); return; } cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); rc = NGX_AGAIN; for ( ;; ) { if (rc == NGX_AGAIN) { // 存储空间已满则开辟更大的空间 if (r->header_in->pos == r->header_in->end) { rv = ngx_http_alloc_large_header_buffer(r, 0); if (rv == NGX_ERROR) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; } if (rv == NGX_DECLINED) { p = r->header_name_start; r->lingering_close = 1; if (p == NULL) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent too large request"); ngx_http_finalize_request(r, NGX_HTTP_REQUEST_HEADER_TOO_LARGE); return; } len = r->header_in->end - p; if (len > NGX_MAX_ERROR_STR - 300) { len = NGX_MAX_ERROR_STR - 300; } ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent too long header line: \"%*s...\"", len, r->header_name_start); ngx_http_finalize_request(r, NGX_HTTP_REQUEST_HEADER_TOO_LARGE); return; } } // 从请求中读取请求头 n = ngx_http_read_request_header(r); if (n == NGX_AGAIN || n == NGX_ERROR) { return; } } /* the host header could change the server configuration context */ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); // header 解析 rc = ngx_http_parse_header_line(r, r->header_in, cscf->underscores_in_headers); if (rc == NGX_OK) { r->request_length += r->header_in->pos - r->header_name_start; if (r->invalid_header && cscf->ignore_invalid_headers) { /* there was error while a header line parsing */ ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent invalid header line: \"%*s\"", r->header_end - r->header_name_start, r->header_name_start); continue; } /* a header line has been parsed successfully */ h = ngx_list_push(&r->headers_in.headers); if (h == NULL) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; } h->hash = r->header_hash; h->key.len = r->header_name_end - r->header_name_start; h->key.data = r->header_name_start; h->key.data[h->key.len] = '\0'; h->value.len = r->header_end - r->header_start; h->value.data = r->header_start; h->value.data[h->value.len] = '\0'; h->lowcase_key = ngx_pnalloc(r->pool, h->key.len); if (h->lowcase_key == NULL) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; } if (h->key.len == r->lowcase_index) { ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len); } else { ngx_strlow(h->lowcase_key, h->key.data, h->key.len); } hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash, h->lowcase_key, h->key.len); if (hh && hh->handler(r, h, hh->offset) != NGX_OK) { return; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http header: \"%V: %V\"", &h->key, &h->value); continue; } // HEADER 解析完成 if (rc == NGX_HTTP_PARSE_HEADER_DONE) { /* a whole header has been parsed successfully */ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http header done"); r->request_length += r->header_in->pos - r->header_name_start; r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE; // 请求 HEADER 校验 rc = ngx_http_process_request_header(r); if (rc != NGX_OK) { return; } ngx_http_process_request(r); return; } if (rc == NGX_AGAIN) { /* a header line parsing is still not complete */ continue; } /* rc == NGX_HTTP_PARSE_INVALID_HEADER: "\r" is not followed by "\n" */ ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent invalid header line: \"%*s\\r...\"", r->header_end - r->header_name_start, r->header_name_start); ngx_http_finalize_request(r, NGX_HTTP_BAD_REQUEST); return; } } // }}}

 

 

他的主要工作是调用 ngx_http_parse_header_line 函数对 HEADER 进行解析

 

 

与请求行解析的过程类似,HEADER 的解析也是使用状态机的方式进行的

 

// ngx_int_t // ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b, // ngx_uint_t allow_underscores) // 请求 HEADER 的解析 {{{ ngx_int_t ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b, ngx_uint_t allow_underscores) { u_char c, ch, *p; ngx_uint_t hash, i; enum { sw_start = 0, sw_name, sw_space_before_value, sw_value, sw_space_after_value, sw_ignore_line, sw_almost_done, sw_header_almost_done } state; /* the last '\0' is not needed because string is zero terminated */ static u_char lowcase[] = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0-\0\0" "0123456789\0\0\0\0\0\0" "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0" "\0abcdefghijklmnopqrstuvwxyz\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; state = r->state; hash = r->header_hash; i = r->lowcase_index; for (p = b->pos; p < b->last; p++) { ch = *p; switch (state) { /* first char */ case sw_start: r->header_name_start = p; r->invalid_header = 0; switch (ch) { case CR: r->header_end = p; state = sw_header_almost_done; break; case LF: r->header_end = p; goto header_done; default: state = sw_name; c = lowcase[ch]; if (c) { hash = ngx_hash(0, c); r->lowcase_header[0] = c; i = 1; break; } if (ch == '_') { if (allow_underscores) { hash = ngx_hash(0, ch); r->lowcase_header[0] = ch; i = 1; } else { r->invalid_header = 1; } break; } if (ch == '\0') { return NGX_HTTP_PARSE_INVALID_HEADER; } r->invalid_header = 1; break; } break; /* header name */ case sw_name: c = lowcase[ch]; if (c) { hash = ngx_hash(hash, c); r->lowcase_header[i++] = c; i &= (NGX_HTTP_LC_HEADER_LEN - 1); break; } if (ch == '_') { if (allow_underscores) { hash = ngx_hash(hash, ch); r->lowcase_header[i++] = ch; i &= (NGX_HTTP_LC_HEADER_LEN - 1); } else { r->invalid_header = 1; } break; } if (ch == ':') { r->header_name_end = p; state = sw_space_before_value; break; } if (ch == CR) { r->header_name_end = p; r->header_start = p; r->header_end = p; state = sw_almost_done; break; } if (ch == LF) { r->header_name_end = p; r->header_start = p; r->header_end = p; goto done; } /* IIS may send the duplicate "HTTP/1.1 ..." lines */ if (ch == '/' && r->upstream && p - r->header_name_start == 4 && ngx_strncmp(r->header_name_start, "HTTP", 4) == 0) { state = sw_ignore_line; break; } if (ch == '\0') { return NGX_HTTP_PARSE_INVALID_HEADER; } r->invalid_header = 1; break; /* space* before header value */ case sw_space_before_value: switch (ch) { case ' ': break; case CR: r->header_start = p; r->header_end = p; state = sw_almost_done; break; case LF: r->header_start = p; r->header_end = p; goto done; case '\0': return NGX_HTTP_PARSE_INVALID_HEADER; default: r->header_start = p; state = sw_value; break; } break; /* header value */ case sw_value: switch (ch) { case ' ': r->header_end = p; state = sw_space_after_value; break; case CR: r->header_end = p; state = sw_almost_done; break; case LF: r->header_end = p; goto done; case '\0': return NGX_HTTP_PARSE_INVALID_HEADER; } break; /* space* before end of header line */ case sw_space_after_value: switch (ch) { case ' ': break; case CR: state = sw_almost_done; break; case LF: goto done; case '\0': return NGX_HTTP_PARSE_INVALID_HEADER; default: state = sw_value; break; } break; /* ignore header line */ case sw_ignore_line: switch (ch) { case LF: state = sw_start; break; default: break; } break; /* end of header line */ case sw_almost_done: switch (ch) { case LF: goto done; case CR: break; default: return NGX_HTTP_PARSE_INVALID_HEADER; } break; /* end of header */ case sw_header_almost_done: switch (ch) { case LF: goto header_done; default: return NGX_HTTP_PARSE_INVALID_HEADER; } } } b->pos = p; r->state = state; r->header_hash = hash; r->lowcase_index = i; return NGX_AGAIN; done: b->pos = p + 1; r->state = sw_start; r->header_hash = hash; r->lowcase_index = i; return NGX_OK; header_done: b->pos = p + 1; r->state = sw_start; return NGX_HTTP_PARSE_HEADER_DONE; } // }}}

 

 

这个函数每次执行会解析 HEADER 的一行,并返回 NGX_OK,直到整个 header 全部解析完成,则返回 NGX_HTTP_PARSE_HEADER_DONE

 

当每个 HEADER 字段解析完成后,ngx_http_parse_header_line 返回 NGX_OK

 

// 解析完成 HEADER 的一行 {{{ if (rc == NGX_OK) { r->request_length += r->header_in->pos - r->header_name_start; if (r->invalid_header && cscf->ignore_invalid_headers) { /* there was error while a header line parsing */ ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent invalid header line: \"%*s\"", r->header_end - r->header_name_start, r->header_name_start); continue; } /* a header line has been parsed successfully */ h = ngx_list_push(&r->headers_in.headers); if (h == NULL) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; } h->hash = r->header_hash; h->key.len = r->header_name_end - r->header_name_start; h->key.data = r->header_name_start; h->key.data[h->key.len] = '\0'; h->value.len = r->header_end - r->header_start; h->value.data = r->header_start; h->value.data[h->value.len] = '\0'; h->lowcase_key = ngx_pnalloc(r->pool, h->key.len); if (h->lowcase_key == NULL) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; } if (h->key.len == r->lowcase_index) { ngx_memcpy(h->lowcase_key, r->lowcase_header, h->key.len); } else { ngx_strlow(h->lowcase_key, h->key.data, h->key.len); } hh = ngx_hash_find(&cmcf->headers_in_hash, h->hash, h->lowcase_key, h->key.len); // ngx_http_process_host if (hh && hh->handler(r, h, hh->offset) != NGX_OK) { return; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http header: \"%V: %V\"", &h->key, &h->value); continue; } // }}}

 

 

这里首先在存储了所有 header 指令描述结构 ngx_http_header_t 的哈希表中找到对应的处理结构,然后执行了他的回调函数

 

在 ngx_http_block 中用下面的数组创建了一个哈希表,保存在 ngx_http_core_main_conf_t 的 headers_in_hash 中

// struct ngx_http_header_t // header 处理函数对应关系 {{{ typedef struct { ngx_str_t name; ngx_uint_t offset; ngx_http_header_handler_pt handler; } ngx_http_header_t; // }}}

 

 

// header 指令处理结构数组 {{{ ngx_http_header_t ngx_http_headers_in[] = { { ngx_string("Host"), offsetof(ngx_http_headers_in_t, host), ngx_http_process_host }, { ngx_string("Connection"), offsetof(ngx_http_headers_in_t, connection), ngx_http_process_connection }, { ngx_string("If-Modified-Since"), offsetof(ngx_http_headers_in_t, if_modified_since), ngx_http_process_unique_header_line }, { ngx_string("If-Unmodified-Since"), offsetof(ngx_http_headers_in_t, if_unmodified_since), ngx_http_process_unique_header_line }, { ngx_string("If-Match"), offsetof(ngx_http_headers_in_t, if_match), ngx_http_process_unique_header_line }, { ngx_string("If-None-Match"), offsetof(ngx_http_headers_in_t, if_none_match), ngx_http_process_unique_header_line }, { ngx_string("User-Agent"), offsetof(ngx_http_headers_in_t, user_agent), ngx_http_process_user_agent }, { ngx_string("Referer"), offsetof(ngx_http_headers_in_t, referer), ngx_http_process_header_line }, { ngx_string("Content-Length"), offsetof(ngx_http_headers_in_t, content_length), ngx_http_process_unique_header_line }, { ngx_string("Content-Type"), offsetof(ngx_http_headers_in_t, content_type), ngx_http_process_header_line }, { ngx_string("Range"), offsetof(ngx_http_headers_in_t, range), ngx_http_process_header_line }, { ngx_string("If-Range"), offsetof(ngx_http_headers_in_t, if_range), ngx_http_process_unique_header_line }, { ngx_string("Transfer-Encoding"), offsetof(ngx_http_headers_in_t, transfer_encoding), ngx_http_process_header_line }, { ngx_string("Expect"), offsetof(ngx_http_headers_in_t, expect), ngx_http_process_unique_header_line }, { ngx_string("Upgrade"), offsetof(ngx_http_headers_in_t, upgrade), ngx_http_process_header_line }, #if (NGX_HTTP_GZIP) { ngx_string("Accept-Encoding"), offsetof(ngx_http_headers_in_t, accept_encoding), ngx_http_process_header_line }, { ngx_string("Via"), offsetof(ngx_http_headers_in_t, via), ngx_http_process_header_line }, #endif { ngx_string("Authorization"), offsetof(ngx_http_headers_in_t, authorization), ngx_http_process_unique_header_line }, { ngx_string("Keep-Alive"), offsetof(ngx_http_headers_in_t, keep_alive), ngx_http_process_header_line }, #if (NGX_HTTP_X_FORWARDED_FOR) { ngx_string("X-Forwarded-For"), offsetof(ngx_http_headers_in_t, x_forwarded_for), ngx_http_process_multi_header_lines }, #endif #if (NGX_HTTP_REALIP) { ngx_string("X-Real-IP"), offsetof(ngx_http_headers_in_t, x_real_ip), ngx_http_process_header_line }, #endif #if (NGX_HTTP_HEADERS) { ngx_string("Accept"), offsetof(ngx_http_headers_in_t, accept), ngx_http_process_header_line }, { ngx_string("Accept-Language"), offsetof(ngx_http_headers_in_t, accept_language), ngx_http_process_header_line }, #endif #if (NGX_HTTP_DAV) { ngx_string("Depth"), offsetof(ngx_http_headers_in_t, depth), ngx_http_process_header_line }, { ngx_string("Destination"), offsetof(ngx_http_headers_in_t, destination), ngx_http_process_header_line }, { ngx_string("Overwrite"), offsetof(ngx_http_headers_in_t, overwrite), ngx_http_process_header_line }, { ngx_string("Date"), offsetof(ngx_http_headers_in_t, date), ngx_http_process_header_line }, #endif { ngx_string("Cookie"), offsetof(ngx_http_headers_in_t, cookies), ngx_http_process_multi_header_lines }, { ngx_null_string, 0, NULL } }; // }}}

 

 

因此,每当解析完成一个 Header 指令,就会调用相应的回调函数,进行相应的处理

这些回调函数的主要工作是校验指令内容的合法性,并且将其存储到相应的字段中

 

当 ngx_http_parse_header_line 函数返回 NGX_HTTP_PARSE_HEADER_DONE 意味着所有的 HEADER 指令已经全部解析完成

随后就要进入正式的请求处理阶段 ngx_http_process_request 了

然而,在进入 ngx_http_process_request 之前,nginx 首先调用 ngx_http_process_request_header 对 HEADER 进行了校验,比如对那些使用 http/1.1 协议但是却没有发送Host头的请求,nginx 给这些请求返回 400 错误。还有 nginx 现在的版本并不支持 chunked 格式的输入,如果某些请求申明自己使用了 chunked 格式的输入(请求带有值为 chunked 的 transfer_encoding 头部),nginx 给这些请求返回 411 错误等等

 






读书笔记      技术帖      linux      c语言      龙潭书斋      nginx      server      源码      sourcecode      http      webserver      source      ngx_http_process_request_headers      request      请求      header     


京ICP备15018585号