《深入理解Nginx》阅读与实践(四):简单的HTTP过滤模块

简介:

一、Nginx的HTTP过滤模块特征

  一个请求可以被任意个HTTP模块处理

  在普通HTTP模块处理请求完毕并调用ngx_http_send_header()发送HTTP头部或调用ngx_http_output_filter()发送HTTP包体时,才会由这两个方法一次调用所有的HTTP过滤模块来处理这个请求。HTTP过滤模块仅处理服务器发送到客户端的响应,而不处理客户端发往服务器的HTTP请求。

  多个过滤模块的顺序的形成以及Nginx自带的过滤模块请参考原书。

 

二、编写一个HTTP过滤模块

   以向返回给用户的文本格式响应包体前加一段字符串"[my filter prefix]"为例,展示如何编写一个HTTP过滤模块。源代码来自于《深入理解Nginx》。

1.config文件的编写

  与前几篇博文的HTTP模块不同,HTTP过滤模块需要HTTP_FILTER_MODULES一项以把所有过滤模块一同编译,因此config写作:

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

  进行configure时,--add-module=PATH是一样的。

 

2.编写模块基本内容:模块定义、配置项处理

  由于需要在nginx.conf中加入一项flag类型的add_fix来控制这个过滤模块的使用与否,与这个配置项处理相关的ngx_http_myfilter_create_conf()、ngx_http_myfilter_merge_conf()、ngx_http_mytest_commands[]需要对应地进行处理。

复制代码
typedef struct {
    ngx_flag_t enable;
} ngx_http_myfilter_conf_t;

typedef struct {
    ngx_int_t add_prefix;
} ngx_http_myfilter_ctx_t;
复制代码

 

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;
    }
    mycf->enable = NGX_CONF_UNSET;
    return mycf;
}
ngx_http_myfilter_create_conf()
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_conf_merge_value(conf->enable,prev->enable,0);
    return NGX_CONF_OK;
}
ngx_http_myfilter_merge_conf
static ngx_command_t ngx_http_mytest_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
};
ngx_http_mytest_commands[]

  这样之后才是模块的上下文和模块定义:

static ngx_http_module_t ngx_http_myfilter_module_ctx = {
    NULL,
    ngx_http_myfilter_init,
    NULL,
    NULL,

    NULL,
    NULL,
    ngx_http_myfilter_create_conf,
    ngx_http_myfilter_merge_conf
};
ngx_http_myfilter_module_ctx
ngx_module_t ngx_http_myfilter_module = {
    NGX_MODULE_V1,
    &ngx_http_myfilter_module_ctx,
    ngx_http_mytest_commands,
    NGX_HTTP_MODULE,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NGX_MODULE_V1_PADDING
};
ngx_http_myfilter_module

  从模块上下文可以看出,过滤功能在模块完成配置项处理后开始,其初始化方法为ngx_myfilter_init()。

 

3.过滤功能实现

  初始化方法ngx_myfilter_init()的功能仅仅是把当前过滤模块插入Nginx所有过滤模块的链表中。

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_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;
}
ngx_int_t ngx_http_myfilter_init()

 

  头部处理方法是为了确定返回的类型是否为text/plain。如果是,则包体处理方法需要添加前缀。这里把前缀硬编码至模块源码中。

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;

    if(r->headers_out.status != NGX_HTTP_OK)
    {
        return ngx_http_next_header_filter(r);
    }

    ctx = ngx_http_get_module_ctx(r,ngx_http_myfilter_module);
    if(ctx) {
        return ngx_http_next_header_filter(r);
    }
    conf = ngx_http_get_module_loc_conf(r,ngx_http_myfilter_module);
    if(conf->enable == 0)
    {
        return ngx_http_next_header_filter(r);
    }
    ctx = ngx_pcalloc(r->pool,sizeof(ngx_http_myfilter_ctx_t));
    if(ctx == NULL)
    {
        return NGX_ERROR;
    }
    ctx->add_prefix = 0;

    ngx_http_set_ctx(r,ctx,ngx_http_myfilter_module);
    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)
    {
        ctx->add_prefix = 1;
        if(r->headers_out.content_length_n > 0) {
            r->headers_out.content_length_n += filter_prefix.len;
        }
    }
        return ngx_http_myfilter_header_filter(r);
}
ngx_http_myfilter_header_filter()

 

  包体处理方法根据头部处理方法的结果来为包体添加前缀。

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);
    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 *c1 = ngx_alloc_chain_link(r->pool);
    c1->buf = b;
    c1->next = in;
    return ngx_http_next_body_filter(r,c1);
}
ngx_http_myfilter_body_filter()

 

三、过滤模块测试

  根据原作者编写的nginx.conf

复制代码
    server {
        listen 8080;

        location / {
            root /;
            add_prefix on;
        }
    }
复制代码

可以看出,需要在/目录下(系统根目录)添加一个或多个任意内容的文本文件来进行测试。我写了一个内容为test的文本文件test.txt。

  输入curl http://localhost:8080/test.txt,可以看到返回的内容是[my filter prefix]test。

  当然,如果你放在/的不是纯文本文件,而是html文件或者其他类型文件,是不会增加这个前缀的。

  另外,把on改成off,你会发现前缀不再出现,说明过滤模块功能已经关闭。

 

  p.s.此书的后续章节是源码分析,实践环节比较少,“《深入理解Nginx》阅读与实践”系列可能到此为止。






本文转自五岳博客园博客,原文链接:www.cnblogs.com/wuyuegb2312/p/3271919.html,如需转载请自行联系原作者

目录
相关文章
|
2月前
|
缓存 应用服务中间件 网络安全
Nginx中配置HTTP2协议的方法
Nginx中配置HTTP2协议的方法
174 7
|
3月前
|
Rust 前端开发 API
Tauri 开发实践 — Tauri HTTP 请求开发
本文介绍了如何在 Tauri 中发起 HTTP 请求。首先通过安装 Tauri 生态中的工具包并配置 `tauri.conf.json` 文件来允许特定域名的 HTTP 通信。接着封装了一个简单的 HTTP 客户端类,并在页面中使用该客户端实现 GET 和 POST 请求。最后提供了完整的源码地址以供参考。此功能使得桌面应用能够与远程服务器进行交互,增强了应用的实用性。
219 1
Tauri 开发实践 — Tauri HTTP 请求开发
|
3天前
|
缓存 负载均衡 应用服务中间件
Nginx七层(应用层)反向代理:HTTP反向代理proxy_pass篇
通过使用Nginx的反向代理功能,可以有效地提高Web应用的性能、安全性和可扩展性。配置过程中需要注意不同场景下的具体需求,如负载均衡、SSL终止和缓存策略等。正确配置和优化Nginx反向代理可以显著提升系统的整体表现。
33 18
|
24天前
|
应用服务中间件 Linux 网络安全
nginx安装部署ssl证书,同时支持http与https方式访问
为了使HTTP服务支持HTTPS访问,需生成并安装SSL证书,并确保Nginx支持SSL模块。首先,在`/usr/local/nginx`目录下生成RSA密钥、证书申请文件及自签名证书。接着,确认Nginx已安装SSL模块,若未安装则重新编译Nginx加入该模块。最后,编辑`nginx.conf`配置文件,启用并配置HTTPS服务器部分,指定证书路径和监听端口(如20000),保存后重启Nginx完成部署。
301 7
|
3月前
|
缓存 JavaScript 安全
nodejs里面的http模块介绍和使用
综上所述,Node.js的http模块是构建Web服务的基础,其灵活性和强大功能,结合Node.js异步非阻塞的特点,为现代Web应用开发提供了坚实的基础。
139 62
|
3月前
|
存储 缓存 NoSQL
保持HTTP会话状态:缓存策略与实践
保持HTTP会话状态:缓存策略与实践
|
3月前
|
存储 JSON API
HTTP 请求与响应处理:C#中的实践
【10月更文挑战第4天】在现代Web开发中,HTTP协议至关重要,无论构建Web应用还是API开发,都需要熟练掌握HTTP请求与响应处理。本文从C#角度出发,介绍HTTP基础知识,包括请求与响应结构,并通过`HttpClient`库演示如何发送GET请求及处理响应,同时分析常见错误并提供解决方案,助你更高效地完成HTTP相关任务。
149 2
|
3月前
|
存储 缓存 监控
HTTP:强缓存优化实践
HTTP强缓存是提升网站性能的关键技术之一。通过精心设计缓存策略,不仅可以显著减少网络延迟,还能降低服务器负载,提升用户体验。实施上述最佳实践,结合持续的监控与调整,能够确保缓存机制高效且稳定地服务于网站性能优化目标。
65 3
|
4月前
|
负载均衡 网络协议 Unix
Nginx负载均衡与故障转移实践
Nginx通过ngx_http_upstream_module模块实现负载均衡与故障转移,适用于多服务器环境。利用`upstream`与`server`指令定义后端服务器组,通过`proxy_pass`将请求代理至这些服务器,实现请求分发。Nginx还提供了多种负载均衡策略,如轮询、权重分配、IP哈希等,并支持自定义故障转移逻辑,确保系统稳定性和高可用性。示例配置展示了如何定义负载均衡设备及状态,并应用到具体server配置中。
|
3月前
|
JSON API 开发者
深入解析Python网络编程与Web开发:urllib、requests和http模块的功能、用法及在构建现代网络应用中的关键作用
深入解析Python网络编程与Web开发:urllib、requests和http模块的功能、用法及在构建现代网络应用中的关键作用
33 0