/* ====================================================================
* The Apache Software License, Version 1.1
*
- * Copyright (c) 2000-2002 The Apache Software Foundation. All rights
+ * Copyright (c) 2000-2003 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
#include "http_vhost.h"
#include "http_main.h" /* For the default_handler below... */
#include "http_log.h"
-#include "rfc1413.h"
#include "util_md5.h"
#include "http_connection.h"
#include "apr_buckets.h"
conf->use_canonical_name = USE_CANONICAL_NAME_UNSET;
conf->hostname_lookups = HOSTNAME_LOOKUP_UNSET;
- conf->do_rfc1413 = DEFAULT_RFC1413 | 2; /* set bit 1 to indicate default */
conf->satisfy = SATISFY_NOSPEC;
#ifdef RLIMIT_CPU
conf->etag_remove = ETAG_UNSET;
conf->enable_mmap = ENABLE_MMAP_UNSET;
+ conf->enable_sendfile = ENABLE_SENDFILE_UNSET;
+ conf->allow_encoded_slashes = 0;
return (void *)conf;
}
conf->hostname_lookups = new->hostname_lookups;
}
- if ((new->do_rfc1413 & 2) == 0) {
- conf->do_rfc1413 = new->do_rfc1413;
- }
-
if ((new->content_md5 & 2) == 0) {
conf->content_md5 = new->content_md5;
}
conf->enable_mmap = new->enable_mmap;
}
+ if (new->enable_sendfile != ENABLE_SENDFILE_UNSET) {
+ conf->enable_sendfile = new->enable_sendfile;
+ }
+
+ conf->allow_encoded_slashes = new->allow_encoded_slashes;
+
return (void*)conf;
}
conf->sec_dir = apr_array_make(a, 40, sizeof(ap_conf_vector_t *));
conf->sec_url = apr_array_make(a, 40, sizeof(ap_conf_vector_t *));
+ /* recursion stopper */
+ conf->redirect_limit = 0; /* 0 == unset */
+ conf->subreq_limit = 0;
+
return (void *)conf;
}
conf->sec_dir = apr_array_append(p, base->sec_dir, virt->sec_dir);
conf->sec_url = apr_array_append(p, base->sec_url, virt->sec_url);
+ conf->redirect_limit = virt->redirect_limit
+ ? virt->redirect_limit
+ : base->redirect_limit;
+
+ conf->subreq_limit = virt->subreq_limit
+ ? virt->subreq_limit
+ : base->subreq_limit;
+
return conf;
}
}
}
+/*
+ * Optional function coming from mod_ident, used for looking up ident user
+ */
+static APR_OPTIONAL_FN_TYPE(ap_ident_lookup) *ident_lookup;
+
AP_DECLARE(const char *) ap_get_remote_logname(request_rec *r)
{
- core_dir_config *dir_conf;
-
if (r->connection->remote_logname != NULL) {
return r->connection->remote_logname;
}
- /* If we haven't checked the identity, and we want to */
- dir_conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
- &core_module);
-
- if (dir_conf->do_rfc1413 & 1) {
- return ap_rfc1413(r->connection, r->server);
- }
- else {
- return NULL;
+ if (ident_lookup) {
+ return ident_lookup(r);
}
+
+ return NULL;
}
/* There are two options regarding what the "name" of a server is. The
return r->server->server_hostname;
}
+/*
+ * Get the current server name from the request for the purposes
+ * of using in a URL. If the server name is an IPv6 literal
+ * address, it will be returned in URL format (e.g., "[fe80::1]").
+ */
+static const char *get_server_name_for_url(request_rec *r)
+{
+ const char *plain_server_name = ap_get_server_name(r);
+
+#if APR_HAVE_IPV6
+ if (ap_strchr_c(plain_server_name, ':')) { /* IPv6 literal? */
+ return apr_psprintf(r->pool, "[%s]", plain_server_name);
+ }
+#endif
+ return plain_server_name;
+}
+
AP_DECLARE(apr_port_t) ap_get_server_port(const request_rec *r)
{
apr_port_t port;
request_rec *r)
{
unsigned port = ap_get_server_port(r);
- const char *host = ap_get_server_name(r);
+ const char *host = get_server_name_for_url(r);
if (ap_is_default_port(port, r)) {
return apr_pstrcat(p, ap_http_method(r), "://", host, uri, NULL);
return err;
}
+ /* Make it absolute, relative to ServerRoot */
+ arg = ap_server_root_relative(cmd->pool, arg);
+
/* TODO: ap_configtestonly && ap_docrootcheck && */
- /* XXX Shouldn't this be relative to ServerRoot ??? */
if (apr_filepath_merge((char**)&conf->ap_document_root, NULL, arg,
APR_FILEPATH_TRUENAME, cmd->pool) != APR_SUCCESS
|| !ap_is_directory(cmd->pool, arg)) {
return err;
}
+ /* Throw a warning if we're in <Location> or <Files> */
+ if (ap_check_cmd_context(cmd, NOT_IN_LOCATION | NOT_IN_FILES)) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server,
+ "Useless use of AllowOverride in line %d.",
+ cmd->directive->line_num);
+ }
+
d->override = OR_NONE;
while (l[0]) {
w = ap_getword_conf(cmd->pool, &l);
return NULL;
}
+static const char *set_enable_sendfile(cmd_parms *cmd, void *d_,
+ const char *arg)
+{
+ core_dir_config *d = d_;
+ const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
+
+ if (err != NULL) {
+ return err;
+ }
+
+ if (strcasecmp(arg, "on") == 0) {
+ d->enable_sendfile = ENABLE_SENDFILE_ON;
+ }
+ else if (strcasecmp(arg, "off") == 0) {
+ d->enable_sendfile = ENABLE_SENDFILE_OFF;
+ }
+ else {
+ return "parameter must be 'on' or 'off'";
+ }
+
+ return NULL;
+}
+
static const char *satisfy(cmd_parms *cmd, void *c_, const char *arg)
{
core_dir_config *c = c_;
/*
* Ensure that the pathname is canonical, and append the trailing /
*/
- if (apr_filepath_merge(&newpath, NULL, cmd->path,
- APR_FILEPATH_TRUENAME, cmd->pool) != APR_SUCCESS) {
+ apr_status_t rv = apr_filepath_merge(&newpath, NULL, cmd->path,
+ APR_FILEPATH_TRUENAME, cmd->pool);
+ if (rv != APR_SUCCESS && rv != APR_EPATHWILD) {
return apr_pstrcat(cmd->pool, "<Directory \"", cmd->path,
"\"> path is invalid.", NULL);
}
return NULL;
}
-static const char *set_idcheck(cmd_parms *cmd, void *d_, int arg)
+static const char *set_allow2f(cmd_parms *cmd, void *d_, int arg)
{
core_dir_config *d = d_;
const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
return err;
}
- d->do_rfc1413 = arg != 0;
+ d->allow_encoded_slashes = arg != 0;
return NULL;
}
{
ap_directive_t *conftree = NULL;
const char* conffile = ap_server_root_relative(cmd->pool, name);
-
+
if (!conffile) {
return apr_pstrcat(cmd->pool, "Invalid Include path ",
name, NULL);
apr_snprintf(sport, sizeof sport, "%u", (unsigned) ap_get_server_port(r));
if (conf->server_signature == srv_sig_withmail) {
- return apr_pstrcat(r->pool, prefix, "<address>" AP_SERVER_BASEVERSION
+ return apr_pstrcat(r->pool, prefix, "<address>",
+ ap_get_server_version(),
" Server at <a href=\"mailto:",
r->server->server_admin, "\">",
- ap_get_server_name(r), "</a> Port ", sport,
+ ap_escape_html(r->pool, ap_get_server_name(r)),
+ "</a> Port ", sport,
"</address>\n", NULL);
}
- return apr_pstrcat(r->pool, prefix, "<address>" AP_SERVER_BASEVERSION
- " Server at ", ap_get_server_name(r), " Port ", sport,
+ return apr_pstrcat(r->pool, prefix, "<address>", ap_get_server_version(),
+ " Server at ",
+ ap_escape_html(r->pool, ap_get_server_name(r)),
+ " Port ", sport,
"</address>\n", NULL);
}
return NULL;
}
-#ifdef _OSD_POSIX /* BS2000 Logon Passwd file */
-static const char *set_bs2000_account(cmd_parms *cmd, void *dummy, char *name)
-{
- const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
- if (err != NULL) {
- return err;
- }
-
- return os_set_account(cmd->pool, name);
-}
-#endif /*_OSD_POSIX*/
-
/*
* Handle a request to include the server's OS platform in the Server
* response header field (the ServerTokens directive). Unfortunately
static int version_locked = 0;
enum server_token_type {
- SrvTk_MIN, /* eg: Apache/1.3.0 */
- SrvTk_OS, /* eg: Apache/1.3.0 (UNIX) */
- SrvTk_FULL, /* eg: Apache/1.3.0 (UNIX) PHP/3.0 FooBar/1.2b */
+ SrvTk_MAJOR, /* eg: Apache/2 */
+ SrvTk_MINOR, /* eg. Apache/2.0 */
+ SrvTk_MINIMAL, /* eg: Apache/2.0.41 */
+ SrvTk_OS, /* eg: Apache/2.0.41 (UNIX) */
+ SrvTk_FULL, /* eg: Apache/2.0.41 (UNIX) PHP/4.2.2 FooBar/1.2b */
SrvTk_PRODUCT_ONLY /* eg: Apache */
};
static enum server_token_type ap_server_tokens = SrvTk_FULL;
if (ap_server_tokens == SrvTk_PRODUCT_ONLY) {
ap_add_version_component(pconf, AP_SERVER_BASEPRODUCT);
}
- else if (ap_server_tokens == SrvTk_MIN) {
+ else if (ap_server_tokens == SrvTk_MINIMAL) {
ap_add_version_component(pconf, AP_SERVER_BASEVERSION);
}
+ else if (ap_server_tokens == SrvTk_MINOR) {
+ ap_add_version_component(pconf, AP_SERVER_BASEPRODUCT "/" AP_SERVER_MINORREVISION);
+ }
+ else if (ap_server_tokens == SrvTk_MAJOR) {
+ ap_add_version_component(pconf, AP_SERVER_BASEPRODUCT "/" AP_SERVER_MAJORVERSION);
+ }
else {
ap_add_version_component(pconf, AP_SERVER_BASEVERSION " (" PLATFORM ")");
}
ap_server_tokens = SrvTk_OS;
}
else if (!strcasecmp(arg, "Min") || !strcasecmp(arg, "Minimal")) {
- ap_server_tokens = SrvTk_MIN;
+ ap_server_tokens = SrvTk_MINIMAL;
+ }
+ else if (!strcasecmp(arg, "Major")) {
+ ap_server_tokens = SrvTk_MAJOR;
+ }
+ else if (!strcasecmp(arg, "Minor") ) {
+ ap_server_tokens = SrvTk_MINOR;
}
else if (!strcasecmp(arg, "Prod") || !strcasecmp(arg, "ProductOnly")) {
ap_server_tokens = SrvTk_PRODUCT_ONLY;
}
#endif
+static const char *set_recursion_limit(cmd_parms *cmd, void *dummy,
+ const char *arg1, const char *arg2)
+{
+ core_server_config *conf = ap_get_module_config(cmd->server->module_config,
+ &core_module);
+ int limit = atoi(arg1);
+
+ if (limit <= 0) {
+ return "The recursion limit must be greater than zero.";
+ }
+ if (limit < 4) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server,
+ "Limiting internal redirects to very low numbers may "
+ "cause normal requests to fail.");
+ }
+
+ conf->redirect_limit = limit;
+
+ if (arg2) {
+ limit = atoi(arg2);
+
+ if (limit <= 0) {
+ return "The recursion limit must be greater than zero.";
+ }
+ if (limit < 4) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, cmd->server,
+ "Limiting the subrequest depth to a very low level may"
+ " cause normal requests to fail.");
+ }
+ }
+
+ conf->subreq_limit = limit;
+
+ return NULL;
+}
+
+static void log_backtrace(const request_rec *r)
+{
+ const request_rec *top = r;
+
+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
+ "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,
+ "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,
+ "subrequested from r->uri = %s",
+ top->uri ? top->uri : "(unexpectedly NULL)");
+ }
+ }
+}
+
+/*
+ * check whether redirect limit is reached
+ */
+AP_DECLARE(int) ap_is_recursion_limit_exceeded(const request_rec *r)
+{
+ core_server_config *conf = ap_get_module_config(r->server->module_config,
+ &core_module);
+ const request_rec *top = r;
+ int redirects = 0, subreqs = 0;
+ int rlimit = conf->redirect_limit
+ ? conf->redirect_limit
+ : AP_DEFAULT_MAX_INTERNAL_REDIRECTS;
+ int slimit = conf->subreq_limit
+ ? conf->subreq_limit
+ : AP_DEFAULT_MAX_SUBREQ_DEPTH;
+
+
+ while (top->prev || top->main) {
+ if (top->prev) {
+ if (++redirects >= rlimit) {
+ /* uuh, too much. */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "Request exceeded the limit of %d internal "
+ "redirects due to probable configuration error. "
+ "Use 'LimitInternalRecursion' to increase the "
+ "limit if necessary. Use 'LogLevel debug' to get "
+ "a backtrace.", rlimit);
+
+ /* post backtrace */
+ log_backtrace(r);
+
+ /* return failure */
+ return 1;
+ }
+
+ top = top->prev;
+ }
+
+ if (!top->prev && top->main) {
+ if (++subreqs >= slimit) {
+ /* uuh, too much. */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "Request exceeded the limit of %d subrequest "
+ "nesting levels due to probable confguration "
+ "error. Use 'LimitInternalRecursion' to increase "
+ "the limit if necessary. Use 'LogLevel debug' to "
+ "get a backtrace.", slimit);
+
+ /* post backtrace */
+ log_backtrace(r);
+
+ /* return failure */
+ return 1;
+ }
+
+ top = top->main;
+ }
+ }
+
+ /* recursion state: ok */
+ return 0;
+}
+
static const char *add_ct_output_filters(cmd_parms *cmd, void *conf_,
const char *arg, const char *arg2)
{
core_dir_config *conf = conf_;
- ap_filter_rec_t *old, *new;
+ ap_filter_rec_t *old, *new = NULL;
+ const char *filter_name;
if (!conf->ct_output_filters) {
conf->ct_output_filters = apr_hash_make(cmd->pool);
else {
old = (ap_filter_rec_t*) apr_hash_get(conf->ct_output_filters, arg2,
APR_HASH_KEY_STRING);
+ /* find last entry */
+ if (old) {
+ while (old->next) {
+ old = old->next;
+ }
+ }
}
- new = apr_pcalloc(cmd->pool, sizeof(ap_filter_rec_t));
- new->name = apr_pstrdup(cmd->pool, arg);
+ while (*arg &&
+ (filter_name = ap_getword(cmd->pool, &arg, ';')) &&
+ strcmp(filter_name, "")) {
+ new = apr_pcalloc(cmd->pool, sizeof(ap_filter_rec_t));
+ new->name = filter_name;
- /* We found something, so let's append it. */
- if (old) {
- new->next = old;
+ /* We found something, so let's append it. */
+ if (old) {
+ old->next = new;
+ }
+ else {
+ apr_hash_set(conf->ct_output_filters, arg2,
+ APR_HASH_KEY_STRING, new);
+ }
+ old = new;
}
- apr_hash_set(conf->ct_output_filters, arg2, APR_HASH_KEY_STRING, new);
-
+ if (!new) {
+ return "invalid filter name";
+ }
+
return NULL;
}
/*
- * Insert filters requested by the AddOutputFiltersByType
+ * Insert filters requested by the AddOutputFilterByType
* configuration directive. We cannot add filters based
* on content-type until after the handler has started
- * to run. Only then do we reliabily know the content-type.
+ * to run. Only then do we reliably know the content-type.
*/
void ap_add_output_filters_by_type(request_rec *r)
{
core_dir_config *conf;
- const char *ctype, *ctypes;
+ const char *ctype;
conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
&core_module);
return;
}
- ctypes = r->content_type;
-
- /* We must be able to handle decorated content-types. */
- while (*ctypes && (ctype = ap_getword(r->pool, &ctypes, ';'))) {
+ /* remove c-t decoration */
+ ctype = ap_field_noparam(r->pool, r->content_type);
+ if (ctype) {
ap_filter_rec_t *ct_filter;
ct_filter = apr_hash_get(conf->ct_output_filters, ctype,
APR_HASH_KEY_STRING);
/* XXX handle checking for non-blocking socket */
while (bytes_written != len) {
- rv = apr_sendv(s, vec + i, nvec - i, &n);
+ rv = apr_socket_sendv(s, vec + i, nvec - i, &n);
bytes_written += n;
if (rv != APR_SUCCESS)
return rv;
*nbytes += n;
/* If the write did not complete, adjust the iovecs and issue
- * apr_sendv again
+ * apr_socket_sendv again
*/
if (bytes_written < len) {
/* Skip over the vectors that have already been written */
apr_off_t file_offset,
apr_size_t file_bytes_left,
apr_size_t total_bytes_left,
+ apr_size_t *bytes_sent,
apr_int32_t flags)
{
apr_status_t rv;
#ifdef AP_DEBUG
- apr_int32_t timeout = 0;
+ apr_interval_time_t timeout = 0;
#endif
- AP_DEBUG_ASSERT((apr_getsocketopt(c->client_socket, APR_SO_TIMEOUT,
- &timeout) == APR_SUCCESS)
+ AP_DEBUG_ASSERT((apr_socket_timeout_get(c->client_socket, &timeout)
+ == APR_SUCCESS)
&& timeout > 0); /* socket must be in timeout mode */
+ /* Reset the bytes_sent field */
+ *bytes_sent = 0;
+
do {
apr_size_t tmplen = file_bytes_left;
- rv = apr_sendfile(c->client_socket, fd, hdtr, &file_offset, &tmplen,
- flags);
+ rv = apr_socket_sendfile(c->client_socket, fd, hdtr, &file_offset, &tmplen,
+ flags);
+ *bytes_sent += tmplen;
total_bytes_left -= tmplen;
if (!total_bytes_left || rv != APR_SUCCESS) {
return rv; /* normal case & error exit */
rv = apr_file_read(fd, buffer, &sendlen);
while (rv == APR_SUCCESS && sendlen) {
bytes_sent = sendlen;
- rv = apr_send(c->client_socket, &buffer[o], &bytes_sent);
+ rv = apr_socket_send(c->client_socket, &buffer[o], &bytes_sent);
if (rv == APR_SUCCESS) {
sendlen -= bytes_sent; /* sendlen != bytes_sent ==> partial write */
o += bytes_sent; /* o is where we are in the buffer */
"Specify components used to construct a file's ETag"),
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"),
/* Old server config file commands */
"The pathname the server can be reached at"),
AP_INIT_TAKE1("Timeout", set_timeout, NULL, RSRC_CONF,
"Timeout duration (sec)"),
-AP_INIT_FLAG("IdentityCheck", set_idcheck, NULL, RSRC_CONF|ACCESS_CONF,
- "Enable identd (RFC 1413) user lookups - SLOW"),
AP_INIT_FLAG("ContentDigest", set_content_md5, NULL, OR_OPTIONS,
"whether or not to send a Content-MD5 header with each request"),
AP_INIT_TAKE1("UseCanonicalName", set_use_canonical_name, NULL,
"Level of verbosity in error logging"),
AP_INIT_TAKE1("NameVirtualHost", ap_set_name_virtual_host, NULL, RSRC_CONF,
"A numeric IP address:port, or the name of a host"),
-#ifdef _OSD_POSIX
-AP_INIT_TAKE1("BS2000Account", set_bs2000_account, NULL, RSRC_CONF,
- "Name of server User's bs2000 logon account name"),
-#endif
AP_INIT_TAKE1("ServerTokens", set_serv_tokens, NULL, RSRC_CONF,
"Determine tokens displayed in the Server: header - Min(imal), OS or Full"),
AP_INIT_TAKE1("LimitRequestLine", set_limit_req_line, NULL, RSRC_CONF,
OR_ALL, "soft/hard limits for max number of processes per uid"),
#endif
+/* internal recursion stopper */
+AP_INIT_TAKE12("LimitInternalRecursion", set_recursion_limit, NULL, RSRC_CONF,
+ "maximum recursion depth of internal redirects and subrequests"),
+
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"),
AP_INIT_ITERATE2("AddOutputFilterByType", add_ct_output_filters,
(void *)APR_OFFSETOF(core_dir_config, ct_output_filters), OR_FILEINFO,
"output filter name followed by one or more content-types"),
+AP_INIT_FLAG("AllowEncodedSlashes", set_allow2f, NULL, RSRC_CONF,
+ "Allow URLs containing '/' encoded as '%2F'"),
/*
* These are default configuration directives that mpms can/should
* #defined them in mpm.h.
*/
#ifdef AP_MPM_WANT_SET_PIDFILE
-AP_INIT_TAKE1("PidFile", ap_mpm_set_pidfile, NULL, RSRC_CONF, \
+AP_INIT_TAKE1("PidFile", ap_mpm_set_pidfile, NULL, RSRC_CONF,
"A file for logging the server process ID"),
#endif
#ifdef AP_MPM_WANT_SET_SCOREBOARD
-AP_INIT_TAKE1("ScoreBoardFile", ap_mpm_set_scoreboard, NULL, RSRC_CONF, \
+AP_INIT_TAKE1("ScoreBoardFile", ap_mpm_set_scoreboard, NULL, RSRC_CONF,
"A file for Apache to maintain runtime process management information"),
#endif
#ifdef AP_MPM_WANT_SET_LOCKFILE
-AP_INIT_TAKE1("LockFile", ap_mpm_set_lockfile, NULL, RSRC_CONF, \
+AP_INIT_TAKE1("LockFile", ap_mpm_set_lockfile, NULL, RSRC_CONF,
"The lockfile used when Apache needs to lock the accept() call"),
#endif
#ifdef AP_MPM_WANT_SET_MAX_REQUESTS
-AP_INIT_TAKE1("MaxRequestsPerChild", ap_mpm_set_max_requests, NULL, RSRC_CONF,\
+AP_INIT_TAKE1("MaxRequestsPerChild", ap_mpm_set_max_requests, NULL, RSRC_CONF,
"Maximum number of requests a particular child serves before dying."),
#endif
#ifdef AP_MPM_WANT_SET_COREDUMPDIR
-AP_INIT_TAKE1("CoreDumpDirectory", ap_mpm_set_coredumpdir, NULL, RSRC_CONF, \
+AP_INIT_TAKE1("CoreDumpDirectory", ap_mpm_set_coredumpdir, NULL, RSRC_CONF,
"The location of the directory Apache changes to before dumping core"),
#endif
#ifdef AP_MPM_WANT_SET_ACCEPT_LOCK_MECH
-AP_INIT_TAKE1("AcceptMutex", ap_mpm_set_accept_lock_mech, NULL, RSRC_CONF, \
+AP_INIT_TAKE1("AcceptMutex", ap_mpm_set_accept_lock_mech, NULL, RSRC_CONF,
ap_valid_accept_mutex_string),
#endif
#ifdef AP_MPM_WANT_SET_MAX_MEM_FREE
-AP_INIT_TAKE1("MaxMemFree", ap_mpm_set_max_mem_free, NULL, RSRC_CONF,\
+AP_INIT_TAKE1("MaxMemFree", ap_mpm_set_max_mem_free, NULL, RSRC_CONF,
"Maximum number of 1k blocks a particular childs allocator may hold."),
#endif
{ NULL }
&& !strncmp(r->uri, r->server->path, r->server->pathlen)
&& (r->server->path[r->server->pathlen - 1] == '/'
|| r->uri[r->server->pathlen] == '/'
- || r->uri[r->server->pathlen] == '\0')) {
- if (apr_filepath_merge(&r->filename, conf->ap_document_root,
- r->uri + r->server->pathlen,
+ || r->uri[r->server->pathlen] == '\0'))
+ {
+ /* skip all leading /'s (e.g. http://localhost///foo)
+ * so we are looking at only the relative path.
+ */
+ char *path = r->uri + r->server->pathlen;
+ while (*path == '/') {
+ ++path;
+ }
+ if (apr_filepath_merge(&r->filename, conf->ap_document_root, path,
APR_FILEPATH_TRUENAME
| APR_FILEPATH_SECUREROOT, r->pool)
!= APR_SUCCESS) {
* /'s in a row. This happens under windows when the document
* root ends with a /
*/
- if (apr_filepath_merge(&r->filename, conf->ap_document_root,
- r->uri + ((*(r->uri) == '/') ? 1 : 0),
+ /* skip all leading /'s (e.g. http://localhost///foo)
+ * so we are looking at only the relative path.
+ */
+ char *path = r->uri;
+ while (*path == '/') {
+ ++path;
+ }
+ if (apr_filepath_merge(&r->filename, conf->ap_document_root, path,
APR_FILEPATH_TRUENAME
| APR_FILEPATH_SECUREROOT, r->pool)
!= APR_SUCCESS) {
return HTTP_NOT_FOUND;
}
- if ((status = apr_file_open(&fd, r->filename, APR_READ | APR_BINARY, 0,
- r->pool)) != APR_SUCCESS) {
+ /* We understood the (non-GET) method, but it might not be legal for
+ this particular resource. Check to see if the 'deliver_script'
+ flag is set. If so, then we go ahead and deliver the file since
+ it isn't really content (only GET normally returns content).
+
+ Note: based on logic further above, the only possible non-GET
+ method at this point is POST. In the future, we should enable
+ script delivery for all methods. */
+ if (r->method_number != M_GET) {
+ core_request_config *req_cfg;
+
+ req_cfg = ap_get_module_config(r->request_config, &core_module);
+ if (!req_cfg->deliver_script) {
+ /* The flag hasn't been set for this request. Punt. */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
+ "This resource does not accept the %s method.",
+ r->method);
+ return HTTP_METHOD_NOT_ALLOWED;
+ }
+ }
+
+
+ if ((status = apr_file_open(&fd, r->filename, APR_READ | APR_BINARY
+#if APR_HAS_SENDFILE
+ | ((d->enable_sendfile == ENABLE_SENDFILE_OFF)
+ ? 0 : APR_SENDFILE_ENABLED)
+#endif
+ , 0, r->pool)) != APR_SUCCESS) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r,
"file permissions deny server access: %s", r->filename);
return HTTP_FORBIDDEN;
}
bb = apr_brigade_create(r->pool, c->bucket_alloc);
-#if APR_HAS_LARGE_FILES
- if (r->finfo.size > AP_MAX_SENDFILE) {
+#if APR_HAS_SENDFILE && APR_HAS_LARGE_FILES
+ if ((d->enable_sendfile != ENABLE_SENDFILE_OFF) &&
+ (r->finfo.size > AP_MAX_SENDFILE)) {
/* APR_HAS_LARGE_FILES issue; must split into mutiple buckets,
* no greater than MAX(apr_size_t), and more granular than that
* in case the brigade code/filters attempt to read it directly.
}
}
+typedef struct net_time_filter_ctx {
+ apr_socket_t *csd;
+ int first_line;
+} net_time_filter_ctx_t;
static int net_time_filter(ap_filter_t *f, apr_bucket_brigade *b,
ap_input_mode_t mode, apr_read_type_e block,
apr_off_t readbytes)
{
+ net_time_filter_ctx_t *ctx = f->ctx;
int keptalive = f->c->keepalive == AP_CONN_KEEPALIVE;
- apr_socket_t *csd = ap_get_module_config(f->c->conn_config, &core_module);
- int *first_line = f->ctx;
- if (!f->ctx) {
- f->ctx = first_line = apr_palloc(f->r->pool, sizeof(*first_line));
- *first_line = 1;
+ if (!ctx) {
+ f->ctx = ctx = apr_palloc(f->r->pool, sizeof(*ctx));
+ ctx->first_line = 1;
+ ctx->csd = ap_get_module_config(f->c->conn_config, &core_module);
}
if (mode != AP_MODE_INIT && mode != AP_MODE_EATCRLF) {
- if (*first_line) {
- apr_setsocketopt(csd, APR_SO_TIMEOUT,
- (int)(keptalive
- ? f->c->base_server->keep_alive_timeout
- : f->c->base_server->timeout));
- *first_line = 0;
+ if (ctx->first_line) {
+ apr_socket_timeout_set(ctx->csd,
+ keptalive
+ ? f->c->base_server->keep_alive_timeout
+ : f->c->base_server->timeout);
+ ctx->first_line = 0;
}
else {
if (keptalive) {
- apr_setsocketopt(csd, APR_SO_TIMEOUT,
- (int)(f->c->base_server->timeout));
+ apr_socket_timeout_set(ctx->csd, f->c->base_server->timeout);
}
}
}
return APR_EOF;
}
+ if (mode == AP_MODE_GETLINE) {
+ /* we are reading a single LF line, e.g. the HTTP headers */
+ rv = apr_brigade_split_line(b, ctx->b, block, HUGE_STRING_LEN);
+ /* We should treat EAGAIN here the same as we do for EOF (brigade is
+ * empty). We do this by returning whatever we have read. This may
+ * or may not be bogus, but is consistent (for now) with EOF logic.
+ */
+ if (APR_STATUS_IS_EAGAIN(rv)) {
+ rv = APR_SUCCESS;
+ }
+ return rv;
+ }
+
/* ### AP_MODE_PEEK is a horrific name for this mode because we also
* eat any CRLFs that we see. That's not the obvious intention of
* this mode. Determine whether anyone actually uses this or not. */
/* FIXME: Is this the right thing to do in the core? */
apr_bucket_delete(e);
}
+ return APR_SUCCESS;
}
/* If mode is EXHAUSTIVE, we want to just read everything until the end
/* Take what was originally there and place it back on ctx->b */
APR_BRIGADE_CONCAT(ctx->b, newbb);
-
- return APR_SUCCESS;
- }
-
- /* we are reading a single LF line, e.g. the HTTP headers */
- rv = apr_brigade_split_line(b, ctx->b, block, HUGE_STRING_LEN);
-
- /* We should treat EAGAIN here the same as we do for EOF (brigade is
- * empty). We do this by returning whatever we have read. This may
- * or may not be bogus, but is consistent (for now) with EOF logic.
- */
- if (APR_STATUS_IS_EAGAIN(rv)) {
- rv = APR_SUCCESS;
}
-
- return rv;
+ return APR_SUCCESS;
}
/* Default filter. This filter should almost always be used. Its only job
*/
#define MAX_IOVEC_TO_WRITE 16
+/* Optional function coming from mod_logio, used for logging of output
+ * traffic
+ */
+static APR_OPTIONAL_FN_TYPE(ap_logio_add_bytes_out) *logio_add_bytes_out;
+
static apr_status_t core_output_filter(ap_filter_t *f, apr_bucket_brigade *b)
{
apr_status_t rv;
+ apr_bucket_brigade *more;
conn_rec *c = f->c;
core_net_rec *net = f->ctx;
core_output_filter_ctx_t *ctx = net->out_ctx;
+ apr_read_type_e eblock = APR_NONBLOCK_READ;
+ apr_pool_t *input_pool = b->p;
if (ctx == NULL) {
ctx = apr_pcalloc(c->pool, sizeof(*ctx));
apr_bucket *last_e = NULL; /* initialized for debugging */
apr_bucket *e;
- /* tail of brigade if we need another pass */
- apr_bucket_brigade *more = NULL;
-
/* one group of iovecs per pass over the brigade */
apr_size_t nvec = 0;
apr_size_t nvec_trailers = 0;
*/
apr_bucket *last_merged_bucket = NULL;
+ /* tail of brigade if we need another pass */
+ more = NULL;
+
/* Iterate over the brigade: collect iovecs and/or a file */
APR_BRIGADE_FOREACH(e, b) {
/* keep track of the last bucket processed */
break;
}
if (APR_BUCKET_IS_FLUSH(e)) {
- more = apr_brigade_split(b, APR_BUCKET_NEXT(e));
+ if (e != APR_BRIGADE_LAST(b)) {
+ more = apr_brigade_split(b, APR_BUCKET_NEXT(e));
+ }
break;
}
const char *str;
apr_size_t n;
- rv = apr_bucket_read(e, &str, &n, APR_BLOCK_READ);
+ rv = apr_bucket_read(e, &str, &n, eblock);
+ if (APR_STATUS_IS_EAGAIN(rv)) {
+ /* send what we have so far since we shouldn't expect more
+ * output for a while... next time we read, block
+ */
+ more = apr_brigade_split(b, e);
+ eblock = APR_BLOCK_READ;
+ break;
+ }
+ eblock = APR_NONBLOCK_READ;
if (n) {
if (!fd) {
if (nvec == MAX_IOVEC_TO_WRITE) {
}
- /* Completed iterating over the brigades, now determine if we want
+ /* Completed iterating over the brigade, now determine if we want
* to buffer the brigade or send the brigade out on the network.
*
- * Save if:
+ * Save if we haven't accumulated enough bytes to send, and:
*
* 1) we didn't see a file, we don't have more passes over the
- * brigade to perform, we haven't accumulated enough bytes to
- * send, AND we didn't stop at a FLUSH bucket.
- * (IOW, we will save away plain old bytes)
+ * brigade to perform, AND we didn't stop at a FLUSH bucket.
+ * (IOW, we will save plain old bytes such as HTTP headers)
* or
* 2) we hit the EOS and have a keep-alive connection
* (IOW, this response is a bit more complex, but we save it
* with the hope of concatenating with another response)
*/
- if ((!fd && !more
- && (nbytes + flen < AP_MIN_BYTES_TO_WRITE)
- && !APR_BUCKET_IS_FLUSH(last_e))
- || (nbytes + flen < AP_MIN_BYTES_TO_WRITE
- && APR_BUCKET_IS_EOS(last_e)
- && c->keepalive == AP_CONN_KEEPALIVE)) {
+ if (nbytes + flen < AP_MIN_BYTES_TO_WRITE
+ && ((!fd && !more && !APR_BUCKET_IS_FLUSH(last_e))
+ || (APR_BUCKET_IS_EOS(last_e)
+ && c->keepalive == AP_CONN_KEEPALIVE))) {
/* NEVER save an EOS in here. If we are saving a brigade with
* an EOS bucket, then we are doing keepalive connections, and
* we want to process to second request fully.
*/
if (APR_BUCKET_IS_EOS(last_e)) {
- apr_bucket *bucket = NULL;
- /* If we are in here, then this request is a keepalive. We
- * need to be certain that any data in a bucket is valid
- * after the request_pool is cleared.
- */
- if (ctx->b == NULL) {
- ctx->b = apr_brigade_create(net->c->pool,
- net->c->bucket_alloc);
- }
-
- APR_BRIGADE_FOREACH(bucket, b) {
- const char *str;
- apr_size_t n;
-
- rv = apr_bucket_read(bucket, &str, &n, APR_BLOCK_READ);
-
- /* This apr_brigade_write does not use a flush function
- because we assume that we will not write enough data
- into it to cause a flush. However, if we *do* write
- "too much", then we could end up with transient
- buckets which would suck. This works for now, but is
- a bit shaky if changes are made to some of the
- buffering sizes. Let's do an assert to prevent
- potential future problems... */
- AP_DEBUG_ASSERT(AP_MIN_BYTES_TO_WRITE <=
- APR_BUCKET_BUFF_SIZE);
- if (rv != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_ERR, rv, c->base_server,
- "core_output_filter: Error reading from bucket.");
- return HTTP_INTERNAL_SERVER_ERROR;
+ apr_bucket *bucket;
+ int file_bucket_saved = 0;
+ apr_bucket_delete(last_e);
+ for (bucket = APR_BRIGADE_FIRST(b);
+ bucket != APR_BRIGADE_SENTINEL(b);
+ bucket = APR_BUCKET_NEXT(bucket)) {
+
+ /* Do a read on each bucket to pull in the
+ * data from pipe and socket buckets, so
+ * that we don't leave their file descriptors
+ * open indefinitely. Do the same for file
+ * buckets, with one exception: allow the
+ * first file bucket in the brigade to remain
+ * a file bucket, so that we don't end up
+ * doing an mmap+memcpy every time a client
+ * requests a <8KB file over a keepalive
+ * connection.
+ */
+ if (APR_BUCKET_IS_FILE(bucket) && !file_bucket_saved) {
+ file_bucket_saved = 1;
+ }
+ else {
+ const char *buf;
+ apr_size_t len = 0;
+ rv = apr_bucket_read(bucket, &buf, &len,
+ APR_BLOCK_READ);
+ if (rv != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_ERR, rv,
+ c->base_server, "core_output_filter:"
+ " Error reading from bucket.");
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
}
-
- apr_brigade_write(ctx->b, NULL, NULL, str, n);
}
-
- apr_brigade_destroy(b);
}
- else {
- ap_save_brigade(f, &ctx->b, &b, c->pool);
+ if (!ctx->deferred_write_pool) {
+ apr_pool_create(&ctx->deferred_write_pool, c->pool);
}
+ ap_save_brigade(f, &ctx->b, &b, ctx->deferred_write_pool);
return APR_SUCCESS;
}
if (fd) {
apr_hdtr_t hdtr;
+ apr_size_t bytes_sent;
+
#if APR_HAS_SENDFILE
apr_int32_t flags = 0;
#endif
}
#if APR_HAS_SENDFILE
- if (c->keepalive == AP_CONN_CLOSE && APR_BUCKET_IS_EOS(last_e)) {
- /* Prepare the socket to be reused */
- flags |= APR_SENDFILE_DISCONNECT_SOCKET;
- }
+ if (apr_file_flags_get(fd) & APR_SENDFILE_ENABLED) {
- rv = sendfile_it_all(net, /* the network information */
- fd, /* the file to send */
- &hdtr, /* header and trailer iovecs */
- foffset, /* offset in the file to begin
- sending from */
- flen, /* length of file */
- nbytes + flen, /* total length including
- headers */
- flags); /* apr_sendfile flags */
-
- /* If apr_sendfile() returns APR_ENOTIMPL, call emulate_sendfile().
- * emulate_sendfile() is useful to enable the same Apache binary
- * distribution to support Windows NT/2000 (supports TransmitFile)
- * and Win95/98 (do not support TransmitFile)
- */
- if (rv == APR_ENOTIMPL)
+ if (c->keepalive == AP_CONN_CLOSE && APR_BUCKET_IS_EOS(last_e)) {
+ /* Prepare the socket to be reused */
+ flags |= APR_SENDFILE_DISCONNECT_SOCKET;
+ }
+
+ rv = sendfile_it_all(net, /* the network information */
+ fd, /* the file to send */
+ &hdtr, /* header and trailer iovecs */
+ foffset, /* offset in the file to begin
+ sending from */
+ flen, /* length of file */
+ nbytes + flen, /* total length including
+ headers */
+ &bytes_sent, /* how many bytes were
+ sent */
+ flags); /* apr_sendfile flags */
+
+ if (logio_add_bytes_out && bytes_sent > 0)
+ logio_add_bytes_out(c, bytes_sent);
+ }
+ else
#endif
{
- apr_size_t unused_bytes_sent;
rv = emulate_sendfile(net, fd, &hdtr, foffset, flen,
- &unused_bytes_sent);
+ &bytes_sent);
+
+ if (logio_add_bytes_out && bytes_sent > 0)
+ logio_add_bytes_out(c, bytes_sent);
}
fd = NULL;
}
else {
- apr_size_t unused_bytes_sent;
+ apr_size_t bytes_sent;
rv = writev_it_all(net->client_socket,
vec, nvec,
- nbytes, &unused_bytes_sent);
+ nbytes, &bytes_sent);
+
+ if (logio_add_bytes_out && bytes_sent > 0)
+ logio_add_bytes_out(c, bytes_sent);
}
apr_brigade_destroy(b);
+
+ /* drive cleanups for resources which were set aside
+ * this may occur before or after termination of the request which
+ * created the resource
+ */
+ if (ctx->deferred_write_pool) {
+ if (more && more->p == ctx->deferred_write_pool) {
+ /* "more" belongs to the deferred_write_pool,
+ * which is about to be cleared.
+ */
+ if (APR_BRIGADE_EMPTY(more)) {
+ more = NULL;
+ }
+ else {
+ /* uh oh... change more's lifetime
+ * to the input brigade's lifetime
+ */
+ apr_bucket_brigade *tmp_more = more;
+ more = NULL;
+ ap_save_brigade(f, &more, &tmp_more, input_pool);
+ }
+ }
+ apr_pool_clear(ctx->deferred_write_pool);
+ }
+
if (rv != APR_SUCCESS) {
ap_log_error(APLOG_MARK, APLOG_INFO, rv, c->base_server,
"core_output_filter: writing data to the network");
if (more)
apr_brigade_destroy(more);
- if (APR_STATUS_IS_ECONNABORTED(rv)
- || APR_STATUS_IS_ECONNRESET(rv)
- || APR_STATUS_IS_EPIPE(rv)) {
+ /* No need to check for SUCCESS, we did that above. */
+ if (!APR_STATUS_IS_EAGAIN(rv)) {
c->aborted = 1;
}
static int core_post_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
{
+ logio_add_bytes_out = APR_RETRIEVE_OPTIONAL_FN(ap_logio_add_bytes_out);
+ ident_lookup = APR_RETRIEVE_OPTIONAL_FN(ap_ident_lookup);
+
ap_set_version(pconf);
ap_setup_make_content_type(pconf);
return OK;
}
-static int core_open_logs(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
-{
- ap_open_logs(s, plog);
- return OK;
-}
-
static void core_insert_filter(request_rec *r)
{
core_dir_config *conf = (core_dir_config *)
req_cfg = apr_pcalloc(r->pool, sizeof(core_request_config) +
sizeof(void *) * num_request_notes);
req_cfg->notes = (void **)((char *)req_cfg + sizeof(core_request_config));
+
+ /* ### temporarily enable script delivery as the default */
+ req_cfg->deliver_script = 1;
+
if (r->main) {
core_request_config *main_req_cfg = (core_request_config *)
ap_get_module_config(r->main->request_config, &core_module);
conn_rec *c = (conn_rec *) apr_pcalloc(ptrans, sizeof(conn_rec));
c->sbh = sbh;
- (void) ap_update_child_status(c->sbh, SERVER_BUSY_READ, (request_rec *) NULL);
+ (void)ap_update_child_status(c->sbh, SERVER_BUSY_READ, (request_rec *)NULL);
/* Got a connection structure, so initialize what fields we can
* (the rest are zeroed out by pcalloc).
ap_hook_post_config(core_post_config,NULL,NULL,APR_HOOK_REALLY_FIRST);
ap_hook_translate_name(ap_core_translate,NULL,NULL,APR_HOOK_REALLY_LAST);
ap_hook_map_to_storage(core_map_to_storage,NULL,NULL,APR_HOOK_REALLY_LAST);
- ap_hook_open_logs(core_open_logs,NULL,NULL,APR_HOOK_MIDDLE);
+ ap_hook_open_logs(ap_open_logs,NULL,NULL,APR_HOOK_REALLY_FIRST);
ap_hook_handler(default_handler,NULL,NULL,APR_HOOK_REALLY_LAST);
/* FIXME: I suspect we can eliminate the need for these do_nothings - Ben */
ap_hook_type_checker(do_nothing,NULL,NULL,APR_HOOK_REALLY_LAST);