1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * mod_headers.c: Add/append/remove HTTP response headers
19 * Written by Paul Sutton, paul@ukweb.com, 1 Oct 1996
21 * The Header directive can be used to add/replace/remove HTTP headers
22 * within the response message. The RequestHeader directive can be used
23 * to add/replace/remove HTTP headers before a request message is processed.
24 * Valid in both per-server and per-dir configurations.
28 * Header action header value
29 * RequestHeader action header value
31 * Where action is one of:
32 * set - set this header, replacing any old value
33 * add - add this header, possible resulting in two or more
34 * headers with the same name
35 * append - append this text onto any existing header of this same
36 * merge - merge this text onto any existing header of this same,
37 * avoiding duplicate values
38 * unset - remove this header
39 * edit - transform the header value according to a regexp
41 * Where action is unset, the third argument (value) should not be given.
42 * The header name can include the colon, or not.
44 * The Header and RequestHeader directives can only be used where allowed
45 * by the FileInfo override.
47 * When the request is processed, the header directives are processed in
48 * this order: firstly, the main server, then the virtual server handling
49 * this request (if any), then any <Directory> sections (working downwards
50 * from the root dir), then an <Location> sections (working down from
51 * shortest URL component), the any <File> sections. This order is
52 * important if any 'set' or 'unset' actions are used. For example,
53 * the following two directives have different effect if applied in
56 * Header append Author "John P. Doe"
61 * To set the "Author" header, use
62 * Header add Author "John P. Doe"
71 #include "apr_strings.h"
72 #include "apr_buckets.h"
75 #define APR_WANT_STRFUNC
79 #include "http_config.h"
80 #include "http_request.h"
82 #include "util_filter.h"
83 #include "http_protocol.h"
85 #include "mod_ssl.h" /* for the ssl_var_lookup optional function defn */
87 /* format_tag_hash is initialized during pre-config */
88 static apr_hash_t
*format_tag_hash
;
91 hdr_add
= 'a', /* add header (could mean multiple hdrs) */
92 hdr_set
= 's', /* set (replace old value) */
93 hdr_append
= 'm', /* append (merge into any old value) */
94 hdr_merge
= 'g', /* merge (merge, but avoid duplicates) */
95 hdr_unset
= 'u', /* unset header */
96 hdr_echo
= 'e', /* echo headers from request to response */
97 hdr_edit
= 'r' /* change value by regexp */
101 * magic cmd->info values
103 static char hdr_in
= '0'; /* RequestHeader */
104 static char hdr_out
= '1'; /* Header onsuccess */
105 static char hdr_err
= '2'; /* Header always */
108 * There is an array of struct format_tag per Header/RequestHeader
112 const char* (*func
)(request_rec
*r
,char *arg
);
116 /* 'Magic' condition_var value to run action in post_read_request */
117 static const char* condition_early
= "early";
119 * There is one "header_entry" per Header/RequestHeader config directive
124 apr_array_header_t
*ta
; /* Array of format_tag structs */
126 const char *condition_var
;
130 /* echo_do is used for Header echo to iterate through the request headers*/
136 /* edit_do is used for Header edit to iterate through the request headers */
144 * headers_conf is our per-module configuration. This is used as both
145 * a per-dir and per-server config
148 apr_array_header_t
*fixup_in
;
149 apr_array_header_t
*fixup_out
;
150 apr_array_header_t
*fixup_err
;
153 module AP_MODULE_DECLARE_DATA headers_module
;
155 /* Pointer to ssl_var_lookup, if available. */
156 static APR_OPTIONAL_FN_TYPE(ssl_var_lookup
) *header_ssl_lookup
= NULL
;
159 * Tag formatting functions
161 static const char *constant_item(request_rec
*r
, char *stuff
)
165 static const char *header_request_duration(request_rec
*r
, char *a
)
167 return apr_psprintf(r
->pool
, "D=%" APR_TIME_T_FMT
,
168 (apr_time_now() - r
->request_time
));
170 static const char *header_request_time(request_rec
*r
, char *a
)
172 return apr_psprintf(r
->pool
, "t=%" APR_TIME_T_FMT
, r
->request_time
);
175 /* unwrap_header returns HDR with any newlines converted into
176 * whitespace if necessary. */
177 static const char *unwrap_header(apr_pool_t
*p
, const char *hdr
)
179 if (ap_strchr_c(hdr
, APR_ASCII_LF
) || ap_strchr_c(hdr
, APR_ASCII_CR
)) {
182 hdr
= ptr
= apr_pstrdup(p
, hdr
);
185 if (*ptr
== APR_ASCII_LF
|| *ptr
== APR_ASCII_CR
)
186 *ptr
= APR_ASCII_BLANK
;
192 static const char *header_request_env_var(request_rec
*r
, char *a
)
194 const char *s
= apr_table_get(r
->subprocess_env
,a
);
197 return unwrap_header(r
->pool
, s
);
202 static const char *header_request_ssl_var(request_rec
*r
, char *name
)
204 if (header_ssl_lookup
) {
205 const char *val
= header_ssl_lookup(r
->pool
, r
->server
,
206 r
->connection
, r
, name
);
208 return unwrap_header(r
->pool
, val
);
221 static void *create_headers_dir_config(apr_pool_t
*p
, char *d
)
223 headers_conf
*conf
= apr_pcalloc(p
, sizeof(*conf
));
225 conf
->fixup_in
= apr_array_make(p
, 2, sizeof(header_entry
));
226 conf
->fixup_out
= apr_array_make(p
, 2, sizeof(header_entry
));
227 conf
->fixup_err
= apr_array_make(p
, 2, sizeof(header_entry
));
232 static void *merge_headers_config(apr_pool_t
*p
, void *basev
, void *overridesv
)
234 headers_conf
*newconf
= apr_pcalloc(p
, sizeof(*newconf
));
235 headers_conf
*base
= basev
;
236 headers_conf
*overrides
= overridesv
;
238 newconf
->fixup_in
= apr_array_append(p
, base
->fixup_in
,
239 overrides
->fixup_in
);
240 newconf
->fixup_out
= apr_array_append(p
, base
->fixup_out
,
241 overrides
->fixup_out
);
242 newconf
->fixup_err
= apr_array_append(p
, base
->fixup_err
,
243 overrides
->fixup_err
);
248 static char *parse_misc_string(apr_pool_t
*p
, format_tag
*tag
, const char **sa
)
253 tag
->func
= constant_item
;
256 while (*s
&& *s
!= '%') {
260 * This might allocate a few chars extra if there's a backslash
261 * escape in the format string.
263 tag
->arg
= apr_palloc(p
, s
- *sa
+ 1);
267 while (*s
&& *s
!= '%') {
294 * Allow the loop to deal with this *s in the normal
295 * fashion so that it handles end of string etc.
308 static char *parse_format_tag(apr_pool_t
*p
, format_tag
*tag
, const char **sa
)
311 const char * (*tag_handler
)(request_rec
*,char *);
313 /* Handle string literal/conditionals */
315 return parse_misc_string(p
, tag
, sa
);
317 s
++; /* skip the % */
319 /* Pass through %% or % at end of string as % */
320 if ((*s
== '%') || (*s
== '\0')) {
321 tag
->func
= constant_item
;
330 /* grab the argument if there is one */
333 tag
->arg
= ap_getword(p
,&s
,'}');
336 tag_handler
= (const char * (*)(request_rec
*,char *))apr_hash_get(format_tag_hash
, s
++, 1);
342 return apr_pstrcat(p
, "Unrecognized header format %", dummy
, NULL
);
344 tag
->func
= tag_handler
;
351 * A format string consists of white space, text and optional format
352 * tags in any order. E.g.,
354 * Header add MyHeader "Free form text %D %t more text"
356 * Decompose the format string into its tags. Each tag (struct format_tag)
357 * contains a pointer to the function used to format the tag. Then save each
358 * tag in the tag array anchored in the header_entry.
360 static char *parse_format_string(apr_pool_t
*p
, header_entry
*hdr
, const char *s
)
364 /* No string to parse with unset and echo commands */
365 if (hdr
->action
== hdr_unset
||
366 hdr
->action
== hdr_edit
||
367 hdr
->action
== hdr_echo
) {
371 hdr
->ta
= apr_array_make(p
, 10, sizeof(format_tag
));
374 if ((res
= parse_format_tag(p
, (format_tag
*) apr_array_push(hdr
->ta
), &s
))) {
381 /* handle RequestHeader and Header directive */
382 static APR_INLINE
const char *header_inout_cmd(cmd_parms
*cmd
,
388 const char *envclause
)
390 headers_conf
*dirconf
= indirconf
;
391 const char *condition_var
= NULL
;
395 apr_array_header_t
*fixup
= (cmd
->info
== &hdr_in
)
396 ? dirconf
->fixup_in
: (cmd
->info
== &hdr_err
)
398 : dirconf
->fixup_out
;
400 new = (header_entry
*) apr_array_push(fixup
);
402 if (!strcasecmp(action
, "set"))
403 new->action
= hdr_set
;
404 else if (!strcasecmp(action
, "add"))
405 new->action
= hdr_add
;
406 else if (!strcasecmp(action
, "append"))
407 new->action
= hdr_append
;
408 else if (!strcasecmp(action
, "merge"))
409 new->action
= hdr_merge
;
410 else if (!strcasecmp(action
, "unset"))
411 new->action
= hdr_unset
;
412 else if (!strcasecmp(action
, "echo"))
413 new->action
= hdr_echo
;
414 else if (!strcasecmp(action
, "edit"))
415 new->action
= hdr_edit
;
417 return "first argument must be 'add', 'set', 'append', 'merge', "
418 "'unset', 'echo', or 'edit'.";
420 if (new->action
== hdr_edit
) {
422 return "Header edit requires a match and a substitution";
424 new->regex
= ap_pregcomp(cmd
->pool
, value
, AP_REG_EXTENDED
);
425 if (new->regex
== NULL
) {
426 return "Header edit regex could not be compiled";
431 /* there's no subs, so envclause is really that argument */
432 if (envclause
!= NULL
) {
433 return "Too many arguments to directive";
437 if (new->action
== hdr_unset
) {
440 return "header unset takes two arguments";
446 else if (new->action
== hdr_echo
) {
451 return "Header echo takes two arguments";
456 if (cmd
->info
!= &hdr_out
&& cmd
->info
!= &hdr_err
)
457 return "Header echo only valid on Header "
460 regex
= ap_pregcomp(cmd
->pool
, hdr
, AP_REG_EXTENDED
| AP_REG_NOSUB
);
462 return "Header echo regex could not be compiled";
468 return "Header requires three arguments";
470 /* Handle the envclause on Header */
471 if (envclause
!= NULL
) {
472 if (strcasecmp(envclause
, "early") == 0) {
473 condition_var
= condition_early
;
476 if (strncasecmp(envclause
, "env=", 4) != 0) {
477 return "error: envclause should be in the form env=envar";
479 if ((envclause
[4] == '\0')
480 || ((envclause
[4] == '!') && (envclause
[5] == '\0'))) {
481 return "error: missing environment variable name. "
482 "envclause should be in the form env=envar ";
484 condition_var
= envclause
+ 4;
488 if ((colon
= ap_strchr_c(hdr
, ':'))) {
489 hdr
= apr_pstrmemdup(cmd
->pool
, hdr
, colon
-hdr
);
493 new->condition_var
= condition_var
;
495 return parse_format_string(cmd
->pool
, new, value
);
498 /* Handle all (xxx)Header directives */
499 static const char *header_cmd(cmd_parms
*cmd
, void *indirconf
,
505 const char *envclause
;
508 action
= ap_getword_conf(cmd
->pool
, &args
);
509 if (cmd
->info
== &hdr_out
) {
510 if (!strcasecmp(action
, "always")) {
511 cmd
->info
= &hdr_err
;
512 action
= ap_getword_conf(cmd
->pool
, &args
);
514 else if (!strcasecmp(action
, "onsuccess")) {
515 action
= ap_getword_conf(cmd
->pool
, &args
);
518 hdr
= ap_getword_conf(cmd
->pool
, &args
);
519 val
= *args
? ap_getword_conf(cmd
->pool
, &args
) : NULL
;
520 subs
= *args
? ap_getword_conf(cmd
->pool
, &args
) : NULL
;
521 envclause
= *args
? ap_getword_conf(cmd
->pool
, &args
) : NULL
;
524 return apr_pstrcat(cmd
->pool
, cmd
->cmd
->name
,
525 " has too many arguments", NULL
);
528 return header_inout_cmd(cmd
, indirconf
, action
, hdr
, val
, subs
, envclause
);
532 * Process the tags in the format string. Tags may be format specifiers
533 * (%D, %t, etc.), whitespace or text strings. For each tag, run the handler
534 * (formatter) specific to the tag. Handlers return text strings.
535 * Concatenate the return from each handler into one string that is
536 * returned from this call.
538 static char* process_tags(header_entry
*hdr
, request_rec
*r
)
544 format_tag
*tag
= (format_tag
*) hdr
->ta
->elts
;
546 for (i
= 0; i
< hdr
->ta
->nelts
; i
++) {
547 s
= tag
[i
].func(r
, tag
[i
].arg
);
549 str
= apr_pstrdup(r
->pool
, s
);
551 str
= apr_pstrcat(r
->pool
, str
, s
, NULL
);
553 return str
? str
: "";
555 static const char *process_regexp(header_entry
*hdr
, const char *value
,
558 unsigned int nmatch
= 10;
559 ap_regmatch_t pmatch
[10];
563 if (ap_regexec(hdr
->regex
, value
, nmatch
, pmatch
, 0)) {
564 /* no match, nothing to do */
567 subs
= ap_pregsub(pool
, hdr
->subs
, value
, nmatch
, pmatch
);
568 diffsz
= strlen(subs
) - (pmatch
[0].rm_eo
- pmatch
[0].rm_so
);
569 ret
= apr_palloc(pool
, strlen(value
) + 1 + diffsz
);
570 memcpy(ret
, value
, pmatch
[0].rm_so
);
571 strcpy(ret
+ pmatch
[0].rm_so
, subs
);
572 strcat(ret
, value
+ pmatch
[0].rm_eo
);
576 static int echo_header(echo_do
*v
, const char *key
, const char *val
)
578 /* If the input header (key) matches the regex, echo it intact to
581 if (!ap_regexec(v
->hdr
->regex
, key
, 0, NULL
, 0)) {
582 apr_table_add(v
->r
->headers_out
, key
, val
);
588 static int edit_header(void *v
, const char *key
, const char *val
)
590 edit_do
*ed
= (edit_do
*)v
;
592 apr_table_addn(ed
->t
, key
, process_regexp(ed
->hdr
, val
, ed
->p
));
596 static int add_them_all(void *v
, const char *key
, const char *val
)
598 apr_table_t
*headers
= (apr_table_t
*)v
;
600 apr_table_addn(headers
, key
, val
);
604 static void do_headers_fixup(request_rec
*r
, apr_table_t
*headers
,
605 apr_array_header_t
*fixup
, int early
)
611 for (i
= 0; i
< fixup
->nelts
; ++i
) {
612 header_entry
*hdr
= &((header_entry
*) (fixup
->elts
))[i
];
613 const char *envar
= hdr
->condition_var
;
615 /* ignore early headers in late calls */
616 if (!early
&& (envar
== condition_early
)) {
619 /* ignore late headers in early calls */
620 else if (early
&& (envar
!= condition_early
)) {
623 /* Have any conditional envar-controlled Header processing to do? */
624 else if (envar
&& !early
) {
626 if (apr_table_get(r
->subprocess_env
, envar
) == NULL
)
630 if (apr_table_get(r
->subprocess_env
, &envar
[1]) != NULL
)
635 switch (hdr
->action
) {
637 apr_table_addn(headers
, hdr
->header
, process_tags(hdr
, r
));
640 apr_table_mergen(headers
, hdr
->header
, process_tags(hdr
, r
));
643 val
= apr_table_get(headers
, hdr
->header
);
645 apr_table_addn(headers
, hdr
->header
, process_tags(hdr
, r
));
647 char *new_val
= process_tags(hdr
, r
);
648 apr_size_t new_val_len
= strlen(new_val
);
651 /* modified version of logic in ap_get_token() */
653 const char *tok_start
;
655 while (*val
&& apr_isspace(*val
))
660 while (*val
&& *val
!= ',') {
667 if (new_val_len
== (apr_size_t
)(val
- tok_start
)
668 && !strncmp(tok_start
, new_val
, new_val_len
)) {
678 apr_table_mergen(headers
, hdr
->header
, new_val
);
683 if (!strcasecmp(hdr
->header
, "Content-Type")) {
684 ap_set_content_type(r
, process_tags(hdr
, r
));
686 apr_table_setn(headers
, hdr
->header
, process_tags(hdr
, r
));
689 apr_table_unset(headers
, hdr
->header
);
694 apr_table_do((int (*) (void *, const char *, const char *))
695 echo_header
, (void *) &v
, r
->headers_in
, NULL
);
698 if (apr_table_get(headers
, hdr
->header
)) {
703 ed
.t
= apr_table_make(r
->pool
, 5);
704 apr_table_do(edit_header
, (void *) &ed
, headers
, hdr
->header
,
706 apr_table_unset(headers
, hdr
->header
);
707 apr_table_do(add_them_all
, (void *) headers
, ed
.t
, NULL
);
714 static void ap_headers_insert_output_filter(request_rec
*r
)
716 headers_conf
*dirconf
= ap_get_module_config(r
->per_dir_config
,
719 if (dirconf
->fixup_out
->nelts
|| dirconf
->fixup_err
->nelts
) {
720 ap_add_output_filter("FIXUP_HEADERS_OUT", NULL
, r
, r
->connection
);
725 * Make sure our error-path filter is in place.
727 static void ap_headers_insert_error_filter(request_rec
*r
)
729 headers_conf
*dirconf
= ap_get_module_config(r
->per_dir_config
,
732 if (dirconf
->fixup_err
->nelts
) {
733 ap_add_output_filter("FIXUP_HEADERS_ERR", NULL
, r
, r
->connection
);
737 static apr_status_t
ap_headers_output_filter(ap_filter_t
*f
,
738 apr_bucket_brigade
*in
)
740 headers_conf
*dirconf
= ap_get_module_config(f
->r
->per_dir_config
,
743 ap_log_error(APLOG_MARK
, APLOG_DEBUG
, 0, f
->r
->server
,
744 "headers: ap_headers_output_filter()");
747 do_headers_fixup(f
->r
, f
->r
->err_headers_out
, dirconf
->fixup_err
, 0);
748 do_headers_fixup(f
->r
, f
->r
->headers_out
, dirconf
->fixup_out
, 0);
750 /* remove ourselves from the filter chain */
751 ap_remove_output_filter(f
);
753 /* send the data up the stack */
754 return ap_pass_brigade(f
->next
,in
);
758 * Make sure we propagate any "Header always" settings on the error
759 * path through http_protocol.c.
761 static apr_status_t
ap_headers_error_filter(ap_filter_t
*f
,
762 apr_bucket_brigade
*in
)
764 headers_conf
*dirconf
;
766 dirconf
= ap_get_module_config(f
->r
->per_dir_config
,
768 ap_log_error(APLOG_MARK
, APLOG_DEBUG
, 0, f
->r
->server
,
769 "headers: ap_headers_error_filter()");
772 * Add any header fields defined by "Header always" to r->err_headers_out.
773 * Server-wide first, then per-directory to allow overriding.
775 do_headers_fixup(f
->r
, f
->r
->err_headers_out
, dirconf
->fixup_err
, 0);
778 * We've done our bit; remove ourself from the filter chain so there's
779 * no possibility we'll be called again.
781 ap_remove_output_filter(f
);
784 * Pass the buck. (euro?)
786 return ap_pass_brigade(f
->next
, in
);
789 static apr_status_t
ap_headers_fixup(request_rec
*r
)
791 headers_conf
*dirconf
= ap_get_module_config(r
->per_dir_config
,
795 if (dirconf
->fixup_in
->nelts
) {
796 do_headers_fixup(r
, r
->headers_in
, dirconf
->fixup_in
, 0);
801 static apr_status_t
ap_headers_early(request_rec
*r
)
803 headers_conf
*dirconf
= ap_get_module_config(r
->per_dir_config
,
807 if (dirconf
->fixup_in
->nelts
) {
808 do_headers_fixup(r
, r
->headers_in
, dirconf
->fixup_in
, 1);
810 if (dirconf
->fixup_err
->nelts
) {
811 do_headers_fixup(r
, r
->err_headers_out
, dirconf
->fixup_err
, 1);
813 if (dirconf
->fixup_out
->nelts
) {
814 do_headers_fixup(r
, r
->headers_out
, dirconf
->fixup_out
, 1);
820 static const command_rec headers_cmds
[] =
822 AP_INIT_RAW_ARGS("Header", header_cmd
, &hdr_out
, OR_FILEINFO
,
823 "an optional condition, an action, header and value "
824 "followed by optional env clause"),
825 AP_INIT_RAW_ARGS("RequestHeader", header_cmd
, &hdr_in
, OR_FILEINFO
,
826 "an action, header and value followed by optional env "
831 static void register_format_tag_handler(const char *tag
,
832 const void *tag_handler
)
834 apr_hash_set(format_tag_hash
, tag
, 1, tag_handler
);
837 static int header_pre_config(apr_pool_t
*p
, apr_pool_t
*plog
, apr_pool_t
*ptemp
)
839 format_tag_hash
= apr_hash_make(p
);
840 register_format_tag_handler("D", (const void *)header_request_duration
);
841 register_format_tag_handler("t", (const void *)header_request_time
);
842 register_format_tag_handler("e", (const void *)header_request_env_var
);
843 register_format_tag_handler("s", (const void *)header_request_ssl_var
);
848 static int header_post_config(apr_pool_t
*pconf
, apr_pool_t
*plog
,
849 apr_pool_t
*ptemp
, server_rec
*s
)
851 header_ssl_lookup
= APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup
);
855 static void register_hooks(apr_pool_t
*p
)
857 ap_register_output_filter("FIXUP_HEADERS_OUT", ap_headers_output_filter
,
858 NULL
, AP_FTYPE_CONTENT_SET
);
859 ap_register_output_filter("FIXUP_HEADERS_ERR", ap_headers_error_filter
,
860 NULL
, AP_FTYPE_CONTENT_SET
);
861 ap_hook_pre_config(header_pre_config
,NULL
,NULL
,APR_HOOK_MIDDLE
);
862 ap_hook_post_config(header_post_config
,NULL
,NULL
,APR_HOOK_MIDDLE
);
863 ap_hook_insert_filter(ap_headers_insert_output_filter
, NULL
, NULL
, APR_HOOK_LAST
);
864 ap_hook_insert_error_filter(ap_headers_insert_error_filter
,
865 NULL
, NULL
, APR_HOOK_LAST
);
866 ap_hook_fixups(ap_headers_fixup
, NULL
, NULL
, APR_HOOK_LAST
);
867 ap_hook_post_read_request(ap_headers_early
, NULL
, NULL
, APR_HOOK_FIRST
);
870 module AP_MODULE_DECLARE_DATA headers_module
=
872 STANDARD20_MODULE_STUFF
,
873 create_headers_dir_config
, /* dir config creater */
874 merge_headers_config
, /* dir merger --- default is to override */
875 NULL
, /* server config */
876 NULL
, /* merge server configs */
877 headers_cmds
, /* command apr_table_t */
878 register_hooks
/* register hooks */