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/19 18:14:04 UTC

svn commit: r1715218 [2/4] - in /httpd/httpd/branches/2.4-http2-alpha: ./ docs/manual/mod/ modules/aaa/ modules/http2/ modules/ssl/ server/

Modified: httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_io.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_io.c?rev=1715218&r1=1715217&r2=1715218&view=diff
==============================================================================
--- httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_io.c (original)
+++ httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_io.c Thu Nov 19 17:14:03 2015
@@ -32,27 +32,24 @@ h2_io *h2_io_create(int id, apr_pool_t *
     if (io) {
         io->id = id;
         io->pool = pool;
+        io->bucket_alloc = bucket_alloc;
         io->bbin = NULL;
-        io->bbout = apr_brigade_create(pool, bucket_alloc);
+        io->bbout = NULL;
     }
     return io;
 }
 
-static void h2_io_cleanup(h2_io *io)
-{
-    if (io->task) {
-        h2_task_destroy(io->task);
-        io->task = NULL;
-    }
-}
-
 void h2_io_destroy(h2_io *io)
 {
-    h2_io_cleanup(io);
+    if (io->pool) {
+        apr_pool_destroy(io->pool);
+        /* gone */
+    }
 }
 
 void h2_io_set_response(h2_io *io, h2_response *response) 
 {
+    AP_DEBUG_ASSERT(io->pool);
     AP_DEBUG_ASSERT(response);
     AP_DEBUG_ASSERT(!io->response);
     io->response = h2_response_copy(io->pool, response);
@@ -70,15 +67,15 @@ void h2_io_rst(h2_io *io, int error)
 
 int h2_io_in_has_eos_for(h2_io *io)
 {
-    return io->eos_in || (io->bbin && h2_util_has_eos(io->bbin, 0));
+    return io->eos_in || (io->bbin && h2_util_has_eos(io->bbin, -1));
 }
 
 int h2_io_out_has_data(h2_io *io)
 {
-    return h2_util_bb_has_data_or_eos(io->bbout);
+    return io->bbout && h2_util_bb_has_data_or_eos(io->bbout);
 }
 
-apr_size_t h2_io_out_length(h2_io *io)
+apr_off_t h2_io_out_length(h2_io *io)
 {
     if (io->bbout) {
         apr_off_t len = 0;
@@ -127,13 +124,12 @@ apr_status_t h2_io_in_write(h2_io *io, a
     if (io->eos_in) {
         return APR_EOF;
     }
-    io->eos_in = h2_util_has_eos(bb, 0);
+    io->eos_in = h2_util_has_eos(bb, -1);
     if (!APR_BRIGADE_EMPTY(bb)) {
         if (!io->bbin) {
-            io->bbin = apr_brigade_create(io->bbout->p, 
-                                          io->bbout->bucket_alloc);
+            io->bbin = apr_brigade_create(io->pool, io->bucket_alloc);
         }
-        return h2_util_move(io->bbin, bb, 0, NULL, "h2_io_in_write");
+        return h2_util_move(io->bbin, bb, -1, NULL, "h2_io_in_write");
     }
     return APR_SUCCESS;
 }
@@ -154,7 +150,7 @@ apr_status_t h2_io_in_close(h2_io *io)
 
 apr_status_t h2_io_out_readx(h2_io *io,  
                              h2_io_data_cb *cb, void *ctx, 
-                             apr_size_t *plen, int *peos)
+                             apr_off_t *plen, int *peos)
 {
     apr_status_t status;
     
@@ -167,6 +163,11 @@ apr_status_t h2_io_out_readx(h2_io *io,
         *peos = 1;
         return APR_SUCCESS;
     }
+    else if (!io->bbout) {
+        *plen = 0;
+        *peos = 0;
+        return APR_EAGAIN;
+    }
     
     if (cb == NULL) {
         /* just checking length available */
@@ -183,7 +184,7 @@ apr_status_t h2_io_out_readx(h2_io *io,
 }
 
 apr_status_t h2_io_out_read_to(h2_io *io, apr_bucket_brigade *bb, 
-                               apr_size_t *plen, int *peos)
+                               apr_off_t *plen, int *peos)
 {
     if (io->rst_error) {
         return APR_ECONNABORTED;
@@ -194,7 +195,11 @@ apr_status_t h2_io_out_read_to(h2_io *io
         *peos = 1;
         return APR_SUCCESS;
     }
-    
+    else if (!io->bbout) {
+        *plen = 0;
+        *peos = 0;
+        return APR_EAGAIN;
+    }
 
     io->eos_out = *peos = h2_util_has_eos(io->bbout, *plen);
     return h2_util_move(bb, io->bbout, *plen, NULL, "h2_io_read_to");
@@ -227,6 +232,10 @@ apr_status_t h2_io_out_write(h2_io *io,
         }
         return status;
     }
+
+    if (!io->bbout) {
+        io->bbout = apr_brigade_create(io->pool, io->bucket_alloc);
+    }
     
     /* Let's move the buckets from the request processing in here, so
      * that the main thread can read them when it has time/capacity.
@@ -239,7 +248,6 @@ apr_status_t h2_io_out_write(h2_io *io,
      * file handles.
      */
     start_allowed = *pfile_handles_allowed;
-
     status = h2_util_move(io->bbout, bb, maxlen, pfile_handles_allowed, 
                           "h2_io_out_write");
     /* track # file buckets moved into our pool */
@@ -255,7 +263,10 @@ apr_status_t h2_io_out_close(h2_io *io)
     if (io->rst_error) {
         return APR_ECONNABORTED;
     }
-    if (!io->eos_out && !h2_util_has_eos(io->bbout, 0)) {
+    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));
     }

Modified: httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_io.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_io.h?rev=1715218&r1=1715217&r2=1715218&view=diff
==============================================================================
--- httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_io.h (original)
+++ httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_io.h Thu Nov 19 17:14:03 2015
@@ -18,10 +18,10 @@
 
 struct h2_response;
 struct apr_thread_cond_t;
-struct h2_task;
+struct h2_request;
 
 
-typedef apr_status_t h2_io_data_cb(void *ctx, const char *data, apr_size_t len);
+typedef apr_status_t h2_io_data_cb(void *ctx, const char *data, apr_off_t len);
 
 typedef int h2_stream_pri_cmp(int stream_id1, int stream_id2, void *ctx);
 
@@ -31,22 +31,24 @@ typedef struct h2_io h2_io;
 struct h2_io {
     int id;                      /* stream identifier */
     apr_pool_t *pool;            /* stream pool */
-    apr_bucket_brigade *bbin;    /* input data for stream */
-    int eos_in;
+    int orphaned;                /* h2_stream is gone for this io */
+    
     int task_done;
+    const struct h2_request *request;  /* request on this io */
+    int request_body;            /* == 0 iff request has no body */
+    struct h2_response *response;/* response for submit, once created */
     int rst_error;
-    int zombie;
-    
-    struct h2_task *task;       /* task created for this io */
 
-    apr_size_t input_consumed;   /* how many bytes have been read */
+    int eos_in;
+    apr_bucket_brigade *bbin;    /* input data for stream */
     struct apr_thread_cond_t *input_arrived; /* block on reading */
+    apr_size_t input_consumed;   /* how many bytes have been read */
     
-    apr_bucket_brigade *bbout;   /* output data from stream */
     int eos_out;
+    apr_bucket_brigade *bbout;   /* output data from stream */
+    apr_bucket_alloc_t *bucket_alloc;
     struct apr_thread_cond_t *output_drained; /* block on writing */
     
-    struct h2_response *response;/* submittable response created */
     int files_handles_owned;
 };
 
@@ -121,11 +123,11 @@ apr_status_t h2_io_in_close(h2_io *io);
  */
 apr_status_t h2_io_out_readx(h2_io *io,  
                              h2_io_data_cb *cb, void *ctx, 
-                             apr_size_t *plen, int *peos);
+                             apr_off_t *plen, int *peos);
 
 apr_status_t h2_io_out_read_to(h2_io *io, 
                                apr_bucket_brigade *bb, 
-                               apr_size_t *plen, int *peos);
+                               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);
@@ -140,7 +142,7 @@ apr_status_t h2_io_out_close(h2_io *io);
  * Gives the overall length of the data that is currently queued for
  * output.
  */
-apr_size_t h2_io_out_length(h2_io *io);
+apr_off_t h2_io_out_length(h2_io *io);
 
 
 #endif /* defined(__mod_h2__h2_io__) */

Modified: httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_mplx.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_mplx.c?rev=1715218&r1=1715217&r2=1715218&view=diff
==============================================================================
--- httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_mplx.c (original)
+++ httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_mplx.c Thu Nov 19 17:14:03 2015
@@ -41,10 +41,24 @@
 #include "h2_task_input.h"
 #include "h2_task_output.h"
 #include "h2_task_queue.h"
+#include "h2_worker.h"
 #include "h2_workers.h"
 #include "h2_util.h"
 
 
+#define H2_MPLX_IO_OUT(lvl,m,io,msg) \
+    do { \
+        if (APLOG_C_IS_LEVEL((m)->c,lvl)) \
+        h2_util_bb_log((m)->c,(io)->id,lvl,msg,(io)->bbout); \
+    } while(0)
+    
+#define H2_MPLX_IO_IN(lvl,m,io,msg) \
+    do { \
+        if (APLOG_C_IS_LEVEL((m)->c,lvl)) \
+        h2_util_bb_log((m)->c,(io)->id,lvl,msg,(io)->bbin); \
+    } while(0)
+
+
 static int is_aborted(h2_mplx *m, apr_status_t *pstatus) {
     AP_DEBUG_ASSERT(m);
     if (m->aborted) {
@@ -74,6 +88,10 @@ static void h2_mplx_destroy(h2_mplx *m)
         m->lock = NULL;
     }
     
+    if (m->spare_pool) {
+        apr_pool_destroy(m->spare_pool);
+        m->spare_pool = NULL;
+    }
     if (m->pool) {
         apr_pool_destroy(m->pool);
     }
@@ -107,7 +125,7 @@ h2_mplx *h2_mplx_create(conn_rec *c, apr
     if (m) {
         m->id = c->id;
         APR_RING_ELEM_INIT(m, link);
-        apr_atomic_set32(&m->refs, 1);
+        m->refs = 1;
         m->c = c;
         apr_pool_create_ex(&m->pool, parent, NULL, allocator);
         if (!m->pool) {
@@ -135,30 +153,28 @@ h2_mplx *h2_mplx_create(conn_rec *c, apr
     return m;
 }
 
-static void reference(h2_mplx *m)
-{
-    apr_atomic_inc32(&m->refs);
-}
-
 static void release(h2_mplx *m, int lock)
 {
-    if (!apr_atomic_dec32(&m->refs)) {
-        if (lock) {
-            apr_thread_mutex_lock(m->lock);
-        }
+    if (lock) {
+        apr_thread_mutex_lock(m->lock);
+        --m->refs;
         if (m->join_wait) {
             apr_thread_cond_signal(m->join_wait);
         }
-        if (lock) {
-            apr_thread_mutex_unlock(m->lock);
-        }
+        apr_thread_mutex_unlock(m->lock);
+    }
+    else {
+        --m->refs;
     }
 }
 
 void h2_mplx_reference(h2_mplx *m)
 {
-    reference(m);
+    apr_thread_mutex_lock(m->lock);
+    ++m->refs;
+    apr_thread_mutex_unlock(m->lock);
 }
+
 void h2_mplx_release(h2_mplx *m)
 {
     release(m, 1);
@@ -191,16 +207,16 @@ apr_status_t h2_mplx_release_and_join(h2
     status = apr_thread_mutex_lock(m->lock);
     if (APR_SUCCESS == status) {
         release(m, 0);
-        while (apr_atomic_read32(&m->refs) > 0) {
+        while (m->refs > 0) {
             m->join_wait = wait;
             ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c,
                           "h2_mplx(%ld): release_join, refs=%d, waiting...", 
                           m->id, m->refs);
             apr_thread_cond_wait(wait, m->lock);
         }
-        m->join_wait = NULL;
         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c,
                       "h2_mplx(%ld): release_join -> destroy", m->id);
+        m->pool = NULL;
         apr_thread_mutex_unlock(m->lock);
         h2_mplx_destroy(m);
     }
@@ -223,23 +239,23 @@ void h2_mplx_abort(h2_mplx *m)
 
 static void io_destroy(h2_mplx *m, h2_io *io)
 {
-    if (io) {
-        apr_pool_t *pool = io->pool;
-        if (pool) {
-            io->pool = NULL;
-            apr_pool_clear(pool);
-            if (m->spare_pool) {
-                apr_pool_destroy(m->spare_pool);
-            }
-            m->spare_pool = pool;
-        }
-        /* The pool is cleared/destroyed which also closes all
-         * allocated file handles. Give this count back to our
-         * file handle pool. */
-        m->file_handles_allowed += io->files_handles_owned;
-        h2_io_set_remove(m->stream_ios, io);
-        h2_io_set_remove(m->ready_ios, io);
-        h2_io_destroy(io);
+    apr_pool_t *pool = io->pool;
+    
+    io->pool = NULL;    
+    /* The pool is cleared/destroyed which also closes all
+     * allocated file handles. Give this count back to our
+     * file handle pool. */
+    m->file_handles_allowed += io->files_handles_owned;
+    h2_io_set_remove(m->stream_ios, io);
+    h2_io_set_remove(m->ready_ios, io);
+    h2_io_destroy(io);
+    
+    if (pool) {
+        apr_pool_clear(pool);
+        if (m->spare_pool) {
+            apr_pool_destroy(m->spare_pool);
+        }
+        m->spare_pool = pool;
     }
 }
 
@@ -254,23 +270,25 @@ apr_status_t h2_mplx_stream_done(h2_mplx
     status = apr_thread_mutex_lock(m->lock);
     if (APR_SUCCESS == status) {
         h2_io *io = h2_io_set_get(m->stream_ios, stream_id);
-        
+
+        /* there should be an h2_io, once the stream has been scheduled
+         * for processing, e.g. when we received all HEADERs. But when
+         * a stream is cancelled very early, it will not exist. */
         if (io) {
             /* Remove io from ready set, we will never submit it */
             h2_io_set_remove(m->ready_ios, io);
-            
-            if (io->task_done) {
+            if (io->task_done || h2_tq_remove(m->q, io->id)) {
+                /* already finished or not even started yet */
                 io_destroy(m, io);
             }
             else {
                 /* cleanup once task is done */
-                io->zombie = 1;
+                io->orphaned = 1;
                 if (rst_error) {
-                    /* Forward error code to fail any further attempt to
-                     * write to io */
                     h2_io_rst(io, rst_error);
                 }
             }
+            
         }
         
         apr_thread_mutex_unlock(m->lock);
@@ -287,7 +305,7 @@ void h2_mplx_task_done(h2_mplx *m, int s
                       "h2_mplx(%ld): task(%d) done", m->id, stream_id);
         if (io) {
             io->task_done = 1;
-            if (io->zombie) {
+            if (io->orphaned) {
                 io_destroy(m, io);
             }
             else {
@@ -310,15 +328,17 @@ apr_status_t h2_mplx_in_read(h2_mplx *m,
     status = apr_thread_mutex_lock(m->lock);
     if (APR_SUCCESS == status) {
         h2_io *io = h2_io_set_get(m->stream_ios, stream_id);
-        if (io) {
+        if (io && !io->orphaned) {
             io->input_arrived = iowait;
-            status = h2_io_in_read(io, bb, 0);
+            H2_MPLX_IO_IN(APLOG_TRACE2, m, io, "h2_mplx_in_read_pre");
+            status = h2_io_in_read(io, bb, -1);
             while (APR_STATUS_IS_EAGAIN(status) 
                    && !is_aborted(m, &status)
                    && block == APR_BLOCK_READ) {
                 apr_thread_cond_wait(io->input_arrived, m->lock);
-                status = h2_io_in_read(io, bb, 0);
+                status = h2_io_in_read(io, bb, -1);
             }
+            H2_MPLX_IO_IN(APLOG_TRACE2, m, io, "h2_mplx_in_read_post");
             io->input_arrived = NULL;
         }
         else {
@@ -340,8 +360,10 @@ apr_status_t h2_mplx_in_write(h2_mplx *m
     status = apr_thread_mutex_lock(m->lock);
     if (APR_SUCCESS == status) {
         h2_io *io = h2_io_set_get(m->stream_ios, stream_id);
-        if (io) {
+        if (io && !io->orphaned) {
+            H2_MPLX_IO_IN(APLOG_TRACE2, m, io, "h2_mplx_in_write_pre");
             status = h2_io_in_write(io, bb);
+            H2_MPLX_IO_IN(APLOG_TRACE2, m, io, "h2_mplx_in_write_post");
             if (io->input_arrived) {
                 apr_thread_cond_signal(io->input_arrived);
             }
@@ -364,8 +386,9 @@ apr_status_t h2_mplx_in_close(h2_mplx *m
     status = apr_thread_mutex_lock(m->lock);
     if (APR_SUCCESS == status) {
         h2_io *io = h2_io_set_get(m->stream_ios, stream_id);
-        if (io) {
+        if (io && !io->orphaned) {
             status = h2_io_in_close(io);
+            H2_MPLX_IO_IN(APLOG_TRACE2, m, io, "h2_mplx_in_close");
             if (io->input_arrived) {
                 apr_thread_cond_signal(io->input_arrived);
             }
@@ -422,16 +445,9 @@ apr_status_t h2_mplx_in_update_windows(h
     return status;
 }
 
-#define H2_MPLX_IO_OUT(lvl,m,io,msg) \
-    do { \
-        if (APLOG_C_IS_LEVEL((m)->c,lvl)) \
-        h2_util_bb_log((m)->c,(io)->id,lvl,msg,(io)->bbout); \
-    } while(0)
-
-
 apr_status_t h2_mplx_out_readx(h2_mplx *m, int stream_id, 
                                h2_io_data_cb *cb, void *ctx, 
-                               apr_size_t *plen, int *peos)
+                               apr_off_t *plen, int *peos)
 {
     apr_status_t status;
     AP_DEBUG_ASSERT(m);
@@ -441,7 +457,7 @@ apr_status_t h2_mplx_out_readx(h2_mplx *
     status = apr_thread_mutex_lock(m->lock);
     if (APR_SUCCESS == status) {
         h2_io *io = h2_io_set_get(m->stream_ios, stream_id);
-        if (io) {
+        if (io && !io->orphaned) {
             H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_readx_pre");
             
             status = h2_io_out_readx(io, cb, ctx, plen, peos);
@@ -461,7 +477,7 @@ 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_size_t *plen, int *peos)
+                                 apr_off_t *plen, int *peos)
 {
     apr_status_t status;
     AP_DEBUG_ASSERT(m);
@@ -471,7 +487,7 @@ apr_status_t h2_mplx_out_read_to(h2_mplx
     status = apr_thread_mutex_lock(m->lock);
     if (APR_SUCCESS == status) {
         h2_io *io = h2_io_set_get(m->stream_ios, stream_id);
-        if (io) {
+        if (io && !io->orphaned) {
             H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_read_to_pre");
             
             status = h2_io_out_read_to(io, bb, plen, peos);
@@ -508,7 +524,9 @@ h2_stream *h2_mplx_next_submit(h2_mplx *
                 }
                 else {
                     AP_DEBUG_ASSERT(io->response);
+                    H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_next_submit_pre");
                     h2_stream_set_response(stream, io->response, io->bbout);
+                    H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_next_submit_post");
                 }
                 
             }
@@ -521,7 +539,14 @@ h2_stream *h2_mplx_next_submit(h2_mplx *
                               "h2_mplx(%ld): stream for response %d closed, "
                               "resetting io to close request processing",
                               m->id, io->id);
-                h2_io_rst(io, H2_ERR_STREAM_CLOSED);
+                io->orphaned = 1;
+                if (io->task_done) {
+                    io_destroy(m, io);
+                }
+                else {
+                    /* hang around until the h2_task is done */
+                    h2_io_rst(io, H2_ERR_STREAM_CLOSED);
+                }
             }
             
             if (io->output_drained) {
@@ -577,11 +602,11 @@ static apr_status_t out_open(h2_mplx *m,
     apr_status_t status = APR_SUCCESS;
     
     h2_io *io = h2_io_set_get(m->stream_ios, stream_id);
-    if (io) {
+    if (io && !io->orphaned) {
         if (f) {
-            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c,
-                          "h2_mplx(%ld-%d): open response: %s, rst=%d",
-                          m->id, stream_id, response->status, 
+            ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c,
+                          "h2_mplx(%ld-%d): open response: %d, rst=%d",
+                          m->id, stream_id, response->http_status, 
                           response->rst_error);
         }
         
@@ -635,7 +660,7 @@ apr_status_t h2_mplx_out_write(h2_mplx *
     if (APR_SUCCESS == status) {
         if (!m->aborted) {
             h2_io *io = h2_io_set_get(m->stream_ios, stream_id);
-            if (io) {
+            if (io && !io->orphaned) {
                 status = out_write(m, io, f, bb, iowait);
                 H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_write");
                 
@@ -667,14 +692,14 @@ apr_status_t h2_mplx_out_close(h2_mplx *
     if (APR_SUCCESS == status) {
         if (!m->aborted) {
             h2_io *io = h2_io_set_get(m->stream_ios, stream_id);
-            if (io) {
+            if (io && !io->orphaned) {
                 if (!io->response && !io->rst_error) {
                     /* In case a close comes before a response was created,
                      * insert an error one so that our streams can properly
                      * reset.
                      */
                     h2_response *r = h2_response_create(stream_id, 0, 
-                                                        "500", NULL, m->pool);
+                                                        500, NULL, m->pool);
                     status = out_open(m, stream_id, r, NULL, NULL, NULL);
                     ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, m->c,
                                   "h2_mplx(%ld-%d): close, no response, no rst", 
@@ -711,7 +736,7 @@ apr_status_t h2_mplx_out_rst(h2_mplx *m,
     if (APR_SUCCESS == status) {
         if (!m->aborted) {
             h2_io *io = h2_io_set_get(m->stream_ios, stream_id);
-            if (io && !io->rst_error) {
+            if (io && !io->rst_error && !io->orphaned) {
                 h2_io_rst(io, error);
                 if (!io->response) {
                         h2_io_set_add(m->ready_ios, io);
@@ -744,7 +769,7 @@ int h2_mplx_in_has_eos_for(h2_mplx *m, i
     if (APR_SUCCESS == status) {
         h2_io *io = h2_io_set_get(m->stream_ios, stream_id);
         if (io) {
-            has_eos = h2_io_in_has_eos_for(io);
+            has_eos = io->orphaned || h2_io_in_has_eos_for(io);
         }
         apr_thread_mutex_unlock(m->lock);
     }
@@ -802,17 +827,6 @@ static void have_out_data_for(h2_mplx *m
     }
 }
 
-typedef struct {
-    h2_stream_pri_cmp *cmp;
-    void *ctx;
-} cmp_ctx;
-
-static int task_cmp(h2_task *t1, h2_task *t2, void *ctx)
-{
-    cmp_ctx *x = ctx;
-    return x->cmp(t1->stream_id, t2->stream_id, x->ctx);
-}
-
 apr_status_t h2_mplx_reprioritize(h2_mplx *m, h2_stream_pri_cmp *cmp, void *ctx)
 {
     apr_status_t status;
@@ -823,11 +837,7 @@ apr_status_t h2_mplx_reprioritize(h2_mpl
     }
     status = apr_thread_mutex_lock(m->lock);
     if (APR_SUCCESS == status) {
-        cmp_ctx x;
-        
-        x.cmp = cmp;
-        x.ctx = ctx;
-        h2_tq_sort(m->q, task_cmp, &x);
+        h2_tq_sort(m->q, cmp, ctx);
         
         ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c,
                       "h2_mplx(%ld): reprioritize tasks", m->id);
@@ -857,7 +867,7 @@ static h2_io *open_io(h2_mplx *m, int st
 
 
 apr_status_t h2_mplx_process(h2_mplx *m, int stream_id,
-                             struct h2_request *r, int eos, 
+                             const h2_request *req, int eos, 
                              h2_stream_pri_cmp *cmp, void *ctx)
 {
     apr_status_t status;
@@ -868,26 +878,19 @@ apr_status_t h2_mplx_process(h2_mplx *m,
     }
     status = apr_thread_mutex_lock(m->lock);
     if (APR_SUCCESS == status) {
-        conn_rec *c;
-        h2_io *io;
-        cmp_ctx x;
-        
-        io = open_io(m, stream_id);
-        c = h2_conn_create(m->c, io->pool);
-        io->task = h2_task_create(m->id, stream_id, io->pool, m, c);
-            
-        status = h2_request_end_headers(r, m, io->task, eos);
-        if (status == APR_SUCCESS && eos) {
+        h2_io *io = open_io(m, stream_id);
+        io->request = req;
+        io->request_body = !eos;
+
+        if (eos) {
             status = h2_io_in_close(io);
         }
         
-        if (status == APR_SUCCESS) {
-            x.cmp = cmp;
-            x.ctx = ctx;
-            h2_tq_add(m->q, io->task, task_cmp, &x);
-        }
+        h2_tq_add(m->q, io->id, cmp, ctx);
+
         ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, m->c,
                       "h2_mplx(%ld-%d): process", m->c->id, stream_id);
+        H2_MPLX_IO_IN(APLOG_TRACE2, m, io, "h2_mplx_process");
         apr_thread_mutex_unlock(m->lock);
     }
     
@@ -897,10 +900,11 @@ apr_status_t h2_mplx_process(h2_mplx *m,
     return status;
 }
 
-h2_task *h2_mplx_pop_task(h2_mplx *m, int *has_more)
+h2_task *h2_mplx_pop_task(h2_mplx *m, h2_worker *w, int *has_more)
 {
     h2_task *task = NULL;
     apr_status_t status;
+    
     AP_DEBUG_ASSERT(m);
     if (m->aborted) {
         *has_more = 0;
@@ -908,7 +912,16 @@ h2_task *h2_mplx_pop_task(h2_mplx *m, in
     }
     status = apr_thread_mutex_lock(m->lock);
     if (APR_SUCCESS == status) {
-        task = h2_tq_shift(m->q);
+        int sid;
+        while (!task && (sid = h2_tq_shift(m->q)) > 0) {
+            /* Anything not already setup correctly in the task
+             * needs to be so now, as task will be executed right about 
+             * when this method returns. */
+            h2_io *io = h2_io_set_get(m->stream_ios, sid);
+            if (io) {
+                task = h2_worker_create_task(w, m, io->request, !io->request_body);
+            }
+        }
         *has_more = !h2_tq_empty(m->q);
         apr_thread_mutex_unlock(m->lock);
     }

Modified: httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_mplx.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_mplx.h?rev=1715218&r1=1715217&r2=1715218&view=diff
==============================================================================
--- httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_mplx.h (original)
+++ httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_mplx.h Thu Nov 19 17:14:03 2015
@@ -44,6 +44,7 @@ struct h2_stream;
 struct h2_request;
 struct h2_io_set;
 struct apr_thread_cond_t;
+struct h2_worker;
 struct h2_workers;
 struct h2_stream_set;
 struct h2_task_queue;
@@ -55,7 +56,7 @@ typedef struct h2_mplx h2_mplx;
 struct h2_mplx {
     long id;
     APR_RING_ENTRY(h2_mplx) link;
-    volatile apr_uint32_t refs;
+    volatile int refs;
     conn_rec *c;
     apr_pool_t *pool;
     apr_bucket_alloc_t *bucket_alloc;
@@ -157,7 +158,7 @@ apr_status_t h2_mplx_out_trywait(h2_mplx
  * @param ctx context data for the compare function
  */
 apr_status_t h2_mplx_process(h2_mplx *m, int stream_id,
-                             struct h2_request *r, int eos, 
+                             const struct h2_request *r, int eos, 
                              h2_stream_pri_cmp *cmp, void *ctx);
 
 /**
@@ -169,7 +170,7 @@ apr_status_t h2_mplx_process(h2_mplx *m,
  */
 apr_status_t h2_mplx_reprioritize(h2_mplx *m, h2_stream_pri_cmp *cmp, void *ctx);
 
-struct h2_task *h2_mplx_pop_task(h2_mplx *mplx, int *has_more);
+struct h2_task *h2_mplx_pop_task(h2_mplx *mplx, struct h2_worker *w, int *has_more);
 
 /*******************************************************************************
  * Input handling of streams.
@@ -208,7 +209,7 @@ int h2_mplx_in_has_eos_for(h2_mplx *m, i
  * Callback invoked for every stream that had input data read since
  * the last invocation.
  */
-typedef void h2_mplx_consumed_cb(void *ctx, int stream_id, apr_size_t consumed);
+typedef void h2_mplx_consumed_cb(void *ctx, int stream_id, apr_off_t consumed);
 
 /**
  * Invoke the callback for all streams that had bytes read since the last
@@ -239,7 +240,7 @@ 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_size_t *plen, int *peos);
+                               apr_off_t *plen, int *peos);
 
 /**
  * Reads output data into the given brigade. Will never block, but
@@ -247,7 +248,7 @@ 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_size_t *plen, int *peos);
+                                 apr_off_t *plen, int *peos);
 
 /**
  * Opens the output for the given stream with the specified response.

Added: httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_push.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_push.c?rev=1715218&view=auto
==============================================================================
--- httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_push.c (added)
+++ httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_push.c Thu Nov 19 17:14:03 2015
@@ -0,0 +1,400 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed 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 <assert.h>
+#include <stdio.h>
+
+#include <apr_strings.h>
+#include <apr_lib.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_log.h>
+
+#include "h2_private.h"
+#include "h2_h2.h"
+#include "h2_util.h"
+#include "h2_push.h"
+#include "h2_request.h"
+#include "h2_response.h"
+
+
+typedef struct {
+    const h2_request *req;
+    apr_pool_t *pool;
+    apr_array_header_t *pushes;
+    const char *s;
+    size_t slen;
+    size_t i;
+    
+    const char *link;
+    apr_table_t *params;
+    char b[4096];
+} link_ctx;
+
+static int attr_char(char c) 
+{
+    switch (c) {
+        case '!':
+        case '#':
+        case '$':
+        case '&':
+        case '+':
+        case '-':
+        case '.':
+        case '^':
+        case '_':
+        case '`':
+        case '|':
+        case '~':
+            return 1;
+        default:
+            return apr_isalnum(c);
+    }
+}
+
+static int ptoken_char(char c) 
+{
+    switch (c) {
+        case '!':
+        case '#':
+        case '$':
+        case '&':
+        case '\'':
+        case '(':
+        case ')':
+        case '*':
+        case '+':
+        case '-':
+        case '.':
+        case '/':
+        case ':':
+        case '<':
+        case '=':
+        case '>':
+        case '?':
+        case '@':
+        case '[':
+        case ']':
+        case '^':
+        case '_':
+        case '`':
+        case '{':
+        case '|':
+        case '}':
+        case '~':
+            return 1;
+        default:
+            return apr_isalnum(c);
+    }
+}
+
+static int skip_ws(link_ctx *ctx)
+{
+    char c;
+    while (ctx->i < ctx->slen 
+           && (((c = ctx->s[ctx->i]) == ' ') || (c == '\t'))) {
+        ++ctx->i;
+    }
+    return (ctx->i < ctx->slen);
+}
+
+static int find_chr(link_ctx *ctx, char c, size_t *pidx)
+{
+    size_t j;
+    for (j = ctx->i; j < ctx->slen; ++j) {
+        if (ctx->s[j] == c) {
+            *pidx = j;
+            return 1;
+        }
+    } 
+    return 0;
+}
+
+static int read_chr(link_ctx *ctx, char c)
+{
+    if (ctx->i < ctx->slen && ctx->s[ctx->i] == c) {
+        ++ctx->i;
+        return 1;
+    }
+    return 0;
+}
+
+static char *mk_str(link_ctx *ctx, size_t end) 
+{
+    if (ctx->i < end) {
+        return apr_pstrndup(ctx->pool, ctx->s + ctx->i, end - ctx->i);
+    }
+    return "";
+}
+
+static int read_qstring(link_ctx *ctx, char **ps)
+{
+    if (skip_ws(ctx) && read_chr(ctx, '\"')) {
+        size_t end;
+        if (find_chr(ctx, '\"', &end)) {
+            *ps = mk_str(ctx, end);
+            ctx->i = end + 1;
+            return 1;
+        }
+    }
+    return 0;
+}
+
+static int read_ptoken(link_ctx *ctx, char **ps)
+{
+    if (skip_ws(ctx)) {
+        size_t i;
+        for (i = ctx->i; i < ctx->slen && ptoken_char(ctx->s[i]); ++i) {
+            /* nop */
+        }
+        if (i > ctx->i) {
+            *ps = mk_str(ctx, i);
+            ctx->i = i;
+            return 1;
+        }
+    }
+    return 0;
+}
+
+
+static int read_link(link_ctx *ctx)
+{
+    if (skip_ws(ctx) && read_chr(ctx, '<')) {
+        size_t end;
+        if (find_chr(ctx, '>', &end)) {
+            ctx->link = mk_str(ctx, end);
+            ctx->i = end + 1;
+            return 1;
+        }
+    }
+    return 0;
+}
+
+static int read_pname(link_ctx *ctx, char **pname)
+{
+    if (skip_ws(ctx)) {
+        size_t i;
+        for (i = ctx->i; i < ctx->slen && attr_char(ctx->s[i]); ++i) {
+            /* nop */
+        }
+        if (i > ctx->i) {
+            *pname = mk_str(ctx, i);
+            ctx->i = i;
+            return 1;
+        }
+    }
+    return 0;
+}
+
+static int read_pvalue(link_ctx *ctx, char **pvalue)
+{
+    if (skip_ws(ctx) && read_chr(ctx, '=')) {
+        if (read_qstring(ctx, pvalue) || read_ptoken(ctx, pvalue)) {
+            return 1;
+        }
+    }
+    return 0;
+}
+
+static int read_param(link_ctx *ctx)
+{
+    if (skip_ws(ctx) && read_chr(ctx, ';')) {
+        char *name, *value = "";
+        if (read_pname(ctx, &name)) {
+            read_pvalue(ctx, &value); /* value is optional */
+            apr_table_setn(ctx->params, name, value);
+            return 1;
+        }
+    }
+    return 0;
+}
+
+static int read_sep(link_ctx *ctx)
+{
+    if (skip_ws(ctx) && read_chr(ctx, ',')) {
+        return 1;
+    }
+    return 0;
+}
+
+static void init_params(link_ctx *ctx) 
+{
+    if (!ctx->params) {
+        ctx->params = apr_table_make(ctx->pool, 5);
+    }
+    else {
+        apr_table_clear(ctx->params);
+    }
+}
+
+static int same_authority(const h2_request *req, const apr_uri_t *uri)
+{
+    if (uri->scheme != NULL && strcmp(uri->scheme, req->scheme)) {
+        return 0;
+    }
+    if (uri->hostinfo != NULL && strcmp(uri->hostinfo, req->authority)) {
+        return 0;
+    }
+    return 1;
+}
+
+static int set_header(void *ctx, const char *key, const char *value) 
+{
+    apr_table_setn(ctx, key, value);
+    return 1;
+}
+
+
+static int add_push(link_ctx *ctx)
+{
+    /* so, we have read a Link header and need to decide
+     * if we transform it into a push.
+     */
+    const char *rel = apr_table_get(ctx->params, "rel");
+    if (rel && !strcmp("preload", rel)) {
+        apr_uri_t uri;
+        if (apr_uri_parse(ctx->pool, ctx->link, &uri) == APR_SUCCESS) {
+            if (uri.path && same_authority(ctx->req, &uri)) {
+                char *path;
+                apr_table_t *headers;
+                h2_request *req;
+                h2_push *push;
+                
+                /* We only want to generate pushes for resources in the
+                 * same authority than the original request.
+                 * icing: i think that is wise, otherwise we really need to
+                 * check that the vhost/server is available and uses the same
+                 * TLS (if any) parameters.
+                 */
+                path = apr_uri_unparse(ctx->pool, &uri, APR_URI_UNP_OMITSITEPART);
+                
+                push = apr_pcalloc(ctx->pool, sizeof(*push));
+                push->initiating_id = ctx->req->id;
+                
+                headers = apr_table_make(ctx->pool, 5);
+                apr_table_do(set_header, headers, ctx->req->headers,
+                             "User-Agent",
+                             "Cache-Control",
+                             "Accept-Language",
+                             NULL);
+                /* TODO: which headers do we add here?
+                 */
+                
+                req = h2_request_createn(0, ctx->pool,
+                                         ctx->req->method, 
+                                         ctx->req->scheme,
+                                         ctx->req->authority, 
+                                         path, headers);
+                h2_request_end_headers(req, ctx->pool, 1);
+                push->req = req;
+                
+                if (!ctx->pushes) {
+                    ctx->pushes = apr_array_make(ctx->pool, 5, sizeof(h2_push*));
+                }
+                APR_ARRAY_PUSH(ctx->pushes, h2_push*) = push;
+            }
+        }
+    }
+    return 0;
+}
+
+static void inspect_link(link_ctx *ctx, const char *s, size_t slen)
+{
+    /* RFC 5988 <https://tools.ietf.org/html/rfc5988#section-6.2.1>
+      Link           = "Link" ":" #link-value
+      link-value     = "<" URI-Reference ">" *( ";" link-param )
+      link-param     = ( ( "rel" "=" relation-types )
+                     | ( "anchor" "=" <"> URI-Reference <"> )
+                     | ( "rev" "=" relation-types )
+                     | ( "hreflang" "=" Language-Tag )
+                     | ( "media" "=" ( MediaDesc | ( <"> MediaDesc <"> ) ) )
+                     | ( "title" "=" quoted-string )
+                     | ( "title*" "=" ext-value )
+                     | ( "type" "=" ( media-type | quoted-mt ) )
+                     | ( link-extension ) )
+      link-extension = ( parmname [ "=" ( ptoken | quoted-string ) ] )
+                     | ( ext-name-star "=" ext-value )
+      ext-name-star  = parmname "*" ; reserved for RFC2231-profiled
+                                    ; extensions.  Whitespace NOT
+                                    ; allowed in between.
+      ptoken         = 1*ptokenchar
+      ptokenchar     = "!" | "#" | "$" | "%" | "&" | "'" | "("
+                     | ")" | "*" | "+" | "-" | "." | "/" | DIGIT
+                     | ":" | "<" | "=" | ">" | "?" | "@" | ALPHA
+                     | "[" | "]" | "^" | "_" | "`" | "{" | "|"
+                     | "}" | "~"
+      media-type     = type-name "/" subtype-name
+      quoted-mt      = <"> media-type <">
+      relation-types = relation-type
+                     | <"> relation-type *( 1*SP relation-type ) <">
+      relation-type  = reg-rel-type | ext-rel-type
+      reg-rel-type   = LOALPHA *( LOALPHA | DIGIT | "." | "-" )
+      ext-rel-type   = URI
+      
+      and from <https://tools.ietf.org/html/rfc5987>
+      parmname      = 1*attr-char
+      attr-char     = ALPHA / DIGIT
+                       / "!" / "#" / "$" / "&" / "+" / "-" / "."
+                       / "^" / "_" / "`" / "|" / "~"
+     */
+
+     ctx->s = s;
+     ctx->slen = slen;
+     ctx->i = 0;
+     
+     while (read_link(ctx)) {
+        init_params(ctx);
+        while (read_param(ctx)) {
+            /* nop */
+        }
+        add_push(ctx);
+        if (!read_sep(ctx)) {
+            break;
+        }
+     }
+}
+
+static int head_iter(void *ctx, const char *key, const char *value) 
+{
+    if (!apr_strnatcasecmp("link", key)) {
+        inspect_link(ctx, value, strlen(value));
+    }
+    return 1;
+}
+
+apr_array_header_t *h2_push_collect(apr_pool_t *p, const h2_request *req, 
+                                    const h2_response *res)
+{
+    /* Collect push candidates from the request/response pair.
+     * 
+     * One source for pushes are "rel=preload" link headers
+     * in the response.
+     * 
+     * TODO: This may be extended in the future by hooks or callbacks
+     * where other modules can provide push information directly.
+     */
+    if (res->header) {
+        link_ctx ctx;
+        
+        memset(&ctx, 0, sizeof(ctx));
+        ctx.req = req;
+        ctx.pool = p;
+    
+        apr_table_do(head_iter, &ctx, res->header, NULL);
+        return ctx.pushes;
+    }
+    return NULL;
+}

Added: httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_push.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_push.h?rev=1715218&view=auto
==============================================================================
--- httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_push.h (added)
+++ httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_push.h Thu Nov 19 17:14:03 2015
@@ -0,0 +1,33 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed 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.
+ */
+#ifndef __mod_h2__h2_push__
+#define __mod_h2__h2_push__
+
+struct h2_request;
+struct h2_response;
+struct h2_ngheader;
+
+typedef struct h2_push {
+    int initiating_id;
+    const struct h2_request *req;
+    const char *as;
+} h2_push;
+
+
+apr_array_header_t *h2_push_collect(apr_pool_t *p, 
+                                    const struct h2_request *req, 
+                                    const struct h2_response *res);
+
+#endif /* defined(__mod_h2__h2_push__) */

Modified: httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_request.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_request.c?rev=1715218&r1=1715217&r2=1715218&view=diff
==============================================================================
--- httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_request.c (original)
+++ httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_request.c Thu Nov 19 17:14:03 2015
@@ -19,68 +19,150 @@
 
 #include <httpd.h>
 #include <http_core.h>
+#include <http_protocol.h>
 #include <http_config.h>
 #include <http_log.h>
 
 #include "h2_private.h"
 #include "h2_mplx.h"
-#include "h2_to_h1.h"
 #include "h2_request.h"
 #include "h2_task.h"
 #include "h2_util.h"
 
 
-h2_request *h2_request_create(int id, apr_pool_t *pool, 
-                              apr_bucket_alloc_t *bucket_alloc)
+h2_request *h2_request_create(int id, apr_pool_t *pool)
+{
+    return h2_request_createn(id, pool, NULL, NULL, NULL, NULL, NULL);
+}
+
+h2_request *h2_request_createn(int id, apr_pool_t *pool,
+                               const char *method, const char *scheme,
+                               const char *authority, const char *path,
+                               apr_table_t *header)
 {
     h2_request *req = apr_pcalloc(pool, sizeof(h2_request));
-    if (req) {
-        req->id = id;
-        req->pool = pool;
-        req->bucket_alloc = bucket_alloc;
-    }
+    
+    req->id             = id;
+    req->method         = method;
+    req->scheme         = scheme;
+    req->authority      = authority;
+    req->path           = path;
+    req->headers        = header? header : apr_table_make(pool, 10);
+    
     return req;
 }
 
 void h2_request_destroy(h2_request *req)
 {
-    if (req->to_h1) {
-        h2_to_h1_destroy(req->to_h1);
-        req->to_h1 = NULL;
+}
+
+static apr_status_t inspect_clen(h2_request *req, const char *s)
+{
+    char *end;
+    req->content_length = apr_strtoi64(s, &end, 10);
+    return (s == end)? APR_EINVAL : APR_SUCCESS;
+}
+
+static apr_status_t add_h1_header(h2_request *req, apr_pool_t *pool, 
+                                  const char *name, size_t nlen,
+                                  const char *value, size_t vlen)
+{
+    char *hname, *hvalue;
+    
+    if (H2_HD_MATCH_LIT("expect", name, nlen)
+        || H2_HD_MATCH_LIT("upgrade", name, nlen)
+        || H2_HD_MATCH_LIT("connection", name, nlen)
+        || H2_HD_MATCH_LIT("proxy-connection", name, nlen)
+        || H2_HD_MATCH_LIT("transfer-encoding", name, nlen)
+        || H2_HD_MATCH_LIT("keep-alive", name, nlen)
+        || H2_HD_MATCH_LIT("http2-settings", name, nlen)) {
+        /* ignore these. */
+        return APR_SUCCESS;
+    }
+    else if (H2_HD_MATCH_LIT("cookie", name, nlen)) {
+        const char *existing = apr_table_get(req->headers, "cookie");
+        if (existing) {
+            char *nval;
+            
+            /* Cookie header come separately in HTTP/2, but need
+             * to be merged by "; " (instead of default ", ")
+             */
+            hvalue = apr_pstrndup(pool, value, vlen);
+            nval = apr_psprintf(pool, "%s; %s", existing, hvalue);
+            apr_table_setn(req->headers, "Cookie", nval);
+            return APR_SUCCESS;
+        }
     }
+    else if (H2_HD_MATCH_LIT("host", name, nlen)) {
+        if (apr_table_get(req->headers, "Host")) {
+            return APR_SUCCESS; /* ignore duplicate */
+        }
+    }
+    
+    hname = apr_pstrndup(pool, name, nlen);
+    hvalue = apr_pstrndup(pool, value, vlen);
+    h2_util_camel_case_header(hname, nlen);
+    apr_table_mergen(req->headers, hname, hvalue);
+    
+    return APR_SUCCESS;
 }
 
-static apr_status_t insert_request_line(h2_request *req, h2_mplx *m);
+typedef struct {
+    h2_request *req;
+    apr_pool_t *pool;
+} h1_ctx;
+
+static int set_h1_header(void *ctx, const char *key, const char *value)
+{
+    h1_ctx *x = ctx;
+    add_h1_header(x->req, x->pool, key, strlen(key), value, strlen(value));
+    return 1;
+}
 
-apr_status_t h2_request_rwrite(h2_request *req, request_rec *r, h2_mplx *m)
+static apr_status_t add_all_h1_header(h2_request *req, apr_pool_t *pool, 
+                                      apr_table_t *header)
+{
+    h1_ctx x;
+    x.req = req;
+    x.pool = pool;
+    apr_table_do(set_h1_header, &x, header, NULL);
+    return APR_SUCCESS;
+}
+
+
+apr_status_t h2_request_rwrite(h2_request *req, request_rec *r)
 {
     apr_status_t status;
-    req->method = r->method;
+    
+    req->method    = r->method;
+    req->scheme    = (r->parsed_uri.scheme? r->parsed_uri.scheme
+                      : ap_http_scheme(r));
     req->authority = r->hostname;
-    req->path = r->uri;
-    if (!ap_strchr_c(req->authority, ':') && r->parsed_uri.port_str) {
-        req->authority = apr_psprintf(req->pool, "%s:%s", req->authority,
-                                      r->parsed_uri.port_str);
+    req->path      = apr_uri_unparse(r->pool, &r->parsed_uri, 
+                                     APR_URI_UNP_OMITSITEPART);
+
+    if (!ap_strchr_c(req->authority, ':') && r->server) {
+        req->authority = apr_psprintf(r->pool, "%s:%d", req->authority,
+                                      (int)r->server->port);
     }
-    req->scheme = NULL;
     
-    
-    status = insert_request_line(req, m);
-    if (status == APR_SUCCESS) {
-        status = h2_to_h1_add_headers(req->to_h1, r->headers_in);
-    }
+    AP_DEBUG_ASSERT(req->scheme);
+    AP_DEBUG_ASSERT(req->authority);
+    AP_DEBUG_ASSERT(req->path);
+    AP_DEBUG_ASSERT(req->method);
+
+    status = add_all_h1_header(req, r->pool, r->headers_in);
 
     ap_log_rerror(APLOG_MARK, APLOG_DEBUG, status, r,
-                  "h2_request(%d): written request %s %s, host=%s",
-                  req->id, req->method, req->path, req->authority);
-    
+                  "h2_request(%d): rwrite %s host=%s://%s%s",
+                  req->id, req->method, req->scheme, req->authority, req->path);
+                  
     return status;
 }
 
-apr_status_t h2_request_write_header(h2_request *req,
-                                     const char *name, size_t nlen,
-                                     const char *value, size_t vlen,
-                                     h2_mplx *m)
+apr_status_t h2_request_add_header(h2_request *req, apr_pool_t *pool, 
+                                   const char *name, size_t nlen,
+                                   const char *value, size_t vlen)
 {
     apr_status_t status = APR_SUCCESS;
     
@@ -90,8 +172,8 @@ apr_status_t h2_request_write_header(h2_
     
     if (name[0] == ':') {
         /* pseudo header, see ch. 8.1.2.3, always should come first */
-        if (req->to_h1) {
-            ap_log_perror(APLOG_MARK, APLOG_ERR, 0, req->pool,
+        if (!apr_is_empty_table(req->headers)) {
+            ap_log_perror(APLOG_MARK, APLOG_ERR, 0, pool,
                           APLOGNO(02917) 
                           "h2_request(%d): pseudo header after request start",
                           req->id);
@@ -100,25 +182,25 @@ apr_status_t h2_request_write_header(h2_
         
         if (H2_HEADER_METHOD_LEN == nlen
             && !strncmp(H2_HEADER_METHOD, name, nlen)) {
-            req->method = apr_pstrndup(req->pool, value, vlen);
+            req->method = apr_pstrndup(pool, value, vlen);
         }
         else if (H2_HEADER_SCHEME_LEN == nlen
                  && !strncmp(H2_HEADER_SCHEME, name, nlen)) {
-            req->scheme = apr_pstrndup(req->pool, value, vlen);
+            req->scheme = apr_pstrndup(pool, value, vlen);
         }
         else if (H2_HEADER_PATH_LEN == nlen
                  && !strncmp(H2_HEADER_PATH, name, nlen)) {
-            req->path = apr_pstrndup(req->pool, value, vlen);
+            req->path = apr_pstrndup(pool, value, vlen);
         }
         else if (H2_HEADER_AUTH_LEN == nlen
                  && !strncmp(H2_HEADER_AUTH, name, nlen)) {
-            req->authority = apr_pstrndup(req->pool, value, vlen);
+            req->authority = apr_pstrndup(pool, value, vlen);
         }
         else {
             char buffer[32];
             memset(buffer, 0, 32);
             strncpy(buffer, name, (nlen > 31)? 31 : nlen);
-            ap_log_perror(APLOG_MARK, APLOG_WARNING, 0, req->pool,
+            ap_log_perror(APLOG_MARK, APLOG_WARNING, 0, pool,
                           APLOGNO(02954) 
                           "h2_request(%d): ignoring unknown pseudo header %s",
                           req->id, buffer);
@@ -126,65 +208,141 @@ apr_status_t h2_request_write_header(h2_
     }
     else {
         /* non-pseudo header, append to work bucket of stream */
-        if (!req->to_h1) {
-            status = insert_request_line(req, m);
-            if (status != APR_SUCCESS) {
-                return status;
-            }
-        }
-        
-        if (status == APR_SUCCESS) {
-            status = h2_to_h1_add_header(req->to_h1,
-                                         name, nlen, value, vlen);
-        }
+        status = add_h1_header(req, pool, name, nlen, value, vlen);
     }
     
     return status;
 }
 
-apr_status_t h2_request_write_data(h2_request *req,
-                                   const char *data, size_t len)
+apr_status_t h2_request_end_headers(h2_request *req, apr_pool_t *pool, int eos)
 {
-    return h2_to_h1_add_data(req->to_h1, data, len);
-}
+    const char *s;
+    
+    if (req->eoh) {
+        return APR_EINVAL;
+    }
 
-apr_status_t h2_request_end_headers(h2_request *req, struct h2_mplx *m,
-                                    h2_task *task, int eos)
-{
-    apr_status_t status;
+    /* be safe, some header we do not accept on h2(c) */
+    apr_table_unset(req->headers, "expect");
+    apr_table_unset(req->headers, "upgrade");
+    apr_table_unset(req->headers, "connection");
+    apr_table_unset(req->headers, "proxy-connection");
+    apr_table_unset(req->headers, "transfer-encoding");
+    apr_table_unset(req->headers, "keep-alive");
+    apr_table_unset(req->headers, "http2-settings");
+
+    if (!apr_table_get(req->headers, "Host")) {
+        /* Need to add a "Host" header if not already there to
+         * make virtual hosts work correctly. */
+        if (!req->authority) {
+            return APR_BADARG;
+        }
+        apr_table_set(req->headers, "Host", req->authority);
+    }
+
+    s = apr_table_get(req->headers, "Content-Length");
+    if (s) {
+        if (inspect_clen(req, s) != APR_SUCCESS) {
+            ap_log_perror(APLOG_MARK, APLOG_WARNING, APR_EINVAL, pool,
+                          APLOGNO(02959) 
+                          "h2_request(%d): content-length value not parsed: %s",
+                          req->id, s);
+            return APR_EINVAL;
+        }
+    }
+    else {
+        /* no content-length given */
+        req->content_length = -1;
+        if (!eos) {
+            /* We have not seen a content-length and have no eos,
+             * simulate a chunked encoding for our HTTP/1.1 infrastructure,
+             * in case we have "H2SerializeHeaders on" here
+             */
+            req->chunked = 1;
+            apr_table_mergen(req->headers, "Transfer-Encoding", "chunked");
+        }
+        else if (apr_table_get(req->headers, "Content-Type")) {
+            /* If we have a content-type, but already see eos, no more
+             * data will come. Signal a zero content length explicitly.
+             */
+            apr_table_setn(req->headers, "Content-Length", "0");
+        }
+    }
+
+    req->eoh = 1;
     
-    if (!req->to_h1) {
-        status = insert_request_line(req, m);
-        if (status != APR_SUCCESS) {
-            return status;
+    /* In the presence of trailers, force behaviour of chunked encoding */
+    s = apr_table_get(req->headers, "Trailer");
+    if (s && s[0]) {
+        req->trailers = apr_table_make(pool, 5);
+        if (!req->chunked) {
+            req->chunked = 1;
+            apr_table_mergen(req->headers, "Transfer-Encoding", "chunked");
         }
     }
-    status = h2_to_h1_end_headers(req->to_h1, eos);
-    h2_task_set_request(task, req->to_h1->method, 
-                        req->to_h1->scheme, 
-                        req->to_h1->authority, 
-                        req->to_h1->path, 
-                        req->to_h1->headers, eos);
-    return status;
+    
+    return APR_SUCCESS;
 }
 
-apr_status_t h2_request_close(h2_request *req)
+static apr_status_t add_h1_trailer(h2_request *req, apr_pool_t *pool, 
+                                   const char *name, size_t nlen,
+                                   const char *value, size_t vlen)
 {
-    return h2_to_h1_close(req->to_h1);
+    char *hname, *hvalue;
+    
+    if (H2_HD_MATCH_LIT("expect", name, nlen)
+        || H2_HD_MATCH_LIT("upgrade", name, nlen)
+        || H2_HD_MATCH_LIT("connection", name, nlen)
+        || H2_HD_MATCH_LIT("host", name, nlen)
+        || H2_HD_MATCH_LIT("proxy-connection", name, nlen)
+        || H2_HD_MATCH_LIT("transfer-encoding", name, nlen)
+        || H2_HD_MATCH_LIT("keep-alive", name, nlen)
+        || H2_HD_MATCH_LIT("http2-settings", name, nlen)) {
+        /* ignore these. */
+        return APR_SUCCESS;
+    }
+    
+    hname = apr_pstrndup(pool, name, nlen);
+    hvalue = apr_pstrndup(pool, value, vlen);
+    h2_util_camel_case_header(hname, nlen);
+
+    apr_table_mergen(req->trailers, hname, hvalue);
+    
+    return APR_SUCCESS;
 }
 
-static apr_status_t insert_request_line(h2_request *req, h2_mplx *m)
+
+apr_status_t h2_request_add_trailer(h2_request *req, apr_pool_t *pool,
+                                    const char *name, size_t nlen,
+                                    const char *value, size_t vlen)
 {
-    req->to_h1 = h2_to_h1_create(req->id, req->pool, req->bucket_alloc, 
-                                 req->method, 
-                                 req->scheme, 
-                                 req->authority, 
-                                 req->path, m);
-    return req->to_h1? APR_SUCCESS : APR_ENOMEM;
+    if (!req->trailers) {
+        ap_log_perror(APLOG_MARK, APLOG_DEBUG, APR_EINVAL, pool,
+                      "h2_request(%d): unanounced trailers",
+                      req->id);
+        return APR_EINVAL;
+    }
+    if (nlen == 0 || name[0] == ':') {
+        ap_log_perror(APLOG_MARK, APLOG_DEBUG, APR_EINVAL, pool,
+                      "h2_request(%d): pseudo header in trailer",
+                      req->id);
+        return APR_EINVAL;
+    }
+    return add_h1_trailer(req, pool, name, nlen, value, vlen);
 }
 
-apr_status_t h2_request_flush(h2_request *req)
+#define OPT_COPY(p, s)  ((s)? apr_pstrdup(p, s) : NULL)
+
+void h2_request_copy(apr_pool_t *p, h2_request *dst, const h2_request *src)
 {
-    return h2_to_h1_flush(req->to_h1);
+    /* keep the dst id */
+    dst->method         = OPT_COPY(p, src->method);
+    dst->scheme         = OPT_COPY(p, src->scheme);
+    dst->authority      = OPT_COPY(p, src->authority);
+    dst->path           = OPT_COPY(p, src->path);
+    dst->headers        = apr_table_clone(p, src->headers);
+    dst->content_length = src->content_length;
+    dst->chunked        = src->chunked;
+    dst->eoh            = src->eoh;
 }
 

Modified: httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_request.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_request.h?rev=1715218&r1=1715217&r2=1715218&view=diff
==============================================================================
--- httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_request.h (original)
+++ httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_request.h Thu Nov 19 17:14:03 2015
@@ -19,9 +19,6 @@
 /* h2_request is the transformer of HTTP2 streams into HTTP/1.1 internal
  * format that will be fed to various httpd input filters to finally
  * become a request_rec to be handled by soemone.
- *
- * Ideally, we would make a request_rec without serializing the headers
- * we have only to make someone else parse them back.
  */
 struct h2_to_h1;
 struct h2_mplx;
@@ -30,38 +27,45 @@ struct h2_task;
 typedef struct h2_request h2_request;
 
 struct h2_request {
-    int id;                 /* http2 stream id */
-    apr_pool_t *pool;
-    apr_bucket_alloc_t *bucket_alloc;
-    struct h2_to_h1 *to_h1; /* Converter to HTTP/1.1 format*/
-    
+    int id;                 /* stream id */
+
     /* pseudo header values, see ch. 8.1.2.3 */
     const char *method;
     const char *scheme;
     const char *authority;
     const char *path;
+    
+    apr_table_t *headers;
+    apr_table_t *trailers;
+
+    apr_off_t content_length;
+    int chunked;
+    int eoh;
 };
 
-h2_request *h2_request_create(int id, apr_pool_t *pool, 
-                              apr_bucket_alloc_t *bucket_alloc);
+h2_request *h2_request_create(int id, apr_pool_t *pool);
+
+h2_request *h2_request_createn(int id, apr_pool_t *pool,
+                               const char *method, const char *scheme,
+                               const char *authority, const char *path,
+                               apr_table_t *headers);
+
 void h2_request_destroy(h2_request *req);
 
-apr_status_t h2_request_flush(h2_request *req);
+apr_status_t h2_request_rwrite(h2_request *req, request_rec *r);
+
+apr_status_t h2_request_add_header(h2_request *req, apr_pool_t *pool,
+                                   const char *name, size_t nlen,
+                                   const char *value, size_t vlen);
 
-apr_status_t h2_request_write_header(h2_request *req,
-                                     const char *name, size_t nlen,
-                                     const char *value, size_t vlen,
-                                     struct h2_mplx *m);
+apr_status_t h2_request_add_trailer(h2_request *req, apr_pool_t *pool,
+                                    const char *name, size_t nlen,
+                                    const char *value, size_t vlen);
 
-apr_status_t h2_request_write_data(h2_request *request,
-                                   const char *data, size_t len);
+apr_status_t h2_request_end_headers(h2_request *req, apr_pool_t *pool, int eos);
 
-apr_status_t h2_request_end_headers(h2_request *req, struct h2_mplx *m, 
-                                    struct h2_task *task, int eos);
+void h2_request_copy(apr_pool_t *p, h2_request *dst, const h2_request *src);
 
-apr_status_t h2_request_close(h2_request *req);
 
-apr_status_t h2_request_rwrite(h2_request *req, request_rec *r,
-                               struct h2_mplx *m);
 
 #endif /* defined(__mod_h2__h2_request__) */

Modified: httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_response.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_response.c?rev=1715218&r1=1715217&r2=1715218&view=diff
==============================================================================
--- httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_response.c (original)
+++ httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_response.c Thu Nov 19 17:14:03 2015
@@ -29,21 +29,10 @@
 #include "h2_util.h"
 #include "h2_response.h"
 
-static h2_ngheader *make_ngheader(apr_pool_t *pool, const char *status,
-                                  apr_table_t *header);
-
-static int ignore_header(const char *name) 
-{
-    return (H2_HD_MATCH_LIT_CS("connection", name)
-            || H2_HD_MATCH_LIT_CS("proxy-connection", name)
-            || H2_HD_MATCH_LIT_CS("upgrade", name)
-            || H2_HD_MATCH_LIT_CS("keep-alive", name)
-            || H2_HD_MATCH_LIT_CS("transfer-encoding", name));
-}
 
 h2_response *h2_response_create(int stream_id,
                                 int rst_error,
-                                const char *http_status,
+                                int http_status,
                                 apr_array_header_t *hlines,
                                 apr_pool_t *pool)
 {
@@ -56,7 +45,7 @@ h2_response *h2_response_create(int stre
     
     response->stream_id = stream_id;
     response->rst_error = rst_error;
-    response->status = http_status? http_status : "500";
+    response->http_status = http_status? http_status : 500;
     response->content_length = -1;
     
     if (hlines) {
@@ -75,10 +64,8 @@ h2_response *h2_response_create(int stre
             while (*sep == ' ' || *sep == '\t') {
                 ++sep;
             }
-            if (ignore_header(hline)) {
-                /* never forward, ch. 8.1.2.2 */
-            }
-            else {
+            
+            if (!h2_util_ignore_header(hline)) {
                 apr_table_merge(header, hline, sep);
                 if (*sep && H2_HD_MATCH_LIT_CS("content-length", hline)) {
                     char *end;
@@ -99,7 +86,7 @@ h2_response *h2_response_create(int stre
         header = apr_table_make(pool, 0);        
     }
 
-    response->rheader = header;
+    response->header = header;
     return response;
 }
 
@@ -112,17 +99,17 @@ h2_response *h2_response_rcreate(int str
     }
     
     response->stream_id = stream_id;
-    response->status = apr_psprintf(pool, "%d", r->status);
+    response->http_status = r->status;
     response->content_length = -1;
-    response->rheader = header;
+    response->header = header;
 
-    if (r->status == HTTP_FORBIDDEN) {
+    if (response->http_status == HTTP_FORBIDDEN) {
         const char *cause = apr_table_get(r->notes, "ssl-renegotiate-forbidden");
         if (cause) {
             /* This request triggered a TLS renegotiation that is now allowed 
              * in HTTP/2. Tell the client that it should use HTTP/1.1 for this.
              */
-            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, r->status, r, 
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, response->http_status, r, 
                           "h2_response(%ld-%d): renegotiate forbidden, cause: %s",
                           (long)r->connection->id, stream_id, cause);
             response->rst_error = H2_ERR_HTTP_1_1_REQUIRED;
@@ -141,108 +128,12 @@ h2_response *h2_response_copy(apr_pool_t
 {
     h2_response *to = apr_pcalloc(pool, sizeof(h2_response));
     to->stream_id = from->stream_id;
-    to->status = apr_pstrdup(pool, from->status);
+    to->http_status = from->http_status;
     to->content_length = from->content_length;
-    if (from->rheader) {
-        to->ngheader = make_ngheader(pool, to->status, from->rheader);
+    if (from->header) {
+        to->header = apr_table_clone(pool, from->header);
     }
     return to;
 }
 
-typedef struct {
-    nghttp2_nv *nv;
-    size_t nvlen;
-    size_t nvstrlen;
-    size_t offset;
-    char *strbuf;
-    apr_pool_t *pool;
-} nvctx_t;
-
-static int count_header(void *ctx, const char *key, const char *value)
-{
-    if (!ignore_header(key)) {
-        nvctx_t *nvctx = (nvctx_t*)ctx;
-        nvctx->nvlen++;
-        nvctx->nvstrlen += strlen(key) + strlen(value) + 2;
-    }
-    return 1;
-}
-
-#define NV_ADD_LIT_CS(nv, k, v)     addnv_lit_cs(nv, k, sizeof(k) - 1, v, strlen(v))
-#define NV_ADD_CS_CS(nv, k, v)      addnv_cs_cs(nv, k, strlen(k), v, strlen(v))
-#define NV_BUF_ADD(nv, s, len)      memcpy(nv->strbuf, s, len); \
-s = nv->strbuf; \
-nv->strbuf += len + 1
-
-static void addnv_cs_cs(nvctx_t *ctx, const char *key, size_t key_len,
-                        const char *value, size_t val_len)
-{
-    nghttp2_nv *nv = &ctx->nv[ctx->offset];
-    
-    NV_BUF_ADD(ctx, key, key_len);
-    NV_BUF_ADD(ctx, value, val_len);
-    
-    nv->name = (uint8_t*)key;
-    nv->namelen = key_len;
-    nv->value = (uint8_t*)value;
-    nv->valuelen = val_len;
-    
-    ctx->offset++;
-}
-
-static void addnv_lit_cs(nvctx_t *ctx, const char *key, size_t key_len,
-                         const char *value, size_t val_len)
-{
-    nghttp2_nv *nv = &ctx->nv[ctx->offset];
-    
-    NV_BUF_ADD(ctx, value, val_len);
-    
-    nv->name = (uint8_t*)key;
-    nv->namelen = key_len;
-    nv->value = (uint8_t*)value;
-    nv->valuelen = val_len;
-    
-    ctx->offset++;
-}
-
-static int add_header(void *ctx, const char *key, const char *value)
-{
-    if (!ignore_header(key)) {
-        nvctx_t *nvctx = (nvctx_t*)ctx;
-        NV_ADD_CS_CS(nvctx, key, value);
-    }
-    return 1;
-}
-
-static h2_ngheader *make_ngheader(apr_pool_t *pool, const char *status,
-                                  apr_table_t *header)
-{
-    size_t n;
-    h2_ngheader *h;
-    nvctx_t ctx;
-    
-    ctx.nv       = NULL;
-    ctx.nvlen    = 1;
-    ctx.nvstrlen = strlen(status) + 1;
-    ctx.offset   = 0;
-    ctx.strbuf   = NULL;
-    ctx.pool     = pool;
-    
-    apr_table_do(count_header, &ctx, header, NULL);
-    
-    n =  (sizeof(h2_ngheader)
-                 + (ctx.nvlen * sizeof(nghttp2_nv)) + ctx.nvstrlen); 
-    h = apr_pcalloc(pool, n);
-    if (h) {
-        ctx.nv = (nghttp2_nv*)(h + 1);
-        ctx.strbuf = (char*)&ctx.nv[ctx.nvlen];
-        
-        NV_ADD_LIT_CS(&ctx, ":status", status);
-        apr_table_do(add_header, &ctx, header, NULL);
-        
-        h->nv = ctx.nv;
-        h->nvlen = ctx.nvlen;
-    }
-    return h;
-}
 

Modified: httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_response.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_response.h?rev=1715218&r1=1715217&r2=1715218&view=diff
==============================================================================
--- httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_response.h (original)
+++ httpd/httpd/branches/2.4-http2-alpha/modules/http2/h2_response.h Thu Nov 19 17:14:03 2015
@@ -16,26 +16,19 @@
 #ifndef __mod_h2__h2_response__
 #define __mod_h2__h2_response__
 
-/* h2_response is just the data belonging the the head of a HTTP response,
- * suitable prepared to be fed to nghttp2 for response submit. 
- */
-typedef struct h2_ngheader {
-    nghttp2_nv *nv;
-    apr_size_t nvlen;
-} h2_ngheader;
+struct h2_push;
 
 typedef struct h2_response {
     int stream_id;
     int rst_error;
-    const char *status;
+    int http_status;
     apr_off_t content_length;
-    apr_table_t *rheader;
-    h2_ngheader *ngheader;
+    apr_table_t *header;
 } h2_response;
 
 h2_response *h2_response_create(int stream_id,
                                 int rst_error,
-                                const char *http_status,
+                                int http_status,
                                 apr_array_header_t *hlines,
                                 apr_pool_t *pool);