Nginx 模块开发

Nginx 模块概述

Nginx 模块有三种角色:

  • 处理请求并产生输出的 Handler 模块
  • 处理由 Handler 产生的输出的 Filter(滤波器)模块;
  • 当出现多个后台服务器时,Load-balancer (负载均衡器)模块负责选择其中一个后台服务器发送请求;

        通常,服务器启动时,任何  Handler 模块都有可能去处理配置文件中的  location 定义。若出现多个 Handler 模块被配置成需要处理某一特定的  location 时,最终只有其中一个 Handler 模块是成功的。 Handler 模块有三种返回方式:
  1. 接收请求,并成功返回;
  2. 接收请求,但是出错返回;
  3. 拒绝请求,使默认的 Handler 模块处理该请求;
        若  Handler 模块的作用是把一个请求反向代理到后台服务器,则会出现另一种类型的空间模块——  Load-balancer。  Load-balancer 负责决定将请求发送给哪个后端服务器。 Nginx 目前支持两种  Load-balancer 模块: round-robin (轮询,处理请求就像打扑克时发牌那样)和 "IP hash" method(众多请求时,保证来自同一  IP 的请求被分发的同一个后端服务器)。
        若  Handler 模块没有产生错误返回时,则会调用  Filter 模块。每个 location 配置里都可以添加多个 Filter 模块 ,因此响应可以被压缩和分块。 Filter 模块之间的处理顺序是在编译时就已经确定的。 Filter 模块采用“ CHAIN OF RESPONSIBILITY”链式的设计模式。当有请求到达时,请求依次经过这条链上的全部  Filter 模块,一个 Filter 被调用并处理,接下来调用下一个 Filter,直到最后一个 Filter 被调用完成, Nginx 才真正完成响应流程。

        总结如下,典型的处理形式如下:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. Client sends HTTP request → Nginx chooses the appropriate handler based on the location config →    
  2.  (if applicable) load-balancer picks a backend server →     
  3. Handler does its thing and passes each output buffer to the first filter →     
  4. First filter passes the output to the second filter → second to third → third to fourth → etc.     
  5. → Final response sent to client    

 

Nginx 模块的结构

模块的配置结构

        模块最多可以定义三个配置结构: mainserverlocation。绝大多数模块仅需要一个 location 配置。名称约定如下以 ngx_http_<module name>_(main|srv|loc)_conf_t为例的 dav module
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. typedef struct {  
  2.       ngx_uint_t methods;  
  3.       ngx_flag_t create_full_put_path;  
  4.       ngx_uint_t access;  
  5. } ngx_http_dav_loc_conf_t;  
       Nginx 模块的数据结构如下定义:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /* Nginx 模块的数据结构 */  
  2. #define NGX_MODULE_V1          0, 0, 0, 0, 0, 0, 1  
  3. #define NGX_MODULE_V1_PADDING  0, 0, 0, 0, 0, 0, 0, 0  
  4.   
  5. struct ngx_module_s {  
  6.     /* 模块类别由type成员决定,ctx_index表示当前模块在type类模块中的序号 */  
  7.     ngx_uint_t            ctx_index;  
  8.     /* index 区别与ctx_index,index表示当前模块在所有模块中的序号 */  
  9.     ngx_uint_t            index;  
  10.   
  11.     /* spare 序列保留变量,暂时不被使用 */  
  12.     ngx_uint_t            spare0;  
  13.     ngx_uint_t            spare1;  
  14.     ngx_uint_t            spare2;  
  15.     ngx_uint_t            spare3;  
  16.   
  17.     /* 当前模块的版本 */  
  18.     ngx_uint_t            version;  
  19.   
  20.     /* ctx指向特定类型模块的公共接口,例如在HTTP模块中,ctx指向ngx_http_module_t结构体 */  
  21.     void                 *ctx;  
  22.     /* 处理nginx.conf中的配置项 */  
  23.     ngx_command_t        *commands;  
  24.     /* type表示当前模块的类型 */  
  25.     ngx_uint_t            type;  
  26.   
  27.     /* 下面的7个函数指针是在Nginx启动或停止时,分别调用的7中方法 */  
  28.     /* 在master进程中回调init_master */  
  29.     ngx_int_t           (*init_master)(ngx_log_t *log);  
  30.   
  31.     /* 初始化所有模块时回调init_module */  
  32.     ngx_int_t           (*init_module)(ngx_cycle_t *cycle);  
  33.   
  34.     /* 在worker进程提供正常服务之前回调init_process初始化进程 */  
  35.     ngx_int_t           (*init_process)(ngx_cycle_t *cycle);  
  36.     /* 初始化多线程 */  
  37.     ngx_int_t           (*init_thread)(ngx_cycle_t *cycle);  
  38.     /* 退出多线程 */  
  39.     void                (*exit_thread)(ngx_cycle_t *cycle);  
  40.     /* 在worker进程停止服务之前回调exit_process */  
  41.     void                (*exit_process)(ngx_cycle_t *cycle);  
  42.   
  43.     /* 在master进程退出之前回调exit_master */  
  44.     void                (*exit_master)(ngx_cycle_t *cycle);  
  45.   
  46.     /* 保留字段,未被使用 */  
  47.     uintptr_t             spare_hook0;  
  48.     uintptr_t             spare_hook1;  
  49.     uintptr_t             spare_hook2;  
  50.     uintptr_t             spare_hook3;  
  51.     uintptr_t             spare_hook4;  
  52.     uintptr_t             spare_hook5;  
  53.     uintptr_t             spare_hook6;  
  54.     uintptr_t             spare_hook7;  
  55. };  
        在该数据结构中,其中最重要的是两个成员  ctxcommands,这里两个成员会在分别在下面的模块配置指令和模块上下文中讲解;若是 HTTP 模块时, type 字段必须定义为 NGX_HTTP_MODULE

模块配置指令

        模块指令存储在一个  ngx_command_t 类型的静态数组结构中,例如:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. static ngx_command_t  ngx_http_circle_gif_commands[] = {  
  2.     { ngx_string("circle_gif"),  
  3.       NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,  
  4.       ngx_http_circle_gif,  
  5.       NGX_HTTP_LOC_CONF_OFFSET,  
  6.       0,  
  7.       NULL },  
  8.   
  9.     { ngx_string("circle_gif_min_radius"),  
  10.       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,  
  11.       ngx_conf_set_num_slot,  
  12.       NGX_HTTP_LOC_CONF_OFFSET,  
  13.       offsetof(ngx_http_circle_gif_loc_conf_t, min_radius),  
  14.       NULL },  
  15.       ...  
  16.       ngx_null_command  
  17. };  
ngx_command_t 类型定义在  core/ngx_conf_file.h
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. struct ngx_command_s {  
  2.     /* 配置项名称 */  
  3.     ngx_str_t             name;  
  4.     /* 配置项类型,type将指定配置项可以出现的位置以及携带参数的个数 */  
  5.     ngx_uint_t            type;  
  6.     /* 处理配置项的参数 */  
  7.     char               *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);  
  8.     /* 在配置文件中的偏移量,conf与offset配合使用 */  
  9.     ngx_uint_t            conf;  
  10.     ngx_uint_t            offset;  
  11.     /* 配置项读取后的处理方法,必须指向ngx_conf_post_t 结构 */  
  12.     void                 *post;  
  13. };  
name :配置指令的名称; type    :该配置的类型,指定配置项的出现位置以及可携带参数的个数,下面规定只是其中一部分,更多信息可查看文件 core/ngx_conf_file.h
[plain]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. NGX_HTTP_MAIN_CONF:  directive is valid in the main config  
  2. NGX_HTTP_SRV_CONF:   directive is valid in the server (host) config  
  3. NGX_HTTP_LOC_CONF:   directive is valid in a location config  
  4. NGX_HTTP_UPS_CONF:   directive is valid in an upstream config  
  5. NGX_CONF_NOARGS:     directive can take 0 arguments  
  6. NGX_CONF_TAKE1:      directive can take exactly 1 argument  
  7. NGX_CONF_TAKE2:      directive can take exactly 2 arguments  
  8. …  
  9. NGX_CONF_TAKE7:      directive can take exactly 7 arguments  
  10. NGX_CONF_FLAG:       directive takes a boolean ("on" or "off")  
  11. NGX_CONF_1MORE:      directive must be passed at least one argument  
  12. NGX_CONF_2MORE:      directive must be passed at least two arguments  
set     :这是一个函数指针,当 Nginx 在解析配置时,若遇到该配置指令,将会把读取到的值传递给这个函数进行分解处理。因为具体每个配置指令的值如何处理,只有定义这个配置指令的人是最清楚的。来看一下这个函数指针要求的函数原型。
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);  
          该函数处理成功时,返回  NGX_OK,否则返回  NGX_CONF_ERROR 或者是一个自定义的错误信息的字符串。该函数传入三个类型的参数:
  1. cf    :指向ngx_conf_t  结构的指针,该结构包括从配置指令传递的参数;
  2. cmd:指向当前ngx_command_t 结构;
  3. conf:指向模块配置结构;
        为了方便实现对配置指令参数的读取, Nginx 已经默认提供了对一些标准类型的参数进行读取的函数,可以直接赋值给 set 字段使用。下面是一部分已经实现的 set 类型函数,更多可参考文件 core/ngx_conf_file.h
  • ngx_conf_set_flag_slot : 把 "on" 或 "off" 解析为 1 或 0;
  • ngx_conf_set_str_slot   : 解析字符串并保存 ngx_str_t 类型;
  • ngx_conf_set_num_slot: 解析一个数字并将其保存为int 类型;
  • ngx_conf_set_size_slot: 解析数据大小 ("8k", "1m", etc.) 并将其保存为size_t
conf   :用于指示配置项所处内存的相对偏移量,仅在 type 中没有设置 NGX_DIRECT_CONF 和 NGX_MAIN_CONF 时才生效。对于 HTTP 模块, conf 必须设置,它的取值如下:
  • NGX_HTTP_MAIN_CONF_OFFSET:使用create_main_conf 方法产生的结构体来存储解析出的配置项参数;
  • NGX_HTTP_SRV_CONF_OFFSET:使用 create_srv_conf 方法产生的结构体来存储解析出的配置项参数;
  • NGX_HTTP_LOC_CONF_OFFSET:使用 create_loc_conf 方法产生的结构体来存储解析出的配置项参数;
offset :表示当前配置项在整个存储配置项的结构体中的偏移位置。

模块上下文

        这是一个静态的  ngx_http_module_t 结构,它的名称是 ngx_http_<module name>_module_ctx。以下是该结构的定义,具体可查阅文件  http/ngx_http_config.h
  • preconfiguration
  • postconfiguration
  • creating the main conf (i.e., do a malloc and set defaults)
  • initializing the main conf (i.e., override the defaults with what's in nginx.conf)
  • creating the server conf
  • merging it with the main conf
  • creating the location conf
  • merging it with the server conf
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. typedef struct{/* 可以把不需要调用的函数指针设置为 NULL */  
  2.     /* 解析配置文件之前被调用 */  
  3.     ngx_int_t   (*preconfiguration)(ngx_conf_t *cf);  
  4.     /* 完成配置文件的解析后被调用 */  
  5.     ngx_int_t   (*postconfiguration)(ngx_conf_t *cf);  
  6.   
  7.     /* 创建存储main级别的全局配置项的结构体(直属于http块) */  
  8.     void        *(*create_main_conf)(ngx_conf_t *cf);  
  9.     /* 初始化main级别的配置项 */  
  10.     char        *(*init_main_conf)(ngx_conf_t *cf);  
  11.   
  12.     /* 创建存储srv级别的配置项的结构体(直属于server块) */  
  13.     void        *(*create_srv_conf)(ngx_conf_t *cf);  
  14.     /* 合并main级别与srv级别下的同名配置项 */  
  15.     char        *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);  
  16.   
  17.     /* 创建存储loc级别的配置项的结构体(直属于location块) */  
  18.     void        *(*create_loc_conf)(ngx_conf_t *cf);  
  19.     /* 合并srv级别与loc级别下的同名配置项 */  
  20.     char        *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);  
  21. }ngx_http_module_t;  
       在以上的结构内容中,大多数模块只使用最后两项: ngx_http_<module name>_create_loc_confngx_http_<module name >_merge_loc_conf例如:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. static ngx_http_module_t  ngx_http_circle_gif_module_ctx = {  
  2.     NULL,                          /* preconfiguration */  
  3.     NULL,                          /* postconfiguration */  
  4.   
  5.     NULL,                          /* create main configuration */  
  6.     NULL,                          /* init main configuration */  
  7.   
  8.     NULL,                          /* create server configuration */  
  9.     NULL,                          /* merge server configuration */  
  10.   
  11.     ngx_http_circle_gif_create_loc_conf,  /* create location configuration */  
  12.     ngx_http_circle_gif_merge_loc_conf /* merge location configuration */  
  13. };  
       下面针对最后两项进行说明,以下是以  circle_gif 模块为例子,该 模块源码
create_loc_conf 函数
       该函数是传入一个  ngx_conf_t 结构的参数,返回新创建模块的配置结构,在这里是返回: ngx_http_circle_gif_loc_conf_t
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. static void *  
  2. ngx_http_circle_gif_create_loc_conf(ngx_conf_t *cf)  
  3. {  
  4.     ngx_http_circle_gif_loc_conf_t  *conf;  
  5.   
  6.     conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_circle_gif_loc_conf_t));  
  7.     if (conf == NULL) {  
  8.         return NGX_CONF_ERROR;  
  9.     }  
  10.     conf->min_radius = NGX_CONF_UNSET_UINT;  
  11.     conf->max_radius = NGX_CONF_UNSET_UINT;  
  12.     return conf;  
  13. }  
merge_loc_conf 函数
           Nginx 为不同的数据类型提供了 merge 函数,可查阅  core/ngx_conf_file.hmerge_loc_conf 函数定义如下:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. static char *  
  2. ngx_http_circle_gif_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)  
  3. {  
  4.     ngx_http_circle_gif_loc_conf_t *prev = parent;  
  5.     ngx_http_circle_gif_loc_conf_t *conf = child;  
  6.   
  7.     ngx_conf_merge_uint_value(conf->min_radius, prev->min_radius, 10);  
  8.     ngx_conf_merge_uint_value(conf->max_radius, prev->max_radius, 20);  
  9.   
  10.     if (conf->min_radius < 1) {  
  11.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,   
  12.             "min_radius must be equal or more than 1");  
  13.         return NGX_CONF_ERROR;  
  14.     }  
  15.     if (conf->max_radius < conf->min_radius) {  
  16.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,   
  17.             "max_radius must be equal or more than min_radius");  
  18.         return NGX_CONF_ERROR;  
  19.     }  
  20.   
  21.     return NGX_CONF_OK;  
  22. }  

模块的定义

        对任何开发模块,都需要定义一个  ngx_module_t 类型的变量来说明这个模块本身的信息,它告诉了  Nginx 这个模块的一些信息。这个变量是  ngx_http_<module name>_module;例如:更多例子可查找文件  core/ngx_conf_file.h
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. ngx_module_t  ngx_http_<module name>_module = {  
  2.     NGX_MODULE_V1,  
  3.     &ngx_http_<module name>_module_ctx, /* module context */  
  4.     ngx_http_<module name>_commands,   /* module directives */  
  5.     NGX_HTTP_MODULE,               /* module type */  
  6.     NULL,                          /* init master */  
  7.     NULL,                          /* init module */  
  8.     NULL,                          /* init process */  
  9.     NULL,                          /* init thread */  
  10.     NULL,                          /* exit thread */  
  11.     NULL,                          /* exit process */  
  12.     NULL,                          /* exit master */  
  13.     NGX_MODULE_V1_PADDING  
  14. };  

Handler 模块

        Handler 模块必须提供一个真正的处理函数,这个函数负责处理来自客户端的请求。该函数既可以选择自己直接生成内容,也可以选择拒绝处理,并由后续的  Handler 去进行处理,或者是选择丢给后续的  Filter 模块进行处理。以下是该函数的原型:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. typedef ngx_int_t (*ngx_http_handler_pt)(ngx_http_request_t *r);  
  其中 r 是  request 结构 http 请求,包含客户端请求所有的信息,例如: request method, URI, and headers。 该函数处理成功返回 NGX_OK,处理发生错误返回 NGX_ERROR,拒绝处理(留给后续的 Handler 进行处理)返回 NGX_DECLINE。 返回 NGX_OK 也就代表给客户端的响应已经生成,否则返回 NGX_ERROR 就发生错误了。
        Handler 模块处理过程中做了四件事情: 获取 location 配置生成合适的响应发送响应的 header 头部发送响应的 body 包体

获取 location 配置

        获取  location 配置 指向调用  ngx_http_get_module_loc_conf 函数即可,该函数传入的参数是  request 结构和  自定义的  module 模块。例如: circle gif模块;
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. static ngx_int_t  
  2. ngx_http_circle_gif_handler(ngx_http_request_t *r)  
  3. {  
  4.     ngx_http_circle_gif_loc_conf_t  *circle_gif_config;  
  5.     circle_gif_config = ngx_http_get_module_loc_conf(r, ngx_http_circle_gif_module);  
  6.     ...  
  7. }  

生成合适的响应

         这里主要是 request 结构,其定义如下:更多可参考文件   http/ngx_http_request.h
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. typedef struct {  
  2. ...  
  3. /* the memory pool, used in the ngx_palloc functions */  
  4.     ngx_pool_t                       *pool;   
  5.     ngx_str_t                         uri;  
  6.     ngx_str_t                         args;  
  7.     ngx_http_headers_in_t             headers_in;  
  8.   
  9. ...  
  10. } ngx_http_request_t;  
其中参数的意义如下:
  • uri              是 request 请求的路径,e.g. "/query.cgi".
  • args           是请求串参数中问号后面的参数(e.g. "name=john").
  • headers_in 包含有用的stuff,例如:cookies 和browser 信息。

发送响应的 header 头部

       发送响应头部有函数 ngx_http_send_header(r) 实现。响应的 header 头部在  headers_out 结构中,定义如下:更多可参考文件  http/ngx_http_request.h
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. typedef stuct {  
  2. ...  
  3.     ngx_uint_t                        status;  
  4.     size_t                            content_type_len;  
  5.     ngx_str_t                         content_type;  
  6.     ngx_table_elt_t                  *content_encoding;  
  7.     off_t                             content_length_n;  
  8.     time_t                            date_time;  
  9.     time_t                            last_modified_time;  
  10. ..  
  11. } ngx_http_headers_out_t;  
       例如,一个模块设置为  Content-Type to "image/gif", Content-Length to 100, and return a 200 OK response,则其实现为:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. r->headers_out.status = NGX_HTTP_OK;  
  2.     r->headers_out.content_length_n = 100;  
  3.     r->headers_out.content_type.len = sizeof("image/gif") - 1;  
  4.     r->headers_out.content_type.data = (u_char *) "image/gif";  
  5.     ngx_http_send_header(r);  
       假如content_encoding 是 ( ngx_table_elt_t*)类型时,则模块需要为这些类型分配内存,可以调用 ngx_list_push 函数,实现如下:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. r->headers_out.content_encoding = ngx_list_push(&r->headers_out.headers);  
  2. if (r->headers_out.content_encoding == NULL) {  
  3. return NGX_ERROR;  
  4. }  
  5. r->headers_out.content_encoding->hash = 1;  
  6. r->headers_out.content_encoding->key.len = sizeof("Content-Encoding") - 1;  
  7. r->headers_out.content_encoding->key.data = (u_char *) "Content-Encoding";  
  8. r->headers_out.content_encoding->value.len = sizeof("deflate") - 1;  
  9. r->headers_out.content_encoding->value.data = (u_char *) "deflate";  
  10. ngx_http_send_header(r);  

发送响应的 body 包体

       到此,该模块已经产生响应,并把它存储在内存中。发送包体的步骤是:首先分配响应特殊的缓冲区,然后分配缓冲区链接到 chain link,然后在  chain link 调用发送函数。
       1、chain links 是 Nginx  使 Handler 模块在缓冲区中产生响应。在 chain 中每个 chain link 有一个指向下一个 link 的指针。首先,模块声明缓冲区 buffer 和 chain link:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. ngx_buf_t    *b;  
  2. ngx_chain_t   out;  
2、然后分配缓冲区 buffer,使响应数据指向它:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));  
  2.     if (b == NULL) {  
  3.         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,   
  4.             "Failed to allocate response buffer.");  
  5.         return NGX_HTTP_INTERNAL_SERVER_ERROR;  
  6.     }  
  7.   
  8.     b->pos = some_bytes; /* first position in memory of the data */  
  9.     b->last = some_bytes + some_bytes_length; /* last position */  
  10.   
  11.     b->memory = 1; /* content is in read-only memory */  
  12.     /* (i.e., filters should copy it rather than rewrite in place) */  
  13.   
  14.     b->last_buf = 1; /* there will be no more buffers in the request */  
3、接着,把模块挂载到 chain link 上:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. out.buf = b;  
  2. out.next = NULL;  
4、最后,发送包体:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. return ngx_http_output_filter(r, &out);  

Handler 模块挂载

        Handler 模块真正的处理函数通过两种方式挂载到处理过程中:按处理阶段挂载;按需挂载。
按处理阶段挂载
        为了更精细地控制对于客户端请求的处理过程, Nginx 把这个处理过程划分成了11个阶段。依次列举如下:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. NGX_HTTP_POST_READ_PHASE:  
  2. 读取请求内容阶段  
  3. NGX_HTTP_SERVER_REWRITE_PHASE:  
  4. Server请求地址重写阶段  
  5. NGX_HTTP_FIND_CONFIG_PHASE:  
  6. 配置查找阶段:  
  7. NGX_HTTP_REWRITE_PHASE:  
  8. Location请求地址重写阶段  
  9. NGX_HTTP_POST_REWRITE_PHASE:  
  10. 请求地址重写提交阶段  
  11. NGX_HTTP_PREACCESS_PHASE:  
  12. 访问权限检查准备阶段  
  13. NGX_HTTP_ACCESS_PHASE:  
  14. 访问权限检查阶段  
  15. NGX_HTTP_POST_ACCESS_PHASE:  
  16. 访问权限检查提交阶段  
  17. NGX_HTTP_TRY_FILES_PHASE:  
  18. 配置项try_files处理阶段  
  19. NGX_HTTP_CONTENT_PHASE:  
  20. 内容产生阶段  
  21. NGX_HTTP_LOG_PHASE:  
  22. 日志模块处理阶段  
        一般情况下,我们自定义的模块,大多数是挂载在 NGX_HTTP_CONTENT_PHASE阶段的。挂载的动作一般是在模块上下文调用的 postconfiguration 函数中。注意:有几个阶段是特例,它不调用挂载任何的 Handler,也就是你就不用挂载到这几个阶段了:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. NGX_HTTP_FIND_CONFIG_PHASE  
  2. NGX_HTTP_POST_ACCESS_PHASE  
  3. NGX_HTTP_POST_REWRITE_PHASE  
  4. NGX_HTTP_TRY_FILES_PHASE  
按需挂载
        以这种方式挂载的 Handler 也被称为 content handler。当一个请求进来以后, Nginx 从 NGX_HTTP_POST_READ_PHASE 阶段开始依次执行每个阶段中所有 Handler。执行到   NGX_HTTP_CONTENT_PHASE 阶段时,如果这个 location 有一个对应的 content handler 模块,那么就去执行这个 content handler 模块真正的处理函数。否则继续依次执行 NGX_HTTP_CONTENT_PHASE 阶段中所有 content phase handlers,直到某个函数处理返回 NGX_OK 或者 NGX_ERROR。但是使用这个方法挂载上去的 handler 有一个特点是必须在 NGX_HTTP_CONTENT_PHASE 阶段才能被执行。如果你想自己的 handler 更早的阶段被执行,那就不要使用这种挂载方式。         以下是例子:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. circle gif ngx_command_t looks like this:  
  2.   
  3.     { ngx_string("circle_gif"),  
  4.       NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,  
  5.       ngx_http_circle_gif,  
  6.       0,  
  7.       0,  
  8.       NULL }  
挂载函数:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. static char *  
  2. ngx_http_circle_gif(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)  
  3. {  
  4.     ngx_http_core_loc_conf_t  *clcf;  
  5.   
  6.     clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);  
  7.     clcf->handler = ngx_http_circle_gif_handler;  
  8.   
  9.     return NGX_CONF_OK;  
  10. }  

Handler 模块编写

Handler 模块编写步骤如下:
  1. 编写模块基本结构:包括模块的定义,模块上下文结构,模块的配置结构等;
  2. 实现 handler 的挂载函数;根据模块的需求选择正确的挂载方式;
  3. 编写 handler 处理函数;模块的功能主要通过这个函数来完成;

Filter 模块

        Filter 处理由 Handler 模块产生的响应。 Filter 模块包括过滤头部( Header Filter)和过滤包体( Body Filter ), Filter 模块过滤头部处理 HTTP 的头部( HTTP headers), Filter 包体处理响应内容( response content),这两个阶段可以对 HTTP 响应头部和内容进行修改。

Filter 模块相关结构

        Filter 模块是采用链表形式的,其基本结构是 ngx_chain_t 和  ngx_buf_t;这两种结构定义如下:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. typedef struct ngx_chain_s ngx_chain_t;  
  2.   
  3.         struct ngx_chain_s {  
  4.                 ngx_buf_t    *buf;  
  5.                 ngx_chain_t  *next;  
  6.         };  
  7.   
  8. struct ngx_buf_s {  
  9.         u_char          *pos;       /* 当前buffer真实内容的起始位置 */  
  10.         u_char          *last;      /* 当前buffer真实内容的结束位置 */  
  11.         off_t            file_pos;  /* 在文件中真实内容的起始位置   */  
  12.         off_t            file_last; /* 在文件中真实内容的结束位置   */  
  13.   
  14.         u_char          *start;    /* buffer内存的开始分配的位置 */  
  15.         u_char          *end;      /* buffer内存的结束分配的位置 */  
  16.         ngx_buf_tag_t    tag;      /* buffer属于哪个模块的标志 */  
  17.         ngx_file_t      *file;     /* buffer所引用的文件 */  
  18.   
  19.         /* 用来引用替换过后的buffer,以便当所有buffer输出以后, 
  20.          * 这个影子buffer可以被释放。 
  21.          */  
  22.         ngx_buf_t       *shadow;  
  23.   
  24.         /* the buf's content could be changed */  
  25.         unsigned         temporary:1;  
  26.   
  27.         /* 
  28.          * the buf's content is in a memory cache or in a read only memory 
  29.          * and must not be changed 
  30.          */  
  31.         unsigned         memory:1;  
  32.   
  33.         /* the buf's content is mmap()ed and must not be changed */  
  34.         unsigned         mmap:1;  
  35.   
  36.         unsigned         recycled:1; /* 内存可以被输出并回收 */  
  37.         unsigned         in_file:1;  /* buffer的内容在文件中 */  
  38.         /* 马上全部输出buffer的内容, gzip模块里面用得比较多 */  
  39.         unsigned         flush:1;  
  40.         /* 基本上是一段输出链的最后一个buffer带的标志,标示可以输出, 
  41.          * 有些零长度的buffer也可以置该标志 
  42.          */  
  43.         unsigned         sync:1;  
  44.         /* 所有请求里面最后一块buffer,包含子请求 */  
  45.         unsigned         last_buf:1;  
  46.         /* 当前请求输出链的最后一块buffer         */  
  47.         unsigned         last_in_chain:1;  
  48.         /* shadow链里面的最后buffer,可以释放buffer了 */  
  49.         unsigned         last_shadow:1;  
  50.         /* 是否是暂存文件 */  
  51.         unsigned         temp_file:1;  
  52.   
  53.         /* 统计用,表示使用次数 */  
  54.         /* STUB */ int   num;  
  55. };  

Filter 过滤头部

header filter 包含三个基本步骤:
  1. 决定是否处理响应;
  2. 对响应进行处理;
  3. 调用下一个 filter
        例如下面的 "not modified" header filter:其中  headers_out 结构可参考文件  http/ngx_http_request.h
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. static  
  2. ngx_int_t ngx_http_not_modified_header_filter(ngx_http_request_t *r)  
  3. {  
  4. time_t if_modified_since;  
  5.   
  6. if_modified_since = ngx_http_parse_time(r->headers_in.if_modified_since->value.data,  
  7. r->headers_in.if_modified_since->value.len);  
  8.   
  9. /* step 1: decide whether to operate */  
  10. if (if_modified_since != NGX_ERROR &&   
  11. if_modified_since == r->headers_out.last_modified_time) {  
  12.   
  13. /* step 2: operate on the header */  
  14. r->headers_out.status = NGX_HTTP_NOT_MODIFIED;  
  15. r->headers_out.content_type.len = 0;  
  16. ngx_http_clear_content_length(r);  
  17. ngx_http_clear_accept_ranges(r);  
  18. }  
  19.   
  20. /* step 3: call the next filter */  
  21. return ngx_http_next_header_filter(r);  
  22. }  

Filter 过滤包体

        Filter 包体只能在 chain link缓冲区 buffer 中操作。模块必须决定是否修改输入缓冲区,或分配新的缓冲区替换当前缓冲区,或是在当前缓冲区之后还是之前插入新的缓冲区。很多模块接收多个缓冲区,导致这些模块在不完整的 chain 缓冲区中操作。 Filter 包体操作如下:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. static ngx_int_t ngx_http_chunked_body_filter(ngx_http_request_t *r, ngx_chain_t *in);  
  以下是一个例子:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /* 
  2.  * Let's take a simple example. 
  3.  * Suppose we want to insert the text "<l!-- Served by Nginx -->" to the end of every request. 
  4.  * First, we need to figure out if the response's final buffer is included in the buffer chain we were given. 
  5.  * Like I said, there's not a fancy API, so we'll be rolling our own for loop: 
  6.  */  
  7.   
  8.     ngx_chain_t *chain_link;  
  9.     int chain_contains_last_buffer = 0;  
  10.   
  11.     chain_link = in;  
  12.     for ( ; ; ) {  
  13.         if (chain_link->buf->last_buf)  
  14.             chain_contains_last_buffer = 1;  
  15.         if (chain_link->next == NULL)  
  16.             break;  
  17.         chain_link = chain_link->next;  
  18.     }  
  19. /* 
  20.  * Now let's bail out if we don't have that last buffer: 
  21.  */  
  22.   
  23.     if (!chain_contains_last_buffer)  
  24.         return ngx_http_next_body_filter(r, in);  
  25. /* 
  26.  * Super, now the last buffer is stored in chain_link. 
  27.  * Now we allocate a new buffer: 
  28.  */  
  29.   
  30.     ngx_buf_t    *b;  
  31.     b = ngx_calloc_buf(r->pool);  
  32.     if (b == NULL) {  
  33.         return NGX_ERROR;  
  34.     }  
  35. /* 
  36.  * And put some data in it: 
  37.  */  
  38.   
  39.     b->pos = (u_char *) "<!-- Served by Nginx -->";  
  40.     b->last = b->pos + sizeof("<!-- Served by Nginx -->") - 1;  
  41. /* 
  42.  * And hook the buffer into a new chain link: 
  43.  */  
  44.   
  45.     ngx_chain_t   *added_link;  
  46.   
  47.     added_link = ngx_alloc_chain_link(r->pool);  
  48.     if (added_link == NULL)  
  49.         return NGX_ERROR;  
  50.   
  51.     added_link->buf = b;  
  52.     added_link->next = NULL;  
  53. /* 
  54.  * Finally, hook the new chain link to the final chain link we found before: 
  55.  */  
  56.   
  57.     chain_link->next = added_link;  
  58. /* 
  59.  * And reset the "last_buf" variables to reflect reality: 
  60.  */  
  61.   
  62.     chain_link->buf->last_buf = 0;  
  63.     added_link->buf->last_buf = 1;  
  64. /* 
  65.  * And pass along the modified chain to the next output filter: 
  66.  */  
  67.   
  68.     return ngx_http_next_body_filter(r, in);  
  69.   
  70. /* 
  71.  * The resulting function takes much more effort than what you'd do with, say, mod_perl ($response->body =~ s/$/<!-- Served by mod_perl -->/), 
  72.  * but the buffer chain is a very powerful construct, allowing programmers to process data incrementally so that the client gets something as soon as possible. 
  73.  * However, in my opinion, the buffer chain desperately needs a cleaner interface so that programmers can't leave the chain in an inconsistent state. 
  74.  * For now, manipulate it at your own risk. 
  75.  */  

Filter 模块挂载

        Filters 模块和 Handler 模块一样,也是挂载到 post-configuration ,如下面代码所示:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. static ngx_http_module_t ngx_http_chunked_filter_module_ctx = {  
  2. NULL, /* preconfiguration */  
  3. ngx_http_chunked_filter_init, /* postconfiguration */  
  4. ...  
  5. };  
        其中  ngx_http_chunked_filter_init 处理如下定义:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. static ngx_int_t  
  2. ngx_http_chunked_filter_init(ngx_conf_t *cf)  
  3. {  
  4. ngx_http_next_header_filter = ngx_http_top_header_filter;  
  5. ngx_http_top_header_filter = ngx_http_chunked_header_filter;  
  6.   
  7. ngx_http_next_body_filter = ngx_http_top_body_filter;  
  8. ngx_http_top_body_filter = ngx_http_chunked_body_filter;  
  9.   
  10. return NGX_OK;  
  11. }  
由于  Filter 模块是 “ CHAIN OF RESPONSIBILITY” 链表模式的。 Handler 模块生成响应后, Filter 模块调用两个函数: ngx_http_output_filter和  ngx_http_send_header,其中 ngx_http_output_filter 函数是调用全局函数  ngx_http_top_body_filterngx_http_send_header 函数是调用全局函数  ngx_http_top_header_filter
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. ngx_int_t  
  2. ngx_http_send_header(ngx_http_request_t *r)  
  3. {  
  4.         ...  
  5.   
  6.         return ngx_http_top_header_filter(r);  
  7. }  
  8.   
  9. ngx_int_t  
  10. ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in)  
  11. {  
  12.     ngx_int_t          rc;  
  13.     ngx_connection_t  *c;  
  14.   
  15.     c = r->connection;  
  16.   
  17.     rc = ngx_http_top_body_filter(r, in);  
  18.   
  19.     if (rc == NGX_ERROR) {  
  20.         /* NGX_ERROR may be returned by any filter */  
  21.         c->error = 1;  
  22.     }  
  23.   
  24.     return rc;  
  25. }  
Filter 模块的指行方式如下图所示:

开发 Nginx 新模块

        把自己开发的模块编译到 N ginx 中需要编写两个文件:
  1. "config",该文件会被 ./configure 包含;
  2. "ngx_http_<your module>_module.c",该文件是定义模块的功能;
        config 文件的编写如下:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. /* 
  2.  * "config" for filter modules: 
  3.  */  
  4.   
  5. ngx_addon_name=ngx_http_<your module>_module  /* 模块的名称 */  
  6. HTTP_AUX_FILTER_MODULES="$HTTP_AUX_FILTER_MODULES ngx_http_<your module>_module"  /* 保存所有 HTTP 模块*/  
  7. NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_<your module>_module.c"       /* 指定新模块的源码路径 */  
  8.   
  9. /* 
  10.  * "config" for other modules: 
  11.  */  
  12.   
  13. ngx_addon_name=ngx_http_<your module>_module  
  14. HTTP_MODULES="$HTTP_MODULES ngx_http_<your module>_module"  
  15. NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_<your module>_module.c"  
        关于  "ngx_http_<your module>_module.c" 文件的编写,可参考上面的 Handler 模块,同时可参考 Nginx 现有的模块: src/http/modules/ 例如下面的 “Hello World ” 代码
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #include <ngx_config.h>  
  2. #include <ngx_core.h>  
  3. #include <ngx_http.h>  
  4.   
  5.   
  6. typedef struct  
  7. {  
  8.         ngx_str_t hello_string;  
  9.         ngx_int_t hello_counter;  
  10. }ngx_http_hello_loc_conf_t;  
  11.   
  12. static ngx_int_t ngx_http_hello_init(ngx_conf_t *cf);  
  13.   
  14. static void *ngx_http_hello_create_loc_conf(ngx_conf_t *cf);  
  15.   
  16. static char *ngx_http_hello_string(ngx_conf_t *cf, ngx_command_t *cmd,  
  17.         void *conf);  
  18. static char *ngx_http_hello_counter(ngx_conf_t *cf, ngx_command_t *cmd,  
  19.         void *conf);  
  20.   
  21. static ngx_command_t ngx_http_hello_commands[] = {  
  22.    {  
  23.                 ngx_string("hello_string"),  
  24.                 NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS|NGX_CONF_TAKE1,  
  25.                 ngx_http_hello_string,  
  26.                 NGX_HTTP_LOC_CONF_OFFSET,  
  27.                 offsetof(ngx_http_hello_loc_conf_t, hello_string),  
  28.                 NULL },  
  29.   
  30.         {  
  31.                 ngx_string("hello_counter"),  
  32.                 NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,  
  33.                 ngx_http_hello_counter,  
  34.                 NGX_HTTP_LOC_CONF_OFFSET,  
  35.                 offsetof(ngx_http_hello_loc_conf_t, hello_counter),  
  36.                 NULL },  
  37.   
  38.         ngx_null_command  
  39. };  
  40.   
  41.   
  42. /* 
  43. static u_char ngx_hello_default_string[] = "Default String: Hello, world!"; 
  44. */  
  45. static int ngx_hello_visited_times = 0;  
  46.   
  47. static ngx_http_module_t ngx_http_hello_module_ctx = {  
  48.         NULL,                          /* preconfiguration */  
  49.         ngx_http_hello_init,           /* postconfiguration */  
  50.   
  51.         NULL,                          /* create main configuration */  
  52.         NULL,                          /* init main configuration */  
  53.   
  54.         NULL,                          /* create server configuration */  
  55.         NULL,                          /* merge server configuration */  
  56.   
  57.         ngx_http_hello_create_loc_conf, /* create location configuration */  
  58.         NULL                            /* merge location configuration */  
  59. };  
  60.   
  61.   
  62. ngx_module_t ngx_http_hello_module = {  
  63.         NGX_MODULE_V1,  
  64.         &ngx_http_hello_module_ctx,    /* module context */  
  65.         ngx_http_hello_commands,       /* module directives */  
  66.         NGX_HTTP_MODULE,               /* module type */  
  67.         NULL,                          /* init master */  
  68.         NULL,                          /* init module */  
  69.         NULL,                          /* init process */  
  70.         NULL,                          /* init thread */  
  71.         NULL,                          /* exit thread */  
  72.         NULL,                          /* exit process */  
  73.         NULL,                          /* exit master */  
  74.         NGX_MODULE_V1_PADDING  
  75. };  
  76.   
  77.   
  78. static ngx_int_t  
  79. ngx_http_hello_handler(ngx_http_request_t *r)  
  80. {  
  81.         ngx_int_t    rc;  
  82.         ngx_buf_t   *b;  
  83.         ngx_chain_t  out;  
  84.         ngx_http_hello_loc_conf_t* my_conf;  
  85.         u_char ngx_hello_string[1024] = {0};  
  86.         ngx_uint_t content_length = 0;  
  87.   
  88.         ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, "ngx_http_hello_handler is called!");  
  89.   
  90.         my_conf = ngx_http_get_module_loc_conf(r, ngx_http_hello_module);  
  91.         if (my_conf->hello_string.len == 0 )  
  92.         {  
  93.                 ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, "hello_string is empty!");  
  94.                 return NGX_DECLINED;  
  95.         }  
  96.   
  97.   
  98.         if (my_conf->hello_counter == NGX_CONF_UNSET  
  99.                 || my_conf->hello_counter == 0)  
  100.         {  
  101.                 ngx_sprintf(ngx_hello_string, "%s", my_conf->hello_string.data);  
  102.         }  
  103.         else  
  104.         {  
  105.                 ngx_sprintf(ngx_hello_string, "%s Visited Times:%d", my_conf->hello_string.data,  
  106.                         ++ngx_hello_visited_times);  
  107.         }  
  108.         ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, "hello_string:%s", ngx_hello_string);  
  109.         content_length = ngx_strlen(ngx_hello_string);  
  110.   
  111.         /* we response to 'GET' and 'HEAD' requests only */  
  112.         if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {  
  113.                 return NGX_HTTP_NOT_ALLOWED;  
  114.         }  
  115.   
  116.         /* discard request body, since we don't need it here */  
  117.         rc = ngx_http_discard_request_body(r);  
  118.   
  119.         if (rc != NGX_OK) {  
  120.                 return rc;  
  121.         }  
  122.   
  123.         /* set the 'Content-type' header */  
  124.         /* 
  125.          *r->headers_out.content_type.len = sizeof("text/html") - 1; 
  126.          *r->headers_out.content_type.data = (u_char *)"text/html"; 
  127.          */  
  128.         ngx_str_set(&r->headers_out.content_type, "text/html");  
  129.   
  130.   
  131.         /* send the header only, if the request type is http 'HEAD' */  
  132.         if (r->method == NGX_HTTP_HEAD) {  
  133.                 r->headers_out.status = NGX_HTTP_OK;  
  134.                 r->headers_out.content_length_n = content_length;  
  135.   
  136.                 return ngx_http_send_header(r);  
  137.         }  
  138.   
  139.         /* allocate a buffer for your response body */  
  140.         b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));  
  141.         if (b == NULL) {  
  142.                 return NGX_HTTP_INTERNAL_SERVER_ERROR;  
  143.         }  
  144.   
  145.         /* attach this buffer to the buffer chain */  
  146.         out.buf = b;  
  147.         out.next = NULL;  
  148.   
  149.         /* adjust the pointers of the buffer */  
  150.         b->pos = ngx_hello_string;  
  151.         b->last = ngx_hello_string + content_length;  
  152.         b->memory = 1;    /* this buffer is in memory */  
  153.         b->last_buf = 1;  /* this is the last buffer in the buffer chain */  
  154.   
  155.         /* set the status line */  
  156.         r->headers_out.status = NGX_HTTP_OK;  
  157.         r->headers_out.content_length_n = content_length;  
  158.   
  159.         /* send the headers of your response */  
  160.         rc = ngx_http_send_header(r);  
  161.   
  162.         if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {  
  163.                 return rc;  
  164.         }  
  165.   
  166.         /* send the buffer chain of your response */  
  167.         return ngx_http_output_filter(r, &out);  
  168. }  
  169.   
  170. static void *ngx_http_hello_create_loc_conf(ngx_conf_t *cf)  
  171. {  
  172.         ngx_http_hello_loc_conf_t* local_conf = NULL;  
  173.         local_conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_hello_loc_conf_t));  
  174.         if (local_conf == NULL)  
  175.         {  
  176.                 return NULL;  
  177.         }  
  178.   
  179.         ngx_str_null(&local_conf->hello_string);  
  180.         local_conf->hello_counter = NGX_CONF_UNSET;  
  181.   
  182.         return local_conf;  
  183. }  
  184.   
  185. /* 
  186. static char *ngx_http_hello_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) 
  187. { 
  188.         ngx_http_hello_loc_conf_t* prev = parent; 
  189.         ngx_http_hello_loc_conf_t* conf = child; 
  190.  
  191.         ngx_conf_merge_str_value(conf->hello_string, prev->hello_string, ngx_hello_default_string); 
  192.         ngx_conf_merge_value(conf->hello_counter, prev->hello_counter, 0); 
  193.  
  194.         return NGX_CONF_OK; 
  195. }*/  
  196.   
  197. static char *  
  198. ngx_http_hello_string(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)  
  199. {  
  200.   
  201.         ngx_http_hello_loc_conf_t* local_conf;  
  202.   
  203.   
  204.         local_conf = conf;  
  205.         char* rv = ngx_conf_set_str_slot(cf, cmd, conf);  
  206.   
  207.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "hello_string:%s", local_conf->hello_string.data);  
  208.   
  209.         return rv;  
  210. }  
  211.   
  212.   
  213. static char *ngx_http_hello_counter(ngx_conf_t *cf, ngx_command_t *cmd,  
  214.         void *conf)  
  215. {  
  216.         ngx_http_hello_loc_conf_t* local_conf;  
  217.   
  218.         local_conf = conf;  
  219.   
  220.         char* rv = NULL;  
  221.   
  222.         rv = ngx_conf_set_flag_slot(cf, cmd, conf);  
  223.   
  224.   
  225.         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "hello_counter:%d", local_conf->hello_counter);  
  226.         return rv;  
  227. }  
  228.   
  229. static ngx_int_t  
  230. ngx_http_hello_init(ngx_conf_t *cf)  
  231. {  
  232.         ngx_http_handler_pt        *h;  
  233.         ngx_http_core_main_conf_t  *cmcf;  
  234.   
  235.         cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);  
  236.   
  237.         h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);  
  238.         if (h == NULL) {  
  239.                 return NGX_ERROR;  
  240.         }  
  241.   
  242.         *h = ngx_http_hello_handler;  
  243.   
  244.         return NGX_OK;  
  245. }  
 
 
        写好上面的两个文件后,在编译 Nginx 时,步骤如下:
[python]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. ./configure --add-module=path/to/your/new/module/directory  
  2. make  
  3. make install  

参考资料:
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值