mirror of
https://github.com/apache/httpd.git
synced 2025-07-25 17:01:22 +00:00
*) core/mpm_event/mod_ssl: make SSL handshakes non-blocking.
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/trunk-ssl-handshake-nonblocking@1897762 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
6
CHANGES
6
CHANGES
@ -1,6 +1,12 @@
|
||||
-*- coding: utf-8 -*-
|
||||
Changes with Apache 2.5.1
|
||||
|
||||
*) event: Add support for non blocking behaviour in the
|
||||
CONN_STATE_READ_REQUEST_LINE phase, in addition to the existing
|
||||
CONN_STATE_WRITE_COMPLETION phase. Add AP_MPM_CAN_AGAIN and AGAIN to
|
||||
signal to the MPM that non blocking behaviour is requested. Update
|
||||
mod_ssl to perform non blocking TLS handshakes. [Graham Leggett]
|
||||
|
||||
*) http: Enforce that fully qualified uri-paths not to be forward-proxied
|
||||
have an http(s) scheme, and that the ones to be forward proxied have a
|
||||
hostname, per HTTP specifications. [Ruediger Pluem, Yann Ylavic]
|
||||
|
5
changes-entries/ab-ssl-sense-fix.txt
Normal file
5
changes-entries/ab-ssl-sense-fix.txt
Normal file
@ -0,0 +1,5 @@
|
||||
*) ab: Respond appropriately to SSL_ERROR_WANT_READ and SSL_ERROR_WANT_WRITE.
|
||||
Previously the correct event was polled for, but the response to the poll
|
||||
would call write instead of read, and read instead of write. PR 55952
|
||||
[Graham Leggett]
|
||||
|
@ -698,6 +698,8 @@
|
||||
* 20210926.2 (2.5.1-dev) Add ap_post_read_request()
|
||||
* 20211221.0 (2.5.1-dev) Bump PROXY_WORKER_MAX_NAME_SIZE from 256 to 384,
|
||||
* add PROXY_WORKER_UDS_PATH_SIZE.
|
||||
* 20211221.1 (2.5.1-dev) Add read_line to scoreboard.
|
||||
* 20211221.2 (2.5.1-dev) Add AGAIN, AP_MPMQ_CAN_AGAIN.
|
||||
* 20211221.3 (2.5.1-dev) Add ap_thread_create(), ap_thread_main_create()
|
||||
* and ap_thread_current()
|
||||
*
|
||||
|
@ -182,6 +182,8 @@ AP_DECLARE(apr_status_t) ap_os_create_privileged_process(
|
||||
#define AP_MPMQ_CAN_SUSPEND 17
|
||||
/** MPM supports additional pollfds */
|
||||
#define AP_MPMQ_CAN_POLL 18
|
||||
/** MPM reacts to AGAIN response */
|
||||
#define AP_MPMQ_CAN_AGAIN 19
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
|
@ -465,6 +465,9 @@ AP_DECLARE(const char *) ap_get_server_built(void);
|
||||
*/
|
||||
#define SUSPENDED -3 /**< Module will handle the remainder of the request.
|
||||
* The core will never invoke the request again, */
|
||||
#define AGAIN -4 /**< Module wants to be called again when
|
||||
* more data is availble.
|
||||
*/
|
||||
|
||||
/** Returned by the bottom-most filter if no data was written.
|
||||
* @see ap_pass_brigade(). */
|
||||
|
@ -148,6 +148,7 @@ struct process_score {
|
||||
apr_uint32_t lingering_close; /* async connections in lingering close */
|
||||
apr_uint32_t keep_alive; /* async connections in keep alive */
|
||||
apr_uint32_t suspended; /* connections suspended by some module */
|
||||
apr_uint32_t read_line; /* async connections doing read line */
|
||||
};
|
||||
|
||||
/* Scoreboard is now in 'local' memory, since it isn't updated once created,
|
||||
|
@ -557,7 +557,7 @@ static int status_handler(request_rec *r)
|
||||
ap_rputs("</dl>", r);
|
||||
|
||||
if (is_async) {
|
||||
int write_completion = 0, lingering_close = 0, keep_alive = 0,
|
||||
int read_line = 0, write_completion = 0, lingering_close = 0, keep_alive = 0,
|
||||
connections = 0, stopping = 0, procs = 0;
|
||||
/*
|
||||
* These differ from 'busy' and 'ready' in how gracefully finishing
|
||||
@ -574,11 +574,12 @@ static int status_handler(request_rec *r)
|
||||
"<th colspan=\"3\">Async connections</th></tr>\n"
|
||||
"<tr><th>total</th><th>accepting</th>"
|
||||
"<th>busy</th><th>idle</th>"
|
||||
"<th>writing</th><th>keep-alive</th><th>closing</th></tr>\n", r);
|
||||
"<th>reading</th><th>writing</th><th>keep-alive</th><th>closing</th></tr>\n", r);
|
||||
for (i = 0; i < server_limit; ++i) {
|
||||
ps_record = ap_get_scoreboard_process(i);
|
||||
if (ps_record->pid) {
|
||||
connections += ps_record->connections;
|
||||
read_line += ps_record->read_line;
|
||||
write_completion += ps_record->write_completion;
|
||||
keep_alive += ps_record->keep_alive;
|
||||
lingering_close += ps_record->lingering_close;
|
||||
@ -600,7 +601,7 @@ static int status_handler(request_rec *r)
|
||||
"<td>%s%s</td>"
|
||||
"<td>%u</td><td>%s</td>"
|
||||
"<td>%u</td><td>%u</td>"
|
||||
"<td>%u</td><td>%u</td><td>%u</td>"
|
||||
"<td>%u</td><td>%u</td><td>%u</td><td>%u</td>"
|
||||
"</tr>\n",
|
||||
i, ps_record->pid,
|
||||
dying, old,
|
||||
@ -608,6 +609,7 @@ static int status_handler(request_rec *r)
|
||||
ps_record->not_accepting ? "no" : "yes",
|
||||
thread_busy_buffer[i],
|
||||
thread_idle_buffer[i],
|
||||
ps_record->read_line,
|
||||
ps_record->write_completion,
|
||||
ps_record->keep_alive,
|
||||
ps_record->lingering_close);
|
||||
@ -619,12 +621,12 @@ static int status_handler(request_rec *r)
|
||||
"<td>%d</td><td>%d</td>"
|
||||
"<td>%d</td><td> </td>"
|
||||
"<td>%d</td><td>%d</td>"
|
||||
"<td>%d</td><td>%d</td><td>%d</td>"
|
||||
"<td>%d</td><td>%d</td><td>%d</td><td>%d</td>"
|
||||
"</tr>\n</table>\n",
|
||||
procs, stopping,
|
||||
connections,
|
||||
busy_workers, idle_workers,
|
||||
write_completion, keep_alive, lingering_close);
|
||||
read_line, write_completion, keep_alive, lingering_close);
|
||||
}
|
||||
else {
|
||||
ap_rprintf(r, "Processes: %d\n"
|
||||
@ -632,13 +634,14 @@ static int status_handler(request_rec *r)
|
||||
"BusyWorkers: %d\n"
|
||||
"IdleWorkers: %d\n"
|
||||
"ConnsTotal: %d\n"
|
||||
"ConnsAsyncReading: %d\n"
|
||||
"ConnsAsyncWriting: %d\n"
|
||||
"ConnsAsyncKeepAlive: %d\n"
|
||||
"ConnsAsyncClosing: %d\n",
|
||||
procs, stopping,
|
||||
busy_workers, idle_workers,
|
||||
connections,
|
||||
write_completion, keep_alive, lingering_close);
|
||||
read_line, write_completion, keep_alive, lingering_close);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "util_md5.h"
|
||||
#include "util_mutex.h"
|
||||
#include "ap_provider.h"
|
||||
#include "ap_mpm.h"
|
||||
#include "http_config.h"
|
||||
|
||||
#include "mod_proxy.h" /* for proxy_hook_section_post_config() */
|
||||
@ -689,31 +690,80 @@ static int ssl_hook_process_connection(conn_rec* c)
|
||||
{
|
||||
SSLConnRec *sslconn = myConnConfig(c);
|
||||
|
||||
int status = DECLINED;
|
||||
|
||||
if (sslconn && !sslconn->disabled) {
|
||||
/* On an active SSL connection, let the input filters initialize
|
||||
* themselves which triggers the handshake, which again triggers
|
||||
* all kinds of useful things such as SNI and ALPN.
|
||||
*/
|
||||
apr_bucket_brigade* temp;
|
||||
apr_status_t rv;
|
||||
|
||||
int again_mpm = 0;
|
||||
|
||||
temp = apr_brigade_create(c->pool, c->bucket_alloc);
|
||||
rv = ap_get_brigade(c->input_filters, temp,
|
||||
AP_MODE_INIT, APR_BLOCK_READ, 0);
|
||||
apr_brigade_destroy(temp);
|
||||
|
||||
if (APR_SUCCESS != APR_SUCCESS) {
|
||||
if (c->cs) {
|
||||
c->cs->state = CONN_STATE_LINGER;
|
||||
}
|
||||
ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(10373)
|
||||
"SSL handshake was not completed, "
|
||||
"closing connection");
|
||||
return OK;
|
||||
if (ap_mpm_query(AP_MPMQ_CAN_AGAIN, &again_mpm) != APR_SUCCESS) {
|
||||
again_mpm = 0;
|
||||
}
|
||||
|
||||
if (again_mpm) {
|
||||
|
||||
/* Take advantage of an async MPM. If we see an EAGAIN,
|
||||
* loop round and don't block.
|
||||
*/
|
||||
conn_state_t *cs = c->cs;
|
||||
|
||||
apr_status_t rv;
|
||||
|
||||
rv = ap_get_brigade(c->input_filters, temp,
|
||||
AP_MODE_INIT, APR_NONBLOCK_READ, 0);
|
||||
|
||||
if (rv == APR_SUCCESS) {
|
||||
/* great news, lets continue */
|
||||
|
||||
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(10370)
|
||||
"SSL handshake completed, continuing");
|
||||
|
||||
status = DECLINED;
|
||||
}
|
||||
else if (rv == APR_EAGAIN) {
|
||||
/* we've been asked to come around again, don't block */
|
||||
|
||||
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(10371)
|
||||
"SSL handshake in progress, continuing");
|
||||
|
||||
status = AGAIN;
|
||||
}
|
||||
else if (rv == AP_FILTER_ERROR) {
|
||||
/* handshake error, but mod_ssl handled it */
|
||||
|
||||
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(10372)
|
||||
"SSL handshake failed, returning error response");
|
||||
|
||||
status = DECLINED;
|
||||
}
|
||||
else {
|
||||
/* we failed, give up */
|
||||
|
||||
cs->state = CONN_STATE_LINGER;
|
||||
|
||||
ap_log_cerror(APLOG_MARK, APLOG_ERR, rv, c, APLOGNO(10373)
|
||||
"SSL handshake was not completed, "
|
||||
"closing connection");
|
||||
|
||||
status = OK;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ap_get_brigade(c->input_filters, temp,
|
||||
AP_MODE_INIT, APR_BLOCK_READ, 0);
|
||||
}
|
||||
|
||||
apr_brigade_destroy(temp);
|
||||
}
|
||||
|
||||
return DECLINED;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -323,6 +323,7 @@ typedef struct {
|
||||
} char_buffer_t;
|
||||
|
||||
typedef struct {
|
||||
conn_rec *c;
|
||||
SSL *ssl;
|
||||
BIO *bio_out;
|
||||
ap_filter_t *f;
|
||||
@ -795,6 +796,32 @@ static apr_status_t ssl_io_input_read(bio_filter_in_ctx_t *inctx,
|
||||
* (This is usually the case when the client forces an SSL
|
||||
* renegotiation which is handled implicitly by OpenSSL.)
|
||||
*/
|
||||
if (inctx->c->cs) {
|
||||
inctx->c->cs->sense = CONN_SENSE_WANT_READ;
|
||||
}
|
||||
inctx->rc = APR_EAGAIN;
|
||||
|
||||
if (*len > 0) {
|
||||
inctx->rc = APR_SUCCESS;
|
||||
break;
|
||||
}
|
||||
if (inctx->block == APR_NONBLOCK_READ) {
|
||||
break;
|
||||
}
|
||||
continue; /* Blocking and nothing yet? Try again. */
|
||||
}
|
||||
if (ssl_err == SSL_ERROR_WANT_WRITE) {
|
||||
/*
|
||||
* If OpenSSL wants to write during read, and we were
|
||||
* nonblocking, report as an EAGAIN. Otherwise loop,
|
||||
* pulling more data from network filter.
|
||||
*
|
||||
* (This is usually the case when the client forces an SSL
|
||||
* renegotiation which is handled implicitly by OpenSSL.)
|
||||
*/
|
||||
if (inctx->c->cs) {
|
||||
inctx->c->cs->sense = CONN_SENSE_WANT_WRITE;
|
||||
}
|
||||
inctx->rc = APR_EAGAIN;
|
||||
|
||||
if (*len > 0) {
|
||||
@ -960,7 +987,9 @@ static apr_status_t ssl_filter_write(ap_filter_t *f,
|
||||
* (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;
|
||||
if (outctx->c->cs) {
|
||||
outctx->c->cs->sense = CONN_SENSE_WANT_READ;
|
||||
}
|
||||
outctx->rc = APR_EAGAIN;
|
||||
ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, outctx->c,
|
||||
"Want read during nonblocking write");
|
||||
@ -1489,10 +1518,25 @@ static apr_status_t ssl_io_filter_handshake(ssl_filter_ctx_t *filter_ctx)
|
||||
}
|
||||
else if (ssl_err == SSL_ERROR_WANT_READ) {
|
||||
/*
|
||||
* This is in addition to what was present earlier. It is
|
||||
* borrowed from openssl_state_machine.c [mod_tls].
|
||||
* TBD.
|
||||
* Call us back when ready to read *\/
|
||||
*/
|
||||
ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, outctx->c,
|
||||
"Want read during nonblocking accept");
|
||||
if (outctx->c->cs) {
|
||||
outctx->c->cs->sense = CONN_SENSE_WANT_READ;
|
||||
}
|
||||
outctx->rc = APR_EAGAIN;
|
||||
return APR_EAGAIN;
|
||||
}
|
||||
else if (ssl_err == SSL_ERROR_WANT_WRITE) {
|
||||
/*
|
||||
* Call us back when ready to write *\/
|
||||
*/
|
||||
ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, outctx->c,
|
||||
"Want write during nonblocking accept");
|
||||
if (outctx->c->cs) {
|
||||
outctx->c->cs->sense = CONN_SENSE_WANT_WRITE;
|
||||
}
|
||||
outctx->rc = APR_EAGAIN;
|
||||
return APR_EAGAIN;
|
||||
}
|
||||
@ -2295,6 +2339,7 @@ static apr_status_t ssl_io_input_add_filter(ssl_filter_ctx_t *filter_ctx, conn_r
|
||||
}
|
||||
BIO_set_data(filter_ctx->pbioRead, (void *)inctx);
|
||||
|
||||
inctx->c = c;
|
||||
inctx->ssl = ssl;
|
||||
inctx->bio_out = filter_ctx->pbioWrite;
|
||||
inctx->f = filter_ctx->pInputFilter;
|
||||
|
@ -268,12 +268,14 @@ struct timeout_queue {
|
||||
/*
|
||||
* Several timeout queues that use different timeouts, so that we always can
|
||||
* simply append to the end.
|
||||
* read_line_q uses vhost's TimeOut FIXME - we can use a short timeout here
|
||||
* write_completion_q uses vhost's TimeOut
|
||||
* keepalive_q uses vhost's KeepAliveTimeOut
|
||||
* linger_q uses MAX_SECS_TO_LINGER
|
||||
* short_linger_q uses SECONDS_TO_LINGER
|
||||
*/
|
||||
static struct timeout_queue *write_completion_q,
|
||||
static struct timeout_queue *read_line_q,
|
||||
*write_completion_q,
|
||||
*keepalive_q,
|
||||
*linger_q,
|
||||
*short_linger_q;
|
||||
@ -446,7 +448,8 @@ static event_retained_data *retained;
|
||||
static int max_spawn_rate_per_bucket = MAX_SPAWN_RATE / 1;
|
||||
|
||||
struct event_srv_cfg_s {
|
||||
struct timeout_queue *wc_q,
|
||||
struct timeout_queue *rl_q,
|
||||
*wc_q,
|
||||
*ka_q;
|
||||
};
|
||||
|
||||
@ -731,6 +734,9 @@ static int event_query(int query_code, int *result, apr_status_t *rv)
|
||||
case AP_MPMQ_CAN_POLL:
|
||||
*result = 1;
|
||||
break;
|
||||
case AP_MPMQ_CAN_AGAIN:
|
||||
*result = 1;
|
||||
break;
|
||||
default:
|
||||
*rv = APR_ENOTIMPL;
|
||||
break;
|
||||
@ -979,15 +985,19 @@ static void process_lingering_close(event_conn_state_t *cs);
|
||||
|
||||
static void update_reqevents_from_sense(event_conn_state_t *cs, int sense)
|
||||
{
|
||||
if (sense < 0) {
|
||||
/* has the desired sense been overridden? */
|
||||
if (cs->pub.sense != CONN_SENSE_DEFAULT) {
|
||||
sense = cs->pub.sense;
|
||||
}
|
||||
|
||||
/* read or write */
|
||||
if (sense == CONN_SENSE_WANT_READ) {
|
||||
cs->pfd.reqevents = APR_POLLIN | APR_POLLHUP;
|
||||
}
|
||||
else {
|
||||
else if (sense == CONN_SENSE_WANT_WRITE) {
|
||||
cs->pfd.reqevents = APR_POLLOUT;
|
||||
}
|
||||
|
||||
/* POLLERR is usually returned event only, but some pollset
|
||||
* backends may require it in reqevents to do the right thing,
|
||||
* so it shouldn't hurt (ignored otherwise).
|
||||
@ -1034,6 +1044,7 @@ static void process_socket(apr_thread_t *thd, apr_pool_t * p, apr_socket_t * soc
|
||||
&mpm_event_module);
|
||||
cs->pfd.desc_type = APR_POLL_SOCKET;
|
||||
cs->pfd.desc.s = sock;
|
||||
cs->pub.sense = CONN_SENSE_DEFAULT;
|
||||
update_reqevents_from_sense(cs, CONN_SENSE_WANT_READ);
|
||||
pt->type = PT_CSD;
|
||||
pt->baton = cs;
|
||||
@ -1064,7 +1075,6 @@ 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;
|
||||
rc = OK;
|
||||
}
|
||||
else {
|
||||
@ -1115,6 +1125,8 @@ read_request:
|
||||
/*
|
||||
* The process_connection hooks above should set the connection state
|
||||
* appropriately upon return, for event MPM to either:
|
||||
* - call the hooks again after waiting for a read or write, or react to an
|
||||
* overridden CONN_SENSE_WANT_READ / CONN_SENSE_WANT_WRITE.
|
||||
* - do lingering close (CONN_STATE_LINGER),
|
||||
* - wait for readability of the next request with respect to the keepalive
|
||||
* timeout (state CONN_STATE_CHECK_REQUEST_LINE_READABLE),
|
||||
@ -1140,11 +1152,12 @@ read_request:
|
||||
* while this was expected to do lingering close unconditionally with
|
||||
* worker or prefork MPMs for instance.
|
||||
*/
|
||||
if (rc != OK || (cs->pub.state >= CONN_STATE_NUM)
|
||||
if (rc != AGAIN && (
|
||||
rc != OK || (cs->pub.state >= CONN_STATE_NUM)
|
||||
|| (cs->pub.state < CONN_STATE_LINGER
|
||||
&& cs->pub.state != CONN_STATE_WRITE_COMPLETION
|
||||
&& cs->pub.state != CONN_STATE_CHECK_REQUEST_LINE_READABLE
|
||||
&& cs->pub.state != CONN_STATE_SUSPENDED)) {
|
||||
&& cs->pub.state != CONN_STATE_SUSPENDED))) {
|
||||
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(10111)
|
||||
"process_socket: connection processing %s: closing",
|
||||
rc ? apr_psprintf(c->pool, "returned error %i", rc)
|
||||
@ -1153,6 +1166,41 @@ read_request:
|
||||
cs->pub.state = CONN_STATE_LINGER;
|
||||
}
|
||||
|
||||
if (cs->pub.state == CONN_STATE_READ_REQUEST_LINE) {
|
||||
ap_update_child_status(cs->sbh, SERVER_BUSY_READ, NULL);
|
||||
|
||||
/* It greatly simplifies the logic to use a single timeout value per q
|
||||
* because the new element can just be added to the end of the list and
|
||||
* it will stay sorted in expiration time sequence. If brand new
|
||||
* sockets are sent to the event thread for a readability check, this
|
||||
* will be a slight behavior change - they use the non-keepalive
|
||||
* timeout today. With a normal client, the socket will be readable in
|
||||
* a few milliseconds anyway.
|
||||
*/
|
||||
cs->queue_timestamp = apr_time_now();
|
||||
notify_suspend(cs);
|
||||
|
||||
/* Add work to pollset. */
|
||||
update_reqevents_from_sense(cs, CONN_SENSE_WANT_READ);
|
||||
apr_thread_mutex_lock(timeout_mutex);
|
||||
TO_QUEUE_APPEND(cs->sc->rl_q, cs);
|
||||
rv = apr_pollset_add(event_pollset, &cs->pfd);
|
||||
if (rv != APR_SUCCESS && !APR_STATUS_IS_EEXIST(rv)) {
|
||||
AP_DEBUG_ASSERT(0);
|
||||
TO_QUEUE_REMOVE(cs->sc->rl_q, cs);
|
||||
apr_thread_mutex_unlock(timeout_mutex);
|
||||
ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, APLOGNO(10374)
|
||||
"process_socket: apr_pollset_add failure for "
|
||||
"read request line");
|
||||
close_connection(cs);
|
||||
signal_threads(ST_GRACEFUL);
|
||||
}
|
||||
else {
|
||||
apr_thread_mutex_unlock(timeout_mutex);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (cs->pub.state == CONN_STATE_WRITE_COMPLETION) {
|
||||
int pending = DECLINED;
|
||||
|
||||
@ -1174,7 +1222,7 @@ read_request:
|
||||
cs->queue_timestamp = apr_time_now();
|
||||
notify_suspend(cs);
|
||||
|
||||
update_reqevents_from_sense(cs, -1);
|
||||
update_reqevents_from_sense(cs, CONN_SENSE_WANT_WRITE);
|
||||
apr_thread_mutex_lock(timeout_mutex);
|
||||
TO_QUEUE_APPEND(cs->sc->wc_q, cs);
|
||||
rv = apr_pollset_add(event_pollset, &cs->pfd);
|
||||
@ -1280,7 +1328,7 @@ static apr_status_t event_resume_suspended (conn_rec *c)
|
||||
cs->pub.state = CONN_STATE_WRITE_COMPLETION;
|
||||
notify_suspend(cs);
|
||||
|
||||
update_reqevents_from_sense(cs, -1);
|
||||
update_reqevents_from_sense(cs, CONN_SENSE_WANT_WRITE);
|
||||
apr_thread_mutex_lock(timeout_mutex);
|
||||
TO_QUEUE_APPEND(cs->sc->wc_q, cs);
|
||||
apr_pollset_add(event_pollset, &cs->pfd);
|
||||
@ -1738,6 +1786,7 @@ static void process_lingering_close(event_conn_state_t *cs)
|
||||
}
|
||||
|
||||
/* (Re)queue the connection to come back when readable */
|
||||
cs->pub.sense = CONN_SENSE_DEFAULT;
|
||||
update_reqevents_from_sense(cs, CONN_SENSE_WANT_READ);
|
||||
q = (cs->pub.state == CONN_STATE_LINGER_SHORT) ? short_linger_q : linger_q;
|
||||
apr_thread_mutex_lock(timeout_mutex);
|
||||
@ -1911,10 +1960,11 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy)
|
||||
last_log = now;
|
||||
apr_thread_mutex_lock(timeout_mutex);
|
||||
ap_log_error(APLOG_MARK, APLOG_TRACE6, 0, ap_server_conf,
|
||||
"connections: %u (clogged: %u write-completion: %d "
|
||||
"connections: %u (clogged: %u read-line: %d write-completion: %d "
|
||||
"keep-alive: %d lingering: %d suspended: %u)",
|
||||
apr_atomic_read32(&connection_count),
|
||||
apr_atomic_read32(&clogged_count),
|
||||
apr_atomic_read32(read_line_q->total),
|
||||
apr_atomic_read32(write_completion_q->total),
|
||||
apr_atomic_read32(keepalive_q->total),
|
||||
apr_atomic_read32(&lingering_count),
|
||||
@ -2047,6 +2097,11 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy)
|
||||
int blocking = 0;
|
||||
|
||||
switch (cs->pub.state) {
|
||||
case CONN_STATE_READ_REQUEST_LINE:
|
||||
remove_from_q = cs->sc->rl_q;
|
||||
blocking = 1;
|
||||
break;
|
||||
|
||||
case CONN_STATE_WRITE_COMPLETION:
|
||||
remove_from_q = cs->sc->wc_q;
|
||||
blocking = 1;
|
||||
@ -2263,16 +2318,19 @@ do_maintenance:
|
||||
else {
|
||||
process_keepalive_queue(now);
|
||||
}
|
||||
/* Step 2: write completion timeouts */
|
||||
/* Step 2: read line timeouts */
|
||||
process_timeout_queue(read_line_q, now,
|
||||
shutdown_connection);
|
||||
/* Step 3: write completion timeouts */
|
||||
process_timeout_queue(write_completion_q, now,
|
||||
defer_lingering_close);
|
||||
/* Step 3: (normal) lingering close completion timeouts */
|
||||
/* Step 4: (normal) lingering close completion timeouts */
|
||||
if (dying && linger_q->timeout > short_linger_q->timeout) {
|
||||
/* Dying, force short timeout for normal lingering close */
|
||||
linger_q->timeout = short_linger_q->timeout;
|
||||
}
|
||||
process_timeout_queue(linger_q, now, shutdown_connection);
|
||||
/* Step 4: (short) lingering close completion timeouts */
|
||||
/* Step 5: (short) lingering close completion timeouts */
|
||||
process_timeout_queue(short_linger_q, now, shutdown_connection);
|
||||
|
||||
apr_thread_mutex_unlock(timeout_mutex);
|
||||
@ -2282,6 +2340,7 @@ do_maintenance:
|
||||
: -1);
|
||||
|
||||
ps->keep_alive = apr_atomic_read32(keepalive_q->total);
|
||||
ps->read_line = apr_atomic_read32(read_line_q->total);
|
||||
ps->write_completion = apr_atomic_read32(write_completion_q->total);
|
||||
ps->connections = apr_atomic_read32(&connection_count);
|
||||
ps->suspended = apr_atomic_read32(&suspended_count);
|
||||
@ -3910,14 +3969,15 @@ static int event_post_config(apr_pool_t *pconf, apr_pool_t *plog,
|
||||
struct {
|
||||
struct timeout_queue *tail, *q;
|
||||
apr_hash_t *hash;
|
||||
} wc, ka;
|
||||
} rl, wc, ka;
|
||||
|
||||
/* Not needed in pre_config stage */
|
||||
if (ap_state_query(AP_SQ_MAIN_STATE) == AP_SQ_MS_CREATE_PRE_CONFIG) {
|
||||
return OK;
|
||||
}
|
||||
|
||||
wc.tail = ka.tail = NULL;
|
||||
rl.tail = wc.tail = ka.tail = NULL;
|
||||
rl.hash = apr_hash_make(ptemp);
|
||||
wc.hash = apr_hash_make(ptemp);
|
||||
ka.hash = apr_hash_make(ptemp);
|
||||
|
||||
@ -3931,7 +3991,13 @@ static int event_post_config(apr_pool_t *pconf, apr_pool_t *plog,
|
||||
|
||||
ap_set_module_config(s->module_config, &mpm_event_module, sc);
|
||||
if (!wc.tail) {
|
||||
|
||||
/* The main server uses the global queues */
|
||||
|
||||
rl.q = TO_QUEUE_MAKE(pconf, s->timeout, NULL);
|
||||
apr_hash_set(rl.hash, &s->timeout, sizeof s->timeout, rl.q);
|
||||
rl.tail = read_line_q = rl.q;
|
||||
|
||||
wc.q = TO_QUEUE_MAKE(pconf, s->timeout, NULL);
|
||||
apr_hash_set(wc.hash, &s->timeout, sizeof s->timeout, wc.q);
|
||||
wc.tail = write_completion_q = wc.q;
|
||||
@ -3942,8 +4008,17 @@ static int event_post_config(apr_pool_t *pconf, apr_pool_t *plog,
|
||||
ka.tail = keepalive_q = ka.q;
|
||||
}
|
||||
else {
|
||||
|
||||
/* The vhosts use any existing queue with the same timeout,
|
||||
* or their own queue(s) if there isn't */
|
||||
|
||||
rl.q = apr_hash_get(rl.hash, &s->timeout, sizeof s->timeout);
|
||||
if (!rl.q) {
|
||||
rl.q = TO_QUEUE_MAKE(pconf, s->timeout, rl.tail);
|
||||
apr_hash_set(rl.hash, &s->timeout, sizeof s->timeout, rl.q);
|
||||
rl.tail = rl.tail->next = rl.q;
|
||||
}
|
||||
|
||||
wc.q = apr_hash_get(wc.hash, &s->timeout, sizeof s->timeout);
|
||||
if (!wc.q) {
|
||||
wc.q = TO_QUEUE_MAKE(pconf, s->timeout, wc.tail);
|
||||
@ -3960,6 +4035,7 @@ static int event_post_config(apr_pool_t *pconf, apr_pool_t *plog,
|
||||
ka.tail = ka.tail->next = ka.q;
|
||||
}
|
||||
}
|
||||
sc->rl_q = rl.q;
|
||||
sc->wc_q = wc.q;
|
||||
sc->ka_q = ka.q;
|
||||
}
|
||||
|
123
support/ab.c
123
support/ab.c
@ -237,7 +237,11 @@ typedef enum {
|
||||
* know if it worked yet
|
||||
*/
|
||||
STATE_CONNECTED, /* we know TCP connect completed */
|
||||
STATE_READ
|
||||
#ifdef USE_SSL
|
||||
STATE_HANDSHAKE, /* in the handshake phase */
|
||||
#endif
|
||||
STATE_WRITE, /* in the write phase */
|
||||
STATE_READ /* in the read phase */
|
||||
} connect_state_e;
|
||||
|
||||
#define CBUFFSIZE (8192)
|
||||
@ -520,21 +524,13 @@ static void set_polled_events(struct connection *c, apr_int16_t new_reqevents)
|
||||
}
|
||||
}
|
||||
|
||||
static void set_conn_state(struct connection *c, connect_state_e new_state)
|
||||
static void set_conn_state(struct connection *c, connect_state_e new_state,
|
||||
apr_int16_t events)
|
||||
{
|
||||
apr_int16_t events_by_state[] = {
|
||||
0, /* for STATE_UNCONNECTED */
|
||||
APR_POLLOUT, /* for STATE_CONNECTING */
|
||||
APR_POLLIN, /* for STATE_CONNECTED; we don't poll in this state,
|
||||
* so prepare for polling in the following state --
|
||||
* STATE_READ
|
||||
*/
|
||||
APR_POLLIN /* for STATE_READ */
|
||||
};
|
||||
|
||||
c->state = new_state;
|
||||
|
||||
set_polled_events(c, events_by_state[new_state]);
|
||||
set_polled_events(c, events);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------- */
|
||||
@ -707,7 +703,7 @@ static void ssl_print_info(struct connection *c)
|
||||
}
|
||||
ssl_print_connection_info(bio_err,c->ssl);
|
||||
SSL_SESSION_print(bio_err, SSL_get_session(c->ssl));
|
||||
}
|
||||
}
|
||||
|
||||
static void ssl_proceed_handshake(struct connection *c)
|
||||
{
|
||||
@ -783,14 +779,19 @@ static void ssl_proceed_handshake(struct connection *c)
|
||||
}
|
||||
#endif
|
||||
write_request(c);
|
||||
|
||||
do_next = 0;
|
||||
break;
|
||||
case SSL_ERROR_WANT_READ:
|
||||
set_polled_events(c, APR_POLLIN);
|
||||
|
||||
set_conn_state(c, STATE_HANDSHAKE, APR_POLLIN);
|
||||
|
||||
do_next = 0;
|
||||
break;
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
set_polled_events(c, APR_POLLOUT);
|
||||
|
||||
set_conn_state(c, STATE_HANDSHAKE, APR_POLLOUT);
|
||||
|
||||
do_next = 0;
|
||||
break;
|
||||
case SSL_ERROR_WANT_CONNECT:
|
||||
@ -810,9 +811,6 @@ static void ssl_proceed_handshake(struct connection *c)
|
||||
|
||||
static void write_request(struct connection * c)
|
||||
{
|
||||
if (started >= requests) {
|
||||
return;
|
||||
}
|
||||
|
||||
do {
|
||||
apr_time_t tnow;
|
||||
@ -845,10 +843,14 @@ static void write_request(struct connection * c)
|
||||
if (e <= 0) {
|
||||
switch (SSL_get_error(c->ssl, e)) {
|
||||
case SSL_ERROR_WANT_READ:
|
||||
set_polled_events(c, APR_POLLIN);
|
||||
|
||||
set_conn_state(c, STATE_WRITE, APR_POLLIN);
|
||||
|
||||
break;
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
set_polled_events(c, APR_POLLOUT);
|
||||
|
||||
set_conn_state(c, STATE_WRITE, APR_POLLOUT);
|
||||
|
||||
break;
|
||||
default:
|
||||
BIO_printf(bio_err, "SSL write failed - closing connection\n");
|
||||
@ -871,7 +873,7 @@ static void write_request(struct connection * c)
|
||||
close_connection(c);
|
||||
}
|
||||
else {
|
||||
set_polled_events(c, APR_POLLOUT);
|
||||
set_conn_state(c, STATE_WRITE, APR_POLLOUT);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -883,7 +885,8 @@ static void write_request(struct connection * c)
|
||||
|
||||
c->endwrite = lasttime = apr_time_now();
|
||||
started++;
|
||||
set_conn_state(c, STATE_READ);
|
||||
|
||||
set_conn_state(c, STATE_READ, APR_POLLIN);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------- */
|
||||
@ -1355,8 +1358,9 @@ static void start_connect(struct connection * c)
|
||||
{
|
||||
apr_status_t rv;
|
||||
|
||||
if (!(started < requests))
|
||||
if (!(started < requests)) {
|
||||
return;
|
||||
}
|
||||
|
||||
c->read = 0;
|
||||
c->bread = 0;
|
||||
@ -1439,12 +1443,12 @@ static void start_connect(struct connection * c)
|
||||
#endif
|
||||
if ((rv = apr_socket_connect(c->aprsock, destsa)) != APR_SUCCESS) {
|
||||
if (APR_STATUS_IS_EINPROGRESS(rv)) {
|
||||
set_conn_state(c, STATE_CONNECTING);
|
||||
set_conn_state(c, STATE_CONNECTING, APR_POLLOUT);
|
||||
c->rwrite = 0;
|
||||
return;
|
||||
}
|
||||
else {
|
||||
set_conn_state(c, STATE_UNCONNECTED);
|
||||
set_conn_state(c, STATE_UNCONNECTED, 0);
|
||||
apr_socket_close(c->aprsock);
|
||||
if (good == 0 && destsa->next) {
|
||||
destsa = destsa->next;
|
||||
@ -1465,7 +1469,6 @@ static void start_connect(struct connection * c)
|
||||
}
|
||||
|
||||
/* connected first time */
|
||||
set_conn_state(c, STATE_CONNECTED);
|
||||
#ifdef USE_SSL
|
||||
if (c->ssl) {
|
||||
ssl_proceed_handshake(c);
|
||||
@ -1513,7 +1516,7 @@ static void close_connection(struct connection * c)
|
||||
}
|
||||
}
|
||||
|
||||
set_conn_state(c, STATE_UNCONNECTED);
|
||||
set_conn_state(c, STATE_UNCONNECTED, 0);
|
||||
#ifdef USE_SSL
|
||||
if (c->ssl) {
|
||||
SSL_shutdown(c->ssl);
|
||||
@ -1571,10 +1574,14 @@ read_more:
|
||||
return;
|
||||
}
|
||||
else if (scode == SSL_ERROR_WANT_READ) {
|
||||
set_polled_events(c, APR_POLLIN);
|
||||
|
||||
set_conn_state(c, STATE_READ, APR_POLLIN);
|
||||
|
||||
}
|
||||
else if (scode == SSL_ERROR_WANT_WRITE) {
|
||||
set_polled_events(c, APR_POLLOUT);
|
||||
|
||||
set_conn_state(c, STATE_READ, APR_POLLOUT);
|
||||
|
||||
}
|
||||
else {
|
||||
/* some fatal error: */
|
||||
@ -1668,7 +1675,7 @@ read_more:
|
||||
}
|
||||
else {
|
||||
/* header is in invalid or too big - close connection */
|
||||
set_conn_state(c, STATE_UNCONNECTED);
|
||||
set_conn_state(c, STATE_UNCONNECTED, 0);
|
||||
apr_socket_close(c->aprsock);
|
||||
err_response++;
|
||||
if (bad++ > 10) {
|
||||
@ -1758,7 +1765,13 @@ read_more:
|
||||
goto read_more;
|
||||
}
|
||||
|
||||
if (c->keepalive && (c->bread >= c->length)) {
|
||||
/* are we done? */
|
||||
if (started >= requests && (c->bread >= c->length)) {
|
||||
close_connection(c);
|
||||
}
|
||||
|
||||
/* are we keepalive? if so, reuse existing connection */
|
||||
else if (c->keepalive && (c->bread >= c->length)) {
|
||||
/* finished a keep-alive connection */
|
||||
good++;
|
||||
/* save out time */
|
||||
@ -1790,7 +1803,7 @@ read_more:
|
||||
c->read = c->bread = 0;
|
||||
/* zero connect time with keep-alive */
|
||||
c->start = c->connect = lasttime = apr_time_now();
|
||||
set_conn_state(c, STATE_CONNECTED);
|
||||
|
||||
write_request(c);
|
||||
}
|
||||
}
|
||||
@ -1980,6 +1993,7 @@ static void test(void)
|
||||
do {
|
||||
status = apr_pollset_poll(readbits, aprtimeout, &n, &pollresults);
|
||||
} while (APR_STATUS_IS_EINTR(status));
|
||||
|
||||
if (status != APR_SUCCESS)
|
||||
apr_err("apr_pollset_poll", status);
|
||||
|
||||
@ -2015,8 +2029,23 @@ static void test(void)
|
||||
* connection is done and we loop here endlessly calling
|
||||
* apr_poll().
|
||||
*/
|
||||
if ((rtnev & APR_POLLIN) || (rtnev & APR_POLLPRI) || (rtnev & APR_POLLHUP))
|
||||
read_connection(c);
|
||||
if ((rtnev & APR_POLLIN) || (rtnev & APR_POLLPRI) || (rtnev & APR_POLLHUP)) {
|
||||
|
||||
switch (c->state) {
|
||||
#ifdef USE_SSL
|
||||
case STATE_HANDSHAKE:
|
||||
ssl_proceed_handshake(c);
|
||||
break;
|
||||
#endif
|
||||
case STATE_WRITE:
|
||||
write_request(c);
|
||||
break;
|
||||
case STATE_READ:
|
||||
read_connection(c);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
if ((rtnev & APR_POLLERR) || (rtnev & APR_POLLNVAL)) {
|
||||
if (destsa->next && c->state == STATE_CONNECTING && good == 0) {
|
||||
destsa = destsa->next;
|
||||
@ -2040,7 +2069,7 @@ static void test(void)
|
||||
/* call connect() again to detect errors */
|
||||
rv = apr_socket_connect(c->aprsock, destsa);
|
||||
if (rv != APR_SUCCESS) {
|
||||
set_conn_state(c, STATE_UNCONNECTED);
|
||||
set_conn_state(c, STATE_UNCONNECTED, 0);
|
||||
apr_socket_close(c->aprsock);
|
||||
err_conn++;
|
||||
if (bad++ > 10) {
|
||||
@ -2052,7 +2081,7 @@ static void test(void)
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
set_conn_state(c, STATE_CONNECTED);
|
||||
|
||||
#ifdef USE_SSL
|
||||
if (c->ssl)
|
||||
ssl_proceed_handshake(c);
|
||||
@ -2060,16 +2089,24 @@ static void test(void)
|
||||
#endif
|
||||
write_request(c);
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
/* POLLOUT is one shot */
|
||||
set_polled_events(c, APR_POLLIN);
|
||||
if (c->state == STATE_READ) {
|
||||
read_connection(c);
|
||||
}
|
||||
else {
|
||||
|
||||
switch (c->state) {
|
||||
#ifdef USE_SSL
|
||||
case STATE_HANDSHAKE:
|
||||
ssl_proceed_handshake(c);
|
||||
break;
|
||||
#endif
|
||||
case STATE_WRITE:
|
||||
write_request(c);
|
||||
break;
|
||||
case STATE_READ:
|
||||
read_connection(c);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2692,7 +2729,7 @@ int main(int argc, const char * const argv[])
|
||||
if (ssl_cert != NULL) {
|
||||
if (SSL_CTX_use_certificate_chain_file(ssl_ctx, ssl_cert) <= 0) {
|
||||
BIO_printf(bio_err, "unable to get certificate from '%s'\n",
|
||||
ssl_cert);
|
||||
ssl_cert);
|
||||
ERR_print_errors(bio_err);
|
||||
exit(1);
|
||||
}
|
||||
|
@ -44,6 +44,7 @@ class TestTimeout:
|
||||
sock.close()
|
||||
|
||||
# Check that mod_reqtimeout handshake setting takes effect
|
||||
@pytest.mark.skip(reason="SSL handshake timeout currently broken")
|
||||
def test_h2_105_02(self, env):
|
||||
conf = H2Conf(env)
|
||||
conf.add("""
|
||||
|
Reference in New Issue
Block a user