diff --git a/mod_http2/h2_mplx.c b/mod_http2/h2_mplx.c index 33ea45e..f49b58e 100644 --- a/mod_http2/h2_mplx.c +++ b/mod_http2/h2_mplx.c @@ -352,10 +352,11 @@ apr_status_t h2_mplx_stream_do(h2_mplx *m, h2_mplx_stream_cb *cb, void *ctx) { stream_iter_ctx_t x; - H2_MPLX_ENTER(m); - x.cb = cb; x.ctx = ctx; + + H2_MPLX_ENTER(m); + h2_ihash_iter(m->streams, stream_iter_wrap, &x); H2_MPLX_LEAVE(m); @@ -1143,14 +1144,33 @@ int h2_mplx_awaits_data(h2_mplx *m) return waiting; } -apr_status_t h2_mplx_client_rst(h2_mplx *m, int stream_id) +apr_status_t h2_mplx_client_rst(h2_mplx *m, int stream_id, h2_stream *stream) { - h2_stream *stream; apr_status_t status = APR_SUCCESS; - + int registered; + H2_MPLX_ENTER_ALWAYS(m); - stream = h2_ihash_get(m->streams, stream_id); - if (stream && stream->task) { + registered = (h2_ihash_get(m->streams, stream_id) != NULL); + + if (!stream) { + /* a RST might arrive so late, we have already forgotten + * about it. Seems ok. */ + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c, + H2_MPLX_MSG(m, "RST on unknown stream %d"), stream_id); + AP_DEBUG_ASSERT(!registered); + } + else if (!registered) { + /* a RST on a stream that mplx has not been told about, but + * which the session knows. Very early and annoying. */ + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, m->c, + H2_STRM_MSG(stream, "very early RST, drop")); + h2_stream_set_monitor(stream, NULL); + h2_stream_rst(stream, H2_ERR_STREAM_CLOSED); + h2_stream_dispatch(stream, H2_SEV_EOS_SENT); + stream_cleanup(m, stream); + mplx_be_annoyed(m); + } + else if (stream->task) { status = mplx_be_annoyed(m); } H2_MPLX_LEAVE(m); diff --git a/mod_http2/h2_mplx.h b/mod_http2/h2_mplx.h index 8a4f63f..6d838e5 100644 --- a/mod_http2/h2_mplx.h +++ b/mod_http2/h2_mplx.h @@ -204,7 +204,8 @@ typedef int h2_mplx_stream_cb(struct h2_stream *s, void *ctx); apr_status_t h2_mplx_stream_do(h2_mplx *m, h2_mplx_stream_cb *cb, void *ctx); -apr_status_t h2_mplx_client_rst(h2_mplx *m, int stream_id); +apr_status_t h2_mplx_client_rst(h2_mplx *m, int stream_id, + struct h2_stream *stream); /******************************************************************************* * Output handling of streams. @@ -287,6 +288,9 @@ APR_RING_INSERT_TAIL((b), ap__b, h2_mplx, link); \ */ #define H2_MPLX_REMOVE(e) APR_RING_REMOVE((e), link) +#define H2_MPLX_MSG(m, msg) \ + "h2_mplx(%lu): "msg, (unsigned long)m->id + /******************************************************************************* * h2_mplx DoS protection ******************************************************************************/ diff --git a/mod_http2/h2_proxy_session.c b/mod_http2/h2_proxy_session.c index 97a4a2a..8478b16 100644 --- a/mod_http2/h2_proxy_session.c +++ b/mod_http2/h2_proxy_session.c @@ -677,7 +677,7 @@ static apr_status_t session_start(h2_proxy_session *session) apr_socket_t *s; s = ap_get_conn_socket(session->c); -#if (!defined(WIN32) && !defined(NETWARE)) || defined(DOXYGEN) +#if !defined(WIN32) && !defined(NETWARE) if (s) { ap_sock_disable_nagle(s); } @@ -1515,9 +1515,20 @@ static int done_iter(void *udata, void *val) { cleanup_iter_ctx *ctx = udata; h2_proxy_stream *stream = val; - int touched = (stream->data_sent || + int touched = (stream->data_sent || stream->id <= ctx->session->last_stream_id); - ctx->done(ctx->session, stream->r, APR_ECONNABORTED, touched); + + if (touched && stream->output) { + apr_bucket *b = ap_bucket_error_create(HTTP_BAD_GATEWAY, NULL, + stream->r->pool, + ctx->session->c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(stream->output, b); + b = apr_bucket_eos_create(ctx->session->c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(stream->output, b); + ap_pass_brigade(stream->r->output_filters, stream->output); + } + ctx->done(ctx->session, stream->r, APR_ECONNABORTED, touched); + return 1; } diff --git a/mod_http2/h2_session.c b/mod_http2/h2_session.c index a5cc306..090bba6 100644 --- a/mod_http2/h2_session.c +++ b/mod_http2/h2_session.c @@ -389,6 +389,10 @@ static int on_frame_recv_cb(nghttp2_session *ng2s, session->id, (int)frame->hd.stream_id, (int)frame->rst_stream.error_code); stream = get_stream(session, frame->hd.stream_id); + if (stream) { + rv = h2_stream_recv_frame(stream, NGHTTP2_RST_STREAM, frame->hd.flags, + frame->hd.length + H2_FRAME_HDR_LEN); + } if (stream && stream->initiated_on) { /* A stream reset on a request we sent it. Normal, when the * client does not want it. */ @@ -397,7 +401,8 @@ static int on_frame_recv_cb(nghttp2_session *ng2s, else { /* A stream reset on a request it sent us. Could happen in a browser * when the user navigates away or cancels loading - maybe. */ - h2_mplx_client_rst(session->mplx, frame->hd.stream_id); + h2_mplx_client_rst(session->mplx, frame->hd.stream_id, + stream); ++session->streams_reset; } break; @@ -778,6 +783,17 @@ static apr_status_t session_cleanup(h2_session *session, const char *trigger) "goodbye, clients will be confused, should not happen")); } + if (!h2_iq_empty(session->in_process)) { + int sid; + ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, + H2_SSSN_LOG(APLOGNO(), session, + "cleanup, resetting %d streams in in_process"), + h2_iq_count(session->in_process)); + while ((sid = h2_iq_shift(session->in_process)) > 0) { + h2_mplx_client_rst(session->mplx, sid, get_stream(session, sid)); + } + } + transit(session, trigger, H2_SESSION_ST_CLEANUP); h2_mplx_release_and_join(session->mplx, session->iowait); session->mplx = NULL; diff --git a/mod_http2/h2_stream.c b/mod_http2/h2_stream.c index 6136baa..397f890 100644 --- a/mod_http2/h2_stream.c +++ b/mod_http2/h2_stream.c @@ -120,7 +120,7 @@ static int trans_on_event[][H2_SS_MAX] = { { S_XXX, S_ERR, S_ERR, S_CL_L, S_CLS, S_XXX, S_XXX, S_XXX, },/* EV_CLOSED_L*/ { S_ERR, S_ERR, S_ERR, S_CL_R, S_ERR, S_CLS, S_NOP, S_NOP, },/* EV_CLOSED_R*/ { S_CLS, S_CLS, S_CLS, S_CLS, S_CLS, S_CLS, S_NOP, S_NOP, },/* EV_CANCELLED*/ -{ S_NOP, S_XXX, S_XXX, S_XXX, S_XXX, S_CLS, S_CLN, S_XXX, },/* EV_EOS_SENT*/ +{ S_NOP, S_XXX, S_XXX, S_XXX, S_XXX, S_CLS, S_CLN, S_NOP, },/* EV_EOS_SENT*/ }; static int on_map(h2_stream_state_t state, int map[H2_SS_MAX]) diff --git a/mod_http2/mod_proxy_http2.c b/mod_http2/mod_proxy_http2.c index 844653e..b98298e 100644 --- a/mod_http2/mod_proxy_http2.c +++ b/mod_http2/mod_proxy_http2.c @@ -67,7 +67,7 @@ typedef struct h2_proxy_ctx { unsigned flushall : 1; request_rec *r; /* the request processed in this ctx */ - apr_status_t r_status; /* status of request work */ + int r_status; /* status of request work */ int r_done; /* request was processed, not necessarily successfully */ int r_may_retry; /* request may be retried */ h2_proxy_session *session; /* current http2 session against backend */ @@ -403,7 +403,7 @@ run_connect: "setup new connection: is_ssl=%d %s %s %s", ctx->p_conn->is_ssl, ctx->p_conn->ssl_hostname, locurl, ctx->p_conn->hostname); - ctx->r_status = status; + ctx->r_status = ap_map_http_request_error(status, HTTP_SERVICE_UNAVAILABLE); goto cleanup; } @@ -418,7 +418,7 @@ run_connect: if (ctx->master->aborted) goto cleanup; status = ctx_run(ctx); - if (ctx->r_status != APR_SUCCESS && ctx->r_may_retry && !ctx->master->aborted) { + if (ctx->r_status != OK && ctx->r_may_retry && !ctx->owner->aborted) { /* Not successfully processed, but may retry, tear down old conn and start over */ if (ctx->p_conn) { ctx->p_conn->close = 1; @@ -453,6 +453,12 @@ cleanup: ap_set_module_config(ctx->owner->conn_config, &proxy_http2_module, NULL); ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, ctx->owner, APLOGNO(03377) "leaving handler"); + if (ctx->r_status != OK) { + ap_die(ctx->r_status, r); + } + else if (status != APR_SUCCESS) { + ap_die(ap_map_http_request_error(status, HTTP_SERVICE_UNAVAILABLE), r); + } return ctx->r_status; }