ngx_http_read_client_request_body
HTTP包体的长度有可能非常大,如果试图一次性调用并读取完所有的包体,那么多半会阻塞Nginx进程。HTTP框架提供了一种方法来异步地接收包体:
ngx_int_t ngx_http_read_client_request_body(ngx_http_request_t *r, ngx_http_client_body_handler_pt post_handler);ngx_http_read_client_request_body是一个异步方法,调用它只是说明要求Nginx开始接收请求的包体,并不表示是否已经接收完,当接收完所有的包体内容后,post_handler指向的回调方法会被调用。因此,即使在调用了ngx_http_read_client_request_body方法后它已经返回,也无法确定这时是否已经调用过post_handler指向的方法。换句话说,ngx_http_read_client_request_body返回时既有可能已经接收完请求中所有的包体(假如包体的长度很小),也有可能还没开始接收包体。如果ngx_http_read_client_request_body是在ngx_http_mytest_handler处理方法中调用的,那么后者一般要返回NGX_DONE,因为下一步就是将它的返回值作为参数传给ngx_http_finalize_request。NGX_DONE的意义如下:
NGX_DONE:表示到此为止,同时HTTP框架将暂时不再继续执行这个请求的后续部分。事实上,这时会检查连接的类型,如果是keepalive类型的用户请求,就会保持住HTTP连接,然后把控制权交给Nginx。这个返回码很有用,考虑以下场景:在一个请求中我们必须访问一个耗时极长的操作(比如某个网络调用),这样会阻塞住Nginx,又因为我们没有把控制权交还给Nginx,而是在ngx_http_mytest_handler中让Nginx worker进程休眠了(如等待网络的回包),所以,这就会导致Nginx出现性能问题,该进程上的其他用户请求也得不到响应。可如果我们把这个耗时极长的操作分为上下两个部分(就像Linux内核中对中断处理的划分),上半部分和下半部分都是无阻塞的(耗时很少的操作),这样,在ngx_http_mytest_handler进入时调用上半部分,然后返回NGX_DONE,把控制交还给Nginx,从而让Nginx继续处理其他请求。在下半部分被触发时(这里不探讨具体的实现方式,事实上使用upstream方式做反向代理时用的就是这种思想),再回调下半部分处理方法,这样就可以保证Nginx的高性能特性了。如果需要彻底了解NGX_DONE的意义,那么必须学习第11章内容,其中还涉及请求的引用计数内容。
下面看一下包体接收完毕后的回调方法原型ngx_http_client_body_handler_pt是如何定义的:
typedef void (*ngx_http_client_body_handler_pt)(ngx_http_request_t *r);
其中,有参数ngx_http_request_t *r,这个请求的信息都可以从r中获得。这样可以定义一个方法void func(ngx_http_request_t *r),在Nginx接收完包体时调用它,另外,后续的流程也都会写在这个方法中,例如:
void ngx_http_mytest_body_handler(ngx_http_request_t *r)
{
…
}
注意 ngx_http_mytest_body_handler的返回类型是void,Nginx不会根据返回值做一些收尾工作,因此,我们在该方法里处理完请求时必须要主动调用ngx_http_finalize_request方法来结束请求。
接收包体时可以这样写:
ngx_int_t rc = ngx_http_read_client_request_body(r, ngx_http_mytest_body_handler);
if (rc >= NGX_http_SPECIAL_RESPONSE) {
return rc;
}
return NGX_DONE;
Nginx异步接收HTTP请求的包体的内容将在11.8节中详述。
如果不想处理请求中的包体,那么可以调用ngx_http_discard_request_body方法将接收自客户端的HTTP包体丢弃掉。例如:
ngx_int_t rc = ngx_http_discard_request_body(r);
if (rc != NGX_OK) {
return rc;
}
ngx_http_discard_request_body只是丢弃包体,不处理包体不就行了吗?何必还要调用ngx_http_discard_request_body方法呢?其实这一步非常有意义,因为有些客户端可能会一直试图发送包体,而如果HTTP模块不接收发来的TCP流,有可能造成客户端发送超时。
接收完请求的包体后,可以在r->request_body->temp_file->file中获取临时文件(假定将r->request_body_in_file_only标志位设为1,那就一定可以在这个变量获取到包体。更复杂的接收包体的方式本节暂不讨论)。file是一个ngx_file_t类型。这里,我们可以从r->request_body->temp_file->file.name中获取Nginx接收到的请求包体所在文件的名称(包括路径)。
返回值的意义
每个阶段都有一个与之相关的handler的列表。一旦把handler注册到对应的阶段,那么handler就会返回某个下面的值:
NGX_OK:请求已经成功处理,请求将会传到下一个阶段。
NGX_DECLINED:请求需要被转发到本阶段的下一个handler
NGX_AGAIN,NGX_DONE:请求已经被正确处理,同时请求被挂起,直到某个事件(子请求结束、socket可写或超时等)到来,handler才会再次被调用。
//函数功能:解析请求行。
static void ngx_http_process_request_line(ngx_event_t *rev)
{
。。。
rc = NGX_AGAIN;
for ( ;; ) {
if (rc == NGX_AGAIN) { // NO.1
n = ngx_http_read_request_header(r);
//recv()读取请求头信息
if (n == NGX_AGAIN || n == NGX_ERROR) {
return;
}
}
//记录请求行中的请求方法(Method),请求uri以及http协议版本在缓冲区中的起始位置
rc = ngx_http_parse_request_line(r, r->header_in);
if (rc == NGX_OK) {
…
c->log->action = "reading client request headers";
rev->handler = ngx_http_process_request_headers;
ngx_http_process_request_headers(rev);
return;
}
。。。
}
}
//函数功能:处理HTTP请求头。
static void ngx_http_process_request_headers(ngx_event_t *rev)
{
。。。
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;
}
。。。
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);
。。。
}
n = ngx_http_read_request_header(r); //快速返回了,因为已经读过
if (n == NGX_AGAIN || n == NGX_ERROR) {
return;
}
}
rc = ngx_http_parse_header_line(r, r->header_in,
cscf->underscores_in_headers); //解析HTTP头部
if (rc == NGX_OK) {
if (r->invalid_header && cscf->ignore_invalid_headers) {
。。。
//以key 和value的格式保存HTTP头
h = ngx_list_push(&r->headers_in.headers); //ngx_http_headers_in_t ,存放key,value
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; // "User-Agent",host
h->key.data[h->key.len] = '\0';
h->value.len = r->header_end - r->header_start;
h->value.data = r->header_start;
。。。
if (hh && hh->handler(r, h, hh->offset) != NGX_OK) { //检验函数
return;
}
continue;
}
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_in->start;
r->http_state = NGX_HTTP_PROCESS_REQUEST_STATE;
rc = ngx_http_process_request_header(r); //检验头信息如1.1协议需要host信息?
if (rc != NGX_OK) {
return;
}
ngx_http_process_request(r);
return;
}
。。。。
}
}
//函数功能:处理请求。
static voidngx_http_process_request(ngx_http_request_t *r)
{
。。。
if (c->read->timer_set) { // 超时,过了一会儿,就在这里了
ngx_del_timer(c->read); //here
}
c->read->handler = ngx_http_request_handler;
c->write->handler = ngx_http_request_handler;
r->read_event_handler = ngx_http_block_reading;
ngx_http_handler(r); //核心是ngx_http_core_run_phases,进入各阶段处理函数
ngx_http_run_posted_requests(c);
}