前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Nginx模块开发:自定义HTTP过滤器filter

Nginx模块开发:自定义HTTP过滤器filter

作者头像
Lion 莱恩呀
发布于 2025-01-07 13:59:33
发布于 2025-01-07 13:59:33
42000
代码可运行
举报
概述
本文介绍了如何在Nginx中开发自定义HTTP过滤器,涵盖Nginx模块的基本结构及编译过程。展示如何通过示例实现请求和响应的修改,包括处理请求头和响应体。
文章被收录于专栏:后端开发技术后端开发技术
运行总次数:0
代码可运行

一、过滤器模块简介

Nginx工作概括图如下:

过滤器作用在服务器回发数据给客户端的中间过程。过滤器基于HTTP协议的基础上。

二、Nginx相关数据结构介绍

2.1、ngx_module的数据结构

代码语言:C
换行
代码运行次数:0
自动换行
运行AI代码解释
#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 struct ngx_module_s { ngx_uint_t ctx_index; ngx_uint_t index; 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; 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_str_t name; void *(*create_conf)(ngx_cycle_t *cycle); char *(*init_conf)(ngx_cycle_t *cycle, void *conf); } ngx_core_module_t;

2.2、ngx_http_module数据结构

代码语言:C
换行
代码运行次数:0
自动换行
运行AI代码解释
typedef struct { void **main_conf; void **srv_conf; void **loc_conf; } ngx_http_conf_ctx_t; typedef struct { ngx_int_t (*preconfiguration)(ngx_conf_t *cf); // 解析配置文件之前 ngx_int_t (*postconfiguration)(ngx_conf_t *cf); // 解析配置文件完成之后 // **_main_ **解析配置文件中http关键字的内部 void *(*create_main_conf)(ngx_conf_t *cf); char *(*init_main_conf)(ngx_conf_t *cf, void *conf); // **_srv_ **解析配置文件中server关键字的内部 void *(*create_srv_conf)(ngx_conf_t *cf); char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf); // **_loc_ **解析配置文件中location关键字的内部 void *(*create_loc_conf)(ngx_conf_t *cf); char *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf); } ngx_http_module_t;

2.3、ngx_command数据结构

代码语言:C
换行
代码运行次数:0
自动换行
运行AI代码解释
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; };

/src/http/ngx_http_config.h中定义的相关宏(type会用到):

代码语言:C
换行
代码运行次数:0
自动换行
运行AI代码解释
#ifndef _NGX_HTTP_CONFIG_H_INCLUDED_ #define _NGX_HTTP_CONFIG_H_INCLUDED_ #include <ngx_config.h> #include <ngx_core.h> #include <ngx_http.h> typedef struct { void **main_conf; void **srv_conf; void **loc_conf; } ngx_http_conf_ctx_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; #define NGX_HTTP_MODULE 0x50545448 /* "HTTP" */ #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 #define NGX_HTTP_MAIN_CONF_OFFSET offsetof(ngx_http_conf_ctx_t, main_conf) #define NGX_HTTP_SRV_CONF_OFFSET offsetof(ngx_http_conf_ctx_t, srv_conf) #define NGX_HTTP_LOC_CONF_OFFSET offsetof(ngx_http_conf_ctx_t, loc_conf) #define ngx_http_get_module_main_conf(r, module) \ (r)->main_conf[module.ctx_index] #define ngx_http_get_module_srv_conf(r, module) (r)->srv_conf[module.ctx_index] #define ngx_http_get_module_loc_conf(r, module) (r)->loc_conf[module.ctx_index] #define ngx_http_conf_get_module_main_conf(cf, module) \ ((ngx_http_conf_ctx_t *) cf->ctx)->main_conf[module.ctx_index] #define ngx_http_conf_get_module_srv_conf(cf, module) \ ((ngx_http_conf_ctx_t *) cf->ctx)->srv_conf[module.ctx_index] #define ngx_http_conf_get_module_loc_conf(cf, module) \ ((ngx_http_conf_ctx_t *) cf->ctx)->loc_conf[module.ctx_index] #define ngx_http_cycle_get_module_main_conf(cycle, module) \ (cycle->conf_ctx[ngx_http_module.index] ? \ ((ngx_http_conf_ctx_t *) cycle->conf_ctx[ngx_http_module.index]) \ ->main_conf[module.ctx_index]: \ NULL) #endif /* _NGX_HTTP_CONFIG_H_INCLUDED_ */

三、Nginx过滤器模块开发

3.1、Nginx模块开发流程

(1)定义一个模块名,ngx_module_t,选择好http模块NGX_HTTP_MODULE。(2)定义cmd命令,有多少条cmd写多少条cmd,ngx_command_t。(3)定义用来解析http block,ngx_http_module_t。(4)执行过程实现添加模块。

3.2、Nginx 模块执行

(1)初始化。当进程启动的时候进行的模块初始化。(2)解析conf文件。解析conf文件中模块的相关命令和设置。(3)Nginx启动之后,有命令或请求到来时,处理请求的流程。

开发模块时,需要实现的主要是这三个流程的功能。

3.3、示例代码

这里主要实现了在返回的网页中添加一个内容。里面在重点地方附上了详细注释。ngx_http_filter_module.c:

代码语言:C
换行
代码运行次数:0
自动换行
运行AI代码解释
#include <ngx_config.h> #include <ngx_http.h> #include <ngx_core.h> typedef struct { ngx_flag_t enable; }ngx_http_filter_conf_t; static ngx_str_t prefix = ngx_string("<h2>FLY. </h2>"); static ngx_http_output_header_filter_pt ngx_http_next_header_filter; static ngx_http_output_body_filter_pt ngx_http_next_body_filter; // ngx_int_t (*ngx_http_output_header_filter_pt)(ngx_http_request_t *r) // 添加头,header ngx_int_t ngx_http_fly_header_filter(ngx_http_request_t *r) { if (r->headers_out.status != NGX_HTTP_OK) { // 不正常返回,则进行next return ngx_http_next_header_filter(r); } //r->headers_out.content_type.len == sizeof("text/html") r->headers_out.content_length_n += prefix.len; return ngx_http_next_header_filter(r); } // ngx_int_t (*ngx_http_output_body_filter_pt)(ngx_http_request_t *r, ngx_chain_t *chain) // 添加内容,body ngx_int_t ngx_http_fly_body_filter(ngx_http_request_t *r, ngx_chain_t *chain) { /* * 关于ngx_chain_t: * 在nginx中,有一个数据链,存放要发送的数据。 * O->O->O->O * 每次send的是ngx_chain_t中的一个ngx_buf_t */ // 添加一个chain buffer ngx_buf_t *b = ngx_create_temp_buf(r->pool, prefix.len); b->start = b->pos = prefix.data; b->last = b->pos + prefix.len; ngx_chain_t *c1 = ngx_alloc_chain_link(r->pool); c1->buf = b; c1->next = chain; return ngx_http_next_body_filter(r, c1); } // ngx_int_t (*postconfiguration)(ngx_conf_t *cf); // 解析完配置文件之后的动作,也就是解析完http关键字模块之后 ngx_int_t ngx_http_fly_filter_init(ngx_conf_t *cf) { // 模块的初始化 // http { } // O->O->O->O // 多个模块的头插法,取出最前面的模块 ngx_http_next_header_filter = ngx_http_top_header_filter; ngx_http_top_header_filter = ngx_http_fly_header_filter; ngx_http_next_body_filter = ngx_http_top_body_filter; ngx_http_top_body_filter = ngx_http_fly_body_filter; return NGX_OK; } // void *(*create_loc_conf)(ngx_conf_t *cf); // 解析conf文件location关键字之前的动作 void *ngx_http_fly_filter_create_loc_conf(ngx_conf_t *cf) { ngx_http_filter_conf_t *conf = ngx_palloc(cf->pool, sizeof(ngx_http_filter_conf_t)); if (conf == NULL) return NULL; conf->enable = NGX_CONF_UNSET; return conf; } // char *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf); // 解析完配置文件location关键字之后的动作 // 模块可能在多个地方定义,这个函数合并所有的值一起使用 char *ngx_http_fly_filter_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) { ngx_http_filter_conf_t *prev = (ngx_http_filter_conf_t*)parent; ngx_http_filter_conf_t *next = (ngx_http_filter_conf_t*)child; // 合并enable的值 ngx_conf_merge_value(next->enable, prev->enable, 0); return NGX_CONF_OK; } /* 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; }; */ /* // conf文件命令解析 char *ngx_http_fly_filter_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { char *p = conf; // 对应 ngx_http_fly_filter_create_loc_conf函数的conf->enable = NGX_CONF_UNSET; ngx_flag_t *flag = (p + cmd->offset); return NGX_CONF_OK; } */ // conf文件中的每一行都是一个指令指令 ngx_command_t ngx_http_fly_filter_module_cmd[] = { { //命令名称,比如listen,定义了就可以在conf文件中使用,注意不能和其他的起冲突 ngx_string("predix"), // 指示name命令放的位置在哪里以及可以带多少个参数,NGX_CONF_FLAGE表示开关标志 // predix on/off NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_CONF_FLAG, // 命令解析,可以使用nginx内部的也可以自己实现 ngx_conf_set_flag_slot,//ngx_http_fly_filter_set_slot, NGX_HTTP_LOC_CONF_OFFSET, // offsetof获取enable在结构体中的偏移位置 offsetof(ngx_http_filter_conf_t,enable), NULL, }, ngx_null_command }; // 用来解析对应的conf文件 static ngx_http_module_t ngx_http_fly_filter_module_ctx = { NULL, ngx_http_fly_filter_init, NULL, NULL, NULL, NULL, ngx_http_fly_filter_create_loc_conf, ngx_http_fly_filter_merge_loc_conf }; // 模块定义 ngx_module_t ngx_http_fly_filter_module = { NGX_MODULE_V1, &ngx_http_fly_filter_module_ctx, ngx_http_fly_filter_module_cmd, // http的ascii值,指示是什么模块 NGX_HTTP_MODULE, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NGX_MODULE_V1_PADDING // 填充 };

3.4、编写config文件

创建:

代码语言:Bash
换行
自动换行
AI代码解释
touch config

内容:

代码语言:Bash
换行
自动换行
AI代码解释
ngx_addon_name=ngx_http_fly_filter_module HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES ngx_http_fly_filter_module" NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_filter_module.c"

注意,config文件要和模块的代码在相同目录。

3.5、编译模块到Nginx源码中

(1)配置中添加模块:

代码语言:Bash
换行
自动换行
AI代码解释
./configure --prefix=/usr/local/nginx --with-http_realip_module --with-http_addition_module --with-http_gzip_static_module --with-http_secure_link_module --with-http_stub_status_module --with-stream --with-pcre=/home/fly/workspace/pcre-8.41 --with-zlib=/home/fly/workspace/zlib-1.2.11 --with-openssl=/home/fly/workspace/openssl-1.1.0g --add-module=/mnt/hgfs/sourcecode_learning/ngx_http_filter_module

注意模块路径要正确。出现如下表示成功:

代码语言:Bash
换行
自动换行
AI代码解释
configuring additional modules adding module in /mnt/hgfs/sourcecode_learning/ngx_http_filter_module + ngx_http_fly_filter_module was configured creating objs/Makefile

(2)查看是否添加模块到动态代码中:

代码语言:Bash
换行
自动换行
AI代码解释
vim objs/ngx_modules.c

(3)编译:

代码语言:Bash
换行
自动换行
AI代码解释
make sudo make install

3.6、执行效果

编译安装完成后,在conf文件中添加模块的开关(predix on):

代码语言:JSON
换行
自动换行
AI代码解释
worker_processes 4; events { worker_connections 1024; } http { upstream backend { server 192.168.7.146:8889; server 192.168.7.146:8890; } server { listen 8888; location / { proxy_pass http://backend; } } server { listen 8889; } server { listen 8890; predix on; } server { listen 8891; } }

执行Nginx:

代码语言:Bash
换行
自动换行
AI代码解释
sudo /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/fly.conf

在网页输入IP和端口,执行效果如下:

可以看到,返回的网页中多出来添加的内容(FLY.)。

四、总结

  1. Nginx中http模块非常多,每个模块都会有ngx_http_module_t,为了防止解析过程出现冲突,Nginx编译的时候会把所有的模块都集中起来,组织到/obj/ngx_module.c(以数组的方式)。

  2. 在编译模块时,需要编写config文件,这个文件最好不要使用笔记本编辑(notepad),容易造成编码方式的错误。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Nginx的模块化设计
高度模块化的设计是 Nginx 的架构基础。Nginx主框架中只提供了少量的核心代码,大量强大的功能是在各模块中实现的。
mazhen
2023/11/24
5390
Nginx的模块化设计
Nginx之如何编写Handler模块
Handler模块简介 基本上作为第三方开发者最可能开发的就是三种类型的模块,即handler,filter和load-balancer。Handler模块就是接受来自客户端的请求并产生输出的模块。有些地方说upstream模块实际上也是一种handler模块,只不过它产生的内容来自于从后端服务器获取的,而非在本机产生的。 在配置文件中使用location指令可以配置content handler模块,当Nginx系统启动的时候,每个handler模块都有一次机会把自己关联到对应的location上。如果有
用户1263954
2018/01/30
1.2K0
Nginx之如何编写Handler模块
Nginx 第三方模块使用与开发
Nginx 允许引入第三方模块来扩展 Nginx 的功能。官方网站 NGINX 3rd Party Modules 列出了 Nginx 很多的第三方模块。除此之外,很多很有用的模块也能在 github 等网站上找到。
Se7en258
2021/07/01
2.5K0
解读Nginx:深入剖析HTTP启动流程
1、遇到conf文件的http模块。http不是在Nginx的mian函数中启动,而是解析conf文件时遇到http才会去解析并启动。
Lion 莱恩呀
2025/01/08
1420
解读Nginx:深入剖析HTTP启动流程
nginx0.1.0之http模块初始化源码分析(1)
http模块的初始化类似event模块,初始化的起点在解析到http指令的时候。对应的处理函数是ngx_http_block,因为该函数比较长,所以我们分段解析。第一部分先解析http模块的pre_conf、create_main_conf函数的实现。
theanarkh
2019/03/06
6070
揭秘Nginx中如何使用红黑树优化数据操作
/src/http/modules/ngx_stream_log_module.c
Lion 莱恩呀
2025/01/13
3800
揭秘Nginx中如何使用红黑树优化数据操作
nginx0.1.0 access_handler模块源码分析
access模块主要是对连接到nginx中的客户端进行权限管理,nginx会根据access模块注册的命令,在解析命令的时候,遇到对应的命令的时候,会调用access注册的处理函数,access模块的处理函数会解析并记录这些配置,然后在每个连接到来时,判断ip是否在封禁的池子了。nginx只判断deny的规则,即同一个ip命中多条规则的时候,只要有一条是deny则结果就是deny。即deny优先。
theanarkh
2019/04/24
4190
Nginx模块开发:http handler实现流量统计(进阶篇)
Nginx通过模块化的方式提供了丰富的功能扩展能力。其中,HTTP Handler是Nginx模块开发中非常重要的一个概念。HTTP Handler可以用来拦截、处理和操作传入的HTTP请求,在请求的生命周期中执行特定的逻辑。它可以用于实现各种功能,如流量统计、访问控制、缓存管理等。
Lion 莱恩呀
2025/01/12
2160
Nginx模块开发:http handler实现流量统计(进阶篇)
nginx0.1.0之event模块初始化源码分析(1)
nginx模块初始化的流程在下面的代码中,核心模块的初始化,各核心模块首先在create_conf中创建保存配置的数据结构,然后在ngx_conf_parse中,通过解析命令,执行对应的命令处理函数,完成赋值和各核心模块的子模块初始化。最后,如果在ngx_conf_parse时,没有设置值,则执行init_conf函数进行默认初始化。
theanarkh
2019/03/06
5040
nginx0.1.0之http模块初始化源码分析(3)
我们继续分析ngx_http_block函数的代码,指令解析完,继续执行各子模块的钩子函数。
theanarkh
2019/03/06
5370
Nginx解读内置非默认模块 ngx_http_stub_status_module
http://nginx.org/en/docs/http/ngx_http_stub_status_module.html
星哥玩云
2022/07/24
8860
nginx http模块数据存储结构
从本节开始,我们将进入http模块实现原理的讲解,关于http模块,有一个非常重要的点就是其是如何存储http块、server块和location块的数据的,而且nginx有的配置项是可以在多个配置块中使用的,当http块、server块和location块中两个或者两个以上的配置块都配置了该配置项的时候,就会有一个问题是,nginx是如何处理这些配置项的。本文主要讲解http块中的各个模块数据的存储方式,这将是理解nginx的http模块的工作方式的重要基石。
会呼吸的Coder
2020/03/11
1.1K0
nginx http模块数据存储结构
nginx0.1.0之event模块初始化源码分析(3)
前面已经分析了event初始化的整体流程和第一步create_conf,接下来看一下第二步ngx_conf_parse。这里不分析该函数的代码,该函数主要是遍历配置文件的内容,然后读取命令和参数。最后匹配nginx所有模块的配置,找到处理该指令的函数。我们首先看一下event模块中ngx_event_core_module模块的指令配置。
theanarkh
2019/03/06
5800
nginx proxy_bind支持多ip绑定
proxy_bind隶属于proxy_module,为向后端建立连接时的local ip,在nginx源码中只支持bind一个ip进行回源,若想使用多个ip进行回源时,可以修改源码支持bind ip数组。在实际应用中我就是这样做的。bind ip数据轮询选择ip进行回源与upstream建立连接,以解决单ip回源连接数限制问题。下面proxy_bind部分就是针对proxy_bind进行优化后的代码,支持bind多ip。
stan1ey
2021/06/07
4.5K0
nginx proxy_bind支持多ip绑定
Nginx模块开发:http handler实现流量统计(入门篇)
Nginx通过模块化的方式提供了丰富的功能扩展能力。其中,HTTP Handler是Nginx模块开发中非常重要的一个概念。HTTP Handler可以用来拦截、处理和操作传入的HTTP请求,在请求的生命周期中执行特定的逻辑。它可以用于实现各种功能,如流量统计、访问控制、缓存管理等。
Lion 莱恩呀
2025/01/11
1530
Nginx模块开发:http handler实现流量统计(入门篇)
nginx0.1.0之http模块初始化源码分析(2)
本文讲解http各个模块create_srv_conf和create_loc_conf钩子,还有指令的解析。 各模块的create_srv_conf和create_loc_conf函数逻辑都类似,不一一列举,执行完后内存视图是。
theanarkh
2019/03/06
3940
nginx ngx_http_dyups_module模块源码分析
nginx 动态修改upstream不reload nginx模块,ngx_http_dyups_module分析。
stan1ey
2021/06/07
1.4K0
nginx源码阅读(6)http处理流程
Nginx作为一款开源的、高性能的HTTP服务器和反向代理服务器而闻名,本文基于nginx-1.15.0,将为读者简要介绍其HTTP处理流程。
golangLeetcode
2022/08/02
1.3K0
nginx源码阅读(6)http处理流程
nginx源码阅读(4)单进程epoll流程解析
我们这里以单进程启动为例 nginx.c中的main 函数调用ngx_single_process_cycle
golangLeetcode
2022/08/02
4520
探索Nginx:深入理解Nginx基础组件的使用
Nginx自己实现了一个内存池组件。Nginx作为服务器,当客户端 TCP连接 &HTTP请求 到来时,Nginx会为该连接创建一个专属的内存池;这个内存池的生命周期是连接建立时创建,连接断开时销毁。客户端和Nginx通信的所有数据和操作(HTTP协议解析、HTTP数据解析等)都在内存池中完成。
Lion 莱恩呀
2025/01/05
3260
探索Nginx:深入理解Nginx基础组件的使用
推荐阅读
相关推荐
Nginx的模块化设计
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验