在nginx中request的buffer size我们能够通过两个命令进行设置,分别是large_client_header_buffers和client_header_buffer_size。这两个是有所不同的。
在解析request中,如果已经读取的request line 或者 request header大于lient_header_buffer_size的话,此时就会重新分配一块大的内存,然后将前面未完成解析的部分拷贝到当前的大的buf中,然后再进入解析处理,这部分的buf也就是large_client_header_buffers,也叫做large hader的处理。接下来我会通过代码来详细的分析这个过程。
首先来看client_header_buffer_size,他的默认值是1024,用这个值来初始化header_in(在ngx_http_init_request中)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 if (c->buffer == NULL) { //创建buffer c->buffer = ngx_create_temp_buf(c->pool, cscf->client_header_buffer_size); if (c->buffer == NULL) { ngx_http_close_connection(c); return; } } if (r->header_in == NULL) { //可以看到header_in就是上面创建的buffer,大小为client_header_buffer_size. r->header_in = c->buffer; }
然后我们来看当已经接收的request line或者request header大于设置的client_header_buffer_size的时候,nginx如果处理,这里nginx判断接收的数据大小在两个地方,一个是在处理request line,一个是处理request header时候。首先来看的是处理request line的时候,下面这段代码是在ngx_http_process_request_line中的,到达下面这里说明request line只解析了一部分,因此这里需要判断是否分配的buf已经全部使用了,如果全部使用则需要进入large header的处理部分。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 //如果pos等于 end,说明buf已经完全使用。 if (r->header_in->pos == r->header_in->end) { //进入large header处理部分. rv = ngx_http_alloc_large_header_buffer(r, 1); if (rv == NGX_ERROR) { ngx_http_close_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); return; } if (rv == NGX_DECLINED) { r->request_line.len = r->header_in->end – r->request_start; r->request_line.data = r->request_start; //返回414给客户端. ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent too long URI"); ngx_http_finalize_request(r, NGX_HTTP_REQUEST_URI_TOO_LARGE); return; } }
第二个地方就是处理request head,这部分是在ngx_http_process_request_headers中进行的,流程和上面的类似。
1 2 3 4 5 6 7 8 9 10 11 12 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; }
从上面两段代码我们可以看到处理large header的部分都是在ngx_http_alloc_large_header_buffer中。因此接下来我们就来详细的分析这个函数。
首先来看这个函数的声明
1 2 3 4 5 6 static ngx_int_t ngx_http_alloc_large_header_buffer(ngx_http_request_t *r, ngx_uint_t request_line)
可以看到它的第二个函数表示了是在处理request line还是说是request header。我们来一段段的分析这个函数。
下面这段首先判断如果是在处理完request_line并且状态为0,则说明用户的request line的第一行是空(注释里面写的比较详细),此时我们需要忽略这个回车换行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 if (request_line && r->state == 0) { /\* the client fills up the buffer with "\r\n" \*/ r->request_length += r->header_in->end – r->header_in->start; r->header_in->pos = r->header_in->start; r->header_in->last = r->header_in->start; return NGX_OK; }
然后接下来这部分就是判断已经解析完毕的request line或者header的大小是否大于large_client_header_buffers的大小,如果大于则说明当前的request 太长,所以直接返回。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 old = request_line ? r->request_start : r->header_name_start; cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); //state不为0,则说明request未解析完毕,此时如果已经解析完毕的部分大小太大,则直接返回. if (r->state != 0 && (size_t) (r->header_in->pos – old) >= cscf->large_client_header_buffers.size) { return NGX_DECLINED; }
接下来这部分就是准备分配新的内存供request使用。这里有一个http_connection的概念,在nginx中,这个主要用于pipeline请求和keepalive请求,等以后详细分析pipeline和keepalive的时候会涉及到这个东西,现在只需要知道这个东西主要是为了buf的重用来设计的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 //取得http_connection. hc = r->http_connection; //如果有已经被free,也就是空闲的buf,则我们就不用重新分配 if (hc->nfree) { //直接取得buf。 b = hc->free[–hc->nfree]; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http large header free: %p %uz", b->pos, b->end – b->last); } else if (hc->nbusy < cscf->large_client_header_buffers.num) { //否则如果large_client_header_buffers的没有被使用完毕,则我们重新alloc新的buf. if (hc->busy == NULL) { hc->busy = ngx_palloc(r->connection->pool, cscf->large_client_header_buffers.num \* sizeof(ngx_buf_t \*)); if (hc->busy == NULL) { return NGX_ERROR; } } //创建新的buf b = ngx_create_temp_buf(r->connection->pool, cscf->large_client_header_buffers.size); if (b == NULL) { return NGX_ERROR; } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http large header alloc: %p %uz", b->pos, b->end – b->last); } else { //否则直接返回。 return NGX_DECLINED; }
下面这段就开始复制request buf.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 //这里nbusy作为一个计数,用来限制large_client_header_buffers中限制的buf个数 hc->busy[hc->nbusy++] = b; if (r->state == 0) { /* * r->state == 0 means that a header line was parsed successfully * and we do not need to copy incomplete header line and * to relocate the parser header pointers */ r->request_length += r->header_in->end – r->header_in->start; r->header_in = b; return NGX_OK; } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http large header copy: %d", r->header_in->pos – old); //设置大小 r->request_length += old – r->header_in->start; //指向新的buf new = b->start; //开始复制 ngx_memcpy(new, old, r->header_in->pos – old); //更新buf b->pos = new + (r->header_in->pos – old); b->last = new + (r->header_in->pos – old);
最后一部分就是更新对应的指针
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 if (request_line) { r->request_start = new; if (r->request_end) { r->request_end = new + (r->request_end – old); } r->method_end = new + (r->method_end – old); r->uri_start = new + (r->uri_start – old); r->uri_end = new + (r->uri_end – old); if (r->schema_start) { r->schema_start = new + (r->schema_start – old); r->schema_end = new + (r->schema_end – old); } if (r->host_start) { r->host_start = new + (r->host_start – old); if (r->host_end) { r->host_end = new + (r->host_end – old); } } if (r->port_start) { r->port_start = new + (r->port_start – old); r->port_end = new + (r->port_end – old); } if (r->uri_ext) { r->uri_ext = new + (r->uri_ext – old); } if (r->args_start) { r->args_start = new + (r->args_start – old); } if (r->http_protocol.data) { r->http_protocol.data = new + (r->http_protocol.data – old); } } else { r->header_name_start = new; r->header_name_end = new + (r->header_name_end – old); r->header_start = new + (r->header_start – old); r->header_end = new + (r->header_end – old); } r->header_in = b;