bottleneck testcase based on rubbos
[bottlenecks.git] / rubbos / app / httpd-2.0.64 / modules / metadata / mod_headers.c
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  *     unset  - remove this header
37  *
38  * Where action is unset, the third argument (value) should not be given.
39  * The header name can include the colon, or not.
40  *
41  * The Header and RequestHeader directives can only be used where allowed
42  * by the FileInfo override.
43  *
44  * When the request is processed, the header directives are processed in
45  * this order: firstly, the main server, then the virtual server handling
46  * this request (if any), then any <Directory> sections (working downwards 
47  * from the root dir), then an <Location> sections (working down from 
48  * shortest URL component), the any <File> sections. This order is
49  * important if any 'set' or 'unset' actions are used. For example,
50  * the following two directives have different effect if applied in
51  * the reverse order:
52  *
53  *   Header append Author "John P. Doe"
54  *   Header unset Author
55  *
56  * Examples:
57  *
58  *  To set the "Author" header, use
59  *     Header add Author "John P. Doe"
60  *
61  *  To remove a header:
62  *     Header unset Author
63  *
64  */
65
66 #include "apr.h"
67 #include "apr_lib.h"
68 #include "apr_strings.h"
69 #include "apr_buckets.h"
70
71 #include "apr_hash.h"
72 #define APR_WANT_STRFUNC
73 #include "apr_want.h"
74
75 #include "httpd.h"
76 #include "http_config.h"
77 #include "http_request.h"
78 #include "http_log.h"
79 #include "util_filter.h"
80 #include "http_protocol.h" /* ap_hook_insert_error_filter */
81
82 /* format_tag_hash is initialized during pre-config */
83 static apr_hash_t *format_tag_hash;
84
85 typedef enum {
86     hdr_add = 'a',              /* add header (could mean multiple hdrs) */
87     hdr_set = 's',              /* set (replace old value) */
88     hdr_append = 'm',           /* append (merge into any old value) */
89     hdr_unset = 'u',            /* unset header */
90     hdr_echo = 'e'              /* echo headers from request to response */
91 } hdr_actions;
92
93 /*
94  * magic cmd->info values
95  */
96 static char hdr_in  = '0';  /* RequestHeader */
97 static char hdr_out = '1';  /* Header onsuccess */
98 static char hdr_err = '2';  /* Header always */
99
100 /*
101  * There is an array of struct format_tag per Header/RequestHeader 
102  * config directive
103  */
104 typedef struct {
105     const char* (*func)(request_rec *r,char *arg);
106     char *arg;
107 } format_tag;
108
109 /*
110  * There is one "header_entry" per Header/RequestHeader config directive
111  */
112 typedef struct {
113     hdr_actions action;
114     char *header;
115     apr_array_header_t *ta;   /* Array of format_tag structs */
116     regex_t *regex;
117     const char *condition_var;
118 } header_entry;
119
120 /* echo_do is used for Header echo to iterate through the request headers*/
121 typedef struct {
122     request_rec *r;
123     header_entry *hdr;
124 } echo_do;
125
126 /*
127  * headers_conf is our per-module configuration. This is used as both
128  * a per-dir and per-server config
129  */
130 typedef struct {
131     apr_array_header_t *fixup_in;
132     apr_array_header_t *fixup_out;
133     apr_array_header_t *fixup_err;
134 } headers_conf;
135
136 module AP_MODULE_DECLARE_DATA headers_module;
137
138 /*
139  * Tag formatting functions
140  */
141 static const char *constant_item(request_rec *r, char *stuff)
142 {
143     return stuff;
144 }
145 static const char *header_request_duration(request_rec *r, char *a)
146 {
147     return apr_psprintf(r->pool, "D=%" APR_TIME_T_FMT, 
148                         (apr_time_now() - r->request_time)); 
149 }
150 static const char *header_request_time(request_rec *r, char *a)
151 {
152     return apr_psprintf(r->pool, "t=%" APR_TIME_T_FMT, r->request_time);
153 }
154 static const char *header_request_env_var(request_rec *r, char *a)
155 {
156     const char *s = apr_table_get(r->subprocess_env,a);
157
158     if (s)
159         return s;
160     else
161         return "(null)";
162 }
163 /*
164  * Config routines
165  */
166 static void *create_headers_config(apr_pool_t *p, char *dummy)
167 {
168     headers_conf *conf = apr_palloc(p, sizeof(*conf));
169
170     conf->fixup_in = apr_array_make(p, 2, sizeof(header_entry));
171     conf->fixup_out = apr_array_make(p, 2, sizeof(header_entry));
172     conf->fixup_err = apr_array_make(p, 2, sizeof(header_entry));
173
174     return conf;
175 }
176
177 static void *merge_headers_config(apr_pool_t *p, void *basev, void *overridesv)
178 {
179     headers_conf *newconf = apr_palloc(p, sizeof(*newconf));
180     headers_conf *base = basev;
181     headers_conf *overrides = overridesv;
182
183     newconf->fixup_in = apr_array_append(p, base->fixup_in, overrides->fixup_in);
184     newconf->fixup_out = apr_array_append(p, base->fixup_out, overrides->fixup_out);
185     newconf->fixup_err = apr_array_append(p, base->fixup_err, overrides->fixup_err);
186
187     return newconf;
188 }
189  
190 static char *parse_misc_string(apr_pool_t *p, format_tag *tag, const char **sa)
191 {
192     const char *s;
193     char *d;
194
195     tag->func = constant_item;
196
197     s = *sa;
198     while (*s && *s != '%') {
199         s++;
200     }
201     /*
202      * This might allocate a few chars extra if there's a backslash
203      * escape in the format string.
204      */
205     tag->arg = apr_palloc(p, s - *sa + 1);
206
207     d = tag->arg;
208     s = *sa;
209     while (*s && *s != '%') {
210         if (*s != '\\') {
211             *d++ = *s++;
212         }
213         else {
214             s++;
215             switch (*s) {
216             case '\\':
217                 *d++ = '\\';
218                 s++;
219                 break;
220             case 'r':
221                 *d++ = '\r';
222                 s++;
223                 break;
224             case 'n':
225                 *d++ = '\n';
226                 s++;
227                 break;
228             case 't':   
229                 *d++ = '\t';
230                 s++;
231                 break;
232             default:
233                 /* copy verbatim */
234                 *d++ = '\\';
235                 /*
236                  * Allow the loop to deal with this *s in the normal
237                  * fashion so that it handles end of string etc.
238                  * properly.
239                  */
240                 break;
241             }
242         }
243     }
244     *d = '\0';
245
246     *sa = s;
247     return NULL;
248 }
249
250 static char *parse_format_tag(apr_pool_t *p, format_tag *tag, const char **sa)
251
252     const char *s = *sa;
253     const char * (*tag_handler)(request_rec *,char *);
254
255     /* Handle string literal/conditionals */
256     if (*s != '%') {
257         return parse_misc_string(p, tag, sa);
258     }
259     s++; /* skip the % */
260     tag->arg = '\0';
261     /* grab the argument if there is one */
262     if (*s == '{') {
263         ++s;
264         tag->arg = ap_getword(p,&s,'}');
265     }
266
267     tag_handler = (const char * (*)(request_rec *,char *))apr_hash_get(format_tag_hash, s++, 1);
268
269     if (!tag_handler) {
270         char dummy[2];
271         dummy[0] = s[-1];
272         dummy[1] = '\0';
273         return apr_pstrcat(p, "Unrecognized Header or RequestHeader directive %",
274                            dummy, NULL);
275     }
276     tag->func = tag_handler;
277
278     *sa = s;
279     return NULL;
280 }
281
282 /*
283  * A format string consists of white space, text and optional format 
284  * tags in any order. E.g., 
285  *
286  * Header add MyHeader "Free form text %D %t more text"
287  *
288  * Decompose the format string into its tags. Each tag (struct format_tag)
289  * contains a pointer to the function used to format the tag. Then save each 
290  * tag in the tag array anchored in the header_entry.
291  */
292 static char *parse_format_string(apr_pool_t *p, header_entry *hdr, const char *s)
293 {
294     char *res;
295
296     /* No string to parse with unset and copy commands */
297     if (hdr->action == hdr_unset ||
298         hdr->action == hdr_echo) {
299         return NULL;
300     }
301
302     hdr->ta = apr_array_make(p, 10, sizeof(format_tag));
303
304     while (*s) {
305         if ((res = parse_format_tag(p, (format_tag *) apr_array_push(hdr->ta), &s))) {
306             return res;
307         }
308     }
309     return NULL;
310 }
311
312 /* handle RequestHeader and Header directive */
313 static const char *header_inout_cmd(cmd_parms *cmd, void *indirconf,
314                               const char *action, const char *inhdr,
315                               const char *value, const char* envclause)
316 {
317     headers_conf *dirconf = indirconf;
318     const char *condition_var = NULL;
319     char *colon;
320     char *hdr = apr_pstrdup(cmd->pool, inhdr);
321     header_entry *new;
322     apr_array_header_t *fixup = (cmd->info == &hdr_in)
323         ? dirconf->fixup_in   : (cmd->info == &hdr_err)
324         ? dirconf->fixup_err
325         : dirconf->fixup_out;
326
327     new = (header_entry *) apr_array_push(fixup);
328
329     if (!strcasecmp(action, "set"))
330         new->action = hdr_set;
331     else if (!strcasecmp(action, "add"))
332         new->action = hdr_add;
333     else if (!strcasecmp(action, "append"))
334         new->action = hdr_append;
335     else if (!strcasecmp(action, "unset"))
336         new->action = hdr_unset;
337     else if (!strcasecmp(action, "echo"))
338         new->action = hdr_echo;
339     else
340         return "first argument must be add, set, append, unset or echo.";
341
342     if (new->action == hdr_unset) {
343         if (value)
344             return "header unset takes two arguments";
345     }
346     else if (new->action == hdr_echo) {
347         regex_t *regex;
348         if (value)
349             return "Header echo takes two arguments";
350         else if (cmd->info == &hdr_in)
351             return "Header echo only valid on Header directive";
352         else {
353             regex = ap_pregcomp(cmd->pool, hdr, REG_EXTENDED | REG_NOSUB);
354             if (regex == NULL) {
355                 return "Header echo regex could not be compiled";
356             }
357         }
358         new->regex = regex;
359     }
360     else if (!value)
361         return "header requires three arguments";
362
363     /* Handle the envclause on Header */
364     if (envclause != NULL) {
365         if (strncasecmp(envclause, "env=", 4) != 0) {
366             return "error: envclause should be in the form env=envar";
367         }
368         if ((envclause[4] == '\0')
369             || ((envclause[4] == '!') && (envclause[5] == '\0'))) {
370             return "error: missing environment variable name. envclause should be in the form env=envar ";
371         }
372         condition_var = apr_pstrdup(cmd->pool, &envclause[4]);
373     }
374     
375     if ((colon = strchr(hdr, ':')))
376         *colon = '\0';
377
378     new->header = hdr;
379     new->condition_var = condition_var;
380
381     return parse_format_string(cmd->pool, new, value);
382 }
383
384 /* Handle all (xxx)Header directives */
385 static const char *header_cmd(cmd_parms *cmd, void *indirconf,
386                               const char *args)
387 {
388     const char *s;
389     const char *action;
390     const char *hdr;
391     const char *val;
392     const char *envclause;
393
394     s = apr_pstrdup(cmd->pool, args);
395     action = ap_getword_conf(cmd->pool, &s);
396     if (cmd->info == &hdr_out) {
397         if (!strcasecmp(action, "always")) {
398             cmd->info = &hdr_err;
399             action = ap_getword_conf(cmd->pool, &s);
400         }
401         else if (!strcasecmp(action, "onsuccess")) {
402             action = ap_getword_conf(cmd->pool, &s);
403         }
404     }
405     hdr = ap_getword_conf(cmd->pool, &s);
406     val = *s ? ap_getword_conf(cmd->pool, &s) : NULL;
407     envclause = *s ? ap_getword_conf(cmd->pool, &s) : NULL;
408
409     if (*s) {
410         return apr_pstrcat(cmd->pool, cmd->cmd->name,
411                            " has too many arguments", NULL);
412     }
413
414     return header_inout_cmd(cmd, indirconf, action, hdr, val, envclause);
415 }
416
417 /*
418  * Process the tags in the format string. Tags may be format specifiers 
419  * (%D, %t, etc.), whitespace or text strings. For each tag, run the handler
420  * (formatter) specific to the tag. Handlers return text strings.
421  * Concatenate the return from each handler into one string that is 
422  * returned from this call.
423  */
424 static char* process_tags(header_entry *hdr, request_rec *r) 
425 {
426     int i;
427     const char *s;
428     char *str = NULL;
429
430     format_tag *tag = (format_tag*) hdr->ta->elts;
431  
432     for (i = 0; i < hdr->ta->nelts; i++) {
433         s = tag[i].func(r, tag[i].arg);
434         if (str == NULL) 
435             str = apr_pstrdup(r->pool, s);
436         else
437             str = apr_pstrcat(r->pool, str, s, NULL);
438     }
439     return str ? str : "";
440 }
441
442 static int echo_header(echo_do *v, const char *key, const char *val)
443 {
444     /* If the input header (key) matches the regex, echo it intact to 
445      * r->headers_out.
446      */
447     if (!ap_regexec(v->hdr->regex, key, 0, NULL, 0)) {
448         apr_table_add(v->r->headers_out, key, val);
449     }
450
451     return 1;
452 }
453
454 static void do_headers_fixup(request_rec *r, apr_table_t *headers,
455                              apr_array_header_t *fixup)
456 {
457     int i;
458
459     for (i = 0; i < fixup->nelts; ++i) {
460         header_entry *hdr = &((header_entry *) (fixup->elts))[i];
461
462         /* Have any conditional envar-controlled Header processing to do? */
463         if (hdr->condition_var) {
464             const char *envar = hdr->condition_var;
465             if (*envar != '!') {
466                 if (apr_table_get(r->subprocess_env, envar) == NULL)
467                     continue;
468             }
469             else {
470                 if (apr_table_get(r->subprocess_env, &envar[1]) != NULL)
471                     continue;
472             }
473         }
474
475         switch (hdr->action) {
476         case hdr_add:
477             apr_table_addn(headers, hdr->header, process_tags(hdr, r));
478             break;
479         case hdr_append:
480             apr_table_mergen(headers, hdr->header, process_tags(hdr, r));
481             break;
482         case hdr_set:
483             apr_table_setn(headers, hdr->header, process_tags(hdr, r));
484             break;
485         case hdr_unset:
486             apr_table_unset(headers, hdr->header);
487             break;
488         case hdr_echo:
489         {
490             echo_do v;
491             v.r = r;
492             v.hdr = hdr;
493             apr_table_do((int (*) (void *, const char *, const char *)) 
494                          echo_header, (void *) &v, r->headers_in, NULL);
495             break;
496         }
497         }
498     }
499 }
500
501 static void ap_headers_insert_output_filter(request_rec *r)
502 {
503     headers_conf *dirconf = ap_get_module_config(r->per_dir_config,
504                                                  &headers_module);
505
506     if (dirconf->fixup_out->nelts || dirconf->fixup_err->nelts) {
507         ap_add_output_filter("FIXUP_HEADERS_OUT", NULL, r, r->connection);
508     }
509 }
510
511 static void ap_headers_insert_error_filter(request_rec *r)
512 {
513     headers_conf *dirconf = ap_get_module_config(r->per_dir_config,
514                                                  &headers_module);
515
516     if (dirconf->fixup_err->nelts) {
517         ap_add_output_filter("FIXUP_HEADERS_ERR", NULL, r, r->connection);
518     }
519 }
520
521 static apr_status_t ap_headers_output_filter(ap_filter_t *f,
522                                              apr_bucket_brigade *in)
523 {
524     headers_conf *dirconf = ap_get_module_config(f->r->per_dir_config,
525                                                  &headers_module);
526
527     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, f->r->server,
528                  "headers: ap_headers_output_filter()");
529
530     /* do the fixup */
531     do_headers_fixup(f->r, f->r->err_headers_out, dirconf->fixup_err);
532     do_headers_fixup(f->r, f->r->headers_out, dirconf->fixup_out);
533
534     /* remove ourselves from the filter chain */
535     ap_remove_output_filter(f);
536
537     /* send the data up the stack */
538     return ap_pass_brigade(f->next,in);
539 }
540
541 static apr_status_t ap_headers_error_filter(ap_filter_t *f,
542                                             apr_bucket_brigade *in)
543 {
544     headers_conf *dirconf = ap_get_module_config(f->r->per_dir_config,
545                                                  &headers_module);
546
547     ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, f->r->server,
548                  "headers: ap_headers_error_filter()");
549
550     /* do the fixup */
551     do_headers_fixup(f->r, f->r->err_headers_out, dirconf->fixup_err);
552
553     /* remove ourselves from the filter chain */
554     ap_remove_output_filter(f);
555
556     /* send the data up the stack */
557     return ap_pass_brigade(f->next,in);
558 }
559
560 static apr_status_t ap_headers_fixup(request_rec *r)
561 {
562     headers_conf *dirconf = ap_get_module_config(r->per_dir_config,
563                                                  &headers_module);
564
565     /* do the fixup */
566     if (dirconf->fixup_in->nelts) {
567         do_headers_fixup(r, r->headers_in, dirconf->fixup_in);
568     }
569
570     return DECLINED;
571 }
572                                         
573 static const command_rec headers_cmds[] =
574 {
575     AP_INIT_RAW_ARGS("Header", header_cmd, &hdr_out, OR_FILEINFO,
576                    "an optional condition, an action, header and value "
577                    "followed by optional env clause"),
578     AP_INIT_RAW_ARGS("RequestHeader", header_cmd, &hdr_in, OR_FILEINFO,
579                    "an action, header and value"),
580     {NULL}
581 };
582
583 static void register_format_tag_handler(apr_pool_t *p, char *tag, void *tag_handler, int def)
584 {
585     const void *h = apr_palloc(p, sizeof(h));
586     h = tag_handler;
587     apr_hash_set(format_tag_hash, tag, 1, h);
588 }
589 static int header_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp)
590 {
591     format_tag_hash = apr_hash_make(p);
592     register_format_tag_handler(p, "D", (void*) header_request_duration, 0);
593     register_format_tag_handler(p, "t", (void*) header_request_time, 0);
594     register_format_tag_handler(p, "e", (void*) header_request_env_var, 0);
595
596     return OK;
597 }
598
599 static void register_hooks(apr_pool_t *p)
600 {
601     ap_hook_pre_config(header_pre_config,NULL,NULL,APR_HOOK_MIDDLE);
602     ap_hook_insert_filter(ap_headers_insert_output_filter, NULL, NULL, APR_HOOK_LAST);
603     ap_hook_insert_error_filter(ap_headers_insert_error_filter, NULL, NULL, APR_HOOK_LAST);
604     ap_hook_fixups(ap_headers_fixup, NULL, NULL, APR_HOOK_LAST);
605     ap_register_output_filter("FIXUP_HEADERS_OUT", ap_headers_output_filter,
606                               NULL, AP_FTYPE_CONTENT_SET);
607     ap_register_output_filter("FIXUP_HEADERS_ERR", ap_headers_error_filter,
608                               NULL, AP_FTYPE_CONTENT_SET);
609 }
610
611 module AP_MODULE_DECLARE_DATA headers_module =
612 {
613     STANDARD20_MODULE_STUFF,
614     create_headers_config,      /* dir config creater */
615     merge_headers_config,       /* dir merger --- default is to override */
616     NULL,                       /* server config */
617     NULL,                       /* merge server configs */
618     headers_cmds,               /* command apr_table_t */
619     register_hooks              /* register hooks */
620 };