-*- 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
</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>
const char* protocol;
ap_slave_t *slave;
+
+ /**
+ * Allow the accept_func to return a wider set of return codes
+ */
+ int use_specific_errors;
+
};
/**
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
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)"), \
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
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
*/
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;
}
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) {
}
}
+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[])
{
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)
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--;
"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;
/* 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;
}
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) {