]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
mod_http2, version 1.0.7
authorStefan Eissing <icing@apache.org>
Tue, 24 Nov 2015 16:58:31 +0000 (16:58 +0000)
committerStefan Eissing <icing@apache.org>
Tue, 24 Nov 2015 16:58:31 +0000 (16:58 +0000)
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1716210 13f79535-47bb-0310-9956-ffa450edef68

32 files changed:
CHANGES
modules/http2/config.m4
modules/http2/h2_bucket_eoc.c
modules/http2/h2_config.c
modules/http2/h2_conn.c
modules/http2/h2_conn_io.c
modules/http2/h2_conn_io.h
modules/http2/h2_from_h1.c
modules/http2/h2_from_h1.h
modules/http2/h2_h2.c
modules/http2/h2_h2.h
modules/http2/h2_io.c
modules/http2/h2_io.h
modules/http2/h2_io_set.c
modules/http2/h2_io_set.h
modules/http2/h2_mplx.c
modules/http2/h2_mplx.h
modules/http2/h2_push.c
modules/http2/h2_push.h
modules/http2/h2_request.c
modules/http2/h2_request.h
modules/http2/h2_response.c
modules/http2/h2_response.h
modules/http2/h2_session.c
modules/http2/h2_session.h
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_version.h
modules/http2/h2_worker.c

diff --git a/CHANGES b/CHANGES
index f3f5c1bb03529838b3a069c2f0046048e72a4bf2..eb50dcbf3c394dadbecc84a9ef82c63cbf136b70 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -2,6 +2,14 @@
 
 Changes with Apache 2.4.18
 
+  *) mod_http2: fixes crash on connection abort for a busy connection.
+     fixes crash on a request that did not produce any response.
+     [Stefan Eissing]
+
+  *) mod_http2: trailers are sent after reponse body if set in request_rec
+     trailers_out before the end-of-request bucket is sent through the 
+     output filters. [Stefan Eissing]
+
   *) mod_http2: incoming trailers (headers after request body) are properly
      forwarded to the processing engine. [Stefan Eissing]
 
index f383b3cd83853f79163b0271bffc6169222d028b..e10bb8158f7b4523b9d0e40bc22c0b19ab35a331 100644 (file)
@@ -154,6 +154,8 @@ AC_DEFUN([APACHE_CHECK_NGHTTP2],[
       if test "x$liberrors" != "x"; then
         AC_MSG_WARN([nghttp2 library is unusable])
       fi
+      AC_CHECK_FUNCS([nghttp2_session_change_stream_priority], 
+        [APR_ADDTO(MOD_CPPFLAGS, ["-DH2_NG2_CHANGE_PRIO"])], [])
     else
       AC_MSG_WARN([nghttp2 version is too old])
     fi
index 8b145cf29edf778b2ef978fa1839c17cf1ce4b2c..3ddb54d68a9d44b79ed3455e14628b021bc17810 100644 (file)
@@ -90,10 +90,11 @@ static void bucket_destroy(void *data)
 
     if (apr_bucket_shared_destroy(h)) {
         h2_session *session = h->session;
+        apr_bucket_free(h);
         if (session) {
             h2_session_eoc_callback(session);
+            /* all is gone now */
         }
-        apr_bucket_free(h);
     }
 }
 
index 7ac4297b32868649099a88d79ec7b6b8a6e22ab4..7dc0b20d20d34649f6f66cae12f2bda9fb467a03 100644 (file)
@@ -43,7 +43,7 @@ static h2_config defconf = {
     H2_INITIAL_WINDOW_SIZE, /* window_size */
     -1,                     /* min workers */
     -1,                     /* max workers */
-    10 * 60,                /* max workers idle secs */
+    10,                     /* max workers idle secs */
     64 * 1024,              /* stream max mem size */
     NULL,                   /* no alt-svcs */
     -1,                     /* alt-svc max age */
index d4f56c66306846cce865134cdaefef7ce21764e9..6fec75ea9a08daa9819c862ac6e698b51da834a2 100644 (file)
@@ -177,10 +177,6 @@ apr_status_t h2_conn_process(conn_rec *c, request_rec *r)
 
     ap_log_cerror( APLOG_MARK, APLOG_DEBUG, status, session->c,
                   "h2_session(%ld): done", session->id);
-    h2_session_close(session);
-    h2_session_flush(session);
-    /* hereafter session might be gone */
-    
     /* Make sure this connection gets closed properly. */
     ap_update_child_status_from_conn(c->sbh, SERVER_CLOSING, c);
     c->keepalive = AP_CONN_CLOSE;
@@ -188,6 +184,8 @@ apr_status_t h2_conn_process(conn_rec *c, request_rec *r)
         c->cs->state = CONN_STATE_WRITE_COMPLETION;
     }
 
+    h2_session_close(session);
+    /* hereafter session will be gone */
     return status;
 }
 
index aa8d4d580285df74038cd74805afb9ca1e6f63db..485a8bd47ea4e8f03ba947d5b042ad99ad3c1b98 100644 (file)
@@ -23,6 +23,7 @@
 #include <http_connection.h>
 
 #include "h2_private.h"
+#include "h2_bucket_eoc.h"
 #include "h2_config.h"
 #include "h2_conn_io.h"
 #include "h2_h2.h"
 
 #define WRITE_BUFFER_SIZE     (8*WRITE_SIZE_MAX)
 
-apr_status_t h2_conn_io_init(h2_conn_io *io, conn_rec *c)
+apr_status_t h2_conn_io_init(h2_conn_io *io, conn_rec *c, apr_pool_t *pool)
 {
     h2_config *cfg = h2_config_get(c);
     
     io->connection         = c;
-    io->input              = apr_brigade_create(c->pool, c->bucket_alloc);
-    io->output             = apr_brigade_create(c->pool, c->bucket_alloc);
+    io->input              = apr_brigade_create(pool, c->bucket_alloc);
+    io->output             = apr_brigade_create(pool, c->bucket_alloc);
     io->buflen             = 0;
     io->is_tls             = h2_h2_is_tls(c);
     io->buffer_output      = io->is_tls;
     
     if (io->buffer_output) {
         io->bufsize = WRITE_BUFFER_SIZE;
-        io->buffer = apr_pcalloc(c->pool, io->bufsize);
+        io->buffer = apr_pcalloc(pool, io->bufsize);
     }
     else {
         io->bufsize = 0;
@@ -115,6 +116,8 @@ static apr_status_t h2_conn_io_bucket_read(h2_conn_io *io,
                                      &bucket_length, block);
             
             if (status == APR_SUCCESS && bucket_length > 0) {
+                apr_size_t consumed = 0;
+
                 if (APLOGctrace2(io->connection)) {
                     char buffer[32];
                     h2_util_hex_dump(buffer, sizeof(buffer)/sizeof(buffer[0]),
@@ -124,20 +127,18 @@ static apr_status_t h2_conn_io_bucket_read(h2_conn_io *io,
                                   io->connection->id, (int)bucket_length, buffer);
                 }
                 
-                if (bucket_length > 0) {
-                    apr_size_t consumed = 0;
-                    status = on_read_cb(bucket_data, bucket_length,
-                                        &consumed, pdone, puser);
-                    if (status == APR_SUCCESS && bucket_length > consumed) {
-                        /* We have data left in the bucket. Split it. */
-                        status = apr_bucket_split(bucket, consumed);
-                    }
-                    readlen += consumed;
+                status = on_read_cb(bucket_data, bucket_length, &consumed, 
+                                    pdone, puser);
+                if (status == APR_SUCCESS && bucket_length > consumed) {
+                    /* We have data left in the bucket. Split it. */
+                    status = apr_bucket_split(bucket, consumed);
                 }
+                readlen += consumed;
             }
         }
         apr_bucket_delete(bucket);
     }
+    
     if (readlen == 0 && status == APR_SUCCESS && block == APR_NONBLOCK_READ) {
         return APR_EAGAIN;
     }
@@ -158,10 +159,10 @@ apr_status_t h2_conn_io_read(h2_conn_io *io,
         /* Seems something is left from a previous read, lets
          * satisfy our caller with the data we already have. */
         status = h2_conn_io_bucket_read(io, block, on_read_cb, puser, &done);
+        apr_brigade_cleanup(io->input);
         if (status != APR_SUCCESS || done) {
             return status;
         }
-        apr_brigade_cleanup(io->input);
     }
 
     /* We only do a blocking read when we have no streams to process. So,
@@ -179,6 +180,9 @@ apr_status_t h2_conn_io_read(h2_conn_io *io,
         ap_update_child_status(io->connection->sbh, SERVER_BUSY_READ, NULL);
     }
 
+    /* TODO: replace this with a connection filter itself, so that we
+     * no longer need to transfer incoming buckets to our own brigade. 
+     */
     status = ap_get_brigade(io->connection->input_filters,
                             io->input, AP_MODE_READBYTES,
                             block, 64 * 4096);
@@ -379,4 +383,19 @@ apr_status_t h2_conn_io_flush(h2_conn_io *io)
 apr_status_t h2_conn_io_pass(h2_conn_io *io)
 {
     return h2_conn_io_flush_int(io, 0);
+}
+
+apr_status_t h2_conn_io_close(h2_conn_io *io, void *session)
+{
+    apr_bucket *b;
+
+    /* Send out anything in our buffers */
+    h2_conn_io_flush_int(io, 0);
+    
+    b = h2_bucket_eoc_create(io->connection->bucket_alloc, session);
+    APR_BRIGADE_INSERT_TAIL(io->output, b);
+    b = apr_bucket_flush_create(io->connection->bucket_alloc);
+    APR_BRIGADE_INSERT_TAIL(io->output, b);
+    return ap_pass_brigade(io->connection->output_filters, io->output);
+    /* and all is gone */
 }
\ No newline at end of file
index 4406261a33b1f594428582cfbe34c4255f869c92..a0dd0d0e5caf3ffaca6ad23ac3a73af336083bfd 100644 (file)
@@ -42,7 +42,7 @@ typedef struct {
     int unflushed;
 } h2_conn_io;
 
-apr_status_t h2_conn_io_init(h2_conn_io *io, conn_rec *c);
+apr_status_t h2_conn_io_init(h2_conn_io *io, conn_rec *c, apr_pool_t *pool);
 
 int h2_conn_io_is_buffered(h2_conn_io *io);
 
@@ -65,5 +65,6 @@ apr_status_t h2_conn_io_consider_flush(h2_conn_io *io);
 
 apr_status_t h2_conn_io_pass(h2_conn_io *io);
 apr_status_t h2_conn_io_flush(h2_conn_io *io);
+apr_status_t h2_conn_io_close(h2_conn_io *io, void *session);
 
 #endif /* defined(__mod_h2__h2_conn_io__) */
index 43a4f0822b3ced81769dba92de441bb832995c04..755b7d6815b65ec0bb9bbee94e8983750e201dfc 100644 (file)
@@ -51,10 +51,6 @@ h2_from_h1 *h2_from_h1_create(int stream_id, apr_pool_t *pool)
 
 apr_status_t h2_from_h1_destroy(h2_from_h1 *from_h1)
 {
-    if (from_h1->response) {
-        h2_response_destroy(from_h1->response);
-        from_h1->response = NULL;
-    }
     from_h1->bb = NULL;
     return APR_SUCCESS;
 }
@@ -520,7 +516,7 @@ apr_status_t h2_response_output_filter(ap_filter_t *f, apr_bucket_brigade *bb)
     if (eb) {
         int st = eb->status;
         apr_brigade_cleanup(bb);
-        ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, f->c,
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c,
                       "h2_from_h1(%d): err bucket status=%d", 
                       from_h1->stream_id, st);
         ap_die(st, r);
@@ -555,3 +551,38 @@ apr_status_t h2_response_output_filter(ap_filter_t *f, apr_bucket_brigade *bb)
     }
     return ap_pass_brigade(f->next, bb);
 }
+
+apr_status_t h2_response_trailers_filter(ap_filter_t *f, apr_bucket_brigade *bb)
+{
+    h2_task *task = f->ctx;
+    h2_from_h1 *from_h1 = task->output? task->output->from_h1 : NULL;
+    request_rec *r = f->r;
+    apr_bucket *b;
+    if (from_h1 && from_h1->response) {
+        /* Detect the EOR bucket and forward any trailers that may have
+         * been set to our h2_response.
+         */
+        for (b = APR_BRIGADE_FIRST(bb);
+             b != APR_BRIGADE_SENTINEL(bb);
+             b = APR_BUCKET_NEXT(b))
+        {
+            if (AP_BUCKET_IS_EOR(b)) {
+                /* FIXME: need a better test case than this.
+                apr_table_setn(r->trailers_out, "X", "1"); */
+                if (r->trailers_out && !apr_is_empty_table(r->trailers_out)) {
+                    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c,
+                                  "h2_from_h1(%d): trailers filter, saving trailers",
+                                  from_h1->stream_id);
+                    h2_response_set_trailers(from_h1->response,
+                                             apr_table_clone(from_h1->pool, 
+                                                             r->trailers_out));
+                }
+                break;
+            }
+        }     
+    }
+     
+    return ap_pass_brigade(f->next, bb);
+}
+
index 4f5ebad618b2db8142ea56b4dd5486de93632e31..cdd38ca605f248a1c844d12f88f2c493513c1e03 100644 (file)
@@ -69,4 +69,6 @@ struct h2_response *h2_from_h1_get_response(h2_from_h1 *from_h1);
 
 apr_status_t h2_response_output_filter(ap_filter_t *f, apr_bucket_brigade *bb);
 
+apr_status_t h2_response_trailers_filter(ap_filter_t *f, apr_bucket_brigade *bb);
+
 #endif /* defined(__mod_h2__h2_from_h1__) */
index 54fe9e0fa0a5631367b8c19c01b829fba8ef04db..e48e64e8a467f2d0acf709141f12a9309cc9ff73 100644 (file)
@@ -667,16 +667,17 @@ static int h2_h2_post_read_req(request_rec *r)
         ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, r,
                       "adding h1_to_h2_resp output filter");
         if (task->serialize_headers) {
-            ap_remove_output_filter_byhandle(r->output_filters, "H1_TO_H2_RESP");
+/*            ap_remove_output_filter_byhandle(r->output_filters, "H1_TO_H2_RESP");*/
             ap_add_output_filter("H1_TO_H2_RESP", task, r, r->connection);
         }
         else {
             /* replace the core http filter that formats response headers
              * in HTTP/1 with our own that collects status and headers */
             ap_remove_output_filter_byhandle(r->output_filters, "HTTP_HEADER");
-            ap_remove_output_filter_byhandle(r->output_filters, "H2_RESPONSE");
+/*            ap_remove_output_filter_byhandle(r->output_filters, "H2_RESPONSE");*/
             ap_add_output_filter("H2_RESPONSE", task, r, r->connection);
         }
+        ap_add_output_filter("H2_TRAILERS", task, r, r->connection);
     }
     return DECLINED;
 }
index 4974d86611550f3e1156fa9bb038a921d3c136f7..563abe3fded1654bfc6f8bb6eccfc8ca9644dd78 100644 (file)
@@ -58,6 +58,17 @@ extern const char *H2_MAGIC_TOKEN;
 
 #define H2_STREAM_CLIENT_INITIATED(id)      (id&0x01)
 
+typedef enum {
+    H2_DEPENDANT_AFTER,
+    H2_DEPENDANT_INTERLEAVED,
+    H2_DEPENDANT_BEFORE,
+} h2_dependency;
+
+typedef struct h2_priority {
+    h2_dependency dependency;
+    int           weight;
+} h2_priority;
+
 /**
  * Provide a user readable description of the HTTP/2 error code-
  * @param h2_error http/2 error code, as in rfc 7540, ch. 7
index b33faee1f3d94d009dfb7416c5a878ac32faf4a5..6bd2b83739e9619a3e0b0b2b0383d429a2fa72dc 100644 (file)
@@ -52,7 +52,7 @@ 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);
+    io->response = h2_response_clone(io->pool, response);
     if (response->rst_error) {
         h2_io_rst(io, response->rst_error);
     }
@@ -205,8 +205,17 @@ apr_status_t h2_io_out_read_to(h2_io *io, apr_bucket_brigade *bb,
     return h2_util_move(bb, io->bbout, *plen, NULL, "h2_io_read_to");
 }
 
+static void process_trailers(h2_io *io, apr_table_t *trailers)
+{
+    if (trailers && io->response) {
+        h2_response_set_trailers(io->response, 
+                                 apr_table_clone(io->pool, trailers));
+    }
+}
+
 apr_status_t h2_io_out_write(h2_io *io, apr_bucket_brigade *bb, 
-                             apr_size_t maxlen, int *pfile_handles_allowed)
+                             apr_size_t maxlen, apr_table_t *trailers,
+                             int *pfile_handles_allowed)
 {
     apr_status_t status;
     int start_allowed;
@@ -233,6 +242,7 @@ apr_status_t h2_io_out_write(h2_io *io, apr_bucket_brigade *bb,
         return status;
     }
 
+    process_trailers(io, trailers);
     if (!io->bbout) {
         io->bbout = apr_brigade_create(io->pool, io->bucket_alloc);
     }
@@ -258,17 +268,20 @@ apr_status_t h2_io_out_write(h2_io *io, apr_bucket_brigade *bb,
 }
 
 
-apr_status_t h2_io_out_close(h2_io *io)
+apr_status_t h2_io_out_close(h2_io *io, apr_table_t *trailers)
 {
     if (io->rst_error) {
         return APR_ECONNABORTED;
     }
-    if (!io->bbout) {
-        io->bbout = apr_brigade_create(io->pool, io->bucket_alloc);
-    }
-    if (!io->eos_out && !h2_util_has_eos(io->bbout, -1)) {
-        APR_BRIGADE_INSERT_TAIL(io->bbout, 
-                                apr_bucket_eos_create(io->bbout->bucket_alloc));
+    if (!io->eos_out) { /* EOS has not been read yet */
+        process_trailers(io, trailers);
+        if (!io->bbout) {
+            io->bbout = apr_brigade_create(io->pool, io->bucket_alloc);
+        }
+        if (!h2_util_has_eos(io->bbout, -1)) {
+            APR_BRIGADE_INSERT_TAIL(io->bbout, 
+                                    apr_bucket_eos_create(io->bbout->bucket_alloc));
+        }
     }
     return APR_SUCCESS;
 }
index 1fd1167747a7034876e36b7d37c170ce63991e38..dcf493539bb38be5e49d6836bdb8d8344be0da2c 100644 (file)
@@ -130,13 +130,14 @@ apr_status_t h2_io_out_read_to(h2_io *io,
                                apr_off_t *plen, int *peos);
 
 apr_status_t h2_io_out_write(h2_io *io, apr_bucket_brigade *bb, 
-                             apr_size_t maxlen, int *pfile_buckets_allowed);
+                             apr_size_t maxlen, apr_table_t *trailers,
+                             int *pfile_buckets_allowed);
 
 /**
  * Closes the input. After existing data has been read, APR_EOF will
  * be returned.
  */
-apr_status_t h2_io_out_close(h2_io *io);
+apr_status_t h2_io_out_close(h2_io *io, apr_table_t *trailers);
 
 /**
  * Gives the overall length of the data that is currently queued for
index 74ab508fefe87591de4c3f05f6457d88ce022061..2bb6e69469171306757fcac64eea59b656c01ebe 100644 (file)
@@ -145,37 +145,23 @@ h2_io *h2_io_set_pop_highest_prio(h2_io_set *set)
     return NULL;
 }
 
-void h2_io_set_destroy_all(h2_io_set *sp)
-{
-    int i;
-    for (i = 0; i < sp->list->nelts; ++i) {
-        h2_io *io = h2_io_IDX(sp->list, i);
-        h2_io_destroy(io);
-    }
-    sp->list->nelts = 0;
-}
-
-void h2_io_set_remove_all(h2_io_set *sp)
-{
-    sp->list->nelts = 0;
-}
-
 int h2_io_set_is_empty(h2_io_set *sp)
 {
     AP_DEBUG_ASSERT(sp);
     return sp->list->nelts == 0;
 }
 
-void h2_io_set_iter(h2_io_set *sp,
+int h2_io_set_iter(h2_io_set *sp,
                         h2_io_set_iter_fn *iter, void *ctx)
 {
     int i;
     for (i = 0; i < sp->list->nelts; ++i) {
         h2_io *s = h2_io_IDX(sp->list, i);
         if (!iter(ctx, s)) {
-            break;
+            return 0;
         }
     }
+    return 1;
 }
 
 apr_size_t h2_io_set_size(h2_io_set *sp)
index 5e7555af92e35bacef141c5a7f86c023ce28ba9c..04ff8702ed28169f4ca6fce3bce917a914da89db 100644 (file)
@@ -32,16 +32,24 @@ apr_status_t h2_io_set_add(h2_io_set *set, struct h2_io *io);
 h2_io *h2_io_set_get(h2_io_set *set, int stream_id);
 h2_io *h2_io_set_remove(h2_io_set *set, struct h2_io *io);
 
-void h2_io_set_remove_all(h2_io_set *set);
-void h2_io_set_destroy_all(h2_io_set *set);
 int h2_io_set_is_empty(h2_io_set *set);
 apr_size_t h2_io_set_size(h2_io_set *set);
 
 
 typedef int h2_io_set_iter_fn(void *ctx, struct h2_io *io);
 
-void h2_io_set_iter(h2_io_set *set,
-                           h2_io_set_iter_fn *iter, void *ctx);
+/**
+ * Iterator over all h2_io* in the set or until a
+ * callback returns 0. It is not safe to add or remove
+ * set members during iteration.
+ *
+ * @param set the set of h2_io to iterate over
+ * @param iter the function to call for each io
+ * @param ctx user data for the callback
+ * @return 1 iff iteration completed for all members
+ */
+int h2_io_set_iter(h2_io_set *set,
+                   h2_io_set_iter_fn *iter, void *ctx);
 
 h2_io *h2_io_set_pop_highest_prio(h2_io_set *set);
 
index 3908590985a52b1eac82315f058c4808b9132b17..6f2a512465146ed6d8d6be6cf864d2c3102a8ec3 100644 (file)
@@ -73,6 +73,9 @@ static void have_out_data_for(h2_mplx *m, int stream_id);
 static void h2_mplx_destroy(h2_mplx *m)
 {
     AP_DEBUG_ASSERT(m);
+    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c,
+                  "h2_mplx(%ld): destroy, refs=%d", 
+                  m->id, m->refs);
     m->aborted = 1;
     if (m->ready_ios) {
         h2_io_set_destroy(m->ready_ios);
@@ -83,15 +86,6 @@ static void h2_mplx_destroy(h2_mplx *m)
         m->stream_ios = NULL;
     }
     
-    if (m->lock) {
-        apr_thread_mutex_destroy(m->lock);
-        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);
     }
@@ -199,13 +193,62 @@ static void workers_unregister(h2_mplx *m) {
     h2_workers_unregister(m->workers, m);
 }
 
+static void io_destroy(h2_mplx *m, h2_io *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;
+    }
+}
+
+static int io_stream_done(h2_mplx *m, h2_io *io, int rst_error) 
+{
+    /* Remove io from ready set, we will never submit it */
+    h2_io_set_remove(m->ready_ios, io);
+    if (io->task_done || h2_tq_remove(m->q, io->id)) {
+        /* already finished or not even started yet */
+        io_destroy(m, io);
+        return 0;
+    }
+    else {
+        /* cleanup once task is done */
+        io->orphaned = 1;
+        if (rst_error) {
+            h2_io_rst(io, rst_error);
+        }
+        return 1;
+    }
+}
+
+static int stream_done_iter(void *ctx, h2_io *io) {
+    return io_stream_done((h2_mplx*)ctx, io, 0);
+}
+
 apr_status_t h2_mplx_release_and_join(h2_mplx *m, apr_thread_cond_t *wait)
 {
     apr_status_t status;
+    
     workers_unregister(m);
-
     status = apr_thread_mutex_lock(m->lock);
     if (APR_SUCCESS == status) {
+        while (!h2_io_set_iter(m->stream_ios, stream_done_iter, m)) {
+            /* iterator until all h2_io have been orphaned or destroyed */
+        }
+    
         release(m, 0);
         while (m->refs > 0) {
             m->join_wait = wait;
@@ -215,10 +258,11 @@ apr_status_t h2_mplx_release_and_join(h2_mplx *m, apr_thread_cond_t *wait)
             apr_thread_cond_wait(wait, m->lock);
         }
         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(%ld): release_join -> destroy, (#ios=%ld)", 
+                      m->id, (long)h2_io_set_size(m->stream_ios));
         h2_mplx_destroy(m);
+        /* all gone */
+        /*apr_thread_mutex_unlock(m->lock);*/
     }
     return status;
 }
@@ -230,33 +274,8 @@ void h2_mplx_abort(h2_mplx *m)
     status = apr_thread_mutex_lock(m->lock);
     if (APR_SUCCESS == status) {
         m->aborted = 1;
-        h2_io_set_destroy_all(m->stream_ios);
         apr_thread_mutex_unlock(m->lock);
     }
-    workers_unregister(m);
-}
-
-
-static void io_destroy(h2_mplx *m, h2_io *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;
-    }
 }
 
 apr_status_t h2_mplx_stream_done(h2_mplx *m, int stream_id, int rst_error)
@@ -264,9 +283,6 @@ apr_status_t h2_mplx_stream_done(h2_mplx *m, int stream_id, int rst_error)
     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);
@@ -275,20 +291,7 @@ apr_status_t h2_mplx_stream_done(h2_mplx *m, int stream_id, int rst_error)
          * 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 || 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->orphaned = 1;
-                if (rst_error) {
-                    h2_io_rst(io, rst_error);
-                }
-            }
-            
+            io_stream_done(m, io, rst_error);
         }
         
         apr_thread_mutex_unlock(m->lock);
@@ -447,7 +450,8 @@ apr_status_t h2_mplx_in_update_windows(h2_mplx *m,
 
 apr_status_t h2_mplx_out_readx(h2_mplx *m, int stream_id, 
                                h2_io_data_cb *cb, void *ctx, 
-                               apr_off_t *plen, int *peos)
+                               apr_off_t *plen, int *peos,
+                               apr_table_t **ptrailers)
 {
     apr_status_t status;
     AP_DEBUG_ASSERT(m);
@@ -461,7 +465,6 @@ apr_status_t h2_mplx_out_readx(h2_mplx *m, int stream_id,
             H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_readx_pre");
             
             status = h2_io_out_readx(io, cb, ctx, plen, peos);
-            
             H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_readx_post");
             if (status == APR_SUCCESS && cb && io->output_drained) {
                 apr_thread_cond_signal(io->output_drained);
@@ -470,6 +473,8 @@ apr_status_t h2_mplx_out_readx(h2_mplx *m, int stream_id,
         else {
             status = APR_ECONNABORTED;
         }
+        
+        *ptrailers = (*peos && io->response)? io->response->trailers : NULL;
         apr_thread_mutex_unlock(m->lock);
     }
     return status;
@@ -477,7 +482,8 @@ apr_status_t h2_mplx_out_readx(h2_mplx *m, int stream_id,
 
 apr_status_t h2_mplx_out_read_to(h2_mplx *m, int stream_id, 
                                  apr_bucket_brigade *bb, 
-                                 apr_off_t *plen, int *peos)
+                                 apr_off_t *plen, int *peos,
+                                 apr_table_t **ptrailers)
 {
     apr_status_t status;
     AP_DEBUG_ASSERT(m);
@@ -500,6 +506,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;
         apr_thread_mutex_unlock(m->lock);
     }
     return status;
@@ -560,6 +567,7 @@ h2_stream *h2_mplx_next_submit(h2_mplx *m, h2_stream_set *streams)
 
 static apr_status_t out_write(h2_mplx *m, h2_io *io, 
                               ap_filter_t* f, apr_bucket_brigade *bb,
+                              apr_table_t *trailers,
                               struct apr_thread_cond_t *iowait)
 {
     apr_status_t status = APR_SUCCESS;
@@ -572,7 +580,7 @@ static apr_status_t out_write(h2_mplx *m, h2_io *io,
            && (status == APR_SUCCESS)
            && !is_aborted(m, &status)) {
         
-        status = h2_io_out_write(io, bb, m->stream_max_mem, 
+        status = h2_io_out_write(io, bb, m->stream_max_mem, trailers,
                                  &m->file_handles_allowed);
         /* Wait for data to drain until there is room again */
         while (!APR_BRIGADE_EMPTY(bb) 
@@ -580,6 +588,7 @@ static apr_status_t out_write(h2_mplx *m, h2_io *io,
                && status == APR_SUCCESS
                && (m->stream_max_mem <= h2_io_out_length(io))
                && !is_aborted(m, &status)) {
+            trailers = NULL;
             io->output_drained = iowait;
             if (f) {
                 ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,
@@ -613,7 +622,7 @@ static apr_status_t out_open(h2_mplx *m, int stream_id, h2_response *response,
         h2_io_set_response(io, response);
         h2_io_set_add(m->ready_ios, io);
         if (bb) {
-            status = out_write(m, io, f, bb, iowait);
+            status = out_write(m, io, f, bb, response->trailers, iowait);
         }
         have_out_data_for(m, stream_id);
     }
@@ -649,6 +658,7 @@ 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, apr_bucket_brigade *bb,
+                               apr_table_t *trailers,
                                struct apr_thread_cond_t *iowait)
 {
     apr_status_t status;
@@ -661,7 +671,10 @@ apr_status_t h2_mplx_out_write(h2_mplx *m, int stream_id,
         if (!m->aborted) {
             h2_io *io = h2_io_set_get(m->stream_ios, stream_id);
             if (io && !io->orphaned) {
-                status = out_write(m, io, f, bb, iowait);
+                status = out_write(m, io, f, bb, trailers, iowait);
+                ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, m->c,
+                              "h2_mplx(%ld-%d): write with trailers=%s", 
+                              m->id, io->id, trailers? "yes" : "no");
                 H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_write");
                 
                 have_out_data_for(m, stream_id);
@@ -681,7 +694,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_status_t h2_mplx_out_close(h2_mplx *m, int stream_id, apr_table_t *trailers)
 {
     apr_status_t status;
     AP_DEBUG_ASSERT(m);
@@ -698,14 +711,17 @@ apr_status_t h2_mplx_out_close(h2_mplx *m, int stream_id)
                      * insert an error one so that our streams can properly
                      * reset.
                      */
-                    h2_response *r = h2_response_create(stream_id, 0
-                                                        500, NULL, m->pool);
+                    h2_response *r = h2_response_die(stream_id, APR_EGENERAL
+                                                     io->request, 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", 
                                   m->id, io->id);
                 }
-                status = h2_io_out_close(io);
+                ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, m->c,
+                              "h2_mplx(%ld-%d): close with trailers=%s", 
+                              m->id, io->id, trailers? "yes" : "no");
+                status = h2_io_out_close(io, trailers);
                 H2_MPLX_IO_OUT(APLOG_TRACE2, m, io, "h2_mplx_out_close");
                 
                 have_out_data_for(m, stream_id);
index 5c950b9c271e41df288a6f5a041bbd75d40dfec9..c570e91fd43b96c72ed08ed3785eb1e7b7518e43 100644 (file)
@@ -240,7 +240,8 @@ struct h2_stream *h2_mplx_next_submit(h2_mplx *m,
  */
 apr_status_t h2_mplx_out_readx(h2_mplx *mplx, int stream_id, 
                                h2_io_data_cb *cb, void *ctx, 
-                               apr_off_t *plen, int *peos);
+                               apr_off_t *plen, int *peos,
+                               apr_table_t **ptrailers);
 
 /**
  * Reads output data into the given brigade. Will never block, but
@@ -248,7 +249,8 @@ apr_status_t h2_mplx_out_readx(h2_mplx *mplx, int stream_id,
  */
 apr_status_t h2_mplx_out_read_to(h2_mplx *mplx, int stream_id, 
                                  apr_bucket_brigade *bb, 
-                                 apr_off_t *plen, int *peos);
+                                 apr_off_t *plen, int *peos,
+                                 apr_table_t **ptrailers);
 
 /**
  * Opens the output for the given stream with the specified response.
@@ -264,17 +266,19 @@ apr_status_t h2_mplx_out_open(h2_mplx *mplx, int stream_id,
  * @param stream_id the stream identifier
  * @param filter the apache filter context of the data
  * @param bb the bucket brigade to append
+ * @param trailers optional trailers for response, maybe NULL
  * @param iowait a conditional used for block/signalling in h2_mplx
  */
 apr_status_t h2_mplx_out_write(h2_mplx *mplx, int stream_id, 
                                ap_filter_t* filter, apr_bucket_brigade *bb,
+                               apr_table_t *trailers,
                                struct apr_thread_cond_t *iowait);
 
 /**
- * Closes the output stream. Readers of this stream will get all pending 
- * data and then only APR_EOF as result. 
+ * Closes the output for stream stream_id. Optionally forwards trailers
+ * fromt the processed stream.  
  */
-apr_status_t h2_mplx_out_close(h2_mplx *m, int stream_id);
+apr_status_t h2_mplx_out_close(h2_mplx *m, int stream_id, apr_table_t *trailers);
 
 apr_status_t h2_mplx_out_rst(h2_mplx *m, int stream_id, int error);
 
index 703ea761a042b9980449ffa778772de036273229..65b2b7b625127cf9a10c46f980de0d9689d0541d 100644 (file)
@@ -301,6 +301,9 @@ static int add_push(link_ctx *ctx)
                 h2_request_end_headers(req, ctx->pool, 1);
                 push->req = req;
                 
+                push->prio.dependency = H2_DEPENDANT_AFTER;
+                push->prio.weight = NGHTTP2_DEFAULT_WEIGHT;
+                
                 if (!ctx->pushes) {
                     ctx->pushes = apr_array_make(ctx->pool, 5, sizeof(h2_push*));
                 }
@@ -386,14 +389,14 @@ apr_array_header_t *h2_push_collect(apr_pool_t *p, const h2_request *req,
      * TODO: This may be extended in the future by hooks or callbacks
      * where other modules can provide push information directly.
      */
-    if (res->header) {
+    if (res->headers) {
         link_ctx ctx;
         
         memset(&ctx, 0, sizeof(ctx));
         ctx.req = req;
         ctx.pool = p;
     
-        apr_table_do(head_iter, &ctx, res->header, NULL);
+        apr_table_do(head_iter, &ctx, res->headers, NULL);
         return ctx.pushes;
     }
     return NULL;
index 64edee65d194ce8d60609030ca18f8f4851d79f0..b98a2f73e17937c4a372e4c1ec8a6951fae818d5 100644 (file)
@@ -20,9 +20,9 @@ struct h2_response;
 struct h2_ngheader;
 
 typedef struct h2_push {
-    int initiating_id;
+    int          initiating_id;
     const struct h2_request *req;
-    const char *as;
+    h2_priority  prio;
 } h2_push;
 
 
index a5f7d9d4fb4567bf189fcb4e77e3bb60017bc7f0..e1a371f6a41ff67033255d81f1afdf44d6140828 100644 (file)
 
 #include <httpd.h>
 #include <http_core.h>
+#include <http_connection.h>
 #include <http_protocol.h>
-#include <http_config.h>
+#include <http_request.h>
 #include <http_log.h>
+#include <http_vhost.h>
+#include <util_filter.h>
+#include <ap_mpm.h>
+#include <mod_core.h>
+#include <scoreboard.h>
 
 #include "h2_private.h"
 #include "h2_mplx.h"
@@ -48,7 +54,8 @@ h2_request *h2_request_createn(int id, apr_pool_t *pool,
     req->authority      = authority;
     req->path           = path;
     req->headers        = header? header : apr_table_make(pool, 10);
-    
+    req->request_time   = apr_time_now();
+
     return req;
 }
 
@@ -322,3 +329,117 @@ void h2_request_copy(apr_pool_t *p, h2_request *dst, const h2_request *src)
     dst->eoh            = src->eoh;
 }
 
+request_rec *h2_request_create_rec(const h2_request *req, conn_rec *conn)
+{
+    request_rec *r;
+    apr_pool_t *p;
+    int access_status = HTTP_OK;    
+    
+    apr_pool_create(&p, conn->pool);
+    apr_pool_tag(p, "request");
+    r = apr_pcalloc(p, sizeof(request_rec));
+    AP_READ_REQUEST_ENTRY((intptr_t)r, (uintptr_t)conn);
+    r->pool            = p;
+    r->connection      = conn;
+    r->server          = conn->base_server;
+    
+    r->user            = NULL;
+    r->ap_auth_type    = NULL;
+    
+    r->allowed_methods = ap_make_method_list(p, 2);
+    
+    r->headers_in      = apr_table_copy(r->pool, req->headers);
+    r->trailers_in     = apr_table_make(r->pool, 5);
+    r->subprocess_env  = apr_table_make(r->pool, 25);
+    r->headers_out     = apr_table_make(r->pool, 12);
+    r->err_headers_out = apr_table_make(r->pool, 5);
+    r->trailers_out    = apr_table_make(r->pool, 5);
+    r->notes           = apr_table_make(r->pool, 5);
+    
+    r->request_config  = ap_create_request_config(r->pool);
+    /* Must be set before we run create request hook */
+    
+    r->proto_output_filters = conn->output_filters;
+    r->output_filters  = r->proto_output_filters;
+    r->proto_input_filters = conn->input_filters;
+    r->input_filters   = r->proto_input_filters;
+    ap_run_create_request(r);
+    r->per_dir_config  = r->server->lookup_defaults;
+    
+    r->sent_bodyct     = 0;                      /* bytect isn't for body */
+    
+    r->read_length     = 0;
+    r->read_body       = REQUEST_NO_BODY;
+    
+    r->status          = HTTP_OK;  /* Until further notice */
+    r->header_only     = 0;
+    r->the_request     = NULL;
+    
+    /* Begin by presuming any module can make its own path_info assumptions,
+     * until some module interjects and changes the value.
+     */
+    r->used_path_info = AP_REQ_DEFAULT_PATH_INFO;
+    
+    r->useragent_addr = conn->client_addr;
+    r->useragent_ip = conn->client_ip;
+    
+    ap_run_pre_read_request(r, conn);
+    
+    /* Time to populate r with the data we have. */
+    r->request_time = req->request_time;
+    r->method = req->method;
+    /* Provide quick information about the request method as soon as known */
+    r->method_number = ap_method_number_of(r->method);
+    if (r->method_number == M_GET && r->method[0] == 'H') {
+        r->header_only = 1;
+    }
+
+    ap_parse_uri(r, req->path);
+    r->protocol = (char*)"HTTP/2";
+    r->proto_num = HTTP_VERSION(2, 0);
+
+    r->the_request = apr_psprintf(r->pool, "%s %s %s", 
+                                  r->method, req->path, r->protocol);
+    
+    /* update what we think the virtual host is based on the headers we've
+     * now read. may update status.
+     * Leave r->hostname empty, vhost will parse if form our Host: header,
+     * otherwise we get complains about port numbers.
+     */
+    r->hostname = NULL;
+    ap_update_vhost_from_headers(r);
+    
+    /* we may have switched to another server */
+    r->per_dir_config = r->server->lookup_defaults;
+    
+    /*
+     * Add the HTTP_IN filter here to ensure that ap_discard_request_body
+     * called by ap_die and by ap_send_error_response works correctly on
+     * status codes that do not cause the connection to be dropped and
+     * in situations where the connection should be kept alive.
+     */
+    ap_add_input_filter_handle(ap_http_input_filter_handle,
+                               NULL, r, r->connection);
+    
+    if (access_status != HTTP_OK
+        || (access_status = ap_run_post_read_request(r))) {
+        /* Request check post hooks failed. An example of this would be a
+         * request for a vhost where h2 is disabled --> 421.
+         */
+        ap_die(access_status, r);
+        ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
+        ap_run_log_transaction(r);
+        r = NULL;
+        goto traceout;
+    }
+    
+    AP_READ_REQUEST_SUCCESS((uintptr_t)r, (char *)r->method, 
+                            (char *)r->uri, (char *)r->server->defn_name, 
+                            r->status);
+    return r;
+traceout:
+    AP_READ_REQUEST_FAILURE((uintptr_t)r);
+    return r;
+}
+
+
index 19005a88e6d660ca25e6536477a2c4dc1d09a917..69d24f38a2eb73acfaddbce385053b221150227d 100644 (file)
@@ -38,6 +38,7 @@ struct h2_request {
     apr_table_t *headers;
     apr_table_t *trailers;
 
+    apr_time_t request_time;
     apr_off_t content_length;
     int chunked;
     int eoh;
@@ -66,6 +67,15 @@ apr_status_t h2_request_end_headers(h2_request *req, apr_pool_t *pool, int eos);
 
 void h2_request_copy(apr_pool_t *p, h2_request *dst, const h2_request *src);
 
+/**
+ * Create a request_rec representing the h2_request to be
+ * processed on the given connection.
+ *
+ * @param req the h2 request to process
+ * @param conn the connection to process the request on
+ * @return the request_rec representing the request
+ */
+request_rec *h2_request_create_rec(const h2_request *req, conn_rec *conn);
 
 
 #endif /* defined(__mod_h2__h2_request__) */
index 2751f2d377cab075af21908f2ff38384cef50195..d16fee29bac32f57e8f40027fdd3588f89fc5459 100644 (file)
 #include <httpd.h>
 #include <http_core.h>
 #include <http_log.h>
+#include <util_time.h>
 
 #include <nghttp2/nghttp2.h>
 
 #include "h2_private.h"
 #include "h2_h2.h"
 #include "h2_util.h"
+#include "h2_request.h"
 #include "h2_response.h"
 
 
-h2_response *h2_response_create(int stream_id,
-                                int rst_error,
-                                int http_status,
-                                apr_array_header_t *hlines,
-                                apr_pool_t *pool)
+static apr_table_t *parse_headers(apr_array_header_t *hlines, apr_pool_t *pool)
 {
-    apr_table_t *header;
-    h2_response *response = apr_pcalloc(pool, sizeof(h2_response));
-    int i;
-    if (response == NULL) {
-        return NULL;
-    }
-    
-    response->stream_id = stream_id;
-    response->rst_error = rst_error;
-    response->http_status = http_status? http_status : 500;
-    response->content_length = -1;
-    
     if (hlines) {
-        header = apr_table_make(pool, hlines->nelts);        
+        apr_table_t *headers = apr_table_make(pool, hlines->nelts);        
+        int i;
+        
         for (i = 0; i < hlines->nelts; ++i) {
             char *hline = ((char **)hlines->elts)[i];
             char *sep = ap_strchr(hline, ':');
             if (!sep) {
                 ap_log_perror(APLOG_MARK, APLOG_WARNING, APR_EINVAL, pool,
-                              APLOGNO(02955) "h2_response(%d): invalid header[%d] '%s'",
-                              response->stream_id, i, (char*)hline);
+                              APLOGNO(02955) "h2_response: invalid header[%d] '%s'",
+                              i, (char*)hline);
                 /* not valid format, abort */
                 return NULL;
             }
@@ -66,30 +54,67 @@ h2_response *h2_response_create(int stream_id,
             }
             
             if (!h2_util_ignore_header(hline)) {
-                apr_table_merge(header, hline, sep);
-                if (*sep && H2_HD_MATCH_LIT_CS("content-length", hline)) {
-                    char *end;
-                    response->content_length = apr_strtoi64(sep, &end, 10);
-                    if (sep == end) {
-                        ap_log_perror(APLOG_MARK, APLOG_WARNING, APR_EINVAL, 
-                                      pool, APLOGNO(02956) 
-                                      "h2_response(%d): content-length"
-                                      " value not parsed: %s", 
-                                      response->stream_id, sep);
-                        response->content_length = -1;
-                    }
-                }
+                apr_table_merge(headers, hline, sep);
             }
         }
+        return headers;
     }
     else {
-        header = apr_table_make(pool, 0);        
+        return apr_table_make(pool, 0);        
     }
+}
+
+static h2_response *h2_response_create_int(int stream_id,
+                                           int rst_error,
+                                           int http_status,
+                                           apr_table_t *headers,
+                                           apr_pool_t *pool)
+{
+    h2_response *response;
+    const char *s;
 
-    response->header = header;
+    if (!headers) {
+        return NULL;
+    }
+    
+    response = apr_pcalloc(pool, sizeof(h2_response));
+    if (response == NULL) {
+        return NULL;
+    }
+    
+    response->stream_id = stream_id;
+    response->rst_error = rst_error;
+    response->http_status = http_status? http_status : 500;
+    response->content_length = -1;
+    response->headers = headers;
+    
+    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;
+        }
+    }
     return response;
 }
 
+
+h2_response *h2_response_create(int stream_id,
+                                int rst_error,
+                                int http_status,
+                                apr_array_header_t *hlines,
+                                apr_pool_t *pool)
+{
+    return h2_response_create_int(stream_id, rst_error, http_status,
+                                  parse_headers(hlines, pool), pool);
+}
+
 h2_response *h2_response_rcreate(int stream_id, request_rec *r,
                                  apr_table_t *header, apr_pool_t *pool)
 {
@@ -101,7 +126,7 @@ h2_response *h2_response_rcreate(int stream_id, request_rec *r,
     response->stream_id = stream_id;
     response->http_status = r->status;
     response->content_length = -1;
-    response->header = header;
+    response->headers = header;
 
     if (response->http_status == HTTP_FORBIDDEN) {
         const char *cause = apr_table_get(r->notes, "ssl-renegotiate-forbidden");
@@ -119,21 +144,37 @@ h2_response *h2_response_rcreate(int stream_id, request_rec *r,
     return response;
 }
 
-void h2_response_destroy(h2_response *response)
+h2_response *h2_response_die(int stream_id, apr_status_t type,
+                             const struct h2_request *req, apr_pool_t *pool)
 {
-    (void)response;
+    apr_table_t *headers = apr_table_make(pool, 5);
+    char *date = NULL;
+    
+    date = apr_palloc(pool, APR_RFC822_DATE_LEN);
+    ap_recent_rfc822_date(date, req->request_time);
+    apr_table_setn(headers, "Date", date);
+    apr_table_setn(headers, "Server", ap_get_server_banner());
+    
+    return h2_response_create_int(stream_id, 0, 500, headers, pool);
 }
 
-h2_response *h2_response_copy(apr_pool_t *pool, h2_response *from)
+h2_response *h2_response_clone(apr_pool_t *pool, h2_response *from)
 {
     h2_response *to = apr_pcalloc(pool, sizeof(h2_response));
     to->stream_id = from->stream_id;
     to->http_status = from->http_status;
     to->content_length = from->content_length;
-    if (from->header) {
-        to->header = apr_table_clone(pool, from->header);
+    if (from->headers) {
+        to->headers = apr_table_clone(pool, from->headers);
+    }
+    if (from->trailers) {
+        to->trailers = apr_table_clone(pool, from->trailers);
     }
     return to;
 }
 
+void h2_response_set_trailers(h2_response *response, apr_table_t *trailers)
+{
+    response->trailers = trailers;
+}
 
index 4085a41bdfa59acb63a560ac98db2ade0afea3a6..426eeead72f4cef83a736596ac8b41e66d4d2fe4 100644 (file)
@@ -16,6 +16,7 @@
 #ifndef __mod_h2__h2_response__
 #define __mod_h2__h2_response__
 
+struct h2_request;
 struct h2_push;
 
 typedef struct h2_response {
@@ -23,21 +24,59 @@ typedef struct h2_response {
     int rst_error;
     int http_status;
     apr_off_t content_length;
-    apr_table_t *header;
-    apr_table_t *trailer;
+    apr_table_t *headers;
+    apr_table_t *trailers;
 } h2_response;
 
+/**
+ * Create the response from the status and parsed header lines.
+ * @param stream_id id of the stream to create the response for
+ * @param rst_error error for reset or 0
+ * @param http_status  http status code of response
+ * @param hlines the text lines of the response header
+ * @param pool the memory pool to use
+ */
 h2_response *h2_response_create(int stream_id,
                                 int rst_error,
                                 int http_status,
                                 apr_array_header_t *hlines,
                                 apr_pool_t *pool);
 
+/**
+ * Create the response from the given request_rec.
+ * @param stream_id id of the stream to create the response for
+ * @param r the request record which was processed
+ * @param header the headers of the response
+ * @param pool the memory pool to use
+ */
 h2_response *h2_response_rcreate(int stream_id, request_rec *r,
                                  apr_table_t *header, apr_pool_t *pool);
 
-void h2_response_destroy(h2_response *response);
+/**
+ * Create the response for the given error.
+ * @param stream_id id of the stream to create the response for
+ * @param type the error code
+ * @param req the original h2_request
+ * @param pool the memory pool to use
+ */
+h2_response *h2_response_die(int stream_id, apr_status_t type,
+                             const struct h2_request *req, apr_pool_t *pool);
 
-h2_response *h2_response_copy(apr_pool_t *pool, h2_response *from);
+/**
+ * Deep copies the response into a new pool.
+ * @param pool the pool to use for the clone
+ * @param from the response to clone
+ * @return the cloned response
+ */
+h2_response *h2_response_clone(apr_pool_t *pool, h2_response *from);
+
+/**
+ * Set the trailers in the reponse. Will replace any existing trailers. Will
+ * *not* clone the table.
+ *
+ * @param response the repsone to set the trailers for
+ * @param trailers the trailers to set
+ */
+void h2_response_set_trailers(h2_response *response, apr_table_t *trailers);
 
 #endif /* defined(__mod_h2__h2_response__) */
index d70eefd2965c3e339ac2d87ec869013692dc1074..91a3d9465cdc02f5f24a5860d84906b56fce82f6 100644 (file)
@@ -14,6 +14,7 @@
  */
 
 #include <assert.h>
+#include <math.h>
 #include <apr_thread_cond.h>
 #include <apr_base64.h>
 #include <apr_strings.h>
@@ -24,7 +25,6 @@
 #include <http_log.h>
 
 #include "h2_private.h"
-#include "h2_bucket_eoc.h"
 #include "h2_bucket_eos.h"
 #include "h2_config.h"
 #include "h2_h2.h"
@@ -84,11 +84,6 @@ h2_stream *h2_session_open_stream(h2_session *session, int stream_id)
     return stream;
 }
 
-apr_status_t h2_session_flush(h2_session *session) 
-{
-    return h2_conn_io_flush(&session->io);
-}
-
 /**
  * Determine the importance of streams when scheduling tasks.
  * - if both stream depend on the same one, compare weights
@@ -590,6 +585,44 @@ static apr_status_t session_pool_cleanup(void *data)
     return APR_SUCCESS;
 }
 
+static void *session_malloc(size_t size, void *ctx)
+{
+    h2_session *session = ctx;
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, session->c,
+                  "h2_session(%ld): malloc(%ld)",
+                  session->id, (long)size);
+    return malloc(size);
+}
+
+static void session_free(void *p, void *ctx)
+{
+    h2_session *session = ctx;
+
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, session->c,
+                  "h2_session(%ld): free()",
+                  session->id);
+    free(p);
+}
+
+static void *session_calloc(size_t n, size_t size, void *ctx)
+{
+    h2_session *session = ctx;
+
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, session->c,
+                  "h2_session(%ld): calloc(%ld, %ld)",
+                  session->id, (long)n, (long)size);
+    return calloc(n, size);
+}
+
+static void *session_realloc(void *p, size_t size, void *ctx)
+{
+    h2_session *session = ctx;
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, session->c,
+                  "h2_session(%ld): realloc(%ld)",
+                  session->id, (long)size);
+    return realloc(p, size);
+}
+
 static h2_session *h2_session_create_int(conn_rec *c,
                                          request_rec *r,
                                          h2_config *config, 
@@ -608,17 +641,18 @@ static h2_session *h2_session_create_int(conn_rec *c,
     session = apr_pcalloc(pool, sizeof(h2_session));
     if (session) {
         int rv;
+        nghttp2_mem *mem;
+        
         session->id = c->id;
         session->c = c;
         session->r = r;
         
+        session->pool = pool;
         apr_pool_pre_cleanup_register(pool, session, session_pool_cleanup);
         
         session->max_stream_count = h2_config_geti(config, H2_CONF_MAX_STREAMS);
         session->max_stream_mem = h2_config_geti(config, H2_CONF_STREAM_MAX_MEM);
 
-        session->pool = pool;
-        
         status = apr_thread_cond_create(&session->iowait, session->pool);
         if (status != APR_SUCCESS) {
             return NULL;
@@ -629,7 +663,7 @@ static h2_session *h2_session_create_int(conn_rec *c,
         session->workers = workers;
         session->mplx = h2_mplx_create(c, session->pool, workers);
         
-        h2_conn_io_init(&session->io, c);
+        h2_conn_io_init(&session->io, c, session->pool);
         session->bbtmp = apr_brigade_create(session->pool, c->bucket_alloc);
         
         status = init_callbacks(c, &callbacks);
@@ -648,16 +682,27 @@ static h2_session *h2_session_create_int(conn_rec *c,
             h2_session_destroy(session);
             return NULL;
         }
-
         nghttp2_option_set_peer_max_concurrent_streams(options, 
                                                        (uint32_t)session->max_stream_count);
-
         /* We need to handle window updates ourself, otherwise we
          * get flooded by nghttp2. */
         nghttp2_option_set_no_auto_window_update(options, 1);
         
-        rv = nghttp2_session_server_new2(&session->ngh2, callbacks,
-                                         session, options);
+        if (APLOGctrace6(c)) {
+            mem = apr_pcalloc(session->pool, sizeof(nghttp2_mem));
+            mem->mem_user_data = session;
+            mem->malloc    = session_malloc;
+            mem->free      = session_free;
+            mem->calloc    = session_calloc;
+            mem->realloc   = session_realloc;
+            
+            rv = nghttp2_session_server_new3(&session->ngh2, callbacks,
+                                             session, options, mem);
+        }
+        else {
+            rv = nghttp2_session_server_new2(&session->ngh2, callbacks,
+                                             session, options);
+        }
         nghttp2_session_callbacks_del(callbacks);
         nghttp2_option_del(options);
         
@@ -703,10 +748,6 @@ static void h2_session_cleanup(h2_session *session)
         apr_pool_destroy(session->spare);
         session->spare = NULL;
     }
-    if (session->mplx) {
-        h2_mplx_release_and_join(session->mplx, session->iowait);
-        session->mplx = NULL;
-    }
 }
 
 void h2_session_destroy(h2_session *session)
@@ -714,6 +755,10 @@ void h2_session_destroy(h2_session *session)
     AP_DEBUG_ASSERT(session);
     h2_session_cleanup(session);
     
+    if (session->mplx) {
+        h2_mplx_release_and_join(session->mplx, session->iowait);
+        session->mplx = NULL;
+    }
     if (session->streams) {
         if (!h2_stream_set_is_empty(session->streams)) {
             ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
@@ -993,10 +1038,8 @@ apr_status_t h2_session_close(h2_session *session)
     ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0,session->c,
                   "h2_session: closing, writing eoc");
     
-    h2_session_cleanup(session);              
-    return h2_conn_io_writeb(&session->io,
-                             h2_bucket_eoc_create(session->c->bucket_alloc, 
-                                                  session));
+    h2_session_cleanup(session);
+    return h2_conn_io_close(&session->io, session);           
 }
 
 static ssize_t stream_data_cb(nghttp2_session *ng2s,
@@ -1082,10 +1125,14 @@ static ssize_t stream_data_cb(nghttp2_session *ng2s,
             int rv;
             
             nh = h2_util_ngheader_make(stream->pool, trailers);
+            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
+                          "h2_stream(%ld-%d): submit %d trailers",
+                          session->id, (int)stream_id,(int) nh->nvlen);
             rv = nghttp2_submit_trailer(ng2s, stream->id, nh->nv, nh->nvlen);
             if (rv < 0) {
                 nread = rv;
             }
+            *data_flags |= NGHTTP2_DATA_FLAG_NO_END_STREAM;
         }
         
         *data_flags |= NGHTTP2_DATA_FLAG_EOF;
@@ -1116,10 +1163,11 @@ static apr_status_t submit_response(h2_session *session, h2_stream *stream)
     if (stream->submitted) {
         rv = NGHTTP2_PROTOCOL_ERROR;
     }
-    else if (stream->response && stream->response->header) {
+    else if (stream->response && stream->response->headers) {
         nghttp2_data_provider provider;
         h2_response *response = stream->response;
         h2_ngheader *ngh;
+        h2_priority *prio;
         
         memset(&provider, 0, sizeof(provider));
         provider.source.fd = stream->id;
@@ -1129,8 +1177,14 @@ static apr_status_t submit_response(h2_session *session, h2_stream *stream)
                       "h2_stream(%ld-%d): submit response %d",
                       session->id, stream->id, response->http_status);
         
+        prio = h2_stream_get_priority(stream);
+        if (prio) {
+            h2_session_set_prio(session, stream, prio);
+            /* no showstopper if that fails for some reason */
+        }
+        
         ngh = h2_util_ngheader_make_res(stream->pool, response->http_status, 
-                                        response->header);
+                                        response->headers);
         rv = nghttp2_submit_response(session->ngh2, response->stream_id,
                                      ngh->nv, ngh->nvlen, &provider);
         
@@ -1208,6 +1262,7 @@ struct h2_stream *h2_session_push(h2_session *session, h2_stream *is,
     stream = h2_session_open_stream(session, nid);
     if (stream) {
         h2_stream_set_h2_request(stream, is->id, push->req);
+        h2_stream_set_priority(stream, &push->prio);
         status = stream_schedule(session, stream, 1);
         if (status != APR_SUCCESS) {
             ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, session->c,
@@ -1232,6 +1287,109 @@ struct h2_stream *h2_session_push(h2_session *session, h2_stream *is,
     return stream;
 }
 
+static int valid_weight(float f) 
+{
+    int w = floor(f);
+    return (w < NGHTTP2_MIN_WEIGHT? NGHTTP2_MIN_WEIGHT : 
+            (w > NGHTTP2_MAX_WEIGHT)? NGHTTP2_MAX_WEIGHT : w);
+}
+
+apr_status_t h2_session_set_prio(h2_session *session, h2_stream *stream, 
+                                 h2_priority *prio)
+{
+    apr_status_t status = APR_SUCCESS;
+#ifdef H2_NG2_CHANGE_PRIO
+    nghttp2_stream *s_grandpa, *s_parent, *s;
+    
+    s = nghttp2_session_find_stream(session->ngh2, stream->id);
+    if (!s) {
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
+                      "h2_stream(%ld-%d): lookup of nghttp2_stream failed",
+                      session->id, stream->id);
+        return APR_EINVAL;
+    }
+    
+    s_parent = nghttp2_stream_get_parent(s);
+    if (s_parent) {
+        nghttp2_priority_spec ps;
+        int id_parent, id_grandpa, w_parent, w, rv = 0;
+        char *ptype = "AFTER";
+        h2_dependency dep = prio->dependency;
+        
+        id_parent = nghttp2_stream_get_stream_id(s_parent);
+        s_grandpa = nghttp2_stream_get_parent(s_parent);
+        if (s_grandpa) {
+            id_grandpa = nghttp2_stream_get_stream_id(s_grandpa);
+        }
+        else {
+            /* parent of parent does not exist, 
+             * only possible if parent == root */
+            dep = H2_DEPENDANT_AFTER;
+        }
+        
+        switch (dep) {
+            case H2_DEPENDANT_INTERLEAVED:
+                /* PUSHed stream is to be interleaved with initiating stream.
+                 * It is made a sibling of the initiating stream and gets a
+                 * proportional weight [1, MAX_WEIGHT] of the initiaing
+                 * stream weight.
+                 */
+                ptype = "INTERLEAVED";
+                w_parent = nghttp2_stream_get_weight(s_parent);
+                w = valid_weight(w_parent * ((float)NGHTTP2_MAX_WEIGHT / prio->weight));
+                nghttp2_priority_spec_init(&ps, id_grandpa, w, 0);
+                break;
+                
+            case H2_DEPENDANT_BEFORE:
+                /* PUSHed stream os to be sent BEFORE the initiating stream.
+                 * It gets the same weight as the initiating stream, replaces
+                 * that stream in the dependency tree and has the initiating
+                 * stream as child with MAX_WEIGHT.
+                 */
+                ptype = "BEFORE";
+                nghttp2_priority_spec_init(&ps, stream->id, NGHTTP2_MAX_WEIGHT, 0);
+                rv = nghttp2_session_change_stream_priority(session->ngh2, id_parent, &ps);
+                if (rv < 0) {
+                    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
+                                  "h2_stream(%ld-%d): PUSH BEFORE2, weight=%d, "
+                                  "depends=%d, returned=%d",
+                                  session->id, id_parent, ps.weight, ps.stream_id, rv);
+                    return APR_EGENERAL;
+                }
+                id_grandpa = nghttp2_stream_get_stream_id(s_grandpa);
+                w_parent = nghttp2_stream_get_weight(s_parent);
+                nghttp2_priority_spec_init(&ps, id_grandpa, valid_weight(w_parent), 0);
+                break;
+                
+            case H2_DEPENDANT_AFTER:
+                /* The PUSHed stream is to be sent after the initiating stream.
+                 * Give if the specified weight and let it depend on the intiating
+                 * stream.
+                 */
+                /* fall through, it's the default */
+            default:
+                nghttp2_priority_spec_init(&ps, id_parent, valid_weight(prio->weight), 0);
+                break;
+        }
+
+
+        rv = nghttp2_session_change_stream_priority(session->ngh2, stream->id, &ps);
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
+                      "h2_stream(%ld-%d): PUSH %s, weight=%d, "
+                      "depends=%d, returned=%d",
+                      session->id, stream->id, ptype, 
+                      ps.weight, ps.stream_id, rv);
+        status = (rv < 0)? APR_EGENERAL : APR_SUCCESS;
+    }
+#else
+    (void)session;
+    (void)stream;
+    (void)prio;
+    (void)valid_weight;
+#endif
+    return status;
+}
+
 apr_status_t h2_session_stream_destroy(h2_session *session, h2_stream *stream)
 {
     apr_pool_t *pool = h2_stream_detach_pool(stream);
index 90052fc9e7fe99ab41c115c524c8ec4a4bdf98e6..16767fb785928a72da636b7cfe26d12d911184c2 100644 (file)
@@ -41,6 +41,7 @@ struct apr_thread_mutext_t;
 struct apr_thread_cond_t;
 struct h2_config;
 struct h2_mplx;
+struct h2_priority;
 struct h2_push;
 struct h2_response;
 struct h2_session;
@@ -147,12 +148,6 @@ apr_status_t h2_session_start(h2_session *session, int *rv);
  */
 apr_status_t h2_session_abort(h2_session *session, apr_status_t reason, int rv);
 
-/**
- * Pass any buffered output data through the connection filters.
- * @param session the session to flush
- */
-apr_status_t h2_session_flush(h2_session *session);
-
 /**
  * Called before a session gets destroyed, might flush output etc. 
  */
@@ -201,4 +196,9 @@ apr_status_t h2_session_stream_destroy(h2_session *session,
 struct h2_stream *h2_session_push(h2_session *session, 
                                   struct h2_stream *is, struct h2_push *push);
 
+apr_status_t h2_session_set_prio(h2_session *session, 
+                                 struct h2_stream *stream, 
+                                 struct h2_priority *prio);
+
+
 #endif /* defined(__mod_h2__h2_session__) */
index ad7f5df102dad3cce3c85059d884242cd120a1bf..c9f88a27b6afa7f81d56a5cd90cded523328b46b 100644 (file)
@@ -223,7 +223,7 @@ apr_status_t h2_stream_set_response(h2_stream *stream, h2_response *response,
          * as we want, since the lifetimes are the same and we are not freeing
          * the ones in h2_mplx->io before this stream is done. */
         H2_STREAM_OUT(APLOG_TRACE2, stream, "h2_stream set_response_pre");
-        status = h2_util_move(stream->bbout, bb, -1, &move_all,  
+        status = h2_util_move(stream->bbout, bb, 16 * 1024, &move_all,  
                               "h2_stream_set_response");
         H2_STREAM_OUT(APLOG_TRACE2, stream, "h2_stream set_response_post");
     }
@@ -476,6 +476,7 @@ apr_status_t h2_stream_prep_read(h2_stream *stream,
 {
     apr_status_t status = APR_SUCCESS;
     const char *src;
+    apr_table_t *trailers = NULL;
     int test_read = (*plen == 0);
     
     if (stream->rst_error) {
@@ -490,19 +491,26 @@ apr_status_t h2_stream_prep_read(h2_stream *stream,
             apr_brigade_cleanup(stream->bbout);
             return h2_stream_prep_read(stream, plen, peos);
         }
+        trailers = stream->response? stream->response->trailers : NULL;
     }
     else {
         src = "mplx";
         status = h2_mplx_out_readx(stream->session->mplx, stream->id, 
-                                   NULL, NULL, plen, peos);
+                                   NULL, NULL, plen, peos, &trailers);
+        if (trailers && stream->response) {
+            h2_response_set_trailers(stream->response, trailers);
+        }    
     }
+    
     if (!test_read && status == APR_SUCCESS && !*peos && !*plen) {
         status = APR_EAGAIN;
     }
+    
     H2_STREAM_OUT(APLOG_TRACE2, stream, "h2_stream prep_read_post");
     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, stream->session->c,
-                  "h2_stream(%ld-%d): prep_read %s, len=%ld eos=%d",
-                  stream->session->id, stream->id, src, (long)*plen, *peos);
+                  "h2_stream(%ld-%d): prep_read %s, len=%ld eos=%d, trailers=%s",
+                  stream->session->id, stream->id, src, (long)*plen, *peos,
+                  trailers? "yes" : "no");
     return status;
 }
 
@@ -511,6 +519,7 @@ apr_status_t h2_stream_readx(h2_stream *stream,
                              apr_off_t *plen, int *peos)
 {
     apr_status_t status = APR_SUCCESS;
+    apr_table_t *trailers = NULL;
     const char *src;
     
     H2_STREAM_OUT(APLOG_TRACE2, stream, "h2_stream readx_pre");
@@ -532,14 +541,21 @@ apr_status_t h2_stream_readx(h2_stream *stream,
     else {
         src = "mplx";
         status = h2_mplx_out_readx(stream->session->mplx, stream->id, 
-                                   cb, ctx, plen, peos);
+                                   cb, ctx, plen, peos, &trailers);
+    }
+    
+    if (trailers && stream->response) {
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, stream->session->c,
+                      "h2_stream(%ld-%d): readx, saving trailers",
+                      stream->session->id, stream->id);
+        h2_response_set_trailers(stream->response, trailers);
     }
     
     if (status == APR_SUCCESS && !*peos && !*plen) {
         status = APR_EAGAIN;
     }
     
-    H2_STREAM_OUT(APLOG_TRACE2, stream, "h2_stream prep_readx_post");
+    H2_STREAM_OUT(APLOG_TRACE2, stream, "h2_stream readx_post");
     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, stream->session->c,
                   "h2_stream(%ld-%d): readx %s, len=%ld eos=%d",
                   stream->session->id, stream->id, src, (long)*plen, *peos);
@@ -552,6 +568,7 @@ apr_status_t h2_stream_read_to(h2_stream *stream, apr_bucket_brigade *bb,
                                apr_off_t *plen, int *peos)
 {
     apr_status_t status = APR_SUCCESS;
+    apr_table_t *trailers = NULL;
 
     H2_STREAM_OUT(APLOG_TRACE2, stream, "h2_stream read_to_pre");
     if (stream->rst_error) {
@@ -562,7 +579,7 @@ apr_status_t h2_stream_read_to(h2_stream *stream, apr_bucket_brigade *bb,
         apr_off_t tlen = *plen;
         int eos;
         status = h2_mplx_out_read_to(stream->session->mplx, stream->id, 
-                                     stream->bbout, &tlen, &eos);
+                                     stream->bbout, &tlen, &eos, &trailers);
     }
     
     if (status == APR_SUCCESS && !APR_BRIGADE_EMPTY(stream->bbout)) {
@@ -574,6 +591,13 @@ apr_status_t h2_stream_read_to(h2_stream *stream, apr_bucket_brigade *bb,
         *peos = 0;
     }
 
+    if (trailers && stream->response) {
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, stream->session->c,
+                      "h2_stream(%ld-%d): read_to, saving trailers",
+                      stream->session->id, stream->id);
+        h2_response_set_trailers(stream->response, trailers);
+    }
+    
     if (status == APR_SUCCESS && !*peos && !*plen) {
         status = APR_EAGAIN;
     }
@@ -642,6 +666,17 @@ apr_status_t h2_stream_submit_pushes(h2_stream *stream)
 
 apr_table_t *h2_stream_get_trailers(h2_stream *stream)
 {
-    /* TODO */
-    return NULL;
+    return stream->response? stream->response->trailers : NULL;
+}
+
+void h2_stream_set_priority(h2_stream *stream, h2_priority *prio)
+{
+    stream->prio = apr_pcalloc(stream->pool, sizeof(*prio));
+    memcpy(stream->prio, prio, sizeof(*prio));
 }
+
+h2_priority *h2_stream_get_priority(h2_stream *stream)
+{
+    return stream->prio;
+}
+
index 79801722f4a315a689bf8be189d4c5a062d174cc..8de3ecbacfcf618f72f0ba367a66a58d7dc77f7b 100644 (file)
@@ -41,6 +41,7 @@ typedef enum {
 } h2_stream_state_t;
 
 struct h2_mplx;
+struct h2_priority;
 struct h2_request;
 struct h2_response;
 struct h2_session;
@@ -69,6 +70,8 @@ struct h2_stream {
     
     apr_bucket_brigade *bbout;  /* output DATA */
     apr_off_t data_frames_sent; /* # of DATA frames sent out for this stream */
+    
+    struct h2_priority *prio;   /* priority information to set before submit */
 };
 
 
@@ -300,4 +303,16 @@ apr_status_t h2_stream_submit_pushes(h2_stream *stream);
  */
 apr_table_t *h2_stream_get_trailers(h2_stream *stream);
 
+/**
+ * Get priority information set for this stream.
+ */
+struct h2_priority *h2_stream_get_priority(h2_stream *stream);
+
+/**
+ * Set the priority information to use on the submit of the stream.
+ * @param stream the stream to set priority on
+ * @param prio the priority information
+ */
+void h2_stream_set_priority(h2_stream *stream, struct h2_priority *prio);
+
 #endif /* defined(__mod_h2__h2_stream__) */
index b7d48a1bf6fe524ab443e2219ed8bc98bd7cee53..fee406e1bbe45c3b962eb42f7c1382063d44bb44 100644 (file)
@@ -82,8 +82,6 @@ static apr_status_t h2_filter_read_response(ap_filter_t* f,
     return h2_from_h1_read_response(task->output->from_h1, f, bb);
 }
 
-static apr_status_t h2_task_process_request(h2_task *task);
-
 /*******************************************************************************
  * Register various hooks
  */
@@ -113,6 +111,8 @@ void h2_task_register_hooks(void)
                               NULL, AP_FTYPE_NETWORK);
     ap_register_output_filter("H1_TO_H2_RESP", h2_filter_read_response,
                               NULL, AP_FTYPE_PROTOCOL);
+    ap_register_output_filter("H2_TRAILERS", h2_response_trailers_filter,
+                              NULL, AP_FTYPE_PROTOCOL);
 }
 
 static int h2_task_pre_conn(conn_rec* c, void *arg)
@@ -135,24 +135,6 @@ static int h2_task_pre_conn(conn_rec* c, void *arg)
     return OK;
 }
 
-static int h2_task_process_conn(conn_rec* c)
-{
-    h2_ctx *ctx = h2_ctx_get(c);
-    
-    if (h2_ctx_is_task(ctx)) {
-        if (!ctx->task->serialize_headers) {
-            ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, 
-                          "h2_h2, processing request directly");
-            h2_task_process_request(ctx->task);
-            return DONE;
-        }
-        ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, 
-                      "h2_task(%s), serialized handling", ctx->task->id);
-    }
-    return DECLINED;
-}
-
-
 h2_task *h2_task_create(long session_id, const h2_request *req, 
                         apr_pool_t *pool, h2_mplx *mplx, int eos)
 {
@@ -161,7 +143,7 @@ h2_task *h2_task_create(long session_id, const h2_request *req,
         ap_log_perror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, pool,
                       APLOGNO(02941) "h2_task(%ld-%d): create stream task", 
                       session_id, req->id);
-        h2_mplx_out_close(mplx, req->id);
+        h2_mplx_out_close(mplx, req->id, NULL);
         return NULL;
     }
     
@@ -228,134 +210,18 @@ apr_status_t h2_task_do(h2_task *task, h2_worker *worker)
         apr_thread_cond_signal(task->io);
     }
     
-    h2_mplx_task_done(task->mplx, task->stream_id);
     h2_worker_release_task(worker, task);
+    h2_mplx_task_done(task->mplx, task->stream_id);
     
     return status;
 }
 
-static request_rec *h2_task_create_request(h2_task *task)
-{
-    conn_rec *conn = task->c;
-    request_rec *r;
-    apr_pool_t *p;
-    int access_status = HTTP_OK;    
-    
-    apr_pool_create(&p, conn->pool);
-    apr_pool_tag(p, "request");
-    r = apr_pcalloc(p, sizeof(request_rec));
-    AP_READ_REQUEST_ENTRY((intptr_t)r, (uintptr_t)conn);
-    r->pool            = p;
-    r->connection      = conn;
-    r->server          = conn->base_server;
-    
-    r->user            = NULL;
-    r->ap_auth_type    = NULL;
-    
-    r->allowed_methods = ap_make_method_list(p, 2);
-    
-    r->headers_in      = apr_table_copy(r->pool, task->request->headers);
-    r->trailers_in     = apr_table_make(r->pool, 5);
-    r->subprocess_env  = apr_table_make(r->pool, 25);
-    r->headers_out     = apr_table_make(r->pool, 12);
-    r->err_headers_out = apr_table_make(r->pool, 5);
-    r->trailers_out    = apr_table_make(r->pool, 5);
-    r->notes           = apr_table_make(r->pool, 5);
-    
-    r->request_config  = ap_create_request_config(r->pool);
-    /* Must be set before we run create request hook */
-    
-    r->proto_output_filters = conn->output_filters;
-    r->output_filters  = r->proto_output_filters;
-    r->proto_input_filters = conn->input_filters;
-    r->input_filters   = r->proto_input_filters;
-    ap_run_create_request(r);
-    r->per_dir_config  = r->server->lookup_defaults;
-    
-    r->sent_bodyct     = 0;                      /* bytect isn't for body */
-    
-    r->read_length     = 0;
-    r->read_body       = REQUEST_NO_BODY;
-    
-    r->status          = HTTP_OK;  /* Until further notice */
-    r->header_only     = 0;
-    r->the_request     = NULL;
-    
-    /* Begin by presuming any module can make its own path_info assumptions,
-     * until some module interjects and changes the value.
-     */
-    r->used_path_info = AP_REQ_DEFAULT_PATH_INFO;
-    
-    r->useragent_addr = conn->client_addr;
-    r->useragent_ip = conn->client_ip;
-    
-    ap_run_pre_read_request(r, conn);
-    
-    /* Time to populate r with the data we have. */
-    r->request_time = apr_time_now();
-    r->method = task->request->method;
-    /* Provide quick information about the request method as soon as known */
-    r->method_number = ap_method_number_of(r->method);
-    if (r->method_number == M_GET && r->method[0] == 'H') {
-        r->header_only = 1;
-    }
-
-    ap_parse_uri(r, task->request->path);
-    r->protocol = (char*)"HTTP/2";
-    r->proto_num = HTTP_VERSION(2, 0);
-
-    r->the_request = apr_psprintf(r->pool, "%s %s %s", 
-                                  r->method, task->request->path, r->protocol);
-    
-    /* update what we think the virtual host is based on the headers we've
-     * now read. may update status.
-     * Leave r->hostname empty, vhost will parse if form our Host: header,
-     * otherwise we get complains about port numbers.
-     */
-    r->hostname = NULL;
-    ap_update_vhost_from_headers(r);
-    
-    /* we may have switched to another server */
-    r->per_dir_config = r->server->lookup_defaults;
-    
-    /*
-     * Add the HTTP_IN filter here to ensure that ap_discard_request_body
-     * called by ap_die and by ap_send_error_response works correctly on
-     * status codes that do not cause the connection to be dropped and
-     * in situations where the connection should be kept alive.
-     */
-    ap_add_input_filter_handle(ap_http_input_filter_handle,
-                               NULL, r, r->connection);
-    
-    if (access_status != HTTP_OK
-        || (access_status = ap_run_post_read_request(r))) {
-        /* Request check post hooks failed. An example of this would be a
-         * request for a vhost where h2 is disabled --> 421.
-         */
-        ap_die(access_status, r);
-        ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
-        ap_run_log_transaction(r);
-        r = NULL;
-        goto traceout;
-    }
-    
-    AP_READ_REQUEST_SUCCESS((uintptr_t)r, (char *)r->method, 
-                            (char *)r->uri, (char *)r->server->defn_name, 
-                            r->status);
-    return r;
-traceout:
-    AP_READ_REQUEST_FAILURE((uintptr_t)r);
-    return r;
-}
-
-
-static apr_status_t h2_task_process_request(h2_task *task)
+static apr_status_t h2_task_process_request(const h2_request *req, conn_rec *c)
 {
-    conn_rec *c = task->c;
     request_rec *r;
     conn_state_t *cs = c->cs;
 
-    r = h2_task_create_request(task);
+    r = h2_request_create_rec(req, c);
     if (r && (r->status == HTTP_OK)) {
         ap_update_child_status(c->sbh, SERVER_BUSY_READ, r);
         
@@ -379,6 +245,24 @@ static apr_status_t h2_task_process_request(h2_task *task)
     return APR_SUCCESS;
 }
 
+static int h2_task_process_conn(conn_rec* c)
+{
+    h2_ctx *ctx = h2_ctx_get(c);
+    
+    if (h2_ctx_is_task(ctx)) {
+        if (!ctx->task->serialize_headers) {
+            ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, 
+                          "h2_h2, processing request directly");
+            h2_task_process_request(ctx->task->request, c);
+            return DONE;
+        }
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, 
+                      "h2_task(%s), serialized handling", ctx->task->id);
+    }
+    return DECLINED;
+}
+
+
 
 
 
index 06a5d7aafbbcbed813733348591f6f7d388271ea..1d097ab3590a51ac39f70a73db47760a16adabbc 100644 (file)
@@ -83,17 +83,31 @@ static apr_status_t open_if_needed(h2_task_output *output, ap_filter_t *f,
             return APR_ECONNABORTED;
         }
         
+        output->trailers_passed = !!response->trailers;
         return h2_mplx_out_open(output->task->mplx, output->task->stream_id, 
                                 response, f, bb, output->task->io);
     }
     return APR_EOF;
 }
 
+static apr_table_t *get_trailers(h2_task_output *output)
+{
+    if (!output->trailers_passed) {
+        h2_response *response = h2_from_h1_get_response(output->from_h1);
+        if (response && response->trailers) {
+            output->trailers_passed = 1;
+            return response->trailers;
+        }
+    }
+    return NULL;
+}
+
 void h2_task_output_close(h2_task_output *output)
 {
     open_if_needed(output, NULL, NULL);
     if (output->state != H2_TASK_OUT_DONE) {
-        h2_mplx_out_close(output->task->mplx, output->task->stream_id);
+        h2_mplx_out_close(output->task->mplx, output->task->stream_id, 
+                          get_trailers(output));
         output->state = H2_TASK_OUT_DONE;
     }
 }
@@ -111,6 +125,7 @@ apr_status_t h2_task_output_write(h2_task_output *output,
                                   ap_filter_t* f, apr_bucket_brigade* bb)
 {
     apr_status_t status;
+    
     if (APR_BRIGADE_EMPTY(bb)) {
         ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c,
                       "h2_task_output(%s): empty write", output->task->id);
@@ -124,9 +139,10 @@ apr_status_t h2_task_output_write(h2_task_output *output,
                       output->task->id);
         return status;
     }
+    
     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c,
                   "h2_task_output(%s): write brigade", output->task->id);
     return h2_mplx_out_write(output->task->mplx, output->task->stream_id, 
-                             f, bb, output->task->io);
+                             f, bb, get_trailers(output), output->task->io);
 }
 
index a326c49096bdc7eb922cb69c840b2f4b1325a245..de03890ed694abd02b00429779b9a5be21046752 100644 (file)
@@ -38,6 +38,7 @@ struct h2_task_output {
     struct h2_task *task;
     h2_task_output_state_t state;
     struct h2_from_h1 *from_h1;
+    int trailers_passed;
 };
 
 h2_task_output *h2_task_output_create(struct h2_task *task, apr_pool_t *pool);
index 98a431b3bf1b0706418f005a8571d68e21879e45..950e43ff4a5dd6e1b8e26bf0af7ad3ca279f6b4f 100644 (file)
@@ -20,7 +20,7 @@
  * @macro
  * Version number of the h2 module as c string
  */
-#define MOD_HTTP2_VERSION "1.0.5-DEV"
+#define MOD_HTTP2_VERSION "1.0.7"
 
 /**
  * @macro
@@ -28,7 +28,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 0x010005
+#define MOD_HTTP2_VERSION_NUM 0x010007
 
 
 #endif /* mod_h2_h2_version_h */
index b11e8549fffefe444c0bb0e10a8de2086aa3ef87..3119cb081eed0b279a255f93d2b1814c2b402948 100644 (file)
@@ -96,8 +96,9 @@ h2_worker *h2_worker_create(int id,
     apr_allocator_t *allocator = NULL;
     apr_pool_t *pool = NULL;
     h2_worker *w;
+    apr_status_t status;
     
-    apr_status_t status = apr_allocator_create(&allocator);
+    status = apr_allocator_create(&allocator);
     if (status != APR_SUCCESS) {
         return NULL;
     }
@@ -126,7 +127,6 @@ h2_worker *h2_worker_create(int id,
         
         apr_pool_pre_cleanup_register(w->pool, w, cleanup_join_thread);
         apr_thread_create(&w->thread, attr, execute, w, w->pool);
-        apr_pool_create(&w->task_pool, w->pool);
     }
     return w;
 }
@@ -167,7 +167,11 @@ h2_task *h2_worker_create_task(h2_worker *worker, h2_mplx *m,
     /* Create a subpool from the worker one to be used for all things
      * with life-time of this task execution.
      */
+    if (!worker->task_pool) {
+        apr_pool_create(&worker->task_pool, worker->pool);
+    }
     task = h2_task_create(m->id, req, worker->task_pool, m, eos);
+    
     /* Link the task to the worker which provides useful things such
      * as mutex, a socket etc. */
     task->io = worker->io;