You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
381 lines
15 KiB
381 lines
15 KiB
diff --git a/include/http_core.h b/include/http_core.h |
|
index 3c47989..f6f4aa2 100644 |
|
--- a/include/http_core.h |
|
+++ b/include/http_core.h |
|
@@ -663,6 +663,10 @@ typedef struct { |
|
#define AP_TRACE_ENABLE 1 |
|
#define AP_TRACE_EXTENDED 2 |
|
int trace_enable; |
|
+#define AP_MERGE_TRAILERS_UNSET 0 |
|
+#define AP_MERGE_TRAILERS_ENABLE 1 |
|
+#define AP_MERGE_TRAILERS_DISABLE 2 |
|
+ int merge_trailers; |
|
|
|
} core_server_config; |
|
|
|
diff --git a/include/httpd.h b/include/httpd.h |
|
index 36cd58d..2e415f9 100644 |
|
--- a/include/httpd.h |
|
+++ b/include/httpd.h |
|
@@ -1032,6 +1032,11 @@ struct request_rec { |
|
*/ |
|
apr_sockaddr_t *useragent_addr; |
|
char *useragent_ip; |
|
+ |
|
+ /** MIME trailer environment from the request */ |
|
+ apr_table_t *trailers_in; |
|
+ /** MIME trailer environment from the response */ |
|
+ apr_table_t *trailers_out; |
|
}; |
|
|
|
/** |
|
diff --git a/modules/http/http_filters.c b/modules/http/http_filters.c |
|
index 24a939a..2ae8f46 100644 |
|
--- a/modules/http/http_filters.c |
|
+++ b/modules/http/http_filters.c |
|
@@ -214,6 +214,49 @@ static apr_status_t get_chunk_line(http_ctx_t *ctx, apr_bucket_brigade *b, |
|
} |
|
|
|
|
|
+static apr_status_t read_chunked_trailers(http_ctx_t *ctx, ap_filter_t *f, |
|
+ apr_bucket_brigade *b, int merge) |
|
+{ |
|
+ int rv; |
|
+ apr_bucket *e; |
|
+ request_rec *r = f->r; |
|
+ apr_table_t *saved_headers_in = r->headers_in; |
|
+ int saved_status = r->status; |
|
+ |
|
+ r->status = HTTP_OK; |
|
+ r->headers_in = r->trailers_in; |
|
+ apr_table_clear(r->headers_in); |
|
+ ctx->state = BODY_NONE; |
|
+ ap_get_mime_headers(r); |
|
+ |
|
+ if(r->status == HTTP_OK) { |
|
+ r->status = saved_status; |
|
+ e = apr_bucket_eos_create(f->c->bucket_alloc); |
|
+ APR_BRIGADE_INSERT_TAIL(b, e); |
|
+ ctx->eos_sent = 1; |
|
+ rv = APR_SUCCESS; |
|
+ } |
|
+ else { |
|
+ const char *error_notes = apr_table_get(r->notes, |
|
+ "error-notes"); |
|
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, |
|
+ "Error while reading HTTP trailer: %i%s%s", |
|
+ r->status, error_notes ? ": " : "", |
|
+ error_notes ? error_notes : ""); |
|
+ rv = APR_EINVAL; |
|
+ } |
|
+ |
|
+ if(!merge) { |
|
+ r->headers_in = saved_headers_in; |
|
+ } |
|
+ else { |
|
+ r->headers_in = apr_table_overlay(r->pool, saved_headers_in, |
|
+ r->trailers_in); |
|
+ } |
|
+ |
|
+ return rv; |
|
+} |
|
+ |
|
/* This is the HTTP_INPUT filter for HTTP requests and responses from |
|
* proxied servers (mod_proxy). It handles chunked and content-length |
|
* bodies. This can only be inserted/used after the headers |
|
@@ -223,6 +266,7 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b, |
|
ap_input_mode_t mode, apr_read_type_e block, |
|
apr_off_t readbytes) |
|
{ |
|
+ core_server_config *conf; |
|
apr_bucket *e; |
|
http_ctx_t *ctx = f->ctx; |
|
apr_status_t rv; |
|
@@ -230,6 +274,9 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b, |
|
int http_error = HTTP_REQUEST_ENTITY_TOO_LARGE; |
|
apr_bucket_brigade *bb; |
|
|
|
+ conf = (core_server_config *) |
|
+ ap_get_module_config(f->r->server->module_config, &core_module); |
|
+ |
|
/* just get out of the way of things we don't want. */ |
|
if (mode != AP_MODE_READBYTES && mode != AP_MODE_GETLINE) { |
|
return ap_get_brigade(f->next, b, mode, block, readbytes); |
|
@@ -403,13 +450,8 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b, |
|
} |
|
|
|
if (!ctx->remaining) { |
|
- /* Handle trailers by calling ap_get_mime_headers again! */ |
|
- ctx->state = BODY_NONE; |
|
- ap_get_mime_headers(f->r); |
|
- e = apr_bucket_eos_create(f->c->bucket_alloc); |
|
- APR_BRIGADE_INSERT_TAIL(b, e); |
|
- ctx->eos_sent = 1; |
|
- return APR_SUCCESS; |
|
+ return read_chunked_trailers(ctx, f, b, |
|
+ conf->merge_trailers == AP_MERGE_TRAILERS_ENABLE); |
|
} |
|
} |
|
} |
|
@@ -509,13 +551,8 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b, |
|
} |
|
|
|
if (!ctx->remaining) { |
|
- /* Handle trailers by calling ap_get_mime_headers again! */ |
|
- ctx->state = BODY_NONE; |
|
- ap_get_mime_headers(f->r); |
|
- e = apr_bucket_eos_create(f->c->bucket_alloc); |
|
- APR_BRIGADE_INSERT_TAIL(b, e); |
|
- ctx->eos_sent = 1; |
|
- return APR_SUCCESS; |
|
+ return read_chunked_trailers(ctx, f, b, |
|
+ conf->merge_trailers == AP_MERGE_TRAILERS_ENABLE); |
|
} |
|
} |
|
break; |
|
diff --git a/modules/http/http_request.c b/modules/http/http_request.c |
|
index 796d506..cdfec8b 100644 |
|
--- a/modules/http/http_request.c |
|
+++ b/modules/http/http_request.c |
|
@@ -463,6 +463,7 @@ static request_rec *internal_internal_redirect(const char *new_uri, |
|
new->main = r->main; |
|
|
|
new->headers_in = r->headers_in; |
|
+ new->trailers_in = r->trailers_in; |
|
new->headers_out = apr_table_make(r->pool, 12); |
|
if (ap_is_HTTP_REDIRECT(new->status)) { |
|
const char *location = apr_table_get(r->headers_out, "Location"); |
|
@@ -470,6 +471,7 @@ static request_rec *internal_internal_redirect(const char *new_uri, |
|
apr_table_setn(new->headers_out, "Location", location); |
|
} |
|
new->err_headers_out = r->err_headers_out; |
|
+ new->trailers_out = apr_table_make(r->pool, 5); |
|
new->subprocess_env = rename_original_env(r->pool, r->subprocess_env); |
|
new->notes = apr_table_make(r->pool, 5); |
|
|
|
@@ -583,6 +585,8 @@ AP_DECLARE(void) ap_internal_fast_redirect(request_rec *rr, request_rec *r) |
|
r->headers_out); |
|
r->err_headers_out = apr_table_overlay(r->pool, rr->err_headers_out, |
|
r->err_headers_out); |
|
+ r->trailers_out = apr_table_overlay(r->pool, rr->trailers_out, |
|
+ r->trailers_out); |
|
r->subprocess_env = apr_table_overlay(r->pool, rr->subprocess_env, |
|
r->subprocess_env); |
|
|
|
diff --git a/modules/loggers/mod_log_config.c b/modules/loggers/mod_log_config.c |
|
index 25f5030..b021dd3 100644 |
|
--- a/modules/loggers/mod_log_config.c |
|
+++ b/modules/loggers/mod_log_config.c |
|
@@ -431,6 +431,12 @@ static const char *log_header_in(request_rec *r, char *a) |
|
return ap_escape_logitem(r->pool, apr_table_get(r->headers_in, a)); |
|
} |
|
|
|
+static const char *log_trailer_in(request_rec *r, char *a) |
|
+{ |
|
+ return ap_escape_logitem(r->pool, apr_table_get(r->trailers_in, a)); |
|
+} |
|
+ |
|
+ |
|
static APR_INLINE char *find_multiple_headers(apr_pool_t *pool, |
|
const apr_table_t *table, |
|
const char *key) |
|
@@ -514,6 +520,11 @@ static const char *log_header_out(request_rec *r, char *a) |
|
return ap_escape_logitem(r->pool, cp); |
|
} |
|
|
|
+static const char *log_trailer_out(request_rec *r, char *a) |
|
+{ |
|
+ return ap_escape_logitem(r->pool, apr_table_get(r->trailers_out, a)); |
|
+} |
|
+ |
|
static const char *log_note(request_rec *r, char *a) |
|
{ |
|
return ap_escape_logitem(r->pool, apr_table_get(r->notes, a)); |
|
@@ -916,7 +927,7 @@ static char *parse_log_misc_string(apr_pool_t *p, log_format_item *it, |
|
static char *parse_log_item(apr_pool_t *p, log_format_item *it, const char **sa) |
|
{ |
|
const char *s = *sa; |
|
- ap_log_handler *handler; |
|
+ ap_log_handler *handler = NULL; |
|
|
|
if (*s != '%') { |
|
return parse_log_misc_string(p, it, sa); |
|
@@ -986,7 +997,16 @@ static char *parse_log_item(apr_pool_t *p, log_format_item *it, const char **sa) |
|
break; |
|
|
|
default: |
|
- handler = (ap_log_handler *)apr_hash_get(log_hash, s++, 1); |
|
+ /* check for '^' + two character format first */ |
|
+ if (*s == '^' && *(s+1) && *(s+2)) { |
|
+ handler = (ap_log_handler *)apr_hash_get(log_hash, s, 3); |
|
+ if (handler) { |
|
+ s += 3; |
|
+ } |
|
+ } |
|
+ if (!handler) { |
|
+ handler = (ap_log_handler *)apr_hash_get(log_hash, s++, 1); |
|
+ } |
|
if (!handler) { |
|
char dummy[2]; |
|
|
|
@@ -1516,7 +1536,7 @@ static void ap_register_log_handler(apr_pool_t *p, char *tag, |
|
log_struct->func = handler; |
|
log_struct->want_orig_default = def; |
|
|
|
- apr_hash_set(log_hash, tag, 1, (const void *)log_struct); |
|
+ apr_hash_set(log_hash, tag, strlen(tag), (const void *)log_struct); |
|
} |
|
static ap_log_writer_init* ap_log_set_writer_init(ap_log_writer_init *handle) |
|
{ |
|
@@ -1686,6 +1706,9 @@ static int log_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp) |
|
log_pfn_register(p, "U", log_request_uri, 1); |
|
log_pfn_register(p, "s", log_status, 1); |
|
log_pfn_register(p, "R", log_handler, 1); |
|
+ |
|
+ log_pfn_register(p, "^ti", log_trailer_in, 0); |
|
+ log_pfn_register(p, "^to", log_trailer_out, 0); |
|
} |
|
|
|
/* reset to default conditions */ |
|
diff --git a/modules/proxy/mod_proxy_http.c b/modules/proxy/mod_proxy_http.c |
|
index 7ae0fa4..05f33b4 100644 |
|
--- a/modules/proxy/mod_proxy_http.c |
|
+++ b/modules/proxy/mod_proxy_http.c |
|
@@ -994,8 +994,11 @@ static request_rec *make_fake_req(conn_rec *c, request_rec *r) |
|
rp->status = HTTP_OK; |
|
|
|
rp->headers_in = apr_table_make(pool, 50); |
|
+ rp->trailers_in = apr_table_make(pool, 5); |
|
+ |
|
rp->subprocess_env = apr_table_make(pool, 50); |
|
rp->headers_out = apr_table_make(pool, 12); |
|
+ rp->trailers_out = apr_table_make(pool, 5); |
|
rp->err_headers_out = apr_table_make(pool, 5); |
|
rp->notes = apr_table_make(pool, 5); |
|
|
|
@@ -1076,6 +1079,7 @@ static void ap_proxy_read_headers(request_rec *r, request_rec *rr, |
|
psc = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module); |
|
|
|
r->headers_out = apr_table_make(r->pool, 20); |
|
+ r->trailers_out = apr_table_make(r->pool, 5); |
|
*pread_len = 0; |
|
|
|
/* |
|
@@ -1206,6 +1210,14 @@ apr_status_t ap_proxygetline(apr_bucket_brigade *bb, char *s, int n, request_rec |
|
#define AP_MAX_INTERIM_RESPONSES 10 |
|
#endif |
|
|
|
+static int add_trailers(void *data, const char *key, const char *val) |
|
+{ |
|
+ if (val) { |
|
+ apr_table_add((apr_table_t*)data, key, val); |
|
+ } |
|
+ return 1; |
|
+} |
|
+ |
|
static |
|
apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r, |
|
proxy_conn_rec **backend_ptr, |
|
@@ -1717,6 +1729,12 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r, |
|
/* next time try a non-blocking read */ |
|
mode = APR_NONBLOCK_READ; |
|
|
|
+ if (!apr_is_empty_table(backend->r->trailers_in)) { |
|
+ apr_table_do(add_trailers, r->trailers_out, |
|
+ backend->r->trailers_in, NULL); |
|
+ apr_table_clear(backend->r->trailers_in); |
|
+ } |
|
+ |
|
apr_brigade_length(bb, 0, &readbytes); |
|
backend->worker->s->read += readbytes; |
|
#if DEBUGGING |
|
diff --git a/server/core.c b/server/core.c |
|
index 024bab6..7cfde63 100644 |
|
--- a/server/core.c |
|
+++ b/server/core.c |
|
@@ -523,6 +523,10 @@ static void *merge_core_server_configs(apr_pool_t *p, void *basev, void *virtv) |
|
if (virt->error_log_req) |
|
conf->error_log_req = virt->error_log_req; |
|
|
|
+ conf->merge_trailers = (virt->merge_trailers != AP_MERGE_TRAILERS_UNSET) |
|
+ ? virt->merge_trailers |
|
+ : base->merge_trailers; |
|
+ |
|
return conf; |
|
} |
|
|
|
@@ -3877,6 +3881,16 @@ AP_DECLARE(void) ap_register_errorlog_handler(apr_pool_t *p, char *tag, |
|
} |
|
|
|
|
|
+static const char *set_merge_trailers(cmd_parms *cmd, void *dummy, int arg) |
|
+{ |
|
+ core_server_config *conf = ap_get_module_config(cmd->server->module_config, |
|
+ &core_module); |
|
+ conf->merge_trailers = (arg ? AP_MERGE_TRAILERS_ENABLE : |
|
+ AP_MERGE_TRAILERS_DISABLE); |
|
+ |
|
+ return NULL; |
|
+} |
|
+ |
|
/* Note --- ErrorDocument will now work from .htaccess files. |
|
* The AllowOverride of Fileinfo allows webmasters to turn it off |
|
*/ |
|
@@ -4124,6 +4138,8 @@ AP_INIT_TAKE1("EnableExceptionHook", ap_mpm_set_exception_hook, NULL, RSRC_CONF, |
|
#endif |
|
AP_INIT_TAKE1("TraceEnable", set_trace_enable, NULL, RSRC_CONF, |
|
"'on' (default), 'off' or 'extended' to trace request body content"), |
|
+AP_INIT_FLAG("MergeTrailers", set_merge_trailers, NULL, RSRC_CONF, |
|
+ "merge request trailers into request headers or not"), |
|
{ NULL } |
|
}; |
|
|
|
@@ -4206,7 +4222,6 @@ static int core_map_to_storage(request_rec *r) |
|
|
|
static int do_nothing(request_rec *r) { return OK; } |
|
|
|
- |
|
static int core_override_type(request_rec *r) |
|
{ |
|
core_dir_config *conf = |
|
diff --git a/server/protocol.c b/server/protocol.c |
|
index 14329eb..46fc034 100644 |
|
--- a/server/protocol.c |
|
+++ b/server/protocol.c |
|
@@ -718,6 +718,8 @@ AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r, apr_bucket_brigade *bb |
|
r->status = HTTP_REQUEST_TIME_OUT; |
|
} |
|
else { |
|
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, |
|
+ "Failed to read request header line %s", field); |
|
r->status = HTTP_BAD_REQUEST; |
|
} |
|
|
|
@@ -917,9 +919,11 @@ request_rec *ap_read_request(conn_rec *conn) |
|
r->allowed_methods = ap_make_method_list(p, 2); |
|
|
|
r->headers_in = apr_table_make(r->pool, 25); |
|
+ r->trailers_in = apr_table_make(r->pool, 5); |
|
r->subprocess_env = apr_table_make(r->pool, 25); |
|
r->headers_out = apr_table_make(r->pool, 12); |
|
r->err_headers_out = apr_table_make(r->pool, 5); |
|
+ r->trailers_out = apr_table_make(r->pool, 5); |
|
r->notes = apr_table_make(r->pool, 5); |
|
|
|
r->request_config = ap_create_request_config(r->pool); |
|
@@ -1162,6 +1166,7 @@ AP_DECLARE(void) ap_set_sub_req_protocol(request_rec *rnew, |
|
rnew->status = HTTP_OK; |
|
|
|
rnew->headers_in = apr_table_copy(rnew->pool, r->headers_in); |
|
+ rnew->trailers_in = apr_table_copy(rnew->pool, r->trailers_in); |
|
|
|
/* did the original request have a body? (e.g. POST w/SSI tags) |
|
* if so, make sure the subrequest doesn't inherit body headers |
|
@@ -1173,6 +1178,7 @@ AP_DECLARE(void) ap_set_sub_req_protocol(request_rec *rnew, |
|
rnew->subprocess_env = apr_table_copy(rnew->pool, r->subprocess_env); |
|
rnew->headers_out = apr_table_make(rnew->pool, 5); |
|
rnew->err_headers_out = apr_table_make(rnew->pool, 5); |
|
+ rnew->trailers_out = apr_table_make(rnew->pool, 5); |
|
rnew->notes = apr_table_make(rnew->pool, 5); |
|
|
|
rnew->expecting_100 = r->expecting_100;
|
|
|