Nginx filter module implementation

1 Nginx module addition process

The nginx module is generally divided into three categories: one is the handler module, which means that when an http request is received, nginx processes it directly and returns it to the client; the other is the filter module, which can be sent to the server when we receive the http request. Modify the content returned by the http server before returning; the third type is the upstream module, which mainly modifies the http request before forwarding it to the server after receiving the http request.
insert image description here

1 Configuration file writing

The configuration file needs to be written before the module is written. Generally, it is placed in the root directory of the module: it contains three parts of information, one is the name of the module where the name needs to be consistent with the module name ngx_module_t defined in the code; the second part is the type of the specified module And the name, here is a filter module; finally, specify the module source file path.

ngx_addon_name=ngx_http_prefix_filter_module
HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES ngx_http_prefix_filter_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_prefix_filter_module.c"

During the compilation process, it is necessary to compile the nginx source code. If you are not sure, you can refer to the nginx installation and configuration ; here you need to add the path of the new module, generally use the absolute path

--add-module=PATH

2 Implementation process

The implementation process is divided into two parts: one is to analyze the configuration, and the other is to implement the business logic.

2.1 Configuration parsing process

The first step is to implement the definition of the module and set the type of the module. The context of the http module is related to the command parsing of the module.

ngx_module_t ngx_http_prefix_filter_module = {
    
    
	NGX_MODULE_V1,
	&ngx_http_prefix_filter_module_ctx,//http模块定义
	ngx_http_prefix_filter_commands,//filter模块定义
	NGX_HTTP_MODULE,//模块类型HTTP
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NGX_MODULE_V1_PADDING
}; 

The second part is the http context callback function setting, which is mainly to prepare for subsequent data processing. Here, you need to choose different creation functions and initialization functions according to the scope of your configuration items, including three scopes. One is the root configuration. The second type is within the scope of the server; the third type has the smallest scope and only acts on the corresponding location.

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);//server配置创建函数
    char       *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);//主配置初始化函数

    void       *(*create_loc_conf)(ngx_conf_t *cf);//location配置创建函数
    char       *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);//location配置初始化函数
} ngx_http_module_t;
static ngx_http_module_t ngx_http_prefix_filter_module_ctx = {
    
    
	NULL,
	ngx_http_prefix_filter_init,
	NULL,
	NULL,
	NULL,
	NULL,
	ngx_http_prefix_filter_create_conf,
	ngx_http_prefix_filter_merge_conf
};

The third part is the parsing part of the initialization configuration item, which includes the configuration name; the scope of the configuration item and the type of the configuration item; setting the slot function;

struct ngx_command_s {
    
    
    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;
};
static ngx_command_t ngx_http_prefix_filter_commands[] = {
    
    
	{
    
    
		ngx_string("add_prefix"),//名称add_prefix
		NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_FLAG,//作用域location和类型flag
		ngx_conf_set_flag_slot,//选择flag类型的槽函数
		NGX_HTTP_LOC_CONF_OFFSET,//location偏移量
		offsetof(ngx_http_prefix_filter_conf_t, enable),//传入结构体和保存变量的成员
		NULL
	},
	ngx_null_command
};

3 nginx filter module data flow:

The overall data flow of the nginx module call is as follows: first, nginx enters the module parsing part, first is the definition part of the parsing module, and the two core parameters are the command object and the ctx context; there are two lines here, and the context of one line is mainly It is to load the http module, insert the new module into the middle of the original filter module process of nginx, initialize the configuration content at the same time, register the callback function of http header analysis and body analysis; the other line is configuration command analysis, mainly to set the name of the configuration item , where scopes, slot functions, and configuration items are saved after resolution.
insert image description here

The first is the configuration structure space allocation: use the nginx memory pool to allocate space, save configuration values; and initialize;

static void *ngx_http_prefix_filter_create_conf(ngx_conf_t *cf) {
    
    

	ngx_http_prefix_filter_conf_t *conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_prefix_filter_conf_t));
	if (conf == NULL) {
    
    
		return NULL;
	}

	conf->enable = NGX_CONF_UNSET;

	return conf;
}

The process of configuration modification is mainly to use the replacement of old and new configurations. When there is a modification, the modification is used first, otherwise the old value is used.

static char *ngx_http_prefix_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child) {
    
    
	ngx_http_prefix_filter_conf_t *prev = (ngx_http_prefix_filter_conf_t*)parent;
	ngx_http_prefix_filter_conf_t *conf = (ngx_http_prefix_filter_conf_t*)child;

	ngx_conf_merge_value(conf->enable, prev->enable, 0);

	return NGX_CONF_OK;
}

Add the module to the httpfilter list: The position added here is fixed, and it is fixed to be added in front of the http_top_header_filter after the module is inserted.

static ngx_int_t ngx_http_prefix_filter_init(ngx_conf_t *cf) {
    
    

	ngx_http_next_header_filter = ngx_http_top_header_filter;
	ngx_http_top_header_filter = ngx_http_prefix_filter_header_filter;//头处理函数

	ngx_http_next_body_filter = ngx_http_top_body_filter;
	ngx_http_top_body_filter = ngx_http_prefix_filter_body_filter;//body处理函数

	return NGX_OK;
}

Head filter function: mainly to modify the length of the header content, so that the content can be added or modified later.

static ngx_int_t ngx_http_prefix_filter_header_filter(ngx_http_request_t *r) {
    
    

	ngx_http_prefix_filter_ctx_t *ctx;
	ngx_http_prefix_filter_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_prefix_filter_module);
	if (ctx) {
    
    
		//交给后面的模块处理
		return ngx_http_next_header_filter(r);
	}
	
	conf = ngx_http_get_module_loc_conf(r, ngx_http_prefix_filter_module);
	if (conf == NULL) {
    
    
		return ngx_http_next_header_filter(r);
	}
	if (conf->enable == 0) {
    
    
		return ngx_http_next_header_filter(r);
	}

	//创建标志,用于防止多次进行处理
	ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_prefix_filter_ctx_t));
	if (ctx == NULL) {
    
    
		return NGX_ERROR;
	}
	ctx->add_prefix = 0;
	//将标志加入到上下文
	ngx_http_set_ctx(r, ctx, ngx_http_prefix_filter_module);
	if (r->headers_out.content_type.len >= sizeof("text/html") - 1
		&& ngx_strncasecmp(r->headers_out.content_type.data, (u_char*)"text/html", sizeof("text/html")-1) == 0) {
    
    
		//这里将http头修改,成新的body的长度
		ctx->add_prefix = 1;
		if (r->headers_out.content_length_n > 0) {
    
    
			r->headers_out.content_length_n += filter_prefix.len;
		}

		
	}
	
	return ngx_http_prefix_filter_header_filter(r);
}

Body content processing: Content processing is mainly to modify the content. The content processing modification here adopts

static ngx_int_t ngx_http_prefix_filter_body_filter(ngx_http_request_t *r, ngx_chain_t *in) {
    
    
	//利用标志位保证头修改正确后,才能修改内容。
	ngx_http_prefix_filter_ctx_t *ctx = ngx_http_get_module_ctx(r, ngx_http_prefix_filter_module);
	if (ctx == NULL || ctx->add_prefix != 1) {
    
    
		return ngx_http_next_body_filter(r, in);
	}
	
	ctx->add_prefix = 2;
	//内容修改采用的是ngx_buf_t进行纯粹,获取到块内存后,将数据加入start,同时设置数据的结束位置。
	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节点将数据填充进入到链头部节点
	ngx_chain_t *cl = ngx_alloc_chain_link(r->pool);
	cl->buf = b;
	cl->next = in;
	//最后将body交由下一个模块处理
	return ngx_http_next_body_filter(r, cl);
}

Guess you like

Origin blog.csdn.net/qq_38731735/article/details/123510681
Recommended