diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 06:33:50 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 06:33:50 +0000 |
commit | fe39ffb8b90ae4e002ed73fe98617cd590abb467 (patch) | |
tree | b80e5956907d8aeaaffe4e4f0c068c0e6157ce8b /modules/filters/mod_reflector.c | |
parent | Initial commit. (diff) | |
download | apache2-fe39ffb8b90ae4e002ed73fe98617cd590abb467.tar.xz apache2-fe39ffb8b90ae4e002ed73fe98617cd590abb467.zip |
Adding upstream version 2.4.56.upstream/2.4.56upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'modules/filters/mod_reflector.c')
-rw-r--r-- | modules/filters/mod_reflector.c | 231 |
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 +}; |