一、前言
需要明确的是:
- 不希望模块对所有的HTTP请求起作用
- 在nginx.conf文件中的http{}、server{}或者location{}块内定义mytest配置项,如果一个用户请求通过主机域名、URI等匹配上了相应的配置块,而这个配置块下又具有mytest 配置项,那么希望mytest 模块开始处理请求。
在这种介入方式下,模块处理请求的顺序是固定的,即必须在HTTP框架定义的NGX_HTTP_CONTENT_PHASE阶段开始处理请求。
二、编码
1、定义函数和引用头文件
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r);
2、定义mytest 配置项的处理(ngx_command_t)
需要定义一个ngx_command_t数组,并设置在出现 mytest 配置后的解析方法由 ngx_http_mytest“担当”。下图为ngx_command_t数组详解。
真实代码如下:
static ngx_command_t ngx_http_hello_commands[] = {
{
ngx_string("mytest"),
NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_NOARGS,
ngx_http_hello,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL
},
ngx_null_command
};
其中,ngx_http_mytest是ngx_command_t结构体中的set 成员,当在某个配置块中出现 mytest 配置项时,Nginx 将会调用ngx_http_mytest方法。
3、实现ngx_http_mytest方法
static char *ngx_http_mytest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {
ngx_http_core_loc_conf_t *clcf;
/*首先找到 mytest 配置项所属的配置块,clcf看上去像是 location 块内的数据结构,其实不然,它可以是main、srv或者1oc级别配置项,也就是说,在每个http{}和server(}内也都有一个ngx httpcore loc conf t结构体*/
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
/*HTTP 框架在处理用户请求进行到NGX_HTTP_CONTENT_PHASE阶段时,如果请求的主机域名、URI 与mytest配置项所在的配置块相匹配,就将调用我们实现的ngx_http_mytest_handler方法处理这个请求 */
clcf->handler = ngx_http_mytest_handler;
return NGX_CONF_OK;
}
4、定义ngx_http_module_t接口
如果没有什么是必须在HTTP框架初始化时完成,可以向下面一样进行定义。
static ngx_http_module_t ngx_http_mytest_module_ctx = { NULL, NULL, NULL, NULL,
NULL, NULL, NULL, NULL };
5、定义mystest模块
**
* 模块的定义
*/
ngx_module_t ngx_http_mytest_module = {
NGX_MODULE_V1,
&ngx_http_mytest_module_ctx,
ngx_http_mytest_commands,
NGX_HTTP_MODULE,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NGX_MODULE_V1_PADDING
};
这样,mytest模块在编译时将会被加入到ngx_modules全局数组中。Nginx在启动时,会调用所有模块的初始化回调方法,当然,这个例子中我们没有实现它们(也没有实现HTTP 框架初始化时会调用的ngxhttp_modulet中的8个方法)
6、实现ngx_http_mytest_handler方法
1)添加HTTP头部
ngx table elt t*h= ngx_list_push(&r->headers_out.headers);
if(h==NULL){
return NGX ERROR;
}
h->hash =1;
h->key.len=sizeof("TestHead")-1;
h->key.data=(uchar*)"TestHead";
h->value.len=sizeof("TestValue")-1;
h->value.data=(uchar*)"TestValue";
这样将会在响应中新增一行 HTTP 头部:
TestHead: TestValud\r\n
如果发送的是一个不含有HTTP包体的响应,这时就可以直接结束请求了(例如,在ngx_http_mytest_handler方法中,直接在ngx_http_send_header 方法执行后将其返回值 return即可)。
若不修改头部这里可省略。
2)将字符串作为包体发送
调用 ngx_http_output_filter方法即可向客户端发送 HTTP 响应包体。
/**
* 模块回调函数,输出hello world
*/
static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r) {
if (!(r->method & (NGX_HTTP_GET | NGX_HTTP_HEAD))) {
return NGX_HTTP_NOT_ALLOWED;
}
/*接受包体并丢弃,这一步必须存在,因为有些客户端可能会一直试图
发送包体,而如果 HTTP模块不接收发来的TCP流,有可能造成客户端发送超时。*/
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 response = ngx_string("Hello World");
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_length_n = response.len;
r->headers_out.content_type = type;
//发送HTTP头部
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, response.len);
if (b == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ngx_memcpy(b->pos, response.data, response.len);
b->last = b->pos + response.len;
b->last_buf = 1;
ngx_chain_t out;
out.buf = b;
out.next = NULL;
return ngx_http_output_filter(r, &out);
}
7、添加对应的config文件
在C文件同级目录下新建config文件
ngx_addon_name=ngx_http_mytest_module
HTTP_MODULES="$HTTP_MODULES ngx_http_mytest_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_mytest_module.c"
8、编译
此处以mytest_module目录为例
./configure --add-module=src/http/modules/mytest_module
make
make install