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
};