]> granicus.if.org Git - apache/commitdiff
improved h2c write performance
authorStefan Eissing <icing@apache.org>
Fri, 30 Oct 2015 16:15:40 +0000 (16:15 +0000)
committerStefan Eissing <icing@apache.org>
Fri, 30 Oct 2015 16:15:40 +0000 (16:15 +0000)
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1711503 13f79535-47bb-0310-9956-ffa450edef68

12 files changed:
modules/http2/h2_conn_io.c
modules/http2/h2_conn_io.h
modules/http2/h2_io.c
modules/http2/h2_io.h
modules/http2/h2_mplx.c
modules/http2/h2_mplx.h
modules/http2/h2_session.c
modules/http2/h2_stream.c
modules/http2/h2_stream.h
modules/http2/h2_task_input.c
modules/http2/h2_util.c
modules/http2/h2_util.h

index 370051b40656e568af5ead6594baaa7161693b5a..9fa42207a5be91cd6ec26f4c9e5c06a85f8dbbeb 100644 (file)
@@ -323,6 +323,16 @@ apr_status_t h2_conn_io_write(h2_conn_io *io,
     return status;
 }
 
+apr_status_t h2_conn_io_append(h2_conn_io *io, apr_bucket *b)
+{
+    APR_BRIGADE_INSERT_TAIL(io->output, b);
+    return APR_SUCCESS;
+}
+
+apr_status_t h2_conn_io_pass(h2_conn_io *io, apr_bucket_brigade *bb)
+{
+    return h2_util_move(io->output, bb, 0, NULL, "h2_conn_io_pass");
+}
 
 apr_status_t h2_conn_io_flush(h2_conn_io *io)
 {
index 250fa33ced10d4b271e33c67a85272ed61cfce77..342f008ed632919d95788c11d344012142004416 100644 (file)
@@ -60,6 +60,9 @@ apr_status_t h2_conn_io_read(h2_conn_io *io,
 apr_status_t h2_conn_io_write(h2_conn_io *io,
                          const char *buf,
                          size_t length);
+                         
+apr_status_t h2_conn_io_append(h2_conn_io *io, apr_bucket *b);
+apr_status_t h2_conn_io_pass(h2_conn_io *io, apr_bucket_brigade *bb);
 
 apr_status_t h2_conn_io_flush(h2_conn_io *io);
 
index 9825a18d5f2bb38dd68211ea603a4caefcbb26c9..119d480554ed63a61b40304820b35d26da5c0d73 100644 (file)
@@ -101,8 +101,7 @@ apr_status_t h2_io_in_read(h2_io *io, apr_bucket_brigade *bb,
     
     apr_brigade_length(bb, 1, &start_len);
     last = APR_BRIGADE_LAST(bb);
-    status = h2_util_move(bb, io->bbin, maxlen, 0, 
-                                       "h2_io_in_read");
+    status = h2_util_move(bb, io->bbin, maxlen, NULL, "h2_io_in_read");
     if (status == APR_SUCCESS) {
         apr_bucket *nlast = APR_BRIGADE_LAST(bb);
         apr_off_t end_len = 0;
@@ -130,7 +129,7 @@ apr_status_t h2_io_in_write(h2_io *io, apr_bucket_brigade *bb)
             io->bbin = apr_brigade_create(io->bbout->p, 
                                           io->bbout->bucket_alloc);
         }
-        return h2_util_move(io->bbin, bb, 0, 0, "h2_io_in_write");
+        return h2_util_move(io->bbin, bb, 0, NULL, "h2_io_in_write");
     }
     return APR_SUCCESS;
 }
@@ -179,6 +178,24 @@ apr_status_t h2_io_out_readx(h2_io *io,
     return status;
 }
 
+apr_status_t h2_io_out_read_to(h2_io *io, apr_bucket_brigade *bb, 
+                               apr_size_t *plen, int *peos)
+{
+    if (io->rst_error) {
+        return APR_ECONNABORTED;
+    }
+    
+    if (io->eos_out) {
+        *plen = 0;
+        *peos = 1;
+        return APR_SUCCESS;
+    }
+    
+
+    io->eos_out = *peos = h2_util_has_eos(io->bbout, *plen);
+    return h2_util_move(bb, io->bbout, *plen, NULL, "h2_io_read_to");
+}
+
 apr_status_t h2_io_out_write(h2_io *io, apr_bucket_brigade *bb, 
                              apr_size_t maxlen, int *pfile_handles_allowed)
 {
index 733d30319b7eb92bda9ffd1ecbfe80e6901b489d..c54c61e2bc55dca209e3b6177fd538cf521342f7 100644 (file)
@@ -120,6 +120,10 @@ apr_status_t h2_io_out_readx(h2_io *io,
                              h2_io_data_cb *cb, void *ctx, 
                              apr_size_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_status_t h2_io_out_write(h2_io *io, apr_bucket_brigade *bb, 
                              apr_size_t maxlen, int *pfile_buckets_allowed);
 
index bcb1c3840f687dcd72a750b8986c051d62767fac..754ad57645b916357171fce9b3abc8a863b7c6b3 100644 (file)
@@ -493,6 +493,36 @@ apr_status_t h2_mplx_out_readx(h2_mplx *m, int stream_id,
     return status;
 }
 
+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_status_t status;
+    AP_DEBUG_ASSERT(m);
+    if (m->aborted) {
+        return APR_ECONNABORTED;
+    }
+    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) {
+            H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_readx_pre");
+            
+            status = h2_io_out_read_to(io, bb, plen, peos);
+            
+            H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_readx_post");
+            if (status == APR_SUCCESS && io->output_drained) {
+                apr_thread_cond_signal(io->output_drained);
+            }
+        }
+        else {
+            status = APR_ECONNABORTED;
+        }
+        apr_thread_mutex_unlock(m->lock);
+    }
+    return status;
+}
+
 h2_stream *h2_mplx_next_submit(h2_mplx *m, h2_stream_set *streams)
 {
     apr_status_t status;
index d3966d0b66c0f26afd3721a7a3d4a3007086322b..40c715a386d14e4cd46df3626f706d8a36de56fd 100644 (file)
@@ -239,6 +239,14 @@ 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);
 
+/**
+ * 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_size_t *plen, int *peos);
+
 /**
  * Opens the output for the given stream with the specified response.
  */
index ca3da31dea4cd0224048aa436a1be382edbf2c7f..93962def20285a92bba6ac4c9442d0f41d14c0aa 100644 (file)
@@ -141,9 +141,6 @@ static apr_status_t stream_end_headers(h2_session *session,
     return h2_stream_schedule(stream, eos, stream_pri_cmp, session);
 }
 
-static apr_status_t send_data(h2_session *session, const char *data, 
-                              apr_size_t length);
-
 /*
  * Callback when nghttp2 wants to send bytes back to the client.
  */
@@ -152,10 +149,11 @@ static ssize_t send_cb(nghttp2_session *ngh2,
                        int flags, void *userp)
 {
     h2_session *session = (h2_session *)userp;
-    apr_status_t status = send_data(session, (const char *)data, length);
+    apr_status_t status;
     
     (void)ngh2;
     (void)flags;
+    status = h2_conn_io_write(&session->io, (const char *)data, length);
     if (status == APR_SUCCESS) {
         return length;
     }
@@ -518,18 +516,14 @@ static int on_frame_recv_cb(nghttp2_session *ng2s,
     return 0;
 }
 
-static apr_status_t send_data(h2_session *session, const char *data, 
-                              apr_size_t length)
-{
-    return h2_conn_io_write(&session->io, data, length);
-}
-
 static apr_status_t pass_data(void *ctx, 
                               const char *data, apr_size_t length)
 {
-    return send_data((h2_session*)ctx, data, length);
+    return h2_conn_io_write(&((h2_session*)ctx)->io, data, length);
 }
 
+static char immortal_zeros[256];
+
 static int on_send_data_cb(nghttp2_session *ngh2, 
                            nghttp2_frame *frame, 
                            const uint8_t *framehd, 
@@ -563,30 +557,59 @@ static int on_send_data_cb(nghttp2_session *ngh2,
                   "h2_stream(%ld-%d): send_data_cb for %ld bytes",
                   session->id, (int)stream_id, (long)length);
                   
-    status = send_data(session, (const char *)framehd, 9);
-    if (status == APR_SUCCESS) {
+    if (h2_conn_io_is_buffered(&session->io)) {
+        status = h2_conn_io_write(&session->io, (const char *)framehd, 9);
+        if (status == APR_SUCCESS) {
+            if (padlen) {
+                status = h2_conn_io_write(&session->io, (const char *)&padlen, 1);
+            }
+            
+            if (status == APR_SUCCESS) {
+                apr_size_t len = length;
+                status = h2_stream_readx(stream, pass_data, session, 
+                                         &len, &eos);
+                if (status == APR_SUCCESS && len != length) {
+                    status = APR_EINVAL;
+                }
+            }
+            
+            if (status == APR_SUCCESS && padlen) {
+                if (padlen) {
+                    char pad[256];
+                    memset(pad, 0, padlen);
+                    status = h2_conn_io_write(&session->io, pad, padlen);
+                }
+            }
+        }
+    }
+    else {
+        apr_bucket *b;
+        char *header = apr_pcalloc(stream->pool, 10);
+        memcpy(header, (const char *)framehd, 9);
         if (padlen) {
-            status = send_data(session, (const char *)&padlen, 1);
+            header[9] = (char)padlen;
         }
-
+        b = apr_bucket_pool_create(header, padlen? 10 : 9, 
+                                   stream->pool, session->c->bucket_alloc);
+        status = h2_conn_io_append(&session->io, b);
+        
         if (status == APR_SUCCESS) {
             apr_size_t len = length;
-            status = h2_stream_readx(stream, pass_data, session, 
-                                     &len, &eos);
+            status = h2_stream_read_to(stream, session->io.output, &len, &eos);
+            session->io.unflushed = 1;
             if (status == APR_SUCCESS && len != length) {
                 status = APR_EINVAL;
             }
         }
-        
+            
         if (status == APR_SUCCESS && padlen) {
-            if (padlen) {
-                char pad[256];
-                memset(pad, 0, padlen);
-                status = send_data(session, pad, padlen);
-            }
+            b = apr_bucket_immortal_create(immortal_zeros, padlen, 
+                                           session->c->bucket_alloc);
+            status = h2_conn_io_append(&session->io, b);
         }
     }
     
+    
     if (status == APR_SUCCESS) {
         return 0;
     }
@@ -1145,7 +1168,7 @@ static apr_status_t copy_buffer(void *ctx, const char *data, apr_size_t len)
     ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c->session->c,
                   "h2_stream(%ld-%d): copy %ld bytes for DATA #%ld",
                   c->session->id, c->stream->id, 
-                  (long)c->stream->data_frames_sent, (long)len);
+                  (long)len, (long)c->stream->data_frames_sent);
     memcpy(c->buf + c->offset, data, len);
     c->offset += len;
     return APR_SUCCESS;
@@ -1180,7 +1203,7 @@ static ssize_t stream_data_cb(nghttp2_session *ng2s,
     
     AP_DEBUG_ASSERT(!h2_stream_is_suspended(stream));
     
-    if (h2_conn_io_is_buffered(&session->io)) {
+    if (1 || h2_conn_io_is_buffered(&session->io)) {
         status = h2_stream_prep_read(stream, &nread, &eos);
         if (nread) {
             *data_flags |=  NGHTTP2_DATA_FLAG_NO_COPY;
index a2c53a1891c2d5ead7b08781edba642692dcfbf8..b67a84b129049f564efdf39d0c48cec110597f7d 100644 (file)
@@ -108,12 +108,15 @@ apr_status_t h2_stream_set_response(h2_stream *stream, h2_response *response,
     
     stream->response = response;
     if (bb && !APR_BRIGADE_EMPTY(bb)) {
+        int move_all = INT_MAX;
         if (!stream->bbout) {
             stream->bbout = apr_brigade_create(stream->pool, 
                                                stream->m->c->bucket_alloc);
         }
-        /* TODO: this does not move complete file buckets.*/
-        status = h2_util_move(stream->bbout, bb, 16 * 1024, NULL,  
+        /* 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. */
+        status = h2_util_move(stream->bbout, bb, 16 * 1024, &move_all,  
                               "h2_stream_set_response");
     }
     if (APLOGctrace1(stream->m->c)) {
@@ -307,6 +310,28 @@ apr_status_t h2_stream_readx(h2_stream *stream,
     return status;
 }
 
+apr_status_t h2_stream_read_to(h2_stream *stream, apr_bucket_brigade *bb, 
+                               apr_size_t *plen, int *peos)
+{
+    apr_status_t status = APR_SUCCESS;
+
+    if (stream->rst_error) {
+        return APR_ECONNRESET;
+    }
+    if (stream->bbout && !APR_BRIGADE_EMPTY(stream->bbout)) {
+        status = h2_transfer_brigade(bb, stream->bbout, bb->p, plen, peos);
+    }
+    else {
+        status = h2_mplx_out_read_to(stream->m, stream->id, bb, plen, peos);
+    }
+    if (status == APR_SUCCESS && !*peos && !*plen) {
+        status = APR_EAGAIN;
+    }
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, stream->m->c,
+                  "h2_stream(%ld-%d): read_to, len=%ld eos=%d",
+                  stream->m->id, stream->id, (long)*plen, *peos);
+    return status;
+}
 
 void h2_stream_set_suspended(h2_stream *stream, int suspended)
 {
index 35348b7a17ec54973a46bf2945183128a164ea84..f4dc05537a1e1361f4738389180dee1f0dbabf84 100644 (file)
@@ -105,6 +105,10 @@ apr_status_t h2_stream_prep_read(h2_stream *stream,
 apr_status_t h2_stream_readx(h2_stream *stream, h2_io_data_cb *cb, 
                              void *ctx, apr_size_t *plen, int *peos);
 
+apr_status_t h2_stream_read_to(h2_stream *stream, apr_bucket_brigade *bb, 
+                               apr_size_t *plen, int *peos);
+
+
 void h2_stream_set_suspended(h2_stream *stream, int suspended);
 int h2_stream_is_suspended(h2_stream *stream);
 
index 616be156bb72910d4fd63bbb1618474e0671a635..cd6392e78587afa26edc99df7ea1cf03b2990f09 100644 (file)
@@ -176,11 +176,11 @@ apr_status_t h2_task_input_read(h2_task_input *input,
     if (!APR_BRIGADE_EMPTY(input->bb)) {
         if (mode == AP_MODE_EXHAUSTIVE) {
             /* return all we have */
-            return h2_util_move(bb, input->bb, readbytes, 0
+            return h2_util_move(bb, input->bb, readbytes, NULL
                                 "task_input_read(exhaustive)");
         }
         else if (mode == AP_MODE_READBYTES) {
-            return h2_util_move(bb, input->bb, readbytes, 0
+            return h2_util_move(bb, input->bb, readbytes, NULL
                                 "task_input_read(readbytes)");
         }
         else if (mode == AP_MODE_SPECULATIVE) {
index ac6d59482a73e961c904f64c2d150dfac1147830..0b4a6f446e90c3a55258a0cd4a9dbb80a60bd3f3 100644 (file)
@@ -718,3 +718,75 @@ void h2_util_bb_log(conn_rec *c, int stream_id, int level,
                   c->id, stream_id, tag, line);
 
 }
+
+AP_DECLARE(apr_status_t) h2_transfer_brigade(apr_bucket_brigade *to,
+                                             apr_bucket_brigade *from, 
+                                             apr_pool_t *p,
+                                             apr_size_t *plen,
+                                             int *peos)
+{
+    apr_bucket *e;
+    apr_size_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);
+            }
+        }
+        
+        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);
+        len += e->length;
+        remain -= e->length;
+    }
+    
+    *plen = len;
+    return APR_SUCCESS;
+}
+
index e1a6b3c4d2039e33217826fe9eec346f82b01d08..a488e2a041eeabeb317c91a579446c0baeb62be1 100644 (file)
@@ -145,4 +145,19 @@ apr_status_t h2_util_bb_readx(apr_bucket_brigade *bb,
 void h2_util_bb_log(conn_rec *c, int stream_id, int level, 
                     const char *tag, apr_bucket_brigade *bb);
 
+/**
+ * Transfer buckets from one brigade to another with a limit on the 
+ * maximum amount of bytes transfered.
+ * @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
+ */
+AP_DECLARE(apr_status_t) h2_transfer_brigade(apr_bucket_brigade *to,
+                                             apr_bucket_brigade *from, 
+                                             apr_pool_t *p,
+                                             apr_size_t *plen,
+                                             int *peos);
+
 #endif /* defined(__mod_h2__h2_util__) */