Nginx模块开发之http过滤器filter

news2025/1/11 22:52:20

Nginx http过滤器filter模块开发

  • 一、过滤器模块简介
  • 二、Nginx相关数据结构介绍
    • 2.1、ngx_module的数据结构
    • 2.2、ngx_http_module数据结构
    • 2.3、ngx_command数据结构
  • 三、Nginx过滤器模块开发
    • 3.1、Nginx模块开发流程
    • 3.2、Nginx 模块执行
    • 3.3、示例代码
    • 3.4、编写config文件
    • 3.5、编译模块到Nginx源码中
    • 3.6、执行效果
  • 总结
  • 后言

一、过滤器模块简介

Nginx工作概括图如下:
nginx_work
过滤器作用在服务器回发数据给客户端的中间过程。过滤器基于HTTP协议的基础上。
nginx_filter

二、Nginx相关数据结构介绍

2.1、ngx_module的数据结构

#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数据结构

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数据结构

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会用到):

#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


#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文件

创建:

touch config

内容:

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)配置中添加模块:

./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

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

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)查看是否添加模块到动态代码中:

vim objs/ngx_modules.c

(3)编译:

make
sudo make install

3.6、执行效果

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

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:

sudo /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/fly.conf 

在网页输入IP和端口,执行效果如下:
filter_show
可以看到,返回的网页中多出来添加的内容(FLY.)。

总结

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

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

后言

本专栏知识点是通过<零声教育>的系统学习,进行梳理总结写下文章,对c/c++linux系统提升感兴趣的读者,可以点击链接,详细查看详细的服务:C/C++服务器课程

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/22573.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

免费嵌入 NFT 数据到任何网站或平台

Nov. 2022, Vincy Data Source: Footprint Analytics - Bingo NFT Widget Footprint Analytics 是一个用于发掘和可视化整个区块链数据的工具&#xff0c;专注于解析 NFT 和 GameFi 等数据。它使用户能够拖放界面以及使用 SQL 或 Python 建立图表和仪表盘&#xff0c;而无需编…

【附源码】计算机毕业设计JAVA学生选拔系统

【附源码】计算机毕业设计JAVA学生选拔系统 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; JAVA mybati…

基于Android的地图服务系统设计与实现

目 录 摘 要 I Abstract II 1绪论 1 1.1 背景及意义 1 1.2 主要方法和研究进展 1 1.3主要内容 1 1.4本论文的结构安排 1 2系统概述 2 2.1开发环境搭建 2 2.1.1 安装JDK 2 2.1.2 安装Eclipse集成开发环境 2 2.1.3 下载安装Android SDK 2 2.1.4 为Eclipse安装ADT插件 3 2.1.5 创建…

2022年最受欢迎的指纹浏览器,你知道几个?

浏览器是我们在电脑上最常使用的软件&#xff0c;以至于我们几乎忽略了它。对于用户来说&#xff0c;如果使用习惯了很难从一种浏览器切换到另一种浏览器。而指纹浏览器对于跨境人来说也是最常使用的软件工具&#xff0c;毕竟它就是为了跨境人打造的一款浏览器。那2022年这几个…

关于typescript中的extends和infer以及用法

extends extend的意思如果不是用在类的扩展中那么就表示约束在。 type Pick<T, K extends keyof T> {[P in K]: T[P]; };比如下面这个例子&#xff1a; 在Picks中K应该约束在keyof T这个类型中。 infer infer表示占位符 逆变和协变 协变&#xff1a;子类型赋给父…

云原生安全系列2:关于镜像安全必须知道的事儿

1.避免特权容器 Docker 提供了一种特权模式&#xff0c;它允许容器在本地计算机上以 root 身份运行。在特权模式下运行容器提供了该主机的功能&#xff0c;包括&#xff1a; 对所有设备的根访问权限能够篡改 AppArmor 和 SELinux 等 Linux 安全模块能够使用主机的内核功能安装…

pytorch神经网络基本骨架nn.module的使用

1.Containers 首先查看官方文档中nn.module骨架&#xff0c;其中有六个模块。 1.1Module import torch.nn as nn import torch.nn.functional as F class Module(nn.Module):def __init__(self):super(Module, self).__init__()self.conv1nn.Conv2d(1, 20, 5)self.conv2nn.C…

ESP32学习笔记 - 基于 ESP32 移植 LVGL8.3

以前写过一篇文章,讲述了如何基于ESP32 芯片移植LVGL这个GUI框架,当时是在LVGL移植好的工程lv_port_esp32上进行的,这个工程最新支持到LVGL7.9版本,关于之前的移植文章,可以参考以下链接: ESP32学习笔记 - 移植LVGL 随着LVGL不断在高频率地迭代大版本,LVGL8.x已经比以…

java基于springboot+vue的酒店预订网站——计算机毕业设计

运行环境&#xff1a; 开发工具:IDEA /Eclipse 数据库:MYSQL5.7 应用服务:Tomcat7/Tomcat8 使用框架springbootvue 项目介绍 民宿管理平台系统&#xff0c;主要的模块包括管理员&#xff1b;首页、个人中心、用户管理、商家管理、民宿信息管理、房间类型管理、房间信息管理、…

基于Springboot+Mybatis+mysql+vue技术交流博客论坛系统

基于SpringbootMybatismysqlvue技术交流博客论坛系统一、系统介绍二、功能展示1.主页(普通用户)2.登陆、注册&#xff08;普通用户&#xff09;3.博客(普通用户)4.文章详情&#xff08;点赞、评论&#xff09;&#xff08;普通用户&#xff09;5.我的文章&#xff08;普通用户&…

模拟电子技术(六)信号的运算与处理

&#xff08;六&#xff09;信号的运算与处理基本运算电路概述比例运算电路反向比例运算电路同相比例运算电路电压跟随器加减运算电路求和运算电路加减运算电路积分运算电路微分运算电路基本微分运算电路逆函数型微分运算电路对数运算电路采用二极管的对数运算电路利用晶体管的…

配置中心微服务(Spring Cloud Config)

为什么需要配置服务中心&#xff1f; 1、统一维护2、配置内容安全与权限微服务之config server 注册到注册中心启动类加注解&#xff1a;EnableConfigServerSpringCloudApplicationEnableDiscoveryClient/{name}-{profiles}.yml <>name 微服务名称 profiles…

【electron】 打包应用修改图标和进程名字

文章目录导读开发环境打包流程制作一个大于等于256*256的icon修改package.json执行 *npm run build* 生成应用效果图踩坑icon必现大于等于 256*256图片有损icon图标要包含各种分辨率的resources\app.asar占用参考资料导读 以下内容在https://gitee.com/zkyt/electron-vue-eleme…

CobaltStrike木马免杀代码篇之python反序列化分离免杀(一)

前言 本篇文章主要用到python来对CobaltStrike生成的Shellcode进行分离免杀处理, 因此要求读者要有一定的python基础, 下面我会介绍pyhon反序列化免杀所需用到的相关函数和库 exec函数 exec函数是python的内置函数, 其功能与eval()函数相同, 但不同的是exec函数支持多行pyth…

Metabase学习教程:提问-3

时间序列比较 如何使用自定义表达式进行同比或逐月比较。 一个强大但也许不明显的东西自定义表达式让我们做的就是创造时间序列比较。例如&#xff0c;如果我们想比较2019年和2018年的每月收入或每天的用户数&#xff0c;我们可以使用Sumif和Countif 聚合功能。 第一步&…

(HAL库)实验1 点亮一个LED

1、实验准备 实验目标&#xff1a;点亮LED 器材&#xff1a;海创stm32开发板和数据线 2、CubeMX初始化 2.1 新建工程 打开STM32CubeMX软件&#xff08;V6.6.1&#xff09;&#xff0c;点击左上角"File"&#xff0c;再点击“New Project”。 在出现的左上角搜索框…

【C语言】初识指针(二)

你可以改变你的行为&#xff0c;但改变不了你想要什么——《浴血黑帮》 目录 1、指针类型 1.1指针加减(、-)整数 1.2指针的解引用 2、野指针 2.1什么叫野指针 2.1.1指针未初始化 2.1.2指针越界访问 2.1.3指针指向的空间被释放了 2.2如何避免野指针 前言&#xff1a; 大…

150. 以前编写好能够正常运行的 SAP UI5 代码,几个月后忽然不能运行了该怎么办?

以笔者本套教材为例,每一步骤的源代码都托管在本人 Github 仓库里,每次上传之前,都确保本地测试通过。 但笔者编写过程中发现,之前测试通过的代码,可能几个月之后再执行,就会遇到白屏现象,即应用无法正常加载,或者无法在调试模式下正常加载。 举个具体的例子。本文写…

渗透测试——找寻绝对路径的方法总结

作者名&#xff1a;Demo不是emo 主页面链接&#xff1a;主页传送门创作初心&#xff1a;舞台再大&#xff0c;你不上台&#xff0c;永远是观众&#xff0c;没人会关心你努不努力&#xff0c;摔的痛不痛&#xff0c;他们只会看你最后站在什么位置&#xff0c;然后羡慕或鄙夷座右…

Nignx部署前端页面

1.在Linux找到nginx的配置 2.使用vim命令打开nginx.conf vim nginx.conf 3.找到server块 将server_name改为服务器ip地址 4.按照原有的location块新建一个同样的location块 如果有多个就新建多个 5.将要部署的前端页面上传到自己知道的linux中的位置 我这里是存放在 data/w…