Nginx(filter)过滤模块开发

什么是过滤模块

在这里插入图片描述
Nignx是一个代理服务器, 他前端被客户端请求,后端连接服务器。这里涉及的数据处理大概有

  1. 客户端请求数据,Nginx直接返回(handler 模块)
  2. 客户端请求数据,Nginx转发给服务器(upstream 模块)
  3. 服务器返回数据,Nginx转发给客户端(filter 模块)

upstream(转发)模块

filter(过滤器)模块

首先明确,nginx目的并非是为了实现某个功能,而是去配置属性,用以增删改conf中的配置类目。

static ngx_command_t ngx_http_prefix_filter_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_prefix_filter_conf_t, enable),
		NULL
	},
	ngx_null_command
};

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
};
ngx_module_t ngx_http_prefix_filter_module = {
    
    
	NGX_MODULE_V1,//填充结构体开头部分
	&ngx_http_prefix_filter_module_ctx,
	ngx_http_prefix_filter_commands,
	NGX_HTTP_MODULE,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NGX_MODULE_V1_PADDING
}; 

handler(前置挡板)模块

创建模块的资源

常用结构体

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;
};


struct ngx_module_s {
    
    
    ngx_uint_t            ctx_index; //是哪个进程
    ngx_uint_t            index;	//进程id

    char                 *name;

    ngx_uint_t            spare0;
    ngx_uint_t            spare1;

    ngx_uint_t            version; //版本号
    const char           *signature; //签名证书

    void                 *ctx;
    ngx_command_t        *commands;
    ngx_uint_t            type;// x模块类型

    ngx_int_t           (*init_master)(ngx_log_t *log);//

    ngx_int_t           (*init_module)(ngx_cycle_t *cycle); //模块启动时候

    ngx_int_t           (*init_process)(ngx_cycle_t *cycle); //进程启动时候
    ngx_int_t           (*init_thread)(ngx_cycle_t *cycle);
    void                (*exit_thread)(ngx_cycle_t *cycle);
    void                (*exit_process)(ngx_cycle_t *cycle);

    void                (*exit_master)(ngx_cycle_t *cycle);

    uintptr_t             spare_hook0;
    uintptr_t             spare_hook1;
    uintptr_t             spare_hook2;
    uintptr_t             spare_hook3;
    uintptr_t             spare_hook4;
    uintptr_t             spare_hook5;
    uintptr_t             spare_hook6;
    uintptr_t             spare_hook7;
};
typedef struct ngx_module_s ngx_module_t;

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);
    char       *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);

    void       *(*create_loc_conf)(ngx_conf_t *cf);
    char       *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);
} ngx_http_module_t;

  1. 其中ngx_http_module_t结构体与下图部分的回调函数按顺序一一匹配,所以实现ngx_module_t时候可以以ngx_http_module_t结构体代替
  2. 这八个回调函数将会按照从上往下的顺序依次执行
    在这里插入图片描述
    ngx_http_module_t结构体与ngx_module_t中的

常用宏定义

#define NGX_MODULE_V1                                                         \
    NGX_MODULE_UNSET_INDEX, NGX_MODULE_UNSET_INDEX,                           \
    NULL, 0, 0, nginx_version, NGX_MODULE_SIGNATURE

#define NGX_MODULE_V1_PADDING  0, 0, 0, 0, 0, 0, 0, 0


#define NGX_HTTP_MODULE           0x50545448   /* "HTTP" 模块*/

/* 以下宏定义为了去确定该项配置属于哪个类目下 
比如service 
比如location
*/
#define NGX_HTTP_MAIN_CONF        0x02000000
#define NGX_HTTP_SRV_CONF         0x04000000
#define NGX_HTTP_LOC_CONF         0x08000000
#define NGX_HTTP_UPS_CONF         0x10000000
#define NGX_HTTP_SIF_CONF         0x20000000
#define NGX_HTTP_LIF_CONF         0x40000000
#define NGX_HTTP_LMT_CONF         0x80000000

filter(过滤器)实现

create_loc_conf

内存池中分配一片kong空间,用以存储指令对应的值

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;
}

merge_loc_conf

判断上级作用域是否有该指令,有则延续上级作用域中该指令的值,否则使用当前作用域中的指令

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;
}

其中 ngx_conf_merge_value

#define ngx_conf_merge_value(conf, prev, default)                            \
    if (conf == NGX_CONF_UNSET) {
      
                                                  \
        conf = (prev == NGX_CONF_UNSET) ? default : prev;                    \
    

postconfiguration

  1. 解析完毕conf文件后执行该指令,设置运行时候的回调函数
  2. 使用头插法,将header_filterbody_filter插入filter队列的头部
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;

	return NGX_OK;
}

修改header信息

  1. 由于修改了body内容,那么header中的length字段自然要做出相应的修改
  2. 设置全局上下文,供给后续的body使用,并使其判断修改header时候是否成功

static ngx_str_t filter_prefix = ngx_string("<h2>Author : Issac</h2><p><a href=\"https://blog.csdn.net/qq_34954047?type=blog\">CSDN</a></p>");


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) {
    
    

		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信息

  1. 找到修改header时候的上下文
  2. 依据将要插入的ngx_string 初始化ngx_buf_t 结构体,
  3. 将ngx_buf_t 结构体插入发送链
  4. 由于是头插法,我们的ngx_string自然的就出现在了body的最前端。
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 *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);
}

将模块编入进nginx

准备环境

安装zlib

curl -O http://www.zlib.net/zlib-1.2.11.tar.gz
tar xvf zlib-1.2.11.tar.gz
cd zlib-1.2.11
./configure --prefix=/usr/share/nginx/zlib-1.2.11
make -j4
make install

安装pcre

wget https://netix.dl.sourceforge.net/project/pcre/pcre/8.40/pcre-8.40.tar.gz
tar xvf pcre-8.40.tar.gz
cd pcre-8.40
./configure --prefix=/usr/share/nginx/pcre-8.40
make -j4
make install

安装openssl

wget https://www.openssl.org/source/openssl-1.1.1b.tar.gz
tar xvf openssl-1.1.1b.tar.gz
cd openssl-1.1.1b
./config --prefix=/usr/share/nginx/openssl-1.1.1b
make -j4
make install

编译

注意:指令中指定的pcre、openssl等路径是源码路径

./configure --prefix=/usr/local/nginx --with-http_realip_module --with-http_addition_module --with-http_ssl_module --with-http_gzip_static_module --with-http_secure_link_module --with-http_stub_status_module --with-stream --with-pcre=/home/lsy/tools/pcre-8.40 --with-zlib=/home/lsy/tools/zlib-1.2.11 --with-openssl=/home/lsy/tools/openssl-1.1.1g --add-module=/home/lsy/study/nginx/nginx_study

在这里插入图片描述
出现上述截图内容,意味该模块已加入工程,也可以通过objs/ngx_modules.c查看模块名是否已经加入其中

module的外部申明出现在该代码文件中,且被下面两个数组所包含,即为可用
extern ngx_module_t ngx_http_header_filter_module;
ngx_module_t *ngx_modules[]
char *ngx_module_names[]

确认无误后继续执行下述指令编译

make
make install

修改配置文件

cd /usr/local/nginx
vim conf/nginx.conf

添加如下指令(该指令是我通过代码新增的指令
在这里插入图片描述

运行

c
./sbin/nginx -s stop
./sbin/nginx -c conf/nginx.conf

在这里插入图片描述

注意

由于浏览器是由cache能的,修改nginx配置后,刷新浏览器可能不会体现出对应的修改,这个时候需要清除浏览器的缓存,不同浏览器缓存的清除方式不同,自行百度吧

猜你喜欢

转载自blog.csdn.net/qq_34954047/article/details/123449456
今日推荐