Nginx重要结构request_t解析之http请求的获取

请在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

本文主要参考为《深入理解nginx模块开发与架构解析》一书,处理用户请求部分,是一篇包含作者理解的读书笔记。欢迎指正,讨论。

handler函数的定义模型如下:

1 static ngx_int_t
2 ngx_http_hello_handler(ngx_http_request_t *r)
3 {}

请求的所有信息都可以在传入的ngx_http_request_t类型指针参数 r 中获得。Ngx_http_request_t结构体包含的内容很多,这里只讨论其中获取HTTP请求的部分,相关定义如下:

 1 struct ngx_http_request_s {
 2     ...
 3   //请求头
 4     ngx_buf_t                        *header_in;
 5
 6     ngx_http_headers_in_t             headers_in;
 7     ngx_http_headers_out_t            headers_out;
 8   //请求体
 9     ngx_http_request_body_t          *request_body;
10   //请求行
11     ngx_uint_t                        method;
12     ngx_uint_t                        http_version;
13
14     ngx_str_t                         request_line;
15     ngx_str_t                         uri;
16     ngx_str_t                         args;
17     ngx_str_t                         exten;
18     ngx_str_t                         unparsed_uri;
19
20     ngx_str_t                         method_name;
21     ngx_str_t                         http_protocol;
22     ...
23     /*
24      * a memory that can be reused after parsing a request line
25      * via ngx_http_ephemeral_t
26      */
27
28     u_char                           *uri_start;
29     u_char                           *uri_end;
30     u_char                           *uri_ext;
31     u_char                           *args_start;
32     u_char                           *request_start;
33     u_char                           *request_end;
34     u_char                           *method_end;
35     u_char                           *schema_start;
36     u_char                           *schema_end;
37     u_char                           *host_start;
38     u_char                           *host_end;
39     u_char                           *port_start;
40     u_char                           *port_end;
41     ...
42 };

相关定义全在上面列出,

下面,我们先从请求行的获取来解释:

http请求行定义如下:

<method><request-URL><version>

首先,需要解析method:

  • method的定义类型为ngx_uint_t,Nginx中通过宏定义,给所有method赋予了不同的整型值。这里的method是Nginx解析了用户请求之后得到的整型值,可以用来判断method。
  • method_name则是ngx_str_t类型,内容是方法名字符串,其使用方式区别于常用str类型,这点一定要注意。
  • 还可以使用request_start和method_end指针取得方法名,request_start指向用户请求的首地址,同时也是方法名的地址,method_end则指向方法名的最后一个字符(注意:这点与其他xxx_end指针不同)。

然后,解析URI:

  • ngx_str_t类型的uri指向用户请求的URI。
  • uchar*类型的uri_start和uri_end也和method的用法类似。不同的是,method_end指向的是方法名的最后一个字符,而uri_end指向URI结束之后的下一个字符。也就是最后一个字符的下一个字符地址。Nginx中大部分的u_char*类型指针变量中的“xxx_start”和“xxx_end”都是这样使用的。
  • ngx_str_t类型的extern类型指向用户请求的文件的扩展名。例如:在访问“GET /a.txt HTTP/1.1”时,extern的值为{len=3,data=“txt”}。
  • uri_ext指针指向的地质与extern.data相同unparsed_uri表示没有进行uri解码的原始请求。例如:“/a b”的原始请求为“/a%20b”(空格字符的编码为%20)。

接着,解析URI参数:

  • Arg指向用户请求中的URL参数。
  • Args_start和配合uri_end使用可以获得URL的参数。

最后,协议版本的获取:

  • http_protocol指向用户请求中的HTTP的起始地址。
  • http_version是nginx解析过得协议版本,他的取值范围如下:

  

#define NGX_HTTP_VERSION_9                 9
#define NGX_HTTP_VERSION_10                1000
#define NGX_HTTP_VERSION_11                1001
  • 建议使用http_version分析HTTP协议的版本。
  • 最后使用request_start 和 request_end可以获取原始的用户请求。

请求行的获取到此结束,下面是请求头的获取:

head_in指向nginx收到的未经解析的HTTP头部。这里先不关注。

ngx_http_request_t中ngx_http_headers_in_t类型的headers_in则储存已经解析过得HTTP头部。结构体定义如下:

 1 typedef struct {
 2     ngx_list_t                        headers;
 3
 4     ngx_table_elt_t                  *host;
 5     ngx_table_elt_t                  *connection;
 6     ngx_table_elt_t                  *if_modified_since;
 7     ngx_table_elt_t                  *if_unmodified_since;
 8     ngx_table_elt_t                  *if_match;
 9     ngx_table_elt_t                  *if_none_match;
10     ngx_table_elt_t                  *user_agent;
11     ngx_table_elt_t                  *referer;
12     ngx_table_elt_t                  *content_length;
13     ngx_table_elt_t                  *content_type;
14
15     ngx_table_elt_t                  *range;
16     ngx_table_elt_t                  *if_range;
17
18     ngx_table_elt_t                  *transfer_encoding;
19     ngx_table_elt_t                  *expect;
20     ngx_table_elt_t                  *upgrade;
21
22 #if (NGX_HTTP_GZIP)
23     ngx_table_elt_t                  *accept_encoding;
24     ngx_table_elt_t                  *via;
25 #endif
26
27     ngx_table_elt_t                  *authorization;
28
29     ngx_table_elt_t                  *keep_alive;
30
31 #if (NGX_HTTP_X_FORWARDED_FOR)
32     ngx_array_t                       x_forwarded_for;
33 #endif
34
35 #if (NGX_HTTP_REALIP)
36     ngx_table_elt_t                  *x_real_ip;
37 #endif
38
39 #if (NGX_HTTP_HEADERS)
40     ngx_table_elt_t                  *accept;
41     ngx_table_elt_t                  *accept_language;
42 #endif
43
44 #if (NGX_HTTP_DAV)
45     ngx_table_elt_t                  *depth;
46     ngx_table_elt_t                  *destination;
47     ngx_table_elt_t                  *overwrite;
48     ngx_table_elt_t                  *date;
49 #endif
50
51     ngx_str_t                         user;
52     ngx_str_t                         passwd;
53
54     ngx_array_t                       cookies;
55
56     ngx_str_t                         server;
57     off_t                             content_length_n;
58     time_t                            keep_alive_n;
59
60     unsigned                          connection_type:2;
61     unsigned                          chunked:1;
62     unsigned                          msie:1;
63     unsigned                          msie6:1;
64     unsigned                          opera:1;
65     unsigned                          gecko:1;
66     unsigned                          chrome:1;
67     unsigned                          safari:1;
68     unsigned                          konqueror:1;
69 } ngx_http_headers_in_t;

结构体中,后面定义了很多ngx_table_elt_t类型的指针变量,ngx_table_elt_t类型是Nginx定义的字典类型。内容都指向Nginx已经解析过得标准常见的HTTP头部,可以直接使用。这些解析过了的头部其实都储存在headers链表中,所以,对于headers_in结构体中未定义的不常用的HTTP头部,就需要遍历headers链表,解析其值。

下面是一个解析的例子(源于《深入理解》一书):

下面尝试在一个用户请求中找到“Rpc-Description”头部,首先判断其值是否为“uploadFile”,再决定后续的服务器行为。

ngx_list_part_t *part = &r->headers_in.headers.part;
ngx_table_elt_t *header = part->elts;

//开始遍历链表
for(i = 0;/*void*/;i++){
    //判断是否到达链表中当前数组结尾
    if(i>=part->nelts){
        //是否还有下一个链表数组元素
        if(part->next == NULL){
            break;
        }
        //part设置为next来访问下一个链表数组;header也指向下一个链表数组的首地址,设置i为0,表示从头开始遍历新的链表数组。
        part = part->next;
        header = part->elts;
        i=0;
    }
    //hash为0表示不是合法的头部
    if(header[i].hash == 0){
        continue;
    }

    //判断当前头部是否是“Rpc-Description”,如果想要忽略大小写,则应先用header[i].lowcase_key代替header[i].key.data,然后比较字符串。

    if(0 == ngx_strncasecmp(header[i].key.data,
                    (u_char*) "Rpc-Description",
                    header[i].key.len))
    {
        //判断头部的值是否是“uploadFile”
        if(0 == ngx_strncmp(header[i].key.data,
                "uploadFile",
                header[i].value.len))
                {
                //找到之后继续处理
                }
    }
}

获取headers的方法讲完了,下面是获取请求体(包体)的方法:

HTTP框架提供了一种方法可以异步接受包体:

ngx_int_t
ngx_http_read_client_request_body(ngx_http_request_t *r,
    ngx_http_client_body_handler_pt post_handler)

它的返回并不表示已经接受完成所有包体,只表示Nginx已经知道这个任务。所以一般返回为NGX_DONE(含义请自查,不赘述)。接受完所有包体之后,会调用post_handler函数指针指向的函数。其原型定义如下:

typedef void (*ngx_http_client_body_handler_pt)(ngx_http_request_t *r);

如果不想处理包体内容,可用如下方式将包体内容丢掉:

ngx_int_t rc = ngx_http_discard_request_body(r);
    if (rc != NGX_OK) {
        return rc;
    }

以上。欢迎留言讨论,联系方式:[email protected]

 

时间: 2024-10-29 11:21:39

Nginx重要结构request_t解析之http请求的获取的相关文章

【Nginx】配置、error日志和请求上下文

处理http配置项可以分为下面4个步骤: 1)创建数据结构用于存储配置项对应的参数 2)设定配置项在nginx.conf中出现时的限制条件与回调方法 3)实现第2步中的回调方法,或者使用Nginx框架预设的14个回调方法 4)合并不同级别的配置块中出现的同名配置项 一.分配用于保存配置参数的数据结构 创建结构体来存储配置项的参数值,使用create_main_conf,create_srv_conf.create_loc_conf这三个回调方法把我们分配的用于保存配置项的结构体传递给http框架

再提供一种解决Nginx文件类型错误解析漏洞的方法

[文章作者:张宴 本文版本:v1.2 最后修改:2010.05.24 转载请注明原文链接:http://blog.zyan.cc/nginx_0day/] 注:2010年5月23日14:00前阅读本文的朋友,请按目前v1.1版本的最新配置进行设置. 昨日,80Sec 爆出Nginx具有严重的0day漏洞,详见<Nginx文件类型错误解析漏洞>.只要用户拥有上传图片权限的Nginx+PHP服务器,就有被入侵的可能. 其实此漏洞并不是Nginx的漏洞,而是PHP PATH_INFO的漏洞,详见:h

nginx文件类型错误解析漏洞

漏洞介绍:nginx是一款高性能的web服务器,使用非常广泛,其不仅经常被用作反向代理,也可以非常好的支持PHP的运行.80sec发现 其中存在一个较为严重的安全问题,默认情况下可能导致服务器错误的将任何类型的文件以PHP的方式进行解析,这将导致严重的安全问题,使得恶意的攻击者可 能攻陷支持php的nginx服务器. 漏洞分析:nginx默认以cgi的方式支持php的运行,譬如在配置文件当中可以以 1 location ~ .php$ { 2 root html; 3 fastcgi_pass

LNP环境下Nginx与PHP配合解析的原理

正在理解中,查阅资料,加上自我理解,得出如下结论,如有错误,欢迎指正.... LNP环境,Nginx与PHP配合运行的原理解释: 以前的互联网时代我们成为web1.0时代,那时用户是被动接受网络信息,服务器上有什么你就看什么,你不能往服务器上传东西,并且主要以静态文件为主,几乎没有动态程序,所以Nginx处理起来很轻松.但是随着祖国的强大,时代和技术的进步,web2.0时代来临,用户为主,动态语言也流行了起来,例如php.java等,所以网络上动态请求就多了起来,但是Nginx有不能处理动态请求

OpenCV 2.4.8组件结构全解析

转自: http://blog.csdn.net/huang9012/article/details/21811271 之前啃了不少OpenCV的官方文档,发现如果了解了一些OpenCV整体的模块架构后,再重点学习自己感兴趣的部分的话,就会有一览众山小的感觉,于是,就决定写出这篇文章,作为启程OpenCV系列博文的第二篇. 至于OpenCV组件结构的研究方法,我们不妨管中窥豹,通过opencv安装路径下include目录里面头文件的分类存放,来一窥OpenCV这些年迅猛发展起来的庞杂组件架构.

使用gSoap规避和修改ONVIF标准类型结构的解析

ONVIF/gSoap依赖关系及问题 ONVIF是一组服务规范,标准参考 gSoap是一套基于实现SOAP通信接口的工具链 即是,当我们需要访问ONVIF的Web Service或实现对ONVIF部分的支持:基于C/C++开发,则需要借助gSoap生成这之间的交互接口调用的代码. gSoap生成代码 wsdl2h 将服务接口描述转换为soapcpp2的转换规则,生成中间头文件. 通常我们前期会选择实现部分服务标准:因此这期间生成的后续多为修改这次生成中间产物.h,而不会一切重新生成. soapc

利用HttpURLConnecion通过Nginx向代理邮件服务器发送POST请求

第一步:获取邮件各种参数,通过URLencode和Base64编码之后发送请求参数. 请求参数中,有邮件附件这样的大件,如何当做请求发送呢? 首先,将邮件内容转为字节数组,转为字节数组之后可以当做二进制操作了,保持了附件最原始的面貌,不会被任何其他因素影响. byte[] att= attachment.getContent(); //附件内容 //利用Base64进行加密传输,虽然加密的不够 Base64 base64 = new Base64(); //org.apache.commons.

nginx配置支持PHP解析

nginx支持php的解析配置, user www-data;(注意此用户,如果注释掉,错误日志里面会出现连接php5-fpm失败,权限拒绝) location ~ \.php$ {            root   html;        #   fastcgi_split_path_info ^(.+\.php)(/.+)$;        #   fastcgi_pass   127.0.0.1:9000;            fastcgi_pass unix:/var/run/p

网络模型中Inception的作用与结构全解析

网络模型中Inception的作用与结构全解析 一 论文下载 本文涉及到的网络模型的相关论文以及下载地址:   [v1] Going Deeper with Convolutions, 6.67% test error http://arxiv.org/abs/1409.4842 [v2] Batch Normalization: Accelerating Deep Network Training by Reducing Internal Covariate Shift, 4.8% test