`

nginx源码分析之http解码实现

阅读更多

分析nginx是如何解析并且存储http请求的。对非法甚至恶意请求的识别能力和处理方式。可以发现nginx采用状态机来解析http协议,有一定容错能力,但并不全面

相关配置

 

跟解码有关的配置

 

merge_slashes  
语法 merge_slashes on | off
默认值 on
上下文 http server
说明 支持解析请求行时,合并相邻的斜线。例如,一个请求 http://www.example.com/foo//bar/ 将生成如下$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结构里

 

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. ngx_http_request_t {  
  2. ngx_but_t  *header_in;   //  buf,保存请求  
  3. Ngx_http_headers_in_t headers_in;   //链表,保存请求中的请求头  
  4. Ngx_http_headers_out_t headers_out;  //链表,保存response中的响应头  
  5. 。。。  
  6. }  

 

 

保存请求头的结构

//Ngx_http_headers_in_t 结构

 

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. typedef struct {  
  2.     ngx_list_t                        headers;  
  3.     ngx_table_elt_t                  *host;  
  4.     ngx_table_elt_t                  *connection;  
  5. 。。。      
  6.     ngx_str_t                         user;  
  7.     ngx_str_t                         passwd;  
  8. 。。。  
  9.     ngx_array_t                       cookies;  
  10.   
  11.     ngx_str_t                         server;  
  12.     off_t                             content_length_n;  
  13.     time_t                            keep_alive_n;  
  14.   
  15.     unsigned                          connection_type:2;  
  16.     unsigned                          chunked:1;  
  17.     unsigned                          msie:1;  
  18.     unsigned                          msie6:1;  
  19.     unsigned                          opera:1;  
  20.     unsigned                          gecko:1;  
  21.     unsigned                          chrome:1;  
  22.     unsigned                          safari:1;  
  23.     unsigned                          konqueror:1;  
  24. } 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_host
ngx_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;

解码请求行

 

 

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. ngx_http_process_request_line(ngx_event_t *rev)  
  2. {  
  3.   ngx_http_read_request_header(r);  //从连接中读取内容,放到header_in buf中,返回读取字节数,或错误码  
  4.     
  5.     
  6.   ngx_http_parse_request_line(r, r->header_in);  
  7.   // 状态机解析请求行,将method  schema host port uri  protocol version 分离出来  
  8.   }  



 


  解析状态机图如下:
  


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 version HTTP/[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, 非紧邻/的.后面的部分

 

逻辑

 

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. if (r->uri_ext) {  
  2.         if (r->args_start) {  
  3.             r->exten.len = r->args_start - 1 - r->uri_ext;  
  4.         } else {  
  5.             r->exten.len = r->uri_end - r->uri_ext;  
  6.         }  
  7.         r->exten.data = r->uri_ext;  
  8. }  


如果含有/.#%, 那么会给uri重新分配内存,并解析uri

 

 

[cpp] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. if (r->complex_uri || r->quoted_uri) {  
  2. r->uri.data = ngx_pnalloc(r->pool, r->uri.len + 1);    
  3. ngx_http_parse_complex_uri(r, cscf->merge_slashes)  
  4. }  

 

uri解析的状态机图

请求行URI特殊字符处理


Nginx不支持第3部分, 不支持@

这里指的是6 7 8部分

 

% %后面必须是两个十六进制数字,否则报错 NGX_HTTP_PARSE_INVALID_REQUEST
/ 相邻重复的斜线,可以合并,有相关配置控制 (merge_slashes on/off控制)
/../ 会进入上一级目录.例如 /foo/bar/../abc  会变成 /foo/abc/../前面必须有一级,否则会报错,  不会退到第一级/前面
. Uri中的点,在最后一个/后面,而且不紧跟在/后面, 会是r->uri_ext的起始位置, 结束位置在args前面,或者uri结尾
# 表示uri的结尾,#后面的全部忽略。  如果没有#, 那么uri的结尾会以 ( *(CR?LF)| H)结束
问号到#号之间认为都是参数,如果?后没有#,那么问号后都是参数
+ 如果遇到+  r->plus_in_uri=1
\r 后面必须接\n,  表示请求行结束
\n 表示请求行结束

 

 

 

HTTP0.9的支持

支持http 0.9

如果是协议版本小于1, 那么不会读取请求头

支持的方法

方法 
GET 

PUT 

POST 

COPY 
MOVE 
LOCK 
HEAD 
MKCOL 

PATCH

TRACE   NGX_HTTP_NOT_ALLOWED error code 405
DELETE 
UNLOCK 
OPTIONS
PROPFIND 

PROPPATCH

 

注意:这里说的支持,是说nginx解码时,能够识别这些方法。但是在后续的处理过程中不一定支持。例如,nginx遇到TRACE方法,会返回405 not allowed

未知的方法: 如果METHOD字符集符合 [A-B_]+, 会将请求放过去,交给后续处理
如果不符合[A-B_]+, 会报错误 400 bad request


解码请求头

ngx_http_process_request_headers(ngx_event_t *rev)

ngx_http_read_request_header // 从网络连接中读取内容
ngx_http_parse_header_line(r, r->header_in, cscf->underscores_in_headers);

请求头 对应的正则
Name    [0-9a-zA-Z-]? 最大长度32, 非法字符被忽略,超过32个的从头覆盖
_是否非法,看allow_underscores配置

value    [^ CRLF\0]+ 没有长度限制,以CRLF结尾

 

请求头解析的状态机图





非法头部处理

Header name中包含非法字符,则认为是非法头部,nginx默认抛弃这一行,另外也可由配置ignore_invalid_headers 确定

相同头部处理

策略一,第二个以上相同头部忽略 ngx_http_process_header_line
策略二,如果重复,返回400错误 ngx_http_process_unique_header_line
策略三,允许多头存在,用数组保存 ngx_http_process_multi_header_lines eg. X-Forwarded-For Cookie

策略四, 使用最靠后的头

 

请求头部 策略    头部类型和值
Connection 策略四 General如果http版本大于1,那么默认为keep-alive, 否则为close 。 只能有close 或keep-alive两种情况,否则报错
Host 策略一 Request headerhttp 1.0以上版本,host不能为空,否则报错
User-Agent 策略一 Request headerNginx会去识别是否如下六种(Msie msie6)  opera gecko chrome Safari Konqueror
Referer 策略一 Request header
Content-Type 策略一 Entity header指明发给接受者的实体主体的媒体类型,或HEAD方法中指明若请求为GET时将发送的媒体类型
Range 策略一 Request header
Transfer-Encoding 策略一 general
Upgrade 策略一 general
Accept-Encoding 策略一 Request header
X-Real-IP 策略一  
Accept 策略一 Request header
Accept-Language 策略一 Request header
Depth 策略一  
Destination 策略一  
Overwrite 策略一  
Date 策略一 general
Via   general
Keep-Alive 策略一  
If-Modified-Since 策略二 Request header
If-Unmodified-Since 策略二 Request header
If-Match 策略二 Request header
If-None-Match 策略二 Request header
If-Range 策略二 Request header
Expect 策略二 Request header
Content-Length 策略二 Entity header  必须为数字,必须为正数ngx_http_process_request_header
Authorization 策略二 Request header
X-Forwarded-For 策略三  
Cookie 策略三  
额外的头 头部都会存在list里,怎么处理自己定义  

 

 

 

 

冲突或关联头部处理

transfer_encoding content-length
在ngx_http_process_request_header中,当transfer-encoding value 为chunked content-length会失效。Transfer-encoding值只能为identity或chunked,否则报错


Connection keep-alive
如果connection 为keep-alive,那么keep-alive的值会生效
如果请求头connection没有指明为close, 且http版本大于1, 那么connection默认为keep-alive.

不支持多行请求头

由于 HTTP/1.1 协议里的规定,所以实际上 HTTP 协议支持多行请求头,它规定任何一个以空格开头的行,都是接续在前一行之后的内容。例如:
X-Random-Comment: 这是个长句子,
所以我们得换行处理一下,看着更整齐。

Nginx不支持多行请求头

Cookies处理
Nginx没有继续解析cookies值
https://github.com/cloudflare/lua-resty-cookie


解码请求体

nginx核心本身不会主动读取请求体,这个工作是交给请求处理阶段的模块来做,但是nginx核心提供了ngx_http_read_client_request_body()接口来读取请求体,另外还提供了一个丢弃请求体的接口-ngx_http_discard_request_body(),在请求执行的各个阶段中,任何一个阶段的模块如果对请求体感兴趣或者希望丢掉客户端发过来的请求体,可以分别调用这两个接口来完成。这两个接口是nginx核心提供的处理请求体的标准接口,如果希望配置文件中一些请求体相关的指令(比如client_body_in_file_only,client_body_buffer_size等)能够预期工作,以及能够正常使用nginx内置的一些和请求体相关的变量(比如$request_body和$request_body_file),一般来说所有模块都必须调用这些接口来完成相应操作,如果需要自定义接口来处理请求体,也应尽量兼容nginx默认的行为。

 

请求体的读取一般发生在nginx的content handler中,一些nginx内置的模块,比如proxy模块,fastcgi模块,uwsgi模块等,这些模块的行为必须将客户端过来的请求体(如果有的话)以相应协议完整的转发到后端服务进程,所有的这些模块都是调用了ngx_http_read_client_request_body()接口来完成请求体读取。值得注意的是这些模块会把客户端的请求体完整的读取后才开始往后端转发数据。

ngx_http_discard_request_body

使用接口



可用变量

 

以下仅为举例,不全

$http_xxxx 类型的变量, xxxx为header name 连接符变成下划线,大写字母变成小写字母

X-Real-IP 变量为 $http_x_real_ip

 

类似的有

$args_XXXX

$cookie_XXXX

 

$uri

$request_body
这个变量包含请求体。这个变量出现在proxy或fastcgi_pass所在的location
$request_body_file
Client请求体临时文件

转自:http://blog.csdn.net/liujiyong7/article/details/44833475

分享到:
评论

相关推荐

    pandas-1.3.5-cp37-cp37m-macosx_10_9_x86_64.zip

    pandas whl安装包,对应各个python版本和系统(具体看资源名字),找准自己对应的下载即可! 下载后解压出来是已.whl为后缀的安装包,进入终端,直接pip install pandas-xxx.whl即可,非常方便。 再也不用担心pip联网下载网络超时,各种安装不成功的问题。

    基于java的大学生兼职信息系统答辩PPT.pptx

    基于java的大学生兼职信息系统答辩PPT.pptx

    基于java的乐校园二手书交易管理系统答辩PPT.pptx

    基于java的乐校园二手书交易管理系统答辩PPT.pptx

    tornado-6.4-cp38-abi3-musllinux_1_1_i686.whl

    tornado-6.4-cp38-abi3-musllinux_1_1_i686.whl

    Android Studio Ladybug(android-studio-2024.2.1.10-mac.zip.002)

    Android Studio Ladybug 2024.2.1(android-studio-2024.2.1.10-mac.dmg)适用于macOS Intel系统,文件使用360压缩软件分割成两个压缩包,必须一起下载使用: part1: https://download.csdn.net/download/weixin_43800734/89954174 part2: https://download.csdn.net/download/weixin_43800734/89954175

    基于ssm框架+mysql+jsp实现的监考安排与查询系统

    有学生和教师两种角色 登录和注册模块 考场信息模块 考试信息模块 点我收藏 功能 监考安排模块 考场类型模块 系统公告模块 个人中心模块: 1、修改个人信息,可以上传图片 2、我的收藏列表 账号管理模块 服务模块 eclipse或者idea 均可以运行 jdk1.8 apache-maven-3.6 mysql5.7及以上 tomcat 8.0及以上版本

    tornado-6.1b2-cp38-cp38-macosx_10_9_x86_64.whl

    tornado-6.1b2-cp38-cp38-macosx_10_9_x86_64.whl

    Android Studio Ladybug(android-studio-2024.2.1.10-mac.zip.001)

    Android Studio Ladybug 2024.2.1(android-studio-2024.2.1.10-mac.dmg)适用于macOS Intel系统,文件使用360压缩软件分割成两个压缩包,必须一起下载使用: part1: https://download.csdn.net/download/weixin_43800734/89954174 part2: https://download.csdn.net/download/weixin_43800734/89954175

    基于MATLAB车牌识别代码实现代码【含界面GUI】.zip

    matlab

    基于java的毕业生就业信息管理系统答辩PPT.pptx

    基于java的毕业生就业信息管理系统答辩PPT.pptx

    基于Web的毕业设计选题系统的设计与实现(springboot+vue+mysql+说明文档).zip

    随着高等教育的普及和毕业设计的日益重要,为了方便教师、学生和管理员进行毕业设计的选题和管理,我们开发了这款基于Web的毕业设计选题系统。 该系统主要包括教师管理、院系管理、学生管理等多个模块。在教师管理模块中,管理员可以新增、删除教师信息,并查看教师的详细资料,方便进行教师资源的分配和管理。院系管理模块则允许管理员对各个院系的信息进行管理和维护,确保信息的准确性和完整性。 学生管理模块是系统的核心之一,它提供了学生选题、任务书管理、开题报告管理、开题成绩管理等功能。学生可以在此模块中进行毕业设计的选题,并上传任务书和开题报告,管理员和教师则可以对学生的报告进行审阅和评分。 此外,系统还具备课题分类管理和课题信息管理功能,方便对毕业设计课题进行分类和归档,提高管理效率。在线留言功能则为学生、教师和管理员提供了一个交流互动的平台,可以就毕业设计相关问题进行讨论和解答。 整个系统设计简洁明了,操作便捷,大大提高了毕业设计的选题和管理效率,为高等教育的发展做出了积极贡献。

    机器学习(预测模型):2000年至2015年期间193个国家的预期寿命和相关健康因素的数据

    这个数据集来自世界卫生组织(WHO),包含了2000年至2015年期间193个国家的预期寿命和相关健康因素的数据。它提供了一个全面的视角,用于分析影响全球人口预期寿命的多种因素。数据集涵盖了从婴儿死亡率、GDP、BMI到免疫接种覆盖率等多个维度,为研究者提供了丰富的信息来探索和预测预期寿命。 该数据集的特点在于其跨国家的比较性,使得研究者能够识别出不同国家之间预期寿命的差异,并分析这些差异背后的原因。数据集包含22个特征列和2938行数据,涉及的变量被分为几个大类:免疫相关因素、死亡因素、经济因素和社会因素。这些数据不仅有助于了解全球健康趋势,还可以辅助制定公共卫生政策和社会福利计划。 数据集的处理包括对缺失值的处理、数据类型转换以及去重等步骤,以确保数据的准确性和可靠性。研究者可以使用这个数据集来探索如教育、健康习惯、生活方式等因素如何影响人们的寿命,以及不同国家的经济发展水平如何与预期寿命相关联。此外,数据集还可以用于预测模型的构建,通过回归分析等统计方法来预测预期寿命。 总的来说,这个数据集是研究全球健康和预期寿命变化的宝贵资源,它不仅提供了历史数据,还为未来的研究和政策制

    基于微信小程序的高校毕业论文管理系统小程序答辩PPT.pptx

    基于微信小程序的高校毕业论文管理系统小程序答辩PPT.pptx

    基于java的超市 Pos 收银管理系统答辩PPT.pptx

    基于java的超市 Pos 收银管理系统答辩PPT.pptx

    基于java的网上报名系统答辩PPT.pptx

    基于java的网上报名系统答辩PPT.pptx

    基于java的网上书城答辩PPT.pptx

    基于java的网上书城答辩PPT.pptx

    婚恋网站 SSM毕业设计 附带论文.zip

    婚恋网站 SSM毕业设计 附带论文 启动教程:https://www.bilibili.com/video/BV1GK1iYyE2B

    基于java的戒烟网站答辩PPT.pptx

    基于java的戒烟网站答辩PPT.pptx

    基于微信小程序的“健康早知道”微信小程序答辩PPT.pptx

    基于微信小程序的“健康早知道”微信小程序答辩PPT.pptx

    机器学习(预测模型):自行车共享使用情况的数据集

    Capital Bikeshare 数据集是一个包含从2020年5月到2024年8月的自行车共享使用情况的数据集。这个数据集记录了华盛顿特区Capital Bikeshare项目中自行车的租赁模式,包括了骑行的持续时间、开始和结束日期时间、起始和结束站点、使用的自行车编号、用户类型(注册会员或临时用户)等信息。这些数据可以帮助分析和预测自行车共享系统的需求模式,以及了解用户行为和偏好。 数据集的特点包括: 时间范围:覆盖了四年多的时间,提供了长期的数据观察。 细节丰富:包含了每次骑行的详细信息,如日期、时间、天气条件、季节等,有助于深入分析。 用户分类:数据中区分了注册用户和临时用户,可以分析不同用户群体的使用习惯。 天气和季节因素:包含了天气情况和季节信息,可以研究这些因素对骑行需求的影响。 通过分析这个数据集,可以得出关于自行车共享使用模式的多种见解,比如一天中不同时间段的使用高峰、不同天气条件下的使用差异、季节性变化对骑行需求的影响等。这些信息对于城市规划者、交通管理者以及自行车共享服务提供商来说都是非常宝贵的,可以帮助他们优化服务、提高效率和满足用户需求。同时,这个数据集也

Global site tag (gtag.js) - Google Analytics