You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by ic...@apache.org on 2015/11/23 20:08:19 UTC

svn commit: r1715919 - in /httpd/httpd/trunk/modules/http2: h2_from_h1.c h2_from_h1.h h2_h2.c h2_io.c h2_io.h h2_mplx.c h2_mplx.h h2_push.c h2_response.c h2_response.h h2_session.c h2_stream.c h2_task.c h2_task_output.c h2_task_output.h

Author: icing
Date: Mon Nov 23 19:08:19 2015
New Revision: 1715919

URL: http://svn.apache.org/viewvc?rev=1715919&view=rev
Log:
sending trailers out, when seeing EOR bucket and r->trailers_out is non-empty

Modified:
    httpd/httpd/trunk/modules/http2/h2_from_h1.c
    httpd/httpd/trunk/modules/http2/h2_from_h1.h
    httpd/httpd/trunk/modules/http2/h2_h2.c
    httpd/httpd/trunk/modules/http2/h2_io.c
    httpd/httpd/trunk/modules/http2/h2_io.h
    httpd/httpd/trunk/modules/http2/h2_mplx.c
    httpd/httpd/trunk/modules/http2/h2_mplx.h
    httpd/httpd/trunk/modules/http2/h2_push.c
    httpd/httpd/trunk/modules/http2/h2_response.c
    httpd/httpd/trunk/modules/http2/h2_response.h
    httpd/httpd/trunk/modules/http2/h2_session.c
    httpd/httpd/trunk/modules/http2/h2_stream.c
    httpd/httpd/trunk/modules/http2/h2_task.c
    httpd/httpd/trunk/modules/http2/h2_task_output.c
    httpd/httpd/trunk/modules/http2/h2_task_output.h

Modified: httpd/httpd/trunk/modules/http2/h2_from_h1.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_from_h1.c?rev=1715919&r1=1715918&r2=1715919&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_from_h1.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_from_h1.c Mon Nov 23 19:08:19 2015
@@ -555,3 +555,38 @@ apr_status_t h2_response_output_filter(a
     }
     return ap_pass_brigade(f->next, bb);
 }
+
+apr_status_t h2_response_trailers_filter(ap_filter_t *f, apr_bucket_brigade *bb)
+{
+    h2_task *task = f->ctx;
+    h2_from_h1 *from_h1 = task->output? task->output->from_h1 : NULL;
+    request_rec *r = f->r;
+    apr_bucket *b;
+ 
+    if (from_h1 && from_h1->response) {
+        /* Detect the EOR bucket and forward any trailers that may have
+         * been set to our h2_response.
+         */
+        for (b = APR_BRIGADE_FIRST(bb);
+             b != APR_BRIGADE_SENTINEL(bb);
+             b = APR_BUCKET_NEXT(b))
+        {
+            if (AP_BUCKET_IS_EOR(b)) {
+                /* FIXME: need a better test case than this.
+                apr_table_setn(r->trailers_out, "X", "1"); */
+                if (r->trailers_out && !apr_is_empty_table(r->trailers_out)) {
+                    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c,
+                                  "h2_from_h1(%d): trailers filter, saving trailers",
+                                  from_h1->stream_id);
+                    h2_response_set_trailers(from_h1->response,
+                                             apr_table_clone(from_h1->pool, 
+                                                             r->trailers_out));
+                }
+                break;
+            }
+        }     
+    }
+     
+    return ap_pass_brigade(f->next, bb);
+}
+

Modified: httpd/httpd/trunk/modules/http2/h2_from_h1.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_from_h1.h?rev=1715919&r1=1715918&r2=1715919&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_from_h1.h (original)
+++ httpd/httpd/trunk/modules/http2/h2_from_h1.h Mon Nov 23 19:08:19 2015
@@ -69,4 +69,6 @@ struct h2_response *h2_from_h1_get_respo
 
 apr_status_t h2_response_output_filter(ap_filter_t *f, apr_bucket_brigade *bb);
 
+apr_status_t h2_response_trailers_filter(ap_filter_t *f, apr_bucket_brigade *bb);
+
 #endif /* defined(__mod_h2__h2_from_h1__) */

Modified: httpd/httpd/trunk/modules/http2/h2_h2.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_h2.c?rev=1715919&r1=1715918&r2=1715919&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_h2.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_h2.c Mon Nov 23 19:08:19 2015
@@ -667,16 +667,17 @@ static int h2_h2_post_read_req(request_r
         ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r,
                       "adding h1_to_h2_resp output filter");
         if (task->serialize_headers) {
-            ap_remove_output_filter_byhandle(r->output_filters, "H1_TO_H2_RESP");
+/*            ap_remove_output_filter_byhandle(r->output_filters, "H1_TO_H2_RESP");*/
             ap_add_output_filter("H1_TO_H2_RESP", task, r, r->connection);
         }
         else {
             /* replace the core http filter that formats response headers
              * in HTTP/1 with our own that collects status and headers */
             ap_remove_output_filter_byhandle(r->output_filters, "HTTP_HEADER");
-            ap_remove_output_filter_byhandle(r->output_filters, "H2_RESPONSE");
+/*            ap_remove_output_filter_byhandle(r->output_filters, "H2_RESPONSE");*/
             ap_add_output_filter("H2_RESPONSE", task, r, r->connection);
         }
+        ap_add_output_filter("H2_TRAILERS", task, r, r->connection);
     }
     return DECLINED;
 }

Modified: httpd/httpd/trunk/modules/http2/h2_io.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_io.c?rev=1715919&r1=1715918&r2=1715919&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_io.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_io.c Mon Nov 23 19:08:19 2015
@@ -205,8 +205,17 @@ apr_status_t h2_io_out_read_to(h2_io *io
     return h2_util_move(bb, io->bbout, *plen, NULL, "h2_io_read_to");
 }
 
+static void process_trailers(h2_io *io, apr_table_t *trailers)
+{
+    if (trailers && io->response) {
+        h2_response_set_trailers(io->response, 
+                                 apr_table_clone(io->pool, trailers));
+    }
+}
+
 apr_status_t h2_io_out_write(h2_io *io, apr_bucket_brigade *bb, 
-                             apr_size_t maxlen, int *pfile_handles_allowed)
+                             apr_size_t maxlen, apr_table_t *trailers,
+                             int *pfile_handles_allowed)
 {
     apr_status_t status;
     int start_allowed;
@@ -233,6 +242,7 @@ apr_status_t h2_io_out_write(h2_io *io,
         return status;
     }
 
+    process_trailers(io, trailers);
     if (!io->bbout) {
         io->bbout = apr_brigade_create(io->pool, io->bucket_alloc);
     }
@@ -258,17 +268,20 @@ apr_status_t h2_io_out_write(h2_io *io,
 }
 
 
-apr_status_t h2_io_out_close(h2_io *io)
+apr_status_t h2_io_out_close(h2_io *io, apr_table_t *trailers)
 {
     if (io->rst_error) {
         return APR_ECONNABORTED;
     }
-    if (!io->bbout) {
-        io->bbout = apr_brigade_create(io->pool, io->bucket_alloc);
-    }
-    if (!io->eos_out && !h2_util_has_eos(io->bbout, -1)) {
-        APR_BRIGADE_INSERT_TAIL(io->bbout, 
-                                apr_bucket_eos_create(io->bbout->bucket_alloc));
+    if (!io->eos_out) { /* EOS has not been read yet */
+        process_trailers(io, trailers);
+        if (!io->bbout) {
+            io->bbout = apr_brigade_create(io->pool, io->bucket_alloc);
+        }
+        if (!h2_util_has_eos(io->bbout, -1)) {
+            APR_BRIGADE_INSERT_TAIL(io->bbout, 
+                                    apr_bucket_eos_create(io->bbout->bucket_alloc));
+        }
     }
     return APR_SUCCESS;
 }

Modified: httpd/httpd/trunk/modules/http2/h2_io.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_io.h?rev=1715919&r1=1715918&r2=1715919&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_io.h (original)
+++ httpd/httpd/trunk/modules/http2/h2_io.h Mon Nov 23 19:08:19 2015
@@ -130,13 +130,14 @@ apr_status_t h2_io_out_read_to(h2_io *io
                                apr_off_t *plen, int *peos);
 
 apr_status_t h2_io_out_write(h2_io *io, apr_bucket_brigade *bb, 
-                             apr_size_t maxlen, int *pfile_buckets_allowed);
+                             apr_size_t maxlen, apr_table_t *trailers,
+                             int *pfile_buckets_allowed);
 
 /**
  * Closes the input. After existing data has been read, APR_EOF will
  * be returned.
  */
-apr_status_t h2_io_out_close(h2_io *io);
+apr_status_t h2_io_out_close(h2_io *io, apr_table_t *trailers);
 
 /**
  * Gives the overall length of the data that is currently queued for

Modified: httpd/httpd/trunk/modules/http2/h2_mplx.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_mplx.c?rev=1715919&r1=1715918&r2=1715919&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_mplx.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_mplx.c Mon Nov 23 19:08:19 2015
@@ -450,7 +450,8 @@ apr_status_t h2_mplx_in_update_windows(h
 
 apr_status_t h2_mplx_out_readx(h2_mplx *m, int stream_id, 
                                h2_io_data_cb *cb, void *ctx, 
-                               apr_off_t *plen, int *peos)
+                               apr_off_t *plen, int *peos,
+                               apr_table_t **ptrailers)
 {
     apr_status_t status;
     AP_DEBUG_ASSERT(m);
@@ -464,7 +465,6 @@ apr_status_t h2_mplx_out_readx(h2_mplx *
             H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_readx_pre");
             
             status = h2_io_out_readx(io, cb, ctx, plen, peos);
-            
             H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_readx_post");
             if (status == APR_SUCCESS && cb && io->output_drained) {
                 apr_thread_cond_signal(io->output_drained);
@@ -473,6 +473,8 @@ apr_status_t h2_mplx_out_readx(h2_mplx *
         else {
             status = APR_ECONNABORTED;
         }
+        
+        *ptrailers = (*peos && io->response)? io->response->trailers : NULL;
         apr_thread_mutex_unlock(m->lock);
     }
     return status;
@@ -480,7 +482,8 @@ apr_status_t h2_mplx_out_readx(h2_mplx *
 
 apr_status_t h2_mplx_out_read_to(h2_mplx *m, int stream_id, 
                                  apr_bucket_brigade *bb, 
-                                 apr_off_t *plen, int *peos)
+                                 apr_off_t *plen, int *peos,
+                                 apr_table_t **ptrailers)
 {
     apr_status_t status;
     AP_DEBUG_ASSERT(m);
@@ -503,6 +506,7 @@ apr_status_t h2_mplx_out_read_to(h2_mplx
         else {
             status = APR_ECONNABORTED;
         }
+        *ptrailers = (*peos && io->response)? io->response->trailers : NULL;
         apr_thread_mutex_unlock(m->lock);
     }
     return status;
@@ -563,6 +567,7 @@ h2_stream *h2_mplx_next_submit(h2_mplx *
 
 static apr_status_t out_write(h2_mplx *m, h2_io *io, 
                               ap_filter_t* f, apr_bucket_brigade *bb,
+                              apr_table_t *trailers,
                               struct apr_thread_cond_t *iowait)
 {
     apr_status_t status = APR_SUCCESS;
@@ -575,7 +580,7 @@ static apr_status_t out_write(h2_mplx *m
            && (status == APR_SUCCESS)
            && !is_aborted(m, &status)) {
         
-        status = h2_io_out_write(io, bb, m->stream_max_mem, 
+        status = h2_io_out_write(io, bb, m->stream_max_mem, trailers,
                                  &m->file_handles_allowed);
         /* Wait for data to drain until there is room again */
         while (!APR_BRIGADE_EMPTY(bb) 
@@ -583,6 +588,7 @@ static apr_status_t out_write(h2_mplx *m
                && status == APR_SUCCESS
                && (m->stream_max_mem <= h2_io_out_length(io))
                && !is_aborted(m, &status)) {
+            trailers = NULL;
             io->output_drained = iowait;
             if (f) {
                 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,
@@ -616,7 +622,7 @@ static apr_status_t out_open(h2_mplx *m,
         h2_io_set_response(io, response);
         h2_io_set_add(m->ready_ios, io);
         if (bb) {
-            status = out_write(m, io, f, bb, iowait);
+            status = out_write(m, io, f, bb, response->trailers, iowait);
         }
         have_out_data_for(m, stream_id);
     }
@@ -652,6 +658,7 @@ apr_status_t h2_mplx_out_open(h2_mplx *m
 
 apr_status_t h2_mplx_out_write(h2_mplx *m, int stream_id, 
                                ap_filter_t* f, apr_bucket_brigade *bb,
+                               apr_table_t *trailers,
                                struct apr_thread_cond_t *iowait)
 {
     apr_status_t status;
@@ -664,7 +671,10 @@ apr_status_t h2_mplx_out_write(h2_mplx *
         if (!m->aborted) {
             h2_io *io = h2_io_set_get(m->stream_ios, stream_id);
             if (io && !io->orphaned) {
-                status = out_write(m, io, f, bb, iowait);
+                status = out_write(m, io, f, bb, trailers, iowait);
+                ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, m->c,
+                              "h2_mplx(%ld-%d): write with trailers=%s", 
+                              m->id, io->id, trailers? "yes" : "no");
                 H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_write");
                 
                 have_out_data_for(m, stream_id);
@@ -684,7 +694,7 @@ apr_status_t h2_mplx_out_write(h2_mplx *
     return status;
 }
 
-apr_status_t h2_mplx_out_close(h2_mplx *m, int stream_id)
+apr_status_t h2_mplx_out_close(h2_mplx *m, int stream_id, apr_table_t *trailers)
 {
     apr_status_t status;
     AP_DEBUG_ASSERT(m);
@@ -708,7 +718,10 @@ apr_status_t h2_mplx_out_close(h2_mplx *
                                   "h2_mplx(%ld-%d): close, no response, no rst", 
                                   m->id, io->id);
                 }
-                status = h2_io_out_close(io);
+                ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, m->c,
+                              "h2_mplx(%ld-%d): close with trailers=%s", 
+                              m->id, io->id, trailers? "yes" : "no");
+                status = h2_io_out_close(io, trailers);
                 H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_close");
                 
                 have_out_data_for(m, stream_id);

Modified: httpd/httpd/trunk/modules/http2/h2_mplx.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_mplx.h?rev=1715919&r1=1715918&r2=1715919&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_mplx.h (original)
+++ httpd/httpd/trunk/modules/http2/h2_mplx.h Mon Nov 23 19:08:19 2015
@@ -240,7 +240,8 @@ struct h2_stream *h2_mplx_next_submit(h2
  */
 apr_status_t h2_mplx_out_readx(h2_mplx *mplx, int stream_id, 
                                h2_io_data_cb *cb, void *ctx, 
-                               apr_off_t *plen, int *peos);
+                               apr_off_t *plen, int *peos,
+                               apr_table_t **ptrailers);
 
 /**
  * Reads output data into the given brigade. Will never block, but
@@ -248,7 +249,8 @@ apr_status_t h2_mplx_out_readx(h2_mplx *
  */
 apr_status_t h2_mplx_out_read_to(h2_mplx *mplx, int stream_id, 
                                  apr_bucket_brigade *bb, 
-                                 apr_off_t *plen, int *peos);
+                                 apr_off_t *plen, int *peos,
+                                 apr_table_t **ptrailers);
 
 /**
  * Opens the output for the given stream with the specified response.
@@ -264,17 +266,19 @@ apr_status_t h2_mplx_out_open(h2_mplx *m
  * @param stream_id the stream identifier
  * @param filter the apache filter context of the data
  * @param bb the bucket brigade to append
+ * @param trailers optional trailers for response, maybe NULL
  * @param iowait a conditional used for block/signalling in h2_mplx
  */
 apr_status_t h2_mplx_out_write(h2_mplx *mplx, int stream_id, 
                                ap_filter_t* filter, apr_bucket_brigade *bb,
+                               apr_table_t *trailers,
                                struct apr_thread_cond_t *iowait);
 
 /**
- * Closes the output stream. Readers of this stream will get all pending 
- * data and then only APR_EOF as result. 
+ * Closes the output for stream stream_id. Optionally forwards trailers
+ * fromt the processed stream.  
  */
-apr_status_t h2_mplx_out_close(h2_mplx *m, int stream_id);
+apr_status_t h2_mplx_out_close(h2_mplx *m, int stream_id, apr_table_t *trailers);
 
 apr_status_t h2_mplx_out_rst(h2_mplx *m, int stream_id, int error);
 

Modified: httpd/httpd/trunk/modules/http2/h2_push.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_push.c?rev=1715919&r1=1715918&r2=1715919&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_push.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_push.c Mon Nov 23 19:08:19 2015
@@ -386,14 +386,14 @@ apr_array_header_t *h2_push_collect(apr_
      * TODO: This may be extended in the future by hooks or callbacks
      * where other modules can provide push information directly.
      */
-    if (res->header) {
+    if (res->headers) {
         link_ctx ctx;
         
         memset(&ctx, 0, sizeof(ctx));
         ctx.req = req;
         ctx.pool = p;
     
-        apr_table_do(head_iter, &ctx, res->header, NULL);
+        apr_table_do(head_iter, &ctx, res->headers, NULL);
         return ctx.pushes;
     }
     return NULL;

Modified: httpd/httpd/trunk/modules/http2/h2_response.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_response.c?rev=1715919&r1=1715918&r2=1715919&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_response.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_response.c Mon Nov 23 19:08:19 2015
@@ -36,7 +36,7 @@ h2_response *h2_response_create(int stre
                                 apr_array_header_t *hlines,
                                 apr_pool_t *pool)
 {
-    apr_table_t *header;
+    apr_table_t *headers;
     h2_response *response = apr_pcalloc(pool, sizeof(h2_response));
     int i;
     if (response == NULL) {
@@ -49,7 +49,7 @@ h2_response *h2_response_create(int stre
     response->content_length = -1;
     
     if (hlines) {
-        header = apr_table_make(pool, hlines->nelts);        
+        headers = apr_table_make(pool, hlines->nelts);        
         for (i = 0; i < hlines->nelts; ++i) {
             char *hline = ((char **)hlines->elts)[i];
             char *sep = ap_strchr(hline, ':');
@@ -66,7 +66,7 @@ h2_response *h2_response_create(int stre
             }
             
             if (!h2_util_ignore_header(hline)) {
-                apr_table_merge(header, hline, sep);
+                apr_table_merge(headers, hline, sep);
                 if (*sep && H2_HD_MATCH_LIT_CS("content-length", hline)) {
                     char *end;
                     response->content_length = apr_strtoi64(sep, &end, 10);
@@ -83,10 +83,10 @@ h2_response *h2_response_create(int stre
         }
     }
     else {
-        header = apr_table_make(pool, 0);        
+        headers = apr_table_make(pool, 0);        
     }
 
-    response->header = header;
+    response->headers = headers;
     return response;
 }
 
@@ -101,7 +101,7 @@ h2_response *h2_response_rcreate(int str
     response->stream_id = stream_id;
     response->http_status = r->status;
     response->content_length = -1;
-    response->header = header;
+    response->headers = header;
 
     if (response->http_status == HTTP_FORBIDDEN) {
         const char *cause = apr_table_get(r->notes, "ssl-renegotiate-forbidden");
@@ -130,10 +130,17 @@ h2_response *h2_response_copy(apr_pool_t
     to->stream_id = from->stream_id;
     to->http_status = from->http_status;
     to->content_length = from->content_length;
-    if (from->header) {
-        to->header = apr_table_clone(pool, from->header);
+    if (from->headers) {
+        to->headers = apr_table_clone(pool, from->headers);
+    }
+    if (from->trailers) {
+        to->trailers = apr_table_clone(pool, from->trailers);
     }
     return to;
 }
 
+void h2_response_set_trailers(h2_response *response, apr_table_t *trailers)
+{
+    response->trailers = trailers;
+}
 

Modified: httpd/httpd/trunk/modules/http2/h2_response.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_response.h?rev=1715919&r1=1715918&r2=1715919&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_response.h (original)
+++ httpd/httpd/trunk/modules/http2/h2_response.h Mon Nov 23 19:08:19 2015
@@ -23,8 +23,8 @@ typedef struct h2_response {
     int rst_error;
     int http_status;
     apr_off_t content_length;
-    apr_table_t *header;
-    apr_table_t *trailer;
+    apr_table_t *headers;
+    apr_table_t *trailers;
 } h2_response;
 
 h2_response *h2_response_create(int stream_id,
@@ -40,4 +40,13 @@ void h2_response_destroy(h2_response *re
 
 h2_response *h2_response_copy(apr_pool_t *pool, h2_response *from);
 
+/**
+ * Set the trailers in the reponse. Will replace any existing trailers. Will
+ * *not* clone the table.
+ *
+ * @param response the repsone to set the trailers for
+ * @param trailers the trailers to set
+ */
+void h2_response_set_trailers(h2_response *response, apr_table_t *trailers);
+
 #endif /* defined(__mod_h2__h2_response__) */

Modified: httpd/httpd/trunk/modules/http2/h2_session.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_session.c?rev=1715919&r1=1715918&r2=1715919&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_session.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_session.c Mon Nov 23 19:08:19 2015
@@ -1124,10 +1124,14 @@ static ssize_t stream_data_cb(nghttp2_se
             int rv;
             
             nh = h2_util_ngheader_make(stream->pool, trailers);
+            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
+                          "h2_stream(%ld-%d): submit %d trailers",
+                          session->id, (int)stream_id,(int) nh->nvlen);
             rv = nghttp2_submit_trailer(ng2s, stream->id, nh->nv, nh->nvlen);
             if (rv < 0) {
                 nread = rv;
             }
+            *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM;
         }
         
         *data_flags |= NGHTTP2_DATA_FLAG_EOF;
@@ -1158,7 +1162,7 @@ static apr_status_t submit_response(h2_s
     if (stream->submitted) {
         rv = NGHTTP2_PROTOCOL_ERROR;
     }
-    else if (stream->response && stream->response->header) {
+    else if (stream->response && stream->response->headers) {
         nghttp2_data_provider provider;
         h2_response *response = stream->response;
         h2_ngheader *ngh;
@@ -1172,7 +1176,7 @@ static apr_status_t submit_response(h2_s
                       session->id, stream->id, response->http_status);
         
         ngh = h2_util_ngheader_make_res(stream->pool, response->http_status, 
-                                        response->header);
+                                        response->headers);
         rv = nghttp2_submit_response(session->ngh2, response->stream_id,
                                      ngh->nv, ngh->nvlen, &provider);
         

Modified: httpd/httpd/trunk/modules/http2/h2_stream.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_stream.c?rev=1715919&r1=1715918&r2=1715919&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_stream.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_stream.c Mon Nov 23 19:08:19 2015
@@ -223,7 +223,7 @@ apr_status_t h2_stream_set_response(h2_s
          * as we want, since the lifetimes are the same and we are not freeing
          * the ones in h2_mplx->io before this stream is done. */
         H2_STREAM_OUT(APLOG_TRACE2, stream, "h2_stream set_response_pre");
-        status = h2_util_move(stream->bbout, bb, -1, &move_all,  
+        status = h2_util_move(stream->bbout, bb, 16 * 1024, &move_all,  
                               "h2_stream_set_response");
         H2_STREAM_OUT(APLOG_TRACE2, stream, "h2_stream set_response_post");
     }
@@ -476,6 +476,7 @@ apr_status_t h2_stream_prep_read(h2_stre
 {
     apr_status_t status = APR_SUCCESS;
     const char *src;
+    apr_table_t *trailers = NULL;
     int test_read = (*plen == 0);
     
     if (stream->rst_error) {
@@ -490,19 +491,26 @@ apr_status_t h2_stream_prep_read(h2_stre
             apr_brigade_cleanup(stream->bbout);
             return h2_stream_prep_read(stream, plen, peos);
         }
+        trailers = stream->response? stream->response->trailers : NULL;
     }
     else {
         src = "mplx";
         status = h2_mplx_out_readx(stream->session->mplx, stream->id, 
-                                   NULL, NULL, plen, peos);
+                                   NULL, NULL, plen, peos, &trailers);
+        if (trailers && stream->response) {
+            h2_response_set_trailers(stream->response, trailers);
+        }    
     }
+    
     if (!test_read && status == APR_SUCCESS && !*peos && !*plen) {
         status = APR_EAGAIN;
     }
+    
     H2_STREAM_OUT(APLOG_TRACE2, stream, "h2_stream prep_read_post");
     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, stream->session->c,
-                  "h2_stream(%ld-%d): prep_read %s, len=%ld eos=%d",
-                  stream->session->id, stream->id, src, (long)*plen, *peos);
+                  "h2_stream(%ld-%d): prep_read %s, len=%ld eos=%d, trailers=%s",
+                  stream->session->id, stream->id, src, (long)*plen, *peos,
+                  trailers? "yes" : "no");
     return status;
 }
 
@@ -511,6 +519,7 @@ apr_status_t h2_stream_readx(h2_stream *
                              apr_off_t *plen, int *peos)
 {
     apr_status_t status = APR_SUCCESS;
+    apr_table_t *trailers = NULL;
     const char *src;
     
     H2_STREAM_OUT(APLOG_TRACE2, stream, "h2_stream readx_pre");
@@ -532,14 +541,21 @@ apr_status_t h2_stream_readx(h2_stream *
     else {
         src = "mplx";
         status = h2_mplx_out_readx(stream->session->mplx, stream->id, 
-                                   cb, ctx, plen, peos);
+                                   cb, ctx, plen, peos, &trailers);
+    }
+    
+    if (trailers && stream->response) {
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, stream->session->c,
+                      "h2_stream(%ld-%d): readx, saving trailers",
+                      stream->session->id, stream->id);
+        h2_response_set_trailers(stream->response, trailers);
     }
     
     if (status == APR_SUCCESS && !*peos && !*plen) {
         status = APR_EAGAIN;
     }
     
-    H2_STREAM_OUT(APLOG_TRACE2, stream, "h2_stream prep_readx_post");
+    H2_STREAM_OUT(APLOG_TRACE2, stream, "h2_stream readx_post");
     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, stream->session->c,
                   "h2_stream(%ld-%d): readx %s, len=%ld eos=%d",
                   stream->session->id, stream->id, src, (long)*plen, *peos);
@@ -552,6 +568,7 @@ apr_status_t h2_stream_read_to(h2_stream
                                apr_off_t *plen, int *peos)
 {
     apr_status_t status = APR_SUCCESS;
+    apr_table_t *trailers = NULL;
 
     H2_STREAM_OUT(APLOG_TRACE2, stream, "h2_stream read_to_pre");
     if (stream->rst_error) {
@@ -562,7 +579,7 @@ apr_status_t h2_stream_read_to(h2_stream
         apr_off_t tlen = *plen;
         int eos;
         status = h2_mplx_out_read_to(stream->session->mplx, stream->id, 
-                                     stream->bbout, &tlen, &eos);
+                                     stream->bbout, &tlen, &eos, &trailers);
     }
     
     if (status == APR_SUCCESS && !APR_BRIGADE_EMPTY(stream->bbout)) {
@@ -574,6 +591,13 @@ apr_status_t h2_stream_read_to(h2_stream
         *peos = 0;
     }
 
+    if (trailers && stream->response) {
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, stream->session->c,
+                      "h2_stream(%ld-%d): read_to, saving trailers",
+                      stream->session->id, stream->id);
+        h2_response_set_trailers(stream->response, trailers);
+    }
+    
     if (status == APR_SUCCESS && !*peos && !*plen) {
         status = APR_EAGAIN;
     }
@@ -642,6 +666,5 @@ apr_status_t h2_stream_submit_pushes(h2_
 
 apr_table_t *h2_stream_get_trailers(h2_stream *stream)
 {
-    /* TODO */
-    return NULL;
+    return stream->response? stream->response->trailers : NULL;
 }

Modified: httpd/httpd/trunk/modules/http2/h2_task.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_task.c?rev=1715919&r1=1715918&r2=1715919&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_task.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_task.c Mon Nov 23 19:08:19 2015
@@ -113,6 +113,8 @@ void h2_task_register_hooks(void)
                               NULL, AP_FTYPE_NETWORK);
     ap_register_output_filter("H1_TO_H2_RESP", h2_filter_read_response,
                               NULL, AP_FTYPE_PROTOCOL);
+    ap_register_output_filter("H2_TRAILERS", h2_response_trailers_filter,
+                              NULL, AP_FTYPE_PROTOCOL);
 }
 
 static int h2_task_pre_conn(conn_rec* c, void *arg)
@@ -161,7 +163,7 @@ h2_task *h2_task_create(long session_id,
         ap_log_perror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, pool,
                       APLOGNO(02941) "h2_task(%ld-%d): create stream task", 
                       session_id, req->id);
-        h2_mplx_out_close(mplx, req->id);
+        h2_mplx_out_close(mplx, req->id, NULL);
         return NULL;
     }
     

Modified: httpd/httpd/trunk/modules/http2/h2_task_output.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_task_output.c?rev=1715919&r1=1715918&r2=1715919&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_task_output.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_task_output.c Mon Nov 23 19:08:19 2015
@@ -83,17 +83,31 @@ static apr_status_t open_if_needed(h2_ta
             return APR_ECONNABORTED;
         }
         
+        output->trailers_passed = !!response->trailers;
         return h2_mplx_out_open(output->task->mplx, output->task->stream_id, 
                                 response, f, bb, output->task->io);
     }
     return APR_EOF;
 }
 
+static apr_table_t *get_trailers(h2_task_output *output)
+{
+    if (!output->trailers_passed) {
+        h2_response *response = h2_from_h1_get_response(output->from_h1);
+        if (response->trailers) {
+            output->trailers_passed = 1;
+            return response->trailers;
+        }
+    }
+    return NULL;
+}
+
 void h2_task_output_close(h2_task_output *output)
 {
     open_if_needed(output, NULL, NULL);
     if (output->state != H2_TASK_OUT_DONE) {
-        h2_mplx_out_close(output->task->mplx, output->task->stream_id);
+        h2_mplx_out_close(output->task->mplx, output->task->stream_id, 
+                          get_trailers(output));
         output->state = H2_TASK_OUT_DONE;
     }
 }
@@ -111,6 +125,7 @@ apr_status_t h2_task_output_write(h2_tas
                                   ap_filter_t* f, apr_bucket_brigade* bb)
 {
     apr_status_t status;
+    
     if (APR_BRIGADE_EMPTY(bb)) {
         ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c,
                       "h2_task_output(%s): empty write", output->task->id);
@@ -124,9 +139,10 @@ apr_status_t h2_task_output_write(h2_tas
                       output->task->id);
         return status;
     }
+    
     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c,
                   "h2_task_output(%s): write brigade", output->task->id);
     return h2_mplx_out_write(output->task->mplx, output->task->stream_id, 
-                             f, bb, output->task->io);
+                             f, bb, get_trailers(output), output->task->io);
 }
 

Modified: httpd/httpd/trunk/modules/http2/h2_task_output.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_task_output.h?rev=1715919&r1=1715918&r2=1715919&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_task_output.h (original)
+++ httpd/httpd/trunk/modules/http2/h2_task_output.h Mon Nov 23 19:08:19 2015
@@ -38,6 +38,7 @@ struct h2_task_output {
     struct h2_task *task;
     h2_task_output_state_t state;
     struct h2_from_h1 *from_h1;
+    int trailers_passed;
 };
 
 h2_task_output *h2_task_output_create(struct h2_task *task, apr_pool_t *pool);