Monday, February 14, 2011

Day 20 - TCP Proxy, HttpRRDModule v0.0 and content handler registration

Let's start wiht an accident. Yes, it also happens on virtual journeys. So, I ran into: the nginx TCP proxy module. Doesn't look like it used or lot or like it has a lot of support, but it could be a good base to run a cheap tcp load balancer/NATter. Not really knowing what I am talking about, I would say it's probably less efficient than IPVS. But one year ago, I would have bet nginx is not as good as Apache...

Enough distraction (the web is good at that). I started to focus on the development of my HttpRRDModule. So, here what I went through trying to get the bare bone infrastructure of the module:

  1. Get the nginx headers in the C file (copy/paste from any module, they are all the same).
  2. Create the config file. It's the file used by nginx to integrate your module into its build system. Pretty much all exemples are the same. Take Evan Miller's one.
  3. Build the thing (configure --add-module ...; make). Now, we have a big nothing that compiles. It's a good start. ;)
  4. Code the request handler. You know, the thing taking a ngx_http_request_t as argument and returning a ngx_int_t. You code something close to the empty function and you try to compile it. And that's where troubles start... It does not compile any more. The reason is fairly simple: nginx compiler settings are strict enough to fail the compilation in case of unused functions. I cannot say I'm complaining about this, I love using the compiler as much as possible to detect problems. But, this is honestly the first time I run into a project which default settings are to fail on unused functions.
  5. So, I went ahead and added all the magic mantras that register the handler with nginx. Declaring the ngx_http_module_t, the ngx_command_t and attaching my function as handler with something like:
    clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
    clcf->handler = ngx_http_rrd_handler;
    It's pretty much the example you find everywhere.

And at this point I look at the thing and am thinking "I'm creating a directive for nothing". Let's say I don't even want a directive, I just want to register my module and have it handle all requests. Yes, it's extreme as in real life I would probably limit my module to a location. But it's a good exercise. So, instead of having my code that registers the handler invoked as the processor of a directive, I make it called as the postconfiguration method of my module (see appropriate field in ngx_http_module_t structure). And...nothing works. At this point, I start wondering very seriously how this works. So, I start checking: my registration is called. It's just that nginx never gives my module a chance to handle the request. That's when I started adding breakpoints and watched the thing run in details.

Where you have to start looking is the ngx_http_core_content_phase function (ngx_http_core_module.c) there is a test to see if the request has a content_handler: if (r->content_handler) and in my case (handler attached from the postconfiguration) this is NULL. So, nginx runs all the other modules (including the index, auto-index and static built-in modules) and finally declares '404 - NOT FOUND'. All I have to find is "who is setting this r->content_handler. Of course, it's one of the previous phases. It's actually the "find config" phase. To be more specific look at functions ngx_http_core_find_config_phase and ngx_http_update_location_config. As I keep debugging, I notice that the handler is not retrieved from the location configuration of the http_core_module. And that's where the trick is: in the postconfiguration method, I can call ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module) but it pretty much returns crap. Or at least what it returns is not tied to a location. Therefore, I was registering my handler in a location that does not exist.

That's long enough for today. I'll tell you the rest of this story in my next post with the two ways you can register your module: as a HTTP handler or as content phase handler.

No comments:

Post a Comment