与 HTTP 处理模块不同。HTTP 过滤模块的工作是对发送给用户的 HTTP 响应做一些加工。
server 返回的一个响应能够被随意多个 HTTP 过滤模块以流水线的方式依次处理。HTTP 响应分为头部和包体,ngx_http_send_header 和 ngx_http_output_filter 函数分别负责发送头部和包体。它们会依次调用各个过滤模块对待发送的响应进行处理。
- // 过滤模块处理HTTP头部的方法原型
- typedef ngx_int_t( * ngx_http_output_header_filter_pt)(ngx_http_request_t * r);
- // 过滤模块处理HTTP包体的方法原型
- typedef ngx_int_t( * ngx_http_output_body_filter_pt)(ngx_http_request_t * r, ngx_chain_t * chain);
- // HTTP过滤模块链表入口
- extern ngx_http_output_header_filter_pt ngx_http_top_header_filter;
- extern ngx_http_output_body_filter_pt ngx_http_top_body_filter;
- ngx_int_t ngx_http_send_header(ngx_http_request_t * r) {
- if (r - >header_sent) {
- ngx_log_error(NGX_LOG_ALERT, r - >connection - >log, 0, "header already sent");
- return NGX_ERROR;
- }
- if (r - >err_status) {
- r - >headers_out.status = r - >err_status;
- r - >headers_out.status_line.len = 0;
- }
- // 从头遍历HTTP头部过滤模块
- return ngx_http_top_header_filter(r);
- }
- ngx_int_t ngx_http_output_filter(ngx_http_request_t * r, ngx_chain_t * in) {
- ngx_int_t rc;
- ngx_connection_t * c;
- c = r - >connection;
- ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c - >log, 0, "http output filter \"%V?%V\"", &r - >uri, &r - >args);
- // 遍历HTTP包体过滤模块
- rc = ngx_http_top_body_filter(r, in);
- if (rc == NGX_ERROR) {
- /* NGX_ERROR may be returned by any filter */
- c - >error = 1;
- }
- return rc;
- }
- 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_addition_filter_init(ngx_conf_t * cf) {
- // 从头部增加
- ngx_http_next_header_filter = ngx_http_top_header_filter;
- ngx_http_top_header_filter = ngx_http_addition_header_filter;
- // 从头部增加
- ngx_http_next_body_filter = ngx_http_top_body_filter;
- ngx_http_top_body_filter = ngx_http_addition_body_filter;
- return NGX_OK;
- }
类似上面 ngx_http_addition_filter_init 这种初始化函数在什么时候被调用呢?答案是依该方法放在 ngx_http_module_t 结构体的哪个成员而定。
一般而言,大多数官方 HTTP 过滤模块通常放在 ngx_http_module_t.postconfiguration 函数指针中,读取全然部配置项后被回调。各个模块初始化的顺序是怎么样的呢?这由 configure 命令生成的 ngx_modules.c 文件里的 ngx_modules 数组的排列顺序决定。数组中靠前的模块先初始化。因为过滤模块是将自己插入到链表头部,使得 ngx_modules 数组中过滤模块的排列顺序和它们实际运行的顺序相反。至于 ngx_modules 数组中的排列顺序,又是由其他脚本决定的。
- typedef struct {
- ngx_flag_t enable; // 保存on或者off
- }
- ngx_http_myfilter_conf_t;
- typedef struct {
- ngx_int_t add_prefix;
- }
- ngx_http_myfilter_ctx_t; // HTTP上下文结构体
这样。当一个请求被拆分成多次处理时。同一个处理函数就行了解该请求已经运行到哪里了,从而接着当前的进度进行处理。
- 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 */
- };
- static ngx_command_t ngx_http_myfilter_commands[] = {
- {
- ngx_string("myfilter"),
- 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
- };
首先来看看处理 HTTP 响应头部的函数:
- // 处理请求的头部
- 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)
- //该请求的上下文已经存在,这说明该模块已经被调用过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) {
- ctx - >add_prefix = 1; //1表示须要在http包体前增加前缀
- //假设处理模块已经在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_str_t filter_prefix = ngx_string("~~~~~~~This is a prefix~~~~~~~\n");
- // 处理请求的包体
- static ngx_int_t ngx_http_myfilter_body_filter(ngx_http_request_t * r, ngx_chain_t * in) {
- ngx_http_myfilter_ctx_t * ctx;
- // 获得HTTP上下文
- 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);
- }
以上函数的流程图例如以下:
- . / configure--add - module = "/work/nginx/modules/mytest /work/nginx/modules/myfilter"
- vim objs / ngx_modules.c
接下来就是 make 和 make install 了。成功安装后。我们还要改动配置文件:
- vim / usr / local / nginx / conf / nginx.conf
- #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;
- ngx_module_t ngx_http_myfilter_module; // 前向声明
- 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("~~~~~~~This is a prefix~~~~~~~\n");
- 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; // 0表示不须要加入前缀
- ngx_http_set_ctx(r, ctx, ngx_http_myfilter_module);
- // 仅仅处理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) {
- ctx - >add_prefix = 1; // 1表示须要加入前缀
- 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); // 获取上下文结构体
- if (ctx == NULL || ctx - >add_prefix != 1) return ngx_http_next_body_filter(r, in); // 不加入前缀
- ctx - >add_prefix = 2; // 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 * cl = ngx_alloc_chain_link(r - >pool);
- cl - >buf = b;
- cl - >next = in;
- return ngx_http_next_body_filter(r, cl); // 跳到下一个过滤模块
- }
- // 初始化HTTP过滤模块
- 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 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;
- }
- // 合并配置项
- 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;
- }
- static ngx_command_t ngx_http_myfilter_commands[] = {
- {
- ngx_string("myfilter"),
- 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
- };
- // HTTP框架初始化时调用的八个函数
- 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,
- };
- // 定义一个HTTP模块
- ngx_module_t ngx_http_myfilter_module = {
- NGX_MODULE_V1,
- // 0,0,0,0,0,0,1
- & ngx_http_myfilter_module_ctx,
- ngx_http_myfilter_commands,
- NGX_HTTP_MODULE,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NGX_MODULE_V1_PADDING,
- // 0,0,0,0,0,0,0,0,保留字段
- };
来源: http://www.bubuko.com/infodetail-2226101.html