一、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,);
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 == )
{
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 = ; ngx_http_set_ctx(r,ctx,ngx_http_myfilter_module);
if(r->headers_out.content_type.len >= sizeof("text/plain")-
&& ngx_strncasecmp(r->headers_out.content_type.data,(u_char *)"text/plain",
sizeof("text/plain")-) == )
{
ctx->add_prefix = ;
if(r->headers_out.content_length_n > ) {
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 != ) {
return ngx_http_next_body_filter(r,in);
} ctx->add_prefix = ; 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 ; location / {
root /;
add_prefix on;
}
}

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

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

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

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

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

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

  1. 简单的HTTP过滤模块

    简单的HTTP过滤模块 一.Nginx的HTTP过滤模块特征 一个请求可以被任意个HTTP模块处理: 在普通HTTP模块处理请求完毕并调用ngx_http_send_header()发送HTTP头部或 ...

  2. Nginx模块开发(5)————开发简单的HTTP过滤模块

    该模块可实现如下的功能,在浏览器输入http://你的IP/lcw.text,能够读出你在根目录下创建的lcw.txt里面的内容,并在前面加上一句字符串where there is a will,th ...

  3. Nginx:HTTP过滤模块

    参考资料<深入理解Nginx> HTTP过滤模块也是一种HTTP模块,与普通HTTP处理模块不同在于: 1.一个请求仅由一个HTTP处理模块处理,而可以被任意个HTTP过滤模块处理 2.普 ...

  4. 【Nginx】开发一个HTTP过滤模块

    与HTTP处理模块不同.HTTP过滤模块的工作是对发送给用户的HTTP响应做一些加工. server返回的一个响应能够被随意多个HTTP过滤模块以流水线的方式依次处理.HTTP响应分为头部和包体,ng ...

  5. 《深入理解Nginx》阅读与实践(三):使用upstream和subrequest访问第三方服务

    本文是对陶辉<深入理解Nginx>第5章内容的梳理以及实现,代码和注释基本出自此书. 一.upstream:以向nginx服务器的请求转化为向google服务器的搜索请求为例 (一)模块框 ...

  6. 《深入理解Nginx》阅读与实践(二):配置项的使用

    前文链接:<深入理解Nginx>阅读与实践(一):Nginx安装配置与HelloWorld HelloWorld的完成意味着已经踏入了nginx的大门,虽然很振奋人心,但在编写中仍有很多疑 ...

  7. 《深入理解Nginx》阅读与实践(一):Nginx安装配置与HelloWorld

    最近在读陶辉的<深入理解Nginx:模块开发与架构解析>,一是想跟着大牛练练阅读和编写开源代码的能力,二是想学学Nginx优秀的架构设计,三是想找一个点深入下Linux下网络编程的细节.侯 ...

  8. 【实战分享】又拍云 OpenResty / Nginx 服务优化实践

    2018 年 11 月 17 日,由 OpenResty 主办的 OpenResty Con 2018 在杭州举行.本次 OpenResty Con 的主题涉及 OpenResty 的新开源特性.业界 ...

  9. Linux及安全实践四——ELF文件格式分析

    Linux及安全实践四——ELF文件格式分析 一.ELF文件格式概述 1. ELF:是一种对象文件的格式,用于定义不同类型的对象文件中都放了什么东西.以及都以什么样的格式去放这些东西. 二.分析一个E ...

随机推荐

  1. svn Q&A

    Q1:在svn commit的时候,会出现某某文件 is missing.这是因为此次提交时:远程repository中并没有该文件,而且本地repository也没有该文件. 具体原因: 1.可能因 ...

  2. 巧用TexturePacker命令行

    游戏开发使用TexturePacker来生成图片的atlas sheet, 工具非常好用. 一般GUI的方法, 新建一个tps文件, 将要图片加载进来,调整参数和输出路径, 最后点publish. 在 ...

  3. Model1

    jsp+javabean的开发模式 此处JavaBean也可是封装的业务逻辑 流程: 浏览器端访问jsp,jsp交给Javabean处理,javabean处理后台数据,交还给Jsp

  4. CSS——几个最新解决方案

    一.重置默认样式 normalize.css ①不像其他CSSreset,它保存了一些有用的默认样式. ②规范了大量样式,纠正了一下bug与表现形式. ③有详细的注释解释代码的作用. 二.清除浮动 / ...

  5. 关于CSS的那些事?

    关于CSS的那些事? 它有精准定位与排版,使得网页布局.信息排版一目了然:它有多姿多彩的样式属性,使得网页中各元素千变万化:它有神奇的渲染天赋,使得网页有了如诗如画.别具一格的魅力.你知道它了吗?没错 ...

  6. 安装 Ghost 博客和 Nginx

    Ghost 认 node 的版本,所以使用 nvm 更好. 1.安装 nvm: 可以去 github 查看 nvm 的说明,通过:wget -qO- https://raw.githubusercon ...

  7. 深入C#中get与set的详解(转)

    转自:http://www.jb51.net/article/37960.htm 释一:属性的访问器包含与获取(读取或计算)或设置(写)属性有关的可执行语句.访问器声明可以包含 get 访问器或 se ...

  8. centos7+nginx 1.9.0+php-fpm+phpstorm+xdebug+vmware开发环境搭建

    1.php-fpm yum install php-fpm 默认配置在本地9000端口监听 service php-fpm restart启动 2.nginx 1.9.0 需先安装gcc zlib o ...

  9. nginx入门到精通目录

    1.nginx入门篇 nginx安装与基础配置 nginx优化配置分析与说明 nginx模块结构 2.nginx功能篇 配置nginx的gzip功能 配置nginx的rewrite功能 配置nginx ...

  10. 北京VR视频外包团队:全景VR视频科普

    近期很多用户资讯问关于全景视频,这里动点给大家介绍一下: 首先,全景360VR视频(全景视频使用VR设备控制)是一种特殊的视频形式,与普通视频的最大区别就是,全景视频的每一帧都是涵盖360度空间场景信 ...