I noticed that currently mod_proxy_http2 cannot serve responses larger than 32 KiB for incoming requests over HTTP/1.1. Below is a simple config that can be used to reproduce the problem:
LoadModule http2_module modules/mod_http2.so LoadModule proxy_module modules/mod_proxy.so LoadModule proxy_http_module modules/mod_proxy_http.so LoadModule proxy_http2_module modules/mod_proxy_http2.so ProxyPass "/" "h2c://nghttp2.org/" Issuing an HTTP/1.1 request to a file that exceeds 32 KiB is going to stall and eventually fail: curl --http1.1 http://localhost/stylesheets/screen.css ... curl: (18) transfer closed with 6016 bytes remaining to read The reason is that in this particular case mod_proxy_http2 doesn't update the HTTP/2 flow control windows. Once the amount of the received data hits a hardcoded threshold of 32 KiB, no data can be received without a WINDOW_UPDATE, and since it doesn't happen, the request eventually times out. Please see the push_request_somewhere() function in mod_proxy_http2.c:441: if (!ctx->engine) { /* No engine was available or has been initialized, handle this * request just by ourself. */ ctx->engine_id = apr_psprintf(ctx->pool, "eng-proxy-%ld", c->id); ctx->engine_type = engine_type; ctx->engine_pool = ctx->pool; ctx->req_buffer_size = (32*1024); ctx->standalone = 1; ... Currently, mod_proxy_http2 can either handle the request to a backend by itself or push the handling of the request to the mod_http2's request engine. In the latter case, the HTTP/2 flow control windows are updated per the h2_req_engine_out_consumed() function calls. But if mod_proxy_http2 processes the request by itself, they are not. This is exactly what happens in the described case with the HTTP/1.1 request. I attached the patch with a fix for this issue. The idea behind the patch is that we keep track of how a particular request is handled and call the nghttp2_session_consume() function if that's required. Regards, Evgeny Kotkov
Index: modules/http2/h2_proxy_session.c =================================================================== --- modules/http2/h2_proxy_session.c (revision 1747688) +++ modules/http2/h2_proxy_session.c (working copy) @@ -36,6 +36,7 @@ typedef struct h2_proxy_stream { const char *url; request_rec *r; h2_request *req; + int standalone; h2_stream_state_t state; unsigned int suspended : 1; @@ -370,6 +371,12 @@ static int on_data_chunk_recv(nghttp2_session *ngh stream_id, NGHTTP2_STREAM_CLOSED); return NGHTTP2_ERR_STREAM_CLOSING; } + if (stream->standalone) { + nghttp2_session_consume(ngh2, stream_id, len); + ap_log_rerror(APLOG_MARK, APLOG_TRACE2, 0, stream->r, + "h2_proxy_session(%s): stream %d, win_update %d bytes", + session->id, stream_id, (int)len); + } return 0; } @@ -576,7 +583,8 @@ static apr_status_t session_start(h2_proxy_session } static apr_status_t open_stream(h2_proxy_session *session, const char *url, - request_rec *r, h2_proxy_stream **pstream) + request_rec *r, int standalone, + h2_proxy_stream **pstream) { h2_proxy_stream *stream; apr_uri_t puri; @@ -588,6 +596,7 @@ static apr_status_t open_stream(h2_proxy_session * stream->pool = r->pool; stream->url = url; stream->r = r; + stream->standalone = standalone; stream->session = session; stream->state = H2_STREAM_ST_IDLE; @@ -761,12 +770,13 @@ static apr_status_t h2_proxy_session_read(h2_proxy } apr_status_t h2_proxy_session_submit(h2_proxy_session *session, - const char *url, request_rec *r) + const char *url, request_rec *r, + int standalone) { h2_proxy_stream *stream; apr_status_t status; - status = open_stream(session, url, r, &stream); + status = open_stream(session, url, r, standalone, &stream); if (status == APR_SUCCESS) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03381) "process stream(%d): %s %s%s, original: %s", Index: modules/http2/h2_proxy_session.h =================================================================== --- modules/http2/h2_proxy_session.h (revision 1747688) +++ modules/http2/h2_proxy_session.h (working copy) @@ -89,7 +89,7 @@ h2_proxy_session *h2_proxy_session_setup(const cha h2_proxy_request_done *done); apr_status_t h2_proxy_session_submit(h2_proxy_session *s, const char *url, - request_rec *r); + request_rec *r, int standalone); /** * Perform a step in processing the proxy session. Will return aftert Index: modules/http2/mod_proxy_http2.c =================================================================== --- modules/http2/mod_proxy_http2.c (revision 1747688) +++ modules/http2/mod_proxy_http2.c (working copy) @@ -258,7 +258,7 @@ static apr_status_t add_request(h2_proxy_session * url = apr_table_get(r->notes, H2_PROXY_REQ_URL_NOTE); apr_table_setn(r->notes, "proxy-source-port", apr_psprintf(r->pool, "%hu", ctx->p_conn->connection->local_addr->port)); - status = h2_proxy_session_submit(session, url, r); + status = h2_proxy_session_submit(session, url, r, ctx->standalone); if (status != APR_SUCCESS) { ap_log_cerror(APLOG_MARK, APLOG_ERR, status, r->connection, APLOGNO(03351) "pass request body failed to %pI (%s) from %s (%s)",