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