From: Stefan Eissing Date: Fri, 5 Aug 2016 13:48:16 +0000 (+0000) Subject: mod_http2: support for intermediate responses X-Git-Tag: 2.5.0-alpha~1323 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=8b0d43678800a852de81084b4a7652e62712b50d;p=apache mod_http2: support for intermediate responses git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1755323 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/CHANGES b/CHANGES index 34579a6f25..df9ed6afc8 100644 --- a/CHANGES +++ b/CHANGES @@ -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] diff --git a/modules/http2/h2.h b/modules/http2/h2.h index 9075b00a79..ab24947daa 100644 --- a/modules/http2/h2.h +++ b/modules/http2/h2.h @@ -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; }; diff --git a/modules/http2/h2_from_h1.c b/modules/http2/h2_from_h1.c index 0f893ec139..876ec58bfb 100644 --- a/modules/http2/h2_from_h1.c +++ b/modules/http2/h2_from_h1.c @@ -471,7 +471,8 @@ static h2_response *create_response(h2_from_h1 *from_h1, request_rec *r) (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) diff --git a/modules/http2/h2_mplx.c b/modules/http2/h2_mplx.c index 4313b5946f..c1de2699b3 100644 --- a/modules/http2/h2_mplx.c +++ b/modules/http2/h2_mplx.c @@ -196,12 +196,17 @@ static int purge_stream(void *ctx, void *val) { 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 *val) 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_mplx *m, apr_thread_cond_t *wait) 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_mplx *m, apr_thread_cond_t *wait) } } + 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_mplx *m, apr_thread_cond_t *wait) 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, int stream_id, h2_response *response) 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, int stream_id, h2_response *response) 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_events(h2_mplx *m, 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_mplx *m, int stream_id) 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); } diff --git a/modules/http2/h2_ngn_shed.c b/modules/http2/h2_ngn_shed.c index f0676421e7..14e57a7aa3 100644 --- a/modules/http2/h2_ngn_shed.c +++ b/modules/http2/h2_ngn_shed.c @@ -168,6 +168,12 @@ apr_status_t h2_ngn_shed_push_task(h2_ngn_shed *shed, const char *ngn_type, 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_ngn_shed *shed, const char *ngn_type, 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; diff --git a/modules/http2/h2_request.c b/modules/http2/h2_request.c index 2f23208062..a8a48742f6 100644 --- a/modules/http2/h2_request.c +++ b/modules/http2/h2_request.c @@ -247,7 +247,8 @@ h2_request *h2_request_clone(apr_pool_t *p, const h2_request *src) 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 h2_request *req, conn_rec *c) /* 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 h2_request *req, conn_rec *c) r = NULL; goto traceout; } - + AP_READ_REQUEST_SUCCESS((uintptr_t)r, (char *)r->method, (char *)r->uri, (char *)r->server->defn_name, r->status); diff --git a/modules/http2/h2_response.c b/modules/http2/h2_response.c index 4cafd3550e..80d8b031e6 100644 --- a/modules/http2/h2_response.c +++ b/modules/http2/h2_response.c @@ -133,7 +133,7 @@ h2_response *h2_response_create(int stream_id, 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 stream_id, request_rec *r, } 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_response *response, apr_table_t *trailers) 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; +} diff --git a/modules/http2/h2_response.h b/modules/http2/h2_response.h index ca57c532e6..5d1bf3767e 100644 --- a/modules/http2/h2_response.h +++ b/modules/http2/h2_response.h @@ -40,7 +40,7 @@ h2_response *h2_response_create(int stream_id, * @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_t *pool, h2_response *from); */ 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__) */ diff --git a/modules/http2/h2_session.c b/modules/http2/h2_session.c index 1a905eab53..20b3be980c 100644 --- a/modules/http2/h2_session.c +++ b/modules/http2/h2_session.c @@ -1419,18 +1419,28 @@ static apr_status_t on_stream_response(void *ctx, int stream_id) 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(void *ctx, int stream_id) * 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(void *ctx, int stream_id) 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_session *session, int status, const char *msg 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; diff --git a/modules/http2/h2_stream.c b/modules/http2/h2_stream.c index fe5e560f01..a3999ae75a 100644 --- a/modules/http2/h2_stream.c +++ b/modules/http2/h2_stream.c @@ -265,6 +265,16 @@ struct h2_response *h2_stream_get_response(h2_stream *stream) 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_stream *stream, apr_size_t amount) 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_stream *stream, h2_response *response, 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); + /* append */ + while (*pr) { + pr = &((*pr)->next); + } + *pr = response; - h2_stream_filter(stream); - if (stream->output) { - status = fill_buffer(stream, 0); + 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_stream *stream, int http_status) } 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_stream *stream, 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_stream *stream) 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_stream *stream) 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); diff --git a/modules/http2/h2_stream.h b/modules/http2/h2_stream.h index 4ffa7c32b1..aba3cca040 100644 --- a/modules/http2/h2_stream.h +++ b/modules/http2/h2_stream.h @@ -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_stream *stream, int eos, int push_enabled, 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_response(h2_stream *stream); * @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); diff --git a/modules/http2/h2_task.c b/modules/http2/h2_task.c index 1893b12fad..daca5f1885 100644 --- a/modules/http2/h2_task.c +++ b/modules/http2/h2_task.c @@ -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 *task, ap_filter_t* f, 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, ap_filter_t* f, * 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 *task, ap_filter_t* f, && (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_task *task) 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_input(ap_filter_t* filter, 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_response(ap_filter_t* filter, * 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_request(h2_task *task, conn_rec *c) } 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); diff --git a/modules/http2/h2_task.h b/modules/http2/h2_task.h index 1086e053e9..76e8780d2a 100644 --- a/modules/http2/h2_task.h +++ b/modules/http2/h2_task.h @@ -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); diff --git a/modules/http2/h2_util.c b/modules/http2/h2_util.c index 8d1060e579..84849551f5 100644 --- a/modules/http2/h2_util.c +++ b/modules/http2/h2_util.c @@ -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"), diff --git a/modules/http2/h2_version.h b/modules/http2/h2_version.h index 427f2ff455..a21aaf9ec5 100644 --- a/modules/http2/h2_version.h +++ b/modules/http2/h2_version.h @@ -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 */