summaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
Diffstat (limited to 'server')
-rw-r--r--server/Makefile.in2
-rw-r--r--server/NWGNUmakefile2
-rw-r--r--server/buildmark.c2
-rw-r--r--server/config.c263
-rw-r--r--server/connection.c28
-rw-r--r--server/core.c487
-rw-r--r--server/core_filters.c749
-rw-r--r--server/eor_bucket.c43
-rw-r--r--server/gen_test_char.c25
-rw-r--r--server/listen.c2
-rw-r--r--server/log.c94
-rw-r--r--server/main.c46
-rw-r--r--server/mpm/event/event.c934
-rw-r--r--server/mpm/mpmt_os2/mpmt_os2.c8
-rw-r--r--server/mpm/mpmt_os2/mpmt_os2_child.c1
-rw-r--r--server/mpm/netware/mpm_netware.c1
-rw-r--r--server/mpm/prefork/prefork.c88
-rw-r--r--server/mpm/winnt/child.c27
-rw-r--r--server/mpm/winnt/mpm_winnt.c23
-rw-r--r--server/mpm/winnt/nt_eventlog.c1
-rw-r--r--server/mpm/winnt/service.c8
-rw-r--r--server/mpm/worker/worker.c125
-rw-r--r--server/mpm_common.c6
-rw-r--r--server/mpm_fdqueue.c4
-rw-r--r--server/mpm_fdqueue.h2
-rw-r--r--server/mpm_unix.c23
-rw-r--r--server/protocol.c453
-rw-r--r--server/provider.c21
-rw-r--r--server/request.c149
-rw-r--r--server/scoreboard.c18
-rw-r--r--server/ssl.c285
-rw-r--r--server/util.c737
-rw-r--r--server/util_expr_eval.c51
-rw-r--r--server/util_expr_parse.c2
-rw-r--r--server/util_filter.c5
-rw-r--r--server/util_md5.c8
-rw-r--r--server/util_pcre.c334
-rw-r--r--server/util_regex.c3
-rw-r--r--server/util_script.c131
-rw-r--r--server/util_time.c35
-rw-r--r--server/util_xml.c2
-rw-r--r--server/vhost.c41
42 files changed, 3487 insertions, 1782 deletions
diff --git a/server/Makefile.in b/server/Makefile.in
index 1fa3344..8111877 100644
--- a/server/Makefile.in
+++ b/server/Makefile.in
@@ -13,7 +13,7 @@ LTLIBRARY_SOURCES = \
mpm_common.c mpm_unix.c mpm_fdqueue.c \
util_charset.c util_cookies.c util_debug.c util_xml.c \
util_filter.c util_pcre.c util_regex.c exports.c \
- scoreboard.c error_bucket.c protocol.c core.c request.c provider.c \
+ scoreboard.c error_bucket.c protocol.c core.c request.c ssl.c provider.c \
eoc_bucket.c eor_bucket.c core_filters.c \
util_expr_parse.c util_expr_scan.c util_expr_eval.c
diff --git a/server/NWGNUmakefile b/server/NWGNUmakefile
index 7f96e81..4917811 100644
--- a/server/NWGNUmakefile
+++ b/server/NWGNUmakefile
@@ -225,7 +225,7 @@ FILES_lib_objs = \
$(EOLIST)
#
-# implement targets and dependancies (leave this section alone)
+# implement targets and dependencies (leave this section alone)
#
libs :: $(OBJDIR) $(TARGET_lib)
diff --git a/server/buildmark.c b/server/buildmark.c
index a9cd684..36bd713 100644
--- a/server/buildmark.c
+++ b/server/buildmark.c
@@ -23,7 +23,7 @@ static const char server_built[] = __DATE__ " " __TIME__;
static const char server_built[] = "unknown";
#endif
-AP_DECLARE(const char *) ap_get_server_built()
+AP_DECLARE(const char *) ap_get_server_built(void)
{
return server_built;
}
diff --git a/server/config.c b/server/config.c
index f815b22..3d11ff5 100644
--- a/server/config.c
+++ b/server/config.c
@@ -59,7 +59,6 @@
AP_DECLARE_DATA const char *ap_server_argv0 = NULL;
AP_DECLARE_DATA const char *ap_server_root = NULL;
-AP_DECLARE_DATA const char *ap_runtime_dir = NULL;
AP_DECLARE_DATA server_rec *ap_server_conf = NULL;
AP_DECLARE_DATA apr_pool_t *ap_pglobal = NULL;
@@ -1038,11 +1037,11 @@ static const char *invoke_cmd(const command_rec *cmd, cmd_parms *parms,
*/
w = ap_getword_conf(parms->temp_pool, &args);
- if (*w == '\0' || (strcasecmp(w, "on") && strcasecmp(w, "off")))
+ if (*w == '\0' || (ap_cstr_casecmp(w, "on") && ap_cstr_casecmp(w, "off")))
return apr_pstrcat(parms->pool, cmd->name, " must be On or Off",
NULL);
- return cmd->AP_FLAG(parms, mconfig, strcasecmp(w, "off") != 0);
+ return cmd->AP_FLAG(parms, mconfig, ap_cstr_casecmp(w, "off") != 0);
default:
return apr_pstrcat(parms->pool, cmd->name,
@@ -1055,7 +1054,7 @@ AP_CORE_DECLARE(const command_rec *) ap_find_command(const char *name,
const command_rec *cmds)
{
while (cmds->name) {
- if (!strcasecmp(name, cmds->name))
+ if (!ap_cstr_casecmp(name, cmds->name))
return cmds;
++cmds;
@@ -1118,8 +1117,9 @@ static const char *ap_build_config_sub(apr_pool_t *p, apr_pool_t *temp_pool,
const char *args;
char *cmd_name;
ap_directive_t *newdir;
- module *mod = ap_top_module;
const command_rec *cmd;
+ ap_mod_list *ml;
+ char *lname;
if (*l == '#' || *l == '\0')
return NULL;
@@ -1157,9 +1157,12 @@ static const char *ap_build_config_sub(apr_pool_t *p, apr_pool_t *temp_pool,
newdir->line_num = parms->config_file->line_number;
newdir->args = apr_pstrdup(p, args);
- if ((cmd = ap_find_command_in_modules(cmd_name, &mod)) != NULL) {
+ lname = apr_pstrdup(temp_pool, cmd_name);
+ ap_str_tolower(lname);
+ ml = apr_hash_get(ap_config_hash, lname, APR_HASH_KEY_STRING);
+
+ if (ml && (cmd = ml->cmd) != NULL) {
newdir->directive = cmd->name;
-
if (cmd->req_override & EXEC_ON_READ) {
ap_directive_t *sub_tree = NULL;
@@ -1220,7 +1223,7 @@ static const char *ap_build_config_sub(apr_pool_t *p, apr_pool_t *temp_pool,
*bracket = '\0';
- if (strcasecmp(cmd_name + 2,
+ if (ap_cstr_casecmp(cmd_name + 2,
(*curr_parent)->directive + 1) != 0) {
parms->err_directive = newdir;
return apr_pstrcat(p, "Expected </",
@@ -1267,7 +1270,7 @@ AP_DECLARE(const char *) ap_build_cont_config(apr_pool_t *p,
while ((rc = ap_varbuf_cfg_getline(&vb, parms->config_file, max_len))
== APR_SUCCESS) {
if (!memcmp(vb.buf, "</", 2)
- && (strcasecmp(vb.buf + 2, bracket) == 0)
+ && (ap_cstr_casecmp(vb.buf + 2, bracket) == 0)
&& (*curr_parent == NULL)) {
break;
}
@@ -1542,8 +1545,8 @@ AP_DECLARE_NONSTD(const char *) ap_set_file_slot(cmd_parms *cmd, void *struct_pt
path = ap_server_root_relative(cmd->pool, arg);
if (!path) {
- return apr_pstrcat(cmd->pool, "Invalid file path ",
- arg, NULL);
+ return apr_pstrcat(cmd->pool, cmd->cmd->name, ": Invalid file path '",
+ arg, "'", NULL);
}
*(const char **) ((char*)struct_ptr + offset) = path;
@@ -1644,7 +1647,7 @@ AP_DECLARE(const char *) ap_soak_end_container(cmd_parms *cmd, char *directive)
if (cmd_name[1] == '/') {
cmd_name[strlen(cmd_name) - 1] = '\0';
- if (strcasecmp(cmd_name + 2, directive + 1) != 0) {
+ if (ap_cstr_casecmp(cmd_name + 2, directive + 1) != 0) {
return apr_pstrcat(cmd->pool, "Expected </",
directive + 1, "> but saw ",
cmd_name, ">", NULL);
@@ -1793,18 +1796,6 @@ static const char *process_command_config(server_rec *s,
return NULL;
}
-typedef struct {
- const char *fname;
-} fnames;
-
-static int fname_alphasort(const void *fn1, const void *fn2)
-{
- const fnames *f1 = fn1;
- const fnames *f2 = fn2;
-
- return strcmp(f1->fname,f2->fname);
-}
-
/**
* Used by -D DUMP_INCLUDES to output the config file "tree".
*/
@@ -1897,200 +1888,15 @@ AP_DECLARE(const char *) ap_process_resource_config(server_rec *s,
return NULL;
}
-static const char *process_resource_config_nofnmatch(server_rec *s,
- const char *fname,
- ap_directive_t **conftree,
- apr_pool_t *p,
- apr_pool_t *ptemp,
- unsigned depth,
- int optional)
-{
- const char *error;
- apr_status_t rv;
-
- if (ap_is_directory(ptemp, fname)) {
- apr_dir_t *dirp;
- apr_finfo_t dirent;
- int current;
- apr_array_header_t *candidates = NULL;
- fnames *fnew;
- char *path = apr_pstrdup(ptemp, fname);
-
- if (++depth > AP_MAX_INCLUDE_DIR_DEPTH) {
- return apr_psprintf(p, "Directory %s exceeds the maximum include "
- "directory nesting level of %u. You have "
- "probably a recursion somewhere.", path,
- AP_MAX_INCLUDE_DIR_DEPTH);
- }
-
- /*
- * first course of business is to grok all the directory
- * entries here and store 'em away. Recall we need full pathnames
- * for this.
- */
- rv = apr_dir_open(&dirp, path, ptemp);
- if (rv != APR_SUCCESS) {
- return apr_psprintf(p, "Could not open config directory %s: %pm",
- path, &rv);
- }
-
- candidates = apr_array_make(ptemp, 1, sizeof(fnames));
- while (apr_dir_read(&dirent, APR_FINFO_DIRENT, dirp) == APR_SUCCESS) {
- /* strip out '.' and '..' */
- if (strcmp(dirent.name, ".")
- && strcmp(dirent.name, "..")) {
- fnew = (fnames *) apr_array_push(candidates);
- fnew->fname = ap_make_full_path(ptemp, path, dirent.name);
- }
- }
-
- apr_dir_close(dirp);
- if (candidates->nelts != 0) {
- qsort((void *) candidates->elts, candidates->nelts,
- sizeof(fnames), fname_alphasort);
-
- /*
- * Now recurse these... we handle errors and subdirectories
- * via the recursion, which is nice
- */
- for (current = 0; current < candidates->nelts; ++current) {
- fnew = &((fnames *) candidates->elts)[current];
- error = process_resource_config_nofnmatch(s, fnew->fname,
- conftree, p, ptemp,
- depth, optional);
- if (error) {
- return error;
- }
- }
- }
-
- return NULL;
- }
- else if (optional) {
- /* If the optinal flag is set (like for IncludeOptional) we can
- * tolerate that no file or directory is present and bail out.
- */
- apr_finfo_t finfo;
- if (apr_stat(&finfo, fname, APR_FINFO_TYPE, ptemp) != APR_SUCCESS
- || finfo.filetype == APR_NOFILE)
- return NULL;
- }
-
- return ap_process_resource_config(s, fname, conftree, p, ptemp);
-}
+typedef struct {
+ server_rec *s;
+ ap_directive_t **conftree;
+} configs;
-static const char *process_resource_config_fnmatch(server_rec *s,
- const char *path,
- const char *fname,
- ap_directive_t **conftree,
- apr_pool_t *p,
- apr_pool_t *ptemp,
- unsigned depth,
- int optional)
+static const char *process_resource_config_cb(ap_dir_match_t *w, const char *fname)
{
- const char *rest;
- apr_status_t rv;
- apr_dir_t *dirp;
- apr_finfo_t dirent;
- apr_array_header_t *candidates = NULL;
- fnames *fnew;
- int current;
-
- /* find the first part of the filename */
- rest = ap_strchr_c(fname, '/');
- if (rest) {
- fname = apr_pstrmemdup(ptemp, fname, rest - fname);
- rest++;
- }
-
- /* optimisation - if the filename isn't a wildcard, process it directly */
- if (!apr_fnmatch_test(fname)) {
- path = ap_make_full_path(ptemp, path, fname);
- if (!rest) {
- return process_resource_config_nofnmatch(s, path,
- conftree, p,
- ptemp, 0, optional);
- }
- else {
- return process_resource_config_fnmatch(s, path, rest,
- conftree, p,
- ptemp, 0, optional);
- }
- }
-
- /*
- * first course of business is to grok all the directory
- * entries here and store 'em away. Recall we need full pathnames
- * for this.
- */
- rv = apr_dir_open(&dirp, path, ptemp);
- if (rv != APR_SUCCESS) {
- /* If the directory doesn't exist and the optional flag is set
- * there is no need to return an error.
- */
- if (rv == APR_ENOENT && optional) {
- return NULL;
- }
- return apr_psprintf(p, "Could not open config directory %s: %pm",
- path, &rv);
- }
-
- candidates = apr_array_make(ptemp, 1, sizeof(fnames));
- while (apr_dir_read(&dirent, APR_FINFO_DIRENT | APR_FINFO_TYPE, dirp) == APR_SUCCESS) {
- /* strip out '.' and '..' */
- if (strcmp(dirent.name, ".")
- && strcmp(dirent.name, "..")
- && (apr_fnmatch(fname, dirent.name,
- APR_FNM_PERIOD) == APR_SUCCESS)) {
- const char *full_path = ap_make_full_path(ptemp, path, dirent.name);
- /* If matching internal to path, and we happen to match something
- * other than a directory, skip it
- */
- if (rest && (dirent.filetype != APR_DIR)) {
- continue;
- }
- fnew = (fnames *) apr_array_push(candidates);
- fnew->fname = full_path;
- }
- }
-
- apr_dir_close(dirp);
- if (candidates->nelts != 0) {
- const char *error;
-
- qsort((void *) candidates->elts, candidates->nelts,
- sizeof(fnames), fname_alphasort);
-
- /*
- * Now recurse these... we handle errors and subdirectories
- * via the recursion, which is nice
- */
- for (current = 0; current < candidates->nelts; ++current) {
- fnew = &((fnames *) candidates->elts)[current];
- if (!rest) {
- error = process_resource_config_nofnmatch(s, fnew->fname,
- conftree, p,
- ptemp, 0, optional);
- }
- else {
- error = process_resource_config_fnmatch(s, fnew->fname, rest,
- conftree, p,
- ptemp, 0, optional);
- }
- if (error) {
- return error;
- }
- }
- }
- else {
-
- if (!optional) {
- return apr_psprintf(p, "No matches for the wildcard '%s' in '%s', failing "
- "(use IncludeOptional if required)", fname, path);
- }
- }
-
- return NULL;
+ configs *cfgs = w->ctx;
+ return ap_process_resource_config(cfgs->s, fname, cfgs->conftree, w->p, w->ptemp);
}
AP_DECLARE(const char *) ap_process_fnmatch_configs(server_rec *s,
@@ -2100,8 +1906,19 @@ AP_DECLARE(const char *) ap_process_fnmatch_configs(server_rec *s,
apr_pool_t *ptemp,
int optional)
{
- /* XXX: lstat() won't work on the wildcard pattern...
- */
+ configs cfgs;
+ ap_dir_match_t w;
+
+ cfgs.s = s;
+ cfgs.conftree = conftree;
+
+ w.prefix = "Include/IncludeOptional: ";
+ w.p = p;
+ w.ptemp = ptemp;
+ w.flags = (optional ? AP_DIR_FLAG_OPTIONAL : AP_DIR_FLAG_NONE) | AP_DIR_FLAG_RECURSIVE;
+ w.cb = process_resource_config_cb;
+ w.ctx = &cfgs;
+ w.depth = 0;
/* don't require conf/httpd.conf if we have a -C or -c switch */
if ((ap_server_pre_read_config->nelts
@@ -2114,7 +1931,7 @@ AP_DECLARE(const char *) ap_process_fnmatch_configs(server_rec *s,
}
if (!apr_fnmatch_test(fname)) {
- return process_resource_config_nofnmatch(s, fname, conftree, p, ptemp, 0, optional);
+ return ap_dir_nofnmatch(&w, fname);
}
else {
apr_status_t status;
@@ -2132,8 +1949,7 @@ AP_DECLARE(const char *) ap_process_fnmatch_configs(server_rec *s,
}
/* walk the filepath */
- return process_resource_config_fnmatch(s, rootpath, filepath, conftree, p, ptemp,
- 0, optional);
+ return ap_dir_fnmatch(&w, rootpath, filepath);
}
}
@@ -2485,6 +2301,9 @@ AP_DECLARE(server_rec*) ap_read_config(process_rec *process, apr_pool_t *ptemp,
if (s == NULL) {
return s;
}
+ if (ap_server_conf == NULL) {
+ ap_server_conf = s;
+ }
init_config_globals(p);
@@ -2720,7 +2539,7 @@ static int count_directives_sub(const char *directive, ap_directive_t *current)
while (current != NULL) {
if (current->first_child != NULL)
count += count_directives_sub(directive, current->first_child);
- if (strcasecmp(current->directive, directive) == 0)
+ if (ap_cstr_casecmp(current->directive, directive) == 0)
count++;
current = current->next;
}
diff --git a/server/connection.c b/server/connection.c
index 1974537..bbc94c4 100644
--- a/server/connection.c
+++ b/server/connection.c
@@ -122,9 +122,7 @@ AP_DECLARE(int) ap_start_lingering_close(conn_rec *c)
{
apr_socket_t *csd = ap_get_conn_socket(c);
- if (!csd) {
- return 1;
- }
+ ap_assert(csd != NULL);
if (ap_prep_lingering_close(c)) {
return 1;
@@ -139,18 +137,12 @@ AP_DECLARE(int) ap_start_lingering_close(conn_rec *c)
ap_flush_conn(c);
#ifdef NO_LINGCLOSE
- apr_socket_close(csd);
return 1;
#else
/* Shut down the socket for write, which will send a FIN
* to the peer.
*/
- if (c->aborted
- || apr_socket_shutdown(csd, APR_SHUTDOWN_WRITE) != APR_SUCCESS) {
- apr_socket_close(csd);
- return 1;
- }
- return 0;
+ return (c->aborted || apr_socket_shutdown(csd, APR_SHUTDOWN_WRITE));
#endif
}
@@ -161,7 +153,17 @@ AP_DECLARE(void) ap_lingering_close(conn_rec *c)
apr_time_t now, timeup = 0;
apr_socket_t *csd = ap_get_conn_socket(c);
+ if (!csd) {
+ /* Be safe with third-party modules that:
+ * ap_set_core_module_config(c->conn_config, NULL)
+ * to no-op ap_lingering_close().
+ */
+ c->aborted = 1;
+ return;
+ }
+
if (ap_start_lingering_close(c)) {
+ apr_socket_close(csd);
return;
}
@@ -207,13 +209,9 @@ AP_DECLARE(void) ap_lingering_close(conn_rec *c)
AP_CORE_DECLARE(void) ap_process_connection(conn_rec *c, void *csd)
{
- int rc;
ap_update_vhost_given_ip(c);
- rc = ap_run_pre_connection(c, csd);
- if (rc != OK && rc != DONE) {
- c->aborted = 1;
- }
+ ap_pre_connection(c, csd);
if (!c->aborted) {
ap_run_process_connection(c);
diff --git a/server/core.c b/server/core.c
index e2a91c7..e5e059e 100644
--- a/server/core.c
+++ b/server/core.c
@@ -22,6 +22,11 @@
#include "apr_thread_proc.h" /* for RLIMIT stuff */
#include "apr_random.h"
+#include "apr_version.h"
+#if APR_MAJOR_VERSION < 2
+#include "apu_version.h"
+#endif
+
#define APR_WANT_IOVEC
#define APR_WANT_STRFUNC
#define APR_WANT_MEMFUNC
@@ -33,6 +38,7 @@
#include "http_core.h"
#include "http_protocol.h" /* For index_of_response(). Grump. */
#include "http_request.h"
+#include "http_ssl.h"
#include "http_vhost.h"
#include "http_main.h" /* For the default_handler below... */
#include "http_log.h"
@@ -61,11 +67,13 @@
/* LimitRequestBody handling */
#define AP_LIMIT_REQ_BODY_UNSET ((apr_off_t) -1)
-#define AP_DEFAULT_LIMIT_REQ_BODY ((apr_off_t) 0)
+#define AP_DEFAULT_LIMIT_REQ_BODY ((apr_off_t) 1<<30) /* 1GB */
/* LimitXMLRequestBody handling */
#define AP_LIMIT_UNSET ((long) -1)
#define AP_DEFAULT_LIMIT_XML_BODY ((apr_size_t)1000000)
+/* Hard limit for ap_escape_html2() */
+#define AP_MAX_LIMIT_XML_BODY ((apr_size_t)(APR_SIZE_MAX / 6 - 1))
#define AP_MIN_SENDFILE_BYTES (256)
@@ -81,9 +89,13 @@
#define AP_CONTENT_MD5_ON 1
#define AP_CONTENT_MD5_UNSET 2
+#define AP_FLUSH_MAX_THRESHOLD 65535
+#define AP_FLUSH_MAX_PIPELINED 4
+
APR_HOOK_STRUCT(
APR_HOOK_LINK(get_mgmt_items)
APR_HOOK_LINK(insert_network_bucket)
+ APR_HOOK_LINK(get_pollfd_from_conn)
)
AP_IMPLEMENT_HOOK_RUN_ALL(int, get_mgmt_items,
@@ -95,6 +107,11 @@ AP_IMPLEMENT_HOOK_RUN_FIRST(apr_status_t, insert_network_bucket,
apr_socket_t *socket),
(c, bb, socket), AP_DECLINED)
+AP_IMPLEMENT_HOOK_RUN_FIRST(apr_status_t, get_pollfd_from_conn,
+ (conn_rec *c, struct apr_pollfd_t *pfd,
+ apr_interval_time_t *ptimeout),
+ (c, pfd, ptimeout), APR_ENOTIMPL)
+
/* Server core module... This module provides support for really basic
* server operations, including options and commands which control the
* operation of other modules. Consider this the bureaucracy module.
@@ -122,8 +139,12 @@ AP_DECLARE_DATA int ap_document_root_check = 1;
/* magic pointer for ErrorDocument xxx "default" */
static char errordocument_default;
+/* Global state allocated out of pconf: variables here MUST be
+ * cleared/reset in reset_config(), a pconf cleanup, to avoid the
+ * variable getting reused after the pool is cleared. */
static apr_array_header_t *saved_server_config_defines = NULL;
static apr_table_t *server_config_defined_vars = NULL;
+AP_DECLARE_DATA const char *ap_runtime_dir = NULL;
AP_DECLARE_DATA int ap_main_state = AP_SQ_MS_INITIAL_STARTUP;
AP_DECLARE_DATA int ap_run_mode = AP_SQ_RM_UNKNOWN;
@@ -390,6 +411,13 @@ static void *merge_core_dir_configs(apr_pool_t *a, void *basev, void *newv)
if (new->enable_sendfile != ENABLE_SENDFILE_UNSET) {
conf->enable_sendfile = new->enable_sendfile;
}
+
+ if (new->read_buf_size) {
+ conf->read_buf_size = new->read_buf_size;
+ }
+ else {
+ conf->read_buf_size = base->read_buf_size;
+ }
conf->allow_encoded_slashes = new->allow_encoded_slashes;
conf->decode_encoded_slashes = new->decode_encoded_slashes;
@@ -462,14 +490,18 @@ static void *create_core_server_config(apr_pool_t *a, server_rec *s)
apr_table_setn(conf->accf_map, "http", "data");
apr_table_setn(conf->accf_map, "https", "data");
#endif
+
+ conf->flush_max_threshold = AP_FLUSH_MAX_THRESHOLD;
+ conf->flush_max_pipelined = AP_FLUSH_MAX_PIPELINED;
}
- /* pcalloc'ed - we have NULL's/0's
- else ** is_virtual ** {
- conf->ap_document_root = NULL;
- conf->access_name = NULL;
- conf->accf_map = NULL;
+ else {
+ /* Use main ErrorLogFormat while the vhost is loading */
+ core_server_config *main_conf =
+ ap_get_core_module_config(ap_server_conf->module_config);
+ conf->error_log_format = main_conf->error_log_format;
+
+ conf->flush_max_pipelined = -1;
}
- */
/* initialization, no special case for global context */
@@ -490,7 +522,10 @@ static void *create_core_server_config(apr_pool_t *a, server_rec *s)
conf->protocols = apr_array_make(a, 5, sizeof(const char *));
conf->protocols_honor_order = -1;
+ conf->merge_slashes = AP_CORE_CONFIG_UNSET;
+ conf->strict_host_check= AP_CORE_CONFIG_UNSET;
+
return (void *)conf;
}
@@ -555,7 +590,22 @@ static void *merge_core_server_configs(apr_pool_t *p, void *basev, void *virtv)
conf->protocols_honor_order = ((virt->protocols_honor_order < 0)?
base->protocols_honor_order :
virt->protocols_honor_order);
+ AP_CORE_MERGE_FLAG(merge_slashes, conf, base, virt);
+
+ conf->flush_max_threshold = (virt->flush_max_threshold)
+ ? virt->flush_max_threshold
+ : base->flush_max_threshold;
+ conf->flush_max_pipelined = (virt->flush_max_pipelined >= 0)
+ ? virt->flush_max_pipelined
+ : base->flush_max_pipelined;
+
+ conf->strict_host_check = (virt->strict_host_check != AP_CORE_CONFIG_UNSET)
+ ? virt->strict_host_check
+ : base->strict_host_check;
+
+ AP_CORE_MERGE_FLAG(strict_host_check, conf, base, virt);
+
return conf;
}
@@ -693,6 +743,7 @@ void ap_core_reorder_directories(apr_pool_t *p, server_rec *s)
/* we have to allocate tmp space to do a stable sort */
apr_pool_create(&tmp, p);
+ apr_pool_tag(tmp, "core_reorder_directories");
sortbin = apr_palloc(tmp, sec_dir->nelts * sizeof(*sortbin));
for (i = 0; i < nelts; ++i) {
sortbin[i].orig_index = i;
@@ -735,7 +786,7 @@ AP_DECLARE(int) ap_allow_overrides(request_rec *r)
/*
* Optional function coming from mod_authn_core, used for
- * retrieving the type of autorization
+ * retrieving the type of authorization
*/
static APR_OPTIONAL_FN_TYPE(authn_ap_auth_type) *authn_ap_auth_type;
@@ -1217,6 +1268,13 @@ AP_DECLARE(apr_off_t) ap_get_limit_req_body(const request_rec *r)
return d->limit_req_body;
}
+AP_DECLARE(apr_size_t) ap_get_read_buf_size(const request_rec *r)
+{
+ core_dir_config *d = ap_get_core_module_config(r->per_dir_config);
+
+ return d->read_buf_size ? d->read_buf_size : AP_IOBUFSIZE;
+}
+
/*****************************************************************
*
@@ -1233,7 +1291,7 @@ static const ap_directive_t * find_parent(const ap_directive_t *dirp,
dirp = dirp->parent;
/* ### it would be nice to have atom-ized directives */
- if (strcasecmp(dirp->directive, what) == 0)
+ if (ap_cstr_casecmp(dirp->directive, what) == 0)
return dirp;
}
@@ -1412,6 +1470,7 @@ static int reset_config_defines(void *dummy)
ap_server_config_defines = saved_server_config_defines;
saved_server_config_defines = NULL;
server_config_defined_vars = NULL;
+ ap_runtime_dir = NULL;
return OK;
}
@@ -1518,7 +1577,7 @@ static const char *set_gprof_dir(cmd_parms *cmd, void *dummy, const char *arg)
return err;
}
- conf->gprof_dir = arg;
+ conf->gprof_dir = apr_pstrdup(cmd->pool, arg);
return NULL;
}
#endif /*GPROF*/
@@ -1528,10 +1587,10 @@ static const char *set_add_default_charset(cmd_parms *cmd,
{
core_dir_config *d = d_;
- if (!strcasecmp(arg, "Off")) {
+ if (!ap_cstr_casecmp(arg, "Off")) {
d->add_default_charset = ADD_DEFAULT_CHARSET_OFF;
}
- else if (!strcasecmp(arg, "On")) {
+ else if (!ap_cstr_casecmp(arg, "On")) {
d->add_default_charset = ADD_DEFAULT_CHARSET_ON;
d->add_default_charset_name = DEFAULT_ADD_DEFAULT_CHARSET_NAME;
}
@@ -1648,7 +1707,7 @@ static const char *set_error_document(cmd_parms *cmd, void *conf_,
conf->response_code_exprs = apr_hash_make(cmd->pool);
}
- if (strcasecmp(msg, "default") == 0) {
+ if (ap_cstr_casecmp(msg, "default") == 0) {
/* special case: ErrorDocument 404 default restores the
* canned server error response
*/
@@ -1704,36 +1763,36 @@ static const char *set_allow_opts(cmd_parms *cmd, allow_options_t *opts,
first = 0;
}
- if (!strcasecmp(w, "Indexes")) {
+ if (!ap_cstr_casecmp(w, "Indexes")) {
opt = OPT_INDEXES;
}
- else if (!strcasecmp(w, "Includes")) {
+ else if (!ap_cstr_casecmp(w, "Includes")) {
/* If Includes is permitted, both Includes and
* IncludesNOEXEC may be changed. */
opt = (OPT_INCLUDES | OPT_INC_WITH_EXEC);
}
- else if (!strcasecmp(w, "IncludesNOEXEC")) {
+ else if (!ap_cstr_casecmp(w, "IncludesNOEXEC")) {
opt = OPT_INCLUDES;
}
- else if (!strcasecmp(w, "FollowSymLinks")) {
+ else if (!ap_cstr_casecmp(w, "FollowSymLinks")) {
opt = OPT_SYM_LINKS;
}
- else if (!strcasecmp(w, "SymLinksIfOwnerMatch")) {
+ else if (!ap_cstr_casecmp(w, "SymLinksIfOwnerMatch")) {
opt = OPT_SYM_OWNER;
}
- else if (!strcasecmp(w, "ExecCGI")) {
+ else if (!ap_cstr_casecmp(w, "ExecCGI")) {
opt = OPT_EXECCGI;
}
- else if (!strcasecmp(w, "MultiViews")) {
+ else if (!ap_cstr_casecmp(w, "MultiViews")) {
opt = OPT_MULTI;
}
- else if (!strcasecmp(w, "RunScripts")) { /* AI backcompat. Yuck */
+ else if (!ap_cstr_casecmp(w, "RunScripts")) { /* AI backcompat. Yuck */
opt = OPT_MULTI|OPT_EXECCGI;
}
- else if (!strcasecmp(w, "None")) {
+ else if (!ap_cstr_casecmp(w, "None")) {
opt = OPT_NONE;
}
- else if (!strcasecmp(w, "All")) {
+ else if (!ap_cstr_casecmp(w, "All")) {
opt = OPT_ALL;
}
else {
@@ -1774,40 +1833,43 @@ static const char *set_override(cmd_parms *cmd, void *d_, const char *l)
*v++ = '\0';
}
- if (!strcasecmp(w, "Limit")) {
+ if (!ap_cstr_casecmp(w, "Limit")) {
d->override |= OR_LIMIT;
}
- else if (!strcasecmp(k, "Options")) {
+ else if (!ap_cstr_casecmp(k, "Options")) {
d->override |= OR_OPTIONS;
if (v)
set_allow_opts(cmd, &(d->override_opts), v);
else
d->override_opts = OPT_ALL;
}
- else if (!strcasecmp(w, "FileInfo")) {
+ else if (!ap_cstr_casecmp(w, "FileInfo")) {
d->override |= OR_FILEINFO;
}
- else if (!strcasecmp(w, "AuthConfig")) {
+ else if (!ap_cstr_casecmp(w, "AuthConfig")) {
d->override |= OR_AUTHCFG;
}
- else if (!strcasecmp(w, "Indexes")) {
+ else if (!ap_cstr_casecmp(w, "Indexes")) {
d->override |= OR_INDEXES;
}
- else if (!strcasecmp(w, "Nonfatal")) {
- if (!strcasecmp(v, "Override")) {
+ else if (!ap_cstr_casecmp(w, "Nonfatal")) {
+ if (!v) {
+ return apr_pstrcat(cmd->pool, "=Override, =Unknown or =All expected after ", w, NULL);
+ }
+ else if (!ap_cstr_casecmp(v, "Override")) {
d->override |= NONFATAL_OVERRIDE;
}
- else if (!strcasecmp(v, "Unknown")) {
+ else if (!ap_cstr_casecmp(v, "Unknown")) {
d->override |= NONFATAL_UNKNOWN;
}
- else if (!strcasecmp(v, "All")) {
+ else if (!ap_cstr_casecmp(v, "All")) {
d->override |= NONFATAL_ALL;
}
}
- else if (!strcasecmp(w, "None")) {
+ else if (!ap_cstr_casecmp(w, "None")) {
d->override = OR_NONE;
}
- else if (!strcasecmp(w, "All")) {
+ else if (!ap_cstr_casecmp(w, "All")) {
d->override = OR_ALL;
}
else {
@@ -1863,6 +1925,13 @@ static const char *set_qualify_redirect_url(cmd_parms *cmd, void *d_, int flag)
return NULL;
}
+static const char *set_core_server_flag(cmd_parms *cmd, void *s_, int flag)
+{
+ core_server_config *conf =
+ ap_get_core_module_config(cmd->server->module_config);
+ return ap_set_flag_slot(cmd, conf, flag);
+}
+
static const char *set_override_list(cmd_parms *cmd, void *d_, int argc, char *const argv[])
{
core_dir_config *d = d_;
@@ -1881,7 +1950,7 @@ static const char *set_override_list(cmd_parms *cmd, void *d_, int argc, char *c
d->override_list = apr_table_make(cmd->pool, argc);
for (i = 0; i < argc; i++) {
- if (!strcasecmp(argv[i], "None")) {
+ if (!ap_cstr_casecmp(argv[i], "None")) {
if (argc != 1) {
return "'None' not allowed with other directives in "
"AllowOverrideList";
@@ -1945,31 +2014,31 @@ static const char *set_options(cmd_parms *cmd, void *d_, const char *l)
return "Either all Options must start with + or -, or no Option may.";
}
- if (!strcasecmp(w, "Indexes")) {
+ if (!ap_cstr_casecmp(w, "Indexes")) {
opt = OPT_INDEXES;
}
- else if (!strcasecmp(w, "Includes")) {
+ else if (!ap_cstr_casecmp(w, "Includes")) {
opt = (OPT_INCLUDES | OPT_INC_WITH_EXEC);
}
- else if (!strcasecmp(w, "IncludesNOEXEC")) {
+ else if (!ap_cstr_casecmp(w, "IncludesNOEXEC")) {
opt = OPT_INCLUDES;
}
- else if (!strcasecmp(w, "FollowSymLinks")) {
+ else if (!ap_cstr_casecmp(w, "FollowSymLinks")) {
opt = OPT_SYM_LINKS;
}
- else if (!strcasecmp(w, "SymLinksIfOwnerMatch")) {
+ else if (!ap_cstr_casecmp(w, "SymLinksIfOwnerMatch")) {
opt = OPT_SYM_OWNER;
}
- else if (!strcasecmp(w, "ExecCGI")) {
+ else if (!ap_cstr_casecmp(w, "ExecCGI")) {
opt = OPT_EXECCGI;
}
- else if (!strcasecmp(w, "MultiViews")) {
+ else if (!ap_cstr_casecmp(w, "MultiViews")) {
opt = OPT_MULTI;
}
- else if (!strcasecmp(w, "RunScripts")) { /* AI backcompat. Yuck */
+ else if (!ap_cstr_casecmp(w, "RunScripts")) { /* AI backcompat. Yuck */
opt = OPT_MULTI|OPT_EXECCGI;
}
- else if (!strcasecmp(w, "None")) {
+ else if (!ap_cstr_casecmp(w, "None")) {
if (!first) {
return "'Options None' must be the first Option given.";
}
@@ -1979,7 +2048,7 @@ static const char *set_options(cmd_parms *cmd, void *d_, const char *l)
opt = OPT_NONE;
all_none = 1;
}
- else if (!strcasecmp(w, "All")) {
+ else if (!ap_cstr_casecmp(w, "All")) {
if (!first) {
return "'Options All' must be the first option given.";
}
@@ -2020,7 +2089,7 @@ static const char *set_options(cmd_parms *cmd, void *d_, const char *l)
static const char *set_default_type(cmd_parms *cmd, void *d_,
const char *arg)
{
- if ((strcasecmp(arg, "off") != 0) && (strcasecmp(arg, "none") != 0)) {
+ if ((ap_cstr_casecmp(arg, "off") != 0) && (ap_cstr_casecmp(arg, "none") != 0)) {
ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server, APLOGNO(00117)
"Ignoring deprecated use of DefaultType in line %d of %s.",
cmd->directive->line_num, cmd->directive->filename);
@@ -2090,7 +2159,7 @@ static const char *set_etag_bits(cmd_parms *cmd, void *mconfig,
}
}
- if (strcasecmp(token, "None") == 0) {
+ if (ap_cstr_casecmp(token, "None") == 0) {
if (action != '*') {
valid = 0;
}
@@ -2099,7 +2168,7 @@ static const char *set_etag_bits(cmd_parms *cmd, void *mconfig,
explicit = 1;
}
}
- else if (strcasecmp(token, "All") == 0) {
+ else if (ap_cstr_casecmp(token, "All") == 0) {
if (action != '*') {
valid = 0;
}
@@ -2108,17 +2177,20 @@ static const char *set_etag_bits(cmd_parms *cmd, void *mconfig,
cfg->etag_bits = bit = ETAG_ALL;
}
}
- else if (strcasecmp(token, "Size") == 0) {
+ else if (ap_cstr_casecmp(token, "Size") == 0) {
bit = ETAG_SIZE;
}
- else if ((strcasecmp(token, "LMTime") == 0)
- || (strcasecmp(token, "MTime") == 0)
- || (strcasecmp(token, "LastModified") == 0)) {
+ else if ((ap_cstr_casecmp(token, "LMTime") == 0)
+ || (ap_cstr_casecmp(token, "MTime") == 0)
+ || (ap_cstr_casecmp(token, "LastModified") == 0)) {
bit = ETAG_MTIME;
}
- else if (strcasecmp(token, "INode") == 0) {
+ else if (ap_cstr_casecmp(token, "INode") == 0) {
bit = ETAG_INODE;
}
+ else if (ap_cstr_casecmp(token, "Digest") == 0) {
+ bit = ETAG_DIGEST;
+ }
else {
return apr_pstrcat(cmd->pool, "Unknown keyword '",
token, "' for ", cmd->cmd->name,
@@ -2183,10 +2255,10 @@ static const char *set_enable_mmap(cmd_parms *cmd, void *d_,
{
core_dir_config *d = d_;
- if (strcasecmp(arg, "on") == 0) {
+ if (ap_cstr_casecmp(arg, "on") == 0) {
d->enable_mmap = ENABLE_MMAP_ON;
}
- else if (strcasecmp(arg, "off") == 0) {
+ else if (ap_cstr_casecmp(arg, "off") == 0) {
d->enable_mmap = ENABLE_MMAP_OFF;
}
else {
@@ -2201,10 +2273,10 @@ static const char *set_enable_sendfile(cmd_parms *cmd, void *d_,
{
core_dir_config *d = d_;
- if (strcasecmp(arg, "on") == 0) {
+ if (ap_cstr_casecmp(arg, "on") == 0) {
d->enable_sendfile = ENABLE_SENDFILE_ON;
}
- else if (strcasecmp(arg, "off") == 0) {
+ else if (ap_cstr_casecmp(arg, "off") == 0) {
d->enable_sendfile = ENABLE_SENDFILE_OFF;
}
else {
@@ -2214,6 +2286,64 @@ static const char *set_enable_sendfile(cmd_parms *cmd, void *d_,
return NULL;
}
+static const char *set_read_buf_size(cmd_parms *cmd, void *d_,
+ const char *arg)
+{
+ core_dir_config *d = d_;
+ apr_off_t size;
+ char *end;
+
+ if (apr_strtoff(&size, arg, &end, 10)
+ || *end || size < 0 || size > APR_UINT32_MAX)
+ return apr_pstrcat(cmd->pool,
+ "parameter must be a number between 0 and "
+ APR_STRINGIFY(APR_UINT32_MAX) "): ",
+ arg, NULL);
+
+ d->read_buf_size = (apr_size_t)size;
+
+ return NULL;
+}
+
+static const char *set_flush_max_threshold(cmd_parms *cmd, void *d_,
+ const char *arg)
+{
+ core_server_config *conf =
+ ap_get_core_module_config(cmd->server->module_config);
+ apr_off_t size;
+ char *end;
+
+ if (apr_strtoff(&size, arg, &end, 10)
+ || *end || size < 0 || size > APR_UINT32_MAX)
+ return apr_pstrcat(cmd->pool,
+ "parameter must be a number between 0 and "
+ APR_STRINGIFY(APR_UINT32_MAX) "): ",
+ arg, NULL);
+
+ conf->flush_max_threshold = (apr_size_t)size;
+
+ return NULL;
+}
+
+static const char *set_flush_max_pipelined(cmd_parms *cmd, void *d_,
+ const char *arg)
+{
+ core_server_config *conf =
+ ap_get_core_module_config(cmd->server->module_config);
+ apr_off_t num;
+ char *end;
+
+ if (apr_strtoff(&num, arg, &end, 10)
+ || *end || num < -1 || num > APR_INT32_MAX)
+ return apr_pstrcat(cmd->pool,
+ "parameter must be a number between -1 and "
+ APR_STRINGIFY(APR_INT32_MAX) ": ",
+ arg, NULL);
+
+ conf->flush_max_pipelined = (apr_int32_t)num;
+
+ return NULL;
+}
/*
* Report a missing-'>' syntax error.
@@ -2778,8 +2908,15 @@ static int test_iffile_section(cmd_parms *cmd, const char *arg)
const char *relative;
apr_finfo_t sb;
+ /*
+ * At least on Windows, if the path we are testing is not valid (for example
+ * a path on a USB key that is not plugged), 'ap_server_root_relative()' will
+ * return NULL. In such a case, consider that the file is not there and that
+ * the section should be skipped.
+ */
relative = ap_server_root_relative(cmd->temp_pool, arg);
- return (apr_stat(&sb, relative, 0, cmd->pool) == APR_SUCCESS);
+ return (relative &&
+ (apr_stat(&sb, relative, APR_FINFO_TYPE, cmd->temp_pool) == APR_SUCCESS));
}
static int test_ifdirective_section(cmd_parms *cmd, const char *arg)
@@ -3052,13 +3189,13 @@ static const char *set_signature_flag(cmd_parms *cmd, void *d_,
{
core_dir_config *d = d_;
- if (strcasecmp(arg, "On") == 0) {
+ if (ap_cstr_casecmp(arg, "On") == 0) {
d->server_signature = srv_sig_on;
}
- else if (strcasecmp(arg, "Off") == 0) {
+ else if (ap_cstr_casecmp(arg, "Off") == 0) {
d->server_signature = srv_sig_off;
}
- else if (strcasecmp(arg, "EMail") == 0) {
+ else if (ap_cstr_casecmp(arg, "EMail") == 0) {
d->server_signature = srv_sig_withmail;
}
else {
@@ -3120,13 +3257,13 @@ static const char *set_allow2f(cmd_parms *cmd, void *d_, const char *arg)
{
core_dir_config *d = d_;
- if (0 == strcasecmp(arg, "on")) {
+ if (0 == ap_cstr_casecmp(arg, "on")) {
d->allow_encoded_slashes = 1;
d->decode_encoded_slashes = 1; /* for compatibility with 2.0 & 2.2 */
- } else if (0 == strcasecmp(arg, "off")) {
+ } else if (0 == ap_cstr_casecmp(arg, "off")) {
d->allow_encoded_slashes = 0;
d->decode_encoded_slashes = 0;
- } else if (0 == strcasecmp(arg, "nodecode")) {
+ } else if (0 == ap_cstr_casecmp(arg, "nodecode")) {
d->allow_encoded_slashes = 1;
d->decode_encoded_slashes = 0;
} else {
@@ -3142,13 +3279,13 @@ static const char *set_hostname_lookups(cmd_parms *cmd, void *d_,
{
core_dir_config *d = d_;
- if (!strcasecmp(arg, "on")) {
+ if (!ap_cstr_casecmp(arg, "on")) {
d->hostname_lookups = HOSTNAME_LOOKUP_ON;
}
- else if (!strcasecmp(arg, "off")) {
+ else if (!ap_cstr_casecmp(arg, "off")) {
d->hostname_lookups = HOSTNAME_LOOKUP_OFF;
}
- else if (!strcasecmp(arg, "double")) {
+ else if (!ap_cstr_casecmp(arg, "double")) {
d->hostname_lookups = HOSTNAME_LOOKUP_DOUBLE;
}
else {
@@ -3184,13 +3321,13 @@ static const char *set_accept_path_info(cmd_parms *cmd, void *d_, const char *ar
{
core_dir_config *d = d_;
- if (strcasecmp(arg, "on") == 0) {
+ if (ap_cstr_casecmp(arg, "on") == 0) {
d->accept_path_info = AP_REQ_ACCEPT_PATH_INFO;
}
- else if (strcasecmp(arg, "off") == 0) {
+ else if (ap_cstr_casecmp(arg, "off") == 0) {
d->accept_path_info = AP_REQ_REJECT_PATH_INFO;
}
- else if (strcasecmp(arg, "default") == 0) {
+ else if (ap_cstr_casecmp(arg, "default") == 0) {
d->accept_path_info = AP_REQ_DEFAULT_PATH_INFO;
}
else {
@@ -3205,13 +3342,13 @@ static const char *set_use_canonical_name(cmd_parms *cmd, void *d_,
{
core_dir_config *d = d_;
- if (strcasecmp(arg, "on") == 0) {
+ if (ap_cstr_casecmp(arg, "on") == 0) {
d->use_canonical_name = USE_CANONICAL_NAME_ON;
}
- else if (strcasecmp(arg, "off") == 0) {
+ else if (ap_cstr_casecmp(arg, "off") == 0) {
d->use_canonical_name = USE_CANONICAL_NAME_OFF;
}
- else if (strcasecmp(arg, "dns") == 0) {
+ else if (ap_cstr_casecmp(arg, "dns") == 0) {
d->use_canonical_name = USE_CANONICAL_NAME_DNS;
}
else {
@@ -3226,10 +3363,10 @@ static const char *set_use_canonical_phys_port(cmd_parms *cmd, void *d_,
{
core_dir_config *d = d_;
- if (strcasecmp(arg, "on") == 0) {
+ if (ap_cstr_casecmp(arg, "on") == 0) {
d->use_canonical_phys_port = USE_CANONICAL_PHYS_PORT_ON;
}
- else if (strcasecmp(arg, "off") == 0) {
+ else if (ap_cstr_casecmp(arg, "off") == 0) {
d->use_canonical_phys_port = USE_CANONICAL_PHYS_PORT_OFF;
}
else {
@@ -3525,22 +3662,22 @@ static const char *set_serv_tokens(cmd_parms *cmd, void *dummy,
return err;
}
- if (!strcasecmp(arg, "OS")) {
+ if (!ap_cstr_casecmp(arg, "OS")) {
ap_server_tokens = SrvTk_OS;
}
- else if (!strcasecmp(arg, "Min") || !strcasecmp(arg, "Minimal")) {
+ else if (!ap_cstr_casecmp(arg, "Min") || !ap_cstr_casecmp(arg, "Minimal")) {
ap_server_tokens = SrvTk_MINIMAL;
}
- else if (!strcasecmp(arg, "Major")) {
+ else if (!ap_cstr_casecmp(arg, "Major")) {
ap_server_tokens = SrvTk_MAJOR;
}
- else if (!strcasecmp(arg, "Minor") ) {
+ else if (!ap_cstr_casecmp(arg, "Minor") ) {
ap_server_tokens = SrvTk_MINOR;
}
- else if (!strcasecmp(arg, "Prod") || !strcasecmp(arg, "ProductOnly")) {
+ else if (!ap_cstr_casecmp(arg, "Prod") || !ap_cstr_casecmp(arg, "ProductOnly")) {
ap_server_tokens = SrvTk_PRODUCT_ONLY;
}
- else if (!strcasecmp(arg, "Full")) {
+ else if (!ap_cstr_casecmp(arg, "Full")) {
ap_server_tokens = SrvTk_FULL;
}
else {
@@ -3637,6 +3774,11 @@ static const char *set_limit_xml_req_body(cmd_parms *cmd, void *conf_,
if (conf->limit_xml_body < 0)
return "LimitXMLRequestBody requires a non-negative integer.";
+ /* zero is AP_MAX_LIMIT_XML_BODY (implicitly) */
+ if ((apr_size_t)conf->limit_xml_body > AP_MAX_LIMIT_XML_BODY)
+ return apr_psprintf(cmd->pool, "LimitXMLRequestBody must not exceed "
+ "%" APR_SIZE_T_FMT, AP_MAX_LIMIT_XML_BODY);
+
return NULL;
}
@@ -3645,13 +3787,13 @@ static const char *set_max_ranges(cmd_parms *cmd, void *conf_, const char *arg)
core_dir_config *conf = conf_;
int val = 0;
- if (!strcasecmp(arg, "none")) {
+ if (!ap_cstr_casecmp(arg, "none")) {
val = AP_MAXRANGES_NORANGES;
}
- else if (!strcasecmp(arg, "default")) {
+ else if (!ap_cstr_casecmp(arg, "default")) {
val = AP_MAXRANGES_DEFAULT;
}
- else if (!strcasecmp(arg, "unlimited")) {
+ else if (!ap_cstr_casecmp(arg, "unlimited")) {
val = AP_MAXRANGES_UNLIMITED;
}
else {
@@ -3671,13 +3813,13 @@ static const char *set_max_overlaps(cmd_parms *cmd, void *conf_, const char *arg
core_dir_config *conf = conf_;
int val = 0;
- if (!strcasecmp(arg, "none")) {
+ if (!ap_cstr_casecmp(arg, "none")) {
val = AP_MAXRANGES_NORANGES;
}
- else if (!strcasecmp(arg, "default")) {
+ else if (!ap_cstr_casecmp(arg, "default")) {
val = AP_MAXRANGES_DEFAULT;
}
- else if (!strcasecmp(arg, "unlimited")) {
+ else if (!ap_cstr_casecmp(arg, "unlimited")) {
val = AP_MAXRANGES_UNLIMITED;
}
else {
@@ -3697,13 +3839,13 @@ static const char *set_max_reversals(cmd_parms *cmd, void *conf_, const char *ar
core_dir_config *conf = conf_;
int val = 0;
- if (!strcasecmp(arg, "none")) {
+ if (!ap_cstr_casecmp(arg, "none")) {
val = AP_MAXRANGES_NORANGES;
}
- else if (!strcasecmp(arg, "default")) {
+ else if (!ap_cstr_casecmp(arg, "default")) {
val = AP_MAXRANGES_DEFAULT;
}
- else if (!strcasecmp(arg, "unlimited")) {
+ else if (!ap_cstr_casecmp(arg, "unlimited")) {
val = AP_MAXRANGES_UNLIMITED;
}
else {
@@ -3725,6 +3867,8 @@ AP_DECLARE(apr_size_t) ap_get_limit_xml_body(const request_rec *r)
conf = ap_get_core_module_config(r->per_dir_config);
if (conf->limit_xml_body == AP_LIMIT_UNSET)
return AP_DEFAULT_LIMIT_XML_BODY;
+ if (conf->limit_xml_body == 0)
+ return AP_MAX_LIMIT_XML_BODY;
return (apr_size_t)conf->limit_xml_body;
}
@@ -3818,24 +3962,26 @@ static const char *set_recursion_limit(cmd_parms *cmd, void *dummy,
static void log_backtrace(const request_rec *r)
{
- const request_rec *top = r;
+ if (APLOGrdebug(r)) {
+ const request_rec *top = r;
- ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00121)
- "r->uri = %s", r->uri ? r->uri : "(unexpectedly NULL)");
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00121)
+ "r->uri = %s", r->uri ? r->uri : "(unexpectedly NULL)");
- while (top && (top->prev || top->main)) {
- if (top->prev) {
- top = top->prev;
- ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00122)
- "redirected from r->uri = %s",
- top->uri ? top->uri : "(unexpectedly NULL)");
- }
+ while (top && (top->prev || top->main)) {
+ if (top->prev) {
+ top = top->prev;
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00122)
+ "redirected from r->uri = %s",
+ top->uri ? top->uri : "(unexpectedly NULL)");
+ }
- if (!top->prev && top->main) {
- top = top->main;
- ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00123)
- "subrequested from r->uri = %s",
- top->uri ? top->uri : "(unexpectedly NULL)");
+ if (!top->prev && top->main) {
+ top = top->main;
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00123)
+ "subrequested from r->uri = %s",
+ top->uri ? top->uri : "(unexpectedly NULL)");
+ }
}
}
}
@@ -3909,13 +4055,13 @@ static const char *set_trace_enable(cmd_parms *cmd, void *dummy,
core_server_config *conf =
ap_get_core_module_config(cmd->server->module_config);
- if (strcasecmp(arg1, "on") == 0) {
+ if (ap_cstr_casecmp(arg1, "on") == 0) {
conf->trace_enable = AP_TRACE_ENABLE;
}
- else if (strcasecmp(arg1, "off") == 0) {
+ else if (ap_cstr_casecmp(arg1, "off") == 0) {
conf->trace_enable = AP_TRACE_DISABLE;
}
- else if (strcasecmp(arg1, "extended") == 0) {
+ else if (ap_cstr_casecmp(arg1, "extended") == 0) {
conf->trace_enable = AP_TRACE_EXTENDED;
}
else {
@@ -3954,10 +4100,10 @@ static const char *set_protocols_honor_order(cmd_parms *cmd, void *dummy,
return err;
}
- if (strcasecmp(arg, "on") == 0) {
+ if (ap_cstr_casecmp(arg, "on") == 0) {
conf->protocols_honor_order = 1;
}
- else if (strcasecmp(arg, "off") == 0) {
+ else if (ap_cstr_casecmp(arg, "off") == 0) {
conf->protocols_honor_order = 0;
}
else {
@@ -4227,7 +4373,7 @@ static const char *set_errorlog_format(cmd_parms *cmd, void *dummy,
conf->error_log_format = parse_errorlog_string(cmd->pool, arg1,
&err_string, 1);
}
- else if (!strcasecmp(arg1, "connection")) {
+ else if (!ap_cstr_casecmp(arg1, "connection")) {
if (!conf->error_log_conn) {
conf->error_log_conn = apr_array_make(cmd->pool, 5,
sizeof(apr_array_header_t *));
@@ -4239,7 +4385,7 @@ static const char *set_errorlog_format(cmd_parms *cmd, void *dummy,
*e = parse_errorlog_string(cmd->pool, arg2, &err_string, 0);
}
}
- else if (!strcasecmp(arg1, "request")) {
+ else if (!ap_cstr_casecmp(arg1, "request")) {
if (!conf->error_log_req) {
conf->error_log_req = apr_array_make(cmd->pool, 5,
sizeof(apr_array_header_t *));
@@ -4374,6 +4520,13 @@ AP_INIT_TAKE1("EnableMMAP", set_enable_mmap, NULL, OR_FILEINFO,
"Controls whether memory-mapping may be used to read files"),
AP_INIT_TAKE1("EnableSendfile", set_enable_sendfile, NULL, OR_FILEINFO,
"Controls whether sendfile may be used to transmit files"),
+AP_INIT_TAKE1("ReadBufferSize", set_read_buf_size, NULL, ACCESS_CONF|RSRC_CONF,
+ "Size (in bytes) of the memory buffers used to read data"),
+AP_INIT_TAKE1("FlushMaxThreshold", set_flush_max_threshold, NULL, RSRC_CONF,
+ "Maximum threshold above which pending data are flushed to the network"),
+AP_INIT_TAKE1("FlushMaxPipelined", set_flush_max_pipelined, NULL, RSRC_CONF,
+ "Maximum number of pipelined responses (pending) above which they are "
+ "flushed to the network"),
/* Old server config file commands */
@@ -4498,9 +4651,12 @@ AP_INIT_FLAG("CGIPassAuth", set_cgi_pass_auth, NULL, OR_AUTHCFG,
AP_INIT_TAKE2("CGIVar", set_cgi_var, NULL, OR_FILEINFO,
"Controls how some CGI variables are set"),
AP_INIT_FLAG("QualifyRedirectURL", set_qualify_redirect_url, NULL, OR_FILEINFO,
- "Controls whether HTTP authorization headers, normally hidden, will "
- "be passed to scripts"),
-
+ "Controls whether the REDIRECT_URL environment variable is fully "
+ "qualified"),
+AP_INIT_FLAG("StrictHostCheck", set_core_server_flag,
+ (void *)APR_OFFSETOF(core_server_config, strict_host_check),
+ RSRC_CONF,
+ "Controls whether a hostname match is required"),
AP_INIT_TAKE1("ForceType", ap_set_string_slot_lower,
(void *)APR_OFFSETOF(core_dir_config, mime_type), OR_FILEINFO,
"a mime type that overrides other configured type"),
@@ -4562,6 +4718,10 @@ AP_INIT_ITERATE("HttpProtocolOptions", set_http_protocol_options, NULL, RSRC_CON
"'Unsafe' or 'Strict' (default). Sets HTTP acceptance rules"),
AP_INIT_ITERATE("RegisterHttpMethod", set_http_method, NULL, RSRC_CONF,
"Registers non-standard HTTP methods"),
+AP_INIT_FLAG("MergeSlashes", set_core_server_flag,
+ (void *)APR_OFFSETOF(core_server_config, merge_slashes),
+ RSRC_CONF,
+ "Controls whether consecutive slashes in the URI path are merged"),
{ NULL }
};
@@ -4583,7 +4743,7 @@ AP_DECLARE_NONSTD(int) ap_core_translate(request_rec *r)
}
if (!r->uri || ((r->uri[0] != '/') && strcmp(r->uri, "*"))) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00126)
- "Invalid URI in request %s", r->the_request);
+ "Invalid URI in request '%s' '%s'", r->uri, r->the_request);
return HTTP_BAD_REQUEST;
}
@@ -4793,7 +4953,7 @@ static int default_handler(request_rec *r)
ap_update_mtime(r, r->finfo.mtime);
ap_set_last_modified(r);
- ap_set_etag(r);
+ ap_set_etag_fd(r, fd);
ap_set_accept_ranges(r);
ap_set_content_length(r, r->finfo.size);
if (bld_content_md5) {
@@ -4815,12 +4975,19 @@ static int default_handler(request_rec *r)
(void)apr_bucket_file_enable_mmap(e, 0);
}
#endif
+#if APR_MAJOR_VERSION > 1 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 6)
+ if (d->read_buf_size) {
+ apr_bucket_file_set_buf_size(e, d->read_buf_size);
+ }
+#endif
}
e = apr_bucket_eos_create(c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(bb, e);
status = ap_pass_brigade(r->output_filters, bb);
+ apr_brigade_cleanup(bb);
+
if (status == APR_SUCCESS
|| r->status != HTTP_OK
|| c->aborted) {
@@ -4924,7 +5091,7 @@ static int core_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptem
apr_pool_cleanup_register(pconf, NULL, reset_config_defines,
apr_pool_cleanup_null);
- ap_regcomp_set_default_cflags(AP_REG_DOLLAR_ENDONLY);
+ ap_regcomp_set_default_cflags(AP_REG_DEFAULT);
mpm_common_pre_config(pconf);
@@ -4943,6 +5110,7 @@ static int core_post_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *pte
set_banner(pconf);
ap_setup_make_content_type(pconf);
ap_setup_auth_internal(ptemp);
+ ap_setup_ssl_optional_fns(pconf);
if (!sys_privileges) {
ap_log_error(APLOG_MARK, APLOG_CRIT, 0, NULL, APLOGNO(00136)
"Server MUST relinquish startup privileges before "
@@ -5059,10 +5227,10 @@ static conn_rec *core_create_conn(apr_pool_t *ptrans, server_rec *server,
/* Got a connection structure, so initialize what fields we can
* (the rest are zeroed out by pcalloc).
*/
+ c->pool = ptrans;
c->conn_config = ap_create_conn_config(ptrans);
c->notes = apr_table_make(ptrans, 5);
- c->pool = ptrans;
if ((rv = apr_socket_addr_get(&c->local_addr, APR_LOCAL, csd))
!= APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_INFO, rv, server, APLOGNO(00137)
@@ -5070,8 +5238,17 @@ static conn_rec *core_create_conn(apr_pool_t *ptrans, server_rec *server,
apr_socket_close(csd);
return NULL;
}
+ if (apr_sockaddr_ip_get(&c->local_ip, c->local_addr)) {
+#if APR_HAVE_SOCKADDR_UN
+ if (c->local_addr->family == APR_UNIX) {
+ c->local_ip = apr_pstrndup(c->pool, c->local_addr->ipaddr_ptr,
+ c->local_addr->ipaddr_len);
+ }
+ else
+#endif
+ c->local_ip = apr_pstrdup(c->pool, "unknown");
+ }
- apr_sockaddr_ip_get(&c->local_ip, c->local_addr);
if ((rv = apr_socket_addr_get(&c->client_addr, APR_REMOTE, csd))
!= APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_INFO, rv, server, APLOGNO(00138)
@@ -5079,8 +5256,17 @@ static conn_rec *core_create_conn(apr_pool_t *ptrans, server_rec *server,
apr_socket_close(csd);
return NULL;
}
+ if (apr_sockaddr_ip_get(&c->client_ip, c->client_addr)) {
+#if APR_HAVE_SOCKADDR_UN
+ if (c->client_addr->family == APR_UNIX) {
+ c->client_ip = apr_pstrndup(c->pool, c->client_addr->ipaddr_ptr,
+ c->client_addr->ipaddr_len);
+ }
+ else
+#endif
+ c->client_ip = apr_pstrdup(c->pool, "unknown");
+ }
- apr_sockaddr_ip_get(&c->client_ip, c->client_addr);
c->base_server = server;
c->id = id;
@@ -5093,9 +5279,14 @@ static conn_rec *core_create_conn(apr_pool_t *ptrans, server_rec *server,
static int core_pre_connection(conn_rec *c, void *csd)
{
- core_net_rec *net = apr_palloc(c->pool, sizeof(*net));
+ core_net_rec *net;
apr_status_t rv;
+ if (c->master) {
+ return DONE;
+ }
+
+ net = apr_palloc(c->pool, sizeof(*net));
/* The Nagle algorithm says that we should delay sending partial
* packets in hopes of getting more data. We don't want to do
* this; we are not telnet. There are bad interactions between
@@ -5136,6 +5327,31 @@ static int core_pre_connection(conn_rec *c, void *csd)
return DONE;
}
+AP_DECLARE(int) ap_pre_connection(conn_rec *c, void *csd)
+{
+ int rc = OK;
+
+ rc = ap_run_pre_connection(c, csd);
+ if (rc != OK && rc != DONE) {
+ c->aborted = 1;
+ /*
+ * In case we errored, the pre_connection hook of the core
+ * module maybe did not run (it is APR_HOOK_REALLY_LAST) and
+ * hence we missed to
+ *
+ * - Put the socket in c->conn_config
+ * - Setup core output and input filters
+ * - Set socket options and timeouts
+ *
+ * Hence call it in this case.
+ */
+ if (!ap_get_conn_socket(c)) {
+ core_pre_connection(c, csd);
+ }
+ }
+ return rc;
+}
+
AP_DECLARE(int) ap_state_query(int query)
{
switch (query) {
@@ -5394,6 +5610,28 @@ static int core_upgrade_storage(request_rec *r)
return DECLINED;
}
+static apr_status_t core_get_pollfd_from_conn(conn_rec *c,
+ struct apr_pollfd_t *pfd,
+ apr_interval_time_t *ptimeout)
+{
+ if (c && !c->master) {
+ pfd->desc_type = APR_POLL_SOCKET;
+ pfd->desc.s = ap_get_conn_socket(c);
+ if (ptimeout) {
+ apr_socket_timeout_get(pfd->desc.s, ptimeout);
+ }
+ return APR_SUCCESS;
+ }
+ return APR_ENOTIMPL;
+}
+
+AP_CORE_DECLARE(apr_status_t) ap_get_pollfd_from_conn(conn_rec *c,
+ struct apr_pollfd_t *pfd,
+ apr_interval_time_t *ptimeout)
+{
+ return ap_run_get_pollfd_from_conn(c, pfd, ptimeout);
+}
+
static void register_hooks(apr_pool_t *p)
{
errorlog_hash = apr_hash_make(p);
@@ -5437,7 +5675,9 @@ static void register_hooks(apr_pool_t *p)
ap_hook_open_htaccess(ap_open_htaccess, NULL, NULL, APR_HOOK_REALLY_LAST);
ap_hook_optional_fn_retrieve(core_optional_fn_retrieve, NULL, NULL,
APR_HOOK_MIDDLE);
-
+ ap_hook_get_pollfd_from_conn(core_get_pollfd_from_conn, NULL, NULL,
+ APR_HOOK_REALLY_LAST);
+
/* register the core's insert_filter hook and register core-provided
* filters
*/
@@ -5470,4 +5710,3 @@ AP_DECLARE_MODULE(core) = {
core_cmds, /* command apr_table_t */
register_hooks /* register hooks */
};
-
diff --git a/server/core_filters.c b/server/core_filters.c
index a6c2bd6..c4ab603 100644
--- a/server/core_filters.c
+++ b/server/core_filters.c
@@ -25,6 +25,7 @@
#include "apr_fnmatch.h"
#include "apr_hash.h"
#include "apr_thread_proc.h" /* for RLIMIT stuff */
+#include "apr_version.h"
#define APR_WANT_IOVEC
#define APR_WANT_STRFUNC
@@ -79,14 +80,10 @@ do { \
struct core_output_filter_ctx {
apr_bucket_brigade *buffered_bb;
- apr_bucket_brigade *tmp_flush_bb;
apr_pool_t *deferred_write_pool;
apr_size_t bytes_written;
-};
-
-struct core_filter_ctx {
- apr_bucket_brigade *b;
- apr_bucket_brigade *tmpbb;
+ struct iovec *vec;
+ apr_size_t nvec;
};
@@ -270,7 +267,7 @@ apr_status_t ap_core_input_filter(ap_filter_t *f, apr_bucket_brigade *b,
while ((len < readbytes) && (rv == APR_SUCCESS)
&& (e != APR_BRIGADE_SENTINEL(ctx->b))) {
/* Check for the availability of buckets with known length */
- if (e->length != -1) {
+ if (e->length != (apr_size_t)-1) {
len += e->length;
e = APR_BUCKET_NEXT(e);
}
@@ -335,50 +332,131 @@ static void setaside_remaining_output(ap_filter_t *f,
static apr_status_t send_brigade_nonblocking(apr_socket_t *s,
apr_bucket_brigade *bb,
- apr_size_t *bytes_written,
+ core_output_filter_ctx_t *ctx,
conn_rec *c);
-static void remove_empty_buckets(apr_bucket_brigade *bb);
-
-static apr_status_t send_brigade_blocking(apr_socket_t *s,
- apr_bucket_brigade *bb,
- apr_size_t *bytes_written,
- conn_rec *c);
-
static apr_status_t writev_nonblocking(apr_socket_t *s,
- struct iovec *vec, apr_size_t nvec,
apr_bucket_brigade *bb,
- apr_size_t *cumulative_bytes_written,
+ core_output_filter_ctx_t *ctx,
+ apr_size_t bytes_to_write,
+ apr_size_t nvec,
conn_rec *c);
#if APR_HAS_SENDFILE
static apr_status_t sendfile_nonblocking(apr_socket_t *s,
apr_bucket *bucket,
- apr_size_t *cumulative_bytes_written,
+ core_output_filter_ctx_t *ctx,
conn_rec *c);
#endif
/* XXX: Should these be configurable parameters? */
#define THRESHOLD_MIN_WRITE 4096
-#define THRESHOLD_MAX_BUFFER 65536
-#define MAX_REQUESTS_IN_PIPELINE 5
/* Optional function coming from mod_logio, used for logging of output
* traffic
*/
extern APR_OPTIONAL_FN_TYPE(ap_logio_add_bytes_out) *ap__logio_add_bytes_out;
+static int should_send_brigade(apr_bucket_brigade *bb, conn_rec *c, int *flush)
+{
+ core_server_config *conf =
+ ap_get_core_module_config(c->base_server->module_config);
+ apr_size_t total_bytes = 0, non_file_bytes = 0;
+ apr_uint32_t eor_buckets = 0;
+ apr_bucket *bucket;
+ int need_flush = 0;
+
+ /* Scan through the brigade and decide whether we need to flush it,
+ * based on the following rules:
+ *
+ * a) The brigade contains a flush bucket: Do a blocking write
+ * of everything up that point.
+ *
+ * b) The request is in CONN_STATE_HANDLER state, and the brigade
+ * contains at least flush_max_threshold bytes in non-file
+ * buckets: Do blocking writes until the amount of data in the
+ * buffer is less than flush_max_threshold. (The point of this
+ * rule is to provide flow control, in case a handler is
+ * streaming out lots of data faster than the data can be
+ * sent to the client.)
+ *
+ * c) The request is in CONN_STATE_HANDLER state, and the brigade
+ * contains at least flush_max_pipelined EOR buckets:
+ * Do blocking writes until less than flush_max_pipelined EOR
+ * buckets are left. (The point of this rule is to prevent too many
+ * FDs being kept open by pipelined requests, possibly allowing a
+ * DoS).
+ *
+ * d) The brigade contains a morphing bucket: otherwise ap_save_brigade()
+ * could read the whole bucket into memory.
+ */
+ for (bucket = APR_BRIGADE_FIRST(bb);
+ bucket != APR_BRIGADE_SENTINEL(bb);
+ bucket = APR_BUCKET_NEXT(bucket)) {
+
+ if (!APR_BUCKET_IS_METADATA(bucket)) {
+ if (bucket->length == (apr_size_t)-1) {
+ if (flush) {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, c,
+ "core_output_filter: flushing because "
+ "of morphing bucket");
+ }
+ need_flush = 1;
+ break;
+ }
+
+ total_bytes += bucket->length;
+ if (!APR_BUCKET_IS_FILE(bucket)) {
+ non_file_bytes += bucket->length;
+ if (non_file_bytes > conf->flush_max_threshold) {
+ if (flush) {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, c,
+ "core_output_filter: flushing because "
+ "of max threshold");
+ }
+ need_flush = 1;
+ break;
+ }
+ }
+ }
+ else if (APR_BUCKET_IS_FLUSH(bucket)) {
+ if (flush) {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, c,
+ "core_output_filter: flushing because "
+ "of FLUSH bucket");
+ }
+ need_flush = 1;
+ break;
+ }
+ else if (AP_BUCKET_IS_EOR(bucket)
+ && conf->flush_max_pipelined >= 0
+ && ++eor_buckets > conf->flush_max_pipelined) {
+ if (flush) {
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, c,
+ "core_output_filter: flushing because "
+ "of max pipelined");
+ }
+ need_flush = 1;
+ break;
+ }
+ }
+ if (flush) {
+ *flush = need_flush;
+ }
+
+ /* Also send if above flush_min_threshold, or if there are FILE buckets */
+ return (need_flush
+ || total_bytes >= THRESHOLD_MIN_WRITE
+ || total_bytes > non_file_bytes);
+}
+
apr_status_t ap_core_output_filter(ap_filter_t *f, apr_bucket_brigade *new_bb)
{
conn_rec *c = f->c;
core_net_rec *net = f->ctx;
core_output_filter_ctx_t *ctx = net->out_ctx;
apr_bucket_brigade *bb = NULL;
- apr_bucket *bucket, *next, *flush_upto = NULL;
- apr_size_t bytes_in_brigade, non_file_bytes_in_brigade;
- int eor_buckets_in_brigade, morphing_bucket_in_brigade;
- apr_status_t rv;
- int loglevel = ap_get_conn_module_loglevel(c, APLOG_MODULE_INDEX);
+ apr_status_t rv = APR_SUCCESS;
/* Fail quickly if the connection has already been aborted. */
if (c->aborted) {
@@ -392,12 +470,10 @@ apr_status_t ap_core_output_filter(ap_filter_t *f, apr_bucket_brigade *new_bb)
ctx = apr_pcalloc(c->pool, sizeof(*ctx));
net->out_ctx = (core_output_filter_ctx_t *)ctx;
/*
- * Need to create tmp brigade with correct lifetime. Passing
- * NULL to apr_brigade_split_ex would result in a brigade
+ * Need to create buffered_bb brigade with correct lifetime. Passing
+ * NULL to ap_save_brigade() would result in a brigade
* allocated from bb->pool which might be wrong.
*/
- ctx->tmp_flush_bb = apr_brigade_create(c->pool, c->bucket_alloc);
- /* same for buffered_bb and ap_save_brigade */
ctx->buffered_bb = apr_brigade_create(c->pool, c->bucket_alloc);
}
@@ -412,191 +488,62 @@ apr_status_t ap_core_output_filter(ap_filter_t *f, apr_bucket_brigade *new_bb)
else {
bb = ctx->buffered_bb;
}
- c->data_in_output_filters = 0;
}
else if (new_bb == NULL) {
+ c->data_in_output_filters = 0;
return APR_SUCCESS;
}
- /* Scan through the brigade and decide whether to attempt a write,
- * and how much to write, based on the following rules:
- *
- * 1) The new_bb is null: Do a nonblocking write of as much as
- * possible: do a nonblocking write of as much data as possible,
- * then save the rest in ctx->buffered_bb. (If new_bb == NULL,
- * it probably means that the MPM is doing asynchronous write
- * completion and has just determined that this connection
- * is writable.)
- *
- * 2) Determine if and up to which bucket we need to do a blocking
- * write:
- *
- * a) The brigade contains a flush bucket: Do a blocking write
- * of everything up that point.
- *
- * b) The request is in CONN_STATE_HANDLER state, and the brigade
- * contains at least THRESHOLD_MAX_BUFFER bytes in non-file
- * buckets: Do blocking writes until the amount of data in the
- * buffer is less than THRESHOLD_MAX_BUFFER. (The point of this
- * rule is to provide flow control, in case a handler is
- * streaming out lots of data faster than the data can be
- * sent to the client.)
- *
- * c) The request is in CONN_STATE_HANDLER state, and the brigade
- * contains at least MAX_REQUESTS_IN_PIPELINE EOR buckets:
- * Do blocking writes until less than MAX_REQUESTS_IN_PIPELINE EOR
- * buckets are left. (The point of this rule is to prevent too many
- * FDs being kept open by pipelined requests, possibly allowing a
- * DoS).
- *
- * d) The brigade contains a morphing bucket: If there was no other
- * reason to do a blocking write yet, try reading the bucket. If its
- * contents fit into memory before THRESHOLD_MAX_BUFFER is reached,
- * everything is fine. Otherwise we need to do a blocking write the
- * up to and including the morphing bucket, because ap_save_brigade()
- * would read the whole bucket into memory later on.
- *
- * 3) Actually do the blocking write up to the last bucket determined
- * by rules 2a-d. The point of doing only one flush is to make as
- * few calls to writev() as possible.
- *
- * 4) If the brigade contains at least THRESHOLD_MIN_WRITE
- * bytes: Do a nonblocking write of as much data as possible,
- * then save the rest in ctx->buffered_bb.
- */
-
- if (new_bb == NULL) {
- rv = send_brigade_nonblocking(net->client_socket, bb,
- &(ctx->bytes_written), c);
- if (rv != APR_SUCCESS && !APR_STATUS_IS_EAGAIN(rv)) {
- /* The client has aborted the connection */
- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, rv, c,
- "core_output_filter: writing data to the network");
- apr_brigade_cleanup(bb);
- c->aborted = 1;
- return rv;
- }
- setaside_remaining_output(f, ctx, bb, c);
- return APR_SUCCESS;
- }
-
- bytes_in_brigade = 0;
- non_file_bytes_in_brigade = 0;
- eor_buckets_in_brigade = 0;
- morphing_bucket_in_brigade = 0;
+ if (!new_bb || should_send_brigade(bb, c, NULL)) {
+ apr_socket_t *sock = net->client_socket;
+ apr_interval_time_t sock_timeout = 0;
- for (bucket = APR_BRIGADE_FIRST(bb); bucket != APR_BRIGADE_SENTINEL(bb);
- bucket = next) {
- next = APR_BUCKET_NEXT(bucket);
+ /* Non-blocking writes on the socket in any case. */
+ apr_socket_timeout_get(sock, &sock_timeout);
+ apr_socket_timeout_set(sock, 0);
- if (!APR_BUCKET_IS_METADATA(bucket)) {
- if (bucket->length == (apr_size_t)-1) {
- /*
- * A setaside of morphing buckets would read everything into
- * memory. Instead, we will flush everything up to and
- * including this bucket.
+ do {
+ rv = send_brigade_nonblocking(sock, bb, ctx, c);
+ if (new_bb && APR_STATUS_IS_EAGAIN(rv)) {
+ /* Scan through the brigade and decide whether we must absolutely
+ * flush the remaining data, based on should_send_brigade() &flush
+ * rules. If so, wait for writability and retry, otherwise we did
+ * our best already and can wait for the next call.
*/
- morphing_bucket_in_brigade = 1;
- }
- else {
- bytes_in_brigade += bucket->length;
- if (!APR_BUCKET_IS_FILE(bucket))
- non_file_bytes_in_brigade += bucket->length;
- }
- }
- else if (AP_BUCKET_IS_EOR(bucket)) {
- eor_buckets_in_brigade++;
- }
-
- if (APR_BUCKET_IS_FLUSH(bucket)
- || non_file_bytes_in_brigade >= THRESHOLD_MAX_BUFFER
- || morphing_bucket_in_brigade
- || eor_buckets_in_brigade > MAX_REQUESTS_IN_PIPELINE) {
- /* this segment of the brigade MUST be sent before returning. */
-
- if (loglevel >= APLOG_TRACE6) {
- char *reason = APR_BUCKET_IS_FLUSH(bucket) ?
- "FLUSH bucket" :
- (non_file_bytes_in_brigade >= THRESHOLD_MAX_BUFFER) ?
- "THRESHOLD_MAX_BUFFER" :
- morphing_bucket_in_brigade ? "morphing bucket" :
- "MAX_REQUESTS_IN_PIPELINE";
- ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, c,
- "will flush because of %s", reason);
- ap_log_cerror(APLOG_MARK, APLOG_TRACE8, 0, c,
- "seen in brigade%s: bytes: %" APR_SIZE_T_FMT
- ", non-file bytes: %" APR_SIZE_T_FMT ", eor "
- "buckets: %d, morphing buckets: %d",
- flush_upto == NULL ? " so far"
- : " since last flush point",
- bytes_in_brigade,
- non_file_bytes_in_brigade,
- eor_buckets_in_brigade,
- morphing_bucket_in_brigade);
+ int flush;
+ (void)should_send_brigade(bb, c, &flush);
+ if (flush) {
+ apr_int32_t nfd;
+ apr_pollfd_t pfd;
+ memset(&pfd, 0, sizeof(pfd));
+ pfd.reqevents = APR_POLLOUT;
+ pfd.desc_type = APR_POLL_SOCKET;
+ pfd.desc.s = sock;
+ pfd.p = c->pool;
+ do {
+ rv = apr_poll(&pfd, 1, &nfd, sock_timeout);
+ } while (APR_STATUS_IS_EINTR(rv));
+ }
}
- /*
- * Defer the actual blocking write to avoid doing many writes.
- */
- flush_upto = next;
-
- bytes_in_brigade = 0;
- non_file_bytes_in_brigade = 0;
- eor_buckets_in_brigade = 0;
- morphing_bucket_in_brigade = 0;
- }
- }
-
- if (flush_upto != NULL) {
- ctx->tmp_flush_bb = apr_brigade_split_ex(bb, flush_upto,
- ctx->tmp_flush_bb);
- if (loglevel >= APLOG_TRACE8) {
- ap_log_cerror(APLOG_MARK, APLOG_TRACE8, 0, c,
- "flushing now");
- }
- rv = send_brigade_blocking(net->client_socket, bb,
- &(ctx->bytes_written), c);
- if (rv != APR_SUCCESS) {
- /* The client has aborted the connection */
- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, rv, c,
- "core_output_filter: writing data to the network");
- apr_brigade_cleanup(bb);
- c->aborted = 1;
- return rv;
- }
- if (loglevel >= APLOG_TRACE8) {
- ap_log_cerror(APLOG_MARK, APLOG_TRACE8, 0, c,
- "total bytes written: %" APR_SIZE_T_FMT,
- ctx->bytes_written);
- }
- APR_BRIGADE_CONCAT(bb, ctx->tmp_flush_bb);
- }
+ } while (rv == APR_SUCCESS && !APR_BRIGADE_EMPTY(bb));
- if (loglevel >= APLOG_TRACE8) {
- ap_log_cerror(APLOG_MARK, APLOG_TRACE8, 0, c,
- "brigade contains: bytes: %" APR_SIZE_T_FMT
- ", non-file bytes: %" APR_SIZE_T_FMT
- ", eor buckets: %d, morphing buckets: %d",
- bytes_in_brigade, non_file_bytes_in_brigade,
- eor_buckets_in_brigade, morphing_bucket_in_brigade);
+ /* Restore original socket timeout before leaving. */
+ apr_socket_timeout_set(sock, sock_timeout);
}
- if (bytes_in_brigade >= THRESHOLD_MIN_WRITE) {
- rv = send_brigade_nonblocking(net->client_socket, bb,
- &(ctx->bytes_written), c);
- if ((rv != APR_SUCCESS) && (!APR_STATUS_IS_EAGAIN(rv))) {
- /* The client has aborted the connection */
- ap_log_cerror(APLOG_MARK, APLOG_TRACE1, rv, c,
- "core_output_filter: writing data to the network");
- apr_brigade_cleanup(bb);
- c->aborted = 1;
- return rv;
- }
- if (loglevel >= APLOG_TRACE8) {
- ap_log_cerror(APLOG_MARK, APLOG_TRACE8, 0, c,
- "tried nonblocking write, total bytes "
- "written: %" APR_SIZE_T_FMT,
- ctx->bytes_written);
- }
+ if (rv != APR_SUCCESS && !APR_STATUS_IS_EAGAIN(rv)) {
+ /* The client has aborted the connection */
+ ap_log_cerror(
+ APLOG_MARK, APLOG_TRACE1, rv, c,
+ "core_output_filter: writing data to the network");
+ /*
+ * Set c->aborted before apr_brigade_cleanup to have the correct status
+ * when logging the request as apr_brigade_cleanup triggers the logging
+ * of the request if it contains an EOR bucket.
+ */
+ c->aborted = 1;
+ apr_brigade_cleanup(bb);
+ return rv;
}
setaside_remaining_output(f, ctx, bb, c);
@@ -612,10 +559,17 @@ static void setaside_remaining_output(ap_filter_t *f,
apr_bucket_brigade *bb,
conn_rec *c)
{
- if (bb == NULL) {
- return;
+ apr_bucket *bucket;
+
+ /* Don't set aside leading empty buckets, all previous data have been
+ * consumed so it's safe to delete them now.
+ */
+ while (((bucket = APR_BRIGADE_FIRST(bb)) != APR_BRIGADE_SENTINEL(bb)) &&
+ (APR_BUCKET_IS_METADATA(bucket) || (bucket->length == 0))) {
+ apr_bucket_delete(bucket);
}
- remove_empty_buckets(bb);
+
+ c->data_in_output_filters = 0;
if (!APR_BRIGADE_EMPTY(bb)) {
c->data_in_output_filters = 1;
if (bb != ctx->buffered_bb) {
@@ -637,285 +591,276 @@ static void setaside_remaining_output(ap_filter_t *f,
}
#ifndef APR_MAX_IOVEC_SIZE
-#define MAX_IOVEC_TO_WRITE 16
+#define NVEC_MIN 16
+#define NVEC_MAX NVEC_MIN
#else
#if APR_MAX_IOVEC_SIZE > 16
-#define MAX_IOVEC_TO_WRITE 16
+#define NVEC_MIN 16
#else
-#define MAX_IOVEC_TO_WRITE APR_MAX_IOVEC_SIZE
+#define NVEC_MIN APR_MAX_IOVEC_SIZE
+#endif
+#define NVEC_MAX APR_MAX_IOVEC_SIZE
+#endif
+
+static APR_INLINE int is_in_memory_bucket(apr_bucket *b)
+{
+ /* These buckets' data are already in memory. */
+ return APR_BUCKET_IS_HEAP(b)
+ || APR_BUCKET_IS_POOL(b)
+ || APR_BUCKET_IS_TRANSIENT(b)
+ || APR_BUCKET_IS_IMMORTAL(b);
+}
+
+#if APR_HAS_SENDFILE
+static APR_INLINE int can_sendfile_bucket(apr_bucket *b)
+{
+ /* Use sendfile to send the bucket unless:
+ * - the bucket is not a file bucket, or
+ * - the file is too small for sendfile to be useful, or
+ * - sendfile is disabled in the httpd config via "EnableSendfile off".
+ */
+ if (APR_BUCKET_IS_FILE(b) && b->length >= AP_MIN_SENDFILE_BYTES) {
+ apr_file_t *file = ((apr_bucket_file *)b->data)->fd;
+ return apr_file_flags_get(file) & APR_SENDFILE_ENABLED;
+ }
+ else {
+ return 0;
+ }
+}
#endif
+
+#if defined(WIN32) && (APR_MAJOR_VERSION == 1 && APR_MINOR_VERSION <= 7)
+#undef APR_TCP_NOPUSH_FLAG
+#define APR_TCP_NOPUSH_FLAG 0
+#endif
+
+static APR_INLINE void sock_nopush(apr_socket_t *s, int to)
+{
+ /* Disable TCP_NOPUSH handling on OSX since unsetting it won't push
+ * retained data, which might introduce delays if further data don't
+ * come soon enough or cause the last chunk to be sent only when the
+ * connection is shutdown (e.g. after KeepAliveTimeout).
+ */
+#if APR_TCP_NOPUSH_FLAG && !defined(__APPLE__)
+ (void)apr_socket_opt_set(s, APR_TCP_NOPUSH, to);
#endif
+}
static apr_status_t send_brigade_nonblocking(apr_socket_t *s,
apr_bucket_brigade *bb,
- apr_size_t *bytes_written,
+ core_output_filter_ctx_t *ctx,
conn_rec *c)
{
+ apr_status_t rv = APR_SUCCESS;
+ core_server_config *conf =
+ ap_get_core_module_config(c->base_server->module_config);
+ apr_size_t nvec = 0, nbytes = 0;
apr_bucket *bucket, *next;
- apr_status_t rv;
- struct iovec vec[MAX_IOVEC_TO_WRITE];
- apr_size_t nvec = 0;
-
- remove_empty_buckets(bb);
+ const char *data;
+ apr_size_t length;
for (bucket = APR_BRIGADE_FIRST(bb);
bucket != APR_BRIGADE_SENTINEL(bb);
bucket = next) {
next = APR_BUCKET_NEXT(bucket);
-#if APR_HAS_SENDFILE
- if (APR_BUCKET_IS_FILE(bucket)) {
- apr_bucket_file *file_bucket = (apr_bucket_file *)(bucket->data);
- apr_file_t *fd = file_bucket->fd;
- /* Use sendfile to send this file unless:
- * - the platform doesn't support sendfile,
- * - the file is too small for sendfile to be useful, or
- * - sendfile is disabled in the httpd config via "EnableSendfile off"
- */
- if ((apr_file_flags_get(fd) & APR_SENDFILE_ENABLED) &&
- (bucket->length >= AP_MIN_SENDFILE_BYTES)) {
- if (nvec > 0) {
- (void)apr_socket_opt_set(s, APR_TCP_NOPUSH, 1);
- rv = writev_nonblocking(s, vec, nvec, bb, bytes_written, c);
- if (rv != APR_SUCCESS) {
- (void)apr_socket_opt_set(s, APR_TCP_NOPUSH, 0);
- return rv;
- }
- }
- rv = sendfile_nonblocking(s, bucket, bytes_written, c);
- if (nvec > 0) {
- (void)apr_socket_opt_set(s, APR_TCP_NOPUSH, 0);
- nvec = 0;
- }
+#if APR_HAS_SENDFILE
+ if (can_sendfile_bucket(bucket)) {
+ if (nvec > 0) {
+ sock_nopush(s, 1);
+ rv = writev_nonblocking(s, bb, ctx, nbytes, nvec, c);
if (rv != APR_SUCCESS) {
- return rv;
+ goto cleanup;
}
- break;
+ nbytes = 0;
+ nvec = 0;
}
+ rv = sendfile_nonblocking(s, bucket, ctx, c);
+ if (rv != APR_SUCCESS) {
+ goto cleanup;
+ }
+ continue;
}
#endif /* APR_HAS_SENDFILE */
- /* didn't sendfile */
- if (!APR_BUCKET_IS_METADATA(bucket)) {
- const char *data;
- apr_size_t length;
-
+
+ if (bucket->length) {
/* Non-blocking read first, in case this is a morphing
* bucket type. */
rv = apr_bucket_read(bucket, &data, &length, APR_NONBLOCK_READ);
if (APR_STATUS_IS_EAGAIN(rv)) {
/* Read would block; flush any pending data and retry. */
if (nvec) {
- rv = writev_nonblocking(s, vec, nvec, bb, bytes_written, c);
- if (rv) {
- return rv;
+ rv = writev_nonblocking(s, bb, ctx, nbytes, nvec, c);
+ if (rv != APR_SUCCESS) {
+ goto cleanup;
}
+ nbytes = 0;
nvec = 0;
}
-
+ sock_nopush(s, 0);
+
rv = apr_bucket_read(bucket, &data, &length, APR_BLOCK_READ);
}
if (rv != APR_SUCCESS) {
- return rv;
+ goto cleanup;
}
/* reading may have split the bucket, so recompute next: */
next = APR_BUCKET_NEXT(bucket);
- vec[nvec].iov_base = (char *)data;
- vec[nvec].iov_len = length;
- nvec++;
- if (nvec == MAX_IOVEC_TO_WRITE) {
- rv = writev_nonblocking(s, vec, nvec, bb, bytes_written, c);
- nvec = 0;
- if (rv != APR_SUCCESS) {
- return rv;
- }
- break;
- }
}
- }
- if (nvec > 0) {
- rv = writev_nonblocking(s, vec, nvec, bb, bytes_written, c);
- if (rv != APR_SUCCESS) {
- return rv;
+ if (!bucket->length) {
+ /* Don't delete empty buckets until all the previous ones have been
+ * sent (nvec == 0); this must happen in sequence since metabuckets
+ * like EOR could free the data still pointed to by the iovec. So
+ * unless the latter is empty, let writev_nonblocking() cleanup the
+ * brigade in order.
+ */
+ if (!nvec) {
+ apr_bucket_delete(bucket);
+ }
}
- }
-
- remove_empty_buckets(bb);
-
- return APR_SUCCESS;
-}
-
-static void remove_empty_buckets(apr_bucket_brigade *bb)
-{
- apr_bucket *bucket;
- while (((bucket = APR_BRIGADE_FIRST(bb)) != APR_BRIGADE_SENTINEL(bb)) &&
- (APR_BUCKET_IS_METADATA(bucket) || (bucket->length == 0))) {
- apr_bucket_delete(bucket);
- }
-}
-
-static apr_status_t send_brigade_blocking(apr_socket_t *s,
- apr_bucket_brigade *bb,
- apr_size_t *bytes_written,
- conn_rec *c)
-{
- apr_status_t rv;
-
- rv = APR_SUCCESS;
- while (!APR_BRIGADE_EMPTY(bb)) {
- rv = send_brigade_nonblocking(s, bb, bytes_written, c);
- if (rv != APR_SUCCESS) {
- if (APR_STATUS_IS_EAGAIN(rv)) {
- /* Wait until we can send more data */
- apr_int32_t nsds;
- apr_interval_time_t timeout;
- apr_pollfd_t pollset;
-
- pollset.p = c->pool;
- pollset.desc_type = APR_POLL_SOCKET;
- pollset.reqevents = APR_POLLOUT;
- pollset.desc.s = s;
- apr_socket_timeout_get(s, &timeout);
- do {
- rv = apr_poll(&pollset, 1, &nsds, timeout);
- } while (APR_STATUS_IS_EINTR(rv));
- if (rv != APR_SUCCESS) {
- break;
+ else {
+ /* Make sure that these new data fit in our iovec. */
+ if (nvec == ctx->nvec) {
+ if (nvec == NVEC_MAX) {
+ sock_nopush(s, 1);
+ rv = writev_nonblocking(s, bb, ctx, nbytes, nvec, c);
+ if (rv != APR_SUCCESS) {
+ goto cleanup;
+ }
+ nbytes = 0;
+ nvec = 0;
+ }
+ else {
+ struct iovec *newvec;
+ apr_size_t newn = nvec * 2;
+ if (newn < NVEC_MIN) {
+ newn = NVEC_MIN;
+ }
+ else if (newn > NVEC_MAX) {
+ newn = NVEC_MAX;
+ }
+ newvec = apr_palloc(c->pool, newn * sizeof(struct iovec));
+ if (nvec) {
+ memcpy(newvec, ctx->vec, nvec * sizeof(struct iovec));
+ }
+ ctx->vec = newvec;
+ ctx->nvec = newn;
}
}
- else {
- break;
+ nbytes += length;
+ ctx->vec[nvec].iov_base = (void *)data;
+ ctx->vec[nvec].iov_len = length;
+ nvec++;
+ }
+
+ /* Flush above max threshold, unless the brigade still contains in
+ * memory buckets which we want to try writing in the same pass (if
+ * we are at the end of the brigade, the write will happen outside
+ * the loop anyway).
+ */
+ if (nbytes > conf->flush_max_threshold
+ && next != APR_BRIGADE_SENTINEL(bb)
+ && next->length && !is_in_memory_bucket(next)) {
+ sock_nopush(s, 1);
+ rv = writev_nonblocking(s, bb, ctx, nbytes, nvec, c);
+ if (rv != APR_SUCCESS) {
+ goto cleanup;
}
+ nbytes = 0;
+ nvec = 0;
}
}
+ if (nvec > 0) {
+ rv = writev_nonblocking(s, bb, ctx, nbytes, nvec, c);
+ }
+
+cleanup:
+ sock_nopush(s, 0);
return rv;
}
static apr_status_t writev_nonblocking(apr_socket_t *s,
- struct iovec *vec, apr_size_t nvec,
apr_bucket_brigade *bb,
- apr_size_t *cumulative_bytes_written,
+ core_output_filter_ctx_t *ctx,
+ apr_size_t bytes_to_write,
+ apr_size_t nvec,
conn_rec *c)
{
- apr_status_t rv = APR_SUCCESS, arv;
- apr_size_t bytes_written = 0, bytes_to_write = 0;
- apr_size_t i, offset;
- apr_interval_time_t old_timeout;
-
- arv = apr_socket_timeout_get(s, &old_timeout);
- if (arv != APR_SUCCESS) {
- return arv;
- }
- arv = apr_socket_timeout_set(s, 0);
- if (arv != APR_SUCCESS) {
- return arv;
- }
+ apr_status_t rv;
+ struct iovec *vec = ctx->vec;
+ apr_size_t bytes_written = 0;
+ apr_size_t i, offset = 0;
- for (i = 0; i < nvec; i++) {
- bytes_to_write += vec[i].iov_len;
- }
- offset = 0;
- while (bytes_written < bytes_to_write) {
+ do {
apr_size_t n = 0;
rv = apr_socket_sendv(s, vec + offset, nvec - offset, &n);
- if (n > 0) {
- bytes_written += n;
- for (i = offset; i < nvec; ) {
- apr_bucket *bucket = APR_BRIGADE_FIRST(bb);
- if (APR_BUCKET_IS_METADATA(bucket)) {
- apr_bucket_delete(bucket);
- }
- else if (n >= vec[i].iov_len) {
- apr_bucket_delete(bucket);
- offset++;
- n -= vec[i++].iov_len;
- }
- else {
+ bytes_written += n;
+
+ for (i = offset; i < nvec; ) {
+ apr_bucket *bucket = APR_BRIGADE_FIRST(bb);
+ if (!bucket->length) {
+ apr_bucket_delete(bucket);
+ }
+ else if (n >= vec[i].iov_len) {
+ apr_bucket_delete(bucket);
+ n -= vec[i++].iov_len;
+ offset++;
+ }
+ else {
+ if (n) {
apr_bucket_split(bucket, n);
apr_bucket_delete(bucket);
vec[i].iov_len -= n;
vec[i].iov_base = (char *) vec[i].iov_base + n;
- break;
}
+ break;
}
}
- if (rv != APR_SUCCESS) {
- break;
- }
- }
+ } while (rv == APR_SUCCESS && bytes_written < bytes_to_write);
+
if ((ap__logio_add_bytes_out != NULL) && (bytes_written > 0)) {
ap__logio_add_bytes_out(c, bytes_written);
}
- *cumulative_bytes_written += bytes_written;
+ ctx->bytes_written += bytes_written;
- arv = apr_socket_timeout_set(s, old_timeout);
- if ((arv != APR_SUCCESS) && (rv == APR_SUCCESS)) {
- return arv;
- }
- else {
- return rv;
- }
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE6, rv, c,
+ "writev_nonblocking: %"APR_SIZE_T_FMT"/%"APR_SIZE_T_FMT,
+ bytes_written, bytes_to_write);
+ return rv;
}
#if APR_HAS_SENDFILE
static apr_status_t sendfile_nonblocking(apr_socket_t *s,
apr_bucket *bucket,
- apr_size_t *cumulative_bytes_written,
+ core_output_filter_ctx_t *ctx,
conn_rec *c)
{
- apr_status_t rv = APR_SUCCESS;
- apr_bucket_file *file_bucket;
- apr_file_t *fd;
- apr_size_t file_length;
- apr_off_t file_offset;
- apr_size_t bytes_written = 0;
+ apr_status_t rv;
+ apr_file_t *file = ((apr_bucket_file *)bucket->data)->fd;
+ apr_size_t bytes_written = bucket->length; /* bytes_to_write for now */
+ apr_off_t file_offset = bucket->start;
- if (!APR_BUCKET_IS_FILE(bucket)) {
- ap_log_error(APLOG_MARK, APLOG_ERR, rv, c->base_server, APLOGNO(00006)
- "core_filter: sendfile_nonblocking: "
- "this should never happen");
- return APR_EGENERAL;
- }
- file_bucket = (apr_bucket_file *)(bucket->data);
- fd = file_bucket->fd;
- file_length = bucket->length;
- file_offset = bucket->start;
-
- if (bytes_written < file_length) {
- apr_size_t n = file_length - bytes_written;
- apr_status_t arv;
- apr_interval_time_t old_timeout;
-
- arv = apr_socket_timeout_get(s, &old_timeout);
- if (arv != APR_SUCCESS) {
- return arv;
- }
- arv = apr_socket_timeout_set(s, 0);
- if (arv != APR_SUCCESS) {
- return arv;
- }
- rv = apr_socket_sendfile(s, fd, NULL, &file_offset, &n, 0);
- if (rv == APR_SUCCESS) {
- bytes_written += n;
- file_offset += n;
- }
- arv = apr_socket_timeout_set(s, old_timeout);
- if ((arv != APR_SUCCESS) && (rv == APR_SUCCESS)) {
- rv = arv;
- }
- }
+ rv = apr_socket_sendfile(s, file, NULL, &file_offset, &bytes_written, 0);
if ((ap__logio_add_bytes_out != NULL) && (bytes_written > 0)) {
ap__logio_add_bytes_out(c, bytes_written);
}
- *cumulative_bytes_written += bytes_written;
- if ((bytes_written < file_length) && (bytes_written > 0)) {
- apr_bucket_split(bucket, bytes_written);
+ ctx->bytes_written += bytes_written;
+
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE6, rv, c,
+ "sendfile_nonblocking: %" APR_SIZE_T_FMT "/%" APR_SIZE_T_FMT,
+ bytes_written, bucket->length);
+ if (bytes_written >= bucket->length) {
apr_bucket_delete(bucket);
}
- else if (bytes_written == file_length) {
+ else if (bytes_written > 0) {
+ apr_bucket_split(bucket, bytes_written);
apr_bucket_delete(bucket);
+ if (rv == APR_SUCCESS) {
+ rv = APR_EAGAIN;
+ }
}
return rv;
}
diff --git a/server/eor_bucket.c b/server/eor_bucket.c
index 4d3e1ec..ecb809c 100644
--- a/server/eor_bucket.c
+++ b/server/eor_bucket.c
@@ -19,17 +19,22 @@
#include "http_protocol.h"
#include "scoreboard.h"
+typedef struct {
+ apr_bucket_refcount refcount;
+ request_rec *data;
+} ap_bucket_eor;
+
static apr_status_t eor_bucket_cleanup(void *data)
{
- apr_bucket *b = (apr_bucket *)data;
- request_rec *r = (request_rec *)b->data;
+ request_rec **rp = data;
- if (r != NULL) {
+ if (*rp) {
+ request_rec *r = *rp;
/*
* If eor_bucket_destroy is called after us, this prevents
* eor_bucket_destroy from trying to destroy the pool again.
*/
- b->data = NULL;
+ *rp = NULL;
/* Update child status and log the transaction */
ap_update_child_status(r->connection->sbh, SERVER_BUSY_LOG, r);
ap_run_log_transaction(r);
@@ -50,11 +55,13 @@ static apr_status_t eor_bucket_read(apr_bucket *b, const char **str,
AP_DECLARE(apr_bucket *) ap_bucket_eor_make(apr_bucket *b, request_rec *r)
{
- b->length = 0;
- b->start = 0;
- b->data = r;
- b->type = &ap_bucket_type_eor;
+ ap_bucket_eor *h;
+
+ h = apr_bucket_alloc(sizeof(*h), b->list);
+ h->data = r;
+ b = apr_bucket_shared_make(b, h, 0, 0);
+ b->type = &ap_bucket_type_eor;
return b;
}
@@ -66,7 +73,9 @@ AP_DECLARE(apr_bucket *) ap_bucket_eor_create(apr_bucket_alloc_t *list,
APR_BUCKET_INIT(b);
b->free = apr_bucket_free;
b->list = list;
+ b = ap_bucket_eor_make(b, r);
if (r) {
+ ap_bucket_eor *h = b->data;
/*
* Register a cleanup for the request pool as the eor bucket could
* have been allocated from a different pool then the request pool
@@ -76,18 +85,22 @@ AP_DECLARE(apr_bucket *) ap_bucket_eor_create(apr_bucket_alloc_t *list,
* We need to use a pre-cleanup here because a module may create a
* sub-pool which is still needed during the log_transaction hook.
*/
- apr_pool_pre_cleanup_register(r->pool, (void *)b, eor_bucket_cleanup);
+ apr_pool_pre_cleanup_register(r->pool, &h->data, eor_bucket_cleanup);
}
- return ap_bucket_eor_make(b, r);
+ return b;
}
static void eor_bucket_destroy(void *data)
{
- request_rec *r = (request_rec *)data;
+ ap_bucket_eor *h = data;
- if (r) {
- /* eor_bucket_cleanup will be called when the pool gets destroyed */
- apr_pool_destroy(r->pool);
+ if (apr_bucket_shared_destroy(h)) {
+ request_rec *r = h->data;
+ if (r) {
+ /* eor_bucket_cleanup will be called when the pool gets destroyed */
+ apr_pool_destroy(r->pool);
+ }
+ apr_bucket_free(h);
}
}
@@ -97,6 +110,6 @@ AP_DECLARE_DATA const apr_bucket_type_t ap_bucket_type_eor = {
eor_bucket_read,
apr_bucket_setaside_noop,
apr_bucket_split_notimpl,
- apr_bucket_simple_copy
+ apr_bucket_shared_copy
};
diff --git a/server/gen_test_char.c b/server/gen_test_char.c
index 48ae6f4..248216b 100644
--- a/server/gen_test_char.c
+++ b/server/gen_test_char.c
@@ -54,6 +54,7 @@
#define T_ESCAPE_URLENCODED (0x40)
#define T_HTTP_CTRLS (0x80)
#define T_VCHAR_OBSTEXT (0x100)
+#define T_URI_UNRESERVED (0x200)
int main(int argc, char *argv[])
{
@@ -71,6 +72,7 @@ int main(int argc, char *argv[])
"#define T_ESCAPE_URLENCODED (%u)\n"
"#define T_HTTP_CTRLS (%u)\n"
"#define T_VCHAR_OBSTEXT (%u)\n"
+ "#define T_URI_UNRESERVED (%u)\n"
"\n"
"static const unsigned short test_char_table[256] = {",
T_ESCAPE_SHELL_CMD,
@@ -81,7 +83,9 @@ int main(int argc, char *argv[])
T_ESCAPE_FORENSIC,
T_ESCAPE_URLENCODED,
T_HTTP_CTRLS,
- T_VCHAR_OBSTEXT);
+ T_VCHAR_OBSTEXT,
+ T_URI_UNRESERVED
+ );
for (c = 0; c < 256; ++c) {
flags = 0;
@@ -123,7 +127,7 @@ int main(int argc, char *argv[])
/* Stop for any non-'token' character, including ctrls, obs-text,
* and "tspecials" (RFC2068) a.k.a. "separators" (RFC2616), which
- * is easer to express as characters remaining in the ASCII token set
+ * is easier to express as characters remaining in the ASCII token set
*/
if (!c || !(apr_isalnum(c) || strchr("!#$%&'*+-.^_`|~", c))) {
flags |= T_HTTP_TOKEN_STOP;
@@ -164,10 +168,25 @@ int main(int argc, char *argv[])
flags |= T_ESCAPE_FORENSIC;
}
+ /* Characters in the RFC 3986 "unreserved" set.
+ * https://datatracker.ietf.org/doc/html/rfc3986#section-2.3 */
+ if (c && (apr_isalnum(c) || strchr("-._~", c))) {
+ flags |= T_URI_UNRESERVED;
+ }
+
printf("0x%03x%c", flags, (c < 255) ? ',' : ' ');
}
- printf("\n};\n");
+ printf("\n};\n\n");
+
+ printf(
+ "/* we assume the folks using this ensure 0 <= c < 256... which means\n"
+ " * you need a cast to (unsigned char) first, you can't just plug a\n"
+ " * char in here and get it to work, because if char is signed then it\n"
+ " * will first be sign extended.\n"
+ " */\n"
+ "#define TEST_CHAR(c, f) (test_char_table[(unsigned char)(c)] & (f))\n"
+ );
return 0;
}
diff --git a/server/listen.c b/server/listen.c
index a8e9e6f..5242c2a 100644
--- a/server/listen.c
+++ b/server/listen.c
@@ -784,7 +784,7 @@ AP_DECLARE(void) ap_listen_pre_config(void)
*
* *BSDs have SO_REUSEPORT too but with a different semantic: the first
* wildcard address bound socket or the last non-wildcard address bound
- * socket will receive connections (no evenness garantee); the rest of
+ * socket will receive connections (no evenness guarantee); the rest of
* the sockets bound to the same port will not.
* This can't (always) work for httpd.
*
diff --git a/server/log.c b/server/log.c
index 42d0b8f..22d2f8d 100644
--- a/server/log.c
+++ b/server/log.c
@@ -55,7 +55,7 @@
#include "ap_mpm.h"
#include "ap_listen.h"
-#if HAVE_GETTID
+#ifdef HAVE_SYS_GETTID
#include <sys/syscall.h>
#include <sys/types.h>
#endif
@@ -276,7 +276,7 @@ AP_DECLARE(apr_status_t) ap_replace_stderr_log(apr_pool_t *p,
/*
* You might ponder why stderr_pool should survive?
* The trouble is, stderr_pool may have s_main->error_log,
- * so we aren't in a position to destory stderr_pool until
+ * so we aren't in a position to destroy stderr_pool until
* the next recycle. There's also an apparent bug which
* is not; if some folk decided to call this function before
* the core open error logs hook, this pool won't survive.
@@ -303,7 +303,7 @@ static void log_child_errfn(apr_pool_t *pool, apr_status_t err,
}
/* Create a child process running PROGNAME with a pipe connected to
- * the childs stdin. The write-end of the pipe will be placed in
+ * the child's stdin. The write-end of the pipe will be placed in
* *FPIN on successful return. If dummy_stderr is non-zero, the
* stderr for the child will be the same as the stdout of the parent.
* Otherwise the child will inherit the stderr from the parent. */
@@ -341,8 +341,10 @@ static int log_child(apr_pool_t *p, const char *progname,
rc = apr_procattr_child_err_set(procattr, errfile, NULL);
}
- rc = apr_proc_create(procnew, args[0], (const char * const *)args,
- NULL, procattr, p);
+ if (rc == APR_SUCCESS) {
+ rc = apr_proc_create(procnew, args[0], (const char * const *)args,
+ NULL, procattr, p);
+ }
if (rc == APR_SUCCESS) {
apr_pool_note_subprocess(p, procnew, APR_KILL_AFTER_TIMEOUT);
@@ -625,14 +627,18 @@ static int log_tid(const ap_errorlog_info *info, const char *arg,
#if APR_HAS_THREADS
int result;
#endif
-#if HAVE_GETTID
+#if defined(HAVE_GETTID) || defined(HAVE_SYS_GETTID)
if (arg && *arg == 'g') {
+#ifdef HAVE_GETTID
+ pid_t tid = gettid();
+#else
pid_t tid = syscall(SYS_gettid);
+#endif
if (tid == -1)
return 0;
return apr_snprintf(buf, buflen, "%"APR_PID_T_FMT, tid);
}
-#endif
+#endif /* HAVE_GETTID || HAVE_SYS_GETTID */
#if APR_HAS_THREADS
if (ap_mpm_query(AP_MPMQ_IS_THREADED, &result) == APR_SUCCESS
&& result != AP_MPMQ_NOT_SUPPORTED)
@@ -650,14 +656,32 @@ static int log_ctime(const ap_errorlog_info *info, const char *arg,
int time_len = buflen;
int option = AP_CTIME_OPTION_NONE;
- while (arg && *arg) {
- switch (*arg) {
- case 'u': option |= AP_CTIME_OPTION_USEC;
- break;
- case 'c': option |= AP_CTIME_OPTION_COMPACT;
- break;
+ if (arg) {
+ if (arg[0] == 'u' && !arg[1]) { /* no ErrorLogFormat (fast path) */
+ option |= AP_CTIME_OPTION_USEC;
+ }
+ else if (!ap_strchr_c(arg, '%')) { /* special "%{cuz}t" formats */
+ while (*arg) {
+ switch (*arg++) {
+ case 'u':
+ option |= AP_CTIME_OPTION_USEC;
+ break;
+ case 'c':
+ option |= AP_CTIME_OPTION_COMPACT;
+ break;
+ case 'z':
+ option |= AP_CTIME_OPTION_GMTOFF;
+ break;
+ }
+ }
+ }
+ else { /* "%{strftime %-format}t" */
+ apr_size_t len = 0;
+ apr_time_exp_t expt;
+ ap_explode_recent_localtime(&expt, apr_time_now());
+ apr_strftime(buf, &len, buflen, arg, &expt);
+ return (int)len;
}
- arg++;
}
ap_recent_ctime_ex(buf, apr_time_now(), option, &time_len);
@@ -1164,6 +1188,11 @@ static void log_error_core(const char *file, int line, int module_index,
#endif
logf = stderr_log;
+
+ /* Use the main ErrorLogFormat if any */
+ if (ap_server_conf) {
+ sconf = ap_get_core_module_config(ap_server_conf->module_config);
+ }
}
else {
int configured_level = r ? ap_get_request_module_loglevel(r, module_index) :
@@ -1217,6 +1246,10 @@ static void log_error_core(const char *file, int line, int module_index,
}
}
}
+ else if (ap_server_conf) {
+ /* Use the main ErrorLogFormat if any */
+ sconf = ap_get_core_module_config(ap_server_conf->module_config);
+ }
}
info.s = s;
@@ -1598,6 +1631,9 @@ AP_DECLARE(void) ap_log_pid(apr_pool_t *p, const char *filename)
pid_t mypid;
apr_status_t rv;
const char *fname;
+ char *temp_fname;
+ apr_fileperms_t perms;
+ char pidstr[64];
if (!filename) {
return;
@@ -1626,19 +1662,31 @@ AP_DECLARE(void) ap_log_pid(apr_pool_t *p, const char *filename)
fname);
}
- if ((rv = apr_file_open(&pid_file, fname,
- APR_WRITE | APR_CREATE | APR_TRUNCATE,
- APR_UREAD | APR_UWRITE | APR_GREAD | APR_WREAD, p))
- != APR_SUCCESS) {
+ temp_fname = apr_pstrcat(p, fname, ".XXXXXX", NULL);
+ rv = apr_file_mktemp(&pid_file, temp_fname,
+ APR_FOPEN_WRITE | APR_FOPEN_CREATE | APR_FOPEN_TRUNCATE, p);
+ if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL, APLOGNO(00099)
- "could not create %s", fname);
+ "could not create %s", temp_fname);
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, APLOGNO(00100)
"%s: could not log pid to file %s",
ap_server_argv0, fname);
exit(1);
}
- apr_file_printf(pid_file, "%" APR_PID_T_FMT APR_EOL_STR, mypid);
- apr_file_close(pid_file);
+
+ apr_snprintf(pidstr, sizeof pidstr, "%" APR_PID_T_FMT APR_EOL_STR, mypid);
+
+ perms = APR_UREAD | APR_UWRITE | APR_GREAD | APR_WREAD;
+ if (((rv = apr_file_perms_set(temp_fname, perms)) != APR_SUCCESS && rv != APR_ENOTIMPL)
+ || (rv = apr_file_write_full(pid_file, pidstr, strlen(pidstr), NULL)) != APR_SUCCESS
+ || (rv = apr_file_close(pid_file)) != APR_SUCCESS
+ || (rv = apr_file_rename(temp_fname, fname, p)) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv, NULL, APLOGNO(10231)
+ "%s: Failed creating pid file %s",
+ ap_server_argv0, temp_fname);
+ exit(1);
+ }
+
saved_pid = mypid;
}
@@ -1930,8 +1978,8 @@ AP_DECLARE(void) ap_close_piped_log(piped_log *pl)
AP_DECLARE(const char *) ap_parse_log_level(const char *str, int *val)
{
- char *err = "Log level keyword must be one of emerg/alert/crit/error/warn/"
- "notice/info/debug/trace1/.../trace8";
+ const char *err = "Log level keyword must be one of emerg/alert/crit/error/"
+ "warn/notice/info/debug/trace1/.../trace8";
int i = 0;
if (str == NULL)
diff --git a/server/main.c b/server/main.c
index f1bebeb..fb23f01 100644
--- a/server/main.c
+++ b/server/main.c
@@ -21,6 +21,7 @@
#include "apr_lib.h"
#include "apr_md5.h"
#include "apr_time.h"
+#include "apr_thread_proc.h"
#include "apr_version.h"
#include "apu_version.h"
@@ -98,13 +99,17 @@ static void show_compile_settings(void)
printf("Server's Module Magic Number: %u:%u\n",
MODULE_MAGIC_NUMBER_MAJOR, MODULE_MAGIC_NUMBER_MINOR);
#if APR_MAJOR_VERSION >= 2
- printf("Server loaded: APR %s\n", apr_version_string());
- printf("Compiled using: APR %s\n", APR_VERSION_STRING);
+ printf("Server loaded: APR %s, PCRE %s\n",
+ apr_version_string(), ap_pcre_version_string(AP_REG_PCRE_LOADED));
+ printf("Compiled using: APR %s, PCRE %s\n",
+ APR_VERSION_STRING, ap_pcre_version_string(AP_REG_PCRE_COMPILED));
#else
- printf("Server loaded: APR %s, APR-UTIL %s\n",
- apr_version_string(), apu_version_string());
- printf("Compiled using: APR %s, APR-UTIL %s\n",
- APR_VERSION_STRING, APU_VERSION_STRING);
+ printf("Server loaded: APR %s, APR-UTIL %s, PCRE %s\n",
+ apr_version_string(), apu_version_string(),
+ ap_pcre_version_string(AP_REG_PCRE_LOADED));
+ printf("Compiled using: APR %s, APR-UTIL %s, PCRE %s\n",
+ APR_VERSION_STRING, APU_VERSION_STRING,
+ ap_pcre_version_string(AP_REG_PCRE_COMPILED));
#endif
/* sizeof(foo) is long on some platforms so we might as well
* make it long everywhere to keep the printf format
@@ -256,7 +261,7 @@ static void destroy_and_exit_process(process_rec *process,
* Sleep for TASK_SWITCH_SLEEP micro seconds to cause a task switch on
* OS layer and thus give possibly started piped loggers a chance to
* process their input. Otherwise it is possible that they get killed
- * by us before they can do so. In this case maybe valueable log messages
+ * by us before they can do so. In this case maybe valuable log messages
* might get lost.
*/
apr_sleep(TASK_SWITCH_SLEEP);
@@ -348,6 +353,23 @@ static process_rec *init_process(int *argc, const char * const * *argv)
process->argc = *argc;
process->argv = *argv;
process->short_name = apr_filepath_name_get((*argv)[0]);
+
+#if AP_HAS_THREAD_LOCAL
+ {
+ apr_status_t rv;
+ apr_thread_t *thd = NULL;
+ if ((rv = ap_thread_main_create(&thd, process->pool))) {
+ char ctimebuff[APR_CTIME_LEN];
+ apr_ctime(ctimebuff, apr_time_now());
+ fprintf(stderr, "[%s] [crit] (%d) %s: failed "
+ "to initialize thread context, exiting\n",
+ ctimebuff, rv, (*argv)[0]);
+ apr_terminate();
+ exit(1);
+ }
+ }
+#endif
+
return process;
}
@@ -656,8 +678,8 @@ int main(int argc, const char * const argv[])
if (temp_error_log) {
ap_replace_stderr_log(process->pool, temp_error_log);
}
- ap_server_conf = ap_read_config(process, ptemp, confname, &ap_conftree);
- if (!ap_server_conf) {
+ ap_server_conf = NULL; /* set early by ap_read_config() for logging */
+ if (!ap_read_config(process, ptemp, confname, &ap_conftree)) {
if (showcompile) {
/* Well, we tried. Show as much as we can, but exit nonzero to
* indicate that something's not right. The cause should have
@@ -666,6 +688,7 @@ int main(int argc, const char * const argv[])
}
destroy_and_exit_process(process, 1);
}
+ ap_assert(ap_server_conf != NULL);
apr_pool_cleanup_register(pconf, &ap_server_conf, ap_pool_cleanup_set_null,
apr_pool_cleanup_null);
@@ -763,10 +786,11 @@ int main(int argc, const char * const argv[])
apr_pool_create(&ptemp, pconf);
apr_pool_tag(ptemp, "ptemp");
ap_server_root = def_server_root;
- ap_server_conf = ap_read_config(process, ptemp, confname, &ap_conftree);
- if (!ap_server_conf) {
+ ap_server_conf = NULL; /* set early by ap_read_config() for logging */
+ if (!ap_read_config(process, ptemp, confname, &ap_conftree)) {
destroy_and_exit_process(process, 1);
}
+ ap_assert(ap_server_conf != NULL);
apr_pool_cleanup_register(pconf, &ap_server_conf,
ap_pool_cleanup_set_null, apr_pool_cleanup_null);
/* sort hooks here to make sure pre_config hooks are sorted properly */
diff --git a/server/mpm/event/event.c b/server/mpm/event/event.c
index 4cfb09c..3672f44 100644
--- a/server/mpm/event/event.c
+++ b/server/mpm/event/event.c
@@ -167,8 +167,6 @@ static int ap_daemons_to_start = 0; /* StartServers */
static int min_spare_threads = 0; /* MinSpareThreads */
static int max_spare_threads = 0; /* MaxSpareThreads */
static int active_daemons_limit = 0; /* MaxRequestWorkers / ThreadsPerChild */
-static int active_daemons = 0; /* workers that still active, i.e. are
- not shutting down gracefully */
static int max_workers = 0; /* MaxRequestWorkers */
static int server_limit = 0; /* ServerLimit */
static int thread_limit = 0; /* ThreadLimit */
@@ -248,6 +246,8 @@ struct event_conn_state_t {
conn_state_t pub;
/** chaining in defer_linger_chain */
struct event_conn_state_t *chain;
+ /** Is lingering close from defer_lingering_close()? */
+ int deferred_linger;
};
APR_RING_HEAD(timeout_head_t, event_conn_state_t);
@@ -285,21 +285,21 @@ static volatile apr_time_t queues_next_expiry;
*/
static void TO_QUEUE_APPEND(struct timeout_queue *q, event_conn_state_t *el)
{
- apr_time_t q_expiry;
+ apr_time_t elem_expiry;
apr_time_t next_expiry;
APR_RING_INSERT_TAIL(&q->head, el, event_conn_state_t, timeout_list);
++*q->total;
++q->count;
- /* Cheaply update the overall queues' next expiry according to the
- * first entry of this queue (oldest), if necessary.
+ /* Cheaply update the global queues_next_expiry with the one of the
+ * first entry of this queue (oldest) if it expires before.
*/
el = APR_RING_FIRST(&q->head);
- q_expiry = el->queue_timestamp + q->timeout;
+ elem_expiry = el->queue_timestamp + q->timeout;
next_expiry = queues_next_expiry;
- if (!next_expiry || next_expiry > q_expiry + TIMEOUT_FUDGE_FACTOR) {
- queues_next_expiry = q_expiry;
+ if (!next_expiry || next_expiry > elem_expiry + TIMEOUT_FUDGE_FACTOR) {
+ queues_next_expiry = elem_expiry;
/* Unblock the poll()ing listener for it to update its timeout. */
if (listener_is_wakeable) {
apr_pollset_wakeup(event_pollset);
@@ -379,7 +379,7 @@ typedef struct event_retained_data {
* We use this value to optimize routines that have to scan the entire
* scoreboard.
*/
- int max_daemons_limit;
+ int max_daemon_used;
/*
* All running workers, active and shutting down, including those that
@@ -387,7 +387,10 @@ typedef struct event_retained_data {
* Not kept up-to-date when shutdown is pending.
*/
int total_daemons;
-
+ /*
+ * Workers that still active, i.e. are not shutting down gracefully.
+ */
+ int active_daemons;
/*
* idle_spawn_rate is the number of children that will be spawned on the
* next maintenance cycle if there aren't enough idle servers. It is
@@ -505,7 +508,7 @@ static APR_INLINE apr_uint32_t listeners_disabled(void)
return apr_atomic_read32(&listensocks_disabled);
}
-static APR_INLINE int connections_above_limit(void)
+static APR_INLINE int connections_above_limit(int *busy)
{
apr_uint32_t i_count = ap_queue_info_num_idlers(worker_queue_info);
if (i_count > 0) {
@@ -519,28 +522,32 @@ static APR_INLINE int connections_above_limit(void)
return 0;
}
}
+ else if (busy) {
+ *busy = 1;
+ }
return 1;
}
-static void abort_socket_nonblocking(apr_socket_t *csd)
+static APR_INLINE int should_enable_listensocks(void)
+{
+ return !dying && listeners_disabled() && !connections_above_limit(NULL);
+}
+
+static void close_socket_nonblocking_(apr_socket_t *csd,
+ const char *from, int line)
{
apr_status_t rv;
- apr_socket_timeout_set(csd, 0);
-#if defined(SOL_SOCKET) && defined(SO_LINGER)
- /* This socket is over now, and we don't want to block nor linger
- * anymore, so reset it. A normal close could still linger in the
- * system, while RST is fast, nonblocking, and what the peer will
- * get if it sends us further data anyway.
- */
- {
- apr_os_sock_t osd = -1;
- struct linger opt;
- opt.l_onoff = 1;
- opt.l_linger = 0; /* zero timeout is RST */
- apr_os_sock_get(&osd, csd);
- setsockopt(osd, SOL_SOCKET, SO_LINGER, (void *)&opt, sizeof opt);
+ apr_os_sock_t fd = -1;
+
+ /* close_worker_sockets() may have closed it already */
+ rv = apr_os_sock_get(&fd, csd);
+ ap_log_error(APLOG_MARK, APLOG_TRACE8, 0, ap_server_conf,
+ "closing socket %i/%pp from %s:%i", (int)fd, csd, from, line);
+ if (rv == APR_SUCCESS && fd == -1) {
+ return;
}
-#endif
+
+ apr_socket_timeout_set(csd, 0);
rv = apr_socket_close(csd);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, APLOGNO(00468)
@@ -548,6 +555,8 @@ static void abort_socket_nonblocking(apr_socket_t *csd)
AP_DEBUG_ASSERT(0);
}
}
+#define close_socket_nonblocking(csd) \
+ close_socket_nonblocking_(csd, __FUNCTION__, __LINE__)
static void close_worker_sockets(void)
{
@@ -556,26 +565,16 @@ static void close_worker_sockets(void)
apr_socket_t *csd = worker_sockets[i];
if (csd) {
worker_sockets[i] = NULL;
- abort_socket_nonblocking(csd);
+ close_socket_nonblocking(csd);
}
}
- for (;;) {
- event_conn_state_t *cs = defer_linger_chain;
- if (!cs) {
- break;
- }
- if (apr_atomic_casptr((void *)&defer_linger_chain, cs->chain,
- cs) != cs) {
- /* Race lost, try again */
- continue;
- }
- cs->chain = NULL;
- abort_socket_nonblocking(cs->pfd.desc.s);
- }
}
static void wakeup_listener(void)
{
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
+ "wake up listener%s", listener_may_exit ? " again" : "");
+
listener_may_exit = 1;
disable_listensocks();
@@ -637,6 +636,8 @@ static void signal_threads(int mode)
ap_queue_interrupt_all(worker_queue);
close_worker_sockets(); /* forcefully kill all current connections */
}
+
+ ap_run_child_stopping(pchild, mode == ST_GRACEFUL);
}
static int event_query(int query_code, int *result, apr_status_t *rv)
@@ -644,7 +645,7 @@ static int event_query(int query_code, int *result, apr_status_t *rv)
*rv = APR_SUCCESS;
switch (query_code) {
case AP_MPMQ_MAX_DAEMON_USED:
- *result = retained->max_daemons_limit;
+ *result = retained->max_daemon_used;
break;
case AP_MPMQ_IS_THREADED:
*result = AP_MPMQ_STATIC;
@@ -695,14 +696,32 @@ static int event_query(int query_code, int *result, apr_status_t *rv)
return OK;
}
-static void event_note_child_killed(int childnum, pid_t pid, ap_generation_t gen)
+static void event_note_child_stopped(int slot, pid_t pid, ap_generation_t gen)
{
- if (childnum != -1) { /* child had a scoreboard slot? */
- ap_run_child_status(ap_server_conf,
- ap_scoreboard_image->parent[childnum].pid,
- ap_scoreboard_image->parent[childnum].generation,
- childnum, MPM_CHILD_EXITED);
- ap_scoreboard_image->parent[childnum].pid = 0;
+ if (slot != -1) { /* child had a scoreboard slot? */
+ process_score *ps = &ap_scoreboard_image->parent[slot];
+ int i;
+
+ pid = ps->pid;
+ gen = ps->generation;
+ for (i = 0; i < threads_per_child; i++) {
+ ap_update_child_status_from_indexes(slot, i, SERVER_DEAD, NULL);
+ }
+ ap_run_child_status(ap_server_conf, pid, gen, slot, MPM_CHILD_EXITED);
+ if (ps->quiescing != 2) { /* vs perform_idle_server_maintenance() */
+ retained->active_daemons--;
+ }
+ retained->total_daemons--;
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
+ "Child %d stopped: pid %d, gen %d, "
+ "active %d/%d, total %d/%d/%d, quiescing %d",
+ slot, (int)pid, (int)gen,
+ retained->active_daemons, active_daemons_limit,
+ retained->total_daemons, retained->max_daemon_used,
+ server_limit, ps->quiescing);
+ ps->not_accepting = 0;
+ ps->quiescing = 0;
+ ps->pid = 0;
}
else {
ap_run_child_status(ap_server_conf, pid, gen, -1, MPM_CHILD_EXITED);
@@ -712,9 +731,19 @@ static void event_note_child_killed(int childnum, pid_t pid, ap_generation_t gen
static void event_note_child_started(int slot, pid_t pid)
{
ap_generation_t gen = retained->mpm->my_generation;
+
+ retained->total_daemons++;
+ retained->active_daemons++;
ap_scoreboard_image->parent[slot].pid = pid;
ap_scoreboard_image->parent[slot].generation = gen;
ap_run_child_status(ap_server_conf, pid, gen, slot, MPM_CHILD_STARTED);
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
+ "Child %d started: pid %d, gen %d, "
+ "active %d/%d, total %d/%d/%d",
+ slot, (int)pid, (int)gen,
+ retained->active_daemons, active_daemons_limit,
+ retained->total_daemons, retained->max_daemon_used,
+ server_limit);
}
static const char *event_get_name(void)
@@ -727,12 +756,16 @@ static void clean_child_exit(int code) __attribute__ ((noreturn));
static void clean_child_exit(int code)
{
retained->mpm->mpm_state = AP_MPMQ_STOPPING;
+ if (terminate_mode == ST_INIT) {
+ ap_run_child_stopping(pchild, 0);
+ }
+
if (pchild) {
apr_pool_destroy(pchild);
}
if (one_process) {
- event_note_child_killed(/* slot */ 0, 0, 0);
+ event_note_child_stopped(/* slot */ 0, 0, 0);
}
exit(code);
@@ -753,7 +786,10 @@ static apr_status_t decrement_connection_count(void *cs_)
{
int is_last_connection;
event_conn_state_t *cs = cs_;
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE8, 0, cs->c,
+ "cleanup connection from state %i", (int)cs->pub.state);
switch (cs->pub.state) {
+ case CONN_STATE_LINGER:
case CONN_STATE_LINGER_NORMAL:
case CONN_STATE_LINGER_SHORT:
apr_atomic_dec32(&lingering_count);
@@ -771,9 +807,13 @@ static apr_status_t decrement_connection_count(void *cs_)
is_last_connection = !apr_atomic_dec32(&connection_count);
if (listener_is_wakeable
&& ((is_last_connection && listener_may_exit)
- || (listeners_disabled() && !connections_above_limit()))) {
+ || should_enable_listensocks())) {
apr_pollset_wakeup(event_pollset);
}
+ if (dying) {
+ /* Help worker_thread_should_exit_early() */
+ ap_queue_interrupt_one(worker_queue);
+ }
return APR_SUCCESS;
}
@@ -792,65 +832,26 @@ static void notify_resume(event_conn_state_t *cs, int cleanup)
}
/*
- * 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 only be called by worker thread.
+ * Defer flush and close of the connection by adding it to defer_linger_chain,
+ * for a worker to grab it and do the job (should that be blocking).
+ * Pre-condition: nonblocking, can be called from anywhere provided cs is not
+ * in any timeout queue or in the pollset.
*/
-static int start_lingering_close_blocking(event_conn_state_t *cs)
+static int defer_lingering_close(event_conn_state_t *cs)
{
- apr_socket_t *csd = cs->pfd.desc.s;
-
- if (ap_start_lingering_close(cs->c)) {
- notify_suspend(cs);
- apr_socket_close(csd);
- ap_queue_info_push_pool(worker_queue_info, cs->p);
- return DONE;
- }
-
-#ifdef AP_DEBUG
- {
- apr_status_t rv;
- rv = apr_socket_timeout_set(csd, 0);
- AP_DEBUG_ASSERT(rv == APR_SUCCESS);
- }
-#else
- apr_socket_timeout_set(csd, 0);
-#endif
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, cs->c,
+ "deferring close from state %i", (int)cs->pub.state);
- cs->queue_timestamp = apr_time_now();
- /*
- * If some module requested a shortened waiting period, only wait for
- * 2s (SECONDS_TO_LINGER). This is useful for mitigating certain
- * DoS attacks.
+ /* The connection is not shutdown() yet strictly speaking, but it's not
+ * in any queue nor handled by a worker either (will be very soon), so
+ * to account for it somewhere we bump lingering_count now (and set
+ * deferred_linger for process_lingering_close() to know).
*/
- if (apr_table_get(cs->c->notes, "short-lingering-close")) {
- cs->pub.state = CONN_STATE_LINGER_SHORT;
- }
- else {
- cs->pub.state = CONN_STATE_LINGER_NORMAL;
- }
+ cs->pub.state = CONN_STATE_LINGER;
apr_atomic_inc32(&lingering_count);
- notify_suspend(cs);
-
- return OK;
-}
-
-/*
- * Defer flush and close of the connection by adding it to defer_linger_chain,
- * for a worker to grab it and do the job (should that be blocking).
- * Pre-condition: cs is not in any timeout queue and not in the pollset,
- * timeout_mutex is not locked
- * return: 1 connection is alive (but aside and about to linger)
- * May be called by listener thread.
- */
-static int start_lingering_close_nonblocking(event_conn_state_t *cs)
-{
- event_conn_state_t *chain;
+ cs->deferred_linger = 1;
for (;;) {
- cs->chain = chain = defer_linger_chain;
+ event_conn_state_t *chain = cs->chain = defer_linger_chain;
if (apr_atomic_casptr((void *)&defer_linger_chain, cs,
chain) != chain) {
/* Race lost, try again */
@@ -860,22 +861,37 @@ static int start_lingering_close_nonblocking(event_conn_state_t *cs)
}
}
-/*
- * forcibly close a lingering connection after the lingering period has
- * expired
- * Pre-condition: cs is not in any timeout queue and not in the pollset
- * return: irrelevant (need same prototype as start_lingering_close)
+/* Close the connection and release its resources (ptrans), either because an
+ * unrecoverable error occured (queues or pollset add/remove) or more usually
+ * if lingering close timed out.
+ * Pre-condition: nonblocking, can be called from anywhere provided cs is not
+ * in any timeout queue or in the pollset.
*/
-static int stop_lingering_close(event_conn_state_t *cs)
+static void close_connection(event_conn_state_t *cs)
{
- apr_socket_t *csd = ap_get_conn_socket(cs->c);
- ap_log_error(APLOG_MARK, APLOG_TRACE4, 0, ap_server_conf,
- "socket abort in state %i", (int)cs->pub.state);
- abort_socket_nonblocking(csd);
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, cs->c,
+ "closing connection from state %i", (int)cs->pub.state);
+
+ close_socket_nonblocking(cs->pfd.desc.s);
ap_queue_info_push_pool(worker_queue_info, cs->p);
- if (dying)
- ap_queue_interrupt_one(worker_queue);
- return 0;
+}
+
+/* Shutdown the connection in case of timeout, error or resources shortage.
+ * This starts short lingering close if not already there, or directly closes
+ * the connection otherwise.
+ * Pre-condition: nonblocking, can be called from anywhere provided cs is not
+ * in any timeout queue or in the pollset.
+ */
+static int shutdown_connection(event_conn_state_t *cs)
+{
+ if (cs->pub.state < CONN_STATE_LINGER) {
+ apr_table_setn(cs->c->notes, "short-lingering-close", "1");
+ defer_lingering_close(cs);
+ }
+ else {
+ close_connection(cs);
+ }
+ return 1;
}
/*
@@ -947,6 +963,27 @@ static int event_post_read_request(request_rec *r)
/* Forward declare */
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) {
+ sense = cs->pub.sense;
+ }
+ if (sense == CONN_SENSE_WANT_READ) {
+ cs->pfd.reqevents = APR_POLLIN | APR_POLLHUP;
+ }
+ else {
+ 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).
+ */
+ cs->pfd.reqevents |= APR_POLLERR;
+
+ /* Reset to default for the next round */
+ cs->pub.sense = CONN_SENSE_DEFAULT;
+}
+
/*
* process one connection in the worker
*/
@@ -976,14 +1013,14 @@ static void process_socket(apr_thread_t *thd, apr_pool_t * p, apr_socket_t * soc
apr_pool_cleanup_null);
ap_set_module_config(c->conn_config, &mpm_event_module, cs);
c->current_thread = thd;
+ c->cs = &cs->pub;
cs->c = c;
- c->cs = &(cs->pub);
cs->p = p;
cs->sc = ap_get_module_config(ap_server_conf->module_config,
&mpm_event_module);
cs->pfd.desc_type = APR_POLL_SOCKET;
- cs->pfd.reqevents = APR_POLLIN;
cs->pfd.desc.s = sock;
+ update_reqevents_from_sense(cs, CONN_SENSE_WANT_READ);
pt->type = PT_CSD;
pt->baton = cs;
cs->pfd.client_data = pt;
@@ -992,11 +1029,10 @@ static void process_socket(apr_thread_t *thd, apr_pool_t * p, apr_socket_t * soc
ap_update_vhost_given_ip(c);
- rc = ap_run_pre_connection(c, sock);
+ rc = ap_pre_connection(c, sock);
if (rc != OK && rc != DONE) {
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, c, APLOGNO(00469)
"process_socket: connection aborted");
- c->aborted = 1;
}
/**
@@ -1074,7 +1110,7 @@ read_request:
* completion at some point may require reads (e.g. SSL_ERROR_WANT_READ),
* an output filter can also set the sense to CONN_SENSE_WANT_READ at any
* time for event MPM to do the right thing,
- * - suspend the connection (SUSPENDED) such that it now interracts with
+ * - suspend the connection (SUSPENDED) such that it now interacts with
* the MPM through suspend/resume_connection() hooks, and/or registered
* poll callbacks (PT_USER), and/or registered timed callbacks triggered
* by timer events.
@@ -1113,27 +1149,16 @@ read_request:
"network write failure in core output filter");
cs->pub.state = CONN_STATE_LINGER;
}
- else if (c->data_in_output_filters) {
+ else if (c->data_in_output_filters ||
+ cs->pub.sense == CONN_SENSE_WANT_READ) {
/* Still in WRITE_COMPLETION_STATE:
- * Set a write timeout for this connection, and let the
- * event thread poll for writeability.
+ * Set a read/write timeout for this connection, and let the
+ * event thread poll for read/writeability.
*/
cs->queue_timestamp = apr_time_now();
notify_suspend(cs);
- if (cs->pub.sense == CONN_SENSE_WANT_READ) {
- cs->pfd.reqevents = APR_POLLIN;
- }
- else {
- cs->pfd.reqevents = APR_POLLOUT;
- }
- /* POLLHUP/ERR are usually returned event only (ignored here), but
- * some pollset backends may require them in reqevents to do the
- * right thing, so it shouldn't hurt.
- */
- cs->pfd.reqevents |= APR_POLLHUP | APR_POLLERR;
- cs->pub.sense = CONN_SENSE_DEFAULT;
-
+ update_reqevents_from_sense(cs, -1);
apr_thread_mutex_lock(timeout_mutex);
TO_QUEUE_APPEND(cs->sc->wc_q, cs);
rv = apr_pollset_add(event_pollset, &cs->pfd);
@@ -1144,25 +1169,27 @@ read_request:
ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, APLOGNO(03465)
"process_socket: apr_pollset_add failure for "
"write completion");
- apr_socket_close(cs->pfd.desc.s);
- ap_queue_info_push_pool(worker_queue_info, cs->p);
+ close_connection(cs);
+ signal_threads(ST_GRACEFUL);
}
else {
apr_thread_mutex_unlock(timeout_mutex);
}
return;
}
- else if (c->keepalive != AP_CONN_KEEPALIVE || c->aborted ||
- listener_may_exit) {
+ else if (c->keepalive != AP_CONN_KEEPALIVE || c->aborted) {
cs->pub.state = CONN_STATE_LINGER;
}
else if (c->data_in_input_filters) {
cs->pub.state = CONN_STATE_READ_REQUEST_LINE;
goto read_request;
}
- else {
+ else if (!listener_may_exit) {
cs->pub.state = CONN_STATE_CHECK_REQUEST_LINE_READABLE;
}
+ else {
+ cs->pub.state = CONN_STATE_LINGER;
+ }
}
if (cs->pub.state == CONN_STATE_CHECK_REQUEST_LINE_READABLE) {
@@ -1180,7 +1207,7 @@ read_request:
notify_suspend(cs);
/* Add work to pollset. */
- cs->pfd.reqevents = APR_POLLIN;
+ update_reqevents_from_sense(cs, CONN_SENSE_WANT_READ);
apr_thread_mutex_lock(timeout_mutex);
TO_QUEUE_APPEND(cs->sc->ka_q, cs);
rv = apr_pollset_add(event_pollset, &cs->pfd);
@@ -1191,8 +1218,8 @@ read_request:
ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, APLOGNO(03093)
"process_socket: apr_pollset_add failure for "
"keep alive");
- apr_socket_close(cs->pfd.desc.s);
- ap_queue_info_push_pool(worker_queue_info, cs->p);
+ close_connection(cs);
+ signal_threads(ST_GRACEFUL);
}
else {
apr_thread_mutex_unlock(timeout_mutex);
@@ -1206,12 +1233,10 @@ read_request:
return;
}
- if (cs->pub.state == CONN_STATE_LINGER) {
- rc = start_lingering_close_blocking(cs);
- }
- if (rc == OK && (cs->pub.state == CONN_STATE_LINGER_NORMAL ||
- cs->pub.state == CONN_STATE_LINGER_SHORT)) {
+ /* CONN_STATE_LINGER[_*] fall through process_lingering_close() */
+ if (cs->pub.state >= CONN_STATE_LINGER) {
process_lingering_close(cs);
+ return;
}
}
@@ -1231,12 +1256,17 @@ static void check_infinite_requests(void)
}
}
-static void close_listeners(int *closed)
+static int close_listeners(int *closed)
{
+ ap_log_error(APLOG_MARK, APLOG_TRACE6, 0, ap_server_conf,
+ "clos%s listeners (connection_count=%u)",
+ *closed ? "ed" : "ing", apr_atomic_read32(&connection_count));
if (!*closed) {
int i;
+
ap_close_listeners_ex(my_bucket->listeners);
- *closed = 1;
+ *closed = 1; /* once */
+
dying = 1;
ap_scoreboard_image->parent[ap_child_slot].quiescing = 1;
for (i = 0; i < threads_per_child; ++i) {
@@ -1248,7 +1278,10 @@ static void close_listeners(int *closed)
ap_queue_info_free_idle_pools(worker_queue_info);
ap_queue_interrupt_all(worker_queue);
+
+ return 1;
}
+ return 0;
}
static void unblock_signal(int sig)
@@ -1297,11 +1330,16 @@ static apr_status_t push2worker(event_conn_state_t *cs, apr_socket_t *csd,
/* trash the connection; we couldn't queue the connected
* socket to a worker
*/
- if (csd) {
- abort_socket_nonblocking(csd);
+ if (cs) {
+ shutdown_connection(cs);
}
- if (ptrans) {
- ap_queue_info_push_pool(worker_queue_info, ptrans);
+ else {
+ if (csd) {
+ close_socket_nonblocking(csd);
+ }
+ if (ptrans) {
+ ap_queue_info_push_pool(worker_queue_info, ptrans);
+ }
}
signal_threads(ST_GRACEFUL);
}
@@ -1349,7 +1387,7 @@ static void get_worker(int *have_idle_worker_p, int blocking, int *all_busy)
}
/* Structures to reuse */
-static APR_RING_HEAD(timer_free_ring_t, timer_event_t) timer_free_ring;
+static timer_event_t timer_free_ring;
static apr_skiplist *timer_skiplist;
static volatile apr_time_t timers_next_expiry;
@@ -1391,8 +1429,8 @@ static apr_status_t event_register_timed_callback(apr_time_t t,
/* oh yeah, and make locking smarter/fine grained. */
apr_thread_mutex_lock(g_timer_skiplist_mtx);
- if (!APR_RING_EMPTY(&timer_free_ring, timer_event_t, link)) {
- te = APR_RING_FIRST(&timer_free_ring);
+ if (!APR_RING_EMPTY(&timer_free_ring.link, timer_event_t, link)) {
+ te = APR_RING_FIRST(&timer_free_ring.link);
APR_RING_REMOVE(te, link);
}
else {
@@ -1411,8 +1449,8 @@ static apr_status_t event_register_timed_callback(apr_time_t t,
/* Okay, add sorted by when.. */
apr_skiplist_insert(timer_skiplist, te);
- /* Cheaply update the overall timers' next expiry according to
- * this event, if necessary.
+ /* Cheaply update the global timers_next_expiry with this event's
+ * if it expires before.
*/
next_expiry = timers_next_expiry;
if (!next_expiry || next_expiry > te->when + EVENT_FUDGE_FACTOR) {
@@ -1431,10 +1469,13 @@ static apr_status_t event_register_timed_callback(apr_time_t t,
/*
- * Close socket and clean up if remote closed its end while we were in
- * lingering close. Only to be called in the worker thread, and since it's
- * in immediate call stack, we can afford a comfortable buffer size to
- * consume data quickly.
+ * Flush data and close our side of the connection, then drain incoming data.
+ * If the latter would block put the connection in one of the linger timeout
+ * queues to be called back when ready, and repeat until it's closed by peer.
+ * Only to be called in the worker thread, and since it's in immediate call
+ * stack, we can afford a comfortable buffer size to consume data quickly.
+ * Pre-condition: cs is not in any timeout queue and not in the pollset,
+ * timeout_mutex is not locked
*/
#define LINGERING_BUF_SIZE (32 * 1024)
static void process_lingering_close(event_conn_state_t *cs)
@@ -1445,22 +1486,55 @@ static void process_lingering_close(event_conn_state_t *cs)
apr_status_t rv;
struct timeout_queue *q;
- /* socket is already in non-blocking state */
+ ap_log_cerror(APLOG_MARK, APLOG_TRACE6, 0, cs->c,
+ "lingering close from state %i", (int)cs->pub.state);
+ AP_DEBUG_ASSERT(cs->pub.state >= CONN_STATE_LINGER);
+
+ if (cs->pub.state == CONN_STATE_LINGER) {
+ /* defer_lingering_close() may have bumped lingering_count already */
+ if (!cs->deferred_linger) {
+ apr_atomic_inc32(&lingering_count);
+ }
+
+ apr_socket_timeout_set(csd, apr_time_from_sec(SECONDS_TO_LINGER));
+ if (ap_start_lingering_close(cs->c)) {
+ notify_suspend(cs);
+ close_connection(cs);
+ return;
+ }
+
+ cs->queue_timestamp = apr_time_now();
+ /* Clear APR_INCOMPLETE_READ if it was ever set, we'll do the poll()
+ * at the listener only from now, if needed.
+ */
+ apr_socket_opt_set(csd, APR_INCOMPLETE_READ, 0);
+ /*
+ * 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->pub.state = CONN_STATE_LINGER_SHORT;
+ }
+ else {
+ cs->pub.state = CONN_STATE_LINGER_NORMAL;
+ }
+ notify_suspend(cs);
+ }
+
+ apr_socket_timeout_set(csd, 0);
do {
nbytes = sizeof(dummybuf);
rv = apr_socket_recv(csd, dummybuf, &nbytes);
} while (rv == APR_SUCCESS);
if (!APR_STATUS_IS_EAGAIN(rv)) {
- rv = apr_socket_close(csd);
- AP_DEBUG_ASSERT(rv == APR_SUCCESS);
- ap_queue_info_push_pool(worker_queue_info, cs->p);
+ close_connection(cs);
return;
}
- /* Re-queue the connection to come back when readable */
- cs->pfd.reqevents = APR_POLLIN;
- cs->pub.sense = CONN_SENSE_DEFAULT;
+ /* (Re)queue the connection to come back when readable */
+ 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);
TO_QUEUE_APPEND(q, cs);
@@ -1471,25 +1545,23 @@ static void process_lingering_close(event_conn_state_t *cs)
apr_thread_mutex_unlock(timeout_mutex);
ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, APLOGNO(03092)
"process_lingering_close: apr_pollset_add failure");
- rv = apr_socket_close(cs->pfd.desc.s);
- AP_DEBUG_ASSERT(rv == APR_SUCCESS);
- ap_queue_info_push_pool(worker_queue_info, cs->p);
+ close_connection(cs);
+ signal_threads(ST_GRACEFUL);
return;
}
apr_thread_mutex_unlock(timeout_mutex);
}
-/* call 'func' for all elements of 'q' with timeout less than 'timeout_time'.
+/* call 'func' for all elements of 'q' above 'expiry'.
* Pre-condition: timeout_mutex must already be locked
* Post-condition: timeout_mutex will be locked again
*/
-static void process_timeout_queue(struct timeout_queue *q,
- apr_time_t timeout_time,
+static void process_timeout_queue(struct timeout_queue *q, apr_time_t expiry,
int (*func)(event_conn_state_t *))
{
apr_uint32_t total = 0, count;
event_conn_state_t *first, *cs, *last;
- struct timeout_head_t trash;
+ struct event_conn_state_t trash;
struct timeout_queue *qp;
apr_status_t rv;
@@ -1497,33 +1569,33 @@ static void process_timeout_queue(struct timeout_queue *q,
return;
}
- APR_RING_INIT(&trash, event_conn_state_t, timeout_list);
+ APR_RING_INIT(&trash.timeout_list, event_conn_state_t, timeout_list);
for (qp = q; qp; qp = qp->next) {
count = 0;
cs = first = last = APR_RING_FIRST(&qp->head);
while (cs != APR_RING_SENTINEL(&qp->head, event_conn_state_t,
timeout_list)) {
/* Trash the entry if:
- * - no timeout_time was given (asked for all), or
+ * - no expiry was given (zero means all), or
* - it expired (according to the queue timeout), or
* - the system clock skewed in the past: no entry should be
- * registered above the given timeout_time (~now) + the queue
+ * registered above the given expiry (~now) + the queue
* timeout, we won't keep any here (eg. for centuries).
*
* Otherwise stop, no following entry will match thanks to the
* single timeout per queue (entries are added to the end!).
* This allows maintenance in O(1).
*/
- if (timeout_time
- && cs->queue_timestamp + qp->timeout > timeout_time
- && cs->queue_timestamp < timeout_time + qp->timeout) {
- /* Since this is the next expiring of this queue, update the
- * overall queues' next expiry if it's later than this one.
+ if (expiry && cs->queue_timestamp + qp->timeout > expiry
+ && cs->queue_timestamp < expiry + qp->timeout) {
+ /* Since this is the next expiring entry of this queue, update
+ * the global queues_next_expiry if it's later than this one.
*/
- apr_time_t q_expiry = cs->queue_timestamp + qp->timeout;
+ apr_time_t elem_expiry = cs->queue_timestamp + qp->timeout;
apr_time_t next_expiry = queues_next_expiry;
- if (!next_expiry || next_expiry > q_expiry) {
- queues_next_expiry = q_expiry;
+ if (!next_expiry
+ || next_expiry > elem_expiry + TIMEOUT_FUDGE_FACTOR) {
+ queues_next_expiry = elem_expiry;
}
break;
}
@@ -1542,7 +1614,7 @@ static void process_timeout_queue(struct timeout_queue *q,
continue;
APR_RING_UNSPLICE(first, last, timeout_list);
- APR_RING_SPLICE_TAIL(&trash, first, last, event_conn_state_t,
+ APR_RING_SPLICE_TAIL(&trash.timeout_list, first, last, event_conn_state_t,
timeout_list);
AP_DEBUG_ASSERT(*q->total >= count && qp->count >= count);
*q->total -= count;
@@ -1553,7 +1625,7 @@ static void process_timeout_queue(struct timeout_queue *q,
return;
apr_thread_mutex_unlock(timeout_mutex);
- first = APR_RING_FIRST(&trash);
+ first = APR_RING_FIRST(&trash.timeout_list);
do {
cs = APR_RING_NEXT(first, timeout_list);
TO_QUEUE_ELEM_INIT(first);
@@ -1563,18 +1635,17 @@ static void process_timeout_queue(struct timeout_queue *q,
apr_thread_mutex_lock(timeout_mutex);
}
-static void process_keepalive_queue(apr_time_t timeout_time)
+static void process_keepalive_queue(apr_time_t expiry)
{
/* If all workers are busy, we kill older keep-alive connections so
* that they may connect to another process.
*/
- if (!timeout_time) {
+ if (!expiry && *keepalive_q->total) {
ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, ap_server_conf,
- "All workers are busy or dying, will close %u "
+ "All workers are busy or dying, will shutdown %u "
"keep-alive connections", *keepalive_q->total);
}
- process_timeout_queue(keepalive_q, timeout_time,
- start_lingering_close_nonblocking);
+ process_timeout_queue(keepalive_q, expiry, shutdown_connection);
}
static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy)
@@ -1593,25 +1664,35 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy)
/* Unblock the signal used to wake this thread up, and set a handler for
* it.
*/
- unblock_signal(LISTENER_SIGNAL);
apr_signal(LISTENER_SIGNAL, dummy_signal_handler);
+ unblock_signal(LISTENER_SIGNAL);
for (;;) {
timer_event_t *te;
const apr_pollfd_t *out_pfd;
apr_int32_t num = 0;
- apr_interval_time_t timeout_interval;
- apr_time_t now, timeout_time;
+ apr_interval_time_t timeout;
+ apr_time_t now, expiry = -1;
int workers_were_busy = 0;
if (conns_this_child <= 0)
check_infinite_requests();
if (listener_may_exit) {
- close_listeners(&closed);
+ int first_close = close_listeners(&closed);
+
if (terminate_mode == ST_UNGRACEFUL
|| apr_atomic_read32(&connection_count) == 0)
break;
+
+ /* Don't wait in poll() for the first close (i.e. dying now), we
+ * want to maintain the queues and schedule defer_linger_chain ASAP
+ * to kill kept-alive connection and shutdown the workers and child
+ * faster.
+ */
+ if (first_close) {
+ goto do_maintenance; /* with expiry == -1 */
+ }
}
now = apr_time_now();
@@ -1625,8 +1706,8 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy)
"keep-alive: %d lingering: %d suspended: %u)",
apr_atomic_read32(&connection_count),
apr_atomic_read32(&clogged_count),
- *(volatile apr_uint32_t*)write_completion_q->total,
- *(volatile apr_uint32_t*)keepalive_q->total,
+ apr_atomic_read32(write_completion_q->total),
+ apr_atomic_read32(keepalive_q->total),
apr_atomic_read32(&lingering_count),
apr_atomic_read32(&suspended_count));
if (dying) {
@@ -1645,18 +1726,19 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy)
* up occurs, otherwise periodic checks (maintenance, shutdown, ...)
* must be performed.
*/
- timeout_interval = -1;
+ now = apr_time_now();
+ timeout = -1;
/* Push expired timers to a worker, the first remaining one determines
* the maximum time to poll() below, if any.
*/
- timeout_time = timers_next_expiry;
- if (timeout_time && timeout_time < now + EVENT_FUDGE_FACTOR) {
+ expiry = timers_next_expiry;
+ if (expiry && expiry < now) {
apr_thread_mutex_lock(g_timer_skiplist_mtx);
while ((te = apr_skiplist_peek(timer_skiplist))) {
- if (te->when > now + EVENT_FUDGE_FACTOR) {
+ if (te->when > now) {
timers_next_expiry = te->when;
- timeout_interval = te->when - now;
+ timeout = te->when - now;
break;
}
apr_skiplist_pop(timer_skiplist, NULL);
@@ -1669,37 +1751,40 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy)
}
/* Same for queues, use their next expiry, if any. */
- timeout_time = queues_next_expiry;
- if (timeout_time
- && (timeout_interval < 0
- || timeout_time <= now
- || timeout_interval > timeout_time - now)) {
- timeout_interval = timeout_time > now ? timeout_time - now : 1;
+ expiry = queues_next_expiry;
+ if (expiry
+ && (timeout < 0
+ || expiry <= now
+ || timeout > expiry - now)) {
+ timeout = expiry > now ? expiry - now : 0;
}
/* When non-wakeable, don't wait more than 100 ms, in any case. */
#define NON_WAKEABLE_POLL_TIMEOUT apr_time_from_msec(100)
if (!listener_is_wakeable
- && (timeout_interval < 0
- || timeout_interval > NON_WAKEABLE_POLL_TIMEOUT)) {
- timeout_interval = NON_WAKEABLE_POLL_TIMEOUT;
+ && (timeout < 0
+ || timeout > NON_WAKEABLE_POLL_TIMEOUT)) {
+ timeout = NON_WAKEABLE_POLL_TIMEOUT;
}
+ else if (timeout > 0) {
+ /* apr_pollset_poll() might round down the timeout to milliseconds,
+ * let's forcibly round up here to never return before the timeout.
+ */
+ timeout = apr_time_from_msec(
+ apr_time_as_msec(timeout + apr_time_from_msec(1) - 1)
+ );
+ }
+
+ ap_log_error(APLOG_MARK, APLOG_TRACE7, 0, ap_server_conf,
+ "polling with timeout=%" APR_TIME_T_FMT
+ " queues_timeout=%" APR_TIME_T_FMT
+ " timers_timeout=%" APR_TIME_T_FMT,
+ timeout, queues_next_expiry - now,
+ timers_next_expiry - now);
- rc = apr_pollset_poll(event_pollset, timeout_interval, &num, &out_pfd);
+ rc = apr_pollset_poll(event_pollset, timeout, &num, &out_pfd);
if (rc != APR_SUCCESS) {
- if (APR_STATUS_IS_EINTR(rc)) {
- /* Woken up, if we are exiting or listeners are disabled we
- * must fall through to kill kept-alive connections or test
- * whether listeners should be re-enabled. Otherwise we only
- * need to update timeouts (logic is above, so simply restart
- * the loop).
- */
- if (!listener_may_exit && !listeners_disabled()) {
- continue;
- }
- timeout_time = 0;
- }
- else if (!APR_STATUS_IS_TIMEUP(rc)) {
+ if (!APR_STATUS_IS_EINTR(rc) && !APR_STATUS_IS_TIMEUP(rc)) {
ap_log_error(APLOG_MARK, APLOG_CRIT, rc, ap_server_conf,
"apr_pollset_poll failed. Attempting to "
"shutdown process gracefully");
@@ -1708,13 +1793,21 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy)
num = 0;
}
- if (listener_may_exit) {
- close_listeners(&closed);
- if (terminate_mode == ST_UNGRACEFUL
- || apr_atomic_read32(&connection_count) == 0)
- break;
+ if (APLOGtrace7(ap_server_conf)) {
+ now = apr_time_now();
+ ap_log_error(APLOG_MARK, APLOG_TRACE7, rc, ap_server_conf,
+ "polled with num=%u exit=%d/%d conns=%d"
+ " queues_timeout=%" APR_TIME_T_FMT
+ " timers_timeout=%" APR_TIME_T_FMT,
+ num, listener_may_exit, dying,
+ apr_atomic_read32(&connection_count),
+ queues_next_expiry - now, timers_next_expiry - now);
}
+ /* XXX possible optimization: stash the current time for use as
+ * r->request_time for new requests or queues maintenance
+ */
+
for (; num; --num, ++out_pfd) {
listener_poll_type *pt = (listener_poll_type *) out_pfd->client_data;
if (pt->type == PT_CSD) {
@@ -1767,25 +1860,21 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy)
AP_DEBUG_ASSERT(0);
ap_log_error(APLOG_MARK, APLOG_ERR, rc, ap_server_conf,
APLOGNO(03094) "pollset remove failed");
- start_lingering_close_nonblocking(cs);
+ close_connection(cs);
+ signal_threads(ST_GRACEFUL);
break;
}
/* If we don't get a worker immediately (nonblocking), we
* close the connection; the client can re-connect to a
* different process for keepalive, and for lingering close
- * the connection will be reset so the choice is to favor
+ * the connection will be shutdown so the choice is to favor
* incoming/alive connections.
*/
get_worker(&have_idle_worker, blocking,
&workers_were_busy);
if (!have_idle_worker) {
- if (remove_from_q == cs->sc->ka_q) {
- start_lingering_close_nonblocking(cs);
- }
- else {
- stop_lingering_close(cs);
- }
+ shutdown_connection(cs);
}
else if (push2worker(cs, NULL, NULL) == APR_SUCCESS) {
have_idle_worker = 0;
@@ -1800,7 +1889,7 @@ 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 (connections_above_limit()) {
+ else if (connections_above_limit(&workers_were_busy)) {
disable_listensocks();
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
"Too many open connections (%u), "
@@ -1809,7 +1898,6 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy)
ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, ap_server_conf,
"Idle workers: %u",
ap_queue_info_num_idlers(worker_queue_info));
- workers_were_busy = 1;
}
else if (!listener_may_exit) {
void *csd = NULL;
@@ -1872,23 +1960,22 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy)
} /* if:else on pt->type */
} /* for processing poll */
- /* XXX possible optimization: stash the current time for use as
- * r->request_time for new requests
- */
- /* We process the timeout queues here only when their overall next
- * expiry (read once above) is over. This happens accurately since
+ /* We process the timeout queues here only when the global
+ * queues_next_expiry is passed. This happens accurately since
* adding to the queues (in workers) can only decrease this expiry,
* while latest ones are only taken into account here (in listener)
* during queues' processing, with the lock held. This works both
* with and without wake-ability.
*/
- if (timeout_time && timeout_time < (now = apr_time_now())) {
- timeout_time = now + TIMEOUT_FUDGE_FACTOR;
-
- /* handle timed out sockets */
+ expiry = queues_next_expiry;
+do_maintenance:
+ if (expiry && expiry < (now = apr_time_now())) {
+ ap_log_error(APLOG_MARK, APLOG_TRACE7, 0, ap_server_conf,
+ "queues maintenance with timeout=%" APR_TIME_T_FMT,
+ expiry > 0 ? expiry - now : -1);
apr_thread_mutex_lock(timeout_mutex);
- /* Processing all the queues below will recompute this. */
+ /* Steps below will recompute this. */
queues_next_expiry = 0;
/* Step 1: keepalive timeouts */
@@ -1896,28 +1983,34 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy)
process_keepalive_queue(0); /* kill'em all \m/ */
}
else {
- process_keepalive_queue(timeout_time);
+ process_keepalive_queue(now);
}
/* Step 2: write completion timeouts */
- process_timeout_queue(write_completion_q, timeout_time,
- start_lingering_close_nonblocking);
+ process_timeout_queue(write_completion_q, now,
+ defer_lingering_close);
/* Step 3: (normal) lingering close completion timeouts */
- process_timeout_queue(linger_q, timeout_time,
- stop_lingering_close);
+ 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 */
- process_timeout_queue(short_linger_q, timeout_time,
- stop_lingering_close);
+ process_timeout_queue(short_linger_q, now, shutdown_connection);
apr_thread_mutex_unlock(timeout_mutex);
+ ap_log_error(APLOG_MARK, APLOG_TRACE7, 0, ap_server_conf,
+ "queues maintained with timeout=%" APR_TIME_T_FMT,
+ queues_next_expiry > now ? queues_next_expiry - now
+ : -1);
- ps->keep_alive = *(volatile apr_uint32_t*)keepalive_q->total;
- ps->write_completion = *(volatile apr_uint32_t*)write_completion_q->total;
+ ps->keep_alive = apr_atomic_read32(keepalive_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);
ps->lingering_close = apr_atomic_read32(&lingering_count);
}
else if ((workers_were_busy || dying)
- && *(volatile apr_uint32_t*)keepalive_q->total) {
+ && apr_atomic_read32(keepalive_q->total)) {
apr_thread_mutex_lock(timeout_mutex);
process_keepalive_queue(0); /* kill'em all \m/ */
apr_thread_mutex_unlock(timeout_mutex);
@@ -1942,14 +2035,11 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t * thd, void *dummy)
}
}
- if (listeners_disabled()
- && !workers_were_busy
- && !connections_above_limit()) {
+ if (!workers_were_busy && should_enable_listensocks()) {
enable_listensocks();
}
} /* listener main loop */
- close_listeners(&closed);
ap_queue_term(worker_queue);
apr_thread_exit(thd, APR_SUCCESS);
@@ -2007,7 +2097,7 @@ static void *APR_THREAD_FUNC worker_thread(apr_thread_t * thd, void *dummy)
ap_update_child_status_from_indexes(process_slot, thread_slot,
SERVER_STARTING, NULL);
- while (!workers_may_exit) {
+ for (;;) {
apr_socket_t *csd = NULL;
event_conn_state_t *cs;
timer_event_t *te = NULL;
@@ -2022,6 +2112,12 @@ static void *APR_THREAD_FUNC worker_thread(apr_thread_t * thd, void *dummy)
signal_threads(ST_GRACEFUL);
break;
}
+ /* A new idler may have changed connections_above_limit(),
+ * let the listener know and decide.
+ */
+ if (listener_is_wakeable && should_enable_listensocks()) {
+ apr_pollset_wakeup(event_pollset);
+ }
is_idle = 1;
}
@@ -2072,7 +2168,7 @@ static void *APR_THREAD_FUNC worker_thread(apr_thread_t * thd, void *dummy)
{
apr_thread_mutex_lock(g_timer_skiplist_mtx);
- APR_RING_INSERT_TAIL(&timer_free_ring, te, timer_event_t, link);
+ APR_RING_INSERT_TAIL(&timer_free_ring.link, te, timer_event_t, link);
apr_thread_mutex_unlock(g_timer_skiplist_mtx);
}
}
@@ -2097,15 +2193,9 @@ static void *APR_THREAD_FUNC worker_thread(apr_thread_t * thd, void *dummy)
continue;
}
cs->chain = NULL;
+ AP_DEBUG_ASSERT(cs->pub.state == CONN_STATE_LINGER);
worker_sockets[thread_slot] = csd = cs->pfd.desc.s;
-#ifdef AP_DEBUG
- rv = apr_socket_timeout_set(csd, SECONDS_TO_LINGER);
- AP_DEBUG_ASSERT(rv == APR_SUCCESS);
-#else
- apr_socket_timeout_set(csd, SECONDS_TO_LINGER);
-#endif
- cs->pub.state = CONN_STATE_LINGER;
process_socket(thd, cs->p, csd, cs, process_slot, thread_slot);
worker_sockets[thread_slot] = NULL;
}
@@ -2139,11 +2229,11 @@ static void create_listener_thread(thread_starter * ts)
my_info = (proc_info *) ap_malloc(sizeof(proc_info));
my_info->pslot = my_child_num;
my_info->tslot = -1; /* listener thread doesn't have a thread slot */
- rv = apr_thread_create(&ts->listener, thread_attr, listener_thread,
- my_info, pruntime);
+ rv = ap_thread_create(&ts->listener, thread_attr, listener_thread,
+ my_info, pruntime);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, APLOGNO(00474)
- "apr_thread_create: unable to create listener thread");
+ "ap_thread_create: unable to create listener thread");
/* let the parent decide how bad this really is */
clean_child_exit(APEXIT_CHILDSICK);
}
@@ -2177,7 +2267,7 @@ static void setup_threads_runtime(void)
apr_pool_create(&pskip, pconf);
apr_pool_tag(pskip, "mpm_skiplist");
apr_thread_mutex_create(&g_timer_skiplist_mtx, APR_THREAD_MUTEX_DEFAULT, pskip);
- APR_RING_INIT(&timer_free_ring, timer_event_t, link);
+ APR_RING_INIT(&timer_free_ring.link, timer_event_t, link);
apr_skiplist_init(&timer_skiplist, pskip);
apr_skiplist_set_compare(timer_skiplist, timer_comp, timer_comp);
@@ -2186,7 +2276,7 @@ static void setup_threads_runtime(void)
* the connections they handle (i.e. ptrans). We can't use this thread's
* self pool because all these objects survive it, nor use pchild or pconf
* directly because this starter thread races with other modules' runtime,
- * nor finally pchild (or subpool thereof) because it is killed explicitely
+ * nor finally pchild (or subpool thereof) because it is killed explicitly
* before pconf (thus connections/ptrans can live longer, which matters in
* ONE_PROCESS mode). So this leaves us with a subpool of pconf, created
* before any ptrans hence destroyed after.
@@ -2271,7 +2361,7 @@ static void setup_threads_runtime(void)
AP_DEBUG_ASSERT(i < num_listensocks);
pfd = &listener_pollfd[i];
- pfd->reqevents = APR_POLLIN;
+ pfd->reqevents = APR_POLLIN | APR_POLLHUP | APR_POLLERR;
pfd->desc_type = APR_POLL_SOCKET;
pfd->desc.s = lr->sd;
@@ -2334,12 +2424,12 @@ static void *APR_THREAD_FUNC start_threads(apr_thread_t * thd, void *dummy)
/* We let each thread update its own scoreboard entry. This is
* done because it lets us deal with tid better.
*/
- rv = apr_thread_create(&threads[i], thread_attr,
- worker_thread, my_info, pruntime);
+ rv = ap_thread_create(&threads[i], thread_attr,
+ worker_thread, my_info, pruntime);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf,
APLOGNO(03104)
- "apr_thread_create: unable to create worker thread");
+ "ap_thread_create: unable to create worker thread");
/* let the parent decide how bad this really is */
clean_child_exit(APEXIT_CHILDSICK);
}
@@ -2402,13 +2492,17 @@ static void join_workers(apr_thread_t * listener, apr_thread_t ** threads)
*/
iter = 0;
- while (iter < 10 && !dying) {
+ while (!dying) {
+ apr_sleep(apr_time_from_msec(500));
+ if (dying || ++iter > 10) {
+ break;
+ }
/* listener has not stopped accepting yet */
- apr_sleep(apr_time_make(0, 500000));
+ ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, ap_server_conf,
+ "listener has not stopped accepting yet (%d iter)", iter);
wakeup_listener();
- ++iter;
}
- if (iter >= 10) {
+ if (iter > 10) {
ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf, APLOGNO(00475)
"the listener thread didn't stop accepting");
}
@@ -2470,6 +2564,17 @@ static void child_main(int child_num_arg, int child_bucket)
apr_pool_create(&pchild, pconf);
apr_pool_tag(pchild, "pchild");
+#if AP_HAS_THREAD_LOCAL
+ if (!one_process) {
+ apr_thread_t *thd = NULL;
+ if ((rv = ap_thread_main_create(&thd, pchild))) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf, APLOGNO(10377)
+ "Couldn't initialize child main thread");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+ }
+#endif
+
/* close unused listeners and pods */
for (i = 0; i < retained->mpm->num_buckets; i++) {
if (i != child_bucket) {
@@ -2490,7 +2595,7 @@ static void child_main(int child_num_arg, int child_bucket)
* from being received. The child processes no longer use signals for
* any communication with the parent process. Let's also do this before
* child_init() hooks are called and possibly create threads that
- * otherwise could "steal" (implicitely) MPM's signals.
+ * otherwise could "steal" (implicitly) MPM's signals.
*/
rv = apr_setup_signal_thread();
if (rv != APR_SUCCESS) {
@@ -2539,11 +2644,11 @@ static void child_main(int child_num_arg, int child_bucket)
ts->child_num_arg = child_num_arg;
ts->threadattr = thread_attr;
- rv = apr_thread_create(&start_thread_id, thread_attr, start_threads,
- ts, pchild);
+ rv = ap_thread_create(&start_thread_id, thread_attr, start_threads,
+ ts, pchild);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, APLOGNO(00480)
- "apr_thread_create: unable to create worker thread");
+ "ap_thread_create: unable to create worker thread");
/* let the parent decide how bad this really is */
clean_child_exit(APEXIT_CHILDSICK);
}
@@ -2583,8 +2688,8 @@ static void child_main(int child_num_arg, int child_bucket)
* the other threads in the process needs to take us down
* (e.g., for MaxConnectionsPerChild) it will send us SIGTERM
*/
- unblock_signal(SIGTERM);
apr_signal(SIGTERM, dummy_signal_handler);
+ unblock_signal(SIGTERM);
/* Watch for any messages from the parent over the POD */
while (1) {
rv = ap_mpm_podx_check(my_bucket->pod);
@@ -2617,7 +2722,13 @@ static void child_main(int child_num_arg, int child_bucket)
* If the worker hasn't exited, then this blocks until
* they have (then cleans up).
*/
+ ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, ap_server_conf,
+ "%s termination received, joining workers",
+ rv == AP_MPM_PODX_GRACEFUL ? "graceful" : "ungraceful");
join_workers(ts->listener, threads);
+ ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, ap_server_conf,
+ "%s termination, workers joined, exiting",
+ rv == AP_MPM_PODX_GRACEFUL ? "graceful" : "ungraceful");
}
free(threads);
@@ -2629,8 +2740,8 @@ static int make_child(server_rec * s, int slot, int bucket)
{
int pid;
- if (slot + 1 > retained->max_daemons_limit) {
- retained->max_daemons_limit = slot + 1;
+ if (slot + 1 > retained->max_daemon_used) {
+ retained->max_daemon_used = slot + 1;
}
if (ap_scoreboard_image->parent[slot].pid != 0) {
@@ -2672,6 +2783,10 @@ static int make_child(server_rec * s, int slot, int bucket)
}
if (!pid) {
+#if AP_HAS_THREAD_LOCAL
+ ap_thread_current_after_fork();
+#endif
+
my_bucket = &all_buckets[bucket];
#ifdef HAVE_BINDPROCESSOR
@@ -2694,12 +2809,7 @@ static int make_child(server_rec * s, int slot, int bucket)
return -1;
}
- ap_scoreboard_image->parent[slot].quiescing = 0;
- ap_scoreboard_image->parent[slot].not_accepting = 0;
- ap_scoreboard_image->parent[slot].bucket = bucket;
event_note_child_started(slot, pid);
- active_daemons++;
- retained->total_daemons++;
return 0;
}
@@ -2719,36 +2829,47 @@ static void startup_children(int number_to_start)
}
}
-static void perform_idle_server_maintenance(int child_bucket, int num_buckets)
+static void perform_idle_server_maintenance(int child_bucket,
+ int *max_daemon_used)
{
- int i, j;
+ int num_buckets = retained->mpm->num_buckets;
int idle_thread_count = 0;
- worker_score *ws;
process_score *ps;
int free_length = 0;
int free_slots[MAX_SPAWN_RATE];
int last_non_dead = -1;
int active_thread_count = 0;
+ int i, j;
for (i = 0; i < server_limit; ++i) {
- /* Initialization to satisfy the compiler. It doesn't know
- * that threads_per_child is always > 0 */
- int status = SERVER_DEAD;
- int child_threads_active = 0;
-
- if (i >= retained->max_daemons_limit &&
+ if (num_buckets > 1 && (i % num_buckets) != child_bucket) {
+ /* We only care about child_bucket in this call */
+ continue;
+ }
+ if (i >= retained->max_daemon_used &&
free_length == retained->idle_spawn_rate[child_bucket]) {
/* short cut if all active processes have been examined and
* enough empty scoreboard slots have been found
*/
-
break;
}
+
ps = &ap_scoreboard_image->parent[i];
if (ps->pid != 0) {
+ int child_threads_active = 0;
+ if (ps->quiescing == 1) {
+ ps->quiescing = 2;
+ retained->active_daemons--;
+ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, ap_server_conf,
+ "Child %d quiescing: pid %d, gen %d, "
+ "active %d/%d, total %d/%d/%d",
+ i, (int)ps->pid, (int)ps->generation,
+ retained->active_daemons, active_daemons_limit,
+ retained->total_daemons, retained->max_daemon_used,
+ server_limit);
+ }
for (j = 0; j < threads_per_child; j++) {
- ws = &ap_scoreboard_image->servers[i][j];
- status = ws->status;
+ int status = ap_scoreboard_image->servers[i][j].status;
/* We consider a starting server as idle because we started it
* at least a cycle ago, and if it still hasn't finished starting
@@ -2757,22 +2878,25 @@ static void perform_idle_server_maintenance(int child_bucket, int num_buckets)
* This depends on the ordering of SERVER_READY and SERVER_STARTING.
*/
if (status <= SERVER_READY && !ps->quiescing && !ps->not_accepting
- && ps->generation == retained->mpm->my_generation
- && ps->bucket == child_bucket)
- {
+ && ps->generation == retained->mpm->my_generation) {
++idle_thread_count;
}
if (status >= SERVER_READY && status < SERVER_GRACEFUL) {
++child_threads_active;
}
}
+ active_thread_count += child_threads_active;
+ if (child_threads_active == threads_per_child) {
+ had_healthy_child = 1;
+ }
last_non_dead = i;
}
- active_thread_count += child_threads_active;
- if (!ps->pid && free_length < retained->idle_spawn_rate[child_bucket])
+ else if (free_length < retained->idle_spawn_rate[child_bucket]) {
free_slots[free_length++] = i;
- else if (child_threads_active == threads_per_child)
- had_healthy_child = 1;
+ }
+ }
+ if (*max_daemon_used < last_non_dead + 1) {
+ *max_daemon_used = last_non_dead + 1;
}
if (retained->sick_child_detected) {
@@ -2783,6 +2907,10 @@ static void perform_idle_server_maintenance(int child_bucket, int num_buckets)
*/
retained->sick_child_detected = 0;
}
+ else if (child_bucket < num_buckets - 1) {
+ /* check for had_healthy_child up to the last child bucket */
+ return;
+ }
else {
/* looks like a basket case, as no child ever fully initialized; give up.
*/
@@ -2798,18 +2926,20 @@ static void perform_idle_server_maintenance(int child_bucket, int num_buckets)
}
}
- retained->max_daemons_limit = last_non_dead + 1;
+ AP_DEBUG_ASSERT(retained->active_daemons <= retained->total_daemons
+ && retained->total_daemons <= retained->max_daemon_used
+ && retained->max_daemon_used <= server_limit);
- if (idle_thread_count > max_spare_threads / num_buckets)
- {
+ if (idle_thread_count > max_spare_threads / num_buckets) {
/*
* Child processes that we ask to shut down won't die immediately
* but may stay around for a long time when they finish their
* requests. If the server load changes many times, many such
* gracefully finishing processes may accumulate, filling up the
* scoreboard. To avoid running out of scoreboard entries, we
- * don't shut down more processes when the total number of processes
- * is high.
+ * don't shut down more processes if there are stopping ones
+ * already (i.e. active_daemons != total_daemons) and not enough
+ * slack space in the scoreboard for a graceful restart.
*
* XXX It would be nice if we could
* XXX - kill processes without keepalive connections first
@@ -2817,23 +2947,28 @@ static void perform_idle_server_maintenance(int child_bucket, int num_buckets)
* XXX depending on server load, later be able to resurrect them
* or kill them
*/
- if (retained->total_daemons <= active_daemons_limit &&
- retained->total_daemons < server_limit) {
- /* Kill off one child */
+ int do_kill = (retained->active_daemons == retained->total_daemons
+ || (server_limit - retained->total_daemons >
+ active_daemons_limit));
+ ap_log_error(APLOG_MARK, APLOG_TRACE5, 0, ap_server_conf,
+ "%shutting down one child: "
+ "active %d/%d, total %d/%d/%d, "
+ "idle threads %d, max workers %d",
+ (do_kill) ? "S" : "Not s",
+ retained->active_daemons, active_daemons_limit,
+ retained->total_daemons, retained->max_daemon_used,
+ server_limit, idle_thread_count, max_workers);
+ if (do_kill) {
ap_mpm_podx_signal(all_buckets[child_bucket].pod,
AP_MPM_PODX_GRACEFUL);
- retained->idle_spawn_rate[child_bucket] = 1;
- active_daemons--;
- } else {
- ap_log_error(APLOG_MARK, APLOG_TRACE5, 0, ap_server_conf,
- "Not shutting down child: total daemons %d / "
- "active limit %d / ServerLimit %d",
- retained->total_daemons, active_daemons_limit,
- server_limit);
}
+ else {
+ /* Wait for dying daemon(s) to exit */
+ }
+ retained->idle_spawn_rate[child_bucket] = 1;
}
else if (idle_thread_count < min_spare_threads / num_buckets) {
- if (active_thread_count >= max_workers) {
+ if (active_thread_count >= max_workers / num_buckets) {
if (0 == idle_thread_count) {
if (!retained->maxclients_reported) {
ap_log_error(APLOG_MARK, APLOG_ERR, 0, ap_server_conf, APLOGNO(00484)
@@ -2864,6 +2999,24 @@ static void perform_idle_server_maintenance(int child_bucket, int num_buckets)
if (free_length > retained->idle_spawn_rate[child_bucket]) {
free_length = retained->idle_spawn_rate[child_bucket];
}
+ if (free_length + retained->active_daemons > active_daemons_limit) {
+ if (retained->active_daemons < active_daemons_limit) {
+ free_length = active_daemons_limit - retained->active_daemons;
+ }
+ else {
+ ap_log_error(APLOG_MARK, APLOG_TRACE1, 0, ap_server_conf,
+ "server is at active daemons limit, spawning "
+ "of %d children cancelled: active %d/%d, "
+ "total %d/%d/%d, rate %d", free_length,
+ retained->active_daemons, active_daemons_limit,
+ retained->total_daemons, retained->max_daemon_used,
+ server_limit, retained->idle_spawn_rate[child_bucket]);
+ /* reset the spawning rate and prevent its growth below */
+ retained->idle_spawn_rate[child_bucket] = 1;
+ ++retained->hold_off_on_exponential_spawning;
+ free_length = 0;
+ }
+ }
if (retained->idle_spawn_rate[child_bucket] >= 8) {
ap_log_error(APLOG_MARK, APLOG_INFO, 0, ap_server_conf, APLOGNO(00486)
"server seems busy, (you may need "
@@ -2872,16 +3025,17 @@ static void perform_idle_server_maintenance(int child_bucket, int num_buckets)
"spawning %d children, there are around %d idle "
"threads, %d active children, and %d children "
"that are shutting down", free_length,
- idle_thread_count, active_daemons,
+ idle_thread_count, retained->active_daemons,
retained->total_daemons);
}
for (i = 0; i < free_length; ++i) {
- ap_log_error(APLOG_MARK, APLOG_TRACE5, 0, ap_server_conf,
- "Spawning new child: slot %d active / "
- "total daemons: %d/%d",
- free_slots[i], active_daemons,
- retained->total_daemons);
- make_child(ap_server_conf, free_slots[i], child_bucket);
+ int slot = free_slots[i];
+ if (make_child(ap_server_conf, slot, child_bucket) < 0) {
+ continue;
+ }
+ if (*max_daemon_used < slot + 1) {
+ *max_daemon_used = slot + 1;
+ }
}
/* the next time around we want to spawn twice as many if this
* wasn't good enough, but not if we've just done a graceful
@@ -2900,8 +3054,11 @@ static void perform_idle_server_maintenance(int child_bucket, int num_buckets)
}
}
-static void server_main_loop(int remaining_children_to_start, int num_buckets)
+static void server_main_loop(int remaining_children_to_start)
{
+ int num_buckets = retained->mpm->num_buckets;
+ int max_daemon_used = 0;
+ int successive_kills = 0;
int child_slot;
apr_exit_why_e exitwhy;
int status, processed_status;
@@ -2947,28 +3104,18 @@ static void server_main_loop(int remaining_children_to_start, int num_buckets)
}
/* non-fatal death... note that it's gone in the scoreboard. */
if (child_slot >= 0) {
- process_score *ps;
-
- for (i = 0; i < threads_per_child; i++)
- ap_update_child_status_from_indexes(child_slot, i,
- SERVER_DEAD, NULL);
-
- event_note_child_killed(child_slot, 0, 0);
- ps = &ap_scoreboard_image->parent[child_slot];
- if (!ps->quiescing)
- active_daemons--;
- ps->quiescing = 0;
- /* NOTE: We don't dec in the (child_slot < 0) case! */
- retained->total_daemons--;
+ event_note_child_stopped(child_slot, 0, 0);
+
if (processed_status == APEXIT_CHILDSICK) {
/* resource shortage, minimize the fork rate */
- retained->idle_spawn_rate[ps->bucket] = 1;
+ retained->idle_spawn_rate[child_slot % num_buckets] = 1;
}
else if (remaining_children_to_start) {
/* we're still doing a 1-for-1 replacement of dead
* children with new children
*/
- make_child(ap_server_conf, child_slot, ps->bucket);
+ make_child(ap_server_conf, child_slot,
+ child_slot % num_buckets);
--remaining_children_to_start;
}
}
@@ -2990,11 +3137,30 @@ static void server_main_loop(int remaining_children_to_start, int num_buckets)
/* Don't perform idle maintenance when a child dies,
* only do it when there's a timeout. Remember only a
* finite number of children can die, and it's pretty
- * pathological for a lot to die suddenly.
+ * pathological for a lot to die suddenly. If a child is
+ * killed by a signal (faulting) we want to restart it ASAP
+ * though, up to 3 successive faults or we stop this until
+ * a timeout happens again (to avoid the flood of fork()ed
+ * processes that keep being killed early).
*/
- continue;
+ if (child_slot < 0 || !APR_PROC_CHECK_SIGNALED(exitwhy)) {
+ continue;
+ }
+ if (++successive_kills >= 3) {
+ if (successive_kills % 10 == 3) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0,
+ ap_server_conf, APLOGNO(10392)
+ "children are killed successively!");
+ }
+ continue;
+ }
+ ++remaining_children_to_start;
+ }
+ else {
+ successive_kills = 0;
}
- else if (remaining_children_to_start) {
+
+ if (remaining_children_to_start) {
/* we hit a 1 second timeout in which none of the previous
* generation of children needed to be reaped... so assume
* they're all done, and pick up the slack if any is left.
@@ -3008,9 +3174,11 @@ static void server_main_loop(int remaining_children_to_start, int num_buckets)
continue;
}
+ max_daemon_used = 0;
for (i = 0; i < num_buckets; i++) {
- perform_idle_server_maintenance(i, num_buckets);
+ perform_idle_server_maintenance(i, &max_daemon_used);
}
+ retained->max_daemon_used = max_daemon_used;
}
}
@@ -3086,7 +3254,7 @@ static int event_run(apr_pool_t * _pconf, apr_pool_t * plog, server_rec * s)
retained->mpm->mpm_state = AP_MPMQ_RUNNING;
- server_main_loop(remaining_children_to_start, num_buckets);
+ server_main_loop(remaining_children_to_start);
retained->mpm->mpm_state = AP_MPMQ_STOPPING;
if (retained->mpm->shutdown_pending && retained->mpm->is_ungraceful) {
@@ -3098,7 +3266,7 @@ static int event_run(apr_pool_t * _pconf, apr_pool_t * plog, server_rec * s)
AP_MPM_PODX_RESTART);
}
ap_reclaim_child_processes(1, /* Start with SIGTERM */
- event_note_child_killed);
+ event_note_child_stopped);
if (!child_fatal) {
/* cleanup pid file on normal shutdown */
@@ -3124,7 +3292,7 @@ static int event_run(apr_pool_t * _pconf, apr_pool_t * plog, server_rec * s)
ap_mpm_podx_killpg(all_buckets[i].pod, active_daemons_limit,
AP_MPM_PODX_GRACEFUL);
}
- ap_relieve_child_processes(event_note_child_killed);
+ ap_relieve_child_processes(event_note_child_stopped);
if (!child_fatal) {
/* cleanup pid file on normal shutdown */
@@ -3146,10 +3314,10 @@ static int event_run(apr_pool_t * _pconf, apr_pool_t * plog, server_rec * s)
apr_sleep(apr_time_from_sec(1));
/* Relieve any children which have now exited */
- ap_relieve_child_processes(event_note_child_killed);
+ ap_relieve_child_processes(event_note_child_stopped);
active_children = 0;
- for (index = 0; index < retained->max_daemons_limit; ++index) {
+ for (index = 0; index < retained->max_daemon_used; ++index) {
if (ap_mpm_safe_kill(MPM_CHILD_PID(index), 0) == APR_SUCCESS) {
active_children = 1;
/* Having just one child is enough to stay around */
@@ -3167,7 +3335,7 @@ static int event_run(apr_pool_t * _pconf, apr_pool_t * plog, server_rec * s)
ap_mpm_podx_killpg(all_buckets[i].pod, active_daemons_limit,
AP_MPM_PODX_RESTART);
}
- ap_reclaim_child_processes(1, event_note_child_killed);
+ ap_reclaim_child_processes(1, event_note_child_stopped);
return DONE;
}
@@ -3187,8 +3355,7 @@ static int event_run(apr_pool_t * _pconf, apr_pool_t * plog, server_rec * s)
if (!retained->mpm->is_ungraceful) {
ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00493)
- AP_SIG_GRACEFUL_STRING
- " received. Doing graceful restart");
+ AP_SIG_GRACEFUL_STRING " received. Doing graceful restart");
/* wake up the children...time to die. But we'll have more soon */
for (i = 0; i < num_buckets; i++) {
ap_mpm_podx_killpg(all_buckets[i].pod, active_daemons_limit,
@@ -3201,6 +3368,8 @@ static int event_run(apr_pool_t * _pconf, apr_pool_t * plog, server_rec * s)
}
else {
+ ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00494)
+ "SIGHUP received. Attempting to restart");
/* Kill 'em all. Since the child acts the same on the parents SIGTERM
* and a SIGHUP, we may as well use the same signal, because some user
* pthreads are stealing signals from us left and right.
@@ -3211,13 +3380,9 @@ static int event_run(apr_pool_t * _pconf, apr_pool_t * plog, server_rec * s)
}
ap_reclaim_child_processes(1, /* Start with SIGTERM */
- event_note_child_killed);
- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, ap_server_conf, APLOGNO(00494)
- "SIGHUP received. Attempting to restart");
+ event_note_child_stopped);
}
- active_daemons = 0;
-
return OK;
}
@@ -3332,8 +3497,9 @@ static int event_open_logs(apr_pool_t * p, apr_pool_t * plog,
new_max = num_buckets;
}
new_ptr = (int *)apr_palloc(ap_pglobal, new_max * sizeof(int));
- memcpy(new_ptr, retained->idle_spawn_rate,
- retained->mpm->num_buckets * sizeof(int));
+ if (retained->idle_spawn_rate) /* NULL at startup */
+ memcpy(new_ptr, retained->idle_spawn_rate,
+ retained->mpm->num_buckets * sizeof(int));
retained->idle_spawn_rate = new_ptr;
retained->mpm->max_buckets = new_max;
}
@@ -3383,7 +3549,6 @@ static int event_pre_config(apr_pool_t * pconf, apr_pool_t * plog,
if (!retained) {
retained = ap_retained_data_create(userdata_key, sizeof(*retained));
retained->mpm = ap_unixd_mpm_get_retained_data();
- retained->max_daemons_limit = -1;
if (retained->mpm->module_loads) {
test_atomics = 1;
}
@@ -3453,6 +3618,7 @@ static int event_pre_config(apr_pool_t * pconf, apr_pool_t * plog,
worker_queue_info = NULL;
listener_os_thread = NULL;
listensocks_disabled = 0;
+ listener_is_wakeable = 0;
return OK;
}
diff --git a/server/mpm/mpmt_os2/mpmt_os2.c b/server/mpm/mpmt_os2/mpmt_os2.c
index 22bf5e7..b3adb03 100644
--- a/server/mpm/mpmt_os2/mpmt_os2.c
+++ b/server/mpm/mpmt_os2/mpmt_os2.c
@@ -26,7 +26,7 @@
*
* Each child process consists of a pool of worker threads and a
* main thread that accepts connections & passes them to the workers via
- * a work queue. The worker thread pool is dynamic, managed by a maintanence
+ * a work queue. The worker thread pool is dynamic, managed by a maintenance
* thread so that the number of idle threads is kept between
* min_spare_threads & max_spare_threads.
*
@@ -79,7 +79,7 @@ int ap_min_spare_threads = 0;
int ap_max_spare_threads = 0;
/* Keep track of a few interesting statistics */
-int ap_max_daemons_limit = -1;
+int ap_max_daemons_limit = 0;
/* volatile just in case */
static int volatile shutdown_pending;
@@ -344,8 +344,8 @@ static void spawn_child(int slot)
"error spawning child, slot %d", slot);
}
- if (ap_max_daemons_limit < slot) {
- ap_max_daemons_limit = slot;
+ if (slot + 1 > ap_max_daemons_limit) {
+ ap_max_daemons_limit = slot + 1;
}
ap_scoreboard_image->parent[slot].pid = proc_rc.codeTerminate;
diff --git a/server/mpm/mpmt_os2/mpmt_os2_child.c b/server/mpm/mpmt_os2/mpmt_os2_child.c
index bb7e136..f405cd2 100644
--- a/server/mpm/mpmt_os2/mpmt_os2_child.c
+++ b/server/mpm/mpmt_os2/mpmt_os2_child.c
@@ -200,6 +200,7 @@ void ap_mpm_child_main(apr_pool_t *pconf)
int last_poll_idx = 0;
apr_pool_create(&pconn, pchild);
+ apr_pool_tag(pconn, "transaction");
worker_args = apr_palloc(pconn, sizeof(worker_args_t));
worker_args->pconn = pconn;
diff --git a/server/mpm/netware/mpm_netware.c b/server/mpm/netware/mpm_netware.c
index 8248033..e89fdef 100644
--- a/server/mpm/netware/mpm_netware.c
+++ b/server/mpm/netware/mpm_netware.c
@@ -896,6 +896,7 @@ static int netware_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
set_signals();
apr_pool_create(&pmain, pconf);
+ apr_pool_tag(pmain, "pmain");
ap_run_child_init(pmain, ap_server_conf);
if (ap_threads_max_free < ap_threads_min_free + 1) /* Don't thrash... */
diff --git a/server/mpm/prefork/prefork.c b/server/mpm/prefork/prefork.c
index 8efda72..b5adb57 100644
--- a/server/mpm/prefork/prefork.c
+++ b/server/mpm/prefork/prefork.c
@@ -223,6 +223,10 @@ static void clean_child_exit(int code)
apr_signal(SIGHUP, SIG_IGN);
apr_signal(SIGTERM, SIG_IGN);
+ if (code == 0) {
+ ap_run_child_stopping(pchild, 0);
+ }
+
if (pchild) {
apr_pool_destroy(pchild);
}
@@ -376,11 +380,22 @@ static void stop_listening(int sig)
static int requests_this_child;
static int num_listensocks = 0;
+#if APR_HAS_THREADS
+static void child_sigmask(sigset_t *new_mask, sigset_t *old_mask)
+{
+#if defined(SIGPROCMASK_SETS_THREAD_MASK)
+ sigprocmask(SIG_SETMASK, new_mask, old_mask);
+#else
+ pthread_sigmask(SIG_SETMASK, new_mask, old_mask);
+#endif
+}
+#endif
+
static void child_main(int child_num_arg, int child_bucket)
{
#if APR_HAS_THREADS
apr_thread_t *thd = NULL;
- apr_os_thread_t osthd;
+ sigset_t sig_mask;
#endif
apr_pool_t *ptrans;
apr_allocator_t *allocator;
@@ -411,9 +426,23 @@ static void child_main(int child_num_arg, int child_bucket)
apr_allocator_owner_set(allocator, pchild);
apr_pool_tag(pchild, "pchild");
+#if AP_HAS_THREAD_LOCAL
+ if (one_process) {
+ thd = ap_thread_current();
+ }
+ else if ((status = ap_thread_main_create(&thd, pchild))) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, status, ap_server_conf, APLOGNO(10378)
+ "Couldn't initialize child main thread");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+#elif APR_HAS_THREADS
+ {
+ apr_os_thread_t osthd = apr_os_thread_current();
+ apr_os_thread_put(&thd, &osthd, pchild);
+ }
+#endif
#if APR_HAS_THREADS
- osthd = apr_os_thread_current();
- apr_os_thread_put(&thd, &osthd, pchild);
+ ap_assert(thd != NULL);
#endif
apr_pool_create(&ptrans, pchild);
@@ -446,8 +475,31 @@ static void child_main(int child_num_arg, int child_bucket)
clean_child_exit(APEXIT_CHILDFATAL);
}
+#if APR_HAS_THREADS
+ /* Save the signal mask and block all the signals from being received by
+ * threads potentially created in child_init() hooks (e.g. mod_watchdog).
+ */
+ child_sigmask(NULL, &sig_mask);
+ {
+ apr_status_t rv;
+ rv = apr_setup_signal_thread();
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf, APLOGNO(10271)
+ "Couldn't initialize signal thread");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+ }
+#endif /* APR_HAS_THREADS */
+
ap_run_child_init(pchild, ap_server_conf);
+#if APR_HAS_THREADS
+ /* Restore the original signal mask for this main thread, the only one
+ * that should possibly get interrupted by signals.
+ */
+ child_sigmask(&sig_mask, NULL);
+#endif
+
ap_create_sb_handle(&sbh, pchild, my_child_num, 0);
(void) ap_update_child_status(sbh, SERVER_READY, (request_rec *) NULL);
@@ -637,8 +689,9 @@ static void child_main(int child_num_arg, int child_bucket)
}
-static int make_child(server_rec *s, int slot, int bucket)
+static int make_child(server_rec *s, int slot)
{
+ int bucket = slot % retained->mpm->num_buckets;
int pid;
if (slot + 1 > retained->max_daemons_limit) {
@@ -682,6 +735,10 @@ static int make_child(server_rec *s, int slot, int bucket)
}
if (!pid) {
+#if AP_HAS_THREAD_LOCAL
+ ap_thread_current_after_fork();
+#endif
+
my_bucket = &all_buckets[bucket];
#ifdef HAVE_BINDPROCESSOR
@@ -702,8 +759,8 @@ static int make_child(server_rec *s, int slot, int bucket)
*/
apr_signal(SIGHUP, just_die);
apr_signal(SIGTERM, just_die);
- /* Ignore SIGINT in child. This fixes race-condition in signals
- * handling when httpd is runnning on foreground and user hits ctrl+c.
+ /* Ignore SIGINT in child. This fixes race-conditions in signals
+ * handling when httpd is running on foreground and user hits ctrl+c.
* In this case, SIGINT is sent to all children followed by SIGTERM
* from the main process, which interrupts the SIGINT handler and
* leads to inconsistency.
@@ -716,7 +773,6 @@ static int make_child(server_rec *s, int slot, int bucket)
child_main(slot, bucket);
}
- ap_scoreboard_image->parent[slot].bucket = bucket;
prefork_note_child_started(slot, pid);
return 0;
@@ -732,7 +788,7 @@ static void startup_children(int number_to_start)
if (ap_scoreboard_image->servers[i][0].status != SERVER_DEAD) {
continue;
}
- if (make_child(ap_server_conf, i, i % retained->mpm->num_buckets) < 0) {
+ if (make_child(ap_server_conf, i) < 0) {
break;
}
--number_to_start;
@@ -741,8 +797,6 @@ static void startup_children(int number_to_start)
static void perform_idle_server_maintenance(apr_pool_t *p)
{
- static int bucket_make_child_record = -1;
- static int bucket_kill_child_record = -1;
int i;
int idle_count;
worker_score *ws;
@@ -789,6 +843,7 @@ static void perform_idle_server_maintenance(apr_pool_t *p)
}
retained->max_daemons_limit = last_non_dead + 1;
if (idle_count > ap_daemons_max_free) {
+ static int bucket_kill_child_record = -1;
/* kill off one child... we use the pod because that'll cause it to
* shut down gracefully, in case it happened to pick up a request
* while we were counting
@@ -819,10 +874,7 @@ static void perform_idle_server_maintenance(apr_pool_t *p)
idle_count, total_non_dead);
}
for (i = 0; i < free_length; ++i) {
- bucket_make_child_record++;
- bucket_make_child_record %= retained->mpm->num_buckets;
- make_child(ap_server_conf, free_slots[i],
- bucket_make_child_record);
+ make_child(ap_server_conf, free_slots[i]);
}
/* the next time around we want to spawn twice as many if this
* wasn't good enough, but not if we've just done a graceful
@@ -867,7 +919,7 @@ static int prefork_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
if (one_process) {
AP_MONCONTROL(1);
- make_child(ap_server_conf, 0, 0);
+ make_child(ap_server_conf, 0);
/* NOTREACHED */
ap_assert(0);
return !OK;
@@ -976,8 +1028,7 @@ static int prefork_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
/* we're still doing a 1-for-1 replacement of dead
* children with new children
*/
- make_child(ap_server_conf, child_slot,
- ap_get_scoreboard_process(child_slot)->bucket);
+ make_child(ap_server_conf, child_slot);
--remaining_children_to_start;
}
#if APR_HAS_OTHER_CHILD
@@ -1254,7 +1305,6 @@ static int prefork_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp
if (!retained) {
retained = ap_retained_data_create(userdata_key, sizeof(*retained));
retained->mpm = ap_unixd_mpm_get_retained_data();
- retained->max_daemons_limit = -1;
retained->idle_spawn_rate = 1;
}
retained->mpm->mpm_state = AP_MPMQ_STARTING;
@@ -1268,7 +1318,7 @@ static int prefork_pre_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp
if (retained->mpm->module_loads == 2) {
if (!one_process && !foreground) {
/* before we detach, setup crash handlers to log to errorlog */
- ap_fatal_signal_setup(ap_server_conf, pconf);
+ ap_fatal_signal_setup(ap_server_conf, p /* == pconf */);
rv = apr_proc_detach(no_detach ? APR_PROC_DETACH_FOREGROUND
: APR_PROC_DETACH_DAEMONIZE);
if (rv != APR_SUCCESS) {
diff --git a/server/mpm/winnt/child.c b/server/mpm/winnt/child.c
index 21755f3..05151a8 100644
--- a/server/mpm/winnt/child.c
+++ b/server/mpm/winnt/child.c
@@ -352,7 +352,7 @@ static unsigned int __stdcall winnt_accept(void *lr_)
ap_listen_rec *lr = (ap_listen_rec *)lr_;
apr_os_sock_info_t sockinfo;
winnt_conn_ctx_t *context = NULL;
- DWORD BytesRead;
+ DWORD BytesRead = 0;
SOCKET nlsd;
LPFN_ACCEPTEX lpfnAcceptEx = NULL;
LPFN_GETACCEPTEXSOCKADDRS lpfnGetAcceptExSockaddrs = NULL;
@@ -784,8 +784,8 @@ static winnt_conn_ctx_t *winnt_get_connection(winnt_conn_ctx_t *context)
*/
static DWORD __stdcall worker_main(void *thread_num_val)
{
- apr_thread_t *thd;
- apr_os_thread_t osthd;
+ apr_thread_t *thd = NULL;
+ apr_os_thread_t osthd = NULL;
static int requests_this_child = 0;
winnt_conn_ctx_t *context = NULL;
int thread_num = (int)thread_num_val;
@@ -793,7 +793,16 @@ static DWORD __stdcall worker_main(void *thread_num_val)
conn_rec *c;
apr_int32_t disconnected;
+#if AP_HAS_THREAD_LOCAL
+ if (ap_thread_current_create(&thd, NULL, pchild) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, ap_server_conf, APLOGNO(10376)
+ "Couldn't initialize worker thread, thread locals won't "
+ "be available");
+ osthd = apr_os_thread_current();
+ }
+#else
osthd = apr_os_thread_current();
+#endif
while (1) {
@@ -826,8 +835,10 @@ static DWORD __stdcall worker_main(void *thread_num_val)
continue;
}
- thd = NULL;
- apr_os_thread_put(&thd, &osthd, context->ptrans);
+ if (osthd) {
+ thd = NULL;
+ apr_os_thread_put(&thd, &osthd, context->ptrans);
+ }
c->current_thread = thd;
ap_process_connection(c, context->sock);
@@ -842,6 +853,12 @@ static DWORD __stdcall worker_main(void *thread_num_val)
ap_update_child_status_from_indexes(0, thread_num, SERVER_DEAD, NULL);
+#if AP_HAS_THREAD_LOCAL
+ if (!osthd) {
+ apr_pool_destroy(apr_thread_pool_get(thd));
+ }
+#endif
+
return 0;
}
diff --git a/server/mpm/winnt/mpm_winnt.c b/server/mpm/winnt/mpm_winnt.c
index 3d71f80..1b8962e 100644
--- a/server/mpm/winnt/mpm_winnt.c
+++ b/server/mpm/winnt/mpm_winnt.c
@@ -437,7 +437,7 @@ static int send_handles_to_child(apr_pool_t *p,
* get_listeners_from_parent()
* The listen sockets are opened in the parent. This function, which runs
* exclusively in the child process, receives them from the parent and
- * makes them availeble in the child.
+ * makes them available in the child.
*/
static void get_listeners_from_parent(server_rec *s)
{
@@ -559,6 +559,7 @@ static int create_process(apr_pool_t *p, HANDLE *child_proc, HANDLE *child_exit_
int envc;
apr_pool_create_ex(&ptemp, p, NULL, NULL);
+ apr_pool_tag(ptemp, "create_process");
/* Build the command line. Should look something like this:
* C:/apache/bin/httpd.exe -f ap_server_confname
@@ -586,10 +587,10 @@ static int create_process(apr_pool_t *p, HANDLE *child_proc, HANDLE *child_exit_
return -1;
}
- args = malloc((ap_server_conf->process->argc + 1) * sizeof (char*));
+ args = ap_malloc((ap_server_conf->process->argc + 1) * sizeof (char*));
memcpy(args + 1, ap_server_conf->process->argv + 1,
(ap_server_conf->process->argc - 1) * sizeof (char*));
- args[0] = malloc(strlen(cmd) + 1);
+ args[0] = ap_malloc(strlen(cmd) + 1);
strcpy(args[0], cmd);
args[ap_server_conf->process->argc] = NULL;
}
@@ -743,7 +744,7 @@ static int create_process(apr_pool_t *p, HANDLE *child_proc, HANDLE *child_exit_
* of this event means that the child process has exited prematurely
* due to a seg fault or other irrecoverable error. For server
* robustness, master_main will restart the child process under this
- * condtion.
+ * condition.
*
* master_main uses the child_exit_event to signal the child process
* to exit.
@@ -1130,7 +1131,7 @@ static void winnt_rewrite_args(process_rec *process)
"Failed to get the full path of %s", process->argv[0]);
exit(APEXIT_INIT);
}
- /* WARNING: There is an implict assumption here that the
+ /* WARNING: There is an implicit assumption here that the
* executable resides in ServerRoot or ServerRoot\bin
*/
def_server_root = (char *) apr_filepath_name_get(binpath);
@@ -1377,7 +1378,7 @@ static int winnt_pre_config(apr_pool_t *pconf_, apr_pool_t *plog, apr_pool_t *pt
ap_exists_config_define("DEBUG"))
one_process = -1;
- /* XXX: presume proper privilages; one nice thing would be
+ /* XXX: presume proper privileges; one nice thing would be
* a loud emit if running as "LocalSystem"/"SYSTEM" to indicate
* they should change to a user with write access to logs/ alone.
*/
@@ -1574,7 +1575,6 @@ static int winnt_post_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *pt
/* This code should be run once in the parent and not run
* across a restart
*/
- PSECURITY_ATTRIBUTES sa = GetNullACL(); /* returns NULL if invalid (Win95?) */
setup_signal_names(apr_psprintf(pconf, "ap%lu", parent_pid));
ap_log_pid(pconf, ap_pid_fname);
@@ -1582,29 +1582,26 @@ static int winnt_post_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *pt
/* Create shutdown event, apPID_shutdown, where PID is the parent
* Apache process ID. Shutdown is signaled by 'apache -k shutdown'.
*/
- shutdown_event = CreateEvent(sa, FALSE, FALSE, signal_shutdown_name);
+ shutdown_event = CreateEvent(NULL, FALSE, FALSE, signal_shutdown_name);
if (!shutdown_event) {
ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf, APLOGNO(00448)
"Parent: Cannot create shutdown event %s", signal_shutdown_name);
- CleanNullACL((void *)sa);
return HTTP_INTERNAL_SERVER_ERROR;
}
/* Create restart event, apPID_restart, where PID is the parent
* Apache process ID. Restart is signaled by 'apache -k restart'.
*/
- restart_event = CreateEvent(sa, FALSE, FALSE, signal_restart_name);
+ restart_event = CreateEvent(NULL, FALSE, FALSE, signal_restart_name);
if (!restart_event) {
CloseHandle(shutdown_event);
ap_log_error(APLOG_MARK, APLOG_CRIT, apr_get_os_error(), ap_server_conf, APLOGNO(00449)
"Parent: Cannot create restart event %s", signal_restart_name);
- CleanNullACL((void *)sa);
return HTTP_INTERNAL_SERVER_ERROR;
}
- CleanNullACL((void *)sa);
/* Create the start mutex, as an unnamed object for security.
- * Ths start mutex is used during a restart to prevent more than
+ * The start mutex is used during a restart to prevent more than
* one child process from entering the accept loop at once.
*/
rv = apr_proc_mutex_create(&start_mutex, NULL,
diff --git a/server/mpm/winnt/nt_eventlog.c b/server/mpm/winnt/nt_eventlog.c
index e7e80d0..cd49ee6 100644
--- a/server/mpm/winnt/nt_eventlog.c
+++ b/server/mpm/winnt/nt_eventlog.c
@@ -39,6 +39,7 @@ static DWORD WINAPI service_stderr_thread(LPVOID hPipe)
apr_pool_t *p;
apr_pool_create_ex(&p, NULL, NULL, NULL);
+ apr_pool_tag(p, "service_stderr_thread");
errarg[0] = "The Apache service named";
errarg[1] = display_name;
diff --git a/server/mpm/winnt/service.c b/server/mpm/winnt/service.c
index 121aca1..2e473cf 100644
--- a/server/mpm/winnt/service.c
+++ b/server/mpm/winnt/service.c
@@ -801,7 +801,7 @@ apr_status_t mpm_service_install(apr_pool_t *ptemp, int argc,
rv = apr_get_os_error();
ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
APLOGNO(00369) "Failed to open the Windows service "
- "manager, perhaps you forgot to log in as Adminstrator?");
+ "manager, perhaps you forgot to log in as Administrator?");
return (rv);
}
@@ -957,7 +957,7 @@ apr_status_t mpm_service_uninstall(void)
rv = apr_get_os_error();
ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
APLOGNO(10009) "Failed to open the Windows service "
- "manager, perhaps you forgot to log in as Adminstrator?");
+ "manager, perhaps you forgot to log in as Administrator?");
return (rv);
}
@@ -1047,7 +1047,7 @@ apr_status_t mpm_service_start(apr_pool_t *ptemp, int argc,
rv = apr_get_os_error();
ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP, rv, NULL,
APLOGNO(10011) "Failed to open the Windows service "
- "manager, perhaps you forgot to log in as Adminstrator?");
+ "manager, perhaps you forgot to log in as Administrator?");
return (rv);
}
@@ -1157,7 +1157,7 @@ void mpm_signal_service(apr_pool_t *ptemp, int signal)
ap_log_error(APLOG_MARK, APLOG_ERR | APLOG_STARTUP,
apr_get_os_error(), NULL,
APLOGNO(10013) "Failed to open the Windows service "
- "manager, perhaps you forgot to log in as Adminstrator?");
+ "manager, perhaps you forgot to log in as Administrator?");
return;
}
diff --git a/server/mpm/worker/worker.c b/server/mpm/worker/worker.c
index 8012fe2..7b572bd 100644
--- a/server/mpm/worker/worker.c
+++ b/server/mpm/worker/worker.c
@@ -324,6 +324,8 @@ static void signal_threads(int mode)
ap_queue_interrupt_all(worker_queue);
close_worker_sockets(); /* forcefully kill all current connections */
}
+
+ ap_run_child_stopping(pchild, mode == ST_GRACEFUL);
}
static int worker_query(int query_code, int *result, apr_status_t *rv)
@@ -432,6 +434,10 @@ static void clean_child_exit(int code) __attribute__ ((noreturn));
static void clean_child_exit(int code)
{
retained->mpm->mpm_state = AP_MPMQ_STOPPING;
+ if (terminate_mode == ST_INIT) {
+ ap_run_child_stopping(pchild, 0);
+ }
+
if (pchild) {
apr_pool_destroy(pchild);
}
@@ -553,8 +559,8 @@ static void * APR_THREAD_FUNC listener_thread(apr_thread_t *thd, void * dummy)
/* Unblock the signal used to wake this thread up, and set a handler for
* it.
*/
- unblock_signal(LISTENER_SIGNAL);
apr_signal(LISTENER_SIGNAL, dummy_signal_handler);
+ unblock_signal(LISTENER_SIGNAL);
/* TODO: Switch to a system where threads reuse the results from earlier
poll calls - manoj */
@@ -748,8 +754,8 @@ static void * APR_THREAD_FUNC worker_thread(apr_thread_t *thd, void * dummy)
SERVER_STARTING, NULL);
#ifdef HAVE_PTHREAD_KILL
- unblock_signal(WORKER_SIGNAL);
apr_signal(WORKER_SIGNAL, dummy_signal_handler);
+ unblock_signal(WORKER_SIGNAL);
#endif
while (!workers_may_exit) {
@@ -841,11 +847,11 @@ static void create_listener_thread(thread_starter *ts)
my_info->pid = my_child_num;
my_info->tid = -1; /* listener thread doesn't have a thread slot */
my_info->sd = 0;
- rv = apr_thread_create(&ts->listener, thread_attr, listener_thread,
- my_info, pruntime);
+ rv = ap_thread_create(&ts->listener, thread_attr, listener_thread,
+ my_info, pruntime);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, APLOGNO(00275)
- "apr_thread_create: unable to create listener thread");
+ "ap_thread_create: unable to create listener thread");
/* let the parent decide how bad this really is */
clean_child_exit(APEXIT_CHILDSICK);
}
@@ -862,7 +868,7 @@ static void setup_threads_runtime(void)
* the connections they handle (i.e. ptrans). We can't use this thread's
* self pool because all these objects survive it, nor use pchild or pconf
* directly because this starter thread races with other modules' runtime,
- * nor finally pchild (or subpool thereof) because it is killed explicitely
+ * nor finally pchild (or subpool thereof) because it is killed explicitly
* before pconf (thus connections/ptrans can live longer, which matters in
* ONE_PROCESS mode). So this leaves us with a subpool of pconf, created
* before any ptrans hence destroyed after.
@@ -961,11 +967,11 @@ static void * APR_THREAD_FUNC start_threads(apr_thread_t *thd, void *dummy)
/* We let each thread update its own scoreboard entry. This is
* done because it lets us deal with tid better.
*/
- rv = apr_thread_create(&threads[i], thread_attr,
- worker_thread, my_info, pruntime);
+ rv = ap_thread_create(&threads[i], thread_attr,
+ worker_thread, my_info, pruntime);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, APLOGNO(03142)
- "apr_thread_create: unable to create worker thread");
+ "ap_thread_create: unable to create worker thread");
/* let the parent decide how bad this really is */
clean_child_exit(APEXIT_CHILDSICK);
}
@@ -1109,6 +1115,17 @@ static void child_main(int child_num_arg, int child_bucket)
apr_pool_create(&pchild, pconf);
apr_pool_tag(pchild, "pchild");
+#if AP_HAS_THREAD_LOCAL
+ if (!one_process) {
+ apr_thread_t *thd = NULL;
+ if ((rv = ap_thread_main_create(&thd, pchild))) {
+ ap_log_error(APLOG_MARK, APLOG_EMERG, rv, ap_server_conf, APLOGNO(10375)
+ "Couldn't initialize child main thread");
+ clean_child_exit(APEXIT_CHILDFATAL);
+ }
+ }
+#endif
+
/* close unused listeners and pods */
for (i = 0; i < retained->mpm->num_buckets; i++) {
if (i != child_bucket) {
@@ -1138,7 +1155,7 @@ static void child_main(int child_num_arg, int child_bucket)
* from being received. The child processes no longer use signals for
* any communication with the parent process. Let's also do this before
* child_init() hooks are called and possibly create threads that
- * otherwise could "steal" (implicitely) MPM's signals.
+ * otherwise could "steal" (implicitly) MPM's signals.
*/
rv = apr_setup_signal_thread();
if (rv != APR_SUCCESS) {
@@ -1188,11 +1205,11 @@ static void child_main(int child_num_arg, int child_bucket)
ts->child_num_arg = child_num_arg;
ts->threadattr = thread_attr;
- rv = apr_thread_create(&start_thread_id, thread_attr, start_threads,
- ts, pchild);
+ rv = ap_thread_create(&start_thread_id, thread_attr, start_threads,
+ ts, pchild);
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_ALERT, rv, ap_server_conf, APLOGNO(00282)
- "apr_thread_create: unable to create worker thread");
+ "ap_thread_create: unable to create worker thread");
/* let the parent decide how bad this really is */
clean_child_exit(APEXIT_CHILDSICK);
}
@@ -1230,8 +1247,8 @@ static void child_main(int child_num_arg, int child_bucket)
* the other threads in the process needs to take us down
* (e.g., for MaxConnectionsPerChild) it will send us SIGTERM
*/
- unblock_signal(SIGTERM);
apr_signal(SIGTERM, dummy_signal_handler);
+ unblock_signal(SIGTERM);
/* Watch for any messages from the parent over the POD */
while (1) {
rv = ap_mpm_podx_check(my_bucket->pod);
@@ -1309,6 +1326,10 @@ static int make_child(server_rec *s, int slot, int bucket)
}
if (!pid) {
+#if AP_HAS_THREAD_LOCAL
+ ap_thread_current_after_fork();
+#endif
+
my_bucket = &all_buckets[bucket];
#ifdef HAVE_BINDPROCESSOR
@@ -1339,7 +1360,6 @@ static int make_child(server_rec *s, int slot, int bucket)
worker_note_child_lost_slot(slot, pid);
}
ap_scoreboard_image->parent[slot].quiescing = 0;
- ap_scoreboard_image->parent[slot].bucket = bucket;
worker_note_child_started(slot, pid);
return 0;
}
@@ -1360,11 +1380,10 @@ static void startup_children(int number_to_start)
}
}
-static void perform_idle_server_maintenance(int child_bucket, int num_buckets)
+static void perform_idle_server_maintenance(int child_bucket)
{
- int i, j;
+ int num_buckets = retained->mpm->num_buckets;
int idle_thread_count;
- worker_score *ws;
process_score *ps;
int free_length;
int totally_free_length = 0;
@@ -1372,6 +1391,7 @@ static void perform_idle_server_maintenance(int child_bucket, int num_buckets)
int last_non_dead;
int total_non_dead;
int active_thread_count = 0;
+ int i, j;
/* initialize the free_list */
free_length = 0;
@@ -1383,12 +1403,15 @@ static void perform_idle_server_maintenance(int child_bucket, int num_buckets)
for (i = 0; i < ap_daemons_limit; ++i) {
/* Initialization to satisfy the compiler. It doesn't know
* that threads_per_child is always > 0 */
- int status = SERVER_DEAD;
int any_dying_threads = 0;
int any_dead_threads = 0;
int all_dead_threads = 1;
int child_threads_active = 0;
+ if (num_buckets > 1 && (i % num_buckets) != child_bucket) {
+ /* We only care about child_bucket in this call */
+ continue;
+ }
if (i >= retained->max_daemons_limit &&
totally_free_length == retained->idle_spawn_rate[child_bucket]) {
/* short cut if all active processes have been examined and
@@ -1398,8 +1421,7 @@ static void perform_idle_server_maintenance(int child_bucket, int num_buckets)
}
ps = &ap_scoreboard_image->parent[i];
for (j = 0; j < threads_per_child; j++) {
- ws = &ap_scoreboard_image->servers[i][j];
- status = ws->status;
+ int status = ap_scoreboard_image->servers[i][j].status;
/* XXX any_dying_threads is probably no longer needed GLA */
any_dying_threads = any_dying_threads ||
@@ -1419,8 +1441,7 @@ static void perform_idle_server_maintenance(int child_bucket, int num_buckets)
loop if no pid? not much else matters */
if (status <= SERVER_READY &&
!ps->quiescing &&
- ps->generation == retained->mpm->my_generation &&
- ps->bucket == child_bucket) {
+ ps->generation == retained->mpm->my_generation) {
++idle_thread_count;
}
if (status >= SERVER_READY && status < SERVER_GRACEFUL) {
@@ -1457,11 +1478,15 @@ static void perform_idle_server_maintenance(int child_bucket, int num_buckets)
}
/* XXX if (!ps->quiescing) is probably more reliable GLA */
if (!any_dying_threads) {
- last_non_dead = i;
++total_non_dead;
}
+ if (ps->pid != 0) {
+ last_non_dead = i;
+ }
}
+ retained->max_daemons_limit = last_non_dead + 1;
+
if (retained->sick_child_detected) {
if (had_healthy_child) {
/* Assume this is a transient error, even though it may not be. Leave
@@ -1470,6 +1495,10 @@ static void perform_idle_server_maintenance(int child_bucket, int num_buckets)
*/
retained->sick_child_detected = 0;
}
+ else if (child_bucket < num_buckets - 1) {
+ /* check for had_healthy_child up to the last child bucket */
+ return;
+ }
else {
/* looks like a basket case, as no child ever fully initialized; give up.
*/
@@ -1485,8 +1514,6 @@ static void perform_idle_server_maintenance(int child_bucket, int num_buckets)
}
}
- retained->max_daemons_limit = last_non_dead + 1;
-
if (idle_thread_count > max_spare_threads / num_buckets) {
/* Kill off one child */
ap_mpm_podx_signal(all_buckets[child_bucket].pod,
@@ -1497,7 +1524,7 @@ static void perform_idle_server_maintenance(int child_bucket, int num_buckets)
/* terminate the free list */
if (free_length == 0) { /* scoreboard is full, can't fork */
- if (active_thread_count >= ap_daemons_limit * threads_per_child) {
+ if (active_thread_count >= max_workers / num_buckets) {
/* no threads are "inactive" - starting, stopping, etc. */
/* have we reached MaxRequestWorkers, or just getting close? */
if (0 == idle_thread_count) {
@@ -1560,8 +1587,10 @@ static void perform_idle_server_maintenance(int child_bucket, int num_buckets)
}
}
-static void server_main_loop(int remaining_children_to_start, int num_buckets)
+static void server_main_loop(int remaining_children_to_start)
{
+ int num_buckets = retained->mpm->num_buckets;
+ int successive_kills = 0;
ap_generation_t old_gen;
int child_slot;
apr_exit_why_e exitwhy;
@@ -1615,14 +1644,15 @@ static void server_main_loop(int remaining_children_to_start, int num_buckets)
ps->quiescing = 0;
if (processed_status == APEXIT_CHILDSICK) {
/* resource shortage, minimize the fork rate */
- retained->idle_spawn_rate[ps->bucket] = 1;
+ retained->idle_spawn_rate[child_slot % num_buckets] = 1;
}
else if (remaining_children_to_start
&& child_slot < ap_daemons_limit) {
/* we're still doing a 1-for-1 replacement of dead
* children with new children
*/
- make_child(ap_server_conf, child_slot, ps->bucket);
+ make_child(ap_server_conf, child_slot,
+ child_slot % num_buckets);
--remaining_children_to_start;
}
}
@@ -1655,11 +1685,30 @@ static void server_main_loop(int remaining_children_to_start, int num_buckets)
/* Don't perform idle maintenance when a child dies,
* only do it when there's a timeout. Remember only a
* finite number of children can die, and it's pretty
- * pathological for a lot to die suddenly.
+ * pathological for a lot to die suddenly. If a child is
+ * killed by a signal (faulting) we want to restart it ASAP
+ * though, up to 3 successive faults or we stop this until
+ * a timeout happens again (to avoid the flood of fork()ed
+ * processes that keep being killed early).
*/
- continue;
+ if (child_slot < 0 || !APR_PROC_CHECK_SIGNALED(exitwhy)) {
+ continue;
+ }
+ if (++successive_kills >= 3) {
+ if (successive_kills % 10 == 3) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0,
+ ap_server_conf, APLOGNO(10393)
+ "children are killed successively!");
+ }
+ continue;
+ }
+ ++remaining_children_to_start;
+ }
+ else {
+ successive_kills = 0;
}
- else if (remaining_children_to_start) {
+
+ if (remaining_children_to_start) {
/* we hit a 1 second timeout in which none of the previous
* generation of children needed to be reaped... so assume
* they're all done, and pick up the slack if any is left.
@@ -1674,7 +1723,7 @@ static void server_main_loop(int remaining_children_to_start, int num_buckets)
}
for (i = 0; i < num_buckets; i++) {
- perform_idle_server_maintenance(i, num_buckets);
+ perform_idle_server_maintenance(i);
}
}
}
@@ -1756,7 +1805,7 @@ static int worker_run(apr_pool_t *_pconf, apr_pool_t *plog, server_rec *s)
apr_proc_mutex_defname());
retained->mpm->mpm_state = AP_MPMQ_RUNNING;
- server_main_loop(remaining_children_to_start, num_buckets);
+ server_main_loop(remaining_children_to_start);
retained->mpm->mpm_state = AP_MPMQ_STOPPING;
if (retained->mpm->shutdown_pending && retained->mpm->is_ungraceful) {
@@ -1960,8 +2009,9 @@ static int worker_open_logs(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp,
new_max = num_buckets;
}
new_ptr = (int *)apr_palloc(ap_pglobal, new_max * sizeof(int));
- memcpy(new_ptr, retained->idle_spawn_rate,
- retained->mpm->num_buckets * sizeof(int));
+ if (retained->idle_spawn_rate) /* NULL at startup */
+ memcpy(new_ptr, retained->idle_spawn_rate,
+ retained->mpm->num_buckets * sizeof(int));
retained->idle_spawn_rate = new_ptr;
retained->mpm->max_buckets = new_max;
}
@@ -2010,7 +2060,6 @@ static int worker_pre_config(apr_pool_t *pconf, apr_pool_t *plog,
if (!retained) {
retained = ap_retained_data_create(userdata_key, sizeof(*retained));
retained->mpm = ap_unixd_mpm_get_retained_data();
- retained->max_daemons_limit = -1;
}
retained->mpm->mpm_state = AP_MPMQ_STARTING;
if (retained->mpm->baton != retained) {
diff --git a/server/mpm_common.c b/server/mpm_common.c
index 04f36d9..9bcd760 100644
--- a/server/mpm_common.c
+++ b/server/mpm_common.c
@@ -72,7 +72,8 @@
APR_HOOK_LINK(end_generation) \
APR_HOOK_LINK(child_status) \
APR_HOOK_LINK(suspend_connection) \
- APR_HOOK_LINK(resume_connection)
+ APR_HOOK_LINK(resume_connection) \
+ APR_HOOK_LINK(child_stopping)
#if AP_ENABLE_EXCEPTION_HOOK
APR_HOOK_STRUCT(
@@ -112,6 +113,9 @@ AP_IMPLEMENT_HOOK_VOID(suspend_connection,
AP_IMPLEMENT_HOOK_VOID(resume_connection,
(conn_rec *c, request_rec *r),
(c, r))
+AP_IMPLEMENT_HOOK_VOID(child_stopping,
+ (apr_pool_t *pchild, int graceful),
+ (pchild, graceful))
/* hooks with no args are implemented last, after disabling APR hook probes */
#if defined(APR_HOOK_PROBES_ENABLED)
diff --git a/server/mpm_fdqueue.c b/server/mpm_fdqueue.c
index c812450..3697ca7 100644
--- a/server/mpm_fdqueue.c
+++ b/server/mpm_fdqueue.c
@@ -493,6 +493,10 @@ static apr_status_t queue_interrupt(fd_queue_t *queue, int all, int term)
{
apr_status_t rv;
+ if (queue->terminated) {
+ return APR_EOF;
+ }
+
if ((rv = apr_thread_mutex_lock(queue->one_big_mutex)) != APR_SUCCESS) {
return rv;
}
diff --git a/server/mpm_fdqueue.h b/server/mpm_fdqueue.h
index 9aeedde..1047f88 100644
--- a/server/mpm_fdqueue.h
+++ b/server/mpm_fdqueue.h
@@ -83,7 +83,7 @@ struct fd_queue_t
unsigned int out;
apr_thread_mutex_t *one_big_mutex;
apr_thread_cond_t *not_empty;
- int terminated;
+ volatile int terminated;
};
typedef struct fd_queue_t fd_queue_t;
diff --git a/server/mpm_unix.c b/server/mpm_unix.c
index 1800f5d..ed4555a 100644
--- a/server/mpm_unix.c
+++ b/server/mpm_unix.c
@@ -259,10 +259,12 @@ AP_DECLARE(void) ap_reclaim_child_processes(int terminate,
while (cur_extra) {
ap_generation_t old_gen;
extra_process_t *next = cur_extra->next;
+ pid_t pid = cur_extra->pid;
- if (reclaim_one_pid(cur_extra->pid, action_table[cur_action].action)) {
- if (ap_unregister_extra_mpm_process(cur_extra->pid, &old_gen) == 1) {
- mpm_callback(-1, cur_extra->pid, old_gen);
+ if (reclaim_one_pid(pid, action_table[cur_action].action)) {
+ if (ap_unregister_extra_mpm_process(pid, &old_gen) == 1) {
+ /* cur_extra dangling pointer from here. */
+ mpm_callback(-1, pid, old_gen);
}
else {
AP_DEBUG_ASSERT(1 == 0);
@@ -307,10 +309,12 @@ AP_DECLARE(void) ap_relieve_child_processes(ap_reclaim_callback_fn_t *mpm_callba
while (cur_extra) {
ap_generation_t old_gen;
extra_process_t *next = cur_extra->next;
+ pid_t pid = cur_extra->pid;
- if (reclaim_one_pid(cur_extra->pid, DO_NOTHING)) {
- if (ap_unregister_extra_mpm_process(cur_extra->pid, &old_gen) == 1) {
- mpm_callback(-1, cur_extra->pid, old_gen);
+ if (reclaim_one_pid(pid, DO_NOTHING)) {
+ if (ap_unregister_extra_mpm_process(pid, &old_gen) == 1) {
+ /* cur_extra dangling pointer from here. */
+ mpm_callback(-1, pid, old_gen);
}
else {
AP_DEBUG_ASSERT(1 == 0);
@@ -632,13 +636,14 @@ static apr_status_t dummy_connection(ap_pod_t *pod)
if (rv != APR_SUCCESS) {
return rv;
}
+ apr_pool_tag(p, "dummy_connection");
/* If possible, find a listener which is configured for
* plain-HTTP, not SSL; using an SSL port would either be
* expensive to do correctly (performing a complete SSL handshake)
* or cause log spam by doing incorrectly (simply sending EOF). */
lp = ap_listeners;
- while (lp && lp->protocol && strcasecmp(lp->protocol, "http") != 0) {
+ while (lp && lp->protocol && ap_cstr_casecmp(lp->protocol, "http") != 0) {
lp = lp->next;
}
if (!lp) {
@@ -686,7 +691,7 @@ static apr_status_t dummy_connection(ap_pod_t *pod)
return rv;
}
- if (lp->protocol && strcasecmp(lp->protocol, "https") == 0) {
+ if (lp->protocol && ap_cstr_casecmp(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
@@ -706,7 +711,7 @@ static apr_status_t dummy_connection(ap_pod_t *pod)
}
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
+ * that administrators 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(). */
diff --git a/server/protocol.c b/server/protocol.c
index 8d90055..6f9540a 100644
--- a/server/protocol.c
+++ b/server/protocol.c
@@ -508,6 +508,8 @@ cleanup:
/* PR#43039: We shouldn't accept NULL bytes within the line */
bytes_handled = strlen(*s);
if (bytes_handled < *read) {
+ ap_log_data(APLOG_MARK, APLOG_DEBUG, ap_server_conf,
+ "NULL bytes in header", *s, *read, 0);
*read = bytes_handled;
if (rv == APR_SUCCESS) {
rv = APR_EINVAL;
@@ -601,7 +603,7 @@ AP_CORE_DECLARE(void) ap_parse_uri(request_rec *r, const char *uri)
if (status == APR_SUCCESS) {
/* if it has a scheme we may need to do absoluteURI vhost stuff */
if (r->parsed_uri.scheme
- && !strcasecmp(r->parsed_uri.scheme, ap_http_scheme(r))) {
+ && !ap_cstr_casecmp(r->parsed_uri.scheme, ap_http_scheme(r))) {
r->hostname = r->parsed_uri.hostname;
}
else if (r->method_number == M_CONNECT) {
@@ -609,8 +611,15 @@ AP_CORE_DECLARE(void) ap_parse_uri(request_rec *r, const char *uri)
}
r->args = r->parsed_uri.query;
- r->uri = r->parsed_uri.path ? r->parsed_uri.path
- : apr_pstrdup(r->pool, "/");
+ if (r->parsed_uri.path) {
+ r->uri = r->parsed_uri.path;
+ }
+ else if (r->method_number == M_OPTIONS) {
+ r->uri = apr_pstrdup(r->pool, "*");
+ }
+ else {
+ r->uri = apr_pstrdup(r->pool, "/");
+ }
#if defined(OS2) || defined(WIN32)
/* Handle path translations for OS/2 and plug security hole.
@@ -645,13 +654,6 @@ static int field_name_len(const char *field)
static int read_request_line(request_rec *r, apr_bucket_brigade *bb)
{
- enum {
- rrl_none, rrl_badmethod, rrl_badwhitespace, rrl_excesswhitespace,
- rrl_missinguri, rrl_baduri, rrl_badprotocol, rrl_trailingtext,
- rrl_badmethod09, rrl_reject09
- } deferred_error = rrl_none;
- char *ll;
- char *uri;
apr_size_t len;
int num_blank_lines = DEFAULT_LIMIT_BLANK_LINES;
core_server_config *conf = ap_get_core_module_config(r->server->module_config);
@@ -704,13 +706,29 @@ static int read_request_line(request_rec *r, apr_bucket_brigade *bb)
}
} while ((len <= 0) && (--num_blank_lines >= 0));
+ /* Set r->request_time before any logging, mod_unique_id needs it. */
+ r->request_time = apr_time_now();
+
if (APLOGrtrace5(r)) {
ap_log_rerror(APLOG_MARK, APLOG_TRACE5, 0, r,
"Request received from client: %s",
ap_escape_logitem(r->pool, r->the_request));
}
- r->request_time = apr_time_now();
+ return 1;
+}
+
+AP_DECLARE(int) ap_parse_request_line(request_rec *r)
+{
+ core_server_config *conf = ap_get_core_module_config(r->server->module_config);
+ int strict = (conf->http_conformance != AP_HTTP_CONFORMANCE_UNSAFE);
+ enum {
+ rrl_none, rrl_badmethod, rrl_badwhitespace, rrl_excesswhitespace,
+ rrl_missinguri, rrl_baduri, rrl_badprotocol, rrl_trailingtext,
+ rrl_badmethod09, rrl_reject09
+ } deferred_error = rrl_none;
+ apr_size_t len = 0;
+ char *uri, *ll;
r->method = r->the_request;
@@ -742,7 +760,6 @@ static int read_request_line(request_rec *r, apr_bucket_brigade *bb)
if (deferred_error == rrl_none)
deferred_error = rrl_missinguri;
r->protocol = uri = "";
- len = 0;
goto rrl_done;
}
else if (strict && ll[0] && apr_isspace(ll[1])
@@ -773,7 +790,6 @@ static int read_request_line(request_rec *r, apr_bucket_brigade *bb)
/* Verify URI terminated with a single SP, or mark as specific error */
if (!ll) {
r->protocol = "";
- len = 0;
goto rrl_done;
}
else if (strict && ll[0] && apr_isspace(ll[1])
@@ -866,6 +882,14 @@ rrl_done:
r->header_only = 1;
ap_parse_uri(r, uri);
+ if (r->status == HTTP_OK
+ && (r->parsed_uri.path != NULL)
+ && (r->parsed_uri.path[0] != '/')
+ && (r->method_number != M_OPTIONS
+ || strcmp(r->parsed_uri.path, "*") != 0)) {
+ /* Invalid request-target per RFC 7230 section 5.3 */
+ r->status = HTTP_BAD_REQUEST;
+ }
/* With the request understood, we can consider HTTP/0.9 specific errors */
if (r->proto_num == HTTP_VERSION(0, 9) && deferred_error == rrl_none) {
@@ -901,7 +925,7 @@ rrl_done:
else if (deferred_error == rrl_excesswhitespace)
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03448)
"HTTP Request Line; Excess whitespace "
- "(disallowed by HttpProtocolOptions Strict");
+ "(disallowed by HttpProtocolOptions Strict)");
else if (deferred_error == rrl_trailingtext)
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(03449)
"HTTP Request Line; Extraneous text found '%.*s' "
@@ -973,6 +997,79 @@ rrl_failed:
return 0;
}
+AP_DECLARE(int) ap_check_request_header(request_rec *r)
+{
+ core_server_config *conf;
+ int strict_host_check;
+ const char *expect;
+ int access_status;
+
+ conf = ap_get_core_module_config(r->server->module_config);
+
+ /* update what we think the virtual host is based on the headers we've
+ * now read. may update status.
+ */
+ strict_host_check = (conf->strict_host_check == AP_CORE_CONFIG_ON);
+ access_status = ap_update_vhost_from_headers_ex(r, strict_host_check);
+ if (strict_host_check && access_status != HTTP_OK) {
+ if (r->server == ap_server_conf) {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(10156)
+ "Requested hostname '%s' did not match any ServerName/ServerAlias "
+ "in the global server configuration ", r->hostname);
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(10157)
+ "Requested hostname '%s' did not match any ServerName/ServerAlias "
+ "in the matching virtual host (default vhost for "
+ "current connection is %s:%u)",
+ r->hostname, r->server->defn_name, r->server->defn_line_number);
+ }
+ r->status = access_status;
+ }
+ if (r->status != HTTP_OK) {
+ return 0;
+ }
+
+ if ((!r->hostname && (r->proto_num >= HTTP_VERSION(1, 1)))
+ || ((r->proto_num == HTTP_VERSION(1, 1))
+ && !apr_table_get(r->headers_in, "Host"))) {
+ /*
+ * Client sent us an HTTP/1.1 or later request without telling us the
+ * hostname, either with a full URL or a Host: header. We therefore
+ * need to (as per the 1.1 spec) send an error. As a special case,
+ * HTTP/1.1 mentions twice (S9, S14.23) that a request MUST contain
+ * a Host: header, and the server MUST respond with 400 if it doesn't.
+ */
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00569)
+ "client sent HTTP/1.1 request without hostname "
+ "(see RFC2616 section 14.23): %s", r->uri);
+ r->status = HTTP_BAD_REQUEST;
+ return 0;
+ }
+
+ if (((expect = apr_table_get(r->headers_in, "Expect")) != NULL)
+ && (expect[0] != '\0')) {
+ /*
+ * The Expect header field was added to HTTP/1.1 after RFC 2068
+ * as a means to signal when a 100 response is desired and,
+ * unfortunately, to signal a poor man's mandatory extension that
+ * the server must understand or return 417 Expectation Failed.
+ */
+ if (ap_cstr_casecmp(expect, "100-continue") == 0) {
+ r->expecting_100 = 1;
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00570)
+ "client sent an unrecognized expectation value "
+ "of Expect: %s", expect);
+ r->status = HTTP_EXPECTATION_FAILED;
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
static int table_do_fn_check_lengths(void *r_, const char *key,
const char *value)
{
@@ -1256,16 +1353,10 @@ AP_DECLARE(void) ap_get_mime_headers(request_rec *r)
apr_brigade_destroy(tmp_bb);
}
-request_rec *ap_read_request(conn_rec *conn)
+AP_DECLARE(request_rec *) ap_create_request(conn_rec *conn)
{
request_rec *r;
apr_pool_t *p;
- const char *expect;
- int access_status;
- apr_bucket_brigade *tmp_bb;
- apr_socket_t *csd;
- apr_interval_time_t cur_timeout;
-
apr_pool_create(&p, conn->pool);
apr_pool_tag(p, "request");
@@ -1304,6 +1395,7 @@ request_rec *ap_read_request(conn_rec *conn)
r->read_body = REQUEST_NO_BODY;
r->status = HTTP_OK; /* Until further notice */
+ r->header_only = 0;
r->the_request = NULL;
/* Begin by presuming any module can make its own path_info assumptions,
@@ -1314,12 +1406,35 @@ request_rec *ap_read_request(conn_rec *conn)
r->useragent_addr = conn->client_addr;
r->useragent_ip = conn->client_ip;
+ return r;
+}
+
+/* Apply the server's timeout/config to the connection/request. */
+static void apply_server_config(request_rec *r)
+{
+ apr_socket_t *csd;
+
+ csd = ap_get_conn_socket(r->connection);
+ apr_socket_timeout_set(csd, r->server->timeout);
+
+ r->per_dir_config = r->server->lookup_defaults;
+}
+
+request_rec *ap_read_request(conn_rec *conn)
+{
+ int access_status;
+ apr_bucket_brigade *tmp_bb;
+
+ request_rec *r = ap_create_request(conn);
+
tmp_bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+ conn->keepalive = AP_CONN_UNKNOWN;
ap_run_pre_read_request(r, conn);
/* Get the request... */
- if (!read_request_line(r, tmp_bb)) {
+ if (!read_request_line(r, tmp_bb) || !ap_parse_request_line(r)) {
+ apr_brigade_cleanup(tmp_bb);
switch (r->status) {
case HTTP_REQUEST_URI_TOO_LARGE:
case HTTP_BAD_REQUEST:
@@ -1335,116 +1450,84 @@ request_rec *ap_read_request(conn_rec *conn)
"request failed: malformed request line");
}
access_status = r->status;
- r->status = HTTP_OK;
- ap_die(access_status, r);
- ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
- ap_run_log_transaction(r);
- r = NULL;
- apr_brigade_destroy(tmp_bb);
- goto traceout;
+ goto die_unusable_input;
+
case HTTP_REQUEST_TIME_OUT:
+ /* Just log, no further action on this connection. */
ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, NULL);
if (!r->connection->keepalives)
ap_run_log_transaction(r);
- apr_brigade_destroy(tmp_bb);
- goto traceout;
- default:
- apr_brigade_destroy(tmp_bb);
- r = NULL;
- goto traceout;
+ break;
}
+ /* Not worth dying with. */
+ conn->keepalive = AP_CONN_CLOSE;
+ apr_pool_destroy(r->pool);
+ goto ignore;
}
+ apr_brigade_cleanup(tmp_bb);
/* We may have been in keep_alive_timeout mode, so toggle back
* to the normal timeout mode as we fetch the header lines,
* as necessary.
*/
- csd = ap_get_conn_socket(conn);
- apr_socket_timeout_get(csd, &cur_timeout);
- if (cur_timeout != conn->base_server->timeout) {
- apr_socket_timeout_set(csd, conn->base_server->timeout);
- cur_timeout = conn->base_server->timeout;
- }
+ apply_server_config(r);
if (!r->assbackwards) {
- const char *tenc;
+ const char *tenc, *clen;
ap_get_mime_headers_core(r, tmp_bb);
+ apr_brigade_cleanup(tmp_bb);
if (r->status != HTTP_OK) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00567)
"request failed: error reading the headers");
- ap_send_error_response(r, 0);
- ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
- ap_run_log_transaction(r);
- apr_brigade_destroy(tmp_bb);
- goto traceout;
+ access_status = r->status;
+ goto die_unusable_input;
+ }
+
+ clen = apr_table_get(r->headers_in, "Content-Length");
+ if (clen) {
+ apr_off_t cl;
+
+ if (!ap_parse_strict_length(&cl, clen)) {
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(10242)
+ "client sent invalid Content-Length "
+ "(%s): %s", clen, r->uri);
+ access_status = HTTP_BAD_REQUEST;
+ goto die_unusable_input;
+ }
}
tenc = apr_table_get(r->headers_in, "Transfer-Encoding");
if (tenc) {
- /* http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-23
+ /* https://tools.ietf.org/html/rfc7230
* Section 3.3.3.3: "If a Transfer-Encoding header field is
* present in a request and the chunked transfer coding is not
* the final encoding ...; the server MUST respond with the 400
* (Bad Request) status code and then close the connection".
*/
- if (!(strcasecmp(tenc, "chunked") == 0 /* fast path */
- || ap_find_last_token(r->pool, tenc, "chunked"))) {
+ if (!ap_is_chunked(r->pool, tenc)) {
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(02539)
"client sent unknown Transfer-Encoding "
"(%s): %s", tenc, r->uri);
- r->status = HTTP_BAD_REQUEST;
- conn->keepalive = AP_CONN_CLOSE;
- ap_send_error_response(r, 0);
- ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
- ap_run_log_transaction(r);
- apr_brigade_destroy(tmp_bb);
- goto traceout;
+ access_status = HTTP_BAD_REQUEST;
+ goto die_unusable_input;
}
- /* http://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-23
+ /* https://tools.ietf.org/html/rfc7230
* Section 3.3.3.3: "If a message is received with both a
* Transfer-Encoding and a Content-Length header field, the
* Transfer-Encoding overrides the Content-Length. ... A sender
* MUST remove the received Content-Length field".
*/
- apr_table_unset(r->headers_in, "Content-Length");
- }
- }
-
- apr_brigade_destroy(tmp_bb);
-
- /* update what we think the virtual host is based on the headers we've
- * now read. may update status.
- */
- ap_update_vhost_from_headers(r);
- access_status = r->status;
-
- /* Toggle to the Host:-based vhost's timeout mode to fetch the
- * request body and send the response body, if needed.
- */
- if (cur_timeout != r->server->timeout) {
- apr_socket_timeout_set(csd, r->server->timeout);
- cur_timeout = r->server->timeout;
- }
+ if (clen) {
+ apr_table_unset(r->headers_in, "Content-Length");
- /* we may have switched to another server */
- r->per_dir_config = r->server->lookup_defaults;
-
- if ((!r->hostname && (r->proto_num >= HTTP_VERSION(1, 1)))
- || ((r->proto_num == HTTP_VERSION(1, 1))
- && !apr_table_get(r->headers_in, "Host"))) {
- /*
- * Client sent us an HTTP/1.1 or later request without telling us the
- * hostname, either with a full URL or a Host: header. We therefore
- * need to (as per the 1.1 spec) send an error. As a special case,
- * HTTP/1.1 mentions twice (S9, S14.23) that a request MUST contain
- * a Host: header, and the server MUST respond with 400 if it doesn't.
- */
- access_status = HTTP_BAD_REQUEST;
- ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(00569)
- "client sent HTTP/1.1 request without hostname "
- "(see RFC2616 section 14.23): %s", r->uri);
+ /* Don't reuse this connection anyway to avoid confusion with
+ * intermediaries and request/reponse spltting.
+ */
+ conn->keepalive = AP_CONN_CLOSE;
+ }
+ }
}
/*
@@ -1453,47 +1536,94 @@ request_rec *ap_read_request(conn_rec *conn)
* status codes that do not cause the connection to be dropped and
* in situations where the connection should be kept alive.
*/
-
ap_add_input_filter_handle(ap_http_input_filter_handle,
NULL, r, r->connection);
- if (access_status != HTTP_OK
- || (access_status = ap_run_post_read_request(r))) {
- ap_die(access_status, r);
- ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
- ap_run_log_transaction(r);
- r = NULL;
- goto traceout;
+ /* Validate Host/Expect headers and select vhost. */
+ if (!ap_check_request_header(r)) {
+ /* we may have switched to another server still */
+ apply_server_config(r);
+ access_status = r->status;
+ goto die_before_hooks;
}
- if (((expect = apr_table_get(r->headers_in, "Expect")) != NULL)
- && (expect[0] != '\0')) {
- /*
- * The Expect header field was added to HTTP/1.1 after RFC 2068
- * as a means to signal when a 100 response is desired and,
- * unfortunately, to signal a poor man's mandatory extension that
- * the server must understand or return 417 Expectation Failed.
- */
- if (strcasecmp(expect, "100-continue") == 0) {
- r->expecting_100 = 1;
- }
- else {
- r->status = HTTP_EXPECTATION_FAILED;
- ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00570)
- "client sent an unrecognized expectation value of "
- "Expect: %s", expect);
- ap_send_error_response(r, 0);
- ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r);
- ap_run_log_transaction(r);
- goto traceout;
- }
+ /* we may have switched to another server */
+ apply_server_config(r);
+
+ if ((access_status = ap_post_read_request(r))) {
+ goto die;
}
- AP_READ_REQUEST_SUCCESS((uintptr_t)r, (char *)r->method, (char *)r->uri, (char *)r->server->defn_name, r->status);
+ AP_READ_REQUEST_SUCCESS((uintptr_t)r, (char *)r->method,
+ (char *)r->uri, (char *)r->server->defn_name,
+ r->status);
return r;
- traceout:
+
+ /* Everything falls through on failure */
+
+die_unusable_input:
+ /* Input filters are in an undeterminate state, cleanup (including
+ * CORE_IN's socket) such that any further attempt to read is EOF.
+ */
+ {
+ ap_filter_t *f = conn->input_filters;
+ while (f) {
+ if (f->frec == ap_core_input_filter_handle) {
+ core_net_rec *net = f->ctx;
+ apr_brigade_cleanup(net->in_ctx->b);
+ break;
+ }
+ ap_remove_input_filter(f);
+ f = f->next;
+ }
+ conn->input_filters = r->input_filters = f;
+ conn->keepalive = AP_CONN_CLOSE;
+ }
+
+die_before_hooks:
+ /* First call to ap_die() (non recursive) */
+ r->status = HTTP_OK;
+
+die:
+ ap_die(access_status, r);
+
+ /* ap_die() sent the response through the output filters, we must now
+ * end the request with an EOR bucket for stream/pipeline accounting.
+ */
+ {
+ apr_bucket_brigade *eor_bb;
+ eor_bb = apr_brigade_create(conn->pool, conn->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(eor_bb,
+ ap_bucket_eor_create(conn->bucket_alloc, r));
+ ap_pass_brigade(conn->output_filters, eor_bb);
+ apr_brigade_cleanup(eor_bb);
+ }
+
+ignore:
+ r = NULL;
AP_READ_REQUEST_FAILURE((uintptr_t)r);
- return r;
+ return NULL;
+}
+
+AP_DECLARE(int) ap_post_read_request(request_rec *r)
+{
+ int status;
+
+ if ((status = ap_run_post_read_request(r))) {
+ return status;
+ }
+
+ /* Enforce http(s) only scheme for non-forward-proxy requests */
+ if (!r->proxyreq
+ && r->parsed_uri.scheme
+ && (ap_cstr_casecmpn(r->parsed_uri.scheme, "http", 4) != 0
+ || (r->parsed_uri.scheme[4] != '\0'
+ && (apr_tolower(r->parsed_uri.scheme[4]) != 's'
+ || r->parsed_uri.scheme[5] != '\0')))) {
+ return HTTP_BAD_REQUEST;
+ }
+
+ return OK;
}
/* if a request with a body creates a subrequest, remove original request's
@@ -1559,23 +1689,29 @@ AP_DECLARE(void) ap_set_sub_req_protocol(request_rec *rnew,
rnew->main = (request_rec *) r;
}
-static void end_output_stream(request_rec *r)
+static void end_output_stream(request_rec *r, int status)
{
conn_rec *c = r->connection;
apr_bucket_brigade *bb;
apr_bucket *b;
bb = apr_brigade_create(r->pool, c->bucket_alloc);
+ if (status != OK) {
+ b = ap_bucket_error_create(status, NULL, r->pool, c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(bb, b);
+ }
b = apr_bucket_eos_create(c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(bb, b);
+
ap_pass_brigade(r->output_filters, bb);
+ apr_brigade_cleanup(bb);
}
AP_DECLARE(void) ap_finalize_sub_req_protocol(request_rec *sub)
{
/* tell the filter chain there is no more content coming */
if (!sub->eos_sent) {
- end_output_stream(sub);
+ end_output_stream(sub, OK);
}
}
@@ -1586,11 +1722,11 @@ AP_DECLARE(void) ap_finalize_sub_req_protocol(request_rec *sub)
*/
AP_DECLARE(void) ap_finalize_request_protocol(request_rec *r)
{
- (void) ap_discard_request_body(r);
+ int status = ap_discard_request_body(r);
/* tell the filter chain there is no more content coming */
if (!r->eos_sent) {
- end_output_stream(r);
+ end_output_stream(r, status);
}
}
@@ -1627,7 +1763,7 @@ AP_DECLARE(int) ap_get_basic_auth_pw(request_rec *r, const char **pw)
: "Authorization");
const char *t;
- if (!(t = ap_auth_type(r)) || strcasecmp(t, "Basic"))
+ if (!(t = ap_auth_type(r)) || ap_cstr_casecmp(t, "Basic"))
return DECLINED;
if (!ap_auth_name(r)) {
@@ -1641,7 +1777,7 @@ AP_DECLARE(int) ap_get_basic_auth_pw(request_rec *r, const char **pw)
return HTTP_UNAUTHORIZED;
}
- if (strcasecmp(ap_getword(r->pool, &auth_line, ' '), "Basic")) {
+ if (ap_cstr_casecmp(ap_getword(r->pool, &auth_line, ' '), "Basic")) {
/* Client tried to authenticate using wrong auth scheme */
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00573)
"client used wrong authentication scheme: %s", r->uri);
@@ -1994,6 +2130,9 @@ AP_DECLARE(int) ap_rputc(int c, request_rec *r)
AP_DECLARE(int) ap_rwrite(const void *buf, int nbyte, request_rec *r)
{
+ if (nbyte < 0)
+ return -1;
+
if (r->connection->aborted)
return -1;
@@ -2036,7 +2175,7 @@ static int r_flush(apr_vformatter_buff_t *buff)
AP_DECLARE(int) ap_vrprintf(request_rec *r, const char *fmt, va_list va)
{
- apr_size_t written;
+ int written;
struct ap_vrprintf_data vd;
char vrprintf_buf[AP_IOBUFSIZE];
@@ -2054,7 +2193,7 @@ AP_DECLARE(int) ap_vrprintf(request_rec *r, const char *fmt, va_list va)
int n = vd.vbuff.curpos - vrprintf_buf;
/* last call to buffer_output, to finish clearing the buffer */
- if (buffer_output(r, vrprintf_buf,n) != APR_SUCCESS)
+ if (buffer_output(r, vrprintf_buf, n) != APR_SUCCESS)
return -1;
written += n;
@@ -2100,6 +2239,7 @@ AP_DECLARE_NONSTD(int) ap_rvputs(request_rec *r, ...)
len = strlen(s);
if (buffer_output(r, s, len) != APR_SUCCESS) {
+ va_end(va);
return -1;
}
@@ -2176,7 +2316,8 @@ static int send_header(void *data, const char *key, const char *val)
AP_DECLARE(void) ap_send_interim_response(request_rec *r, int send_headers)
{
hdr_ptr x;
- char *status_line = NULL;
+ char *response_line = NULL;
+ const char *status_line;
request_rec *rr;
if (r->proto_num < HTTP_VERSION(1,1)) {
@@ -2188,30 +2329,38 @@ AP_DECLARE(void) ap_send_interim_response(request_rec *r, int send_headers)
"Status is %d - not sending interim response", r->status);
return;
}
- if ((r->status == HTTP_CONTINUE) && !r->expecting_100) {
- /*
- * Don't send 100-Continue when there was no Expect: 100-continue
- * in the request headers. For origin servers this is a SHOULD NOT
- * for proxies it is a MUST NOT according to RFC 2616 8.2.3
+ if (r->status == HTTP_CONTINUE) {
+ if (!r->expecting_100) {
+ /*
+ * Don't send 100-Continue when there was no Expect: 100-continue
+ * in the request headers. For origin servers this is a SHOULD NOT
+ * for proxies it is a MUST NOT according to RFC 2616 8.2.3
+ */
+ return;
+ }
+
+ /* if we send an interim response, we're no longer in a state of
+ * expecting one. Also, this could feasibly be in a subrequest,
+ * so we need to propagate the fact that we responded.
*/
- return;
+ for (rr = r; rr != NULL; rr = rr->main) {
+ rr->expecting_100 = 0;
+ }
}
- /* if we send an interim response, we're no longer in a state of
- * expecting one. Also, this could feasibly be in a subrequest,
- * so we need to propagate the fact that we responded.
- */
- for (rr = r; rr != NULL; rr = rr->main) {
- rr->expecting_100 = 0;
+ status_line = r->status_line;
+ if (status_line == NULL) {
+ status_line = ap_get_status_line_ex(r->pool, r->status);
}
-
- status_line = apr_pstrcat(r->pool, AP_SERVER_PROTOCOL, " ", r->status_line, CRLF, NULL);
- ap_xlate_proto_to_ascii(status_line, strlen(status_line));
+ response_line = apr_pstrcat(r->pool,
+ AP_SERVER_PROTOCOL " ", status_line, CRLF,
+ NULL);
+ ap_xlate_proto_to_ascii(response_line, strlen(response_line));
x.f = r->connection->output_filters;
x.bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
- ap_fputs(x.f, x.bb, status_line);
+ ap_fputs(x.f, x.bb, response_line);
if (send_headers) {
apr_table_do(send_header, &x, r->headers_out, NULL);
apr_table_clear(r->headers_out);
@@ -2239,7 +2388,7 @@ static int protocol_cmp(const apr_array_header_t *preferences,
return (index2 >= 0) ? -1 : 1;
}
}
- /* both have the same index (mabye -1 or no pref configured) and we compare
+ /* both have the same index (maybe -1 or no pref configured) and we compare
* the names so that spdy3 gets precedence over spdy2. That makes
* the outcome at least deterministic. */
return strcmp(proto1, proto2);
diff --git a/server/provider.c b/server/provider.c
index cf307e7..f54fb5e 100644
--- a/server/provider.c
+++ b/server/provider.c
@@ -55,7 +55,6 @@ AP_DECLARE(apr_status_t) ap_register_provider(apr_pool_t *pool,
provider_group_hash = apr_hash_make(pool);
apr_hash_set(global_providers, provider_group, APR_HASH_KEY_STRING,
provider_group_hash);
-
}
provider_version_hash = apr_hash_get(provider_group_hash, provider_name,
@@ -65,7 +64,6 @@ AP_DECLARE(apr_status_t) ap_register_provider(apr_pool_t *pool,
provider_version_hash = apr_hash_make(pool);
apr_hash_set(provider_group_hash, provider_name, APR_HASH_KEY_STRING,
provider_version_hash);
-
}
/* just set it. no biggy if it was there before. */
@@ -80,7 +78,6 @@ AP_DECLARE(apr_status_t) ap_register_provider(apr_pool_t *pool,
provider_group_hash = apr_hash_make(pool);
apr_hash_set(global_providers_names, provider_group, APR_HASH_KEY_STRING,
provider_group_hash);
-
}
provider_version_hash = apr_hash_get(provider_group_hash, provider_version,
@@ -90,7 +87,6 @@ AP_DECLARE(apr_status_t) ap_register_provider(apr_pool_t *pool,
provider_version_hash = apr_hash_make(pool);
apr_hash_set(provider_group_hash, provider_version, APR_HASH_KEY_STRING,
provider_version_hash);
-
}
/* just set it. no biggy if it was there before. */
@@ -132,35 +128,40 @@ AP_DECLARE(apr_array_header_t *) ap_list_provider_names(apr_pool_t *pool,
const char *provider_group,
const char *provider_version)
{
- apr_array_header_t *ret = apr_array_make(pool, 10, sizeof(ap_list_provider_names_t));
+ apr_array_header_t *ret = NULL;
ap_list_provider_names_t *entry;
apr_hash_t *provider_group_hash, *h;
apr_hash_index_t *hi;
char *val;
if (global_providers_names == NULL) {
- return ret;
+ goto out;
}
provider_group_hash = apr_hash_get(global_providers_names, provider_group,
APR_HASH_KEY_STRING);
if (provider_group_hash == NULL) {
- return ret;
+ goto out;
}
- h = apr_hash_get(provider_group_hash, provider_version,
- APR_HASH_KEY_STRING);
+ h = apr_hash_get(provider_group_hash, provider_version, APR_HASH_KEY_STRING);
if (h == NULL) {
- return ret;
+ goto out;
}
+ ret = apr_array_make(pool, apr_hash_count(h), sizeof(ap_list_provider_names_t));
for (hi = apr_hash_first(pool, h); hi; hi = apr_hash_next(hi)) {
apr_hash_this(hi, NULL, NULL, (void *)&val);
entry = apr_array_push(ret);
entry->provider_name = apr_pstrdup(pool, val);
}
+
+out:
+ if (ret == NULL) {
+ ret = apr_array_make(pool, 1, sizeof(ap_list_provider_names_t));
+ }
return ret;
}
diff --git a/server/request.c b/server/request.c
index dbe3e07..5599b2c 100644
--- a/server/request.c
+++ b/server/request.c
@@ -59,6 +59,7 @@
#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
APR_HOOK_STRUCT(
+ APR_HOOK_LINK(pre_translate_name)
APR_HOOK_LINK(translate_name)
APR_HOOK_LINK(map_to_storage)
APR_HOOK_LINK(check_user_id)
@@ -74,6 +75,8 @@ APR_HOOK_STRUCT(
APR_HOOK_LINK(force_authn)
)
+AP_IMPLEMENT_HOOK_RUN_FIRST(int,pre_translate_name,
+ (request_rec *r), (r), DECLINED)
AP_IMPLEMENT_HOOK_RUN_FIRST(int,translate_name,
(request_rec *r), (r), DECLINED)
AP_IMPLEMENT_HOOK_RUN_FIRST(int,map_to_storage,
@@ -157,6 +160,25 @@ AP_DECLARE(int) ap_some_authn_required(request_rec *r)
return rv;
}
+static int walk_location_and_if(request_rec *r)
+{
+ int access_status;
+ core_dir_config *d;
+
+ if ((access_status = ap_location_walk(r))) {
+ return access_status;
+ }
+ if ((access_status = ap_if_walk(r))) {
+ return access_status;
+ }
+
+ d = ap_get_core_module_config(r->per_dir_config);
+ if (d->log)
+ r->log = d->log;
+
+ return OK;
+}
+
/* This is the master logic for processing requests. Do NOT duplicate
* this logic elsewhere, or the security model will be broken by future
* API changes. Each phase must be individually optimized to pick up
@@ -164,51 +186,95 @@ AP_DECLARE(int) ap_some_authn_required(request_rec *r)
*/
AP_DECLARE(int) ap_process_request_internal(request_rec *r)
{
+ int access_status = DECLINED;
int file_req = (r->main && r->filename);
- int access_status;
- core_dir_config *d;
+ core_server_config *sconf =
+ ap_get_core_module_config(r->server->module_config);
+ unsigned int normalize_flags;
+
+ normalize_flags = AP_NORMALIZE_NOT_ABOVE_ROOT;
+ if (sconf->merge_slashes != AP_CORE_CONFIG_OFF) {
+ normalize_flags |= AP_NORMALIZE_MERGE_SLASHES;
+ }
+ if (file_req) {
+ /* File subrequests can have a relative path. */
+ normalize_flags |= AP_NORMALIZE_ALLOW_RELATIVE;
+ }
- /* Ignore embedded %2F's in path for proxy requests */
- if (!r->proxyreq && r->parsed_uri.path) {
- d = ap_get_core_module_config(r->per_dir_config);
- if (d->allow_encoded_slashes) {
- access_status = ap_unescape_url_keep2f(r->parsed_uri.path, d->decode_encoded_slashes);
+ if (r->parsed_uri.path) {
+ /* Normalize: remove /./ and shrink /../ segments, plus
+ * decode unreserved chars (first time only to avoid
+ * double decoding after ap_unescape_url() below).
+ */
+ if (!ap_normalize_path(r->parsed_uri.path,
+ normalize_flags |
+ AP_NORMALIZE_DECODE_UNRESERVED)) {
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10244)
+ "invalid URI path (%s)", r->unparsed_uri);
+ return HTTP_BAD_REQUEST;
}
- else {
- access_status = ap_unescape_url(r->parsed_uri.path);
+ }
+
+ /* All file subrequests are a huge pain... they cannot bubble through the
+ * next several steps. Only file subrequests are allowed an empty uri,
+ * otherwise let (pre_)translate_name kill the request.
+ */
+ if (!file_req) {
+ ap_conf_vector_t *per_dir_config = r->per_dir_config;
+
+ if ((access_status = walk_location_and_if(r))) {
+ return access_status;
+ }
+
+ /* Let pre_translate_name hooks work with non-decoded URIs, and
+ * eventually prevent further URI transformations (return DONE).
+ */
+ access_status = ap_run_pre_translate_name(r);
+ if (ap_is_HTTP_ERROR(access_status)) {
+ return access_status;
+ }
+
+ /* Throw away pre_trans only merging */
+ r->per_dir_config = per_dir_config;
+ }
+
+ /* Ignore URL unescaping for translated URIs already */
+ if (access_status != DONE && r->parsed_uri.path) {
+ core_dir_config *d = ap_get_core_module_config(r->per_dir_config);
+ /* Unreserved chars were already decoded by ap_normalize_path() */
+ unsigned int unescape_flags = AP_UNESCAPE_URL_KEEP_UNRESERVED;
+ if (!d->allow_encoded_slashes) {
+ unescape_flags |= AP_UNESCAPE_URL_FORBID_SLASHES;
+ }
+ else if (!d->decode_encoded_slashes) {
+ unescape_flags |= AP_UNESCAPE_URL_KEEP_SLASHES;
}
+ access_status = ap_unescape_url_ex(r->parsed_uri.path, unescape_flags);
if (access_status) {
if (access_status == HTTP_NOT_FOUND) {
if (! d->allow_encoded_slashes) {
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, APLOGNO(00026)
- "found %%2f (encoded '/') in URI "
- "(decoded='%s'), returning 404",
- r->parsed_uri.path);
+ "found %%2f (encoded '/') in URI path (%s), "
+ "returning 404", r->unparsed_uri);
}
}
return access_status;
}
- }
- ap_getparents(r->uri); /* OK --- shrinking transformations... */
+ if (d->allow_encoded_slashes && d->decode_encoded_slashes) {
+ /* Decoding slashes might have created new // or /./ or /../
+ * segments (e.g. "/.%2F/"), so re-normalize.
+ */
+ ap_normalize_path(r->parsed_uri.path, normalize_flags);
+ }
+ }
- /* All file subrequests are a huge pain... they cannot bubble through the
- * next several steps. Only file subrequests are allowed an empty uri,
- * otherwise let translate_name kill the request.
- */
+ /* Same, translate_name is not suited for file subrequests */
if (!file_req) {
- if ((access_status = ap_location_walk(r))) {
- return access_status;
- }
- if ((access_status = ap_if_walk(r))) {
+ if ((access_status = walk_location_and_if(r))) {
return access_status;
}
- d = ap_get_core_module_config(r->per_dir_config);
- if (d->log) {
- r->log = d->log;
- }
-
if ((access_status = ap_run_translate_name(r))) {
return decl_die(access_status, "translate", r);
}
@@ -225,17 +291,9 @@ AP_DECLARE(int) ap_process_request_internal(request_rec *r)
/* Rerun the location walk, which overrides any map_to_storage config.
*/
- if ((access_status = ap_location_walk(r))) {
+ if ((access_status = walk_location_and_if(r))) {
return access_status;
}
- if ((access_status = ap_if_walk(r))) {
- return access_status;
- }
-
- d = ap_get_core_module_config(r->per_dir_config);
- if (d->log) {
- r->log = d->log;
- }
if ((access_status = ap_run_post_perdir_config(r))) {
return access_status;
@@ -1256,6 +1314,7 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
if (entry_core->refs && entry_core->refs->nelts) {
if (!rxpool) {
apr_pool_create(&rxpool, r->pool);
+ apr_pool_tag(rxpool, "directory_walk_rxpool");
}
nmatch = entry_core->refs->nelts;
pmatch = apr_palloc(rxpool, nmatch*sizeof(ap_regmatch_t));
@@ -1361,7 +1420,7 @@ AP_DECLARE(int) ap_directory_walk(request_rec *r)
r->canonical_filename = r->filename;
if (r->finfo.filetype == APR_DIR) {
- cache->cached = r->filename;
+ cache->cached = apr_pstrdup(r->pool, r->filename);
}
else {
cache->cached = ap_make_dirstr_parent(r->pool, r->filename);
@@ -1412,12 +1471,12 @@ AP_DECLARE(int) ap_location_walk(request_rec *r)
cache = prep_walk_cache(AP_NOTE_LOCATION_WALK, r);
cached = (cache->cached != NULL);
- /* Location and LocationMatch differ on their behaviour w.r.t. multiple
- * slashes. Location matches multiple slashes with a single slash,
- * LocationMatch doesn't. An exception, for backwards brokenness is
- * absoluteURIs... in which case neither match multiple slashes.
- */
- if (r->uri[0] != '/') {
+ /*
+ * When merge_slashes is set to AP_CORE_CONFIG_OFF the slashes in r->uri
+ * have not been merged. But for Location walks we always go with merged
+ * slashes no matter what merge_slashes is set to.
+ */
+ if (sconf->merge_slashes != AP_CORE_CONFIG_OFF) {
entry_uri = r->uri;
}
else {
@@ -1458,7 +1517,7 @@ AP_DECLARE(int) ap_location_walk(request_rec *r)
apr_pool_t *rxpool = NULL;
cached &= auth_internal_per_conf;
- cache->cached = entry_uri;
+ cache->cached = apr_pstrdup(r->pool, entry_uri);
/* Go through the location entries, and check for matches.
* We apply the directive sections in given order, we should
@@ -1486,6 +1545,7 @@ AP_DECLARE(int) ap_location_walk(request_rec *r)
if (entry_core->refs && entry_core->refs->nelts) {
if (!rxpool) {
apr_pool_create(&rxpool, r->pool);
+ apr_pool_tag(rxpool, "location_walk_rxpool");
}
nmatch = entry_core->refs->nelts;
pmatch = apr_palloc(rxpool, nmatch*sizeof(ap_regmatch_t));
@@ -1688,6 +1748,7 @@ AP_DECLARE(int) ap_file_walk(request_rec *r)
if (entry_core->refs && entry_core->refs->nelts) {
if (!rxpool) {
apr_pool_create(&rxpool, r->pool);
+ apr_pool_tag(rxpool, "file_walk_rxpool");
}
nmatch = entry_core->refs->nelts;
pmatch = apr_palloc(rxpool, nmatch*sizeof(ap_regmatch_t));
diff --git a/server/scoreboard.c b/server/scoreboard.c
index 23e3d70..49d1600 100644
--- a/server/scoreboard.c
+++ b/server/scoreboard.c
@@ -364,6 +364,18 @@ AP_DECLARE(int) ap_exists_scoreboard_image(void)
return (ap_scoreboard_image ? 1 : 0);
}
+AP_DECLARE(void) ap_set_conn_count(ap_sb_handle_t *sb, request_rec *r,
+ unsigned short conn_count)
+{
+ worker_score *ws;
+
+ if (!sb)
+ return;
+
+ ws = &ap_scoreboard_image->servers[sb->child_num][sb->thread_num];
+ ws->conn_count = conn_count;
+}
+
AP_DECLARE(void) ap_increment_counts(ap_sb_handle_t *sb, request_rec *r)
{
worker_score *ws;
@@ -376,7 +388,7 @@ AP_DECLARE(void) ap_increment_counts(ap_sb_handle_t *sb, request_rec *r)
if (pfn_ap_logio_get_last_bytes != NULL) {
bytes = pfn_ap_logio_get_last_bytes(r->connection);
}
- else if (r->method_number == M_GET && r->method[0] == 'H') {
+ else if (r->method_number == M_GET && r->method && r->method[0] == 'H') {
bytes = 0;
}
else {
@@ -641,7 +653,7 @@ AP_DECLARE(void) ap_time_process_request(ap_sb_handle_t *sbh, int status)
}
}
-AP_DECLARE(int) ap_update_global_status()
+AP_DECLARE(int) ap_update_global_status(void)
{
#ifdef HAVE_TIMES
if (ap_scoreboard_image == NULL) {
@@ -695,7 +707,7 @@ AP_DECLARE(process_score *) ap_get_scoreboard_process(int x)
return &ap_scoreboard_image->parent[x];
}
-AP_DECLARE(global_score *) ap_get_scoreboard_global()
+AP_DECLARE(global_score *) ap_get_scoreboard_global(void)
{
return ap_scoreboard_image->global;
}
diff --git a/server/ssl.c b/server/ssl.c
new file mode 100644
index 0000000..edf958b
--- /dev/null
+++ b/server/ssl.c
@@ -0,0 +1,285 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * ssl.c --- routines for SSL/TLS server infrastructure.
+ *
+ */
+
+#include "apr.h"
+#include "apr_strings.h"
+#include "apr_buckets.h"
+#include "apr_lib.h"
+#include "apr_signal.h"
+#include "apr_strmatch.h"
+
+#define APR_WANT_STDIO /* for sscanf */
+#define APR_WANT_STRFUNC
+#define APR_WANT_MEMFUNC
+#include "apr_want.h"
+
+#include "util_filter.h"
+#include "ap_config.h"
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_connection.h"
+#include "http_protocol.h"
+#include "http_request.h"
+#include "http_main.h"
+#include "http_ssl.h"
+#include "http_log.h" /* For errors detected in basic auth common
+ * support code... */
+#include "mod_core.h"
+
+
+#if APR_HAVE_STDARG_H
+#include <stdarg.h>
+#endif
+#if APR_HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+/* we know core's module_index is 0 */
+#undef APLOG_MODULE_INDEX
+#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
+
+APR_HOOK_STRUCT(
+ APR_HOOK_LINK(ssl_conn_is_ssl)
+ APR_HOOK_LINK(ssl_var_lookup)
+ APR_HOOK_LINK(ssl_add_cert_files)
+ APR_HOOK_LINK(ssl_add_fallback_cert_files)
+ APR_HOOK_LINK(ssl_answer_challenge)
+ APR_HOOK_LINK(ssl_ocsp_prime_hook)
+ APR_HOOK_LINK(ssl_ocsp_get_resp_hook)
+ APR_HOOK_LINK(ssl_bind_outgoing)
+)
+
+APR_DECLARE_OPTIONAL_FN(int, ssl_is_https, (conn_rec *));
+static APR_OPTIONAL_FN_TYPE(ssl_is_https) *module_ssl_is_https;
+APR_DECLARE_OPTIONAL_FN(int, ssl_proxy_enable, (conn_rec *));
+static APR_OPTIONAL_FN_TYPE(ssl_proxy_enable) *module_ssl_proxy_enable;
+APR_DECLARE_OPTIONAL_FN(int, ssl_engine_disable, (conn_rec *));
+static APR_OPTIONAL_FN_TYPE(ssl_engine_disable) *module_ssl_engine_disable;
+APR_DECLARE_OPTIONAL_FN(int, ssl_engine_set, (conn_rec *,
+ ap_conf_vector_t *,
+ int proxy, int enable));
+static APR_OPTIONAL_FN_TYPE(ssl_engine_set) *module_ssl_engine_set;
+
+
+static int ssl_is_https(conn_rec *c)
+{
+ /* Someone retrieved the optional function., not knowing about the
+ * new API. We redirect them to what they should have invoked. */
+ return ap_ssl_conn_is_ssl(c);
+}
+
+AP_DECLARE(int) ap_ssl_conn_is_ssl(conn_rec *c)
+{
+ int r = (ap_run_ssl_conn_is_ssl(c) == OK);
+ if (r == 0 && module_ssl_is_https) {
+ r = module_ssl_is_https(c);
+ }
+ return r;
+}
+
+static int ssl_engine_set(conn_rec *c,
+ ap_conf_vector_t *per_dir_config,
+ int proxy, int enable)
+{
+ if (proxy) {
+ return ap_ssl_bind_outgoing(c, per_dir_config, enable) == OK;
+ }
+ else if (module_ssl_engine_set) {
+ return module_ssl_engine_set(c, per_dir_config, 0, enable);
+ }
+ else if (enable && module_ssl_proxy_enable) {
+ return module_ssl_proxy_enable(c);
+ }
+ else if (!enable && module_ssl_engine_disable) {
+ return module_ssl_engine_disable(c);
+ }
+ return 0;
+}
+
+static int ssl_proxy_enable(conn_rec *c)
+{
+ return ap_ssl_bind_outgoing(c, NULL, 1);
+}
+
+static int ssl_engine_disable(conn_rec *c)
+{
+ return ap_ssl_bind_outgoing(c, NULL, 0);
+}
+
+AP_DECLARE(int) ap_ssl_bind_outgoing(conn_rec *c, struct ap_conf_vector_t *dir_conf,
+ int enable_ssl)
+{
+ int rv, enabled = 0;
+
+ c->outgoing = 1;
+ rv = ap_run_ssl_bind_outgoing(c, dir_conf, enable_ssl);
+ enabled = (rv == OK);
+ if (enable_ssl && !enabled) {
+ /* the hooks did not take over. Is there an old skool optional that will? */
+ if (module_ssl_engine_set) {
+ enabled = module_ssl_engine_set(c, dir_conf, 1, 1);
+ }
+ else if (module_ssl_proxy_enable) {
+ enabled = module_ssl_proxy_enable(c);
+ }
+ }
+ else {
+ /* !enable_ssl || enabled
+ * any existing optional funcs need to not enable here */
+ if (module_ssl_engine_set) {
+ module_ssl_engine_set(c, dir_conf, 1, 0);
+ }
+ else if (module_ssl_engine_disable) {
+ module_ssl_engine_disable(c);
+ }
+ }
+ if (enable_ssl && !enabled) {
+ ap_log_cerror(APLOG_MARK, APLOG_ERR, 0,
+ c, APLOGNO(01961) " failed to enable ssl support "
+ "[Hint: if using mod_ssl, see SSLProxyEngine]");
+ return DECLINED;
+ }
+ return OK;
+}
+
+AP_DECLARE(int) ap_ssl_has_outgoing_handlers(void)
+{
+ apr_array_header_t *hooks = ap_hook_get_ssl_bind_outgoing();
+ return (hooks && hooks->nelts > 0)
+ || module_ssl_engine_set || module_ssl_proxy_enable;
+}
+
+APR_DECLARE_OPTIONAL_FN(const char *, ssl_var_lookup,
+ (apr_pool_t *p, server_rec *s,
+ conn_rec *c, request_rec *r,
+ const char *name));
+static APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *module_ssl_var_lookup;
+
+static const char *ssl_var_lookup(apr_pool_t *p, server_rec *s,
+ conn_rec *c, request_rec *r,
+ const char *name)
+{
+ /* Someone retrieved the optional function., not knowing about the
+ * new API. We redirect them to what they should have invoked. */
+ return ap_ssl_var_lookup(p, s, c, r, name);
+}
+
+AP_DECLARE(const char *) ap_ssl_var_lookup(apr_pool_t *p, server_rec *s,
+ conn_rec *c, request_rec *r,
+ const char *name)
+{
+ const char *val = ap_run_ssl_var_lookup(p, s, c, r, name);
+ if (val == NULL && module_ssl_var_lookup) {
+ val = module_ssl_var_lookup(p, s, c, r, name);
+ }
+ return val;
+}
+
+AP_DECLARE(void) ap_setup_ssl_optional_fns(apr_pool_t *pool)
+{
+ /* Run as core's very early 'post config' hook, check for any already
+ * installed optional functions related to SSL and save them. Install
+ * our own instances that invoke the new hooks. */
+ APR_OPTIONAL_FN_TYPE(ssl_is_https) *fn_is_https;
+ APR_OPTIONAL_FN_TYPE(ssl_var_lookup) *fn_ssl_var_lookup;
+
+ fn_is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);
+ module_ssl_is_https = (fn_is_https
+ && fn_is_https != ssl_is_https)? fn_is_https : NULL;
+ APR_REGISTER_OPTIONAL_FN(ssl_is_https);
+
+ fn_ssl_var_lookup = APR_RETRIEVE_OPTIONAL_FN(ssl_var_lookup);
+ module_ssl_var_lookup = (fn_ssl_var_lookup
+ && fn_ssl_var_lookup != ssl_var_lookup)? fn_ssl_var_lookup : NULL;
+ APR_REGISTER_OPTIONAL_FN(ssl_var_lookup);
+
+ module_ssl_proxy_enable = APR_RETRIEVE_OPTIONAL_FN(ssl_proxy_enable);
+ APR_REGISTER_OPTIONAL_FN(ssl_proxy_enable);
+ module_ssl_engine_disable = APR_RETRIEVE_OPTIONAL_FN(ssl_engine_disable);
+ APR_REGISTER_OPTIONAL_FN(ssl_engine_disable);
+ module_ssl_engine_set = APR_RETRIEVE_OPTIONAL_FN(ssl_engine_set);
+ APR_REGISTER_OPTIONAL_FN(ssl_engine_set);
+}
+
+AP_DECLARE(apr_status_t) ap_ssl_add_cert_files(server_rec *s, apr_pool_t *p,
+ apr_array_header_t *cert_files,
+ apr_array_header_t *key_files)
+{
+ int rv = ap_run_ssl_add_cert_files(s, p, cert_files, key_files);
+ return (rv == OK || rv == DECLINED)? APR_SUCCESS : APR_EGENERAL;
+}
+
+AP_DECLARE(apr_status_t) ap_ssl_add_fallback_cert_files(server_rec *s, apr_pool_t *p,
+ apr_array_header_t *cert_files,
+ apr_array_header_t *key_files)
+{
+ int rv = ap_run_ssl_add_fallback_cert_files(s, p, cert_files, key_files);
+ return (rv == OK || rv == DECLINED)? APR_SUCCESS : APR_EGENERAL;
+}
+
+AP_DECLARE(int) ap_ssl_answer_challenge(conn_rec *c, const char *server_name,
+ const char **pcert_pem, const char **pkey_pem)
+{
+ return (ap_run_ssl_answer_challenge(c, server_name, pcert_pem, pkey_pem) == OK);
+}
+
+AP_DECLARE(apr_status_t) ap_ssl_ocsp_prime(server_rec *s, apr_pool_t *p,
+ const char *id, apr_size_t id_len,
+ const char *pem)
+{
+ int rv = ap_run_ssl_ocsp_prime_hook(s, p, id, id_len, pem);
+ return rv == OK? APR_SUCCESS : (rv == DECLINED? APR_ENOENT : APR_EGENERAL);
+}
+
+AP_DECLARE(apr_status_t) ap_ssl_ocsp_get_resp(server_rec *s, conn_rec *c,
+ const char *id, apr_size_t id_len,
+ ap_ssl_ocsp_copy_resp *cb, void *userdata)
+{
+ int rv = ap_run_ssl_ocsp_get_resp_hook(s, c, id, id_len, cb, userdata);
+ return rv == OK? APR_SUCCESS : (rv == DECLINED? APR_ENOENT : APR_EGENERAL);
+}
+
+AP_IMPLEMENT_HOOK_RUN_FIRST(int, ssl_conn_is_ssl,
+ (conn_rec *c), (c), DECLINED)
+AP_IMPLEMENT_HOOK_RUN_FIRST(const char *,ssl_var_lookup,
+ (apr_pool_t *p, server_rec *s, conn_rec *c, request_rec *r, const char *name),
+ (p, s, c, r, name), NULL)
+AP_IMPLEMENT_HOOK_RUN_ALL(int, ssl_add_cert_files,
+ (server_rec *s, apr_pool_t *p,
+ apr_array_header_t *cert_files, apr_array_header_t *key_files),
+ (s, p, cert_files, key_files), OK, DECLINED)
+AP_IMPLEMENT_HOOK_RUN_ALL(int, ssl_add_fallback_cert_files,
+ (server_rec *s, apr_pool_t *p,
+ apr_array_header_t *cert_files, apr_array_header_t *key_files),
+ (s, p, cert_files, key_files), OK, DECLINED)
+AP_IMPLEMENT_HOOK_RUN_FIRST(int, ssl_answer_challenge,
+ (conn_rec *c, const char *server_name, const char **pcert_pem, const char **pkey_pem),
+ (c, server_name, pcert_pem, pkey_pem), DECLINED)
+AP_IMPLEMENT_HOOK_RUN_FIRST(int, ssl_ocsp_prime_hook,
+ (server_rec *s, apr_pool_t *p, const char *id, apr_size_t id_len, const char *pem),
+ (s, p, id, id_len, pem), DECLINED)
+AP_IMPLEMENT_HOOK_RUN_FIRST(int, ssl_ocsp_get_resp_hook,
+ (server_rec *s, conn_rec *c, const char *id, apr_size_t id_len,
+ ap_ssl_ocsp_copy_resp *cb, void *userdata),
+ (s, c, id, id_len, cb, userdata), DECLINED)
+AP_IMPLEMENT_HOOK_RUN_FIRST(int,ssl_bind_outgoing,(conn_rec *c, ap_conf_vector_t *dir_conf, int require_ssl),
+ (c, dir_conf, require_ssl), DECLINED)
diff --git a/server/util.c b/server/util.c
index fd7a0a1..45502b8 100644
--- a/server/util.c
+++ b/server/util.c
@@ -47,6 +47,7 @@
#include "ap_config.h"
#include "apr_base64.h"
+#include "apr_fnmatch.h"
#include "httpd.h"
#include "http_main.h"
#include "http_log.h"
@@ -74,15 +75,8 @@
*/
#include "test_char.h"
-/* we assume the folks using this ensure 0 <= c < 256... which means
- * you need a cast to (unsigned char) first, you can't just plug a
- * char in here and get it to work, because if char is signed then it
- * will first be sign extended.
- */
-#define TEST_CHAR(c, f) (test_char_table[(unsigned char)(c)] & (f))
-
/* Win32/NetWare/OS2 need to check for both forward and back slashes
- * in ap_getparents() and ap_escape_url.
+ * in ap_normalize_path() and ap_escape_url().
*/
#ifdef CASE_BLIND_FILESYSTEM
#define IS_SLASH(s) ((s == '/') || (s == '\\'))
@@ -96,6 +90,11 @@
#undef APLOG_MODULE_INDEX
#define APLOG_MODULE_INDEX AP_CORE_MODULE_INDEX
+/* maximum nesting level for config directories */
+#ifndef AP_MAX_FNMATCH_DIR_DEPTH
+#define AP_MAX_FNMATCH_DIR_DEPTH (128)
+#endif
+
/*
* Examine a field value (such as a media-/content-type) string and return
* it sans any parameters; e.g., strip off any ';charset=foo' and the like.
@@ -186,11 +185,9 @@ AP_DECLARE(char *) ap_ht_time(apr_pool_t *p, apr_time_t t, const char *fmt,
*/
AP_DECLARE(int) ap_strcmp_match(const char *str, const char *expected)
{
- int x, y;
+ apr_size_t x, y;
for (x = 0, y = 0; expected[y]; ++y, ++x) {
- if ((!str[x]) && (expected[y] != '*'))
- return -1;
if (expected[y] == '*') {
while (expected[++y] == '*');
if (!expected[y])
@@ -202,6 +199,8 @@ AP_DECLARE(int) ap_strcmp_match(const char *str, const char *expected)
}
return -1;
}
+ else if (!str[x])
+ return -1;
else if ((expected[y] != '?') && (str[x] != expected[y]))
return 1;
}
@@ -210,7 +209,7 @@ AP_DECLARE(int) ap_strcmp_match(const char *str, const char *expected)
AP_DECLARE(int) ap_strcasecmp_match(const char *str, const char *expected)
{
- int x, y;
+ apr_size_t x, y;
for (x = 0, y = 0; expected[y]; ++y, ++x) {
if (!str[x] && expected[y] != '*')
@@ -251,10 +250,8 @@ AP_DECLARE(int) ap_os_is_path_absolute(apr_pool_t *p, const char *dir)
AP_DECLARE(int) ap_is_matchexp(const char *str)
{
- int x;
-
- for (x = 0; str[x]; x++)
- if ((str[x] == '*') || (str[x] == '?'))
+ for (; *str; str++)
+ if ((*str == '*') || (*str == '?'))
return 1;
return 0;
}
@@ -492,85 +489,149 @@ AP_DECLARE(apr_status_t) ap_pregsub_ex(apr_pool_t *p, char **result,
return rc;
}
+/* Forward declare */
+static char x2c(const char *what);
+
+#define IS_SLASH_OR_NUL(s) (s == '\0' || IS_SLASH(s))
+
/*
- * Parse .. so we don't compromise security
+ * Inspired by mod_jk's jk_servlet_normalize().
*/
-AP_DECLARE(void) ap_getparents(char *name)
+AP_DECLARE(int) ap_normalize_path(char *path, unsigned int flags)
{
- char *next;
- int l, w, first_dot;
+ int ret = 1;
+ apr_size_t l = 1, w = 1, n;
+ int decode_unreserved = (flags & AP_NORMALIZE_DECODE_UNRESERVED) != 0;
+ int merge_slashes = (flags & AP_NORMALIZE_MERGE_SLASHES) != 0;
- /* Four paseses, as per RFC 1808 */
- /* a) remove ./ path segments */
- for (next = name; *next && (*next != '.'); next++) {
- }
+ if (!IS_SLASH(path[0])) {
+ /* Besides "OPTIONS *", a request-target should start with '/'
+ * per RFC 7230 section 5.3, so anything else is invalid.
+ */
+ if (path[0] == '*' && path[1] == '\0') {
+ return 1;
+ }
+ /* However, AP_NORMALIZE_ALLOW_RELATIVE can be used to bypass
+ * this restriction (e.g. for subrequest file lookups).
+ */
+ if (!(flags & AP_NORMALIZE_ALLOW_RELATIVE) || path[0] == '\0') {
+ return 0;
+ }
- l = w = first_dot = next - name;
- while (name[l] != '\0') {
- if (name[l] == '.' && IS_SLASH(name[l + 1])
- && (l == 0 || IS_SLASH(name[l - 1])))
- l += 2;
- else
- name[w++] = name[l++];
+ l = w = 0;
}
- /* b) remove trailing . path, segment */
- if (w == 1 && name[0] == '.')
- w--;
- else if (w > 1 && name[w - 1] == '.' && IS_SLASH(name[w - 2]))
- w--;
- name[w] = '\0';
+ while (path[l] != '\0') {
+ /* RFC-3986 section 2.3:
+ * For consistency, percent-encoded octets in the ranges of
+ * ALPHA (%41-%5A and %61-%7A), DIGIT (%30-%39), hyphen (%2D),
+ * period (%2E), underscore (%5F), or tilde (%7E) should [...]
+ * be decoded to their corresponding unreserved characters by
+ * URI normalizers.
+ */
+ if (decode_unreserved && path[l] == '%') {
+ if (apr_isxdigit(path[l + 1]) && apr_isxdigit(path[l + 2])) {
+ const char c = x2c(&path[l + 1]);
+ if (TEST_CHAR(c, T_URI_UNRESERVED)) {
+ /* Replace last char and fall through as the current
+ * read position */
+ l += 2;
+ path[l] = c;
+ }
+ }
+ else {
+ /* Invalid encoding */
+ ret = 0;
+ }
+ }
- /* c) remove all xx/../ segments. (including leading ../ and /../) */
- l = first_dot;
+ if (w == 0 || IS_SLASH(path[w - 1])) {
+ /* Collapse ///// sequences to / */
+ if (merge_slashes && IS_SLASH(path[l])) {
+ do {
+ l++;
+ } while (IS_SLASH(path[l]));
+ continue;
+ }
- while (name[l] != '\0') {
- if (name[l] == '.' && name[l + 1] == '.' && IS_SLASH(name[l + 2])
- && (l == 0 || IS_SLASH(name[l - 1]))) {
- int m = l + 3, n;
+ if (path[l] == '.') {
+ /* Remove /./ segments */
+ if (IS_SLASH_OR_NUL(path[l + 1])) {
+ l++;
+ if (path[l]) {
+ l++;
+ }
+ continue;
+ }
- l = l - 2;
- if (l >= 0) {
- while (l >= 0 && !IS_SLASH(name[l]))
- l--;
- l++;
+ /* Remove /xx/../ segments (or /xx/.%2e/ when
+ * AP_NORMALIZE_DECODE_UNRESERVED is set since we
+ * decoded only the first dot above).
+ */
+ n = l + 1;
+ if ((path[n] == '.' || (decode_unreserved
+ && path[n] == '%'
+ && path[++n] == '2'
+ && (path[++n] == 'e'
+ || path[n] == 'E')))
+ && IS_SLASH_OR_NUL(path[n + 1])) {
+ /* Wind w back to remove the previous segment */
+ if (w > 1) {
+ do {
+ w--;
+ } while (w && !IS_SLASH(path[w - 1]));
+ }
+ else {
+ /* Already at root, ignore and return a failure
+ * if asked to.
+ */
+ if (flags & AP_NORMALIZE_NOT_ABOVE_ROOT) {
+ ret = 0;
+ }
+ }
+
+ /* Move l forward to the next segment */
+ l = n + 1;
+ if (path[l]) {
+ l++;
+ }
+ continue;
+ }
}
- else
- l = 0;
- n = l;
- while ((name[n] = name[m]))
- (++n, ++m);
}
- else
- ++l;
+
+ path[w++] = path[l++];
}
+ path[w] = '\0';
- /* d) remove trailing xx/.. segment. */
- if (l == 2 && name[0] == '.' && name[1] == '.')
+ return ret;
+}
+
+/*
+ * Parse .. so we don't compromise security
+ */
+AP_DECLARE(void) ap_getparents(char *name)
+{
+ if (!ap_normalize_path(name, AP_NORMALIZE_NOT_ABOVE_ROOT |
+ AP_NORMALIZE_ALLOW_RELATIVE)) {
name[0] = '\0';
- else if (l > 2 && name[l - 1] == '.' && name[l - 2] == '.'
- && IS_SLASH(name[l - 3])) {
- l = l - 4;
- if (l >= 0) {
- while (l >= 0 && !IS_SLASH(name[l]))
- l--;
- l++;
- }
- else
- l = 0;
- name[l] = '\0';
}
}
-AP_DECLARE(void) ap_no2slash(char *name)
+AP_DECLARE(void) ap_no2slash_ex(char *name, int is_fs_path)
{
+
char *d, *s;
+ if (!*name) {
+ return;
+ }
+
s = d = name;
#ifdef HAVE_UNC_PATHS
/* Check for UNC names. Leave leading two slashes. */
- if (s[0] == '/' && s[1] == '/')
+ if (is_fs_path && s[0] == '/' && s[1] == '/')
*d++ = *s++;
#endif
@@ -587,6 +648,10 @@ AP_DECLARE(void) ap_no2slash(char *name)
*d = '\0';
}
+AP_DECLARE(void) ap_no2slash(char *name)
+{
+ ap_no2slash_ex(name, 1);
+}
/*
* copy at most n leading directories of s into d
@@ -934,7 +999,7 @@ AP_DECLARE(apr_status_t) ap_pcfg_openfile(ap_configfile_t **ret_cfg,
if (finfo.filetype != APR_REG &&
#if defined(WIN32) || defined(OS2) || defined(NETWARE)
- strcasecmp(apr_filepath_name_get(name), "nul") != 0) {
+ ap_cstr_casecmp(apr_filepath_name_get(name), "nul") != 0) {
#else
strcmp(name, "/dev/null") != 0) {
#endif /* WIN32 || OS2 */
@@ -1691,7 +1756,7 @@ AP_DECLARE(int) ap_find_token(apr_pool_t *p, const char *line, const char *tok)
while (*s && !TEST_CHAR(*s, T_HTTP_TOKEN_STOP)) {
++s;
}
- if (!strncasecmp((const char *)start_token, (const char *)tok,
+ if (!ap_cstr_casecmpn((const char *)start_token, (const char *)tok,
s - start_token)) {
return 1;
}
@@ -1701,14 +1766,13 @@ AP_DECLARE(int) ap_find_token(apr_pool_t *p, const char *line, const char *tok)
}
}
-
-AP_DECLARE(int) ap_find_last_token(apr_pool_t *p, const char *line,
- const char *tok)
+static const char *find_last_token(apr_pool_t *p, const char *line,
+ const char *tok)
{
int llen, tlen, lidx;
if (!line)
- return 0;
+ return NULL;
llen = strlen(line);
tlen = strlen(tok);
@@ -1716,9 +1780,44 @@ AP_DECLARE(int) ap_find_last_token(apr_pool_t *p, const char *line,
if (lidx < 0 ||
(lidx > 0 && !(apr_isspace(line[lidx - 1]) || line[lidx - 1] == ',')))
+ return NULL;
+
+ if (ap_cstr_casecmpn(&line[lidx], tok, tlen) == 0) {
+ return &line[lidx];
+ }
+ return NULL;
+}
+
+AP_DECLARE(int) ap_find_last_token(apr_pool_t *p, const char *line,
+ const char *tok)
+{
+ return find_last_token(p, line, tok) != NULL;
+}
+
+AP_DECLARE(int) ap_is_chunked(apr_pool_t *p, const char *line)
+{
+ const char *s;
+
+ if (!line)
return 0;
+ if (!ap_cstr_casecmp(line, "chunked")) {
+ return 1;
+ }
+
+ s = find_last_token(p, line, "chunked");
- return (strncasecmp(&line[lidx], tok, tlen) == 0);
+ if (!s) return 0;
+
+ /* eat spaces right-to-left to see what precedes "chunked" */
+ while (--s > line) {
+ if (*s != ' ') break;
+ }
+
+ /* found delim, or leading ws (input wasn't parsed by httpd as a header) */
+ if (*s == ',' || *s == ' ') {
+ return 1;
+ }
+ return 0;
}
AP_DECLARE(char *) ap_escape_shell_cmd(apr_pool_t *p, const char *str)
@@ -1786,8 +1885,12 @@ static char x2c(const char *what)
* decoding %00 or a forbidden character returns HTTP_NOT_FOUND
*/
-static int unescape_url(char *url, const char *forbid, const char *reserved)
+static int unescape_url(char *url, const char *forbid, const char *reserved,
+ unsigned int flags)
{
+ const int keep_slashes = (flags & AP_UNESCAPE_URL_KEEP_SLASHES) != 0,
+ forbid_slashes = (flags & AP_UNESCAPE_URL_FORBID_SLASHES) != 0,
+ keep_unreserved = (flags & AP_UNESCAPE_URL_KEEP_UNRESERVED) != 0;
int badesc, badpath;
char *x, *y;
@@ -1812,12 +1915,16 @@ static int unescape_url(char *url, const char *forbid, const char *reserved)
char decoded;
decoded = x2c(y + 1);
if ((decoded == '\0')
+ || (forbid_slashes && IS_SLASH(decoded))
|| (forbid && ap_strchr_c(forbid, decoded))) {
badpath = 1;
*x = decoded;
y += 2;
}
- else if (reserved && ap_strchr_c(reserved, decoded)) {
+ else if ((keep_unreserved && TEST_CHAR(decoded,
+ T_URI_UNRESERVED))
+ || (keep_slashes && IS_SLASH(decoded))
+ || (reserved && ap_strchr_c(reserved, decoded))) {
*x++ = *y++;
*x++ = *y++;
*x = *y;
@@ -1843,19 +1950,24 @@ static int unescape_url(char *url, const char *forbid, const char *reserved)
AP_DECLARE(int) ap_unescape_url(char *url)
{
/* Traditional */
- return unescape_url(url, SLASHES, NULL);
+ return unescape_url(url, SLASHES, NULL, 0);
}
AP_DECLARE(int) ap_unescape_url_keep2f(char *url, int decode_slashes)
{
/* AllowEncodedSlashes (corrected) */
if (decode_slashes) {
/* no chars reserved */
- return unescape_url(url, NULL, NULL);
+ return unescape_url(url, NULL, NULL, 0);
} else {
/* reserve (do not decode) encoded slashes */
- return unescape_url(url, NULL, SLASHES);
+ return unescape_url(url, NULL, SLASHES, 0);
}
}
+AP_DECLARE(int) ap_unescape_url_ex(char *url, unsigned int flags)
+{
+ return unescape_url(url, NULL, NULL, flags);
+}
+
#ifdef NEW_APIS
/* IFDEF these out until they've been thought through.
* Just a germ of an API extension for now
@@ -1865,7 +1977,7 @@ AP_DECLARE(int) ap_unescape_url_proxy(char *url)
/* leave RFC1738 reserved characters intact, * so proxied URLs
* don't get mangled. Where does that leave encoded '&' ?
*/
- return unescape_url(url, NULL, "/;?");
+ return unescape_url(url, NULL, "/;?", 0);
}
AP_DECLARE(int) ap_unescape_url_reserved(char *url, const char *reserved)
{
@@ -1887,7 +1999,7 @@ AP_DECLARE(int) ap_unescape_urlencoded(char *query)
}
/* unescape everything else */
- return unescape_url(query, NULL, NULL);
+ return unescape_url(query, NULL, NULL, 0);
}
AP_DECLARE(char *) ap_construct_server(apr_pool_t *p, const char *hostname,
@@ -1903,7 +2015,7 @@ AP_DECLARE(char *) ap_construct_server(apr_pool_t *p, const char *hostname,
AP_DECLARE(int) ap_unescape_all(char *url)
{
- return unescape_url(url, NULL, NULL);
+ return unescape_url(url, NULL, NULL, 0);
}
/* c2x takes an unsigned, and expects the caller has guaranteed that
@@ -2029,11 +2141,14 @@ AP_DECLARE(char *) ap_escape_urlencoded(apr_pool_t *p, const char *buffer)
AP_DECLARE(char *) ap_escape_html2(apr_pool_t *p, const char *s, int toasc)
{
- int i, j;
+ apr_size_t i, j;
char *x;
/* first, count the number of extra characters */
- for (i = 0, j = 0; s[i] != '\0'; i++)
+ for (i = 0, j = 0; s[i] != '\0'; i++) {
+ if (i + j > APR_SIZE_MAX - 6) {
+ abort();
+ }
if (s[i] == '<' || s[i] == '>')
j += 3;
else if (s[i] == '&')
@@ -2042,6 +2157,7 @@ AP_DECLARE(char *) ap_escape_html2(apr_pool_t *p, const char *s, int toasc)
j += 5;
else if (toasc && !apr_isascii(s[i]))
j += 5;
+ }
if (j == 0)
return apr_pstrmemdup(p, s, i);
@@ -2372,11 +2488,9 @@ char *ap_get_local_host(apr_pool_t *a)
AP_DECLARE(char *) ap_pbase64decode(apr_pool_t *p, const char *bufcoded)
{
char *decoded;
- int l;
- decoded = (char *) apr_palloc(p, 1 + apr_base64_decode_len(bufcoded));
- l = apr_base64_decode(decoded, bufcoded);
- decoded[l] = '\0'; /* make binary sequence into string */
+ decoded = (char *) apr_palloc(p, apr_base64_decode_len(bufcoded));
+ apr_base64_decode(decoded, bufcoded);
return decoded;
}
@@ -2386,9 +2500,8 @@ AP_DECLARE(char *) ap_pbase64encode(apr_pool_t *p, char *string)
char *encoded;
int l = strlen(string);
- encoded = (char *) apr_palloc(p, 1 + apr_base64_encode_len(l));
- l = apr_base64_encode(encoded, string, l);
- encoded[l] = '\0'; /* make binary sequence into string */
+ encoded = (char *) apr_palloc(p, apr_base64_encode_len(l));
+ apr_base64_encode(encoded, string, l);
return encoded;
}
@@ -2421,7 +2534,7 @@ AP_DECLARE(void) ap_content_type_tolower(char *str)
*/
AP_DECLARE(char *) ap_escape_quotes(apr_pool_t *p, const char *instring)
{
- int newlen = 0;
+ apr_size_t size, extra = 0;
const char *inchr = instring;
char *outchr, *outstring;
@@ -2430,21 +2543,41 @@ AP_DECLARE(char *) ap_escape_quotes(apr_pool_t *p, const char *instring)
* string up by an extra byte each time we find an unescaped ".
*/
while (*inchr != '\0') {
- newlen++;
if (*inchr == '"') {
- newlen++;
+ extra++;
}
/*
* If we find a slosh, and it's not the last byte in the string,
* it's escaping something - advance past both bytes.
*/
- if ((*inchr == '\\') && (inchr[1] != '\0')) {
+ else if ((*inchr == '\\') && (inchr[1] != '\0')) {
inchr++;
- newlen++;
}
inchr++;
}
- outstring = apr_palloc(p, newlen + 1);
+
+ if (!extra) {
+ return apr_pstrdup(p, instring);
+ }
+
+ /* How large will the string become, once we escaped all the quotes?
+ * The tricky cases are
+ * - an `instring` that is already longer than `ptrdiff_t`
+ * can hold (which is an undefined case in C, as C defines ptrdiff_t as
+ * a signed difference between pointers into the same array and one index
+ * beyond).
+ * - an `instring` that, including the `extra` chars we want to add, becomes
+ * even larger than apr_size_t can handle.
+ * Since this function was not designed to ever return NULL for failure, we
+ * can only trigger a hard assertion failure. It seems more a programming
+ * mistake (or failure to verify the input causing this) that leads to this
+ * situation.
+ */
+ ap_assert(inchr - instring > 0);
+ size = ((apr_size_t)(inchr - instring)) + 1;
+ ap_assert(size + extra > size);
+
+ outstring = apr_palloc(p, size + extra);
inchr = instring;
outchr = outstring;
/*
@@ -2452,16 +2585,13 @@ AP_DECLARE(char *) ap_escape_quotes(apr_pool_t *p, const char *instring)
* in front of every " that doesn't already have one.
*/
while (*inchr != '\0') {
- if ((*inchr == '\\') && (inchr[1] != '\0')) {
- *outchr++ = *inchr++;
- *outchr++ = *inchr++;
- }
if (*inchr == '"') {
*outchr++ = '\\';
}
- if (*inchr != '\0') {
+ else if ((*inchr == '\\') && (inchr[1] != '\0')) {
*outchr++ = *inchr++;
}
+ *outchr++ = *inchr++;
}
*outchr = '\0';
return outstring;
@@ -2499,6 +2629,7 @@ AP_DECLARE(char *) ap_append_pid(apr_pool_t *p, const char *string,
* in timeout_parameter.
* @return Status value indicating whether the parsing was successful or not.
*/
+#define CHECK_OVERFLOW(a, b) if (a > b) return APR_EGENERAL
AP_DECLARE(apr_status_t) ap_timeout_parameter_parse(
const char *timeout_parameter,
apr_interval_time_t *timeout,
@@ -2507,6 +2638,7 @@ AP_DECLARE(apr_status_t) ap_timeout_parameter_parse(
char *endp;
const char *time_str;
apr_int64_t tout;
+ apr_uint64_t check;
tout = apr_strtoi64(timeout_parameter, &endp, 10);
if (errno) {
@@ -2519,24 +2651,32 @@ AP_DECLARE(apr_status_t) ap_timeout_parameter_parse(
time_str = endp;
}
+ if (tout < 0) {
+ return APR_EGENERAL;
+ }
+
switch (*time_str) {
/* Time is in seconds */
case 's':
- *timeout = (apr_interval_time_t) apr_time_from_sec(tout);
+ CHECK_OVERFLOW(tout, apr_time_sec(APR_INT64_MAX));
+ check = apr_time_from_sec(tout);
break;
- case 'h':
/* Time is in hours */
- *timeout = (apr_interval_time_t) apr_time_from_sec(tout * 3600);
+ case 'h':
+ CHECK_OVERFLOW(tout, apr_time_sec(APR_INT64_MAX / 3600));
+ check = apr_time_from_sec(tout * 3600);
break;
case 'm':
switch (*(++time_str)) {
/* Time is in milliseconds */
case 's':
- *timeout = (apr_interval_time_t) tout * 1000;
+ CHECK_OVERFLOW(tout, apr_time_as_msec(APR_INT64_MAX));
+ check = apr_time_from_msec(tout);
break;
/* Time is in minutes */
case 'i':
- *timeout = (apr_interval_time_t) apr_time_from_sec(tout * 60);
+ CHECK_OVERFLOW(tout, apr_time_sec(APR_INT64_MAX / 60));
+ check = apr_time_from_sec(tout * 60);
break;
default:
return APR_EGENERAL;
@@ -2545,8 +2685,20 @@ AP_DECLARE(apr_status_t) ap_timeout_parameter_parse(
default:
return APR_EGENERAL;
}
+
+ *timeout = (apr_interval_time_t)check;
return APR_SUCCESS;
}
+#undef CHECK_OVERFLOW
+
+AP_DECLARE(int) ap_parse_strict_length(apr_off_t *len, const char *str)
+{
+ char *end;
+
+ return (apr_isdigit(*str)
+ && apr_strtoff(len, str, &end, 10) == APR_SUCCESS
+ && *end == '\0');
+}
/**
* Determine if a request has a request body or not.
@@ -2557,20 +2709,13 @@ AP_DECLARE(apr_status_t) ap_timeout_parameter_parse(
AP_DECLARE(int) ap_request_has_body(request_rec *r)
{
apr_off_t cl;
- char *estr;
const char *cls;
- int has_body;
- has_body = (!r->header_only
- && (r->kept_body
- || apr_table_get(r->headers_in, "Transfer-Encoding")
- || ( (cls = apr_table_get(r->headers_in, "Content-Length"))
- && (apr_strtoff(&cl, cls, &estr, 10) == APR_SUCCESS)
- && (!*estr)
- && (cl > 0) )
- )
- );
- return has_body;
+ return (!r->header_only
+ && (r->kept_body
+ || apr_table_get(r->headers_in, "Transfer-Encoding")
+ || ((cls = apr_table_get(r->headers_in, "Content-Length"))
+ && ap_parse_strict_length(&cl, cls) && cl > 0)));
}
AP_DECLARE_NONSTD(apr_status_t) ap_pool_cleanup_set_null(void *data_)
@@ -2669,7 +2814,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 || strncasecmp("application/x-www-form-urlencoded", ct, 33)) {
+ if (!ct || ap_cstr_casecmpn("application/x-www-form-urlencoded", ct, 33)) {
return ap_discard_request_body(r);
}
@@ -2754,12 +2899,11 @@ AP_DECLARE(int) ap_parse_form_data(request_rec *r, ap_filter_t *f,
case FORM_NAME:
if (offset < HUGE_STRING_LEN) {
if ('=' == c) {
- buffer[offset] = 0;
- offset = 0;
pair = (ap_form_pair_t *) apr_array_push(pairs);
- pair->name = apr_pstrdup(r->pool, buffer);
+ pair->name = apr_pstrmemdup(r->pool, buffer, offset);
pair->value = apr_brigade_create(r->pool, r->connection->bucket_alloc);
state = FORM_VALUE;
+ offset = 0;
}
else {
buffer[offset++] = c;
@@ -2820,7 +2964,7 @@ static apr_status_t varbuf_cleanup(void *info_)
return APR_SUCCESS;
}
-const char nul = '\0';
+static const char nul = '\0';
static char * const varbuf_empty = (char *)&nul;
AP_DECLARE(void) ap_varbuf_init(apr_pool_t *p, struct ap_varbuf *vb,
@@ -2889,6 +3033,11 @@ AP_DECLARE(void) ap_varbuf_grow(struct ap_varbuf *vb, apr_size_t new_len)
/* The required block is rather larger. Use allocator directly so that
* the memory can be freed independently from the pool. */
allocator = apr_pool_allocator_get(vb->pool);
+ /* Happens if APR was compiled with APR_POOL_DEBUG */
+ if (allocator == NULL) {
+ apr_allocator_create(&allocator);
+ ap_assert(allocator != NULL);
+ }
if (new_len <= VARBUF_MAX_SIZE)
new_node = apr_allocator_alloc(allocator,
new_len + APR_ALIGN_DEFAULT(sizeof(*new_info)));
@@ -2995,7 +3144,7 @@ AP_DECLARE(apr_status_t) ap_varbuf_regsub(struct ap_varbuf *vb,
static const char * const oom_message = "[crit] Memory allocation failed, "
"aborting process." APR_EOL_STR;
-AP_DECLARE(void) ap_abort_on_oom()
+AP_DECLARE(void) ap_abort_on_oom(void)
{
int written, count = strlen(oom_message);
const char *buf = oom_message;
@@ -3035,6 +3184,135 @@ AP_DECLARE(void *) ap_realloc(void *ptr, size_t size)
return p;
}
+#if APR_HAS_THREADS
+
+#if APR_VERSION_AT_LEAST(1,8,0) && !defined(AP_NO_THREAD_LOCAL)
+
+#define ap_thread_current_create apr_thread_current_create
+
+#else /* APR_VERSION_AT_LEAST(1,8,0) && !defined(AP_NO_THREAD_LOCAL) */
+
+#if AP_HAS_THREAD_LOCAL
+
+struct thread_ctx {
+ apr_thread_start_t func;
+ void *data;
+};
+
+static AP_THREAD_LOCAL apr_thread_t *current_thread = NULL;
+
+static void *APR_THREAD_FUNC thread_start(apr_thread_t *thread, void *data)
+{
+ struct thread_ctx *ctx = data;
+
+ current_thread = thread;
+ return ctx->func(thread, ctx->data);
+}
+
+AP_DECLARE(apr_status_t) ap_thread_create(apr_thread_t **thread,
+ apr_threadattr_t *attr,
+ apr_thread_start_t func,
+ void *data, apr_pool_t *pool)
+{
+ struct thread_ctx *ctx = apr_palloc(pool, sizeof(*ctx));
+
+ ctx->func = func;
+ ctx->data = data;
+ return apr_thread_create(thread, attr, thread_start, ctx, pool);
+}
+
+#endif /* AP_HAS_THREAD_LOCAL */
+
+AP_DECLARE(apr_status_t) ap_thread_current_create(apr_thread_t **current,
+ apr_threadattr_t *attr,
+ apr_pool_t *pool)
+{
+ apr_status_t rv;
+ apr_abortfunc_t abort_fn = apr_pool_abort_get(pool);
+ apr_allocator_t *allocator;
+ apr_os_thread_t osthd;
+ apr_pool_t *p;
+
+ *current = ap_thread_current();
+ if (*current) {
+ return APR_EEXIST;
+ }
+
+ rv = apr_allocator_create(&allocator);
+ if (rv != APR_SUCCESS) {
+ if (abort_fn)
+ abort_fn(rv);
+ return rv;
+ }
+ rv = apr_pool_create_unmanaged_ex(&p, abort_fn, allocator);
+ if (rv != APR_SUCCESS) {
+ apr_allocator_destroy(allocator);
+ return rv;
+ }
+ apr_allocator_owner_set(allocator, p);
+
+ osthd = apr_os_thread_current();
+ rv = apr_os_thread_put(current, &osthd, p);
+ if (rv != APR_SUCCESS) {
+ apr_pool_destroy(p);
+ return rv;
+ }
+
+#if AP_HAS_THREAD_LOCAL
+ current_thread = *current;
+#endif
+ return APR_SUCCESS;
+}
+
+AP_DECLARE(void) ap_thread_current_after_fork(void)
+{
+#if AP_HAS_THREAD_LOCAL
+ current_thread = NULL;
+#endif
+}
+
+AP_DECLARE(apr_thread_t *) ap_thread_current(void)
+{
+#if AP_HAS_THREAD_LOCAL
+ return current_thread;
+#else
+ return NULL;
+#endif
+}
+
+#endif /* APR_VERSION_AT_LEAST(1,8,0) && !defined(AP_NO_THREAD_LOCAL) */
+
+static apr_status_t main_thread_cleanup(void *arg)
+{
+ apr_thread_t *thd = arg;
+ apr_pool_destroy(apr_thread_pool_get(thd));
+ return APR_SUCCESS;
+}
+
+AP_DECLARE(apr_status_t) ap_thread_main_create(apr_thread_t **thread,
+ apr_pool_t *pool)
+{
+ apr_status_t rv;
+ apr_threadattr_t *attr = NULL;
+
+ /* Create an apr_thread_t for the main child thread to set up its Thread
+ * Local Storage. Since it's detached and won't apr_thread_exit(), destroy
+ * its pool before exiting via a cleanup of the given pool.
+ */
+ if ((rv = apr_threadattr_create(&attr, pool))
+ || (rv = apr_threadattr_detach_set(attr, 1))
+ || (rv = ap_thread_current_create(thread, attr, pool))) {
+ *thread = NULL;
+ return rv;
+ }
+
+ apr_pool_cleanup_register(pool, *thread, main_thread_cleanup,
+ apr_pool_cleanup_null);
+ return APR_SUCCESS;
+}
+
+#endif /* APR_HAS_THREADS */
+
AP_DECLARE(void) ap_get_sload(ap_sload_t *ld)
{
int i, j, server_limit, thread_limit;
@@ -3307,3 +3585,206 @@ AP_DECLARE(int) ap_cstr_casecmpn(const char *s1, const char *s2, apr_size_t n)
return 0;
}
+typedef struct {
+ const char *fname;
+} fnames;
+
+static int fname_alphasort(const void *fn1, const void *fn2)
+{
+ const fnames *f1 = fn1;
+ const fnames *f2 = fn2;
+
+ return strcmp(f1->fname, f2->fname);
+}
+
+AP_DECLARE(ap_dir_match_t *)ap_dir_cfgmatch(cmd_parms *cmd, int flags,
+ const char *(*cb)(ap_dir_match_t *w, const char *fname), void *ctx)
+{
+ ap_dir_match_t *w = apr_palloc(cmd->temp_pool, sizeof(*w));
+
+ w->prefix = apr_pstrcat(cmd->pool, cmd->cmd->name, ": ", NULL);
+ w->p = cmd->pool;
+ w->ptemp = cmd->temp_pool;
+ w->flags = flags;
+ w->cb = cb;
+ w->ctx = ctx;
+ w->depth = 0;
+
+ return w;
+}
+
+AP_DECLARE(const char *)ap_dir_nofnmatch(ap_dir_match_t *w, const char *fname)
+{
+ const char *error;
+ apr_status_t rv;
+
+ if ((w->flags & AP_DIR_FLAG_RECURSIVE) && ap_is_directory(w->ptemp, fname)) {
+ apr_dir_t *dirp;
+ apr_finfo_t dirent;
+ int current;
+ apr_array_header_t *candidates = NULL;
+ fnames *fnew;
+ char *path = apr_pstrdup(w->ptemp, fname);
+
+ if (++w->depth > AP_MAX_FNMATCH_DIR_DEPTH) {
+ return apr_psprintf(w->p, "%sDirectory '%s' exceeds the maximum include "
+ "directory nesting level of %u. You have "
+ "probably a recursion somewhere.", w->prefix ? w->prefix : "", path,
+ AP_MAX_FNMATCH_DIR_DEPTH);
+ }
+
+ /*
+ * first course of business is to grok all the directory
+ * entries here and store 'em away. Recall we need full pathnames
+ * for this.
+ */
+ rv = apr_dir_open(&dirp, path, w->ptemp);
+ if (rv != APR_SUCCESS) {
+ return apr_psprintf(w->p, "%sCould not open directory %s: %pm",
+ w->prefix ? w->prefix : "", path, &rv);
+ }
+
+ candidates = apr_array_make(w->ptemp, 1, sizeof(fnames));
+ while (apr_dir_read(&dirent, APR_FINFO_DIRENT, dirp) == APR_SUCCESS) {
+ /* strip out '.' and '..' */
+ if (strcmp(dirent.name, ".")
+ && strcmp(dirent.name, "..")) {
+ fnew = (fnames *) apr_array_push(candidates);
+ fnew->fname = ap_make_full_path(w->ptemp, path, dirent.name);
+ }
+ }
+
+ apr_dir_close(dirp);
+ if (candidates->nelts != 0) {
+ qsort((void *) candidates->elts, candidates->nelts,
+ sizeof(fnames), fname_alphasort);
+
+ /*
+ * Now recurse these... we handle errors and subdirectories
+ * via the recursion, which is nice
+ */
+ for (current = 0; current < candidates->nelts; ++current) {
+ fnew = &((fnames *) candidates->elts)[current];
+ error = ap_dir_nofnmatch(w, fnew->fname);
+ if (error) {
+ return error;
+ }
+ }
+ }
+
+ w->depth--;
+
+ return NULL;
+ }
+ else if (w->flags & AP_DIR_FLAG_OPTIONAL) {
+ /* If the optional flag is set (like for IncludeOptional) we can
+ * tolerate that no file or directory is present and bail out.
+ */
+ apr_finfo_t finfo;
+ if (apr_stat(&finfo, fname, APR_FINFO_TYPE, w->ptemp) != APR_SUCCESS
+ || finfo.filetype == APR_NOFILE)
+ return NULL;
+ }
+
+ return w->cb(w, fname);
+}
+
+AP_DECLARE(const char *)ap_dir_fnmatch(ap_dir_match_t *w, const char *path,
+ const char *fname)
+{
+ const char *rest;
+ apr_status_t rv;
+ apr_dir_t *dirp;
+ apr_finfo_t dirent;
+ apr_array_header_t *candidates = NULL;
+ fnames *fnew;
+ int current;
+
+ /* find the first part of the filename */
+ rest = ap_strchr_c(fname, '/');
+ if (rest) {
+ fname = apr_pstrmemdup(w->ptemp, fname, rest - fname);
+ rest++;
+ }
+
+ /* optimisation - if the filename isn't a wildcard, process it directly */
+ if (!apr_fnmatch_test(fname)) {
+ path = path ? ap_make_full_path(w->ptemp, path, fname) : fname;
+ if (!rest) {
+ return ap_dir_nofnmatch(w, path);
+ }
+ else {
+ return ap_dir_fnmatch(w, path, rest);
+ }
+ }
+
+ /*
+ * first course of business is to grok all the directory
+ * entries here and store 'em away. Recall we need full pathnames
+ * for this.
+ */
+ rv = apr_dir_open(&dirp, path, w->ptemp);
+ if (rv != APR_SUCCESS) {
+ /* If the directory doesn't exist and the optional flag is set
+ * there is no need to return an error.
+ */
+ if (rv == APR_ENOENT && (w->flags & AP_DIR_FLAG_OPTIONAL)) {
+ return NULL;
+ }
+ return apr_psprintf(w->p, "%sCould not open directory %s: %pm",
+ w->prefix ? w->prefix : "", path, &rv);
+ }
+
+ candidates = apr_array_make(w->ptemp, 1, sizeof(fnames));
+ while (apr_dir_read(&dirent, APR_FINFO_DIRENT | APR_FINFO_TYPE, dirp) == APR_SUCCESS) {
+ /* strip out '.' and '..' */
+ if (strcmp(dirent.name, ".")
+ && strcmp(dirent.name, "..")
+ && (apr_fnmatch(fname, dirent.name,
+ APR_FNM_PERIOD) == APR_SUCCESS)) {
+ const char *full_path = ap_make_full_path(w->ptemp, path, dirent.name);
+ /* If matching internal to path, and we happen to match something
+ * other than a directory, skip it
+ */
+ if (rest && (dirent.filetype != APR_DIR)) {
+ continue;
+ }
+ fnew = (fnames *) apr_array_push(candidates);
+ fnew->fname = full_path;
+ }
+ }
+
+ apr_dir_close(dirp);
+ if (candidates->nelts != 0) {
+ const char *error;
+
+ qsort((void *) candidates->elts, candidates->nelts,
+ sizeof(fnames), fname_alphasort);
+
+ /*
+ * Now recurse these... we handle errors and subdirectories
+ * via the recursion, which is nice
+ */
+ for (current = 0; current < candidates->nelts; ++current) {
+ fnew = &((fnames *) candidates->elts)[current];
+ if (!rest) {
+ error = ap_dir_nofnmatch(w, fnew->fname);
+ }
+ else {
+ error = ap_dir_fnmatch(w, fnew->fname, rest);
+ }
+ if (error) {
+ return error;
+ }
+ }
+ }
+ else {
+
+ if (!(w->flags & AP_DIR_FLAG_OPTIONAL)) {
+ return apr_psprintf(w->p, "%sNo matches for the wildcard '%s' in '%s', failing",
+ w->prefix ? w->prefix : "", fname, path);
+ }
+ }
+
+ return NULL;
+}
diff --git a/server/util_expr_eval.c b/server/util_expr_eval.c
index 0f92f41..db4be95 100644
--- a/server/util_expr_eval.c
+++ b/server/util_expr_eval.c
@@ -23,6 +23,7 @@
#include "http_core.h"
#include "http_protocol.h"
#include "http_request.h"
+#include "http_ssl.h"
#include "ap_provider.h"
#include "util_expr_private.h"
#include "util_md5.h"
@@ -31,6 +32,10 @@
#include "apr_fnmatch.h"
#include "apr_base64.h"
#include "apr_sha1.h"
+#include "apr_version.h"
+#if APR_VERSION_AT_LEAST(1,5,0)
+#include "apr_escape.h"
+#endif
#include <limits.h> /* for INT_MAX */
@@ -317,7 +322,7 @@ static int ap_expr_eval_comp(ap_expr_eval_ctx_t *ctx, const ap_expr_t *node)
/* combined string/int comparison for compatibility with ssl_expr */
static int strcmplex(const char *str1, const char *str2)
{
- int i, n1, n2;
+ apr_size_t i, n1, n2;
if (str1 == NULL)
return -1;
@@ -1075,7 +1080,7 @@ static const char *sha1_func(ap_expr_eval_ctx_t *ctx, const void *data,
out = apr_palloc(ctx->p, APR_SHA1_DIGESTSIZE*2+1);
apr_sha1_init(&context);
- apr_sha1_update(&context, arg, strlen(arg));
+ apr_sha1_update(&context, arg, (unsigned int)strlen(arg));
apr_sha1_final(sha1, &context);
ap_bin2hex(sha1, APR_SHA1_DIGESTSIZE, out);
@@ -1086,9 +1091,16 @@ static const char *sha1_func(ap_expr_eval_ctx_t *ctx, const void *data,
static const char *md5_func(ap_expr_eval_ctx_t *ctx, const void *data,
const char *arg)
{
- return ap_md5(ctx->p, (const unsigned char *)arg);
+ return ap_md5(ctx->p, (const unsigned char *)arg);
}
+#if APR_VERSION_AT_LEAST(1,6,0)
+static const char *ldap_func(ap_expr_eval_ctx_t *ctx, const void *data,
+ const char *arg)
+{
+ return apr_pescape_ldap(ctx->p, arg, APR_ESCAPE_STRING, APR_ESCAPE_LDAP_ALL);
+}
+#endif
#define MAX_FILE_SIZE 10*1024*1024
static const char *file_func(ap_expr_eval_ctx_t *ctx, const void *data,
@@ -1256,13 +1268,10 @@ static int op_file_subr(ap_expr_eval_ctx_t *ctx, const void *data, const char *a
}
-APR_DECLARE_OPTIONAL_FN(int, ssl_is_https, (conn_rec *));
-static APR_OPTIONAL_FN_TYPE(ssl_is_https) *is_https = NULL;
-
APR_DECLARE_OPTIONAL_FN(int, http2_is_h2, (conn_rec *));
static APR_OPTIONAL_FN_TYPE(http2_is_h2) *is_http2 = NULL;
-static const char *conn_var_names[] = {
+static const char *const conn_var_names[] = {
"HTTPS", /* 0 */
"IPV6", /* 1 */
"CONN_LOG_ID", /* 2 */
@@ -1280,7 +1289,7 @@ static const char *conn_var_fn(ap_expr_eval_ctx_t *ctx, const void *data)
switch (index) {
case 0:
- if (is_https && is_https(c))
+ if (ap_ssl_conn_is_ssl(c))
return "on";
else
return "off";
@@ -1312,7 +1321,7 @@ static const char *conn_var_fn(ap_expr_eval_ctx_t *ctx, const void *data)
}
}
-static const char *request_var_names[] = {
+static const char *const request_var_names[] = {
"REQUEST_METHOD", /* 0 */
"REQUEST_SCHEME", /* 1 */
"REQUEST_URI", /* 2 */
@@ -1440,7 +1449,7 @@ static const char *request_var_fn(ap_expr_eval_ctx_t *ctx, const void *data)
}
}
-static const char *req_header_var_names[] = {
+static const char *const req_header_var_names[] = {
"HTTP_USER_AGENT", /* 0 */
"HTTP_PROXY_CONNECTION", /* 1 */
"HTTP_REFERER", /* 2 */
@@ -1451,7 +1460,7 @@ static const char *req_header_var_names[] = {
NULL
};
-static const char *req_header_header_names[] = {
+static const char *const req_header_header_names[] = {
"User-Agent",
"Proxy-Connection",
"Referer",
@@ -1463,7 +1472,7 @@ static const char *req_header_header_names[] = {
static const char *req_header_var_fn(ap_expr_eval_ctx_t *ctx, const void *data)
{
- const char **varname = (const char **)data;
+ const char **const varname = (const char **)data;
int index = (varname - req_header_var_names);
const char *name;
@@ -1481,7 +1490,7 @@ static const char *req_header_var_fn(ap_expr_eval_ctx_t *ctx, const void *data)
return apr_table_get(ctx->r->headers_in, name);
}
-static const char *misc_var_names[] = {
+static const char *const misc_var_names[] = {
"TIME_YEAR", /* 0 */
"TIME_MON", /* 1 */
"TIME_DAY", /* 2 */
@@ -1638,7 +1647,7 @@ struct expr_provider_single {
struct expr_provider_multi {
const void *func;
- const char **names;
+ const char *const *names;
};
static const struct expr_provider_multi var_providers[] = {
@@ -1669,6 +1678,9 @@ static const struct expr_provider_single string_func_providers[] = {
{ unbase64_func, "unbase64", NULL, 0 },
{ sha1_func, "sha1", NULL, 0 },
{ md5_func, "md5", NULL, 0 },
+#if APR_VERSION_AT_LEAST(1,6,0)
+ { ldap_func, "ldap", NULL, 0 },
+#endif
{ NULL, NULL, NULL}
};
@@ -1704,9 +1716,9 @@ static int core_expr_lookup(ap_expr_lookup_parms *parms)
case AP_EXPR_FUNC_VAR: {
const struct expr_provider_multi *prov = var_providers;
while (prov->func) {
- const char **name = prov->names;
+ const char *const *name = prov->names;
while (*name) {
- if (strcasecmp(*name, parms->name) == 0) {
+ if (ap_cstr_casecmp(*name, parms->name) == 0) {
*parms->func = prov->func;
*parms->data = name;
return OK;
@@ -1739,7 +1751,7 @@ static int core_expr_lookup(ap_expr_lookup_parms *parms)
if (parms->type == AP_EXPR_FUNC_OP_UNARY)
match = !strcmp(prov->name, parms->name);
else
- match = !strcasecmp(prov->name, parms->name);
+ match = !ap_cstr_casecmp(prov->name, parms->name);
if (match) {
if ((parms->flags & AP_EXPR_FLAG_RESTRICTED)
&& prov->restricted) {
@@ -1791,7 +1803,7 @@ static int expr_lookup_not_found(ap_expr_lookup_parms *parms)
type = "Binary operator";
break;
default:
- *parms->err = "Inavalid expression type in expr_lookup";
+ *parms->err = "Invalid expression type in expr_lookup";
return !OK;
}
if ( parms->type == AP_EXPR_FUNC_OP_UNARY
@@ -1806,10 +1818,7 @@ static int expr_lookup_not_found(ap_expr_lookup_parms *parms)
static int ap_expr_post_config(apr_pool_t *pconf, apr_pool_t *plog,
apr_pool_t *ptemp, server_rec *s)
{
- is_https = APR_RETRIEVE_OPTIONAL_FN(ssl_is_https);
is_http2 = APR_RETRIEVE_OPTIONAL_FN(http2_is_h2);
- apr_pool_cleanup_register(pconf, &is_https, ap_pool_cleanup_set_null,
- apr_pool_cleanup_null);
return OK;
}
diff --git a/server/util_expr_parse.c b/server/util_expr_parse.c
index bcf0173..ac4a323 100644
--- a/server/util_expr_parse.c
+++ b/server/util_expr_parse.c
@@ -1326,6 +1326,8 @@ YYSTYPE yylval;
goto yysetstate;
+ /* TODO: comppiler warning that this is unused, and it seems to */
+ (void)yynerrs;
/*------------------------------------------------------------.
| yynewstate -- Push a new state, which is found in yystate. |
`------------------------------------------------------------*/
diff --git a/server/util_filter.c b/server/util_filter.c
index 51e24f6..664f649 100644
--- a/server/util_filter.c
+++ b/server/util_filter.c
@@ -565,8 +565,9 @@ AP_DECLARE(apr_status_t) ap_pass_brigade(ap_filter_t *next,
apr_bucket_brigade *bb)
{
if (next) {
- apr_bucket *e;
- if ((e = APR_BRIGADE_LAST(bb)) && APR_BUCKET_IS_EOS(e) && next->r) {
+ apr_bucket *e = APR_BRIGADE_LAST(bb);
+
+ if (e != APR_BRIGADE_SENTINEL(bb) && APR_BUCKET_IS_EOS(e) && next->r) {
/* This is only safe because HTTP_HEADER filter is always in
* the filter stack. This ensures that there is ALWAYS a
* request-based filter that we can attach this to. If the
diff --git a/server/util_md5.c b/server/util_md5.c
index 148c60c..bba3b88 100644
--- a/server/util_md5.c
+++ b/server/util_md5.c
@@ -54,7 +54,7 @@ AP_DECLARE(char *) ap_md5_binary(apr_pool_t *p, const unsigned char *buf, int le
{
apr_md5_ctx_t my_md5;
unsigned char hash[APR_MD5_DIGESTSIZE];
- char result[2 * APR_MD5_DIGESTSIZE + 1];
+ char *result;
/*
* Take the MD5 hash of the string argument.
@@ -67,9 +67,9 @@ AP_DECLARE(char *) ap_md5_binary(apr_pool_t *p, const unsigned char *buf, int le
apr_md5_update(&my_md5, buf, (unsigned int)length);
apr_md5_final(hash, &my_md5);
- ap_bin2hex(hash, APR_MD5_DIGESTSIZE, result);
-
- return apr_pstrndup(p, result, APR_MD5_DIGESTSIZE*2);
+ result = apr_palloc(p, 2 * APR_MD5_DIGESTSIZE + 1);
+ ap_bin2hex(hash, APR_MD5_DIGESTSIZE, result); /* sets final '\0' */
+ return result;
}
AP_DECLARE(char *) ap_md5(apr_pool_t *p, const unsigned char *string)
diff --git a/server/util_pcre.c b/server/util_pcre.c
index f2cb1bb..0a9dc50 100644
--- a/server/util_pcre.c
+++ b/server/util_pcre.c
@@ -55,7 +55,21 @@ POSSIBILITY OF SUCH DAMAGE.
#include "httpd.h"
#include "apr_strings.h"
#include "apr_tables.h"
+#include "apr_thread_proc.h"
+
+#ifdef HAVE_PCRE2
+#define PCRE2_CODE_UNIT_WIDTH 8
+#include "pcre2.h"
+#define PCREn(x) PCRE2_ ## x
+#else
#include "pcre.h"
+#define PCREn(x) PCRE_ ## x
+#endif
+
+/* PCRE_DUPNAMES is only present since version 6.7 of PCRE */
+#if !defined(PCRE_DUPNAMES) && !defined(HAVE_PCRE2)
+#error PCRE Version 6.7 or later required!
+#else
#define APR_WANT_STRFUNC
#include "apr_want.h"
@@ -76,6 +90,26 @@ static const char *const pstring[] = {
"match failed" /* AP_REG_NOMATCH */
};
+AP_DECLARE(const char *) ap_pcre_version_string(int which)
+{
+#ifdef HAVE_PCRE2
+ static char buf[80];
+#endif
+ switch (which) {
+ case AP_REG_PCRE_COMPILED:
+ return APR_STRINGIFY(PCREn(MAJOR)) "." APR_STRINGIFY(PCREn(MINOR)) " " APR_STRINGIFY(PCREn(DATE));
+ case AP_REG_PCRE_LOADED:
+#ifdef HAVE_PCRE2
+ pcre2_config(PCRE2_CONFIG_VERSION, buf);
+ return buf;
+#else
+ return pcre_version();
+#endif
+ default:
+ return "Unknown";
+ }
+}
+
AP_DECLARE(apr_size_t) ap_regerror(int errcode, const ap_regex_t *preg,
char *errbuf, apr_size_t errbuf_size)
{
@@ -110,7 +144,11 @@ AP_DECLARE(apr_size_t) ap_regerror(int errcode, const ap_regex_t *preg,
AP_DECLARE(void) ap_regfree(ap_regex_t *preg)
{
+#ifdef HAVE_PCRE2
+ pcre2_code_free(preg->re_pcre);
+#else
(pcre_free)(preg->re_pcre);
+#endif
}
@@ -120,7 +158,7 @@ AP_DECLARE(void) ap_regfree(ap_regex_t *preg)
* Compile a regular expression *
*************************************************/
-static int default_cflags = AP_REG_DOLLAR_ENDONLY;
+static int default_cflags = AP_REG_DEFAULT;
AP_DECLARE(int) ap_regcomp_get_default_cflags(void)
{
@@ -163,37 +201,53 @@ AP_DECLARE(int) ap_regcomp_default_cflag_by_name(const char *name)
*/
AP_DECLARE(int) ap_regcomp(ap_regex_t * preg, const char *pattern, int cflags)
{
+#ifdef HAVE_PCRE2
+ uint32_t capcount;
+ size_t erroffset;
+#else
const char *errorptr;
int erroffset;
+#endif
int errcode = 0;
- int options = PCRE_DUPNAMES;
+ int options = PCREn(DUPNAMES);
+
+ if ((cflags & AP_REG_NO_DEFAULT) == 0)
+ cflags |= default_cflags;
- cflags |= default_cflags;
if ((cflags & AP_REG_ICASE) != 0)
- options |= PCRE_CASELESS;
+ options |= PCREn(CASELESS);
if ((cflags & AP_REG_NEWLINE) != 0)
- options |= PCRE_MULTILINE;
+ options |= PCREn(MULTILINE);
if ((cflags & AP_REG_DOTALL) != 0)
- options |= PCRE_DOTALL;
+ options |= PCREn(DOTALL);
if ((cflags & AP_REG_DOLLAR_ENDONLY) != 0)
- options |= PCRE_DOLLAR_ENDONLY;
+ options |= PCREn(DOLLAR_ENDONLY);
+
+#ifdef HAVE_PCRE2
+ preg->re_pcre = pcre2_compile((const unsigned char *)pattern,
+ PCRE2_ZERO_TERMINATED, options, &errcode,
+ &erroffset, NULL);
+#else
+ preg->re_pcre = pcre_compile2(pattern, options, &errcode,
+ &errorptr, &erroffset, NULL);
+#endif
- preg->re_pcre =
- pcre_compile2(pattern, options, &errcode, &errorptr, &erroffset, NULL);
preg->re_erroffset = erroffset;
-
if (preg->re_pcre == NULL) {
- /*
- * There doesn't seem to be constants defined for compile time error
- * codes. 21 is "failed to get memory" according to pcreapi(3).
- */
+ /* Internal ERR21 is "failed to get memory" according to pcreapi(3) */
if (errcode == 21)
return AP_REG_ESPACE;
return AP_REG_INVARG;
}
+#ifdef HAVE_PCRE2
+ pcre2_pattern_info((const pcre2_code *)preg->re_pcre,
+ PCRE2_INFO_CAPTURECOUNT, &capcount);
+ preg->re_nsub = capcount;
+#else
pcre_fullinfo((const pcre *)preg->re_pcre, NULL,
- PCRE_INFO_CAPTURECOUNT, &(preg->re_nsub));
+ PCRE_INFO_CAPTURECOUNT, &(preg->re_nsub));
+#endif
return 0;
}
@@ -210,7 +264,134 @@ AP_DECLARE(int) ap_regcomp(ap_regex_t * preg, const char *pattern, int cflags)
* ints. However, if the number of possible capturing brackets is small, use a
* block of store on the stack, to reduce the use of malloc/free. The threshold
* is in a macro that can be changed at configure time.
+ * Yet more unfortunately, PCRE2 wants an opaque context by providing the API
+ * to allocate and free it, so to minimize these calls we maintain one opaque
+ * context per thread (in Thread Local Storage, TLS) grown as needed, and while
+ * at it we do the same for PCRE1 ints vectors. Note that this requires a fast
+ * TLS mechanism to be worth it, which is the case of apr_thread_data_get/set()
+ * from/to ap_thread_current() when AP_HAS_THREAD_LOCAL; otherwise we'll do
+ * the allocation and freeing for each ap_regexec().
*/
+
+#ifdef HAVE_PCRE2
+typedef pcre2_match_data* match_data_pt;
+typedef size_t* match_vector_pt;
+#else
+typedef int* match_data_pt;
+typedef int* match_vector_pt;
+#endif
+
+static APR_INLINE
+match_data_pt alloc_match_data(apr_size_t size,
+ match_vector_pt small_vector)
+{
+ match_data_pt data;
+
+#ifdef HAVE_PCRE2
+ data = pcre2_match_data_create(size, NULL);
+#else
+ if (size > POSIX_MALLOC_THRESHOLD) {
+ data = malloc(size * sizeof(int) * 3);
+ }
+ else {
+ data = small_vector;
+ }
+#endif
+
+ return data;
+}
+
+static APR_INLINE
+void free_match_data(match_data_pt data, apr_size_t size)
+{
+#ifdef HAVE_PCRE2
+ pcre2_match_data_free(data);
+#else
+ if (size > POSIX_MALLOC_THRESHOLD) {
+ free(data);
+ }
+#endif
+}
+
+#if AP_HAS_THREAD_LOCAL && !defined(APREG_NO_THREAD_LOCAL)
+
+struct apreg_tls {
+ match_data_pt data;
+ apr_size_t size;
+};
+
+#ifdef HAVE_PCRE2
+static apr_status_t apreg_tls_cleanup(void *arg)
+{
+ struct apreg_tls *tls = arg;
+ pcre2_match_data_free(tls->data); /* NULL safe */
+ return APR_SUCCESS;
+}
+#endif
+
+static match_data_pt get_match_data(apr_size_t size,
+ match_vector_pt small_vector,
+ int *to_free)
+{
+ apr_thread_t *current;
+ struct apreg_tls *tls = NULL;
+
+ /* Even though AP_HAS_THREAD_LOCAL, we may still be called by a
+ * native/non-apr thread, let's fall back to alloc/free in this case.
+ */
+ current = ap_thread_current();
+ if (!current) {
+ *to_free = 1;
+ return alloc_match_data(size, small_vector);
+ }
+
+ apr_thread_data_get((void **)&tls, "apreg", current);
+ if (!tls || tls->size < size) {
+ apr_pool_t *tp = apr_thread_pool_get(current);
+ if (!tls) {
+ tls = apr_pcalloc(tp, sizeof(*tls));
+#ifdef HAVE_PCRE2
+ apr_thread_data_set(tls, "apreg", apreg_tls_cleanup, current);
+#else
+ apr_thread_data_set(tls, "apreg", NULL, current);
+#endif
+ }
+
+ tls->size *= 2;
+ if (tls->size < size) {
+ tls->size = size;
+ if (tls->size < POSIX_MALLOC_THRESHOLD) {
+ tls->size = POSIX_MALLOC_THRESHOLD;
+ }
+ }
+
+#ifdef HAVE_PCRE2
+ pcre2_match_data_free(tls->data); /* NULL safe */
+ tls->data = pcre2_match_data_create(tls->size, NULL);
+ if (!tls->data) {
+ tls->size = 0;
+ return NULL;
+ }
+#else
+ tls->data = apr_palloc(tp, tls->size * sizeof(int) * 3);
+#endif
+ }
+
+ return tls->data;
+}
+
+#else /* AP_HAS_THREAD_LOCAL && !defined(APREG_NO_THREAD_LOCAL) */
+
+static APR_INLINE match_data_pt get_match_data(apr_size_t size,
+ match_vector_pt small_vector,
+ int *to_free)
+{
+ *to_free = 1;
+ return alloc_match_data(size, small_vector);
+}
+
+#endif /* AP_HAS_THREAD_LOCAL && !defined(APREG_NO_THREAD_LOCAL) */
+
AP_DECLARE(int) ap_regexec(const ap_regex_t *preg, const char *string,
apr_size_t nmatch, ap_regmatch_t *pmatch,
int eflags)
@@ -224,75 +405,84 @@ AP_DECLARE(int) ap_regexec_len(const ap_regex_t *preg, const char *buff,
ap_regmatch_t *pmatch, int eflags)
{
int rc;
- int options = 0;
- int *ovector = NULL;
- int small_ovector[POSIX_MALLOC_THRESHOLD * 3];
- int allocated_ovector = 0;
-
- if ((eflags & AP_REG_NOTBOL) != 0)
- options |= PCRE_NOTBOL;
- if ((eflags & AP_REG_NOTEOL) != 0)
- options |= PCRE_NOTEOL;
-
- ((ap_regex_t *)preg)->re_erroffset = (apr_size_t)(-1); /* Only has meaning after compile */
+ int options = 0, to_free = 0;
+ match_vector_pt ovector = NULL;
+ apr_size_t ncaps = (apr_size_t)preg->re_nsub + 1;
+#ifdef HAVE_PCRE2
+ match_data_pt data = get_match_data(ncaps, NULL, &to_free);
+#else
+ int small_vector[POSIX_MALLOC_THRESHOLD * 3];
+ match_data_pt data = get_match_data(ncaps, small_vector, &to_free);
+#endif
- if (nmatch > 0) {
- if (nmatch <= POSIX_MALLOC_THRESHOLD) {
- ovector = &(small_ovector[0]);
- }
- else {
- ovector = (int *)malloc(sizeof(int) * nmatch * 3);
- if (ovector == NULL)
- return AP_REG_ESPACE;
- allocated_ovector = 1;
- }
+ if (!data) {
+ return AP_REG_ESPACE;
}
+ if ((eflags & AP_REG_NOTBOL) != 0)
+ options |= PCREn(NOTBOL);
+ if ((eflags & AP_REG_NOTEOL) != 0)
+ options |= PCREn(NOTEOL);
+
+#ifdef HAVE_PCRE2
+ rc = pcre2_match((const pcre2_code *)preg->re_pcre,
+ (const unsigned char *)buff, len,
+ 0, options, data, NULL);
+ ovector = pcre2_get_ovector_pointer(data);
+#else
+ ovector = data;
rc = pcre_exec((const pcre *)preg->re_pcre, NULL, buff, (int)len,
- 0, options, ovector, nmatch * 3);
-
- if (rc == 0)
- rc = nmatch; /* All captured slots were filled in */
+ 0, options, ovector, ncaps * 3);
+#endif
if (rc >= 0) {
- apr_size_t i;
- for (i = 0; i < (apr_size_t)rc; i++) {
+ apr_size_t n = rc, i;
+ if (n == 0 || n > nmatch)
+ rc = n = nmatch; /* All capture slots were filled in */
+ for (i = 0; i < n; i++) {
pmatch[i].rm_so = ovector[i * 2];
pmatch[i].rm_eo = ovector[i * 2 + 1];
}
- if (allocated_ovector)
- free(ovector);
for (; i < nmatch; i++)
pmatch[i].rm_so = pmatch[i].rm_eo = -1;
+ if (to_free) {
+ free_match_data(data, ncaps);
+ }
return 0;
}
-
else {
- if (allocated_ovector)
- free(ovector);
+ if (to_free) {
+ free_match_data(data, ncaps);
+ }
+#ifdef HAVE_PCRE2
+ if (rc <= PCRE2_ERROR_UTF8_ERR1 && rc >= PCRE2_ERROR_UTF8_ERR21)
+ return AP_REG_INVARG;
+#endif
switch (rc) {
- case PCRE_ERROR_NOMATCH:
+ case PCREn(ERROR_NOMATCH):
return AP_REG_NOMATCH;
- case PCRE_ERROR_NULL:
+ case PCREn(ERROR_NULL):
return AP_REG_INVARG;
- case PCRE_ERROR_BADOPTION:
+ case PCREn(ERROR_BADOPTION):
return AP_REG_INVARG;
- case PCRE_ERROR_BADMAGIC:
+ case PCREn(ERROR_BADMAGIC):
return AP_REG_INVARG;
- case PCRE_ERROR_UNKNOWN_NODE:
- return AP_REG_ASSERT;
- case PCRE_ERROR_NOMEMORY:
+ case PCREn(ERROR_NOMEMORY):
return AP_REG_ESPACE;
-#ifdef PCRE_ERROR_MATCHLIMIT
- case PCRE_ERROR_MATCHLIMIT:
+#if defined(HAVE_PCRE2) || defined(PCRE_ERROR_MATCHLIMIT)
+ case PCREn(ERROR_MATCHLIMIT):
return AP_REG_ESPACE;
#endif
-#ifdef PCRE_ERROR_BADUTF8
- case PCRE_ERROR_BADUTF8:
+#if defined(PCRE_ERROR_UNKNOWN_NODE)
+ case PCRE_ERROR_UNKNOWN_NODE:
+ return AP_REG_ASSERT;
+#endif
+#if defined(PCRE_ERROR_BADUTF8)
+ case PCREn(ERROR_BADUTF8):
return AP_REG_INVARG;
#endif
-#ifdef PCRE_ERROR_BADUTF8_OFFSET
- case PCRE_ERROR_BADUTF8_OFFSET:
+#if defined(PCRE_ERROR_BADUTF8_OFFSET)
+ case PCREn(ERROR_BADUTF8_OFFSET):
return AP_REG_INVARG;
#endif
default:
@@ -305,17 +495,29 @@ AP_DECLARE(int) ap_regname(const ap_regex_t *preg,
apr_array_header_t *names, const char *prefix,
int upper)
{
+ char *nametable;
+
+#ifdef HAVE_PCRE2
+ uint32_t namecount;
+ uint32_t nameentrysize;
+ uint32_t i;
+ pcre2_pattern_info((const pcre2_code *)preg->re_pcre,
+ PCRE2_INFO_NAMECOUNT, &namecount);
+ pcre2_pattern_info((const pcre2_code *)preg->re_pcre,
+ PCRE2_INFO_NAMEENTRYSIZE, &nameentrysize);
+ pcre2_pattern_info((const pcre2_code *)preg->re_pcre,
+ PCRE2_INFO_NAMETABLE, &nametable);
+#else
int namecount;
int nameentrysize;
int i;
- char *nametable;
-
pcre_fullinfo((const pcre *)preg->re_pcre, NULL,
- PCRE_INFO_NAMECOUNT, &namecount);
+ PCRE_INFO_NAMECOUNT, &namecount);
pcre_fullinfo((const pcre *)preg->re_pcre, NULL,
- PCRE_INFO_NAMEENTRYSIZE, &nameentrysize);
+ PCRE_INFO_NAMEENTRYSIZE, &nameentrysize);
pcre_fullinfo((const pcre *)preg->re_pcre, NULL,
- PCRE_INFO_NAMETABLE, &nametable);
+ PCRE_INFO_NAMETABLE, &nametable);
+#endif
for (i = 0; i < namecount; i++) {
const char *offset = nametable + i * nameentrysize;
@@ -340,4 +542,6 @@ AP_DECLARE(int) ap_regname(const ap_regex_t *preg,
return namecount;
}
+#endif /* PCRE_DUPNAMES defined */
+
/* End of pcreposix.c */
diff --git a/server/util_regex.c b/server/util_regex.c
index 2a30d68..5405f8d 100644
--- a/server/util_regex.c
+++ b/server/util_regex.c
@@ -94,6 +94,7 @@ AP_DECLARE(ap_rxplus_t*) ap_rxplus_compile(apr_pool_t *pool,
}
/* anything after the current delimiter is flags */
+ ret->flags = ap_regcomp_get_default_cflags() & AP_REG_DOLLAR_ENDONLY;
while (*++endp) {
switch (*endp) {
case 'i': ret->flags |= AP_REG_ICASE; break;
@@ -106,7 +107,7 @@ AP_DECLARE(ap_rxplus_t*) ap_rxplus_compile(apr_pool_t *pool,
default: break; /* we should probably be stricter here */
}
}
- if (ap_regcomp(&ret->rx, rxstr, ret->flags) == 0) {
+ if (ap_regcomp(&ret->rx, rxstr, AP_REG_NO_DEFAULT | ret->flags) == 0) {
apr_pool_cleanup_register(pool, &ret->rx, rxplus_cleanup,
apr_pool_cleanup_null);
}
diff --git a/server/util_script.c b/server/util_script.c
index 599ba58..1fa4276 100644
--- a/server/util_script.c
+++ b/server/util_script.c
@@ -45,7 +45,7 @@
/*
* Various utility functions which are common to a whole lot of
* script-type extensions mechanisms, and might as well be gathered
- * in one place (if only to avoid creating inter-module dependancies
+ * in one place (if only to avoid creating inter-module dependencies
* where there don't have to be).
*/
@@ -92,9 +92,21 @@ static void add_unless_null(apr_table_t *table, const char *name, const char *va
}
}
-static void env2env(apr_table_t *table, const char *name)
+/* Sets variable @name in table @dest from r->subprocess_env if
+ * available, else from the environment, else from @fallback if
+ * non-NULL. */
+static void env2env(apr_table_t *dest, request_rec *r,
+ const char *name, const char *fallback)
{
- add_unless_null(table, name, getenv(name));
+ const char *val;
+
+ val = apr_table_get(r->subprocess_env, name);
+ if (!val)
+ val = apr_pstrdup(r->pool, getenv(name));
+ if (!val)
+ val = apr_pstrdup(r->pool, fallback);
+ if (val)
+ apr_table_addn(dest, name, val);
}
AP_DECLARE(char **) ap_create_environment(apr_pool_t *p, apr_table_t *t)
@@ -180,10 +192,10 @@ AP_DECLARE(void) ap_add_common_vars(request_rec *r)
* for no particular reason.
*/
- if (!strcasecmp(hdrs[i].key, "Content-type")) {
+ if (!ap_cstr_casecmp(hdrs[i].key, "Content-type")) {
apr_table_addn(e, "CONTENT_TYPE", hdrs[i].val);
}
- else if (!strcasecmp(hdrs[i].key, "Content-length")) {
+ else if (!ap_cstr_casecmp(hdrs[i].key, "Content-length")) {
apr_table_addn(e, "CONTENT_LENGTH", hdrs[i].val);
}
/* HTTP_PROXY collides with a popular envvar used to configure
@@ -200,8 +212,8 @@ AP_DECLARE(void) ap_add_common_vars(request_rec *r)
* in the environment with "ps -e". But, if you must...
*/
#ifndef SECURITY_HOLE_PASS_AUTHORIZATION
- else if (!strcasecmp(hdrs[i].key, "Authorization")
- || !strcasecmp(hdrs[i].key, "Proxy-Authorization")) {
+ else if (!ap_cstr_casecmp(hdrs[i].key, "Authorization")
+ || !ap_cstr_casecmp(hdrs[i].key, "Proxy-Authorization")) {
if (conf->cgi_pass_auth == AP_CGI_PASS_AUTH_ON) {
add_unless_null(e, http2env(r, hdrs[i].key), hdrs[i].val);
}
@@ -211,37 +223,29 @@ AP_DECLARE(void) ap_add_common_vars(request_rec *r)
add_unless_null(e, http2env(r, hdrs[i].key), hdrs[i].val);
}
- env_temp = apr_table_get(r->subprocess_env, "PATH");
- if (env_temp == NULL) {
- env_temp = getenv("PATH");
- }
- if (env_temp == NULL) {
- env_temp = DEFAULT_PATH;
- }
- apr_table_addn(e, "PATH", apr_pstrdup(r->pool, env_temp));
-
+ env2env(e, r, "PATH", DEFAULT_PATH);
#if defined(WIN32)
- env2env(e, "SystemRoot");
- env2env(e, "COMSPEC");
- env2env(e, "PATHEXT");
- env2env(e, "WINDIR");
+ env2env(e, r, "SystemRoot", NULL);
+ env2env(e, r, "COMSPEC", NULL);
+ env2env(e, r, "PATHEXT", NULL);
+ env2env(e, r, "WINDIR", NULL);
#elif defined(OS2)
- env2env(e, "COMSPEC");
- env2env(e, "ETC");
- env2env(e, "DPATH");
- env2env(e, "PERLLIB_PREFIX");
+ env2env(e, r, "COMSPEC", NULL);
+ env2env(e, r, "ETC", NULL);
+ env2env(e, r, "DPATH", NULL);
+ env2env(e, r, "PERLLIB_PREFIX", NULL);
#elif defined(BEOS)
- env2env(e, "LIBRARY_PATH");
+ env2env(e, r, "LIBRARY_PATH", NULL);
#elif defined(DARWIN)
- env2env(e, "DYLD_LIBRARY_PATH");
+ env2env(e, r, "DYLD_LIBRARY_PATH", NULL);
#elif defined(_AIX)
- env2env(e, "LIBPATH");
+ env2env(e, r, "LIBPATH", NULL);
#elif defined(__HPUX__)
/* HPUX PARISC 2.0W knows both, otherwise redundancy is harmless */
- env2env(e, "SHLIB_PATH");
- env2env(e, "LD_LIBRARY_PATH");
+ env2env(e, r, "SHLIB_PATH", NULL);
+ env2env(e, r, "LD_LIBRARY_PATH", NULL);
#else /* Some Unix */
- env2env(e, "LD_LIBRARY_PATH");
+ env2env(e, r, "LD_LIBRARY_PATH", NULL);
#endif
apr_table_addn(e, "SERVER_SIGNATURE", ap_psignature("", r));
@@ -620,7 +624,7 @@ AP_DECLARE(int) ap_scan_script_header_err_core_ex(request_rec *r, char *buffer,
++l;
}
- if (!strcasecmp(w, "Content-type")) {
+ if (!ap_cstr_casecmp(w, "Content-type")) {
char *tmp;
/* Nuke trailing whitespace */
@@ -638,7 +642,7 @@ AP_DECLARE(int) ap_scan_script_header_err_core_ex(request_rec *r, char *buffer,
* If the script returned a specific status, that's what
* we'll use - otherwise we assume 200 OK.
*/
- else if (!strcasecmp(w, "Status")) {
+ else if (!ap_cstr_casecmp(w, "Status")) {
r->status = cgi_status = atoi(l);
if (!ap_is_HTTP_VALID_RESPONSE(cgi_status))
/* Intentional no APLOGNO */
@@ -652,30 +656,73 @@ AP_DECLARE(int) ap_scan_script_header_err_core_ex(request_rec *r, char *buffer,
apr_filepath_name_get(r->filename), l);
r->status_line = apr_pstrdup(r->pool, l);
}
- else if (!strcasecmp(w, "Location")) {
+ else if (!ap_cstr_casecmp(w, "Location")) {
apr_table_set(r->headers_out, w, l);
}
- else if (!strcasecmp(w, "Content-Length")) {
+ else if (!ap_cstr_casecmp(w, "Content-Length")) {
apr_table_set(r->headers_out, w, l);
}
- else if (!strcasecmp(w, "Content-Range")) {
+ else if (!ap_cstr_casecmp(w, "Content-Range")) {
apr_table_set(r->headers_out, w, l);
}
- else if (!strcasecmp(w, "Transfer-Encoding")) {
+ else if (!ap_cstr_casecmp(w, "Transfer-Encoding")) {
apr_table_set(r->headers_out, w, l);
}
- else if (!strcasecmp(w, "ETag")) {
+ else if (!ap_cstr_casecmp(w, "ETag")) {
apr_table_set(r->headers_out, w, l);
}
/*
* If the script gave us a Last-Modified header, we can't just
- * pass it on blindly because of restrictions on future values.
+ * pass it on blindly because of restrictions on future or invalid values.
*/
- else if (!strcasecmp(w, "Last-Modified")) {
- ap_update_mtime(r, apr_date_parse_http(l));
- ap_set_last_modified(r);
+ else if (!ap_cstr_casecmp(w, "Last-Modified")) {
+ apr_time_t parsed_date = apr_date_parse_http(l);
+ if (parsed_date != APR_DATE_BAD) {
+ ap_update_mtime(r, parsed_date);
+ ap_set_last_modified(r);
+ if (APLOGrtrace1(r)) {
+ apr_time_t last_modified_date = apr_date_parse_http(apr_table_get(r->headers_out,
+ "Last-Modified"));
+ /*
+ * A Last-Modified header value coming from a (F)CGI source
+ * is considered HTTP input so we assume the GMT timezone.
+ * The following logs should inform the admin about violations
+ * and related actions taken by httpd.
+ * The apr_date_parse_rfc function is 'timezone aware'
+ * and it will be used to generate a more informative set of logs
+ * (we don't use it as a replacement of apr_date_parse_http
+ * for the aforementioned reason).
+ */
+ apr_time_t parsed_date_tz_aware = apr_date_parse_rfc(l);
+
+ /*
+ * The parsed Last-Modified header datestring has been replaced by httpd.
+ */
+ if (parsed_date > last_modified_date) {
+ ap_log_rerror(SCRIPT_LOG_MARK, APLOG_TRACE1, 0, r,
+ "The Last-Modified header value %s (%s) "
+ "has been replaced with '%s'", l,
+ parsed_date != parsed_date_tz_aware ? "not in GMT"
+ : "in the future",
+ apr_table_get(r->headers_out, "Last-Modified"));
+ /*
+ * Last-Modified header datestring not in GMT and not considered in the future
+ * by httpd (like now() + 1 hour in the PST timezone). No action is taken but
+ * the admin is warned about the violation.
+ */
+ } else if (parsed_date != parsed_date_tz_aware) {
+ ap_log_rerror(SCRIPT_LOG_MARK, APLOG_TRACE1, 0, r,
+ "The Last-Modified header value is not set "
+ "within the GMT timezone (as required)");
+ }
+ }
+ }
+ else {
+ ap_log_rerror(SCRIPT_LOG_MARK, APLOG_INFO, 0, r, APLOGNO(10247)
+ "Ignored invalid header value: Last-Modified: '%s'", l);
+ }
}
- else if (!strcasecmp(w, "Set-Cookie")) {
+ else if (!ap_cstr_casecmp(w, "Set-Cookie")) {
apr_table_add(cookie_table, w, l);
}
else {
diff --git a/server/util_time.c b/server/util_time.c
index 3632d0d..299b53c 100644
--- a/server/util_time.c
+++ b/server/util_time.c
@@ -22,9 +22,11 @@
* */
#define AP_CTIME_USEC_LENGTH 7
-/* Length of ISO 8601 date/time */
+/* Length of ISO 8601 date/time (including trailing '\0') */
#define AP_CTIME_COMPACT_LEN 20
+/* Length of timezone offset from GMT ([+-]hhmm) plus leading space */
+#define AP_CTIME_GMTOFF_LEN 6
/* Cache for exploded values of recent timestamps
*/
@@ -181,7 +183,13 @@ AP_DECLARE(apr_status_t) ap_recent_ctime_ex(char *date_str, apr_time_t t,
needed += AP_CTIME_USEC_LENGTH;
}
- /* Check the provided buffer length */
+ if (option & AP_CTIME_OPTION_GMTOFF) {
+ needed += AP_CTIME_GMTOFF_LEN;
+ }
+
+ /* Check the provided buffer length (note: above AP_CTIME_COMPACT_LEN
+ * and APR_CTIME_LEN include the trailing '\0'; so does 'needed' then).
+ */
if (len && *len >= needed) {
*len = needed;
}
@@ -193,9 +201,10 @@ AP_DECLARE(apr_status_t) ap_recent_ctime_ex(char *date_str, apr_time_t t,
}
/* example without options: "Wed Jun 30 21:49:08 1993" */
- /* 123456789012345678901234 */
/* example for compact format: "1993-06-30 21:49:08" */
- /* 1234567890123456789 */
+ /* example for compact+usec+gmtoff format:
+ * "1993-06-30 22:49:08.123456 +0100"
+ */
ap_explode_recent_localtime(&xt, t);
real_year = 1900 + xt.tm_year;
@@ -249,7 +258,23 @@ AP_DECLARE(apr_status_t) ap_recent_ctime_ex(char *date_str, apr_time_t t,
*date_str++ = real_year % 100 / 10 + '0';
*date_str++ = real_year % 10 + '0';
}
- *date_str++ = 0;
+ if (option & AP_CTIME_OPTION_GMTOFF) {
+ int off = xt.tm_gmtoff, off_hh, off_mm;
+ char sign = '+';
+ if (off < 0) {
+ off = -off;
+ sign = '-';
+ }
+ off_hh = off / 3600;
+ off_mm = off % 3600 / 60;
+ *date_str++ = ' ';
+ *date_str++ = sign;
+ *date_str++ = off_hh / 10 + '0';
+ *date_str++ = off_hh % 10 + '0';
+ *date_str++ = off_mm / 10 + '0';
+ *date_str++ = off_mm % 10 + '0';
+ }
+ *date_str = 0;
return APR_SUCCESS;
}
diff --git a/server/util_xml.c b/server/util_xml.c
index 4845194..22806fa 100644
--- a/server/util_xml.c
+++ b/server/util_xml.c
@@ -85,7 +85,7 @@ AP_DECLARE(int) ap_xml_parse_input(request_rec * r, apr_xml_doc **pdoc)
}
total_read += len;
- if (limit_xml_body && total_read > limit_xml_body) {
+ if (total_read > limit_xml_body) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(00539)
"XML request body is larger than the configured "
"limit of %lu", (unsigned long)limit_xml_body);
diff --git a/server/vhost.c b/server/vhost.c
index b23b2dd..489c141 100644
--- a/server/vhost.c
+++ b/server/vhost.c
@@ -34,6 +34,7 @@
#include "http_vhost.h"
#include "http_protocol.h"
#include "http_core.h"
+#include "http_main.h"
#if APR_HAVE_ARPA_INET_H
#include <arpa/inet.h>
@@ -973,7 +974,13 @@ AP_DECLARE(int) ap_matches_request_vhost(request_rec *r, const char *host,
}
-static void check_hostalias(request_rec *r)
+/*
+ * Updates r->server from ServerName/ServerAlias. Per the interaction
+ * of ip and name-based vhosts, it only looks in the best match from the
+ * connection-level ip-based matching.
+ * Returns HTTP_BAD_REQUEST if there was no match.
+ */
+static int update_server_from_aliases(request_rec *r)
{
/*
* Even if the request has a Host: header containing a port we ignore
@@ -1031,7 +1038,6 @@ static void check_hostalias(request_rec *r)
goto found;
}
}
- last_s = s;
/* Fallback: does it match the virthost from the sar? */
if (!strcasecmp(host, sar->virthost)) {
@@ -1040,6 +1046,8 @@ static void check_hostalias(request_rec *r)
virthost_s = s;
}
}
+
+ last_s = s;
}
/* If ServerName and ServerAlias check failed, we end up here. If it
@@ -1050,11 +1058,18 @@ static void check_hostalias(request_rec *r)
goto found;
}
- return;
+ if (!r->connection->vhost_lookup_data) {
+ if (matches_aliases(r->server, host)) {
+ s = r->server;
+ goto found;
+ }
+ }
+ return HTTP_BAD_REQUEST;
found:
/* s is the first matching server, we're done */
r->server = s;
+ return HTTP_OK;
}
@@ -1071,7 +1086,7 @@ static void check_serverpath(request_rec *r)
* This is in conjunction with the ServerPath code in http_core, so we
* get the right host attached to a non- Host-sending request.
*
- * See the comment in check_hostalias about how each vhost can be
+ * See the comment in update_server_from_aliases about how each vhost can be
* listed multiple times.
*/
@@ -1135,10 +1150,16 @@ static APR_INLINE const char *construct_host_header(request_rec *r,
AP_DECLARE(void) ap_update_vhost_from_headers(request_rec *r)
{
+ ap_update_vhost_from_headers_ex(r, 0);
+}
+
+AP_DECLARE(int) ap_update_vhost_from_headers_ex(request_rec *r, int require_match)
+{
core_server_config *conf = ap_get_core_module_config(r->server->module_config);
const char *host_header = apr_table_get(r->headers_in, "Host");
int is_v6literal = 0;
int have_hostname_from_url = 0;
+ int rc = HTTP_OK;
if (r->hostname) {
/*
@@ -1151,8 +1172,8 @@ AP_DECLARE(void) ap_update_vhost_from_headers(request_rec *r)
else if (host_header != NULL) {
is_v6literal = fix_hostname(r, host_header, conf->http_conformance);
}
- if (r->status != HTTP_OK)
- return;
+ if (!require_match && r->status != HTTP_OK)
+ return HTTP_OK;
if (conf->http_conformance != AP_HTTP_CONFORMANCE_UNSAFE) {
/*
@@ -1173,10 +1194,16 @@ AP_DECLARE(void) ap_update_vhost_from_headers(request_rec *r)
/* check if we tucked away a name_chain */
if (r->connection->vhost_lookup_data) {
if (r->hostname)
- check_hostalias(r);
+ rc = update_server_from_aliases(r);
else
check_serverpath(r);
}
+ else if (require_match && r->hostname) {
+ /* check the base server config */
+ rc = update_server_from_aliases(r);
+ }
+
+ return rc;
}
/**