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

svn commit: r1720608 - in /httpd/httpd/trunk/modules/http2: config.m4 h2_conn.c h2_conn_io.c h2_conn_io.h h2_filter.c h2_filter.h h2_h2.c h2_session.c h2_session.h mod_http2.dsp

Author: icing
Date: Thu Dec 17 16:12:43 2015
New Revision: 1720608

URL: http://svn.apache.org/viewvc?rev=1720608&view=rev
Log:
moving session reading into a connection input filter

Added:
    httpd/httpd/trunk/modules/http2/h2_filter.c
    httpd/httpd/trunk/modules/http2/h2_filter.h
Modified:
    httpd/httpd/trunk/modules/http2/config.m4
    httpd/httpd/trunk/modules/http2/h2_conn.c
    httpd/httpd/trunk/modules/http2/h2_conn_io.c
    httpd/httpd/trunk/modules/http2/h2_conn_io.h
    httpd/httpd/trunk/modules/http2/h2_h2.c
    httpd/httpd/trunk/modules/http2/h2_session.c
    httpd/httpd/trunk/modules/http2/h2_session.h
    httpd/httpd/trunk/modules/http2/mod_http2.dsp

Modified: httpd/httpd/trunk/modules/http2/config.m4
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/config.m4?rev=1720608&r1=1720607&r2=1720608&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/config.m4 (original)
+++ httpd/httpd/trunk/modules/http2/config.m4 Thu Dec 17 16:12:43 2015
@@ -26,6 +26,7 @@ h2_config.lo dnl
 h2_conn.lo dnl
 h2_conn_io.lo dnl
 h2_ctx.lo dnl
+h2_filter.lo dnl
 h2_from_h1.lo dnl
 h2_h2.lo dnl
 h2_io.lo dnl

Modified: httpd/httpd/trunk/modules/http2/h2_conn.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_conn.c?rev=1720608&r1=1720607&r2=1720608&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_conn.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_conn.c Thu Dec 17 16:12:43 2015
@@ -28,6 +28,7 @@
 #include "h2_private.h"
 #include "h2_config.h"
 #include "h2_ctx.h"
+#include "h2_filter.h"
 #include "h2_mplx.h"
 #include "h2_session.h"
 #include "h2_stream.h"
@@ -72,8 +73,7 @@ apr_status_t h2_conn_child_init(apr_pool
     const h2_config *config = h2_config_sget(s);
     apr_status_t status = APR_SUCCESS;
     int minw = h2_config_geti(config, H2_CONF_MIN_WORKERS);
-    int maxw = h2_config_geti(config, H2_CONF_MAX_WORKERS);
-    
+    int maxw = h2_config_geti(config, H2_CONF_MAX_WORKERS);    
     int max_threads_per_child = 0;
     int idle_secs = 0;
     int i;
@@ -112,7 +112,10 @@ apr_status_t h2_conn_child_init(apr_pool
     workers = h2_workers_create(s, pool, minw, maxw);
     idle_secs = h2_config_geti(config, H2_CONF_MAX_WORKER_IDLE_SECS);
     h2_workers_set_max_idle_secs(workers, idle_secs);
-    
+ 
+    ap_register_input_filter("H2_IN", h2_filter_core_input,
+                             NULL, AP_FTYPE_CONNECTION);
+   
     return status;
 }
 
@@ -129,6 +132,7 @@ static module *h2_conn_mpm_module(void)
 apr_status_t h2_conn_setup(h2_ctx *ctx, conn_rec *c, request_rec *r)
 {
     h2_session *session;
+    h2_filter_core_in *in;
     
     if (!workers) {
         ap_log_cerror(APLOG_MARK, APLOG_ERR, 0, c, APLOGNO(02911) 
@@ -144,6 +148,11 @@ apr_status_t h2_conn_setup(h2_ctx *ctx,
     }
 
     h2_ctx_session_set(ctx, session);
+    
+    in = apr_pcalloc(session->pool, sizeof(*in));
+    in->session = session;
+    ap_add_input_filter("H2_IN", in, r, c);
+    
     ap_update_child_status_from_conn(c->sbh, SERVER_BUSY_READ, c);
 
     return APR_SUCCESS;
@@ -155,9 +164,16 @@ apr_status_t h2_conn_process(h2_ctx *ctx
     h2_session *session;
     
     session = h2_ctx_session_get(ctx);
+    if (session->c->cs) {
+        session->c->cs->sense = CONN_SENSE_DEFAULT;
+    }
 
     status = h2_session_process(session, async);
 
+    if (session->c->cs) {
+        session->c->cs->state = CONN_STATE_WRITE_COMPLETION;
+    }
+    
     if (status == APR_EOF) {
         ap_log_cerror( APLOG_MARK, APLOG_DEBUG, status, session->c,
                       "h2_session(%ld): done", session->id);
@@ -169,14 +185,9 @@ apr_status_t h2_conn_process(h2_ctx *ctx
         /* hereafter session will be gone */
     }
     else {
-        session->c->data_in_input_filters = 0;
         session->c->keepalive = AP_CONN_KEEPALIVE;
     }
     
-    if (session->c->cs) {
-        session->c->cs->state = CONN_STATE_WRITE_COMPLETION;
-    }
-    
     return DONE;
 }
 
@@ -261,11 +272,7 @@ apr_status_t h2_slave_setup(h2_task *tas
             break;
     }
     
-    /* TODO: we simulate that we had already a request on this connection.
-     * This keeps the mod_ssl SNI vs. Host name matcher from answering 
-     * 400 Bad Request
-     * when names do not match. We prefer a predictable 421 status.
-     */
+    /* Simulate that we had already a request on this connection. */
     task->c->keepalives = 1;
     
     return APR_SUCCESS;

Modified: httpd/httpd/trunk/modules/http2/h2_conn_io.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_conn_io.c?rev=1720608&r1=1720607&r2=1720608&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_conn_io.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_conn_io.c Thu Dec 17 16:12:43 2015
@@ -27,6 +27,7 @@
 #include "h2_config.h"
 #include "h2_conn_io.h"
 #include "h2_h2.h"
+#include "h2_session.h"
 #include "h2_util.h"
 
 #define TLS_DATA_MAX          (16*1024) 
@@ -50,7 +51,6 @@ apr_status_t h2_conn_io_init(h2_conn_io
                              apr_pool_t *pool)
 {
     io->connection         = c;
-    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);
@@ -93,112 +93,6 @@ int h2_conn_io_is_buffered(h2_conn_io *i
     return io->bufsize > 0;
 }
 
-static apr_status_t h2_conn_io_bucket_read(h2_conn_io *io,
-                                           apr_read_type_e block,
-                                           h2_conn_io_on_read_cb on_read_cb,
-                                           void *puser, int *pdone)
-{
-    apr_status_t status = APR_SUCCESS;
-    apr_size_t readlen = 0;
-    *pdone = 0;
-    
-    while (status == APR_SUCCESS && !*pdone && !APR_BRIGADE_EMPTY(io->input)) {
-        
-        apr_bucket* bucket = APR_BRIGADE_FIRST(io->input);
-        if (APR_BUCKET_IS_METADATA(bucket)) {
-            /* we do nothing regarding any meta here */
-        }
-        else {
-            const char *bucket_data = NULL;
-            apr_size_t bucket_length = 0;
-            status = apr_bucket_read(bucket, &bucket_data,
-                                     &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]),
-                                     bucket_data, bucket_length);
-                    ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, io->connection,
-                                  "h2_conn_io(%ld): read %d bytes: %s",
-                                  io->connection->id, (int)bucket_length, buffer);
-                }
-                
-                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;
-    }
-    return status;
-}
-
-apr_status_t h2_conn_io_read(h2_conn_io *io,
-                             apr_read_type_e block,
-                             h2_conn_io_on_read_cb on_read_cb,
-                             void *puser)
-{
-    apr_status_t status;
-    int done = 0;
-    ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, io->connection,
-                  "h2_conn_io: try read, block=%d", block);
-    
-    if (!APR_BRIGADE_EMPTY(io->input)) {
-        /* 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;
-        }
-    }
-
-    /* We only do a blocking read when we have no streams to process. So,
-     * in httpd scoreboard lingo, we are in a KEEPALIVE connection state.
-     * When reading non-blocking, we do have streams to process and update
-     * child with NULL request. That way, any current request information
-     * in the scoreboard is preserved.
-     */
-    if (block == APR_BLOCK_READ) {
-        ap_update_child_status_from_conn(io->connection->sbh, 
-                                         SERVER_BUSY_KEEPALIVE, 
-                                         io->connection);
-    }
-    else {
-        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);
-    switch (status) {
-        case APR_SUCCESS:
-            return h2_conn_io_bucket_read(io, block, on_read_cb, puser, &done);
-        case APR_EOF:
-        case APR_EAGAIN:
-            break;
-        default:
-            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, io->connection,
-                          "h2_conn_io: error reading");
-            break;
-    }
-    return status;
-}
-
 static apr_status_t pass_out(apr_bucket_brigade *bb, void *ctx) 
 {
     h2_conn_io *io = (h2_conn_io*)ctx;

Modified: httpd/httpd/trunk/modules/http2/h2_conn_io.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_conn_io.h?rev=1720608&r1=1720607&r2=1720608&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_conn_io.h (original)
+++ httpd/httpd/trunk/modules/http2/h2_conn_io.h Thu Dec 17 16:12:43 2015
@@ -17,6 +17,7 @@
 #define __mod_h2__h2_conn_io__
 
 struct h2_config;
+struct h2_session;
 
 /* h2_io is the basic handler of a httpd connection. It keeps two brigades,
  * one for input, one for output and works with the installed connection
@@ -26,7 +27,6 @@ struct h2_config;
  */
 typedef struct {
     conn_rec *connection;
-    apr_bucket_brigade *input;
     apr_bucket_brigade *output;
 
     int is_tls;
@@ -50,15 +50,6 @@ apr_status_t h2_conn_io_init(h2_conn_io
 
 int h2_conn_io_is_buffered(h2_conn_io *io);
 
-typedef apr_status_t (*h2_conn_io_on_read_cb)(const char *data, apr_size_t len,
-                                         apr_size_t *readlen, int *done,
-                                         void *puser);
-
-apr_status_t h2_conn_io_read(h2_conn_io *io,
-                        apr_read_type_e block,
-                        h2_conn_io_on_read_cb on_read_cb,
-                        void *puser);
-
 apr_status_t h2_conn_io_write(h2_conn_io *io,
                          const char *buf,
                          size_t length);

Added: httpd/httpd/trunk/modules/http2/h2_filter.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_filter.c?rev=1720608&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_filter.c (added)
+++ httpd/httpd/trunk/modules/http2/h2_filter.c Thu Dec 17 16:12:43 2015
@@ -0,0 +1,129 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <assert.h>
+
+#include <httpd.h>
+#include <http_core.h>
+#include <http_log.h>
+#include <http_connection.h>
+#include <scoreboard.h>
+
+#include "h2_private.h"
+#include "h2_session.h"
+#include "h2_conn_io.h"
+#include "h2_util.h"
+
+#include "h2_filter.h"
+
+
+static apr_status_t consume_brigade(h2_filter_core_in *in, 
+                                    apr_bucket_brigade *bb, 
+                                    apr_read_type_e block)
+{
+    apr_status_t status = APR_SUCCESS;
+    apr_size_t readlen = 0;
+    
+    while (status == APR_SUCCESS && !APR_BRIGADE_EMPTY(bb)) {
+        
+        apr_bucket* bucket = APR_BRIGADE_FIRST(bb);
+        if (APR_BUCKET_IS_METADATA(bucket)) {
+            /* we do nothing regarding any meta here */
+        }
+        else {
+            const char *bucket_data = NULL;
+            apr_size_t bucket_length = 0;
+            status = apr_bucket_read(bucket, &bucket_data,
+                                     &bucket_length, block);
+            
+            if (status == APR_SUCCESS && bucket_length > 0) {
+                apr_size_t consumed = 0;
+
+                status = h2_session_receive(in->session, bucket_data, 
+                                            bucket_length, &consumed);
+                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;
+    }
+    return status;
+}
+
+
+apr_status_t h2_filter_core_input(ap_filter_t* f,
+                                  apr_bucket_brigade* brigade,
+                                  ap_input_mode_t mode,
+                                  apr_read_type_e block,
+                                  apr_off_t readbytes) 
+{
+    h2_filter_core_in *in = f->ctx;
+    apr_status_t status = APR_SUCCESS;
+    
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, f->c,
+                  "core_input: read, block=%d, mode=%d, readbytes=%ld", 
+                  block, mode, (long)readbytes);
+    
+    if (mode == AP_MODE_INIT || mode == AP_MODE_SPECULATIVE) {
+        return ap_get_brigade(f->next, brigade, mode, block, readbytes);
+    }
+    
+    if (mode != AP_MODE_READBYTES) {
+        return (block == APR_BLOCK_READ)? APR_SUCCESS : APR_EAGAIN;
+    }
+    
+    if (!f->bb) {
+        f->bb = apr_brigade_create(in->session->pool, f->c->bucket_alloc);
+    }
+    
+    if (APR_BRIGADE_EMPTY(f->bb)) {
+        /* We only do a blocking read when we have no streams to process. So,
+         * in httpd scoreboard lingo, we are in a KEEPALIVE connection state.
+         * When reading non-blocking, we do have streams to process and update
+         * child with NULL request. That way, any current request information
+         * in the scoreboard is preserved.
+         */
+        if (block == APR_BLOCK_READ) {
+            ap_update_child_status_from_conn(f->c->sbh, 
+                                             SERVER_BUSY_KEEPALIVE, f->c);
+        }
+        else {
+            ap_update_child_status(f->c->sbh, SERVER_BUSY_READ, NULL);
+        }
+        
+        status = ap_get_brigade(f->next, f->bb, AP_MODE_READBYTES,
+                                block, readbytes);
+    }
+    
+    switch (status) {
+        case APR_SUCCESS:
+            return consume_brigade(in, f->bb, block);
+        case APR_EOF:
+        case APR_EAGAIN:
+            break;
+        default:
+            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, f->c,
+                          "h2_conn_io: error reading");
+            break;
+    }
+    return status;
+}

Added: httpd/httpd/trunk/modules/http2/h2_filter.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_filter.h?rev=1720608&view=auto
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_filter.h (added)
+++ httpd/httpd/trunk/modules/http2/h2_filter.h Thu Dec 17 16:12:43 2015
@@ -0,0 +1,32 @@
+/* Copyright 2015 greenbytes GmbH (https://www.greenbytes.de)
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __mod_h2__h2_filter__
+#define __mod_h2__h2_filter__
+
+struct h2_session;
+
+typedef struct {
+    struct h2_session *session;
+} h2_filter_core_in;
+
+apr_status_t h2_filter_core_input(ap_filter_t* filter,
+                                  apr_bucket_brigade* brigade,
+                                  ap_input_mode_t mode,
+                                  apr_read_type_e block,
+                                  apr_off_t readbytes);
+
+
+#endif /* __mod_h2__h2_filter__ */

Modified: httpd/httpd/trunk/modules/http2/h2_h2.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_h2.c?rev=1720608&r1=1720607&r2=1720608&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_h2.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_h2.c Thu Dec 17 16:12:43 2015
@@ -434,7 +434,6 @@ static int cipher_is_blacklisted(const c
 
 /*******************************************************************************
  * Hooks for processing incoming connections:
- * - pre_conn_before_tls switches SSL off for stream connections
  * - process_conn take over connection in case of h2
  */
 static int h2_h2_process_conn(conn_rec* c);
@@ -528,19 +527,15 @@ int h2_is_acceptable_connection(conn_rec
 int h2_allows_h2_direct(conn_rec *c)
 {
     const h2_config *cfg = h2_config_get(c);
+    int is_tls = h2_h2_is_tls(c);
+    const char *needed_protocol = is_tls? "h2" : "h2c";
     int h2_direct = h2_config_geti(cfg, H2_CONF_DIRECT);
     
     if (h2_direct < 0) {
-        if (h2_h2_is_tls(c)) {
-            /* disabled by default on TLS */
-            h2_direct = 0;
-        }
-        else {
-            /* enabled if "Protocols h2c" is configured */
-            h2_direct = ap_is_allowed_protocol(c, NULL, NULL, "h2c");
-        }
+        h2_direct = is_tls? 0 : 1;
     }
-    return !!h2_direct;
+    return (h2_direct 
+            && ap_is_allowed_protocol(c, NULL, NULL, needed_protocol));
 }
 
 int h2_allows_h2_upgrade(conn_rec *c)
@@ -592,45 +587,55 @@ int h2_h2_process_conn(conn_rec* c)
         return DECLINED;
     }
     
-    if (!ctx && c->keepalives == 0
-        && !strcmp(AP_PROTOCOL_HTTP1, ap_get_protocol(c))
-        && h2_allows_h2_direct(c) 
-        && h2_is_acceptable_connection(c, 1)) {
-        /* Fresh connection still is on http/1.1 and H2Direct is enabled. 
-         * Otherwise connection is in a fully acceptable state.
-         * -> peek at the first 24 incoming bytes
-         */
-        apr_bucket_brigade *temp;
-        char *s = NULL;
-        apr_size_t slen;
+    if (!ctx && c->keepalives == 0) {
+        const char *proto = ap_get_protocol(c);
         
-        temp = apr_brigade_create(c->pool, c->bucket_alloc);
-        status = ap_get_brigade(c->input_filters, temp,
-                                AP_MODE_SPECULATIVE, APR_BLOCK_READ, 24);
-        
-        if (status != APR_SUCCESS) {
-            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c,
-                          "h2_h2, error reading 24 bytes speculative");
-            apr_brigade_destroy(temp);
-            return DECLINED;
-        }
-        
-        apr_brigade_pflatten(temp, &s, &slen, c->pool);
-        if ((slen >= 24) && !memcmp(H2_MAGIC_TOKEN, s, 24)) {
-            ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
-                          "h2_h2, direct mode detected");
-            if (!ctx) {
-                ctx = h2_ctx_get(c, 1);
+        if (APLOGctrace1(c)) {
+            ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c, "h2_h2, process_conn, "
+                          "new connection using protocol '%s', direct=%d, "
+                          "tls acceptable=%d", proto, h2_allows_h2_direct(c), 
+                          h2_is_acceptable_connection(c, 1));
+        }
+        
+        if (!strcmp(AP_PROTOCOL_HTTP1, proto)
+            && h2_allows_h2_direct(c) 
+            && h2_is_acceptable_connection(c, 1)) {
+            /* Fresh connection still is on http/1.1 and H2Direct is enabled. 
+             * Otherwise connection is in a fully acceptable state.
+             * -> peek at the first 24 incoming bytes
+             */
+            apr_bucket_brigade *temp;
+            char *s = NULL;
+            apr_size_t slen;
+            
+            temp = apr_brigade_create(c->pool, c->bucket_alloc);
+            status = ap_get_brigade(c->input_filters, temp,
+                                    AP_MODE_SPECULATIVE, APR_BLOCK_READ, 24);
+            
+            if (status != APR_SUCCESS) {
+                ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c,
+                              "h2_h2, error reading 24 bytes speculative");
+                apr_brigade_destroy(temp);
+                return DECLINED;
             }
-            h2_ctx_protocol_set(ctx, h2_h2_is_tls(c)? "h2" : "h2c");
-        }
-        else {
-            ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
-                          "h2_h2, not detected in %d bytes: %s", 
-                          (int)slen, s);
+            
+            apr_brigade_pflatten(temp, &s, &slen, c->pool);
+            if ((slen >= 24) && !memcmp(H2_MAGIC_TOKEN, s, 24)) {
+                ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
+                              "h2_h2, direct mode detected");
+                if (!ctx) {
+                    ctx = h2_ctx_get(c, 1);
+                }
+                h2_ctx_protocol_set(ctx, h2_h2_is_tls(c)? "h2" : "h2c");
+            }
+            else {
+                ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c,
+                              "h2_h2, not detected in %d bytes: %s", 
+                              (int)slen, s);
+            }
+            
+            apr_brigade_destroy(temp);
         }
-        
-        apr_brigade_destroy(temp);
     }
 
     if (ctx) {

Modified: httpd/httpd/trunk/modules/http2/h2_session.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_session.c?rev=1720608&r1=1720607&r2=1720608&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_session.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_session.c Thu Dec 17 16:12:43 2015
@@ -1065,36 +1065,6 @@ h2_stream *h2_session_get_stream(h2_sess
     return session->last_stream;
 }
 
-/* h2_io_on_read_cb implementation that offers the data read
- * directly to the session for consumption.
- */
-static apr_status_t session_receive(const char *data, apr_size_t len,
-                                    apr_size_t *readlen, int *done,
-                                    void *puser)
-{
-    h2_session *session = (h2_session *)puser;
-    AP_DEBUG_ASSERT(session);
-    if (len > 0) {
-        ssize_t n = nghttp2_session_mem_recv(session->ngh2,
-                                             (const uint8_t *)data, len);
-        if (n < 0) {
-            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_EGENERAL,
-                          session->c,
-                          "h2_session: nghttp2_session_mem_recv error %d",
-                          (int)n);
-            if (nghttp2_is_fatal((int)n)) {
-                *done = 1;
-                h2_session_abort_int(session, (int)n);
-                return APR_EGENERAL;
-            }
-        }
-        else {
-            *readlen = n;
-        }
-    }
-    return APR_SUCCESS;
-}
-
 apr_status_t h2_session_close(h2_session *session)
 {
     AP_DEBUG_ASSERT(session);
@@ -1127,9 +1097,7 @@ static ssize_t stream_data_cb(nghttp2_se
      * to find out how much of the requested length we can send without
      * blocking.
      * Indicate EOS when we encounter it or DEFERRED if the stream
-     * should be suspended.
-     * TODO: for handling of TRAILERS,  the EOF indication needs
-     * to be aware of that.
+     * should be suspended. Beware of trailers.
      */
  
     (void)ng2s;
@@ -1587,46 +1555,83 @@ static apr_status_t h2_session_send(h2_s
     return APR_SUCCESS;
 }
 
+apr_status_t h2_session_receive(h2_session *session, 
+                                const char *data, apr_size_t len,
+                                apr_size_t *readlen)
+{
+    if (len > 0) {
+        ssize_t n = nghttp2_session_mem_recv(session->ngh2,
+                                             (const uint8_t *)data, len);
+        if (n < 0) {
+            ap_log_cerror(APLOG_MARK, APLOG_DEBUG, APR_EGENERAL,
+                          session->c,
+                          "h2_session: nghttp2_session_mem_recv error %d",
+                          (int)n);
+            if (nghttp2_is_fatal((int)n)) {
+                h2_session_abort(session, 0, (int)n);
+                return APR_EGENERAL;
+            }
+        }
+        else {
+            *readlen = n;
+        }
+    }
+    return APR_SUCCESS;
+}
+
+
+
 static apr_status_t h2_session_read(h2_session *session, int block)
 {
-    apr_status_t status;
     
-    status = h2_conn_io_read(&session->io, 
-                             block? APR_BLOCK_READ : APR_NONBLOCK_READ, 
-                             session_receive, session);
-    switch (status) {
-        case APR_SUCCESS:
-            /* successful read, reset our idle timers */
-            session->wait_micros = 0;
-            break;
-        case APR_EAGAIN:
-            break;
-        default:
-            if (APR_STATUS_IS_ETIMEDOUT(status)
-                || APR_STATUS_IS_ECONNABORTED(status)
-                || APR_STATUS_IS_ECONNRESET(status)
-                || APR_STATUS_IS_EOF(status)
-                || APR_STATUS_IS_EBADF(status)) {
-                /* common status for a client that has left */
-                ap_log_cerror( APLOG_MARK, APLOG_DEBUG, status, session->c,
-                              "h2_session(%ld): terminating",
-                              session->id);
-                /* Stolen from mod_reqtimeout to speed up lingering when
-                 * a read timeout happened.
-                 */
-                apr_table_setn(session->c->notes, "short-lingering-close", "1");
-            }
-            else {
-                /* uncommon status, log on INFO so that we see this */
-                ap_log_cerror( APLOG_MARK, APLOG_INFO, status, session->c,
-                              APLOGNO(02950) 
-                              "h2_session(%ld): error reading, terminating",
-                              session->id);
-            }
-            h2_session_abort(session, status, 0);
-            break;
+    while (1) {
+        apr_status_t status;
+        
+        /* H2_IN filter handles all incoming data against the session.
+         * We just pull at the filter chain to make it happen */
+        status = ap_get_brigade(session->c->input_filters,
+                                session->bbtmp, AP_MODE_READBYTES,
+                                block? APR_BLOCK_READ : APR_NONBLOCK_READ,
+                                APR_BUCKET_BUFF_SIZE);
+        /* get rid of any possible data we do not expect to get */
+        apr_brigade_cleanup(session->bbtmp); 
+
+        switch (status) {
+            case APR_SUCCESS:
+                /* successful read, reset our idle timers */
+                session->wait_micros = 0;
+                if (block) {
+                    return APR_SUCCESS;
+                }
+                break;
+            case APR_EAGAIN:
+                return status;
+            default:
+                if (APR_STATUS_IS_ETIMEDOUT(status)
+                    || APR_STATUS_IS_ECONNABORTED(status)
+                    || APR_STATUS_IS_ECONNRESET(status)
+                    || APR_STATUS_IS_EOF(status)
+                    || APR_STATUS_IS_EBADF(status)) {
+                    /* common status for a client that has left */
+                    ap_log_cerror( APLOG_MARK, APLOG_DEBUG, status, session->c,
+                                  "h2_session(%ld): terminating",
+                                  session->id);
+                    /* Stolen from mod_reqtimeout to speed up lingering when
+                     * a read timeout happened.
+                     */
+                    apr_table_setn(session->c->notes, "short-lingering-close", "1");
+                }
+                else {
+                    /* uncommon status, log on INFO so that we see this */
+                    ap_log_cerror( APLOG_MARK, APLOG_INFO, status, session->c,
+                                  APLOGNO(02950) 
+                                  "h2_session(%ld): error reading, terminating",
+                                  session->id);
+                }
+                h2_session_abort(session, status, 0);
+                return status;
+        }
     }
-    return status;
 }
 
 static apr_status_t h2_session_submit(h2_session *session)
@@ -1701,6 +1706,58 @@ apr_status_t h2_session_process(h2_sessi
         }
         
         got_streams = !h2_stream_set_is_empty(session->streams);
+
+        /* If we want client data, see if some is there. */
+        if (nghttp2_session_want_read(session->ngh2)) {
+            int idle      = (session->frames_received > 2 && !got_streams);
+            int may_block = ((session->frames_received <= 1) 
+                             || idle
+                             || (!h2_stream_set_has_unsubmitted(session->streams)
+                                 && !h2_stream_set_has_suspended(session->streams)));
+            
+            status = h2_session_read(session, may_block && !async);
+            
+            ap_log_cerror( APLOG_MARK, APLOG_TRACE1, status, session->c,
+                          "h2_session(%ld): process -> read", session->id);
+            if (status == APR_SUCCESS) {
+                have_read = 1;
+                got_streams = !h2_stream_set_is_empty(session->streams);
+                if (session->reprioritize) {
+                    ap_log_cerror( APLOG_MARK, APLOG_TRACE2, status, session->c,
+                                  "h2_session(%ld): process -> reprioritize", session->id);
+                    h2_mplx_reprioritize(session->mplx, stream_pri_cmp, session);
+                    session->reprioritize = 0;
+                }
+            }
+            else if (status == APR_EAGAIN) {
+                /* FIXME: disabling this as event currently discards
+                 * connections on load when we return. */
+                if (async && may_block && 0) {
+                    /* There is nothing to read and we are in a state where we
+                     * have nothing to write until new input comes. Return to
+                     * our caller so that the MPM may schedule us again when
+                     * read seems possible.
+                     */
+                    ap_log_cerror( APLOG_MARK, APLOG_TRACE1, status, session->c,
+                                  "h2_session(%ld): process -> BLOCK_READ, "
+                                  "frames_received=%d, got_streams=%d, "
+                                  "have_written=%d", 
+                                  session->id, (int)session->frames_received,
+                                  (int)got_streams, (int)have_written);
+                    h2_conn_io_flush(&session->io);
+                    if (session->c->cs) {
+                        session->c->cs->sense = CONN_SENSE_WANT_READ;
+                    }
+                    return APR_SUCCESS;
+                }
+            }
+            else {
+                ap_log_cerror( APLOG_MARK, APLOG_DEBUG, status, session->c,
+                              "h2_session(%ld): failed read", session->id);
+                return status;
+            }
+        }
+            
         if (got_streams) {
             ap_log_cerror( APLOG_MARK, APLOG_TRACE2, status, session->c,
                           "h2_session(%ld): process -> check resume", session->id);
@@ -1745,52 +1802,6 @@ apr_status_t h2_session_process(h2_sessi
             }
         }
         
-        /* If we want client data, see if some is there. */
-        if (nghttp2_session_want_read(session->ngh2)) {
-            int idle      = (session->frames_received > 2 && !got_streams);
-            int may_block = ((session->frames_received <= 1) 
-                             || (idle && !async)
-                             || (!have_written && !h2_stream_set_has_unsubmitted(session->streams)
-                                 && !h2_stream_set_has_suspended(session->streams)));
-            status = h2_session_read(session, may_block);
-            
-            ap_log_cerror( APLOG_MARK, APLOG_TRACE1, status, session->c,
-                          "h2_session(%ld): process -> read", session->id);
-            if (status == APR_SUCCESS) {
-                have_read = 1;
-                got_streams = !h2_stream_set_is_empty(session->streams);
-                if (session->reprioritize) {
-                    ap_log_cerror( APLOG_MARK, APLOG_TRACE2, status, session->c,
-                                  "h2_session(%ld): process -> reprioritize", session->id);
-                    h2_mplx_reprioritize(session->mplx, stream_pri_cmp, session);
-                    session->reprioritize = 0;
-                }
-            }
-            else if (status == APR_EAGAIN && idle && async) {
-                /* There is nothing to read and we are in a state where we
-                 * have nothing to write until new input comes. Return to
-                 * our caller so that the MPM may schedule us again when
-                 * read seems possible.
-                 */
-                ap_log_cerror( APLOG_MARK, APLOG_TRACE1, status, session->c,
-                              "h2_session(%ld): process -> BLOCK_READ, "
-                              "frames_received=%d, got_streams=%d, "
-                              "have_written=%d", 
-                              session->id, (int)session->frames_received,
-                              (int)got_streams, (int)have_written);
-                if (have_written) {
-                    h2_conn_io_flush(&session->io);
-                }
-                if (session->c->cs) {
-                    session->c->cs->state = CONN_STATE_WRITE_COMPLETION;
-                    session->c->cs->sense = (have_written? 
-                                             CONN_SENSE_DEFAULT
-                                             : CONN_SENSE_WANT_READ);
-                }
-                return APR_SUCCESS;
-            }
-        }
-            
         if (!have_read && !have_written) {
             if (session->wait_micros == 0) {
                 session->wait_micros = 10;

Modified: httpd/httpd/trunk/modules/http2/h2_session.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_session.h?rev=1720608&r1=1720607&r2=1720608&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_session.h (original)
+++ httpd/httpd/trunk/modules/http2/h2_session.h Thu Dec 17 16:12:43 2015
@@ -62,6 +62,7 @@ struct h2_session {
                                      * of 'h2c', NULL otherwise */
     server_rec *s;                  /* server/vhost we're starting on */
     const struct h2_config *config; /* Relevant config for this session */
+    
     int started;
     int aborted;                    /* this session is being aborted */
     int reprioritize;               /* scheduled streams priority needs to 
@@ -83,6 +84,7 @@ struct h2_session {
     struct apr_thread_cond_t *iowait; /* our cond when trywaiting for data */
     
     h2_conn_io io;                  /* io on httpd conn filters */
+
     struct h2_mplx *mplx;           /* multiplexer for stream data */
     
     struct h2_stream *last_stream;  /* last stream worked with */
@@ -121,6 +123,14 @@ h2_session *h2_session_rcreate(request_r
                                struct h2_workers *workers);
 
 /**
+ * Recieve len bytes of raw HTTP/2 input data. Return the amount
+ * consumed and if the session is done.
+ */
+apr_status_t h2_session_receive(h2_session *session, 
+                                const char *data, apr_size_t len,
+                                apr_size_t *readlen);
+
+/**
  * Process the given HTTP/2 session until it is ended or a fatal
  * error occured.
  *

Modified: httpd/httpd/trunk/modules/http2/mod_http2.dsp
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/mod_http2.dsp?rev=1720608&r1=1720607&r2=1720608&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/mod_http2.dsp (original)
+++ httpd/httpd/trunk/modules/http2/mod_http2.dsp Thu Dec 17 16:12:43 2015
@@ -129,6 +129,10 @@ SOURCE=./h2_ctx.c
 # End Source File
 # Begin Source File
 
+SOURCE=./h2_filter.c
+# End Source File
+# Begin Source File
+
 SOURCE=./h2_from_h1.c
 # End Source File
 # Begin Source File