summaryrefslogtreecommitdiff
path: root/server
diff options
context:
space:
mode:
Diffstat (limited to 'server')
-rw-r--r--server/config.c6
-rw-r--r--server/core.c6
-rw-r--r--server/core_filters.c4
-rw-r--r--server/log.c10
-rw-r--r--server/main.c10
-rw-r--r--server/mpm/event/event.c286
-rw-r--r--server/mpm/prefork/prefork.c6
-rw-r--r--server/mpm/winnt/child.c16
-rw-r--r--server/mpm/worker/worker.c30
-rw-r--r--server/mpm_unix.c55
-rw-r--r--server/protocol.c24
-rw-r--r--server/provider.c36
-rw-r--r--server/request.c10
-rw-r--r--server/scoreboard.c5
-rw-r--r--server/util.c2
-rw-r--r--server/util_mutex.c2
-rw-r--r--server/util_script.c11
17 files changed, 336 insertions, 183 deletions
diff --git a/server/config.c b/server/config.c
index 75857046..fb07623b 100644
--- a/server/config.c
+++ b/server/config.c
@@ -845,9 +845,11 @@ static const char *invoke_cmd(const command_rec *cmd, cmd_parms *parms,
const char *errmsg = NULL;
/** Have we been provided a list of acceptable directives? */
- if(parms->override_list != NULL)
- if(apr_table_get(parms->override_list, cmd->name) != NULL)
+ if (parms->override_list != NULL) {
+ if (apr_table_get(parms->override_list, cmd->name) != NULL) {
override_list_ok = 1;
+ }
+ }
if ((parms->override & cmd->req_override) == 0 && !override_list_ok) {
if (parms->override & NONFATAL_OVERRIDE) {
diff --git a/server/core.c b/server/core.c
index 541f6e8e..03137577 100644
--- a/server/core.c
+++ b/server/core.c
@@ -996,7 +996,7 @@ AP_DECLARE(const char *) ap_get_server_name_for_url(request_rec *r)
#if APR_HAVE_IPV6
if (ap_strchr_c(plain_server_name, ':')) { /* IPv6 literal? */
- return apr_psprintf(r->pool, "[%s]", plain_server_name);
+ return apr_pstrcat(r->pool, "[", plain_server_name, "]", NULL);
}
#endif
return plain_server_name;
@@ -1509,7 +1509,7 @@ static const char *set_error_document(cmd_parms *cmd, void *conf_,
* a msg and a (local) path.
*/
conf->response_code_strings[index_number] = (what == MSG) ?
- apr_pstrcat(cmd->pool, "\"",msg,NULL) :
+ apr_pstrcat(cmd->pool, "\"", msg, NULL) :
apr_pstrdup(cmd->pool, msg);
}
}
@@ -1612,7 +1612,7 @@ static const char *set_override(cmd_parms *cmd, void *d_, const char *l)
if (v)
set_allow_opts(cmd, &(d->override_opts), v);
else
- d->override_opts = OPT_SYM_LINKS;
+ d->override_opts = OPT_ALL;
}
else if (!strcasecmp(w, "FileInfo")) {
d->override |= OR_FILEINFO;
diff --git a/server/core_filters.c b/server/core_filters.c
index 18a32dde..0798d2ef 100644
--- a/server/core_filters.c
+++ b/server/core_filters.c
@@ -391,10 +391,6 @@ apr_status_t ap_core_output_filter(ap_filter_t *f, apr_bucket_brigade *new_bb)
if (ctx == NULL) {
ctx = apr_pcalloc(c->pool, sizeof(*ctx));
net->out_ctx = (core_output_filter_ctx_t *)ctx;
- rv = apr_socket_opt_set(net->client_socket, APR_SO_NONBLOCK, 1);
- if (rv != APR_SUCCESS) {
- return rv;
- }
/*
* Need to create tmp brigade with correct lifetime. Passing
* NULL to apr_brigade_split_ex would result in a brigade
diff --git a/server/log.c b/server/log.c
index de560efc..b33aa1fc 100644
--- a/server/log.c
+++ b/server/log.c
@@ -563,10 +563,10 @@ static int log_remote_address(const ap_errorlog_info *info, const char *arg,
{
if (info->r && !(arg && *arg == 'c'))
return apr_snprintf(buf, buflen, "%s:%d", info->r->useragent_ip,
- info->r->useragent_addr->port);
+ info->r->useragent_addr ? info->r->useragent_addr->port : 0);
else if (info->c)
return apr_snprintf(buf, buflen, "%s:%d", info->c->client_ip,
- info->c->client_addr->port);
+ info->c->client_addr ? info->c->client_addr->port : 0);
else
return 0;
}
@@ -968,12 +968,14 @@ static int do_errorlog_default(const ap_errorlog_info *info, char *buf,
if (info->r) {
len += apr_snprintf(buf + len, buflen - len,
info->r->connection->sbh ? "[client %s:%d] " : "[remote %s:%d] ",
- info->r->useragent_ip, info->r->useragent_addr->port);
+ info->r->useragent_ip,
+ info->r->useragent_addr ? info->r->useragent_addr->port : 0);
}
else if (info->c) {
len += apr_snprintf(buf + len, buflen - len,
info->c->sbh ? "[client %s:%d] " : "[remote %s:%d] ",
- info->c->client_ip, info->c->client_addr->port);
+ info->c->client_ip,
+ info->c->client_addr ? info->c->client_addr->port : 0);
}
/* the actual error message */
diff --git a/server/main.c b/server/main.c
index 9b88943f..28d18720 100644
--- a/server/main.c
+++ b/server/main.c
@@ -671,6 +671,11 @@ int main(int argc, const char * const argv[])
}
}
+ /* If our config failed, deal with that here. */
+ if (rv != OK) {
+ destroy_and_exit_process(process, 1);
+ }
+
signal_server = APR_RETRIEVE_OPTIONAL_FN(ap_signal_server);
if (signal_server) {
int exit_status;
@@ -680,11 +685,6 @@ int main(int argc, const char * const argv[])
}
}
- /* If our config failed, deal with that here. */
- if (rv != OK) {
- destroy_and_exit_process(process, 1);
- }
-
apr_pool_clear(plog);
if ( ap_run_open_logs(pconf, plog, ptemp, ap_server_conf) != OK) {
diff --git a/server/mpm/event/event.c b/server/mpm/event/event.c
index 0ab822a8..fa724688 100644
--- a/server/mpm/event/event.c
+++ b/server/mpm/event/event.c
@@ -166,13 +166,18 @@ static int ap_daemons_limit = 0;
static int max_workers = 0;
static int server_limit = 0;
static int thread_limit = 0;
+static int had_healthy_child = 0;
static int dying = 0;
static int workers_may_exit = 0;
static int start_thread_may_exit = 0;
static int listener_may_exit = 0;
-static int requests_this_child;
static int num_listensocks = 0;
-static apr_uint32_t connection_count = 0;
+static apr_int32_t conns_this_child; /* MaxConnectionsPerChild, only access
+ in listener thread */
+static apr_uint32_t connection_count = 0; /* Number of open connections */
+static apr_uint32_t lingering_count = 0; /* Number of connections in lingering close */
+static apr_uint32_t suspended_count = 0; /* Number of suspended connections */
+static apr_uint32_t clogged_count = 0; /* Number of threads processing ssl conns */
static int resource_shortage = 0;
static fd_queue_t *worker_queue;
static fd_queue_info_t *worker_queue_info;
@@ -372,8 +377,12 @@ static void enable_listensocks(int process_slot)
int i;
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(00457)
"Accepting new connections again: "
- "%u active conns, %u idle workers",
+ "%u active conns (%u lingering/%u clogged/%u suspended), "
+ "%u idle workers",
apr_atomic_read32(&connection_count),
+ apr_atomic_read32(&lingering_count),
+ apr_atomic_read32(&clogged_count),
+ apr_atomic_read32(&suspended_count),
ap_queue_info_get_idlers(worker_queue_info));
for (i = 0; i < num_listensocks; i++)
apr_pollset_add(event_pollset, &listener_pollfd[i]);
@@ -587,7 +596,20 @@ static int child_fatal;
static int volatile shutdown_pending;
static int volatile restart_pending;
-static apr_status_t decrement_connection_count(void *dummy) {
+static apr_status_t decrement_connection_count(void *cs_)
+{
+ event_conn_state_t *cs = cs_;
+ switch (cs->pub.state) {
+ case CONN_STATE_LINGER_NORMAL:
+ case CONN_STATE_LINGER_SHORT:
+ apr_atomic_dec32(&lingering_count);
+ break;
+ case CONN_STATE_SUSPENDED:
+ apr_atomic_dec32(&suspended_count);
+ break;
+ default:
+ break;
+ }
apr_atomic_dec32(&connection_count);
return APR_SUCCESS;
}
@@ -735,74 +757,97 @@ static void set_signals(void)
#endif
}
+static int start_lingering_close_common(event_conn_state_t *cs)
+{
+ apr_status_t rv;
+ struct timeout_queue *q;
+ apr_socket_t *csd = cs->pfd.desc.s;
+#ifdef AP_DEBUG
+ {
+ rv = apr_socket_timeout_set(csd, 0);
+ AP_DEBUG_ASSERT(rv == APR_SUCCESS);
+ }
+#else
+ apr_socket_timeout_set(csd, 0);
+#endif
+ /*
+ * If some module requested a shortened waiting period, only wait for
+ * 2s (SECONDS_TO_LINGER). This is useful for mitigating certain
+ * DoS attacks.
+ */
+ if (apr_table_get(cs->c->notes, "short-lingering-close")) {
+ cs->expiration_time =
+ apr_time_now() + apr_time_from_sec(SECONDS_TO_LINGER);
+ q = &short_linger_q;
+ cs->pub.state = CONN_STATE_LINGER_SHORT;
+ }
+ else {
+ cs->expiration_time =
+ apr_time_now() + apr_time_from_sec(MAX_SECS_TO_LINGER);
+ q = &linger_q;
+ cs->pub.state = CONN_STATE_LINGER_NORMAL;
+ }
+ 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;
+ rv = apr_pollset_add(event_pollset, &cs->pfd);
+ apr_thread_mutex_unlock(timeout_mutex);
+ if (rv != APR_SUCCESS && !APR_STATUS_IS_EEXIST(rv)) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf,
+ "start_lingering_close: apr_pollset_add failure");
+ apr_thread_mutex_lock(timeout_mutex);
+ TO_QUEUE_REMOVE(*q, cs);
+ apr_thread_mutex_unlock(timeout_mutex);
+ apr_socket_close(cs->pfd.desc.s);
+ apr_pool_clear(cs->p);
+ ap_push_pool(worker_queue_info, cs->p);
+ return 0;
+ }
+ return 1;
+}
+
/*
- * close our side of the connection
+ * Close our side of the connection, flushing data to the client first.
* Pre-condition: cs is not in any timeout queue and not in the pollset,
* timeout_mutex is not locked
* return: 0 if connection is fully closed,
* 1 if connection is lingering
- * may be called by listener or by worker thread
+ * May only be called by worker thread.
*/
-static int start_lingering_close(event_conn_state_t *cs)
+static int start_lingering_close_blocking(event_conn_state_t *cs)
{
- apr_status_t rv;
-
- cs->c->sbh = NULL; /* prevent scoreboard updates from the listener
- * worker will loop around and set SERVER_READY soon
- */
-
if (ap_start_lingering_close(cs->c)) {
apr_pool_clear(cs->p);
ap_push_pool(worker_queue_info, cs->p);
return 0;
}
- else {
- apr_socket_t *csd = ap_get_conn_socket(cs->c);
- struct timeout_queue *q;
+ return start_lingering_close_common(cs);
+}
-#ifdef AP_DEBUG
- {
- rv = apr_socket_timeout_set(csd, 0);
- AP_DEBUG_ASSERT(rv == APR_SUCCESS);
- }
-#else
- apr_socket_timeout_set(csd, 0);
-#endif
- /*
- * If some module requested a shortened waiting period, only wait for
- * 2s (SECONDS_TO_LINGER). This is useful for mitigating certain
- * DoS attacks.
- */
- if (apr_table_get(cs->c->notes, "short-lingering-close")) {
- cs->expiration_time =
- apr_time_now() + apr_time_from_sec(SECONDS_TO_LINGER);
- q = &short_linger_q;
- cs->pub.state = CONN_STATE_LINGER_SHORT;
- }
- else {
- cs->expiration_time =
- apr_time_now() + apr_time_from_sec(MAX_SECS_TO_LINGER);
- q = &linger_q;
- cs->pub.state = CONN_STATE_LINGER_NORMAL;
- }
- apr_thread_mutex_lock(timeout_mutex);
- TO_QUEUE_APPEND(*q, cs);
- cs->pfd.reqevents = APR_POLLIN | APR_POLLHUP | APR_POLLERR;
- rv = apr_pollset_add(event_pollset, &cs->pfd);
- apr_thread_mutex_unlock(timeout_mutex);
- if (rv != APR_SUCCESS && !APR_STATUS_IS_EEXIST(rv)) {
- ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf,
- "start_lingering_close: apr_pollset_add failure");
- apr_thread_mutex_lock(timeout_mutex);
- TO_QUEUE_REMOVE(*q, cs);
- apr_thread_mutex_unlock(timeout_mutex);
- apr_socket_close(cs->pfd.desc.s);
- apr_pool_clear(cs->p);
- ap_push_pool(worker_queue_info, cs->p);
- return 0;
- }
+/*
+ * Close our side of the connection, NOT flushing data to the client.
+ * This should only be called if there has been an error or if we know
+ * that our send buffers are empty.
+ * Pre-condition: cs is not in any timeout queue and not in the pollset,
+ * timeout_mutex is not locked
+ * return: 0 if connection is fully closed,
+ * 1 if connection is lingering
+ * may be called by listener thread
+ */
+static int start_lingering_close_nonblocking(event_conn_state_t *cs)
+{
+ conn_rec *c = cs->c;
+ apr_socket_t *csd = cs->pfd.desc.s;
+
+ if (c->aborted
+ || apr_socket_shutdown(csd, APR_SHUTDOWN_WRITE) != APR_SUCCESS) {
+ apr_socket_close(csd);
+ apr_pool_clear(cs->p);
+ ap_push_pool(worker_queue_info, cs->p);
+ return 0;
}
- return 1;
+ return start_lingering_close_common(cs);
}
/*
@@ -829,10 +874,8 @@ static int stop_lingering_close(event_conn_state_t *cs)
/*
* process one connection in the worker
- * return: 1 if the connection has been completed,
- * 0 if it is still open and waiting for some event
*/
-static int process_socket(apr_thread_t *thd, apr_pool_t * p, apr_socket_t * sock,
+static void process_socket(apr_thread_t *thd, apr_pool_t * p, apr_socket_t * sock,
event_conn_state_t * cs, int my_child_num,
int my_thread_num)
{
@@ -841,6 +884,7 @@ static int process_socket(apr_thread_t *thd, apr_pool_t * p, apr_socket_t * sock
int rc;
ap_sb_handle_t *sbh;
+ /* XXX: This will cause unbounded mem usage for long lasting connections */
ap_create_sb_handle(&sbh, p, my_child_num, my_thread_num);
if (cs == NULL) { /* This is a new connection */
@@ -853,10 +897,11 @@ static int process_socket(apr_thread_t *thd, apr_pool_t * p, apr_socket_t * sock
apr_bucket_alloc_destroy(cs->bucket_alloc);
apr_pool_clear(p);
ap_push_pool(worker_queue_info, p);
- return 1;
+ return;
}
apr_atomic_inc32(&connection_count);
- apr_pool_cleanup_register(c->pool, NULL, decrement_connection_count, apr_pool_cleanup_null);
+ apr_pool_cleanup_register(c->pool, cs, decrement_connection_count,
+ apr_pool_cleanup_null);
c->current_thread = thd;
cs->c = c;
c->cs = &(cs->pub);
@@ -905,10 +950,12 @@ static int process_socket(apr_thread_t *thd, apr_pool_t * p, apr_socket_t * sock
* like mod_ssl, lets just do the normal read from input filters,
* like the Worker MPM does.
*/
+ apr_atomic_inc32(&clogged_count);
ap_run_process_connection(c);
if (cs->pub.state != CONN_STATE_SUSPENDED) {
cs->pub.state = CONN_STATE_LINGER;
}
+ apr_atomic_dec32(&clogged_count);
}
read_request:
@@ -950,7 +997,7 @@ read_request:
cs->pfd.reqevents = APR_POLLOUT | APR_POLLHUP | APR_POLLERR;
rc = apr_pollset_add(event_pollset, &cs->pfd);
apr_thread_mutex_unlock(timeout_mutex);
- return 1;
+ return;
}
else if (c->keepalive != AP_CONN_KEEPALIVE || c->aborted ||
listener_may_exit) {
@@ -966,8 +1013,8 @@ read_request:
}
if (cs->pub.state == CONN_STATE_LINGER) {
- if (!start_lingering_close(cs))
- return 0;
+ if (!start_lingering_close_blocking(cs))
+ return;
}
else if (cs->pub.state == CONN_STATE_CHECK_REQUEST_LINE_READABLE) {
apr_status_t rc;
@@ -996,19 +1043,33 @@ read_request:
AP_DEBUG_ASSERT(rc == APR_SUCCESS);
}
}
- return 1;
+ else if (cs->pub.state == CONN_STATE_SUSPENDED) {
+ apr_atomic_inc32(&suspended_count);
+ }
+ /*
+ * Prevent this connection from writing to our connection state after it
+ * is no longer associated with this thread. This would happen if the EOR
+ * bucket is destroyed from the listener thread due to a connection abort
+ * or timeout.
+ */
+ c->sbh = NULL;
+
+ return;
}
-/* requests_this_child has gone to zero or below. See if the admin coded
+/* conns_this_child has gone to zero or below. See if the admin coded
"MaxConnectionsPerChild 0", and keep going in that case. Doing it this way
simplifies the hot path in worker_thread */
static void check_infinite_requests(void)
{
if (ap_max_requests_per_child) {
+ ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, ap_server_conf,
+ "Stopping process due to MaxConnectionsPerChild");
signal_threads(ST_GRACEFUL);
}
else {
- requests_this_child = INT_MAX; /* keep going */
+ /* keep going */
+ conns_this_child = APR_INT32_MAX;
}
}
@@ -1354,9 +1415,8 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy)
break;
}
- if (requests_this_child <= 0) {
+ if (conns_this_child <= 0)
check_infinite_requests();
- }
now = apr_time_now();
if (APLOGtrace6(ap_server_conf)) {
@@ -1365,11 +1425,14 @@ 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: %d (write-completion: %d "
- "keep-alive: %d lingering: %d)",
- connection_count, write_completion_q.count,
+ "connections: %u (clogged: %u write-completion: %d "
+ "keep-alive: %d lingering: %d suspended: %u)",
+ apr_atomic_read32(&connection_count),
+ apr_atomic_read32(&clogged_count),
+ write_completion_q.count,
keepalive_q.count,
- linger_q.count + short_linger_q.count);
+ apr_atomic_read32(&lingering_count),
+ apr_atomic_read32(&suspended_count));
apr_thread_mutex_unlock(timeout_mutex);
}
}
@@ -1457,7 +1520,7 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy)
ap_log_error(APLOG_MARK, APLOG_ERR, rc, ap_server_conf,
"pollset remove failed");
apr_thread_mutex_unlock(timeout_mutex);
- start_lingering_close(cs);
+ start_lingering_close_nonblocking(cs);
break;
}
@@ -1468,7 +1531,7 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy)
* re-connect to a different process.
*/
if (!have_idle_worker) {
- start_lingering_close(cs);
+ start_lingering_close_nonblocking(cs);
break;
}
rc = push2worker(out_pfd, event_pollset);
@@ -1502,9 +1565,11 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy)
"All workers busy, not accepting new conns"
"in this process");
}
- else if (apr_atomic_read32(&connection_count) > threads_per_child
- + ap_queue_info_get_idlers(worker_queue_info) *
- worker_factor / WORKER_FACTOR_SCALE)
+ else if ( (int)apr_atomic_read32(&connection_count)
+ - (int)apr_atomic_read32(&lingering_count)
+ > threads_per_child
+ + ap_queue_info_get_idlers(worker_queue_info) *
+ worker_factor / WORKER_FACTOR_SCALE)
{
if (!listeners_disabled)
disable_listensocks(process_slot);
@@ -1559,6 +1624,7 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy)
}
if (csd != NULL) {
+ conns_this_child--;
rc = ap_queue_push(worker_queue, csd, NULL, ptrans);
if (rc != APR_SUCCESS) {
/* trash the connection; we couldn't queue the connected
@@ -1608,14 +1674,15 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy)
keepalive_q.count);
process_timeout_queue(&keepalive_q,
timeout_time + ap_server_conf->keep_alive_timeout,
- start_lingering_close);
+ start_lingering_close_nonblocking);
}
else {
process_timeout_queue(&keepalive_q, timeout_time,
- start_lingering_close);
+ start_lingering_close_nonblocking);
}
/* Step 2: write completion timeouts */
- process_timeout_queue(&write_completion_q, timeout_time, start_lingering_close);
+ process_timeout_queue(&write_completion_q, timeout_time,
+ start_lingering_close_nonblocking);
/* Step 3: (normal) lingering close completion timeouts */
process_timeout_queue(&linger_q, timeout_time, stop_lingering_close);
/* Step 4: (short) lingering close completion timeouts */
@@ -1623,17 +1690,18 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy)
ps = ap_get_scoreboard_process(process_slot);
ps->write_completion = write_completion_q.count;
- ps->lingering_close = linger_q.count + short_linger_q.count;
ps->keep_alive = keepalive_q.count;
apr_thread_mutex_unlock(timeout_mutex);
ps->connections = apr_atomic_read32(&connection_count);
- /* XXX: should count CONN_STATE_SUSPENDED and set ps->suspended */
- }
- if (listeners_disabled && !workers_were_busy &&
- (int)apr_atomic_read32(&connection_count) <
- ((int)ap_queue_info_get_idlers(worker_queue_info) - 1) *
- worker_factor / WORKER_FACTOR_SCALE + threads_per_child)
+ ps->suspended = apr_atomic_read32(&suspended_count);
+ ps->lingering_close = apr_atomic_read32(&lingering_count);
+ }
+ if (listeners_disabled && !workers_were_busy
+ && (int)apr_atomic_read32(&connection_count)
+ - (int)apr_atomic_read32(&lingering_count)
+ < ((int)ap_queue_info_get_idlers(worker_queue_info) - 1)
+ * worker_factor / WORKER_FACTOR_SCALE + threads_per_child)
{
listeners_disabled = 0;
enable_listensocks(process_slot);
@@ -1741,10 +1809,7 @@ static void *APR_THREAD_FUNC worker_thread(apr_thread_t * thd, void *dummy)
else {
is_idle = 0;
worker_sockets[thread_slot] = csd;
- rv = process_socket(thd, ptrans, csd, cs, process_slot, thread_slot);
- if (!rv) {
- requests_this_child--;
- }
+ process_socket(thd, ptrans, csd, cs, process_slot, thread_slot);
worker_sockets[thread_slot] = NULL;
}
}
@@ -2041,11 +2106,11 @@ static void child_main(int child_num_arg)
}
if (ap_max_requests_per_child) {
- requests_this_child = ap_max_requests_per_child;
+ conns_this_child = ap_max_requests_per_child;
}
else {
/* coding a value of zero means infinity */
- requests_this_child = INT_MAX;
+ conns_this_child = APR_INT32_MAX;
}
/* Setup worker threads */
@@ -2264,6 +2329,7 @@ static void perform_idle_server_maintenance(void)
int any_dying_threads = 0;
int any_dead_threads = 0;
int all_dead_threads = 1;
+ int child_threads_active = 0;
if (i >= retained->max_daemons_limit
&& totally_free_length == retained->idle_spawn_rate)
@@ -2298,10 +2364,11 @@ static void perform_idle_server_maintenance(void)
++idle_thread_count;
}
if (status >= SERVER_READY && status < SERVER_GRACEFUL) {
- ++active_thread_count;
+ ++child_threads_active;
}
}
}
+ active_thread_count += child_threads_active;
if (any_dead_threads
&& totally_free_length < retained->idle_spawn_rate
&& free_length < MAX_SPAWN_RATE
@@ -2325,6 +2392,9 @@ static void perform_idle_server_maintenance(void)
}
++free_length;
}
+ else if (child_threads_active == threads_per_child) {
+ had_healthy_child = 1;
+ }
/* XXX if (!ps->quiescing) is probably more reliable GLA */
if (!any_dying_threads) {
last_non_dead = i;
@@ -2333,21 +2403,23 @@ static void perform_idle_server_maintenance(void)
}
if (retained->sick_child_detected) {
- if (active_thread_count > 0) {
- /* some child processes appear to be working. don't kill the
- * whole server.
+ if (had_healthy_child) {
+ /* Assume this is a transient error, even though it may not be. Leave
+ * the server up in case it is able to serve some requests or the
+ * problem will be resolved.
*/
retained->sick_child_detected = 0;
}
else {
- /* looks like a basket case. give up.
+ /* looks like a basket case, as no child ever fully initialized; give up.
*/
shutdown_pending = 1;
child_fatal = 1;
ap_log_error(APLOG_MARK, APLOG_ALERT, 0,
- ap_server_conf, APLOGNO(00483)
- "No active workers found..."
- " Apache is exiting!");
+ ap_server_conf, APLOGNO(02324)
+ "A resource shortage or other unrecoverable failure "
+ "was encountered before any child process initialized "
+ "successfully... httpd is exiting!");
/* the child already logged the failure details */
return;
}
@@ -2480,6 +2552,11 @@ static void server_main_loop(int remaining_children_to_start)
event_note_child_killed(-1, /* already out of the scoreboard */
pid.pid, old_gen);
+ if (processed_status == APEXIT_CHILDSICK
+ && old_gen == retained->my_generation) {
+ /* resource shortage, minimize the fork rate */
+ retained->idle_spawn_rate = 1;
+ }
#if APR_HAS_OTHER_CHILD
}
else if (apr_proc_other_child_alert(&pid, APR_OC_REASON_DEATH,
@@ -2795,6 +2872,7 @@ static int event_pre_config(apr_pool_t * pconf, apr_pool_t * plog,
ap_daemons_limit = server_limit;
threads_per_child = DEFAULT_THREADS_PER_CHILD;
max_workers = ap_daemons_limit * threads_per_child;
+ had_healthy_child = 0;
ap_extended_status = 0;
return OK;
diff --git a/server/mpm/prefork/prefork.c b/server/mpm/prefork/prefork.c
index 5de88e32..d0cf59b4 100644
--- a/server/mpm/prefork/prefork.c
+++ b/server/mpm/prefork/prefork.c
@@ -180,7 +180,7 @@ static void chdir_for_gprof(void)
}
}
else {
- use_dir = ap_server_root_relative(pconf, DEFAULT_REL_RUNTIMEDIR);
+ use_dir = ap_runtime_dir_relative(pconf, "");
}
chdir(use_dir);
@@ -641,7 +641,7 @@ static void child_main(int child_num_arg)
ap_log_error(APLOG_MARK, APLOG_ERR, status,
ap_server_conf, APLOGNO(00158) "apr_pollset_poll: (listen)");
SAFE_ACCEPT(accept_mutex_off());
- clean_child_exit(1);
+ clean_child_exit(APEXIT_CHILDSICK);
}
/* We can always use pdesc[0], but sockets at position N
@@ -678,7 +678,7 @@ static void child_main(int child_num_arg)
if (status == APR_EGENERAL) {
/* resource shortage or should-not-occur occured */
- clean_child_exit(1);
+ clean_child_exit(APEXIT_CHILDSICK);
}
else if (status != APR_SUCCESS) {
continue;
diff --git a/server/mpm/winnt/child.c b/server/mpm/winnt/child.c
index 2e42071f..ae34b07b 100644
--- a/server/mpm/winnt/child.c
+++ b/server/mpm/winnt/child.c
@@ -352,9 +352,8 @@ reinit: /* target of data or connect upon too many AcceptEx failures */
}
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(00334)
- "Child: Accept thread listening on %s:%d using AcceptFilter %s",
- lr->bind_addr->hostname ? lr->bind_addr->hostname : "*",
- lr->bind_addr->port, accf_name);
+ "Child: Accept thread listening on %pI using AcceptFilter %s",
+ lr->bind_addr, accf_name);
while (!shutdown_in_progress) {
if (!context) {
@@ -650,11 +649,12 @@ reinit: /* target of data or connect upon too many AcceptEx failures */
}
}
- sockinfo.os_sock = &context->accept_socket;
- sockinfo.local = context->sa_server;
- sockinfo.remote = context->sa_client;
- sockinfo.family = context->sa_server->sa_family;
- sockinfo.type = SOCK_STREAM;
+ sockinfo.os_sock = &context->accept_socket;
+ sockinfo.local = context->sa_server;
+ sockinfo.remote = context->sa_client;
+ sockinfo.family = context->sa_server->sa_family;
+ sockinfo.type = SOCK_STREAM;
+ sockinfo.protocol = IPPROTO_TCP;
/* Restore the state corresponding to apr_os_sock_make's default
* assumption of timeout -1 (really, a flaw of os_sock_make and
* os_sock_put that it does not query to determine ->timeout).
diff --git a/server/mpm/worker/worker.c b/server/mpm/worker/worker.c
index 75f15ebd..548fcaec 100644
--- a/server/mpm/worker/worker.c
+++ b/server/mpm/worker/worker.c
@@ -122,6 +122,7 @@ static int ap_daemons_limit = 0;
static int max_workers = 0;
static int server_limit = 0;
static int thread_limit = 0;
+static int had_healthy_child = 0;
static int dying = 0;
static int workers_may_exit = 0;
static int start_thread_may_exit = 0;
@@ -1473,6 +1474,7 @@ static void perform_idle_server_maintenance(void)
int any_dying_threads = 0;
int any_dead_threads = 0;
int all_dead_threads = 1;
+ int child_threads_active = 0;
if (i >= retained->max_daemons_limit && totally_free_length == retained->idle_spawn_rate)
/* short cut if all active processes have been examined and
@@ -1506,10 +1508,11 @@ static void perform_idle_server_maintenance(void)
++idle_thread_count;
}
if (status >= SERVER_READY && status < SERVER_GRACEFUL) {
- ++active_thread_count;
+ ++child_threads_active;
}
}
}
+ active_thread_count += child_threads_active;
if (any_dead_threads && totally_free_length < retained->idle_spawn_rate
&& free_length < MAX_SPAWN_RATE
&& (!ps->pid /* no process in the slot */
@@ -1532,6 +1535,9 @@ static void perform_idle_server_maintenance(void)
}
++free_length;
}
+ else if (child_threads_active == threads_per_child) {
+ had_healthy_child = 1;
+ }
/* XXX if (!ps->quiescing) is probably more reliable GLA */
if (!any_dying_threads) {
last_non_dead = i;
@@ -1540,21 +1546,23 @@ static void perform_idle_server_maintenance(void)
}
if (retained->sick_child_detected) {
- if (active_thread_count > 0) {
- /* some child processes appear to be working. don't kill the
- * whole server.
+ if (had_healthy_child) {
+ /* Assume this is a transient error, even though it may not be. Leave
+ * the server up in case it is able to serve some requests or the
+ * problem will be resolved.
*/
retained->sick_child_detected = 0;
}
else {
- /* looks like a basket case. give up.
+ /* looks like a basket case, as no child ever fully initialized; give up.
*/
shutdown_pending = 1;
child_fatal = 1;
ap_log_error(APLOG_MARK, APLOG_ALERT, 0,
- ap_server_conf, APLOGNO(00285)
- "No active workers found..."
- " Apache is exiting!");
+ ap_server_conf, APLOGNO(02325)
+ "A resource shortage or other unrecoverable failure "
+ "was encountered before any child process initialized "
+ "successfully... httpd is exiting!");
/* the child already logged the failure details */
return;
}
@@ -1699,6 +1707,11 @@ static void server_main_loop(int remaining_children_to_start)
else if (ap_unregister_extra_mpm_process(pid.pid, &old_gen) == 1) {
worker_note_child_killed(-1, /* already out of the scoreboard */
pid.pid, old_gen);
+ if (processed_status == APEXIT_CHILDSICK
+ && old_gen == retained->my_generation) {
+ /* resource shortage, minimize the fork rate */
+ retained->idle_spawn_rate = 1;
+ }
#if APR_HAS_OTHER_CHILD
}
else if (apr_proc_other_child_alert(&pid, APR_OC_REASON_DEATH,
@@ -2015,6 +2028,7 @@ static int worker_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
ap_daemons_limit = server_limit;
threads_per_child = DEFAULT_THREADS_PER_CHILD;
max_workers = ap_daemons_limit * threads_per_child;
+ had_healthy_child = 0;
ap_extended_status = 0;
return OK;
diff --git a/server/mpm_unix.c b/server/mpm_unix.c
index 87df8f57..65c09407 100644
--- a/server/mpm_unix.c
+++ b/server/mpm_unix.c
@@ -501,14 +501,14 @@ static apr_status_t pod_signal_internal(ap_pod_t *pod)
return rv;
}
-/* This function connects to the server, then immediately closes the connection.
- * This permits the MPM to skip the poll when there is only one listening
- * socket, because it provides a alternate way to unblock an accept() when
- * the pod is used.
- */
+/* This function connects to the server and sends enough data to
+ * ensure the child wakes up and processes a new connection. This
+ * permits the MPM to skip the poll when there is only one listening
+ * socket, because it provides a alternate way to unblock an accept()
+ * when the pod is used. */
static apr_status_t dummy_connection(ap_pod_t *pod)
{
- char *srequest;
+ const char *data;
apr_status_t rv;
apr_socket_t *sock;
apr_pool_t *p;
@@ -574,24 +574,37 @@ static apr_status_t dummy_connection(ap_pod_t *pod)
return rv;
}
- /* Create the request string. We include a User-Agent so that
- * adminstrators can track down the cause of the odd-looking
- * requests in their logs.
- */
- srequest = apr_pstrcat(p, "OPTIONS * HTTP/1.0\r\nUser-Agent: ",
+ if (lp->protocol && strcasecmp(lp->protocol, "https") == 0) {
+ /* Send a TLS 1.0 close_notify alert. This is perhaps the
+ * "least wrong" way to open and cleanly terminate an SSL
+ * connection. It should "work" without noisy error logs if
+ * the server actually expects SSLv3/TLSv1. With
+ * SSLv23_server_method() OpenSSL's SSL_accept() fails
+ * ungracefully on receipt of this message, since it requires
+ * an 11-byte ClientHello message and this is too short. */
+ static const unsigned char tls10_close_notify[7] = {
+ '\x15', /* TLSPlainText.type = Alert (21) */
+ '\x03', '\x01', /* TLSPlainText.version = {3, 1} */
+ '\x00', '\x02', /* TLSPlainText.length = 2 */
+ '\x01', /* Alert.level = warning (1) */
+ '\x00' /* Alert.description = close_notify (0) */
+ };
+ data = (const char *)tls10_close_notify;
+ len = sizeof(tls10_close_notify);
+ }
+ else /* ... XXX other request types here? */ {
+ /* Create an HTTP request string. We include a User-Agent so
+ * that adminstrators can track down the cause of the
+ * odd-looking requests in their logs. A complete request is
+ * used since kernel-level filtering may require that much
+ * data before returning from accept(). */
+ data = apr_pstrcat(p, "OPTIONS * HTTP/1.0\r\nUser-Agent: ",
ap_get_server_description(),
" (internal dummy connection)\r\n\r\n", NULL);
+ len = strlen(data);
+ }
- /* Since some operating systems support buffering of data or entire
- * requests in the kernel, we send a simple request, to make sure
- * the server pops out of a blocking accept().
- */
- /* XXX: This is HTTP specific. We should look at the Protocol for each
- * listener, and send the correct type of request to trigger any Accept
- * Filters.
- */
- len = strlen(srequest);
- apr_socket_send(sock, srequest, &len);
+ apr_socket_send(sock, data, &len);
apr_socket_close(sock);
apr_pool_destroy(p);
diff --git a/server/protocol.c b/server/protocol.c
index 4fcff4da..30b3cd5e 100644
--- a/server/protocol.c
+++ b/server/protocol.c
@@ -746,19 +746,29 @@ AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r, apr_bucket_brigade *bb
* finding the end-of-line. This is only going to happen if it
* exceeds the configured limit for a field size.
*/
- if (rv == APR_ENOSPC && field) {
- /* ensure ap_escape_html will terminate correctly */
- field[len - 1] = '\0';
+ if (rv == APR_ENOSPC) {
+ const char *field_escaped;
+ if (field) {
+ /* ensure ap_escape_html will terminate correctly */
+ field[len - 1] = '\0';
+ field_escaped = ap_escape_html(r->pool, field);
+ }
+ else {
+ field_escaped = field = "";
+ }
+
apr_table_setn(r->notes, "error-notes",
apr_psprintf(r->pool,
"Size of a request header field "
"exceeds server limit.<br />\n"
"<pre>\n%.*s\n</pre>\n",
- field_name_len(field),
- ap_escape_html(r->pool, field)));
+ field_name_len(field_escaped),
+ field_escaped));
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00561)
- "Request header exceeds LimitRequestFieldSize: "
- "%.*s", field_name_len(field), field);
+ "Request header exceeds LimitRequestFieldSize%s"
+ "%.*s",
+ *field ? ": " : "",
+ field_name_len(field), field);
}
return;
}
diff --git a/server/provider.c b/server/provider.c
index a5406ab1..a102dd85 100644
--- a/server/provider.c
+++ b/server/provider.c
@@ -136,7 +136,7 @@ AP_DECLARE(apr_array_header_t *) ap_list_provider_names(apr_pool_t *pool,
ap_list_provider_names_t *entry;
apr_hash_t *provider_group_hash, *h;
apr_hash_index_t *hi;
- char *val, *key;
+ char *val;
if (global_providers_names == NULL) {
return ret;
@@ -157,9 +157,41 @@ AP_DECLARE(apr_array_header_t *) ap_list_provider_names(apr_pool_t *pool,
}
for (hi = apr_hash_first(pool, h); hi; hi = apr_hash_next(hi)) {
- apr_hash_this(hi, (void *)&key, NULL, (void *)&val);
+ apr_hash_this(hi, NULL, NULL, (void *)&val);
entry = apr_array_push(ret);
entry->provider_name = apr_pstrdup(pool, val);
}
return ret;
}
+
+AP_DECLARE(apr_array_header_t *) ap_list_provider_groups(apr_pool_t *pool)
+{
+ apr_array_header_t *ret = apr_array_make(pool, 10, sizeof(ap_list_provider_groups_t));
+ ap_list_provider_groups_t *entry;
+ apr_hash_t *provider_group_hash;
+ apr_hash_index_t *groups_hi, *vers_hi;
+ char *group, *version;
+
+ if (global_providers_names == NULL)
+ return ret;
+
+ for (groups_hi = apr_hash_first(pool, global_providers_names);
+ groups_hi;
+ groups_hi = apr_hash_next(groups_hi))
+ {
+ apr_hash_this(groups_hi, (void *)&group, NULL, (void *)&provider_group_hash);
+ if (provider_group_hash == NULL)
+ continue;
+ for (vers_hi = apr_hash_first(pool, provider_group_hash);
+ vers_hi;
+ vers_hi = apr_hash_next(vers_hi))
+ {
+ apr_hash_this(vers_hi, (void *)&version, NULL, NULL);
+
+ entry = apr_array_push(ret);
+ entry->provider_group = group;
+ entry->provider_version = version;
+ }
+ }
+ return ret;
+}
diff --git a/server/request.c b/server/request.c
index c2fdd89e..d26d4691 100644
--- a/server/request.c
+++ b/server/request.c
@@ -236,7 +236,9 @@ AP_DECLARE(int) ap_process_request_internal(request_rec *r)
if (r->user == NULL) {
/* don't let buggy authn module crash us in authz */
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00027)
- "Buggy authn provider failed to set user for %s",
+ "No authentication done but request not "
+ "allowed without authentication for %s. "
+ "Authentication not configured?",
r->uri);
access_status = HTTP_INTERNAL_SERVER_ERROR;
return decl_die(access_status, "check user", r);
@@ -271,7 +273,9 @@ AP_DECLARE(int) ap_process_request_internal(request_rec *r)
if (r->user == NULL) {
/* don't let buggy authn module crash us in authz */
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00028)
- "Buggy authn provider failed to set user for %s",
+ "No authentication done but request not "
+ "allowed without authentication for %s. "
+ "Authentication not configured?",
r->uri);
access_status = HTTP_INTERNAL_SERVER_ERROR;
return decl_die(access_status, "check user", r);
@@ -750,7 +754,7 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
/* Set aside path_info to merge back onto path_info later.
* If r->filename is a directory, we must remerge the path_info,
- * before we continue! [Directories cannot, by defintion, have
+ * before we continue! [Directories cannot, by definition, have
* path info. Either the next segment is not-found, or a file.]
*
* r->path_info tracks the unconsumed source path.
diff --git a/server/scoreboard.c b/server/scoreboard.c
index 344dc5ae..bef2b909 100644
--- a/server/scoreboard.c
+++ b/server/scoreboard.c
@@ -488,8 +488,9 @@ static int update_child_status_internal(int child_num,
REMOTE_NOLOOKUP, NULL), sizeof(ws->client));
copy_request(ws->request, sizeof(ws->request), r);
if (r->server) {
- apr_cpystrn(ws->vhost, r->server->server_hostname,
- sizeof(ws->vhost));
+ apr_snprintf(ws->vhost, sizeof(ws->vhost), "%s:%d",
+ r->server->server_hostname,
+ r->connection->local_addr->port);
}
}
else if (c) {
diff --git a/server/util.c b/server/util.c
index 177f3780..75e91a7b 100644
--- a/server/util.c
+++ b/server/util.c
@@ -2393,7 +2393,7 @@ AP_DECLARE(int) ap_parse_form_data(request_rec *r, ap_filter_t *f,
/* sanity check - we only support forms for now */
ct = apr_table_get(r->headers_in, "Content-Type");
- if (!ct || strcmp("application/x-www-form-urlencoded", ct)) {
+ if (!ct || strncasecmp("application/x-www-form-urlencoded", ct, 33)) {
return ap_discard_request_body(r);
}
diff --git a/server/util_mutex.c b/server/util_mutex.c
index 0ff9483d..e49cca55 100644
--- a/server/util_mutex.c
+++ b/server/util_mutex.c
@@ -157,7 +157,7 @@ AP_DECLARE_NONSTD(void) ap_mutex_init(apr_pool_t *p)
/* initialize default mutex configuration */
def = apr_pcalloc(p, sizeof *def);
def->mech = APR_LOCK_DEFAULT;
- def->dir = DEFAULT_REL_RUNTIMEDIR;
+ def->dir = ap_runtime_dir_relative(p, "");
apr_hash_set(mxcfg_by_type, "default", APR_HASH_KEY_STRING, def);
}
diff --git a/server/util_script.c b/server/util_script.c
index 18c4aea4..703d1600 100644
--- a/server/util_script.c
+++ b/server/util_script.c
@@ -553,7 +553,7 @@ AP_DECLARE(int) ap_scan_script_header_err_core_ex(request_rec *r, char *buffer,
if (!(l = strchr(w, ':'))) {
if (!buffer) {
/* Soak up all the script output - may save an outright kill */
- while ((*getsfunc) (w, MAX_STRING_LEN - 1, getsfunc_data)) {
+ while ((*getsfunc)(w, MAX_STRING_LEN - 1, getsfunc_data) > 0) {
continue;
}
}
@@ -592,11 +592,11 @@ AP_DECLARE(int) ap_scan_script_header_err_core_ex(request_rec *r, char *buffer,
if (!ap_is_HTTP_VALID_RESPONSE(cgi_status))
ap_log_rerror(SCRIPT_LOG_MARK, APLOG_ERR|APLOG_TOCLIENT, 0, r,
"Invalid status line from script '%s': %s",
- apr_filepath_name_get(r->filename), w);
+ apr_filepath_name_get(r->filename), l);
else
ap_log_rerror(SCRIPT_LOG_MARK, APLOG_TRACE1, 0, r,
"Status line from script '%s': %s",
- apr_filepath_name_get(r->filename), w);
+ apr_filepath_name_get(r->filename), l);
r->status_line = apr_pstrdup(r->pool, l);
}
else if (!strcasecmp(w, "Location")) {
@@ -672,7 +672,8 @@ static int getsfunc_BRIGADE(char *buf, int len, void *arg)
apr_status_t rv;
int done = 0;
- while ((dst < dst_end) && !done && !APR_BUCKET_IS_EOS(e)) {
+ while ((dst < dst_end) && !done && e != APR_BRIGADE_SENTINEL(bb)
+ && !APR_BUCKET_IS_EOS(e)) {
const char *bucket_data;
apr_size_t bucket_data_len;
const char *src;
@@ -706,7 +707,7 @@ static int getsfunc_BRIGADE(char *buf, int len, void *arg)
e = next;
}
*dst = 0;
- return 1;
+ return done;
}
AP_DECLARE(int) ap_scan_script_header_err_brigade(request_rec *r,