httpd-modules-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Michael Spiegle <>
Subject How do you handle multiple custom config containers?
Date Tue, 10 Feb 2009 05:55:00 GMT
Hi All,
I'm writing a module called mod_gfx which will perform on-the-fly image 
resizing via libgd.  I wanted to make the module as configurable as 
possible so others can take advantage of it as well.  I'm currently 
running into a rough spot with custom containers, but first I'd like to 
make mention of how the module works.

Essentially, you define conversion profiles in the module's conf file 
that define what happens when that image is requested.  Typical URLs 
implementing this scheme will look like:

The first component of the path is a "profile" name that can be declared 
in a configuration file like this:

<GfxProfile "fullsize">
     GfxActions resize
     GfxMaxX 640
     GfxMaxY 480
     GfxQual 75

<GfxProfile "thumbnail">
     GfxActions resize
     GfxMaxX 75
     GfxMaxY 75
     GfxQual 60

<GfxProfile "watermark">
     GfxActions watermark
     GfxFile watermark.gif
     GfxLocX 0
     GfxLocY 0

**Skip to here for problem**
I've got most of the above all working well.  The other part of this 
module is to perform a match on the URL and be able to select which 
origin server it gets the content from.  The big problem comes when I 
want to implement another container.  I would like to do something like 
the following:

GfxOriginMatch OldServer ^/images/.*$
GfxOriginMatch NewServer ^/[a-z]/.*$

<GfxOrigin "OldServer">
     #round robin origin requests between these servers

<GfxOrigin "NewServer">

I can't seem to figure out how to get both of these containers playing 
nicely with each other.  I've been trying different things all day and 
trying to wrap my head around how apache handles configs, but I just 
don't seem to get it.  At this point, my source code is riddled with 
debugging and random experiments.  I don't know if it will be much use, 
but I posted it at the end of this email.  For now, I have some direct 

1) Do I need to implement a config allocator under 
"STANDARD20_MODULE_STUFF"?  If so, what exactly am I allocating?  It 
seems like I can only allocate 1 type of container, yet I have 2.

2) When I run ap_walk_config() from inside of gfx_config_profile(), it 
appears to invoke the functions for the directives inside of my 
<GfxProfile> block, however these functions handling the directives 
(gfx_profile_actions in my source) don't appear to get a pointer to the 
parent config block (which I need to work with).  Why is that?

3) What does ap_conf_vector do?  Is it just an opaque type to hide 
something?  I would really like to just pass some pointers around.


static const char*
gfx_config_profile(cmd_parms* cmd, void* mconfig, const char* arg) {
     const char* endp = ap_strrchr_c(arg, '>');
     const char* args;
     char* profile_name;
     ap_conf_vector_t* gfx_conf;

     //create a new profile for each <GfxProfile> encountered
     gfx_conf = ap_create_per_dir_config(cmd->pool);
     gfx_server_config_t* sconf = 
ap_get_module_config(cmd->server->module_config, &gfx_module);
     gfx_profile_config_t* profile = 
(gfx_profile_config_t*)apr_pcalloc(cmd->pool, sizeof(gfx_profile_config_t));
     gfx_conf = (ap_conf_vector_t*)profile;
     const char* err = ap_check_cmd_context(cmd, NOT_IN_LIMIT | 

     if (err != NULL)
         return err;

     //get the profile name (argument to <GfxProfile>)
     //do some checks for good measure
     args = apr_pstrndup(cmd->pool, arg, endp - arg);
     if (!args[0])
         return apr_psprintf(cmd->pool, "%s %s", cmd->cmd->name, "> 
directive requires profile name");

     if (endp == NULL)
         return apr_psprintf(cmd->pool, "%s %s", cmd->cmd->name, "> 
directive missing closing '>'");

     profile_name = ap_getword_conf(cmd->pool, &args);
     if (!profile_name)
         return apr_psprintf(cmd->pool, "%s %s", cmd->cmd->name, "> 
error reading profile name");

     //i think this associates the generic config type with my profile 
     //profile = ap_set_config_vectors(cmd->server, gfx_conf, 
profile_name, &gfx_module, cmd->pool);

     //not sure yet - i hope it iterates over the gfx parms in the 
     err = ap_walk_config(cmd->directive->first_child, cmd, gfx_conf);
     if (err != NULL)
         return err;

     //setup profile
     profile->name = apr_pstrdup(cmd->pool, profile_name);
     profile->output_format = IMAGE_TYPE_SRC;
     profile->actions = apr_array_make(cmd->pool, 8, MAX_LEN);
     profile->rsx = -1;
     profile->rsy = -1;
     profile->qual = -1;

     //add profile to server profiles hashmap
     //gfx_server_config_t* sconf = 
ap_get_module_config(cmd->server->module_config, &gfx_module);
     apr_hash_set(sconf->profiles, profile->name, APR_HASH_KEY_STRING, 

     return NULL;

static const char*
gfx_profile_actions(cmd_parms* cmd, void* p, const char* arg) {
     gfx_profile_config_t* profile = p;
     if (profile == NULL)
         return apr_psprintf(cmd->pool, "%s %s", cmd->cmd->name, " error 
getting profile");
     *(const char**)apr_array_push(profile->actions) = 
apr_pstrdup(cmd->pool, arg);

     return NULL;

static void
gfx_register_hooks(apr_pool_t* pool) {
     ap_hook_handler(gfx_main_handler, NULL, NULL, APR_HOOK_MIDDLE);

static const
command_rec gfx_cmds[] = {
     AP_INIT_RAW_ARGS("<GfxProfile", gfx_config_profile, NULL, RSRC_CONF,
                          "Container to create profiles"),
     AP_INIT_NO_ARGS("</GfxProfile>", NULL, NULL, RSRC_CONF, "Place 
     AP_INIT_RAW_ARGS("<GfxOrigin", gfx_config_origin, NULL, RSRC_CONF,
                      "Container to create origin hosts"),
     AP_INIT_ITERATE("GfxActions", gfx_profile_actions, NULL, RSRC_CONF,
                     "Defines a list of actions to perform on the image"),
     AP_INIT_TAKE1("GfxRsX", gfx_profile_rsx, NULL, RSRC_CONF,
                   "Specifies a maximum width to resize to"),
     AP_INIT_TAKE1("GfxRsY", gfx_profile_rsy, NULL, RSRC_CONF,
                   "Specifies a maximum height to resize to"),
     AP_INIT_TAKE1("GfxQual", gfx_profile_qual, NULL, RSRC_CONF,
                   "Specifies the quality (if supported by output format)"),
     AP_INIT_TAKE1("GfxOutputFormat", gfx_profile_outputformat, NULL, 
                   "Specifies the ouput format"),
     AP_INIT_TAKE2("GfxOriginMatch", gfx_config_originmatch, NULL, 
                   "A regex to declare origins"),
     AP_INIT_TAKE1("GfxOriginHost", gfx_config_originhost, NULL, RSRC_CONF,
                   "Define a host to origin content from"),
     { NULL }

module AP_MODULE_DECLARE_DATA gfx_module = {
     NULL,                        //do I really need this?
     NULL,                        //i'm too lazy to implement merging
     gfx_create_server_config,    //create server config
     NULL,                        //i'm too lazy to implement merging
     gfx_cmds,                    //command table
     gfx_register_hooks           //hooks

View raw message