static struct h2_workers *workers;
-static apr_status_t h2_session_process(h2_session *session);
+static apr_status_t h2_conn_loop(h2_session *session);
static h2_mpm_type_t mpm_type = H2_MPM_UNKNOWN;
static module *mpm_module;
return APR_EGENERAL;
}
- return h2_session_process(session);
+ return h2_conn_loop(session);
}
apr_status_t h2_conn_main(conn_rec *c)
return APR_EGENERAL;
}
- status = h2_session_process(session);
+ status = h2_conn_loop(session);
/* Make sure this connection gets closed properly. */
c->keepalive = AP_CONN_CLOSE;
return status;
}
-apr_status_t h2_session_process(h2_session *session)
+static apr_status_t h2_conn_loop(h2_session *session)
{
apr_status_t status = APR_SUCCESS;
int rv = 0;
* submit our settings and need the ACK.
*/
got_streams = !h2_stream_set_is_empty(session->streams);
- status = h2_session_read(session,
- (!got_streams
- || session->frames_received <= 1)?
- APR_BLOCK_READ : APR_NONBLOCK_READ);
+ if (!got_streams || session->frames_received <= 1) {
+ if (session->c->cs) {
+ session->c->cs->state = CONN_STATE_WRITE_COMPLETION;
+ }
+ status = h2_session_read(session, APR_BLOCK_READ);
+ }
+ else {
+ if (session->c->cs) {
+ session->c->cs->state = CONN_STATE_HANDLER;
+ }
+ status = h2_session_read(session, APR_NONBLOCK_READ);
+ }
+
switch (status) {
case APR_SUCCESS: /* successful read, reset our idle timers */
have_read = 1;
break;
case APR_EAGAIN: /* non-blocking read, nothing there */
break;
- case APR_EBADF: /* connection is not there any more */
- case APR_EOF:
- case APR_ECONNABORTED:
- case APR_ECONNRESET:
- case APR_TIMEUP: /* blocked read, timed out */
- ap_log_cerror( APLOG_MARK, APLOG_DEBUG, status, session->c,
- "h2_session(%ld): reading",
- session->id);
- h2_session_abort(session, status, 0);
- break;
default:
- ap_log_cerror( APLOG_MARK, APLOG_INFO, status, session->c,
- APLOGNO(02950)
- "h2_session(%ld): error reading, terminating",
- session->id);
+ if (APR_STATUS_IS_ETIMEDOUT(status)
+ || APR_STATUS_IS_ECONNABORTED(status)
+ || APR_STATUS_IS_ECONNRESET(status)
+ || APR_STATUS_IS_EOF(status)
+ || APR_STATUS_IS_EBADF(status)) {
+ /* common status for a client that has left */
+ ap_log_cerror( APLOG_MARK, APLOG_DEBUG, status, session->c,
+ "h2_session(%ld): terminating",
+ session->id);
+ /* Stolen from mod_reqtimeout to speed up lingering when
+ * a read timeout happened.
+ */
+ apr_table_setn(session->c->notes, "short-lingering-close", "1");
+ }
+ else {
+ /* uncommon status, log on INFO so that we see this */
+ ap_log_cerror( APLOG_MARK, APLOG_INFO, status, session->c,
+ APLOGNO(02950)
+ "h2_session(%ld): error reading, terminating",
+ session->id);
+ }
h2_session_abort(session, status, 0);
break;
}
{
AP_DEBUG_ASSERT(c);
- c->clogging_input_filters = 1;
ap_process_connection(c, socket);
return APR_SUCCESS;
return 0;
}
+
/*******************************************************************************
* Register various hooks
*/
ap_hook_post_read_request(h2_h2_post_read_req, NULL, NULL, APR_HOOK_REALLY_FIRST);
}
-int h2_h2_remove_timeout(conn_rec* c)
+static int h2_h2_remove_timeout(conn_rec* c)
{
h2_ctx *ctx = h2_ctx_get(c);
apr_bucket *last;
apr_status_t status;
+ if (io->rst_error) {
+ return APR_ECONNABORTED;
+ }
+
if (!io->bbin || APR_BRIGADE_EMPTY(io->bbin)) {
return io->eos_in? APR_EOF : APR_EAGAIN;
}
apr_status_t h2_io_in_write(h2_io *io, apr_bucket_brigade *bb)
{
+ if (io->rst_error) {
+ return APR_ECONNABORTED;
+ }
+
if (io->eos_in) {
return APR_EOF;
}
apr_status_t h2_io_in_close(h2_io *io)
{
+ if (io->rst_error) {
+ return APR_ECONNABORTED;
+ }
+
if (io->bbin) {
APR_BRIGADE_INSERT_TAIL(io->bbin,
apr_bucket_eos_create(io->bbin->bucket_alloc));
{
apr_status_t status;
+ if (io->rst_error) {
+ return APR_ECONNABORTED;
+ }
+
if (io->eos_out) {
*plen = 0;
*peos = 1;
apr_status_t status;
int start_allowed;
+ if (io->rst_error) {
+ return APR_ECONNABORTED;
+ }
+
if (io->eos_out) {
apr_off_t len;
/* We have already delivered an EOS bucket to a reader, no
*/
start_allowed = *pfile_handles_allowed;
- if (io->rst_error) {
- return APR_ECONNABORTED;
- }
status = h2_util_move(io->bbout, bb, maxlen, pfile_handles_allowed,
"h2_io_out_write");
/* track # file buckets moved into our pool */
apr_status_t h2_io_out_close(h2_io *io)
{
+ if (io->rst_error) {
+ return APR_ECONNABORTED;
+ }
if (!io->eos_out && !h2_util_has_eos(io->bbout, 0)) {
APR_BRIGADE_INSERT_TAIL(io->bbout,
apr_bucket_eos_create(io->bbout->bucket_alloc));
#include "h2_private.h"
#include "h2_config.h"
#include "h2_conn.h"
+#include "h2_h2.h"
#include "h2_io.h"
#include "h2_io_set.h"
#include "h2_response.h"
status = apr_thread_mutex_lock(m->lock);
if (APR_SUCCESS == status) {
h2_io *io = h2_io_set_get(m->stream_ios, stream->id);
- if (!io || io->task_done) {
- /* No more io or task already done -> cleanup immediately */
- stream_destroy(m, stream, io);
- }
- else {
+ if (io) {
+ /* Remove io from ready set, we will never submit it */
+ h2_io_set_remove(m->ready_ios, io);
if (stream->rst_error) {
/* Forward error code to fail any further attempt to
* write to io */
h2_io_rst(io, stream->rst_error);
}
- /* Remove io from ready set (if there), since we will never submit it */
- h2_io_set_remove(m->ready_ios, io);
+ }
+
+ if (!io || io->task_done) {
+ /* No more io or task already done -> cleanup immediately */
+ stream_destroy(m, stream, io);
+ }
+ else {
/* Add stream to closed set for cleanup when task is done */
h2_stream_set_add(m->closed, stream);
}
h2_stream_set_response(stream, io->response, io->bbout);
}
- if (io->output_drained) {
- apr_thread_cond_signal(io->output_drained);
- }
}
else {
- ap_log_cerror(APLOG_MARK, APLOG_WARNING, 0, m->c, APLOGNO(02953)
- "h2_mplx(%ld): stream for response %d not found",
- m->id, io->id);
/* We have the io ready, but the stream has gone away, maybe
* reset by the client. Should no longer happen since such
* streams should clear io's from the ready queue.
*/
+ ap_log_cerror(APLOG_MARK, APLOG_INFO, 0, m->c, APLOGNO(02953)
+ "h2_mplx(%ld): stream for response %d closed, "
+ "resetting io to close request processing",
+ m->id, io->id);
+ h2_io_rst(io, NGHTTP2_ERR_STREAM_CLOSED);
+ }
+
+ if (io->output_drained) {
+ apr_thread_cond_signal(io->output_drained);
}
}
apr_thread_mutex_unlock(m->lock);
session->id, stream_id, (int)len);
if (status != APR_SUCCESS) {
rv = nghttp2_submit_rst_stream(ngh2, NGHTTP2_FLAG_NONE, stream_id,
- NGHTTP2_INTERNAL_ERROR);
+ H2_STREAM_RST(stream, NGHTTP2_INTERNAL_ERROR));
if (nghttp2_is_fatal(rv)) {
return NGHTTP2_ERR_CALLBACK_FAILURE;
}
}
/* If we have responses ready, submit them now. */
- while ((stream = h2_mplx_next_submit(session->mplx,
- session->streams)) != NULL) {
+ while (!session->aborted
+ && (stream = h2_mplx_next_submit(session->mplx, session->streams)) != NULL) {
status = h2_session_handle_response(session, stream);
flush_output = 1;
}
- if (h2_session_resume_streams_with_data(session) > 0) {
+ if (!session->aborted && h2_session_resume_streams_with_data(session) > 0) {
flush_output = 1;
}
- if (!flush_output && timeout > 0 && !h2_session_want_write(session)) {
+ if (!session->aborted && !flush_output
+ && timeout > 0 && !h2_session_want_write(session)) {
status = h2_mplx_out_trywait(session->mplx, timeout, session->iowait);
if (status != APR_TIMEUP
if (stream->response && stream->response->ngheader) {
rv = submit_response(session, stream->response);
}
- else if (stream->rst_error) {
- rv = nghttp2_submit_rst_stream(session->ngh2, NGHTTP2_FLAG_NONE,
- stream->id, stream->rst_error);
- }
else {
rv = nghttp2_submit_rst_stream(session->ngh2, NGHTTP2_FLAG_NONE,
- stream->id, NGHTTP2_PROTOCOL_ERROR);
+ stream->id,
+ H2_STREAM_RST(stream, NGHTTP2_PROTOCOL_ERROR));
}
if (nghttp2_is_fatal(rv)) {
&& !nghttp2_session_want_write(session->ngh2)));
}
-static int log_stream(void *ctx, h2_stream *stream)
-{
- h2_session *session = (h2_session *)ctx;
- AP_DEBUG_ASSERT(session);
- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
- "h2_stream(%ld-%d): in set, suspended=%d, aborted=%d, "
- "has_data=%d",
- session->id, stream->id, stream->suspended, stream->aborted,
- h2_mplx_out_has_data_for(session->mplx, stream->id));
- return 1;
-}
-
-void h2_session_log_stats(h2_session *session)
-{
- AP_DEBUG_ASSERT(session);
- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, 0, session->c,
- "h2_session(%ld): %d open streams",
- session->id, (int)h2_stream_set_size(session->streams));
- h2_stream_set_iter(session->streams, log_stream, session);
-}
-
static int frame_print(const nghttp2_frame *frame, char *buffer, size_t maxlen)
{
char scratch[128];
/* Get the h2_stream for the given stream idenrtifier. */
struct h2_stream *h2_session_get_stream(h2_session *session, int stream_id);
-void h2_session_log_stats(h2_session *session);
-
#endif /* defined(__mod_h2__h2_session__) */
};
+#define H2_STREAM_RST(s, def) (s->rst_error? s->rst_error : (def))
+
h2_stream *h2_stream_create(int id, apr_pool_t *pool, struct h2_mplx *m);
apr_status_t h2_stream_destroy(h2_stream *stream);
* @macro
* Version number of the h2 module as c string
*/
-#define MOD_HTTP2_VERSION "1.0.1-DEV"
+#define MOD_HTTP2_VERSION "1.0.2-DEV"
/**
* @macro
* 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 0x010001
+#define MOD_HTTP2_VERSION_NUM 0x010002
#endif /* mod_h2_h2_version_h */