《深入理解Nginx》阅读与实践(四):简单的HTTP过滤模块
一、Nginx的HTTP過(guò)濾模塊特征
一個(gè)請(qǐng)求可以被任意個(gè)HTTP模塊處理;
在普通HTTP模塊處理請(qǐng)求完畢并調(diào)用ngx_http_send_header()發(fā)送HTTP頭部或調(diào)用ngx_http_output_filter()發(fā)送HTTP包體時(shí),才會(huì)由這兩個(gè)方法一次調(diào)用所有的HTTP過(guò)濾模塊來(lái)處理這個(gè)請(qǐng)求。HTTP過(guò)濾模塊僅處理服務(wù)器發(fā)送到客戶(hù)端的響應(yīng),而不處理客戶(hù)端發(fā)往服務(wù)器的HTTP請(qǐng)求。
多個(gè)過(guò)濾模塊的順序的形成以及Nginx自帶的過(guò)濾模塊請(qǐng)參考原書(shū)。
?
二、編寫(xiě)一個(gè)HTTP過(guò)濾模塊
? 以向返回給用戶(hù)的文本格式響應(yīng)包體前加一段字符串"[my filter prefix]"為例,展示如何編寫(xiě)一個(gè)HTTP過(guò)濾模塊。源代碼來(lái)自于《深入理解Nginx》。
1.config文件的編寫(xiě)
與前幾篇博文的HTTP模塊不同,HTTP過(guò)濾模塊需要HTTP_FILTER_MODULES一項(xiàng)以把所有過(guò)濾模塊一同編譯,因此config寫(xiě)作:
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"進(jìn)行configure時(shí),--add-module=PATH是一樣的。
?
2.編寫(xiě)模塊基本內(nèi)容:模塊定義、配置項(xiàng)處理
由于需要在nginx.conf中加入一項(xiàng)flag類(lèi)型的add_fix來(lái)控制這個(gè)過(guò)濾模塊的使用與否,與這個(gè)配置項(xiàng)處理相關(guān)的ngx_http_myfilter_create_conf()、ngx_http_myfilter_merge_conf()、ngx_http_mytest_commands[]需要對(duì)應(yīng)地進(jìn)行處理。
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從模塊上下文可以看出,過(guò)濾功能在模塊完成配置項(xiàng)處理后開(kāi)始,其初始化方法為ngx_myfilter_init()。
?
3.過(guò)濾功能實(shí)現(xiàn)
初始化方法ngx_myfilter_init()的功能僅僅是把當(dāng)前過(guò)濾模塊插入Nginx所有過(guò)濾模塊的鏈表中。
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()?
頭部處理方法是為了確定返回的類(lèi)型是否為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()?
包體處理方法根據(jù)頭部處理方法的結(jié)果來(lái)為包體添加前綴。
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()?
三、過(guò)濾模塊測(cè)試
根據(jù)原作者編寫(xiě)的nginx.conf
server {listen 8080;location / {root /;add_prefix on;}}可以看出,需要在/目錄下(系統(tǒng)根目錄)添加一個(gè)或多個(gè)任意內(nèi)容的文本文件來(lái)進(jìn)行測(cè)試。我寫(xiě)了一個(gè)內(nèi)容為test的文本文件test.txt。
輸入curl http://localhost:8080/test.txt,可以看到返回的內(nèi)容是[my filter prefix]test。
當(dāng)然,如果你放在/的不是純文本文件,而是html文件或者其他類(lèi)型文件,是不會(huì)增加這個(gè)前綴的。
另外,把on改成off,你會(huì)發(fā)現(xiàn)前綴不再出現(xiàn),說(shuō)明過(guò)濾模塊功能已經(jīng)關(guān)閉。
?
p.s.此書(shū)的后續(xù)章節(jié)是源碼分析,實(shí)踐環(huán)節(jié)比較少,“《深入理解Nginx》閱讀與實(shí)踐”系列可能到此為止。
總結(jié)
以上是生活随笔為你收集整理的《深入理解Nginx》阅读与实践(四):简单的HTTP过滤模块的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Spanning-tree Potoco
- 下一篇: 开源视频直播软件介绍