You are viewing a plain text version of this content. The canonical link for it is here.
Posted to modules-dev@httpd.apache.org by Michael Spiegle <mi...@nauticaltech.com> on 2009/02/10 06:55:00 UTC

How do you handle multiple custom config containers?

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:

http://image.server.com/largesize/12/23/34/122334.jpg
http://image.server.com/thumbnail/images/logo.png
http://image.server.com/watermark/m/my_dog.gif

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>

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

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



**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
     GfxOriginHost server1.olddomain.com
     GfxOriginHost server2.olddomain.com
</GfxOrigin>

<GfxOrigin "NewServer">
     GfxOriginHost server1.imageserver.com
     GfxOriginHost server2.imageserver.com
     GfxOriginHost server3.imageserver.com
</GfxOrigin>

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 
questions:

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.


Thanks,
Mike




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 | 
NOT_IN_DIRECTORY);

     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 
config
     //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 
<GfxProfile>
     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, 
profile);

     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 
Holder"),
     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, 
RSRC_CONF,
                   "Specifies the ouput format"),
     AP_INIT_TAKE2("GfxOriginMatch", gfx_config_originmatch, NULL, 
RSRC_CONF,
                   "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 = {
     STANDARD20_MODULE_STUFF,
     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
};

Re: How do you handle multiple custom config containers?

Posted by Michael Spiegle <mi...@nauticaltech.com>.
Michael Spiegle wrote:
> 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.

I don't think it is correct, but I have found a solution for now.  Since 
  I can only seem to execute directives inside a container via 
ap_walk_config(), and it requires a section_vector to be working, I'm 
(ab)using ap_set_config_vectors() to send a tag to the per-dir config 
allocator so it knows what type of config to allocate.  I have no idea 
how bad this is, but it appears to be working.  Here's the relevant code:

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

     ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Entering 
gfx_config_profile callback");

     //create a new profile for each <GfxProfile> encountered
     gfx_conf = ap_create_per_dir_config(cmd->pool);

     const char* err = ap_check_cmd_context(cmd, NOT_IN_LIMIT | 
NOT_IN_DIRECTORY);

     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 abusing this by sending <GfxProfile> so the container creator 
knows what type to give me back
     gfx_profile_config_t* profile = ap_set_config_vectors(cmd->server, 
gfx_conf, "<GfxProfile>", &gfx_module, cmd->pool);

     //setup profile
     profile->name = apr_pstrdup(cmd->pool, profile_name);

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

     //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, 
profile);

     return NULL;
}

static const char*
gfx_config_origin(cmd_parms* cmd, void* p, const char* arg) {
     const char* endp = ap_strrchr_c(arg, '>');
     const char* args;
     char* origin_name;
     ap_conf_vector_t* gfx_conf;

     ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Entering 
gfx_config_origin callback");

     //create a new origin for each <GfxOrigin> encountered
     gfx_conf = ap_create_per_dir_config(cmd->pool);

     const char* err = ap_check_cmd_context(cmd, NOT_IN_LIMIT | 
NOT_IN_DIRECTORY);

     if (err != NULL)
         return err;

     //get the origin name (argument to <GfxOrigin>)
     //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 origin name");

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

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

     //i think this associates the generic config type with my origin config
     gfx_origin_config_t* origin = ap_set_config_vectors(cmd->server, 
gfx_conf, "<GfxOrigin>", &gfx_module, cmd->pool);

     //setup origin
     origin->name = apr_pstrdup(cmd->pool, origin_name);

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

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

     return NULL;
}


static void*
gfx_create_server_config(apr_pool_t* pool, server_rec* server) {
     gfx_server_config_t* sconf;
     sconf = (gfx_server_config_t*)apr_pcalloc(pool, 
sizeof(gfx_server_config_t));

     sconf->profiles = apr_hash_make(pool);
     sconf->origins = apr_hash_make(pool);

     ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Created server config");

     return (void*)sconf;
}

static void*
gfx_create_container_config(apr_pool_t* pool, char* arg) {
     //I'm not really sure why apache calls this function at startup
     //I don't think I need it, so I'm just going to ignore it...
     if (arg == NULL)
         return (void*)NULL;

     //Create a GfxProfile struct on-command and pass it back to the caller
     if (strcmp(arg, "<GfxProfile>") == 0) {
         gfx_profile_config_t* profile;
         profile = (gfx_profile_config_t*)apr_pcalloc(pool, 
sizeof(gfx_profile_config_t));
         profile->name = NULL;
         profile->output_format = IMAGE_TYPE_SRC;
         profile->actions = apr_array_make(pool, 8, sizeof(const char*));
         profile->rsx = 0;
         profile->rsy = 0;
         profile->qual = 0;
         ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Created 
<GfxProfile> container");
         return profile;
     }

     //Create a GfxOrigin struct on-command and pass it back to the caller
     if (strcmp(arg, "<GfxOrigin>") == 0) {
         gfx_origin_config_t* origin;
         origin = (gfx_origin_config_t*)apr_pcalloc(pool, 
sizeof(gfx_origin_config_t));
         origin->name = NULL;
         origin->hosts = apr_array_make(pool, 16, sizeof(const char*));
         ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Created 
<GfxOrigin> container");
         return origin;
     }

     //default case.  shouldn't ever happen
     ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Error creating 
container (%s)", arg);
     return (void*)NULL;
}


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 
Holder"),
     AP_INIT_RAW_ARGS("<GfxOrigin", gfx_config_origin, NULL, RSRC_CONF,
                      "Container to create origin hosts"),
     AP_INIT_NO_ARGS("</GfxOrigin>", NULL, NULL, RSRC_CONF, "Place Holder"),
     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, 
RSRC_CONF,
                   "Specifies the ouput format"),
     AP_INIT_TAKE2("GfxOriginMatch", gfx_config_originmatch, NULL, 
RSRC_CONF,
                   "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 = {
     STANDARD20_MODULE_STUFF,
     gfx_create_container_config, //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
};