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