开发一个HTTP模块

这里写目录标题

HTTP模块的数据结构

ngx_module_t模块的数据结构

c 复制代码
// core.ngx_core.h
typedef struct ngx_module_s          ngx_module_t;

// core.ngx_module.h
struct ngx_module_s {
    /* 表示当前模块在这个模块中的序号,
     * 既表示模块的优先级,也表明模块的位置,
     * 借以帮助Nginx框架快速获得某个模块的数据
     */
    ngx_uint_t            ctx_index;
    /*
     * 表示当前模块在ngx_modules数组中的序号,
     * ctx_index表示当前模块在一类模块中的序号,
     * index是在所有模块中的序号
     */
    ngx_uint_t            index;

    // 模块名称
    char                 *name;

    // spare系列的保留变量
    ngx_uint_t            spare0;
    ngx_uint_t            spare1;
    // 模块的版本号
    ngx_uint_t            version;
    const char           *signature; //
    /*
     * 用于指向一类模块的上下文结构体,
     * Nginx模块有很多种类的模块,不同模块之间功能差距很大
     * 事件类型的模块主要处理IO事件,HTTP类型模块主要处理HTTP应用层的功能
     * ctx指向特定类型模块的公共接口,例如HTTP模块中,指向ngx_http_module_t
     */
    void                 *ctx;
    ngx_command_t        *commands;// 将处理nginx.conf中的配置项
    /*
     * 模块的类型,有5种
     * NGX_HTTP_MODULE,NGX_CORE_MODULE,NGX_CONF_MODULE,NGX_EVENT_MODULE,NGX_MALL_MODULE
     * 实际上可以自己定义类型
     * */
    ngx_uint_t            type;

    /*
     * 如下7个函数指针表示有7个执行点会分别调用这7个方法,对于任何一个方法
     * 如果不需要Nginx在某个时刻执行它,那么可以简单的设置为NULL空指针即可
     * */

    // 当master进程启动时进行回调init_master,但是目前为止,框架代码从来不会调用,设置为NULL
    ngx_int_t           (*init_master)(ngx_log_t *log);
    // init_module初始化所有模块时被调用,在master/worker模式下,这个阶段将在启动worker子进程前完成
    ngx_int_t           (*init_module)(ngx_cycle_t *cycle);
    // 在正常服务前被调用,在master/worker模式下,多个worker子进程已经产生,在每个worker进程的初始化过程会调用所有模块的init_process
    ngx_int_t           (*init_process)(ngx_cycle_t *cycle);
    // Nginx不支持多线程模式,所以init_thread在框架代码中没有被调用过,设置为NULL
    ngx_int_t           (*init_thread)(ngx_cycle_t *cycle);
    // exit_thread也不支持,设置为NULL
    void                (*exit_thread)(ngx_cycle_t *cycle);
    // 在服务停止前调用,master/worker模式下,worker进程会在退出前调用它
    void                (*exit_process)(ngx_cycle_t *cycle);
    // 在master进程退出前被调用
    void                (*exit_master)(ngx_cycle_t *cycle);
    /*
     * 以下8个变量也是保留字段,目前没有使用,但可用Nginx提供的NGX_MODULE_V1_PADDING宏来填充
     * */
    uintptr_t             spare_hook0;
    uintptr_t             spare_hook1;
    uintptr_t             spare_hook2;
    uintptr_t             spare_hook3;
    uintptr_t             spare_hook4;
    uintptr_t             spare_hook5;
    uintptr_t             spare_hook6;
    uintptr_t             spare_hook7;
};

当定义HTTP模块时,type设置为NGX_HTTP_MODULE,对于回调方法:init_module,init_process,exit_process,exit_master,调用它们的是Nginx代码,和HTTP框架无关,即使nginx.conf中没有定义http{...},这种开启HTTP功能的配置项,这些毁掉方法仍然会被调用。因此,通常开发HTTP模块时会都设为NULL,当不作为Web服务器使用时,不会执行HTTP模块的任何代码。

定义HTTP模块时,最终要的是设置ctx和commands两个成员,对于HTTP类型的模块来说ctx指向的是ngx_http_module_t接口。

ngx_http_module_t数据结构

c 复制代码
// http.ngx_http_config.h
typedef struct {
    // 解析配置文件前被调用
    ngx_int_t   (*preconfiguration)(ngx_conf_t *cf);
    // 完成配置文件的解析后调用
    ngx_int_t   (*postconfiguration)(ngx_conf_t *cf);
    //当需要创建数据结构用于存储main级别的全局配置项时,可以通过create_main_conf回调方法创建存储全局配置项的结构体
    void       *(*create_main_conf)(ngx_conf_t *cf);
    // 常用与初始化main级别配置项
    char       *(*init_main_conf)(ngx_conf_t *cf, void *conf);
    // 当需要创建数据结构用于存储srv级别的配置项时,可以通过create_srv_conf回调方法创建存储srv级别配置项的结构体
    void       *(*create_srv_conf)(ngx_conf_t *cf);
    // 合并main级别和srv级别的同名配置项
    char       *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);
    // 当需要创建结构体用于存储loc级别的配置项时,可以实现create_loc_conf回调方法
    void       *(*create_loc_conf)(ngx_conf_t *cf);
    // 合并srv级别和loc级别下的同名配置项
    char       *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
} ngx_http_module_t;

上述八个阶段的调用顺序与定义顺序不同,Nginx启动过程中,HTTP框架调用这些回调方法的实际顺序有可能是:(与nginx.conf配置项有关)

1)create_main_conf

2)create_srv_conf

3)create_loc_conf

4)preconfiguration

5)init_main_conf

6)merge_srv_conf

7)merge_loc_conf

8)postconfiguration

commands数组用于定义模块的配置文件参数,每个数组元素都是ngx_command_t类型,数组的结尾用ngx_null_command表示,Nginx在解析配置文件中的一个配置项时首先会编译所有的模块,对于每一个模块而言,即通过遍历commands数组进行,在数组中检查到ngx_null_command时,会停止使用当前模块解析该配置项

ngx_command_s 数据结构

c 复制代码
// core.ngx_core.h
typedef struct ngx_command_s         ngx_command_t;

// core.ngx_conf_file.h
struct ngx_command_s {
    ngx_str_t             name;// 配置项名称,列入:gzip
    ngx_uint_t            type;// 配置项类型,指定可以出现的位置,例如server,location中以及它可以携带的参数个数
    // 出现了name中指定的配置项后,将会调用set方法处理配置项的参数
    char               *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
    // 在配置文件中的偏移量
    ngx_uint_t            conf;
    // 通常用于预设的解析方法解析配置项
    ngx_uint_t            offset;
    // 配置项读取后的处理方法,必须是ngx_conf_post_t结构体指针
    void                 *post;
};

#define ngx_null_command  { ngx_null_string, 0, NULL, 0, 0, NULL }

定义一个HTTP模块

c 复制代码
// http.modules.ngx_http_mytest_module.c
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>

static char *ngx_http_mytest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

static ngx_command_t ngx_http_mytest_commands[] = {
        {
                ngx_string("mytest"),
                NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_NOARGS,
                ngx_http_mytest,
                NGX_HTTP_LOC_CONF_OFFSET,
                0,
                NULL
        },
        ngx_null_command
};
static ngx_http_module_t  ngx_http_mytest_module_ctx = {
        NULL,                                  /* preconfiguration */
        NULL,                  /* postconfiguration */

        NULL,                                  /* create main configuration */
        NULL,                                  /* init main configuration */

        NULL,                                  /* create server configuration */
        NULL,                                  /* merge server configuration */

        NULL,       /* create location configuration */
        NULL         /* merge location configuration */
};

ngx_module_t  ngx_http_mytest_module = {
        NGX_MODULE_V1,
        &ngx_http_mytest_module_ctx,           /* module context */
        ngx_http_mytest_commands,              /* module directives */
        NGX_HTTP_MODULE,                       /* module type */
        NULL,                                  /* init master */
        NULL,                                  /* init module */
        NULL,                                  /* init process */
        NULL,                                  /* init thread */
        NULL,                                  /* exit thread */
        NULL,                                  /* exit process */
        NULL,                                  /* exit master */
        NGX_MODULE_V1_PADDING
};

static char *ngx_http_mytest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {
    ngx_http_core_loc_conf_t *clcf;
    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
    clcf->handler = ngx_http_mytest_handler;
    return NGX_CONF_OK;
}

处理用户请求

// http.ngx_http_core_module.h
typedef struct ngx_http_core_loc_conf_s  ngx_http_core_loc_conf_t;
struct ngx_http_core_loc_conf_s {
    ngx_str_t     name;          /* location name */
    ngx_str_t     escaped_name;

#if (NGX_PCRE)
    ngx_http_regex_t  *regex;
#endif

    unsigned      noname:1;   /* "if () {}" block or limit_except */
    unsigned      lmt_excpt:1;
    unsigned      named:1;

    unsigned      exact_match:1;
    unsigned      noregex:1;

    unsigned      auto_redirect:1;
#if (NGX_HTTP_GZIP)
    unsigned      gzip_disable_msie6:2;
    unsigned      gzip_disable_degradation:2;
#endif

    ngx_http_location_tree_node_t   *static_locations;
#if (NGX_PCRE)
    ngx_http_core_loc_conf_t       **regex_locations;
#endif

    /* pointer to the modules' loc_conf */
    void        **loc_conf;

    uint32_t      limit_except;
    void        **limit_except_loc_conf;

    ngx_http_handler_pt  handler;

    /* location name length for inclusive location with inherited alias */
    size_t        alias;
    ngx_str_t     root;                    /* root, alias */
    ngx_str_t     post_action;

    ngx_array_t  *root_lengths;
    ngx_array_t  *root_values;

    ngx_array_t  *types;
    ngx_hash_t    types_hash;
    ngx_str_t     default_type;

    off_t         client_max_body_size;    /* client_max_body_size */
    off_t         directio;                /* directio */
    off_t         directio_alignment;      /* directio_alignment */

    size_t        client_body_buffer_size; /* client_body_buffer_size */
    size_t        send_lowat;              /* send_lowat */
    size_t        postpone_output;         /* postpone_output */
    size_t        sendfile_max_chunk;      /* sendfile_max_chunk */
    size_t        read_ahead;              /* read_ahead */
    size_t        subrequest_output_buffer_size;
                                           /* subrequest_output_buffer_size */

    ngx_http_complex_value_t  *limit_rate; /* limit_rate */
    ngx_http_complex_value_t  *limit_rate_after; /* limit_rate_after */

    ngx_msec_t    client_body_timeout;     /* client_body_timeout */
    ngx_msec_t    send_timeout;            /* send_timeout */
    ngx_msec_t    keepalive_time;          /* keepalive_time */
    ngx_msec_t    keepalive_timeout;       /* keepalive_timeout */
    ngx_msec_t    lingering_time;          /* lingering_time */
    ngx_msec_t    lingering_timeout;       /* lingering_timeout */
    ngx_msec_t    resolver_timeout;        /* resolver_timeout */
    ngx_msec_t    auth_delay;              /* auth_delay */

    ngx_resolver_t  *resolver;             /* resolver */

    time_t        keepalive_header;        /* keepalive_timeout */

    ngx_uint_t    keepalive_requests;      /* keepalive_requests */
    ngx_uint_t    keepalive_disable;       /* keepalive_disable */
    ngx_uint_t    satisfy;                 /* satisfy */
    ngx_uint_t    lingering_close;         /* lingering_close */
    ngx_uint_t    if_modified_since;       /* if_modified_since */
    ngx_uint_t    max_ranges;              /* max_ranges */
    ngx_uint_t    client_body_in_file_only; /* client_body_in_file_only */

    ngx_flag_t    client_body_in_single_buffer;
                                           /* client_body_in_singe_buffer */
    ngx_flag_t    internal;                /* internal */
    ngx_flag_t    sendfile;                /* sendfile */
    ngx_flag_t    aio;                     /* aio */
    ngx_flag_t    aio_write;               /* aio_write */
    ngx_flag_t    tcp_nopush;              /* tcp_nopush */
    ngx_flag_t    tcp_nodelay;             /* tcp_nodelay */
    ngx_flag_t    reset_timedout_connection; /* reset_timedout_connection */
    ngx_flag_t    absolute_redirect;       /* absolute_redirect */
    ngx_flag_t    server_name_in_redirect; /* server_name_in_redirect */
    ngx_flag_t    port_in_redirect;        /* port_in_redirect */
    ngx_flag_t    msie_padding;            /* msie_padding */
    ngx_flag_t    msie_refresh;            /* msie_refresh */
    ngx_flag_t    log_not_found;           /* log_not_found */
    ngx_flag_t    log_subrequest;          /* log_subrequest */
    ngx_flag_t    recursive_error_pages;   /* recursive_error_pages */
    ngx_uint_t    server_tokens;           /* server_tokens */
    ngx_flag_t    chunked_transfer_encoding; /* chunked_transfer_encoding */
    ngx_flag_t    etag;                    /* etag */

#if (NGX_HTTP_GZIP)
    ngx_flag_t    gzip_vary;               /* gzip_vary */

    ngx_uint_t    gzip_http_version;       /* gzip_http_version */
    ngx_uint_t    gzip_proxied;            /* gzip_proxied */

#if (NGX_PCRE)
    ngx_array_t  *gzip_disable;            /* gzip_disable */
#endif
#endif

#if (NGX_THREADS || NGX_COMPAT)
    ngx_thread_pool_t         *thread_pool;
    ngx_http_complex_value_t  *thread_pool_value;
#endif

#if (NGX_HAVE_OPENAT)
    ngx_uint_t    disable_symlinks;        /* disable_symlinks */
    ngx_http_complex_value_t  *disable_symlinks_from;
#endif

    ngx_array_t  *error_pages;             /* error_page */

    ngx_path_t   *client_body_temp_path;   /* client_body_temp_path */

    ngx_open_file_cache_t  *open_file_cache;
    time_t        open_file_cache_valid;
    ngx_uint_t    open_file_cache_min_uses;
    ngx_flag_t    open_file_cache_errors;
    ngx_flag_t    open_file_cache_events;

    ngx_log_t    *error_log;

    ngx_uint_t    types_hash_max_size;
    ngx_uint_t    types_hash_bucket_size;

    ngx_queue_t  *locations;

#if 0
    ngx_http_core_loc_conf_t  *prev_location;
#endif
};

typedef ngx_int_t (*ngx_http_handler_pt)(ngx_http_request_t *r);

返回值

c 复制代码
// http.ngx_http_request.h

#define NGX_HTTP_CONTINUE                  100
#define NGX_HTTP_SWITCHING_PROTOCOLS       101
#define NGX_HTTP_PROCESSING                102

#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_PERMANENT_REDIRECT        308

#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
#define NGX_HTTP_MISDIRECTED_REQUEST       421
#define NGX_HTTP_TOO_MANY_REQUESTS         429


/* Our own HTTP codes */

/* 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

/*
 * We use the special code for the plain HTTP requests that are sent to
 * HTTPS port to distinguish it from 4XX in an error page redirection
 */
#define NGX_HTTP_TO_HTTPS                  497

/* 498 is the canceled code for the requests with invalid host name */

/*
 * HTTP does not define the code for the case when a client closed
 * the connection while we are processing its request so we introduce
 * own code to log such situation when a client has closed the connection
 * before we even try to send the HTTP header to it
 */
#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_VERSION_NOT_SUPPORTED     505
#define NGX_HTTP_INSUFFICIENT_STORAGE      507

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

Nginx全局定义的错误码
// core.ngx_core.h
#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

当最后调用ngx_http_output_filter向用户发送响应包时,可以将ngx_http_output_filter的返回值作为ngx_http_mytest_handler方法的返回值使用。

获取URI和参数

c 复制代码
// http.ngx_http.h
typedef struct ngx_http_request_s     ngx_http_request_t;

// http.ngx_http_request.h

struct ngx_http_request_s {
	...
	ngx_uint_t                        method;
    ngx_uint_t                        http_version;

    ngx_str_t                         request_line;
    ngx_str_t                         uri;
    ngx_str_t                         args;
    ngx_str_t                         exten;
    ngx_str_t                         unparsed_uri;

    ngx_str_t                         method_name;
    ngx_str_t                         http_protocol;
    ngx_str_t                         schema;
	...
	u_char                           *uri_start;
    u_char                           *uri_end;
    u_char                           *uri_ext;
    u_char                           *args_start;
    u_char                           *request_start;
    u_char                           *request_end;
    u_char                           *method_end;
    u_char                           *schema_start;
    u_char                           *schema_end;
    ...
}

方法名

// http.ngx_http_request.h
ngx_uint_t                        method;

// http.ngx_http_request.h
#define NGX_HTTP_UNKNOWN                   0x00000001
#define NGX_HTTP_GET                       0x00000002
#define NGX_HTTP_HEAD                      0x00000004
#define NGX_HTTP_POST                      0x00000008
#define NGX_HTTP_PUT                       0x00000010
#define NGX_HTTP_DELETE                    0x00000020
#define NGX_HTTP_MKCOL                     0x00000040
#define NGX_HTTP_COPY                      0x00000080
#define NGX_HTTP_MOVE                      0x00000100
#define NGX_HTTP_OPTIONS                   0x00000200
#define NGX_HTTP_PROPFIND                  0x00000400
#define NGX_HTTP_PROPPATCH                 0x00000800
#define NGX_HTTP_LOCK                      0x00001000
#define NGX_HTTP_UNLOCK                    0x00002000
#define NGX_HTTP_PATCH                     0x00004000
#define NGX_HTTP_TRACE                     0x00008000
#define NGX_HTTP_CONNECT                   0x00010000

获取方法的方式

  • method为上述定义的宏,直接比较(如果使用method_name成员与字符串比较,效率会差很多)
  • 如果需要获取方法名,可以使用method_name,与可以联合request_start与method_end指针获取

URI

c 复制代码
	ngx_str_t                         uri;
	
	ngx_str_t                         unparsed_uri;
	
    u_char                           *uri_start;
    u_char                           *uri_end;
    u_char                           *uri_ext;

uri表示请求中的URI,uri_start和uri_end也是可以获取到。

exten成员变量指向用户请求的文件扩展名。

unparsed_uri表示没有进行URL解码的原始请求。例如,uri为/a b时,unparsed_uri为/a%20b

URL

args指向用户请求中的URL参数。同理:args_start和uri_end也可以获取URL

协议版本

http_protocol的data成员指向用户请求中的HTTP协议版本字符串,len成员为协议版本字符串长度

http_version是Nginx解析过的协议版本

ngx_uint_t                        http_version;

#define NGX_HTTP_VERSION_9                 9
#define NGX_HTTP_VERSION_10                1000
#define NGX_HTTP_VERSION_11                1001
#define NGX_HTTP_VERSION_20                2000
#define NGX_HTTP_VERSION_30                3000

建议使用http_version分析HTTP协议版本。

使用request_start和request_end获取原始的用户请求行

获取HTTP头

c 复制代码
struct ngx_http_request_s {
	...
	ngx_buf_t                        *header_in;
	ngx_http_headers_in_t             headers_in;
	...
}

header_in指向Nginx收到的未经解析的HTTP头部,这里暂不关注,header_in是接受HTTP头部的缓冲区,ngx_http_headers_in_t类型的headers_in是存储已经解析过的HTTP头部。

c 复制代码
// http.ngx_http_request.h
typedef struct {
    //所有解析过的HTTP头部都在headers链表中
    ngx_list_t                        headers;
    // 每个ngx_table_elt_t成员都是RFC2616规范定义的HTTP头部,
    // 它们实际上指向headers链表中相应成员,当为NULL时表示没有解析到相应的HTTP头部
    ngx_table_elt_t                  *host;
    ngx_table_elt_t                  *connection;
    ngx_table_elt_t                  *if_modified_since;
    ngx_table_elt_t                  *if_unmodified_since;
    ngx_table_elt_t                  *if_match;
    ngx_table_elt_t                  *if_none_match;
    ngx_table_elt_t                  *user_agent;
    ngx_table_elt_t                  *referer;
    ngx_table_elt_t                  *content_length;
    ngx_table_elt_t                  *content_range;
    ngx_table_elt_t                  *content_type;

    ngx_table_elt_t                  *range;
    ngx_table_elt_t                  *if_range;

    ngx_table_elt_t                  *transfer_encoding;
    ngx_table_elt_t                  *te;
    ngx_table_elt_t                  *expect;
    ngx_table_elt_t                  *upgrade;

#if (NGX_HTTP_GZIP || NGX_HTTP_HEADERS)
    ngx_table_elt_t                  *accept_encoding;
    ngx_table_elt_t                  *via;
#endif

    ngx_table_elt_t                  *authorization;

    ngx_table_elt_t                  *keep_alive;

#if (NGX_HTTP_X_FORWARDED_FOR)
    ngx_table_elt_t                  *x_forwarded_for;
#endif

#if (NGX_HTTP_REALIP)
    ngx_table_elt_t                  *x_real_ip;
#endif

#if (NGX_HTTP_HEADERS)
    ngx_table_elt_t                  *accept;
    ngx_table_elt_t                  *accept_language;
#endif

#if (NGX_HTTP_DAV)
    ngx_table_elt_t                  *depth;
    ngx_table_elt_t                  *destination;
    ngx_table_elt_t                  *overwrite;
    ngx_table_elt_t                  *date;
#endif
    ngx_table_elt_t                  *cookie;
    // 只有ngx_http_auth_basic_module才会用到的成员
    ngx_str_t                         user;
    ngx_str_t                         passwd;
    // server名称
    ngx_str_t                         server;
    // 计算出的HTTP包体大小(ngx_table_elt_t * content_length)
    off_t                             content_length_n;
    time_t                            keep_alive_n;
    // HTTP连接类型,取值范围是0,
    // #define NGX_HTTP_CONNECTION_CLOSE          1
    //#define NGX_HTTP_CONNECTION_KEEP_ALIVE     2
    unsigned                          connection_type:2;
    unsigned                          chunked:1;
    unsigned                          multi:1;
    unsigned                          multi_linked:1;
    // 以下7个标志位是 HTTP框架根据浏览器传来的useragent头部,可以判断浏览器的类型,只为1表示相应的浏览器发来的请求
    unsigned                          msie:1;
    unsigned                          msie6:1;
    unsigned                          opera:1;
    unsigned                          gecko:1;
    unsigned                          chrome:1;
    unsigned                          safari:1;
    unsigned                          konqueror:1;
} ngx_http_headers_in_t;

例如:对于不常见的HTTP请求头如何寻找,只能通过遍历headers_in.headers

c 复制代码
static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r) {
	...
    ngx_list_part_t *part = &r->header_in.headers.part;
    ngx_table_elt_t *header = part->elts;
    for (i = 0;; ++i) {
        if (i >= part->nelts) {
            if (part->next == NULL) {
                break;
            }
            part = part->next;
            header = part->elts;
            i = 0;
        }
        if (header[i].hash == 0){
            continue;
        }
        if (0 == ngx_strncasecmp(header[i].key.data,(u_char*) "Rpc-Description",header[i].key.len)){
            if (0 == ngx_strncmp(header[i].value.data,"uploadFile",header[i].value.len)){
                // 找到了正确的头,继续向下执行
            }
        }
    }
    ...
}

获取HTTP包体

HTTP包体的长度有可能非常大,如果尝试一次性调用并读取完所有的包体,那么多半阻塞nginx进程,HTTP框架提供一种异步接收包体方法:

ngx_int_t ngx_http_read_client_request_body(ngx_http_request_t *r,
    ngx_http_client_body_handler_pt post_handler);

这是一个异步方法,调用只能说明要求Nginx开始接受请求的包体,并不表示是否已经接受完,当接收完所有的包体内容后,post_handler指向的回调方法会被回调。

发送响应

响应包含响应行,响应头部,包体三部分。

发送HTTP头

ngx_int_t ngx_http_send_header(ngx_http_request_t *r)
c 复制代码
struct ngx_http_request_s {
	ngx_http_headers_out_t            headers_out;
}

typedef struct {
    ngx_list_t                        headers;
    ngx_list_t                        trailers;

    ngx_uint_t                        status;
    ngx_str_t                         status_line;

    ngx_table_elt_t                  *server;
    ngx_table_elt_t                  *date;
    ngx_table_elt_t                  *content_length;
    ngx_table_elt_t                  *content_encoding;
    ngx_table_elt_t                  *location;
    ngx_table_elt_t                  *refresh;
    ngx_table_elt_t                  *last_modified;
    ngx_table_elt_t                  *content_range;
    ngx_table_elt_t                  *accept_ranges;
    ngx_table_elt_t                  *www_authenticate;
    ngx_table_elt_t                  *expires;
    ngx_table_elt_t                  *etag;

    ngx_table_elt_t                  *cache_control;
    ngx_table_elt_t                  *link;

    ngx_str_t                        *override_charset;

    size_t                            content_type_len;
    ngx_str_t                         content_type;
    ngx_str_t                         charset;
    u_char                           *content_type_lowcase;
    ngx_uint_t                        content_type_hash;

    off_t                             content_length_n;
    off_t                             content_offset;
    time_t                            date_time;
    time_t                            last_modified_time;
} ngx_http_headers_out_t;

添加请求头

c 复制代码
   ngx_table_elt_t  h = ngx_list_push(&r->headers_out.headers);
   if (h == NULL){
       return NGX_ERROR
   }
   h.hash = 1;
   h.key.len = sizeof("TestHeader") - 1;
   h.key.data = (u_char *) "TestHeader";
   h.value.len = sizeof("TestValue") - 1;
   h.value.data = (u_char *) "TestValue" ;

发送内存中的字符串作为包体

c 复制代码
ngx_int_t ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in)

向客户端发送HTTP响应包体

c 复制代码
    ngx_buf_t *b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
    b->start = (u_char*)ngx_pcalloc(r->pool,128);
    b->pos = b->start;
    b->last = b->start;
    b->end = b->last + 128;
    b->temporary = 1;
    
    // 等同于如下
    ngx_buf_t *b = ngx_create_temp_buf(r->pool,128) 

返回一个Hello world

c 复制代码
static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r) {
    // 必须为GET或者HEAD,否则返回405
    if (!(r->method & (NGX_HTTP_GET | NGX_HTTP_HEAD))){
        return NGX_HTTP_NOT_ALLOWED;
    }
    // 丢弃请求中的包体
    ngx_int_t rc = ngx_http_discard_request_body(r);
    if (rc != NGX_OK){
        return rc;
    }
    // 设置返回类型
    ngx_str_t  type = ngx_string("text/plain");
    // 设置返回体
    ngx_str_t response = ngx_string("Hello World!");
    // 设置响应码
    r->headers_out.status = NGX_HTTP_OK;
    // 设置响应体大小
    r->headers_out.content_length_n = response.len;
    // 设置类型
    r->headers_out.content_type = type;
    // 发送HTTP头部
    rc = ngx_http_send_header(r);
    if (rc == NGX_ERROR || rc > NGX_OK || r->header_only){
        return rc;
    }
    // 构造ngx_buf_t结构体发送包体
    ngx_buf_t *b;
    b = ngx_create_temp_buf(r->pool,response.len);
    if (b == NULL){
        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }
    // 将hello world复制到ngx_buf_t指向的内存中
    ngx_memcpy(b->pos,response.data,response.len);
    // 注意设置好last指针
    b->last = b->pos + response.len;
    // 声明这是最后一块缓冲区
    b->last_buf = 1;
    // 构造发送时的ngx_chain_t结构体
    ngx_chain_t  out;
    out.buf = b;
    out.next = NULL;
    // 发送包体
    return ngx_http_output_filter(r,&out);
}

磁盘文件作为包体发送

设置ngx_buf_t的in_file为1,表示缓冲区是文件,不是内存。file为ngx_file_t,结构如下:

c 复制代码
struct ngx_file_s {
    ngx_fd_t                   fd;// 文件句柄描述符
    ngx_str_t                  name;// 文件名称
    ngx_file_info_t            info;// 文件大小等基本信息,就是Linux系统定义的stat结构

    off_t                      offset;// 表示处理到文件何处了
    off_t                      sys_offset;// 当前文件系统偏移量

    ngx_log_t                 *log;// 日志对象,相关的日志会输出到log指定的日志文件中

#if (NGX_THREADS || NGX_COMPAT)
    ngx_int_t                (*thread_handler)(ngx_thread_task_t *task,
                                               ngx_file_t *file);
    void                      *thread_ctx;
    ngx_thread_task_t         *thread_task;
#endif

#if (NGX_HAVE_FILE_AIO || NGX_COMPAT)
    ngx_event_aio_t           *aio;
#endif

    unsigned                   valid_info:1;
    unsigned                   directio:1;// 在发送大文件时可以设置为1
};
相关推荐
ajsbxi2 分钟前
苍穹外卖学习记录
java·笔记·后端·学习·nginx·spring·servlet
JustCouvrir19 小时前
macOS|前端工程部署到Nginx服务器
服务器·前端·nginx
AlbertS20 小时前
使用 Let’s Encrypt 获取免费SSL证书
nginx·免费·centos7·ssl证书·let’s encrypt
航月20 小时前
FTP、ISCSI、CHRONY、DNS、NFS、DOCKER、MARIADB、NGINX、PHP、CA各服务开启方法
nginx·docker·mariadb
IT-民工211101 天前
nginx监控指标有哪些
运维·nginx
陌路物是人非1 天前
docker对nginx.conf进行修改后页面无变化或页面报错
nginx·docker
草明2 天前
Nginx 做反向代理,一个服务优先被使用,当无法提供服务时才使用其他的备用服务
运维·nginx·github
吉吉612 天前
Nginx:我自己的网站
运维·nginx
凉忆-2 天前
nginx安装ssl模块教程
运维·nginx·ssl
俎树振2 天前
树莓派上安装与配置 Nginx Web 服务器教程
服务器·前端·nginx