]> granicus.if.org Git - apache/commitdiff
Merge r1736463,r1737006,r1737021,r1737102,r1737125,r1737254 from trunk:
authorStefan Eissing <icing@apache.org>
Thu, 31 Mar 2016 16:29:43 +0000 (16:29 +0000)
committerStefan Eissing <icing@apache.org>
Thu, 31 Mar 2016 16:29:43 +0000 (16:29 +0000)
mod_http2: backport of version 1.4.6

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1737255 13f79535-47bb-0310-9956-ffa450edef68

22 files changed:
CHANGES
docs/manual/mod/mod_http2.xml
modules/http2/h2.h
modules/http2/h2_ctx.c
modules/http2/h2_filter.c
modules/http2/h2_filter.h
modules/http2/h2_io.c
modules/http2/h2_io.h
modules/http2/h2_mplx.c
modules/http2/h2_mplx.h
modules/http2/h2_request.c
modules/http2/h2_response.c
modules/http2/h2_session.c
modules/http2/h2_stream.c
modules/http2/h2_stream.h
modules/http2/h2_task.c
modules/http2/h2_task_output.c
modules/http2/h2_task_output.h
modules/http2/h2_util.c
modules/http2/h2_util.h
modules/http2/h2_version.h
modules/http2/mod_http2.c

diff --git a/CHANGES b/CHANGES
index 58033bfb7a085279956a33755974c5d2a1f19d85..5a71b72e88f9c2905404d609a28546fcf31c9eeb 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -2,8 +2,17 @@
 
 Changes with Apache 2.4.20
 
+  *) mod_http2: incrementing keepalives on each request started so that logging
+     %k gives increasing numbers per master http2 connection. 
+     New documented variables in env, usable in custom log formats: H2_PUSH,
+     H2_PUSHED, H2_PUSHED_ON, H2_STREAM_ID and H2_STREAM_TAG.
+     [Stefan Eissing]
+
+  *) mod_http2: more efficient passing of response bodies with less contention
+     and file bucket forwarding. [Stefan Eissing]
+
   *) mod_http2: fix for missing score board updates on request count, fix for
-     memory leak on slave connection reuse.
+     memory leak on slave connection reuse. [Stefan Eissing]
      
   *) mod_http2: Fix build on Windows from dsp files.
      [Stefan Eissing] 
index e16f158e125095fefd61bff55ce21a7ff3979efa..737a88b0dc70bd1159774228ed85c483b6168615 100644 (file)
@@ -56,7 +56,8 @@
     <section id="envvars"><title>Environment Variables</title>
         
         <p>This module can be configured to provide HTTP/2 related information
-            as additional environment variables to the SSI and CGI namespace.
+            as additional environment variables to the SSI and CGI namespace, as well
+            as in custom log configurations (see <code>%{VAR_NAME}e</code>).
         </p>
         
         <table border="1">
                 <th>Value Type:</th>
                 <th>Description:</th>
             </tr>
-            <tr><td><code>HTTPe</code></td>                         <td>flag</td>      <td>HTTP/2 is being used.</td></tr>
-            <tr><td><code>H2PUSH</code></td>                        <td>flag</td>      <td>HTTP/2 Server Push is enabled for this request and also supported by the client.</td></tr>
+            <tr><td><code>HTTP2</code></td><td>flag</td><td>HTTP/2 is being used.</td></tr>
+            <tr><td><code>H2PUSH</code></td><td>flag</td><td>HTTP/2 Server Push is enabled for this connection and also supported by the client.</td></tr>
+            <tr><td><code>H2_PUSH</code></td><td>flag</td><td>alternate name for <code>H2PUSH</code></td></tr>
+            <tr><td><code>H2_PUSHED</code></td><td>string</td><td>empty or <code>PUSHED</code> for a request being pushed by the server.</td></tr>
+            <tr><td><code>H2_PUSHED_ON</code></td><td>number</td><td>HTTP/2 stream number that triggered the push of this request.</td></tr>
+            <tr><td><code>H2_STREAM_ID</code></td><td>number</td><td>HTTP/2 stream number of this request.</td></tr>
+            <tr><td><code>H2_STREAM_TAG</code></td><td>string</td><td>HTTP/2 process unique stream identifier, consisting of connection id and stream id separated by <code>-</code>.</td></tr>
         </table>
         
     </section>
index 335d368899edf37bac3a5aa4b1247e5c9a34d6b3..acb79cd2e239254135d37b5da0ed82c513232c9d 100644 (file)
@@ -117,7 +117,8 @@ typedef struct h2_request h2_request;
 
 struct h2_request {
     int id;             /* stream id */
-
+    int initiated_on;   /* initiating stream id (PUSH) or 0 */
+    
     const char *method; /* pseudo header values, see ch. 8.1.2.3 */
     const char *scheme;
     const char *authority;
index e8294fcb2b3e6b37683b364bf18bcbc39d664aa4..8b786b94d943e6c96a654dd87dabccf25a3a07d4 100644 (file)
@@ -65,7 +65,11 @@ h2_ctx *h2_ctx_rget(const request_rec *r)
 
 const char *h2_ctx_protocol_get(const conn_rec *c)
 {
-    h2_ctx *ctx = (h2_ctx*)ap_get_module_config(c->conn_config, &http2_module);
+    h2_ctx *ctx;
+    if (c->master) {
+        c = c->master;
+    }
+    ctx = (h2_ctx*)ap_get_module_config(c->conn_config, &http2_module);
     return ctx? ctx->protocol : NULL;
 }
 
index c036b02fd4612b051ad83a22f3a638d78c50afa6..8bf7fbcb40ca4fcae95975e1a2b924a85e454e32 100644 (file)
@@ -275,9 +275,9 @@ static apr_status_t h2_sos_h2_status_read_to(h2_sos *sos, apr_bucket_brigade *bb
     return sos->prev->read_to(sos->prev, bb, plen, peos);
 }
 
-static apr_status_t h2_sos_h2_status_prep_read(h2_sos *sos, apr_off_t *plen, int *peos)
+static apr_status_t h2_sos_h2_status_prepare(h2_sos *sos, apr_off_t *plen, int *peos)
 {
-    return sos->prev->prep_read(sos->prev, plen, peos);
+    return sos->prev->prepare(sos->prev, plen, peos);
 }
 
 static apr_status_t h2_sos_h2_status_readx(h2_sos *sos, h2_io_data_cb *cb, void *ctx,
@@ -304,7 +304,7 @@ static h2_sos *h2_sos_h2_status_create(h2_sos *prev)
     sos->response     = response;
     sos->stream       = prev->stream;
     sos->buffer       = h2_sos_h2_status_buffer;
-    sos->prep_read    = h2_sos_h2_status_prep_read;
+    sos->prepare      = h2_sos_h2_status_prepare;
     sos->readx        = h2_sos_h2_status_readx;
     sos->read_to      = h2_sos_h2_status_read_to;
     sos->get_trailers = h2_sos_h2_status_get_trailers;
index 9a38a9b9cbcfbd7acea42c12c3f04392009d9ce5..2f281f8be1b8a31a3b0a83b52671c7e740df6c47 100644 (file)
@@ -47,7 +47,7 @@ typedef struct h2_sos h2_sos;
 typedef apr_status_t h2_sos_data_cb(void *ctx, const char *data, apr_off_t len);
 
 typedef apr_status_t h2_sos_buffer(h2_sos *sos, apr_bucket_brigade *bb);
-typedef apr_status_t h2_sos_prep_read(h2_sos *sos, apr_off_t *plen, int *peos);
+typedef apr_status_t h2_sos_prepare(h2_sos *sos, apr_off_t *plen, int *peos);
 typedef apr_status_t h2_sos_readx(h2_sos *sos, h2_sos_data_cb *cb, 
                                   void *ctx, apr_off_t *plen, int *peos);
 typedef apr_status_t h2_sos_read_to(h2_sos *sos, apr_bucket_brigade *bb, 
@@ -63,7 +63,7 @@ struct h2_sos {
     struct h2_response *response;
     void             *ctx;
     h2_sos_buffer    *buffer;
-    h2_sos_prep_read *prep_read;
+    h2_sos_prepare   *prepare;
     h2_sos_readx     *readx;
     h2_sos_read_to   *read_to;
     h2_sos_get_trailers *get_trailers;
index 3b36ca181ea1050064d06600739c2d88f833bfd9..5bbf09e99b7e1d18b5df33ea1f18e64575f21c85 100644 (file)
@@ -108,10 +108,16 @@ 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_clone(io->pool, response);
+    /* we used to clone the response into the io->pool. But
+     * we have much tighter control over the EOR bucket nowadays,
+     * so just use the instance given */
+    io->response = response;
     if (response->rst_error) {
         h2_io_rst(io, response->rst_error);
     }
+    else if (response->content_length == 0) {
+        io->eos_out = 1;
+    }
 }
 
 void h2_io_rst(h2_io *io, int error)
@@ -346,75 +352,40 @@ apr_status_t h2_io_in_close(h2_io *io)
     return APR_SUCCESS;
 }
 
-static int is_out_readable(h2_io *io, apr_off_t *plen, int *peos
-                           apr_status_t *ps)
+apr_status_t h2_io_out_get_brigade(h2_io *io, apr_bucket_brigade *bb
+                                   apr_off_t len)
 {
     if (io->rst_error) {
-        *ps = APR_ECONNABORTED;
-        return 0;
+        return APR_ECONNABORTED;
     }
     if (io->eos_out_read) {
-        *plen = 0;
-        *peos = 1;
-        *ps = APR_SUCCESS;
-        return 0;
-    }
-    else if (!io->bbout) {
-        *plen = 0;
-        *peos = 0;
-        *ps = APR_EAGAIN;
-        return 0;
-    }
-    return 1;
-}
-
-apr_status_t h2_io_out_readx(h2_io *io,  
-                             h2_io_data_cb *cb, void *ctx, 
-                             apr_off_t *plen, int *peos)
-{
-    apr_status_t status;
-    if (!is_out_readable(io, plen, peos, &status)) {
-        return status;
+        return APR_EOF;
     }
-    if (cb == NULL) {
-        /* just checking length available */
-        status = h2_util_bb_avail(io->bbout, plen, peos);
+    else if (!io->bbout || APR_BRIGADE_EMPTY(io->bbout)) {
+        return APR_EAGAIN;
     }
     else {
-        status = h2_util_bb_readx(io->bbout, cb, ctx, plen, peos);
-        if (status == APR_SUCCESS) {
-            io->eos_out_read = *peos;
-            io->output_consumed += *plen;
+        apr_status_t status;
+        apr_off_t pre_len, post_len;
+        /* Allow file handles pass through without limits. If they
+         * already have the lifetime of this stream, we might as well
+         * pass them on to the master connection */
+        apr_size_t files = INT_MAX;
+        
+        apr_brigade_length(bb, 0, &pre_len);
+        status = h2_util_move(bb, io->bbout, len, &files, "h2_io_read_to");
+        if (status == APR_SUCCESS && io->eos_out 
+            && APR_BRIGADE_EMPTY(io->bbout)) {
+            io->eos_out_read = 1;
         }
-    }
-    return status;
-}
-
-apr_status_t h2_io_out_read_to(h2_io *io, apr_bucket_brigade *bb, 
-                               apr_off_t *plen, int *peos)
-{
-    apr_status_t status;
-    if (!is_out_readable(io, plen, peos, &status)) {
+        apr_brigade_length(bb, 0, &post_len);
+        io->output_consumed += (post_len - pre_len);
         return status;
     }
-    status = h2_util_move(bb, io->bbout, *plen, NULL, "h2_io_read_to");
-    if (status == APR_SUCCESS && io->eos_out && APR_BRIGADE_EMPTY(io->bbout)) {
-        io->eos_out_read = *peos = 1;
-    }
-    io->output_consumed += *plen;
-    return status;
-}
-
-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, apr_table_t *trailers,
+                             apr_size_t maxlen, 
                              apr_size_t *pfile_buckets_allowed)
 {
     apr_status_t status;
@@ -442,8 +413,6 @@ apr_status_t h2_io_out_write(h2_io *io, apr_bucket_brigade *bb,
         }
     }     
     
-    process_trailers(io, trailers);
-    
     /* Let's move the buckets from the request processing in here, so
      * that the main thread can read them when it has time/capacity.
      *
@@ -466,13 +435,12 @@ apr_status_t h2_io_out_write(h2_io *io, apr_bucket_brigade *bb,
 }
 
 
-apr_status_t h2_io_out_close(h2_io *io, apr_table_t *trailers)
+apr_status_t h2_io_out_close(h2_io *io)
 {
     if (io->rst_error) {
         return APR_ECONNABORTED;
     }
     if (!io->eos_out_read) { /* EOS has not been read yet */
-        process_trailers(io, trailers);
         if (!io->eos_out) {
             check_bbout(io);
             io->eos_out = 1;
index 90d0cde8f2ea1405c312d166b7ab932b6c966f18..d700f6f32203794a58215b92e6792ff21939bc27 100644 (file)
@@ -37,7 +37,7 @@ typedef struct h2_io h2_io;
 
 struct h2_io {
     int id;                          /* stream identifier */
-    apr_pool_t *pool;                /* stream pool */
+     apr_pool_t *pool;                /* stream pool */
     apr_bucket_alloc_t *bucket_alloc;
     
     const struct h2_request *request;/* request on this io */
@@ -151,23 +151,19 @@ apr_status_t h2_io_in_shutdown(h2_io *io);
  * @param plen the requested max len, set to amount of data on return
  * @param peos != 0 iff the end of stream has been reached
  */
-apr_status_t h2_io_out_readx(h2_io *io,  
-                             h2_io_data_cb *cb, void *ctx, 
-                             apr_off_t *plen, int *peos);
-
-apr_status_t h2_io_out_read_to(h2_io *io, 
-                               apr_bucket_brigade *bb, 
-                               apr_off_t *plen, int *peos);
+apr_status_t h2_io_out_get_brigade(h2_io *io, 
+                                   apr_bucket_brigade *bb, 
+                                   apr_off_t len);
 
 apr_status_t h2_io_out_write(h2_io *io, apr_bucket_brigade *bb, 
-                             apr_size_t maxlen, apr_table_t *trailers,
+                             apr_size_t maxlen, 
                              apr_size_t *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_table_t *trailers);
+apr_status_t h2_io_out_close(h2_io *io);
 
 /**
  * Gives the overall length of the data that is currently queued for
index 00a64ecf2a8abb1c1da0750bcb6acb175c9667b5..a4dbf1f40f6daf069d623999e5e0a417bf48c7fd 100644 (file)
@@ -306,12 +306,13 @@ static void io_destroy(h2_mplx *m, h2_io *io, int events)
         h2_task_destroy(io->task);
         io->task = NULL;
         
-        if (reuse_slave) {
+        if (reuse_slave && slave->keepalive == AP_CONN_KEEPALIVE) {
             apr_bucket_delete(io->eor);
             io->eor = NULL;
             APR_ARRAY_PUSH(m->spare_slaves, conn_rec*) = slave;
         }
         else {
+            slave->sbh = NULL;
             h2_slave_destroy(slave, NULL);
         }
     }
@@ -613,40 +614,9 @@ apr_status_t h2_mplx_in_update_windows(h2_mplx *m)
     return status;
 }
 
-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_table_t **ptrailers)
-{
-    apr_status_t status;
-    int acquired;
-    
-    AP_DEBUG_ASSERT(m);
-    if ((status = enter_mutex(m, &acquired)) == APR_SUCCESS) {
-        h2_io *io = h2_io_set_get(m->stream_ios, stream_id);
-        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);
-            H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_readx_post");
-            if (status == APR_SUCCESS && cb) {
-                h2_io_signal(io, H2_IO_WRITE);
-            }
-        }
-        else {
-            status = APR_ECONNABORTED;
-        }
-        
-        *ptrailers = (*peos && io->response)? io->response->trailers : NULL;
-        leave_mutex(m, acquired);
-    }
-    return status;
-}
-
-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_table_t **ptrailers)
+apr_status_t h2_mplx_out_get_brigade(h2_mplx *m, int stream_id, 
+                                     apr_bucket_brigade *bb, 
+                                     apr_off_t len, apr_table_t **ptrailers)
 {
     apr_status_t status;
     int acquired;
@@ -655,11 +625,11 @@ apr_status_t h2_mplx_out_read_to(h2_mplx *m, int stream_id,
     if ((status = enter_mutex(m, &acquired)) == APR_SUCCESS) {
         h2_io *io = h2_io_set_get(m->stream_ios, stream_id);
         if (io && !io->orphaned) {
-            H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_read_to_pre");
+            H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_get_brigade_pre");
             
-            status = h2_io_out_read_to(io, bb, plen, peos);
+            status = h2_io_out_get_brigade(io, bb, len);
             
-            H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_read_to_post");
+            H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_get_brigade_post");
             if (status == APR_SUCCESS) {
                 h2_io_signal(io, H2_IO_WRITE);
             }
@@ -667,7 +637,7 @@ apr_status_t h2_mplx_out_read_to(h2_mplx *m, int stream_id,
         else {
             status = APR_ECONNABORTED;
         }
-        *ptrailers = (*peos && io->response)? io->response->trailers : NULL;
+        *ptrailers = io->response? io->response->trailers : NULL;
         leave_mutex(m, acquired);
     }
     return status;
@@ -728,7 +698,6 @@ h2_stream *h2_mplx_next_submit(h2_mplx *m, h2_ihash_t *streams)
 static apr_status_t out_write(h2_mplx *m, h2_io *io, 
                               ap_filter_t* f, int blocking,
                               apr_bucket_brigade *bb,
-                              apr_table_t *trailers,
                               struct apr_thread_cond_t *iowait)
 {
     apr_status_t status = APR_SUCCESS;
@@ -742,7 +711,7 @@ static apr_status_t out_write(h2_mplx *m, h2_io *io,
            && !is_aborted(m, &status)) {
         
         status = h2_io_out_write(io, bb, blocking? m->stream_max_mem : INT_MAX, 
-                                 trailers, &m->tx_handles_reserved);
+                                 &m->tx_handles_reserved);
         io_out_consumed_signal(m, io);
         
         /* Wait for data to drain until there is room again or
@@ -759,7 +728,6 @@ static apr_status_t out_write(h2_mplx *m, h2_io *io,
                               m->id, io->id);
                 return APR_INCOMPLETE;
             }
-            trailers = NULL;
             if (f) {
                 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,
                               "h2_mplx(%ld-%d): waiting for out drain", 
@@ -797,7 +765,7 @@ static apr_status_t out_open(h2_mplx *m, int stream_id, h2_response *response,
             check_tx_reservation(m);
         }
         if (bb) {
-            status = out_write(m, io, f, 0, bb, response->trailers, iowait);
+            status = out_write(m, io, f, 0, bb, iowait);
             if (status == APR_INCOMPLETE) {
                 /* write will have transferred as much data as possible.
                    caller has to deal with non-empty brigade */
@@ -838,7 +806,6 @@ apr_status_t h2_mplx_out_open(h2_mplx *m, int stream_id, h2_response *response,
 apr_status_t h2_mplx_out_write(h2_mplx *m, int stream_id, 
                                ap_filter_t* f, int blocking,
                                apr_bucket_brigade *bb,
-                               apr_table_t *trailers,
                                struct apr_thread_cond_t *iowait)
 {
     apr_status_t status;
@@ -848,10 +815,9 @@ apr_status_t h2_mplx_out_write(h2_mplx *m, int stream_id,
     if ((status = enter_mutex(m, &acquired)) == APR_SUCCESS) {
         h2_io *io = h2_io_set_get(m->stream_ios, stream_id);
         if (io && !io->orphaned) {
-            status = out_write(m, io, f, blocking, bb, trailers, iowait);
+            status = out_write(m, io, f, blocking, bb, iowait);
             ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, m->c,
-                          "h2_mplx(%ld-%d): write with trailers=%s", 
-                          m->id, io->id, trailers? "yes" : "no");
+                          "h2_mplx(%ld-%d): write", m->id, io->id);
             H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_write");
             
             have_out_data_for(m, stream_id);
@@ -864,7 +830,7 @@ apr_status_t h2_mplx_out_write(h2_mplx *m, int stream_id,
     return status;
 }
 
-apr_status_t h2_mplx_out_close(h2_mplx *m, int stream_id, apr_table_t *trailers)
+apr_status_t h2_mplx_out_close(h2_mplx *m, int stream_id)
 {
     apr_status_t status;
     int acquired;
@@ -886,10 +852,9 @@ apr_status_t h2_mplx_out_close(h2_mplx *m, int stream_id, apr_table_t *trailers)
                               m->id, io->id);
             }
             ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, m->c,
-                          "h2_mplx(%ld-%d): close with eor=%s, trailers=%s", 
-                          m->id, io->id, io->eor? "yes" : "no", 
-                          trailers? "yes" : "no");
-            status = h2_io_out_close(io, trailers);
+                          "h2_mplx(%ld-%d): close with eor=%s", 
+                          m->id, io->id, io->eor? "yes" : "no");
+            status = h2_io_out_close(io);
             H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_close");
             io_out_consumed_signal(m, io);
             
@@ -1018,7 +983,8 @@ static h2_io *open_io(h2_mplx *m, int stream_id, const h2_request *request)
 }
 
 
-apr_status_t h2_mplx_process(h2_mplx *m, int stream_id, const h2_request *req, 
+apr_status_t h2_mplx_process(h2_mplx *m, int stream_id, 
+                             const h2_request *req, 
                              h2_stream_pri_cmp *cmp, void *ctx)
 {
     apr_status_t status;
@@ -1080,7 +1046,9 @@ static h2_task *pop_task(h2_mplx *m)
                 new_conn = 1;
             }
             
+            slave->sbh = m->c->sbh;
             io->task = task = h2_task_create(m->id, io->request, slave, m);
+            m->c->keepalives++;
             apr_table_setn(slave->notes, H2_TASK_ID_NOTE, task->id);
             if (new_conn) {
                 h2_slave_run_pre_connection(slave, ap_get_conn_socket(slave));
@@ -1144,7 +1112,7 @@ static void task_done(h2_mplx *m, h2_task *task, h2_req_engine *ngn)
             /* TODO: this will keep a worker attached to this h2_mplx as
              * long as it has requests to handle. Might no be fair to
              * other mplx's. Perhaps leave after n requests? */
-            h2_mplx_out_close(m, task->stream_id, NULL);
+            h2_mplx_out_close(m, task->stream_id);
             
             if (ngn && io) {
                 apr_off_t bytes = io->output_consumed + h2_io_out_length(io);
index dac5aac4c0a7a6b5214799589b50c861a82fbb99..40298476789e7d3db546501a64f2a0201d8d7fcd 100644 (file)
@@ -190,7 +190,8 @@ apr_status_t h2_mplx_out_trywait(h2_mplx *m, apr_interval_time_t timeout,
  * @param cmp the stream priority compare function
  * @param ctx context data for the compare function
  */
-apr_status_t h2_mplx_process(h2_mplx *m, int stream_id, const struct h2_request *r, 
+apr_status_t h2_mplx_process(h2_mplx *m, int stream_id, 
+                             const struct h2_request *r, 
                              h2_stream_pri_cmp *cmp, void *ctx);
 
 /**
@@ -265,23 +266,13 @@ apr_status_t h2_mplx_in_update_windows(h2_mplx *m);
 struct h2_stream *h2_mplx_next_submit(h2_mplx *m, 
                                       struct h2_ihash_t *streams);
 
-/**
- * Reads output data from the given stream. Will never block, but
- * return APR_EAGAIN until data arrives or the stream is closed.
- */
-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_table_t **ptrailers);
-
 /**
  * Reads output data into the given brigade. Will never block, but
  * return APR_EAGAIN until data arrives or the stream is closed.
  */
-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_table_t **ptrailers);
+apr_status_t h2_mplx_out_get_brigade(h2_mplx *mplx, int stream_id, 
+                                     apr_bucket_brigade *bb, 
+                                     apr_off_t len, apr_table_t **ptrailers);
 
 /**
  * Opens the output for the given stream with the specified response.
@@ -299,21 +290,18 @@ apr_status_t h2_mplx_out_open(h2_mplx *mplx, int stream_id,
  * @param blocking == 0 iff call should return with APR_INCOMPLETE if
  *                 the full brigade cannot be written at once
  * @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, 
                                int blocking,
                                apr_bucket_brigade *bb,
-                               apr_table_t *trailers,
                                struct apr_thread_cond_t *iowait);
 
 /**
- * Closes the output for stream stream_id. Optionally forwards trailers
- * fromt the processed stream.  
+ * Closes the output for stream 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_close(h2_mplx *m, int stream_id);
 
 apr_status_t h2_mplx_out_rst(h2_mplx *m, int stream_id, int error);
 
index 251c0c01fb1802d1fb50990aa2c950b460ff30bc..2652661e78ac019e3827798bfb496ebfd7b1dd20 100644 (file)
@@ -342,6 +342,7 @@ apr_status_t h2_request_add_trailer(h2_request *req, apr_pool_t *pool,
 void h2_request_copy(apr_pool_t *p, h2_request *dst, const h2_request *src)
 {
     /* keep the dst id */
+    dst->initiated_on   = src->initiated_on;
     dst->method         = OPT_COPY(p, src->method);
     dst->scheme         = OPT_COPY(p, src->scheme);
     dst->authority      = OPT_COPY(p, src->authority);
@@ -350,9 +351,15 @@ void h2_request_copy(apr_pool_t *p, h2_request *dst, const h2_request *src)
     if (src->trailers) {
         dst->trailers   = apr_table_clone(p, src->trailers);
     }
+    else {
+        dst->trailers   = NULL;
+    }
     dst->content_length = src->content_length;
     dst->chunked        = src->chunked;
     dst->eoh            = src->eoh;
+    dst->body           = src->body;
+    dst->serialize      = src->serialize;
+    dst->push_policy    = src->push_policy;
 }
 
 h2_request *h2_request_clone(apr_pool_t *p, const h2_request *src)
index 01bb20b990987389bc4d5d8867fd683756749494..eb9043d0dba12949a843c495e5b072fde4b2dd0a 100644 (file)
@@ -70,6 +70,28 @@ static const char *get_sos_filter(apr_table_t *notes)
     return notes? apr_table_get(notes, H2_RESP_SOS_NOTE) : NULL;
 }
 
+static void check_clen(h2_response *response, request_rec *r, apr_pool_t *pool)
+{
+    
+    if (r && r->header_only) {
+        response->content_length = 0;
+    }
+    else if (response->headers) {
+        const char *s = apr_table_get(response->headers, "Content-Length");
+        if (s) {
+            char *end;
+            response->content_length = apr_strtoi64(s, &end, 10);
+            if (s == end) {
+                ap_log_perror(APLOG_MARK, APLOG_WARNING, APR_EINVAL, 
+                              pool, APLOGNO(02956) 
+                              "h2_response: content-length"
+                              " value not parsed: %s", s);
+                response->content_length = -1;
+            }
+        }
+    }
+}
+
 static h2_response *h2_response_create_int(int stream_id,
                                            int rst_error,
                                            int http_status,
@@ -78,7 +100,6 @@ static h2_response *h2_response_create_int(int stream_id,
                                            apr_pool_t *pool)
 {
     h2_response *response;
-    const char *s;
 
     if (!headers) {
         return NULL;
@@ -96,19 +117,7 @@ static h2_response *h2_response_create_int(int stream_id,
     response->headers        = headers;
     response->sos_filter     = get_sos_filter(notes);
     
-    s = apr_table_get(headers, "Content-Length");
-    if (s) {
-        char *end;
-        
-        response->content_length = apr_strtoi64(s, &end, 10);
-        if (s == end) {
-            ap_log_perror(APLOG_MARK, APLOG_WARNING, APR_EINVAL, 
-                          pool, APLOGNO(02956) 
-                          "h2_response: content-length"
-                          " value not parsed: %s", s);
-            response->content_length = -1;
-        }
-    }
+    check_clen(response, NULL, pool);
     return response;
 }
 
@@ -138,6 +147,8 @@ h2_response *h2_response_rcreate(int stream_id, request_rec *r,
     response->headers        = header;
     response->sos_filter     = get_sos_filter(r->notes);
 
+    check_clen(response, r, pool);
+    
     if (response->http_status == HTTP_FORBIDDEN) {
         const char *cause = apr_table_get(r->notes, "ssl-renegotiate-forbidden");
         if (cause) {
index cbdae8cbbf7123a0e56e86010a1fe40f9dfee8f1..928bb4a673be6066c11ad490ea11aa3590319513 100644 (file)
@@ -491,7 +491,7 @@ static int on_frame_recv_cb(nghttp2_session *ng2s,
                           session->id, (int)frame->hd.stream_id,
                           (int)frame->rst_stream.error_code);
             stream = h2_session_get_stream(session, frame->hd.stream_id);
-            if (stream && stream->initiated_on) {
+            if (stream && stream->request && stream->request->initiated_on) {
                 ++session->pushes_reset;
             }
             else {
@@ -555,6 +555,7 @@ static int on_send_data_cb(nghttp2_session *ngh2,
     unsigned char padlen;
     int eos;
     h2_stream *stream;
+    apr_bucket *b;
     
     (void)ngh2;
     (void)source;
@@ -599,16 +600,10 @@ static int on_send_data_cb(nghttp2_session *ngh2,
         }
     }
     else {
-        apr_bucket *b;
-        char *header = apr_pcalloc(stream->pool, 10);
-        memcpy(header, (const char *)framehd, 9);
-        if (padlen) {
-            header[9] = (char)padlen;
+        status = h2_conn_io_write(&session->io, (const char *)framehd, 9);
+        if (padlen && status == APR_SUCCESS) {
+            status = h2_conn_io_write(&session->io, (const char *)&padlen, 1);
         }
-        b = apr_bucket_pool_create(header, padlen? 10 : 9, 
-                                   stream->pool, session->c->bucket_alloc);
-        status = h2_conn_io_writeb(&session->io, b);
-        
         if (status == APR_SUCCESS) {
             apr_off_t len = length;
             status = h2_stream_read_to(stream, session->io.output, &len, &eos);
@@ -1114,7 +1109,12 @@ static int resume_on_data(void *ctx, void *val)
     AP_DEBUG_ASSERT(stream);
     
     if (h2_stream_is_suspended(stream)) {
-        if (h2_mplx_out_has_data_for(stream->session->mplx, stream->id)) {
+        apr_status_t status;
+        apr_off_t len = -1;
+        int eos;
+        
+        status = h2_stream_out_prepare(stream, &len, &eos);
+        if (status == APR_SUCCESS) {
             int rv;
             h2_stream_set_suspended(stream, 0);
             ++rctx->resume_count;
@@ -1123,8 +1123,9 @@ static int resume_on_data(void *ctx, void *val)
             ap_log_cerror(APLOG_MARK, nghttp2_is_fatal(rv)?
                           APLOG_ERR : APLOG_DEBUG, 0, session->c,
                           APLOGNO(02936) 
-                          "h2_stream(%ld-%d): resuming %s",
-                          session->id, stream->id, rv? nghttp2_strerror(rv) : "");
+                          "h2_stream(%ld-%d): resuming %s, len=%ld, eos=%d",
+                          session->id, stream->id, 
+                          rv? nghttp2_strerror(rv) : "", (long)len, eos);
         }
     }
     return 1;
@@ -1189,7 +1190,7 @@ static ssize_t stream_data_cb(nghttp2_session *ng2s,
     
     AP_DEBUG_ASSERT(!h2_stream_is_suspended(stream));
     
-    status = h2_stream_prep_read(stream, &nread, &eos);
+    status = h2_stream_out_prepare(stream, &nread, &eos);
     if (nread) {
         *data_flags |=  NGHTTP2_DATA_FLAG_NO_COPY;
     }
@@ -1214,11 +1215,6 @@ static ssize_t stream_data_cb(nghttp2_session *ng2s,
                           session->id, (int)stream_id);
             return NGHTTP2_ERR_DEFERRED;
             
-        case APR_EOF:
-            nread = 0;
-            eos = 1;
-            break;
-            
         default:
             nread = 0;
             ap_log_cerror(APLOG_MARK, APLOG_ERR, status, session->c,
@@ -1274,18 +1270,21 @@ static apr_status_t submit_response(h2_session *session, h2_stream *stream)
         rv = NGHTTP2_PROTOCOL_ERROR;
     }
     else if (response && response->headers) {
-        nghttp2_data_provider provider;
+        nghttp2_data_provider provider, *pprovider = NULL;
         h2_ngheader *ngh;
         const h2_priority *prio;
         
-        memset(&provider, 0, sizeof(provider));
-        provider.source.fd = stream->id;
-        provider.read_callback = stream_data_cb;
-        
         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03073)
                       "h2_stream(%ld-%d): submit response %d",
                       session->id, stream->id, response->http_status);
         
+        if (response->content_length != 0) {
+            memset(&provider, 0, sizeof(provider));
+            provider.source.fd = stream->id;
+            provider.read_callback = stream_data_cb;
+            pprovider = &provider;
+        }
+        
         /* If this stream is not a pushed one itself,
          * and HTTP/2 server push is enabled here,
          * and the response is in the range 200-299 *),
@@ -1301,7 +1300,7 @@ static apr_status_t submit_response(h2_session *session, h2_stream *stream)
          *    as the client, having this resource in its cache, might
          *    also have the pushed ones as well.
          */
-        if (!stream->initiated_on
+        if (stream->request && !stream->request->initiated_on
             && H2_HTTP_2XX(response->http_status)
             && h2_session_push_enabled(session)) {
             
@@ -1317,7 +1316,7 @@ static apr_status_t submit_response(h2_session *session, h2_stream *stream)
         ngh = h2_util_ngheader_make_res(stream->pool, response->http_status, 
                                         response->headers);
         rv = nghttp2_submit_response(session->ngh2, response->stream_id,
-                                     ngh->nv, ngh->nvlen, &provider);
+                                     ngh->nv, ngh->nvlen, pprovider);
     }
     else {
         int err = H2_STREAM_RST(stream, H2_ERR_PROTOCOL_ERROR);
@@ -1331,7 +1330,7 @@ static apr_status_t submit_response(h2_session *session, h2_stream *stream)
     }
     
     stream->submitted = 1;
-    if (stream->initiated_on) {
+    if (stream->request && stream->request->initiated_on) {
         ++session->pushes_submitted;
     }
     else {
@@ -1876,6 +1875,9 @@ static void h2_session_ev_no_io(h2_session *session, int arg, const char *msg)
              * CPU cycles. Ideally, we'd like to do a blocking read, but that
              * is not possible if we have scheduled tasks and wait
              * for them to produce something. */
+            if (h2_conn_io_flush(&session->io) != APR_SUCCESS) {
+                dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 0, NULL);
+            }
             if (h2_ihash_is_empty(session->streams)) {
                 if (!is_accepting_streams(session)) {
                     /* We are no longer accepting new streams and have
@@ -2249,6 +2251,9 @@ apr_status_t h2_session_process(h2_session *session, int async)
                 if (session->wait_us <= 0) {
                     session->wait_us = 10;
                     session->start_wait = apr_time_now();
+                    if (h2_conn_io_flush(&session->io) != APR_SUCCESS) {
+                        dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 0, NULL);
+                    }
                     update_child_status(session, SERVER_BUSY_READ, "wait");
                 }
                 else if ((apr_time_now() - session->start_wait) >= session->s->timeout) {
@@ -2301,10 +2306,6 @@ apr_status_t h2_session_process(h2_session *session, int async)
                 break;
         }
 
-        status = h2_conn_io_flush(&session->io);
-        if (status != APR_SUCCESS) {
-            dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 0, NULL);
-        }
         if (!nghttp2_session_want_read(session->ngh2) 
                  && !nghttp2_session_want_write(session->ngh2)) {
             dispatch_event(session, H2_SESSION_EV_NGH2_DONE, 0, NULL); 
@@ -2316,8 +2317,6 @@ apr_status_t h2_session_process(h2_session *session, int async)
     }
     
 out:
-    h2_conn_io_flush(&session->io);
-    
     ap_log_cerror( APLOG_MARK, APLOG_TRACE1, status, c,
                   "h2_session(%ld): [%s] process returns", 
                   session->id, state_name(session->state));
index 2b368b67cf04f2b6fd49bc827812b6f3ec8bb101..0a1dadf9ddf246330109524699fc441f1b6d1fac 100644 (file)
@@ -235,7 +235,7 @@ void h2_stream_set_h2_request(h2_stream *stream, int initiated_on,
                               const h2_request *req)
 {
     h2_request_copy(stream->pool, stream->request, req);
-    stream->initiated_on = initiated_on;
+    stream->request->initiated_on = initiated_on;
     stream->request->eoh = 0;
 }
 
@@ -387,17 +387,17 @@ int h2_stream_is_suspended(const h2_stream *stream)
     return stream->suspended;
 }
 
-apr_status_t h2_stream_prep_read(h2_stream *stream, 
-                                 apr_off_t *plen, int *peos)
+apr_status_t h2_stream_out_prepare(h2_stream *stream, 
+                                   apr_off_t *plen, int *peos)
 {
     if (stream->rst_error) {
+        *plen = 0;
+        *peos = 1;
         return APR_ECONNRESET;
     }
 
-    if (!stream->sos) {
-        return APR_EGENERAL;
-    }
-    return stream->sos->prep_read(stream->sos, plen, peos);
+    AP_DEBUG_ASSERT(stream->sos);
+    return stream->sos->prepare(stream->sos, plen, peos);
 }
 
 apr_status_t h2_stream_readx(h2_stream *stream, 
@@ -476,7 +476,7 @@ const h2_priority *h2_stream_get_priority(h2_stream *stream)
 {
     h2_response *response = h2_stream_get_response(stream);
     
-    if (stream->initiated_on && response) {
+    if (response && stream->request && stream->request->initiated_on) {
         const char *ctype = apr_table_get(response->headers, "content-type");
         if (ctype) {
             /* FIXME: Not good enough, config needs to come from request->server */
@@ -493,7 +493,9 @@ const h2_priority *h2_stream_get_priority(h2_stream *stream)
 typedef struct h2_sos_mplx {
     h2_mplx *m;
     apr_bucket_brigade *bb;
+    apr_bucket_brigade *tmp;
     apr_table_t *trailers;
+    apr_off_t  buffer_size;
 } h2_sos_mplx;
 
 #define H2_SOS_MPLX_OUT(lvl,msos,msg) \
@@ -503,129 +505,82 @@ typedef struct h2_sos_mplx {
     } while(0)
     
 
-static apr_status_t h2_sos_mplx_read_to(h2_sos *sos, apr_bucket_brigade *bb
-                                        apr_off_t *plen, int *peos)
+static apr_status_t mplx_transfer(h2_sos_mplx *msos, int stream_id
+                                  apr_pool_t *pool)
 {
-    h2_sos_mplx *msos = sos->ctx;
-    apr_status_t status = APR_SUCCESS;
+    apr_status_t status;
     apr_table_t *trailers = NULL;
-
-    H2_SOS_MPLX_OUT(APLOG_TRACE2, msos, "h2_sos_mplx read_to_pre");
     
-    if (APR_BRIGADE_EMPTY(msos->bb)) {
-        apr_off_t tlen = *plen;
-        int eos;
-        status = h2_mplx_out_read_to(msos->m, sos->stream->id, 
-                                     msos->bb, &tlen, &eos, &trailers);
+    if (!msos->tmp) {
+        msos->tmp = apr_brigade_create(msos->bb->p, msos->bb->bucket_alloc);
     }
-    
-    if (status == APR_SUCCESS && !APR_BRIGADE_EMPTY(msos->bb)) {
-        status = h2_transfer_brigade(bb, msos->bb, sos->stream->pool, 
-                                     plen, peos);
-    }
-    else {
-        *plen = 0;
-        *peos = 0;
+    status = h2_mplx_out_get_brigade(msos->m, stream_id, msos->tmp, 
+                                     msos->buffer_size-1, &trailers);
+    if (!APR_BRIGADE_EMPTY(msos->tmp)) {
+        h2_transfer_brigade(msos->bb, msos->tmp, pool);
     }
-
     if (trailers) {
-        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, msos->m->c,
-                      "h2_stream(%ld-%d): read_to, saving trailers",
-                      msos->m->id, sos->stream->id);
         msos->trailers = trailers;
     }
-    
+    return status;
+}
+static apr_status_t h2_sos_mplx_read_to(h2_sos *sos, apr_bucket_brigade *bb, 
+                                        apr_off_t *plen, int *peos)
+{
+    h2_sos_mplx *msos = sos->ctx;
+    apr_status_t status;
+
+    status = h2_append_brigade(bb, msos->bb, plen, peos);
     if (status == APR_SUCCESS && !*peos && !*plen) {
         status = APR_EAGAIN;
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, msos->m->c,
+                      "h2_stream(%ld-%d): read_to, len=%ld eos=%d",
+                      msos->m->id, sos->stream->id, (long)*plen, *peos);
     }
-    H2_SOS_MPLX_OUT(APLOG_TRACE2, msos, "h2_sos_mplx read_to_post");
     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, msos->m->c,
                   "h2_stream(%ld-%d): read_to, len=%ld eos=%d",
                   msos->m->id, sos->stream->id, (long)*plen, *peos);
     return status;
 }
 
-static apr_status_t h2_sos_mplx_prep_read(h2_sos *sos, apr_off_t *plen, int *peos)
+static apr_status_t h2_sos_mplx_readx(h2_sos *sos, h2_io_data_cb *cb, void *ctx,
+                                      apr_off_t *plen, int *peos)
 {
     h2_sos_mplx *msos = sos->ctx;
     apr_status_t status = APR_SUCCESS;
-    const char *src;
-    apr_table_t *trailers = NULL;
-    int test_read = (*plen == 0);
     
-    H2_SOS_MPLX_OUT(APLOG_TRACE2, msos, "h2_sos_mplx prep_read_pre");
-    if (!APR_BRIGADE_EMPTY(msos->bb)) {
-        src = "stream";
-        status = h2_util_bb_avail(msos->bb, plen, peos);
-        if (!test_read && status == APR_SUCCESS && !*peos && !*plen) {
-            apr_brigade_cleanup(msos->bb);
-            return h2_sos_mplx_prep_read(sos, plen, peos);
-        }
-    }
-    else {
-        src = "mplx";
-        status = h2_mplx_out_readx(msos->m, sos->stream->id, 
-                                   NULL, NULL, plen, peos, &trailers);
-        if (trailers) {
-            msos->trailers = trailers;
-        }    
-    }
-    
-    if (!test_read && status == APR_SUCCESS && !*peos && !*plen) {
+    status = h2_util_bb_readx(msos->bb, cb, ctx, plen, peos);
+    if (status == APR_SUCCESS && !*peos && !*plen) {
         status = APR_EAGAIN;
     }
-    
-    H2_SOS_MPLX_OUT(APLOG_TRACE2, msos, "h2_sos_mplx prep_read_post");
-    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, msos->m->c,
-                  "h2_stream(%ld-%d): prep_read %s, len=%ld eos=%d, trailers=%s",
-                  msos->m->id, sos->stream->id, src, (long)*plen, *peos,
-                  msos->trailers? "yes" : "no");
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, msos->m->c,
+                  "h2_stream(%ld-%d): readx, len=%ld eos=%d",
+                  msos->m->id, sos->stream->id, (long)*plen, *peos);
     return status;
 }
 
-static apr_status_t h2_sos_mplx_readx(h2_sos *sos, h2_io_data_cb *cb, void *ctx,
-                                      apr_off_t *plen, int *peos)
+static apr_status_t h2_sos_mplx_prepare(h2_sos *sos, apr_off_t *plen, int *peos)
 {
     h2_sos_mplx *msos = sos->ctx;
     apr_status_t status = APR_SUCCESS;
-    apr_table_t *trailers = NULL;
-    const char *src;
     
-    H2_SOS_MPLX_OUT(APLOG_TRACE2, msos, "h2_sos_mplx readx_pre");
-    *peos = 0;
-    if (!APR_BRIGADE_EMPTY(msos->bb)) {
-        apr_off_t origlen = *plen;
-        
-        src = "stream";
-        status = h2_util_bb_readx(msos->bb, cb, ctx, plen, peos);
-        if (status == APR_SUCCESS && !*peos && !*plen) {
-            apr_brigade_cleanup(msos->bb);
-            *plen = origlen;
-            return h2_sos_mplx_readx(sos, cb, ctx, plen, peos);
-        }
-    }
-    else {
-        src = "mplx";
-        status = h2_mplx_out_readx(msos->m, sos->stream->id, 
-                                   cb, ctx, plen, peos, &trailers);
-    }
+    H2_SOS_MPLX_OUT(APLOG_TRACE2, msos, "h2_sos_mplx prepare_pre");
     
-    if (trailers) {
-        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, msos->m->c,
-                      "h2_stream(%ld-%d): readx, saving trailers",
-                      msos->m->id, sos->stream->id);
-        msos->trailers = trailers;
+    if (APR_BRIGADE_EMPTY(msos->bb)) {
+        status = mplx_transfer(msos, sos->stream->id, sos->stream->pool);
     }
+    h2_util_bb_avail(msos->bb, plen, peos);
     
-    if (status == APR_SUCCESS && !*peos && !*plen) {
+    H2_SOS_MPLX_OUT(APLOG_TRACE2, msos, "h2_sos_mplx prepare_post");
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, msos->m->c,
+                  "h2_stream(%ld-%d): prepare, len=%ld eos=%d, trailers=%s",
+                  msos->m->id, sos->stream->id, (long)*plen, *peos,
+                  msos->trailers? "yes" : "no");
+    if (!*peos && !*plen) {
         status = APR_EAGAIN;
     }
     
-    H2_SOS_MPLX_OUT(APLOG_TRACE2, msos, "h2_stream readx_post");
-    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, msos->m->c,
-                  "h2_stream(%ld-%d): readx %s, len=%ld eos=%d",
-                  msos->m->id, sos->stream->id, src, (long)*plen, *peos);
-    
     return status;
 }
 
@@ -642,13 +597,8 @@ static apr_status_t h2_sos_mplx_buffer(h2_sos *sos, apr_bucket_brigade *bb)
     apr_status_t status = APR_SUCCESS;
 
     if (bb && !APR_BRIGADE_EMPTY(bb)) {
-        apr_size_t move_all = INT_MAX;
-        /* we can move file handles from h2_mplx into this h2_stream as many
-         * 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_SOS_MPLX_OUT(APLOG_TRACE2, msos, "h2_sos_mplx set_response_pre");
-        status = h2_util_move(msos->bb, bb, 16 * 1024, &move_all,  
-                              "h2_stream_set_response");
+        status = mplx_transfer(msos, sos->stream->id, sos->stream->pool);
         H2_SOS_MPLX_OUT(APLOG_TRACE2, msos, "h2_sos_mplx set_response_post");
     }
     return status;
@@ -662,14 +612,15 @@ static h2_sos *h2_sos_mplx_create(h2_stream *stream, h2_response *response)
     msos = apr_pcalloc(stream->pool, sizeof(*msos));
     msos->m = stream->session->mplx;
     msos->bb = apr_brigade_create(stream->pool, msos->m->c->bucket_alloc);
-
+    msos->buffer_size = 32 * 1024;
+    
     sos = apr_pcalloc(stream->pool, sizeof(*sos));
     sos->stream = stream;
     sos->response = response;
     
     sos->ctx = msos;
     sos->buffer = h2_sos_mplx_buffer;
-    sos->prep_read = h2_sos_mplx_prep_read;
+    sos->prepare = h2_sos_mplx_prepare;
     sos->readx = h2_sos_mplx_readx;
     sos->read_to = h2_sos_mplx_read_to;
     sos->get_trailers = h2_sos_mplx_get_trailers;
index b7df632502c0fa79748f78ee99b89b35c18c65d6..f0cd2167a3768ba7a10d47b92f307a1aae0d3989 100644 (file)
@@ -43,7 +43,6 @@ typedef struct h2_stream h2_stream;
 
 struct h2_stream {
     int id;                     /* http2 stream id */
-    int initiated_on;           /* http2 stream id this was initiated on or 0 */
     h2_stream_state_t state;    /* http/2 state of this stream */
     struct h2_session *session; /* the session this stream belongs to */
     
@@ -203,8 +202,8 @@ apr_status_t h2_stream_set_response(h2_stream *stream,
  *         APR_EAGAIN if not data is available and end of stream has not been
  *         reached yet.
  */
-apr_status_t h2_stream_prep_read(h2_stream *stream, 
-                                 apr_off_t *plen, int *peos);
+apr_status_t h2_stream_out_prepare(h2_stream *stream, 
+                                   apr_off_t *plen, int *peos);
 
 /**
  * Read data from the stream output.
index 9fa69b2365a1851b4191cd9e43a048832338745c..dff1bcdd573c6bf25439640c04b26da2fdb4c1ca 100644 (file)
@@ -165,7 +165,7 @@ h2_task *h2_task_create(long session_id, const h2_request *req,
         ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, c,
                       APLOGNO(02941) "h2_task(%ld-%d): create stream task", 
                       session_id, req->id);
-        h2_mplx_out_close(mplx, req->id, NULL);
+        h2_mplx_out_close(mplx, req->id);
         return NULL;
     }
     
@@ -173,6 +173,7 @@ h2_task *h2_task_create(long session_id, const h2_request *req,
     task->stream_id   = req->id;
     task->c           = c;
     task->mplx        = mplx;
+    task->c->keepalives = mplx->c->keepalives;
     task->pool        = pool;
     task->request     = req;
     task->input_eos   = !req->body;
index 959398d1d10bbe1e6c79cecd99ddef8d62d8eb1f..80938d1fccaba38e3b56e042721e62dd0f952f4f 100644 (file)
@@ -45,23 +45,6 @@ h2_task_output *h2_task_output_create(h2_task *task, conn_rec *c)
     return output;
 }
 
-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 && response->trailers) {
-            output->trailers_passed = 1;
-            if (h2_task_logio_add_bytes_out) {
-                /* counter trailers as if we'd do a HTTP/1.1 serialization */
-                h2_task_logio_add_bytes_out(output->task->c, 
-                                            h2_util_table_bytes(response->trailers, 3)+1);
-            }
-            return response->trailers;
-        }
-    }
-    return NULL;
-}
-
 static apr_status_t open_response(h2_task_output *output, ap_filter_t *f,
                                   apr_bucket_brigade *bb, const char *caller)
 {
@@ -71,7 +54,7 @@ static apr_status_t open_response(h2_task_output *output, ap_filter_t *f,
         if (f) {
             /* This happens currently when ap_die(status, r) is invoked
              * by a read request filter. */
-            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, output->task->c, APLOGNO(03204)
+            ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, output->task->c, APLOGNO(03204)
                           "h2_task_output(%s): write without response by %s "
                           "for %s %s %s",
                           output->task->id, caller, 
@@ -91,7 +74,6 @@ static apr_status_t open_response(h2_task_output *output, ap_filter_t *f,
         output->written = h2_util_table_bytes(response->headers, 3)+1;
         h2_task_logio_add_bytes_out(output->task->c, output->written);
     }
-    get_trailers(output);
     ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, output->task->c, APLOGNO(03348)
                   "h2_task(%s): open response to %s %s %s",
                   output->task->id, output->task->request->method, 
@@ -113,8 +95,7 @@ static apr_status_t write_brigade_raw(h2_task_output *output,
                   output->task->id, (long)written);
     
     status = h2_mplx_out_write(output->task->mplx, output->task->stream_id, 
-                               f, output->task->blocking, bb, 
-                               get_trailers(output), output->task->io);
+                               f, output->task->blocking, bb, output->task->io);
     if (status == APR_INCOMPLETE) {
         apr_brigade_length(bb, 0, &left);
         written -= left;
index ba396f07ebd0ccdf75147a7a4fbdc45a49f12cb8..3135bc459e1828bf57c000ac179ecb8d19a109b0 100644 (file)
@@ -33,7 +33,6 @@ struct h2_task_output {
     struct h2_from_h1 *from_h1;
     
     unsigned int response_open : 1;
-    unsigned int trailers_passed : 1;
 
     apr_off_t written;
     apr_bucket_brigade *bb;
index 94a2987ca03b0256ebcf1df46a2ad0bfe9c1dda6..06472425f229e9e27e814eb20f4391304d9aba2d 100644 (file)
@@ -697,7 +697,7 @@ apr_status_t h2_util_bb_avail(apr_bucket_brigade *bb,
         return status;
     }
     else if (blen == 0) {
-        /* empty brigade, does it have an EOS bucket somwhere? */
+        /* brigade without data, does it have an EOS bucket somwhere? */
         *plen = 0;
         *peos = h2_util_has_eos(bb, -1);
     }
@@ -861,7 +861,7 @@ void h2_util_bb_log(conn_rec *c, int stream_id, int level,
 
 }
 
-apr_status_t h2_transfer_brigade(apr_bucket_brigade *to,
+apr_status_t h2_ltransfer_brigade(apr_bucket_brigade *to,
                                  apr_bucket_brigade *from, 
                                  apr_pool_t *p,
                                  apr_off_t *plen,
@@ -932,6 +932,92 @@ apr_status_t h2_transfer_brigade(apr_bucket_brigade *to,
     return APR_SUCCESS;
 }
 
+apr_status_t h2_transfer_brigade(apr_bucket_brigade *to,
+                                 apr_bucket_brigade *from, 
+                                 apr_pool_t *p)
+{
+    apr_bucket *e;
+    apr_status_t rv;
+
+    while (!APR_BRIGADE_EMPTY(from)) {
+        e = APR_BRIGADE_FIRST(from);
+        
+        rv = apr_bucket_setaside(e, p);
+        
+        /* If the bucket type does not implement setaside, then
+         * (hopefully) morph it into a bucket type which does, and set
+         * *that* aside... */
+        if (rv == APR_ENOTIMPL) {
+            const char *s;
+            apr_size_t n;
+            
+            rv = apr_bucket_read(e, &s, &n, APR_BLOCK_READ);
+            if (rv == APR_SUCCESS) {
+                rv = apr_bucket_setaside(e, p);
+            }
+        }
+        
+        if (rv != APR_SUCCESS) {
+            /* Return an error but still save the brigade if
+             * ->setaside() is really not implemented. */
+            if (rv != APR_ENOTIMPL) {
+                return rv;
+            }
+        }
+        
+        APR_BUCKET_REMOVE(e);
+        APR_BRIGADE_INSERT_TAIL(to, e);
+    }
+    return APR_SUCCESS;
+}
+
+apr_status_t h2_append_brigade(apr_bucket_brigade *to,
+                               apr_bucket_brigade *from, 
+                               apr_off_t *plen,
+                               int *peos)
+{
+    apr_bucket *e;
+    apr_off_t len = 0, remain = *plen;
+    apr_status_t rv;
+
+    *peos = 0;
+    
+    while (!APR_BRIGADE_EMPTY(from)) {
+        e = APR_BRIGADE_FIRST(from);
+        
+        if (APR_BUCKET_IS_METADATA(e)) {
+            if (APR_BUCKET_IS_EOS(e)) {
+                *peos = 1;
+            }
+        }
+        else {        
+            if (remain > 0 && e->length == ((apr_size_t)-1)) {
+                const char *ign;
+                apr_size_t ilen;
+                rv = apr_bucket_read(e, &ign, &ilen, APR_BLOCK_READ);
+                if (rv != APR_SUCCESS) {
+                    return rv;
+                }
+            }
+            
+            if (remain < e->length) {
+                if (remain <= 0) {
+                    return APR_SUCCESS;
+                }
+                apr_bucket_split(e, remain);
+            }
+        }
+        
+        APR_BUCKET_REMOVE(e);
+        APR_BRIGADE_INSERT_TAIL(to, e);
+        len += e->length;
+        remain -= e->length;
+    }
+    
+    *plen = len;
+    return APR_SUCCESS;
+}
+
 apr_off_t h2_brigade_mem_size(apr_bucket_brigade *bb)
 {
     apr_bucket *b;
index a83f362ffccd010af98ad4c1d61e69943d0737b5..4ca2f9b65b68f9181ca3b120f173a19aa2253346 100644 (file)
@@ -243,18 +243,44 @@ void h2_util_bb_log(conn_rec *c, int stream_id, int level,
 
 /**
  * Transfer buckets from one brigade to another with a limit on the 
- * maximum amount of bytes transfered.
+ * maximum amount of bytes transfered. Sets aside the buckets to
+ * pool p.
  * @param to   brigade to transfer buckets to
  * @param from brigades to remove buckets from
  * @param p    pool that buckets should be setaside to
  * @param plen maximum bytes to transfer, actual bytes transferred
  * @param peos if an EOS bucket was transferred
  */
+apr_status_t h2_ltransfer_brigade(apr_bucket_brigade *to,
+                                  apr_bucket_brigade *from, 
+                                  apr_pool_t *p,
+                                  apr_off_t *plen,
+                                  int *peos);
+
+/**
+ * Transfer all buckets from one brigade to another. Sets aside the buckets to
+ * pool p.
+ * @param to   brigade to transfer buckets to
+ * @param from brigades to remove buckets from
+ * @param p    pool that buckets should be setaside to
+ */
 apr_status_t h2_transfer_brigade(apr_bucket_brigade *to,
                                  apr_bucket_brigade *from, 
-                                 apr_pool_t *p,
-                                 apr_off_t *plen,
-                                 int *peos);
+                                 apr_pool_t *p);
+
+/**
+ * Transfer buckets from one brigade to another with a limit on the 
+ * maximum amount of bytes transfered. Does no setaside magic, lifetime
+ * of brigades must fit. 
+ * @param to   brigade to transfer buckets to
+ * @param from brigades to remove buckets from
+ * @param plen maximum bytes to transfer, actual bytes transferred
+ * @param peos if an EOS bucket was transferred
+ */
+apr_status_t h2_append_brigade(apr_bucket_brigade *to,
+                               apr_bucket_brigade *from, 
+                               apr_off_t *plen,
+                               int *peos);
 
 /**
  * Get an approximnation of the memory footprint of the given
index 1b961c51e7cb8887b8797df51cd3247ba52e10d7..d68130db71a15d59380f7ce93ffd584aceb5e135 100644 (file)
@@ -26,7 +26,7 @@
  * @macro
  * Version number of the http2 module as c string
  */
-#define MOD_HTTP2_VERSION "1.4.5"
+#define MOD_HTTP2_VERSION "1.4.6"
 
 /**
  * @macro
@@ -34,7 +34,7 @@
  * release. This is a 24 bit number with 8 bits for major number, 8 bits
  * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203.
  */
-#define MOD_HTTP2_VERSION_NUM 0x010405
+#define MOD_HTTP2_VERSION_NUM 0x010406
 
 
 #endif /* mod_h2_h2_version_h */
index 6450eb9ea01b900478aae2f408597bc5d348c8a2..0d33969161d6dd7ac617aab57e3d0fca4b7eb6a5 100644 (file)
@@ -15,6 +15,7 @@
 
 #include <apr_optional.h>
 #include <apr_optional_hooks.h>
+#include <apr_strings.h>
 #include <apr_time.h>
 #include <apr_want.h>
 
@@ -198,36 +199,83 @@ static void h2_hooks(apr_pool_t *pool)
     ap_hook_handler(h2_filter_h2_status_handler, NULL, NULL, APR_HOOK_MIDDLE);
 }
 
-static char *value_of_HTTP2(apr_pool_t *p, server_rec *s,
-                              conn_rec *c, request_rec *r)
+static const char *val_HTTP2(apr_pool_t *p, server_rec *s,
+                             conn_rec *c, request_rec *r, h2_ctx *ctx)
 {
-    return c && http2_is_h2(c)? "on" : "off";
+    return ctx? "on" : "off";
 }
 
-static char *value_of_H2PUSH(apr_pool_t *p, server_rec *s,
-                             conn_rec *c, request_rec *r)
+static const char *val_H2_PUSH(apr_pool_t *p, server_rec *s,
+                               conn_rec *c, request_rec *r, h2_ctx *ctx)
 {
-    h2_ctx *ctx;
-    if (r) {
-        ctx = h2_ctx_rget(r);
-        if (ctx) {
+    if (ctx) {
+        if (r) {
             h2_task *task = h2_ctx_get_task(ctx);
-            return (task && task->request->push_policy != H2_PUSH_NONE)? "on" : "off";
+            if (task && task->request->push_policy != H2_PUSH_NONE) {
+                return "on";
+            }
+        }
+        else if (c && h2_session_push_enabled(ctx->session)) {
+            return "on";
         }
-    }
-    else if (c) {
-        ctx = h2_ctx_get(c, 0);
-        return ctx && h2_session_push_enabled(ctx->session)? "on" : "off";
     }
     else if (s) {
         const h2_config *cfg = h2_config_sget(s);
-        return cfg && h2_config_geti(cfg, H2_CONF_PUSH)? "on" : "off";
+        if (cfg && h2_config_geti(cfg, H2_CONF_PUSH)) {
+            return "on";
+        }
     }
     return "off";
 }
 
-typedef char *h2_var_lookup(apr_pool_t *p, server_rec *s,
-                             conn_rec *c, request_rec *r);
+static const char *val_H2_PUSHED(apr_pool_t *p, server_rec *s,
+                                 conn_rec *c, request_rec *r, h2_ctx *ctx)
+{
+    if (ctx) {
+        h2_task *task = h2_ctx_get_task(ctx);
+        if (task && !H2_STREAM_CLIENT_INITIATED(task->stream_id)) {
+            return "PUSHED";
+        }
+    }
+    return "";
+}
+
+static const char *val_H2_PUSHED_ON(apr_pool_t *p, server_rec *s,
+                                    conn_rec *c, request_rec *r, h2_ctx *ctx)
+{
+    if (ctx) {
+        h2_task *task = h2_ctx_get_task(ctx);
+        if (task && !H2_STREAM_CLIENT_INITIATED(task->stream_id)) {
+            return apr_itoa(p, task->request->initiated_on);
+        }
+    }
+    return "";
+}
+
+static const char *val_H2_STREAM_TAG(apr_pool_t *p, server_rec *s,
+                                     conn_rec *c, request_rec *r, h2_ctx *ctx)
+{
+    if (ctx) {
+        h2_task *task = h2_ctx_get_task(ctx);
+        if (task) {
+            return task->id;
+        }
+    }
+    return "";
+}
+
+static const char *val_H2_STREAM_ID(apr_pool_t *p, server_rec *s,
+                                    conn_rec *c, request_rec *r, h2_ctx *ctx)
+{
+    const char *cp = val_H2_STREAM_TAG(p, s, c, r, ctx);
+    if (cp && (cp = ap_strchr_c(cp, '-'))) {
+        return ++cp;
+    }
+    return NULL;
+}
+
+typedef const char *h2_var_lookup(apr_pool_t *p, server_rec *s,
+                                  conn_rec *c, request_rec *r, h2_ctx *ctx);
 typedef struct h2_var_def {
     const char *name;
     h2_var_lookup *lookup;
@@ -235,8 +283,13 @@ typedef struct h2_var_def {
 } h2_var_def;
 
 static h2_var_def H2_VARS[] = {
-    { "HTTP2",     value_of_HTTP2,  1 },
-    { "H2PUSH",    value_of_H2PUSH, 1 },
+    { "HTTP2",               val_HTTP2,  1 },
+    { "H2PUSH",              val_H2_PUSH, 1 },
+    { "H2_PUSH",             val_H2_PUSH, 1 },
+    { "H2_PUSHED",           val_H2_PUSHED, 1 },
+    { "H2_PUSHED_ON",        val_H2_PUSHED_ON, 1 },
+    { "H2_STREAM_ID",        val_H2_STREAM_ID, 1 },
+    { "H2_STREAM_TAG",       val_H2_STREAM_TAG, 1 },
 };
 
 #ifndef H2_ALEN
@@ -257,7 +310,9 @@ static char *http2_var_lookup(apr_pool_t *p, server_rec *s,
     for (i = 0; i < H2_ALEN(H2_VARS); ++i) {
         h2_var_def *vdef = &H2_VARS[i];
         if (!strcmp(vdef->name, name)) {
-            return vdef->lookup(p, s, c, r);
+            h2_ctx *ctx = (r? h2_ctx_rget(r) : 
+                           h2_ctx_get(c->master? c->master : c, 0));
+            return (char *)vdef->lookup(p, s, c, r, ctx);
         }
     }
     return "";
@@ -273,7 +328,8 @@ static int h2_h2_fixups(request_rec *r)
             h2_var_def *vdef = &H2_VARS[i];
             if (vdef->subprocess) {
                 apr_table_setn(r->subprocess_env, vdef->name, 
-                               vdef->lookup(r->pool, r->server, r->connection, r));
+                               vdef->lookup(r->pool, r->server, r->connection, 
+                                            r, ctx));
             }
         }
     }