分享

nginx中request buf的设计与实现

 enrol 2012-02-19

nginx中request buf的设计和实现

WP Greet Box icon
Hello there! If you are new here, you might want to subscribe to the RSS feed for updates on this topic.

原创文章,转载请注明: 转载自pagefault

本文链接地址: nginx中request buf的设计和实现

在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
    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
//如果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
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
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
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
    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
//取得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
//这里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
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;
Share

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多