1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
#main
...
events {
...
}
http {
...
upstream {
...
}
server {
...
location {
...
}
}
}
|
main块:主要控制Nginx子进程的所属用户/用户组、派生子进程数、错误日志位置/级别、pid位置、子进程优先级、进程对应cpu、进程能够打开的文件描述符数目等。
events块:控制Nginx处理连接的方式。
http块:是Nginx处理http请求的主要配置模块,大多数配置都在这里面进行。
server块:是Nginx中主机的配置块,可以配置多个虚拟主机。
location块:是server中对应的目录级别的控制块,可以有多个。
upstream块:是Nginx做反向代理和负载均衡的配置块,可以有多个。
其中我们经常关注的模块有main、server、upstream和location。main部分设置的指令将影响其它所有设置;server部分的指令主要用于指定主机和端口;upstream的指令用于设置一系列的后端服务器;location部分用于匹配网页位置(比如,根目录“/”,“/images”,等等)。他们之间的关系式:server继承main,location继承server;upstream既不会继承指令也不会被继承,它有自己的特殊指令,不需要在其他地方应用。
Nginx模块主要有三种角色:
handlers :处理http请求并构造输出。
filters :处理handler产生的输出。
load-balancers :当有多于一个的后端服务器时,选择一台将http请求发送过去。
Nginx中许多工作都是由模块来完成的。任何时候,Nginx提供文件或者转发请求到另一个server,都是通过handler来实现的。而当需要对输出在服务端加一些东西的话,filter就派上用场了。如果handler的作用是把请求反向代理到后端服务器,那么就会用到模块的第三种角色load-balancer了。如果handler正常返回,那么filter就会被调用。filter采用了经典的“接力链表”模式:一个filter被调用并处理,接下来调用下一个filter,直到最后一个filter被调用完成,Nginx才真正完成响应流程。
总结一下,一个典型的请求响应周期: 客户端发送HTTP请求 → Nginx基于location的配置选择一个合适的handler → (如果有) load-balancer选择一个后端服务器 → Handler处理请求并将响应发送给第一个filter → 第一个filter讲输出交给第二个filter → 第二个给第三个→ 以此类推 → 最终响应发送给客户端 。
之所以说“典型地”是因为Ngingx的模块具有很强的定制性。模块开发者需要花很多精力精确定义模块在何时如何产生作用。模块调用实际上是通过一系列的回调函数做到的。理论上来讲,你的函数可以在以下时候被执行:
● server读取配置文件之前
● 读取location和server的每一条配置指令
● 当Nginx初始化main配置段时
● 当Nginx初始化server配置段时(例如:host/port)
● 当Nginx合并server配置和main配置时
● 当Nginx初始化location配置时
● 当Nginx合并location配置和它的父server配置时
● 当Nginx的主进程启动时
● 当一个新的worker进程启动时
● 当一个worker进程退出时
● 当主进程退出时
● handler一个请求
● Filter响应头
● Filter响应体
● 选择一个后端服务器
● 初始化一个将发往后端服务器的请求
● 重新初始化一个将发往后端服务器的请求
● 处理来自后端服务器的响应
● 完成与后端服务器的交互
1
2
|
模块的存储struct有三种,分别是main,server和location。绝大多数模块仅需要一个location配置。名称约定如下:ngx_http_
|
1
2
3
4
5
|
typedef struct
{
ngx_str_t hello_string;
ngx_int_t hello_counter;
} ngx_http_hello_loc_conf_t;
|
这是后面将要讲的hello模块的存储结构的定义。
配置指令是用来定义你所开发模块的配置信息,例如指令的名称、参数等信息。它是一个静态的ngx_command_t类型的数组。同样举个例子:
1
2
3
4
5
6
7
8
9
10
11
|
static
ngx_command_t ngx_http_hello_commands[] = {
{
ngx_string(
"hello_string"
),
NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS|NGX_CONF_TAKE1,
ngx_http_hello_string,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_hello_loc_conf_t, hello_string),
NULL
},
ngx_null_command
};
|
下面我们来分析下ngx_command_t 结构体。
1
2
3
4
5
6
7
8
|
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;
};
|
name 这个字段表示我们所开发模块的指令名称。ngx_str_t 就理解成一个String类型就可以了。
type 表示该指令的作用域,即这个指令配置在哪里,以及指令参数的个数。 ngx_uint_t 理解成一个无符号整型。
可选的值有如下一些:
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_CONF_TAKE12:配置指令接受1个或者2个参数。
NGX_CONF_TAKE13:配置指令接受1个或者3个参数。
……
NGX_CONF_MULTI: 配置指令可以接受多个参数,即个数不定。
NGX_CONF_BLOCK:配置指令可以接受的值是一个配置信息块。也就是一对大括号括起来的内容。里面可以再包括很多的配置指令。比如常见的server指令就是这个属性的。
NGX_CONF_ANY:配置指令可以接受的任意的参数值。一个或者多个,或者’on’或者’off’,或者是配置块。
set 是一个函数指针,它指向的函数用来进行模块配置;它一般用来将配置文件中的参数传递给程序,并保存在配置结构体中。此函数有三个入参:
指向结构体 ngx_conf_t 的指针, 这个结构体里包含需要传递给指令的参数
指向结构体 ngx_command_t 的指针
指向模块自定义配置结构体的指针
这个函数会在遇到指令时执行,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:解析一个数据大小, 并保存为size_t
conf 该字段指定当前配置项存储的内存位置,因为http模块对所有http模块所要保存的配置信息,划分了main, server和location三个地方进行存储,每个地方都有一个内存池用来分配存储这些信息的内存。可取的值有:NGX_HTTP_MAIN_CONF_OFFSET、NGX_HTTP_SRV_CONF_OFFSET及NGX_HTTP_LOC_CONF_OFFSET,当然这里你可以填写0,即默认为NGX_HTTP_MAIN_CONF_OFFSET。
offset 指定该配置项值的精确存放位置,一般指定为某一个结构体变量的字段偏移。例如上面提到的offsetof(ngx_http_hello_loc_conf_t, hello_string) 表示存储到ngx_http_hello_loc_conf_t结构体的hello_string结构成员中。
post 该字段存储一个指针。可以指向任何一个在读取配置过程中需要的数据,以便于进行配置读取的处理。大多数时候,都不需要,所以简单地设为0即可。
模块上下文定义了一大坨函数引用,用来创建和合并三个部分的配置(main,server,location),它是一个静态的ngx_http_module_t结构体,命名方式一般是:ngx_http__module_ctx 。这些函数引用依次是:
◇ preconfiguration: 在读入配置前调用
◇ postconfiguration: 在读入配置后调用
◇ create_main_conf: 在创建main配置时调用
◇ init_main_conf: 在初始化main配置时调用
◇ init_main_conf: 在创建server配置时调用
◇ merge_srv_conf: 合并server和main配置时调用
◇ create_loc_conf: 创建location配置时调用
◇ merge_loc_conf: 合并location和server配置时调用
来看下结构体ngx_http_module_t 的定义:
1
2
3
4
5
6
7
8
9
10
|
typedef struct {
ngx_int_t (*preconfiguration)(ngx_conf_t *cf);
ngx_int_t (*postconfiguration)(ngx_conf_t *cf);
void
*(*create_main_conf)(ngx_conf_t *cf);
char
*(*init_main_conf)(ngx_conf_t *cf,
void
*conf);
void
*(*create_srv_conf)(ngx_conf_t *cf);
char
*(*merge_srv_conf)(ngx_conf_t *cf,
void
*prev,
void
*conf);
void
*(*create_loc_conf)(ngx_conf_t *cf);
char
*(*merge_loc_conf)(ngx_conf_t *cf,
void
*prev,
void
*conf);
} ngx_http_module_t;
|
任何模块,我们都需要定义一个ngx_module_t 类型的变量,来说明这个模块本身的信息,这是这个模块最重要的一个信息,它告诉了nginx这个模块的一些信息,上面定义的配置信息,还有模块上下文信息。加载模块的上层代码,都需要通过定义的这个结构,来获取这些信息。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
typedef struct ngx_module_s ngx_module_t;
struct ngx_module_s {
ngx_uint_t ctx_index;
ngx_uint_t index;
ngx_uint_t spare0;
ngx_uint_t spare1;
ngx_uint_t abi_compatibility;
ngx_uint_t major_version;
ngx_uint_t minor_version;
void
*ctx;
ngx_command_t *commands;
ngx_uint_t type;
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);
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;
};
#define NGX_NUMBER_MAJOR
3
#define NGX_NUMBER_MINOR
1
#define NGX_MODULE_V1
0
,
0
,
0
,
0
, NGX_DSO_ABI_COMPATIBILITY, NGX_NUMBER_MAJOR, NGX_NUMBER_MINOR
#define NGX_MODULE_V1_PADDING
0
,
0
,
0
,
0
,
0
,
0
,
0
,
0
|
举个hello模块的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
ngx_module_t ngx_http_hello_module = {
NGX_MODULE_V1,
/* 前7个结构成员*/
&ngx_http_hello_module_ctx,
/* 模块上下文 */
ngx_http_hello_commands,
/* 模块指令 */
NGX_HTTP_MODULE,
/* 模块类型,我们所开发的基本都是NGX_HTTP_MODULE类型 */
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
/* 后8个结构成员*/
};
|
Handler一般做4件事:获取location配置、生成合适的响应、发送响应头、发送响应体。Handler有一个参数,即请求结构体。请求结构体包含很多关于客户请求的有用信息,比如说请求方法,URI,请求头等等。
在开发Handler模块时,除了提供上一节介绍的的基本结构体外,handler还需要提供一个处理函数。这个函数负责对来自客户端请求的真正处理。这个函数的处理,既可以选择自己直接生成内容,也可以选择拒绝处理,由后续的handler去进行处理,或者是选择丢给后续的filter进行处理。来看一下这个函数的原型声明:
1
2
|
/*r 是http请求, 里面包含请求所有的信息 */
typedef ngx_int_t (*ngx_http_handler_pt) (ngx_http_request_t * r);
|
该函数处理成功返回NGX_OK,处理发生错误返回NGX_ERROR,拒绝处理(留给后续的handler进行处理)返回NGX_DECLINE。 返回NGX_OK也就代表给客户端的响应已经生成好了,否则返回NGX_ERROR就发生错误了。
当一切该定义的结构都已经定义好之后,你需要指定所编写的模块挂载的具体方式及位置。handler有两种挂载方式:
1. 按处理阶段挂载
2. 按需挂载
为了更精细地控制对于客户端请求的处理过程,nginx把这个处理过程划分成了11个阶段。他们从前到后,依次列举如下:
● NGX_HTTP_POST_READ_PHASE:读取请求内容阶段
● NGX_HTTP_SERVER_REWRITE_PHASE:Server请求地址重写阶段
● NGX_HTTP_FIND_CONFIG_PHASE:配置查找阶段
● NGX_HTTP_REWRITE_PHASE:Location请求地址重写阶段
● NGX_HTTP_POST_REWRITE_PHASE:请求地址重写提交阶段
● NGX_HTTP_PREACCESS_PHASE:访问权限检查准备阶段
● NGX_HTTP_ACCESS_PHASE:访问权限检查阶段
● NGX_HTTP_POST_ACCESS_PHASE:访问权限检查提交阶段
● NGX_HTTP_TRY_FILES_PHASE:配置项try_files处理阶段
● NGX_HTTP_CONTENT_PHASE:内容产生阶段
● NGX_HTTP_LOG_PHASE:日志模块处理阶段
一般情况下,我们自定义的模块,大多数是挂载在NGX_HTTP_CONTENT_PHASE阶段的。挂载的动作一般是在模块上下文调用的postconfiguration函数中。使用这种方式挂载的handler也被称为 content phase handler。举个hello模块挂载的实例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
static
ngx_int_t ngx_http_hello_init(ngx_conf_t *cf)
{
ngx_http_handler_pt *h;
ngx_http_core_main_conf_t *cmcf;
/** http模块主配置 */
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
/** 挂载到NGX_HTTP_CONTENT_PHASE阶段 */
h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
if
(h == NULL) {
return
NGX_ERROR;
}
/** 指定handler处理函数 */
*h = ngx_http_hello_handler;
return
NGX_OK;
}
|
注意:有几个阶段是特例,它不调用任何挂载的handler,所以你在开发过程中不要挂载到这几个阶段:
● NGX_HTTP_FIND_CONFIG_PHASE
● NGX_HTTP_POST_ACCESS_PHASE
● NGX_HTTP_POST_REWRITE_PHASE
● NGX_HTTP_TRY_FILES_PHASE
所以其实只有7个phase你可以挂载自己的handler。
这种方式挂载的handler被称为content handler。当一个请求进来以后,nginx从第一个阶段开始依次执行每个阶段中所有handler。执行到内容产生阶段阶段的时候,如果这个location有一个对应的content handler模块,那么就去执行这个content handler模块真正的处理函数。否则继续依次执行内容产生阶段中所有content phase handler,直到某个函数处理返回NGX_OK或者NGX_ERROR。所以当某个location处理到NGX_HTTP_CONTENT_PHASE阶段时,如果有content handler模块,那么NGX_HTTP_CONTENT_PHASE挂载的所有content phase handler都不会被执行了。使用这个方法挂载上去的handler有一个特点是必须在NGX_HTTP_CONTENT_PHASE阶段才能执行到。如果你想自己的handler在更早的阶段执行,那就不要使用这种挂载方式。同样举个例子:
1
2
3
4
5
6
7
8
9
|
static
char
* ngx_http_hello_string(ngx_conf_t *cf, ngx_command_t *cmd,
void
*conf)
{
ngx_http_core_loc_conf_t *clcf;
/** 获取location配置 */
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
/** 指定handler */
clcf->handler = ngx_http_hello_handler;
return
NGX_CONF_OK;
}
|
编写handler模块大致分为以下几个步骤:
1. 模块基本结构的编写。包括模块的配置结构,模块指令的定义,模块上下文结构,模块的定义等。
2. 实现handler的挂载函数。根据模块的需求选择正确的挂载方式。
3. 编写handler处理函数。模块的功能主要通过这个函数来完成。
这一节我们将要实现一个自己的hello模块,它的主要功能很简单,当请求匹配到此模块处理时,返回一个配置的字符串,并且增加一个访问次数的统计。
创建一个文件ngx_http_hello_module.c ,输入如下完整代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
|
#include
#include
#include
/** 该模块的配置结构*/
typedef struct
{
ngx_str_t hello_string;
ngx_int_t hello_counter;
} ngx_http_hello_loc_conf_t;
static
ngx_int_t ngx_http_hello_init(ngx_conf_t *cf);
static
void
*ngx_http_hello_create_loc_conf(ngx_conf_t *cf);
static
char
*ngx_http_hello_string(ngx_conf_t *cf, ngx_command_t *cmd,
void
*conf);
static
char
*ngx_http_hello_counter(ngx_conf_t *cf, ngx_command_t *cmd,
void
*conf);
/**
*该模块的配置指令,这里我们定义了两个指令,分别是hello_string,和hello_counter
*/
static
ngx_command_t ngx_http_hello_commands[] = {
{
ngx_string(
"hello_string"
),
/* 指定该指令的作用域是location,无参或者有一个参数*/
NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS|NGX_CONF_TAKE1,
/* 回调函数,用于解析参数*/
ngx_http_hello_string,
/* 该指令配置信息存储的位置在location区*/
NGX_HTTP_LOC_CONF_OFFSET,
/* 存储的具体结构及位置*/
offsetof(ngx_http_hello_loc_conf_t, hello_string),
NULL
},
{
ngx_string(
"hello_counter"
),
/* 指定该指令的作用域是location,有一个布尔类型的参数*/
NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
/* 回调函数,用于解析参数*/
ngx_http_hello_counter,
/* 该指令配置信息存储的位置在location区*/
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_hello_loc_conf_t, hello_counter),
NULL
},
/* 该数组读到此处停止*/
ngx_null_command
};
static
int
ngx_hello_visited_times =
0
;
/**
* 该指令上下文的定义。
* ngx_http_hello_init:这个回调函数负责在读取完配置信息之后挂载此模块
* ngx_http_hello_create_loc_conf:此回调函数负责在读取location配置之后,创建模块配置结构的存储空间
*/
static
ngx_http_module_t ngx_http_hello_module_ctx = {
NULL,
/* preconfiguration 在创建和读取该模块的配置信息之前被调用*/
ngx_http_hello_init,
/* postconfiguration 在创建和读取该模块的配置信息之后被调用*/
NULL,
/* create main configuration */
NULL,
/* init main configuration */
NULL,
/* create server configuration */
NULL,
/* merge server configuration */
ngx_http_hello_create_loc_conf,
/* create location configuration */
NULL
/* merge location configuration */
};
/** 模块的定义 */
ngx_module_t ngx_http_hello_module = {
NGX_MODULE_V1,
/* 指定该模块的上下文结构*/
&ngx_http_hello_module_ctx,
/* module context */
/* 指定该模块指令的定义*/
ngx_http_hello_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
ngx_int_t ngx_http_hello_handler(ngx_http_request_t *r)
{
ngx_int_t rc;
ngx_buf_t * b;
ngx_chain_t out;
ngx_http_hello_loc_conf_t * my_conf;
u_char ngx_hello_string[
1024
] = {
0
};
ngx_uint_t content_length =
0
;
ngx_log_error(NGX_LOG_EMERG, r->connection->log,
0
,
"ngx_http_hello_handler is called!"
);
/* 获取指令配置 */
my_conf = ngx_http_get_module_loc_conf(r, ngx_http_hello_module);
if
(my_conf->hello_string.len ==
0
)
{
ngx_log_error(NGX_LOG_EMERG, r->connection->log,
0
,
"hello_string is empty!"
);
return
NGX_DECLINED;
}
if
(my_conf->hello_counter == NGX_CONF_UNSET || my_conf->hello_counter ==
0
)
{
/* 将拼接后的字符串写入到ngx_hello_string */
ngx_sprintf(ngx_hello_string,
"%s"
, my_conf->hello_string.data);
}
else
{
ngx_sprintf(ngx_hello_string,
"%s Visited Times:%d"
, my_conf->hello_string.data,
++ngx_hello_visited_times);
}
ngx_log_error(NGX_LOG_EMERG, r->connection->log,
0
,
"hello_string:%s"
, ngx_hello_string);
/* 获取ngx_hello_string的长度*/
content_length = ngx_strlen(ngx_hello_string);
/* 只处理get方式的请求 */
if
(!(r->method & (NGX_HTTP_GET))) {
return
NGX_HTTP_NOT_ALLOWED;
}
/* 这里我们不需要处理请求体,所以主动丢弃请求体 */
rc = ngx_http_discard_request_body(r);
if
(rc != NGX_OK) {
return
rc;
}
/* 设置响应的content type为text/html */
ngx_str_set(&r->headers_out.content_type,
"text/html"
);
/* 申请内存buffer b存储响应体 */
b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
if
(b == NULL) {
return
NGX_HTTP_INTERNAL_SERVER_ERROR;
}
/* 绑定buffer b到buffer链中 */
out.buf = b;
out.next = NULL;
/* buffer指针指向正确的内容 */
b->pos = ngx_hello_string;
b->last = ngx_hello_string + content_length;
b->memory =
1
;
/* buffer b是 buffer chain中最后一个buffer*/
b->last_buf =
1
;
/* 设置状态行 */
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_length_n = content_length;
/* 发送响应头 */
rc = ngx_http_send_header(r);
if
(rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
return
rc;
}
/* 输出内容 */
return
ngx_http_output_filter(r, &out);
}
/** 申请内存空间,用于存储模块配置结构*/
static
void
* ngx_http_hello_create_loc_conf(ngx_conf_t *cf)
{
ngx_http_hello_loc_conf_t* local_conf = NULL;
local_conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_hello_loc_conf_t));
if
(local_conf == NULL)
{
return
NULL;
}
ngx_str_null(&local_conf->hello_string);
local_conf->hello_counter = NGX_CONF_UNSET;
return
local_conf;
}
/** 解析hello_string指令的参数为String类型*/
static
char
* ngx_http_hello_string(ngx_conf_t *cf, ngx_command_t *cmd,
void
*conf)
{
ngx_http_hello_loc_conf_t* local_conf;
local_conf = conf;
char
* rv = ngx_conf_set_str_slot(cf, cmd, conf);
ngx_conf_log_error(NGX_LOG_EMERG, cf,
0
,
"hello_string:%s"
, local_conf->hello_string.data);
return
rv;
}
/** 解析hello_string指令的参数为boolean类型*/
static
char
*ngx_http_hello_counter(ngx_conf_t *cf, ngx_command_t *cmd,
void
*conf)
{
ngx_http_hello_loc_conf_t* local_conf;
local_conf = conf;
char
* rv = NULL;
rv = ngx_conf_set_flag_slot(cf, cmd, conf);
ngx_conf_log_error(NGX_LOG_EMERG, cf,
0
,
"hello_counter:%d"
, local_conf->hello_counter);
return
rv;
}
/** 挂载该模块的逻辑,此处是按处理阶段挂载,挂载到NGX_HTTP_CONTENT_PHASE阶段 */
static
ngx_int_t ngx_http_hello_init(ngx_conf_t *cf)
{
ngx_http_handler_pt *h;
ngx_http_core_main_conf_t *cmcf;
cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
|