转载:http://blog.csdn.net/zhangxiao93/article/details/53691642
记录为了学习
开发一个HTTP过滤模块的步骤和相关知识跟开发一个普通的HTTP模块是类似的,只不过HTTP过滤模块的地位、作用与正常的HTTP过滤模块不同,它的工作是对发送给用户的HTTP响应包做一些加工。
本文将学习开发一个简单的HTTP过滤模块,它能够对Content-Type为text/plain
的包体前加上前缀字符串prefix。
过滤模块可以叠加,也就是说一个请求会被所有的HTTP过滤模块依次处理。
过滤模块的调用是有顺序的,它的顺序在编译的时候就决定了。控制编译的脚本位于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指针)
过滤模块单链表示意图:
这两个指针的声明如下:
typedef ngx_int_t (*ngx_http_output_header_filter_pt)(ngx_http_request_t *r);
typedef ngx_int_t (*ngx_http_output_body_filter_pt)
(ngx_http_request_t *r, ngx_chain_t *chain);
在我们定义的第三方模块中则有如下声明:
static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
那么怎么将这个源文件(节点),插入到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;
其中两个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;
由于是头插法,这样就解释了,越早插入链表的过滤模块,就会越晚执行。
要开发一个简单的过滤模块,它的功能是对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"
2.2 定义过滤模块,实例化ngx_module_t
ngx_module_t ngx_http_myfilter_module =
{
NGX_MODULE_V1,
&ngx_http_myfilter_module_ctx,
ngx_http_myfilter_commands,
NGX_HTTP_MODULE,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NGX_MODULE_V1_PADDING
};
- 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,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_myfilter_conf_t, enable),
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;
2.4 实现初始化方法
头插入法将本过滤模块插入到单链表的首部:
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;
}
- 1
- 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);
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;
if (r->headers_out.status != NGX_HTTP_OK)
{
return ngx_http_next_header_filter(r);
}
ctx = ngx_http_get_module_ctx(r, ngx_http_myfilter_module);
if(ctx)
{
return ngx_http_next_header_filter(r);
}
conf = ngx_http_get_module_loc_conf(r, ngx_http_myfilter_module);
if (conf->enable == 0)
{
return ngx_http_next_header_filter(r);
}
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);
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);
if (ctx == NULL || ctx->add_prefix != 1)
{
return ngx_http_next_body_filter(r, in);
}
ctx->add_prefix = 2;
ngx_buf_t* b = ngx_create_temp_buf(r->pool, filter_prefix.len);
b->start = b->pos = filter_prefix.data;
b->last = b->pos + filter_prefix.len;
ngx_chain_t *cl = ngx_alloc_chain_link(r->pool);
cl->buf = b;
cl->next = in;
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 完整代码
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
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,
ngx_conf_set_flag_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_myfilter_conf_t, enable),
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,
ngx_http_myfilter_init,
NULL,
NULL,
NULL,
NULL,
ngx_http_myfilter_create_conf,
ngx_http_myfilter_merge_conf
};
ngx_module_t ngx_http_myfilter_module =
{
NGX_MODULE_V1,
&ngx_http_myfilter_module_ctx,
ngx_http_myfilter_commands,
NGX_HTTP_MODULE,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NGX_MODULE_V1_PADDING
};
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;
}
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_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;
if (r->headers_out.status != NGX_HTTP_OK)
{
return ngx_http_next_header_filter(r);
}
ctx = ngx_http_get_module_ctx(r, ngx_http_myfilter_module);
if(ctx)
{
return ngx_http_next_header_filter(r);
}
conf = ngx_http_get_module_loc_conf(r, ngx_http_myfilter_module);
if (conf->enable == 0)
{
return ngx_http_next_header_filter(r);
}
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);
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);
if (ctx == NULL || ctx->add_prefix != 1)
{
return ngx_http_next_body_filter(r, in);
}
ctx->add_prefix = 2;
ngx_buf_t* b = ngx_create_temp_buf(r->pool, filter_prefix.len);
b->start = b->pos = filter_prefix.data;
b->last = b->pos + filter_prefix.len;
ngx_chain_t *cl = ngx_alloc_chain_link(r->pool);
cl->buf = b;
cl->next = in;
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
- 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;
#...
}
也即当请求资源为txt时才会调用我们过滤模块,如果想要强制将响应的Content-Type
设置为text/plain
呢?
只需要修改nginx.conf文件如下即可:
worker_processes 1;
error_log logs/error.log debug;
events {
worker_connections 1024;
}
http {
keepalive_timeout 65;
server {
listen 1024;
location / {
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 install
重启nginx,用curl工具进行测试:
curl -v localhost:1024
可以看到返回的包体添加了前缀:
1.《深入理解Nginx》
2. Nginx开发从入门到精通