]> granicus.if.org Git - apache/blob - modules/filters/mod_reflector.c
mod_brotli: Allow compression ratio logging with new BrotliFilterNote
[apache] / modules / filters / mod_reflector.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 #include "apr_strings.h"
17 #include "apr_tables.h"
18
19 #include "httpd.h"
20 #include "http_config.h"
21 #include "http_core.h"
22 #include "http_log.h"
23 #include "http_protocol.h"
24 #include "http_request.h"
25 #include "mod_core.h"
26
27 module AP_MODULE_DECLARE_DATA reflector_module;
28
29 typedef struct {
30     apr_table_t *headers;
31 } reflector_cfg;
32
33 static int header_do(void *dummy, const char *key, const char *value)
34 {
35     request_rec *r = (request_rec *) dummy;
36     const char *payload;
37
38     payload = apr_table_get(r->headers_in, key);
39     if (payload) {
40         apr_table_setn(r->headers_out, value, payload);
41     }
42
43     return 1;
44 }
45
46 static int reflector_handler(request_rec * r)
47 {
48     apr_bucket_brigade *bbin, *bbout;
49     reflector_cfg *conf;
50     apr_status_t status;
51
52     if (strcmp(r->handler, "reflector")) {
53         return DECLINED;
54     }
55
56     conf = (reflector_cfg *) ap_get_module_config(r->per_dir_config,
57                                                   &reflector_module);
58
59     ap_allow_methods(r, 1, "POST", "OPTIONS", NULL);
60
61     if (r->method_number == M_OPTIONS) {
62         return ap_send_http_options(r);
63     }
64
65     else if (r->method_number == M_POST) {
66         const char *content_length, *content_type;
67         int seen_eos;
68
69         /*
70          * Sometimes we'll get in a state where the input handling has
71          * detected an error where we want to drop the connection, so if
72          * that's the case, don't read the data as that is what we're trying
73          * to avoid.
74          *
75          * This function is also a no-op on a subrequest.
76          */
77         if (r->main || r->connection->keepalive == AP_CONN_CLOSE ||
78             ap_status_drops_connection(r->status)) {
79             return OK;
80         }
81
82         /* copy headers from in to out if configured */
83         apr_table_do(header_do, r, conf->headers, NULL);
84
85         /* last modified defaults to now, unless otherwise set on the way in */
86         if (!apr_table_get(r->headers_out, "Last-Modified")) {
87             ap_update_mtime(r, apr_time_now());
88             ap_set_last_modified(r);
89         }
90         ap_set_accept_ranges(r);
91
92         /* reflect the content length, if present */
93         if ((content_length = apr_table_get(r->headers_in, "Content-Length"))) {
94             apr_off_t offset;
95
96             apr_strtoff(&offset, content_length, NULL, 10);
97             ap_set_content_length(r, offset);
98
99         }
100
101         /* reflect the content type, if present */
102         if ((content_type = apr_table_get(r->headers_in, "Content-Type"))) {
103
104             ap_set_content_type(r, content_type);
105
106         }
107
108         bbin = apr_brigade_create(r->pool, r->connection->bucket_alloc);
109         bbout = apr_brigade_create(r->pool, r->connection->bucket_alloc);
110
111         seen_eos = 0;
112         do {
113             apr_bucket *bucket;
114
115             status = ap_get_brigade(r->input_filters, bbin, AP_MODE_READBYTES,
116                                     APR_BLOCK_READ, HUGE_STRING_LEN);
117
118             if (status != APR_SUCCESS) {
119                 apr_brigade_destroy(bbin);
120                 return ap_map_http_request_error(status, HTTP_BAD_REQUEST);
121             }
122
123             for (bucket = APR_BRIGADE_FIRST(bbin);
124                  bucket != APR_BRIGADE_SENTINEL(bbin);
125                  bucket = APR_BUCKET_NEXT(bucket)) {
126                 const char *data;
127                 apr_size_t len;
128
129                 if (APR_BUCKET_IS_EOS(bucket)) {
130                     seen_eos = 1;
131                     break;
132                 }
133
134                 /* These are metadata buckets. */
135                 if (bucket->length == 0) {
136                     continue;
137                 }
138
139                 /*
140                  * We MUST read because in case we have an unknown-length
141                  * bucket or one that morphs, we want to exhaust it.
142                  */
143                 status = apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);
144                 if (status != APR_SUCCESS) {
145                     apr_brigade_destroy(bbin);
146                     return HTTP_BAD_REQUEST;
147                 }
148
149                 apr_brigade_write(bbout, NULL, NULL, data, len);
150
151                 status = ap_pass_brigade(r->output_filters, bbout);
152                 if (status != APR_SUCCESS) {
153                     /* no way to know what type of error occurred */
154                     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r, APLOGNO(01410)
155                              "reflector_handler: ap_pass_brigade returned %i",
156                                   status);
157                     return AP_FILTER_ERROR;
158                 }
159
160             }
161
162             apr_brigade_cleanup(bbin);
163
164         } while (!seen_eos);
165
166         return OK;
167
168     }
169
170     else {
171         return HTTP_METHOD_NOT_ALLOWED;
172     }
173
174 }
175
176 static void *create_reflector_dir_config(apr_pool_t * p, char *d)
177 {
178     reflector_cfg *conf = apr_pcalloc(p, sizeof(reflector_cfg));
179
180     conf->headers = apr_table_make(p, 8);
181
182     return conf;
183 }
184
185 static void *merge_reflector_dir_config(apr_pool_t * p, void *basev, void *addv)
186 {
187     reflector_cfg *new = (reflector_cfg *) apr_pcalloc(p,
188             sizeof(reflector_cfg));
189     reflector_cfg *add = (reflector_cfg *) addv;
190     reflector_cfg *base = (reflector_cfg *) basev;
191
192     new->headers = apr_table_overlay(p, add->headers, base->headers);
193
194     return new;
195 }
196
197 static const char *reflector_header(cmd_parms * cmd, void *dummy, const char *in,
198         const char *out)
199 {
200     reflector_cfg *cfg = (reflector_cfg *) dummy;
201
202     apr_table_addn(cfg->headers, in, out ? out : in);
203
204     return NULL;
205 }
206
207 static void reflector_hooks(apr_pool_t * p)
208 {
209     ap_hook_handler(reflector_handler, NULL, NULL, APR_HOOK_MIDDLE);
210 }
211
212 static const command_rec reflector_cmds[] = {
213     AP_INIT_TAKE12("ReflectorHeader", reflector_header, NULL, OR_OPTIONS,
214       "Header to reflect back in the response, with an optional new name."),
215     {NULL}
216 };
217
218 AP_DECLARE_MODULE(reflector) = {
219     STANDARD20_MODULE_STUFF,
220     create_reflector_dir_config,
221     merge_reflector_dir_config,
222     NULL,
223     NULL,
224     reflector_cmds,
225     reflector_hooks
226 };