nginx學習三 nginx配置項解析詳解及代碼實現

nginx配置項解析詳解及代碼實現

0回顧

 在上一節,用nginx簡單實現了一個hello world程序:當我們在瀏覽器中輸入lochost/hello ,瀏覽器就返回:hello world。爲什麼會這樣呢,簡單一點說就是當我們請求訪問hello這個服務,nginx就會看配置文件中是否有,如果有,根據具體的handler處理後把處理的結果返回給用戶,沒有就返回not found。

 location /hello {
            test_hello ;//無參數的配置
     

這其實是一個簡單的配置。這節我們來看一個複雜的配置文件解析:

location /myconfig {
    mystring    smile_to_life;
    mycounter   24;
    myflag      on;
    mygrage     better;
    mybufs      2  4k;
}

這個配置幾乎覆蓋了nginx的基本類型,還有9種類型和這幾種類型很相同,後面我們在詳細講。

先看一下存儲配置參數的結構體:

typedef struct{
    ngx_str_t       arg_str;
    ngx_int_t       arg_counter;
    ngx_flag_t      arg_flag;
    ngx_uint_t      arg_enum_seq;
    ngx_bufs_t      arg_bufs;
}ngx_http_myconfig_loc_conf_t;
      我們想實現的結果是:當我們在瀏覽器中輸入myconfig時:瀏覽器輸出:

mystring = smile_to_life, my_counter = 24, myflag = on, mygrage = better, buf.num = 2, buf.size = 4096;

1模塊的基本結構

      首先讓我們看上一節出現的三個基本的數據結構。

   

1.1  模塊配置指令 nginx_command_t

 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);

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

再看一下這個函數被調用的時候,傳入的三個參數。

cf: 該參數裏面保存從配置文件讀取到的原始字符串以及相關的一些信息。特別注意的是這個參數的args字段是一個ngx_str_t類型的數組,該數組的首個元素是這個配置指令本身,第二個元素是指令的第一個參數,第三個元素是第二個參數,依次類推。

cmd: 這個配置指令對應的ngx_command_t結構。

conf: 就是定義的存儲這個配置值的結構體,比如在上面展示的那個ngx_http_myconfig_loc_conf_t。當解析這個arg_str變量的時候,傳入的conf就指向一個ngx_http_myconfig_loc_conf_t類型的變量。用戶在處理的時候可以使用類型轉換,轉換成自己知道的類型,再進行字段的賦值。

爲了更加方便的實現對配置指令參數的讀取,nginx已經默認提供了對一些標準類型的參數進行讀取的函數,可以直接賦值給set字段使用。下面來看一下這些已經實現的set類型函數。

ngx_conf_set_flag_slot:讀取NGX_CONF_FLAG類型的參數。

ngx_conf_set_str_slot:讀取字符串類型的參數。

ngx_conf_set_str_array_slot: 讀取字符串數組類型的參數。

ngx_conf_set_keyval_slot:讀取鍵值對類型的參數。

ngx_conf_set_num_slot: 讀取整數類型(有符號整數ngx_int_t)的參數。

ngx_conf_set_size_slot:讀取size_t類型的參數,也就是無符號數。

ngx_conf_set_off_slot: 讀取off_t類型的參數。

ngx_conf_set_msec_slot: 讀取毫秒值類型的參數。

ngx_conf_set_sec_slot: 讀取秒值類型的參數。

ngx_conf_set_bufs_slot:讀取的參數值是2個,一個是buf的個數,一個是buf的大小。例如: output_buffers 1 128k;

ngx_conf_set_enum_slot: 讀取枚舉類型的參數,將其轉換成整數ngx_uint_t類型。

ngx_conf_set_bitmask_slot: 讀取參數的值,並將這些參數的值以bit位的形式存儲。例如:HttpDavModule模塊的dav_methods指令。

conf:      該字段被NGX_HTTP_MODULE類型模塊所用 (我們編寫的基本上都是NGX_HTTP_MOUDLE,只有一些nginx核心模塊是非NGX_HTTP_MODULE),該字段指定當前配置項存儲的內存位置。實際上是使用哪個內存池的問題。因爲http模塊對所有http模塊所要保存的配置信息,劃分了main, server和location三個地方進行存儲,每個地方都有一個內存池用來分配存儲這些信息的內存。這裏可能的值爲 NGX_HTTP_MAIN_CONF_OFFSET、NGX_HTTP_SRV_CONF_OFFSET或NGX_HTTP_LOC_CONF_OFFSET。當然也可以直接置爲0,就是NGX_HTTP_MAIN_CONF_OFFSET。

offset:    指定該配置項值的精確存放位置,一般指定爲某一個結構體變量的字段偏移。因爲對於配置信息的存儲,一般我們都是定義個結構體來存儲的。那麼比如我們定義了一個結構體A,該項配置的值需要存儲到該結構體的b字段。那麼在這裏就可以填寫爲offsetof(A, b)。對於有些配置項,它的值不需要保存或者是需要保存到更爲複雜的結構中時,這裏可以設置爲0。

post:      該字段存儲一個指針。可以指向任何一個在讀取配置過程中需要的數據,以便於進行配置讀取的處理。大多數時候,都不需要,所以簡單地設爲0即可。

好了,這個結構題我們已經知道,現在來看實現本節的配置指令結構體:

配置文件:

location /myconfig {
    mystring    smile_to_life;
    mycounter   24;
    myflag      on;
    mygrage     better;
    mybufs      2  4k;
}

配置文件的結構體:

typedef struct{
    ngx_str_t       arg_str;//存儲mystring的值
    ngx_int_t       arg_counter; //存儲mycounter的值
    ngx_flag_t      arg_flag; //存儲myflag的值
    ngx_uint_t      arg_enum_seq; //存儲mygrage的值
    ngx_bufs_t      arg_bufs;//存儲mybufs的值
}ngx_http_myconfig_loc_conf_t;
arg_enum_seq存的是一個enum類型的值,enum定義如下:

static ngx_conf_enum_t test_enums[] = {
    { ngx_string("good"), 1},
    { ngx_string("better"), 2},
    { ngx_string("best"), 3},
    { ngx_null_string, 0}
}; 

當arg_enum_seq=2時我們會輸出better!

本節配置指令如下:

static ngx_command_t ngx_http_myconfig_commands[] = {
    {
      ngx_string("mystring"),//配置指令名稱
      NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, //配置參數屬性
      ngx_http_mystring, //set 函數,具體實現請參看源代碼
      NGX_HTTP_LOC_CONF_OFFSET, //該配置在http的local配置中
      offsetof(ngx_http_myconfig_loc_conf_t, arg_str),//offsetof(type, member):&(((type*)0)->member)
      NULL
    },
    {
       ngx_string("mycounter"),//配置指令名稱
       NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1,//配置參數屬性
       ngx_http_mycounter,//set 函數,具體實現請參看源代碼
       NGX_HTTP_LOC_CONF_OFFSET,//該配置在http的local配置中
       offsetof(ngx_http_myconfig_loc_conf_t, arg_counter), //在配置結構體中對應的位置
       NULL
    },
    {
       ngx_string("myflag"),
       NGX_HTTP_LOC_CONF | NGX_CONF_FLAG | NGX_CONF_TAKE1,//ngx_conf_t 的類型要用NGX_CONF_FLAG
       ngx_http_myflag,
       NGX_HTTP_LOC_CONF_OFFSET,
       offsetof(ngx_http_myconfig_loc_conf_t, arg_flag),
       NULL
    },
    {
       ngx_string("mygrade"),
       NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1,
       ngx_http_mygrade,
       NGX_HTTP_LOC_CONF_OFFSET,
       offsetof(ngx_http_myconfig_loc_conf_t, arg_enum_seq),
       test_enums //post = test_enums, 參數post指向一個emum類型
    },
    {
       ngx_string("mybufs"),
       NGX_HTTP_LOC_CONF | NGX_CONF_TAKE2,//ngx_bufs_t 有兩個數據成員
       ngx_http_mybufs,
       NGX_HTTP_LOC_CONF_OFFSET,
       offsetof(ngx_http_myconfig_loc_conf_t, arg_bufs),
       NULL
    },
    ngx_null_command
};

1.2  模塊上下文 ngx_http_module_t

   這是一個ngx_http_module_t類型的靜態變量。這個變量實際上是提供一組回調函數指針,這些函數有在創建存儲配置信息的對象的函數,也有在創建前和創建後會調用的函數。這些函數都將被nginx在合適的時間進行調用。

定義:

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:

      在創建和讀取該模塊的配置信息之前被調用。

postconfiguration:

      在創建和讀取該模塊的配置信息之後被調用。

create_main_conf:

      調用該函數創建本模塊位於http block的配置信息存儲結構。該函數成功的時候,返回創建的配置對象。失敗的話,返回NULL。

init_main_conf:

       調用該函數初始化本模塊位於http block的配置信息存儲結構。該函數成功的時候,返回NGX_CONF_OK。失敗的話,返回NGX_CONF_ERROR或錯誤字符串。

create_srv_conf:

      調用該函數創建本模塊位於http server block的配置信息存儲結構,每個server block會創建一個。該函數成功的時候,返回創建的配置對象。失敗的話,返回NULL。

merge_srv_conf:

       因爲有些配置指令既可以出現在http block,也可以出現在http server block中。那麼遇到這種情況,每個server都會有自己存儲結構來存儲該server的配置,但是在這種情況下http block中的配置與server block中的配置信息發生衝突的時候,就需要調用此函數進行合併,該函數並非必須提供,當預計到絕對不會發生需要合併的情況的時候,就無需提供。當然爲了安全起見還是建議提供。該函數執行成功的時候,返回NGX_CONF_OK。失敗的話,返回NGX_CONF_ERROR或錯誤字符串。

create_loc_conf:

      調用該函數創建本模塊位於location block的配置信息存儲結構。每個在配置中指明的location創建一個。該函數執行成功,返回創建的配置對象。失敗的話,返回NULL。

merge_loc_conf:

      與merge_srv_conf類似,這個也是進行配置值合併的地方。該函數成功的時候,返回NGX_CONF_OK。失敗的話,返回NGX_CONF_ERROR或錯誤字符串。

Nginx裏面的配置信息都是上下一層層的嵌套的,對於具體某個location的話,對於同一個配置,如果當前層次沒有定義,那麼就使用上層的配置,否則使用當前層次的配置。

這些參數如果不用,即我們不使用任何的回調函數可以設爲NULL。不過本節使用最後兩個回調函數作爲實例。

好,看看myconfig模塊的上下文:

static ngx_http_module_t ngx_http_myconfig_module_ctx = {
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    ngx_http_myconfig_create_loc_conf, //創建myconig_loc_conf_t結構體並初始化,見代碼
    ngx_http_myconfig_merge_loc_conf// 合併可能出現的重複項,僅作樣例用
};

1.3  模塊的定義 ngx_module_t

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

我們先來看下ngx_module_t的定義:

當然那7個回調函數我們不用的話就設置爲NULL

好我們來看myconfig的實現:

ngx_module_t ngx_http_myconfig_module = {
    NGX_MODULE_V1,
    &ngx_http_myconfig_module_ctx, //設置爲自己的模塊上下文
    ngx_http_myconfig_commands,//設置爲自己的模塊配置指令
    NGX_HTTP_MODULE,//表明是HTTP模塊
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NGX_MODULE_V1_PADDING
};

2解析配置項set函數

我們看看nginx的預設配置解析函數,這裏僅僅簡單接受,詳細請參看陶輝寫的nginx深入詳解第四章,講的很詳細。

2.1nginx的預設配置項解析函數

nginx提供了14個預設配置解析函數,分別解析下面14種常見的數據類型:

typedef struct{  
        ngx_str_t arg_str;  
        ngx_int_t arg_num;  
        ngx_flag_t arg_flag;  
        size_t arg_size;  
        ngx_array_t* arg_str_array;  
        ngx_array_t* arg_keyval;  
        off_t arg_off;  
        ngx_msec_t arg_msec;  
        time_t arg_sec;  
        ngx_bufs_t arg_bufs;  
        ngx_uint_t arg_enum_seq;  
        ngx_uint_t arg_bitmask;  
        ngx_uint_t arg_access;  
        ngx_path_t* arg_path;  
}ngx_http_commondata_loc_conf_t;

相應的14個預設配置解析函數

ngx_conf_set_flag_slot:讀取NGX_CONF_FLAG類型的參數。

ngx_conf_set_str_slot:讀取字符串類型的參數。

ngx_conf_set_str_array_slot: 讀取字符串數組類型的參數。

ngx_conf_set_keyval_slot:讀取鍵值對類型的參數。

ngx_conf_set_num_slot: 讀取整數類型(有符號整數ngx_int_t)的參數。

ngx_conf_set_size_slot:讀取size_t類型的參數,也就是無符號數。

ngx_conf_set_off_slot: 讀取off_t類型的參數。

ngx_conf_set_msec_slot: 讀取毫秒值類型的參數。

ngx_conf_set_sec_slot: 讀取秒值類型的參數。

ngx_conf_set_bufs_slot:讀取的參數值是2個,一個是buf的個數,一個是buf的大小。例如: output_buffers 1 128k;

ngx_conf_set_enum_slot: 讀取枚舉類型的參數,將其轉換成整數ngx_uint_t類型。

ngx_conf_set_bitmask_slot: 讀取參數的值,並將這些參數的值以bit位的形式存儲。例如:HttpDavModule模塊的dav_methods指令。


2.1簡單的例子

看看ngx_conf_set_str_slot的用法:

static char* ngx_http_mystring(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "mystring begin");
    ngx_http_myconfig_loc_conf_t *mlcf;
    mlcf = conf;
    char* rt = ngx_conf_set_str_slot(cf, cmd, conf); //調用ngx_conf_set_str_slot 處理ngx_str_t類型的變量
    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "mystring = %s", mlcf->arg_str.data);

	//這裏我們按需求掛載了handler函數(還有按處理階段掛在,這裏不做介紹)
    ngx_http_core_loc_conf_t *clcf;
    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
    clcf->handler = ngx_http_myconfig_handler;
    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "handler");
    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "mystring end");

    return rt;
}


看看ngx_buf_set_bufs_slot的用法:

static char* ngx_http_mybufs(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "buffer begin");
    ngx_http_myconfig_loc_conf_t *mlcf;
    mlcf = conf;
    char* rt = ngx_conf_set_bufs_slot(cf, cmd, conf);//調用ngx_conf_set_bufs_slot 處理ngx_bufs_t類型的變量
    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "buf_nums = %d, buf_size = %z", 
                                             mlcf->arg_bufs.num, mlcf->arg_bufs.size);

    return rt;
}

最後強烈建議大家去看幾個預設配置項解析函數的源代碼加深印象(源代碼也比較簡單)。


3程序實例代碼

 模塊編程的主要思路:

1編寫模塊基本結構。包括模塊的定義,模塊上下文結構,模塊的配置結構等。

2 實現handler的掛載函數。根據模塊的需求選擇正確的掛載方式。

3 編寫handler處理函數。模塊的功能主要通過這個函數來完成。

實例代碼:

#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>

static ngx_conf_enum_t test_enums[] = {
    { ngx_string("good"), 1},
    { ngx_string("better"), 2},
    { ngx_string("best"), 3},
    { ngx_null_string, 0}
}; 

typedef struct{
    ngx_str_t       arg_str;
    ngx_int_t       arg_counter;
    ngx_flag_t      arg_flag;
    ngx_uint_t      arg_enum_seq;
    ngx_bufs_t      arg_bufs;
}ngx_http_myconfig_loc_conf_t;

/*
location /myconfig {
    mystring    smile_to_life;
    mycounter   24;
    myflag      on;
    mygrage     better;
    mybufs      2 4k;
}
*/
static char* ngx_http_mystring(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static char* ngx_http_mycounter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static char* ngx_http_myflag(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static char* ngx_http_mygrade(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static char* ngx_http_mybufs(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static ngx_int_t ngx_http_myconfig_handler(ngx_http_request_t *r);
static void* ngx_http_myconfig_create_loc_conf(ngx_conf_t* cf);
static char *ngx_http_myconfig_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)

static ngx_command_t ngx_http_myconfig_commands[] = {
    {
      ngx_string("mystring"),//配置指令名稱
      NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, //配置參數屬性
      ngx_http_mystring, //set 函數,具體實現請參看源代碼
      NGX_HTTP_LOC_CONF_OFFSET, //該配置在http的local配置中
      offsetof(ngx_http_myconfig_loc_conf_t, arg_str),//offsetof(type, member):&(((type*)0)->member)
      NULL
    },
    {
       ngx_string("mycounter"),//配置指令名稱
       NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1,//配置參數屬性
       ngx_http_mycounter,//set 函數,具體實現請參看源代碼
       NGX_HTTP_LOC_CONF_OFFSET,//該配置在http的local配置中
       offsetof(ngx_http_myconfig_loc_conf_t, arg_counter), //在配置結構體中對應的位置
       NULL
    },
    {
       ngx_string("myflag"),
       NGX_HTTP_LOC_CONF | NGX_CONF_FLAG | NGX_CONF_TAKE1,//ngx_conf_t 的類型要用NGX_CONF_FLAG
       ngx_http_myflag,
       NGX_HTTP_LOC_CONF_OFFSET,
       offsetof(ngx_http_myconfig_loc_conf_t, arg_flag),
       NULL
    },
    {
       ngx_string("mygrade"),
       NGX_HTTP_LOC_CONF | NGX_CONF_TAKE1,
       ngx_http_mygrade,
       NGX_HTTP_LOC_CONF_OFFSET,
       offsetof(ngx_http_myconfig_loc_conf_t, arg_enum_seq),
       test_enums //post = test_enums, 參數post指向一個emum類型
    },
    {
       ngx_string("mybufs"),
       NGX_HTTP_LOC_CONF | NGX_CONF_TAKE2,//ngx_bufs_t 有兩個數據成員
       ngx_http_mybufs,
       NGX_HTTP_LOC_CONF_OFFSET,
       offsetof(ngx_http_myconfig_loc_conf_t, arg_bufs),
       NULL
    },
    ngx_null_command
};

static ngx_http_module_t ngx_http_myconfig_module_ctx = {
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    ngx_http_myconfig_create_loc_conf, //創建myconig_loc_conf_t結構體並初始化,見代碼
    ngx_http_myconfig_merge_loc_conf// 合併可能出現的重複項,僅作樣例用
};

ngx_module_t ngx_http_myconfig_module = {
    NGX_MODULE_V1,
    &ngx_http_myconfig_module_ctx, //設置爲自己的模塊上下文
    ngx_http_myconfig_commands,//設置爲自己的模塊配置指令
    NGX_HTTP_MODULE,//表明是HTTP模塊
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NGX_MODULE_V1_PADDING
};

static char* ngx_http_mystring(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "mystring begin");
    ngx_http_myconfig_loc_conf_t *mlcf;
    mlcf = conf;
    char* rt = ngx_conf_set_str_slot(cf, cmd, conf); //調用ngx_conf_set_str_slot 處理ngx_str_t類型的變量
    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "mystring = %s", mlcf->arg_str.data);

	//這裏我們按需求掛載了handler函數(還有按處理階段掛在,這裏不做介紹)
    ngx_http_core_loc_conf_t *clcf;
    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
    clcf->handler = ngx_http_myconfig_handler;
    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "handler");
    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "mystring end");

    return rt;
}

static char* ngx_http_mycounter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "c begin");
    ngx_http_myconfig_loc_conf_t *mlcf;
    mlcf = conf;
    char* rt = ngx_conf_set_num_slot(cf, cmd, conf); //調用ngx_conf_set_num_slot 處理ngx_int_t類型的變量
    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "num=%d c end", mlcf->arg_counter);

    return rt;
}

static char* ngx_http_myflag(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_http_myconfig_loc_conf_t *mlcf;
    mlcf = conf;
    char* rt = ngx_conf_set_flag_slot(cf, cmd, conf);//調用ngx_conf_set_flag_slot 處理ngx_flag_t類型的變量
    
    int flag = mlcf->arg_flag;
    if (flag == 0)
    {
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "myflag = on");
    }
    else
    {
         ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "myflag = off");
    }
    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "myflag=%d", mlcf->arg_flag);
    return rt;
}

static char* ngx_http_mygrade(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "grage begin");
    ngx_http_myconfig_loc_conf_t *mlcf;
    mlcf = conf;
    char* rt = ngx_conf_set_enum_slot(cf, cmd, conf);//調用ngx_conf_set_enum_slot 處理ngx_enum_t類型的變量
	                                                 //有興趣的話可以看一下源碼,很簡單的
    
    ngx_str_t grade;
    if (mlcf->arg_enum_seq == 1)
    {
       ngx_str_set(&grade, "good");
    }
    else if(mlcf->arg_enum_seq == 2)
    {
       ngx_str_set(&grade, "better");
    }
    else
    {
       ngx_str_set(&grade, "best");
    }
    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "mygrage = %s", grade.data);

    return rt;
}

static char* ngx_http_mybufs(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "buffer begin");
    ngx_http_myconfig_loc_conf_t *mlcf;
    mlcf = conf;
    char* rt = ngx_conf_set_bufs_slot(cf, cmd, conf);//調用ngx_conf_set_bufs_slot 處理ngx_bufs_t類型的變量
    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "buf_nums = %d, buf_size = %z", 
                                             mlcf->arg_bufs.num, mlcf->arg_bufs.size);

    return rt;
}

static void* ngx_http_myconfig_create_loc_conf(ngx_conf_t *cf)
{
    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "create loc_conf_t begin");
    ngx_http_myconfig_loc_conf_t *mlcf;
	//申請內存空間
    mlcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_myconfig_loc_conf_t));
    if (mlcf == NULL)
    {
       return NGX_CONF_ERROR;;
    }
    
	/*一定要初始話結構體的成員變量,不然會出現一些錯誤*/
	
    //ngx_str_set(&mlcf->arg_str, "");///////////nginx: [emerg] "mystring" directive is duplicate in,這個錯誤調試了一整天
    //看ngx_conf_set_str_slot的實現,當arg_str.data不爲空時會返回“is duplicate”
    ngx_str_null(&mlcf->arg_str);
    mlcf->arg_counter = NGX_CONF_UNSET;
    mlcf->arg_flag = NGX_CONF_UNSET;
    mlcf->arg_enum_seq = NGX_CONF_UNSET_UINT;
    //mlcf->arg_bufs不用初始化
    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "create loc_conf_t end");
    return mlcf;
}

//合併可能出現的重複項,僅作樣例用
static char *ngx_http_myconfig_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->arg_str, prev->hello_str, "");
    ngx_conf_merge_value(conf->arg_counter, prev->arg_counter, 0);
    return NGX_CONF_OK;
}

//handler 函數
static ngx_int_t ngx_http_myconfig_handler(ngx_http_request_t *r)
{
    ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, "ngx_http_myconfig_handler is called!");
    if (!(r->method & (NGX_HTTP_HEAD | NGX_HTTP_GET))) 
    {
       ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, "method is failed!");
       return NGX_HTTP_NOT_ALLOWED;
    }

    ngx_int_t rc = ngx_http_discard_request_body(r);
    if (rc != NGX_OK)
    {
      ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, "discard_qeuwest_body is failed!");
       return rc;
    }
    
    ngx_str_t type = ngx_string("text/html");
    r->headers_out.content_type = type;
    r->headers_out.status = NGX_HTTP_OK;
    if (r->method == NGX_HTTP_HEAD)
    {
        ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, "only header!");
        r->headers_out.content_length_n = type.len;
       return ngx_http_send_header(r);
    }
    
    ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, "handling is beginning");
    ngx_http_myconfig_loc_conf_t *mlcf;
    mlcf = ngx_http_get_module_loc_conf(r, ngx_http_myconfig_module);
    if (mlcf == NULL)
    {
       ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, " mlcf is empty!");
       return NGX_ERROR;
    }
    ngx_str_t mystring = mlcf->arg_str;
    ngx_str_t myflag;
    if (mlcf->arg_flag == 1)
    {
       ngx_str_set(&myflag, "on");
    }
    else
    {
       ngx_str_set(&myflag, "off");
    }
    ngx_str_t grade;
    if (mlcf->arg_enum_seq == 1)
    {
       ngx_str_set(&grade, "good");
    }
    else if(mlcf->arg_enum_seq == 2)
    {
       ngx_str_set(&grade, "better");
    }
    else
    {
       ngx_str_set(&grade, "best");
    }
    ngx_str_t format = ngx_string("mystring=%s, mycounter=%d, myflag=%s, mygrage=%s, buf_num=%d, buf_size=%z");
    ngx_int_t content_length = format.len + mystring.len + myflag.len + grade.len;
    r->headers_out.content_length_n = content_length;
    
    ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, "buffer is initing!");
    u_char* content_buf = (u_char*)ngx_pcalloc(r->pool, content_length);
    if (content_buf == NULL)
    {
       return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }
    ngx_sprintf(content_buf, (char*)format.data, mystring.data, mlcf->arg_counter, myflag.data, 
                grade.data, mlcf->arg_bufs.num, mlcf->arg_bufs.size);

    ngx_log_error(NGX_LOG_EMERG, r->connection->log, 0, "buffer is ended!");
    ngx_buf_t* buf = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
    if (buf == NULL)
    {
       return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }
    buf->pos = content_buf;
    buf->last = buf->pos + content_length;
    buf->last_buf = 1;
    buf->memory = 1;
    
    ngx_chain_t out;
    out.buf = buf;
    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);
}


http://blog.csdn.net/xiaoliangsky/article/details/39315683

word hard!















 








發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章