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!!!!!

时间: 2024-11-13 03:43:19

nginx学习三 nginx配置项解析详解及代码实现的相关文章

Nginx学习之二-配置项解析及编程实现

在开发功能灵活的Nginx模块时,需要从配置文件中获取特定的信息.不过,我们并不需要再编写一套读取配置的系统,Nginx已经为用户提供了强大的配置项解析机制,同时还支持“-s reload”命令,可以在不重启服务的情况下可使配置生效. 一.Nginx配置文件简介 如果编译安装Nginx时使用默认路径,那么Nginx运行目录是/usr/local/nginx,其配置文件存放目录是/usr/local/nginx/conf/nginx.conf.其内容默认如下: [cpp] view plainco

Nginx 介绍及日常管理的详解

这篇文章主要介绍了Nginx 介绍及日常管理的详解的相关资料,希望通过本文能帮助到大家,需要的朋友可以参考下Nginx 介绍及日常管理的详解 Nginx(发音同 engine x)是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,并在一个BSD-like 协议下发行.其特点是占有内存少,并发能力强,事实上nginx的并发能力确实在同类型的网页服务器中表现较好.中国大陆使用nginx网站用户有:新浪.网易. 腾讯等.本文简要描述了Nginx的基本特性及其配置文

Linux下nginx编译安装教程和编译参数详解

这篇文章主要介绍了Linux下nginx编译安装教程和编译参数详解,需要的朋友可以参考下 一.必要软件准备1.安装pcre 为了支持rewrite功能,我们需要安装pcre 复制代码代码如下: # yum install pcre* //如过你已经装了,请跳过这一步 2.安装openssl 需要ssl的支持,如果不需要ssl支持,请跳过这一步 复制代码代码如下: # yum install openssl* 3.gzip 类库安装 复制代码代码如下: yum install zlib zlib-

Nginx+Tomcat的服务器端环境配置详解

这篇文章主要介绍了Nginx+Tomcat的服务器端环境配置详解,包括Nginx与Tomcat的监控开启方法,需要的朋友可以参考下 Nginx+tomcat是目前主流的Javaweb架构,如何让nginx+tomcat同时工作呢,也可以说如何使用nginx来反向代理tomcat后端均衡呢?直接安装配置如下: 1.Java JDK安装: #下载相应的jdk软件包,然后解压安装,我这里包名称为:jdk-7u25-Linux-x64.tar.gz ? 1 tar -xzf jdk-7u25-linux

Windows下Nginx Virtual Host多站点配置详解

Windows下Nginx Virtual Host多站点配置详解 此教程适用于Windows系统已经配置好Nginx+Php+Mysql环境的同学. 如果您还未搭建WNMP环境,请查看 windows7配置Nginx+php+mysql教程. 先说明一下配置多站点的目的:在生产环境中,如果将系统所有代码文件都放在公开目录中,则很容易被查看到系统源码,这样是很不安全的,所以需要只公开index.php的入口文件目录.而同一个服务器中,可能运行多个系统,这样就必须公开多个入口文件目录,以便用不同的

php学习之道:WSDL详解(三)

通过声明方式定义绑定(binding)属性 如果你在服务中采用SOAP binding,你可以使用JAX-WS来指定一定数量的属性binding.这些属性指定对应你在WSDL中指定的属性.某些设置,比如参数类型,可以约束你实现的方法,这些设置也影响声明的效用. @SOAPBinding声明,定义在javax.jws.soap.SOAPBinding接口中.它提供发布时的SOAP Binding细节.如果@SOAPBinding没有被指定,则用缺省的doc/literal SOAPBinding.

J2EE学习篇之--JQuery技术详解

前面我们讲解了的J2EE的技术都是服务端的技术,下面我们来看一下前端的一些开发技术,这一篇我们来看一下jQuery技术 简介: jQuery由美国人John Resig创建,至今已吸引了来自世界各地的众多 javascript高手加入其team. jQuery是继prototype之后又一个优秀的Javascript框架.其宗旨是--WRITE LESS,DO MORE,写更少的代码,做更多的事情. 它是轻量级的js库(压缩后只有21k) ,这是其它的js库所不及的,它兼容CSS3,还兼容各种浏

[No000013F]WPF学习之X名称空间详解

原文:[No000013F]WPF学习之X名称空间详解 X名称空间里面的成员(如X:Name,X:Class)都是写给XAML编译器看的.用来引导XAML代码将XAML代码编译为CLR代码. 4.1X名称空间里面到底都有些什么? x名称空间映射的是:http://schemas.microsoft.com/winfx/2006/xaml,望文生义,它包含的类均与解析XAML语言相关,所以亦称之为“XAML名称空间”. 与C#语言一样,XAML也有自己的编译器.XAML语言被解析并编译,最终形成微

hadoop 学习笔记:mapreduce框架详解

hadoop 学习笔记:mapreduce框架详解 开始聊mapreduce,mapreduce是hadoop的计算框架,我 学hadoop是从hive开始入手,再到hdfs,当我学习hdfs时候,就感觉到hdfs和mapreduce关系的紧密.这个可能是我做技术研究的 思路有关,我开始学习某一套技术总是想着这套技术到底能干什么,只有当我真正理解了这套技术解决了什么问题时候,我后续的学习就能逐步的加快,而学习 hdfs时候我就发现,要理解hadoop框架的意义,hdfs和mapreduce是密不