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