summaryrefslogtreecommitdiffstats
path: root/modules/filters/mod_reflector.c
diff options
context:
space:
mode:
Diffstat (limited to 'modules/filters/mod_reflector.c')
-rw-r--r--modules/filters/mod_reflector.c231
1 files changed, 231 insertions, 0 deletions
diff --git a/modules/filters/mod_reflector.c b/modules/filters/mod_reflector.c
new file mode 100644
index 0000000..5979cb8
--- /dev/null
+++ b/modules/filters/mod_reflector.c
@@ -0,0 +1,231 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "apr_strings.h"
+#include "apr_tables.h"
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_log.h"
+#include "http_protocol.h"
+#include "http_request.h"
+#include "mod_core.h"
+
+module AP_MODULE_DECLARE_DATA reflector_module;
+
+typedef struct {
+ apr_table_t *headers;
+} reflector_cfg;
+
+static int header_do(void *dummy, const char *key, const char *value)
+{
+ request_rec *r = (request_rec *) dummy;
+ const char *payload;
+
+ payload = apr_table_get(r->headers_in, key);
+ if (payload) {
+ apr_table_setn(r->headers_out, value, payload);
+ }
+
+ return 1;
+}
+
+static int reflector_handler(request_rec * r)
+{
+ apr_bucket_brigade *bbin, *bbout;
+ reflector_cfg *conf;
+ apr_status_t status;
+
+ if (strcmp(r->handler, "reflector")) {
+ return DECLINED;
+ }
+
+ conf = (reflector_cfg *) ap_get_module_config(r->per_dir_config,
+ &reflector_module);
+
+ ap_allow_methods(r, 1, "POST", "OPTIONS", NULL);
+
+ if (r->method_number == M_OPTIONS) {
+ return ap_send_http_options(r);
+ }
+
+ else if (r->method_number == M_POST) {
+ const char *content_length, *content_type;
+ int seen_eos;
+
+ /*
+ * Sometimes we'll get in a state where the input handling has
+ * detected an error where we want to drop the connection, so if
+ * that's the case, don't read the data as that is what we're trying
+ * to avoid.
+ *
+ * This function is also a no-op on a subrequest.
+ */
+ if (r->main || r->connection->keepalive == AP_CONN_CLOSE ||
+ ap_status_drops_connection(r->status)) {
+ return OK;
+ }
+
+ /* copy headers from in to out if configured */
+ apr_table_do(header_do, r, conf->headers, NULL);
+
+ /* last modified defaults to now, unless otherwise set on the way in */
+ if (!apr_table_get(r->headers_out, "Last-Modified")) {
+ ap_update_mtime(r, apr_time_now());
+ ap_set_last_modified(r);
+ }
+ ap_set_accept_ranges(r);
+
+ /* reflect the content length, if present */
+ if ((content_length = apr_table_get(r->headers_in, "Content-Length"))) {
+ apr_off_t clen;
+
+ if (!ap_parse_strict_length(&clen, content_length)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10243)
+ "reflector_handler: invalid content-length '%s'",
+ content_length);
+ return HTTP_BAD_REQUEST;
+ }
+
+ ap_set_content_length(r, clen);
+ }
+
+ /* reflect the content type, if present */
+ if ((content_type = apr_table_get(r->headers_in, "Content-Type"))) {
+
+ ap_set_content_type(r, content_type);
+
+ }
+
+ bbin = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+ bbout = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+
+ seen_eos = 0;
+ do {
+ apr_bucket *bucket;
+
+ status = ap_get_brigade(r->input_filters, bbin, AP_MODE_READBYTES,
+ APR_BLOCK_READ, HUGE_STRING_LEN);
+
+ if (status != APR_SUCCESS) {
+ apr_brigade_destroy(bbin);
+ return ap_map_http_request_error(status, HTTP_BAD_REQUEST);
+ }
+
+ for (bucket = APR_BRIGADE_FIRST(bbin);
+ bucket != APR_BRIGADE_SENTINEL(bbin);
+ bucket = APR_BUCKET_NEXT(bucket)) {
+ const char *data;
+ apr_size_t len;
+
+ if (APR_BUCKET_IS_EOS(bucket)) {
+ seen_eos = 1;
+ break;
+ }
+
+ /* These are metadata buckets. */
+ if (bucket->length == 0) {
+ continue;
+ }
+
+ /*
+ * We MUST read because in case we have an unknown-length
+ * bucket or one that morphs, we want to exhaust it.
+ */
+ status = apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);
+ if (status != APR_SUCCESS) {
+ apr_brigade_destroy(bbin);
+ return HTTP_BAD_REQUEST;
+ }
+
+ apr_brigade_write(bbout, NULL, NULL, data, len);
+
+ status = ap_pass_brigade(r->output_filters, bbout);
+ if (status != APR_SUCCESS) {
+ /* no way to know what type of error occurred */
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r, APLOGNO(01410)
+ "reflector_handler: ap_pass_brigade returned %i",
+ status);
+ return AP_FILTER_ERROR;
+ }
+
+ }
+
+ apr_brigade_cleanup(bbin);
+
+ } while (!seen_eos);
+
+ return OK;
+
+ }
+
+ else {
+ return HTTP_METHOD_NOT_ALLOWED;
+ }
+
+}
+
+static void *create_reflector_dir_config(apr_pool_t * p, char *d)
+{
+ reflector_cfg *conf = apr_pcalloc(p, sizeof(reflector_cfg));
+
+ conf->headers = apr_table_make(p, 8);
+
+ return conf;
+}
+
+static void *merge_reflector_dir_config(apr_pool_t * p, void *basev, void *addv)
+{
+ reflector_cfg *new = (reflector_cfg *) apr_pcalloc(p,
+ sizeof(reflector_cfg));
+ reflector_cfg *add = (reflector_cfg *) addv;
+ reflector_cfg *base = (reflector_cfg *) basev;
+
+ new->headers = apr_table_overlay(p, add->headers, base->headers);
+
+ return new;
+}
+
+static const char *reflector_header(cmd_parms * cmd, void *dummy, const char *in,
+ const char *out)
+{
+ reflector_cfg *cfg = (reflector_cfg *) dummy;
+
+ apr_table_addn(cfg->headers, in, out ? out : in);
+
+ return NULL;
+}
+
+static void reflector_hooks(apr_pool_t * p)
+{
+ ap_hook_handler(reflector_handler, NULL, NULL, APR_HOOK_MIDDLE);
+}
+
+static const command_rec reflector_cmds[] = {
+ AP_INIT_TAKE12("ReflectorHeader", reflector_header, NULL, OR_OPTIONS,
+ "Header to reflect back in the response, with an optional new name."),
+ {NULL}
+};
+
+AP_DECLARE_MODULE(reflector) = {
+ STANDARD20_MODULE_STUFF,
+ create_reflector_dir_config,
+ merge_reflector_dir_config,
+ NULL,
+ NULL,
+ reflector_cmds,
+ reflector_hooks
+};