Wednesday, February 16, 2011

Day 21 - HTTP handler vs. content phase handler

I hope you have been able to handle the suspense. So, there are basically two ways to register your handler for a HTTP request and the two ways are very different.

Way one is the most common: you want your code to run as soon as you use a certain directive in the configuration of a certain location. And as soon as you use it you want your code to run and no other code... To do so, all you have to do is to copy/paste/adapt from ngx_http_flv_module.c:
static ngx_command_t ngx_http_flv_commands[] = {

{ ngx_string("flv"),
NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
ngx_http_flv,
0,
0,
NULL },

ngx_null_command
};
[...]
static char *
ngx_http_flv(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_flv_handler; /* this is your specific handler */

return NGX_CONF_OK;
}
The good thing is that nginx will handle most of the plumbing: parsing the location configuration, figuring out which requests match this location and run the appropriate handler for those who do.

Way two is more complicated but can let you do things you never dreamt of. You add an extra handler for the NGX_HTTP_CONTENT_PHASE. The idea here is to use the postconfiguration field of your module definition structure. Here is an example from ngx_http_autoindex_module.c:
static ngx_http_module_t ngx_http_autoindex_module_ctx = {
NULL, /* preconfiguration */
ngx_http_autoindex_init, /* postconfiguration */

NULL, /* create main configuration */
NULL, /* init main configuration */

NULL, /* create server configuration */
NULL, /* merge server configuration */

ngx_http_autoindex_create_loc_conf, /* create location configration */
ngx_http_autoindex_merge_loc_conf /* merge location configration */
};
[...]
static ngx_int_t
ngx_http_autoindex_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_autoindex_handler;

return NGX_OK;
}
The main advantage is that you get a chance to act on each request, not just the ones corresponding to a configuration you explicitly configured to do so. But, as you know from another crappy blockbuster "with great power comes great responsibility". So, you want to be careful with this. In particular, remember that you should politely NGX_DECLINED if you don't want your handler to run for a given request and that "other" modules might decide to handle the request before you. And if they do, you won't get your chance. So, at the end of the day, it's the order in which you specify modules in the config that will decide who has highest priority. That's what Igor means in auto/modules circa line 86 when he comments "# the module order is important".

Now that we know at least two ways of doing things, all we have to do is to decide which one we should use. I went for "Way one" because I think we want this to be enabled only on certain locations and when enabled the RRD module should take precedence over everything else.

No comments:

Post a Comment