Posts Nginx 过滤模块
Post
Cancel

Nginx 过滤模块

1. 概览

1. nginx 模块分类


// 25.8.14 补充

《深入理解nginx》书中, Nginx源码中明确标记为NGX_CORE_MODULE类型的模块只有以下6个,它们是Nginx运行的最底层基础:

Nginx的核心模块采用分层架构设计,每个核心模块不仅是功能实现者,更是模块类型的管理者。ngx_events_module、ngx_http_module和ngx_mail_module这三个核心模块分别定义了NGX_EVENT_MODULE、NGX_HTTP_MODULE和NGX_MAIL_MODULE三种模块类型,并全权管理对应类型的模块。这种设计赋予核心模块双重角色:既作为基础功能的提供者,又作为模块体系的架构师,通过类型定义实现功能解耦,使得事件处理、HTTP服务和邮件代理等不同领域的模块能够独立演化,同时保持整体架构的统一性。

模块名称源码文件核心作用
ngx_core_modulengx_core.c解析main配置块(worker_processes/pid等)
ngx_errlog_modulengx_errlog.c错误日志系统初始化
ngx_events_modulengx_event.c事件驱动框架抽象层
ngx_openssl_modulengx_event_openssl.cOpenSSL加密库集成
ngx_http_modulengx_http.cHTTP协议框架
ngx_mail_modulengx_mail.c邮件协议框架

大类模块作用配置块示例模块
Core 模块Nginx 基础功能(进程、事件、日志等)全局或 events {}ngx_core_module
ngx_events_module
HTTP 模块处理 HTTP/HTTPS 请求(Web 服务)http {}ngx_http_core_module
Mail 模块代理邮件协议(SMTP/IMAP/POP3)mail {}ngx_mail_core_module
Stream 模块处理 TCP/UDP 流量(四层代理)stream {}ngx_stream_core_module

2. http 模块分类

HTTP 模块是 Nginx 中处理 HTTP/HTTPS 请求的核心模块,负责请求解析、路由、过滤、反向代理等。以下是 HTTP 模块的详细分类和核心机制:

2.1 按照4 层架构分类

举例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
http {
    # 1. 核心结构
    server {
        listen 80;
        
        # 2. 请求处理(三选一)
        location /static {
            root /data;  # 静态资源
        }
        location /api {
            proxy_pass http://backend;  # 反向代理
        }
        
        # 3. 响应加工(可叠加)
        gzip on;                   # 压缩
        sub_filter 'old' 'new';    # 内容替换
        
        # 4. 辅助控制
        deny 192.168.1.1;         # 访问控制
        access_log /var/log/nginx/access.log;  # 日志
    }
    
    # 负载均衡定义
    upstream backend {
        server 10.0.0.1:8080;
    }
}
1. 核心结构模块
  • 作用:定义 HTTP 基础框架,不可禁用。
  • 模块ngx_http_core_module
  • 关键指令
    1
    2
    
    server { listen 80; }
    location / { ... }
    
2. 请求处理模块
  • 作用:直接生成响应或转发请求(必须选择一种,互斥)。
  • ​​子类与示例
类型功能模块指令
静态资源返回文件/目录ngx_http_static_moduleroot, try_files
反向代理转发请求到后端ngx_http_proxy_moduleproxy_pass
FastCGI处理 PHP/Python 等动态请求ngx_http_fastcgi_modulefastcgi_pass
重定向返回 3xx 跳转ngx_http_rewrite_modulereturn 302, rewrite
3. 响应加工模块
  • 作用:对已有响应进行修改(可叠加使用)。
  • ​​子类与示例
类型功能模块指令
压缩Gzip/Brotli 压缩ngx_http_gzip_modulegzip on
内容替换修改响应文本ngx_http_sub_filter_modulesub_filter
缓存控制设置缓存头ngx_http_headers_moduleexpires, add_header
4. 辅助控制模块
  • 作用:安全、日志、负载均衡等。
  • ​​子类与示例
类型功能模块指令
访问控制IP/密码限制ngx_http_access_moduleallow, deny
负载均衡多后端服务器分发ngx_http_upstream_moduleupstream
日志记录记录请求信息ngx_http_log_moduleaccess_log
5. 总结

​1. ​层级关系​​: 核心结构 → 选择处理方式 → 加工响应 → 附加控制。 ​​2. 互斥性​​: 每个请求只能由 ​​一个请求处理模块​​ 响应(如不能同时用 proxy_pass 和 static)。

  1. ​​叠加性​​: ​​响应加工模块​​ 可组合使用(如 Gzip + 内容替换)。

2.2 按照是否为过滤模块分类

HTTP 模块可进一步细分为非过滤模块和过滤模块,具体如下:

模块类型功能典型模块示例
非过滤模块处理请求逻辑(路由、代理等)ngx_http_core_modulengx_http_proxy_module
Header Filters修改 HTTP 响应头ngx_http_headers_filter_module
Body Filters修改 HTTP 响应体ngx_http_gzip_filter_module

2.3 总结

过滤模块的执行确实 ​​不依赖阶段(Phase)​​,而是通过 ​​全局过滤链​​ 动态执行。要准确判断一个自定义模块是否为过滤模块,需要从 ​​代码实现机制​​ 和 ​​运行时行为​​ 两个维度验证。

1. 过滤模块的绝对判定标准(代码级)
(1) 必须插入全局过滤链
1
2
3
4
5
6
// 在模块初始化函数中
ngx_http_next_header_filter = ngx_http_top_header_filter;
ngx_http_top_header_filter = my_header_filter;  // 插入Header过滤链

ngx_http_next_body_filter = ngx_http_top_body_filter;
ngx_http_top_body_filter = my_body_filter;      // 插入Body过滤链

如果没有这段代码,100%不是过滤模块

(2) 必须实现过滤函数签名
1
2
3
4
5
// Header过滤函数
static ngx_int_t my_header_filter(ngx_http_request_t *r);

// Body过滤函数
static ngx_int_t my_body_filter(ngx_http_request_t *r, ngx_chain_t *in);
(3) 必须调用下一跳过滤函数
1
2
3
4
static ngx_int_t my_body_filter(ngx_http_request_t *r, ngx_chain_t *in) {
    // 处理逻辑...
    return ngx_http_next_body_filter(r, in);  // 关键!
}
2. 过滤模块的运行时特征
(1) 依赖前置响应
1
2
3
4
location / {
    my_filter "text";        # 过滤模块
    proxy_pass http://backend; # 需要前置生成器
}
(2) 可叠加执行
1
2
3
4
5
location / {
    proxy_pass http://backend;
    my_filter "A";     # 过滤模块A
    other_filter "B";   # 过滤模块B → 可叠加
}
3. 快速验证方案
步骤1:检查模块初始化函数
1
2
3
// 过滤模块必有:
// Nginx的全局header过滤链入口指针,所有header过滤模块通过此链串联。
ngx_http_top_header_filter = my_header_filter;
步骤2:测试配置依赖
1
2
3
location / {
    my_custom_module on;  # 单独使用
}
  • 报错要求前置响应 → 过滤模块
  • 能独立工作 → 非过滤模块
总结对比表

| 特征 | 过滤模块 | 非过滤模块 | |——————–|—————————-|————————–| | 注册方式 | 插入ngx_http_top_*_filter | 注册ngx_http_handler_pt | | 执行时机 | 所有响应生成后 | 特定阶段 | | 返回值 | 必须调用next_filter | 直接返回状态码 | | 配置依赖 | 需要前置响应 | 可独立运行 |

2. HTTP 请求处理的 11 个阶段

Nginx 将 HTTP 请求处理分为 11 个阶段,非过滤模块可以挂载到不同阶段执行逻辑:

阶段说明典型模块
NGX_HTTP_POST_READ_PHASE读取请求头后立即执行ngx_http_realip_module(修改客户端IP)
NGX_HTTP_SERVER_REWRITE_PHASEServer 块内的 URL 重写ngx_http_rewrite_module
NGX_HTTP_FIND_CONFIG_PHASE查找匹配的 Location 配置(Nginx 内部逻辑,无模块直接介入)
NGX_HTTP_REWRITE_PHASELocation 块内的 URL 重写ngx_http_rewrite_module
NGX_HTTP_POST_REWRITE_PHASE重写后的后处理(通常无模块介入)
NGX_HTTP_PREACCESS_PHASE访问控制前的预处理ngx_http_limit_req_module(限流)
NGX_HTTP_ACCESS_PHASE访问权限控制(如 IP 黑白名单)ngx_http_access_module
NGX_HTTP_POST_ACCESS_PHASE访问控制后的处理(通常无模块介入)
NGX_HTTP_PRECONTENT_PHASE生成内容前的预处理ngx_http_try_files_module(检查文件是否存在)
NGX_HTTP_CONTENT_PHASE生成响应内容(核心阶段)ngx_http_proxy_module(反向代理)
NGX_HTTP_LOG_PHASE请求日志记录ngx_http_log_module

3. 过滤模块的执行顺序

过滤模块(Header/Body Filters)不依赖阶段,而是通过过滤链执行:

过滤模块类型执行顺序典型模块
Header Filtersngx_modules[] 顺序执行ngx_http_headers_filter_module
Body Filters反向 ngx_modules[] 顺序执行ngx_http_gzip_filter_module

为什么 Body Filters 要反向执行?

假设有两个 Body Filters:

  • 分块模块(A):将响应体分块传输。
  • 压缩模块(B):压缩响应体。

逻辑需求: 应该先压缩整个响应体,再分块传输(即 B → A)。 但 Nginx 的模块是按 A → B 顺序编译的,因此通过反向执行实现 B → A 的实际效果。

4. HTTP 模块开发关键点

(1) 非过滤模块如何挂载到阶段?

在模块的 postconfiguration 回调中注册阶段处理器:

1
2
3
4
5
static ngx_int_t my_postconfiguration(ngx_conf_t *cf) {
    ngx_http_handler_pt *h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
    *h = my_content_handler;  // 模块的处理函数
    return NGX_OK;
}

(2) 过滤模块如何注册?

通过 ngx_http_next_header_filter 或 ngx_http_next_body_filter 插入过滤链

1
2
3
4
5
6
7
8
9
static ngx_int_t my_header_filter(ngx_http_request_t *r) {
    // 修改响应头
    return ngx_http_next_header_filter(r);
}

static ngx_int_t my_body_filter(ngx_http_request_t *r, ngx_chain_t *in) {
    // 修改响应体
    return ngx_http_next_body_filter(r, in);
}

5. 总结

  • HTTP 模块的核心机制 ​- 非过滤模块​​:按 11 个阶段顺序执行(如代理、重定向)。 ​​- Header Filters​​:按模块编译顺序处理响应头。 ​​- Body Filters​​:反向模块编译顺序处理响应体(确保逻辑正确性)。

1. Nginx HTTP 模块详解

Nginx 的 HTTP 模块是处理 HTTP 请求和响应的核心组件。根据其职责和用途,HTTP 模块可以分为多种类型。以下是常见的 HTTP 模块类型及其作用。

1
2
3
4
5
6
7
ngx_module_t ngx_http_xxx_module = {  // 模块变量名带 `http_` 前缀
    NGX_MODULE_V1,
    &ngx_http_xxx_module_ctx,          // 上下文
    ngx_http_xxx_commands,             // 指令集
    NGX_HTTP_MODULE,                   // 关键标记:必须是这个类型
    ...
};
大分类分类说明模块名称作用阶段功能说明典型指令
核心基础模块Nginx 必备基础功能,提供 HTTP 服务核心配置ngx_http_core_module多阶段HTTP 基础配置(server/location 块、请求解析、变量处理)server, listen, root
Handler 模块生成响应内容(直接处理请求并返回数据,如静态文件、代理结果、动态程序输出)ngx_http_static_moduleNGX_HTTP_CONTENT_PHASE处理静态文件请求(如 HTML/图片)root, alias
  ngx_http_proxy_moduleNGX_HTTP_CONTENT_PHASE反向代理(转发请求到后端服务器)proxy_pass
  ngx_http_fastcgi_moduleNGX_HTTP_CONTENT_PHASE处理 FastCGI 请求(如 PHP-FPM)fastcgi_pass
Filter 模块修改响应内容(对 Handler 生成的响应进行加工,如压缩、修改 Header、日志记录)ngx_http_headers_moduleNGX_HTTP_HEADER_FILTER_PHASE修改响应头(如 Cache-ControlCORSadd_header
  ngx_http_gzip_filter_moduleNGX_HTTP_BODY_FILTER_PHASEGzip 压缩响应内容(减少传输体积)gzip on
  ngx_http_log_moduleNGX_HTTP_LOG_PHASE记录访问日志(时间、IP、状态码)access_log
Upstream 模块管理与后端服务器的交互(负载均衡、健康检查)ngx_http_upstream_moduleNGX_HTTP_UPSTREAM_PHASE定义后端服务器组(负载均衡、健康检查)upstream, server
访问控制模块实现请求的访问控制(如 IP 黑白名单、鉴权)ngx_http_access_moduleNGX_HTTP_ACCESS_PHASE基于 IP 的访问控制(黑白名单)allow, deny
SSL/TLS 模块提供 HTTPS 加密支持ngx_http_ssl_moduleNGX_HTTP_SSL_PHASE配置 HTTPS 证书和加密协议ssl_certificate
URL 重写模块修改请求的 URL 路径(重定向、正则匹配)ngx_http_rewrite_moduleNGX_HTTP_REWRITE_PHASEURL 重定向和重写(正则匹配)rewrite, return
第三方模块扩展 Nginx 功能(需手动编译或动态加载)ngx_http_lua_module多阶段嵌入 Lua 脚本(动态逻辑、鉴权)content_by_lua

2. 过滤模块意义 ———— http 框架的11 个阶段

阶段(Phase)是否允许模块接入说明
1. POST_READ✅ 是读取请求头后立即执行,可用于全局变量初始化(如 ngx_http_realip_module)。
2. SERVER_REWRITE✅ 是server 块内执行 URL 重写(如 ngx_http_rewrite_module)。
3. FIND_CONFIG❌ 否Nginx 核心阶段,决定使用哪个 location 块,模块无法干预。
4. REWRITE✅ 是location 块内执行 URL 重写(如 ngx_http_rewrite_module)。
5. POST_REWRITE❌ 否检查 REWRITE 阶段是否导致 location 变更,模块无法干预。
6. PREACCESS✅ 是访问控制前执行(如限流 ngx_http_limit_req_module)。
7. ACCESS✅ 是访问权限检查(如 ngx_http_access_modulengx_http_auth_basic_module)。
8. POST_ACCESS❌ 否检查 ACCESS 阶段结果,模块无法干预。
9. PRECONTENT✅ 是生成内容前的最后处理(如 ngx_http_try_files_module)。
10. CONTENT✅ 是生成响应内容(如 ngx_http_proxy_modulengx_http_static_module)。
11. LOG✅ 是记录访问日志(如 ngx_http_log_module)。

3. Nginx 中的所有模块 ? 启动顺序等

概览

  1. ngx_modules 中保存着所有的ngxin 编译模块
  2. Nginx 启动时,会按 ngx_modules[] 数组顺序依次调用每个模块的 init_module 钩子(如果定义了)。​越靠前的模块越早初始化​​(例如核心模块 ngx_http_core_module 通常在最前面)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
~/nginx-1.0.14/objs/ngx_modules.c

ngx_module_t *ngx_modules[] = {
    &ngx_core_module,
    &ngx_errlog_module,
    &ngx_conf_module,
    &ngx_events_module,
    &ngx_event_core_module,
    &ngx_epoll_module,
    &ngx_http_module,
    &ngx_http_core_module,
    &ngx_http_log_module,
    &ngx_http_upstream_module,
    &ngx_http_static_module,
    &ngx_http_autoindex_module,
    &ngx_http_index_module,
    &ngx_http_auth_basic_module,
    &ngx_http_access_module,
    &ngx_http_limit_zone_module,
    &ngx_http_limit_req_module,
    &ngx_http_geo_module,
    &ngx_http_map_module,
    &ngx_http_split_clients_module,
    &ngx_http_referer_module,
    &ngx_http_rewrite_module,
    &ngx_http_proxy_module,
    &ngx_http_fastcgi_module,
    &ngx_http_uwsgi_module,
    &ngx_http_scgi_module,
    &ngx_http_memcached_module,
    &ngx_http_empty_gif_module,
    &ngx_http_browser_module,
    &ngx_http_upstream_ip_hash_module,
    &ngx_http_mytest_module,
    &ngx_http_write_filter_module,
    &ngx_http_header_filter_module,
    &ngx_http_chunked_filter_module,
    &ngx_http_range_header_filter_module,
    &ngx_http_gzip_filter_module,
    &ngx_http_postpone_filter_module,
    &ngx_http_ssi_filter_module,
    &ngx_http_charset_filter_module,
    &ngx_http_userid_filter_module,
    &ngx_http_headers_filter_module,
    &ngx_http_copy_filter_module,
    &ngx_http_range_body_filter_module,
    &ngx_http_not_modified_filter_module,
    NULL
};

3.1顺序说明

所有模块(核心/第三方)均按编译顺序存入 ngx_module_t *ngx_modules[] 数组,其顺序严格决定以下行为

1
2
3
4
5
6
7
8
// 示例数组结构(实际由configure生成)
ngx_module_t *ngx_modules[] = {
    &ngx_core_module,            // 核心模块1(最先加载)
    &ngx_http_core_module,       // HTTP核心模块
    &ngx_http_rewrite_module,    // 标准HTTP模块
    &my_custom_module,           // 第三方模块
    NULL                         // 数组结束
};

1. 第一定律:全局初始化顺序​

1
2
3
4
5
6
// nginx.c 中的初始化逻辑
for (i = 0; ngx_modules[i]; i++) {
    if (ngx_modules[i]->init_module) {
        ngx_modules[i]->init_module(cycle); // 绝对按数组顺序执行
    }
}
  • 做什么​​:内存池/全局变量等基础初始化

  • 顺序不可变​​:ngx_core_module 必须第一个初始化

2. 第二定律:HTTP模块配置初始化​

1
2
3
4
5
6
7
8
// ngx_http_block() 中的HTTP模块初始化
for (i = 0; ngx_modules[i]; i++) {
    if (ngx_modules[i]->type != NGX_HTTP_MODULE) continue;
    
    hmod = ngx_modules[i]->ctx;
    if (hmod->preconfiguration) hmod->preconfiguration(cf);  // 阶段1
    if (hmod->postconfiguration) hmod->postconfiguration(cf); // 阶段2
}
  • 顺序规则​​:
  • 仍按 ngx_modules[] 顺序调用
  • 但 preconfiguration 和 postconfiguration 是分开的两个阶段

3. 第三定律:请求处理顺序​

不是所有 HTTP 模块都会完整参与 Nginx 的 ​​11 个请求处理阶段​​。模块是否介入这些阶段,完全取决于它的 ​​功能定位​​ 和 ​​开发者的设计意图​​。

模块类型顺序规则示例模块
非过滤模块按11个阶段顺序执行ngx_http_proxy_module
Header Filtersngx_modules[] 顺序相同ngx_http_headers_filter_module
Body Filtersngx_modules[] 顺序相反ngx_http_gzip_filter_module

3.2再谈配置解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static ngx_http_module_t  ngx_http_mytest_module_ctx =
        {
                NULL,                              /* preconfiguration */
                NULL,                  	          /* postconfiguration */

                NULL,                              /* create main configuration */
                NULL,                              /* init main configuration */

                NULL,                              /* create server configuration */
                NULL,                              /* merge server configuration */

                NULL,       			/* create location configuration */
                NULL         			/* merge location configuration */
        };

调用每个模块的 create xxx configuration && preconfiguration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 当解析到http{}块时执行的伪代码(ngx_http_block函数简化版)
void ngx_http_block(ngx_conf_t *cf) {
    // 阶段1:调用所有HTTP模块的preconfiguration
    for (i = 0; ngx_modules[i]; i++) {
        if (ngx_modules[i]->type != NGX_HTTP_MODULE) continue;
        hmod = ngx_modules[i]->ctx;
        if (hmod->preconfiguration) {
            hmod->preconfiguration(cf);  // 此时尚未创建任何配置结构
        }
    }

    // 阶段2:创建配置结构(MAIN/SRV/LOC)
    for (i = 0; ngx_modules[i]; i++) {
        if (ngx_modules[i]->type != NGX_HTTP_MODULE) continue;
        hmod = ngx_modules[i]->ctx;
        
        if (hmod->create_main_conf) {
            ctx->main_conf[mi] = hmod->create_main_conf(cf);
        }
        if (hmod->create_srv_conf) {
            ctx->srv_conf[mi] = hmod->create_srv_conf(cf);
        }
        if (hmod->create_loc_conf) {
            ctx->loc_conf[mi] = hmod->create_loc_conf(cf);
        }
    }

    // 阶段3:解析server{}和location{}配置
    parse_servers_and_locations(cf);

    // 阶段4:调用所有HTTP模块的postconfiguration
    for (i = 0; ngx_modules[i]; i++) {
        if (ngx_modules[i]->type != NGX_HTTP_MODULE) continue;
        hmod = ngx_modules[i]->ctx;
        if (hmod->postconfiguration) {
            hmod->postconfiguration(cf);  // 此时所有配置已就绪
        }
    }
}

preconfiguration & postconfiguration | 特性 | preconfiguration | postconfiguration | |———————|——————————————|——————————————| | 调用阶段 | 刚进入http{}块,尚未解析任何server/location | 已完成http{}内所有server/location的解析 | | 典型用途 | 早期全局初始化 | 注册处理程序/过滤器 | | 执行顺序 | 按ngx_modules[]数组顺序 | 按ngx_modules[]数组顺序 | | 可否访问配置 | ❌ 配置未生成 | ✅ 可读取main/srv/loc配置 | | 官方模块使用案例 | ngx_http_realip_module | ngx_http_gzip_filter_module |

ngxin中的过滤模块顺序

模块名称功能说明
ngx_http_not_modified_filter_module仅对 HTTP 头部做处理。在返回 200 成功时,根据请求中的 If-Modified-SinceIf-Unmodified-Since 头部获取浏览器缓存文件的最后修改时间,决定是否直接发送 304 Not Modified 响应给用户。
ngx_http_range_header_filter_module处理请求中的 Range 信息,根据 Range 中的要求返回文件的一部分给用户。
ngx_http_copy_filter_module仅对 HTTP 包体做处理。将用户发送的 ngx_chain_t 结构的 HTTP 包体复制到新的 ngx_chain_t 结构中(仅复制指针,不包括实际 HTTP 响应内容),后续的 HTTP 过滤模块处理的 ngx_chain_t 类型的成员都是该模块处理后的变量。
ngx_http_headers_filter_module仅对 HTTP 头部做处理。允许通过配置文件在响应中添加任意的 HTTP 头部。
ngx_http_userid_filter_module基于 Cookie 提供简单的认证管理功能。
ngx_http_charset_filter_module仅对 HTTP 包体做处理。将响应包按照配置文件中的配置重新编码后返回给用户。
ngx_http_ssi_filter_module仅对 HTTP 包体做处理。支持 SSI(Server Side Include),将文件内容嵌入到网页中并返回给用户。
ngx_http_postpone_filter_module处理子请求的响应,确保多个子请求的响应按顺序发送。
ngx_http_gzip_filter_module仅对 HTTP 包体做处理。对特定的 HTTP 响应包体(如网页或文本文件)进行 Gzip 压缩后返回给用户。
ngx_http_range_filter_module支持 Range 协议。
ngx_http_chunked_filter_module支持 chunked 编码。
ngx_http_header_filter_module将响应头部序列化为字符流,并通过 ngx_http_write_filter_module 发送到客户端。
ngx_http_write_filter_module负责向客户端发送 HTTP 响应包体。

第三方过滤模块执行顺序

p4

3. Http 模块 和 Http Filter 模块区别?

1
2
// config
HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES ngx_http_myfilter_module"

还有的一些http 模块:

  • ngx_http_ssl_module
  • ngx_http_upstream_module

4. Nginx filter 模块示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>


typedef struct
{
    ngx_flag_t		enable;
} ngx_http_myfilter_conf_t;

typedef struct
{
    ngx_int_t   	add_prefix;
} ngx_http_myfilter_ctx_t;


static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
static ngx_http_output_body_filter_pt    ngx_http_next_body_filter;

//将在包体中添加这个前缀
static ngx_str_t filter_prefix = ngx_string("[my filter prefix]");



static void* ngx_http_myfilter_create_conf(ngx_conf_t *cf);
static char *
ngx_http_myfilter_merge_conf(ngx_conf_t *cf, void *parent, void *child);

static ngx_int_t ngx_http_myfilter_init(ngx_conf_t *cf);
static ngx_int_t
ngx_http_myfilter_header_filter(ngx_http_request_t *r);
static ngx_int_t
ngx_http_myfilter_body_filter(ngx_http_request_t *r, ngx_chain_t *in);




static ngx_command_t  ngx_http_myfilter_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_myfilter_conf_t, enable),
                        NULL
                },

                ngx_null_command
        };


static ngx_http_module_t  ngx_http_myfilter_module_ctx =
        {
                NULL,                                  /* preconfiguration方法  */
                ngx_http_myfilter_init,            /* postconfiguration方法 */

                NULL,                                  /*create_main_conf 方法 */
                NULL,                                  /* init_main_conf方法 */

                NULL,                                  /* create_srv_conf方法 */
                NULL,                                  /* merge_srv_conf方法 */

                ngx_http_myfilter_create_conf,    /* create_loc_conf方法 */
                ngx_http_myfilter_merge_conf      /*merge_loc_conf方法*/
        };


ngx_module_t  ngx_http_myfilter_module =
        {
                NGX_MODULE_V1,
                &ngx_http_myfilter_module_ctx,     /* module context */
                ngx_http_myfilter_commands,        /* module directives */
                NGX_HTTP_MODULE,                       /* module type */
                NULL,                                  /* init master */
                NULL,                                  /* init module */
                NULL,                                  /* init process */
                NULL,                                  /* init thread */
                NULL,                                  /* exit thread */
                NULL,                                  /* exit process */
                NULL,                                  /* exit master */
                NGX_MODULE_V1_PADDING
        };



static ngx_int_t ngx_http_myfilter_init(ngx_conf_t *cf)
{
    //插入到头部处理方法链表的首部
    ngx_http_next_header_filter = ngx_http_top_header_filter;
    ngx_http_top_header_filter = ngx_http_myfilter_header_filter;

    //插入到包体处理方法链表的首部
    ngx_http_next_body_filter = ngx_http_top_body_filter;
    ngx_http_top_body_filter = ngx_http_myfilter_body_filter;

    return NGX_OK;
}

static ngx_int_t
ngx_http_myfilter_header_filter(ngx_http_request_t *r)
{
    ngx_http_myfilter_ctx_t   *ctx;
    ngx_http_myfilter_conf_t  *conf;

    //如果不是返回成功,这时是不需要理会是否加前缀的,直接交由下一个过滤模块
    //处理响应码非200的情形
    if (r->headers_out.status != NGX_HTTP_OK)
    {
        return ngx_http_next_header_filter(r);
    }

    //获取http上下文
    ctx = ngx_http_get_module_ctx(r, ngx_http_myfilter_module);
    if (ctx)
    {
        //该请求的上下文已经存在,这说明
        // ngx_http_myfilter_header_filter已经被调用过1次,
        //直接交由下一个过滤模块处理
        return ngx_http_next_header_filter(r);
    }

    //获取存储配置项的ngx_http_myfilter_conf_t结构体
    conf = ngx_http_get_module_loc_conf(r, ngx_http_myfilter_module);

    //如果enable成员为0,也就是配置文件中没有配置add_prefix配置项,
    //或者add_prefix配置项的参数值是off,这时直接交由下一个过滤模块处理
    if (conf->enable == 0)
    {
        return ngx_http_next_header_filter(r);
    }

    //构造http上下文结构体ngx_http_myfilter_ctx_t
    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_myfilter_ctx_t));
    if (ctx == NULL)
    {
        return NGX_ERROR;
    }

    //add_prefix为0表示不加前缀
    ctx->add_prefix = 0;

    //将构造的上下文设置到当前请求中
    ngx_http_set_ctx(r, ctx, ngx_http_myfilter_module);

    //myfilter过滤模块只处理Content-Type是"text/plain"类型的http响应
    if (r->headers_out.content_type.len >= sizeof("text/plain") - 1
        && ngx_strncasecmp(r->headers_out.content_type.data, (u_char *) "text/plain", sizeof("text/plain") - 1) == 0)
    {
        //1表示需要在http包体前加入前缀
        ctx->add_prefix = 1;

        //如果处理模块已经在Content-Length写入了http包体的长度,由于
        //我们加入了前缀字符串,所以需要把这个字符串的长度也加入到
        //Content-Length中
        if (r->headers_out.content_length_n > 0)
            r->headers_out.content_length_n += filter_prefix.len;
    }

    //交由下一个过滤模块继续处理
    return ngx_http_next_header_filter(r);
}


static ngx_int_t
ngx_http_myfilter_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
    ngx_http_myfilter_ctx_t   *ctx;
    ctx = ngx_http_get_module_ctx(r, ngx_http_myfilter_module);
    //如果获取不到上下文,或者上下文结构体中的add_prefix为0或者2时,
    //都不会添加前缀,这时直接交给下一个http过滤模块处理
    if (ctx == NULL || ctx->add_prefix != 1)
    {
        return ngx_http_next_body_filter(r, in);
    }

    //将add_prefix设置为2,这样即使ngx_http_myfilter_body_filter
    //再次回调时,也不会重复添加前缀
    ctx->add_prefix = 2;

    //从请求的内存池中分配内存,用于存储字符串前缀
    ngx_buf_t* b = ngx_create_temp_buf(r->pool, filter_prefix.len);
    //将ngx_buf_t中的指针正确地指向filter_prefix字符串
    b->start = b->pos = filter_prefix.data;
    b->last = b->pos + filter_prefix.len;

    //从请求的内存池中生成ngx_chain_t链表,将刚分配的ngx_buf_t设置到
    //其buf成员中,并将它添加到原先待发送的http包体前面
    ngx_chain_t *cl = ngx_alloc_chain_link(r->pool);
    cl->buf = b;
    cl->next = in;

    //调用下一个模块的http包体处理方法,注意这时传入的是新生成的cl链表
    return ngx_http_next_body_filter(r, cl);
}


static void* ngx_http_myfilter_create_conf(ngx_conf_t *cf)
{
    ngx_http_myfilter_conf_t  *mycf;

    //创建存储配置项的结构体
    mycf = (ngx_http_myfilter_conf_t  *)ngx_pcalloc(cf->pool, sizeof(ngx_http_myfilter_conf_t));
    if (mycf == NULL)
    {
        return NULL;
    }

    //ngx_flat_t类型的变量,如果使用预设函数ngx_conf_set_flag_slot
    //解析配置项参数,必须初始化为NGX_CONF_UNSET
    mycf->enable = NGX_CONF_UNSET;

    return mycf;
}

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

//合并ngx_flat_t类型的配置项enable
    ngx_conf_merge_value(conf->enable, prev->enable, 0);

    return NGX_CONF_OK;
}
This post is licensed under CC BY 4.0 by the author.

Contents

Trending Tags