summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES5
-rw-r--r--docs/manual/mod/mpm_common.xml31
-rw-r--r--include/ap_listen.h18
-rw-r--r--os/unix/unixd.c6
-rw-r--r--server/listen.c20
-rw-r--r--server/mpm/event/event.c4
-rw-r--r--server/mpm/motorz/motorz.c5
-rw-r--r--server/mpm/prefork/prefork.c7
-rw-r--r--server/mpm/worker/worker.c4
9 files changed, 97 insertions, 3 deletions
diff --git a/CHANGES b/CHANGES
index 518f1f04f3..5e90aefc4f 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,6 +1,11 @@
-*- coding: utf-8 -*-
Changes with Apache 2.5.1
+
+ *) core: Add "AcceptErrorsNonFatal" to allow ECONNREFUSED, ECONNABORTED, and
+ ECONNRESET during the client accept() to not trigger graceful shutdown of
+ the child process. [Eric Covener]
+
*) mod_md v1.1.7:
- MDMustStaple was unable to create the necessary OpenSSL OBJ identifier on some platforms,
possibly because this fails if the OID is already configured in ```openssl.cnf```, see
diff --git a/docs/manual/mod/mpm_common.xml b/docs/manual/mod/mpm_common.xml
index 2978b20794..d1e09eeed5 100644
--- a/docs/manual/mod/mpm_common.xml
+++ b/docs/manual/mod/mpm_common.xml
@@ -873,4 +873,35 @@ client connections</description>
</usage>
</directivesynopsis>
+<directivesynopsis>
+<name>AcceptErrorsNonFatal</name>
+<description>Treat some errors accepting a new connection as non-fatal
+to the httpd process.</description>
+<syntax>AcceptErrorsNonFatal ON</syntax>
+<default>OFF (ECONNREFUSED, ECONNABORTED, ECONNRESET cause the process to
+ exit)</default>
+<contextlist><context>server config</context></contextlist>
+<modulelist><module>event</module><module>worker</module>
+<module>prefork</module>
+</modulelist>
+<compatibility>2.5.1 and later</compatibility>
+
+<usage>
+ <p>The <directive>AcceptErrorsNonFatal</directive> alters the servers
+ behavior under some rare errors that may occur while accepting a new
+ client connection. By default, the child process handling a request
+ will gracefully exit when nearly any socket error occurs during the
+ accept() system call. This is to ensure a potentially unhealthy
+ child process does not try to take on more new connections.</p>
+
+ <p>With <directive>AcceptErrorsNonFatal</directive> set to "ON",
+ the process will <em>not</em> begin to exit if the accept() error is
+ ECONNREFUSED, ECONNABORTED, or ECONNRESET.</p>
+
+ <note>Some third-party firwewall software components may inject errors
+ into accept() processing, using return codes not specified by the
+ operating system</note>
+</usage>
+</directivesynopsis>
+
</modulesynopsis>
diff --git a/include/ap_listen.h b/include/ap_listen.h
index 58c2574ff4..9cbaaa4910 100644
--- a/include/ap_listen.h
+++ b/include/ap_listen.h
@@ -71,6 +71,12 @@ struct ap_listen_rec {
const char* protocol;
ap_slave_t *slave;
+
+ /**
+ * Allow the accept_func to return a wider set of return codes
+ */
+ int use_specific_errors;
+
};
/**
@@ -79,6 +85,9 @@ struct ap_listen_rec {
AP_DECLARE_DATA extern ap_listen_rec *ap_listeners;
AP_DECLARE_DATA extern int ap_num_listen_buckets;
AP_DECLARE_DATA extern int ap_have_so_reuseport;
+AP_DECLARE_DATA extern int ap_accept_errors_nonfatal;
+
+AP_DECLARE(int) ap_accept_error_is_nonfatal(apr_status_t rv);
/**
* Setup all of the defaults for the listener list
@@ -143,6 +152,10 @@ AP_DECLARE_NONSTD(const char *) ap_set_receive_buffer_size(cmd_parms *cmd,
void *dummy,
const char *arg);
+AP_DECLARE_NONSTD(const char *) ap_set_accept_errors_nonfatal(cmd_parms *cmd,
+ void *dummy,
+ int flag);
+
#define LISTEN_COMMANDS \
AP_INIT_TAKE1("ListenBacklog", ap_set_listenbacklog, NULL, RSRC_CONF, \
"Maximum length of the queue of pending connections, as used by listen(2)"), \
@@ -153,8 +166,9 @@ AP_INIT_TAKE_ARGV("Listen", ap_set_listener, NULL, RSRC_CONF, \
AP_INIT_TAKE1("SendBufferSize", ap_set_send_buffer_size, NULL, RSRC_CONF, \
"Send buffer size in bytes"), \
AP_INIT_TAKE1("ReceiveBufferSize", ap_set_receive_buffer_size, NULL, \
- RSRC_CONF, "Receive buffer size in bytes")
-
+ RSRC_CONF, "Receive buffer size in bytes"), \
+AP_INIT_FLAG("AcceptErrorsNonFatal", ap_set_accept_errors_nonfatal, NULL, \
+ RSRC_CONF, "Some accept() errors are not fatal to the process")
#ifdef __cplusplus
}
#endif
diff --git a/os/unix/unixd.c b/os/unix/unixd.c
index 43645f09da..b939355289 100644
--- a/os/unix/unixd.c
+++ b/os/unix/unixd.c
@@ -320,6 +320,12 @@ AP_DECLARE(apr_status_t) ap_unixd_accept(void **accepted, ap_listen_rec *lr,
if (APR_STATUS_IS_EINTR(status)) {
return status;
}
+
+ /* Let the caller handle slightly more varied return values */
+ if (lr->use_specific_errors && ap_accept_error_is_nonfatal(status)) {
+ return status;
+ }
+
/* Our old behaviour here was to continue after accept()
* errors. But this leads us into lots of troubles
* because most of the errors are quite fatal. For
diff --git a/server/listen.c b/server/listen.c
index 2d1db6dc29..56308e21cc 100644
--- a/server/listen.c
+++ b/server/listen.c
@@ -59,6 +59,9 @@ static ap_listen_rec **ap_listen_buckets;
*/
AP_DECLARE_DATA int ap_have_so_reuseport = -1;
+/* Whether some accept() errors are non-fatal to the process */
+AP_DECLARE_DATA int ap_accept_errors_nonfatal = 0;
+
static ap_listen_rec *old_listeners;
static int ap_listenbacklog;
static int ap_listencbratio;
@@ -793,6 +796,7 @@ AP_DECLARE(int) ap_setup_listeners(server_rec *s)
}
for (lr = ap_listeners; lr; lr = lr->next) {
+ lr->use_specific_errors = ap_accept_errors_nonfatal;
num_listeners++;
found = 0;
for (ls = s; ls && !found; ls = ls->next) {
@@ -1004,6 +1008,14 @@ AP_DECLARE(void) ap_listen_pre_config(void)
}
}
+AP_DECLARE(int) ap_accept_error_is_nonfatal(apr_status_t status)
+{
+
+ return APR_STATUS_IS_ECONNREFUSED(status)
+ || APR_STATUS_IS_ECONNABORTED(status)
+ || APR_STATUS_IS_ECONNRESET(status);
+}
+
AP_DECLARE_NONSTD(const char *) ap_set_listener(cmd_parms *cmd, void *dummy,
int argc, char *const argv[])
{
@@ -1128,6 +1140,14 @@ AP_DECLARE_NONSTD(const char *) ap_set_send_buffer_size(cmd_parms *cmd,
return NULL;
}
+AP_DECLARE_NONSTD(const char *) ap_set_accept_errors_nonfatal(cmd_parms *cmd,
+ void *dummy,
+ int flag)
+{
+ ap_accept_errors_nonfatal = flag;
+ return NULL;
+}
+
AP_DECLARE_NONSTD(const char *) ap_set_receive_buffer_size(cmd_parms *cmd,
void *dummy,
const char *arg)
diff --git a/server/mpm/event/event.c b/server/mpm/event/event.c
index 8d33442b8c..6ccc208445 100644
--- a/server/mpm/event/event.c
+++ b/server/mpm/event/event.c
@@ -2068,6 +2068,10 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy)
resource_shortage = 1;
signal_threads(ST_GRACEFUL);
}
+ else if (ap_accept_error_is_nonfatal(rc)) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, rc, ap_server_conf,
+ "accept() on client socket failed");
+ }
if (csd != NULL) {
conns_this_child--;
diff --git a/server/mpm/motorz/motorz.c b/server/mpm/motorz/motorz.c
index d8d9522295..be8f3020e4 100644
--- a/server/mpm/motorz/motorz.c
+++ b/server/mpm/motorz/motorz.c
@@ -202,6 +202,11 @@ static apr_status_t motorz_io_accept(motorz_core_t *mz, motorz_sb_t *sb)
"motorz_io_accept failed");
clean_child_exit(APEXIT_CHILDSICK);
}
+ else if (ap_accept_error_is_nonfatal(rv)) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, ap_server_conf,
+ "accept() on client socket failed");
+ }
+
else {
motorz_conn_t *scon = apr_pcalloc(ptrans, sizeof(motorz_conn_t));
scon->pool = ptrans;
diff --git a/server/mpm/prefork/prefork.c b/server/mpm/prefork/prefork.c
index 649cc97d63..e20c516c7e 100644
--- a/server/mpm/prefork/prefork.c
+++ b/server/mpm/prefork/prefork.c
@@ -600,7 +600,12 @@ static void child_main(int child_num_arg, int child_bucket)
/* resource shortage or should-not-occur occurred */
clean_child_exit(APEXIT_CHILDSICK);
}
- else if (status != APR_SUCCESS) {
+ else if (ap_accept_error_is_nonfatal(rc)) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, rc, ap_server_conf,
+ "accept() on client socket failed");
+ }
+
+ if (status != APR_SUCCESS) {
continue;
}
diff --git a/server/mpm/worker/worker.c b/server/mpm/worker/worker.c
index c87c003c7e..9a971ebd57 100644
--- a/server/mpm/worker/worker.c
+++ b/server/mpm/worker/worker.c
@@ -699,6 +699,10 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t *thd, void * dummy)
resource_shortage = 1;
signal_threads(ST_GRACEFUL);
}
+ else if (ap_accept_error_is_nonfatal(rv)) {
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, rv, ap_server_conf,
+ "accept() on client socket failed");
+ }
if ((rv = SAFE_ACCEPT(apr_proc_mutex_unlock(my_bucket->mutex)))
!= APR_SUCCESS) {