Nginx模块开发:模块结构的源码阅读以及过滤器(Filter)模块的实现

简介: Nginx模块开发:模块结构的源码阅读以及过滤器(Filter)模块的实现

一、Nignx中的模块是什么?

比如下面的html,在index.html中并没有蓝框中的那部分,这部分是哪来的呢?

这是通过http模块来实现的,后续如果要对一批网页都进行添加这个内容,依次都去修改html比较麻烦,通过模块化的方式,只要配置conf文件即可。

这些公共的部分,可以在nginx内做成一个模块。

模块不仅可以实现上面这种方式,还可以在客户端可以在发送请求后,接受到html,通过md5,用来验证接受到的网页是否正确

按请求响应来划分,在Nginx中有三种模块:

  • 两根红线(浏览器->Nginx->服务器):upstream模块
  • 两根绿线(服务器->Nginx->浏览器):Filter模块
  • 一红一绿(浏览器->Nginx->浏览器):handler模块

应用:比如说Nginx受到攻击,可以通过handler模块去处理

二、模块的基本结构

ngx_module_s

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;//签名
  //上面字段可以用宏NGX_MODULE_V1进行初始化(如果不太关心上面内容的话)
    void                 *ctx;//具体的公共模块接口
    ngx_command_t        *commands;//模块支持的指令
    ngx_uint_t            type;//模块的标识类型
  //以下为hook函数
    ngx_int_t           (*init_master)(ngx_log_t *log);//主进程master初始化时调用
    ngx_int_t           (*init_module)(ngx_cycle_t *cycle);//模块初始化时调用
    ngx_int_t           (*init_process)(ngx_cycle_t *cycle);//工作进程worker初始化时调用
    ngx_int_t           (*init_thread)(ngx_cycle_t *cycle);//线程初始化时调用
    void                (*exit_thread)(ngx_cycle_t *cycle);//退出线程时调用
    void                (*exit_process)(ngx_cycle_t *cycle);//退出工作进程worker时调用
    void                (*exit_master)(ngx_cycle_t *cycle);//退出主进程时调用
  //8个预留字段,可以通过宏NGX_MODULE_V1_PADDING进行初始化
    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;
};

初始化ngx_module_s时,用到的宏

#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

ngx_module_s中较为核心的是以下三个内容

  • ctx:具体的公共模块接口
          需要自定义一个具体的模块,比如ngx_http_module_t,要对应type中的模块标识
  • commands:模块支持的指令
          需要自定义ngx_command_t
  • type:模块的标识类型
           NGX_CORE_MODULE     核心模块
           NGX_CONF_MODULE     配置模块
           NGX_EVENT_MODULE   event模块
           NGX_HTTP_MODULE     http模块
           NGX_MAIL_MODULE      mail模块

ngx_command_s

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;
};

比较重要的参数

  • name:配置指令名称
    比如:ngx_string(“count”)表示conf配置文件中的可以输入的命令count
  • type:配置指令类型
    type主要是一些标志位,比如命令的配置位置(本文最后附录有解释),以及命令的参数数目等
  • set:配置指令的执行函数

ngx_http_module_t

http_module不是用来存属性的,只负责去解析conf文件

HTTP block中的配置结构主要分为3中,main、server{}、location{}

提供了4对(8个)回调函数,这些函数的执行顺序如下图所示

三、实现filter模块

要实现的功能,就是像下图那样,蓝色框的那部分,没有添加在html里面,但是可以在conf配置中,添加add_header命令,就可以实现下面的功能

1、定义模块指令数组、ngx_http模块、ngx模块

//模块支持的指令 数组
static ngx_command_t ngx_http_prefix_filter_commands[] = {
  {
    ngx_string("add_prefix"),//自定义的http模块指令为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,//(4)//解析命令的参数,这个函数是自带的,用于解析on和off
    NGX_HTTP_LOC_CONF_OFFSET,//指令存储地址
    offsetof(ngx_http_prefix_filter_conf_t, enable),//指令存储地址的偏移量
    NULL
  },
  ngx_null_command
};
//具体的模块信息:http模块,并配置它的回调函数
static ngx_http_module_t ngx_http_prefix_filter_module_ctx = {
  NULL,
  ngx_http_prefix_filter_init,//(1)//解析完conf要做的事,用于设置运行时的参数,也就是可以去初始化http_prefix_filter,
  NULL,
  NULL,
  NULL,
  NULL,
  ngx_http_prefix_filter_create_conf,//(2)//创建一块内存,用于存放配置信息
  ngx_http_prefix_filter_merge_conf//(3)//合并配置信息
};
//回调函数的执行顺序是  2--(4)--3--1     如果conf中没有调用指令,就不会有(4)
//主模块,也就是将上面的http模块和commands整合到一起
ngx_module_t ngx_http_prefix_filter_module = {
  NGX_MODULE_V1,//宏
  &ngx_http_prefix_filter_module_ctx,//具体的模块  http模块
  ngx_http_prefix_filter_commands,//模块支持的指令
  NGX_HTTP_MODULE,//模块类型为http模块
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NGX_MODULE_V1_PADDING//宏
}; 

根据上述的模块配置信息,也就是说要去实现以下四个函数

static ngx_int_t ngx_http_prefix_filter_init(ngx_conf_t *cf)
static void *ngx_http_prefix_filter_create_conf(ngx_conf_t *cf)
static char *ngx_http_prefix_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child)
char *ngx_conf_set_flag_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)//这个是自带的,不需要实现

以下按照执行顺序来以此解释

1.要执行自己定义的add_prefix参数,那么就需要定义一个结构体去接受参数

2.接受参数前,首先要进参数结构体的内存空间的分配,也就是在ngx_http_prefix_filter_create_conf中进行。

3.创建完这保存参数的结构体后,就需要把参数解析出来,给这个结构体赋值,在ngx_conf_set_flag_slot中进行

4.获得结构体参数后,接下去就可以去使用这个参数,去实现自己的功能了。系统会自动调用ngx_http_top_header_filterngx_http_top_body_filter这两个函数,那么如何去添加自己想要的功能呢?这里可以使用一个hook的方法,通过临时变量存储默认调用的ngx_http_top_header_filterngx_http_top_body_filter函数。ngx_http_top_header_filter指向自己实现的函数,在自己实现的函数里面再去调用默认的ngx_http_top_header_filter,来实现一个hook的功能。

5.另外,为了保证header和body中数据的一致,比如header中的数据长度和body中 数据要对应起来。可以对当前模块设置一个ctx,对header和body对应的函数之间的通信,保证一致性。

2、ngx_http_prefix_filter_create_conf

存储该函数的函数指针

char *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);

这部分主要是用于分配存储参数的结构体的空间(通过内存池来分配)

static void *ngx_http_prefix_filter_create_conf(ngx_conf_t *cf) {//主要是分配空间
  ngx_http_prefix_filter_conf_t *conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_prefix_filter_conf_t));//从内存池中拿出一块内存出来
  if (conf == NULL) {
    return NULL;
  }
  conf->enable = NGX_CONF_UNSET;
  return conf;
}

ngx_http_prefix_filter_conf_t这个是用于存储命令参数的结构体

typedef struct {
  ngx_flag_t enable;
} ngx_http_prefix_filter_conf_t;

现在只是将该参数的结构体定义完了,可以看到这个函数,分配完空间后,将它返回了,那么哪里去接受这个conf呢?

3、ngx_conf_set_flag_slot(nginx已经实现的)

这个函数是nginx自己实现的,用来解析参数中的on和off

char *
ngx_conf_set_flag_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    char  *p = conf;
    ngx_str_t        *value;
    ngx_flag_t       *fp;
    ngx_conf_post_t  *post;
    fp = (ngx_flag_t *) (p + cmd->offset);
    if (*fp != NGX_CONF_UNSET) {
        return "is duplicate";
    }
    value = cf->args->elts;
    if (ngx_strcasecmp(value[1].data, (u_char *) "on") == 0) {
        *fp = 1;
    } else if (ngx_strcasecmp(value[1].data, (u_char *) "off") == 0) {
        *fp = 0;
    } else {
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                     "invalid value \"%s\" in \"%s\" directive, "
                     "it must be \"on\" or \"off\"",
                     value[1].data, cmd->name.data);
        return NGX_CONF_ERROR;
    }
    if (cmd->post) {
        post = cmd->post;
        return post->post_handler(cf, post, fp);
    }
    return NGX_CONF_OK;
}

这里的红箭头指向的void* conf参数就是ngx_http_prefix_filter_create_conf中返回的confngx_http_prefix_filter_create_conf中只分配了一块内存,还没有进行赋值

当解析的参数为"on",那么下面结构体中enable的值就为1

当解析的参数为"off",那么下面结构体中enable的值就为0

5、ngx_http_prefix_filter_merge_conf

存储该函数的函数指针

char *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);

主要是用于给位置处于location下的 add_header指令,根据上一层的指令给值。

static char *ngx_http_prefix_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child) {
  ngx_http_prefix_filter_conf_t *prev = (ngx_http_prefix_filter_conf_t*)parent;
  ngx_http_prefix_filter_conf_t *conf = (ngx_http_prefix_filter_conf_t*)child;
  ngx_conf_merge_value(conf->enable, prev->enable, 0);
  return NGX_CONF_OK;
}

merge做了什么事?

比如下图的conf文件,有多个add_header指令(自定义的),用于给当前的配置位置的命令传输值。

比如location下如果没有定义add_header,那么就会沿用上一层的add_header的命令。

#define ngx_conf_merge_value(conf, prev, default)                            \
    if (conf == NGX_CONF_UNSET) {                                            \
        conf = (prev == NGX_CONF_UNSET) ? default : prev;                    \
    }

如果当前层没有设置,上一层也没有设置,那么就给默认值default

如果当前层没有设置,上一层设置了,那么就给上一层设置的值

也就是说,如果上一层有,想沿用上一层的设置,当前层设置不设置都无所谓

6、ngx_http_prefix_filter_init

ngx_http_top_header_filterngx_http_top_body_filter是nginx对header和body的filter,是nginx中默认实现的,并且在回应请求时,会调用。

现在要实现自己的filter(比如网页,头部插入固定的模块内容),就要自己去实现功能。这里采用头插法,对nginx自带的http_filter函数用两个函数指针ngx_http_next_header_filterngx_http_next_body_filter暂时存储起来。

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_prefix_filter_init(ngx_conf_t *cf) {
  //(用自己实现的替换原先的,然后在自己实现的里面调用 原先的),  
  //采用头插法加入header和body的两个filter模块    这些模块都是http请求后,响应时处理的模块
  ngx_http_next_header_filter = ngx_http_top_header_filter;
  ngx_http_top_header_filter = ngx_http_prefix_filter_header_filter;
  ngx_http_next_body_filter = ngx_http_top_body_filter;
  ngx_http_top_body_filter = ngx_http_prefix_filter_body_filter;//把自己实现的filter,作为top,在这个函数里面再调用ngx_http_next_body_filter
  return NGX_OK;
}

1)ngx_http_prefix_filter_header_filter

主要是为了做下面的步骤,由于body中要多传输一部分数据,那么header中就要对应增加相应的长度

r->headers_out.content_length_n += filter_prefix.len

但是如果出现,header中的长度和body中的数据长度不一致,那么就会出现问题,为了确保数据的一致性,那么就可以通过在filter_header中ngx_http_set_ctx来设置ctx,在filter_body中来ngx_http_get_module_ctx,保证数据的一致。

并且在filte_header中添加一些判断,保证不出错。

最后再把其他处理过程交给ngx_http_prefix_filter_header_filter,否则最后的网页,只有新添加的模块内容,原来的其他内容就没了。

static ngx_int_t ngx_http_prefix_filter_header_filter(ngx_http_request_t *r) {
  ngx_http_prefix_filter_ctx_t *ctx;
  ngx_http_prefix_filter_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_prefix_filter_module);
  if (ctx) {
    return ngx_http_next_header_filter(r);
  }
  conf = ngx_http_get_module_loc_conf(r, ngx_http_prefix_filter_module);
  if (conf == NULL) {
    return ngx_http_next_header_filter(r);
  }
  if (conf->enable == 0) {
    return ngx_http_next_header_filter(r);
  }
  //在这里设置ngx_http_set_ctx,在下面body部分可以ngx_http_get_module_ctx来获得ctx,相当于是存储了一个通信的资源,以此来保证header和body的数据的一致性,不然可能出现header中的r->headers_out.content_length_n和下面body新添加的数据不对应
  ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_prefix_filter_ctx_t));
  if (ctx == NULL) {
    return NGX_ERROR;
  }
  ctx->add_prefix = 0;
  ngx_http_set_ctx(r, ctx, ngx_http_prefix_filter_module);
  if (r->headers_out.content_type.len >= sizeof("text/html") - 1
    && ngx_strncasecmp(r->headers_out.content_type.data, (u_char*)"text/html", sizeof("text/html")-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_prefix_filter_header_filter(r);
}

2)ngx_http_prefix_filter_body_filter

这部分主要是创建一块buffer内存,赋值模块的内容,然后接到 链表上,就是全部的数据内容了。然后交给ngx_http_next_body_filter(r, cl)去处理。

static ngx_str_t filter_prefix = ngx_string("<h2>Author : King</h2><p><a href=\"http://www.0voice.com\">0voice</a></p>");
//通过自己的实现的filter,加入自己要实现的部分,然后调用原来的ngx_http_next_body_filter就行了
//后续http响应,处理过程就会调用这个filter模块。自己的部分+原先的部分(原先部分通过调用ngx_http_next_body_filter)
static ngx_int_t ngx_http_prefix_filter_body_filter(ngx_http_request_t *r, ngx_chain_t *in) {
  ngx_http_prefix_filter_ctx_t *ctx = ngx_http_get_module_ctx(r, ngx_http_prefix_filter_module);
  if (ctx == NULL || ctx->add_prefix != 1) {
    return ngx_http_next_body_filter(r, in);
  }
  ctx->add_prefix = 2;
  //创建一块buffer(从内存池取)(用于存储字符)并添加数据         html中的数据都是存在buffer中的
  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;
  //创建一个链的节点,分配buffer,并且插入链表的头部
  ngx_chain_t *cl = ngx_alloc_chain_link(r->pool);//chain是链的节点, 链由若干个buffer组成       (这块内存什么时候释放?连接池释放的时候才会释放)
  cl->buf = b;
  cl->next = in;
  return ngx_http_next_body_filter(r, cl);
}

四、模块config

gx_addon_name=ngx_http_prefix_filter_module
HTTP_FILTER_MODULES="$HTTP_FILTER_MODULES ngx_http_prefix_filter_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_prefix_filter_module.c"
  • ngx_addon_name:添加的模块名称
  • HTTP_MODULES:所有http模块
  • NGX_ADDON_SRCS:所有要编译的.c源文件

config中添加模块的方式有点像添加环境变量的方式

五、编译模块

./configure去生成可以的编译文件

./configure --add-module=/home/xuheding/share/Open_source_code/nginx-1.21.6/ngx_http_prefix_filter_module

执行完后,可以看到新加模块已经被生成可编译文件的一部分了

在configure生成的参与编译的objs/ngx_modules.c文件中,可以找到新添加的模块

后续通过make就可以进行编译了

六、附录:

1、命令的配置位置是什么意思

type主要是一些标志位,比如命令的作用域,以及命令的传参类型等…

NGX_HTTP_LOC_CONF表示什么意思呢?

先看看下面,表示一些命令所在conf文件中的作用位置

比如设置程成NGX_HTTP_MAIN_CONF表示,该命令作用于下图http中,也就是在http大括号内,可以输入命令

同理,NGX_HTTP_SRV_CONF表示,在http中的server中,可以输入的命令

NGX_HTTP_LOC_CONF表示在,http中的server的location中,可以输入的命令

也就是说这些标志位决定命令的作用域

那么NGX_CONF_NOARGS是什么意思?表示配置指令不接受任何参数

用的比较多的还有NGX_CONF_FLAG,表示后面参数要么是on或者off

2、完整代码

#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
typedef struct {
  ngx_flag_t enable;
} ngx_http_prefix_filter_conf_t;
typedef struct {
  ngx_int_t add_prefix;
} ngx_http_prefix_filter_ctx_t;
static ngx_int_t ngx_http_prefix_filter_init(ngx_conf_t *cf);
static ngx_int_t ngx_http_prefix_filter_header_filter(ngx_http_request_t *r);
static ngx_int_t ngx_http_prefix_filter_body_filter(ngx_http_request_t *r, ngx_chain_t *in);
static ngx_str_t filter_prefix = ngx_string("<h2>Author : King</h2><p><a href=\"http://www.0voice.com\">0voice</a></p>");
static void *ngx_http_prefix_filter_create_conf(ngx_conf_t *cf) {//主要是分配空间
  ngx_http_prefix_filter_conf_t *conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_prefix_filter_conf_t));//从内存池中拿出一块内存出来
  if (conf == NULL) {
    return NULL;
  }
  conf->enable = NGX_CONF_UNSET;
  return conf;
}
static char *ngx_http_prefix_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child) {
  ngx_http_prefix_filter_conf_t *prev = (ngx_http_prefix_filter_conf_t*)parent;
  ngx_http_prefix_filter_conf_t *conf = (ngx_http_prefix_filter_conf_t*)child;
  ngx_conf_merge_value(conf->enable, prev->enable, 0);
  return NGX_CONF_OK;
}
//模块支持的指令 数组
static ngx_command_t ngx_http_prefix_filter_commands[] = {
  {
    ngx_string("add_prefix"),//自定义的http模块指令为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,//(4)//解析命令的参数,这个函数是自带的,用于解析on和off
    NGX_HTTP_LOC_CONF_OFFSET,//指令存储地址
    offsetof(ngx_http_prefix_filter_conf_t, enable),//指令存储地址的偏移量
    NULL
  },
  ngx_null_command
};
//具体的模块信息:http模块,并配置它的回调函数
static ngx_http_module_t ngx_http_prefix_filter_module_ctx = {
  NULL,
  ngx_http_prefix_filter_init,//(1)//解析完conf要做的事,用于设置运行时的参数,也就是可以去初始化http_prefix_filter,
  NULL,
  NULL,
  NULL,
  NULL,
  ngx_http_prefix_filter_create_conf,//(2)//创建一块内存,用于存放配置信息
  ngx_http_prefix_filter_merge_conf//(3)//合并配置信息
};
//回调函数的执行顺序是  2--(4)--3--1     如果conf中没有调用指令,就不会有(4)
//主模块,也就是将上面的http模块和commands整合到一起
ngx_module_t ngx_http_prefix_filter_module = {
  NGX_MODULE_V1,//宏
  &ngx_http_prefix_filter_module_ctx,//具体的模块  http模块
  ngx_http_prefix_filter_commands,//模块支持的指令
  NGX_HTTP_MODULE,//模块类型为http模块
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NULL,
  NGX_MODULE_V1_PADDING//宏
}; 
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_prefix_filter_init(ngx_conf_t *cf) {
  //(用自己实现的替换原先的,然后在自己实现的里面调用 原先的),  
  //采用头插法加入header和body的两个filter模块    这些模块都是http请求后,响应时处理的模块
  ngx_http_next_header_filter = ngx_http_top_header_filter;
  ngx_http_top_header_filter = ngx_http_prefix_filter_header_filter;
  ngx_http_next_body_filter = ngx_http_top_body_filter;
  ngx_http_top_body_filter = ngx_http_prefix_filter_body_filter;//把自己实现的filter,作为top,在这个函数里面再调用ngx_http_next_body_filter
  return NGX_OK;
}
static ngx_int_t ngx_http_prefix_filter_header_filter(ngx_http_request_t *r) {
  ngx_http_prefix_filter_ctx_t *ctx;
  ngx_http_prefix_filter_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_prefix_filter_module);
  if (ctx) {
    return ngx_http_next_header_filter(r);
  }
  conf = ngx_http_get_module_loc_conf(r, ngx_http_prefix_filter_module);
  if (conf == NULL) {
    return ngx_http_next_header_filter(r);
  }
  if (conf->enable == 0) {
    return ngx_http_next_header_filter(r);
  }
  //在这里设置ngx_http_set_ctx,在下面body部分可以ngx_http_get_module_ctx来获得ctx,相当于是存储了一个通信的资源,以此来保证header和body的数据的一致性,不然可能出现header中的r->headers_out.content_length_n和下面body新添加的数据不对应
  ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_prefix_filter_ctx_t));
  if (ctx == NULL) {
    return NGX_ERROR;
  }
  ctx->add_prefix = 0;
  ngx_http_set_ctx(r, ctx, ngx_http_prefix_filter_module);
  if (r->headers_out.content_type.len >= sizeof("text/html") - 1
    && ngx_strncasecmp(r->headers_out.content_type.data, (u_char*)"text/html", sizeof("text/html")-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_prefix_filter_header_filter(r);
}
//通过自己的实现的filter,加入自己要实现的部分,然后调用原来的ngx_http_next_body_filter就行了
//后续http响应,处理过程就会调用这个filter模块。自己的部分+原先的部分(原先部分通过调用ngx_http_next_body_filter)
static ngx_int_t ngx_http_prefix_filter_body_filter(ngx_http_request_t *r, ngx_chain_t *in) {
  ngx_http_prefix_filter_ctx_t *ctx = ngx_http_get_module_ctx(r, ngx_http_prefix_filter_module);
  if (ctx == NULL || ctx->add_prefix != 1) {
    return ngx_http_next_body_filter(r, in);
  }
  ctx->add_prefix = 2;
  //创建一块buffer(从内存池取)(用于存储字符)并添加数据         html中的数据都是存在buffer中的
  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;
  //创建一个链的节点,分配buffer,并且插入链表的头部
  ngx_chain_t *cl = ngx_alloc_chain_link(r->pool);//chain是链的节点, 链由若干个buffer组成       (这块内存什么时候释放?连接池释放的时候才会释放)
  cl->buf = b;
  cl->next = in;
  return ngx_http_next_body_filter(r, cl);
}

相关文章
|
1月前
|
应用服务中间件 nginx
Nginx安装nginx-rtmp-module模块
【2月更文挑战第4天】 nginx中的模块虽然就是类似插件的概念,但是它无法像VsCode那样轻松的安装扩展。 nginx要安装其它模块必须同时拿到nginx源代码和模块源代码,然后手动编译,将模块打到nginx中,最终生成一个名为nginx的可执行文件。
70 6
|
6天前
|
负载均衡 算法 应用服务中间件
面试题:Nginx有哪些负载均衡算法?Nginx位于七层网络结构中的哪一层?
字节跳动面试题:Nginx有哪些负载均衡算法?Nginx位于七层网络结构中的哪一层?
25 0
|
3月前
|
应用服务中间件 nginx
百度搜索:蓝易云【利用nginx内置ngx_http_mirror_module模块实现流量复制及流量放大】
以上就是使用Nginx内置 `ngx_http_mirror_module`模块实现流量复制和流量放大的简要示例。通过合理配置和利用该模块,可以实现更复杂的流量控制和调试需求。
54 1
|
2月前
|
消息中间件 关系型数据库 MySQL
使用Nginx的stream模块实现MySQL反向代理与RabbitMQ负载均衡
使用Nginx的stream模块实现MySQL反向代理与RabbitMQ负载均衡
56 0
|
3月前
|
存储 应用服务中间件 nginx
Nginx模块开发:handler模块实现
Nginx模块开发:handler模块实现
27 0
|
3月前
|
应用服务中间件 nginx
Nginx源码阅读:共享内存ngx_shm_t和它的组织方式ngx_shm_zone_t、ngx_list_t
Nginx源码阅读:共享内存ngx_shm_t和它的组织方式ngx_shm_zone_t、ngx_list_t
22 0
|
3月前
|
应用服务中间件 nginx
Nginx源码阅读:ngx_list_t 链表
Nginx源码阅读:ngx_list_t 链表
51 0
|
3月前
|
存储 网络协议 应用服务中间件
Nginx源码阅读:ngx_palloc 内存池
Nginx源码阅读:ngx_palloc 内存池
54 0
|
3月前
|
应用服务中间件 nginx
Nginx源码阅读:nginx_shmtx共享互斥锁(进程锁)
Nginx源码阅读:nginx_shmtx共享互斥锁(进程锁)
37 0
|
3月前
|
负载均衡 应用服务中间件 Linux
Nginx源码阅读:避免惊群以及负载均衡的原理与具体实现
Nginx源码阅读:避免惊群以及负载均衡的原理与具体实现
42 0