栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 系统运维 > 运维 > Linux

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

Linux 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

Nginx模块开发:模块结构的源码阅读以及过滤器(Filter)模块的实现
  • 一、Nignx中的模块是什么?
  • 二、模块的基本结构
    • `ngx_module_s`
    • `ngx_command_s`
    • `ngx_http_module_t`
  • 三、实现filter模块
    • 1、定义模块指令数组、ngx_http模块、ngx模块
    • 2、`ngx_http_prefix_filter_create_conf`
    • 3、`ngx_conf_set_flag_slot`(nginx已经实现的)
    • 5、`ngx_http_prefix_filter_merge_conf`
    • 6、`ngx_http_prefix_filter_init`
      • 1)`ngx_http_prefix_filter_header_filter`
      • 2)`ngx_http_prefix_filter_body_filter`
  • 四、模块config
  • 五、编译模块
  • 六、附录:
    • 1、命令的配置位置是什么意思
    • 2、完整代码

一、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)//这个是自带的,不需要实现

按照执行顺序

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;
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;
}
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_filter和ngx_http_top_body_filter是nginx对header和body的filter,是nginx中默认实现的,并且在回应请求时,会调用。

现在要实现自己的filter(比如网页,头部插入固定的模块内容),就要自己去实现功能。这里采用头插法,对nginx自带的http_filter函数用两个函数指针ngx_http_next_header_filter和ngx_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("Author : King

0voice

"); //通过自己的实现的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 
#include 
#include 




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("Author : King

0voice

"); 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); }
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/893749.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号