-*- coding: utf-8 -*-
Changes with Apache 2.5.0
+ *) mod_proxy_http2: rescheduling of requests that have not been processed
+ by the backend when receiving a GOAWAY frame before done.
+ [Stefan Eissign]
+
*) mod_reqtimeout: Prevent long response times from triggering a timeout once
the request has been fully read. PR 59045. [Yann Ylavic]
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, task->c,
"h2_ngn_shed(%ld): pushing request %s to %s",
shed->c->id, task->id, ngn->id);
- h2_task_freeze(task, r);
+ if (!h2_task_is_detached(task)) {
+ h2_task_freeze(task, r);
+ }
/* FIXME: sometimes ngn is garbage, probly alread freed */
ngn_add_req(ngn, task, r);
ngn->no_assigned++;
}
break;
case NGHTTP2_GOAWAY:
+ /* we expect the remote server to tell us the highest stream id
+ * that it has started processing. */
+ session->last_stream_id = frame->goaway.last_stream_id;
dispatch_event(session, H2_PROXYS_EV_REMOTE_GOAWAY, 0, NULL);
if (APLOGcinfo(session->c)) {
char buffer[256];
h2_ihash_remove(session->streams, stream_id);
h2_iq_remove(session->suspended, stream_id);
if (session->done) {
- session->done(session, stream->r);
+ session->done(session, stream->r, 1, 1);
}
}
h2_proxy_request_done *done;
} cleanup_iter_ctx;
-static int cleanup_iter(void *udata, void *val)
+static int done_iter(void *udata, void *val)
{
cleanup_iter_ctx *ctx = udata;
h2_proxy_stream *stream = val;
- ctx->done(ctx->session, stream->r);
+ int touched = (stream->id <= ctx->session->last_stream_id);
+ ctx->done(ctx->session, stream->r, 0, touched);
return 1;
}
cleanup_iter_ctx ctx;
ctx.session = session;
ctx.done = done;
- ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, session->c,
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
"h2_proxy_session(%s): terminated, %d streams unfinished",
session->id, (int)h2_ihash_count(session->streams));
- h2_ihash_iter(session->streams, cleanup_iter, &ctx);
+ h2_ihash_iter(session->streams, done_iter, &ctx);
h2_ihash_clear(session->streams);
}
}
typedef struct h2_proxy_session h2_proxy_session;
-typedef void h2_proxy_request_done(h2_proxy_session *s, request_rec *r);
+typedef void h2_proxy_request_done(h2_proxy_session *s, request_rec *r,
+ int complete, int touched);
struct h2_proxy_session {
const char *id;
struct h2_ihash_t *streams;
struct h2_int_queue *suspended;
apr_size_t remote_max_concurrent;
- int max_stream_recv;
+ int last_stream_id; /* last stream id processed by backend, or 0 */
apr_bucket_brigade *input;
apr_bucket_brigade *output;
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, task->c,
"h2_task(%s), thawed", task->id);
}
+ task->detached = 1;
return APR_SUCCESS;
}
+int h2_task_is_detached(h2_task *task)
+{
+ return task->detached;
+}
unsigned int ser_headers : 1;
unsigned int frozen : 1;
unsigned int blocking : 1;
+ unsigned int detached : 1;
struct h2_task_input *input;
struct h2_task_output *output;
apr_status_t h2_task_freeze(h2_task *task, request_rec *r);
apr_status_t h2_task_thaw(h2_task *task);
+int h2_task_is_detached(h2_task *task);
void h2_task_set_io_blocking(h2_task *task, int blocking);
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)
+int 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);
+ return apr_hash_do(ihash_iter, &ictx, ih->hash);
}
void h2_ihash_add(h2_ihash_t *ih, void *val)
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);
+ return apr_snprintf(buffer, maxlen, "GOAWAY[error=%d, reason='%s', "
+ "last_stream=%d]", frame->goaway.error_code,
+ scratch, frame->goaway.last_stream_id);
}
case NGHTTP2_WINDOW_UPDATE: {
return apr_snprintf(buffer, maxlen,
* @param ih the hash to iterate over
* @param fn the function to invoke on each member
* @param ctx user supplied data passed into each iteration call
+ * @param 0 if one iteration returned 0, otherwise != 0
*/
-void h2_ihash_iter(h2_ihash_t *ih, h2_ihash_iter_t *fn, void *ctx);
+int h2_ihash_iter(h2_ihash_t *ih, h2_ihash_iter_t *fn, void *ctx);
void h2_ihash_add(h2_ihash_t *ih, void *val);
void h2_ihash_remove(h2_ihash_t *ih, int id);
return status;
}
-static void request_done(h2_proxy_session *session, request_rec *r)
+static void request_done(h2_proxy_session *session, request_rec *r,
+ int complete, int touched)
{
h2_proxy_ctx *ctx = session->user_data;
+ const char *task_id = apr_table_get(r->connection->notes, H2_TASK_ID_NOTE);
- if (r == ctx->rbase) {
+ if (!complete && !touched) {
+ /* untouched request, need rescheduling */
+ if (req_engine_push && is_h2 && is_h2(ctx->owner)) {
+ if (req_engine_push(ctx->engine_type, r, NULL) == APR_SUCCESS) {
+ /* push to engine */
+ ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, r->connection,
+ "h2_proxy_session(%s): rescheduled request %s",
+ ctx->engine_id, task_id);
+ return;
+ }
+ }
+ }
+
+ if (r == ctx->rbase && complete) {
ctx->r_status = APR_SUCCESS;
}
- if (req_engine_done && ctx->engine) {
- ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, r->connection,
- "h2_proxy_session(%s): request %s",
- ctx->engine_id, r->the_request);
- req_engine_done(ctx->engine, r->connection);
+ if (complete) {
+ if (req_engine_done && ctx->engine) {
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, r->connection,
+ "h2_proxy_session(%s): finished request %s",
+ ctx->engine_id, task_id);
+ req_engine_done(ctx->engine, r->connection);
+ }
}
-}
+ else {
+ if (req_engine_done && ctx->engine) {
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, r->connection,
+ "h2_proxy_session(%s): failed request %s",
+ ctx->engine_id, task_id);
+ req_engine_done(ctx->engine, r->connection);
+ }
+ }
+}
static apr_status_t next_request(h2_proxy_ctx *ctx, int before_leave)
{
apr_pool_t *p = c->pool;
apr_uri_t *uri = apr_palloc(p, sizeof(*uri));
h2_proxy_ctx *ctx;
-
+ int reconnected = 0;
+
/* find the scheme */
if ((url[0] != 'h' && url[0] != 'H') || url[1] != '2') {
return DECLINED;
}
cleanup:
- if (ctx->engine && next_request(ctx, 1) == APR_SUCCESS) {
+ if (!reconnected && ctx->engine && next_request(ctx, 1) == APR_SUCCESS) {
/* Still more to do, tear down old conn and start over */
if (ctx->p_conn) {
ctx->p_conn->close = 1;
ap_proxy_release_connection(ctx->proxy_func, ctx->p_conn, ctx->server);
ctx->p_conn = NULL;
}
+ reconnected = 1; /* we do this only once, then fail */
goto run_connect;
}