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 2016/03/02 12:21:29 UTC

svn commit: r1733259 [3/4] - in /httpd/httpd/branches/2.4.x: ./ modules/http2/

Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_response.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_response.h?rev=1733259&r1=1733258&r2=1733259&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_response.h (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_response.h Wed Mar  2 11:21:28 2016
@@ -16,18 +16,7 @@
 #ifndef __mod_h2__h2_response__
 #define __mod_h2__h2_response__
 
-struct h2_request;
-struct h2_push;
-
-typedef struct h2_response {
-    int         stream_id;
-    int         rst_error;
-    int         http_status;
-    apr_off_t   content_length;
-    apr_table_t *headers;
-    apr_table_t *trailers;
-    const char  *sos_filter;
-} h2_response;
+#include "h2.h"
 
 /**
  * Create the response from the status and parsed header lines.

Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_session.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_session.c?rev=1733259&r1=1733258&r2=1733259&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_session.c (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_session.c Wed Mar  2 11:21:28 2016
@@ -14,10 +14,13 @@
  */
 
 #include <assert.h>
+#include <stddef.h>
 #include <apr_thread_cond.h>
 #include <apr_base64.h>
 #include <apr_strings.h>
 
+#include <ap_mpm.h>
+
 #include <httpd.h>
 #include <http_core.h>
 #include <http_config.h>
@@ -36,7 +39,6 @@
 #include "h2_request.h"
 #include "h2_response.h"
 #include "h2_stream.h"
-#include "h2_stream_set.h"
 #include "h2_from_h1.h"
 #include "h2_task.h"
 #include "h2_session.h"
@@ -45,8 +47,6 @@
 #include "h2_workers.h"
 
 
-static int frame_print(const nghttp2_frame *frame, char *buffer, size_t maxlen);
-
 static int h2_session_status_from_apr_status(apr_status_t rv)
 {
     if (rv == APR_SUCCESS) {
@@ -89,11 +89,12 @@ h2_stream *h2_session_open_stream(h2_ses
     }
     else {
         apr_pool_create(&stream_pool, session->pool);
+        apr_pool_tag(stream_pool, "h2_stream");
     }
     
     stream = h2_stream_open(stream_id, stream_pool, session);
     
-    h2_stream_set_add(session->streams, stream);
+    h2_ihash_add(session->streams, stream);
     if (H2_STREAM_CLIENT_INITIATED(stream_id)
         && stream_id > session->max_stream_received) {
         ++session->requests_received;
@@ -103,8 +104,6 @@ h2_stream *h2_session_open_stream(h2_ses
     return stream;
 }
 
-#ifdef H2_NG2_STREAM_API
-
 /**
  * Determine the importance of streams when scheduling tasks.
  * - if both stream depend on the same one, compare weights
@@ -158,20 +157,6 @@ static int stream_pri_cmp(int sid1, int
     return spri_cmp(sid1, s1, sid2, s2, session);
 }
 
-#else /* ifdef H2_NG2_STREAM_API */
-
-/* In absence of nghttp2_stream API, which gives information about
- * priorities since nghttp2 1.3.x, we just sort the streams by
- * their identifier, aka. order of arrival.
- */
-static int stream_pri_cmp(int sid1, int sid2, void *ctx)
-{
-    (void)ctx;
-    return sid1 - sid2;
-}
-
-#endif /* (ifdef else) H2_NG2_STREAM_API */
-
 static apr_status_t stream_schedule(h2_session *session,
                                     h2_stream *stream, int eos)
 {
@@ -214,7 +199,7 @@ static int on_invalid_frame_recv_cb(nght
     if (APLOGcdebug(session->c)) {
         char buffer[256];
         
-        frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0]));
+        h2_util_frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0]));
         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03063)
                       "h2_session(%ld): recv unknown FRAME[%s], frames=%ld/%ld (r/s)",
                       session->id, buffer, (long)session->frames_received,
@@ -375,7 +360,7 @@ static int on_frame_recv_cb(nghttp2_sess
     if (APLOGcdebug(session->c)) {
         char buffer[256];
         
-        frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0]));
+        h2_util_frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0]));
         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03066)
                       "h2_session(%ld): recv FRAME[%s], frames=%ld/%ld (r/s)",
                       session->id, buffer, (long)session->frames_received,
@@ -464,8 +449,8 @@ static int on_frame_recv_cb(nghttp2_sess
             if (APLOGctrace2(session->c)) {
                 char buffer[256];
                 
-                frame_print(frame, buffer,
-                            sizeof(buffer)/sizeof(buffer[0]));
+                h2_util_frame_print(frame, buffer,
+                                    sizeof(buffer)/sizeof(buffer[0]));
                 ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, session->c,
                               "h2_session: on_frame_rcv %s", buffer);
             }
@@ -584,7 +569,7 @@ static int on_send_data_cb(nghttp2_sessi
     
     if (status == APR_SUCCESS) {
         stream->data_frames_sent++;
-        h2_conn_io_consider_flush(&session->io);
+        h2_conn_io_consider_pass(&session->io);
         return 0;
     }
     else {
@@ -605,13 +590,22 @@ static int on_frame_send_cb(nghttp2_sess
     if (APLOGcdebug(session->c)) {
         char buffer[256];
         
-        frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0]));
+        h2_util_frame_print(frame, buffer, sizeof(buffer)/sizeof(buffer[0]));
         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03068)
                       "h2_session(%ld): sent FRAME[%s], frames=%ld/%ld (r/s)",
                       session->id, buffer, (long)session->frames_received,
                      (long)session->frames_sent);
     }
     ++session->frames_sent;
+    switch (frame->hd.type) {
+        case NGHTTP2_HEADERS:
+        case NGHTTP2_DATA:
+            /* no explicit flushing necessary */
+            break;
+        default:
+            session->flush = 1;
+            break;
+    }
     return 0;
 }
 
@@ -672,23 +666,20 @@ static void h2_session_destroy(h2_sessio
     if (APLOGctrace1(session->c)) {
         ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
                       "h2_session(%ld): destroy, %d streams open",
-                      session->id, (int)h2_stream_set_size(session->streams));
+                      session->id, (int)h2_ihash_count(session->streams));
     }
     if (session->mplx) {
         h2_mplx_set_consumed_cb(session->mplx, NULL, NULL);
         h2_mplx_release_and_join(session->mplx, session->iowait);
         session->mplx = NULL;
     }
-    if (session->streams) {
-        h2_stream_set_destroy(session->streams);
-        session->streams = NULL;
-    }
     if (session->pool) {
         apr_pool_destroy(session->pool);
     }
 }
 
-static apr_status_t h2_session_shutdown(h2_session *session, int reason, const char *msg)
+static apr_status_t h2_session_shutdown(h2_session *session, int reason, 
+                                        const char *msg, int force_close)
 {
     apr_status_t status = APR_SUCCESS;
     const char *err = msg;
@@ -701,11 +692,16 @@ static apr_status_t h2_session_shutdown(
                           h2_mplx_get_max_stream_started(session->mplx), 
                           reason, (uint8_t*)err, err? strlen(err):0);
     status = nghttp2_session_send(session->ngh2);
-    h2_conn_io_flush(&session->io);
+    h2_conn_io_pass(&session->io, 1);
     ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03069)
                   "session(%ld): sent GOAWAY, err=%d, msg=%s", 
                   session->id, reason, err? err : "");
     dispatch_event(session, H2_SESSION_EV_LOCAL_GOAWAY, reason, err);
+    
+    if (force_close) {
+        h2_mplx_abort(session->mplx);
+    }
+    
     return status;
 }
 
@@ -796,6 +792,7 @@ static h2_session *h2_session_create_int
     if (status != APR_SUCCESS) {
         return NULL;
     }
+    apr_pool_tag(pool, "h2_session");
 
     session = apr_pcalloc(pool, sizeof(h2_session));
     if (session) {
@@ -815,24 +812,16 @@ static h2_session *h2_session_create_int
         
         session->max_stream_count = h2_config_geti(session->config, H2_CONF_MAX_STREAMS);
         session->max_stream_mem = h2_config_geti(session->config, H2_CONF_STREAM_MAX_MEM);
-        session->timeout_secs = h2_config_geti(session->config, H2_CONF_TIMEOUT_SECS);
-        if (session->timeout_secs <= 0) {
-            session->timeout_secs = apr_time_sec(session->s->timeout);
-        }
-        session->keepalive_secs = h2_config_geti(session->config, H2_CONF_KEEPALIVE_SECS);
-        if (session->keepalive_secs <= 0) {
-            session->keepalive_secs = apr_time_sec(session->s->keep_alive_timeout);
-        }
-        
+
         status = apr_thread_cond_create(&session->iowait, session->pool);
         if (status != APR_SUCCESS) {
             return NULL;
         }
         
-        session->streams = h2_stream_set_create(session->pool, session->max_stream_count);
-        
+        session->streams = h2_ihash_create(session->pool,offsetof(h2_stream, id));
         session->workers = workers;
-        session->mplx = h2_mplx_create(c, session->pool, session->config, workers);
+        session->mplx = h2_mplx_create(c, session->pool, session->config, 
+                                       session->s->timeout, workers);
         
         h2_mplx_set_consumed_cb(session->mplx, update_window, session);
         
@@ -896,12 +885,9 @@ static h2_session *h2_session_create_int
         
         if (APLOGcdebug(c)) {
             ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(03200)
-                          "session(%ld) created, timeout=%d, keepalive_timeout=%d, "
-                          "max_streams=%d, stream_mem=%d, push_diary(type=%d,N=%d)",
-                          session->id, session->timeout_secs, session->keepalive_secs,
-                          (int)session->max_stream_count, (int)session->max_stream_mem,
-                          session->push_diary->dtype,
-                          (int)session->push_diary->N);
+                          "session(%ld) created, max_streams=%d, stream_mem=%d, push_diary(type=%d,N=%d)",
+                          session->id, (int)session->max_stream_count, (int)session->max_stream_mem,
+                          session->push_diary->dtype, (int)session->push_diary->N);
         }
     }
     return session;
@@ -1031,6 +1017,8 @@ static apr_status_t h2_session_start(h2_
                           nghttp2_strerror(*rv));        
         }
     }
+    
+    h2_conn_io_pass(&session->io, 1);
     return status;
 }
 
@@ -1039,8 +1027,9 @@ typedef struct {
     int resume_count;
 } resume_ctx;
 
-static int resume_on_data(void *ctx, h2_stream *stream)
+static int resume_on_data(void *ctx, void *val)
 {
+    h2_stream *stream = val;
     resume_ctx *rctx = (resume_ctx*)ctx;
     h2_session *session = rctx->session;
     AP_DEBUG_ASSERT(session);
@@ -1066,7 +1055,7 @@ static int resume_on_data(void *ctx, h2_
 static int h2_session_resume_streams_with_data(h2_session *session)
 {
     AP_DEBUG_ASSERT(session);
-    if (!h2_stream_set_is_empty(session->streams)
+    if (!h2_ihash_is_empty(session->streams)
         && session->mplx && !session->mplx->aborted) {
         resume_ctx ctx;
         
@@ -1075,7 +1064,7 @@ static int h2_session_resume_streams_wit
 
         /* Resume all streams where we have data in the out queue and
          * which had been suspended before. */
-        h2_stream_set_iter(session->streams, resume_on_data, &ctx);
+        h2_ihash_iter(session->streams, resume_on_data, &ctx);
         return ctx.resume_count;
     }
     return 0;
@@ -1084,7 +1073,7 @@ static int h2_session_resume_streams_wit
 h2_stream *h2_session_get_stream(h2_session *session, int stream_id)
 {
     if (!session->last_stream || stream_id != session->last_stream->id) {
-        session->last_stream = h2_stream_set_get(session->streams, stream_id);
+        session->last_stream = h2_ihash_get(session->streams, stream_id);
     }
     return session->last_stream;
 }
@@ -1445,16 +1434,16 @@ apr_status_t h2_session_stream_destroy(h
     apr_pool_t *pool = h2_stream_detach_pool(stream);
 
     /* this may be called while the session has already freed
-     * some internal structures. */
+     * some internal structures or even when the mplx is locked. */
     if (session->mplx) {
         h2_mplx_stream_done(session->mplx, stream->id, stream->rst_error);
-        if (session->last_stream == stream) {
-            session->last_stream = NULL;
-        }
     }
     
+    if (session->last_stream == stream) {
+        session->last_stream = NULL;
+    }
     if (session->streams) {
-        h2_stream_set_remove(session->streams, stream->id);
+        h2_ihash_remove(session->streams, stream->id);
     }
     h2_stream_destroy(stream);
     
@@ -1468,83 +1457,6 @@ apr_status_t h2_session_stream_destroy(h
     return APR_SUCCESS;
 }
 
-static int frame_print(const nghttp2_frame *frame, char *buffer, size_t maxlen)
-{
-    char scratch[128];
-    size_t s_len = sizeof(scratch)/sizeof(scratch[0]);
-    
-    switch (frame->hd.type) {
-        case NGHTTP2_DATA: {
-            return apr_snprintf(buffer, maxlen,
-                                "DATA[length=%d, flags=%d, stream=%d, padlen=%d]",
-                                (int)frame->hd.length, frame->hd.flags,
-                                frame->hd.stream_id, (int)frame->data.padlen);
-        }
-        case NGHTTP2_HEADERS: {
-            return apr_snprintf(buffer, maxlen,
-                                "HEADERS[length=%d, hend=%d, stream=%d, eos=%d]",
-                                (int)frame->hd.length,
-                                !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS),
-                                frame->hd.stream_id,
-                                !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM));
-        }
-        case NGHTTP2_PRIORITY: {
-            return apr_snprintf(buffer, maxlen,
-                                "PRIORITY[length=%d, flags=%d, stream=%d]",
-                                (int)frame->hd.length,
-                                frame->hd.flags, frame->hd.stream_id);
-        }
-        case NGHTTP2_RST_STREAM: {
-            return apr_snprintf(buffer, maxlen,
-                                "RST_STREAM[length=%d, flags=%d, stream=%d]",
-                                (int)frame->hd.length,
-                                frame->hd.flags, frame->hd.stream_id);
-        }
-        case NGHTTP2_SETTINGS: {
-            if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
-                return apr_snprintf(buffer, maxlen,
-                                    "SETTINGS[ack=1, stream=%d]",
-                                    frame->hd.stream_id);
-            }
-            return apr_snprintf(buffer, maxlen,
-                                "SETTINGS[length=%d, stream=%d]",
-                                (int)frame->hd.length, frame->hd.stream_id);
-        }
-        case NGHTTP2_PUSH_PROMISE: {
-            return apr_snprintf(buffer, maxlen,
-                                "PUSH_PROMISE[length=%d, hend=%d, stream=%d]",
-                                (int)frame->hd.length,
-                                !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS),
-                                frame->hd.stream_id);
-        }
-        case NGHTTP2_PING: {
-            return apr_snprintf(buffer, maxlen,
-                                "PING[length=%d, ack=%d, stream=%d]",
-                                (int)frame->hd.length,
-                                frame->hd.flags&NGHTTP2_FLAG_ACK,
-                                frame->hd.stream_id);
-        }
-        case NGHTTP2_GOAWAY: {
-            size_t len = (frame->goaway.opaque_data_len < s_len)?
-            frame->goaway.opaque_data_len : s_len-1;
-            memcpy(scratch, frame->goaway.opaque_data, len);
-            scratch[len+1] = '\0';
-            return apr_snprintf(buffer, maxlen, "GOAWAY[error=%d, reason='%s']",
-                                frame->goaway.error_code, scratch);
-        }
-        case NGHTTP2_WINDOW_UPDATE: {
-            return apr_snprintf(buffer, maxlen,
-                                "WINDOW_UPDATE[length=%d, stream=%d]",
-                                (int)frame->hd.length, frame->hd.stream_id);
-        }
-        default:
-            return apr_snprintf(buffer, maxlen,
-                                "type=%d[length=%d, flags=%d, stream=%d]",
-                                frame->hd.type, (int)frame->hd.length,
-                                frame->hd.flags, frame->hd.stream_id);
-    }
-}
-
 int h2_session_push_enabled(h2_session *session)
 {
     /* iff we can and they can */
@@ -1555,7 +1467,21 @@ int h2_session_push_enabled(h2_session *
 
 static apr_status_t h2_session_send(h2_session *session)
 {
-    int rv = nghttp2_session_send(session->ngh2);
+    apr_interval_time_t saved_timeout;
+    int rv;
+    apr_socket_t *socket;
+    
+    socket = ap_get_conn_socket(session->c);
+    if (socket) {
+        apr_socket_timeout_get(socket, &saved_timeout);
+        apr_socket_timeout_set(socket, session->s->timeout);
+    }
+    
+    rv = nghttp2_session_send(session->ngh2);
+    
+    if (socket) {
+        apr_socket_timeout_set(socket, saved_timeout);
+    }
     if (rv != 0) {
         if (nghttp2_is_fatal(rv)) {
             dispatch_event(session, H2_SESSION_EV_PROTO_ERROR, rv, nghttp2_strerror(rv));
@@ -1594,13 +1520,13 @@ static apr_status_t h2_session_receive(v
     return APR_SUCCESS;
 }
 
-static apr_status_t h2_session_read(h2_session *session, int block, int loops)
+static apr_status_t h2_session_read(h2_session *session, int block)
 {
     apr_status_t status, rstatus = APR_EAGAIN;
     conn_rec *c = session->c;
-    int i;
+    apr_off_t read_start = session->io.bytes_read;
     
-    for (i = 0; i < loops; ++i) {
+    while (1) {
         /* 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(c->input_filters,
@@ -1625,7 +1551,7 @@ static apr_status_t h2_session_read(h2_s
             case APR_TIMEUP:
                 return status;
             default:
-                if (!i) {
+                if (session->io.bytes_read == read_start) {
                     /* first attempt failed */
                     if (APR_STATUS_IS_ETIMEDOUT(status)
                         || APR_STATUS_IS_ECONNABORTED(status)
@@ -1652,16 +1578,56 @@ static apr_status_t h2_session_read(h2_s
         if (!is_accepting_streams(session)) {
             break;
         }
+        if ((session->io.bytes_read - read_start) > (64*1024)) {
+            /* read enough in one go, give write a chance */
+            ap_log_cerror(APLOG_MARK, APLOG_TRACE2, status, c,
+                          "h2_session(%ld): read 64k, returning", session->id);
+            break;
+        }
     }
     return rstatus;
 }
 
+static int unsubmitted_iter(void *ctx, void *val)
+{
+    h2_stream *stream = val;
+    if (h2_stream_needs_submit(stream)) {
+        *((int *)ctx) = 1;
+        return 0;
+    }
+    return 1;
+}
+
+static int has_unsubmitted_streams(h2_session *session)
+{
+    int has_unsubmitted = 0;
+    h2_ihash_iter(session->streams, unsubmitted_iter, &has_unsubmitted);
+    return has_unsubmitted;
+}
+
+static int suspended_iter(void *ctx, void *val)
+{
+    h2_stream *stream = val;
+    if (h2_stream_is_suspended(stream)) {
+        *((int *)ctx) = 1;
+        return 0;
+    }
+    return 1;
+}
+
+static int has_suspended_streams(h2_session *session)
+{
+    int has_suspended = 0;
+    h2_ihash_iter(session->streams, suspended_iter, &has_suspended);
+    return has_suspended;
+}
+
 static apr_status_t h2_session_submit(h2_session *session)
 {
     apr_status_t status = APR_EAGAIN;
     h2_stream *stream;
     
-    if (h2_stream_set_has_unsubmitted(session->streams)) {
+    if (has_unsubmitted_streams(session)) {
         /* If we have responses ready, submit them now. */
         while ((stream = h2_mplx_next_submit(session->mplx, session->streams))) {
             status = submit_response(session, stream);
@@ -1784,7 +1750,7 @@ static void h2_session_ev_conn_error(h2_
         default:
             ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
                           "h2_session(%ld): conn error -> shutdown", session->id);
-            h2_session_shutdown(session, arg, msg);
+            h2_session_shutdown(session, arg, msg, 0);
             break;
     }
 }
@@ -1801,7 +1767,7 @@ static void h2_session_ev_proto_error(h2
         default:
             ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
                           "h2_session(%ld): proto error -> shutdown", session->id);
-            h2_session_shutdown(session, arg, msg);
+            h2_session_shutdown(session, arg, msg, 0);
             break;
     }
 }
@@ -1813,7 +1779,7 @@ static void h2_session_ev_conn_timeout(h
             transit(session, "conn timeout", H2_SESSION_ST_DONE);
             break;
         default:
-            h2_session_shutdown(session, arg, msg);
+            h2_session_shutdown(session, arg, msg, 1);
             transit(session, "conn timeout", H2_SESSION_ST_DONE);
             break;
     }
@@ -1823,22 +1789,37 @@ static void h2_session_ev_no_io(h2_sessi
 {
     switch (session->state) {
         case H2_SESSION_ST_BUSY:
+        case H2_SESSION_ST_LOCAL_SHUTDOWN:
+        case H2_SESSION_ST_REMOTE_SHUTDOWN:
             /* nothing for input and output to do. If we remain
              * in this state, we go into a tight loop and suck up
              * CPU cycles. Ideally, we'd like to do a blocking read, but that
              * is not possible if we have scheduled tasks and wait
              * for them to produce something. */
-            if (h2_stream_set_is_empty(session->streams)) {
-                /* When we have no streams, no task event are possible,
-                 * switch to blocking reads */
-                transit(session, "no io", H2_SESSION_ST_IDLE);
+            if (h2_ihash_is_empty(session->streams)) {
+                if (!is_accepting_streams(session)) {
+                    /* We are no longer accepting new streams and have
+                     * finished processing existing ones. Time to leave. */
+                    h2_session_shutdown(session, arg, msg, 0);
+                    transit(session, "no io", H2_SESSION_ST_DONE);
+                }
+                else {
+                    /* When we have no streams, no task event are possible,
+                     * switch to blocking reads */
+                    transit(session, "no io", H2_SESSION_ST_IDLE);
+                    session->idle_until = (session->requests_received? 
+                                           session->s->keep_alive_timeout : 
+                                           session->s->timeout) + apr_time_now();
+                }
             }
-            else if (!h2_stream_set_has_unsubmitted(session->streams)
-                     && !h2_stream_set_has_suspended(session->streams)) {
+            else if (!has_unsubmitted_streams(session)
+                     && !has_suspended_streams(session)) {
                 /* none of our streams is waiting for a response or
                  * new output data from task processing, 
-                 * switch to blocking reads. */
+                 * switch to blocking reads. We are probably waiting on
+                 * window updates. */
                 transit(session, "no io", H2_SESSION_ST_IDLE);
+                session->idle_until = apr_time_now() + session->s->timeout;
             }
             else {
                 /* Unable to do blocking reads, as we wait on events from
@@ -1853,11 +1834,11 @@ static void h2_session_ev_no_io(h2_sessi
     }
 }
 
-static void h2_session_ev_wait_timeout(h2_session *session, int arg, const char *msg)
+static void h2_session_ev_stream_ready(h2_session *session, int arg, const char *msg)
 {
     switch (session->state) {
         case H2_SESSION_ST_WAIT:
-            transit(session, "wait timeout", H2_SESSION_ST_BUSY);
+            transit(session, "stream ready", H2_SESSION_ST_BUSY);
             break;
         default:
             /* nop */
@@ -1865,39 +1846,54 @@ static void h2_session_ev_wait_timeout(h
     }
 }
 
-static void h2_session_ev_stream_ready(h2_session *session, int arg, const char *msg)
+static void h2_session_ev_data_read(h2_session *session, int arg, const char *msg)
 {
     switch (session->state) {
+        case H2_SESSION_ST_IDLE:
         case H2_SESSION_ST_WAIT:
-            transit(session, "stream ready", H2_SESSION_ST_BUSY);
+            transit(session, "data read", H2_SESSION_ST_BUSY);
             break;
+            /* fall through */
         default:
             /* nop */
             break;
     }
 }
 
-static void h2_session_ev_data_read(h2_session *session, int arg, const char *msg)
+static void h2_session_ev_ngh2_done(h2_session *session, int arg, const char *msg)
 {
     switch (session->state) {
-        case H2_SESSION_ST_IDLE:
-            transit(session, "data read", H2_SESSION_ST_BUSY);
+        case H2_SESSION_ST_DONE:
+            /* nop */
             break;
-            /* fall through */
         default:
+            transit(session, "nghttp2 done", H2_SESSION_ST_DONE);
+            break;
+    }
+}
+
+static void h2_session_ev_mpm_stopping(h2_session *session, int arg, const char *msg)
+{
+    switch (session->state) {
+        case H2_SESSION_ST_DONE:
+        case H2_SESSION_ST_LOCAL_SHUTDOWN:
             /* nop */
             break;
+        default:
+            h2_session_shutdown(session, arg, msg, 0);
+            break;
     }
 }
 
-static void h2_session_ev_ngh2_done(h2_session *session, int arg, const char *msg)
+static void h2_session_ev_pre_close(h2_session *session, int arg, const char *msg)
 {
     switch (session->state) {
         case H2_SESSION_ST_DONE:
+        case H2_SESSION_ST_LOCAL_SHUTDOWN:
             /* nop */
             break;
         default:
-            transit(session, "nghttp2 done", H2_SESSION_ST_DONE);
+            h2_session_shutdown(session, arg, msg, 1);
             break;
     }
 }
@@ -1927,9 +1923,6 @@ static void dispatch_event(h2_session *s
         case H2_SESSION_EV_NO_IO:
             h2_session_ev_no_io(session, arg, msg);
             break;
-        case H2_SESSION_EV_WAIT_TIMEOUT:
-            h2_session_ev_wait_timeout(session, arg, msg);
-            break;
         case H2_SESSION_EV_STREAM_READY:
             h2_session_ev_stream_ready(session, arg, msg);
             break;
@@ -1939,6 +1932,12 @@ static void dispatch_event(h2_session *s
         case H2_SESSION_EV_NGH2_DONE:
             h2_session_ev_ngh2_done(session, arg, msg);
             break;
+        case H2_SESSION_EV_MPM_STOPPING:
+            h2_session_ev_mpm_stopping(session, arg, msg);
+            break;
+        case H2_SESSION_EV_PRE_CLOSE:
+            h2_session_ev_pre_close(session, arg, msg);
+            break;
         default:
             ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
                           "h2_session(%ld): unknown event %d", 
@@ -1953,28 +1952,55 @@ static void dispatch_event(h2_session *s
 
 static const int MAX_WAIT_MICROS = 200 * 1000;
 
+static void update_child_status(h2_session *session, int status, const char *msg)
+{
+    apr_snprintf(session->status, sizeof(session->status),
+                 "%s, streams: %d/%d/%d/%d/%d (open/recv/resp/push/rst)", 
+                 msg? msg : "-",
+                 (int)h2_ihash_count(session->streams), 
+                 (int)session->requests_received,
+                 (int)session->responses_submitted,
+                 (int)session->pushes_submitted,
+                 (int)session->pushes_reset + session->streams_reset);
+    ap_update_child_status_descr(session->c->sbh, status, session->status);
+}
+
 apr_status_t h2_session_process(h2_session *session, int async)
 {
     apr_status_t status = APR_SUCCESS;
     conn_rec *c = session->c;
-    int rv, have_written, have_read;
+    int rv, have_written, have_read, mpm_state, no_streams;
 
     ap_log_cerror( APLOG_MARK, APLOG_TRACE1, status, c,
                   "h2_session(%ld): process start, async=%d", session->id, async);
                   
+    if (c->cs) {
+        c->cs->state = CONN_STATE_WRITE_COMPLETION;
+    }
+    
     while (1) {
         have_read = have_written = 0;
 
+        if (!ap_mpm_query(AP_MPMQ_MPM_STATE, &mpm_state)) {
+            if (mpm_state == AP_MPMQ_STOPPING) {
+                dispatch_event(session, H2_SESSION_EV_MPM_STOPPING, 0, NULL);
+                break;
+            }
+        }
+        
+        session->status[0] = '\0';
+        
         switch (session->state) {
             case H2_SESSION_ST_INIT:
+                ap_update_child_status_from_conn(c->sbh, SERVER_BUSY_READ, c);
                 if (!h2_is_acceptable_connection(c, 1)) {
-                    h2_session_shutdown(session, NGHTTP2_INADEQUATE_SECURITY, NULL);
+                    update_child_status(session, SERVER_BUSY_READ, "inadequate security");
+                    h2_session_shutdown(session, NGHTTP2_INADEQUATE_SECURITY, NULL, 1);
                 } 
                 else {
-                    ap_update_child_status(c->sbh, SERVER_BUSY_READ, NULL);
+                    update_child_status(session, SERVER_BUSY_READ, "init");
                     status = h2_session_start(session, &rv);
-                    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c,
-                                  APLOGNO(03079)
+                    ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c, APLOGNO(03079)
                                   "h2_session(%ld): started on %s:%d", session->id,
                                   session->s->server_hostname,
                                   c->local_addr->port);
@@ -1986,32 +2012,80 @@ apr_status_t h2_session_process(h2_sessi
                 break;
                 
             case H2_SESSION_ST_IDLE:
-                h2_filter_cin_timeout_set(session->cin, session->keepalive_secs);
-                ap_update_child_status(c->sbh, SERVER_BUSY_KEEPALIVE, NULL);
-                status = h2_session_read(session, 1, 10);
-                if (status == APR_SUCCESS) {
-                    have_read = 1;
-                    dispatch_event(session, H2_SESSION_EV_DATA_READ, 0, NULL);
-                }
-                else if (status == APR_EAGAIN) {
-                    /* nothing to read */
-                }
-                else if (APR_STATUS_IS_TIMEUP(status)) {
-                    dispatch_event(session, H2_SESSION_EV_CONN_TIMEOUT, 0, NULL);
-                    break;
+                no_streams = h2_ihash_is_empty(session->streams);
+                update_child_status(session, (no_streams? SERVER_BUSY_KEEPALIVE
+                                              : SERVER_BUSY_READ), "idle");
+                if (async && no_streams && !session->r && session->requests_received) {
+                    ap_log_cerror( APLOG_MARK, APLOG_TRACE1, status, c,
+                                  "h2_session(%ld): async idle, nonblock read", session->id);
+                    /* We do not return to the async mpm immediately, since under
+                     * load, mpms show the tendency to throw keep_alive connections
+                     * away very rapidly.
+                     * So, if we are still processing streams, we wait for the
+                     * normal timeout first and, on timeout, close.
+                     * If we have no streams, we still wait a short amount of
+                     * time here for the next frame to arrive, before handing
+                     * it to keep_alive processing of the mpm.
+                     */
+                    status = h2_session_read(session, 0);
+                    
+                    if (status == APR_SUCCESS) {
+                        have_read = 1;
+                        dispatch_event(session, H2_SESSION_EV_DATA_READ, 0, NULL);
+                    }
+                    else if (APR_STATUS_IS_EAGAIN(status) || APR_STATUS_IS_TIMEUP(status)) {
+                        if (apr_time_now() > session->idle_until) {
+                            dispatch_event(session, H2_SESSION_EV_CONN_TIMEOUT, 0, NULL);
+                        }
+                        else {
+                            status = APR_EAGAIN;
+                            goto out;
+                        }
+                    }
+                    else {
+                        ap_log_cerror( APLOG_MARK, APLOG_DEBUG, status, c,
+                                      "h2_session(%ld): idle, no data, error", 
+                                      session->id);
+                        dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 0, "timeout");
+                    }
                 }
                 else {
-                    dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 0, NULL);
+                    /* We wait in smaller increments, using a 1 second timeout.
+                     * That gives us the chance to check for MPMQ_STOPPING often. 
+                     */
+                    status = h2_mplx_idle(session->mplx);
+                    if (status != APR_SUCCESS) {
+                        dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 
+                                       H2_ERR_ENHANCE_YOUR_CALM, "less is more");
+                    }
+                    h2_filter_cin_timeout_set(session->cin, apr_time_from_sec(1));
+                    status = h2_session_read(session, 1);
+                    if (status == APR_SUCCESS) {
+                        have_read = 1;
+                        dispatch_event(session, H2_SESSION_EV_DATA_READ, 0, NULL);
+                    }
+                    else if (status == APR_EAGAIN) {
+                        /* nothing to read */
+                    }
+                    else if (APR_STATUS_IS_TIMEUP(status)) {
+                        if (apr_time_now() > session->idle_until) {
+                            dispatch_event(session, H2_SESSION_EV_CONN_TIMEOUT, 0, "timeout");
+                        }
+                        /* continue reading handling */
+                    }
+                    else {
+                        dispatch_event(session, H2_SESSION_EV_CONN_ERROR, 0, "error");
+                    }
                 }
+                
                 break;
                 
             case H2_SESSION_ST_BUSY:
             case H2_SESSION_ST_LOCAL_SHUTDOWN:
             case H2_SESSION_ST_REMOTE_SHUTDOWN:
                 if (nghttp2_session_want_read(session->ngh2)) {
-                    ap_update_child_status(c->sbh, SERVER_BUSY_READ, NULL);
-                    h2_filter_cin_timeout_set(session->cin, session->timeout_secs);
-                    status = h2_session_read(session, 0, 10);
+                    h2_filter_cin_timeout_set(session->cin, session->s->timeout);
+                    status = h2_session_read(session, 0);
                     if (status == APR_SUCCESS) {
                         have_read = 1;
                         dispatch_event(session, H2_SESSION_EV_DATA_READ, 0, NULL);
@@ -2028,7 +2102,7 @@ apr_status_t h2_session_process(h2_sessi
                     }
                 }
                 
-                if (!h2_stream_set_is_empty(session->streams)) {
+                if (!h2_ihash_is_empty(session->streams)) {
                     /* resume any streams for which data is available again */
                     h2_session_resume_streams_with_data(session);
                     /* Submit any responses/push_promises that are ready */
@@ -2050,7 +2124,8 @@ apr_status_t h2_session_process(h2_sessi
                     }
                 }
                 
-                if (nghttp2_session_want_write(session->ngh2)) {
+                while (nghttp2_session_want_write(session->ngh2)) {
+                    ap_update_child_status(session->c->sbh, SERVER_BUSY_WRITE, NULL);
                     status = h2_session_send(session);
                     if (status == APR_SUCCESS) {
                         have_written = 1;
@@ -2063,39 +2138,55 @@ apr_status_t h2_session_process(h2_sessi
                 }
                 
                 if (have_read || have_written) {
-                    session->wait_us = 0;
+                    if (session->wait_us) {
+                        session->wait_us = 0;
+                        update_child_status(session, SERVER_BUSY_READ, "busy");
+                    }
                 }
-                else {
+                else if (!nghttp2_session_want_write(session->ngh2)) {
                     dispatch_event(session, H2_SESSION_EV_NO_IO, 0, NULL);
                 }
                 break;
                 
             case H2_SESSION_ST_WAIT:
-                session->wait_us = H2MAX(session->wait_us, 10);
+                if (session->wait_us <= 0) {
+                    session->wait_us = 10;
+                    session->start_wait = apr_time_now();
+                    update_child_status(session, SERVER_BUSY_READ, "wait");
+                }
+                else if ((apr_time_now() - session->start_wait) >= session->s->timeout) {
+                    /* waited long enough */
+                    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, APR_TIMEUP, c,
+                                  "h2_session: wait for data");
+                    dispatch_event(session, H2_SESSION_EV_CONN_TIMEOUT, 0, NULL);
+                }
+                else {
+                    /* repeating, increase timer for graceful backoff */
+                    session->wait_us = H2MIN(session->wait_us*2, MAX_WAIT_MICROS);
+                }
+
                 if (APLOGctrace1(c)) {
                     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
                                   "h2_session: wait for data, %ld micros", 
                                   (long)session->wait_us);
                 }
-                
-                ap_log_cerror( APLOG_MARK, APLOG_TRACE2, status, c,
-                              "h2_session(%ld): process -> trywait", session->id);
                 status = h2_mplx_out_trywait(session->mplx, session->wait_us, 
                                              session->iowait);
                 if (status == APR_SUCCESS) {
-                    dispatch_event(session, H2_SESSION_EV_STREAM_READY, 0, NULL);
+                    session->wait_us = 0;
+                    dispatch_event(session, H2_SESSION_EV_DATA_READ, 0, NULL);
                 }
                 else if (status == APR_TIMEUP) {
-                    /* nothing, increase timer for graceful backup */
-                    session->wait_us = H2MIN(session->wait_us*2, MAX_WAIT_MICROS);
-                    dispatch_event(session, H2_SESSION_EV_WAIT_TIMEOUT, 0, NULL);
+                    /* go back to checking all inputs again */
+                    transit(session, "wait cycle", H2_SESSION_ST_BUSY);
                 }
                 else {
-                    h2_session_shutdown(session, H2_ERR_INTERNAL_ERROR, "cond wait error");
+                    h2_session_shutdown(session, H2_ERR_INTERNAL_ERROR, "cond wait error", 0);
                 }
                 break;
                 
             case H2_SESSION_ST_DONE:
+                update_child_status(session, SERVER_CLOSING, "done");
                 status = APR_EOF;
                 goto out;
                 
@@ -2107,19 +2198,16 @@ apr_status_t h2_session_process(h2_sessi
                 break;
         }
 
-        if (have_written) {
-            h2_conn_io_flush(&session->io);
-        }
-        else if (!nghttp2_session_want_read(session->ngh2) 
+        h2_conn_io_pass(&session->io, 1);
+        if (!nghttp2_session_want_read(session->ngh2) 
                  && !nghttp2_session_want_write(session->ngh2)) {
             dispatch_event(session, H2_SESSION_EV_NGH2_DONE, 0, NULL); 
         }
     }
     
 out:
-    if (have_written) {
-        h2_conn_io_flush(&session->io);
-    }
+    h2_conn_io_pass(&session->io, session->flush);
+    session->flush = 0;
     
     ap_log_cerror( APLOG_MARK, APLOG_TRACE1, status, c,
                   "h2_session(%ld): [%s] process returns", 
@@ -2136,10 +2224,17 @@ out:
     if (session->state == H2_SESSION_ST_DONE) {
         if (!session->eoc_written) {
             session->eoc_written = 1;
-            h2_conn_io_write_eoc(&session->io, 
-                                 h2_bucket_eoc_create(session->c->bucket_alloc, session));
+            h2_conn_io_write_eoc(&session->io, session);
         }
     }
     
     return status;
 }
+
+apr_status_t h2_session_pre_close(h2_session *session, int async)
+{
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c, 
+                  "h2_session(%ld): pre_close", session->id);
+    dispatch_event(session, H2_SESSION_EV_PRE_CLOSE, 0, "timeout");
+    return APR_SUCCESS;
+}

Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_session.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_session.h?rev=1733259&r1=1733258&r2=1733259&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_session.h (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_session.h Wed Mar  2 11:21:28 2016
@@ -37,11 +37,14 @@
  *
  */
 
+#include "h2.h"
+
 struct apr_thread_mutext_t;
 struct apr_thread_cond_t;
 struct h2_ctx;
 struct h2_config;
 struct h2_filter_cin;
+struct h2_ihash_t;
 struct h2_mplx;
 struct h2_priority;
 struct h2_push;
@@ -55,16 +58,6 @@ struct h2_workers;
 struct nghttp2_session;
 
 typedef enum {
-    H2_SESSION_ST_INIT,             /* send initial SETTINGS, etc. */
-    H2_SESSION_ST_DONE,             /* finished, connection close */
-    H2_SESSION_ST_IDLE,             /* nothing to write, expecting data inc */
-    H2_SESSION_ST_BUSY,             /* read/write without stop */
-    H2_SESSION_ST_WAIT,             /* waiting for tasks reporting back */
-    H2_SESSION_ST_LOCAL_SHUTDOWN,   /* we announced GOAWAY */
-    H2_SESSION_ST_REMOTE_SHUTDOWN,  /* client announced GOAWAY */
-} h2_session_state;
-
-typedef enum {
     H2_SESSION_EV_INIT,             /* session was initialized */
     H2_SESSION_EV_LOCAL_GOAWAY,     /* we send a GOAWAY */
     H2_SESSION_EV_REMOTE_GOAWAY,    /* remote send us a GOAWAY */
@@ -72,10 +65,11 @@ typedef enum {
     H2_SESSION_EV_PROTO_ERROR,      /* protocol error */
     H2_SESSION_EV_CONN_TIMEOUT,     /* connection timeout */
     H2_SESSION_EV_NO_IO,            /* nothing has been read or written */
-    H2_SESSION_EV_WAIT_TIMEOUT,     /* timeout waiting for tasks */
     H2_SESSION_EV_STREAM_READY,     /* stream signalled availability of headers/data */
     H2_SESSION_EV_DATA_READ,        /* connection data has been read */
     H2_SESSION_EV_NGH2_DONE,        /* nghttp2 wants neither read nor write anything */
+    H2_SESSION_EV_MPM_STOPPING,     /* the process is stopping */
+    H2_SESSION_EV_PRE_CLOSE,        /* connection will close after this */
 } h2_session_event_t;
 
 typedef struct h2_session {
@@ -90,6 +84,7 @@ typedef struct h2_session {
     h2_session_state state;         /* state session is in */
     unsigned int reprioritize  : 1; /* scheduled streams priority changed */
     unsigned int eoc_written   : 1; /* h2 eoc bucket written */
+    unsigned int flush         : 1; /* flushing output necessary */
     apr_interval_time_t  wait_us;   /* timout during BUSY_WAIT state, micro secs */
     
     int unsent_submits;             /* number of submitted, but not yet written responses. */
@@ -111,8 +106,8 @@ typedef struct h2_session {
     apr_size_t max_stream_count;    /* max number of open streams */
     apr_size_t max_stream_mem;      /* max buffer memory for a single stream */
     
-    int timeout_secs;               /* connection timeout (seconds) */
-    int keepalive_secs;             /* connection idle timeout (seconds) */
+    apr_time_t start_wait;          /* Time we started waiting for sth. to happen */
+    apr_time_t idle_until;          /* Time we shut down due to sheer boredom */
     
     apr_pool_t *pool;               /* pool to use in session handling */
     apr_bucket_brigade *bbtmp;      /* brigade for keeping temporary data */
@@ -124,7 +119,7 @@ typedef struct h2_session {
     struct h2_mplx *mplx;           /* multiplexer for stream data */
     
     struct h2_stream *last_stream;  /* last stream worked with */
-    struct h2_stream_set *streams;  /* streams handled by this session */
+    struct h2_ihash_t *streams;     /* streams handled by this session */
     
     apr_pool_t *spare;              /* spare stream pool */
     
@@ -132,6 +127,8 @@ typedef struct h2_session {
     struct h2_workers *workers;     /* for executing stream tasks */
     
     struct h2_push_diary *push_diary; /* remember pushes, avoid duplicates */
+    
+    char status[64];                /* status message for scoreboard */
 } h2_session;
 
 
@@ -166,6 +163,11 @@ h2_session *h2_session_rcreate(request_r
 apr_status_t h2_session_process(h2_session *session, int async);
 
 /**
+ * Last chance to do anything before the connection is closed.
+ */
+apr_status_t h2_session_pre_close(h2_session *session, int async);
+
+/**
  * Cleanup the session and all objects it still contains. This will not
  * destroy h2_task instances that have not finished yet. 
  * @param session the session to destroy

Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_stream.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_stream.c?rev=1733259&r1=1733258&r2=1733259&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_stream.c (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_stream.c Wed Mar  2 11:21:28 2016
@@ -120,7 +120,7 @@ static int close_output(h2_stream *strea
     return 1;
 }
 
-static int input_open(h2_stream *stream) 
+static int input_open(const h2_stream *stream) 
 {
     switch (stream->state) {
         case H2_STREAM_ST_OPEN:
@@ -158,7 +158,8 @@ h2_stream *h2_stream_open(int id, apr_po
 {
     h2_stream *stream = h2_stream_create(id, pool, session);
     set_state(stream, H2_STREAM_ST_OPEN);
-    stream->request   = h2_request_create(id, pool, session->config);
+    stream->request   = h2_request_create(id, pool, 
+        h2_config_geti(session->config, H2_CONF_SER_HEADERS));
     
     ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03082)
                   "h2_stream(%ld-%d): opened", session->id, stream->id);
@@ -168,11 +169,6 @@ h2_stream *h2_stream_open(int id, apr_po
 apr_status_t h2_stream_destroy(h2_stream *stream)
 {
     AP_DEBUG_ASSERT(stream);
-    if (stream->request) {
-        h2_request_destroy(stream->request);
-        stream->request = NULL;
-    }
-    
     if (stream->pool) {
         apr_pool_destroy(stream->pool);
     }
@@ -242,6 +238,9 @@ apr_status_t h2_stream_set_request(h2_st
     }
     set_state(stream, H2_STREAM_ST_OPEN);
     status = h2_request_rwrite(stream->request, r);
+    stream->request->serialize = h2_config_geti(h2_config_rget(r), 
+                                                H2_CONF_SER_HEADERS);
+
     return status;
 }
 
@@ -324,7 +323,7 @@ apr_status_t h2_stream_schedule(h2_strea
     return status;
 }
 
-int h2_stream_is_scheduled(h2_stream *stream)
+int h2_stream_is_scheduled(const h2_stream *stream)
 {
     return stream->scheduled;
 }
@@ -431,7 +430,7 @@ void h2_stream_set_suspended(h2_stream *
                   stream->session->id, stream->id, stream->suspended);
 }
 
-int h2_stream_is_suspended(h2_stream *stream)
+int h2_stream_is_suspended(const h2_stream *stream)
 {
     AP_DEBUG_ASSERT(stream);
     return stream->suspended;
@@ -475,12 +474,12 @@ apr_status_t h2_stream_read_to(h2_stream
     return stream->sos->read_to(stream->sos, bb, plen, peos);
 }
 
-int h2_stream_input_is_open(h2_stream *stream) 
+int h2_stream_input_is_open(const h2_stream *stream) 
 {
     return input_open(stream);
 }
 
-int h2_stream_needs_submit(h2_stream *stream)
+int h2_stream_needs_submit(const h2_stream *stream)
 {
     switch (stream->state) {
         case H2_STREAM_ST_OPEN:

Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_stream.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_stream.h?rev=1733259&r1=1733258&r2=1733259&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_stream.h (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_stream.h Wed Mar  2 11:21:28 2016
@@ -16,6 +16,8 @@
 #ifndef __mod_h2__h2_stream__
 #define __mod_h2__h2_stream__
 
+#include "h2.h"
+
 /**
  * A HTTP/2 stream, e.g. a client request+response in HTTP/1.1 terms.
  * 
@@ -30,16 +32,6 @@
  */
 #include "h2_io.h"
 
-typedef enum {
-    H2_STREAM_ST_IDLE,
-    H2_STREAM_ST_OPEN,
-    H2_STREAM_ST_RESV_LOCAL,
-    H2_STREAM_ST_RESV_REMOTE,
-    H2_STREAM_ST_CLOSED_INPUT,
-    H2_STREAM_ST_CLOSED_OUTPUT,
-    H2_STREAM_ST_CLOSED,
-} h2_stream_state_t;
-
 struct h2_mplx;
 struct h2_priority;
 struct h2_request;
@@ -191,7 +183,7 @@ apr_status_t h2_stream_schedule(h2_strea
  * @param stream the stream to check on
  * @return != 0 iff stream has been scheduled
  */
-int h2_stream_is_scheduled(h2_stream *stream);
+int h2_stream_is_scheduled(const h2_stream *stream);
 
 struct h2_response *h2_stream_get_response(h2_stream *stream);
 
@@ -278,21 +270,21 @@ void h2_stream_set_suspended(h2_stream *
  * @param stream the stream to check
  * @return != 0 iff stream is suspended.
  */
-int h2_stream_is_suspended(h2_stream *stream);
+int h2_stream_is_suspended(const h2_stream *stream);
 
 /**
  * Check if the stream has open input.
  * @param stream the stream to check
  * @return != 0 iff stream has open input.
  */
-int h2_stream_input_is_open(h2_stream *stream);
+int h2_stream_input_is_open(const h2_stream *stream);
 
 /**
  * Check if the stream has not submitted a response or RST yet.
  * @param stream the stream to check
  * @return != 0 iff stream has not submitted a response or RST.
  */
-int h2_stream_needs_submit(h2_stream *stream);
+int h2_stream_needs_submit(const h2_stream *stream);
 
 /**
  * Submit any server push promises on this stream and schedule

Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_task.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_task.c?rev=1733259&r1=1733258&r2=1733259&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_task.c (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_task.c Wed Mar  2 11:21:28 2016
@@ -35,6 +35,7 @@
 #include "h2_private.h"
 #include "h2_conn.h"
 #include "h2_config.h"
+#include "h2_ctx.h"
 #include "h2_from_h1.h"
 #include "h2_h2.h"
 #include "h2_mplx.h"
@@ -85,6 +86,27 @@ static apr_status_t h2_filter_read_respo
     return h2_from_h1_read_response(task->output->from_h1, f, bb);
 }
 
+static apr_status_t h2_response_freeze_filter(ap_filter_t* f,
+                                              apr_bucket_brigade* bb)
+{
+    h2_task *task = f->ctx;
+    AP_DEBUG_ASSERT(task);
+    
+    if (task->frozen) {
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r,
+                      "h2_response_freeze_filter, saving");
+        return ap_save_brigade(f, &task->frozen_out, &bb, task->c->pool);
+    }
+    
+    if (APR_BRIGADE_EMPTY(bb)) {
+        return APR_SUCCESS;
+    }
+
+    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, f->r,
+                  "h2_response_freeze_filter, passing");
+    return ap_pass_brigade(f->next, bb);
+}
+
 /*******************************************************************************
  * Register various hooks
  */
@@ -92,6 +114,9 @@ static const char *const mod_ssl[]
 static int h2_task_pre_conn(conn_rec* c, void *arg);
 static int h2_task_process_conn(conn_rec* c);
 
+APR_OPTIONAL_FN_TYPE(ap_logio_add_bytes_in) *h2_task_logio_add_bytes_in;
+APR_OPTIONAL_FN_TYPE(ap_logio_add_bytes_out) *h2_task_logio_add_bytes_out;
+
 void h2_task_register_hooks(void)
 {
     /* This hook runs on new connections before mod_ssl has a say.
@@ -116,6 +141,17 @@ void h2_task_register_hooks(void)
                               NULL, AP_FTYPE_PROTOCOL);
     ap_register_output_filter("H2_TRAILERS", h2_response_trailers_filter,
                               NULL, AP_FTYPE_PROTOCOL);
+    ap_register_output_filter("H2_RESPONSE_FREEZE", h2_response_freeze_filter,
+                              NULL, AP_FTYPE_RESOURCE);
+}
+
+/* post config init */
+apr_status_t h2_task_init(apr_pool_t *pool, server_rec *s)
+{
+    h2_task_logio_add_bytes_in = APR_RETRIEVE_OPTIONAL_FN(ap_logio_add_bytes_in);
+    h2_task_logio_add_bytes_out = APR_RETRIEVE_OPTIONAL_FN(ap_logio_add_bytes_out);
+
+    return APR_SUCCESS;
 }
 
 static int h2_task_pre_conn(conn_rec* c, void *arg)
@@ -143,60 +179,80 @@ static int h2_task_pre_conn(conn_rec* c,
 }
 
 h2_task *h2_task_create(long session_id, const h2_request *req, 
-                        apr_pool_t *pool, h2_mplx *mplx)
+                        conn_rec *c, h2_mplx *mplx)
 {
-    h2_task *task     = apr_pcalloc(pool, sizeof(h2_task));
+    h2_task *task     = apr_pcalloc(c->pool, sizeof(h2_task));
     if (task == NULL) {
-        ap_log_perror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, pool,
+        ap_log_cerror(APLOG_MARK, APLOG_ERR, APR_ENOMEM, c,
                       APLOGNO(02941) "h2_task(%ld-%d): create stream task", 
                       session_id, req->id);
         h2_mplx_out_close(mplx, req->id, NULL);
         return NULL;
     }
     
-    task->id          = apr_psprintf(pool, "%ld-%d", session_id, req->id);
+    task->id          = apr_psprintf(c->pool, "%ld-%d", session_id, req->id);
     task->stream_id   = req->id;
+    task->c           = c;
     task->mplx        = mplx;
     task->request     = req;
     task->input_eos   = !req->body;
-    task->ser_headers = h2_config_geti(req->config, H2_CONF_SER_HEADERS);
+    task->ser_headers = req->serialize;
+
+    h2_ctx_create_for(c, task);
 
     return task;
 }
 
-apr_status_t h2_task_do(h2_task *task, conn_rec *c, apr_thread_cond_t *cond, 
-                        apr_socket_t *socket)
+apr_status_t h2_task_do(h2_task *task, apr_thread_cond_t *cond)
 {
+    apr_status_t status;
+    
     AP_DEBUG_ASSERT(task);
     task->io = cond;
-    task->input = h2_task_input_create(task, c->pool, c->bucket_alloc);
-    task->output = h2_task_output_create(task, c->pool);
-    
-    ap_process_connection(c, socket);
+    task->input = h2_task_input_create(task, task->c);
+    task->output = h2_task_output_create(task, task->c);
     
-    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
-                  "h2_task(%s): processing done", task->id);
+    ap_process_connection(task->c, ap_get_conn_socket(task->c));
     
-    h2_task_input_destroy(task->input);
-    h2_task_output_close(task->output);
-    h2_task_output_destroy(task->output);
-    task->io = NULL;
+    if (task->frozen) {
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, task->c,
+                      "h2_task(%s): process_conn returned frozen task", 
+                      task->id);
+        /* cleanup delayed */
+        status = APR_EAGAIN;
+    }
+    else {
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, task->c,
+                      "h2_task(%s): processing done", task->id);
+        status = APR_SUCCESS;
+    }
     
-    return APR_SUCCESS;
+    return status;
 }
 
-static apr_status_t h2_task_process_request(const h2_request *req, conn_rec *c)
+static apr_status_t h2_task_process_request(h2_task *task, conn_rec *c)
 {
-    request_rec *r;
+    const h2_request *req = task->request;
     conn_state_t *cs = c->cs;
+    request_rec *r;
 
     r = h2_request_create_rec(req, c);
     if (r && (r->status == HTTP_OK)) {
         ap_update_child_status(c->sbh, SERVER_BUSY_READ, r);
         
-        if (cs)
+        if (cs) {
             cs->state = CONN_STATE_HANDLER;
+        }
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
+                      "h2_task(%s): start process_request", task->id);
         ap_process_request(r);
+        if (task->frozen) {
+            ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
+                          "h2_task(%s): process_request frozen", task->id);
+        }
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
+                      "h2_task(%s): process_request done", task->id);
+        
         /* After the call to ap_process_request, the
          * request pool will have been deleted.  We set
          * r=NULL here to ensure that any dereference
@@ -204,11 +260,10 @@ static apr_status_t h2_task_process_requ
          * will result in a segfault immediately instead
          * of nondeterministic failures later.
          */
-        if (cs)
+        if (cs) 
             cs->state = CONN_STATE_WRITE_COMPLETION;
         r = NULL;
     }
-    ap_update_child_status(c->sbh, SERVER_BUSY_WRITE, NULL);
     c->sbh = NULL;
 
     return APR_SUCCESS;
@@ -227,7 +282,7 @@ static int h2_task_process_conn(conn_rec
         if (!ctx->task->ser_headers) {
             ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, 
                           "h2_h2, processing request directly");
-            h2_task_process_request(ctx->task->request, c);
+            h2_task_process_request(ctx->task, c);
             return DONE;
         }
         ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, c, 
@@ -235,3 +290,28 @@ static int h2_task_process_conn(conn_rec
     }
     return DECLINED;
 }
+
+apr_status_t h2_task_freeze(h2_task *task, request_rec *r)
+{   
+    if (!task->frozen) {
+        conn_rec *c = task->c;
+        
+        task->frozen = 1;
+        task->frozen_out = apr_brigade_create(c->pool, c->bucket_alloc);
+        ap_add_output_filter("H2_RESPONSE_FREEZE", task, r, r->connection);
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, 
+                      "h2_task(%s), frozen", task->id);
+    }
+    return APR_SUCCESS;
+}
+
+apr_status_t h2_task_thaw(h2_task *task)
+{
+    if (task->frozen) {
+        task->frozen = 0;
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, task->c, 
+                      "h2_task(%s), thawed", task->id);
+    }
+    return APR_SUCCESS;
+}
+

Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_task.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_task.h?rev=1733259&r1=1733258&r2=1733259&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_task.h (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_task.h Wed Mar  2 11:21:28 2016
@@ -16,6 +16,8 @@
 #ifndef __mod_h2__h2_task__
 #define __mod_h2__h2_task__
 
+#include <http_core.h>
+
 /**
  * A h2_task fakes a HTTP/1.1 request from the data in a HTTP/2 stream 
  * (HEADER+CONT.+DATA) the module recieves.
@@ -48,24 +50,37 @@ typedef struct h2_task h2_task;
 struct h2_task {
     const char *id;
     int stream_id;
+    conn_rec *c;
     struct h2_mplx *mplx;    
     const struct h2_request *request;
     
     unsigned int filters_set : 1;
     unsigned int input_eos   : 1;
     unsigned int ser_headers : 1;
+    unsigned int frozen      : 1;
     
     struct h2_task_input *input;
     struct h2_task_output *output;
     struct apr_thread_cond_t *io;   /* used to wait for events on */
+
+    apr_bucket_brigade *frozen_out;
 };
 
 h2_task *h2_task_create(long session_id, const struct h2_request *req, 
-                        apr_pool_t *pool, struct h2_mplx *mplx);
+                        conn_rec *c, struct h2_mplx *mplx);
 
-apr_status_t h2_task_do(h2_task *task, conn_rec *c, 
-                        struct apr_thread_cond_t *cond, apr_socket_t *socket);
+apr_status_t h2_task_do(h2_task *task, struct apr_thread_cond_t *cond);
 
 void h2_task_register_hooks(void);
+/*
+ * One time, post config intialization.
+ */
+apr_status_t h2_task_init(apr_pool_t *pool, server_rec *s);
+
+extern APR_OPTIONAL_FN_TYPE(ap_logio_add_bytes_in) *h2_task_logio_add_bytes_in;
+extern APR_OPTIONAL_FN_TYPE(ap_logio_add_bytes_out) *h2_task_logio_add_bytes_out;
+
+apr_status_t h2_task_freeze(h2_task *task, request_rec *r);
+apr_status_t h2_task_thaw(h2_task *task);
 
 #endif /* defined(__mod_h2__h2_task__) */

Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_task_input.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_task_input.c?rev=1733259&r1=1733258&r2=1733259&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_task_input.c (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_task_input.c Wed Mar  2 11:21:28 2016
@@ -43,29 +43,30 @@ static int ser_header(void *ctx, const c
     return 1;
 }
 
-h2_task_input *h2_task_input_create(h2_task *task, apr_pool_t *pool, 
-                                    apr_bucket_alloc_t *bucket_alloc)
+h2_task_input *h2_task_input_create(h2_task *task, conn_rec *c)
 {
-    h2_task_input *input = apr_pcalloc(pool, sizeof(h2_task_input));
+    h2_task_input *input = apr_pcalloc(c->pool, sizeof(h2_task_input));
     if (input) {
+        input->c = c;
         input->task = task;
         input->bb = NULL;
+        input->block = APR_BLOCK_READ;
         
         if (task->ser_headers) {
-            ap_log_perror(APLOG_MARK, APLOG_TRACE1, 0, pool,
+            ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
                           "h2_task_input(%s): serialize request %s %s", 
                           task->id, task->request->method, task->request->path);
-            input->bb = apr_brigade_create(pool, bucket_alloc);
+            input->bb = apr_brigade_create(c->pool, c->bucket_alloc);
             apr_brigade_printf(input->bb, NULL, NULL, "%s %s HTTP/1.1\r\n", 
                                task->request->method, task->request->path);
             apr_table_do(ser_header, input, task->request->headers, NULL);
             apr_brigade_puts(input->bb, NULL, NULL, "\r\n");
             if (input->task->input_eos) {
-                APR_BRIGADE_INSERT_TAIL(input->bb, apr_bucket_eos_create(bucket_alloc));
+                APR_BRIGADE_INSERT_TAIL(input->bb, apr_bucket_eos_create(c->bucket_alloc));
             }
         }
         else if (!input->task->input_eos) {
-            input->bb = apr_brigade_create(pool, bucket_alloc);
+            input->bb = apr_brigade_create(c->pool, c->bucket_alloc);
         }
         else {
             /* We do not serialize and have eos already, no need to
@@ -75,9 +76,9 @@ h2_task_input *h2_task_input_create(h2_t
     return input;
 }
 
-void h2_task_input_destroy(h2_task_input *input)
+void h2_task_input_block_set(h2_task_input *input, apr_read_type_e block)
 {
-    input->bb = NULL;
+    input->block = block;
 }
 
 apr_status_t h2_task_input_read(h2_task_input *input,
@@ -120,7 +121,7 @@ apr_status_t h2_task_input_read(h2_task_
         return APR_EOF;
     }
     
-    while ((bblen == 0) || (mode == AP_MODE_READBYTES && bblen < readbytes)) {
+    while (bblen == 0) {
         /* Get more data for our stream from mplx.
          */
         ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,
@@ -129,30 +130,38 @@ apr_status_t h2_task_input_read(h2_task_
                       input->task->id, block, 
                       (long)readbytes, (long)bblen);
         
-        /* Although we sometimes get called with APR_NONBLOCK_READs, 
-         we seem to  fill our buffer blocking. Otherwise we get EAGAIN,
-         return that to our caller and everyone throws up their hands,
-         never calling us again. */
-        status = h2_mplx_in_read(input->task->mplx, APR_BLOCK_READ,
+        /* Override the block mode we get called with depending on the input's
+         * setting. 
+         */
+        status = h2_mplx_in_read(input->task->mplx, block,
                                  input->task->stream_id, input->bb, 
                                  f->r? f->r->trailers_in : NULL, 
                                  input->task->io);
         ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,
                       "h2_task_input(%s): mplx in read returned",
                       input->task->id);
-        if (status != APR_SUCCESS) {
+        if (APR_STATUS_IS_EAGAIN(status) 
+            && (mode == AP_MODE_GETLINE || block == APR_BLOCK_READ)) {
+            /* chunked input handling does not seem to like it if we
+             * return with APR_EAGAIN from a GETLINE read... 
+             * upload 100k test on test-ser.example.org hangs */
+            status = APR_SUCCESS;
+        }
+        else if (status != APR_SUCCESS) {
             return status;
         }
+        
         status = apr_brigade_length(input->bb, 1, &bblen);
         if (status != APR_SUCCESS) {
             return status;
         }
-        if ((bblen == 0) && (block == APR_NONBLOCK_READ)) {
-            return h2_util_has_eos(input->bb, -1)? APR_EOF : APR_EAGAIN;
-        }
+        
         ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,
                       "h2_task_input(%s): mplx in read, %ld bytes in brigade",
                       input->task->id, (long)bblen);
+        if (h2_task_logio_add_bytes_in) {
+            h2_task_logio_add_bytes_in(f->c, bblen);
+        }
     }
     
     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,

Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_task_input.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_task_input.h?rev=1733259&r1=1733258&r2=1733259&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_task_input.h (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_task_input.h Wed Mar  2 11:21:28 2016
@@ -26,15 +26,14 @@ struct h2_task;
 
 typedef struct h2_task_input h2_task_input;
 struct h2_task_input {
+    conn_rec *c;
     struct h2_task *task;
     apr_bucket_brigade *bb;
+    apr_read_type_e block;
 };
 
 
-h2_task_input *h2_task_input_create(struct h2_task *task, apr_pool_t *pool,
-                                    apr_bucket_alloc_t *bucket_alloc);
-
-void h2_task_input_destroy(h2_task_input *input);
+h2_task_input *h2_task_input_create(struct h2_task *task, conn_rec *c);
 
 apr_status_t h2_task_input_read(h2_task_input *input,
                                   ap_filter_t* filter,
@@ -43,4 +42,6 @@ apr_status_t h2_task_input_read(h2_task_
                                   apr_read_type_e block,
                                   apr_off_t readbytes);
 
+void h2_task_input_block_set(h2_task_input *input, apr_read_type_e block);
+
 #endif /* defined(__mod_h2__h2_task_input__) */

Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_task_output.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_task_output.c?rev=1733259&r1=1733258&r2=1733259&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_task_output.c (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_task_output.c Wed Mar  2 11:21:28 2016
@@ -34,14 +34,14 @@
 #include "h2_util.h"
 
 
-h2_task_output *h2_task_output_create(h2_task *task, apr_pool_t *pool)
+h2_task_output *h2_task_output_create(h2_task *task, conn_rec *c)
 {
-    h2_task_output *output = apr_pcalloc(pool, sizeof(h2_task_output));
-    
+    h2_task_output *output = apr_pcalloc(c->pool, sizeof(h2_task_output));
     if (output) {
+        output->c = c;
         output->task = task;
         output->state = H2_TASK_OUT_INIT;
-        output->from_h1 = h2_from_h1_create(task->stream_id, pool);
+        output->from_h1 = h2_from_h1_create(task->stream_id, c->pool);
         if (!output->from_h1) {
             return NULL;
         }
@@ -49,16 +49,25 @@ h2_task_output *h2_task_output_create(h2
     return output;
 }
 
-void h2_task_output_destroy(h2_task_output *output)
+static apr_table_t *get_trailers(h2_task_output *output)
 {
-    if (output->from_h1) {
-        h2_from_h1_destroy(output->from_h1);
-        output->from_h1 = NULL;
+    if (!output->trailers_passed) {
+        h2_response *response = h2_from_h1_get_response(output->from_h1);
+        if (response && response->trailers) {
+            output->trailers_passed = 1;
+            if (h2_task_logio_add_bytes_out) {
+                /* counter trailers as if we'd do a HTTP/1.1 serialization */
+                h2_task_logio_add_bytes_out(output->c, 
+                                            h2_util_table_bytes(response->trailers, 3)+1);
+            }
+            return response->trailers;
+        }
     }
+    return NULL;
 }
 
 static apr_status_t open_if_needed(h2_task_output *output, ap_filter_t *f,
-                                   apr_bucket_brigade *bb)
+                                   apr_bucket_brigade *bb, const char *caller)
 {
     if (output->state == H2_TASK_OUT_INIT) {
         h2_response *response;
@@ -67,12 +76,12 @@ static apr_status_t open_if_needed(h2_ta
         if (!response) {
             if (f) {
                 /* This happens currently when ap_die(status, r) is invoked
-                 * by a read request filter.
-                 */
+                 * by a read request filter. */
                 ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c, APLOGNO(03204)
-                              "h2_task_output(%s): write without response "
+                              "h2_task_output(%s): write without response by %s "
                               "for %s %s %s",
-                              output->task->id, output->task->request->method, 
+                              output->task->id, caller, 
+                              output->task->request->method, 
                               output->task->request->authority, 
                               output->task->request->path);
                 f->c->aborted = 1;
@@ -83,40 +92,41 @@ static apr_status_t open_if_needed(h2_ta
             return APR_ECONNABORTED;
         }
         
-        output->trailers_passed = !!response->trailers;
+        if (h2_task_logio_add_bytes_out) {
+            /* counter headers as if we'd do a HTTP/1.1 serialization */
+            /* TODO: counter a virtual status line? */
+            apr_off_t bytes_written;
+            apr_brigade_length(bb, 0, &bytes_written);
+            bytes_written += h2_util_table_bytes(response->headers, 3)+1;
+            h2_task_logio_add_bytes_out(f->c, bytes_written);
+        }
+        get_trailers(output);
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, f->c, APLOGNO(03348)
+                      "h2_task_output(%s): open as needed %s %s %s",
+                      output->task->id, output->task->request->method, 
+                      output->task->request->authority, 
+                      output->task->request->path);
         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);
+    open_if_needed(output, NULL, NULL, "close");
     if (output->state != H2_TASK_OUT_DONE) {
+        if (output->task->frozen_out 
+            && !APR_BRIGADE_EMPTY(output->task->frozen_out)) {
+            h2_mplx_out_write(output->task->mplx, output->task->stream_id, 
+                NULL, output->task->frozen_out, NULL, NULL);
+        }
         h2_mplx_out_close(output->task->mplx, output->task->stream_id, 
                           get_trailers(output));
         output->state = H2_TASK_OUT_DONE;
     }
 }
 
-int h2_task_output_has_started(h2_task_output *output)
-{
-    return output->state >= H2_TASK_OUT_STARTED;
-}
-
 /* Bring the data from the brigade (which represents the result of the
  * request_rec out filter chain) into the h2_mplx for further sending
  * on the master connection. 
@@ -132,7 +142,14 @@ apr_status_t h2_task_output_write(h2_tas
         return APR_SUCCESS;
     }
     
-    status = open_if_needed(output, f, bb);
+    if (output->task->frozen) {
+        h2_util_bb_log(output->c, output->task->stream_id, APLOG_TRACE2,
+                       "frozen task output write", bb);
+        return ap_save_brigade(f, &output->task->frozen_out, &bb, 
+                               output->c->pool);
+    }
+    
+    status = open_if_needed(output, f, bb, "write");
     if (status != APR_EOF) {
         ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, f->c,
                       "h2_task_output(%s): opened and passed brigade", 
@@ -142,6 +159,11 @@ apr_status_t h2_task_output_write(h2_tas
     
     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, f->c,
                   "h2_task_output(%s): write brigade", output->task->id);
+    if (h2_task_logio_add_bytes_out) {
+        apr_off_t bytes_written;
+        apr_brigade_length(bb, 0, &bytes_written);
+        h2_task_logio_add_bytes_out(f->c, bytes_written);
+    }
     return h2_mplx_out_write(output->task->mplx, output->task->stream_id, 
                              f, bb, get_trailers(output), output->task->io);
 }

Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_task_output.h
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_task_output.h?rev=1733259&r1=1733258&r2=1733259&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_task_output.h (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_task_output.h Wed Mar  2 11:21:28 2016
@@ -35,15 +35,14 @@ typedef enum {
 typedef struct h2_task_output h2_task_output;
 
 struct h2_task_output {
+    conn_rec *c;
     struct h2_task *task;
     h2_task_output_state_t state;
     struct h2_from_h1 *from_h1;
     unsigned int trailers_passed : 1;
 };
 
-h2_task_output *h2_task_output_create(struct h2_task *task, apr_pool_t *pool);
-
-void h2_task_output_destroy(h2_task_output *output);
+h2_task_output *h2_task_output_create(struct h2_task *task, conn_rec *c);
 
 apr_status_t h2_task_output_write(h2_task_output *output,
                                   ap_filter_t* filter,
@@ -51,6 +50,7 @@ apr_status_t h2_task_output_write(h2_tas
 
 void h2_task_output_close(h2_task_output *output);
 
-int h2_task_output_has_started(h2_task_output *output);
+apr_status_t h2_task_output_freeze(h2_task_output *output);
+apr_status_t h2_task_output_thaw(h2_task_output *output);
 
 #endif /* defined(__mod_h2__h2_task_output__) */

Modified: httpd/httpd/branches/2.4.x/modules/http2/h2_util.c
URL: http://svn.apache.org/viewvc/httpd/httpd/branches/2.4.x/modules/http2/h2_util.c?rev=1733259&r1=1733258&r2=1733259&view=diff
==============================================================================
--- httpd/httpd/branches/2.4.x/modules/http2/h2_util.c (original)
+++ httpd/httpd/branches/2.4.x/modules/http2/h2_util.c Wed Mar  2 11:21:28 2016
@@ -28,6 +28,36 @@
 #include "h2_request.h"
 #include "h2_util.h"
 
+/* h2_log2(n) iff n is a power of 2 */
+unsigned char h2_log2(apr_uint32_t n)
+{
+    int lz = 0;
+    if (!n) {
+        return 0;
+    }
+    if (!(n & 0xffff0000u)) {
+        lz += 16;
+        n = (n << 16);
+    }
+    if (!(n & 0xff000000u)) {
+        lz += 8;
+        n = (n << 8);
+    }
+    if (!(n & 0xf0000000u)) {
+        lz += 4;
+        n = (n << 4);
+    }
+    if (!(n & 0xc0000000u)) {
+        lz += 2;
+        n = (n << 2);
+    }
+    if (!(n & 0x80000000u)) {
+        lz += 1;
+    }
+    
+    return 31 - lz;
+}
+
 size_t h2_util_hex_dump(char *buffer, size_t maxlen,
                         const char *data, size_t datalen)
 {
@@ -230,6 +260,111 @@ const char *h2_util_first_token_match(ap
     return NULL;
 }
 
+
+/*******************************************************************************
+ * ihash - hash for structs with int identifier
+ ******************************************************************************/
+struct h2_ihash_t {
+    apr_hash_t *hash;
+    size_t ioff;
+};
+
+static unsigned int ihash(const char *key, apr_ssize_t *klen)
+{
+    return (unsigned int)(*((int*)key));
+}
+
+h2_ihash_t *h2_ihash_create(apr_pool_t *pool, size_t offset_of_int)
+{
+    h2_ihash_t *ih = apr_pcalloc(pool, sizeof(h2_ihash_t));
+    ih->hash = apr_hash_make_custom(pool, ihash);
+    ih->ioff = offset_of_int;
+    return ih;
+}
+
+size_t h2_ihash_count(h2_ihash_t *ih)
+{
+    return apr_hash_count(ih->hash);
+}
+
+int h2_ihash_is_empty(h2_ihash_t *ih)
+{
+    return apr_hash_count(ih->hash) == 0;
+}
+
+void *h2_ihash_get(h2_ihash_t *ih, int id)
+{
+    return apr_hash_get(ih->hash, &id, sizeof(id));
+}
+
+typedef struct {
+    h2_ihash_iter_t *iter;
+    void *ctx;
+} iter_ctx;
+
+static int ihash_iter(void *ctx, const void *key, apr_ssize_t klen, 
+                     const void *val)
+{
+    iter_ctx *ictx = ctx;
+    return ictx->iter(ictx->ctx, (void*)val); /* why is this passed const?*/
+}
+
+void h2_ihash_iter(h2_ihash_t *ih, h2_ihash_iter_t *fn, void *ctx)
+{
+    iter_ctx ictx;
+    ictx.iter = fn;
+    ictx.ctx = ctx;
+    apr_hash_do(ihash_iter, &ictx, ih->hash);
+}
+
+void h2_ihash_add(h2_ihash_t *ih, void *val)
+{
+    apr_hash_set(ih->hash, ((char *)val + ih->ioff), sizeof(int), val);
+}
+
+void h2_ihash_remove(h2_ihash_t *ih, int id)
+{
+    apr_hash_set(ih->hash, &id, sizeof(id), NULL);
+}
+
+void h2_ihash_clear(h2_ihash_t *ih)
+{
+    apr_hash_clear(ih->hash);
+}
+
+/*******************************************************************************
+ * h2_util for apt_table_t
+ ******************************************************************************/
+ 
+typedef struct {
+    apr_size_t bytes;
+    apr_size_t pair_extra;
+} table_bytes_ctx;
+
+static int count_bytes(void *x, const char *key, const char *value)
+{
+    table_bytes_ctx *ctx = x;
+    if (key) {
+        ctx->bytes += strlen(key);
+    }
+    if (value) {
+        ctx->bytes += strlen(value);
+    }
+    ctx->bytes += ctx->pair_extra;
+    return 1;
+}
+
+apr_size_t h2_util_table_bytes(apr_table_t *t, apr_size_t pair_extra)
+{
+    table_bytes_ctx ctx;
+    
+    ctx.bytes = 0;
+    ctx.pair_extra = pair_extra;
+    apr_table_do(count_bytes, &ctx, t, NULL);
+    return ctx.bytes;
+}
+
+
 /*******************************************************************************
  * h2_util for bucket brigades
  ******************************************************************************/
@@ -970,6 +1105,9 @@ static literal IgnoredResponseTrailers[]
     H2_DEF_LITERAL("www-authenticate"),
     H2_DEF_LITERAL("proxy-authenticate"),
 };
+static literal IgnoredProxyRespHds[] = {
+    H2_DEF_LITERAL("alt-svc"),
+};
 
 static int ignore_header(const literal *lits, size_t llen,
                          const char *name, size_t nlen)
@@ -1002,12 +1140,125 @@ int h2_res_ignore_trailer(const char *na
     return ignore_header(H2_LIT_ARGS(IgnoredResponseTrailers), name, len);
 }
 
-void h2_req_strip_ignored_header(apr_table_t *headers)
+int h2_proxy_res_ignore_header(const char *name, size_t len)
 {
-    int i;
-    for (i = 0; i < H2_ALEN(IgnoredRequestHeaders); ++i) {
-        apr_table_unset(headers, IgnoredRequestHeaders[i].name);
+    return (h2_req_ignore_header(name, len) 
+            || ignore_header(H2_LIT_ARGS(IgnoredProxyRespHds), name, len));
+}
+
+
+/*******************************************************************************
+ * frame logging
+ ******************************************************************************/
+
+int h2_util_frame_print(const nghttp2_frame *frame, char *buffer, size_t maxlen)
+{
+    char scratch[128];
+    size_t s_len = sizeof(scratch)/sizeof(scratch[0]);
+    
+    switch (frame->hd.type) {
+        case NGHTTP2_DATA: {
+            return apr_snprintf(buffer, maxlen,
+                                "DATA[length=%d, flags=%d, stream=%d, padlen=%d]",
+                                (int)frame->hd.length, frame->hd.flags,
+                                frame->hd.stream_id, (int)frame->data.padlen);
+        }
+        case NGHTTP2_HEADERS: {
+            return apr_snprintf(buffer, maxlen,
+                                "HEADERS[length=%d, hend=%d, stream=%d, eos=%d]",
+                                (int)frame->hd.length,
+                                !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS),
+                                frame->hd.stream_id,
+                                !!(frame->hd.flags & NGHTTP2_FLAG_END_STREAM));
+        }
+        case NGHTTP2_PRIORITY: {
+            return apr_snprintf(buffer, maxlen,
+                                "PRIORITY[length=%d, flags=%d, stream=%d]",
+                                (int)frame->hd.length,
+                                frame->hd.flags, frame->hd.stream_id);
+        }
+        case NGHTTP2_RST_STREAM: {
+            return apr_snprintf(buffer, maxlen,
+                                "RST_STREAM[length=%d, flags=%d, stream=%d]",
+                                (int)frame->hd.length,
+                                frame->hd.flags, frame->hd.stream_id);
+        }
+        case NGHTTP2_SETTINGS: {
+            if (frame->hd.flags & NGHTTP2_FLAG_ACK) {
+                return apr_snprintf(buffer, maxlen,
+                                    "SETTINGS[ack=1, stream=%d]",
+                                    frame->hd.stream_id);
+            }
+            return apr_snprintf(buffer, maxlen,
+                                "SETTINGS[length=%d, stream=%d]",
+                                (int)frame->hd.length, frame->hd.stream_id);
+        }
+        case NGHTTP2_PUSH_PROMISE: {
+            return apr_snprintf(buffer, maxlen,
+                                "PUSH_PROMISE[length=%d, hend=%d, stream=%d]",
+                                (int)frame->hd.length,
+                                !!(frame->hd.flags & NGHTTP2_FLAG_END_HEADERS),
+                                frame->hd.stream_id);
+        }
+        case NGHTTP2_PING: {
+            return apr_snprintf(buffer, maxlen,
+                                "PING[length=%d, ack=%d, stream=%d]",
+                                (int)frame->hd.length,
+                                frame->hd.flags&NGHTTP2_FLAG_ACK,
+                                frame->hd.stream_id);
+        }
+        case NGHTTP2_GOAWAY: {
+            size_t len = (frame->goaway.opaque_data_len < s_len)?
+            frame->goaway.opaque_data_len : s_len-1;
+            memcpy(scratch, frame->goaway.opaque_data, len);
+            scratch[len] = '\0';
+            return apr_snprintf(buffer, maxlen, "GOAWAY[error=%d, reason='%s']",
+                                frame->goaway.error_code, scratch);
+        }
+        case NGHTTP2_WINDOW_UPDATE: {
+            return apr_snprintf(buffer, maxlen,
+                                "WINDOW_UPDATE[stream=%d, incr=%d]",
+                                frame->hd.stream_id, 
+                                frame->window_update.window_size_increment);
+        }
+        default:
+            return apr_snprintf(buffer, maxlen,
+                                "type=%d[length=%d, flags=%d, stream=%d]",
+                                frame->hd.type, (int)frame->hd.length,
+                                frame->hd.flags, frame->hd.stream_id);
     }
 }
 
+/*******************************************************************************
+ * push policy
+ ******************************************************************************/
+void h2_push_policy_determine(struct h2_request *req, apr_pool_t *p, int push_enabled)
+{
+    h2_push_policy policy = H2_PUSH_NONE;
+    if (push_enabled) {
+        const char *val = apr_table_get(req->headers, "accept-push-policy");
+        if (val) {
+            if (ap_find_token(p, val, "fast-load")) {
+                policy = H2_PUSH_FAST_LOAD;
+            }
+            else if (ap_find_token(p, val, "head")) {
+                policy = H2_PUSH_HEAD;
+            }
+            else if (ap_find_token(p, val, "default")) {
+                policy = H2_PUSH_DEFAULT;
+            }
+            else if (ap_find_token(p, val, "none")) {
+                policy = H2_PUSH_NONE;
+            }
+            else {
+                /* nothing known found in this header, go by default */
+                policy = H2_PUSH_DEFAULT;
+            }
+        }
+        else {
+            policy = H2_PUSH_DEFAULT;
+        }
+    }
+    req->push_policy = policy;
+}