• 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;
        }

    以上。欢迎留言讨论,联系方式:rwhsysu@163.com

     

  • 相关阅读:
    命名mangling(压榨)
    Redis的源代码分析
    开源数据库 Sharding 技术 (Share Nothing)
    Python中time模块详解
    关于海量数据的数据模型
    字符数组,字符指针,Sizeof总结
    ConfigParser模块的使用
    分享懒人张RDLC报表(七、八)
    VS中提示:未能启用约束。一行或多行中包含违反非空、唯一或外键约束的值。
    VS用正则表达式统计代码行数
  • 原文地址:https://www.cnblogs.com/paulweihan/p/4662080.html
Copyright © 2020-2023  润新知