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.
16 #include "apr_strings.h"
17 #include "apr_tables.h"
20 #include "http_config.h"
21 #include "http_core.h"
23 #include "http_protocol.h"
24 #include "http_request.h"
27 module AP_MODULE_DECLARE_DATA reflector_module;
33 static int header_do(void *dummy, const char *key, const char *value)
35 request_rec *r = (request_rec *) dummy;
38 payload = apr_table_get(r->headers_in, key);
40 apr_table_setn(r->headers_out, value, payload);
46 static int reflector_handler(request_rec * r)
48 apr_bucket_brigade *bbin, *bbout;
52 if (strcmp(r->handler, "reflector")) {
56 conf = (reflector_cfg *) ap_get_module_config(r->per_dir_config,
59 ap_allow_methods(r, 1, "POST", "OPTIONS", NULL);
61 if (r->method_number == M_OPTIONS) {
62 return ap_send_http_options(r);
65 else if (r->method_number == M_POST) {
66 const char *content_length, *content_type;
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
75 * This function is also a no-op on a subrequest.
77 if (r->main || r->connection->keepalive == AP_CONN_CLOSE ||
78 ap_status_drops_connection(r->status)) {
82 /* copy headers from in to out if configured */
83 apr_table_do(header_do, r, conf->headers, NULL);
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);
90 ap_set_accept_ranges(r);
92 /* reflect the content length, if present */
93 if ((content_length = apr_table_get(r->headers_in, "Content-Length"))) {
96 apr_strtoff(&offset, content_length, NULL, 10);
97 ap_set_content_length(r, offset);
101 /* reflect the content type, if present */
102 if ((content_type = apr_table_get(r->headers_in, "Content-Type"))) {
104 ap_set_content_type(r, content_type);
108 bbin = apr_brigade_create(r->pool, r->connection->bucket_alloc);
109 bbout = apr_brigade_create(r->pool, r->connection->bucket_alloc);
115 status = ap_get_brigade(r->input_filters, bbin, AP_MODE_READBYTES,
116 APR_BLOCK_READ, HUGE_STRING_LEN);
118 if (status != APR_SUCCESS) {
119 apr_brigade_destroy(bbin);
120 return ap_map_http_request_error(status, HTTP_BAD_REQUEST);
123 for (bucket = APR_BRIGADE_FIRST(bbin);
124 bucket != APR_BRIGADE_SENTINEL(bbin);
125 bucket = APR_BUCKET_NEXT(bucket)) {
129 if (APR_BUCKET_IS_EOS(bucket)) {
134 /* These are metadata buckets. */
135 if (bucket->length == 0) {
140 * We MUST read because in case we have an unknown-length
141 * bucket or one that morphs, we want to exhaust it.
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;
149 apr_brigade_write(bbout, NULL, NULL, data, len);
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",
157 return AP_FILTER_ERROR;
162 apr_brigade_cleanup(bbin);
171 return HTTP_METHOD_NOT_ALLOWED;
176 static void *create_reflector_dir_config(apr_pool_t * p, char *d)
178 reflector_cfg *conf = apr_pcalloc(p, sizeof(reflector_cfg));
180 conf->headers = apr_table_make(p, 8);
185 static void *merge_reflector_dir_config(apr_pool_t * p, void *basev, void *addv)
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;
192 new->headers = apr_table_overlay(p, add->headers, base->headers);
197 static const char *reflector_header(cmd_parms * cmd, void *dummy, const char *in,
200 reflector_cfg *cfg = (reflector_cfg *) dummy;
202 apr_table_addn(cfg->headers, in, out ? out : in);
207 static void reflector_hooks(apr_pool_t * p)
209 ap_hook_handler(reflector_handler, NULL, NULL, APR_HOOK_MIDDLE);
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."),
218 AP_DECLARE_MODULE(reflector) = {
219 STANDARD20_MODULE_STUFF,
220 create_reflector_dir_config,
221 merge_reflector_dir_config,