欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
handler模塊(100%)

相信大家在看了前一章的模塊概述以后,都對nginx的模塊有了一個(gè)基本的認識?;旧献鳛榈谌介_(kāi)發(fā)者最可能開(kāi)發(fā)的就是三種類(lèi)型的模塊,即handler,filter和load-balancer。Handler模塊就是接受來(lái)自客戶(hù)端的請求并產(chǎn)生輸出的模塊。有些地方說(shuō)upstream模塊實(shí)際上也是一種handler模塊,只不過(guò)它產(chǎn)生的內容來(lái)自于從后端服務(wù)器獲取的,而非在本機產(chǎn)生的。

在上一章提到,配置文件中使用location指令可以配置content handler模塊,當Nginx系統啟動(dòng)的時(shí)候,每個(gè)handler模塊都有一次機會(huì )把自己關(guān)聯(lián)到對應的location上。如果有多個(gè)handler模塊都關(guān)聯(lián)了同一個(gè)location,那么實(shí)際上只有一個(gè)handler模塊真正會(huì )起作用。當然大多數情況下,模塊開(kāi)發(fā)人員都會(huì )避免出現這種情況。

handler模塊處理的結果通常有三種情況: 處理成功,處理失?。ㄌ幚淼臅r(shí)候發(fā)生了錯誤)或者是拒絕去處理。在拒絕處理的情況下,這個(gè)location的處理就會(huì )由默認的handler模塊來(lái)進(jìn)行處理。例如,當請求一個(gè)靜態(tài)文件的時(shí)候,如果關(guān)聯(lián)到這個(gè)location上的一個(gè)handler模塊拒絕處理,就會(huì )由默認的ngx_http_static_module模塊進(jìn)行處理,該模塊是一個(gè)典型的handler模塊。

本章主要講述的是如何編寫(xiě)handler模塊,在研究handler模塊編寫(xiě)之前先來(lái)了解一下模塊的一些基本數據結構。

模塊的基本結構?

在這一節我們將會(huì )對通常的模塊開(kāi)發(fā)過(guò)程中,每個(gè)模塊所包含的一些常用的部分進(jìn)行說(shuō)明。這些部分有些是必須的,有些不是必須的。同時(shí)這里所列出的這些東西對于其他類(lèi)型的模塊,例如filter模塊等也都是相同的。

模塊配置結構?

基本上每個(gè)模塊都會(huì )提供一些配置指令,以便于用戶(hù)可以通過(guò)配置來(lái)控制該模塊的行為。那么這些配置信息怎么存儲呢?那就需要定義該模塊的配置結構來(lái)進(jìn)行存儲。

大家都知道Nginx的配置信息分成了幾個(gè)作用域(scope,有時(shí)也稱(chēng)作上下文),這就是main, server, 以及l(fā)ocation。同樣的每個(gè)模塊提供的配置指令也可以出現在這幾個(gè)作用域里。那對于這三個(gè)作用域的配置信息,每個(gè)模塊就需要定義三個(gè)不同的數據結構去進(jìn)行存儲。當然,不是每個(gè)模塊都會(huì )在這三個(gè)作用域都提供配置指令的。那么也就不一定每個(gè)模塊都需要定義三個(gè)數據結構去存儲這些配置信息了。視模塊的實(shí)現而言,需要幾個(gè)就定義幾個(gè)。

有一點(diǎn)需要特別注意的就是,在模塊的開(kāi)發(fā)過(guò)程中,我們最好使用nginx原有的命名習慣。這樣跟原代碼的契合度更高,看起來(lái)也更舒服。

對于模塊配置信息的定義,命名習慣是ngx_http_<module name>_(main|srv|loc)_conf_t。這里有個(gè)例子,就是從我們后面將要展示給大家的hello module中截取的。

typedef struct{    ngx_str_t hello_string;    ngx_int_t hello_counter;}ngx_http_hello_loc_conf_t;

模塊配置指令?

一個(gè)模塊的配置指令是定義在一個(gè)靜態(tài)數組中的。同樣地,我們來(lái)看一下從hello module中截取的模塊配置指令的定義。

static ngx_command_t ngx_http_hello_commands[] = {   {        ngx_string("hello_string"),        NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS|NGX_CONF_TAKE1,        ngx_http_hello_string,        NGX_HTTP_LOC_CONF_OFFSET,        offsetof(ngx_http_hello_loc_conf_t, hello_string),        NULL },    {        ngx_string("hello_counter"),        NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,        ngx_http_hello_counter,        NGX_HTTP_LOC_CONF_OFFSET,        offsetof(ngx_http_hello_loc_conf_t, hello_counter),        NULL },    ngx_null_command};

其實(shí)看這個(gè)定義,就基本能看出來(lái)一些信息。例如,我們是定義了兩個(gè)配置指令,一個(gè)是叫hello_string,可以接受一個(gè)參數,或者是沒(méi)有參數。另外一個(gè)命令是hello_counter,接受一個(gè)NGX_CONF_FLAG類(lèi)型的參數。除此之外,似乎看起來(lái)有點(diǎn)迷惑。沒(méi)有關(guān)系,我們來(lái)詳細看一下ngx_command_t,一旦我們了解這個(gè)結構的詳細信息,那么我相信上述這個(gè)定義所表達的所有信息就不言自明了。

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:配置指令的名稱(chēng)。
type:該配置的類(lèi)型,其實(shí)更準確一點(diǎn)說(shuō),是該配置指令屬性的集合。nginx提供了很多預定義的屬性值(一些宏定義),通過(guò)邏輯或運算符可組合在一起,形成對這個(gè)配置指令的詳細的說(shuō)明。下面列出可在這里使用的預定義屬性值及說(shuō)明。
  • NGX_CONF_NOARGS:配置指令不接受任何參數。

  • NGX_CONF_TAKE1:配置指令接受1個(gè)參數。

  • NGX_CONF_TAKE2:配置指令接受2個(gè)參數。

  • NGX_CONF_TAKE3:配置指令接受3個(gè)參數。

  • NGX_CONF_TAKE4:配置指令接受4個(gè)參數。

  • NGX_CONF_TAKE5:配置指令接受5個(gè)參數。

  • NGX_CONF_TAKE6:配置指令接受6個(gè)參數。

  • NGX_CONF_TAKE7:配置指令接受7個(gè)參數。

    可以組合多個(gè)屬性,比如一個(gè)指令即可以不填參數,也可以接受1個(gè)或者2個(gè)參數。那么就是NGX_CONF_NOARGS|NGX_CONF_TAKE1|NGX_CONF_TAKE2。如果寫(xiě)上面三個(gè)屬性在一起,你覺(jué)得麻煩,那么沒(méi)有關(guān)系,nginx提供了一些定義,使用起來(lái)更簡(jiǎn)潔。

  • NGX_CONF_TAKE12:配置指令接受1個(gè)或者2個(gè)參數。

  • NGX_CONF_TAKE13:配置指令接受1個(gè)或者3個(gè)參數。

  • NGX_CONF_TAKE23:配置指令接受2個(gè)或者3個(gè)參數。

  • NGX_CONF_TAKE123:配置指令接受1個(gè)或者2個(gè)或者3參數。

  • NGX_CONF_TAKE1234:配置指令接受1個(gè)或者2個(gè)或者3個(gè)或者4個(gè)參數。

  • NGX_CONF_1MORE:配置指令接受至少一個(gè)參數。

  • NGX_CONF_2MORE:配置指令接受至少兩個(gè)參數。

  • NGX_CONF_MULTI: 配置指令可以接受多個(gè)參數,即個(gè)數不定。

  • NGX_CONF_BLOCK:配置指令可以接受的值是一個(gè)配置信息塊。也就是一對大括號括起來(lái)的內容。里面可以再包括很多的配置指令。比如常見(jiàn)的server指令就是這個(gè)屬性的。

  • NGX_CONF_FLAG:配置指令可以接受的值是”on”或者”off”,最終會(huì )被轉成bool值。

  • NGX_CONF_ANY:配置指令可以接受的任意的參數值。一個(gè)或者多個(gè),或者”on”或者”off”,或者是配置塊。

    最后要說(shuō)明的是,無(wú)論如何,nginx的配置指令的參數個(gè)數不可以超過(guò)NGX_CONF_MAX_ARGS個(gè)。目前這個(gè)值被定義為8,也就是不能超過(guò)8個(gè)參數值。

    下面介紹一組說(shuō)明配置指令可以出現的位置的屬性。

  • NGX_DIRECT_CONF:可以出現在配置文件中最外層。例如已經(jīng)提供的配置指令daemon,master_process等。

  • NGX_MAIN_CONF: http、mail、events、error_log等。

  • NGX_ANY_CONF: 該配置指令可以出現在任意配置級別上。

    對于我們編寫(xiě)的大多數模塊而言,都是在處理http相關(guān)的事情,也就是所謂的都是NGX_HTTP_MODULE,對于這樣類(lèi)型的模塊,其配置可能出現的位置也是分為直接出現在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語(yǔ)句所在的block中。

  • NGX_HTTP_LMT_CONF: 可以出現在http里面的limit_except指令的block中。

  • NGX_HTTP_LIF_CONF: 可以出現在http server塊里面的location配置指令里的if語(yǔ)句所在的block中。

set:這是一個(gè)函數指針,當nginx在解析配置的時(shí)候,如果遇到這個(gè)配置指令,將會(huì )把讀取到的值傳遞給這個(gè)函數進(jìn)行分解處理。因為具體每個(gè)配置指令的值如何處理,只有定義這個(gè)配置指令的人是最清楚的。來(lái)看一下這個(gè)函數指針要求的函數原型。
char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

先看該函數的返回值,處理成功時(shí),返回NGX_OK,否則返回NGX_CONF_ERROR或者是一個(gè)自定義的錯誤信息的字符串。

再看一下這個(gè)函數被調用的時(shí)候,傳入的三個(gè)參數。

  • cf: 該參數里面保存從配置文件讀取到的原始字符串以及相關(guān)的一些信息。特別注意的是這個(gè)參數的args字段是一個(gè)ngx_str_t類(lèi)型的數組,該數組的首個(gè)元素是這個(gè)配置指令本身,第二個(gè)元素是指令的第一個(gè)參數,第三個(gè)元素是第二個(gè)參數,依次類(lèi)推。
  • cmd: 這個(gè)配置指令對應的ngx_command_t結構。
  • conf: 就是定義的存儲這個(gè)配置值的結構體,比如在上面展示的那個(gè)ngx_http_hello_loc_conf_t。當解析這個(gè)hello_string變量的時(shí)候,傳入的conf就指向一個(gè)ngx_http_hello_loc_conf_t類(lèi)型的變量。用戶(hù)在處理的時(shí)候可以使用類(lèi)型轉換,轉換成自己知道的類(lèi)型,再進(jìn)行字段的賦值。

為了更加方便的實(shí)現對配置指令參數的讀取,nginx已經(jīng)默認提供了對一些標準類(lèi)型的參數進(jìn)行讀取的函數,可以直接賦值給set字段使用。下面來(lái)看一下這些已經(jīng)實(shí)現的set類(lèi)型函數。

  • ngx_conf_set_flag_slot: 讀取NGX_CONF_FLAG類(lèi)型的參數。
  • ngx_conf_set_str_slot:讀取字符串類(lèi)型的參數。
  • ngx_conf_set_str_array_slot: 讀取字符串數組類(lèi)型的參數。
  • ngx_conf_set_keyval_slot: 讀取鍵值對類(lèi)型的參數。
  • ngx_conf_set_num_slot: 讀取整數類(lèi)型(有符號整數ngx_int_t)的參數。
  • ngx_conf_set_size_slot:讀取size_t類(lèi)型的參數,也就是無(wú)符號數。
  • ngx_conf_set_off_slot: 讀取off_t類(lèi)型的參數。
  • ngx_conf_set_msec_slot: 讀取毫秒值類(lèi)型的參數。
  • ngx_conf_set_sec_slot: 讀取秒值類(lèi)型的參數。
  • ngx_conf_set_bufs_slot: 讀取的參數值是2個(gè),一個(gè)是buf的個(gè)數,一個(gè)是buf的大小。例如: output_buffers 1 128k;
  • ngx_conf_set_enum_slot: 讀取枚舉類(lèi)型的參數,將其轉換成整數ngx_uint_t類(lèi)型。
  • ngx_conf_set_bitmask_slot: 讀取參數的值,并將這些參數的值以bit位的形式存儲。例如:HttpDavModule模塊的dav_methods指令。
conf:該字段被NGX_HTTP_MODULE類(lèi)型模塊所用 (我們編寫(xiě)的基本上都是NGX_HTTP_MOUDLE,只有一些nginx核心模塊是非NGX_HTTP_MODULE),該字段指定當前配置項存儲的內存位置。實(shí)際上是使用哪個(gè)內存池的問(wèn)題。因為http模塊對所有http模塊所要保存的配置信息,劃分了main, server和location三個(gè)地方進(jìn)行存儲,每個(gè)地方都有一個(gè)內存池用來(lái)分配存儲這些信息的內存。這里可能的值為 NGX_HTTP_MAIN_CONF_OFFSET、NGX_HTTP_SRV_CONF_OFFSET或NGX_HTTP_LOC_CONF_OFFSET。當然也可以直接置為0,就是NGX_HTTP_MAIN_CONF_OFFSET。
offset:指定該配置項值的精確存放位置,一般指定為某一個(gè)結構體變量的字段偏移。因為對于配置信息的存儲,一般我們都是定義個(gè)結構體來(lái)存儲的。那么比如我們定義了一個(gè)結構體A,該項配置的值需要存儲到該結構體的b字段。那么在這里就可以填寫(xiě)為offsetof(A, b)。對于有些配置項,它的值不需要保存或者是需要保存到更為復雜的結構中時(shí),這里可以設置為0。
post:該字段存儲一個(gè)指針??梢灾赶蛉魏我粋€(gè)在讀取配置過(guò)程中需要的數據,以便于進(jìn)行配置讀取的處理。大多數時(shí)候,都不需要,所以簡(jiǎn)單地設為0即可。

看到這里,應該就比較清楚了。ngx_http_hello_commands這個(gè)數組每5個(gè)元素為一組,用來(lái)描述一個(gè)配置項的所有情況。那么如果有多個(gè)配置項,只要按照需要再增加5個(gè)對應的元素對新的配置項進(jìn)行說(shuō)明。

需要注意的是,就是在ngx_http_hello_commands這個(gè)數組定義的最后,都要加一個(gè)ngx_null_command作為結尾。

模塊上下文結構?

這是一個(gè)ngx_http_module_t類(lèi)型的靜態(tài)變量。這個(gè)變量實(shí)際上是提供一組回調函數指針,這些函數有在創(chuàng )建存儲配置信息的對象的函數,也有在創(chuàng )建前和創(chuàng )建后會(huì )調用的函數。這些函數都將被nginx在合適的時(shí)間進(jìn)行調用。

typedef struct {    ngx_int_t   (*preconfiguration)(ngx_conf_t *cf);    ngx_int_t   (*postconfiguration)(ngx_conf_t *cf);    void       *(*create_main_conf)(ngx_conf_t *cf);    char       *(*init_main_conf)(ngx_conf_t *cf, void *conf);    void       *(*create_srv_conf)(ngx_conf_t *cf);    char       *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);    void       *(*create_loc_conf)(ngx_conf_t *cf);    char       *(*merge_loc_conf)(ngx_conf_t *cf, void *prev, void *conf);} ngx_http_module_t;
preconfiguration:
 在創(chuàng )建和讀取該模塊的配置信息之前被調用。
postconfiguration:
 在創(chuàng )建和讀取該模塊的配置信息之后被調用。
create_main_conf:
 調用該函數創(chuàng )建本模塊位于http block的配置信息存儲結構。該函數成功的時(shí)候,返回創(chuàng )建的配置對象。失敗的話(huà),返回NULL。
init_main_conf:調用該函數初始化本模塊位于http block的配置信息存儲結構。該函數成功的時(shí)候,返回NGX_CONF_OK。失敗的話(huà),返回NGX_CONF_ERROR或錯誤字符串。
create_srv_conf:
 調用該函數創(chuàng )建本模塊位于http server block的配置信息存儲結構,每個(gè)server block會(huì )創(chuàng )建一個(gè)。該函數成功的時(shí)候,返回創(chuàng )建的配置對象。失敗的話(huà),返回NULL。
merge_srv_conf:因為有些配置指令既可以出現在http block,也可以出現在http server block中。那么遇到這種情況,每個(gè)server都會(huì )有自己存儲結構來(lái)存儲該server的配置,但是在這種情況下http block中的配置與server block中的配置信息發(fā)生沖突的時(shí)候,就需要調用此函數進(jìn)行合并,該函數并非必須提供,當預計到絕對不會(huì )發(fā)生需要合并的情況的時(shí)候,就無(wú)需提供。當然為了安全起見(jiàn)還是建議提供。該函數執行成功的時(shí)候,返回NGX_CONF_OK。失敗的話(huà),返回NGX_CONF_ERROR或錯誤字符串。
create_loc_conf:
 調用該函數創(chuàng )建本模塊位于location block的配置信息存儲結構。每個(gè)在配置中指明的location創(chuàng )建一個(gè)。該函數執行成功,返回創(chuàng )建的配置對象。失敗的話(huà),返回NULL。
merge_loc_conf:與merge_srv_conf類(lèi)似,這個(gè)也是進(jìn)行配置值合并的地方。該函數成功的時(shí)候,返回NGX_CONF_OK。失敗的話(huà),返回NGX_CONF_ERROR或錯誤字符串。

Nginx里面的配置信息都是上下一層層的嵌套的,對于具體某個(gè)location的話(huà),對于同一個(gè)配置,如果當前層次沒(méi)有定義,那么就使用上層的配置,否則使用當前層次的配置。

這些配置信息一般默認都應該設為一個(gè)未初始化的值,針對這個(gè)需求,Nginx定義了一系列的宏定義來(lái)代表各種配置所對應數據類(lèi)型的未初始化值,如下:

#define NGX_CONF_UNSET       -1#define NGX_CONF_UNSET_UINT  (ngx_uint_t) -1#define NGX_CONF_UNSET_PTR   (void *) -1#define NGX_CONF_UNSET_SIZE  (size_t) -1#define NGX_CONF_UNSET_MSEC  (ngx_msec_t) -1

又因為對于配置項的合并,邏輯都類(lèi)似,也就是前面已經(jīng)說(shuō)過(guò)的,如果在本層次已經(jīng)配置了,也就是配置項的值已經(jīng)被讀取進(jìn)來(lái)了(那么這些配置項的值就不會(huì )等于上面已經(jīng)定義的那些UNSET的值),就使用本層次的值作為定義合并的結果,否則,使用上層的值,如果上層的值也是這些UNSET類(lèi)的值,那就賦值為默認值,否則就使用上層的值作為合并的結果。對于這樣類(lèi)似的操作,Nginx定義了一些宏操作來(lái)做這些事情,我們來(lái)看其中一個(gè)的定義。

#define ngx_conf_merge_uint_value(conf, prev, default)                           if (conf == NGX_CONF_UNSET_UINT) {                                               conf = (prev == NGX_CONF_UNSET_UINT) ? default : prev;                   }

顯而易見(jiàn),這個(gè)邏輯確實(shí)比較簡(jiǎn)單,所以其它的宏定義也類(lèi)似,我們就列具其中的一部分吧。

ngx_conf_merge_valuengx_conf_merge_ptr_valuengx_conf_merge_uint_valuengx_conf_merge_msec_valuengx_conf_merge_sec_value

等等。

下面來(lái)看一下hello模塊的模塊上下文的定義,加深一下印象。

static ngx_http_module_t ngx_http_hello_module_ctx = {    NULL,                          /* preconfiguration */    ngx_http_hello_init,           /* postconfiguration */    NULL,                          /* create main configuration */    NULL,                          /* init main configuration */    NULL,                          /* create server configuration */    NULL,                          /* merge server configuration */    ngx_http_hello_create_loc_conf, /* create location configuration */    NULL                        /* merge location configuration */};

注意:這里并沒(méi)有提供merge_loc_conf函數,因為我們這個(gè)模塊的配置指令已經(jīng)確定只出現在NGX_HTTP_LOC_CONF中這一個(gè)層次上,不會(huì )發(fā)生需要合并的情況。

模塊的定義?

對于開(kāi)發(fā)一個(gè)模塊來(lái)說(shuō),我們都需要定義一個(gè)ngx_module_t類(lèi)型的變量來(lái)說(shuō)明這個(gè)模塊本身的信息,從某種意義上來(lái)說(shuō),這是這個(gè)模塊最重要的一個(gè)信息,它告訴了nginx這個(gè)模塊的一些信息,上面定義的配置信息,還有模塊上下文信息,都是通過(guò)這個(gè)結構來(lái)告訴nginx系統的,也就是加載模塊的上層代碼,都需要通過(guò)定義的這個(gè)結構,來(lái)獲取這些信息。

我們先來(lái)看下ngx_module_t的定義

typedef struct ngx_module_s      ngx_module_t;struct ngx_module_s {    ngx_uint_t            ctx_index;    ngx_uint_t            index;    ngx_uint_t            spare0;    ngx_uint_t            spare1;    ngx_uint_t            abi_compatibility;    ngx_uint_t            major_version;    ngx_uint_t            minor_version;    void                 *ctx;    ngx_command_t        *commands;    ngx_uint_t            type;    ngx_int_t           (*init_master)(ngx_log_t *log);    ngx_int_t           (*init_module)(ngx_cycle_t *cycle);    ngx_int_t           (*init_process)(ngx_cycle_t *cycle);    ngx_int_t           (*init_thread)(ngx_cycle_t *cycle);    void                (*exit_thread)(ngx_cycle_t *cycle);    void                (*exit_process)(ngx_cycle_t *cycle);    void                (*exit_master)(ngx_cycle_t *cycle);    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;};#define NGX_NUMBER_MAJOR  3#define NGX_NUMBER_MINOR  1#define NGX_MODULE_V1          0, 0, 0, 0,                                  NGX_DSO_ABI_COMPATIBILITY, NGX_NUMBER_MAJOR, NGX_NUMBER_MINOR#define NGX_MODULE_V1_PADDING  0, 0, 0, 0, 0, 0, 0, 0

再看一下hello模塊的模塊定義。

ngx_module_t ngx_http_hello_module = {    NGX_MODULE_V1,    &ngx_http_hello_module_ctx,    /* module context */    ngx_http_hello_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,當nginx在創(chuàng )建進(jìn)程線(xiàn)程或者結束進(jìn)程線(xiàn)程時(shí)進(jìn)行調用。但大多數模塊在這些時(shí)刻并不需要做什么,所以都簡(jiǎn)單賦值為NULL。

handler模塊的基本結構?

除了上一節介紹的模塊的基本結構以外,handler模塊必須提供一個(gè)真正的處理函數,這個(gè)函數負責對來(lái)自客戶(hù)端請求的真正處理。這個(gè)函數的處理,既可以選擇自己直接生成內容,也可以選擇拒絕處理,由后續的handler去進(jìn)行處理,或者是選擇丟給后續的filter進(jìn)行處理。來(lái)看一下這個(gè)函數的原型申明。

typedef ngx_int_t (*ngx_http_handler_pt)(ngx_http_request_t *r);

r是http請求。里面包含請求所有的信息,這里不詳細說(shuō)明了,可以參考別的章節的介紹。該函數處理成功返回NGX_OK,處理發(fā)生錯誤返回NGX_ERROR,拒絕處理(留給后續的handler進(jìn)行處理)返回NGX_DECLINE。返回NGX_OK也就代表給客戶(hù)端的響應已經(jīng)生成好了,否則返回NGX_ERROR就發(fā)生錯誤了。

handler模塊的掛載?

handler模塊真正的處理函數通過(guò)兩種方式掛載到處理過(guò)程中,一種方式就是按處理階段掛載;另外一種掛載方式就是按需掛載。

按處理階段掛載?

為了更精細地控制對于客戶(hù)端請求的處理過(guò)程,nginx把這個(gè)處理過(guò)程劃分成了11個(gè)階段。他們從前到后,依次列舉如下:

NGX_HTTP_POST_READ_PHASE:
 讀取請求內容階段
NGX_HTTP_SERVER_REWRITE_PHASE:
 Server請求地址重寫(xiě)階段
NGX_HTTP_FIND_CONFIG_PHASE:
 配置查找階段:
NGX_HTTP_REWRITE_PHASE:
 Location請求地址重寫(xiě)階段
NGX_HTTP_POST_REWRITE_PHASE:
 請求地址重寫(xiě)提交階段
NGX_HTTP_PREACCESS_PHASE:
 訪(fǎng)問(wèn)權限檢查準備階段
NGX_HTTP_ACCESS_PHASE:
 訪(fǎng)問(wèn)權限檢查階段
NGX_HTTP_POST_ACCESS_PHASE:
 訪(fǎng)問(wèn)權限檢查提交階段
NGX_HTTP_TRY_FILES_PHASE:
 配置項try_files處理階段
NGX_HTTP_CONTENT_PHASE:
 內容產(chǎn)生階段
NGX_HTTP_LOG_PHASE:
 日志模塊處理階段

一般情況下,我們自定義的模塊,大多數是掛載在NGX_HTTP_CONTENT_PHASE階段的。掛載的動(dòng)作一般是在模塊上下文調用的postconfiguration函數中。

注意:有幾個(gè)階段是特例,它不調用掛載地任何的handler,也就是你就不用掛載到這幾個(gè)階段了:

  • NGX_HTTP_FIND_CONFIG_PHASE
  • NGX_HTTP_POST_ACCESS_PHASE
  • NGX_HTTP_POST_REWRITE_PHASE
  • NGX_HTTP_TRY_FILES_PHASE

所以其實(shí)真正是有7個(gè)phase你可以去掛載handler。

掛載的代碼如下(摘自hello module):

static ngx_int_tngx_http_hello_init(ngx_conf_t *cf){        ngx_http_handler_pt        *h;        ngx_http_core_main_conf_t  *cmcf;        cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);        h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);        if (h == NULL) {                return NGX_ERROR;        }        *h = ngx_http_hello_handler;        return NGX_OK;}

使用這種方式掛載的handler也被稱(chēng)為 content phase handlers。

按需掛載?

以這種方式掛載的handler也被稱(chēng)為 content handler。

當一個(gè)請求進(jìn)來(lái)以后,nginx從NGX_HTTP_POST_READ_PHASE階段開(kāi)始依次執行每個(gè)階段中所有handler。執行到 NGX_HTTP_CONTENT_PHASE階段的時(shí)候,如果這個(gè)location有一個(gè)對應的content handler模塊,那么就去執行這個(gè)content handler模塊真正的處理函數。否則繼續依次執行NGX_HTTP_CONTENT_PHASE階段中所有content phase handlers,直到某個(gè)函數處理返回NGX_OK或者NGX_ERROR。

換句話(huà)說(shuō),當某個(gè)location處理到NGX_HTTP_CONTENT_PHASE階段時(shí),如果有content handler模塊,那么NGX_HTTP_CONTENT_PHASE掛載的所有content phase handlers都不會(huì )被執行了。

但是使用這個(gè)方法掛載上去的handler有一個(gè)特點(diǎn)是必須在NGX_HTTP_CONTENT_PHASE階段才能執行到。如果你想自己的handler在更早的階段執行,那就不要使用這種掛載方式。

那么在什么情況會(huì )使用這種方式來(lái)掛載呢?一般情況下,某個(gè)模塊對某個(gè)location進(jìn)行了處理以后,發(fā)現符合自己處理的邏輯,而且也沒(méi)有必要再調用NGX_HTTP_CONTENT_PHASE階段的其它handler進(jìn)行處理的時(shí)候,就動(dòng)態(tài)掛載上這個(gè)handler。

下面來(lái)看一下使用這種掛載方式的具體例子(摘自Emiller’s Guide To Nginx Module Development)。

static char *ngx_http_circle_gif(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_circle_gif_handler;        return NGX_CONF_OK;}

handler的編寫(xiě)步驟?

好,到了這里,讓我們稍微整理一下思路,回顧一下實(shí)現一個(gè)handler的步驟:

  1. 編寫(xiě)模塊基本結構。包括模塊的定義,模塊上下文結構,模塊的配置結構等。
  2. 實(shí)現handler的掛載函數。根據模塊的需求選擇正確的掛載方式。
  3. 編寫(xiě)handler處理函數。模塊的功能主要通過(guò)這個(gè)函數來(lái)完成。

看起來(lái)不是那么難,對吧?還是那句老話(huà),世上無(wú)難事,只怕有心人! 現在我們來(lái)完整的分析前面提到的hello handler module示例的功能和代碼。

示例: hello handler 模塊?

在前面已經(jīng)看到了這個(gè)hello handler module的部分重要的結構。該模塊提供了2個(gè)配置指令,僅可以出現在location指令的作用域中。這兩個(gè)指令是hello_string, 該指令接受一個(gè)參數來(lái)設置顯示的字符串。如果沒(méi)有跟參數,那么就使用默認的字符串作為響應字符串。

另一個(gè)指令是hello_counter,如果設置為on,則會(huì )在響應的字符串后面追加Visited Times:的字樣,以統計請求的次數。

這里有兩點(diǎn)注意一下:

  1. 對于flag類(lèi)型的配置指令,當值為off的時(shí)候,使用ngx_conf_set_flag_slot函數,會(huì )轉化為0,為on,則轉化為非0。
  2. 另外一個(gè)是,我提供了merge_loc_conf函數,但是卻沒(méi)有設置到模塊的上下文定義中。這樣有一個(gè)缺點(diǎn),就是如果一個(gè)指令沒(méi)有出現在配置文件中的時(shí)候,配置信息中的值,將永遠會(huì )保持在create_loc_conf中的初始化的值。那如果,在類(lèi)似create_loc_conf這樣的函數中,對創(chuàng )建出來(lái)的配置信息的值,沒(méi)有設置為合理的值的話(huà),后面用戶(hù)又沒(méi)有配置,就會(huì )出現問(wèn)題。

下面來(lái)完整的給出ngx_http_hello_module模塊的完整代碼。

#include <ngx_config.h>#include <ngx_core.h>#include <ngx_http.h>typedef struct{        ngx_str_t hello_string;        ngx_int_t hello_counter;}ngx_http_hello_loc_conf_t;static ngx_int_t ngx_http_hello_init(ngx_conf_t *cf);static void *ngx_http_hello_create_loc_conf(ngx_conf_t *cf);static char *ngx_http_hello_string(ngx_conf_t *cf, ngx_command_t *cmd,        void *conf);static char *ngx_http_hello_counter(ngx_conf_t *cf, ngx_command_t *cmd,        void *conf);static ngx_command_t ngx_http_hello_commands[] = {   {                ngx_string("hello_string"),                NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS|NGX_CONF_TAKE1,                ngx_http_hello_string,                NGX_HTTP_LOC_CONF_OFFSET,                offsetof(ngx_http_hello_loc_conf_t, hello_string),                NULL },        {                ngx_string("hello_counter"),                NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,                ngx_http_hello_counter,                NGX_HTTP_LOC_CONF_OFFSET,                offsetof(ngx_http_hello_loc_conf_t, hello_counter),                NULL },        ngx_null_command};/*static u_char ngx_hello_default_string[] = "Default String: Hello, world!";*/static int ngx_hello_visited_times = 0;static ngx_http_module_t ngx_http_hello_module_ctx = {        NULL,                          /* preconfiguration */        ngx_http_hello_init,           /* postconfiguration */        NULL,                          /* create main configuration */        NULL,                          /* init main configuration */        NULL,                          /* create server configuration */        NULL,                          /* merge server configuration */        ngx_http_hello_create_loc_conf, /* create location configuration */        NULL                            /* merge location configuration */};ngx_module_t ngx_http_hello_module = {        NGX_MODULE_V1,        &ngx_http_hello_module_ctx,    /* module context */        ngx_http_hello_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 ngx_int_tngx_http_hello_handler(ngx_http_request_t *r){        ngx_int_t    rc;        ngx_buf_t   *b;        ngx_chain_t  out;        ngx_http_hello_loc_conf_t* my_conf;        u_char ngx_hello_string[1024] = {0};        ngx_uint_t content_length = 0;        ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, "ngx_http_hello_handler is called!");        my_conf = ngx_http_get_module_loc_conf(r, ngx_http_hello_module);        if (my_conf->hello_string.len == 0 )        {                ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, "hello_string is empty!");                return NGX_DECLINED;        }        if (my_conf->hello_counter == NGX_CONF_UNSET                || my_conf->hello_counter == 0)        {                ngx_sprintf(ngx_hello_string, "%s", my_conf->hello_string.data);        }        else        {                ngx_sprintf(ngx_hello_string, "%s Visited Times:%d", my_conf->hello_string.data,                        ++ngx_hello_visited_times);        }        ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, "hello_string:%s", ngx_hello_string);        content_length = ngx_strlen(ngx_hello_string);        /* we response to 'GET' and 'HEAD' requests only */        if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {                return NGX_HTTP_NOT_ALLOWED;        }        /* discard request body, since we don't need it here */        rc = ngx_http_discard_request_body(r);        if (rc != NGX_OK) {                return rc;        }        /* set the 'Content-type' header */        /*         *r->headers_out.content_type.len = sizeof("text/html") - 1;         *r->headers_out.content_type.data = (u_char *)"text/html";         */        ngx_str_set(&r->headers_out.content_type, "text/html");        /* send the header only, if the request type is http 'HEAD' */        if (r->method == NGX_HTTP_HEAD) {                r->headers_out.status = NGX_HTTP_OK;                r->headers_out.content_length_n = content_length;                return ngx_http_send_header(r);        }        /* allocate a buffer for your response body */        b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));        if (b == NULL) {                return NGX_HTTP_INTERNAL_SERVER_ERROR;        }        /* attach this buffer to the buffer chain */        out.buf = b;        out.next = NULL;        /* adjust the pointers of the buffer */        b->pos = ngx_hello_string;        b->last = ngx_hello_string + content_length;        b->memory = 1;    /* this buffer is in memory */        b->last_buf = 1;  /* this is the last buffer in the buffer chain */        /* set the status line */        r->headers_out.status = NGX_HTTP_OK;        r->headers_out.content_length_n = content_length;        /* send the headers of your response */        rc = ngx_http_send_header(r);        if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {                return rc;        }        /* send the buffer chain of your response */        return ngx_http_output_filter(r, &out);}static void *ngx_http_hello_create_loc_conf(ngx_conf_t *cf){        ngx_http_hello_loc_conf_t* local_conf = NULL;        local_conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_hello_loc_conf_t));        if (local_conf == NULL)        {                return NULL;        }        ngx_str_null(&local_conf->hello_string);        local_conf->hello_counter = NGX_CONF_UNSET;        return local_conf;}/*static char *ngx_http_hello_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child){        ngx_http_hello_loc_conf_t* prev = parent;        ngx_http_hello_loc_conf_t* conf = child;        ngx_conf_merge_str_value(conf->hello_string, prev->hello_string, ngx_hello_default_string);        ngx_conf_merge_value(conf->hello_counter, prev->hello_counter, 0);        return NGX_CONF_OK;}*/static char *ngx_http_hello_string(ngx_conf_t *cf, ngx_command_t *cmd, void *conf){        ngx_http_hello_loc_conf_t* local_conf;        local_conf = conf;        char* rv = ngx_conf_set_str_slot(cf, cmd, conf);        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "hello_string:%s", local_conf->hello_string.data);        return rv;}static char *ngx_http_hello_counter(ngx_conf_t *cf, ngx_command_t *cmd,        void *conf){        ngx_http_hello_loc_conf_t* local_conf;        local_conf = conf;        char* rv = NULL;        rv = ngx_conf_set_flag_slot(cf, cmd, conf);        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "hello_counter:%d", local_conf->hello_counter);        return rv;}static ngx_int_tngx_http_hello_init(ngx_conf_t *cf){        ngx_http_handler_pt        *h;        ngx_http_core_main_conf_t  *cmcf;        cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);        h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);        if (h == NULL) {                return NGX_ERROR;        }        *h = ngx_http_hello_handler;        return NGX_OK;}

通過(guò)上面一些介紹,我相信大家都能對整個(gè)示例模塊有一個(gè)比較好的理解。唯一可能感覺(jué)有些理解困難的地方在于ngx_http_hello_handler函數里面產(chǎn)生和設置輸出。但其實(shí)大家在本書(shū)的前面的相關(guān)章節都可以看到對ngx_buf_t和request等相關(guān)數據結構的說(shuō)明。如果仔細看了這些地方的說(shuō)明的話(huà),應該對這里代碼的實(shí)現就比較容易理解了。因此,這里不再贅述解釋。

handler模塊的編譯和使用?

模塊的功能開(kāi)發(fā)完了之后,模塊的使用還需要編譯才能夠執行,下面我們來(lái)看下模塊的編譯和使用。

config文件的編寫(xiě)?

對于開(kāi)發(fā)一個(gè)模塊,我們是需要把這個(gè)模塊的C代碼組織到一個(gè)目錄里,同時(shí)需要編寫(xiě)一個(gè)config文件。這個(gè)config文件的內容就是告訴nginx的編譯腳本,該如何進(jìn)行編譯。我們來(lái)看一下hello handler module的config文件的內容,然后再做解釋。

ngx_addon_name=ngx_http_hello_moduleHTTP_MODULES="$HTTP_MODULES ngx_http_hello_module"NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_hello_module.c"

其實(shí)文件很簡(jiǎn)單,幾乎不需要做什么解釋。大家一看都懂了。唯一需要說(shuō)明的是,如果這個(gè)模塊的實(shí)現有多個(gè)源文件,那么都在NGX_ADDON_SRCS這個(gè)變量里,依次寫(xiě)進(jìn)去就可以。

編譯?

對于模塊的編譯,nginx并不像apache一樣,提供了單獨的編譯工具,可以在沒(méi)有apache源代碼的情況下來(lái)單獨編譯一個(gè)模塊的代碼。nginx必須去到nginx的源代碼目錄里,通過(guò)configure指令的參數,來(lái)進(jìn)行編譯。下面看一下hello module的configure指令:

./configure –prefix=/usr/local/nginx-1.3.1 –add-module=/home/jizhao/open_source/book_module

我寫(xiě)的這個(gè)示例模塊的代碼和config文件都放在/home/jizhao/open_source/book_module這個(gè)目錄下。所以一切都很明了,也沒(méi)什么好說(shuō)的了。

使用?

使用一個(gè)模塊需要根據這個(gè)模塊定義的配置指令來(lái)做。比如我們這個(gè)簡(jiǎn)單的hello handler module的使用就很簡(jiǎn)單。在我的測試服務(wù)器的配置文件里,就是在http里面的默認的server里面加入如下的配置:

location /test {                hello_string jizhao;                hello_counter on;}

當我們訪(fǎng)問(wèn)這個(gè)地址的時(shí)候, lynx http://127.0.0.1/test的時(shí)候,就可以看到返回的結果。

jizhao Visited Times:1

當然你訪(fǎng)問(wèn)多次,這個(gè)次數是會(huì )增加的。

更多handler模塊示例分析?

http access module?

該模塊的代碼位于src/http/modules/ngx_http_access_module.c中。該模塊的作用是提供對于特定host的客戶(hù)端的訪(fǎng)問(wèn)控制??梢韵薅ㄌ囟╤ost的客戶(hù)端對于服務(wù)端全部,或者某個(gè)server,或者是某個(gè)location的訪(fǎng)問(wèn)。該模塊的實(shí)現非常簡(jiǎn)單,總共也就只有幾個(gè)函數。

static ngx_int_t ngx_http_access_handler(ngx_http_request_t *r);static ngx_int_t ngx_http_access_inet(ngx_http_request_t *r,        ngx_http_access_loc_conf_t *alcf, in_addr_t addr);#if (NGX_HAVE_INET6)static ngx_int_t ngx_http_access_inet6(ngx_http_request_t *r,        ngx_http_access_loc_conf_t *alcf, u_char *p);#endifstatic ngx_int_t ngx_http_access_found(ngx_http_request_t *r, ngx_uint_t deny);static char *ngx_http_access_rule(ngx_conf_t *cf, ngx_command_t *cmd,        void *conf);static void *ngx_http_access_create_loc_conf(ngx_conf_t *cf);static char *ngx_http_access_merge_loc_conf(ngx_conf_t *cf,        void *parent, void *child);static ngx_int_t ngx_http_access_init(ngx_conf_t *cf);

對于與配置相關(guān)的幾個(gè)函數都不需要做解釋了,需要提一下的是函數ngx_http_access_init,該函數在實(shí)現上把本模塊掛載到了NGX_HTTP_ACCESS_PHASE階段的handler上,從而使自己的被調用時(shí)機發(fā)生在了NGX_HTTP_CONTENT_PHASE等階段前。因為進(jìn)行客戶(hù)端地址的限制檢查,根本不需要等到這么后面。

另外看一下這個(gè)模塊的主處理函數ngx_http_access_handler。這個(gè)函數的邏輯也非常簡(jiǎn)單,主要是根據客戶(hù)端地址的類(lèi)型,來(lái)分別選擇ipv4類(lèi)型的處理函數ngx_http_access_inet還是ipv6類(lèi)型的處理函數ngx_http_access_inet6。

而這個(gè)兩個(gè)處理函數內部也非常簡(jiǎn)單,就是循環(huán)檢查每個(gè)規則,檢查是否有匹配的規則,如果有就返回匹配的結果,如果都沒(méi)有匹配,就默認拒絕。

http static module?

從某種程度上來(lái)說(shuō),此模塊可以算的上是“最正宗的”,“最古老”的content handler。因為本模塊的作用就是讀取磁盤(pán)上的靜態(tài)文件,并把文件內容作為產(chǎn)生的輸出。在Web技術(shù)發(fā)展的早期,只有靜態(tài)頁(yè)面,沒(méi)有服務(wù)端腳本來(lái)動(dòng)態(tài)生成HTML的時(shí)候??峙麻_(kāi)發(fā)個(gè)Web服務(wù)器的時(shí)候,第一個(gè)要開(kāi)發(fā)就是這樣一個(gè)content handler。

http static module的代碼位于src/http/modules/ngx_http_static_module.c中,總共只有兩百多行近三百行??梢哉f(shuō)是非常短小。

我們首先來(lái)看一下該模塊的模塊上下文的定義。

ngx_http_module_t  ngx_http_static_module_ctx = {        NULL,                                  /* preconfiguration */        ngx_http_static_init,                  /* 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 */};

是非常的簡(jiǎn)潔吧,連任何與配置相關(guān)的函數都沒(méi)有。對了,因為該模塊沒(méi)有提供任何配置指令。大家想想也就知道了,這個(gè)模塊做的事情實(shí)在是太簡(jiǎn)單了,也確實(shí)沒(méi)什么好配置的。唯一需要調用的函數是一個(gè)ngx_http_static_init函數。好了,來(lái)看一下這個(gè)函數都干了寫(xiě)什么。

static ngx_int_tngx_http_static_init(ngx_conf_t *cf){        ngx_http_handler_pt        *h;        ngx_http_core_main_conf_t  *cmcf;        cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);        h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);        if (h == NULL) {                return NGX_ERROR;        }        *h = ngx_http_static_handler;        return NGX_OK;}

僅僅是掛載這個(gè)handler到NGX_HTTP_CONTENT_PHASE處理階段。簡(jiǎn)單吧?

下面我們就看一下這個(gè)模塊最核心的處理邏輯所在的ngx_http_static_handler函數。該函數大概占了這個(gè)模塊代碼量的百分之八九十。

static ngx_int_tngx_http_static_handler(ngx_http_request_t *r){        u_char                    *last, *location;        size_t                     root, len;        ngx_str_t                  path;        ngx_int_t                  rc;        ngx_uint_t                 level;        ngx_log_t                 *log;        ngx_buf_t                 *b;        ngx_chain_t                out;        ngx_open_file_info_t       of;        ngx_http_core_loc_conf_t  *clcf;        if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST))) {                return NGX_HTTP_NOT_ALLOWED;        }        if (r->uri.data[r->uri.len - 1] == '/') {                return NGX_DECLINED;        }        log = r->connection->log;        /*         * ngx_http_map_uri_to_path() allocates memory for terminating '\0'         * so we do not need to reserve memory for '/' for possible redirect         */        last = ngx_http_map_uri_to_path(r, &path, &root, 0);        if (last == NULL) {                return NGX_HTTP_INTERNAL_SERVER_ERROR;        }        path.len = last - path.data;        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0,                                   "http filename: \"%s\"", path.data);        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);        ngx_memzero(&of, sizeof(ngx_open_file_info_t));        of.read_ahead = clcf->read_ahead;        of.directio = clcf->directio;        of.valid = clcf->open_file_cache_valid;        of.min_uses = clcf->open_file_cache_min_uses;        of.errors = clcf->open_file_cache_errors;        of.events = clcf->open_file_cache_events;        if (ngx_http_set_disable_symlinks(r, clcf, &path, &of) != NGX_OK) {                return NGX_HTTP_INTERNAL_SERVER_ERROR;        }        if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool)                != NGX_OK)        {                switch (of.err) {                case 0:                        return NGX_HTTP_INTERNAL_SERVER_ERROR;                case NGX_ENOENT:                case NGX_ENOTDIR:                case NGX_ENAMETOOLONG:                        level = NGX_LOG_ERR;                        rc = NGX_HTTP_NOT_FOUND;                        break;                case NGX_EACCES:#if (NGX_HAVE_OPENAT)                case NGX_EMLINK:                case NGX_ELOOP:#endif                        level = NGX_LOG_ERR;                        rc = NGX_HTTP_FORBIDDEN;                        break;                default:                        level = NGX_LOG_CRIT;                        rc = NGX_HTTP_INTERNAL_SERVER_ERROR;                        break;                }                if (rc != NGX_HTTP_NOT_FOUND || clcf->log_not_found) {                        ngx_log_error(level, log, of.err,                                                  "%s \"%s\" failed", of.failed, path.data);                }                return rc;        }        r->root_tested = !r->error_page;        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, log, 0, "http static fd: %d", of.fd);        if (of.is_dir) {                ngx_log_debug0(NGX_LOG_DEBUG_HTTP, log, 0, "http dir");                ngx_http_clear_location(r);                r->headers_out.location = ngx_palloc(r->pool, sizeof(ngx_table_elt_t));                if (r->headers_out.location == NULL) {                        return NGX_HTTP_INTERNAL_SERVER_ERROR;                }                len = r->uri.len + 1;                if (!clcf->alias && clcf->root_lengths == NULL && r->args.len == 0) {                        location = path.data + clcf->root.len;                        *last = '/';                } else {                        if (r->args.len) {                                len += r->args.len + 1;                        }                        location = ngx_pnalloc(r->pool, len);                        if (location == NULL) {                                return NGX_HTTP_INTERNAL_SERVER_ERROR;                        }                        last = ngx_copy(location, r->uri.data, r->uri.len);                        *last = '/';                        if (r->args.len) {                                *++last = '?';                                ngx_memcpy(++last, r->args.data, r->args.len);                        }                }                /*                 * we do not need to set the r->headers_out.location->hash and                 * r->headers_out.location->key fields                 */                r->headers_out.location->value.len = len;                r->headers_out.location->value.data = location;                return NGX_HTTP_MOVED_PERMANENTLY;        }#if !(NGX_WIN32) /* the not regular files are probably Unix specific */        if (!of.is_file) {                ngx_log_error(NGX_LOG_CRIT, log, 0,                                          "\"%s\" is not a regular file", path.data);                return NGX_HTTP_NOT_FOUND;        }#endif        if (r->method & NGX_HTTP_POST) {                return NGX_HTTP_NOT_ALLOWED;        }        rc = ngx_http_discard_request_body(r);        if (rc != NGX_OK) {                return rc;        }        log->action = "sending response to client";        r->headers_out.status = NGX_HTTP_OK;        r->headers_out.content_length_n = of.size;        r->headers_out.last_modified_time = of.mtime;        if (ngx_http_set_content_type(r) != NGX_OK) {                return NGX_HTTP_INTERNAL_SERVER_ERROR;        }        if (r != r->main && of.size == 0) {                return ngx_http_send_header(r);        }        r->allow_ranges = 1;        /* we need to allocate all before the header would be sent */        b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));        if (b == NULL) {                return NGX_HTTP_INTERNAL_SERVER_ERROR;        }        b->file = ngx_pcalloc(r->pool, sizeof(ngx_file_t));        if (b->file == NULL) {                return NGX_HTTP_INTERNAL_SERVER_ERROR;        }        rc = ngx_http_send_header(r);        if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {                return rc;        }        b->file_pos = 0;        b->file_last = of.size;        b->in_file = b->file_last ? 1: 0;        b->last_buf = (r == r->main) ? 1: 0;        b->last_in_chain = 1;        b->file->fd = of.fd;        b->file->name = path;        b->file->log = log;        b->file->directio = of.is_directio;        out.buf = b;        out.next = NULL;        return ngx_http_output_filter(r, &out);}

首先是檢查客戶(hù)端的http請求類(lèi)型(r->method),如果請求類(lèi)型為NGX_HTTP_GET|NGX_HTTP_HEAD|NGX_HTTP_POST,則繼續進(jìn)行處理,否則一律返回NGX_HTTP_NOT_ALLOWED從而拒絕客戶(hù)端的發(fā)起的請求。

其次是檢查請求的url的結尾字符是不是斜杠‘/’,如果是說(shuō)明請求的不是一個(gè)文件,給后續的handler去處理,比如后續的ngx_http_autoindex_handler(如果是請求的是一個(gè)目錄下面,可以列出這個(gè)目錄的文件),或者是ngx_http_index_handler(如果請求的路徑下面有個(gè)默認的index文件,直接返回index文件的內容)。

然后接下來(lái)調用了一個(gè)ngx_http_map_uri_to_path函數,該函數的作用是把請求的http協(xié)議的路徑轉化成一個(gè)文件系統的路徑。

然后根據轉化出來(lái)的具體路徑,去打開(kāi)文件,打開(kāi)文件的時(shí)候做了2種檢查,一種是,如果請求的文件是個(gè)symbol link,根據配置,是否允許符號鏈接,不允許返回錯誤。還有一個(gè)檢查是,如果請求的是一個(gè)名稱(chēng),是一個(gè)目錄的名字,也返回錯誤。如果都沒(méi)有錯誤,就讀取文件,返回內容。其實(shí)說(shuō)返回內容可能不是特別準確,比較準確的說(shuō)法是,把產(chǎn)生的內容傳遞給后續的filter去處理。

http log module?

該模塊提供了對于每一個(gè)http請求進(jìn)行記錄的功能,也就是我們見(jiàn)到的access.log。當然這個(gè)模塊對于log提供了一些配置指令,使得可以比較方便的定制access.log。

這個(gè)模塊的代碼位于src/http/modules/ngx_http_log_module.c,雖然這個(gè)模塊的代碼有接近1400行,但是主要的邏輯在于對日志本身格式啊,等細節的處理。我們在這里進(jìn)行分析主要是關(guān)注,如何編寫(xiě)一個(gè)log handler的問(wèn)題。

由于log handler的時(shí)候,拿到的參數也是request這個(gè)東西,那么也就意味著(zhù)我們如果需要,可以好好研究下這個(gè)結構,把我們需要的所有信息都記錄下來(lái)。

對于log handler,有一點(diǎn)特別需要注意的就是,log handler是無(wú)論如何都會(huì )被調用的,就是只要服務(wù)端接受到了一個(gè)客戶(hù)端的請求,也就是產(chǎn)生了一個(gè)request對象,那么這些個(gè)log handler的處理函數都會(huì )被調用的,就是在釋放request的時(shí)候被調用的(ngx_http_free_request函數)。

那么當然絕對不能忘記的就是log handler最好,也是建議被掛載在NGX_HTTP_LOG_PHASE階段。因為掛載在其他階段,有可能在某些情況下被跳過(guò),而沒(méi)有執行到,導致你的log模塊記錄的信息不全。

還有一點(diǎn)要說(shuō)明的是,由于nginx是允許在某個(gè)階段有多個(gè)handler模塊存在的,根據其處理結果,確定是否要調用下一個(gè)handler。但是對于掛載在NGX_HTTP_LOG_PHASE階段的handler,則根本不關(guān)注這里handler的具體處理函數的返回值,所有的都被調用。如下,位于src/http/ngx_http_request.c中的ngx_http_log_request函數。

static voidngx_http_log_request(ngx_http_request_t *r){        ngx_uint_t                  i, n;        ngx_http_handler_pt        *log_handler;        ngx_http_core_main_conf_t  *cmcf;        cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);        log_handler = cmcf->phases[NGX_HTTP_LOG_PHASE].handlers.elts;        n = cmcf->phases[NGX_HTTP_LOG_PHASE].handlers.nelts;        for (i = 0; i < n; i++) {                log_handler[i](r);        }
本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
nginx源代碼分析_浪灣(langwan) 一個(gè)思想跳躍的程序員
ngx
【Linux網(wǎng)絡(luò )編程】Nginx -- 模塊開(kāi)發(fā)(基本模塊解析)
解剖Nginx·模塊開(kāi)發(fā)篇(5)解讀內置非默認模塊 ngx
nginx中upstream的設計和實(shí)現(一)
upstream模塊
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久