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 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 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. 15 */ 16 17/* 18 * mod_headers.c: Add/append/remove HTTP response headers 19 * Written by Paul Sutton, paul@ukweb.com, 1 Oct 1996 20 * 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. 25 * 26 * Syntax is: 27 * 28 * Header action header value 29 * RequestHeader action header value 30 * 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 40 * 41 * Where action is unset, the third argument (value) should not be given. 42 * The header name can include the colon, or not. 43 * 44 * The Header and RequestHeader directives can only be used where allowed 45 * by the FileInfo override. 46 * 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: 55 * 56 * Header append Author "John P. Doe" 57 * Header unset Author 58 * 59 * Examples: 60 * 61 * To set the "Author" header, use 62 * Header add Author "John P. Doe" 63 * 64 * To remove a header: 65 * Header unset Author 66 * 67 */ 68 69#include "apr.h" 70#include "apr_lib.h" 71#include "apr_strings.h" 72#include "apr_buckets.h" 73 74#include "apr_hash.h" 75#define APR_WANT_STRFUNC 76#include "apr_want.h" 77 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" 84#include "ap_expr.h" 85 86#include "mod_ssl.h" /* for the ssl_var_lookup optional function defn */ 87 88/* format_tag_hash is initialized during pre-config */ 89static apr_hash_t *format_tag_hash; 90 91typedef enum { 92 hdr_add = 'a', /* add header (could mean multiple hdrs) */ 93 hdr_set = 's', /* set (replace old value) */ 94 hdr_append = 'm', /* append (merge into any old value) */ 95 hdr_merge = 'g', /* merge (merge, but avoid duplicates) */ 96 hdr_unset = 'u', /* unset header */ 97 hdr_echo = 'e', /* echo headers from request to response */ 98 hdr_edit = 'r', /* change value by regexp, match once */ 99 hdr_edit_r = 'R', /* change value by regexp, everymatch */ 100 hdr_setifempty = 'i', /* set value if header not already present*/ 101 hdr_note = 'n' /* set value of header in a note */ 102} hdr_actions; 103 104/* 105 * magic cmd->info values 106 */ 107static char hdr_in = '0'; /* RequestHeader */ 108static char hdr_out_onsuccess = '1'; /* Header onsuccess */ 109static char hdr_out_always = '2'; /* Header always */ 110 111/* Callback function type. */ 112typedef const char *format_tag_fn(request_rec *r, char *a); 113 114/* 115 * There is an array of struct format_tag per Header/RequestHeader 116 * config directive 117 */ 118typedef struct { 119 format_tag_fn *func; 120 char *arg; 121} format_tag; 122 123/* 'Magic' condition_var value to run action in post_read_request */ 124static const char* condition_early = "early"; 125/* 126 * There is one "header_entry" per Header/RequestHeader config directive 127 */ 128typedef struct { 129 hdr_actions action; 130 const char *header; 131 apr_array_header_t *ta; /* Array of format_tag structs */ 132 ap_regex_t *regex; 133 const char *condition_var; 134 const char *subs; 135 ap_expr_info_t *expr; 136} header_entry; 137 138/* echo_do is used for Header echo to iterate through the request headers*/ 139typedef struct { 140 request_rec *r; 141 header_entry *hdr; 142} echo_do; 143 144/* edit_do is used for Header edit to iterate through the request headers */ 145typedef struct { 146 request_rec *r; 147 header_entry *hdr; 148 apr_table_t *t; 149} edit_do; 150 151/* 152 * headers_conf is our per-module configuration. This is used as both 153 * a per-dir and per-server config 154 */ 155typedef struct { 156 apr_array_header_t *fixup_in; 157 apr_array_header_t *fixup_out; 158 apr_array_header_t *fixup_err; 159} headers_conf; 160 161module AP_MODULE_DECLARE_DATA headers_module; 162 163/* Pointer to ssl_var_lookup, if available. */ 164static APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *header_ssl_lookup = NULL; 165 166/* 167 * Tag formatting functions 168 */ 169static const char *constant_item(request_rec *r, char *stuff) 170{ 171 return stuff; 172} 173static const char *header_request_duration(request_rec *r, char *a) 174{ 175 return apr_psprintf(r->pool, "D=%" APR_TIME_T_FMT, 176 (apr_time_now() - r->request_time)); 177} 178static const char *header_request_time(request_rec *r, char *a) 179{ 180 return apr_psprintf(r->pool, "t=%" APR_TIME_T_FMT, r->request_time); 181} 182 183/* unwrap_header returns HDR with any newlines converted into 184 * whitespace if necessary. */ 185static const char *unwrap_header(apr_pool_t *p, const char *hdr) 186{ 187 if (ap_strchr_c(hdr, APR_ASCII_LF) || ap_strchr_c(hdr, APR_ASCII_CR)) { 188 char *ptr; 189 190 hdr = ptr = apr_pstrdup(p, hdr); 191 192 do { 193 if (*ptr == APR_ASCII_LF || *ptr == APR_ASCII_CR) 194 *ptr = APR_ASCII_BLANK; 195 } while (*ptr++); 196 } 197 return hdr; 198} 199 200static const char *header_request_env_var(request_rec *r, char *a) 201{ 202 const char *s = apr_table_get(r->subprocess_env,a); 203 204 if (s) 205 return unwrap_header(r->pool, s); 206 else 207 return "(null)"; 208} 209 210static const char *header_request_ssl_var(request_rec *r, char *name) 211{ 212 if (header_ssl_lookup) { 213 const char *val = header_ssl_lookup(r->pool, r->server, 214 r->connection, r, name); 215 if (val && val[0]) 216 return unwrap_header(r->pool, val); 217 else 218 return "(null)"; 219 } 220 else { 221 return "(null)"; 222 } 223} 224 225static const char *header_request_loadavg(request_rec *r, char *a) 226{ 227 ap_loadavg_t t; 228 ap_get_loadavg(&t); 229 return apr_psprintf(r->pool, "l=%.2f/%.2f/%.2f", t.loadavg, 230 t.loadavg5, t.loadavg15); 231} 232 233static const char *header_request_idle(request_rec *r, char *a) 234{ 235 ap_sload_t t; 236 ap_get_sload(&t); 237 return apr_psprintf(r->pool, "i=%d", t.idle); 238} 239 240static const char *header_request_busy(request_rec *r, char *a) 241{ 242 ap_sload_t t; 243 ap_get_sload(&t); 244 return apr_psprintf(r->pool, "b=%d", t.busy); 245} 246 247/* 248 * Config routines 249 */ 250 251static void *create_headers_dir_config(apr_pool_t *p, char *d) 252{ 253 headers_conf *conf = apr_pcalloc(p, sizeof(*conf)); 254 255 conf->fixup_in = apr_array_make(p, 2, sizeof(header_entry)); 256 conf->fixup_out = apr_array_make(p, 2, sizeof(header_entry)); 257 conf->fixup_err = apr_array_make(p, 2, sizeof(header_entry)); 258 259 return conf; 260} 261 262static void *merge_headers_config(apr_pool_t *p, void *basev, void *overridesv) 263{ 264 headers_conf *newconf = apr_pcalloc(p, sizeof(*newconf)); 265 headers_conf *base = basev; 266 headers_conf *overrides = overridesv; 267 268 newconf->fixup_in = apr_array_append(p, base->fixup_in, 269 overrides->fixup_in); 270 newconf->fixup_out = apr_array_append(p, base->fixup_out, 271 overrides->fixup_out); 272 newconf->fixup_err = apr_array_append(p, base->fixup_err, 273 overrides->fixup_err); 274 275 return newconf; 276} 277 278static char *parse_misc_string(apr_pool_t *p, format_tag *tag, const char **sa) 279{ 280 const char *s; 281 char *d; 282 283 tag->func = constant_item; 284 285 s = *sa; 286 while (*s && *s != '%') { 287 s++; 288 } 289 /* 290 * This might allocate a few chars extra if there's a backslash 291 * escape in the format string. 292 */ 293 tag->arg = apr_palloc(p, s - *sa + 1); 294 295 d = tag->arg; 296 s = *sa; 297 while (*s && *s != '%') { 298 if (*s != '\\') { 299 *d++ = *s++; 300 } 301 else { 302 s++; 303 switch (*s) { 304 case '\\': 305 *d++ = '\\'; 306 s++; 307 break; 308 case 'r': 309 *d++ = '\r'; 310 s++; 311 break; 312 case 'n': 313 *d++ = '\n'; 314 s++; 315 break; 316 case 't': 317 *d++ = '\t'; 318 s++; 319 break; 320 default: 321 /* copy verbatim */ 322 *d++ = '\\'; 323 /* 324 * Allow the loop to deal with this *s in the normal 325 * fashion so that it handles end of string etc. 326 * properly. 327 */ 328 break; 329 } 330 } 331 } 332 *d = '\0'; 333 334 *sa = s; 335 return NULL; 336} 337 338static char *parse_format_tag(apr_pool_t *p, format_tag *tag, const char **sa) 339{ 340 const char *s = *sa; 341 const char * (*tag_handler)(request_rec *,char *); 342 343 /* Handle string literal/conditionals */ 344 if (*s != '%') { 345 return parse_misc_string(p, tag, sa); 346 } 347 s++; /* skip the % */ 348 349 /* Pass through %% or % at end of string as % */ 350 if ((*s == '%') || (*s == '\0')) { 351 tag->func = constant_item; 352 tag->arg = "%"; 353 if (*s) 354 s++; 355 *sa = s; 356 return NULL; 357 } 358 359 tag->arg = "\0"; 360 /* grab the argument if there is one */ 361 if (*s == '{') { 362 ++s; 363 tag->arg = ap_getword(p,&s,'}'); 364 } 365 366 tag_handler = (const char * (*)(request_rec *,char *))apr_hash_get(format_tag_hash, s++, 1); 367 368 if (!tag_handler) { 369 char dummy[2]; 370 dummy[0] = s[-1]; 371 dummy[1] = '\0'; 372 return apr_pstrcat(p, "Unrecognized header format %", dummy, NULL); 373 } 374 tag->func = tag_handler; 375 376 *sa = s; 377 return NULL; 378} 379 380/* 381 * A format string consists of white space, text and optional format 382 * tags in any order. E.g., 383 * 384 * Header add MyHeader "Free form text %D %t more text" 385 * 386 * Decompose the format string into its tags. Each tag (struct format_tag) 387 * contains a pointer to the function used to format the tag. Then save each 388 * tag in the tag array anchored in the header_entry. 389 */ 390static char *parse_format_string(apr_pool_t *p, header_entry *hdr, const char *s) 391{ 392 char *res; 393 394 /* No string to parse with unset and echo commands */ 395 if (hdr->action == hdr_unset || hdr->action == hdr_echo) { 396 return NULL; 397 } 398 /* Tags are in the replacement value for edit */ 399 else if (hdr->action == hdr_edit || hdr->action == hdr_edit_r ) { 400 s = hdr->subs; 401 } 402 403 hdr->ta = apr_array_make(p, 10, sizeof(format_tag)); 404 405 while (*s) { 406 if ((res = parse_format_tag(p, (format_tag *) apr_array_push(hdr->ta), &s))) { 407 return res; 408 } 409 } 410 return NULL; 411} 412 413/* handle RequestHeader and Header directive */ 414static APR_INLINE const char *header_inout_cmd(cmd_parms *cmd, 415 void *indirconf, 416 const char *action, 417 const char *hdr, 418 const char *value, 419 const char *subs, 420 const char *envclause) 421{ 422 headers_conf *dirconf = indirconf; 423 const char *condition_var = NULL; 424 const char *colon; 425 header_entry *new; 426 ap_expr_info_t *expr = NULL; 427 428 apr_array_header_t *fixup = (cmd->info == &hdr_in) 429 ? dirconf->fixup_in : (cmd->info == &hdr_out_always) 430 ? dirconf->fixup_err 431 : dirconf->fixup_out; 432 433 new = (header_entry *) apr_array_push(fixup); 434 435 if (!strcasecmp(action, "set")) 436 new->action = hdr_set; 437 else if (!strcasecmp(action, "setifempty")) 438 new->action = hdr_setifempty; 439 else if (!strcasecmp(action, "add")) 440 new->action = hdr_add; 441 else if (!strcasecmp(action, "append")) 442 new->action = hdr_append; 443 else if (!strcasecmp(action, "merge")) 444 new->action = hdr_merge; 445 else if (!strcasecmp(action, "unset")) 446 new->action = hdr_unset; 447 else if (!strcasecmp(action, "echo")) 448 new->action = hdr_echo; 449 else if (!strcasecmp(action, "edit")) 450 new->action = hdr_edit; 451 else if (!strcasecmp(action, "edit*")) 452 new->action = hdr_edit_r; 453 else if (!strcasecmp(action, "note")) 454 new->action = hdr_note; 455 else 456 return "first argument must be 'add', 'set', 'setifempty', 'append', 'merge', " 457 "'unset', 'echo', 'note', 'edit', or 'edit*'."; 458 459 if (new->action == hdr_edit || new->action == hdr_edit_r) { 460 if (subs == NULL) { 461 return "Header edit requires a match and a substitution"; 462 } 463 new->regex = ap_pregcomp(cmd->pool, value, AP_REG_EXTENDED); 464 if (new->regex == NULL) { 465 return "Header edit regex could not be compiled"; 466 } 467 new->subs = subs; 468 } 469 else { 470 /* there's no subs, so envclause is really that argument */ 471 if (envclause != NULL) { 472 return "Too many arguments to directive"; 473 } 474 envclause = subs; 475 } 476 if (new->action == hdr_unset) { 477 if (value) { 478 if (envclause) { 479 return "header unset takes two arguments"; 480 } 481 envclause = value; 482 value = NULL; 483 } 484 } 485 else if (new->action == hdr_echo) { 486 ap_regex_t *regex; 487 488 if (value) { 489 if (envclause) { 490 return "Header echo takes two arguments"; 491 } 492 envclause = value; 493 value = NULL; 494 } 495 if (cmd->info != &hdr_out_onsuccess && cmd->info != &hdr_out_always) 496 return "Header echo only valid on Header " 497 "directives"; 498 else { 499 regex = ap_pregcomp(cmd->pool, hdr, AP_REG_EXTENDED | AP_REG_NOSUB); 500 if (regex == NULL) { 501 return "Header echo regex could not be compiled"; 502 } 503 } 504 new->regex = regex; 505 } 506 else if (!value) 507 return "Header requires three arguments"; 508 509 /* Handle the envclause on Header */ 510 if (envclause != NULL) { 511 if (strcasecmp(envclause, "early") == 0) { 512 condition_var = condition_early; 513 } 514 else if (strncasecmp(envclause, "env=", 4) == 0) { 515 if ((envclause[4] == '\0') 516 || ((envclause[4] == '!') && (envclause[5] == '\0'))) { 517 return "error: missing environment variable name. " 518 "envclause should be in the form env=envar "; 519 } 520 condition_var = envclause + 4; 521 } 522 else if (strncasecmp(envclause, "expr=", 5) == 0) { 523 const char *err = NULL; 524 expr = ap_expr_parse_cmd(cmd, envclause + 5, 0, &err, NULL); 525 if (err) { 526 return apr_pstrcat(cmd->pool, 527 "Can't parse envclause/expression: ", err, 528 NULL); 529 } 530 } 531 else { 532 return apr_pstrcat(cmd->pool, "Unknown parameter: ", envclause, 533 NULL); 534 } 535 } 536 537 if ((colon = ap_strchr_c(hdr, ':'))) { 538 hdr = apr_pstrmemdup(cmd->pool, hdr, colon-hdr); 539 } 540 541 new->header = hdr; 542 new->condition_var = condition_var; 543 new->expr = expr; 544 545 return parse_format_string(cmd->pool, new, value); 546} 547 548/* Handle all (xxx)Header directives */ 549static const char *header_cmd(cmd_parms *cmd, void *indirconf, 550 const char *args) 551{ 552 const char *action; 553 const char *hdr; 554 const char *val; 555 const char *envclause; 556 const char *subs; 557 558 action = ap_getword_conf(cmd->temp_pool, &args); 559 if (cmd->info == &hdr_out_onsuccess) { 560 if (!strcasecmp(action, "always")) { 561 cmd->info = &hdr_out_always; 562 action = ap_getword_conf(cmd->temp_pool, &args); 563 } 564 else if (!strcasecmp(action, "onsuccess")) { 565 action = ap_getword_conf(cmd->temp_pool, &args); 566 } 567 } 568 hdr = ap_getword_conf(cmd->pool, &args); 569 val = *args ? ap_getword_conf(cmd->pool, &args) : NULL; 570 subs = *args ? ap_getword_conf(cmd->pool, &args) : NULL; 571 envclause = *args ? ap_getword_conf(cmd->pool, &args) : NULL; 572 573 if (*args) { 574 return apr_pstrcat(cmd->pool, cmd->cmd->name, 575 " has too many arguments", NULL); 576 } 577 578 return header_inout_cmd(cmd, indirconf, action, hdr, val, subs, envclause); 579} 580 581/* 582 * Process the tags in the format string. Tags may be format specifiers 583 * (%D, %t, etc.), whitespace or text strings. For each tag, run the handler 584 * (formatter) specific to the tag. Handlers return text strings. 585 * Concatenate the return from each handler into one string that is 586 * returned from this call. 587 */ 588static char* process_tags(header_entry *hdr, request_rec *r) 589{ 590 int i; 591 const char *s; 592 char *str = NULL; 593 594 format_tag *tag = (format_tag*) hdr->ta->elts; 595 596 for (i = 0; i < hdr->ta->nelts; i++) { 597 s = tag[i].func(r, tag[i].arg); 598 if (str == NULL) 599 str = apr_pstrdup(r->pool, s); 600 else 601 str = apr_pstrcat(r->pool, str, s, NULL); 602 } 603 return str ? str : ""; 604} 605static const char *process_regexp(header_entry *hdr, const char *value, 606 request_rec *r) 607{ 608 ap_regmatch_t pmatch[AP_MAX_REG_MATCH]; 609 const char *subs; 610 const char *remainder; 611 char *ret; 612 int diffsz; 613 if (ap_regexec(hdr->regex, value, AP_MAX_REG_MATCH, pmatch, 0)) { 614 /* no match, nothing to do */ 615 return value; 616 } 617 /* Process tags in the input string rather than the resulting 618 * substitution to avoid surprises 619 */ 620 subs = ap_pregsub(r->pool, process_tags(hdr, r), value, AP_MAX_REG_MATCH, pmatch); 621 if (subs == NULL) 622 return NULL; 623 diffsz = strlen(subs) - (pmatch[0].rm_eo - pmatch[0].rm_so); 624 if (hdr->action == hdr_edit) { 625 remainder = value + pmatch[0].rm_eo; 626 } 627 else { /* recurse to edit multiple matches if applicable */ 628 remainder = process_regexp(hdr, value + pmatch[0].rm_eo, r); 629 if (remainder == NULL) 630 return NULL; 631 diffsz += strlen(remainder) - strlen(value + pmatch[0].rm_eo); 632 } 633 ret = apr_palloc(r->pool, strlen(value) + 1 + diffsz); 634 memcpy(ret, value, pmatch[0].rm_so); 635 strcpy(ret + pmatch[0].rm_so, subs); 636 strcat(ret, remainder); 637 return ret; 638} 639 640static int echo_header(echo_do *v, const char *key, const char *val) 641{ 642 /* If the input header (key) matches the regex, echo it intact to 643 * r->headers_out. 644 */ 645 if (!ap_regexec(v->hdr->regex, key, 0, NULL, 0)) { 646 apr_table_add(v->r->headers_out, key, val); 647 } 648 649 return 1; 650} 651 652static int edit_header(void *v, const char *key, const char *val) 653{ 654 edit_do *ed = (edit_do *)v; 655 const char *repl = process_regexp(ed->hdr, val, ed->r); 656 if (repl == NULL) 657 return 0; 658 659 apr_table_addn(ed->t, key, repl); 660 return 1; 661} 662 663static int add_them_all(void *v, const char *key, const char *val) 664{ 665 apr_table_t *headers = (apr_table_t *)v; 666 667 apr_table_addn(headers, key, val); 668 return 1; 669} 670 671static int do_headers_fixup(request_rec *r, apr_table_t *headers, 672 apr_array_header_t *fixup, int early) 673{ 674 echo_do v; 675 int i; 676 const char *val; 677 678 for (i = 0; i < fixup->nelts; ++i) { 679 header_entry *hdr = &((header_entry *) (fixup->elts))[i]; 680 const char *envar = hdr->condition_var; 681 682 /* ignore early headers in late calls */ 683 if (!early && (envar == condition_early)) { 684 continue; 685 } 686 /* ignore late headers in early calls */ 687 else if (early && (envar != condition_early)) { 688 continue; 689 } 690 /* Do we have an expression to evaluate? */ 691 else if (hdr->expr != NULL) { 692 const char *err = NULL; 693 int eval = ap_expr_exec(r, hdr->expr, &err); 694 if (err) { 695 ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01501) 696 "Failed to evaluate expression (%s) - ignoring", 697 err); 698 } 699 else if (!eval) { 700 continue; 701 } 702 } 703 /* Have any conditional envar-controlled Header processing to do? */ 704 else if (envar && !early) { 705 if (*envar != '!') { 706 if (apr_table_get(r->subprocess_env, envar) == NULL) 707 continue; 708 } 709 else { 710 if (apr_table_get(r->subprocess_env, &envar[1]) != NULL) 711 continue; 712 } 713 } 714 715 switch (hdr->action) { 716 case hdr_add: 717 apr_table_addn(headers, hdr->header, process_tags(hdr, r)); 718 break; 719 case hdr_append: 720 apr_table_mergen(headers, hdr->header, process_tags(hdr, r)); 721 break; 722 case hdr_merge: 723 val = apr_table_get(headers, hdr->header); 724 if (val == NULL) { 725 apr_table_addn(headers, hdr->header, process_tags(hdr, r)); 726 } else { 727 char *new_val = process_tags(hdr, r); 728 apr_size_t new_val_len = strlen(new_val); 729 int tok_found = 0; 730 731 /* modified version of logic in ap_get_token() */ 732 while (*val) { 733 const char *tok_start; 734 735 while (apr_isspace(*val)) 736 ++val; 737 738 tok_start = val; 739 740 while (*val && *val != ',') { 741 if (*val++ == '"') 742 while (*val) 743 if (*val++ == '"') 744 break; 745 } 746 747 if (new_val_len == (apr_size_t)(val - tok_start) 748 && !strncmp(tok_start, new_val, new_val_len)) { 749 tok_found = 1; 750 break; 751 } 752 753 if (*val) 754 ++val; 755 } 756 757 if (!tok_found) { 758 apr_table_mergen(headers, hdr->header, new_val); 759 } 760 } 761 break; 762 case hdr_set: 763 if (!strcasecmp(hdr->header, "Content-Type")) { 764 ap_set_content_type(r, process_tags(hdr, r)); 765 } 766 apr_table_setn(headers, hdr->header, process_tags(hdr, r)); 767 break; 768 case hdr_setifempty: 769 if (NULL == apr_table_get(headers, hdr->header)) { 770 if (!strcasecmp(hdr->header, "Content-Type")) { 771 ap_set_content_type(r, process_tags(hdr, r)); 772 } 773 apr_table_setn(headers, hdr->header, process_tags(hdr, r)); 774 } 775 break; 776 case hdr_unset: 777 apr_table_unset(headers, hdr->header); 778 break; 779 case hdr_echo: 780 v.r = r; 781 v.hdr = hdr; 782 apr_table_do((int (*) (void *, const char *, const char *)) 783 echo_header, (void *) &v, r->headers_in, NULL); 784 break; 785 case hdr_edit: 786 case hdr_edit_r: 787 if (!strcasecmp(hdr->header, "Content-Type") && r->content_type) { 788 const char *repl = process_regexp(hdr, r->content_type, r); 789 if (repl == NULL) 790 return 0; 791 ap_set_content_type(r, repl); 792 } 793 if (apr_table_get(headers, hdr->header)) { 794 edit_do ed; 795 796 ed.r = r; 797 ed.hdr = hdr; 798 ed.t = apr_table_make(r->pool, 5); 799 if (!apr_table_do(edit_header, (void *) &ed, headers, 800 hdr->header, NULL)) 801 return 0; 802 apr_table_unset(headers, hdr->header); 803 apr_table_do(add_them_all, (void *) headers, ed.t, NULL); 804 } 805 break; 806 case hdr_note: 807 apr_table_setn(r->notes, process_tags(hdr, r), apr_table_get(headers, hdr->header)); 808 break; 809 810 } 811 } 812 return 1; 813} 814 815static void ap_headers_insert_output_filter(request_rec *r) 816{ 817 headers_conf *dirconf = ap_get_module_config(r->per_dir_config, 818 &headers_module); 819 820 if (dirconf->fixup_out->nelts || dirconf->fixup_err->nelts) { 821 ap_add_output_filter("FIXUP_HEADERS_OUT", NULL, r, r->connection); 822 } 823} 824 825/* 826 * Make sure our error-path filter is in place. 827 */ 828static void ap_headers_insert_error_filter(request_rec *r) 829{ 830 headers_conf *dirconf = ap_get_module_config(r->per_dir_config, 831 &headers_module); 832 833 if (dirconf->fixup_err->nelts) { 834 ap_add_output_filter("FIXUP_HEADERS_ERR", NULL, r, r->connection); 835 } 836} 837 838static apr_status_t ap_headers_output_filter(ap_filter_t *f, 839 apr_bucket_brigade *in) 840{ 841 headers_conf *dirconf = ap_get_module_config(f->r->per_dir_config, 842 &headers_module); 843 844 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, f->r->server, APLOGNO(01502) 845 "headers: ap_headers_output_filter()"); 846 847 /* do the fixup */ 848 do_headers_fixup(f->r, f->r->err_headers_out, dirconf->fixup_err, 0); 849 do_headers_fixup(f->r, f->r->headers_out, dirconf->fixup_out, 0); 850 851 /* remove ourselves from the filter chain */ 852 ap_remove_output_filter(f); 853 854 /* send the data up the stack */ 855 return ap_pass_brigade(f->next,in); 856} 857 858/* 859 * Make sure we propagate any "Header always" settings on the error 860 * path through http_protocol.c. 861 */ 862static apr_status_t ap_headers_error_filter(ap_filter_t *f, 863 apr_bucket_brigade *in) 864{ 865 headers_conf *dirconf; 866 867 dirconf = ap_get_module_config(f->r->per_dir_config, 868 &headers_module); 869 ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, f->r->server, APLOGNO(01503) 870 "headers: ap_headers_error_filter()"); 871 872 /* 873 * Add any header fields defined by "Header always" to r->err_headers_out. 874 * Server-wide first, then per-directory to allow overriding. 875 */ 876 do_headers_fixup(f->r, f->r->err_headers_out, dirconf->fixup_err, 0); 877 878 /* 879 * We've done our bit; remove ourself from the filter chain so there's 880 * no possibility we'll be called again. 881 */ 882 ap_remove_output_filter(f); 883 884 /* 885 * Pass the buck. (euro?) 886 */ 887 return ap_pass_brigade(f->next, in); 888} 889 890static apr_status_t ap_headers_fixup(request_rec *r) 891{ 892 headers_conf *dirconf = ap_get_module_config(r->per_dir_config, 893 &headers_module); 894 895 /* do the fixup */ 896 if (dirconf->fixup_in->nelts) { 897 do_headers_fixup(r, r->headers_in, dirconf->fixup_in, 0); 898 } 899 900 return DECLINED; 901} 902static apr_status_t ap_headers_early(request_rec *r) 903{ 904 headers_conf *dirconf = ap_get_module_config(r->per_dir_config, 905 &headers_module); 906 907 /* do the fixup */ 908 if (dirconf->fixup_in->nelts) { 909 if (!do_headers_fixup(r, r->headers_in, dirconf->fixup_in, 1)) 910 goto err; 911 } 912 if (dirconf->fixup_err->nelts) { 913 if (!do_headers_fixup(r, r->err_headers_out, dirconf->fixup_err, 1)) 914 goto err; 915 } 916 if (dirconf->fixup_out->nelts) { 917 if (!do_headers_fixup(r, r->headers_out, dirconf->fixup_out, 1)) 918 goto err; 919 } 920 921 return DECLINED; 922err: 923 ap_log_rerror(APLOG_MARK, APLOG_CRIT, 0, r, APLOGNO(01504) 924 "Regular expression replacement failed (replacement too long?)"); 925 return HTTP_INTERNAL_SERVER_ERROR; 926} 927 928static const command_rec headers_cmds[] = 929{ 930 AP_INIT_RAW_ARGS("Header", header_cmd, &hdr_out_onsuccess, OR_FILEINFO, 931 "an optional condition, an action, header and value " 932 "followed by optional env clause"), 933 AP_INIT_RAW_ARGS("RequestHeader", header_cmd, &hdr_in, OR_FILEINFO, 934 "an action, header and value followed by optional env " 935 "clause"), 936 {NULL} 937}; 938 939static void register_format_tag_handler(const char *tag, 940 format_tag_fn *tag_handler) 941{ 942 apr_hash_set(format_tag_hash, tag, 1, tag_handler); 943} 944 945static int header_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp) 946{ 947 format_tag_hash = apr_hash_make(p); 948 register_format_tag_handler("D", header_request_duration); 949 register_format_tag_handler("t", header_request_time); 950 register_format_tag_handler("e", header_request_env_var); 951 register_format_tag_handler("s", header_request_ssl_var); 952 register_format_tag_handler("l", header_request_loadavg); 953 register_format_tag_handler("i", header_request_idle); 954 register_format_tag_handler("b", header_request_busy); 955 956 return OK; 957} 958 959static int header_post_config(apr_pool_t *pconf, apr_pool_t *plog, 960 apr_pool_t *ptemp, server_rec *s) 961{ 962 header_ssl_lookup = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup); 963 return OK; 964} 965 966static void register_hooks(apr_pool_t *p) 967{ 968 ap_register_output_filter("FIXUP_HEADERS_OUT", ap_headers_output_filter, 969 NULL, AP_FTYPE_CONTENT_SET); 970 ap_register_output_filter("FIXUP_HEADERS_ERR", ap_headers_error_filter, 971 NULL, AP_FTYPE_CONTENT_SET); 972 ap_hook_pre_config(header_pre_config,NULL,NULL,APR_HOOK_MIDDLE); 973 ap_hook_post_config(header_post_config,NULL,NULL,APR_HOOK_MIDDLE); 974 ap_hook_insert_filter(ap_headers_insert_output_filter, NULL, NULL, APR_HOOK_LAST); 975 ap_hook_insert_error_filter(ap_headers_insert_error_filter, 976 NULL, NULL, APR_HOOK_LAST); 977 ap_hook_fixups(ap_headers_fixup, NULL, NULL, APR_HOOK_LAST); 978 ap_hook_post_read_request(ap_headers_early, NULL, NULL, APR_HOOK_FIRST); 979} 980 981AP_DECLARE_MODULE(headers) = 982{ 983 STANDARD20_MODULE_STUFF, 984 create_headers_dir_config, /* dir config creater */ 985 merge_headers_config, /* dir merger --- default is to override */ 986 NULL, /* server config */ 987 NULL, /* merge server configs */ 988 headers_cmds, /* command apr_table_t */ 989 register_hooks /* register hooks */ 990}; 991