nginx 編寫簡單HTTP模組以及nginx http handler的hello world示例編寫
編寫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;
}
相關文章
- Pingora:替代Nginx、Rust編寫的HTTP伺服器GoNginxRustHTTP伺服器
- 解剖Nginx·模組開發篇(3)ngx_http_hello_world_module 模組的基本函式實現NginxHTTP函式
- 解剖Nginx·模組開發篇(2)ngx_http_hello_world_module 模組基本結構定義NginxHTTP
- 使用Golang語言編寫Hello World Web應用GolangWeb
- 解剖Nginx·模組開發篇(1)跑起你的 Hello World 模組!Nginx
- nginx學習筆記(2):開發一個簡單的HTTP模組Nginx筆記HTTP
- Nginx的ngx_http_fastcgi_module模組NginxHTTPAST
- 用php編寫我的第一段程式碼:hello worldPHP
- Viper 微服務框架 編寫一個hello world 外掛-02微服務框架
- SQL WHERE IN引數化編譯寫法簡單示例SQL編譯
- React入門系列 – 2 編寫第一個Hello world的React程式React
- React入門系列 - 2 編寫第一個Hello world的React程式React
- nginx學習-ngx_http_rewrite_module模組NginxHTTP
- elsa-core—2.Hello World: HTTPHTTP
- javascript編寫一個簡單的編譯器JavaScript編譯
- 編寫最簡單的核心:HelloWorld
- Nginx的 http_image_filter_module 模組使用說明NginxHTTPFilter
- 深入學習用 Go 編寫 HTTP 伺服器GoHTTP伺服器
- 編寫一個非常簡單的 JavaScript 編輯器JavaScript
- 案例十:shell編寫nginx服務啟動程式Nginx
- 編寫Node原生模組
- C編寫的簡單密碼程式密碼
- Nginx的HTTP模組與Stream模組:區別與應用場景NginxHTTP
- Nginx編譯安裝第三方模組http_substitutions_filter_module2222Nginx編譯HTTPFilter
- 使用 HTTP 模組執行 URL 重寫HTTP
- CentOS 下重新編譯 nginx 新增模組CentOS編譯Nginx
- Nginx簡單的負載均衡配置示例Nginx負載
- 細述:nginx http核心模組提供的變數和解釋NginxHTTP變數
- 在docker中寫個Hello WorldDocker
- Nginx 動態模組 nginx-mod-http-image-filter 載入失敗解決NginxHTTPFilter
- Mocker-api 一款基於 python 編寫的極簡 http server 工具MockAPIPythonHTTPServer
- 編寫一個簡單的智慧合約
- 編寫一個簡單的babel外掛Babel
- 編寫一個簡單的JavaScript模板引擎JavaScript
- python編寫簡單的setup.pyPython
- 編寫簡單的Java程式碼:HelloWoridJava
- 如何編寫python模組Python
- 【ningoo】編寫Perl模組Go