enabled block processing properly.
[httpd-crcsyncproxy.git] / modules / metadata / mod_headers.c
blobb65fbfd07716c07aca038b1f195db5199b0bef0f
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.
26 * Syntax is:
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
54 * the reverse order:
56 * Header append Author "John P. Doe"
57 * Header unset Author
59 * Examples:
61 * To set the "Author" header, use
62 * Header add Author "John P. Doe"
64 * To remove a header:
65 * Header unset Author
69 #include "apr.h"
70 #include "apr_lib.h"
71 #include "apr_strings.h"
72 #include "apr_buckets.h"
74 #include "apr_hash.h"
75 #define APR_WANT_STRFUNC
76 #include "apr_want.h"
78 #include "httpd.h"
79 #include "http_config.h"
80 #include "http_request.h"
81 #include "http_log.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;
90 typedef enum {
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 */
98 } hdr_actions;
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
109 * config directive
111 typedef struct {
112 const char* (*func)(request_rec *r,char *arg);
113 char *arg;
114 } format_tag;
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
121 typedef struct {
122 hdr_actions action;
123 const char *header;
124 apr_array_header_t *ta; /* Array of format_tag structs */
125 ap_regex_t *regex;
126 const char *condition_var;
127 const char *subs;
128 } header_entry;
130 /* echo_do is used for Header echo to iterate through the request headers*/
131 typedef struct {
132 request_rec *r;
133 header_entry *hdr;
134 } echo_do;
136 /* edit_do is used for Header edit to iterate through the request headers */
137 typedef struct {
138 apr_pool_t *p;
139 header_entry *hdr;
140 apr_table_t *t;
141 } edit_do;
144 * headers_conf is our per-module configuration. This is used as both
145 * a per-dir and per-server config
147 typedef struct {
148 apr_array_header_t *fixup_in;
149 apr_array_header_t *fixup_out;
150 apr_array_header_t *fixup_err;
151 } headers_conf;
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)
163 return 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)) {
180 char *ptr;
182 hdr = ptr = apr_pstrdup(p, hdr);
184 do {
185 if (*ptr == APR_ASCII_LF || *ptr == APR_ASCII_CR)
186 *ptr = APR_ASCII_BLANK;
187 } while (*ptr++);
189 return hdr;
192 static const char *header_request_env_var(request_rec *r, char *a)
194 const char *s = apr_table_get(r->subprocess_env,a);
196 if (s)
197 return unwrap_header(r->pool, s);
198 else
199 return "(null)";
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);
207 if (val && val[0])
208 return unwrap_header(r->pool, val);
209 else
210 return "(null)";
212 else {
213 return "(null)";
218 * Config routines
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));
229 return conf;
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);
245 return newconf;
248 static char *parse_misc_string(apr_pool_t *p, format_tag *tag, const char **sa)
250 const char *s;
251 char *d;
253 tag->func = constant_item;
255 s = *sa;
256 while (*s && *s != '%') {
257 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);
265 d = tag->arg;
266 s = *sa;
267 while (*s && *s != '%') {
268 if (*s != '\\') {
269 *d++ = *s++;
271 else {
272 s++;
273 switch (*s) {
274 case '\\':
275 *d++ = '\\';
276 s++;
277 break;
278 case 'r':
279 *d++ = '\r';
280 s++;
281 break;
282 case 'n':
283 *d++ = '\n';
284 s++;
285 break;
286 case 't':
287 *d++ = '\t';
288 s++;
289 break;
290 default:
291 /* copy verbatim */
292 *d++ = '\\';
294 * Allow the loop to deal with this *s in the normal
295 * fashion so that it handles end of string etc.
296 * properly.
298 break;
302 *d = '\0';
304 *sa = s;
305 return NULL;
308 static char *parse_format_tag(apr_pool_t *p, format_tag *tag, const char **sa)
310 const char *s = *sa;
311 const char * (*tag_handler)(request_rec *,char *);
313 /* Handle string literal/conditionals */
314 if (*s != '%') {
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;
322 tag->arg = "%";
323 if (*s)
324 s++;
325 *sa = s;
326 return NULL;
329 tag->arg = '\0';
330 /* grab the argument if there is one */
331 if (*s == '{') {
332 ++s;
333 tag->arg = ap_getword(p,&s,'}');
336 tag_handler = (const char * (*)(request_rec *,char *))apr_hash_get(format_tag_hash, s++, 1);
338 if (!tag_handler) {
339 char dummy[2];
340 dummy[0] = s[-1];
341 dummy[1] = '\0';
342 return apr_pstrcat(p, "Unrecognized header format %", dummy, NULL);
344 tag->func = tag_handler;
346 *sa = s;
347 return NULL;
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)
362 char *res;
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) {
368 return NULL;
371 hdr->ta = apr_array_make(p, 10, sizeof(format_tag));
373 while (*s) {
374 if ((res = parse_format_tag(p, (format_tag *) apr_array_push(hdr->ta), &s))) {
375 return res;
378 return NULL;
381 /* handle RequestHeader and Header directive */
382 static APR_INLINE const char *header_inout_cmd(cmd_parms *cmd,
383 void *indirconf,
384 const char *action,
385 const char *hdr,
386 const char *value,
387 const char *subs,
388 const char *envclause)
390 headers_conf *dirconf = indirconf;
391 const char *condition_var = NULL;
392 const char *colon;
393 header_entry *new;
395 apr_array_header_t *fixup = (cmd->info == &hdr_in)
396 ? dirconf->fixup_in : (cmd->info == &hdr_err)
397 ? dirconf->fixup_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;
416 else
417 return "first argument must be 'add', 'set', 'append', 'merge', "
418 "'unset', 'echo', or 'edit'.";
420 if (new->action == hdr_edit) {
421 if (subs == NULL) {
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";
428 new->subs = subs;
430 else {
431 /* there's no subs, so envclause is really that argument */
432 if (envclause != NULL) {
433 return "Too many arguments to directive";
435 envclause = subs;
437 if (new->action == hdr_unset) {
438 if (value) {
439 if (envclause) {
440 return "header unset takes two arguments";
442 envclause = value;
443 value = NULL;
446 else if (new->action == hdr_echo) {
447 ap_regex_t *regex;
449 if (value) {
450 if (envclause) {
451 return "Header echo takes two arguments";
453 envclause = value;
454 value = NULL;
456 if (cmd->info != &hdr_out && cmd->info != &hdr_err)
457 return "Header echo only valid on Header "
458 "directives";
459 else {
460 regex = ap_pregcomp(cmd->pool, hdr, AP_REG_EXTENDED | AP_REG_NOSUB);
461 if (regex == NULL) {
462 return "Header echo regex could not be compiled";
465 new->regex = regex;
467 else if (!value)
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;
475 else {
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);
492 new->header = 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,
500 const char *args)
502 const char *action;
503 const char *hdr;
504 const char *val;
505 const char *envclause;
506 const char *subs;
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;
523 if (*args) {
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)
540 int i;
541 const char *s;
542 char *str = NULL;
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);
548 if (str == NULL)
549 str = apr_pstrdup(r->pool, s);
550 else
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,
556 apr_pool_t *pool)
558 unsigned int nmatch = 10;
559 ap_regmatch_t pmatch[10];
560 const char *subs;
561 char *ret;
562 int diffsz;
563 if (ap_regexec(hdr->regex, value, nmatch, pmatch, 0)) {
564 /* no match, nothing to do */
565 return value;
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);
573 return ret;
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
579 * r->headers_out.
581 if (!ap_regexec(v->hdr->regex, key, 0, NULL, 0)) {
582 apr_table_add(v->r->headers_out, key, val);
585 return 1;
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));
593 return 1;
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);
601 return 1;
604 static void do_headers_fixup(request_rec *r, apr_table_t *headers,
605 apr_array_header_t *fixup, int early)
607 echo_do v;
608 int i;
609 const char *val;
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)) {
617 continue;
619 /* ignore late headers in early calls */
620 else if (early && (envar != condition_early)) {
621 continue;
623 /* Have any conditional envar-controlled Header processing to do? */
624 else if (envar && !early) {
625 if (*envar != '!') {
626 if (apr_table_get(r->subprocess_env, envar) == NULL)
627 continue;
629 else {
630 if (apr_table_get(r->subprocess_env, &envar[1]) != NULL)
631 continue;
635 switch (hdr->action) {
636 case hdr_add:
637 apr_table_addn(headers, hdr->header, process_tags(hdr, r));
638 break;
639 case hdr_append:
640 apr_table_mergen(headers, hdr->header, process_tags(hdr, r));
641 break;
642 case hdr_merge:
643 val = apr_table_get(headers, hdr->header);
644 if (val == NULL) {
645 apr_table_addn(headers, hdr->header, process_tags(hdr, r));
646 } else {
647 char *new_val = process_tags(hdr, r);
648 apr_size_t new_val_len = strlen(new_val);
649 int tok_found = 0;
651 /* modified version of logic in ap_get_token() */
652 while (*val) {
653 const char *tok_start;
655 while (*val && apr_isspace(*val))
656 ++val;
658 tok_start = val;
660 while (*val && *val != ',') {
661 if (*val++ == '"')
662 while (*val)
663 if (*val++ == '"')
664 break;
667 if (new_val_len == (apr_size_t)(val - tok_start)
668 && !strncmp(tok_start, new_val, new_val_len)) {
669 tok_found = 1;
670 break;
673 if (*val)
674 ++val;
677 if (!tok_found) {
678 apr_table_mergen(headers, hdr->header, new_val);
681 break;
682 case hdr_set:
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));
687 break;
688 case hdr_unset:
689 apr_table_unset(headers, hdr->header);
690 break;
691 case hdr_echo:
692 v.r = r;
693 v.hdr = hdr;
694 apr_table_do((int (*) (void *, const char *, const char *))
695 echo_header, (void *) &v, r->headers_in, NULL);
696 break;
697 case hdr_edit:
698 if (apr_table_get(headers, hdr->header)) {
699 edit_do ed;
701 ed.p = r->pool;
702 ed.hdr = hdr;
703 ed.t = apr_table_make(r->pool, 5);
704 apr_table_do(edit_header, (void *) &ed, headers, hdr->header,
705 NULL);
706 apr_table_unset(headers, hdr->header);
707 apr_table_do(add_them_all, (void *) headers, ed.t, NULL);
709 break;
714 static void ap_headers_insert_output_filter(request_rec *r)
716 headers_conf *dirconf = ap_get_module_config(r->per_dir_config,
717 &headers_module);
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,
730 &headers_module);
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,
741 &headers_module);
743 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, f->r->server,
744 "headers: ap_headers_output_filter()");
746 /* do the fixup */
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,
767 &headers_module);
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,
792 &headers_module);
794 /* do the fixup */
795 if (dirconf->fixup_in->nelts) {
796 do_headers_fixup(r, r->headers_in, dirconf->fixup_in, 0);
799 return DECLINED;
801 static apr_status_t ap_headers_early(request_rec *r)
803 headers_conf *dirconf = ap_get_module_config(r->per_dir_config,
804 &headers_module);
806 /* do the fixup */
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);
817 return DECLINED;
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 "
827 "clause"),
828 {NULL}
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);
845 return OK;
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);
852 return OK;
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 */