3.6 处理用户请求

本节介绍如何处理一个实际的HTTP请求。回顾一下上文,在出现mytest配置项时,ngx_http_mytest方法会被调用,这时将ngx_http_core_loc_conf_t结构的handler成员指定为ngx_http_mytest_handler,另外,HTTP框架在接收完HTTP请求的头部后,会调用handler指向的方法。下面看一下handler成员的原型ngx_http_handler_pt:


typedef ngx_int_t(ngx_http_handler_pt)(ngx_http_request_tr);


从上面这段代码可以看出,实际处理请求的方法ngx_http_mytest_handler将接收一个ngx_http_request_t类型的参数r,返回一个ngx_int_t(参见3.2.1节)类型的结果。下面先探讨一下ngx_http_mytest_handler方法可以返回什么,再看一下参数r包含了哪些Nginx已经解析完的用户请求信息。

3.6.1 处理方法的返回值

这个返回值可以是HTTP中响应包的返回码,其中包括了HTTP框架已经在/src/http/ngx_http_request.h文件中定义好的宏,如下所示。


define NGX_HTTP_OK

200

define NGX_HTTP_CREATED

201

define NGX_HTTP_ACCEPTED

202

define NGX_HTTP_NO_CONTENT

204

define NGX_HTTP_PARTIAL_CONTENT

206

define NGX_HTTP_SPECIAL_RESPONSE

300

define NGX_HTTP_MOVED_PERMANENTLY

301

define NGX_HTTP_MOVED_TEMPORARILY

302

define NGX_HTTP_SEE_OTHER

303

define NGX_HTTP_NOT_MODIFIED

304

define NGX_HTTP_TEMPORARY_REDIRECT

307

define NGX_HTTP_BAD_REQUEST

400

define NGX_HTTP_UNAUTHORIZED

401

define NGX_HTTP_FORBIDDEN

403

define NGX_HTTP_NOT_FOUND

404

define NGX_HTTP_NOT_ALLOWED

405

define NGX_HTTP_REQUEST_TIME_OUT

408

define NGX_HTTP_CONFLICT

409

define NGX_HTTP_LENGTH_REQUIRED

411

define NGX_HTTP_PRECONDITION_FAILED

412

define NGX_HTTP_REQUEST_ENTITY_TOO_LARGE

413

define NGX_HTTP_REQUEST_URI_TOO_LARGE

414

define NGX_HTTP_UNSUPPORTED_MEDIA_TYPE

415

define NGX_HTTP_RANGE_NOT_SATISFIABLE

416

/The special code to close connection without any response/

define NGX_HTTP_CLOSE

444

define NGX_HTTP_NGINX_CODES

494

define NGX_HTTP_REQUEST_HEADER_TOO_LARGE

494

define NGX_HTTPS_CERT_ERROR

495

define NGX_HTTPS_NO_CERT

496

define NGX_HTTP_TO_HTTPS

497

define NGX_HTTP_CLIENT_CLOSED_REQUEST

499

define NGX_HTTP_INTERNAL_SERVER_ERROR

500

define NGX_HTTP_NOT_IMPLEMENTED

501

define NGX_HTTP_BAD_GATEWAY

502

define NGX_HTTP_SERVICE_UNAVAILABLE

503

define NGX_HTTP_GATEWAY_TIME_OUT

504

define NGX_HTTP_INSUFFICIENT_STORAGE

507


注意 以上返回值除了RFC2616规范中定义的返回码外,还有Nginx自身定义的HTTP返回码。例如,NGX_HTTP_CLOSE就是用于要求HTTP框架直接关闭用户连接的。

在ngx_http_mytest_handler的返回值中,如果是正常的HTTP返回码,Nginx就会按照规范构造合法的响应包发送给用户。例如,假设对于PUT方法暂不支持,那么,在处理方法中发现方法名是PUT时,返回NGX_HTTP_NOT_ALLOWED,这样Nginx也就会构造类似下面的响应包给用户。


http/1.1 405 Not Allowed

Server:nginx/1.0.14

Date:Sat,28 Apr 2012 06:07:17 GMT

Content-Type:text/html

Content-Length:173

Connection:keep-alive

<html>

<head><title>405 Not Allowed</title></head>

<body bgcolor="white">

<center><h1>405 Not Allowed</h1></center>

<hr><center>nginx/1.0.14</center>

</body>

</html>


在处理方法中除了返回HTTP响应码外,还可以返回Nginx全局定义的几个错误码,包括:


define NGX_OK

0

define NGX_ERROR

-1

define NGX_AGAIN

-2

define NGX_BUSY

-3

define NGX_DONE

-4

define NGX_DECLINED

-5

define NGX_ABORT

-6


这些错误码对于Nginx自身提供的大部分方法来说都是通用的。所以,当我们最后调用ngx_http_output_filter(参见3.7节)向用户发送响应包时,可以将ngx_http_output_filter的返回值作为ngx_http_mytest_handler方法的返回值使用。例如:


static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t*r)

{

……

ngx_int_t rc=ngx_http_send_header(r);

if(rc==NGX_ERROR||rc>NGX_OK||r->header_only){

return rc;

}

return ngx_http_output_filter(r,&out);

}


当然,直接返回以上7个通用值也是可以的。在不同的场景下,这7个通用返回值代表的含义不尽相同。在mytest的例子中,HTTP框架在NGX_HTTP_CONTENT_PHASE阶段调用ngx_http_mytest_handler后,会将ngx_http_mytest_handler的返回值作为参数传给ngx_http_finalize_request方法,如下所示。


if(r->content_handler){

r->write_event_handler=ngx_http_request_empty_handler;

ngx_http_finalize_request(r,r->content_handler(r));

return NGX_OK;

}


上面的r->content_handler会指向ngx_http_mytest_handler处理方法。也就是说,事实上ngx_http_finalize_request决定了ngx_http_mytest_handler如何起作用。本章不探讨ngx_http_finalize_request的实现(详见11.10节),只简单地说明一下4个通用返回码,另外,在11.10节中介绍这4个返回码引发的Nginx一系列动作。

❑NGX_OK:表示成功。Nginx将会继续执行该请求的后续动作(如执行subrequest或撤销这个请求)。

❑NGX_DECLINED:继续在NGX_HTTP_CONTENT_PHASE阶段寻找下一个对于该请求感兴趣的HTTP模块来再次处理这个请求。

❑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_ERROR:表示错误。这时会调用ngx_http_terminate_request终止请求。如果还有POST子请求,那么将会在执行完POST请求后再终止本次请求。