/* Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License.
*/
/* * mod_headers.c: Add/append/remove HTTP response headers * Written by Paul Sutton, paul@ukweb.com, 1 Oct 1996 * * The Header directive can be used to add/replace/remove HTTP headers * within the response message. The RequestHeader directive can be used * to add/replace/remove HTTP headers before a request message is processed. * Valid in both per-server and per-dir configurations. * * Syntax is: * * Header action header value * RequestHeader action header value * * Where action is one of: * set - set this header, replacing any old value * add - add this header, possible resulting in two or more * headers with the same name * append - append this text onto any existing header of this same * merge - merge this text onto any existing header of this same, * avoiding duplicate values * unset - remove this header * edit - transform the header value according to a regexp * * Where action is unset, the third argument (value) should not be given. * The header name can include the colon, or not. * * The Header and RequestHeader directives can only be used where allowed * by the FileInfo override. * * When the request is processed, the header directives are processed in * this order: firstly, the main server, then the virtual server handling * this request (if any), then any <Directory> sections (working downwards * from the root dir), then an <Location> sections (working down from * shortest URL component), the any <File> sections. This order is * important if any 'set' or 'unset' actions are used. For example, * the following two directives have different effect if applied in * the reverse order: * * Header append Author "John P. Doe" * Header unset Author * * Examples: * * To set the "Author" header, use * Header add Author "John P. Doe" * * To remove a header: * Header unset Author *
*/
/* format_tag_hash is initialized during pre-config */ static apr_hash_t *format_tag_hash;
typedefenum {
hdr_add = 'a', /* add header (could mean multiple hdrs) */
hdr_set = 's', /* set (replace old value) */
hdr_append = 'm', /* append (merge into any old value) */
hdr_merge = 'g', /* merge (merge, but avoid duplicates) */
hdr_unset = 'u', /* unset header */
hdr_echo = 'e', /* echo headers from request to response */
hdr_edit = 'r', /* change value by regexp, match once */
hdr_edit_r = 'R', /* change value by regexp, everymatch */
hdr_setifempty = 'i', /* set value if header not already present*/
hdr_note = 'n'/* set value of header in a note */
} hdr_actions;
/* Callback function type. */ typedefconstchar *format_tag_fn(request_rec *r, char *a);
/* * There is an array of struct format_tag per Header/RequestHeader * config directive
*/ typedefstruct {
format_tag_fn *func; char *arg;
} format_tag;
/* 'Magic' condition_var value to run action in post_read_request */ staticconstchar* condition_early = "early"; /* * There is one "header_entry" per Header/RequestHeader config directive
*/ typedefstruct {
hdr_actions action; constchar *header;
apr_array_header_t *ta; /* Array of format_tag structs */
ap_regex_t *regex; constchar *condition_var; constchar *subs;
ap_expr_info_t *expr;
ap_expr_info_t *expr_out;
} header_entry;
/* echo_do is used for Header echo to iterate through the request headers*/ typedefstruct {
request_rec *r;
header_entry *hdr;
} echo_do;
/* edit_do is used for Header edit to iterate through the request headers */ typedefstruct {
request_rec *r;
header_entry *hdr;
apr_table_t *t;
} edit_do;
/* * headers_conf is our per-module configuration. This is used as both * a per-dir and per-server config
*/ typedefstruct {
apr_array_header_t *fixup_in;
apr_array_header_t *fixup_out;
apr_array_header_t *fixup_err;
} headers_conf;
s = *sa; while (*s && *s != '%') {
s++;
} /* * This might allocate a few chars extra if there's a backslash * escape in the format string.
*/
tag->arg = apr_palloc(p, s - *sa + 1);
d = tag->arg;
s = *sa; while (*s && *s != '%') { if (*s != '\\') {
*d++ = *s++;
} else {
s++; switch (*s) { case'\\':
*d++ = '\\';
s++; break; case'r':
*d++ = '\r';
s++; break; case'n':
*d++ = '\n';
s++; break; case't':
*d++ = '\t';
s++; break; default: /* copy verbatim */
*d++ = '\\'; /* * Allow the loop to deal with this *s in the normal * fashion so that it handles end of string etc. * properly.
*/ break;
}
}
}
*d = '\0';
/* Pass through %% or % at end of string as % */ if ((*s == '%') || (*s == '\0')) {
tag->func = constant_item;
tag->arg = "%"; if (*s)
s++;
*sa = s; return NULL;
}
tag->arg = "\0"; /* grab the argument if there is one */ if (*s == '{') {
++s;
tag->arg = ap_getword(p,&s,'}');
}
/* * A format string consists of white space, text and optional format * tags in any order. E.g., * * Header add MyHeader "Free form text %D %t more text" * * Decompose the format string into its tags. Each tag (struct format_tag) * contains a pointer to the function used to format the tag. Then save each * tag in the tag array anchored in the header_entry.
*/ staticchar *parse_format_string(cmd_parms *cmd, header_entry *hdr, constchar *s)
{
apr_pool_t *p = cmd->pool; char *res;
/* No string to parse with unset and echo commands */ if (hdr->action == hdr_unset || hdr->action == hdr_echo) { return NULL;
} /* Tags are in the replacement value for edit */ elseif (hdr->action == hdr_edit || hdr->action == hdr_edit_r ) {
s = hdr->subs;
}
if (new->action == hdr_edit || new->action == hdr_edit_r) { if (subs == NULL) { return"Header edit requires a match and a substitution";
}
new->regex = ap_pregcomp(cmd->pool, value, AP_REG_EXTENDED); if (new->regex == NULL) { return"Header edit regex could not be compiled";
}
new->subs = subs;
} else { /* there's no subs, so envclause is really that argument */ if (envclause != NULL) { return"Too many arguments to directive";
}
envclause = subs;
} if (new->action == hdr_unset) { if (value) { if (envclause) { return"header unset takes two arguments";
}
envclause = value;
value = NULL;
}
} elseif (new->action == hdr_echo) {
ap_regex_t *regex;
if (value) { if (envclause) { return"Header echo takes two arguments";
}
envclause = value;
value = NULL;
} if (cmd->info != &hdr_out_onsuccess && cmd->info != &hdr_out_always) return"Header echo only valid on Header " "directives"; else {
regex = ap_pregcomp(cmd->pool, hdr, AP_REG_EXTENDED | AP_REG_NOSUB); if (regex == NULL) { return"Header echo regex could not be compiled";
}
}
new->regex = regex;
} elseif (!value) return"Header requires three arguments";
/* Handle the envclause on Header */ if (envclause != NULL) { if (strcasecmp(envclause, "early") == 0) {
condition_var = condition_early;
} elseif (strncasecmp(envclause, "env=", 4) == 0) { if ((envclause[4] == '\0')
|| ((envclause[4] == '!') && (envclause[5] == '\0'))) { return"error: missing environment variable name. " "envclause should be in the form env=envar ";
}
condition_var = envclause + 4;
} elseif (strncasecmp(envclause, "expr=", 5) == 0) { constchar *err = NULL;
expr = ap_expr_parse_cmd(cmd, envclause + 5, 0, &err, NULL); if (err) { return apr_pstrcat(cmd->pool, "Can't parse envclause/expression: ", err,
NULL);
}
} else { return apr_pstrcat(cmd->pool, "Unknown parameter: ", envclause,
NULL);
}
}
/* * Process the tags in the format string. Tags may be format specifiers * (%D, %t, etc.), whitespace or text strings. For each tag, run the handler * (formatter) specific to the tag. Handlers return text strings. * Concatenate the return from each handler into one string that is * returned from this call. * If the original value was prefixed with "expr=", processing is * handled instead by ap_expr.
*/ staticchar* process_tags(header_entry *hdr, request_rec *r)
{ int i; constchar *s; char *str = NULL;
format_tag *tag = NULL;
if (hdr->expr_out) { constchar *err; constchar *val;
val = ap_expr_str_exec(r, hdr->expr_out, &err); if (err) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(02557) "Can't evaluate value expression: %s", err); return"";
} return apr_pstrdup(r->pool, val);
}
tag = (format_tag*) hdr->ta->elts;
for (i = 0; i < hdr->ta->nelts; i++) {
s = tag[i].func(r, tag[i].arg); if (str == NULL)
str = apr_pstrdup(r->pool, s); else
str = apr_pstrcat(r->pool, str, s, NULL);
} return str ? str : "";
} staticconstchar *process_regexp(header_entry *hdr, constchar *value,
request_rec *r)
{
ap_regmatch_t pmatch[AP_MAX_REG_MATCH]; constchar *subs; constchar *remainder; char *ret; int diffsz; if (ap_regexec(hdr->regex, value, AP_MAX_REG_MATCH, pmatch, 0)) { /* no match, nothing to do */ return value;
} /* Process tags in the input string rather than the resulting * substitution to avoid surprises
*/
subs = ap_pregsub(r->pool, process_tags(hdr, r), value, AP_MAX_REG_MATCH, pmatch); if (subs == NULL) return NULL;
diffsz = strlen(subs) - (pmatch[0].rm_eo - pmatch[0].rm_so); if (hdr->action == hdr_edit) {
remainder = value + pmatch[0].rm_eo;
} else { /* recurse to edit multiple matches if applicable */
remainder = process_regexp(hdr, value + pmatch[0].rm_eo, r); if (remainder == NULL) return NULL;
diffsz += strlen(remainder) - strlen(value + pmatch[0].rm_eo);
}
ret = apr_palloc(r->pool, strlen(value) + 1 + diffsz);
memcpy(ret, value, pmatch[0].rm_so);
strcpy(ret + pmatch[0].rm_so, subs);
strcat(ret, remainder); return ret;
}
/* If the input header (key) matches the regex, echo it intact to * r->headers_out.
*/ if (!ap_regexec(ed->hdr->regex, key, 0, NULL, 0)) {
apr_table_add(ed->r->headers_out, key, val);
}
/* do the fixup */
do_headers_fixup(f->r, f->r->err_headers_out, dirconf->fixup_err, 0);
do_headers_fixup(f->r, f->r->headers_out, dirconf->fixup_out, 0);
/* remove ourselves from the filter chain */
ap_remove_output_filter(f);
/* send the data up the stack */ return ap_pass_brigade(f->next,in);
}
/* * Make sure we propagate any "Header always" settings on the error * path through http_protocol.c.
*/ static apr_status_t ap_headers_error_filter(ap_filter_t *f,
apr_bucket_brigade *in)
{
headers_conf *dirconf;
/* * Add any header fields defined by "Header always" to r->err_headers_out. * Server-wide first, then per-directory to allow overriding.
*/
do_headers_fixup(f->r, f->r->err_headers_out, dirconf->fixup_err, 0);
/* * We've done our bit; remove ourself from the filter chain so there's * no possibility we'll be called again.
*/
ap_remove_output_filter(f);
/* do the fixup */ if (dirconf->fixup_in->nelts) { if (!do_headers_fixup(r, r->headers_in, dirconf->fixup_in, 1)) goto err;
} if (dirconf->fixup_err->nelts) { if (!do_headers_fixup(r, r->err_headers_out, dirconf->fixup_err, 1)) goto err;
} if (dirconf->fixup_out->nelts) { if (!do_headers_fixup(r, r->headers_out, dirconf->fixup_out, 1)) goto err;
}
staticconst command_rec headers_cmds[] =
{
AP_INIT_RAW_ARGS("Header", header_cmd, &hdr_out_onsuccess, OR_FILEINFO, "an optional condition, an action, header and value " "followed by optional env clause"),
AP_INIT_RAW_ARGS("RequestHeader", header_cmd, &hdr_in, OR_FILEINFO, "an action, header and value followed by optional env " "clause"),
{NULL}
};
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.