Nginx开发一个简单的HTTP过滤模块
2017-04-26 18:23
716 查看
转载:http://blog.csdn.net/zhangxiao93/article/details/53691642
记录为了学习
开发一个HTTP过滤模块的步骤和相关知识跟开发一个普通的HTTP模块是类似的,只不过HTTP过滤模块的地位、作用与正常的HTTP过滤模块不同,它的工作是对发送给用户的HTTP响应包做一些加工。
本文将学习开发一个简单的HTTP过滤模块,它能够对
一过滤模块的调用顺序
二开发一个简单的过滤模块
1 确定源代码文件目录编写config文件
2 定义过滤模块实例化ngx_module_t
3 处理感兴趣的配置项
4 实现初始化方法
5 实现头部和包体过滤方法
51 函数声明
52 函数实现
三完整代码与测试
1 完整代码
2 测试
四参考
过滤模块可以叠加,也就是说一个请求会被所有的HTTP过滤模块依次处理。
过滤模块的调用是有顺序的,它的顺序在编译的时候就决定了。控制编译的脚本位于
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
从
在编译Nginx源码时,已经定义了一个由所有HTTP过滤模块组成的单链表,这个单链表是这样的:
链表的每一个元素都是一个C源代码文件,这个C源代码文件中有两个指针,分别指向下一个过滤模块(文件)的过滤头部和包体的方法(可理解为链表中的next指针)
过滤模块单链表示意图:
这两个指针的声明如下:
2
3
4
5
1
2
3
4
5
在我们定义的第三方模块中则有如下声明:
2
3
1
2
3
那么怎么将这个源文件(节点),插入到HTTP过滤模块组成的单链表中去呢?
Nginx采用头插法的办法,所有的新节点都插入在链表的开头:
2
3
4
5
6
1
2
3
4
5
6
其中两个top指针声明如下:
2
1
2
由于是头插法,这样就解释了,越早插入链表的过滤模块,就会越晚执行。
要开发一个简单的过滤模块,它的功能是对
1.确定源代码文件名称,源代码所在目录创建
2.定义过滤模块。实例化
3.处理感兴趣的配置项,通过设置
4.实现初始化方法。初始化方法就是把本模块中处理HTTP头部的
5.实现4.中提到两个处理头部和包体的方法。
接下来按照上述步骤依次来实现:
config 文件如下
2
3
1
2
3
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
1
2
3
4
5
6
7
8
9
10
11
12
13
14
其中定义结构体
2
3
4
1
2
3
4
头插入法将本过滤模块插入到单链表的首部:
2
3
4
5
6
7
8
9
10
11
12
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
1
2
3
4
5
6
7
(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
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
(2)响应包体处理方法:最终处理效果在包体前面添加前缀。
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
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
至此,已经完成大部分的工作,我们还需要为这个过滤模块编写模块上下文,编写创建和合并配置项参数结构体的函数等。
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
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
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
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
我们的过滤模块只对
2
3
4
5
1
2
3
4
5
也即当请求资源为txt时才会调用我们过滤模块,如果想要强制将响应的
只需要修改nginx.conf文件如下即可:
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
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
将自定义的过滤模块编译进Nginx:
2
1
2
重启nginx,用curl工具进行测试:
1
可以看到返回的包体添加了前缀:
1.《深入理解Nginx》
2. Nginx开发从入门到精通
记录为了学习
开发一个HTTP过滤模块的步骤和相关知识跟开发一个普通的HTTP模块是类似的,只不过HTTP过滤模块的地位、作用与正常的HTTP过滤模块不同,它的工作是对发送给用户的HTTP响应包做一些加工。
本文将学习开发一个简单的HTTP过滤模块,它能够对
Content-Type为text/plain的包体前加上前缀字符串prefix。
一过滤模块的调用顺序
二开发一个简单的过滤模块
1 确定源代码文件目录编写config文件
2 定义过滤模块实例化ngx_module_t
3 处理感兴趣的配置项
4 实现初始化方法
5 实现头部和包体过滤方法
51 函数声明
52 函数实现
三完整代码与测试
1 完整代码
2 测试
四参考
(一)过滤模块的调用顺序 |
过滤模块的调用是有顺序的,它的顺序在编译的时候就决定了。控制编译的脚本位于
auto/modules中,当你编译完Nginx以后,可以在objs目录下面看到一个ngx_modules.c的文件。打开这个文件,有类似的代码:
ngx_module_t *ngx_modules[] = { ... &ngx_http_write_filter_module, &ngx_http_header_filter_module, &ngx_http_chunked_filter_module, &ngx_http_range_header_filter_module, &ngx_http_gzip_filter_module, &ngx_http_postpone_filter_module, &ngx_http_ssi_filter_module, &ngx_http_charset_filter_module, &ngx_http_userid_filter_module, &ngx_http_headers_filter_module, &ngx_http_copy_filter_module, &ngx_http_range_body_filter_module, &ngx_http_not_modified_filter_module, NULL };1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
从
write_filter到
not_modified_filter,模块的执行顺序是反向的。也就是说最早执行的是
not_modified_filter,然后各个模块依次执行。所有第三方的模块只能加入到
copy_filter和
headers_filter模块之间执行。
在编译Nginx源码时,已经定义了一个由所有HTTP过滤模块组成的单链表,这个单链表是这样的:
链表的每一个元素都是一个C源代码文件,这个C源代码文件中有两个指针,分别指向下一个过滤模块(文件)的过滤头部和包体的方法(可理解为链表中的next指针)
过滤模块单链表示意图:
这两个指针的声明如下:
/*过滤模块处理HTTP头部的函数指针类型定义,它携带一个参数:请求*/ typedef ngx_int_t (*ngx_http_output_header_filter_pt)(ngx_http_request_t *r); /*过滤模块处理HTTP包体的函数指针类型定义,它携带两个参数:请求、要发送的包体*/ typedef ngx_int_t (*ngx_http_output_body_filter_pt) (ngx_http_request_t *r, ngx_chain_t *chain);1
2
3
4
5
1
2
3
4
5
在我们定义的第三方模块中则有如下声明:
/*用static修饰只在本文件生效,因此允许所有的过滤模块都有自己的这两个指针*/ static ngx_http_output_header_filter_pt ngx_http_next_header_filter; static ngx_http_output_body_filter_pt ngx_http_next_body_filter;1
2
3
1
2
3
那么怎么将这个源文件(节点),插入到HTTP过滤模块组成的单链表中去呢?
Nginx采用头插法的办法,所有的新节点都插入在链表的开头:
//插入到头部处理方法链表的首部 ngx_http_next_header_filter=ngx_http_top_header_filter; ngx_http_top_header_filter=ngx_http_myfilter_header_filter; //插入到包体处理方法链表的首部 ngx_http_next_body_filter = ngx_http_top_body_filter; ngx_http_top_body_filter = ngx_http_myfilter_body_filter;1
2
3
4
5
6
1
2
3
4
5
6
其中两个top指针声明如下:
extern ngx_http_output_header_filter_pt ngx_http_next_header_filter; extern ngx_http_output_body_filter_pt ngx_http_next_body_filter;1
2
1
2
由于是头插法,这样就解释了,越早插入链表的过滤模块,就会越晚执行。
(二)开发一个简单的过滤模块 |
Content-Type为
text/plain的响应添加一个前缀,类似于开发一个HTTP模块,它应该遵循如下步骤:
1.确定源代码文件名称,源代码所在目录创建
config脚本文件,
config文件的编写方式跟HTTP模块开发基本一致,不同的是需要将
HTTP_MODULES改成
HTTP_FILTER_MODULES。
2.定义过滤模块。实例化
ngx_module_t类型模块结构,因为HTTP过滤模块也是HTTP模块,所以其中的type成员也是
NGX_HTTP_MODULE。
3.处理感兴趣的配置项,通过设置
ngx_module_t中的
ngx_command_t数组来处理感兴趣的配置项。
4.实现初始化方法。初始化方法就是把本模块中处理HTTP头部的
ngx_http_output_header_filter_pt方法和处理HTTP包体的
ngx_http_output_body_filter_pt方法插入到过滤模块链表的首部。
5.实现4.中提到两个处理头部和包体的方法。
接下来按照上述步骤依次来实现:
2.1 确定源代码文件目录,编写config文件
config 文件如下ngx_addon_name=ngx_http_myfilter_module HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES ngx_http_myfilter_module" NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_myfilter_module.c"1
2
3
1
2
3
2.2 定义过滤模块,实例化ngx_module_t
/*定义过滤模块,ngx_module_t结构体实例化*/ ngx_module_t ngx_http_myfilter_module = { NGX_MODULE_V1, /*Macro*/ &ngx_http_myfilter_module_ctx, /*module context*/ ngx_http_myfilter_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 /*Macro*/ };1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2.3 处理感兴趣的配置项
/*处理感兴趣的配置项*/ static ngx_command_t ngx_http_myfilter_commands[]= { { ngx_string("add_prefix"), //配置项名称 NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_FLAG,//配置项只能携带一个参数并且是on或者off ngx_conf_set_flag_slot,//使用nginx自带方法,参数on/off NGX_HTTP_LOC_CONF_OFFSET,//使用create_loc_conf方法产生的结构体来存储 //解析出来的配置项参数 offsetof(ngx_http_myfilter_conf_t, enable),//on/off NULL }, ngx_null_command // };1
2
3
4
5
6
7
8
9
10
11
12
13
14
1
2
3
4
5
6
7
8
9
10
11
12
13
14
其中定义结构体
ngx_http_myfilter_conf_t来保存配置项参数:
typedef struct { ngx_flag_t enable; }ngx_http_myfilter_conf_t;1
2
3
4
1
2
3
4
2.4 实现初始化方法
头插入法将本过滤模块插入到单链表的首部:/*初始化方法*/1
static ngx_int_t
ngx_http_myfilter_init(ngx_conf_t*cf)
{
//插入到头部处理方法链表的首部 ngx_http_next_header_filter=ngx_http_top_header_filter; ngx_http_top_header_filter=ngx_http_myfilter_header_filter; //插入到包体处理方法链表的首部 ngx_http_next_body_filter = ngx_http_top_body_filter; ngx_http_top_body_filter = ngx_http_myfilter_body_filter;
}
2
3
4
5
6
7
8
9
10
11
12
1
2
3
4
5
6
7
8
9
10
11
12
2.5 实现头部和包体过滤方法
2.5.1 函数声明
/*头部处理方法*/ static ngx_int_t ngx_http_myfilter_header_filter(ngx_http_request_t *r); /*包体处理方法*/ static ngx_int_t ngx_http_myfilter_body_filter(ngx_http_request_t *r, ngx_chain_t *in);1
2
3
4
5
6
7
1
2
3
4
5
6
7
2.5.2 函数实现
(1)头部处理方法:最终处理效果头部信息Content-Length的值加上prefix的长度。
/*头部处理方法*/ static ngx_int_t ngx_http_myfilter_header_filter(ngx_http_request_t *r) { ngx_http_myfilter_ctx_t *ctx; ngx_http_myfilter_conf_t *conf; //如果不是返回成功,这时是不需要理会是否加前缀的, //直接交由下一个过滤模块 //处理响应码非200的情形 if (r->headers_out.status != NGX_HTTP_OK) { return ngx_http_next_header_filter(r); } /*获取http上下文*/ ctx = ngx_http_get_module_ctx(r, ngx_http_myfilter_module); if(ctx) { //该请求的上下文已经存在,这说明 // ngx_http_myfilter_header_filter已经被调用过1次, //直接交由下一个过滤模块处理 return ngx_http_next_header_filter(r); } //获取存储配置项参数的结构体 conf = ngx_http_get_module_loc_conf(r, ngx_http_myfilter_module); //如果enable成员为0,也就是配置文件中没有配置add_prefix配置项, //或者add_prefix配置项的参数值是off,这时直接交由下一个过滤模块处理 if (conf->enable == 0) { return ngx_http_next_header_filter(r); } //conf->enable==1 //构造http上下文结构体ngx_http_myfilter_ctx_t ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_myfilter_ctx_t)); if(NULL==ctx) { return NGX_ERROR; } ctx->add_prefix=0; ngx_http_set_ctx(r,ctx,ngx_http_myfilter_module); //只处理Content-Type是"text/plain"类型的http响应 if (r->headers_out.content_type.len >= sizeof("text/plain") - 1 && ngx_strncasecmp(r->headers_out.content_type.data, (u_char *) "text/plain", sizeof("text/plain") - 1) == 0) { ctx->add_prefix=1; if(r->headers_out.content_length_n > 0) { r->headers_out.content_length_n+=filter_prefix.len; } } //交由下一个过滤模块继续处理 return ngx_http_next_header_filter(r); }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
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
(2)响应包体处理方法:最终处理效果在包体前面添加前缀。
/*包体处理方法*/ static ngx_int_t ngx_http_myfilter_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { ngx_http_myfilter_ctx_t *ctx; ctx = ngx_http_get_module_ctx(r, ngx_http_myfilter_module); //如果获取不到上下文,或者上下文结构体中的add_prefix为0或者2时, //都不会添加前缀,这时直接交给下一个http过滤模块处理 if (ctx == NULL || ctx->add_prefix != 1) { return ngx_http_next_body_filter(r, in); } //将add_prefix设置为2,这样即使ngx_http_myfilter_body_filter //再次回调时,也不会重复添加前缀 ctx->add_prefix = 2; //从请求的内存池中分配内存,用于存储字符串前缀 ngx_buf_t* b = ngx_create_temp_buf(r->pool, filter_prefix.len); //将ngx_buf_t中的指针正确地指向filter_prefix字符串 b->start = b->pos = filter_prefix.data; b->last = b->pos + filter_prefix.len; //从请求的内存池中生成ngx_chain_t链表,将刚分配的ngx_buf_t设置到 //其buf成员中,并将它添加到原先待发送的http包体前面 ngx_chain_t *cl = ngx_alloc_chain_link(r->pool); /*note: in表示原来待发送的包体*/ cl->buf = b; cl->next = in; //调用下一个模块的http包体处理方法,注意这时传入的是新生成的cl链表 return ngx_http_next_body_filter(r, cl); }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
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
(三)完整代码与测试 |
3.1 完整代码
/*ngx_http_myfilter_module.c*/1
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
/*用static修饰只在本文件生效,因此允许所有的过滤模块都有自己的这两个指针*/ static ngx_http_output_header_filter_pt ngx_http_next_header_filter; static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
/*初始化方法,将过滤模块插入到链表头部*/
static ngx_int_t
ngx_http_myfilter_init(ngx_conf_t *cf);
/*头部处理方法*/ static ngx_int_t ngx_http_myfilter_header_filter(ngx_http_request_t *r); /*包体处理方法*/ static ngx_int_t ngx_http_myfilter_body_filter(ngx_http_request_t *r, ngx_chain_t *in);
typedef struct { ngx_flag_t enable; }ngx_http_myfilter_conf_t;
/*请求上下文*/
typedef struct
{
ngx_int_t add_prefix;
}ngx_http_myfilter_ctx_t;
/*在包体中添加的前缀*/
static ngx_str_t filter_prefix=ngx_string("[my filter prefix]");
/*处理感兴趣的配置项*/ static ngx_command_t ngx_http_myfilter_commands[]= { { ngx_string("add_prefix"), //配置项名称 NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_FLAG,//配置项只能携带一个参数并且是on或者off ngx_conf_set_flag_slot,//使用nginx自带方法,参数on/off NGX_HTTP_LOC_CONF_OFFSET,//使用create_loc_conf方法产生的结构体来存储 //解析出来的配置项参数 offsetof(ngx_http_myfilter_conf_t, enable),//on/off NULL }, ngx_null_command // };
static void* ngx_http_myfilter_create_conf(ngx_conf_t *cf);
static char*
ngx_http_myfilter_merge_conf(ngx_conf_t *cf,void*parent,void*child);
/*模块上下文*/
static ngx_http_module_t ngx_http_myfilter_module_ctx=
{
NULL, /* preconfiguration方法 */
ngx_http_myfilter_init, /* postconfiguration方法 */
NULL, /*create_main_conf 方法 */
NULL, /* init_main_conf方法 */
NULL, /* create_srv_conf方法 */
NULL, /* merge_srv_conf方法 */
ngx_http_myfilter_create_conf, /* create_loc_conf方法 */
ngx_http_myfilter_merge_conf /*merge_loc_conf方法*/
};
/*定义过滤模块,ngx_module_t结构体实例化*/ ngx_module_t ngx_http_myfilter_module = { NGX_MODULE_V1, /*Macro*/ &ngx_http_myfilter_module_ctx, /*module context*/ ngx_http_myfilter_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 /*Macro*/ };
static void* ngx_http_myfilter_create_conf(ngx_conf_t *cf)
{
ngx_http_myfilter_conf_t *mycf;
//创建存储配置项的结构体
mycf = (ngx_http_myfilter_conf_t *)ngx_pcalloc(cf->pool, sizeof(ngx_http_myfilter_conf_t));
if (mycf == NULL)
{
return NULL;
}
//ngx_flat_t类型的变量,如果使用预设函数ngx_conf_set_flag_slot
//解析配置项参数,必须初始化为NGX_CONF_UNSET
mycf->enable = NGX_CONF_UNSET;
return mycf;
}
static char*
ngx_http_myfilter_merge_conf(ngx_conf_t *cf,void*parent,void*child)
{
ngx_http_myfilter_conf_t *prev = (ngx_http_myfilter_conf_t *)parent;
ngx_http_myfilter_conf_t *conf = (ngx_http_myfilter_conf_t *)child;
//合并ngx_flat_t类型的配置项enable
ngx_conf_merge_value(conf->enable, prev->enable, 0);
return NGX_CONF_OK;
}
/*初始化方法*/
static ngx_int_t
ngx_http_myfilter_init(ngx_conf_t*cf)
{
//插入到头部处理方法链表的首部 ngx_http_next_header_filter=ngx_http_top_header_filter; ngx_http_top_header_filter=ngx_http_myfilter_header_filter; //插入到包体处理方法链表的首部 ngx_http_next_body_filter = ngx_http_top_body_filter; ngx_http_top_body_filter = ngx_http_myfilter_body_filter;
return NGX_OK;
}
/*头部处理方法*/
static ngx_int_t
ngx_http_myfilter_header_filter(ngx_http_request_t *r)
{
ngx_http_myfilter_ctx_t *ctx;
ngx_http_myfilter_conf_t *conf;
//如果不是返回成功,这时是不需要理会是否加前缀的,
//直接交由下一个过滤模块
//处理响应码非200的情形
if (r->headers_out.status != NGX_HTTP_OK)
{
return ngx_http_next_header_filter(r);
}
/*获取http上下文*/
ctx = ngx_http_get_module_ctx(r, ngx_http_myfilter_module);
if(ctx)
{
//该请求的上下文已经存在,这说明
// ngx_http_myfilter_header_filter已经被调用过1次,
//直接交由下一个过滤模块处理
return ngx_http_next_header_filter(r);
}
//获取存储配置项参数的结构体
conf = ngx_http_get_module_loc_conf(r, ngx_http_myfilter_module);
//如果enable成员为0,也就是配置文件中没有配置add_prefix配置项,
//或者add_prefix配置项的参数值是off,这时直接交由下一个过滤模块处理
if (conf->enable == 0)
{
return ngx_http_next_header_filter(r);
}
//conf->enable==1
//构造http上下文结构体ngx_http_myfilter_ctx_t
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_myfilter_ctx_t));
if(NULL==ctx)
{
return NGX_ERROR;
}
ctx->add_prefix=0;
ngx_http_set_ctx(r,ctx,ngx_http_myfilter_module);
//只处理Content-Type是"text/plain"类型的http响应
if (r->headers_out.content_type.len >= sizeof("text/plain") - 1
&& ngx_strncasecmp(r->headers_out.content_type.data, (u_char *) "text/plain", sizeof("text/plain") - 1) == 0)
{
ctx->add_prefix=1;
if(r->headers_out.content_length_n > 0)
{
r->headers_out.content_length_n+=filter_prefix.len;
}
}
//交由下一个过滤模块继续处理
return ngx_http_next_header_filter(r);
}
/*包体处理方法*/
static ngx_int_t
ngx_http_myfilter_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
ngx_http_myfilter_ctx_t *ctx;
ctx = ngx_http_get_module_ctx(r, ngx_http_myfilter_module);
//如果获取不到上下文,或者上下文结构体中的add_prefix为0或者2时,
//都不会添加前缀,这时直接交给下一个http过滤模块处理
if (ctx == NULL || ctx->add_prefix != 1)
{
return ngx_http_next_body_filter(r, in);
}
//将add_prefix设置为2,这样即使ngx_http_myfilter_body_filter
//再次回调时,也不会重复添加前缀
ctx->add_prefix = 2;
//从请求的内存池中分配内存,用于存储字符串前缀
ngx_buf_t* b = ngx_create_temp_buf(r->pool, filter_prefix.len);
//将ngx_buf_t中的指针正确地指向filter_prefix字符串
b->start = b->pos = filter_prefix.data;
b->last = b->pos + filter_prefix.len;
//从请求的内存池中生成ngx_chain_t链表,将刚分配的ngx_buf_t设置到
//其buf成员中,并将它添加到原先待发送的http包体前面
ngx_chain_t *cl = ngx_alloc_chain_link(r->pool);
/*note: in表示原来待发送的包体*/
cl->buf = b;
cl->next = in;
//调用下一个模块的http包体处理方法,注意这时传入的是新生成的cl链表
return ngx_http_next_body_filter(r, cl);
}
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
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
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
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
3.2 测试
我们的过滤模块只对Content-Type为
text/plain的响应有效,查看Nginx的默认配置中的
mime.types文件,发现
types{ #... text/plain txt; #... }1
2
3
4
5
1
2
3
4
5
也即当请求资源为txt时才会调用我们过滤模块,如果想要强制将响应的
Content-Type设置为
text/plain呢?
只需要修改nginx.conf文件如下即可:
#user root; worker_processes 1; error_log logs/error.log debug; events { worker_connections 1024; } http { # 注释掉http块下的配置 # include mime.types; # default_type application/octet-stream; keepalive_timeout 65; server { listen 1024; location / { #在location块下将默认类型设置为text/plain default_type text/plain; root html; add_prefix on; index index.htm index.html; } } }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
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
将自定义的过滤模块编译进Nginx:
./configure --add-module=/home/zhangxiao/nginx/nginx-1.0.15/src/myHttpFilterModule/ make;sudo make install1
2
1
2
重启nginx,用curl工具进行测试:
curl -v localhost:10241
1
可以看到返回的包体添加了前缀:
(四)参考 |
2. Nginx开发从入门到精通
相关文章推荐
- Nginx开发一个简单的HTTP过滤模块
- 【Nginx】开发一个简单的HTTP模块
- nginx 开发一个简单的 HTTP 模块
- 【Nginx】开发一个HTTP过滤模块
- Nginx模块开发(5)————开发简单的HTTP过滤模块
- 深入理解nginx chap3 开发一个简单的HTTP模块
- 【Nginx】开发一个简单的HTTP模块
- 【Nginx】开发一个HTTP过滤模块
- nginx的http模块开发--一个验证url参数的例子
- 开发一个HTTP过滤模块
- nginx学习笔记(2):开发一个简单的HTTP模块
- Nginx HTTP过滤模块开发
- 【Nginx】初识nginx---实现一个简单的http模块
- Nginx源码分析与实践---(一)编写一个简单的Http模块
- nginx 开发简单的http模块
- Nginx基础. 开发HTTP过滤模块
- 淘宝开发:Nginx模块Nginx-Http-Footer-Filter
- nginx http 模块开发config原理
- Nginx Http模块开发
- nginx 学习五 filter模块简介和实现一个简单的filter模块