分析nginx是如何解析并且存储http请求的。对非法甚至恶意请求的识别能力和处理方式。可以发现nginx采用状态机来解析http协议,有一定容错能力,但并不全面
相关配置
跟解码有关的配置
merge_slashes
语法merge_slashes on | off
默认值on
上下文http server
说明支持解析请求行时,合并相邻的斜线。例如,,一个请求 将生成如下$uri 值: on: /foo/bar/ off: /foo//bar/要知道,静态location匹配是一个字符串比较,所以如果merge_slashes关闭, 一个类似/foo//bar/的请求将不会匹配location /foo/bar/.在HttpCoreModule中
underscores_in_headers
语法underscores_in_headers on | off
默认值Off
上下文http server
说明允许或者不允许headers中的下划线
ignore_invalid_headers
语法ignore_invalid_headers on | off
默认值On
上下文http server
说明控制是否有无效name的header应该被忽略。 有效的名字是由数字 字母 连字符- 可能有下划线组成, 前后都不能有空格。如果该指令在sever级别被指定,它的值仅当server是默认的那个才使用。 指定的值被应用到所有监听同样的地址和端口的虚拟主机上。
请求体有关配置
client_body_buffer_size
语法client_body_buffer_size size
默认值8k|16k
上下文http server locatioin
说明指定client request body buffer大小如果request body大小超过buffer大小,那么整个请求体会写入临时文件。默认大小是page 大小2倍。 根据不同平台,可能为8K或者16K。当content-length请求头指定了比buffer size较小的值,那么nginx会使用较小的那个。结果就是,nginx将不会给每个请求分配一个这个size大小的buffer
client_body_in_single_buffer
语法client_body_in_single_buffer on | off
默认值Off
上下文http server location
说明该指令制定是否保持这整个body在一个client request buffer中。 当使用变量$request_body来减少copy操作时,这个指令是推荐的。注意到,当请求体不能保存在一个buffer中时(看client_body_buffer_size),这个body将存到磁盘上。
client_body_in_file_only
语法client_body_in_file_only on | clean | off
默认值off
上下文http server location
说明这个指令强制nginx总是将请求体存入临时磁盘文件,甚至即使这个请求体size为0请注意,如果指令是on,文件在请求完成后也不会移除该指令可以用于debug,和嵌入的perl模块中$r->request_body_file 方法
数据结构
解码的所有结果都保存在request结构里
ngx_http_request_t {ngx_but_t *header_in; // buf,保存请求Ngx_http_headers_in_t headers_in; //链表,保存请求中的请求头Ngx_http_headers_out_t headers_out; //链表,保存response中的响应头。。。}
保存请求头的结构
//Ngx_http_headers_in_t 结构
typedef struct {ngx_list_theaders;ngx_table_elt_t*host;ngx_table_elt_t*connection;。。。ngx_str_tuser;ngx_str_tpasswd;。。。ngx_array_tcookies;ngx_str_tserver;off_tcontent_length_n;time_tkeep_alive_n;unsignedconnection_type:2;unsignedchunked:1;unsignedmsie:1;unsignedmsie6:1;unsignedopera:1;unsignedgecko:1;unsignedchrome:1;unsignedsafari:1;unsignedkonqueror:1;} ngx_http_headers_in_t;解码流程
ngx_http_process_request_line(ngx_event_t *rev) //请求行 解码总入口ngx_http_process_request_headers(ngx_event_t *rev) //请求头解码入口所有请求头的handler在ngx_http_request.c:ngx_http_headers_in中 例如:Host头的handler是 ngx_http_process_hostngx_http_process_host功能是,验证host有效性,查找virtual server,找到对应的server配置。请求初始化ngx_http_request_t *ngx_http_create_request(ngx_connection_t *c)r->header_in = hc->nbusy ? hc->busy[0] : c->buffer;r->http_state = NGX_HTTP_READING_REQUEST_STATE;解码请求行ngx_http_process_request_line(ngx_event_t *rev){ ngx_http_read_request_header(r); //从连接中读取内容,放到header_in buf中,返回读取字节数,或错误码 ngx_http_parse_request_line(r, r->header_in); // 状态机解析请求行,将method schema host port uri protocol version 分离出来 } 解析状态机图如下:
CR=’\r’LF=’\n’
请求行 对应的正则
请求行对应的正则
Method([A-Z_]+)
Schema([a-zA-Z]+)
Host (\[[a-zA-Z0-9:._~!$&\\(\)*+,;=-]*\]|[a-zA-Z0-9.-]*)
Port[0-9]*)?
Uri.%/?#+ 不能是’\0’ /(([^CRLF.%/?#+\0 ]+/)*(([^CRLF.%/?#+\0 ]+[%?#]|.%/?#)[^ CRLFH\0]*)?)?(?=CR|LF| +H)
Protocol versionHTTP/[1-9]+\.[0-9]+(?=( *)CR?LF)
ngx_http_process_request_uri //解析uri的函数,对自特殊字符进行了一些处理
定义:
Complex_uri: uri with “/.#” Quoted_uri: uri with “%”Plus_in_uri: uri with “+”Space_in_uri: uri with “ ”
Uri_ext: 最后一截uri, 非紧邻/的.后面的部分
发光并非太阳的专利,你也可以发光