/* 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
*
*/
#include "apr.h"
#include "apr_lib.h"
#include "apr_strings.h"
#include "apr_buckets.h"
#include "apr_hash.h"
#define APR_WANT_STRFUNC
#include "apr_want.h"
#include "httpd.h"
#include "http_config.h"
#include "http_request.h"
#include "http_ssl.h"
#include "http_log.h"
#include "util_filter.h"
#include "http_protocol.h"
#include "ap_expr.h"
/* format_tag_hash is initialized during pre-config */
static apr_hash_t *format_tag_hash;
typedef enum {
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;
/*
* magic cmd - > info values
*/
static char hdr_in = '0' ; /* RequestHeader */
static char hdr_out_onsuccess = '1' ; /* Header onsuccess */
static char hdr_out_always = '2' ; /* Header always */
/* Callback function type. */
typedef const char *format_tag_fn(request_rec *r, char *a);
/*
* There is an array of struct format_tag per Header / RequestHeader
* config directive
*/
typedef struct {
format_tag_fn *func;
char *arg;
} format_tag;
/* 'Magic' condition_var value to run action in post_read_request */
static const char * condition_early = "early" ;
/*
* There is one " header_entry " per Header / RequestHeader config directive
*/
typedef struct {
hdr_actions action;
const char *header;
apr_array_header_t *ta; /* Array of format_tag structs */
ap_regex_t *regex;
const char *condition_var;
const char *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*/
typedef struct {
request_rec *r;
header_entry *hdr;
} echo_do;
/* edit_do is used for Header edit to iterate through the request headers */
typedef struct {
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
*/
typedef struct {
apr_array_header_t *fixup_in;
apr_array_header_t *fixup_out;
apr_array_header_t *fixup_err;
} headers_conf;
module AP_MODULE_DECLARE_DATA headers_module;
/*
* Tag formatting functions
*/
static const char *constant_item(request_rec *r, char *stuff)
{
return stuff;
}
static const char *header_request_duration(request_rec *r, char *a)
{
return apr_psprintf(r->pool, "D=%" APR_TIME_T_FMT,
(apr_time_now() - r->request_time));
}
static const char *header_request_time(request_rec *r, char *a)
{
return apr_psprintf(r->pool, "t=%" APR_TIME_T_FMT, r->request_time);
}
/* unwrap_header returns HDR with any newlines converted into
* whitespace if necessary. */
static const char *unwrap_header(apr_pool_t *p, const char *hdr)
{
if (ap_strchr_c(hdr, APR_ASCII_LF) || ap_strchr_c(hdr, APR_ASCII_CR)) {
char *ptr;
hdr = ptr = apr_pstrdup(p, hdr);
do {
if (*ptr == APR_ASCII_LF || *ptr == APR_ASCII_CR)
*ptr = APR_ASCII_BLANK;
} while (*ptr++);
}
return hdr;
}
static const char *header_request_env_var(request_rec *r, char *a)
{
const char *s = apr_table_get(r->subprocess_env,a);
if (s)
return unwrap_header(r->pool, s);
else
return "(null)" ;
}
static const char *header_request_ssl_var(request_rec *r, char *name)
{
const char *val = ap_ssl_var_lookup(r->pool, r->server,
r->connection, r, name);
if (val && val[0 ])
return unwrap_header(r->pool, val);
else
return "(null)" ;
}
static const char *header_request_loadavg(request_rec *r, char *a)
{
ap_loadavg_t t;
ap_get_loadavg(&t);
return apr_psprintf(r->pool, "l=%.2f/%.2f/%.2f" , t.loadavg,
t.loadavg5, t.loadavg15);
}
static const char *header_request_idle(request_rec *r, char *a)
{
ap_sload_t t;
ap_get_sload(&t);
return apr_psprintf(r->pool, "i=%d" , t.idle);
}
static const char *header_request_busy(request_rec *r, char *a)
{
ap_sload_t t;
ap_get_sload(&t);
return apr_psprintf(r->pool, "b=%d" , t.busy);
}
/*
* Config routines
*/
static void *create_headers_dir_config(apr_pool_t *p, char *d)
{
headers_conf *conf = apr_pcalloc(p, sizeof (*conf));
conf->fixup_in = apr_array_make(p, 2 , sizeof (header_entry));
conf->fixup_out = apr_array_make(p, 2 , sizeof (header_entry));
conf->fixup_err = apr_array_make(p, 2 , sizeof (header_entry));
return conf;
}
static void *merge_headers_config(apr_pool_t *p, void *basev, void *overridesv)
{
headers_conf *newconf = apr_pcalloc(p, sizeof (*newconf));
headers_conf *base = basev;
headers_conf *overrides = overridesv;
newconf->fixup_in = apr_array_append(p, base->fixup_in,
overrides->fixup_in);
newconf->fixup_out = apr_array_append(p, base->fixup_out,
overrides->fixup_out);
newconf->fixup_err = apr_array_append(p, base->fixup_err,
overrides->fixup_err);
return newconf;
}
static char *parse_misc_string(apr_pool_t *p, format_tag *tag, const char **sa)
{
const char *s;
char *d;
tag->func = constant_item;
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' ;
*sa = s;
return NULL;
}
static char *parse_format_tag(apr_pool_t *p, format_tag *tag, const char **sa)
{
const char *s = *sa;
const char * (*tag_handler)(request_rec *,char *);
/* Handle string literal/conditionals */
if (*s != '%' ) {
return parse_misc_string(p, tag, sa);
}
s++; /* skip the % */
/* 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,'}' );
}
tag_handler = (const char * (*)(request_rec *,char *))apr_hash_get(format_tag_hash, s++, 1 );
if (!tag_handler) {
char dummy[2 ];
dummy[0 ] = s[-1 ];
dummy[1 ] = '\0' ;
return apr_pstrcat(p, "Unrecognized header format %" , dummy, NULL);
}
tag->func = tag_handler;
*sa = s;
return NULL;
}
/*
* 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 .
*/
static char *parse_format_string(cmd_parms *cmd, header_entry *hdr, const char *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 */
else if (hdr->action == hdr_edit || hdr->action == hdr_edit_r ) {
s = hdr->subs;
}
if (!strncmp(s, "expr=" , 5 )) {
const char *err;
hdr->expr_out = ap_expr_parse_cmd(cmd, s+5 ,
AP_EXPR_FLAG_STRING_RESULT,
&err, NULL);
if (err) {
return apr_pstrcat(cmd->pool,
"Can't parse value expression : " , err, NULL);
}
return NULL;
}
hdr->ta = apr_array_make(p, 10 , sizeof (format_tag));
while (*s) {
if ((res = parse_format_tag(p, (format_tag *) apr_array_push(hdr->ta), &s))) {
return res;
}
}
return NULL;
}
/* handle RequestHeader and Header directive */
static APR_INLINE const char *header_inout_cmd(cmd_parms *cmd,
void *indirconf,
const char *action,
const char *hdr,
const char *value,
const char *subs,
const char *envclause)
{
headers_conf *dirconf = indirconf;
const char *condition_var = NULL;
const char *colon;
header_entry *new ;
ap_expr_info_t *expr = NULL;
apr_array_header_t *fixup = (cmd->info == &hdr_in)
? dirconf->fixup_in : (cmd->info == &hdr_out_always)
? dirconf->fixup_err
: dirconf->fixup_out;
new = (header_entry *) apr_array_push(fixup);
if (!strcasecmp(action, "set" ))
new ->action = hdr_set;
else if (!strcasecmp(action, "setifempty" ))
new ->action = hdr_setifempty;
else if (!strcasecmp(action, "add" ))
new ->action = hdr_add;
else if (!strcasecmp(action, "append" ))
new ->action = hdr_append;
else if (!strcasecmp(action, "merge" ))
new ->action = hdr_merge;
else if (!strcasecmp(action, "unset" ))
new ->action = hdr_unset;
else if (!strcasecmp(action, "echo" ))
new ->action = hdr_echo;
else if (!strcasecmp(action, "edit" ))
new ->action = hdr_edit;
else if (!strcasecmp(action, "edit*" ))
new ->action = hdr_edit_r;
else if (!strcasecmp(action, "note" ))
new ->action = hdr_note;
else
return "first argument must be 'add', 'set', 'setifempty', 'append', 'merge', "
"'unset', 'echo', 'note', 'edit', or 'edit*'." ;
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;
}
}
else if (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;
}
else if (!value)
return "Header requires three arguments" ;
/* Handle the envclause on Header */
if (envclause != NULL) {
if (strcasecmp(envclause, "early" ) == 0 ) {
condition_var = condition_early;
}
else if (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 ;
}
else if (strncasecmp(envclause, "expr=" , 5 ) == 0 ) {
const char *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);
}
}
if ((colon = ap_strchr_c(hdr, ':' ))) {
hdr = apr_pstrmemdup(cmd->pool, hdr, colon-hdr);
}
new ->header = hdr;
new ->condition_var = condition_var;
new ->expr = expr;
return parse_format_string(cmd, new , value);
}
/* Handle all (xxx)Header directives */
static const char *header_cmd(cmd_parms *cmd, void *indirconf,
const char *args)
{
const char *action;
const char *hdr;
const char *val;
const char *envclause;
const char *subs;
action = ap_getword_conf(cmd->temp_pool, &args);
if (cmd->info == &hdr_out_onsuccess) {
if (!strcasecmp(action, "always" )) {
cmd->info = &hdr_out_always;
action = ap_getword_conf(cmd->temp_pool, &args);
}
else if (!strcasecmp(action, "onsuccess" )) {
action = ap_getword_conf(cmd->temp_pool, &args);
}
}
hdr = ap_getword_conf(cmd->pool, &args);
val = *args ? ap_getword_conf(cmd->pool, &args) : NULL;
subs = *args ? ap_getword_conf(cmd->pool, &args) : NULL;
envclause = *args ? ap_getword_conf(cmd->pool, &args) : NULL;
if (*args) {
return apr_pstrcat(cmd->pool, cmd->cmd->name,
" has too many arguments" , NULL);
}
return header_inout_cmd(cmd, indirconf, action, hdr, val, subs, envclause);
}
/*
* 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 .
*/
static char * process_tags(header_entry *hdr, request_rec *r)
{
int i;
const char *s;
char *str = NULL;
format_tag *tag = NULL;
if (hdr->expr_out) {
const char *err;
const char *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 : "" ;
}
static const char *process_regexp(header_entry *hdr, const char *value,
request_rec *r)
{
ap_regmatch_t pmatch[AP_MAX_REG_MATCH];
const char *subs;
const char *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;
}
static int echo_header(void *v, const char *key, const char *val)
{
echo_do *ed = (echo_do *)v;
/* 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);
}
return 1 ;
}
static int edit_header(void *v, const char *key, const char *val)
{
edit_do *ed = (edit_do *)v;
const char *repl = process_regexp(ed->hdr, val, ed->r);
if (repl == NULL)
return 0 ;
apr_table_addn(ed->t, key, repl);
return 1 ;
}
static int add_them_all(void *v, const char *key, const char *val)
{
apr_table_t *headers = (apr_table_t *)v;
apr_table_addn(headers, key, val);
return 1 ;
}
static int do_headers_fixup(request_rec *r, apr_table_t *headers,
apr_array_header_t *fixup, int early)
{
echo_do v;
int i;
const char *val;
for (i = 0 ; i < fixup->nelts; ++i) {
header_entry *hdr = &((header_entry *) (fixup->elts))[i];
const char *envar = hdr->condition_var;
/* ignore early headers in late calls */
if (!early && (envar == condition_early)) {
continue ;
}
/* ignore late headers in early calls */
else if (early && (envar != condition_early)) {
continue ;
}
/* Do we have an expression to evaluate? */
else if (hdr->expr != NULL) {
const char *err = NULL;
int eval = ap_expr_exec(r, hdr->expr, &err);
if (err) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0 , r, APLOGNO(01501 )
"Failed to evaluate expression (%s) - ignoring" ,
err);
}
else if (!eval) {
continue ;
}
}
/* Have any conditional envar-controlled Header processing to do? */
else if (envar && !early) {
if (*envar != '!' ) {
if (apr_table_get(r->subprocess_env, envar) == NULL)
continue ;
}
else {
if (apr_table_get(r->subprocess_env, &envar[1 ]) != NULL)
continue ;
}
}
switch (hdr->action) {
case hdr_add:
apr_table_addn(headers, hdr->header, process_tags(hdr, r));
break ;
case hdr_append:
apr_table_mergen(headers, hdr->header, process_tags(hdr, r));
break ;
case hdr_merge:
val = apr_table_get(headers, hdr->header);
if (val == NULL) {
apr_table_addn(headers, hdr->header, process_tags(hdr, r));
} else {
char *new_val = process_tags(hdr, r);
apr_size_t new_val_len = strlen(new_val);
int tok_found = 0 ;
/* modified version of logic in ap_get_token() */
while (*val) {
const char *tok_start;
while (apr_isspace(*val))
++val;
tok_start = val;
while (*val && *val != ',' ) {
if (*val++ == '"' )
while (*val)
if (*val++ == '"' )
break ;
}
if (new_val_len == (apr_size_t)(val - tok_start)
&& !strncmp(tok_start, new_val, new_val_len)) {
tok_found = 1 ;
break ;
}
if (*val)
++val;
}
if (!tok_found) {
apr_table_mergen(headers, hdr->header, new_val);
}
}
break ;
case hdr_set:
if (r->headers_in != headers &&
!ap_cstr_casecmp(hdr->header, "Content-Type" )) {
ap_set_content_type(r, process_tags(hdr, r));
}
apr_table_setn(headers, hdr->header, process_tags(hdr, r));
break ;
case hdr_setifempty:
if (NULL == apr_table_get(headers, hdr->header)) {
if (r->headers_in != headers &&
!ap_cstr_casecmp(hdr->header, "Content-Type" )) {
ap_set_content_type(r, process_tags(hdr, r));
}
apr_table_setn(headers, hdr->header, process_tags(hdr, r));
}
break ;
case hdr_unset:
apr_table_unset(headers, hdr->header);
if (r->headers_in != headers &&
!ap_cstr_casecmp(hdr->header, "Content-Type" )) {
ap_set_content_type(r, NULL);
}
break ;
case hdr_echo:
v.r = r;
v.hdr = hdr;
apr_table_do(echo_header, &v, r->headers_in, NULL);
break ;
case hdr_edit:
case hdr_edit_r:
if (!ap_cstr_casecmp(hdr->header, "Content-Type" ) && r->content_type) {
const char *repl = process_regexp(hdr, r->content_type, r);
if (repl == NULL)
return 0 ;
if (r->headers_in != headers) ap_set_content_type_ex(r, repl, 1 );
}
if (apr_table_get(headers, hdr->header)) {
edit_do ed;
ed.r = r;
ed.hdr = hdr;
ed.t = apr_table_make(r->pool, 5 );
if (!apr_table_do(edit_header, (void *) &ed, headers,
hdr->header, NULL))
return 0 ;
apr_table_unset(headers, hdr->header);
apr_table_do(add_them_all, (void *) headers, ed.t, NULL);
}
break ;
case hdr_note:
apr_table_setn(r->notes, process_tags(hdr, r), apr_table_get(headers, hdr->header));
break ;
}
}
return 1 ;
}
static void ap_headers_insert_output_filter(request_rec *r)
{
headers_conf *dirconf = ap_get_module_config(r->per_dir_config,
&headers_module);
if (dirconf->fixup_out->nelts || dirconf->fixup_err->nelts) {
ap_add_output_filter("FIXUP_HEADERS_OUT" , NULL, r, r->connection);
}
}
/*
* Make sure our error - path filter is in place .
*/
static void ap_headers_insert_error_filter(request_rec *r)
{
headers_conf *dirconf = ap_get_module_config(r->per_dir_config,
&headers_module);
if (dirconf->fixup_err->nelts) {
ap_add_output_filter("FIXUP_HEADERS_ERR" , NULL, r, r->connection);
}
}
static apr_status_t ap_headers_output_filter(ap_filter_t *f,
apr_bucket_brigade *in)
{
headers_conf *dirconf = ap_get_module_config(f->r->per_dir_config,
&headers_module);
ap_log_error(APLOG_MARK, APLOG_TRACE2, 0 , f->r->server, APLOGNO(01502 )
"headers: ap_headers_output_filter()" );
/* 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;
dirconf = ap_get_module_config(f->r->per_dir_config,
&headers_module);
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0 , f->r->server, APLOGNO(01503 )
"headers: ap_headers_error_filter()" );
/*
* 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);
/*
* Pass the buck . ( euro ? )
*/
return ap_pass_brigade(f->next, in);
}
static apr_status_t ap_headers_fixup(request_rec *r)
{
headers_conf *dirconf = ap_get_module_config(r->per_dir_config,
&headers_module);
/* do the fixup */
if (dirconf->fixup_in->nelts) {
do_headers_fixup(r, r->headers_in, dirconf->fixup_in, 0 );
}
return DECLINED;
}
static apr_status_t ap_headers_early(request_rec *r)
{
headers_conf *dirconf = ap_get_module_config(r->per_dir_config,
&headers_module);
/* 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;
}
return DECLINED;
err:
ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0 , r, APLOGNO(01504 )
"Regular expression replacement failed (replacement too long?)" );
return HTTP_INTERNAL_SERVER_ERROR;
}
static const 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}
};
static void register_format_tag_handler(const char *tag,
format_tag_fn *tag_handler)
{
apr_hash_set(format_tag_hash, tag, 1 , tag_handler);
}
static int header_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp)
{
format_tag_hash = apr_hash_make(p);
register_format_tag_handler("D" , header_request_duration);
register_format_tag_handler("t" , header_request_time);
register_format_tag_handler("e" , header_request_env_var);
register_format_tag_handler("s" , header_request_ssl_var);
register_format_tag_handler("l" , header_request_loadavg);
register_format_tag_handler("i" , header_request_idle);
register_format_tag_handler("b" , header_request_busy);
return OK;
}
static int header_post_config(apr_pool_t *pconf, apr_pool_t *plog,
apr_pool_t *ptemp, server_rec *s)
{
return OK;
}
static void register_hooks(apr_pool_t *p)
{
ap_register_output_filter("FIXUP_HEADERS_OUT" , ap_headers_output_filter,
NULL, AP_FTYPE_CONTENT_SET);
ap_register_output_filter("FIXUP_HEADERS_ERR" , ap_headers_error_filter,
NULL, AP_FTYPE_CONTENT_SET);
ap_hook_pre_config(header_pre_config,NULL,NULL,APR_HOOK_MIDDLE);
ap_hook_post_config(header_post_config,NULL,NULL,APR_HOOK_MIDDLE);
ap_hook_insert_filter(ap_headers_insert_output_filter, NULL, NULL, APR_HOOK_LAST);
ap_hook_insert_error_filter(ap_headers_insert_error_filter,
NULL, NULL, APR_HOOK_LAST);
ap_hook_fixups(ap_headers_fixup, NULL, NULL, APR_HOOK_LAST);
ap_hook_post_read_request(ap_headers_early, NULL, NULL, APR_HOOK_FIRST);
}
AP_DECLARE_MODULE(headers) =
{
STANDARD20_MODULE_STUFF,
create_headers_dir_config, /* dir config creater */
merge_headers_config, /* dir merger --- default is to override */
NULL, /* server config */
NULL, /* merge server configs */
headers_cmds, /* command apr_table_t */
register_hooks /* register hooks */
};
Messung V0.5 in Prozent C=94 H=90 G=91
¤ Dauer der Verarbeitung: 0.30 Sekunden
(vorverarbeitet am 2026-09-29)
¤
*© Formatika GbR, Deutschland