}
}
+static apr_status_t h2_session_shutdown(h2_session *session, int reason)
+{
+ apr_status_t status = APR_SUCCESS;
+
+ AP_DEBUG_ASSERT(session);
+ session->aborted = 1;
+ if (session->state != H2_SESSION_ST_CLOSING
+ && session->state != H2_SESSION_ST_ABORTED) {
+ h2_mplx_abort(session->mplx);
+ if (session->server_goaway) {
+ /* already sent one */
+ }
+ else if (!reason) {
+ nghttp2_submit_goaway(session->ngh2, NGHTTP2_FLAG_NONE,
+ h2_mplx_get_max_stream_started(session->mplx),
+ reason, NULL, 0);
+ status = nghttp2_session_send(session->ngh2);
+ session->server_goaway = 1;
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
+ "session(%ld): shutdown, no err", session->id);
+ }
+ else {
+ const char *err = nghttp2_strerror(reason);
+ nghttp2_submit_goaway(session->ngh2, NGHTTP2_FLAG_NONE,
+ h2_mplx_get_max_stream_started(session->mplx),
+ reason, (const uint8_t *)err,
+ strlen(err));
+ status = nghttp2_session_send(session->ngh2);
+ session->server_goaway = 1;
+ ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
+ "session(%ld): shutdown, err=%d '%s'",
+ session->id, reason, err);
+ }
+
+ h2_conn_io_flush(&session->io);
+ session->state = H2_SESSION_ST_CLOSING;
+ }
+ return status;
+}
+
static apr_status_t session_pool_cleanup(void *data)
{
h2_session *session = data;
*/
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
"session(%ld): pool_cleanup", session->id);
+
+ AP_DEBUG_ASSERT(session->aborted || session->server_goaway);
+ if (!session->aborted && !session->server_goaway) {
+ /* Not good. The connection is being torn down and we have
+ * not sent a goaway. This is considered a protocol error and
+ * the client has to assume that any streams "in flight" may have
+ * been processed and are not safe to retry.
+ * As clients with idle connection may only learn about a closed
+ * connection when sending the next request, this has the effect
+ * that at least this one request will fail.
+ */
+ ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, session->c,
+ "session(%ld): connection disappeared without proper "
+ "goodbye, clients will be confused, should not happen",
+ session->id);
+ }
/* keep us from destroying the pool, since that is already ongoing. */
session->pool = NULL;
h2_session_destroy(session);
h2_session_destroy(session);
}
-static apr_status_t h2_session_shutdown(h2_session *session, int reason)
-{
- apr_status_t status = APR_SUCCESS;
-
- AP_DEBUG_ASSERT(session);
- session->aborted = 1;
- if (session->state != H2_SESSION_ST_CLOSING
- && session->state != H2_SESSION_ST_ABORTED) {
- h2_mplx_abort(session->mplx);
- if (session->server_goaway) {
- /* already sent one */
- }
- else if (!reason) {
- nghttp2_submit_goaway(session->ngh2, NGHTTP2_FLAG_NONE,
- h2_mplx_get_max_stream_started(session->mplx),
- reason, NULL, 0);
- status = nghttp2_session_send(session->ngh2);
- session->server_goaway = 1;
- ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
- "session(%ld): shutdown, no err", session->id);
- }
- else {
- const char *err = nghttp2_strerror(reason);
- nghttp2_submit_goaway(session->ngh2, NGHTTP2_FLAG_NONE,
- h2_mplx_get_max_stream_started(session->mplx),
- reason, (const uint8_t *)err,
- strlen(err));
- status = nghttp2_session_send(session->ngh2);
- session->server_goaway = 1;
- ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, session->c,
- "session(%ld): shutdown, err=%d '%s'",
- session->id, reason, err);
- }
- session->state = H2_SESSION_ST_CLOSING;
- }
- return status;
-}
-
void h2_session_abort(h2_session *session, apr_status_t status)
{
AP_DEBUG_ASSERT(session);
{
apr_status_t status = APR_SUCCESS;
conn_rec *c = session->c;
- int rv, have_written, have_read, remain_secs;
+ int rv, have_written, have_read;
const char *reason = "";
ap_log_cerror( APLOG_MARK, APLOG_TRACE1, status, c,
session->server_goaway = 1;
}
+ ap_update_child_status(c->sbh, SERVER_BUSY_READ, NULL);
status = h2_session_start(session, &rv);
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, status, c,
"h2_session(%ld): started on %s:%d", session->id,
break;
case H2_SESSION_ST_IDLE_READ:
- h2_filter_cin_timeout_set(session->cin, session->timeout_secs);
- ap_update_child_status(c->sbh, SERVER_BUSY_READ, NULL);
+ h2_filter_cin_timeout_set(session->cin, session->keepalive_secs);
+ ap_update_child_status(c->sbh, SERVER_BUSY_KEEPALIVE, NULL);
status = h2_session_read(session, 1, 10);
if (APR_STATUS_IS_TIMEUP(status)) {
ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, c,
"h2_session(%ld): IDLE -> KEEPALIVE", session->id);
- session->state = H2_SESSION_ST_KEEPALIVE;
+ h2_session_shutdown(session, 0);
+ goto out;
}
else if (status == APR_SUCCESS) {
/* got something, go busy again */
case H2_SESSION_ST_BUSY:
if (nghttp2_session_want_read(session->ngh2)) {
+ ap_update_child_status(c->sbh, SERVER_BUSY_READ, NULL);
h2_filter_cin_timeout_set(session->cin, session->timeout_secs);
status = h2_session_read(session, 0, 10);
if (status == APR_SUCCESS) {
}
else if (status == APR_TIMEUP) {
if (nghttp2_session_want_read(session->ngh2)) {
+ ap_update_child_status(c->sbh, SERVER_BUSY_READ, NULL);
status = h2_session_read(session, 0, 1);
if (status == APR_SUCCESS) {
/* got something, go busy again */
}
break;
- case H2_SESSION_ST_KEEPALIVE:
- /* Our normal H2Timeout has passed and we are considering to
- * extend that with the H2KeepAliveTimeout. */
- remain_secs = session->keepalive_secs - session->timeout_secs;
- if (remain_secs <= 0) {
- /* keepalive is <= normal timeout, close the session */
- reason = "keepalive expired";
- h2_session_shutdown(session, 0);
- goto out;
- }
- session->c->keepalive = AP_CONN_KEEPALIVE;
- ap_update_child_status_from_conn(c->sbh, SERVER_BUSY_KEEPALIVE, c);
-
- if ((apr_time_sec(session->s->keep_alive_timeout) >= remain_secs)
- && async && session->c->cs
- && !session->r) {
- /* Async MPMs are able to handle keep-alive connections without
- * blocking a thread. For this to happen, we need to return from
- * processing, indicating the IO event we are waiting for, and
- * may be called again if the event happens.
- * TODO: this does not properly GOAWAY connections...
- * TODO: This currently does not work on upgraded requests...
- */
- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, c,
- "h2_session(%ld): async KEEPALIVE -> IDLE_READ", session->id);
- session->state = H2_SESSION_ST_IDLE_READ;
- session->c->cs->state = CONN_STATE_WRITE_COMPLETION;
- reason = "async keepalive";
- status = APR_SUCCESS;
- goto out;
- }
- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, c,
- "h2_session(%ld): KEEPALIVE read", session->id);
- h2_filter_cin_timeout_set(session->cin, remain_secs);
- status = h2_session_read(session, 1, 1);
- if (APR_STATUS_IS_TIMEUP(status)) {
- reason = "keepalive expired";
- h2_session_shutdown(session, 0);
- goto out;
- }
- else if (status != APR_SUCCESS) {
- reason = "keepalive error";
- goto out;
- }
- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, status, c,
- "h2_session(%ld): KEEPALIVE -> BUSY", session->id);
- session->state = H2_SESSION_ST_BUSY;
- break;
-
case H2_SESSION_ST_CLOSING:
if (nghttp2_session_want_write(session->ngh2)) {
+ ap_update_child_status(c->sbh, SERVER_BUSY_READ, NULL);
status = h2_session_send(session);
if (status != APR_SUCCESS) {
reason = "send error";