1 /* Copyright 1999-2004 The Apache Software Foundation
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
17 * mod_headers.c: Add/append/remove HTTP response headers
18 * Written by Paul Sutton, paul@ukweb.com, 1 Oct 1996
20 * The Header directive can be used to add/replace/remove HTTP headers
21 * within the response message. The RequestHeader directive can be used
22 * to add/replace/remove HTTP headers before a request message is processed.
23 * Valid in both per-server and per-dir configurations.
27 * Header action header value
28 * RequestHeader action header value
30 * Where action is one of:
31 * set - set this header, replacing any old value
32 * add - add this header, possible resulting in two or more
33 * headers with the same name
34 * append - append this text onto any existing header of this same
35 * unset - remove this header
37 * Where action is unset, the third argument (value) should not be given.
38 * The header name can include the colon, or not.
40 * The Header and RequestHeader directives can only be used where allowed
41 * by the FileInfo override.
43 * When the request is processed, the header directives are processed in
44 * this order: firstly, the main server, then the virtual server handling
45 * this request (if any), then any <Directory> sections (working downwards
46 * from the root dir), then an <Location> sections (working down from
47 * shortest URL component), the any <File> sections. This order is
48 * important if any 'set' or 'unset' actions are used. For example,
49 * the following two directives have different effect if applied in
52 * Header append Author "John P. Doe"
57 * To set the "Author" header, use
58 * Header add Author "John P. Doe"
67 #include "apr_strings.h"
68 #include "apr_buckets.h"
71 #define APR_WANT_STRFUNC
75 #include "http_config.h"
76 #include "http_request.h"
78 #include "util_filter.h"
79 #include "http_protocol.h"
81 #include "mod_ssl.h" /* for the ssl_var_lookup optional function defn */
83 /* format_tag_hash is initialized during pre-config */
84 static apr_hash_t *format_tag_hash;
87 hdr_add = 'a', /* add header (could mean multiple hdrs) */
88 hdr_set = 's', /* set (replace old value) */
89 hdr_append = 'm', /* append (merge into any old value) */
90 hdr_unset = 'u', /* unset header */
91 hdr_echo = 'e' /* echo headers from request to response */
95 * magic cmd->info values
97 static char hdr_in = '0'; /* RequestHeader */
98 static char hdr_out = '1'; /* Header */
99 static char hdr_err = '2'; /* ErrorHeader */
102 * There is an array of struct format_tag per Header/RequestHeader
106 const char* (*func)(request_rec *r,char *arg);
111 * There is one "header_entry" per Header/RequestHeader config directive
116 apr_array_header_t *ta; /* Array of format_tag structs */
118 const char *condition_var;
121 /* echo_do is used for Header echo to iterate through the request headers*/
128 * headers_conf is our per-module configuration. This is used as both
129 * a per-dir and per-server config
132 apr_array_header_t *fixup_in;
133 apr_array_header_t *fixup_out;
134 apr_array_header_t *fixup_err;
137 module AP_MODULE_DECLARE_DATA headers_module;
139 /* Pointer to ssl_var_lookup, if available. */
140 static APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *header_ssl_lookup = NULL;
143 * Tag formatting functions
145 static const char *constant_item(request_rec *r, char *stuff)
149 static const char *header_request_duration(request_rec *r, char *a)
151 return apr_psprintf(r->pool, "D=%" APR_TIME_T_FMT,
152 (apr_time_now() - r->request_time));
154 static const char *header_request_time(request_rec *r, char *a)
156 return apr_psprintf(r->pool, "t=%" APR_TIME_T_FMT, r->request_time);
159 /* unwrap_header returns HDR with any newlines converted into
160 * whitespace if necessary. */
161 static const char *unwrap_header(apr_pool_t *p, const char *hdr)
163 if (ap_strchr_c(hdr, APR_ASCII_LF) || ap_strchr_c(hdr, APR_ASCII_CR)) {
166 hdr = ptr = apr_pstrdup(p, hdr);
169 if (*ptr == APR_ASCII_LF || *ptr == APR_ASCII_CR)
170 *ptr = APR_ASCII_BLANK;
176 static const char *header_request_env_var(request_rec *r, char *a)
178 const char *s = apr_table_get(r->subprocess_env,a);
181 return unwrap_header(r->pool, s);
186 static const char *header_request_ssl_var(request_rec *r, char *name)
188 if (header_ssl_lookup) {
189 const char *val = header_ssl_lookup(r->pool, r->server,
190 r->connection, r, name);
192 return unwrap_header(r->pool, val);
205 static void *create_headers_dir_config(apr_pool_t *p, char *d)
207 headers_conf *conf = apr_pcalloc(p, sizeof(*conf));
209 conf->fixup_in = apr_array_make(p, 2, sizeof(header_entry));
210 conf->fixup_out = apr_array_make(p, 2, sizeof(header_entry));
211 conf->fixup_err = apr_array_make(p, 2, sizeof(header_entry));
216 static void *merge_headers_config(apr_pool_t *p, void *basev, void *overridesv)
218 headers_conf *newconf = apr_pcalloc(p, sizeof(*newconf));
219 headers_conf *base = basev;
220 headers_conf *overrides = overridesv;
222 newconf->fixup_in = apr_array_append(p, base->fixup_in,
223 overrides->fixup_in);
224 newconf->fixup_out = apr_array_append(p, base->fixup_out,
225 overrides->fixup_out);
226 newconf->fixup_err = apr_array_append(p, base->fixup_err,
227 overrides->fixup_err);
232 static char *parse_misc_string(apr_pool_t *p, format_tag *tag, const char **sa)
237 tag->func = constant_item;
240 while (*s && *s != '%') {
244 * This might allocate a few chars extra if there's a backslash
245 * escape in the format string.
247 tag->arg = apr_palloc(p, s - *sa + 1);
251 while (*s && *s != '%') {
278 * Allow the loop to deal with this *s in the normal
279 * fashion so that it handles end of string etc.
292 static char *parse_format_tag(apr_pool_t *p, format_tag *tag, const char **sa)
295 const char * (*tag_handler)(request_rec *,char *);
297 /* Handle string literal/conditionals */
299 return parse_misc_string(p, tag, sa);
301 s++; /* skip the % */
303 /* grab the argument if there is one */
306 tag->arg = ap_getword(p,&s,'}');
309 tag_handler = (const char * (*)(request_rec *,char *))apr_hash_get(format_tag_hash, s++, 1);
315 return apr_pstrcat(p, "Unrecognized Header or RequestHeader directive %",
318 tag->func = tag_handler;
325 * A format string consists of white space, text and optional format
326 * tags in any order. E.g.,
328 * Header add MyHeader "Free form text %D %t more text"
330 * Decompose the format string into its tags. Each tag (struct format_tag)
331 * contains a pointer to the function used to format the tag. Then save each
332 * tag in the tag array anchored in the header_entry.
334 static char *parse_format_string(apr_pool_t *p, header_entry *hdr, const char *s)
338 /* No string to parse with unset and echo commands */
339 if (hdr->action == hdr_unset ||
340 hdr->action == hdr_echo) {
344 hdr->ta = apr_array_make(p, 10, sizeof(format_tag));
347 if ((res = parse_format_tag(p, (format_tag *) apr_array_push(hdr->ta), &s))) {
354 /* handle RequestHeader and Header directive */
355 static APR_INLINE const char *header_inout_cmd(cmd_parms *cmd,
360 const char* envclause)
362 headers_conf *dirconf = indirconf;
363 const char *condition_var = NULL;
367 apr_array_header_t *fixup = (cmd->info == &hdr_in)
368 ? dirconf->fixup_in : (cmd->info == &hdr_err)
370 : dirconf->fixup_out;
372 new = (header_entry *) apr_array_push(fixup);
374 if (!strcasecmp(action, "set"))
375 new->action = hdr_set;
376 else if (!strcasecmp(action, "add"))
377 new->action = hdr_add;
378 else if (!strcasecmp(action, "append"))
379 new->action = hdr_append;
380 else if (!strcasecmp(action, "unset"))
381 new->action = hdr_unset;
382 else if (!strcasecmp(action, "echo"))
383 new->action = hdr_echo;
385 return "first argument must be 'add', 'set', 'append', 'unset' or "
388 if (new->action == hdr_unset) {
391 return "header unset takes two arguments";
397 else if (new->action == hdr_echo) {
402 return "Header echo takes two arguments";
407 if (cmd->info != &hdr_out && cmd->info != &hdr_err)
408 return "Header echo only valid on Header and ErrorHeader "
411 regex = ap_pregcomp(cmd->pool, hdr, REG_EXTENDED | REG_NOSUB);
413 return "Header echo regex could not be compiled";
419 return "Header requires three arguments";
421 /* Handle the envclause on Header */
422 if (envclause != NULL) {
423 if (strncasecmp(envclause, "env=", 4) != 0) {
424 return "error: envclause should be in the form env=envar";
426 if ((envclause[4] == '\0')
427 || ((envclause[4] == '!') && (envclause[5] == '\0'))) {
428 return "error: missing environment variable name. "
429 "envclause should be in the form env=envar ";
431 condition_var = envclause + 4;
434 if ((colon = ap_strchr_c(hdr, ':'))) {
435 hdr = apr_pstrmemdup(cmd->pool, hdr, colon-hdr);
439 new->condition_var = condition_var;
441 return parse_format_string(cmd->pool, new, value);
444 /* Handle all (xxx)Header directives */
445 static const char *header_cmd(cmd_parms *cmd, void *indirconf,
451 const char *envclause;
453 action = ap_getword_conf(cmd->pool, &args);
454 hdr = ap_getword_conf(cmd->pool, &args);
455 val = *args ? ap_getword_conf(cmd->pool, &args) : NULL;
456 envclause = *args ? ap_getword_conf(cmd->pool, &args) : NULL;
459 return apr_pstrcat(cmd->pool, cmd->cmd->name,
460 " takes only 4 arguments at max.", NULL);
463 return header_inout_cmd(cmd, indirconf, action, hdr, val, envclause);
467 * Process the tags in the format string. Tags may be format specifiers
468 * (%D, %t, etc.), whitespace or text strings. For each tag, run the handler
469 * (formatter) specific to the tag. Handlers return text strings.
470 * Concatenate the return from each handler into one string that is
471 * returned from this call.
473 static char* process_tags(header_entry *hdr, request_rec *r)
479 format_tag *tag = (format_tag*) hdr->ta->elts;
481 for (i = 0; i < hdr->ta->nelts; i++) {
482 s = tag[i].func(r, tag[i].arg);
484 str = apr_pstrdup(r->pool, s);
486 str = apr_pstrcat(r->pool, str, s, NULL);
488 return str ? str : "";
491 static int echo_header(echo_do *v, const char *key, const char *val)
493 /* If the input header (key) matches the regex, echo it intact to
496 if (!ap_regexec(v->hdr->regex, key, 0, NULL, 0)) {
497 apr_table_add(v->r->headers_out, key, val);
503 static void do_headers_fixup(request_rec *r, apr_table_t *headers,
504 apr_array_header_t *fixup)
508 for (i = 0; i < fixup->nelts; ++i) {
509 header_entry *hdr = &((header_entry *) (fixup->elts))[i];
511 /* Have any conditional envar-controlled Header processing to do? */
512 if (hdr->condition_var) {
513 const char *envar = hdr->condition_var;
515 if (apr_table_get(r->subprocess_env, envar) == NULL)
519 if (apr_table_get(r->subprocess_env, &envar[1]) != NULL)
524 switch (hdr->action) {
526 apr_table_addn(headers, hdr->header, process_tags(hdr, r));
529 apr_table_mergen(headers, hdr->header, process_tags(hdr, r));
532 apr_table_setn(headers, hdr->header, process_tags(hdr, r));
535 apr_table_unset(headers, hdr->header);
542 apr_table_do((int (*) (void *, const char *, const char *))
543 echo_header, (void *) &v, r->headers_in, NULL);
550 static void ap_headers_insert_output_filter(request_rec *r)
552 headers_conf *dirconf = ap_get_module_config(r->per_dir_config,
555 if (dirconf->fixup_out->nelts || dirconf->fixup_err->nelts) {
556 ap_add_output_filter("FIXUP_HEADERS_OUT", NULL, r, r->connection);
561 * Make sure our error-path filter is in place.
563 static void ap_headers_insert_error_filter(request_rec *r)
565 headers_conf *dirconf = ap_get_module_config(r->per_dir_config,
568 if (dirconf->fixup_err->nelts) {
569 ap_add_output_filter("FIXUP_HEADERS_ERR", NULL, r, r->connection);
573 static apr_status_t ap_headers_output_filter(ap_filter_t *f,
574 apr_bucket_brigade *in)
576 headers_conf *dirconf = ap_get_module_config(f->r->per_dir_config,
579 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, f->r->server,
580 "headers: ap_headers_output_filter()");
583 do_headers_fixup(f->r, f->r->err_headers_out, dirconf->fixup_err);
584 do_headers_fixup(f->r, f->r->headers_out, dirconf->fixup_out);
586 /* remove ourselves from the filter chain */
587 ap_remove_output_filter(f);
589 /* send the data up the stack */
590 return ap_pass_brigade(f->next,in);
594 * Make sure we propagate any ErrorHeader settings on the error
595 * path through http_protocol.c.
597 static apr_status_t ap_headers_error_filter(ap_filter_t *f,
598 apr_bucket_brigade *in)
600 headers_conf *dirconf;
602 dirconf = ap_get_module_config(f->r->per_dir_config,
604 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, f->r->server,
605 "headers: ap_headers_error_filter()");
608 * Add any header fields defined by ErrorHeader to r->err_headers_out.
609 * Server-wide first, then per-directory to allow overriding.
611 do_headers_fixup(f->r, f->r->err_headers_out, dirconf->fixup_err);
614 * We've done our bit; remove ourself from the filter chain so there's
615 * no possibility we'll be called again.
617 ap_remove_output_filter(f);
620 * Pass the buck. (euro?)
622 return ap_pass_brigade(f->next, in);
625 static apr_status_t ap_headers_fixup(request_rec *r)
627 headers_conf *dirconf = ap_get_module_config(r->per_dir_config,
631 if (dirconf->fixup_in->nelts) {
632 do_headers_fixup(r, r->headers_in, dirconf->fixup_in);
638 static const command_rec headers_cmds[] =
640 AP_INIT_RAW_ARGS("Header", header_cmd, &hdr_out, OR_FILEINFO,
641 "an action, header and value followed by optional env "
643 AP_INIT_RAW_ARGS("RequestHeader", header_cmd, &hdr_in, OR_FILEINFO,
644 "an action, header and value followed by optional env "
646 AP_INIT_RAW_ARGS("ErrorHeader", header_cmd, &hdr_err, OR_FILEINFO,
647 "an action, header and value followed by optional env "
652 static void register_format_tag_handler(const char *tag,
653 const void *tag_handler)
655 apr_hash_set(format_tag_hash, tag, 1, tag_handler);
658 static int header_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp)
660 format_tag_hash = apr_hash_make(p);
661 register_format_tag_handler("D", (const void *)header_request_duration);
662 register_format_tag_handler("t", (const void *)header_request_time);
663 register_format_tag_handler("e", (const void *)header_request_env_var);
664 register_format_tag_handler("s", (const void *)header_request_ssl_var);
669 static int header_post_config(apr_pool_t *pconf, apr_pool_t *plog,
670 apr_pool_t *ptemp, server_rec *s)
672 header_ssl_lookup = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup);
676 static void register_hooks(apr_pool_t *p)
678 ap_register_output_filter("FIXUP_HEADERS_OUT", ap_headers_output_filter,
679 NULL, AP_FTYPE_CONTENT_SET);
680 ap_register_output_filter("FIXUP_HEADERS_ERR", ap_headers_error_filter,
681 NULL, AP_FTYPE_CONTENT_SET);
682 ap_hook_pre_config(header_pre_config,NULL,NULL,APR_HOOK_MIDDLE);
683 ap_hook_post_config(header_post_config,NULL,NULL,APR_HOOK_MIDDLE);
684 ap_hook_insert_filter(ap_headers_insert_output_filter, NULL, NULL, APR_HOOK_LAST);
685 ap_hook_insert_error_filter(ap_headers_insert_error_filter,
686 NULL, NULL, APR_HOOK_LAST);
687 ap_hook_fixups(ap_headers_fixup, NULL, NULL, APR_HOOK_LAST);
690 module AP_MODULE_DECLARE_DATA headers_module =
692 STANDARD20_MODULE_STUFF,
693 create_headers_dir_config, /* dir config creater */
694 merge_headers_config, /* dir merger --- default is to override */
695 NULL, /* server config */
696 NULL, /* merge server configs */
697 headers_cmds, /* command apr_table_t */
698 register_hooks /* register hooks */