Hi,
The directive AddOutputFilterByType can be used to insert filters to the
output filter chain depending on the content type of the HTTP response.
So far so good.
PROBLEM DESCRIPTION
I observed that the behavior of this directive changed in Apache 2.4 for
filters. Starting with Apache 2.4 filters are inserted at an earlier
place in the filter chain than they were inserted with Apache 2.2. For
example, if you use the directive
AddOutputFilterByType DEFLATE text/html
The filter is inserted with AP_FTYPE_RESOURCE, even though it was
registered in mod_deflate.c as AP_FTYPE_SET_CONTENT.
The effect is that using "AddOutputFilterByType DEFLATE text/html" the
resulting filter chain is ordered diferrently compared to using
"SetOutputFilter DEFLATE".
This can be fixed in configuration by adding the following directive
right after AddOutputFilterByType:
FilterDeclare BYTYPE:DEFLATE CONTENT_SET
Unfortunately the order and placement of FilterDeclare seems to be
relevant, i.e. this fix only works if FilterDeclare comes *after*
AddOutputFilterByType within the same container.
I wonder whether this is an intentional behavior change of
AddOutputFilterByType or not.
ANALYSIS
Apparently the filter type (ap_filter_rec_t struct member ftype) of the
filter provider isn't respected anymore.
The intended filter type is provided when registering the output filter
by calling ap_register_output_filter(). In branch 2.2.x this was
sufficient, because the conditional filter (based on the MIME type) was
added directly by name (i.e. by calling ap_add_output_filter() in
ap_add_output_filters_by_type). In branches 2.4.x and trunk the
implementation of AddOutputFilterByType apparently moved to mod_filter
and a layer of indirection (the filter harness) is introduced. But the
filter harness is apparently created unconditionally with
AP_FTYPE_RESOURCE.
FIX APPROACH
When implicitly creating a filter harness by calling the function
add_filter(), we have access to the provider's ap_filter_rec_t anyways.
So I recommend to just copy it from the filter provider (e.g. DEFLATE)
to the filter harness (e.g. BYTYPE:DEFLATE).
I've tested this approach with the patch below for a setup without any
FilterDeclare directive, and it fixed the regression; the DEFLATE filter
was ordered correctly at AP_FTYPE_CONTENT_SET again.
--------------------- 8><
--------------------------------------------------
diff --git a/modules/filters/mod_filter.c b/modules/filters/mod_filter.c
index 7b69223..5b5ecf6 100644
--- a/modules/filters/mod_filter.c
+++ b/modules/filters/mod_filter.c
@@ -444,6 +444,12 @@ static const char *add_filter(cmd_parms *cmd, void
*CFG,
ap_expr_info_t *node;
const char *err = NULL;
+ /* if provider has been registered, we can look it up */
+ provider_frec = ap_get_output_filter_handle(pname);
+ if (!provider_frec) {
+ return apr_psprintf(cmd->pool, "Unknown filter provider %s",
pname);
+ }
+
/* fname has been declared with DeclareFilter, so we can look it up
*/
frec = apr_hash_get(cfg->live_filters, fname, APR_HASH_KEY_STRING);
@@ -454,17 +460,13 @@ static const char *add_filter(cmd_parms *cmd, void
*CFG,
return c;
}
frec = apr_hash_get(cfg->live_filters, fname,
APR_HASH_KEY_STRING);
+ frec->ftype = provider_frec->ftype;
}
if (!frec) {
return apr_psprintf(cmd->pool, "Undeclared smart filter %s",
fname);
}
- /* if provider has been registered, we can look it up */
- provider_frec = ap_get_output_filter_handle(pname);
- if (!provider_frec) {
- return apr_psprintf(cmd->pool, "Unknown filter provider %s",
pname);
- }
provider = apr_palloc(cmd->pool, sizeof(ap_filter_provider_t));
if (expr) {
node = ap_expr_parse_cmd(cmd, expr, 0, &err, NULL);
--------------------- 8><
--------------------------------------------------
For setups with both, FilterDeclare and AddOutputFilterByType (as
described above as fix), I observed some issues with properly merging
the two filter harnesses. However, I have no clue what semantics the
original author wanted to have in this situation.
I hope my explanations are clear enough for others to follows. If not,
please don't hesitate to ask.
Best regards,
Micha