首页 > 代码库 > ngx_http_hello
ngx_http_hello
tengine.taobao.org/book/chapter_03.html
程序解析:
1,核心是定义三个结构体,分别是
a,ngx_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的成员存在
b,ngx_http_mocule_t(HTTP模块通用接口,也是模块上下文):结构体本身由8个回调函数指针构成,本次示例中定义了两个,第一个用来挂载,第二个用来创建配置结构体。
c,ngx_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_t,nginx没有定义结构体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_t的set成员,加载和配置解析分开。采用按需加载的一种较简单模式是将ngx_http_module_t的8个回调方法全部设置为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_counter为off或UNSET的情况。
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_t的data成员分配的空间,比如字符串“hello”长度为5,那么size参数就为5。
如果使用ngx_pcalloc的话,参数size就应该是sizeof(ngx_buf_t),ngx_buf_t结构体中的pos,start等成员都是指针,所以先给结构体指针分配一个内存,对于字符串(ngx_buf_t指针指向的内容)的存储需要再次调用ngx_pcalloc进行分配,函数ngx_create_temp_buf只是将上面两步合并了而已。
如下是ngx_buf_t的内存结构图:
在发送内存数据时,如果不设置ngx_buf_t的last_buf成员为1的话,浏览器会一直处于正在连接状态,而nginx则永远不会发送数据给客户端,过了几分钟才出现找不到服务器。
比较奇葩的一个问题是我第一次运行ngx_http_mytest_module模块时,浏览器是接收的数据,然后另存为。而再次运行则变成了直接显示在浏览器上面了。
在发送文件的时候总是出现Notfound,后来发现是文件权限问题,即客户端对文件没有读权限导致不能正常打开文件,确定的方法是在程序中添加perror函数,这样,错误就被输出到log文件中去了。
ngx_http_hello