編寫nginx http handler模塊以便開發自己模塊,本文提供hello編寫到編譯的詳細步驟 , 文章最後提供整個示例代碼
編寫http handler模塊的幾個組成部分講解:
1、ngx_command_t
示例:
static ngx_command_t ngx_http_mytest_commands[] =
{
{
ngx_string("mytest"),
NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_NOARGS,
ngx_http_mytest,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL
},
ngx_null_command
};
在nginx.conf 中編寫的配置項 mytest 來說, nginx 首先會遍歷所有的模塊(modules),而對於每個模塊, 會遍歷他所對應的ngx_command_t 數組, 試圖找到關於我們的配置項mytest 的解析方式。
command中用於處理配置項參數的set 方法,函數名格式寫法ngx_http_xxxxx,如下所示:
static char *
ngx_http_mytest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_core_loc_conf_t *clcf;
//首先找到mytest配置項所屬的配置塊,clcf貌似是location塊內的數據
//結構,其實不然,它可以是main、srv或者loc級別配置項,也就是說在每個
//http{}和server{}內也都有一個ngx_http_core_loc_conf_t結構體
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
//http框架在處理用戶請求進行到NGX_HTTP_CONTENT_PHASE階段時,如果
//請求的主機域名、URI與mytest配置項所在的配置塊相匹配,就將調用我們
//實現的ngx_http_mytest_handler方法處理這個請求
clcf->handler = ngx_http_mytest_handler;
return NGX_CONF_OK;
}
關於ngx_http_conf_get_module_loc_conf 的定義可以參考: http://lxr.nginx.org/source/src/http/ngx_http_config.h#0065 本質: 就是設置ngx_http_mytest_handler, 匹配項被選中的時候, 應該如何解析。
ngx_command_t的定義,位於src/core/ngx_conf_file.h中。
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: 配置指令的名稱。
type: 該配置的類型,其實更準確一點說,是該配置指令屬性的集合。nginx提供了很多預定義的屬性值(一些宏定義),通過邏輯或運算符可組合在一起,形成對這個配置指令的詳細的說明。下面列出可在這裏使用的預定義屬性值及說明。
NGX_CONF_NOARGS:配置指令不接受任何參數。
NGX_CONF_TAKE1:配置指令接受1個參數。
NGX_CONF_TAKE2:配置指令接受2個參數。
NGX_CONF_TAKE3:配置指令接受3個參數。
NGX_CONF_TAKE4:配置指令接受4個參數。
NGX_CONF_TAKE5:配置指令接受5個參數。
NGX_CONF_TAKE6:配置指令接受6個參數。
NGX_CONF_TAKE7:配置指令接受7個參數。
可以組合多個屬性,比如一個指令即可以不填參數,也可以接受1個或者2個參數。那麼就是NGX_CONF_NOARGS|NGX_CONF_TAKE1|NGX_CONF_TAKE2。如果寫上面三個屬性在一起,你覺得麻煩,那麼沒有關係,nginx提供了一些定義,使用起來更簡潔。
NGX_CONF_TAKE12:配置指令接受1個或者2個參數。
NGX_CONF_TAKE13:配置指令接受1個或者3個參數。
NGX_CONF_TAKE23:配置指令接受2個或者3個參數。
NGX_CONF_TAKE123:配置指令接受1個或者2個或者3參數。
NGX_CONF_TAKE1234:配置指令接受1個或者2個或者3個或者4個參數。
NGX_CONF_1MORE:配置指令接受至少一個參數。
NGX_CONF_2MORE:配置指令接受至少兩個參數。
NGX_CONF_MULTI: 配置指令可以接受多個參數,即個數不定。
NGX_CONF_BLOCK:配置指令可以接受的值是一個配置信息塊。也就是一對大括號括起來的內容。裏面可以再包括很多的配置指令。比如常見的server指令就是這個屬性的。
NGX_CONF_FLAG:配置指令可以接受的值是”on”或者”off”,最終會被轉成bool值。
NGX_CONF_ANY:配置指令可以接受的任意的參數值。一個或者多個,或者”on”或者”off”,或者是配置塊。 最後要說明的是,無論如何,nginx的配置指令的參數個數不可以超過NGX_CONF_MAX_ARGS個。目前這個值被定義爲8,也就是不能超過8個參數值。
下面介紹一組說明配置指令可以出現的位置的屬性。
NGX_DIRECT_CONF:可以出現在配置文件中最外層。例如已經提供的配置指令daemon,master_process等。
NGX_MAIN_CONF: http、mail、events、error_log等。
NGX_ANY_CONF: 該配置指令可以出現在任意配置級別上。 對於我們編寫的大多數模塊而言,都是在處理http相關的事情,也就是所謂的都是NGX_HTTP_MODULE,對於這樣類型的模塊,其配置可能出現的位置也是分爲直接出現在http裏面,以及其他位置。
NGX_HTTP_MAIN_CONF: 可以直接出現在http配置指令裏。
NGX_HTTP_SRV_CONF: 可以出現在http裏面的server配置指令裏。
NGX_HTTP_LOC_CONF: 可以出現在http server塊裏面的location配置指令裏。
NGX_HTTP_UPS_CONF: 可以出現在http裏面的upstream配置指令裏。
NGX_HTTP_SIF_CONF: 可以出現在http裏面的server配置指令裏的if語句所在的block中。
NGX_HTTP_LMT_CONF: 可以出現在http裏面的limit_except指令的block中。
NGX_HTTP_LIF_CONF: 可以出現在http server塊裏面的location配置指令裏的if語句所在的block中。
set: 這是一個函數指針,當nginx在解析配置的時候,如果遇到這個配置指令,將會把讀取到的值傳遞給這個函數進行分解處理。因爲具體每個配置指令的值如何處理,只有定義這個配置指令的人是最清楚的。來看一下這個函數指針要求的函數原型。
char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
2、定義ngx_http_module_t 接口
這部分的代碼, 是用於http框架的, 相當於http框架的回掉函數, 由於這裏並不需要框架做任何操作,格式ngx_http_xxxxx_module_ctx。
static ngx_http_module_t ngx_http_mytest_module_ctx =
{
NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
};
3、ngx_module_t模塊的定義
示例: 格式ngx_http_xxxx_module
只需要簡單的設置3個項目: ctx(指向模塊的上下文), commands, type(模塊類型)
ngx_module_t ngx_http_mytest_module =
{
NGX_MODULE_V1,
&ngx_http_mytest_module_ctx, /* module context */
ngx_http_mytest_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
來參考一下深入理解nginx書中對http模塊數據結構的理解:
定義 HTTP 模塊方式很簡單,例如:
ngx_module_t ngx_http_mytest_module;
其中,ngx_module_t 是一個 Nginx 模塊的數據結構(詳見 8.2 節)。下面來分析一下
Nginx 模塊中所有的成員,如下所示:
typedef struct ngx_module_s ngx_module_t;
struct ngx_module_s {
/* 下面的 ctx_index、index、spare0、spare1、spare2、spare3、version 變量不需要在定義時賦值,
可以用 Nginx 準備好的宏 NGX_MODULE_V1 來定義,它已經定義好了這 7 個值。
#define NGX_MODULE_V1 0, 0, 0, 0, 0, 0, 1
對於一類模塊(由下面的 type 成員決定類別)而言,ctx_index 表示當前模塊在這類模塊中的序號。這
個成員常常是由管理這類模塊的一個 Nginx 核心模塊設置的,對於所有的 HTTP 模塊而言,ctx_index 是由核心模
塊 ngx_http_module 設置的。ctx_index 非常重要,Nginx 的模塊化設計非常依賴於各個模塊的順序,它們既用
於表達優先級,也用於表明每個模塊的位置,藉以幫助 Nginx 框架快速獲得某個模塊的數據(HTTP 框架設置 ctx_
index 的過程參見 10.7 節)*/
ngx_uint_t ctx_index;
/*index 表示當前模塊在 ngx_modules 數組中的序號。注意,ctx_index 表示的是當前模塊在一類模
塊中的序號,而 index 表示當前模塊在所有模塊中的序號,它同樣關鍵。Nginx 啓動時會根據 ngx_modules 數組
設置各模塊的 index 值。例如:
ngx_max_module = 0;
for (i = 0; ngx_modules[i]; i++) {
ngx_modules[i]->index = ngx_max_module++;
}
*/
ngx_uint_t index;
//spare 系列的保留變量,暫未使用
ngx_uint_t spare0;
ngx_uint_t spare1;
ngx_uint_t spare2;
ngx_uint_t spare3;
// 模塊的版本,便於將來的擴展。目前只有一種,默認爲 1
ngx_uint_t version;
/*ctx 用於指向一類模塊的上下文結構體,爲什麼需要 ctx 呢?因爲前面說過,Nginx 模塊有許多種類,
不同類模塊之間的功能差別很大。例如,事件類型的模塊主要處理 I/O 事件相關的功能,HTTP 類型的模塊主要處理
HTTP 應用層的功能。這樣,每個模塊都有了自己的特性,而 ctx 將會指向特定類型模塊的公共接口。例如,在 HTTP
模塊中,ctx 需要指向 ngx_http_module_t 結構體 */
void *ctx;
//commands 將處理 nginx.conf 中的配置項,詳見第 4 章
ngx_command_t *commands;
/*type 表示該模塊的類型,它與 ctx 指針是緊密相關的。在官方 Nginx 中,它的取值範圍是以下 5 種 :
NGX_HTTP_MODULE、NGX_CORE_MODULE、NGX_CONF_MODULE、NGX_EVENT_MODULE、NGX_MAIL_MODULE。這
5 種模塊間的關係參考圖 8-2。實際上,還可以自定義新的模塊類型 */
ngx_uint_t type;
/* 在 Nginx 的啓動、停止過程中,以下 7 個函數指針表示有 7 個執行點會分別調用這 7 種方法(參見
8.4 節~ 8.6 節)。對於任一個方法而言,如果不需要 Nginx 在某個時刻執行它,那麼簡單地把它設爲 NULL 空指針
即可 */
/* 雖然從字面上理解應當在 master 進程啓動時回調 init_master,但到目前爲止,框架代碼從來不會
調用它,因此,可將 init_master 設爲 NULL */
ngx_int_t (*init_master)(ngx_log_t *log);
/*init_module 回調方法在初始化所有模塊時被調用。在 master/worker 模式下,這個階段將在啓動
worker 子進程前完成 */
ngx_int_t (*init_module)(ngx_cycle_t *cycle);
/* init_process 回調方法在正常服務前被調用。在 master/worker 模式下,多個 worker 子進程已經產
生,在每個 worker 進程的初始化過程會調用所有模塊的 init_process 函數 */
ngx_int_t (*init_process)(ngx_cycle_t *cycle);
/* 由於 Nginx 暫不支持多線程模式,所以 init_thread 在框架代碼中沒有被調用過,設爲 NULL*/
ngx_int_t (*init_thread)(ngx_cycle_t *cycle);
// 同上,exit_thread 也不支持,設爲 NULL
void (*exit_thread)(ngx_cycle_t *cycle);
/* exit_process 回調方法在服務停止前調用。在 master/worker 模式下,worker 進程會在退出前調用它 */
void (*exit_process)(ngx_cycle_t *cycle);
// exit_master 回調方法將在 master 進程退出前被調用
void (*exit_master)(ngx_cycle_t *cycle);
/* 以下 8 個 spare_hook 變量也是保留字段,目前沒有使用,但可用 Nginx 提供的 NGX_MODULE_V1_
PADDING 宏來填充。看一下該宏的定義:#define NGX_MODULE_V1_PADDING 0, 0, 0, 0, 0, 0, 0, 0*/
uintptr_t spare_hook0;
uintptr_t spare_hook1;
uintptr_t spare_hook2;
第 3 章 開發一個簡單的 HTTP 模塊 87
uintptr_t spare_hook3;
uintptr_t spare_hook4;
uintptr_t spare_hook5;
uintptr_t spare_hook6;
uintptr_t spare_hook7;
};
定義一個 HTTP 模塊時,務必把 type 字段設爲 NGX_HTTP_MODULE。
對於下列回調方法 :init_module、init_process、exit_process、exit_master,調用它們的 是 Nginx 的框架代碼。換句話說,這 4 個回調方法與 HTTP 框架無關,即使 nginx.conf 中沒 有配置 http {...} 這種開啓 HTTP 功能的配置項,這些回調方法仍然會被調用。因此,通常 開發 HTTP 模塊時都把它們設爲 NULL 空指針。這樣,當 Nginx 不作爲 Web 服務器使用時, 不會執行 HTTP 模塊的任何代碼。
定義 HTTP 模塊時,最重要的是要設置 ctx 和 commands 這兩個成員。對於 HTTP 類型 的模塊來說,ngx_module_t 中的 ctx 指針必須指向 ngx_http_module_t 接口(HTTP 框架的要 求)。下面先來分析 ngx_http_module_t 結構體的成員。
HTTP 框架在讀取、重載配置文件時定義了由 ngx_http_module_t 接口描述的 8 個階段, HTTP 框架在啓動過程中會在每個階段中調用 ngx_http_module_t 中相應的方法。當然,如果 ngx_http_module_t 中的某個回調方法設爲 NULL 空指針,那麼 HTTP 框架是不會調用它的。
4.編寫config文件,編譯代碼的時候用
config文件和編寫c文件放在同一個文件下:比如我放在:
[root@hadoop2 nginx-1.13.8]# pwd
/root/nginx-1.13.8
[root@hadoop2 nginx-1.13.8]# cd mytesthttp/
[root@hadoop2 mytesthttp]# ll
total 16
-rw-r--r-- 1 root root 163 Jan 18 09:56 config
-rw-r--r-- 1 root root 8915 Jan 18 15:52 ngx_http_mytest_module.c
[root@hadoop2 mytesthttp]#
其中config文件內容基本下面的形式:
ngx_addon_name=ngx_http_mytest_module
HTTP_MODULES="$HTTP_MODULES ngx_http_mytest_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_mytest_module.c"
$ngx_addon_dir 這個值是執行configure 時候添加的選線--add-module提供的:--add-module=/root/nginx-1.13.8/mytesthttp
執行nginx目錄下configure生成makefile,--prefix=/root/nginx-1.13.8/bin提供nginx的安裝路徑,隨便設置成你自己的想安裝到的目錄都是可以的。
./configure \
--prefix=/root/nginx-1.13.8/bin \
--user=root\
--group=root\
--add-module=/root/nginx-1.13.8/mytesthttp \
--with-http_stub_status_module \
--with-http_flv_module \
--with-http_stub_status_module \
--with-http_gzip_static_module \
--with-http_gunzip_module \
--with-pcre \
--with-debug
5.配置nginx配置文件nginx.conf。一般nginx的conf文件在安裝路徑目錄下(--prefix提供的那個路徑):
添加內容參考如下:
server {
listen 8017;
server_name localhost;
access_log logs/get.log main;
#access_log off;
location /
{
root html;
index index.html index.htm;
}
location /hello
{
mytest;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html
{
root html;
}
}
訪問示例:
http://192.168.0.153:8017/hello/mytest
整體code參考:(結合文章理解)
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#define PB_SIZE (1024 * 2)
#define CONTENT_TYPE "application/json;charset=GB2312"
static char* ngx_http_mytest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r);
static void ngx_http_read_client_request_body_handler(ngx_http_request_t *r);
static ngx_command_t ngx_http_mytest_commands[] =
{
{
ngx_string("mytest"),
NGX_HTTP_MAIN_CONF | NGX_HTTP_SRV_CONF | NGX_HTTP_LOC_CONF | NGX_HTTP_LMT_CONF | NGX_CONF_NOARGS,
ngx_http_mytest,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL
},
ngx_null_command
};
static ngx_http_module_t ngx_http_mytest_module_ctx =
{
NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
};
ngx_module_t ngx_http_mytest_module =
{
NGX_MODULE_V1,
&ngx_http_mytest_module_ctx, /* module context */
ngx_http_mytest_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static char *ngx_http_mytest(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_core_loc_conf_t *clcf;
clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
clcf->handler = ngx_http_mytest_handler;
return NGX_CONF_OK;
}
static ngx_int_t ngx_http_mytest_handler(ngx_http_request_t *r)
{
static char uri[PB_SIZE];
static char decode[PB_SIZE];
static char args[PB_SIZE];
char* src;
char* dst;
int status=NGX_HTTP_OK;
//int reply_len=0;
//char *reply=0;
ngx_int_t rc;
ngx_chain_t out;
//post handle
if ((r->method & (NGX_HTTP_POST|NGX_HTTP_HEAD)))
{
//get body
rc = ngx_http_read_client_request_body(r, ngx_http_read_client_request_body_handler);
if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
return rc;
}
return NGX_DONE;
}
//get handle
else if ((r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD)))
{
//get uri
if (r->uri.len>=PB_SIZE)
return NGX_HTTP_NOT_ALLOWED;
ngx_memcpy(uri,r->uri.data,r->uri.len);
uri[r->uri.len]=0;
src = uri;
dst = decode;
ngx_unescape_uri((u_char**)&dst, (u_char**)&src, r->uri.len, 0);
ngx_memcpy(uri,decode,dst - decode);
uri[dst - decode] = '\0';
//get args
if (r->args.len>=PB_SIZE)
return NGX_HTTP_NOT_ALLOWED;
ngx_memcpy(args,r->args.data,r->args.len);
args[r->args.len]=0;
src = args;
dst =decode;
ngx_unescape_uri((u_char**)&dst, (u_char**)&src, r->args.len, 0);
ngx_memcpy(args,decode,dst - decode);
args[dst - decode] = '\0';
//reply=request(uri,args,&status,&reply_len);
ngx_str_t response = ngx_string("Hello World!");
if (status!=NGX_HTTP_OK)
{
return status;
}
ngx_str_t type =ngx_string(CONTENT_TYPE);
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_type = type;
//r->headers_out.content_length_n = reply_len;
r->headers_out.content_length_n = response.len;
ngx_buf_t *b = ngx_create_temp_buf(r->pool, response.len);
if(b == NULL)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Failed to allocate response buffer.");
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
ngx_memcpy(b->pos, response.data, response.len);
b->last = b->pos+response.len;
b->last_buf = 1;
out.buf = b;
out.next = NULL;
rc = ngx_http_send_header(r);
if (rc == NGX_ERROR || rc > NGX_OK || r->header_only)
{
return rc;
}
return ngx_http_output_filter(r, &out);
}
else
{
return NGX_HTTP_NOT_ALLOWED;
}
}
static void ngx_http_read_client_request_body_handler(ngx_http_request_t *r)
{
static char uri[PB_SIZE];
static char decode[PB_SIZE];
char* body = NULL;
int body_size = 0;
char* src;
char* dst;
//char *reply=0;
int status=NGX_HTTP_OK;
//int reply_len=0;
ngx_int_t rc;
ngx_chain_t out;
ngx_chain_t* bufs = r->request_body->bufs;
ngx_buf_t* buf = NULL;
uint8_t* data_buf = NULL;
size_t content_length = 0;
size_t body_length = 0;
//get uri
if (r->uri.len>=PB_SIZE)
{
ngx_http_finalize_request(r,NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
ngx_memcpy(uri,r->uri.data,r->uri.len);
uri[r->uri.len]=0;
src = uri;
dst = decode;
ngx_unescape_uri((u_char**)&dst, (u_char**)&src, r->uri.len, 0);
ngx_memcpy(uri,decode,dst - decode);
uri[dst - decode] = '\0';
//get body
if (r->request_body == NULL)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "reqeust_body:null");
ngx_http_finalize_request(r,NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
if ( r->headers_in.content_length == NULL )
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "r->headers_in.content_length == NULL");
ngx_http_finalize_request(r,NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
content_length = atoi( (char*)(r->headers_in.content_length->value.data) );
data_buf = ( uint8_t* )ngx_palloc( r->pool , content_length + 1 );
size_t buf_length = 0;
while ( bufs )
{
buf = bufs->buf;
bufs = bufs->next;
buf_length = buf->last - buf->pos ;
if( body_length + buf_length > content_length )
{
memcpy( data_buf + body_length, buf->pos, content_length - body_length);
body_length = content_length ;
break;
}
memcpy( data_buf + body_length, buf->pos, buf->last - buf->pos );
body_length += buf->last - buf->pos;
}
if ( body_length )
{
data_buf[body_length] = 0;
}
body = (char *)data_buf;
body_size = body_length;
//int sequence = getSequence(r);
//reply = mypost(uri, body, body_size,sequence,&status, &reply_len);
//ÕâÀïmypostÆäʵ¾ÍÊÇÀ©Õ¹´¦ÀípostÌá½»±íµ¥Êý¾Ýbody£¬¿ÉÒÔ°Ñ´æµ½dbÖÐÖ®ÀàµÄÆäËûÈÎÒâÒµÎñÂß¼
ngx_str_t response = ngx_string("Hello World!");
if(status != NGX_HTTP_OK)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Post failed.");
ngx_http_finalize_request(r,NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
ngx_str_t type =ngx_string(CONTENT_TYPE);
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_type = type;
//r->headers_out.content_length_n = reply_len;
r->headers_out.content_length_n = response.len;
ngx_buf_t *b = ngx_create_temp_buf(r->pool, response.len);
if(b == NULL)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Failed to allocate response buffer.");
ngx_http_finalize_request(r,NGX_HTTP_INTERNAL_SERVER_ERROR);
return;
}
ngx_memcpy(b->pos, response.data, response.len);
b->last = b->pos+response.len;
b->last_buf = 1;
out.buf = b;
out.next = NULL;
rc = ngx_http_send_header(r);
if (rc == NGX_ERROR || rc > NGX_OK || r->header_only)
{
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "Failed to do ngx_http_send_header.");
ngx_http_finalize_request(r,NGX_HTTP_INTERNAL_SERVER_ERROR);
return ;
}
ngx_http_finalize_request(r,ngx_http_output_filter(r, &out));
return;
}