【Nginx】Nginx中定义自己的HTTP模块

一、前言

需要明确的是:

  1. 不希望模块对所有的HTTP请求起作用
  2. 在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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值