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/08/05 13:48:16 UTC

svn commit: r1755323 - in /httpd/httpd/trunk: ./ modules/http2/

Author: icing
Date: Fri Aug  5 13:48:16 2016
New Revision: 1755323

URL: http://svn.apache.org/viewvc?rev=1755323&view=rev
Log:
mod_http2: support for intermediate responses

Modified:
    httpd/httpd/trunk/CHANGES
    httpd/httpd/trunk/modules/http2/h2.h
    httpd/httpd/trunk/modules/http2/h2_from_h1.c
    httpd/httpd/trunk/modules/http2/h2_mplx.c
    httpd/httpd/trunk/modules/http2/h2_ngn_shed.c
    httpd/httpd/trunk/modules/http2/h2_request.c
    httpd/httpd/trunk/modules/http2/h2_response.c
    httpd/httpd/trunk/modules/http2/h2_response.h
    httpd/httpd/trunk/modules/http2/h2_session.c
    httpd/httpd/trunk/modules/http2/h2_stream.c
    httpd/httpd/trunk/modules/http2/h2_stream.h
    httpd/httpd/trunk/modules/http2/h2_task.c
    httpd/httpd/trunk/modules/http2/h2_task.h
    httpd/httpd/trunk/modules/http2/h2_util.c
    httpd/httpd/trunk/modules/http2/h2_version.h

Modified: httpd/httpd/trunk/CHANGES
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/CHANGES?rev=1755323&r1=1755322&r2=1755323&view=diff
==============================================================================
--- httpd/httpd/trunk/CHANGES [utf-8] (original)
+++ httpd/httpd/trunk/CHANGES [utf-8] Fri Aug  5 13:48:16 2016
@@ -1,6 +1,9 @@
                                                          -*- coding: utf-8 -*-
 Changes with Apache 2.5.0
 
+  *) mod_http2: adding support for intermediate responses.
+     [Stefan Eissing]
+
   *) mod_reqtimeout: Fix body timeout disabling for CONNECT requests to avoid
      triggering mod_proxy_connect's AH01018 once the tunnel is established.
      [Yann Ylavic]

Modified: httpd/httpd/trunk/modules/http2/h2.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2.h?rev=1755323&r1=1755322&r2=1755323&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2.h (original)
+++ httpd/httpd/trunk/modules/http2/h2.h Fri Aug  5 13:48:16 2016
@@ -146,6 +146,8 @@ struct h2_response {
     apr_off_t   content_length;
     apr_table_t *headers;
     apr_table_t *trailers;
+    struct h2_response *next;
+    
     const char  *sos_filter;
 };
 

Modified: httpd/httpd/trunk/modules/http2/h2_from_h1.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_from_h1.c?rev=1755323&r1=1755322&r2=1755323&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_from_h1.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_from_h1.c Fri Aug  5 13:48:16 2016
@@ -471,7 +471,8 @@ static h2_response *create_response(h2_f
                      (void *) headers, r->headers_out, NULL);
     }
     
-    return h2_response_rcreate(from_h1->stream_id, r, headers, r->pool);
+    return h2_response_rcreate(from_h1->stream_id, r, r->status, 
+                               headers, r->pool);
 }
 
 apr_status_t h2_response_output_filter(ap_filter_t *f, apr_bucket_brigade *bb)

Modified: httpd/httpd/trunk/modules/http2/h2_mplx.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_mplx.c?rev=1755323&r1=1755322&r2=1755323&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_mplx.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_mplx.c Fri Aug  5 13:48:16 2016
@@ -196,12 +196,17 @@ static int purge_stream(void *ctx, void
 {
     h2_mplx *m = ctx;
     h2_stream *stream = val;
-    h2_task *task = h2_ihash_get(m->tasks, stream->id);
-    h2_ihash_remove(m->spurge, stream->id);
+    int stream_id = stream->id;
+    h2_task *task = h2_ihash_get(m->tasks, stream_id);
+    
+    h2_ihash_remove(m->spurge, stream_id);
     h2_stream_destroy(stream);
     if (task) {
         task_destroy(m, task, 1);
     }
+    /* FIXME: task_destroy() might in some twisted way place the
+     * stream in the spurge hash again. Remove it last. */
+    h2_ihash_remove(m->spurge, stream_id);
     return 0;
 }
 
@@ -212,6 +217,7 @@ static void purge_streams(h2_mplx *m)
             /* repeat until empty */
         }
         h2_ihash_clear(m->spurge);
+        AP_DEBUG_ASSERT(h2_ihash_empty(m->spurge));
     }
 }
 
@@ -504,13 +510,13 @@ static int task_print(void *ctx, void *v
 
         ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, m->c, /* NO APLOGNO */
                       "->03198: h2_stream(%s): %s %s %s -> %s %d"
-                      "[orph=%d/started=%d/done=%d]", 
+                      "[orph=%d/started=%d/done=%d/frozen=%d]", 
                       task->id, task->request->method, 
                       task->request->authority, task->request->path,
                       task->response? "http" : (task->rst_error? "reset" : "?"),
                       task->response? task->response->http_status : task->rst_error,
                       (stream? 0 : 1), task->worker_started, 
-                      task->worker_done);
+                      task->worker_done, task->frozen);
     }
     else if (task) {
         ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, m->c, /* NO APLOGNO */
@@ -593,8 +599,11 @@ apr_status_t h2_mplx_release_and_join(h2
     
         if (!h2_ihash_empty(m->shold)) {
             ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, m->c,
-                          "h2_mplx(%ld): 2. release_join with %d streams in hold", 
-                          m->id, (int)h2_ihash_count(m->shold));
+                          "h2_mplx(%ld): 2. release_join with %d streams in "
+                          "hold, %d workers busy, %d tasks", 
+                          m->id, (int)h2_ihash_count(m->shold),
+                          m->workers_busy,  
+                          (int)h2_ihash_count(m->tasks));
         }
         if (!h2_ihash_empty(m->spurge)) {
             ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, m->c,
@@ -635,6 +644,12 @@ apr_status_t h2_mplx_release_and_join(h2
             }
         }
         
+        if (!h2_ihash_empty(m->tasks) && APLOGctrace1(m->c)) {
+            ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c,
+                          "h2_mplx(%ld): 3. release_join with %d tasks",
+                          m->id, (int)h2_ihash_count(m->tasks));
+            h2_ihash_iter(m->tasks, task_print, m);
+        }
         AP_DEBUG_ASSERT(h2_ihash_empty(m->shold));
         if (!h2_ihash_empty(m->spurge)) {
             ap_log_cerror(APLOG_MARK, APLOG_TRACE2, 0, m->c,
@@ -642,7 +657,6 @@ apr_status_t h2_mplx_release_and_join(h2
                           m->id, (int)h2_ihash_count(m->spurge));
             purge_streams(m);
         }
-        AP_DEBUG_ASSERT(h2_ihash_empty(m->spurge));
         
         if (!h2_ihash_empty(m->tasks)) {
             ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c, APLOGNO(03056)
@@ -702,13 +716,15 @@ static apr_status_t out_open(h2_mplx *m,
         return APR_ECONNABORTED;
     }
     
-    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, m->c,
-                  "h2_mplx(%s): open response: %d, rst=%d",
+    status = h2_task_add_response(task, response);
+    ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, m->c,
+                  "h2_mplx(%s): add response: %d, rst=%d",
                   task->id, response->http_status, response->rst_error);
+    if (status != APR_SUCCESS) {
+        return status;
+    }
     
-    h2_task_set_response(task, response);
-    
-    if (task->output.beam) {
+    if (task->output.beam && !task->output.opened) {
         h2_beam_buffer_size_set(task->output.beam, m->stream_max_mem);
         h2_beam_timeout_set(task->output.beam, m->stream_timeout);
         h2_beam_on_consumed(task->output.beam, stream_output_consumed, task);
@@ -717,6 +733,7 @@ static apr_status_t out_open(h2_mplx *m,
             h2_beam_on_file_beam(task->output.beam, can_beam_file, m);
         }
         h2_beam_mutex_set(task->output.beam, beam_enter, task->cond, m);
+        task->output.opened = 1;
     }
     
     h2_ihash_add(m->sready, stream);
@@ -1402,13 +1419,21 @@ apr_status_t h2_mplx_dispatch_master_eve
                               m->id, stream->id);
                 task = h2_ihash_get(m->tasks, stream->id);
                 if (task) {
-                    task->submitted = 1;
+                    task->response_sent = 1;
                     if (task->rst_error) {
                         h2_stream_rst(stream, task->rst_error);
                     }
                     else {
                         AP_DEBUG_ASSERT(task->response);
-                        h2_stream_set_response(stream, task->response, task->output.beam);
+                        status = h2_stream_add_response(stream, task->response, 
+                                                        task->output.beam);
+                        if (status != APR_SUCCESS) {
+                            h2_stream_rst(stream, H2_ERR_INTERNAL_ERROR);
+                        }
+                        if (!h2_response_get_final(task->response)) {
+                            /* the final response needs still to arrive */
+                            task->response = NULL;
+                        }
                     }
                 }
                 else {
@@ -1476,7 +1501,7 @@ apr_status_t h2_mplx_suspend_stream(h2_m
             if (stream->started && (!task || task->worker_done)) {
                 h2_ihash_add(m->sresume, stream);
             }
-            else {
+            else if (task->output.beam) {
                 /* register callback so that we can resume on new output */
                 h2_beam_on_produced(task->output.beam, output_produced, m);
             }

Modified: httpd/httpd/trunk/modules/http2/h2_ngn_shed.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_ngn_shed.c?rev=1755323&r1=1755322&r2=1755323&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_ngn_shed.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_ngn_shed.c Fri Aug  5 13:48:16 2016
@@ -168,6 +168,12 @@ apr_status_t h2_ngn_shed_push_task(h2_ng
         return APR_EOF;
     }
     
+    if (task->assigned) {
+        --task->assigned->no_assigned;
+        --task->assigned->no_live;
+        task->assigned = NULL;
+    }
+    
     ngn = apr_hash_get(shed->ngns, ngn_type, APR_HASH_KEY_STRING);
     if (ngn && !ngn->shutdown) {
         /* this task will be processed in another thread,
@@ -178,7 +184,6 @@ apr_status_t h2_ngn_shed_push_task(h2_ng
         if (!h2_task_is_detached(task)) {
             h2_task_freeze(task);
         }
-        /* FIXME: sometimes ngn is garbage, probly alread freed */
         ngn_add_task(ngn, task);
         ngn->no_assigned++;
         return APR_SUCCESS;

Modified: httpd/httpd/trunk/modules/http2/h2_request.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_request.c?rev=1755323&r1=1755322&r2=1755323&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_request.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_request.c Fri Aug  5 13:48:16 2016
@@ -247,7 +247,8 @@ h2_request *h2_request_clone(apr_pool_t
 request_rec *h2_request_create_rec(const h2_request *req, conn_rec *c)
 {
     int access_status = HTTP_OK;    
-    
+    const char *expect;
+
     request_rec *r = ap_create_request(c);
 
     r->headers_in = apr_table_clone(r->pool, req->headers);
@@ -281,6 +282,18 @@ request_rec *h2_request_create_rec(const
     /* we may have switched to another server */
     r->per_dir_config = r->server->lookup_defaults;
     
+    if (r && ((expect = apr_table_get(r->headers_in, "Expect")) != NULL)
+        && (expect[0] != '\0')) {
+        if (ap_cstr_casecmp(expect, "100-continue") == 0) {
+            r->expecting_100 = 1;
+            ap_add_input_filter("H2_CONTINUE", NULL, r, c);
+        }
+        else {
+            r->status = HTTP_EXPECTATION_FAILED;
+            ap_send_error_response(r, 0);
+        }
+    }
+    
     /*
      * Add the HTTP_IN filter here to ensure that ap_discard_request_body
      * called by ap_die and by ap_send_error_response works correctly on
@@ -304,7 +317,7 @@ request_rec *h2_request_create_rec(const
         r = NULL;
         goto traceout;
     }
-    
+
     AP_READ_REQUEST_SUCCESS((uintptr_t)r, (char *)r->method, 
                             (char *)r->uri, (char *)r->server->defn_name, 
                             r->status);

Modified: httpd/httpd/trunk/modules/http2/h2_response.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_response.c?rev=1755323&r1=1755322&r2=1755323&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_response.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_response.c Fri Aug  5 13:48:16 2016
@@ -133,7 +133,7 @@ h2_response *h2_response_create(int stre
                                   parse_headers(hlines, pool), notes, pool);
 }
 
-h2_response *h2_response_rcreate(int stream_id, request_rec *r,
+h2_response *h2_response_rcreate(int stream_id, request_rec *r, int status,
                                  apr_table_t *header, apr_pool_t *pool)
 {
     h2_response *response = apr_pcalloc(pool, sizeof(h2_response));
@@ -142,7 +142,7 @@ h2_response *h2_response_rcreate(int str
     }
     
     response->stream_id      = stream_id;
-    response->http_status    = r->status;
+    response->http_status    = status;
     response->content_length = -1;
     response->headers        = header;
     response->sos_filter     = get_sos_filter(r->notes);
@@ -203,3 +203,17 @@ void h2_response_set_trailers(h2_respons
     response->trailers = trailers;
 }
 
+int h2_response_is_final(h2_response *response)
+{
+    return response->http_status >= 200;
+}
+
+h2_response *h2_response_get_final(h2_response *response)
+{
+    for (/**/; response; response = response->next) {
+        if (h2_response_is_final(response)) {
+            return response;
+        }
+    }
+    return NULL;
+}

Modified: httpd/httpd/trunk/modules/http2/h2_response.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_response.h?rev=1755323&r1=1755322&r2=1755323&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_response.h (original)
+++ httpd/httpd/trunk/modules/http2/h2_response.h Fri Aug  5 13:48:16 2016
@@ -40,7 +40,7 @@ h2_response *h2_response_create(int stre
  * @param header the headers of the response
  * @param pool the memory pool to use
  */
-h2_response *h2_response_rcreate(int stream_id, request_rec *r,
+h2_response *h2_response_rcreate(int stream_id, request_rec *r, int status, 
                                  apr_table_t *header, apr_pool_t *pool);
 
 /**
@@ -70,4 +70,7 @@ h2_response *h2_response_clone(apr_pool_
  */
 void h2_response_set_trailers(h2_response *response, apr_table_t *trailers);
 
+int h2_response_is_final(h2_response *response);
+h2_response *h2_response_get_final(h2_response *response);
+
 #endif /* defined(__mod_h2__h2_response__) */

Modified: httpd/httpd/trunk/modules/http2/h2_session.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_session.c?rev=1755323&r1=1755322&r2=1755323&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_session.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_session.c Fri Aug  5 13:48:16 2016
@@ -1419,18 +1419,28 @@ static apr_status_t on_stream_response(v
     if (!stream) {
         return APR_NOTFOUND;
     }
-    
-    response = h2_stream_get_response(stream);
-    AP_DEBUG_ASSERT(response || stream->rst_error);
-    
-    if (stream->submitted) {
-        rv = NGHTTP2_PROTOCOL_ERROR;
+    else if (!stream->response) {
+        int err = H2_STREAM_RST(stream, H2_ERR_PROTOCOL_ERROR);
+        
+        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03074)
+                      "h2_stream(%ld-%d): RST_STREAM, err=%d",
+                      session->id, stream->id, err);
+
+        rv = nghttp2_submit_rst_stream(session->ngh2, NGHTTP2_FLAG_NONE,
+                                       stream->id, err);
+        goto leave;
     }
-    else if (response && response->headers) {
+    
+    while ((response = h2_stream_get_unsent_response(stream)) != NULL) {
         nghttp2_data_provider provider, *pprovider = NULL;
         h2_ngheader *ngh;
         const h2_priority *prio;
         
+        if (stream->submitted) {
+            rv = NGHTTP2_PROTOCOL_ERROR;
+            goto leave;
+        }
+        
         ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03073)
                       "h2_stream(%ld-%d): submit response %d, REMOTE_WINDOW_SIZE=%u",
                       session->id, stream->id, response->http_status,
@@ -1458,7 +1468,9 @@ static apr_status_t on_stream_response(v
          *    as the client, having this resource in its cache, might
          *    also have the pushed ones as well.
          */
-        if (stream->request && !stream->request->initiated_on
+        if (stream->request 
+            && !stream->request->initiated_on
+            && h2_response_is_final(response)
             && H2_HTTP_2XX(response->http_status)
             && h2_session_push_enabled(session)) {
             
@@ -1468,35 +1480,24 @@ static apr_status_t on_stream_response(v
         prio = h2_stream_get_priority(stream);
         if (prio) {
             h2_session_set_prio(session, stream, prio);
-            /* no showstopper if that fails for some reason */
         }
         
         ngh = h2_util_ngheader_make_res(stream->pool, response->http_status, 
                                         response->headers);
         rv = nghttp2_submit_response(session->ngh2, response->stream_id,
                                      ngh->nv, ngh->nvlen, pprovider);
-    }
-    else {
-        int err = H2_STREAM_RST(stream, H2_ERR_PROTOCOL_ERROR);
+        stream->submitted = h2_response_is_final(response);
+        session->have_written = 1;
         
-        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03074)
-                      "h2_stream(%ld-%d): RST_STREAM, err=%d",
-                      session->id, stream->id, err);
-
-        rv = nghttp2_submit_rst_stream(session->ngh2, NGHTTP2_FLAG_NONE,
-                                       stream->id, err);
-    }
-    
-    stream->submitted = 1;
-    session->have_written = 1;
-    
-    if (stream->request && stream->request->initiated_on) {
-        ++session->pushes_submitted;
-    }
-    else {
-        ++session->responses_submitted;
+        if (stream->request && stream->request->initiated_on) {
+            ++session->pushes_submitted;
+        }
+        else {
+            ++session->responses_submitted;
+        }
     }
     
+leave:
     if (nghttp2_is_fatal(rv)) {
         status = APR_EGENERAL;
         dispatch_event(session, H2_SESSION_EV_PROTO_ERROR, rv, nghttp2_strerror(rv));
@@ -1699,7 +1700,12 @@ static void update_child_status(h2_sessi
 static void transit(h2_session *session, const char *action, h2_session_state nstate)
 {
     if (session->state != nstate) {
-        ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c, APLOGNO(03078)
+        int loglvl = APLOG_DEBUG;
+        if ((session->state == H2_SESSION_ST_BUSY && nstate == H2_SESSION_ST_WAIT)
+            || (session->state == H2_SESSION_ST_WAIT && nstate == H2_SESSION_ST_BUSY)){
+            loglvl = APLOG_TRACE1;
+        }
+        ap_log_cerror(APLOG_MARK, loglvl, 0, session->c, APLOGNO(03078)
                       "h2_session(%ld): transit [%s] -- %s --> [%s]", session->id,
                       state_name(session->state), action, state_name(nstate));
         session->state = nstate;

Modified: httpd/httpd/trunk/modules/http2/h2_stream.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_stream.c?rev=1755323&r1=1755322&r2=1755323&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_stream.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_stream.c Fri Aug  5 13:48:16 2016
@@ -265,6 +265,16 @@ struct h2_response *h2_stream_get_respon
     return stream->response;
 }
 
+struct h2_response *h2_stream_get_unsent_response(h2_stream *stream)
+{
+    h2_response *unsent = (stream->last_sent? 
+                           stream->last_sent->next : stream->response);
+    if (unsent) {
+        stream->last_sent = unsent;
+    }
+    return unsent;
+}
+
 apr_status_t h2_stream_set_request(h2_stream *stream, request_rec *r)
 {
     apr_status_t status;
@@ -524,11 +534,12 @@ static apr_status_t fill_buffer(h2_strea
     return status;
 }
 
-apr_status_t h2_stream_set_response(h2_stream *stream, h2_response *response,
+apr_status_t h2_stream_add_response(h2_stream *stream, h2_response *response,
                                     h2_bucket_beam *output)
 {
     apr_status_t status = APR_SUCCESS;
     conn_rec *c = stream->session->c;
+    h2_response **pr = &stream->response;
     
     if (!output_open(stream)) {
         ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
@@ -536,15 +547,29 @@ apr_status_t h2_stream_set_response(h2_s
                       stream->session->id, stream->id);
         return APR_ECONNRESET;
     }
+    if (stream->submitted) {
+        ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
+                      "h2_stream(%ld-%d): already submitted final response", 
+                      stream->session->id, stream->id);
+        return APR_ECONNRESET;
+    }
     
-    stream->response = response;
-    stream->output = output;
-    stream->buffer = apr_brigade_create(stream->pool, c->bucket_alloc);
-    
-    h2_stream_filter(stream);
-    if (stream->output) {
-        status = fill_buffer(stream, 0);
+    /* append */
+    while (*pr) {
+        pr = &((*pr)->next);
+    }
+    *pr = response;
+    
+    if (h2_response_is_final(response)) {
+        stream->output = output;
+        stream->buffer = apr_brigade_create(stream->pool, c->bucket_alloc);
+        
+        h2_stream_filter(stream);
+        if (stream->output) {
+            status = fill_buffer(stream, 0);
+        }
     }
+    
     ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, c,
                   "h2_stream(%ld-%d): set_response(%d)", 
                   stream->session->id, stream->id, 
@@ -561,7 +586,7 @@ apr_status_t h2_stream_set_error(h2_stre
     }
     response = h2_response_die(stream->id, http_status, stream->request, 
                                stream->pool);
-    return h2_stream_set_response(stream, response, NULL);
+    return h2_stream_add_response(stream, response, NULL);
 }
 
 static const apr_size_t DATA_CHUNK_SIZE = ((16*1024) - 100 - 9); 
@@ -579,6 +604,10 @@ apr_status_t h2_stream_out_prepare(h2_st
         return APR_ECONNRESET;
     }
 
+    if (!stream->buffer) {
+        return APR_EAGAIN;
+    }
+    
     if (*plen > 0) {
         requested = H2MIN(*plen, DATA_CHUNK_SIZE);
     }
@@ -662,7 +691,7 @@ apr_status_t h2_stream_submit_pushes(h2_
     int i;
     
     pushes = h2_push_collect_update(stream, stream->request, 
-                                    h2_stream_get_response(stream));
+                                    stream->response);
     if (pushes && !apr_is_empty_array(pushes)) {
         ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, stream->session->c,
                       "h2_stream(%ld-%d): found %d push candidates",
@@ -686,10 +715,8 @@ apr_table_t *h2_stream_get_trailers(h2_s
 
 const h2_priority *h2_stream_get_priority(h2_stream *stream)
 {
-    h2_response *response = h2_stream_get_response(stream);
-    
-    if (response && stream->request && stream->request->initiated_on) {
-        const char *ctype = apr_table_get(response->headers, "content-type");
+    if (stream->response && stream->request && stream->request->initiated_on) {
+        const char *ctype = apr_table_get(stream->response->headers, "content-type");
         if (ctype) {
             /* FIXME: Not good enough, config needs to come from request->server */
             return h2_config_get_priority(stream->session->config, ctype);

Modified: httpd/httpd/trunk/modules/http2/h2_stream.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_stream.h?rev=1755323&r1=1755322&r2=1755323&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_stream.h (original)
+++ httpd/httpd/trunk/modules/http2/h2_stream.h Fri Aug  5 13:48:16 2016
@@ -52,6 +52,7 @@ struct h2_stream {
     int request_headers_added;  /* number of request headers added */
     
     struct h2_response *response;
+    struct h2_response *last_sent;
     struct h2_bucket_beam *output;
     apr_bucket_brigade *buffer;
     apr_bucket_brigade *tmp;
@@ -179,6 +180,7 @@ apr_status_t h2_stream_schedule(h2_strea
 int h2_stream_is_scheduled(const h2_stream *stream);
 
 struct h2_response *h2_stream_get_response(h2_stream *stream);
+struct h2_response *h2_stream_get_unsent_response(h2_stream *stream);
 
 /**
  * Set the response for this stream. Invoked when all meta data for
@@ -189,7 +191,7 @@ struct h2_response *h2_stream_get_respon
  * @param bb bucket brigade with output data for the stream. Optional,
  *        may be incomplete.
  */
-apr_status_t h2_stream_set_response(h2_stream *stream, 
+apr_status_t h2_stream_add_response(h2_stream *stream, 
                                     struct h2_response *response,
                                     struct h2_bucket_beam *output);
 

Modified: httpd/httpd/trunk/modules/http2/h2_task.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_task.c?rev=1755323&r1=1755322&r2=1755323&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_task.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_task.c Fri Aug  5 13:48:16 2016
@@ -42,6 +42,7 @@
 #include "h2_h2.h"
 #include "h2_mplx.h"
 #include "h2_request.h"
+#include "h2_response.h"
 #include "h2_session.h"
 #include "h2_stream.h"
 #include "h2_task.h"
@@ -163,6 +164,21 @@ static apr_status_t input_read(h2_task *
         return APR_EOF;
     }
     
+    /*
+    if (f->r && f->r->expecting_100) {
+        ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, f->c,
+                      "h2_task(%s): need to send 100 Continue here", 
+                      task->id);
+        f->r->expecting_100 = 0;
+    }
+    if (task->r && task->r->expecting_100) {
+        ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, f->c,
+                      "h2_task2(%s): need to send 100 Continue here", 
+                      task->id);
+        task->r->expecting_100 = 0;
+    }
+    */
+
     /* Cleanup brigades from those nasty 0 length non-meta buckets
      * that apr_brigade_split_line() sometimes produces. */
     for (b = APR_BRIGADE_FIRST(task->input.bb);
@@ -314,10 +330,8 @@ static apr_status_t input_read(h2_task *
  * task output handling
  ******************************************************************************/
 
-static apr_status_t open_response(h2_task *task)
+static apr_status_t open_response(h2_task *task, h2_response *response)
 {
-    h2_response *response;
-    response = h2_from_h1_get_response(task->output.from_h1);
     if (!response) {
         /* This happens currently when ap_die(status, r) is invoked
          * by a read request filter. */
@@ -461,7 +475,8 @@ static apr_status_t output_write(h2_task
         && (flush || h2_beam_get_mem_used(task->output.beam) > (32*1024))) {
         /* if we have enough buffered or we got a flush bucket, open
         * the response now. */
-        status = open_response(task);
+        status = open_response(task, 
+            h2_from_h1_get_response(task->output.from_h1));
         task->output.response_open = 1;
     }
     
@@ -473,7 +488,8 @@ static apr_status_t output_finish(h2_tas
     apr_status_t status = APR_SUCCESS;
     
     if (!task->output.response_open) {
-        status = open_response(task);
+        status = open_response(task,
+            h2_from_h1_get_response(task->output.from_h1));
         task->output.response_open = 1;
     }
     return status;
@@ -494,6 +510,33 @@ static apr_status_t h2_filter_stream_inp
     return input_read(task, filter, brigade, mode, block, readbytes);
 }
 
+static apr_status_t h2_filter_continue(ap_filter_t* f,
+                                       apr_bucket_brigade* brigade,
+                                       ap_input_mode_t mode,
+                                       apr_read_type_e block,
+                                       apr_off_t readbytes)
+{
+    h2_task *task = h2_ctx_cget_task(f->c);
+    apr_status_t status;
+    
+    AP_DEBUG_ASSERT(task);
+    if (f->r->expecting_100) {
+        h2_response *response;
+
+        response = h2_response_rcreate(task->stream_id, f->r, HTTP_CONTINUE, 
+                                       f->r->headers_out, f->r->pool);
+        ap_log_rerror(APLOG_MARK, APLOG_TRACE1, 0, f->r,
+                      "h2_task(%s): send 100 Continue", task->id);
+        status = open_response(task, response);
+        if (status != APR_SUCCESS) {
+            return status;
+        }
+        f->r->expecting_100 = 0;
+        apr_table_clear(f->r->headers_out);
+    }
+    return ap_get_brigade(f->next, brigade, mode, block, readbytes);
+}
+
 static apr_status_t h2_filter_stream_output(ap_filter_t* filter,
                                             apr_bucket_brigade* brigade)
 {
@@ -517,22 +560,23 @@ static apr_status_t h2_filter_read_respo
  * task things
  ******************************************************************************/
  
-void h2_task_set_response(h2_task *task, h2_response *response) 
+apr_status_t h2_task_add_response(h2_task *task, h2_response *response) 
 {
     AP_DEBUG_ASSERT(response);
-    AP_DEBUG_ASSERT(!task->response);
     /* we used to clone the response into out own pool. But
      * we have much tighter control over the EOR bucket nowadays,
      * so just use the instance given */
+    response->next = task->response;
     task->response = response;
     if (response->rst_error) {
         h2_task_rst(task, response->rst_error);
     }
+    return APR_SUCCESS;
 }
 
 
 int h2_task_can_redo(h2_task *task) {
-    if (task->submitted
+    if (task->response_sent
         || (task->input.beam && h2_beam_was_received(task->input.beam)) 
         || !task->request) {
         /* cannot repeat that. */
@@ -591,6 +635,8 @@ void h2_task_register_hooks(void)
                               NULL, AP_FTYPE_PROTOCOL);
     ap_register_input_filter("H2_TO_H1", h2_filter_stream_input,
                              NULL, AP_FTYPE_NETWORK);
+    ap_register_input_filter("H2_CONTINUE", h2_filter_continue,
+                             NULL, AP_FTYPE_PROTOCOL);
     ap_register_output_filter("H1_TO_H2", h2_filter_stream_output,
                               NULL, AP_FTYPE_NETWORK);
     ap_register_output_filter("H1_TO_H2_RESP", h2_filter_read_response,
@@ -749,11 +795,16 @@ static apr_status_t h2_task_process_requ
         }
         ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
                       "h2_task(%s): start process_request", task->id);
+        task->r = r;
+    
         ap_process_request(r);
         if (task->frozen) {
             ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
                           "h2_task(%s): process_request frozen", task->id);
         }
+        else {
+            task->r = NULL;
+        }
         ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, c,
                       "h2_task(%s): process_request done", task->id);
         

Modified: httpd/httpd/trunk/modules/http2/h2_task.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_task.h?rev=1755323&r1=1755322&r2=1755323&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_task.h (original)
+++ httpd/httpd/trunk/modules/http2/h2_task.h Fri Aug  5 13:48:16 2016
@@ -70,6 +70,7 @@ struct h2_task {
     struct {
         struct h2_bucket_beam *beam;
         struct h2_from_h1 *from_h1;
+        unsigned int opened : 1;
         unsigned int response_open : 1;
         unsigned int copy_files : 1;
         apr_off_t written;
@@ -85,7 +86,7 @@ struct h2_task {
     unsigned int frozen         : 1;
     unsigned int blocking       : 1;
     unsigned int detached       : 1;
-    unsigned int submitted      : 1; /* response has been submitted to client */
+    unsigned int response_sent  : 1; /* a response has been sent to client */
     unsigned int worker_started : 1; /* h2_worker started processing for this io */
     unsigned int worker_done    : 1; /* h2_worker finished for this io */
     
@@ -105,7 +106,7 @@ void h2_task_destroy(h2_task *task);
 
 apr_status_t h2_task_do(h2_task *task, apr_thread_t *thread);
 
-void h2_task_set_response(h2_task *task, struct h2_response *response);
+apr_status_t h2_task_add_response(h2_task *task, struct h2_response *response);
 
 void h2_task_redo(h2_task *task);
 int h2_task_can_redo(h2_task *task);

Modified: httpd/httpd/trunk/modules/http2/h2_util.c
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_util.c?rev=1755323&r1=1755322&r2=1755323&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_util.c (original)
+++ httpd/httpd/trunk/modules/http2/h2_util.c Fri Aug  5 13:48:16 2016
@@ -1160,7 +1160,7 @@ typedef struct {
 #define H2_LIT_ARGS(a)      (a),H2_ALEN(a)
 
 static literal IgnoredRequestHeaders[] = {
-    H2_DEF_LITERAL("expect"),
+/*H2_DEF_LITERAL("expect"),*/
     H2_DEF_LITERAL("upgrade"),
     H2_DEF_LITERAL("connection"),
     H2_DEF_LITERAL("keep-alive"),

Modified: httpd/httpd/trunk/modules/http2/h2_version.h
URL: http://svn.apache.org/viewvc/httpd/httpd/trunk/modules/http2/h2_version.h?rev=1755323&r1=1755322&r2=1755323&view=diff
==============================================================================
--- httpd/httpd/trunk/modules/http2/h2_version.h (original)
+++ httpd/httpd/trunk/modules/http2/h2_version.h Fri Aug  5 13:48:16 2016
@@ -26,7 +26,7 @@
  * @macro
  * Version number of the http2 module as c string
  */
-#define MOD_HTTP2_VERSION "1.5.14-DEV"
+#define MOD_HTTP2_VERSION "1.6.0-DEV"
 
 /**
  * @macro
@@ -34,7 +34,7 @@
  * release. This is a 24 bit number with 8 bits for major number, 8 bits
  * for minor and 8 bits for patch. Version 1.2.3 becomes 0x010203.
  */
-#define MOD_HTTP2_VERSION_NUM 0x01050e
+#define MOD_HTTP2_VERSION_NUM 0x010600
 
 
 #endif /* mod_h2_h2_version_h */