From: Jim Jagielski
The improved connection handling does not yet work for certain
- connection filters, in particular SSL. For SSL connections, this MPM will
- fall back to the behaviour of the
The improved connection handling may not work for certain connection
+ filters that have declared themselves as incompatible with event. In these
+ cases, this MPM will fall back to the behaviour of the
+
A similar restriction is currently present for requests involving an + output filter that needs to read and/or modify the whole response body, + like for example mod_ssl, mod_deflate, or mod_include. If the + connection to the client blocks while the filter is processing the + data, and the amount of data produced by the filter is too big to be + buffered in memory, the thread used for the request is not freed while + httpd waits until the pending data is sent to the client.
The MPM assumes that the underlying The event MPM handles some connections in an asynchronous way, where
request worker threads are only allocated for short periods of time as
- needed, and other (mostly SSL) connections with one request worker thread
- reserved per connection. This can lead to situations where all workers are
- tied up and no worker thread is available to handle new work on established
- async connections.apr_pollset
implementation is reasonably threadsafe. This enables the MPM to
@@ -152,10 +161,10 @@ of consuming threads only for connections with active processing
To mitigate this problem, the event MPM does two things: Firstly, it limits the number of connections accepted per process, depending on the diff --git a/include/ap_mmn.h b/include/ap_mmn.h index 82a0acb00d..4f5ed0677e 100644 --- a/include/ap_mmn.h +++ b/include/ap_mmn.h @@ -419,6 +419,7 @@ * 20120211.22 (2.4.5-dev) No longer prevent usage of strtoul() * 20120211.23 (2.4.5-dev) Add ap_proxy_clear_connection() * 20120211.24 (2.4.7-dev) add open_htaccess hook. + * 20120211.25 (2.4.7-dev) Add conn_sense_e */ #define MODULE_MAGIC_COOKIE 0x41503234UL /* "AP24" */ @@ -426,7 +427,7 @@ #ifndef MODULE_MAGIC_NUMBER_MAJOR #define MODULE_MAGIC_NUMBER_MAJOR 20120211 #endif -#define MODULE_MAGIC_NUMBER_MINOR 24 /* 0...n */ +#define MODULE_MAGIC_NUMBER_MINOR 25 /* 0...n */ /** * Determine if the server's current MODULE_MAGIC_NUMBER is at least a diff --git a/include/httpd.h b/include/httpd.h index 36cd58d542..c84e24468a 100644 --- a/include/httpd.h +++ b/include/httpd.h @@ -1175,12 +1175,20 @@ typedef enum { CONN_STATE_LINGER_SHORT /* MPM has started lingering close with short timeout */ } conn_state_e; +typedef enum { + CONN_SENSE_DEFAULT, + CONN_SENSE_WANT_READ, /* next event must be read */ + CONN_SENSE_WANT_WRITE /* next event must be write */ +} conn_sense_e; + /** * @brief A structure to contain connection state information */ struct conn_state_t { /** Current state of the connection */ conn_state_e state; + /** Whether to read instead of write, or write instead of read */ + conn_sense_e sense; }; /* Per-vhost config... */ diff --git a/modules/ssl/ssl_engine_io.c b/modules/ssl/ssl_engine_io.c index d135bd3815..cca15b4c7c 100644 --- a/modules/ssl/ssl_engine_io.c +++ b/modules/ssl/ssl_engine_io.c @@ -775,6 +775,18 @@ static apr_status_t ssl_filter_write(ap_filter_t *f, */ outctx->rc = APR_EAGAIN; } + else if (ssl_err == SSL_ERROR_WANT_READ) { + /* + * If OpenSSL wants to read during write, and we were + * nonblocking, set the sense explicitly to read and + * report as an EAGAIN. + * + * (This is usually the case when the client forces an SSL + * renegotiation which is handled implicitly by OpenSSL.) + */ + outctx->c->cs->sense = CONN_SENSE_WANT_READ; + outctx->rc = APR_EAGAIN; + } else if (ssl_err == SSL_ERROR_SYSCALL) { ap_log_cerror(APLOG_MARK, APLOG_INFO, outctx->rc, c, APLOGNO(01993) "SSL output filter write failed."); @@ -1902,8 +1914,10 @@ void ssl_io_filter_init(conn_rec *c, request_rec *r, SSL *ssl) filter_ctx->pbioWrite = BIO_new(&bio_filter_out_method); filter_ctx->pbioWrite->ptr = (void *)bio_filter_out_ctx_new(filter_ctx, c); - /* We insert a clogging input filter. Let the core know. */ - c->clogging_input_filters = 1; + /* write is non blocking for the benefit of async mpm */ + if (c->cs) { + BIO_set_nbio(filter_ctx->pbioWrite, 1); + } ssl_io_input_add_filter(filter_ctx, c, r, ssl); diff --git a/server/mpm/event/event.c b/server/mpm/event/event.c index c64b08f5f4..f12468f51a 100644 --- a/server/mpm/event/event.c +++ b/server/mpm/event/event.c @@ -790,7 +790,10 @@ static int start_lingering_close_common(event_conn_state_t *cs) apr_atomic_inc32(&lingering_count); apr_thread_mutex_lock(timeout_mutex); TO_QUEUE_APPEND(*q, cs); - cs->pfd.reqevents = APR_POLLIN | APR_POLLHUP | APR_POLLERR; + cs->pfd.reqevents = ( + cs->pub.sense == CONN_SENSE_WANT_WRITE ? APR_POLLOUT : + APR_POLLIN) | APR_POLLHUP | APR_POLLERR; + cs->pub.sense = CONN_SENSE_DEFAULT; rv = apr_pollset_add(event_pollset, &cs->pfd); apr_thread_mutex_unlock(timeout_mutex); if (rv != APR_SUCCESS && !APR_STATUS_IS_EEXIST(rv)) { @@ -938,6 +941,7 @@ static void process_socket(apr_thread_t *thd, apr_pool_t * p, apr_socket_t * soc */ cs->pub.state = CONN_STATE_READ_REQUEST_LINE; + cs->pub.sense = CONN_SENSE_DEFAULT; } else { c = cs->c; @@ -946,9 +950,11 @@ static void process_socket(apr_thread_t *thd, apr_pool_t * p, apr_socket_t * soc } if (c->clogging_input_filters && !c->aborted) { - /* Since we have an input filter which 'cloggs' the input stream, - * like mod_ssl, lets just do the normal read from input filters, - * like the Worker MPM does. + /* Since we have an input filter which 'clogs' the input stream, + * like mod_ssl used to, lets just do the normal read from input + * filters, like the Worker MPM does. Filters that need to write + * where they would otherwise read, or read where they would + * otherwise write, should set the sense appropriately. */ apr_atomic_inc32(&clogged_count); ap_run_process_connection(c); @@ -994,7 +1000,10 @@ read_request: cs->expiration_time = ap_server_conf->timeout + apr_time_now(); apr_thread_mutex_lock(timeout_mutex); TO_QUEUE_APPEND(write_completion_q, cs); - cs->pfd.reqevents = APR_POLLOUT | APR_POLLHUP | APR_POLLERR; + cs->pfd.reqevents = ( + cs->pub.sense == CONN_SENSE_WANT_READ ? APR_POLLIN : + APR_POLLOUT) | APR_POLLHUP | APR_POLLERR; + cs->pub.sense = CONN_SENSE_DEFAULT; rc = apr_pollset_add(event_pollset, &cs->pfd); apr_thread_mutex_unlock(timeout_mutex); return;