首页 > 代码库 > ngx_http_hello

ngx_http_hello

<style></style>

tengine.taobao.org/book/chapter_03.html

程序解析:

1,核心是定义三个结构体,分别是

angx_command_t(模块的配置结构):由于定义了自己的配置结构体

typedef struct

{

ngx_str_t hello_string;

ngx_int_t hello_counter;

}ngx_http_hello_loc_conf_t

结构体中有两个配置项,所以需要同步定义两个获取配置信息的函数

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 void *ngx_http_hello_create_loc_conf(ngx_conf_t *cf);

以及一个合并配置信息结构体的函数

static char *ngx_http_hello_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) ;

两个获取配置信息的函数作为ngx_command_t结构体数组的set成员,创建配置结构体和合并配置结构体的函数作为ngx_http_module_t的成员存在


bngx_http_mocule_tHTTP模块通用接口,也是模块上下文):结构体本身由8个回调函数指针构成,本次示例中定义了两个,第一个用来挂载,第二个用来创建配置结构体。


cngx_module_t(模块的定义):包含了上面两个结构体以及模块类型。



2,实现模块的加载,有两种加载方式:按处理阶段加载和按需加载。前者一般选取在NGX_HTTP_CONTENT_PHASE阶段加载,设置方式是在结构体ngx_http_module_t中设置回调方法。后者则在结构体ngx_command_t中的函数指针set指向的函数内部定义配置项结构体,HTTP框架在处理用户请求进行到NGX_HTTP_CONTENT_PHASE阶段时,如果请求与配置项所在的配置块匹配,就调用配置结构体的handler成员处理这个请求。给出两个实例:

按处理阶段加载

staticngx_int_t

ngx_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) {

returnNGX_ERROR;

}

*h= ngx_http_hello_handler;

returnNGX_OK;

}

注(函数原型):ngx_http_config.h:#definengx_http_conf_get_module_main_conf(cf, module)


按需加载

staticchar *

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;

returnNGX_CONF_OK;

}

ngx_http_core_loc_conf_t结构体是第一个HTTP模块ngx_http_core_module模块的create_loc_conf方法生成的,它对应者当前解析的location块,它的handler成员即是被执行的处理方法。

注:ngx_http_conf_get_module_loc_conf函数原型同上。有一点需要注意的就是,在加载时获取配置结构体用的函数是ngx_http_conf_get_module_xxx(main,srv, loc)_conf,而在处理函数中获取配置结构体采用的是ngx_http_get_module_xxx(main,srv, loc)_conf函数。函数原型分别为:

#definengx_http_conf_get_module_main_conf(cf, module) \

((ngx_http_conf_ctx_t*) cf->ctx)->main_conf[module.ctx_index]

#definengx_http_conf_get_module_srv_conf(cf, module) \

((ngx_http_conf_ctx_t*) cf->ctx)->srv_conf[module.ctx_index]

#definengx_http_conf_get_module_loc_conf(cf, module) \

((ngx_http_conf_ctx_t*) cf->ctx)->loc_conf[module.ctx_index]


#definengx_http_get_module_loc_conf(r, module) (r)->loc_conf[module.ctx_index]


使用按处理阶段加载方式需要注意一点,加载函数中定义的配置结构体是ngx_http_core_main_conf_tnginx没有定义结构体ngx_http_core_loc_conf_t,只有main_conf_t结构体中才定义了阶段数组成员

ngx_http_phase_t phases[NGX_HTTP_LOG_PHASE + 1];

因此,尽管自定义的配置项是在location块中出现的,只要使用按处理阶段加载的方式,都只能使用ngx_http_core_main_conf_t结构体。


本示例采用按处理阶段加载方式,并且调用预定义的配置解析函数来设置ngx_comman_tset成员,加载和配置解析分开。采用按需加载的一种较简单模式是将ngx_http_module_t8个回调方法全部设置为NULL,设置set函数指针为自定义的配置解析函数,并在函数体内设置handler方法,这样在配置解析完后就会调用匹配的handler



3,实现handler函数,也就是所有的处理工作,包括:从ngx_http_request_t中读取配置信息并处理,判断HTTP方法并决定是否响应,决定是否丢弃收到的包体,发送响应头,发送响应包体。由以下函数实现功能:


#definengx_http_get_module_loc_conf(r, module) (r)->loc_conf[module.ctx_index]

获取配置信息,并进行处理,比如配置项hello_counteroffUNSET的情况。


if(!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD)))

如果是不支持的方法,直接返回NGX_HTTP_NOTALLOWED


ngx_int_tngx_http_discard_request_body(ngx_http_request_t *r);

本示例中不需要对包体进行处理,所以丢弃。


ngx_int_tngx_http_send_header(ngx_http_request_t *r)

如果request方法是head,那么在执行完这一步后就返回。

发送头部有两种方法,本示例利用request_t中的headers_out成员,直接设置后发送。也可以自定义HTTP头部,然后加入到headers链表。


ngx_int_tngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *chain)

将内存中的字符串作为包体发送,利用ngx_buf_t携带字符串信息。可以利用函数

ngx_buf_t*ngx_create_temp_buf(ngx_pool_t *pool, size_t size);

来简化ngx_buf_t的创建过程,size参数表示为ngx_buf_tdata成员分配的空间,比如字符串“hello”长度为5,那么size参数就为5

如果使用ngx_pcalloc的话,参数size就应该是sizeof(ngx_buf_t)ngx_buf_t结构体中的posstart等成员都是指针,所以先给结构体指针分配一个内存,对于字符串(ngx_buf_t指针指向的内容)的存储需要再次调用ngx_pcalloc进行分配,函数ngx_create_temp_buf只是将上面两步合并了而已。

如下是ngx_buf_t的内存结构图:





 

 

 

 

在发送内存数据时,如果不设置ngx_buf_tlast_buf成员为1的话,浏览器会一直处于正在连接状态,而nginx则永远不会发送数据给客户端,过了几分钟才出现找不到服务器。


比较奇葩的一个问题是我第一次运行ngx_http_mytest_module模块时,浏览器是接收的数据,然后另存为。而再次运行则变成了直接显示在浏览器上面了。


在发送文件的时候总是出现Notfound,后来发现是文件权限问题,即客户端对文件没有读权限导致不能正常打开文件,确定的方法是在程序中添加perror函数,这样,错误就被输出到log文件中去了。

ngx_http_hello