nginx模块要负责三种角色
handler:接收请求+产生Output
filters:处理hander产生的output
load-balancer:负载均衡,选择一个后端server发送请求(如果把nginx当做负载均衡服务器的话,这个角色必须实现)
1 nginx内部流程
客户端发送http请求 -- nginx根据配置文件conf中的location来确定由哪个handler处理-- handler执行完request返回output给filter--第一个filter处理output -- 第二个filter处理output--- … -- 生成Response。
Nginx模块的几个数据结构
1 Module Configuration Struct(s) 模块配置结构
2 Module Directives 模块命令结构
3 The Module Context模块内容
3.1 create_loc_conf
3.2 merge_loc_conf
4 The Module Definition模块整合
5 Module Installation模块安装
2模块配置
这个结构的命名规则为ngx_http_[module-name]_[main|srv|loc]_conf_t。
main,srv,loc表示这个模块的作用范围是配置文件中的main/server/location三种范围(这个需要记住,后面会经常用到)
例子:
typedef struct {
ngx_str_t ed; //echo模块只有一个参数比如 echo "hello"
} ngx_http_echo_loc_conf_t; //echo 模块
3模块命令ngx_command_t
对应的结构体ngx_command_t(静态数组里的每一个元素)的定义 , 你可以在core/ngx_conf_file.h找到它:
struct ngx_command_t {
ngx_str_t name;
ngx_uint_t type;
char *(*set)(ngx_conf_t *cf,ngx_command_t *cmd, void *conf);
ngx_uint_t conf;
ngx_uint_t offset;
void *post;
};
第一项参数ngx_string("hello")为指令的名称,不能包含空格.它的类型是ngx_str_t,注意: ngx_str_t 包含一个存放字符串内容的data字段,和一个存放字符串长度的len字段。Nginx广
泛地使用这个类型来存放字串。
第二项参数用来设置指令在配置文件位置的哪一部分使用是合法的,可选值如下,多个选项以‘|’隔开:
* NGX_HTTP_MAIN_CONF: 指令出现在main配置部分是合法的
* NGX_HTTP_SRV_CONF: 指令在server配置部分出现是合法的config
* NGX_HTTP_LOC_CONF: 指令在location配置部分出现是合法的
* NGX_HTTP_UPS_CONF: 指令在upstream配置部分出现是合法的
* NGX_CONF_NOARGS: 指令没有参数
* NGX_CONF_TAKE1: 指令读入1个参数
* NGX_CONF_TAKE2: 指令读入2个参数
* ...
* NGX_CONF_TAKE7: 指令读入7个参数
* NGX_CONF_FLAG: 指令读入1个布尔型数据("on" or "off")
* NGX_CONF_1MORE: 指令至少读入1个参数
* NGX_CONF_2MORE: 指令至少读入2个参数
第三项参数ngx_http_hello是一个回调函数,设定函数有三个入参:
1. 指向结构体ngx_conf_t 的指针, 这个结构体里包含需要传递给指令的参数
2. 指向结构体ngx_command_t 的指针
3. 指向模块自定义配置结构体的指针
设定函数会在遇到指令时执行,Nginx提供了多个函数用来保存特定类型的数据,这些函数包含有:
* ngx_conf_set_flag_slot: 将"on" or "off" 转换成 1 or 0
* ngx_conf_set_str_slot: 将字符串保存为ngx_str_t
* ngx_conf_set_num_slot: 解析一个数字并保存为int
* ngx_conf_set_size_slot: 解析一个数据大小(如:"8k","1m") 并保存为size_t
第四项参数用于告诉Nginx是要把保持的值放在main配置、server 配置, 还是 location 配置 (通过NGX_HTTP_MAIN_CONF_OFFSET, NGX_HTTP_SRV_CONF_OFFSET, 或者
NGX_HTTP_LOC_CONF_OFFSET)。
第五项参数用于设置指令的值保存在结构体的哪个位置。
第六项参数指向模块在读配置的时候需要的一些零碎变量。一般它是NULL。
ngx_command_t数组以ngx_null_command 为终结符(就好像字符串以'\0'为终结符一样)。
例如:
static ngx_command_t ngx_http_echo_commands[] = {
{ ngx_string("echo"), //命令名字
NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, //代表是local配置,带一个参数
ngx_http_echo, //组装模块配置结构
NGX_HTTP_LOC_CONF_OFFSET, //上面的组装模块配置获取完参数后存放到哪里?使用这个和下面的offset参数来进行定位
offsetof(ngx_http_echo_loc_conf_t, ed), //同上
NULL // Finally, post is just a pointer to other crap the modulemight need while it's reading the configuration. It's often NULL.我也没理解是什么意思。通常情况下设置
为NULL
},
ngx_null_command //必须使用ngx_null_command作为commands的结束标记
};
4模块内容ngx_http_module_t
静态ngx_http_module_t结构体,包含很多函数的引用,用来创建3个部分的配置和合并配置。一般结构题命名为ngx_http_<module name>_module_ctx。
模块的内容ngx_http_<modulename>_module_ctx是为了定义各种钩子函数,就是nginx在各个不同的时期将会运行的函数。绝大多数的只使用最后两个: 一个用来为特定location配置来分配内存,(叫做 ngx_http_<module name>_create_loc_conf), 另一个用来设定默认值以及合并继承过来的配置值(叫做 ngx_http_<module name >_merge_loc_conf)。合并函数同时还会检查配置的有效性,如果有错误,则server的启动将被挂起。
例如:
static ngx_http_module_t ngx_http_circle_gif_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration 这里是放置filter的地方,在filter章节会说*/
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_circle_gif_create_loc_conf, /* create locationconfiguration */
ngx_http_circle_gif_merge_loc_conf /* merge location configuration */
};
5模块整合ngx_module_t
nginx中所有模块的类型都是ngx_module_t类型的,定义了模块的一些属性。nginx是完全模块化的,所有的组件都是模块,从而实现了nginx的高度松耦合。同时,我们在进行nginx模块开发时,也离不开这个数据结构。
struct ngx_module_s {
/* 在具体类型模块(http、event等)的全局配置结构数组的下标。以http module模块为例, nginx把所有的httpmodule的config信息存放在ngx_http_conf_ctx_t类型的变量中,这个变量只有3个属性,分别是所有httpmodule的main、srv、loc的config信息的数组。 如果该模块是httpmodule,则ctx_index是该模块的config信息(main、srv、loc)在ngx_http_conf_ctx_t中的下标。 */
ngx_uint_t ctx_index;
/*nginx把所有模块(ngx_module_t)存放到ngx_modules数组中,这个数组在nginx源码路径的objs/ngx_modules.c中,是在运行configure脚本后生成的。index属性就是该模块在ngx_modules数组中的下标。同时nginx把所有的coremodule的配置结构存放到ngx_cycle的conf_ctx数组中,index也是该模块的配置结构在ngx_cycle->conf_ctx数组中的下标。*/
ngx_uint_t index;
……
/*模块的上下文属性,同一类型的模块的属性是相同的,比如core module的ctx是ngx_core_module_t类型。而http module的ctx是ngx_http_moduel_t类型,eventmodule的ctx是ngx_event_module_t类型等等。相应类型的模块由分开处理的,比如所有的http module由ngx_http_module解析处理,而所有的event module由ngx_events_module解析处理。*/
void *ctx;
/*该模块支持的指令的数组,最后以一个空指令结尾。ngx_commond_t的分析见下文。 */
ngx_command_t *commands;
/*模块的类型,nginx所有的模块类型:
* NGX_CORE_MODULE
* NGX_CONF_MODULE
* NGX_HTTP_MODULE
* NGX_EVENT_MODULE
* NGX_MAIL_MODULE
* 这些不同的类型也指定了不同的ctx。 */
ngx_uint_t type; /* 接下来都是一些回调函数,在nginx初始化过程的特定时间点调用*/
ngx_int_t (*init_master)(ngx_log_t *log); /*主进程初始化*/
ngx_int_t (*init_module)(ngx_cycle_t *cycle); /* 模块初始化时调用 */
ngx_int_t (*init_process)(ngx_cycle_t *cycle); /*工作进程初始化*/
ngx_int_t (*init_thread)(ngx_cycle_t *cycle);
void (*exit_thread)(ngx_cycle_t*cycle);
void (*exit_process)(ngx_cycle_t*cycle);
void (*exit_master)(ngx_cycle_t*cycle);
……
};
一些执行问题:
workers=4,mastar=on,daemon=on,启动执行postconfiguration,init module,reload只执行postconfiguration。
workers=4,mastar=on,daemon=off,启动执行postconfiguration,init module,4次init_process,reload重新再打印一次。
workers=4,mastar=off,daemon=off,启动执行postconfiguration,init module,1次init_process,reload只执行postconfiguration,init module。
workers=4,mastar=off,daemon=on,启动执行postconfiguration,init module,reload只执行postconfiguration。
例如:
定义结构体ngx_http_hello_module,代码如下:
ngx_module_t ngx_http_hello_module = {
NGX_MODULE_V1, //#define NGX_MODULE_V1 0, 0, 0, 0, 0, 0, 1
&ngx_http_hello_module_ctx,
ngx_http_hello_commands,
NGX_HTTP_MODULE,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NGX_MODULE_V1_PADDING
};
结构体ngx_module_t。该结构体变量命名方式为ngx_http_<modulename>_module。它包含模块的主要内容和指令执行部分,同时也还包含一些回调函数(退出线程,退出进程,等等)。
模块定义在有的时候会被用作查找的关键字,来查找与特定模块相关联的数据。模块可以添加一些回调函数来处理线程/进程的创建和销毁,但是绝大多数模块都用NULL忽略这些东东。
6模块安装
模块安装文件的编写依赖于这个模块是handler,filter还是load-balancer的工作角色下面开始是Handler,filter,load-balancer的编写和安装。这个语句就是handler的安装。
1 Handler安装
还记得在模块命令的时候有设置handler的语句吗?
clcf->handler = ngx_http_echo_handler;
这个语句就是handler的安装
2 Handler编写
Handler的执行有四部:
读入模块配置
处理功能业务
产生HTTP header
产生HTTP body
读入模块配置
例子:
static ngx_int_t
ngx_http_circle_gif_handler(ngx_http_request_t *r)
{
ngx_http_circle_gif_loc_conf_t *circle_gif_config;
circle_gif_config = ngx_http_get_module_loc_conf(r,ngx_http_circle_gif_module);
...
就是使用nginx已经有的函数ngx_http_get_module_loc_conf,第一个参数是当前请求,第二个参数是前面写好的模块。
处理功能业务
这个部分是我们要模块处理的实际部
用需求举例:
这个模块有个命令是 getRedisInfo 192.168.0.1 //获取redis的信息
那么这个功能业务就是(伪代码):
casecmd->opcode
{
"getRedisInfo":
获取redis的信息
}
这里应该把所有这个模块设置的命令的业务逻辑都写好
产生HTTPHeader
例子:
r->headers_out.status= NGX_HTTP_OK;
r->headers_out.content_length_n= 100;
r->headers_out.content_type.len= sizeof("image/gif") - 1;
r->headers_out.content_type.data= (u_char *) "image/gif";
ngx_http_send_header(r);
产生HTTPBody
这个部分是最重要的一步
借用codingLabs的图讲解一下nginx的IO
handler是可以一次产生出一个输出,也可以产生出多个输出使用ngx_chain_t的链表来进行连接
struct ngx_chain_s {
ngx_buf_t *buf;
ngx_chain_t *next;
};
buf中有pos和last来代表out数据在内存中的位置,next是代表下一个ngx_chain_t
下面来说一下只有一个ngx_chain_t的设置
1 申明
ngx_buf_t *b;
ngx_chain_t out
2 设置buffer
b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
if (b == NULL) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"Failed to allocate response buffer.");
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
b->pos = some_bytes; /* first position in memory of thedata */
b->last = some_bytes + some_bytes_length; /* lastposition */
b->memory = 1; /* content is in read-only memory */
/* (i.e., filters should copy it rather than rewrite inplace) */
b->last_buf = 1; /* there will be no more buffers in therequest */
3 模块加入链表
out.buf = b;
out.next = NULL; //如果有下一个链表可以放到这里
4 返回
return ngx_http_output_filter(r, &out);
7 模块开发例子
#测试配置项2
location /test2 {
test_str "hello my dear HUST!";
test_flag on;
test_num 10;
test_size 1000;
mytest;
}
要实现的效果是,当在浏览器中输入http://localhost/test2时,将所有包含参数的配置项名及其对应的参数输出。
分配用于保存配置参数的数据结构
这个数据结构依据需要保存的参数自定义即可。
一般情况下这个结构是(包含了各种类型的配置项,但是在本例中只实现了部分类型的配置项的解析):
//存储配置项参数的结构体
typedef struct{
ngx_str_t arg_str;//保存一个字符串类型的参数
ngx_int_t arg_num;
ngx_flag_t arg_flag;
size_t arg_size;
ngx_array_t* arg_str_array;
ngx_array_t* arg_keyval;
off_t arg_off;
ngx_msec_t arg_msec;
time_t arg_sec;
ngx_bufs_t arg_bufs;
ngx_uint_t arg_enum_seq;
ngx_uint_t arg_bitmask;
ngx_uint_t arg_access;
ngx_path_t* arg_path;
}ngx_http_mytest2_loc_conf_t;
需要注意的是,这个结构会Nginx的内存中保存许多份。http框架在解析nginx.conf文件时,只要遇到http{}、server{}、或者location{}配置块就会立刻分配一个新的结构体。
Nginx如何管理我们自定义的存储配置的结构体呢?
是通过ngx_http_module_t中的8个回调方法(ngx_http_config.h):
其中以create开头的三个回调方法负责把我们分配的用于保存配置项的结构体传递给http框架。为什么会定义三个回调方法呢?
http框架定义了三个级别的配置main、srv、loc,分别表示直接出现在http{}、server{}、location{}、块内的配置。当nginx.conf中出现http{}时,http框架会接管配置文件中http{}块内的配置项解析。当遇到http{}配置块时,http框架会调用所有的http模块可能实现的create_main_conf、create_srv_conf、create_loc_conf方法生成存储main级别的配置参数的结构体;在遇到server{}配置块时,会再次调用所有的http模块可能实现的create_srv_conf、create_loc_conf方法生成存储srv级别的配置参数的结构体;在遇到location{}配置块时,会再次调用所有的http模块可能实现的create_loc_conf方法生成存储loc级别的配置参数的结构体。实现三个回调方法的意义是不同的。在一个模块中,http块内只会调用一次create_main_conf,但是create_loc_conf可能会被调用很多次,也就是有许多由create_loc_conf生成的结构体。
普通http请求往往只实现create_loc_conf回调方法,因为它们只关注匹配某种URL的请求。
设定配置项的解析方式
我们在ngx_command_t结构体中设定配置项的解析方式,在本例中,前四个配置项都用预设的方法进行解析,而最后一个配置项mytest用自定义的方法,并在这个方法中将前面各个配置项的参数组合成一个字符串返回给客户。
我们需要通过定义ngx_command_t数组来设置配置项的解析方式:
//设置配置项的解析方式
static ngx_command_t ngx_http_mytest2_commands[] = {
{
//test_str配置项
ngx_string("test_str"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_slot,//预设的配置项解析方法
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_mytest2_loc_conf_t,arg_str),
NULL
},
{
//test_flag配置项
ngx_string("test_flag"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE1,
ngx_conf_set_flag_slot,//预设的配置项解析方法
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_mytest2_loc_conf_t,arg_flag),
NULL
},
{
//test_num配置项
ngx_string("test_num"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,//预设的配置项解析方法
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_mytest2_loc_conf_t,arg_num),
NULL
},
{
//test_size配置项
ngx_string("test_size"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,//预设的配置项解析方法
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_mytest2_loc_conf_t,arg_size),
NULL
},
{
//mytest配置项
ngx_string("mytest"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_NOARGS,
ngx_http_mytest2,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL
},
ngx_null_command
};
其中自定义的配置项解析方法ngx_http_mytest2:
//模块的回调方法
static char *
ngx_http_mytest2(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_mytest2_handler;//每当遇到配置项mytest的时候会回调这个方法
return NGX_CONF_OK;
}
真正完成处理工作的handler是 ngx_http_mytest2_handler:
//模块真正完成处理工作的handler
static ngx_int_t ngx_http_mytest2_handler(ngx_http_request_t *r)
{
ngx_http_mytest2_loc_conf_t *elcf;//存储配置项参数的结构体
elcf = ngx_http_get_module_loc_conf(r,ngx_http_mytest2_module);
if (!(r->method & (NGX_HTTP_GET | NGX_HTTP_HEAD | NGX_HTTP_POST))) {
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 str_format = ngx_string("test_str=%V,test_flag=%i,test_num=%i,test_size=%z");
ngx_str_t test_str = elcf->arg_str;
ngx_flag_t test_flag = elcf->arg_flag;
ngx_int_t test_num = elcf->arg_num;
size_t test_size = elcf->arg_size;
int data_len = str_format.len + test_str.len + 1;
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_length_n = data_len;//响应包包体内容长度
r->headers_out.content_type = type;
rc = ngx_http_send_header(r);
if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
return rc;
}
ngx_buf_t *b;
b = ngx_create_temp_buf(r->pool,data_len);
if (b == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ngx_snprintf(b->pos,data_len,(char *)str_format.data,&test_str,test_flag,test_num,test_size);
b->last = b->pos + data_len;
b->last_buf = 1;
ngx_chain_t out;
out.buf = b;
out.next = NULL;
return ngx_http_output_filter(r, &out);
}
三、新添加模块的编译
普通编译方式是:
./configure --prefix=/usr/local/nginx --add-module=XX(新模块的config文件以及源码所存放的目录)
make
sudo make install
但是这样的一个缺点是:每次都要编译所有的nginx源码,速度慢。如果自己编写的新模块中的源代码中有错误,调试起来很不方便。有一个方法是自己编写一个makefile文件,先单独编译新
模块的代码,修正所有错误之后再将其编译进Nginx。
这是我编写的MakeFile文件:
#编译新模块的makefile文件
ngx_http_mytest_module.o: ngx_http_mytest_module.c
gcc -c -pipe -O -W -Wall -Wpointer-arith -Wno-unused-parameter -Werror -g -I /home/xiajun/TEST/Nginx/nginx-1.4.1/src/core -I /home/xiajun/TEST/Nginx/nginx-
1.4.1/src/event -I /home/xiajun/TEST/Nginx/nginx-1.4.1/src/event/modules -I /home/xiajun/TEST/Nginx/nginx-1.4.1/src/os/unix -I /home/xiajun/TEST/Nginx/nginx-1.4.1/objs
-I /home/xiajun/TEST/Nginx/nginx-1.4.1/src/http -I /home/xiajun/TEST/Nginx/nginx-1.4.1/src/http/modules -I /home/xiajun/TEST/Nginx/nginx-1.4.1/src/mail -o
ngx_http_mytest_module.o /home/xiajun/TEST/Nginx/nginx-1.4.1/mytest/ngx_http_mytest_module.c
四、完整代码及结果演示
config文件:
ngx_addon_name=ngx_http_mytest2
HTTP_MODULES="$HTTP_MODULES ngx_http_mytest2_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_mytest2_module.c"
ngx_http_mytest2_module.c:
//Nginx自定义模块实现代码
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
static ngx_int_t
ngx_http_mytest2_handler(ngx_http_request_t *r);
static char *
ngx_http_mytest2(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static void*
ngx_http_mytest2_create_loc_conf(ngx_conf_t *cf);
static char*
ngx_http_mytest2_merge_loc_conf(ngx_conf_t *cf,void *parent,void *child);
//存储配置项参数的结构体
typedef struct{
ngx_str_t arg_str;//保存一个字符串类型的参数
ngx_int_t arg_num;
ngx_flag_t arg_flag;
size_t arg_size;
ngx_array_t* arg_str_array;
ngx_array_t* arg_keyval;
off_t arg_off;
ngx_msec_t arg_msec;
time_t arg_sec;
ngx_bufs_t arg_bufs;
ngx_uint_t arg_enum_seq;
ngx_uint_t arg_bitmask;
ngx_uint_t arg_access;
ngx_path_t* arg_path;
}ngx_http_mytest2_loc_conf_t;
//设置配置项的解析方式
static ngx_command_t ngx_http_mytest2_commands[] = {
{
//test_str配置项
ngx_string("test_str"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_slot,//预设的配置项解析方法
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_mytest2_loc_conf_t,arg_str),
NULL
},
{
//test_flag配置项
ngx_string("test_flag"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE1,
ngx_conf_set_flag_slot,//预设的配置项解析方法
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_mytest2_loc_conf_t,arg_flag),
NULL
},
{
//test_num配置项
ngx_string("test_num"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,//预设的配置项解析方法
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_mytest2_loc_conf_t,arg_num),
NULL
},
{
//test_size配置项
ngx_string("test_size"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,//预设的配置项解析方法
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_mytest2_loc_conf_t,arg_size),
NULL
},
{
//mytest配置项
ngx_string("mytest"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_NOARGS,
ngx_http_mytest2,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL
},
ngx_null_command
};
//模块上下文定义
static ngx_http_module_t ngx_http_mytest2_module_ctx = {
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
ngx_http_mytest2_create_loc_conf,//创建数据结构存储loc级别的配置项的回调方法
ngx_http_mytest2_merge_loc_conf//合并loc级别的配置项
};
//模块定义
ngx_module_t ngx_http_mytest2_module = {
NGX_MODULE_V1,
&ngx_http_mytest2_module_ctx,
ngx_http_mytest2_commands,
NGX_HTTP_MODULE,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NGX_MODULE_V1_PADDING
};
//模块的回调方法
static char *
ngx_http_mytest2(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_mytest2_handler;
//ngx_conf_set_str_slot(cf,cmd,conf);//预设的配置项处理方法
return NGX_CONF_OK;
}
//模块真正完成处理工作的handler
static ngx_int_t ngx_http_mytest2_handler(ngx_http_request_t *r)
{
ngx_http_mytest2_loc_conf_t *elcf;//存储配置项参数的结构体
elcf = ngx_http_get_module_loc_conf(r,ngx_http_mytest2_module);
if (!(r->method & (NGX_HTTP_GET | NGX_HTTP_HEAD | NGX_HTTP_POST))) {
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 str_format = ngx_string("test_str=%V,test_flag=%i,test_num=%i,test_size=%z");
ngx_str_t test_str = elcf->arg_str;
ngx_flag_t test_flag = elcf->arg_flag;
ngx_int_t test_num = elcf->arg_num;
size_t test_size = elcf->arg_size;
int data_len = str_format.len + test_str.len + 1;
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_length_n = data_len;//响应包包体内容长度
r->headers_out.content_type = type;
rc = ngx_http_send_header(r);
if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
return rc;
}
ngx_buf_t *b;
b = ngx_create_temp_buf(r->pool,data_len);
if (b == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ngx_snprintf(b->pos,data_len,(char *)str_format.data,&test_str,test_flag,test_num,test_size);
b->last = b->pos + data_len;
b->last_buf = 1;
ngx_chain_t out;
out.buf = b;
out.next = NULL;
return ngx_http_output_filter(r, &out);
}
static void*
ngx_http_mytest2_create_loc_conf(ngx_conf_t *cf){
ngx_http_mytest2_loc_conf_t *conf;
conf = ngx_pcalloc(cf->pool,sizeof(ngx_http_mytest2_loc_conf_t));
if(NULL == conf){
return NGX_CONF_ERROR;
}
conf->arg_str.len = 0;
conf->arg_str.data = NULL;
//注意一下设定必不可少,否则会出错
conf->arg_flag = NGX_CONF_UNSET;
conf->arg_num = NGX_CONF_UNSET;
conf->arg_str_array = NGX_CONF_UNSET_PTR;
conf->arg_keyval = NULL;
conf->arg_off = NGX_CONF_UNSET;
conf->arg_msec = NGX_CONF_UNSET_MSEC;
conf->arg_sec = NGX_CONF_UNSET;
conf->arg_size = NGX_CONF_UNSET_SIZE;
return conf;
}
static char*
ngx_http_mytest2_merge_loc_conf(ngx_conf_t *cf,void *parent,void *child){
ngx_http_mytest2_loc_conf_t *prev = parent;
ngx_http_mytest2_loc_conf_t *conf = child;
ngx_conf_merge_str_value(conf->arg_str,prev->arg_str,"");
return NGX_CONF_OK;
}