Return-Path:
Delivered-To: apmail-httpd-cvs-archive@www.apache.org
Received: (qmail 63676 invoked from network); 27 Nov 2004 08:07:52 -0000
Received: from hermes.apache.org (HELO mail.apache.org) (209.237.227.199)
by minotaur-2.apache.org with SMTP; 27 Nov 2004 08:07:52 -0000
Received: (qmail 63653 invoked by uid 500); 27 Nov 2004 08:07:51 -0000
Delivered-To: apmail-httpd-cvs-archive@httpd.apache.org
Received: (qmail 63629 invoked by uid 500); 27 Nov 2004 08:07:50 -0000
Mailing-List: contact cvs-help@httpd.apache.org; run by ezmlm
Precedence: bulk
Reply-To: dev@httpd.apache.org
list-help:
list-unsubscribe:
list-post:
Delivered-To: mailing list cvs@httpd.apache.org
Received: (qmail 63601 invoked by uid 99); 27 Nov 2004 08:07:49 -0000
Received: from minotaur.apache.org (HELO minotaur.apache.org)
(209.237.227.194)
by apache.org (qpsmtpd/0.28) with SMTP; Sat, 27 Nov 2004 00:07:47 -0800
Received: (qmail 63642 invoked by uid 65534); 27 Nov 2004 08:07:46 -0000
Date: 27 Nov 2004 08:07:46 -0000
Message-ID: <20041127080746.63634.qmail@minotaur.apache.org>
From: jerenkrantz@apache.org
To: cvs@httpd.apache.org
Subject: svn commit: r106692 - in httpd/httpd/trunk: . build modules/http
server
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
X-Virus-Checked: Checked
X-Spam-Rating: minotaur-2.apache.org 1.6.2 0/1000/N
Author: jerenkrantz
Date: Sat Nov 27 00:07:44 2004
New Revision: 106692
URL: http://svn.apache.org/viewcvs?view=rev&rev=106692
Log:
Initial pass at refactoring some files to eliminate our 150K C source behemoths.
* Makefile.in: Change order of dependencies to bring in exports.o first so that
we have every symbol 'used' before the linker starts processing.
* build/rules.mk.in: Add a 'program-install' target which just copies httpd.
* server/Makefile.in, modules/http/config2.m4: Add in new file targets.
* NWGNUmakefile, libhttpd.dsp: Blind updates for Netware and Win32. (I tried.)
* server/core.c: Move core_input_filter, net_time_filter, and core_output_filter and all supporting functions to...
* server/core_filters.c (copied): ...here.
* modules/http/http_protocol.c: Move functions from here to there...namely:
* modules/http/byterange_filter.c (copied): Relocate ap_byterange_filter() and
friends.
* modules/http/chunk_filter.c (copied): Relocate chunk_filter().
* modules/http/http_etag.c (copied): Relocate ap_set_etag and ap_make_etag().
* modules/http/http_filters.c (copied): Relocate ap_http_filter(),
ap_http_header_filter(), ap_discard_request_body(), ap_setup_client_block(),
ap_should_client_block(), and ap_get_client_block().
Added:
httpd/httpd/trunk/modules/http/byterange_filter.c
- copied, changed from r106679, httpd/httpd/trunk/modules/http/http_protocol.c
httpd/httpd/trunk/modules/http/chunk_filter.c
- copied, changed from r106679, httpd/httpd/trunk/modules/http/http_core.c
httpd/httpd/trunk/modules/http/http_etag.c
- copied, changed from r106679, httpd/httpd/trunk/modules/http/http_protocol.c
httpd/httpd/trunk/modules/http/http_filters.c
- copied, changed from r106679, httpd/httpd/trunk/modules/http/http_protocol.c
httpd/httpd/trunk/server/core_filters.c
- copied, changed from r106679, httpd/httpd/trunk/server/core.c
Modified:
httpd/httpd/trunk/Makefile.in
httpd/httpd/trunk/NWGNUmakefile
httpd/httpd/trunk/build/rules.mk.in
httpd/httpd/trunk/libhttpd.dsp
httpd/httpd/trunk/modules/http/config2.m4
httpd/httpd/trunk/modules/http/http_protocol.c
httpd/httpd/trunk/server/Makefile.in
httpd/httpd/trunk/server/core.c
Modified: httpd/httpd/trunk/Makefile.in
Url: http://svn.apache.org/viewcvs/httpd/httpd/trunk/Makefile.in?view=diff&rev=106692&p1=httpd/httpd/trunk/Makefile.in&r1=106691&p2=httpd/httpd/trunk/Makefile.in&r2=106692
==============================================================================
--- httpd/httpd/trunk/Makefile.in (original)
+++ httpd/httpd/trunk/Makefile.in Sat Nov 27 00:07:44 2004
@@ -6,9 +6,9 @@
PROGRAM_SOURCES = modules.c
PROGRAM_LDADD = $(HTTPD_LDFLAGS) $(PROGRAM_DEPENDENCIES) $(EXTRA_LIBS) $(AP_LIBS) $(LIBS)
PROGRAM_DEPENDENCIES = \
+ server/libmain.la \
$(BUILTIN_LIBS) \
$(MPM_LIB) \
- server/libmain.la \
os/$(OS_DIR)/libos.la
PROGRAMS = $(PROGRAM_NAME)
Modified: httpd/httpd/trunk/NWGNUmakefile
Url: http://svn.apache.org/viewcvs/httpd/httpd/trunk/NWGNUmakefile?view=diff&rev=106692&p1=httpd/httpd/trunk/NWGNUmakefile&r1=106691&p2=httpd/httpd/trunk/NWGNUmakefile&r2=106692
==============================================================================
--- httpd/httpd/trunk/NWGNUmakefile (original)
+++ httpd/httpd/trunk/NWGNUmakefile Sat Nov 27 00:07:44 2004
@@ -195,11 +195,16 @@
$(OBJDIR)/config.o \
$(OBJDIR)/connection.o \
$(OBJDIR)/core.o \
+ $(OBJDIR)/core_filters.o \
$(OBJDIR)/eoc_bucket.o \
$(OBJDIR)/error_bucket.o \
$(OBJDIR)/http_core.o \
$(OBJDIR)/http_protocol.o \
$(OBJDIR)/http_request.o \
+ $(OBJDIR)/byterange_filter.o \
+ $(OBJDIR)/chunk_filter.o \
+ $(OBJDIR)/http_etag.o \
+ $(OBJDIR)/http_filters.o \
$(OBJDIR)/listen.o \
$(OBJDIR)/log.o \
$(OBJDIR)/main.o \
Modified: httpd/httpd/trunk/build/rules.mk.in
Url: http://svn.apache.org/viewcvs/httpd/httpd/trunk/build/rules.mk.in?view=diff&rev=106692&p1=httpd/httpd/trunk/build/rules.mk.in&r1=106691&p2=httpd/httpd/trunk/build/rules.mk.in&r2=106692
==============================================================================
--- httpd/httpd/trunk/build/rules.mk.in (original)
+++ httpd/httpd/trunk/build/rules.mk.in Sat Nov 27 00:07:44 2004
@@ -160,13 +160,15 @@
rm -f $(EXTRACLEAN_TARGETS) ; \
fi
-local-install: $(TARGETS) $(SHARED_TARGETS) $(INSTALL_TARGETS)
+program-install: $(TARGETS) $(SHARED_TARGETS)
@if test -n '$(PROGRAMS)'; then \
test -d $(DESTDIR)$(sbindir) || $(MKINSTALLDIRS) $(DESTDIR)$(sbindir); \
list='$(PROGRAMS)'; for i in $$list; do \
$(INSTALL_PROGRAM) $$i $(DESTDIR)$(sbindir); \
done; \
fi
+
+local-install: program-install $(INSTALL_TARGETS)
# to be filled in by the actual Makefile if extra commands are needed
x-local-depend x-local-clean x-local-distclean x-local-extraclean:
Modified: httpd/httpd/trunk/libhttpd.dsp
Url: http://svn.apache.org/viewcvs/httpd/httpd/trunk/libhttpd.dsp?view=diff&rev=106692&p1=httpd/httpd/trunk/libhttpd.dsp&r1=106691&p2=httpd/httpd/trunk/libhttpd.dsp&r2=106692
==============================================================================
--- httpd/httpd/trunk/libhttpd.dsp (original)
+++ httpd/httpd/trunk/libhttpd.dsp Sat Nov 27 00:07:44 2004
@@ -382,6 +382,10 @@
# End Source File
# Begin Source File
+SOURCE=.\server\core_filters.c
+# End Source File
+# Begin Source File
+
SOURCE=.\modules\http\http_core.c
# End Source File
# Begin Source File
@@ -391,6 +395,22 @@
# Begin Source File
SOURCE=.\modules\http\http_request.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\modules\http\byterange_filter.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\modules\http\chunk_filter.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\modules\http\http_etag.c
+# End Source File
+# Begin Source File
+
+SOURCE=.\modules\http\http_filters.c
# End Source File
# Begin Source File
Copied: httpd/httpd/trunk/modules/http/byterange_filter.c (from r106679, httpd/httpd/trunk/modules/http/http_protocol.c)
Url: http://svn.apache.org/viewcvs/httpd/httpd/trunk/modules/http/byterange_filter.c?view=diff&rev=106692&p1=httpd/httpd/trunk/modules/http/http_protocol.c&r1=106679&p2=httpd/httpd/trunk/modules/http/byterange_filter.c&r2=106692
==============================================================================
--- httpd/httpd/trunk/modules/http/http_protocol.c (original)
+++ httpd/httpd/trunk/modules/http/byterange_filter.c Sat Nov 27 00:07:44 2004
@@ -14,10 +14,7 @@
*/
/*
- * http_protocol.c --- routines which directly communicate with the client.
- *
- * Code originally by Rob McCool; much redone by Robert S. Thau
- * and the Apache Software Foundation.
+ * byterange_filter.c --- HTTP byterange filter and friends.
*/
#include "apr.h"
@@ -56,2735 +53,6 @@
#if APR_HAVE_UNISTD_H
#include
#endif
-
-/* New Apache routine to map status codes into array indicies
- * e.g. 100 -> 0, 101 -> 1, 200 -> 2 ...
- * The number of status lines must equal the value of RESPONSE_CODES (httpd.h)
- * and must be listed in order.
- */
-
-#ifdef UTS21
-/* The second const triggers an assembler bug on UTS 2.1.
- * Another workaround is to move some code out of this file into another,
- * but this is easier. Dave Dykstra, 3/31/99
- */
-static const char * status_lines[RESPONSE_CODES] =
-#else
-static const char * const status_lines[RESPONSE_CODES] =
-#endif
-{
- "100 Continue",
- "101 Switching Protocols",
- "102 Processing",
-#define LEVEL_200 3
- "200 OK",
- "201 Created",
- "202 Accepted",
- "203 Non-Authoritative Information",
- "204 No Content",
- "205 Reset Content",
- "206 Partial Content",
- "207 Multi-Status",
-#define LEVEL_300 11
- "300 Multiple Choices",
- "301 Moved Permanently",
- "302 Found",
- "303 See Other",
- "304 Not Modified",
- "305 Use Proxy",
- "306 unused",
- "307 Temporary Redirect",
-#define LEVEL_400 19
- "400 Bad Request",
- "401 Authorization Required",
- "402 Payment Required",
- "403 Forbidden",
- "404 Not Found",
- "405 Method Not Allowed",
- "406 Not Acceptable",
- "407 Proxy Authentication Required",
- "408 Request Time-out",
- "409 Conflict",
- "410 Gone",
- "411 Length Required",
- "412 Precondition Failed",
- "413 Request Entity Too Large",
- "414 Request-URI Too Large",
- "415 Unsupported Media Type",
- "416 Requested Range Not Satisfiable",
- "417 Expectation Failed",
- "418 unused",
- "419 unused",
- "420 unused",
- "421 unused",
- "422 Unprocessable Entity",
- "423 Locked",
- "424 Failed Dependency",
- /* This is a hack, but it is required for ap_index_of_response
- * to work with 426.
- */
- "425 No code",
- "426 Upgrade Required",
-#define LEVEL_500 46
- "500 Internal Server Error",
- "501 Method Not Implemented",
- "502 Bad Gateway",
- "503 Service Temporarily Unavailable",
- "504 Gateway Time-out",
- "505 HTTP Version Not Supported",
- "506 Variant Also Negotiates",
- "507 Insufficient Storage",
- "508 unused",
- "509 unused",
- "510 Not Extended"
-};
-
-APR_HOOK_STRUCT(
- APR_HOOK_LINK(insert_error_filter)
-)
-
-AP_IMPLEMENT_HOOK_VOID(insert_error_filter, (request_rec *r), (r))
-
-/* The index of the first bit field that is used to index into a limit
- * bitmask. M_INVALID + 1 to METHOD_NUMBER_LAST.
- */
-#define METHOD_NUMBER_FIRST (M_INVALID + 1)
-
-/* The max method number. Method numbers are used to shift bitmasks,
- * so this cannot exceed 63, and all bits high is equal to -1, which is a
- * special flag, so the last bit used has index 62.
- */
-#define METHOD_NUMBER_LAST 62
-
-
-AP_DECLARE(int) ap_set_keepalive(request_rec *r)
-{
- int ka_sent = 0;
- int wimpy = ap_find_token(r->pool,
- apr_table_get(r->headers_out, "Connection"),
- "close");
- const char *conn = apr_table_get(r->headers_in, "Connection");
-
- /* The following convoluted conditional determines whether or not
- * the current connection should remain persistent after this response
- * (a.k.a. HTTP Keep-Alive) and whether or not the output message
- * body should use the HTTP/1.1 chunked transfer-coding. In English,
- *
- * IF we have not marked this connection as errored;
- * and the response body has a defined length due to the status code
- * being 304 or 204, the request method being HEAD, already
- * having defined Content-Length or Transfer-Encoding: chunked, or
- * the request version being HTTP/1.1 and thus capable of being set
- * as chunked [we know the (r->chunked = 1) side-effect is ugly];
- * and the server configuration enables keep-alive;
- * and the server configuration has a reasonable inter-request timeout;
- * and there is no maximum # requests or the max hasn't been reached;
- * and the response status does not require a close;
- * and the response generator has not already indicated close;
- * and the client did not request non-persistence (Connection: close);
- * and we haven't been configured to ignore the buggy twit
- * or they're a buggy twit coming through a HTTP/1.1 proxy
- * and the client is requesting an HTTP/1.0-style keep-alive
- * or the client claims to be HTTP/1.1 compliant (perhaps a proxy);
- * THEN we can be persistent, which requires more headers be output.
- *
- * Note that the condition evaluation order is extremely important.
- */
- if ((r->connection->keepalive != AP_CONN_CLOSE)
- && ((r->status == HTTP_NOT_MODIFIED)
- || (r->status == HTTP_NO_CONTENT)
- || r->header_only
- || apr_table_get(r->headers_out, "Content-Length")
- || ap_find_last_token(r->pool,
- apr_table_get(r->headers_out,
- "Transfer-Encoding"),
- "chunked")
- || ((r->proto_num >= HTTP_VERSION(1,1))
- && (r->chunked = 1))) /* THIS CODE IS CORRECT, see above. */
- && r->server->keep_alive
- && (r->server->keep_alive_timeout > 0)
- && ((r->server->keep_alive_max == 0)
- || (r->server->keep_alive_max > r->connection->keepalives))
- && !ap_status_drops_connection(r->status)
- && !wimpy
- && !ap_find_token(r->pool, conn, "close")
- && (!apr_table_get(r->subprocess_env, "nokeepalive")
- || apr_table_get(r->headers_in, "Via"))
- && ((ka_sent = ap_find_token(r->pool, conn, "keep-alive"))
- || (r->proto_num >= HTTP_VERSION(1,1)))) {
- int left = r->server->keep_alive_max - r->connection->keepalives;
-
- r->connection->keepalive = AP_CONN_KEEPALIVE;
- r->connection->keepalives++;
-
- /* If they sent a Keep-Alive token, send one back */
- if (ka_sent) {
- if (r->server->keep_alive_max) {
- apr_table_setn(r->headers_out, "Keep-Alive",
- apr_psprintf(r->pool, "timeout=%d, max=%d",
- (int)apr_time_sec(r->server->keep_alive_timeout),
- left));
- }
- else {
- apr_table_setn(r->headers_out, "Keep-Alive",
- apr_psprintf(r->pool, "timeout=%d",
- (int)apr_time_sec(r->server->keep_alive_timeout)));
- }
- apr_table_mergen(r->headers_out, "Connection", "Keep-Alive");
- }
-
- return 1;
- }
-
- /* Otherwise, we need to indicate that we will be closing this
- * connection immediately after the current response.
- *
- * We only really need to send "close" to HTTP/1.1 clients, but we
- * always send it anyway, because a broken proxy may identify itself
- * as HTTP/1.0, but pass our request along with our HTTP/1.1 tag
- * to a HTTP/1.1 client. Better safe than sorry.
- */
- if (!wimpy) {
- apr_table_mergen(r->headers_out, "Connection", "close");
- }
-
- r->connection->keepalive = AP_CONN_CLOSE;
-
- return 0;
-}
-
-AP_DECLARE(int) ap_meets_conditions(request_rec *r)
-{
- const char *etag;
- const char *if_match, *if_modified_since, *if_unmodified, *if_nonematch;
- apr_time_t tmp_time;
- apr_int64_t mtime;
-
- /* Check for conditional requests --- note that we only want to do
- * this if we are successful so far and we are not processing a
- * subrequest or an ErrorDocument.
- *
- * The order of the checks is important, since ETag checks are supposed
- * to be more accurate than checks relative to the modification time.
- * However, not all documents are guaranteed to *have* ETags, and some
- * might have Last-Modified values w/o ETags, so this gets a little
- * complicated.
- */
-
- if (!ap_is_HTTP_SUCCESS(r->status) || r->no_local_copy) {
- return OK;
- }
-
- etag = apr_table_get(r->headers_out, "ETag");
-
- /* All of our comparisons must be in seconds, because that's the
- * highest time resolution the HTTP specification allows.
- */
- /* XXX: we should define a "time unset" constant */
- tmp_time = ((r->mtime != 0) ? r->mtime : apr_time_now());
- mtime = apr_time_sec(tmp_time);
-
- /* If an If-Match request-header field was given
- * AND the field value is not "*" (meaning match anything)
- * AND if our strong ETag does not match any entity tag in that field,
- * respond with a status of 412 (Precondition Failed).
- */
- if ((if_match = apr_table_get(r->headers_in, "If-Match")) != NULL) {
- if (if_match[0] != '*'
- && (etag == NULL || etag[0] == 'W'
- || !ap_find_list_item(r->pool, if_match, etag))) {
- return HTTP_PRECONDITION_FAILED;
- }
- }
- else {
- /* Else if a valid If-Unmodified-Since request-header field was given
- * AND the requested resource has been modified since the time
- * specified in this field, then the server MUST
- * respond with a status of 412 (Precondition Failed).
- */
- if_unmodified = apr_table_get(r->headers_in, "If-Unmodified-Since");
- if (if_unmodified != NULL) {
- apr_time_t ius = apr_date_parse_http(if_unmodified);
-
- if ((ius != APR_DATE_BAD) && (mtime > apr_time_sec(ius))) {
- return HTTP_PRECONDITION_FAILED;
- }
- }
- }
-
- /* If an If-None-Match request-header field was given
- * AND the field value is "*" (meaning match anything)
- * OR our ETag matches any of the entity tags in that field, fail.
- *
- * If the request method was GET or HEAD, failure means the server
- * SHOULD respond with a 304 (Not Modified) response.
- * For all other request methods, failure means the server MUST
- * respond with a status of 412 (Precondition Failed).
- *
- * GET or HEAD allow weak etag comparison, all other methods require
- * strong comparison. We can only use weak if it's not a range request.
- */
- if_nonematch = apr_table_get(r->headers_in, "If-None-Match");
- if (if_nonematch != NULL) {
- if (r->method_number == M_GET) {
- if (if_nonematch[0] == '*') {
- return HTTP_NOT_MODIFIED;
- }
- if (etag != NULL) {
- if (apr_table_get(r->headers_in, "Range")) {
- if (etag[0] != 'W'
- && ap_find_list_item(r->pool, if_nonematch, etag)) {
- return HTTP_NOT_MODIFIED;
- }
- }
- else if (ap_strstr_c(if_nonematch, etag)) {
- return HTTP_NOT_MODIFIED;
- }
- }
- }
- else if (if_nonematch[0] == '*'
- || (etag != NULL
- && ap_find_list_item(r->pool, if_nonematch, etag))) {
- return HTTP_PRECONDITION_FAILED;
- }
- }
- /* Else if a valid If-Modified-Since request-header field was given
- * AND it is a GET or HEAD request
- * AND the requested resource has not been modified since the time
- * specified in this field, then the server MUST
- * respond with a status of 304 (Not Modified).
- * A date later than the server's current request time is invalid.
- */
- else if ((r->method_number == M_GET)
- && ((if_modified_since =
- apr_table_get(r->headers_in,
- "If-Modified-Since")) != NULL)) {
- apr_time_t ims_time;
- apr_int64_t ims, reqtime;
-
- ims_time = apr_date_parse_http(if_modified_since);
- ims = apr_time_sec(ims_time);
- reqtime = apr_time_sec(r->request_time);
-
- if ((ims >= mtime) && (ims <= reqtime)) {
- return HTTP_NOT_MODIFIED;
- }
- }
- return OK;
-}
-
-/**
- * Singleton registry of additional methods. This maps new method names
- * such as "MYGET" to methnums, which are int offsets into bitmasks.
- *
- * This follows the same technique as standard M_GET, M_POST, etc. These
- * are dynamically assigned when modules are loaded and
- * directives are processed.
- */
-static apr_hash_t *methods_registry = NULL;
-static int cur_method_number = METHOD_NUMBER_FIRST;
-
-/* internal function to register one method/number pair */
-static void register_one_method(apr_pool_t *p, const char *methname,
- int methnum)
-{
- int *pnum = apr_palloc(p, sizeof(*pnum));
-
- *pnum = methnum;
- apr_hash_set(methods_registry, methname, APR_HASH_KEY_STRING, pnum);
-}
-
-/* This internal function is used to clear the method registry
- * and reset the cur_method_number counter.
- */
-static apr_status_t ap_method_registry_destroy(void *notused)
-{
- methods_registry = NULL;
- cur_method_number = METHOD_NUMBER_FIRST;
- return APR_SUCCESS;
-}
-
-AP_DECLARE(void) ap_method_registry_init(apr_pool_t *p)
-{
- methods_registry = apr_hash_make(p);
- apr_pool_cleanup_register(p, NULL,
- ap_method_registry_destroy,
- apr_pool_cleanup_null);
-
- /* put all the standard methods into the registry hash to ease the
- mapping operations between name and number */
- register_one_method(p, "GET", M_GET);
- register_one_method(p, "PUT", M_PUT);
- register_one_method(p, "POST", M_POST);
- register_one_method(p, "DELETE", M_DELETE);
- register_one_method(p, "CONNECT", M_CONNECT);
- register_one_method(p, "OPTIONS", M_OPTIONS);
- register_one_method(p, "TRACE", M_TRACE);
- register_one_method(p, "PATCH", M_PATCH);
- register_one_method(p, "PROPFIND", M_PROPFIND);
- register_one_method(p, "PROPPATCH", M_PROPPATCH);
- register_one_method(p, "MKCOL", M_MKCOL);
- register_one_method(p, "COPY", M_COPY);
- register_one_method(p, "MOVE", M_MOVE);
- register_one_method(p, "LOCK", M_LOCK);
- register_one_method(p, "UNLOCK", M_UNLOCK);
- register_one_method(p, "VERSION-CONTROL", M_VERSION_CONTROL);
- register_one_method(p, "CHECKOUT", M_CHECKOUT);
- register_one_method(p, "UNCHECKOUT", M_UNCHECKOUT);
- register_one_method(p, "CHECKIN", M_CHECKIN);
- register_one_method(p, "UPDATE", M_UPDATE);
- register_one_method(p, "LABEL", M_LABEL);
- register_one_method(p, "REPORT", M_REPORT);
- register_one_method(p, "MKWORKSPACE", M_MKWORKSPACE);
- register_one_method(p, "MKACTIVITY", M_MKACTIVITY);
- register_one_method(p, "BASELINE-CONTROL", M_BASELINE_CONTROL);
- register_one_method(p, "MERGE", M_MERGE);
-}
-
-AP_DECLARE(int) ap_method_register(apr_pool_t *p, const char *methname)
-{
- int *methnum;
-
- if (methods_registry == NULL) {
- ap_method_registry_init(p);
- }
-
- if (methname == NULL) {
- return M_INVALID;
- }
-
- /* Check if the method was previously registered. If it was
- * return the associated method number.
- */
- methnum = (int *)apr_hash_get(methods_registry, methname,
- APR_HASH_KEY_STRING);
- if (methnum != NULL)
- return *methnum;
-
- if (cur_method_number > METHOD_NUMBER_LAST) {
- /* The method registry has run out of dynamically
- * assignable method numbers. Log this and return M_INVALID.
- */
- ap_log_perror(APLOG_MARK, APLOG_ERR, 0, p,
- "Maximum new request methods %d reached while "
- "registering method %s.",
- METHOD_NUMBER_LAST, methname);
- return M_INVALID;
- }
-
- register_one_method(p, methname, cur_method_number);
- return cur_method_number++;
-}
-
-#define UNKNOWN_METHOD (-1)
-
-static int lookup_builtin_method(const char *method, apr_size_t len)
-{
- /* Note: the following code was generated by the "shilka" tool from
- the "cocom" parsing/compilation toolkit. It is an optimized lookup
- based on analysis of the input keywords. Postprocessing was done
- on the shilka output, but the basic structure and analysis is
- from there. Should new HTTP methods be added, then manual insertion
- into this code is fine, or simply re-running the shilka tool on
- the appropriate input. */
-
- /* Note: it is also quite reasonable to just use our method_registry,
- but I'm assuming (probably incorrectly) we want more speed here
- (based on the optimizations the previous code was doing). */
-
- switch (len)
- {
- case 3:
- switch (method[0])
- {
- case 'P':
- return (method[1] == 'U'
- && method[2] == 'T'
- ? M_PUT : UNKNOWN_METHOD);
- case 'G':
- return (method[1] == 'E'
- && method[2] == 'T'
- ? M_GET : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
-
- case 4:
- switch (method[0])
- {
- case 'H':
- return (method[1] == 'E'
- && method[2] == 'A'
- && method[3] == 'D'
- ? M_GET : UNKNOWN_METHOD);
- case 'P':
- return (method[1] == 'O'
- && method[2] == 'S'
- && method[3] == 'T'
- ? M_POST : UNKNOWN_METHOD);
- case 'M':
- return (method[1] == 'O'
- && method[2] == 'V'
- && method[3] == 'E'
- ? M_MOVE : UNKNOWN_METHOD);
- case 'L':
- return (method[1] == 'O'
- && method[2] == 'C'
- && method[3] == 'K'
- ? M_LOCK : UNKNOWN_METHOD);
- case 'C':
- return (method[1] == 'O'
- && method[2] == 'P'
- && method[3] == 'Y'
- ? M_COPY : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
-
- case 5:
- switch (method[2])
- {
- case 'T':
- return (memcmp(method, "PATCH", 5) == 0
- ? M_PATCH : UNKNOWN_METHOD);
- case 'R':
- return (memcmp(method, "MERGE", 5) == 0
- ? M_MERGE : UNKNOWN_METHOD);
- case 'C':
- return (memcmp(method, "MKCOL", 5) == 0
- ? M_MKCOL : UNKNOWN_METHOD);
- case 'B':
- return (memcmp(method, "LABEL", 5) == 0
- ? M_LABEL : UNKNOWN_METHOD);
- case 'A':
- return (memcmp(method, "TRACE", 5) == 0
- ? M_TRACE : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
-
- case 6:
- switch (method[0])
- {
- case 'U':
- switch (method[5])
- {
- case 'K':
- return (memcmp(method, "UNLOCK", 6) == 0
- ? M_UNLOCK : UNKNOWN_METHOD);
- case 'E':
- return (memcmp(method, "UPDATE", 6) == 0
- ? M_UPDATE : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
- case 'R':
- return (memcmp(method, "REPORT", 6) == 0
- ? M_REPORT : UNKNOWN_METHOD);
- case 'D':
- return (memcmp(method, "DELETE", 6) == 0
- ? M_DELETE : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
-
- case 7:
- switch (method[1])
- {
- case 'P':
- return (memcmp(method, "OPTIONS", 7) == 0
- ? M_OPTIONS : UNKNOWN_METHOD);
- case 'O':
- return (memcmp(method, "CONNECT", 7) == 0
- ? M_CONNECT : UNKNOWN_METHOD);
- case 'H':
- return (memcmp(method, "CHECKIN", 7) == 0
- ? M_CHECKIN : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
-
- case 8:
- switch (method[0])
- {
- case 'P':
- return (memcmp(method, "PROPFIND", 8) == 0
- ? M_PROPFIND : UNKNOWN_METHOD);
- case 'C':
- return (memcmp(method, "CHECKOUT", 8) == 0
- ? M_CHECKOUT : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
-
- case 9:
- return (memcmp(method, "PROPPATCH", 9) == 0
- ? M_PROPPATCH : UNKNOWN_METHOD);
-
- case 10:
- switch (method[0])
- {
- case 'U':
- return (memcmp(method, "UNCHECKOUT", 10) == 0
- ? M_UNCHECKOUT : UNKNOWN_METHOD);
- case 'M':
- return (memcmp(method, "MKACTIVITY", 10) == 0
- ? M_MKACTIVITY : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
-
- case 11:
- return (memcmp(method, "MKWORKSPACE", 11) == 0
- ? M_MKWORKSPACE : UNKNOWN_METHOD);
-
- case 15:
- return (memcmp(method, "VERSION-CONTROL", 15) == 0
- ? M_VERSION_CONTROL : UNKNOWN_METHOD);
-
- case 16:
- return (memcmp(method, "BASELINE-CONTROL", 16) == 0
- ? M_BASELINE_CONTROL : UNKNOWN_METHOD);
-
- default:
- return UNKNOWN_METHOD;
- }
-
- /* NOTREACHED */
-}
-
-/* Get the method number associated with the given string, assumed to
- * contain an HTTP method. Returns M_INVALID if not recognized.
- *
- * This is the first step toward placing method names in a configurable
- * list. Hopefully it (and other routines) can eventually be moved to
- * something like a mod_http_methods.c, complete with config stuff.
- */
-AP_DECLARE(int) ap_method_number_of(const char *method)
-{
- int len = strlen(method);
- int which = lookup_builtin_method(method, len);
-
- if (which != UNKNOWN_METHOD)
- return which;
-
- /* check if the method has been dynamically registered */
- if (methods_registry != NULL) {
- int *methnum = apr_hash_get(methods_registry, method, len);
-
- if (methnum != NULL) {
- return *methnum;
- }
- }
-
- return M_INVALID;
-}
-
-/*
- * Turn a known method number into a name.
- */
-AP_DECLARE(const char *) ap_method_name_of(apr_pool_t *p, int methnum)
-{
- apr_hash_index_t *hi = apr_hash_first(p, methods_registry);
-
- /* scan through the hash table, looking for a value that matches
- the provided method number. */
- for (; hi; hi = apr_hash_next(hi)) {
- const void *key;
- void *val;
-
- apr_hash_this(hi, &key, NULL, &val);
- if (*(int *)val == methnum)
- return key;
- }
-
- /* it wasn't found in the hash */
- return NULL;
-}
-
-static long get_chunk_size(char *);
-
-typedef struct http_filter_ctx {
- apr_off_t remaining;
- apr_off_t limit;
- apr_off_t limit_used;
- enum {
- BODY_NONE,
- BODY_LENGTH,
- BODY_CHUNK
- } state;
- int eos_sent;
-} http_ctx_t;
-
-/* This is the HTTP_INPUT filter for HTTP requests and responses from
- * proxied servers (mod_proxy). It handles chunked and content-length
- * bodies. This can only be inserted/used after the headers
- * are successfully parsed.
- */
-apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b,
- ap_input_mode_t mode, apr_read_type_e block,
- apr_off_t readbytes)
-{
- apr_bucket *e;
- http_ctx_t *ctx = f->ctx;
- apr_status_t rv;
- apr_off_t totalread;
-
- /* just get out of the way of things we don't want. */
- if (mode != AP_MODE_READBYTES && mode != AP_MODE_GETLINE) {
- return ap_get_brigade(f->next, b, mode, block, readbytes);
- }
-
- if (!ctx) {
- const char *tenc, *lenp;
- f->ctx = ctx = apr_palloc(f->r->pool, sizeof(*ctx));
- ctx->state = BODY_NONE;
- ctx->remaining = 0;
- ctx->limit_used = 0;
- ctx->eos_sent = 0;
-
- /* LimitRequestBody does not apply to proxied responses.
- * Consider implementing this check in its own filter.
- * Would adding a directive to limit the size of proxied
- * responses be useful?
- */
- if (!f->r->proxyreq) {
- ctx->limit = ap_get_limit_req_body(f->r);
- }
- else {
- ctx->limit = 0;
- }
-
- tenc = apr_table_get(f->r->headers_in, "Transfer-Encoding");
- lenp = apr_table_get(f->r->headers_in, "Content-Length");
-
- if (tenc) {
- if (!strcasecmp(tenc, "chunked")) {
- ctx->state = BODY_CHUNK;
- }
- }
- else if (lenp) {
- char *endstr;
-
- ctx->state = BODY_LENGTH;
- errno = 0;
-
- /* Protects against over/underflow, non-digit chars in the
- * string (excluding leading space) (the endstr checks)
- * and a negative number. */
- if (apr_strtoff(&ctx->remaining, lenp, &endstr, 10)
- || endstr == lenp || *endstr || ctx->remaining < 0) {
- apr_bucket_brigade *bb;
-
- ctx->remaining = 0;
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r,
- "Invalid Content-Length");
-
- bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
- e = ap_bucket_error_create(HTTP_REQUEST_ENTITY_TOO_LARGE, NULL,
- f->r->pool, f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
- ctx->eos_sent = 1;
- return ap_pass_brigade(f->r->output_filters, bb);
- }
-
- /* If we have a limit in effect and we know the C-L ahead of
- * time, stop it here if it is invalid.
- */
- if (ctx->limit && ctx->limit < ctx->remaining) {
- apr_bucket_brigade *bb;
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r,
- "Requested content-length of %" APR_OFF_T_FMT
- " is larger than the configured limit"
- " of %" APR_OFF_T_FMT, ctx->remaining, ctx->limit);
- bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
- e = ap_bucket_error_create(HTTP_REQUEST_ENTITY_TOO_LARGE, NULL,
- f->r->pool, f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
- ctx->eos_sent = 1;
- return ap_pass_brigade(f->r->output_filters, bb);
- }
- }
-
- /* If we don't have a request entity indicated by the headers, EOS.
- * (BODY_NONE is a valid intermediate state due to trailers,
- * but it isn't a valid starting state.)
- *
- * RFC 2616 Section 4.4 note 5 states that connection-close
- * is invalid for a request entity - request bodies must be
- * denoted by C-L or T-E: chunked.
- *
- * Note that since the proxy uses this filter to handle the
- * proxied *response*, proxy responses MUST be exempt.
- */
- if (ctx->state == BODY_NONE && f->r->proxyreq != PROXYREQ_RESPONSE) {
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(b, e);
- ctx->eos_sent = 1;
- return APR_SUCCESS;
- }
-
- /* Since we're about to read data, send 100-Continue if needed.
- * Only valid on chunked and C-L bodies where the C-L is > 0. */
- if ((ctx->state == BODY_CHUNK ||
- (ctx->state == BODY_LENGTH && ctx->remaining > 0)) &&
- f->r->expecting_100 && f->r->proto_num >= HTTP_VERSION(1,1)) {
- char *tmp;
- apr_bucket_brigade *bb;
-
- tmp = apr_pstrcat(f->r->pool, AP_SERVER_PROTOCOL, " ",
- status_lines[0], CRLF CRLF, NULL);
- bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
- e = apr_bucket_pool_create(tmp, strlen(tmp), f->r->pool,
- f->c->bucket_alloc);
- APR_BRIGADE_INSERT_HEAD(bb, e);
- e = apr_bucket_flush_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
-
- ap_pass_brigade(f->c->output_filters, bb);
- }
-
- /* We can't read the chunk until after sending 100 if required. */
- if (ctx->state == BODY_CHUNK) {
- char line[30];
- apr_bucket_brigade *bb;
- apr_size_t len = 30;
- apr_off_t brigade_length;
-
- bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
-
- rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE,
- APR_BLOCK_READ, 0);
-
- if (rv == APR_SUCCESS) {
- /* We have to check the length of the brigade we got back.
- * We will not accept partial lines.
- */
- rv = apr_brigade_length(bb, 1, &brigade_length);
- if (rv == APR_SUCCESS
- && brigade_length > f->r->server->limit_req_line) {
- rv = APR_ENOSPC;
- }
- if (rv == APR_SUCCESS) {
- rv = apr_brigade_flatten(bb, line, &len);
- if (rv == APR_SUCCESS) {
- ctx->remaining = get_chunk_size(line);
- }
- }
- }
- apr_brigade_cleanup(bb);
-
- /* Detect chunksize error (such as overflow) */
- if (rv != APR_SUCCESS || ctx->remaining < 0) {
- ctx->remaining = 0; /* Reset it in case we have to
- * come back here later */
- e = ap_bucket_error_create(HTTP_REQUEST_ENTITY_TOO_LARGE, NULL,
- f->r->pool,
- f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
- ctx->eos_sent = 1;
- return ap_pass_brigade(f->r->output_filters, bb);
- }
-
- if (!ctx->remaining) {
- /* Handle trailers by calling ap_get_mime_headers again! */
- ctx->state = BODY_NONE;
- ap_get_mime_headers(f->r);
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(b, e);
- ctx->eos_sent = 1;
- return APR_SUCCESS;
- }
- }
- }
-
- if (ctx->eos_sent) {
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(b, e);
- return APR_SUCCESS;
- }
-
- if (!ctx->remaining) {
- switch (ctx->state) {
- case BODY_NONE:
- break;
- case BODY_LENGTH:
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(b, e);
- ctx->eos_sent = 1;
- return APR_SUCCESS;
- case BODY_CHUNK:
- {
- char line[30];
- apr_bucket_brigade *bb;
- apr_size_t len = 30;
-
- bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
-
- /* We need to read the CRLF after the chunk. */
- rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE,
- APR_BLOCK_READ, 0);
- apr_brigade_cleanup(bb);
-
- if (rv == APR_SUCCESS) {
- /* Read the real chunk line. */
- rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE,
- APR_BLOCK_READ, 0);
- if (rv == APR_SUCCESS) {
- rv = apr_brigade_flatten(bb, line, &len);
- if (rv == APR_SUCCESS) {
- ctx->remaining = get_chunk_size(line);
- }
- }
- apr_brigade_cleanup(bb);
- }
-
- /* Detect chunksize error (such as overflow) */
- if (rv != APR_SUCCESS || ctx->remaining < 0) {
- ctx->remaining = 0; /* Reset it in case we have to
- * come back here later */
- e = ap_bucket_error_create(HTTP_REQUEST_ENTITY_TOO_LARGE,
- NULL, f->r->pool,
- f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
- ctx->eos_sent = 1;
- return ap_pass_brigade(f->r->output_filters, bb);
- }
-
- if (!ctx->remaining) {
- /* Handle trailers by calling ap_get_mime_headers again! */
- ctx->state = BODY_NONE;
- ap_get_mime_headers(f->r);
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(b, e);
- ctx->eos_sent = 1;
- return APR_SUCCESS;
- }
- }
- break;
- }
- }
-
- /* Ensure that the caller can not go over our boundary point. */
- if (ctx->state == BODY_LENGTH || ctx->state == BODY_CHUNK) {
- if (ctx->remaining < readbytes) {
- readbytes = ctx->remaining;
- }
- AP_DEBUG_ASSERT(readbytes > 0);
- }
-
- rv = ap_get_brigade(f->next, b, mode, block, readbytes);
-
- if (rv != APR_SUCCESS) {
- return rv;
- }
-
- /* How many bytes did we just read? */
- apr_brigade_length(b, 0, &totalread);
-
- /* If this happens, we have a bucket of unknown length. Die because
- * it means our assumptions have changed. */
- AP_DEBUG_ASSERT(totalread >= 0);
-
- if (ctx->state != BODY_NONE) {
- ctx->remaining -= totalread;
- }
-
- /* If we have no more bytes remaining on a C-L request,
- * save the callter a roundtrip to discover EOS.
- */
- if (ctx->state == BODY_LENGTH && ctx->remaining == 0) {
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(b, e);
- }
-
- /* We have a limit in effect. */
- if (ctx->limit) {
- /* FIXME: Note that we might get slightly confused on chunked inputs
- * as we'd need to compensate for the chunk lengths which may not
- * really count. This seems to be up for interpretation. */
- ctx->limit_used += totalread;
- if (ctx->limit < ctx->limit_used) {
- apr_bucket_brigade *bb;
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r,
- "Read content-length of %" APR_OFF_T_FMT
- " is larger than the configured limit"
- " of %" APR_OFF_T_FMT, ctx->limit_used, ctx->limit);
- bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
- e = ap_bucket_error_create(HTTP_REQUEST_ENTITY_TOO_LARGE, NULL,
- f->r->pool,
- f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
- ctx->eos_sent = 1;
- return ap_pass_brigade(f->r->output_filters, bb);
- }
- }
-
- return APR_SUCCESS;
-}
-
-/* The index is found by its offset from the x00 code of each level.
- * Although this is fast, it will need to be replaced if some nutcase
- * decides to define a high-numbered code before the lower numbers.
- * If that sad event occurs, replace the code below with a linear search
- * from status_lines[shortcut[i]] to status_lines[shortcut[i+1]-1];
- */
-AP_DECLARE(int) ap_index_of_response(int status)
-{
- static int shortcut[6] = {0, LEVEL_200, LEVEL_300, LEVEL_400,
- LEVEL_500, RESPONSE_CODES};
- int i, pos;
-
- if (status < 100) { /* Below 100 is illegal for HTTP status */
- return LEVEL_500;
- }
-
- for (i = 0; i < 5; i++) {
- status -= 100;
- if (status < 100) {
- pos = (status + shortcut[i]);
- if (pos < shortcut[i + 1]) {
- return pos;
- }
- else {
- return LEVEL_500; /* status unknown (falls in gap) */
- }
- }
- }
- return LEVEL_500; /* 600 or above is also illegal */
-}
-
-AP_DECLARE(const char *) ap_get_status_line(int status)
-{
- return status_lines[ap_index_of_response(status)];
-}
-
-typedef struct header_struct {
- apr_pool_t *pool;
- apr_bucket_brigade *bb;
-} header_struct;
-
-/* Send a single HTTP header field to the client. Note that this function
- * is used in calls to table_do(), so their interfaces are co-dependent.
- * In other words, don't change this one without checking table_do in alloc.c.
- * It returns true unless there was a write error of some kind.
- */
-static int form_header_field(header_struct *h,
- const char *fieldname, const char *fieldval)
-{
-#if APR_CHARSET_EBCDIC
- char *headfield;
- apr_size_t len;
- apr_size_t name_len;
- apr_size_t val_len;
- char *next;
-
- name_len = strlen(fieldname);
- val_len = strlen(fieldval);
- len = name_len + val_len + 4; /* 4 for ": " plus CRLF */
- headfield = (char *)apr_palloc(h->pool, len + 1);
- memcpy(headfield, fieldname, name_len);
- next = headfield + name_len;
- *next++ = ':';
- *next++ = ' ';
- memcpy(next, fieldval, val_len);
- next += val_len;
- *next++ = CR;
- *next++ = LF;
- *next = 0;
- ap_xlate_proto_to_ascii(headfield, len);
- apr_brigade_write(h->bb, NULL, NULL, headfield, len);
-#else
- struct iovec vec[4];
- struct iovec *v = vec;
- v->iov_base = (void *)fieldname;
- v->iov_len = strlen(fieldname);
- v++;
- v->iov_base = ": ";
- v->iov_len = sizeof(": ") - 1;
- v++;
- v->iov_base = (void *)fieldval;
- v->iov_len = strlen(fieldval);
- v++;
- v->iov_base = CRLF;
- v->iov_len = sizeof(CRLF) - 1;
- apr_brigade_writev(h->bb, NULL, NULL, vec, 4);
-#endif /* !APR_CHARSET_EBCDIC */
- return 1;
-}
-
-/* Send a request's HTTP response headers to the client.
- */
-static apr_status_t send_all_header_fields(header_struct *h,
- const request_rec *r)
-{
- const apr_array_header_t *elts;
- const apr_table_entry_t *t_elt;
- const apr_table_entry_t *t_end;
- struct iovec *vec;
- struct iovec *vec_next;
-
- elts = apr_table_elts(r->headers_out);
- if (elts->nelts == 0) {
- return APR_SUCCESS;
- }
- t_elt = (const apr_table_entry_t *)(elts->elts);
- t_end = t_elt + elts->nelts;
- vec = (struct iovec *)apr_palloc(h->pool, 4 * elts->nelts *
- sizeof(struct iovec));
- vec_next = vec;
-
- /* For each field, generate
- * name ": " value CRLF
- */
- do {
- vec_next->iov_base = (void*)(t_elt->key);
- vec_next->iov_len = strlen(t_elt->key);
- vec_next++;
- vec_next->iov_base = ": ";
- vec_next->iov_len = sizeof(": ") - 1;
- vec_next++;
- vec_next->iov_base = (void*)(t_elt->val);
- vec_next->iov_len = strlen(t_elt->val);
- vec_next++;
- vec_next->iov_base = CRLF;
- vec_next->iov_len = sizeof(CRLF) - 1;
- vec_next++;
- t_elt++;
- } while (t_elt < t_end);
-
-#if APR_CHARSET_EBCDIC
- {
- apr_size_t len;
- char *tmp = apr_pstrcatv(r->pool, vec, vec_next - vec, &len);
- ap_xlate_proto_to_ascii(tmp, len);
- return apr_brigade_write(h->bb, NULL, NULL, tmp, len);
- }
-#else
- return apr_brigade_writev(h->bb, NULL, NULL, vec, vec_next - vec);
-#endif
-}
-
-/*
- * Determine the protocol to use for the response. Potentially downgrade
- * to HTTP/1.0 in some situations and/or turn off keepalives.
- *
- * also prepare r->status_line.
- */
-static void basic_http_header_check(request_rec *r,
- const char **protocol)
-{
- if (r->assbackwards) {
- /* no such thing as a response protocol */
- return;
- }
-
- if (!r->status_line) {
- r->status_line = status_lines[ap_index_of_response(r->status)];
- }
-
- /* Note that we must downgrade before checking for force responses. */
- if (r->proto_num > HTTP_VERSION(1,0)
- && apr_table_get(r->subprocess_env, "downgrade-1.0")) {
- r->proto_num = HTTP_VERSION(1,0);
- }
-
- /* kludge around broken browsers when indicated by force-response-1.0
- */
- if (r->proto_num == HTTP_VERSION(1,0)
- && apr_table_get(r->subprocess_env, "force-response-1.0")) {
- *protocol = "HTTP/1.0";
- r->connection->keepalive = AP_CONN_CLOSE;
- }
- else {
- *protocol = AP_SERVER_PROTOCOL;
- }
-
-}
-
-/* fill "bb" with a barebones/initial HTTP response header */
-static void basic_http_header(request_rec *r, apr_bucket_brigade *bb,
- const char *protocol)
-{
- char *date;
- const char *server;
- header_struct h;
- struct iovec vec[4];
-
- if (r->assbackwards) {
- /* there are no headers to send */
- return;
- }
-
- /* Output the HTTP/1.x Status-Line and the Date and Server fields */
-
- vec[0].iov_base = (void *)protocol;
- vec[0].iov_len = strlen(protocol);
- vec[1].iov_base = (void *)" ";
- vec[1].iov_len = sizeof(" ") - 1;
- vec[2].iov_base = (void *)(r->status_line);
- vec[2].iov_len = strlen(r->status_line);
- vec[3].iov_base = (void *)CRLF;
- vec[3].iov_len = sizeof(CRLF) - 1;
-#if APR_CHARSET_EBCDIC
- {
- char *tmp;
- apr_size_t len;
- tmp = apr_pstrcatv(r->pool, vec, 4, &len);
- ap_xlate_proto_to_ascii(tmp, len);
- apr_brigade_write(bb, NULL, NULL, tmp, len);
- }
-#else
- apr_brigade_writev(bb, NULL, NULL, vec, 4);
-#endif
-
- date = apr_palloc(r->pool, APR_RFC822_DATE_LEN);
- ap_recent_rfc822_date(date, r->request_time);
-
- h.pool = r->pool;
- h.bb = bb;
- form_header_field(&h, "Date", date);
-
- /* keep the set-by-proxy server header, otherwise
- * generate a new server header */
- if (r->proxyreq != PROXYREQ_NONE) {
- server = apr_table_get(r->headers_out, "Server");
- if (server) {
- form_header_field(&h, "Server", server);
- }
- }
- else {
- form_header_field(&h, "Server", ap_get_server_version());
- }
-
- /* unset so we don't send them again */
- apr_table_unset(r->headers_out, "Date"); /* Avoid bogosity */
- apr_table_unset(r->headers_out, "Server");
-}
-
-AP_DECLARE(void) ap_basic_http_header(request_rec *r, apr_bucket_brigade *bb)
-{
- const char *protocol;
-
- basic_http_header_check(r, &protocol);
- basic_http_header(r, bb, protocol);
-}
-
-/* Navigator versions 2.x, 3.x and 4.0 betas up to and including 4.0b2
- * have a header parsing bug. If the terminating \r\n occur starting
- * at offset 256, 257 or 258 of output then it will not properly parse
- * the headers. Curiously it doesn't exhibit this problem at 512, 513.
- * We are guessing that this is because their initial read of a new request
- * uses a 256 byte buffer, and subsequent reads use a larger buffer.
- * So the problem might exist at different offsets as well.
- *
- * This should also work on keepalive connections assuming they use the
- * same small buffer for the first read of each new request.
- *
- * At any rate, we check the bytes written so far and, if we are about to
- * tickle the bug, we instead insert a bogus padding header. Since the bug
- * manifests as a broken image in Navigator, users blame the server. :(
- * It is more expensive to check the User-Agent than it is to just add the
- * bytes, so we haven't used the BrowserMatch feature here.
- */
-static void terminate_header(apr_bucket_brigade *bb)
-{
- char tmp[] = "X-Pad: avoid browser bug" CRLF;
- char crlf[] = CRLF;
- apr_off_t len;
- apr_size_t buflen;
-
- (void) apr_brigade_length(bb, 1, &len);
-
- if (len >= 255 && len <= 257) {
- buflen = strlen(tmp);
- ap_xlate_proto_to_ascii(tmp, buflen);
- apr_brigade_write(bb, NULL, NULL, tmp, buflen);
- }
- buflen = strlen(crlf);
- ap_xlate_proto_to_ascii(crlf, buflen);
- apr_brigade_write(bb, NULL, NULL, crlf, buflen);
-}
-
-/* Build the Allow field-value from the request handler method mask.
- * Note that we always allow TRACE, since it is handled below.
- */
-static char *make_allow(request_rec *r)
-{
- char *list;
- apr_int64_t mask;
- apr_array_header_t *allow = apr_array_make(r->pool, 10, sizeof(char *));
- apr_hash_index_t *hi = apr_hash_first(r->pool, methods_registry);
-
- mask = r->allowed_methods->method_mask;
-
- for (; hi; hi = apr_hash_next(hi)) {
- const void *key;
- void *val;
-
- apr_hash_this(hi, &key, NULL, &val);
- if ((mask & (AP_METHOD_BIT << *(int *)val)) != 0) {
- *(const char **)apr_array_push(allow) = key;
-
- /* the M_GET method actually refers to two methods */
- if (*(int *)val == M_GET)
- *(const char **)apr_array_push(allow) = "HEAD";
- }
- }
-
- /* TRACE is always allowed */
- *(const char **)apr_array_push(allow) = "TRACE";
-
- list = apr_array_pstrcat(r->pool, allow, ',');
-
- /* ### this is rather annoying. we should enforce registration of
- ### these methods */
- if ((mask & (AP_METHOD_BIT << M_INVALID))
- && (r->allowed_methods->method_list != NULL)
- && (r->allowed_methods->method_list->nelts != 0)) {
- int i;
- char **xmethod = (char **) r->allowed_methods->method_list->elts;
-
- /*
- * Append all of the elements of r->allowed_methods->method_list
- */
- for (i = 0; i < r->allowed_methods->method_list->nelts; ++i) {
- list = apr_pstrcat(r->pool, list, ",", xmethod[i], NULL);
- }
- }
-
- return list;
-}
-
-AP_DECLARE_NONSTD(int) ap_send_http_trace(request_rec *r)
-{
- int rv;
- apr_bucket_brigade *b;
- header_struct h;
-
- if (r->method_number != M_TRACE) {
- return DECLINED;
- }
-
- /* Get the original request */
- while (r->prev) {
- r = r->prev;
- }
-
- if ((rv = ap_setup_client_block(r, REQUEST_NO_BODY))) {
- return rv;
- }
-
- ap_set_content_type(r, "message/http");
-
- /* Now we recreate the request, and echo it back */
-
- b = apr_brigade_create(r->pool, r->connection->bucket_alloc);
- apr_brigade_putstrs(b, NULL, NULL, r->the_request, CRLF, NULL);
- h.pool = r->pool;
- h.bb = b;
- apr_table_do((int (*) (void *, const char *, const char *))
- form_header_field, (void *) &h, r->headers_in, NULL);
- apr_brigade_puts(b, NULL, NULL, CRLF);
- ap_pass_brigade(r->output_filters, b);
-
- return DONE;
-}
-
-AP_DECLARE(int) ap_send_http_options(request_rec *r)
-{
- if (r->assbackwards) {
- return DECLINED;
- }
-
- apr_table_setn(r->headers_out, "Allow", make_allow(r));
-
- /* the request finalization will send an EOS, which will flush all
- * the headers out (including the Allow header)
- */
-
- return OK;
-}
-
-/* This routine is called by apr_table_do and merges all instances of
- * the passed field values into a single array that will be further
- * processed by some later routine. Originally intended to help split
- * and recombine multiple Vary fields, though it is generic to any field
- * consisting of comma/space-separated tokens.
- */
-static int uniq_field_values(void *d, const char *key, const char *val)
-{
- apr_array_header_t *values;
- char *start;
- char *e;
- char **strpp;
- int i;
-
- values = (apr_array_header_t *)d;
-
- e = apr_pstrdup(values->pool, val);
-
- do {
- /* Find a non-empty fieldname */
-
- while (*e == ',' || apr_isspace(*e)) {
- ++e;
- }
- if (*e == '\0') {
- break;
- }
- start = e;
- while (*e != '\0' && *e != ',' && !apr_isspace(*e)) {
- ++e;
- }
- if (*e != '\0') {
- *e++ = '\0';
- }
-
- /* Now add it to values if it isn't already represented.
- * Could be replaced by a ap_array_strcasecmp() if we had one.
- */
- for (i = 0, strpp = (char **) values->elts; i < values->nelts;
- ++i, ++strpp) {
- if (*strpp && strcasecmp(*strpp, start) == 0) {
- break;
- }
- }
- if (i == values->nelts) { /* if not found */
- *(char **)apr_array_push(values) = start;
- }
- } while (*e != '\0');
-
- return 1;
-}
-
-/*
- * Since some clients choke violently on multiple Vary fields, or
- * Vary fields with duplicate tokens, combine any multiples and remove
- * any duplicates.
- */
-static void fixup_vary(request_rec *r)
-{
- apr_array_header_t *varies;
-
- varies = apr_array_make(r->pool, 5, sizeof(char *));
-
- /* Extract all Vary fields from the headers_out, separate each into
- * its comma-separated fieldname values, and then add them to varies
- * if not already present in the array.
- */
- apr_table_do((int (*)(void *, const char *, const char *))uniq_field_values,
- (void *) varies, r->headers_out, "Vary", NULL);
-
- /* If we found any, replace old Vary fields with unique-ified value */
-
- if (varies->nelts > 0) {
- apr_table_setn(r->headers_out, "Vary",
- apr_array_pstrcat(r->pool, varies, ','));
- }
-}
-
-AP_DECLARE(void) ap_set_content_type(request_rec *r, const char *ct)
-{
- if (!ct) {
- r->content_type = NULL;
- }
- else if (!r->content_type || strcmp(r->content_type, ct)) {
- r->content_type = ct;
-
- /* Insert filters requested by the AddOutputFiltersByType
- * configuration directive. Content-type filters must be
- * inserted after the content handlers have run because
- * only then, do we reliably know the content-type.
- */
- ap_add_output_filters_by_type(r);
- }
-}
-
-typedef struct header_filter_ctx {
- int headers_sent;
-} header_filter_ctx;
-
-AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f,
- apr_bucket_brigade *b)
-{
- request_rec *r = f->r;
- conn_rec *c = r->connection;
- const char *clheader;
- const char *protocol;
- apr_bucket *e;
- apr_bucket_brigade *b2;
- header_struct h;
- header_filter_ctx *ctx = f->ctx;
-
- AP_DEBUG_ASSERT(!r->main);
-
- if (r->header_only) {
- if (!ctx) {
- ctx = f->ctx = apr_pcalloc(r->pool, sizeof(header_filter_ctx));
- }
- else if (ctx->headers_sent) {
- apr_brigade_destroy(b);
- return OK;
- }
- }
-
- for (e = APR_BRIGADE_FIRST(b);
- e != APR_BRIGADE_SENTINEL(b);
- e = APR_BUCKET_NEXT(e))
- {
- if (e->type == &ap_bucket_type_error) {
- ap_bucket_error *eb = e->data;
-
- ap_die(eb->status, r);
- return AP_FILTER_ERROR;
- }
- }
-
- if (r->assbackwards) {
- r->sent_bodyct = 1;
- ap_remove_output_filter(f);
- return ap_pass_brigade(f->next, b);
- }
-
- /*
- * Now that we are ready to send a response, we need to combine the two
- * header field tables into a single table. If we don't do this, our
- * later attempts to set or unset a given fieldname might be bypassed.
- */
- if (!apr_is_empty_table(r->err_headers_out)) {
- r->headers_out = apr_table_overlay(r->pool, r->err_headers_out,
- r->headers_out);
- }
-
- /*
- * Remove the 'Vary' header field if the client can't handle it.
- * Since this will have nasty effects on HTTP/1.1 caches, force
- * the response into HTTP/1.0 mode.
- *
- * Note: the force-response-1.0 should come before the call to
- * basic_http_header_check()
- */
- if (apr_table_get(r->subprocess_env, "force-no-vary") != NULL) {
- apr_table_unset(r->headers_out, "Vary");
- r->proto_num = HTTP_VERSION(1,0);
- apr_table_set(r->subprocess_env, "force-response-1.0", "1");
- }
- else {
- fixup_vary(r);
- }
-
- /*
- * Now remove any ETag response header field if earlier processing
- * says so (such as a 'FileETag None' directive).
- */
- if (apr_table_get(r->notes, "no-etag") != NULL) {
- apr_table_unset(r->headers_out, "ETag");
- }
-
- /* determine the protocol and whether we should use keepalives. */
- basic_http_header_check(r, &protocol);
- ap_set_keepalive(r);
-
- if (r->chunked) {
- apr_table_mergen(r->headers_out, "Transfer-Encoding", "chunked");
- apr_table_unset(r->headers_out, "Content-Length");
- }
-
- apr_table_setn(r->headers_out, "Content-Type",
- ap_make_content_type(r, r->content_type));
-
- if (r->content_encoding) {
- apr_table_setn(r->headers_out, "Content-Encoding",
- r->content_encoding);
- }
-
- if (!apr_is_empty_array(r->content_languages)) {
- int i;
- char **languages = (char **)(r->content_languages->elts);
- for (i = 0; i < r->content_languages->nelts; ++i) {
- apr_table_mergen(r->headers_out, "Content-Language", languages[i]);
- }
- }
-
- /*
- * Control cachability for non-cachable responses if not already set by
- * some other part of the server configuration.
- */
- if (r->no_cache && !apr_table_get(r->headers_out, "Expires")) {
- char *date = apr_palloc(r->pool, APR_RFC822_DATE_LEN);
- ap_recent_rfc822_date(date, r->request_time);
- apr_table_addn(r->headers_out, "Expires", date);
- }
-
- /* This is a hack, but I can't find anyway around it. The idea is that
- * we don't want to send out 0 Content-Lengths if it is a head request.
- * This happens when modules try to outsmart the server, and return
- * if they see a HEAD request. Apache 1.3 handlers were supposed to
- * just return in that situation, and the core handled the HEAD. In
- * 2.0, if a handler returns, then the core sends an EOS bucket down
- * the filter stack, and the content-length filter computes a C-L of
- * zero and that gets put in the headers, and we end up sending a
- * zero C-L to the client. We can't just remove the C-L filter,
- * because well behaved 2.0 handlers will send their data down the stack,
- * and we will compute a real C-L for the head request. RBB
- */
- if (r->header_only
- && (clheader = apr_table_get(r->headers_out, "Content-Length"))
- && !strcmp(clheader, "0")) {
- apr_table_unset(r->headers_out, "Content-Length");
- }
-
- b2 = apr_brigade_create(r->pool, c->bucket_alloc);
- basic_http_header(r, b2, protocol);
-
- h.pool = r->pool;
- h.bb = b2;
-
- if (r->status == HTTP_NOT_MODIFIED) {
- apr_table_do((int (*)(void *, const char *, const char *)) form_header_field,
- (void *) &h, r->headers_out,
- "Connection",
- "Keep-Alive",
- "ETag",
- "Content-Location",
- "Expires",
- "Cache-Control",
- "Vary",
- "Warning",
- "WWW-Authenticate",
- "Proxy-Authenticate",
- "Set-Cookie",
- "Set-Cookie2",
- NULL);
- }
- else {
- send_all_header_fields(&h, r);
- }
-
- terminate_header(b2);
-
- ap_pass_brigade(f->next, b2);
-
- if (r->header_only) {
- apr_brigade_destroy(b);
- ctx->headers_sent = 1;
- return OK;
- }
-
- r->sent_bodyct = 1; /* Whatever follows is real body stuff... */
-
- if (r->chunked) {
- /* We can't add this filter until we have already sent the headers.
- * If we add it before this point, then the headers will be chunked
- * as well, and that is just wrong.
- */
- ap_add_output_filter("CHUNK", NULL, r, r->connection);
- }
-
- /* Don't remove this filter until after we have added the CHUNK filter.
- * Otherwise, f->next won't be the CHUNK filter and thus the first
- * brigade won't be chunked properly.
- */
- ap_remove_output_filter(f);
- return ap_pass_brigade(f->next, b);
-}
-
-/* Here we deal with getting the request message body from the client.
- * Whether or not the request contains a body is signaled by the presence
- * of a non-zero Content-Length or by a Transfer-Encoding: chunked.
- *
- * Note that this is more complicated than it was in Apache 1.1 and prior
- * versions, because chunked support means that the module does less.
- *
- * The proper procedure is this:
- *
- * 1. Call setup_client_block() near the beginning of the request
- * handler. This will set up all the necessary properties, and will
- * return either OK, or an error code. If the latter, the module should
- * return that error code. The second parameter selects the policy to
- * apply if the request message indicates a body, and how a chunked
- * transfer-coding should be interpreted. Choose one of
- *
- * REQUEST_NO_BODY Send 413 error if message has any body
- * REQUEST_CHUNKED_ERROR Send 411 error if body without Content-Length
- * REQUEST_CHUNKED_DECHUNK If chunked, remove the chunks for me.
- *
- * In order to use the last two options, the caller MUST provide a buffer
- * large enough to hold a chunk-size line, including any extensions.
- *
- * 2. When you are ready to read a body (if any), call should_client_block().
- * This will tell the module whether or not to read input. If it is 0,
- * the module should assume that there is no message body to read.
- * This step also sends a 100 Continue response to HTTP/1.1 clients,
- * so should not be called until the module is *definitely* ready to
- * read content. (otherwise, the point of the 100 response is defeated).
- * Never call this function more than once.
- *
- * 3. Finally, call get_client_block in a loop. Pass it a buffer and its size.
- * It will put data into the buffer (not necessarily a full buffer), and
- * return the length of the input block. When it is done reading, it will
- * return 0 if EOF, or -1 if there was an error.
- * If an error occurs on input, we force an end to keepalive.
- */
-
-AP_DECLARE(int) ap_setup_client_block(request_rec *r, int read_policy)
-{
- const char *tenc = apr_table_get(r->headers_in, "Transfer-Encoding");
- const char *lenp = apr_table_get(r->headers_in, "Content-Length");
-
- r->read_body = read_policy;
- r->read_chunked = 0;
- r->remaining = 0;
-
- if (tenc) {
- if (strcasecmp(tenc, "chunked")) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "Unknown Transfer-Encoding %s", tenc);
- return HTTP_NOT_IMPLEMENTED;
- }
- if (r->read_body == REQUEST_CHUNKED_ERROR) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "chunked Transfer-Encoding forbidden: %s", r->uri);
- return (lenp) ? HTTP_BAD_REQUEST : HTTP_LENGTH_REQUIRED;
- }
-
- r->read_chunked = 1;
- }
- else if (lenp) {
- char *endstr;
-
- if (apr_strtoff(&r->remaining, lenp, &endstr, 10)
- || *endstr || r->remaining < 0) {
- r->remaining = 0;
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "Invalid Content-Length");
- return HTTP_BAD_REQUEST;
- }
- }
-
- if ((r->read_body == REQUEST_NO_BODY)
- && (r->read_chunked || (r->remaining > 0))) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "%s with body is not allowed for %s", r->method, r->uri);
- return HTTP_REQUEST_ENTITY_TOO_LARGE;
- }
-
-#ifdef AP_DEBUG
- {
- /* Make sure ap_getline() didn't leave any droppings. */
- core_request_config *req_cfg =
- (core_request_config *)ap_get_module_config(r->request_config,
- &core_module);
- AP_DEBUG_ASSERT(APR_BRIGADE_EMPTY(req_cfg->bb));
- }
-#endif
-
- return OK;
-}
-
-AP_DECLARE(int) ap_should_client_block(request_rec *r)
-{
- /* First check if we have already read the request body */
-
- if (r->read_length || (!r->read_chunked && (r->remaining <= 0))) {
- return 0;
- }
-
- return 1;
-}
-
-/**
- * Parse a chunk extension, detect overflow.
- * There are two error cases:
- * 1) If the conversion would require too many bits, a -1 is returned.
- * 2) If the conversion used the correct number of bits, but an overflow
- * caused only the sign bit to flip, then that negative number is
- * returned.
- * In general, any negative number can be considered an overflow error.
- */
-static long get_chunk_size(char *b)
-{
- long chunksize = 0;
- size_t chunkbits = sizeof(long) * 8;
-
- /* Skip leading zeros */
- while (*b == '0') {
- ++b;
- }
-
- while (apr_isxdigit(*b) && (chunkbits > 0)) {
- int xvalue = 0;
-
- if (*b >= '0' && *b <= '9') {
- xvalue = *b - '0';
- }
- else if (*b >= 'A' && *b <= 'F') {
- xvalue = *b - 'A' + 0xa;
- }
- else if (*b >= 'a' && *b <= 'f') {
- xvalue = *b - 'a' + 0xa;
- }
-
- chunksize = (chunksize << 4) | xvalue;
- chunkbits -= 4;
- ++b;
- }
- if (apr_isxdigit(*b) && (chunkbits <= 0)) {
- /* overflow */
- return -1;
- }
-
- return chunksize;
-}
-
-/* get_client_block is called in a loop to get the request message body.
- * This is quite simple if the client includes a content-length
- * (the normal case), but gets messy if the body is chunked. Note that
- * r->remaining is used to maintain state across calls and that
- * r->read_length is the total number of bytes given to the caller
- * across all invocations. It is messy because we have to be careful not
- * to read past the data provided by the client, since these reads block.
- * Returns 0 on End-of-body, -1 on error or premature chunk end.
- *
- */
-AP_DECLARE(long) ap_get_client_block(request_rec *r, char *buffer,
- apr_size_t bufsiz)
-{
- apr_status_t rv;
- apr_bucket_brigade *bb;
-
- if (r->remaining < 0 || (!r->read_chunked && r->remaining == 0)) {
- return 0;
- }
-
- bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
- if (bb == NULL) {
- r->connection->keepalive = AP_CONN_CLOSE;
- return -1;
- }
-
- rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES,
- APR_BLOCK_READ, bufsiz);
-
- /* We lose the failure code here. This is why ap_get_client_block should
- * not be used.
- */
- if (rv != APR_SUCCESS) {
- /* if we actually fail here, we want to just return and
- * stop trying to read data from the client.
- */
- r->connection->keepalive = AP_CONN_CLOSE;
- apr_brigade_destroy(bb);
- return -1;
- }
-
- /* If this fails, it means that a filter is written incorrectly and that
- * it needs to learn how to properly handle APR_BLOCK_READ requests by
- * returning data when requested.
- */
- AP_DEBUG_ASSERT(!APR_BRIGADE_EMPTY(bb));
-
- /* Check to see if EOS in the brigade.
- *
- * If so, we have to leave a nugget for the *next* ap_get_client_block
- * call to return 0.
- */
- if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) {
- if (r->read_chunked) {
- r->remaining = -1;
- }
- else {
- r->remaining = 0;
- }
- }
-
- rv = apr_brigade_flatten(bb, buffer, &bufsiz);
- if (rv != APR_SUCCESS) {
- apr_brigade_destroy(bb);
- return -1;
- }
-
- /* XXX yank me? */
- r->read_length += bufsiz;
-
- apr_brigade_destroy(bb);
- return bufsiz;
-}
-
-/* In HTTP/1.1, any method can have a body. However, most GET handlers
- * wouldn't know what to do with a request body if they received one.
- * This helper routine tests for and reads any message body in the request,
- * simply discarding whatever it receives. We need to do this because
- * failing to read the request body would cause it to be interpreted
- * as the next request on a persistent connection.
- *
- * Since we return an error status if the request is malformed, this
- * routine should be called at the beginning of a no-body handler, e.g.,
- *
- * if ((retval = ap_discard_request_body(r)) != OK) {
- * return retval;
- * }
- */
-AP_DECLARE(int) ap_discard_request_body(request_rec *r)
-{
- apr_bucket_brigade *bb;
- int rv, seen_eos;
-
- /* Sometimes we'll get in a state where the input handling has
- * detected an error where we want to drop the connection, so if
- * that's the case, don't read the data as that is what we're trying
- * to avoid.
- *
- * This function is also a no-op on a subrequest.
- */
- if (r->main || r->connection->keepalive == AP_CONN_CLOSE ||
- ap_status_drops_connection(r->status)) {
- return OK;
- }
-
- bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
- seen_eos = 0;
- do {
- apr_bucket *bucket;
-
- rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES,
- APR_BLOCK_READ, HUGE_STRING_LEN);
-
- if (rv != APR_SUCCESS) {
- /* FIXME: If we ever have a mapping from filters (apr_status_t)
- * to HTTP error codes, this would be a good place for them.
- *
- * If we received the special case AP_FILTER_ERROR, it means
- * that the filters have already handled this error.
- * Otherwise, we should assume we have a bad request.
- */
- if (rv == AP_FILTER_ERROR) {
- apr_brigade_destroy(bb);
- return rv;
- }
- else {
- apr_brigade_destroy(bb);
- return HTTP_BAD_REQUEST;
- }
- }
-
- for (bucket = APR_BRIGADE_FIRST(bb);
- bucket != APR_BRIGADE_SENTINEL(bb);
- bucket = APR_BUCKET_NEXT(bucket))
- {
- const char *data;
- apr_size_t len;
-
- if (APR_BUCKET_IS_EOS(bucket)) {
- seen_eos = 1;
- break;
- }
-
- /* These are metadata buckets. */
- if (bucket->length == 0) {
- continue;
- }
-
- /* We MUST read because in case we have an unknown-length
- * bucket or one that morphs, we want to exhaust it.
- */
- rv = apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);
- if (rv != APR_SUCCESS) {
- apr_brigade_destroy(bb);
- return HTTP_BAD_REQUEST;
- }
- }
- apr_brigade_cleanup(bb);
- } while (!seen_eos);
-
- return OK;
-}
-
-static const char *add_optional_notes(request_rec *r,
- const char *prefix,
- const char *key,
- const char *suffix)
-{
- const char *notes, *result;
-
- if ((notes = apr_table_get(r->notes, key)) == NULL) {
- result = apr_pstrcat(r->pool, prefix, suffix, NULL);
- }
- else {
- result = apr_pstrcat(r->pool, prefix, notes, suffix, NULL);
- }
-
- return result;
-}
-
-/* construct and return the default error message for a given
- * HTTP defined error code
- */
-static const char *get_canned_error_string(int status,
- request_rec *r,
- const char *location)
-{
- apr_pool_t *p = r->pool;
- const char *error_notes, *h1, *s1;
-
- switch (status) {
- case HTTP_MOVED_PERMANENTLY:
- case HTTP_MOVED_TEMPORARILY:
- case HTTP_TEMPORARY_REDIRECT:
- return(apr_pstrcat(p,
- "The document has moved pool, location),
- "\">here.
\n",
- NULL));
- case HTTP_SEE_OTHER:
- return(apr_pstrcat(p,
- "The answer to your request is located "
- "pool, location),
- "\">here.
\n",
- NULL));
- case HTTP_USE_PROXY:
- return(apr_pstrcat(p,
- "This resource is only accessible "
- "through the proxy\n",
- ap_escape_html(r->pool, location),
- "
\nYou will need to configure "
- "your client to use that proxy.
\n",
- NULL));
- case HTTP_PROXY_AUTHENTICATION_REQUIRED:
- case HTTP_UNAUTHORIZED:
- return("This server could not verify that you\n"
- "are authorized to access the document\n"
- "requested. Either you supplied the wrong\n"
- "credentials (e.g., bad password), or your\n"
- "browser doesn't understand how to supply\n"
- "the credentials required.
\n");
- case HTTP_BAD_REQUEST:
- return(add_optional_notes(r,
- "Your browser sent a request that "
- "this server could not understand.
\n",
- "error-notes",
- "
\n"));
- case HTTP_FORBIDDEN:
- return(apr_pstrcat(p,
- "You don't have permission to access ",
- ap_escape_html(r->pool, r->uri),
- "\non this server.
\n",
- NULL));
- case HTTP_NOT_FOUND:
- return(apr_pstrcat(p,
- "The requested URL ",
- ap_escape_html(r->pool, r->uri),
- " was not found on this server.
\n",
- NULL));
- case HTTP_METHOD_NOT_ALLOWED:
- return(apr_pstrcat(p,
- "The requested method ", r->method,
- " is not allowed for the URL ",
- ap_escape_html(r->pool, r->uri),
- ".
\n",
- NULL));
- case HTTP_NOT_ACCEPTABLE:
- s1 = apr_pstrcat(p,
- "An appropriate representation of the "
- "requested resource ",
- ap_escape_html(r->pool, r->uri),
- " could not be found on this server.
\n",
- NULL);
- return(add_optional_notes(r, s1, "variant-list", ""));
- case HTTP_MULTIPLE_CHOICES:
- return(add_optional_notes(r, "", "variant-list", ""));
- case HTTP_LENGTH_REQUIRED:
- s1 = apr_pstrcat(p,
- "A request of the requested method ",
- r->method,
- " requires a valid Content-length.
\n",
- NULL);
- return(add_optional_notes(r, s1, "error-notes", "
\n"));
- case HTTP_PRECONDITION_FAILED:
- return(apr_pstrcat(p,
- "The precondition on the request "
- "for the URL ",
- ap_escape_html(r->pool, r->uri),
- " evaluated to false.
\n",
- NULL));
- case HTTP_NOT_IMPLEMENTED:
- s1 = apr_pstrcat(p,
- "",
- ap_escape_html(r->pool, r->method), " to ",
- ap_escape_html(r->pool, r->uri),
- " not supported.
\n",
- NULL);
- return(add_optional_notes(r, s1, "error-notes", "
\n"));
- case HTTP_BAD_GATEWAY:
- s1 = "The proxy server received an invalid" CRLF
- "response from an upstream server.
" CRLF;
- return(add_optional_notes(r, s1, "error-notes", "
\n"));
- case HTTP_VARIANT_ALSO_VARIES:
- return(apr_pstrcat(p,
- "A variant for the requested "
- "resource\n
\n",
- ap_escape_html(r->pool, r->uri),
- "\n
\nis itself a negotiable resource. "
- "This indicates a configuration error.
\n",
- NULL));
- case HTTP_REQUEST_TIME_OUT:
- return("Server timeout waiting for the HTTP request from the client.
\n");
- case HTTP_GONE:
- return(apr_pstrcat(p,
- "The requested resource
",
- ap_escape_html(r->pool, r->uri),
- "
\nis no longer available on this server "
- "and there is no forwarding address.\n"
- "Please remove all references to this "
- "resource.
\n",
- NULL));
- case HTTP_REQUEST_ENTITY_TOO_LARGE:
- return(apr_pstrcat(p,
- "The requested resource
",
- ap_escape_html(r->pool, r->uri), "
\n",
- "does not allow request data with ",
- r->method,
- " requests, or the amount of data provided in\n"
- "the request exceeds the capacity limit.\n",
- NULL));
- case HTTP_REQUEST_URI_TOO_LARGE:
- s1 = "The requested URL's length exceeds the capacity\n"
- "limit for this server.
\n";
- return(add_optional_notes(r, s1, "error-notes", "
\n"));
- case HTTP_UNSUPPORTED_MEDIA_TYPE:
- return("The supplied request data is not in a format\n"
- "acceptable for processing by this resource.
\n");
- case HTTP_RANGE_NOT_SATISFIABLE:
- return("None of the range-specifier values in the Range\n"
- "request-header field overlap the current extent\n"
- "of the selected resource.
\n");
- case HTTP_EXPECTATION_FAILED:
- return(apr_pstrcat(p,
- "The expectation given in the Expect "
- "request-header"
- "\nfield could not be met by this server.
\n"
- "The client sent
\n Expect: ",
- apr_table_get(r->headers_in, "Expect"),
- "\n
\n"
- "but we only allow the 100-continue "
- "expectation.\n",
- NULL));
- case HTTP_UNPROCESSABLE_ENTITY:
- return("The server understands the media type of the\n"
- "request entity, but was unable to process the\n"
- "contained instructions.
\n");
- case HTTP_LOCKED:
- return("The requested resource is currently locked.\n"
- "The lock must be released or proper identification\n"
- "given before the method can be applied.
\n");
- case HTTP_FAILED_DEPENDENCY:
- return("The method could not be performed on the resource\n"
- "because the requested action depended on another\n"
- "action and that other action failed.
\n");
- case HTTP_UPGRADE_REQUIRED:
- return("The requested resource can only be retrieved\n"
- "using SSL. The server is willing to upgrade the current\n"
- "connection to SSL, but your client doesn't support it.\n"
- "Either upgrade your client, or try requesting the page\n"
- "using https://\n");
- case HTTP_INSUFFICIENT_STORAGE:
- return("
The method could not be performed on the resource\n"
- "because the server is unable to store the\n"
- "representation needed to successfully complete the\n"
- "request. There is insufficient free space left in\n"
- "your storage allocation.
\n");
- case HTTP_SERVICE_UNAVAILABLE:
- return("The server is temporarily unable to service your\n"
- "request due to maintenance downtime or capacity\n"
- "problems. Please try again later.
\n");
- case HTTP_GATEWAY_TIME_OUT:
- return("The proxy server did not receive a timely response\n"
- "from the upstream server.
\n");
- case HTTP_NOT_EXTENDED:
- return("A mandatory extension policy in the request is not\n"
- "accepted by the server for this resource.
\n");
- default: /* HTTP_INTERNAL_SERVER_ERROR */
- /*
- * This comparison to expose error-notes could be modified to
- * use a configuration directive and export based on that
- * directive. For now "*" is used to designate an error-notes
- * that is totally safe for any user to see (ie lacks paths,
- * database passwords, etc.)
- */
- if (((error_notes = apr_table_get(r->notes,
- "error-notes")) != NULL)
- && (h1 = apr_table_get(r->notes, "verbose-error-to")) != NULL
- && (strcmp(h1, "*") == 0)) {
- return(apr_pstrcat(p, error_notes, "\n", NULL));
- }
- else {
- return(apr_pstrcat(p,
- "The server encountered an internal "
- "error or\n"
- "misconfiguration and was unable to complete\n"
- "your request.
\n"
- "Please contact the server "
- "administrator,\n ",
- ap_escape_html(r->pool,
- r->server->server_admin),
- " and inform them of the time the "
- "error occurred,\n"
- "and anything you might have done that "
- "may have\n"
- "caused the error.
\n"
- "More information about this error "
- "may be available\n"
- "in the server error log.
\n",
- NULL));
- }
- /*
- * It would be nice to give the user the information they need to
- * fix the problem directly since many users don't have access to
- * the error_log (think University sites) even though they can easily
- * get this error by misconfiguring an htaccess file. However, the
- * e error notes tend to include the real file pathname in this case,
- * which some people consider to be a breach of privacy. Until we
- * can figure out a way to remove the pathname, leave this commented.
- *
- * if ((error_notes = apr_table_get(r->notes,
- * "error-notes")) != NULL) {
- * return(apr_pstrcat(p, error_notes, "\n", NULL);
- * }
- * else {
- * return "";
- * }
- */
- }
-}
-
-/* We should have named this send_canned_response, since it is used for any
- * response that can be generated by the server from the request record.
- * This includes all 204 (no content), 3xx (redirect), 4xx (client error),
- * and 5xx (server error) messages that have not been redirected to another
- * handler via the ErrorDocument feature.
- */
-AP_DECLARE(void) ap_send_error_response(request_rec *r, int recursive_error)
-{
- int status = r->status;
- int idx = ap_index_of_response(status);
- char *custom_response;
- const char *location = apr_table_get(r->headers_out, "Location");
-
- /* At this point, we are starting the response over, so we have to reset
- * this value.
- */
- r->eos_sent = 0;
-
- /* and we need to get rid of any RESOURCE filters that might be lurking
- * around, thinking they are in the middle of the original request
- */
-
- r->output_filters = r->proto_output_filters;
-
- ap_run_insert_error_filter(r);
-
- /*
- * It's possible that the Location field might be in r->err_headers_out
- * instead of r->headers_out; use the latter if possible, else the
- * former.
- */
- if (location == NULL) {
- location = apr_table_get(r->err_headers_out, "Location");
- }
- /* We need to special-case the handling of 204 and 304 responses,
- * since they have specific HTTP requirements and do not include a
- * message body. Note that being assbackwards here is not an option.
- */
- if (status == HTTP_NOT_MODIFIED) {
- ap_finalize_request_protocol(r);
- return;
- }
-
- if (status == HTTP_NO_CONTENT) {
- ap_finalize_request_protocol(r);
- return;
- }
-
- if (!r->assbackwards) {
- apr_table_t *tmp = r->headers_out;
-
- /* For all HTTP/1.x responses for which we generate the message,
- * we need to avoid inheriting the "normal status" header fields
- * that may have been set by the request handler before the
- * error or redirect, except for Location on external redirects.
- */
- r->headers_out = r->err_headers_out;
- r->err_headers_out = tmp;
- apr_table_clear(r->err_headers_out);
-
- if (ap_is_HTTP_REDIRECT(status) || (status == HTTP_CREATED)) {
- if ((location != NULL) && *location) {
- apr_table_setn(r->headers_out, "Location", location);
- }
- else {
- location = ""; /* avoids coredump when printing, below */
- }
- }
-
- r->content_languages = NULL;
- r->content_encoding = NULL;
- r->clength = 0;
- ap_set_content_type(r, "text/html; charset=iso-8859-1");
-
- if ((status == HTTP_METHOD_NOT_ALLOWED)
- || (status == HTTP_NOT_IMPLEMENTED)) {
- apr_table_setn(r->headers_out, "Allow", make_allow(r));
- }
-
- if (r->header_only) {
- ap_finalize_request_protocol(r);
- return;
- }
- }
-
- if ((custom_response = ap_response_code_string(r, idx))) {
- /*
- * We have a custom response output. This should only be
- * a text-string to write back. But if the ErrorDocument
- * was a local redirect and the requested resource failed
- * for any reason, the custom_response will still hold the
- * redirect URL. We don't really want to output this URL
- * as a text message, so first check the custom response
- * string to ensure that it is a text-string (using the
- * same test used in ap_die(), i.e. does it start with a ").
- *
- * If it's not a text string, we've got a recursive error or
- * an external redirect. If it's a recursive error, ap_die passes
- * us the second error code so we can write both, and has already
- * backed up to the original error. If it's an external redirect,
- * it hasn't happened yet; we may never know if it fails.
- */
- if (custom_response[0] == '\"') {
- ap_rputs(custom_response + 1, r);
- ap_finalize_request_protocol(r);
- return;
- }
- }
- {
- const char *title = status_lines[idx];
- const char *h1;
-
- /* Accept a status_line set by a module, but only if it begins
- * with the 3 digit status code
- */
- if (r->status_line != NULL
- && strlen(r->status_line) > 4 /* long enough */
- && apr_isdigit(r->status_line[0])
- && apr_isdigit(r->status_line[1])
- && apr_isdigit(r->status_line[2])
- && apr_isspace(r->status_line[3])
- && apr_isalnum(r->status_line[4])) {
- title = r->status_line;
- }
-
- /* folks decided they didn't want the error code in the H1 text */
- h1 = &title[4];
-
- /* can't count on a charset filter being in place here,
- * so do ebcdic->ascii translation explicitly (if needed)
- */
-
- ap_rvputs_proto_in_ascii(r,
- DOCTYPE_HTML_2_0
- "\n", title,
- "\n\n", h1, "
\n",
- NULL);
-
- ap_rvputs_proto_in_ascii(r,
- get_canned_error_string(status, r, location),
- NULL);
-
- if (recursive_error) {
- ap_rvputs_proto_in_ascii(r, "Additionally, a ",
- status_lines[ap_index_of_response(recursive_error)],
- "\nerror was encountered while trying to use an "
- "ErrorDocument to handle the request.
\n", NULL);
- }
- ap_rvputs_proto_in_ascii(r, ap_psignature("
\n", r), NULL);
- ap_rvputs_proto_in_ascii(r, "\n", NULL);
- }
- ap_finalize_request_protocol(r);
-}
-
-/*
- * Create a new method list with the specified number of preallocated
- * extension slots.
- */
-AP_DECLARE(ap_method_list_t *) ap_make_method_list(apr_pool_t *p, int nelts)
-{
- ap_method_list_t *ml;
-
- ml = (ap_method_list_t *) apr_palloc(p, sizeof(ap_method_list_t));
- ml->method_mask = 0;
- ml->method_list = apr_array_make(p, nelts, sizeof(char *));
- return ml;
-}
-
-/*
- * Make a copy of a method list (primarily for subrequests that may
- * subsequently change it; don't want them changing the parent's, too!).
- */
-AP_DECLARE(void) ap_copy_method_list(ap_method_list_t *dest,
- ap_method_list_t *src)
-{
- int i;
- char **imethods;
- char **omethods;
-
- dest->method_mask = src->method_mask;
- imethods = (char **) src->method_list->elts;
- for (i = 0; i < src->method_list->nelts; ++i) {
- omethods = (char **) apr_array_push(dest->method_list);
- *omethods = apr_pstrdup(dest->method_list->pool, imethods[i]);
- }
-}
-
-/*
- * Invoke a callback routine for each method in the specified list.
- */
-AP_DECLARE_NONSTD(void) ap_method_list_do(int (*comp) (void *urec,
- const char *mname,
- int mnum),
- void *rec,
- const ap_method_list_t *ml, ...)
-{
- va_list vp;
- va_start(vp, ml);
- ap_method_list_vdo(comp, rec, ml, vp);
- va_end(vp);
-}
-
-AP_DECLARE(void) ap_method_list_vdo(int (*comp) (void *mrec,
- const char *mname,
- int mnum),
- void *rec, const ap_method_list_t *ml,
- va_list vp)
-{
-
-}
-
-/*
- * Return true if the specified HTTP method is in the provided
- * method list.
- */
-AP_DECLARE(int) ap_method_in_list(ap_method_list_t *l, const char *method)
-{
- int methnum;
- int i;
- char **methods;
-
- /*
- * If it's one of our known methods, use the shortcut and check the
- * bitmask.
- */
- methnum = ap_method_number_of(method);
- if (methnum != M_INVALID) {
- return !!(l->method_mask & (AP_METHOD_BIT << methnum));
- }
- /*
- * Otherwise, see if the method name is in the array or string names
- */
- if ((l->method_list == NULL) || (l->method_list->nelts == 0)) {
- return 0;
- }
- methods = (char **)l->method_list->elts;
- for (i = 0; i < l->method_list->nelts; ++i) {
- if (strcmp(method, methods[i]) == 0) {
- return 1;
- }
- }
- return 0;
-}
-
-/*
- * Add the specified method to a method list (if it isn't already there).
- */
-AP_DECLARE(void) ap_method_list_add(ap_method_list_t *l, const char *method)
-{
- int methnum;
- int i;
- const char **xmethod;
- char **methods;
-
- /*
- * If it's one of our known methods, use the shortcut and use the
- * bitmask.
- */
- methnum = ap_method_number_of(method);
- l->method_mask |= (AP_METHOD_BIT << methnum);
- if (methnum != M_INVALID) {
- return;
- }
- /*
- * Otherwise, see if the method name is in the array of string names.
- */
- if (l->method_list->nelts != 0) {
- methods = (char **)l->method_list->elts;
- for (i = 0; i < l->method_list->nelts; ++i) {
- if (strcmp(method, methods[i]) == 0) {
- return;
- }
- }
- }
- xmethod = (const char **) apr_array_push(l->method_list);
- *xmethod = method;
-}
-
-/*
- * Remove the specified method from a method list.
- */
-AP_DECLARE(void) ap_method_list_remove(ap_method_list_t *l,
- const char *method)
-{
- int methnum;
- char **methods;
-
- /*
- * If it's a known methods, either builtin or registered
- * by a module, use the bitmask.
- */
- methnum = ap_method_number_of(method);
- l->method_mask |= ~(AP_METHOD_BIT << methnum);
- if (methnum != M_INVALID) {
- return;
- }
- /*
- * Otherwise, see if the method name is in the array of string names.
- */
- if (l->method_list->nelts != 0) {
- register int i, j, k;
- methods = (char **)l->method_list->elts;
- for (i = 0; i < l->method_list->nelts; ) {
- if (strcmp(method, methods[i]) == 0) {
- for (j = i, k = i + 1; k < l->method_list->nelts; ++j, ++k) {
- methods[j] = methods[k];
- }
- --l->method_list->nelts;
- }
- else {
- ++i;
- }
- }
- }
-}
-
-/*
- * Reset a method list to be completely empty.
- */
-AP_DECLARE(void) ap_clear_method_list(ap_method_list_t *l)
-{
- l->method_mask = 0;
- l->method_list->nelts = 0;
-}
-
-/* Generate the human-readable hex representation of an unsigned long
- * (basically a faster version of 'sprintf("%lx")')
- */
-#define HEX_DIGITS "0123456789abcdef"
-static char *etag_ulong_to_hex(char *next, unsigned long u)
-{
- int printing = 0;
- int shift = sizeof(unsigned long) * 8 - 4;
- do {
- unsigned long next_digit = ((u >> shift) & (unsigned long)0xf);
- if (next_digit) {
- *next++ = HEX_DIGITS[next_digit];
- printing = 1;
- }
- else if (printing) {
- *next++ = HEX_DIGITS[next_digit];
- }
- shift -= 4;
- } while (shift);
- *next++ = HEX_DIGITS[u & (unsigned long)0xf];
- return next;
-}
-
-#define ETAG_WEAK "W/"
-#define CHARS_PER_UNSIGNED_LONG (sizeof(unsigned long) * 2)
-/*
- * Construct an entity tag (ETag) from resource information. If it's a real
- * file, build in some of the file characteristics. If the modification time
- * is newer than (request-time minus 1 second), mark the ETag as weak - it
- * could be modified again in as short an interval. We rationalize the
- * modification time we're given to keep it from being in the future.
- */
-AP_DECLARE(char *) ap_make_etag(request_rec *r, int force_weak)
-{
- char *weak;
- apr_size_t weak_len;
- char *etag;
- char *next;
- core_dir_config *cfg;
- etag_components_t etag_bits;
- etag_components_t bits_added;
-
- cfg = (core_dir_config *)ap_get_module_config(r->per_dir_config,
- &core_module);
- etag_bits = (cfg->etag_bits & (~ cfg->etag_remove)) | cfg->etag_add;
-
- /*
- * If it's a file (or we wouldn't be here) and no ETags
- * should be set for files, return an empty string and
- * note it for the header-sender to ignore.
- */
- if (etag_bits & ETAG_NONE) {
- apr_table_setn(r->notes, "no-etag", "omit");
- return "";
- }
-
- if (etag_bits == ETAG_UNSET) {
- etag_bits = ETAG_BACKWARD;
- }
- /*
- * Make an ETag header out of various pieces of information. We use
- * the last-modified date and, if we have a real file, the
- * length and inode number - note that this doesn't have to match
- * the content-length (i.e. includes), it just has to be unique
- * for the file.
- *
- * If the request was made within a second of the last-modified date,
- * we send a weak tag instead of a strong one, since it could
- * be modified again later in the second, and the validation
- * would be incorrect.
- */
- if ((r->request_time - r->mtime > (1 * APR_USEC_PER_SEC)) &&
- !force_weak) {
- weak = NULL;
- weak_len = 0;
- }
- else {
- weak = ETAG_WEAK;
- weak_len = sizeof(ETAG_WEAK);
- }
-
- if (r->finfo.filetype != 0) {
- /*
- * ETag gets set to [W/]"inode-size-mtime", modulo any
- * FileETag keywords.
- */
- etag = apr_palloc(r->pool, weak_len + sizeof("\"--\"") +
- 3 * CHARS_PER_UNSIGNED_LONG + 1);
- next = etag;
- if (weak) {
- while (*weak) {
- *next++ = *weak++;
- }
- }
- *next++ = '"';
- bits_added = 0;
- if (etag_bits & ETAG_INODE) {
- next = etag_ulong_to_hex(next, (unsigned long)r->finfo.inode);
- bits_added |= ETAG_INODE;
- }
- if (etag_bits & ETAG_SIZE) {
- if (bits_added != 0) {
- *next++ = '-';
- }
- next = etag_ulong_to_hex(next, (unsigned long)r->finfo.size);
- bits_added |= ETAG_SIZE;
- }
- if (etag_bits & ETAG_MTIME) {
- if (bits_added != 0) {
- *next++ = '-';
- }
- next = etag_ulong_to_hex(next, (unsigned long)r->mtime);
- }
- *next++ = '"';
- *next = '\0';
- }
- else {
- /*
- * Not a file document, so just use the mtime: [W/]"mtime"
- */
- etag = apr_palloc(r->pool, weak_len + sizeof("\"\"") +
- CHARS_PER_UNSIGNED_LONG + 1);
- next = etag;
- if (weak) {
- while (*weak) {
- *next++ = *weak++;
- }
- }
- *next++ = '"';
- next = etag_ulong_to_hex(next, (unsigned long)r->mtime);
- *next++ = '"';
- *next = '\0';
- }
-
- return etag;
-}
-
-AP_DECLARE(void) ap_set_etag(request_rec *r)
-{
- char *etag;
- char *variant_etag, *vlv;
- int vlv_weak;
-
- if (!r->vlist_validator) {
- etag = ap_make_etag(r, 0);
-
- /* If we get a blank etag back, don't set the header. */
- if (!etag[0]) {
- return;
- }
- }
- else {
- /* If we have a variant list validator (vlv) due to the
- * response being negotiated, then we create a structured
- * entity tag which merges the variant etag with the variant
- * list validator (vlv). This merging makes revalidation
- * somewhat safer, ensures that caches which can deal with
- * Vary will (eventually) be updated if the set of variants is
- * changed, and is also a protocol requirement for transparent
- * content negotiation.
- */
-
- /* if the variant list validator is weak, we make the whole
- * structured etag weak. If we would not, then clients could
- * have problems merging range responses if we have different
- * variants with the same non-globally-unique strong etag.
- */
-
- vlv = r->vlist_validator;
- vlv_weak = (vlv[0] == 'W');
-
- variant_etag = ap_make_etag(r, vlv_weak);
-
- /* If we get a blank etag back, don't append vlv and stop now. */
- if (!variant_etag[0]) {
- return;
- }
-
- /* merge variant_etag and vlv into a structured etag */
- variant_etag[strlen(variant_etag) - 1] = '\0';
- if (vlv_weak) {
- vlv += 3;
- }
- else {
- vlv++;
- }
- etag = apr_pstrcat(r->pool, variant_etag, ";", vlv, NULL);
- }
-
- apr_table_setn(r->headers_out, "ETag", etag);
-}
static int parse_byterange(char *range, apr_off_t clength,
apr_off_t *start, apr_off_t *end)
Copied: httpd/httpd/trunk/modules/http/chunk_filter.c (from r106679, httpd/httpd/trunk/modules/http/http_core.c)
Url: http://svn.apache.org/viewcvs/httpd/httpd/trunk/modules/http/chunk_filter.c?view=diff&rev=106692&p1=httpd/httpd/trunk/modules/http/http_core.c&r1=106679&p2=httpd/httpd/trunk/modules/http/chunk_filter.c&r2=106692
==============================================================================
--- httpd/httpd/trunk/modules/http/http_core.c (original)
+++ httpd/httpd/trunk/modules/http/chunk_filter.c Sat Nov 27 00:07:44 2004
@@ -13,6 +13,10 @@
* limitations under the License.
*/
+/*
+ * chunk_filter.c --- HTTP/1.1 chunked transfer encoding filter.
+ */
+
#include "apr_strings.h"
#include "apr_thread_proc.h" /* for RLIMIT stuff */
@@ -34,69 +38,6 @@
#include "mod_core.h"
-/* Handles for core filters */
-AP_DECLARE_DATA ap_filter_rec_t *ap_http_input_filter_handle;
-AP_DECLARE_DATA ap_filter_rec_t *ap_http_header_filter_handle;
-AP_DECLARE_DATA ap_filter_rec_t *ap_chunk_filter_handle;
-AP_DECLARE_DATA ap_filter_rec_t *ap_byterange_filter_handle;
-
-static const char *set_keep_alive_timeout(cmd_parms *cmd, void *dummy,
- const char *arg)
-{
- const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
- if (err != NULL) {
- return err;
- }
-
- cmd->server->keep_alive_timeout = apr_time_from_sec(atoi(arg));
- return NULL;
-}
-
-static const char *set_keep_alive(cmd_parms *cmd, void *dummy,
- const char *arg)
-{
- const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
- if (err != NULL) {
- return err;
- }
-
- /* We've changed it to On/Off, but used to use numbers
- * so we accept anything but "Off" or "0" as "On"
- */
- if (!strcasecmp(arg, "off") || !strcmp(arg, "0")) {
- cmd->server->keep_alive = 0;
- }
- else {
- cmd->server->keep_alive = 1;
- }
- return NULL;
-}
-
-static const char *set_keep_alive_max(cmd_parms *cmd, void *dummy,
- const char *arg)
-{
- const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
- if (err != NULL) {
- return err;
- }
-
- cmd->server->keep_alive_max = atoi(arg);
- return NULL;
-}
-
-static const command_rec http_cmds[] = {
- AP_INIT_TAKE1("KeepAliveTimeout", set_keep_alive_timeout, NULL, RSRC_CONF,
- "Keep-Alive timeout duration (sec)"),
- AP_INIT_TAKE1("MaxKeepAliveRequests", set_keep_alive_max, NULL, RSRC_CONF,
- "Maximum number of Keep-Alive requests per connection, or 0 for infinite"),
- AP_INIT_TAKE1("KeepAlive", set_keep_alive, NULL, RSRC_CONF,
- "Whether persistent connections should be On or Off"),
- { NULL }
-};
-
-/*
- * HTTP/1.1 chunked transfer encoding filter.
- */
static apr_status_t chunk_filter(ap_filter_t *f, apr_bucket_brigade *b)
{
#define ASCII_CRLF "\015\012"
@@ -224,156 +165,3 @@
}
return APR_SUCCESS;
}
-
-static const char *http_method(const request_rec *r)
- { return "http"; }
-
-static apr_port_t http_port(const request_rec *r)
- { return DEFAULT_HTTP_PORT; }
-
-static int ap_process_http_async_connection(conn_rec *c)
-{
- request_rec *r;
- conn_state_t *cs = c->cs;
-
- AP_DEBUG_ASSERT(cs->state == CONN_STATE_READ_REQUEST_LINE);
-
- while (cs->state == CONN_STATE_READ_REQUEST_LINE) {
- ap_update_child_status(c->sbh, SERVER_BUSY_READ, NULL);
-
- if ((r = ap_read_request(c))) {
-
- c->keepalive = AP_CONN_UNKNOWN;
- /* process the request if it was read without error */
-
- ap_update_child_status(c->sbh, SERVER_BUSY_WRITE, r);
- if (r->status == HTTP_OK)
- ap_process_request(r);
-
- if (ap_extended_status)
- ap_increment_counts(c->sbh, r);
-
- if (c->keepalive != AP_CONN_KEEPALIVE || c->aborted
- || ap_graceful_stop_signalled()) {
- cs->state = CONN_STATE_LINGER;
- }
- else if (!c->data_in_input_filters) {
- cs->state = CONN_STATE_CHECK_REQUEST_LINE_READABLE;
- }
-
- /* else we are pipelining. Stay in READ_REQUEST_LINE state
- * and stay in the loop
- */
-
- apr_pool_destroy(r->pool);
- }
- else { /* ap_read_request failed - client may have closed */
- cs->state = CONN_STATE_LINGER;
- }
- }
-
- return OK;
-}
-
-static int ap_process_http_connection(conn_rec *c)
-{
- request_rec *r;
- int csd_set = 0;
- apr_socket_t *csd = NULL;
-
- /*
- * Read and process each request found on our connection
- * until no requests are left or we decide to close.
- */
-
- ap_update_child_status(c->sbh, SERVER_BUSY_READ, NULL);
- while ((r = ap_read_request(c)) != NULL) {
-
- c->keepalive = AP_CONN_UNKNOWN;
- /* process the request if it was read without error */
-
- ap_update_child_status(c->sbh, SERVER_BUSY_WRITE, r);
- if (r->status == HTTP_OK)
- ap_process_request(r);
-
- if (ap_extended_status)
- ap_increment_counts(c->sbh, r);
-
- if (c->keepalive != AP_CONN_KEEPALIVE || c->aborted)
- break;
-
- ap_update_child_status(c->sbh, SERVER_BUSY_KEEPALIVE, r);
- apr_pool_destroy(r->pool);
-
- if (ap_graceful_stop_signalled())
- break;
- /* Go straight to select() to wait for the next request */
- if (!csd_set) {
- csd = ap_get_module_config(c->conn_config, &core_module);
- csd_set = 1;
- }
- apr_socket_opt_set(csd, APR_INCOMPLETE_READ, 1);
- }
-
- return OK;
-}
-
-static int http_create_request(request_rec *r)
-{
- if (!r->main && !r->prev) {
- ap_add_output_filter_handle(ap_byterange_filter_handle,
- NULL, r, r->connection);
- ap_add_output_filter_handle(ap_content_length_filter_handle,
- NULL, r, r->connection);
- ap_add_output_filter_handle(ap_http_header_filter_handle,
- NULL, r, r->connection);
- }
-
- return OK;
-}
-
-static void register_hooks(apr_pool_t *p)
-{
- /**
- * If we ae using an MPM That Supports Async Connections,
- * use a different processing function
- */
- int async_mpm = 0;
- if(ap_mpm_query(AP_MPMQ_IS_ASYNC, &async_mpm) == APR_SUCCESS
- && async_mpm == 1) {
- ap_hook_process_connection(ap_process_http_async_connection,NULL,
- NULL,APR_HOOK_REALLY_LAST);
- }
- else {
- ap_hook_process_connection(ap_process_http_connection,NULL,NULL,
- APR_HOOK_REALLY_LAST);
- }
-
- ap_hook_map_to_storage(ap_send_http_trace,NULL,NULL,APR_HOOK_MIDDLE);
- ap_hook_http_method(http_method,NULL,NULL,APR_HOOK_REALLY_LAST);
- ap_hook_default_port(http_port,NULL,NULL,APR_HOOK_REALLY_LAST);
- ap_hook_create_request(http_create_request, NULL, NULL, APR_HOOK_REALLY_LAST);
- ap_http_input_filter_handle =
- ap_register_input_filter("HTTP_IN", ap_http_filter,
- NULL, AP_FTYPE_PROTOCOL);
- ap_http_header_filter_handle =
- ap_register_output_filter("HTTP_HEADER", ap_http_header_filter,
- NULL, AP_FTYPE_PROTOCOL);
- ap_chunk_filter_handle =
- ap_register_output_filter("CHUNK", chunk_filter,
- NULL, AP_FTYPE_TRANSCODE);
- ap_byterange_filter_handle =
- ap_register_output_filter("BYTERANGE", ap_byterange_filter,
- NULL, AP_FTYPE_PROTOCOL);
- ap_method_registry_init(p);
-}
-
-module AP_MODULE_DECLARE_DATA http_module = {
- STANDARD20_MODULE_STUFF,
- NULL, /* create per-directory config structure */
- NULL, /* merge per-directory config structures */
- NULL, /* create per-server config structure */
- NULL, /* merge per-server config structures */
- http_cmds, /* command apr_table_t */
- register_hooks /* register hooks */
-};
Modified: httpd/httpd/trunk/modules/http/config2.m4
Url: http://svn.apache.org/viewcvs/httpd/httpd/trunk/modules/http/config2.m4?view=diff&rev=106692&p1=httpd/httpd/trunk/modules/http/config2.m4&r1=106691&p2=httpd/httpd/trunk/modules/http/config2.m4&r2=106692
==============================================================================
--- httpd/httpd/trunk/modules/http/config2.m4 (original)
+++ httpd/httpd/trunk/modules/http/config2.m4 Sat Nov 27 00:07:44 2004
@@ -2,7 +2,7 @@
APACHE_MODPATH_INIT(http)
-http_objects="http_core.lo http_protocol.lo http_request.lo"
+http_objects="http_core.lo http_protocol.lo http_request.lo http_filters.lo chunk_filter.lo byterange_filter.lo http_etag.lo"
dnl mod_http should only be built as a static module for now.
dnl this will hopefully be "fixed" at some point in the future by
Copied: httpd/httpd/trunk/modules/http/http_etag.c (from r106679, httpd/httpd/trunk/modules/http/http_protocol.c)
Url: http://svn.apache.org/viewcvs/httpd/httpd/trunk/modules/http/http_etag.c?view=diff&rev=106692&p1=httpd/httpd/trunk/modules/http/http_protocol.c&r1=106679&p2=httpd/httpd/trunk/modules/http/http_etag.c&r2=106692
==============================================================================
--- httpd/httpd/trunk/modules/http/http_protocol.c (original)
+++ httpd/httpd/trunk/modules/http/http_etag.c Sat Nov 27 00:07:44 2004
@@ -13,2586 +13,19 @@
* limitations under the License.
*/
-/*
- * http_protocol.c --- routines which directly communicate with the client.
- *
- * Code originally by Rob McCool; much redone by Robert S. Thau
- * and the Apache Software Foundation.
- */
-
-#include "apr.h"
-#include "apr_strings.h"
-#include "apr_buckets.h"
-#include "apr_lib.h"
-#include "apr_signal.h"
-
-#define APR_WANT_STDIO /* for sscanf */
-#define APR_WANT_STRFUNC
-#define APR_WANT_MEMFUNC
-#include "apr_want.h"
-
-#define CORE_PRIVATE
-#include "util_filter.h"
-#include "ap_config.h"
-#include "httpd.h"
-#include "http_config.h"
-#include "http_core.h"
-#include "http_protocol.h"
-#include "http_main.h"
-#include "http_request.h"
-#include "http_vhost.h"
-#include "http_log.h" /* For errors detected in basic auth common
- * support code... */
-#include "apr_date.h" /* For apr_date_parse_http and APR_DATE_BAD */
-#include "util_charset.h"
-#include "util_ebcdic.h"
-#include "util_time.h"
-
-#include "mod_core.h"
-
-#if APR_HAVE_STDARG_H
-#include
-#endif
-#if APR_HAVE_UNISTD_H
-#include
-#endif
-
-/* New Apache routine to map status codes into array indicies
- * e.g. 100 -> 0, 101 -> 1, 200 -> 2 ...
- * The number of status lines must equal the value of RESPONSE_CODES (httpd.h)
- * and must be listed in order.
- */
-
-#ifdef UTS21
-/* The second const triggers an assembler bug on UTS 2.1.
- * Another workaround is to move some code out of this file into another,
- * but this is easier. Dave Dykstra, 3/31/99
- */
-static const char * status_lines[RESPONSE_CODES] =
-#else
-static const char * const status_lines[RESPONSE_CODES] =
-#endif
-{
- "100 Continue",
- "101 Switching Protocols",
- "102 Processing",
-#define LEVEL_200 3
- "200 OK",
- "201 Created",
- "202 Accepted",
- "203 Non-Authoritative Information",
- "204 No Content",
- "205 Reset Content",
- "206 Partial Content",
- "207 Multi-Status",
-#define LEVEL_300 11
- "300 Multiple Choices",
- "301 Moved Permanently",
- "302 Found",
- "303 See Other",
- "304 Not Modified",
- "305 Use Proxy",
- "306 unused",
- "307 Temporary Redirect",
-#define LEVEL_400 19
- "400 Bad Request",
- "401 Authorization Required",
- "402 Payment Required",
- "403 Forbidden",
- "404 Not Found",
- "405 Method Not Allowed",
- "406 Not Acceptable",
- "407 Proxy Authentication Required",
- "408 Request Time-out",
- "409 Conflict",
- "410 Gone",
- "411 Length Required",
- "412 Precondition Failed",
- "413 Request Entity Too Large",
- "414 Request-URI Too Large",
- "415 Unsupported Media Type",
- "416 Requested Range Not Satisfiable",
- "417 Expectation Failed",
- "418 unused",
- "419 unused",
- "420 unused",
- "421 unused",
- "422 Unprocessable Entity",
- "423 Locked",
- "424 Failed Dependency",
- /* This is a hack, but it is required for ap_index_of_response
- * to work with 426.
- */
- "425 No code",
- "426 Upgrade Required",
-#define LEVEL_500 46
- "500 Internal Server Error",
- "501 Method Not Implemented",
- "502 Bad Gateway",
- "503 Service Temporarily Unavailable",
- "504 Gateway Time-out",
- "505 HTTP Version Not Supported",
- "506 Variant Also Negotiates",
- "507 Insufficient Storage",
- "508 unused",
- "509 unused",
- "510 Not Extended"
-};
-
-APR_HOOK_STRUCT(
- APR_HOOK_LINK(insert_error_filter)
-)
-
-AP_IMPLEMENT_HOOK_VOID(insert_error_filter, (request_rec *r), (r))
-
-/* The index of the first bit field that is used to index into a limit
- * bitmask. M_INVALID + 1 to METHOD_NUMBER_LAST.
- */
-#define METHOD_NUMBER_FIRST (M_INVALID + 1)
-
-/* The max method number. Method numbers are used to shift bitmasks,
- * so this cannot exceed 63, and all bits high is equal to -1, which is a
- * special flag, so the last bit used has index 62.
- */
-#define METHOD_NUMBER_LAST 62
-
-
-AP_DECLARE(int) ap_set_keepalive(request_rec *r)
-{
- int ka_sent = 0;
- int wimpy = ap_find_token(r->pool,
- apr_table_get(r->headers_out, "Connection"),
- "close");
- const char *conn = apr_table_get(r->headers_in, "Connection");
-
- /* The following convoluted conditional determines whether or not
- * the current connection should remain persistent after this response
- * (a.k.a. HTTP Keep-Alive) and whether or not the output message
- * body should use the HTTP/1.1 chunked transfer-coding. In English,
- *
- * IF we have not marked this connection as errored;
- * and the response body has a defined length due to the status code
- * being 304 or 204, the request method being HEAD, already
- * having defined Content-Length or Transfer-Encoding: chunked, or
- * the request version being HTTP/1.1 and thus capable of being set
- * as chunked [we know the (r->chunked = 1) side-effect is ugly];
- * and the server configuration enables keep-alive;
- * and the server configuration has a reasonable inter-request timeout;
- * and there is no maximum # requests or the max hasn't been reached;
- * and the response status does not require a close;
- * and the response generator has not already indicated close;
- * and the client did not request non-persistence (Connection: close);
- * and we haven't been configured to ignore the buggy twit
- * or they're a buggy twit coming through a HTTP/1.1 proxy
- * and the client is requesting an HTTP/1.0-style keep-alive
- * or the client claims to be HTTP/1.1 compliant (perhaps a proxy);
- * THEN we can be persistent, which requires more headers be output.
- *
- * Note that the condition evaluation order is extremely important.
- */
- if ((r->connection->keepalive != AP_CONN_CLOSE)
- && ((r->status == HTTP_NOT_MODIFIED)
- || (r->status == HTTP_NO_CONTENT)
- || r->header_only
- || apr_table_get(r->headers_out, "Content-Length")
- || ap_find_last_token(r->pool,
- apr_table_get(r->headers_out,
- "Transfer-Encoding"),
- "chunked")
- || ((r->proto_num >= HTTP_VERSION(1,1))
- && (r->chunked = 1))) /* THIS CODE IS CORRECT, see above. */
- && r->server->keep_alive
- && (r->server->keep_alive_timeout > 0)
- && ((r->server->keep_alive_max == 0)
- || (r->server->keep_alive_max > r->connection->keepalives))
- && !ap_status_drops_connection(r->status)
- && !wimpy
- && !ap_find_token(r->pool, conn, "close")
- && (!apr_table_get(r->subprocess_env, "nokeepalive")
- || apr_table_get(r->headers_in, "Via"))
- && ((ka_sent = ap_find_token(r->pool, conn, "keep-alive"))
- || (r->proto_num >= HTTP_VERSION(1,1)))) {
- int left = r->server->keep_alive_max - r->connection->keepalives;
-
- r->connection->keepalive = AP_CONN_KEEPALIVE;
- r->connection->keepalives++;
-
- /* If they sent a Keep-Alive token, send one back */
- if (ka_sent) {
- if (r->server->keep_alive_max) {
- apr_table_setn(r->headers_out, "Keep-Alive",
- apr_psprintf(r->pool, "timeout=%d, max=%d",
- (int)apr_time_sec(r->server->keep_alive_timeout),
- left));
- }
- else {
- apr_table_setn(r->headers_out, "Keep-Alive",
- apr_psprintf(r->pool, "timeout=%d",
- (int)apr_time_sec(r->server->keep_alive_timeout)));
- }
- apr_table_mergen(r->headers_out, "Connection", "Keep-Alive");
- }
-
- return 1;
- }
-
- /* Otherwise, we need to indicate that we will be closing this
- * connection immediately after the current response.
- *
- * We only really need to send "close" to HTTP/1.1 clients, but we
- * always send it anyway, because a broken proxy may identify itself
- * as HTTP/1.0, but pass our request along with our HTTP/1.1 tag
- * to a HTTP/1.1 client. Better safe than sorry.
- */
- if (!wimpy) {
- apr_table_mergen(r->headers_out, "Connection", "close");
- }
-
- r->connection->keepalive = AP_CONN_CLOSE;
-
- return 0;
-}
-
-AP_DECLARE(int) ap_meets_conditions(request_rec *r)
-{
- const char *etag;
- const char *if_match, *if_modified_since, *if_unmodified, *if_nonematch;
- apr_time_t tmp_time;
- apr_int64_t mtime;
-
- /* Check for conditional requests --- note that we only want to do
- * this if we are successful so far and we are not processing a
- * subrequest or an ErrorDocument.
- *
- * The order of the checks is important, since ETag checks are supposed
- * to be more accurate than checks relative to the modification time.
- * However, not all documents are guaranteed to *have* ETags, and some
- * might have Last-Modified values w/o ETags, so this gets a little
- * complicated.
- */
-
- if (!ap_is_HTTP_SUCCESS(r->status) || r->no_local_copy) {
- return OK;
- }
-
- etag = apr_table_get(r->headers_out, "ETag");
-
- /* All of our comparisons must be in seconds, because that's the
- * highest time resolution the HTTP specification allows.
- */
- /* XXX: we should define a "time unset" constant */
- tmp_time = ((r->mtime != 0) ? r->mtime : apr_time_now());
- mtime = apr_time_sec(tmp_time);
-
- /* If an If-Match request-header field was given
- * AND the field value is not "*" (meaning match anything)
- * AND if our strong ETag does not match any entity tag in that field,
- * respond with a status of 412 (Precondition Failed).
- */
- if ((if_match = apr_table_get(r->headers_in, "If-Match")) != NULL) {
- if (if_match[0] != '*'
- && (etag == NULL || etag[0] == 'W'
- || !ap_find_list_item(r->pool, if_match, etag))) {
- return HTTP_PRECONDITION_FAILED;
- }
- }
- else {
- /* Else if a valid If-Unmodified-Since request-header field was given
- * AND the requested resource has been modified since the time
- * specified in this field, then the server MUST
- * respond with a status of 412 (Precondition Failed).
- */
- if_unmodified = apr_table_get(r->headers_in, "If-Unmodified-Since");
- if (if_unmodified != NULL) {
- apr_time_t ius = apr_date_parse_http(if_unmodified);
-
- if ((ius != APR_DATE_BAD) && (mtime > apr_time_sec(ius))) {
- return HTTP_PRECONDITION_FAILED;
- }
- }
- }
-
- /* If an If-None-Match request-header field was given
- * AND the field value is "*" (meaning match anything)
- * OR our ETag matches any of the entity tags in that field, fail.
- *
- * If the request method was GET or HEAD, failure means the server
- * SHOULD respond with a 304 (Not Modified) response.
- * For all other request methods, failure means the server MUST
- * respond with a status of 412 (Precondition Failed).
- *
- * GET or HEAD allow weak etag comparison, all other methods require
- * strong comparison. We can only use weak if it's not a range request.
- */
- if_nonematch = apr_table_get(r->headers_in, "If-None-Match");
- if (if_nonematch != NULL) {
- if (r->method_number == M_GET) {
- if (if_nonematch[0] == '*') {
- return HTTP_NOT_MODIFIED;
- }
- if (etag != NULL) {
- if (apr_table_get(r->headers_in, "Range")) {
- if (etag[0] != 'W'
- && ap_find_list_item(r->pool, if_nonematch, etag)) {
- return HTTP_NOT_MODIFIED;
- }
- }
- else if (ap_strstr_c(if_nonematch, etag)) {
- return HTTP_NOT_MODIFIED;
- }
- }
- }
- else if (if_nonematch[0] == '*'
- || (etag != NULL
- && ap_find_list_item(r->pool, if_nonematch, etag))) {
- return HTTP_PRECONDITION_FAILED;
- }
- }
- /* Else if a valid If-Modified-Since request-header field was given
- * AND it is a GET or HEAD request
- * AND the requested resource has not been modified since the time
- * specified in this field, then the server MUST
- * respond with a status of 304 (Not Modified).
- * A date later than the server's current request time is invalid.
- */
- else if ((r->method_number == M_GET)
- && ((if_modified_since =
- apr_table_get(r->headers_in,
- "If-Modified-Since")) != NULL)) {
- apr_time_t ims_time;
- apr_int64_t ims, reqtime;
-
- ims_time = apr_date_parse_http(if_modified_since);
- ims = apr_time_sec(ims_time);
- reqtime = apr_time_sec(r->request_time);
-
- if ((ims >= mtime) && (ims <= reqtime)) {
- return HTTP_NOT_MODIFIED;
- }
- }
- return OK;
-}
-
-/**
- * Singleton registry of additional methods. This maps new method names
- * such as "MYGET" to methnums, which are int offsets into bitmasks.
- *
- * This follows the same technique as standard M_GET, M_POST, etc. These
- * are dynamically assigned when modules are loaded and
- * directives are processed.
- */
-static apr_hash_t *methods_registry = NULL;
-static int cur_method_number = METHOD_NUMBER_FIRST;
-
-/* internal function to register one method/number pair */
-static void register_one_method(apr_pool_t *p, const char *methname,
- int methnum)
-{
- int *pnum = apr_palloc(p, sizeof(*pnum));
-
- *pnum = methnum;
- apr_hash_set(methods_registry, methname, APR_HASH_KEY_STRING, pnum);
-}
-
-/* This internal function is used to clear the method registry
- * and reset the cur_method_number counter.
- */
-static apr_status_t ap_method_registry_destroy(void *notused)
-{
- methods_registry = NULL;
- cur_method_number = METHOD_NUMBER_FIRST;
- return APR_SUCCESS;
-}
-
-AP_DECLARE(void) ap_method_registry_init(apr_pool_t *p)
-{
- methods_registry = apr_hash_make(p);
- apr_pool_cleanup_register(p, NULL,
- ap_method_registry_destroy,
- apr_pool_cleanup_null);
-
- /* put all the standard methods into the registry hash to ease the
- mapping operations between name and number */
- register_one_method(p, "GET", M_GET);
- register_one_method(p, "PUT", M_PUT);
- register_one_method(p, "POST", M_POST);
- register_one_method(p, "DELETE", M_DELETE);
- register_one_method(p, "CONNECT", M_CONNECT);
- register_one_method(p, "OPTIONS", M_OPTIONS);
- register_one_method(p, "TRACE", M_TRACE);
- register_one_method(p, "PATCH", M_PATCH);
- register_one_method(p, "PROPFIND", M_PROPFIND);
- register_one_method(p, "PROPPATCH", M_PROPPATCH);
- register_one_method(p, "MKCOL", M_MKCOL);
- register_one_method(p, "COPY", M_COPY);
- register_one_method(p, "MOVE", M_MOVE);
- register_one_method(p, "LOCK", M_LOCK);
- register_one_method(p, "UNLOCK", M_UNLOCK);
- register_one_method(p, "VERSION-CONTROL", M_VERSION_CONTROL);
- register_one_method(p, "CHECKOUT", M_CHECKOUT);
- register_one_method(p, "UNCHECKOUT", M_UNCHECKOUT);
- register_one_method(p, "CHECKIN", M_CHECKIN);
- register_one_method(p, "UPDATE", M_UPDATE);
- register_one_method(p, "LABEL", M_LABEL);
- register_one_method(p, "REPORT", M_REPORT);
- register_one_method(p, "MKWORKSPACE", M_MKWORKSPACE);
- register_one_method(p, "MKACTIVITY", M_MKACTIVITY);
- register_one_method(p, "BASELINE-CONTROL", M_BASELINE_CONTROL);
- register_one_method(p, "MERGE", M_MERGE);
-}
-
-AP_DECLARE(int) ap_method_register(apr_pool_t *p, const char *methname)
-{
- int *methnum;
-
- if (methods_registry == NULL) {
- ap_method_registry_init(p);
- }
-
- if (methname == NULL) {
- return M_INVALID;
- }
-
- /* Check if the method was previously registered. If it was
- * return the associated method number.
- */
- methnum = (int *)apr_hash_get(methods_registry, methname,
- APR_HASH_KEY_STRING);
- if (methnum != NULL)
- return *methnum;
-
- if (cur_method_number > METHOD_NUMBER_LAST) {
- /* The method registry has run out of dynamically
- * assignable method numbers. Log this and return M_INVALID.
- */
- ap_log_perror(APLOG_MARK, APLOG_ERR, 0, p,
- "Maximum new request methods %d reached while "
- "registering method %s.",
- METHOD_NUMBER_LAST, methname);
- return M_INVALID;
- }
-
- register_one_method(p, methname, cur_method_number);
- return cur_method_number++;
-}
-
-#define UNKNOWN_METHOD (-1)
-
-static int lookup_builtin_method(const char *method, apr_size_t len)
-{
- /* Note: the following code was generated by the "shilka" tool from
- the "cocom" parsing/compilation toolkit. It is an optimized lookup
- based on analysis of the input keywords. Postprocessing was done
- on the shilka output, but the basic structure and analysis is
- from there. Should new HTTP methods be added, then manual insertion
- into this code is fine, or simply re-running the shilka tool on
- the appropriate input. */
-
- /* Note: it is also quite reasonable to just use our method_registry,
- but I'm assuming (probably incorrectly) we want more speed here
- (based on the optimizations the previous code was doing). */
-
- switch (len)
- {
- case 3:
- switch (method[0])
- {
- case 'P':
- return (method[1] == 'U'
- && method[2] == 'T'
- ? M_PUT : UNKNOWN_METHOD);
- case 'G':
- return (method[1] == 'E'
- && method[2] == 'T'
- ? M_GET : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
-
- case 4:
- switch (method[0])
- {
- case 'H':
- return (method[1] == 'E'
- && method[2] == 'A'
- && method[3] == 'D'
- ? M_GET : UNKNOWN_METHOD);
- case 'P':
- return (method[1] == 'O'
- && method[2] == 'S'
- && method[3] == 'T'
- ? M_POST : UNKNOWN_METHOD);
- case 'M':
- return (method[1] == 'O'
- && method[2] == 'V'
- && method[3] == 'E'
- ? M_MOVE : UNKNOWN_METHOD);
- case 'L':
- return (method[1] == 'O'
- && method[2] == 'C'
- && method[3] == 'K'
- ? M_LOCK : UNKNOWN_METHOD);
- case 'C':
- return (method[1] == 'O'
- && method[2] == 'P'
- && method[3] == 'Y'
- ? M_COPY : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
-
- case 5:
- switch (method[2])
- {
- case 'T':
- return (memcmp(method, "PATCH", 5) == 0
- ? M_PATCH : UNKNOWN_METHOD);
- case 'R':
- return (memcmp(method, "MERGE", 5) == 0
- ? M_MERGE : UNKNOWN_METHOD);
- case 'C':
- return (memcmp(method, "MKCOL", 5) == 0
- ? M_MKCOL : UNKNOWN_METHOD);
- case 'B':
- return (memcmp(method, "LABEL", 5) == 0
- ? M_LABEL : UNKNOWN_METHOD);
- case 'A':
- return (memcmp(method, "TRACE", 5) == 0
- ? M_TRACE : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
-
- case 6:
- switch (method[0])
- {
- case 'U':
- switch (method[5])
- {
- case 'K':
- return (memcmp(method, "UNLOCK", 6) == 0
- ? M_UNLOCK : UNKNOWN_METHOD);
- case 'E':
- return (memcmp(method, "UPDATE", 6) == 0
- ? M_UPDATE : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
- case 'R':
- return (memcmp(method, "REPORT", 6) == 0
- ? M_REPORT : UNKNOWN_METHOD);
- case 'D':
- return (memcmp(method, "DELETE", 6) == 0
- ? M_DELETE : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
-
- case 7:
- switch (method[1])
- {
- case 'P':
- return (memcmp(method, "OPTIONS", 7) == 0
- ? M_OPTIONS : UNKNOWN_METHOD);
- case 'O':
- return (memcmp(method, "CONNECT", 7) == 0
- ? M_CONNECT : UNKNOWN_METHOD);
- case 'H':
- return (memcmp(method, "CHECKIN", 7) == 0
- ? M_CHECKIN : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
-
- case 8:
- switch (method[0])
- {
- case 'P':
- return (memcmp(method, "PROPFIND", 8) == 0
- ? M_PROPFIND : UNKNOWN_METHOD);
- case 'C':
- return (memcmp(method, "CHECKOUT", 8) == 0
- ? M_CHECKOUT : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
-
- case 9:
- return (memcmp(method, "PROPPATCH", 9) == 0
- ? M_PROPPATCH : UNKNOWN_METHOD);
-
- case 10:
- switch (method[0])
- {
- case 'U':
- return (memcmp(method, "UNCHECKOUT", 10) == 0
- ? M_UNCHECKOUT : UNKNOWN_METHOD);
- case 'M':
- return (memcmp(method, "MKACTIVITY", 10) == 0
- ? M_MKACTIVITY : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
-
- case 11:
- return (memcmp(method, "MKWORKSPACE", 11) == 0
- ? M_MKWORKSPACE : UNKNOWN_METHOD);
-
- case 15:
- return (memcmp(method, "VERSION-CONTROL", 15) == 0
- ? M_VERSION_CONTROL : UNKNOWN_METHOD);
-
- case 16:
- return (memcmp(method, "BASELINE-CONTROL", 16) == 0
- ? M_BASELINE_CONTROL : UNKNOWN_METHOD);
-
- default:
- return UNKNOWN_METHOD;
- }
-
- /* NOTREACHED */
-}
-
-/* Get the method number associated with the given string, assumed to
- * contain an HTTP method. Returns M_INVALID if not recognized.
- *
- * This is the first step toward placing method names in a configurable
- * list. Hopefully it (and other routines) can eventually be moved to
- * something like a mod_http_methods.c, complete with config stuff.
- */
-AP_DECLARE(int) ap_method_number_of(const char *method)
-{
- int len = strlen(method);
- int which = lookup_builtin_method(method, len);
-
- if (which != UNKNOWN_METHOD)
- return which;
-
- /* check if the method has been dynamically registered */
- if (methods_registry != NULL) {
- int *methnum = apr_hash_get(methods_registry, method, len);
-
- if (methnum != NULL) {
- return *methnum;
- }
- }
-
- return M_INVALID;
-}
-
-/*
- * Turn a known method number into a name.
- */
-AP_DECLARE(const char *) ap_method_name_of(apr_pool_t *p, int methnum)
-{
- apr_hash_index_t *hi = apr_hash_first(p, methods_registry);
-
- /* scan through the hash table, looking for a value that matches
- the provided method number. */
- for (; hi; hi = apr_hash_next(hi)) {
- const void *key;
- void *val;
-
- apr_hash_this(hi, &key, NULL, &val);
- if (*(int *)val == methnum)
- return key;
- }
-
- /* it wasn't found in the hash */
- return NULL;
-}
-
-static long get_chunk_size(char *);
-
-typedef struct http_filter_ctx {
- apr_off_t remaining;
- apr_off_t limit;
- apr_off_t limit_used;
- enum {
- BODY_NONE,
- BODY_LENGTH,
- BODY_CHUNK
- } state;
- int eos_sent;
-} http_ctx_t;
-
-/* This is the HTTP_INPUT filter for HTTP requests and responses from
- * proxied servers (mod_proxy). It handles chunked and content-length
- * bodies. This can only be inserted/used after the headers
- * are successfully parsed.
- */
-apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b,
- ap_input_mode_t mode, apr_read_type_e block,
- apr_off_t readbytes)
-{
- apr_bucket *e;
- http_ctx_t *ctx = f->ctx;
- apr_status_t rv;
- apr_off_t totalread;
-
- /* just get out of the way of things we don't want. */
- if (mode != AP_MODE_READBYTES && mode != AP_MODE_GETLINE) {
- return ap_get_brigade(f->next, b, mode, block, readbytes);
- }
-
- if (!ctx) {
- const char *tenc, *lenp;
- f->ctx = ctx = apr_palloc(f->r->pool, sizeof(*ctx));
- ctx->state = BODY_NONE;
- ctx->remaining = 0;
- ctx->limit_used = 0;
- ctx->eos_sent = 0;
-
- /* LimitRequestBody does not apply to proxied responses.
- * Consider implementing this check in its own filter.
- * Would adding a directive to limit the size of proxied
- * responses be useful?
- */
- if (!f->r->proxyreq) {
- ctx->limit = ap_get_limit_req_body(f->r);
- }
- else {
- ctx->limit = 0;
- }
-
- tenc = apr_table_get(f->r->headers_in, "Transfer-Encoding");
- lenp = apr_table_get(f->r->headers_in, "Content-Length");
-
- if (tenc) {
- if (!strcasecmp(tenc, "chunked")) {
- ctx->state = BODY_CHUNK;
- }
- }
- else if (lenp) {
- char *endstr;
-
- ctx->state = BODY_LENGTH;
- errno = 0;
-
- /* Protects against over/underflow, non-digit chars in the
- * string (excluding leading space) (the endstr checks)
- * and a negative number. */
- if (apr_strtoff(&ctx->remaining, lenp, &endstr, 10)
- || endstr == lenp || *endstr || ctx->remaining < 0) {
- apr_bucket_brigade *bb;
-
- ctx->remaining = 0;
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r,
- "Invalid Content-Length");
-
- bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
- e = ap_bucket_error_create(HTTP_REQUEST_ENTITY_TOO_LARGE, NULL,
- f->r->pool, f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
- ctx->eos_sent = 1;
- return ap_pass_brigade(f->r->output_filters, bb);
- }
-
- /* If we have a limit in effect and we know the C-L ahead of
- * time, stop it here if it is invalid.
- */
- if (ctx->limit && ctx->limit < ctx->remaining) {
- apr_bucket_brigade *bb;
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r,
- "Requested content-length of %" APR_OFF_T_FMT
- " is larger than the configured limit"
- " of %" APR_OFF_T_FMT, ctx->remaining, ctx->limit);
- bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
- e = ap_bucket_error_create(HTTP_REQUEST_ENTITY_TOO_LARGE, NULL,
- f->r->pool, f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
- ctx->eos_sent = 1;
- return ap_pass_brigade(f->r->output_filters, bb);
- }
- }
-
- /* If we don't have a request entity indicated by the headers, EOS.
- * (BODY_NONE is a valid intermediate state due to trailers,
- * but it isn't a valid starting state.)
- *
- * RFC 2616 Section 4.4 note 5 states that connection-close
- * is invalid for a request entity - request bodies must be
- * denoted by C-L or T-E: chunked.
- *
- * Note that since the proxy uses this filter to handle the
- * proxied *response*, proxy responses MUST be exempt.
- */
- if (ctx->state == BODY_NONE && f->r->proxyreq != PROXYREQ_RESPONSE) {
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(b, e);
- ctx->eos_sent = 1;
- return APR_SUCCESS;
- }
-
- /* Since we're about to read data, send 100-Continue if needed.
- * Only valid on chunked and C-L bodies where the C-L is > 0. */
- if ((ctx->state == BODY_CHUNK ||
- (ctx->state == BODY_LENGTH && ctx->remaining > 0)) &&
- f->r->expecting_100 && f->r->proto_num >= HTTP_VERSION(1,1)) {
- char *tmp;
- apr_bucket_brigade *bb;
-
- tmp = apr_pstrcat(f->r->pool, AP_SERVER_PROTOCOL, " ",
- status_lines[0], CRLF CRLF, NULL);
- bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
- e = apr_bucket_pool_create(tmp, strlen(tmp), f->r->pool,
- f->c->bucket_alloc);
- APR_BRIGADE_INSERT_HEAD(bb, e);
- e = apr_bucket_flush_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
-
- ap_pass_brigade(f->c->output_filters, bb);
- }
-
- /* We can't read the chunk until after sending 100 if required. */
- if (ctx->state == BODY_CHUNK) {
- char line[30];
- apr_bucket_brigade *bb;
- apr_size_t len = 30;
- apr_off_t brigade_length;
-
- bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
-
- rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE,
- APR_BLOCK_READ, 0);
-
- if (rv == APR_SUCCESS) {
- /* We have to check the length of the brigade we got back.
- * We will not accept partial lines.
- */
- rv = apr_brigade_length(bb, 1, &brigade_length);
- if (rv == APR_SUCCESS
- && brigade_length > f->r->server->limit_req_line) {
- rv = APR_ENOSPC;
- }
- if (rv == APR_SUCCESS) {
- rv = apr_brigade_flatten(bb, line, &len);
- if (rv == APR_SUCCESS) {
- ctx->remaining = get_chunk_size(line);
- }
- }
- }
- apr_brigade_cleanup(bb);
-
- /* Detect chunksize error (such as overflow) */
- if (rv != APR_SUCCESS || ctx->remaining < 0) {
- ctx->remaining = 0; /* Reset it in case we have to
- * come back here later */
- e = ap_bucket_error_create(HTTP_REQUEST_ENTITY_TOO_LARGE, NULL,
- f->r->pool,
- f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
- ctx->eos_sent = 1;
- return ap_pass_brigade(f->r->output_filters, bb);
- }
-
- if (!ctx->remaining) {
- /* Handle trailers by calling ap_get_mime_headers again! */
- ctx->state = BODY_NONE;
- ap_get_mime_headers(f->r);
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(b, e);
- ctx->eos_sent = 1;
- return APR_SUCCESS;
- }
- }
- }
-
- if (ctx->eos_sent) {
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(b, e);
- return APR_SUCCESS;
- }
-
- if (!ctx->remaining) {
- switch (ctx->state) {
- case BODY_NONE:
- break;
- case BODY_LENGTH:
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(b, e);
- ctx->eos_sent = 1;
- return APR_SUCCESS;
- case BODY_CHUNK:
- {
- char line[30];
- apr_bucket_brigade *bb;
- apr_size_t len = 30;
-
- bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
-
- /* We need to read the CRLF after the chunk. */
- rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE,
- APR_BLOCK_READ, 0);
- apr_brigade_cleanup(bb);
-
- if (rv == APR_SUCCESS) {
- /* Read the real chunk line. */
- rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE,
- APR_BLOCK_READ, 0);
- if (rv == APR_SUCCESS) {
- rv = apr_brigade_flatten(bb, line, &len);
- if (rv == APR_SUCCESS) {
- ctx->remaining = get_chunk_size(line);
- }
- }
- apr_brigade_cleanup(bb);
- }
-
- /* Detect chunksize error (such as overflow) */
- if (rv != APR_SUCCESS || ctx->remaining < 0) {
- ctx->remaining = 0; /* Reset it in case we have to
- * come back here later */
- e = ap_bucket_error_create(HTTP_REQUEST_ENTITY_TOO_LARGE,
- NULL, f->r->pool,
- f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
- ctx->eos_sent = 1;
- return ap_pass_brigade(f->r->output_filters, bb);
- }
-
- if (!ctx->remaining) {
- /* Handle trailers by calling ap_get_mime_headers again! */
- ctx->state = BODY_NONE;
- ap_get_mime_headers(f->r);
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(b, e);
- ctx->eos_sent = 1;
- return APR_SUCCESS;
- }
- }
- break;
- }
- }
-
- /* Ensure that the caller can not go over our boundary point. */
- if (ctx->state == BODY_LENGTH || ctx->state == BODY_CHUNK) {
- if (ctx->remaining < readbytes) {
- readbytes = ctx->remaining;
- }
- AP_DEBUG_ASSERT(readbytes > 0);
- }
-
- rv = ap_get_brigade(f->next, b, mode, block, readbytes);
-
- if (rv != APR_SUCCESS) {
- return rv;
- }
-
- /* How many bytes did we just read? */
- apr_brigade_length(b, 0, &totalread);
-
- /* If this happens, we have a bucket of unknown length. Die because
- * it means our assumptions have changed. */
- AP_DEBUG_ASSERT(totalread >= 0);
-
- if (ctx->state != BODY_NONE) {
- ctx->remaining -= totalread;
- }
-
- /* If we have no more bytes remaining on a C-L request,
- * save the callter a roundtrip to discover EOS.
- */
- if (ctx->state == BODY_LENGTH && ctx->remaining == 0) {
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(b, e);
- }
-
- /* We have a limit in effect. */
- if (ctx->limit) {
- /* FIXME: Note that we might get slightly confused on chunked inputs
- * as we'd need to compensate for the chunk lengths which may not
- * really count. This seems to be up for interpretation. */
- ctx->limit_used += totalread;
- if (ctx->limit < ctx->limit_used) {
- apr_bucket_brigade *bb;
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r,
- "Read content-length of %" APR_OFF_T_FMT
- " is larger than the configured limit"
- " of %" APR_OFF_T_FMT, ctx->limit_used, ctx->limit);
- bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
- e = ap_bucket_error_create(HTTP_REQUEST_ENTITY_TOO_LARGE, NULL,
- f->r->pool,
- f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
- ctx->eos_sent = 1;
- return ap_pass_brigade(f->r->output_filters, bb);
- }
- }
-
- return APR_SUCCESS;
-}
-
-/* The index is found by its offset from the x00 code of each level.
- * Although this is fast, it will need to be replaced if some nutcase
- * decides to define a high-numbered code before the lower numbers.
- * If that sad event occurs, replace the code below with a linear search
- * from status_lines[shortcut[i]] to status_lines[shortcut[i+1]-1];
- */
-AP_DECLARE(int) ap_index_of_response(int status)
-{
- static int shortcut[6] = {0, LEVEL_200, LEVEL_300, LEVEL_400,
- LEVEL_500, RESPONSE_CODES};
- int i, pos;
-
- if (status < 100) { /* Below 100 is illegal for HTTP status */
- return LEVEL_500;
- }
-
- for (i = 0; i < 5; i++) {
- status -= 100;
- if (status < 100) {
- pos = (status + shortcut[i]);
- if (pos < shortcut[i + 1]) {
- return pos;
- }
- else {
- return LEVEL_500; /* status unknown (falls in gap) */
- }
- }
- }
- return LEVEL_500; /* 600 or above is also illegal */
-}
-
-AP_DECLARE(const char *) ap_get_status_line(int status)
-{
- return status_lines[ap_index_of_response(status)];
-}
-
-typedef struct header_struct {
- apr_pool_t *pool;
- apr_bucket_brigade *bb;
-} header_struct;
-
-/* Send a single HTTP header field to the client. Note that this function
- * is used in calls to table_do(), so their interfaces are co-dependent.
- * In other words, don't change this one without checking table_do in alloc.c.
- * It returns true unless there was a write error of some kind.
- */
-static int form_header_field(header_struct *h,
- const char *fieldname, const char *fieldval)
-{
-#if APR_CHARSET_EBCDIC
- char *headfield;
- apr_size_t len;
- apr_size_t name_len;
- apr_size_t val_len;
- char *next;
-
- name_len = strlen(fieldname);
- val_len = strlen(fieldval);
- len = name_len + val_len + 4; /* 4 for ": " plus CRLF */
- headfield = (char *)apr_palloc(h->pool, len + 1);
- memcpy(headfield, fieldname, name_len);
- next = headfield + name_len;
- *next++ = ':';
- *next++ = ' ';
- memcpy(next, fieldval, val_len);
- next += val_len;
- *next++ = CR;
- *next++ = LF;
- *next = 0;
- ap_xlate_proto_to_ascii(headfield, len);
- apr_brigade_write(h->bb, NULL, NULL, headfield, len);
-#else
- struct iovec vec[4];
- struct iovec *v = vec;
- v->iov_base = (void *)fieldname;
- v->iov_len = strlen(fieldname);
- v++;
- v->iov_base = ": ";
- v->iov_len = sizeof(": ") - 1;
- v++;
- v->iov_base = (void *)fieldval;
- v->iov_len = strlen(fieldval);
- v++;
- v->iov_base = CRLF;
- v->iov_len = sizeof(CRLF) - 1;
- apr_brigade_writev(h->bb, NULL, NULL, vec, 4);
-#endif /* !APR_CHARSET_EBCDIC */
- return 1;
-}
-
-/* Send a request's HTTP response headers to the client.
- */
-static apr_status_t send_all_header_fields(header_struct *h,
- const request_rec *r)
-{
- const apr_array_header_t *elts;
- const apr_table_entry_t *t_elt;
- const apr_table_entry_t *t_end;
- struct iovec *vec;
- struct iovec *vec_next;
-
- elts = apr_table_elts(r->headers_out);
- if (elts->nelts == 0) {
- return APR_SUCCESS;
- }
- t_elt = (const apr_table_entry_t *)(elts->elts);
- t_end = t_elt + elts->nelts;
- vec = (struct iovec *)apr_palloc(h->pool, 4 * elts->nelts *
- sizeof(struct iovec));
- vec_next = vec;
-
- /* For each field, generate
- * name ": " value CRLF
- */
- do {
- vec_next->iov_base = (void*)(t_elt->key);
- vec_next->iov_len = strlen(t_elt->key);
- vec_next++;
- vec_next->iov_base = ": ";
- vec_next->iov_len = sizeof(": ") - 1;
- vec_next++;
- vec_next->iov_base = (void*)(t_elt->val);
- vec_next->iov_len = strlen(t_elt->val);
- vec_next++;
- vec_next->iov_base = CRLF;
- vec_next->iov_len = sizeof(CRLF) - 1;
- vec_next++;
- t_elt++;
- } while (t_elt < t_end);
-
-#if APR_CHARSET_EBCDIC
- {
- apr_size_t len;
- char *tmp = apr_pstrcatv(r->pool, vec, vec_next - vec, &len);
- ap_xlate_proto_to_ascii(tmp, len);
- return apr_brigade_write(h->bb, NULL, NULL, tmp, len);
- }
-#else
- return apr_brigade_writev(h->bb, NULL, NULL, vec, vec_next - vec);
-#endif
-}
-
-/*
- * Determine the protocol to use for the response. Potentially downgrade
- * to HTTP/1.0 in some situations and/or turn off keepalives.
- *
- * also prepare r->status_line.
- */
-static void basic_http_header_check(request_rec *r,
- const char **protocol)
-{
- if (r->assbackwards) {
- /* no such thing as a response protocol */
- return;
- }
-
- if (!r->status_line) {
- r->status_line = status_lines[ap_index_of_response(r->status)];
- }
-
- /* Note that we must downgrade before checking for force responses. */
- if (r->proto_num > HTTP_VERSION(1,0)
- && apr_table_get(r->subprocess_env, "downgrade-1.0")) {
- r->proto_num = HTTP_VERSION(1,0);
- }
-
- /* kludge around broken browsers when indicated by force-response-1.0
- */
- if (r->proto_num == HTTP_VERSION(1,0)
- && apr_table_get(r->subprocess_env, "force-response-1.0")) {
- *protocol = "HTTP/1.0";
- r->connection->keepalive = AP_CONN_CLOSE;
- }
- else {
- *protocol = AP_SERVER_PROTOCOL;
- }
-
-}
-
-/* fill "bb" with a barebones/initial HTTP response header */
-static void basic_http_header(request_rec *r, apr_bucket_brigade *bb,
- const char *protocol)
-{
- char *date;
- const char *server;
- header_struct h;
- struct iovec vec[4];
-
- if (r->assbackwards) {
- /* there are no headers to send */
- return;
- }
-
- /* Output the HTTP/1.x Status-Line and the Date and Server fields */
-
- vec[0].iov_base = (void *)protocol;
- vec[0].iov_len = strlen(protocol);
- vec[1].iov_base = (void *)" ";
- vec[1].iov_len = sizeof(" ") - 1;
- vec[2].iov_base = (void *)(r->status_line);
- vec[2].iov_len = strlen(r->status_line);
- vec[3].iov_base = (void *)CRLF;
- vec[3].iov_len = sizeof(CRLF) - 1;
-#if APR_CHARSET_EBCDIC
- {
- char *tmp;
- apr_size_t len;
- tmp = apr_pstrcatv(r->pool, vec, 4, &len);
- ap_xlate_proto_to_ascii(tmp, len);
- apr_brigade_write(bb, NULL, NULL, tmp, len);
- }
-#else
- apr_brigade_writev(bb, NULL, NULL, vec, 4);
-#endif
-
- date = apr_palloc(r->pool, APR_RFC822_DATE_LEN);
- ap_recent_rfc822_date(date, r->request_time);
-
- h.pool = r->pool;
- h.bb = bb;
- form_header_field(&h, "Date", date);
-
- /* keep the set-by-proxy server header, otherwise
- * generate a new server header */
- if (r->proxyreq != PROXYREQ_NONE) {
- server = apr_table_get(r->headers_out, "Server");
- if (server) {
- form_header_field(&h, "Server", server);
- }
- }
- else {
- form_header_field(&h, "Server", ap_get_server_version());
- }
-
- /* unset so we don't send them again */
- apr_table_unset(r->headers_out, "Date"); /* Avoid bogosity */
- apr_table_unset(r->headers_out, "Server");
-}
-
-AP_DECLARE(void) ap_basic_http_header(request_rec *r, apr_bucket_brigade *bb)
-{
- const char *protocol;
-
- basic_http_header_check(r, &protocol);
- basic_http_header(r, bb, protocol);
-}
-
-/* Navigator versions 2.x, 3.x and 4.0 betas up to and including 4.0b2
- * have a header parsing bug. If the terminating \r\n occur starting
- * at offset 256, 257 or 258 of output then it will not properly parse
- * the headers. Curiously it doesn't exhibit this problem at 512, 513.
- * We are guessing that this is because their initial read of a new request
- * uses a 256 byte buffer, and subsequent reads use a larger buffer.
- * So the problem might exist at different offsets as well.
- *
- * This should also work on keepalive connections assuming they use the
- * same small buffer for the first read of each new request.
- *
- * At any rate, we check the bytes written so far and, if we are about to
- * tickle the bug, we instead insert a bogus padding header. Since the bug
- * manifests as a broken image in Navigator, users blame the server. :(
- * It is more expensive to check the User-Agent than it is to just add the
- * bytes, so we haven't used the BrowserMatch feature here.
- */
-static void terminate_header(apr_bucket_brigade *bb)
-{
- char tmp[] = "X-Pad: avoid browser bug" CRLF;
- char crlf[] = CRLF;
- apr_off_t len;
- apr_size_t buflen;
-
- (void) apr_brigade_length(bb, 1, &len);
-
- if (len >= 255 && len <= 257) {
- buflen = strlen(tmp);
- ap_xlate_proto_to_ascii(tmp, buflen);
- apr_brigade_write(bb, NULL, NULL, tmp, buflen);
- }
- buflen = strlen(crlf);
- ap_xlate_proto_to_ascii(crlf, buflen);
- apr_brigade_write(bb, NULL, NULL, crlf, buflen);
-}
-
-/* Build the Allow field-value from the request handler method mask.
- * Note that we always allow TRACE, since it is handled below.
- */
-static char *make_allow(request_rec *r)
-{
- char *list;
- apr_int64_t mask;
- apr_array_header_t *allow = apr_array_make(r->pool, 10, sizeof(char *));
- apr_hash_index_t *hi = apr_hash_first(r->pool, methods_registry);
-
- mask = r->allowed_methods->method_mask;
-
- for (; hi; hi = apr_hash_next(hi)) {
- const void *key;
- void *val;
-
- apr_hash_this(hi, &key, NULL, &val);
- if ((mask & (AP_METHOD_BIT << *(int *)val)) != 0) {
- *(const char **)apr_array_push(allow) = key;
-
- /* the M_GET method actually refers to two methods */
- if (*(int *)val == M_GET)
- *(const char **)apr_array_push(allow) = "HEAD";
- }
- }
-
- /* TRACE is always allowed */
- *(const char **)apr_array_push(allow) = "TRACE";
-
- list = apr_array_pstrcat(r->pool, allow, ',');
-
- /* ### this is rather annoying. we should enforce registration of
- ### these methods */
- if ((mask & (AP_METHOD_BIT << M_INVALID))
- && (r->allowed_methods->method_list != NULL)
- && (r->allowed_methods->method_list->nelts != 0)) {
- int i;
- char **xmethod = (char **) r->allowed_methods->method_list->elts;
-
- /*
- * Append all of the elements of r->allowed_methods->method_list
- */
- for (i = 0; i < r->allowed_methods->method_list->nelts; ++i) {
- list = apr_pstrcat(r->pool, list, ",", xmethod[i], NULL);
- }
- }
-
- return list;
-}
-
-AP_DECLARE_NONSTD(int) ap_send_http_trace(request_rec *r)
-{
- int rv;
- apr_bucket_brigade *b;
- header_struct h;
-
- if (r->method_number != M_TRACE) {
- return DECLINED;
- }
-
- /* Get the original request */
- while (r->prev) {
- r = r->prev;
- }
-
- if ((rv = ap_setup_client_block(r, REQUEST_NO_BODY))) {
- return rv;
- }
-
- ap_set_content_type(r, "message/http");
-
- /* Now we recreate the request, and echo it back */
-
- b = apr_brigade_create(r->pool, r->connection->bucket_alloc);
- apr_brigade_putstrs(b, NULL, NULL, r->the_request, CRLF, NULL);
- h.pool = r->pool;
- h.bb = b;
- apr_table_do((int (*) (void *, const char *, const char *))
- form_header_field, (void *) &h, r->headers_in, NULL);
- apr_brigade_puts(b, NULL, NULL, CRLF);
- ap_pass_brigade(r->output_filters, b);
-
- return DONE;
-}
-
-AP_DECLARE(int) ap_send_http_options(request_rec *r)
-{
- if (r->assbackwards) {
- return DECLINED;
- }
-
- apr_table_setn(r->headers_out, "Allow", make_allow(r));
-
- /* the request finalization will send an EOS, which will flush all
- * the headers out (including the Allow header)
- */
-
- return OK;
-}
-
-/* This routine is called by apr_table_do and merges all instances of
- * the passed field values into a single array that will be further
- * processed by some later routine. Originally intended to help split
- * and recombine multiple Vary fields, though it is generic to any field
- * consisting of comma/space-separated tokens.
- */
-static int uniq_field_values(void *d, const char *key, const char *val)
-{
- apr_array_header_t *values;
- char *start;
- char *e;
- char **strpp;
- int i;
-
- values = (apr_array_header_t *)d;
-
- e = apr_pstrdup(values->pool, val);
-
- do {
- /* Find a non-empty fieldname */
-
- while (*e == ',' || apr_isspace(*e)) {
- ++e;
- }
- if (*e == '\0') {
- break;
- }
- start = e;
- while (*e != '\0' && *e != ',' && !apr_isspace(*e)) {
- ++e;
- }
- if (*e != '\0') {
- *e++ = '\0';
- }
-
- /* Now add it to values if it isn't already represented.
- * Could be replaced by a ap_array_strcasecmp() if we had one.
- */
- for (i = 0, strpp = (char **) values->elts; i < values->nelts;
- ++i, ++strpp) {
- if (*strpp && strcasecmp(*strpp, start) == 0) {
- break;
- }
- }
- if (i == values->nelts) { /* if not found */
- *(char **)apr_array_push(values) = start;
- }
- } while (*e != '\0');
-
- return 1;
-}
-
-/*
- * Since some clients choke violently on multiple Vary fields, or
- * Vary fields with duplicate tokens, combine any multiples and remove
- * any duplicates.
- */
-static void fixup_vary(request_rec *r)
-{
- apr_array_header_t *varies;
-
- varies = apr_array_make(r->pool, 5, sizeof(char *));
-
- /* Extract all Vary fields from the headers_out, separate each into
- * its comma-separated fieldname values, and then add them to varies
- * if not already present in the array.
- */
- apr_table_do((int (*)(void *, const char *, const char *))uniq_field_values,
- (void *) varies, r->headers_out, "Vary", NULL);
-
- /* If we found any, replace old Vary fields with unique-ified value */
-
- if (varies->nelts > 0) {
- apr_table_setn(r->headers_out, "Vary",
- apr_array_pstrcat(r->pool, varies, ','));
- }
-}
-
-AP_DECLARE(void) ap_set_content_type(request_rec *r, const char *ct)
-{
- if (!ct) {
- r->content_type = NULL;
- }
- else if (!r->content_type || strcmp(r->content_type, ct)) {
- r->content_type = ct;
-
- /* Insert filters requested by the AddOutputFiltersByType
- * configuration directive. Content-type filters must be
- * inserted after the content handlers have run because
- * only then, do we reliably know the content-type.
- */
- ap_add_output_filters_by_type(r);
- }
-}
-
-typedef struct header_filter_ctx {
- int headers_sent;
-} header_filter_ctx;
-
-AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f,
- apr_bucket_brigade *b)
-{
- request_rec *r = f->r;
- conn_rec *c = r->connection;
- const char *clheader;
- const char *protocol;
- apr_bucket *e;
- apr_bucket_brigade *b2;
- header_struct h;
- header_filter_ctx *ctx = f->ctx;
-
- AP_DEBUG_ASSERT(!r->main);
-
- if (r->header_only) {
- if (!ctx) {
- ctx = f->ctx = apr_pcalloc(r->pool, sizeof(header_filter_ctx));
- }
- else if (ctx->headers_sent) {
- apr_brigade_destroy(b);
- return OK;
- }
- }
-
- for (e = APR_BRIGADE_FIRST(b);
- e != APR_BRIGADE_SENTINEL(b);
- e = APR_BUCKET_NEXT(e))
- {
- if (e->type == &ap_bucket_type_error) {
- ap_bucket_error *eb = e->data;
-
- ap_die(eb->status, r);
- return AP_FILTER_ERROR;
- }
- }
-
- if (r->assbackwards) {
- r->sent_bodyct = 1;
- ap_remove_output_filter(f);
- return ap_pass_brigade(f->next, b);
- }
-
- /*
- * Now that we are ready to send a response, we need to combine the two
- * header field tables into a single table. If we don't do this, our
- * later attempts to set or unset a given fieldname might be bypassed.
- */
- if (!apr_is_empty_table(r->err_headers_out)) {
- r->headers_out = apr_table_overlay(r->pool, r->err_headers_out,
- r->headers_out);
- }
-
- /*
- * Remove the 'Vary' header field if the client can't handle it.
- * Since this will have nasty effects on HTTP/1.1 caches, force
- * the response into HTTP/1.0 mode.
- *
- * Note: the force-response-1.0 should come before the call to
- * basic_http_header_check()
- */
- if (apr_table_get(r->subprocess_env, "force-no-vary") != NULL) {
- apr_table_unset(r->headers_out, "Vary");
- r->proto_num = HTTP_VERSION(1,0);
- apr_table_set(r->subprocess_env, "force-response-1.0", "1");
- }
- else {
- fixup_vary(r);
- }
-
- /*
- * Now remove any ETag response header field if earlier processing
- * says so (such as a 'FileETag None' directive).
- */
- if (apr_table_get(r->notes, "no-etag") != NULL) {
- apr_table_unset(r->headers_out, "ETag");
- }
-
- /* determine the protocol and whether we should use keepalives. */
- basic_http_header_check(r, &protocol);
- ap_set_keepalive(r);
-
- if (r->chunked) {
- apr_table_mergen(r->headers_out, "Transfer-Encoding", "chunked");
- apr_table_unset(r->headers_out, "Content-Length");
- }
-
- apr_table_setn(r->headers_out, "Content-Type",
- ap_make_content_type(r, r->content_type));
-
- if (r->content_encoding) {
- apr_table_setn(r->headers_out, "Content-Encoding",
- r->content_encoding);
- }
-
- if (!apr_is_empty_array(r->content_languages)) {
- int i;
- char **languages = (char **)(r->content_languages->elts);
- for (i = 0; i < r->content_languages->nelts; ++i) {
- apr_table_mergen(r->headers_out, "Content-Language", languages[i]);
- }
- }
-
- /*
- * Control cachability for non-cachable responses if not already set by
- * some other part of the server configuration.
- */
- if (r->no_cache && !apr_table_get(r->headers_out, "Expires")) {
- char *date = apr_palloc(r->pool, APR_RFC822_DATE_LEN);
- ap_recent_rfc822_date(date, r->request_time);
- apr_table_addn(r->headers_out, "Expires", date);
- }
-
- /* This is a hack, but I can't find anyway around it. The idea is that
- * we don't want to send out 0 Content-Lengths if it is a head request.
- * This happens when modules try to outsmart the server, and return
- * if they see a HEAD request. Apache 1.3 handlers were supposed to
- * just return in that situation, and the core handled the HEAD. In
- * 2.0, if a handler returns, then the core sends an EOS bucket down
- * the filter stack, and the content-length filter computes a C-L of
- * zero and that gets put in the headers, and we end up sending a
- * zero C-L to the client. We can't just remove the C-L filter,
- * because well behaved 2.0 handlers will send their data down the stack,
- * and we will compute a real C-L for the head request. RBB
- */
- if (r->header_only
- && (clheader = apr_table_get(r->headers_out, "Content-Length"))
- && !strcmp(clheader, "0")) {
- apr_table_unset(r->headers_out, "Content-Length");
- }
-
- b2 = apr_brigade_create(r->pool, c->bucket_alloc);
- basic_http_header(r, b2, protocol);
-
- h.pool = r->pool;
- h.bb = b2;
-
- if (r->status == HTTP_NOT_MODIFIED) {
- apr_table_do((int (*)(void *, const char *, const char *)) form_header_field,
- (void *) &h, r->headers_out,
- "Connection",
- "Keep-Alive",
- "ETag",
- "Content-Location",
- "Expires",
- "Cache-Control",
- "Vary",
- "Warning",
- "WWW-Authenticate",
- "Proxy-Authenticate",
- "Set-Cookie",
- "Set-Cookie2",
- NULL);
- }
- else {
- send_all_header_fields(&h, r);
- }
-
- terminate_header(b2);
-
- ap_pass_brigade(f->next, b2);
-
- if (r->header_only) {
- apr_brigade_destroy(b);
- ctx->headers_sent = 1;
- return OK;
- }
-
- r->sent_bodyct = 1; /* Whatever follows is real body stuff... */
-
- if (r->chunked) {
- /* We can't add this filter until we have already sent the headers.
- * If we add it before this point, then the headers will be chunked
- * as well, and that is just wrong.
- */
- ap_add_output_filter("CHUNK", NULL, r, r->connection);
- }
-
- /* Don't remove this filter until after we have added the CHUNK filter.
- * Otherwise, f->next won't be the CHUNK filter and thus the first
- * brigade won't be chunked properly.
- */
- ap_remove_output_filter(f);
- return ap_pass_brigade(f->next, b);
-}
-
-/* Here we deal with getting the request message body from the client.
- * Whether or not the request contains a body is signaled by the presence
- * of a non-zero Content-Length or by a Transfer-Encoding: chunked.
- *
- * Note that this is more complicated than it was in Apache 1.1 and prior
- * versions, because chunked support means that the module does less.
- *
- * The proper procedure is this:
- *
- * 1. Call setup_client_block() near the beginning of the request
- * handler. This will set up all the necessary properties, and will
- * return either OK, or an error code. If the latter, the module should
- * return that error code. The second parameter selects the policy to
- * apply if the request message indicates a body, and how a chunked
- * transfer-coding should be interpreted. Choose one of
- *
- * REQUEST_NO_BODY Send 413 error if message has any body
- * REQUEST_CHUNKED_ERROR Send 411 error if body without Content-Length
- * REQUEST_CHUNKED_DECHUNK If chunked, remove the chunks for me.
- *
- * In order to use the last two options, the caller MUST provide a buffer
- * large enough to hold a chunk-size line, including any extensions.
- *
- * 2. When you are ready to read a body (if any), call should_client_block().
- * This will tell the module whether or not to read input. If it is 0,
- * the module should assume that there is no message body to read.
- * This step also sends a 100 Continue response to HTTP/1.1 clients,
- * so should not be called until the module is *definitely* ready to
- * read content. (otherwise, the point of the 100 response is defeated).
- * Never call this function more than once.
- *
- * 3. Finally, call get_client_block in a loop. Pass it a buffer and its size.
- * It will put data into the buffer (not necessarily a full buffer), and
- * return the length of the input block. When it is done reading, it will
- * return 0 if EOF, or -1 if there was an error.
- * If an error occurs on input, we force an end to keepalive.
- */
-
-AP_DECLARE(int) ap_setup_client_block(request_rec *r, int read_policy)
-{
- const char *tenc = apr_table_get(r->headers_in, "Transfer-Encoding");
- const char *lenp = apr_table_get(r->headers_in, "Content-Length");
-
- r->read_body = read_policy;
- r->read_chunked = 0;
- r->remaining = 0;
-
- if (tenc) {
- if (strcasecmp(tenc, "chunked")) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "Unknown Transfer-Encoding %s", tenc);
- return HTTP_NOT_IMPLEMENTED;
- }
- if (r->read_body == REQUEST_CHUNKED_ERROR) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "chunked Transfer-Encoding forbidden: %s", r->uri);
- return (lenp) ? HTTP_BAD_REQUEST : HTTP_LENGTH_REQUIRED;
- }
-
- r->read_chunked = 1;
- }
- else if (lenp) {
- char *endstr;
-
- if (apr_strtoff(&r->remaining, lenp, &endstr, 10)
- || *endstr || r->remaining < 0) {
- r->remaining = 0;
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "Invalid Content-Length");
- return HTTP_BAD_REQUEST;
- }
- }
-
- if ((r->read_body == REQUEST_NO_BODY)
- && (r->read_chunked || (r->remaining > 0))) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "%s with body is not allowed for %s", r->method, r->uri);
- return HTTP_REQUEST_ENTITY_TOO_LARGE;
- }
-
-#ifdef AP_DEBUG
- {
- /* Make sure ap_getline() didn't leave any droppings. */
- core_request_config *req_cfg =
- (core_request_config *)ap_get_module_config(r->request_config,
- &core_module);
- AP_DEBUG_ASSERT(APR_BRIGADE_EMPTY(req_cfg->bb));
- }
-#endif
-
- return OK;
-}
-
-AP_DECLARE(int) ap_should_client_block(request_rec *r)
-{
- /* First check if we have already read the request body */
-
- if (r->read_length || (!r->read_chunked && (r->remaining <= 0))) {
- return 0;
- }
-
- return 1;
-}
-
-/**
- * Parse a chunk extension, detect overflow.
- * There are two error cases:
- * 1) If the conversion would require too many bits, a -1 is returned.
- * 2) If the conversion used the correct number of bits, but an overflow
- * caused only the sign bit to flip, then that negative number is
- * returned.
- * In general, any negative number can be considered an overflow error.
- */
-static long get_chunk_size(char *b)
-{
- long chunksize = 0;
- size_t chunkbits = sizeof(long) * 8;
-
- /* Skip leading zeros */
- while (*b == '0') {
- ++b;
- }
-
- while (apr_isxdigit(*b) && (chunkbits > 0)) {
- int xvalue = 0;
-
- if (*b >= '0' && *b <= '9') {
- xvalue = *b - '0';
- }
- else if (*b >= 'A' && *b <= 'F') {
- xvalue = *b - 'A' + 0xa;
- }
- else if (*b >= 'a' && *b <= 'f') {
- xvalue = *b - 'a' + 0xa;
- }
-
- chunksize = (chunksize << 4) | xvalue;
- chunkbits -= 4;
- ++b;
- }
- if (apr_isxdigit(*b) && (chunkbits <= 0)) {
- /* overflow */
- return -1;
- }
-
- return chunksize;
-}
-
-/* get_client_block is called in a loop to get the request message body.
- * This is quite simple if the client includes a content-length
- * (the normal case), but gets messy if the body is chunked. Note that
- * r->remaining is used to maintain state across calls and that
- * r->read_length is the total number of bytes given to the caller
- * across all invocations. It is messy because we have to be careful not
- * to read past the data provided by the client, since these reads block.
- * Returns 0 on End-of-body, -1 on error or premature chunk end.
- *
- */
-AP_DECLARE(long) ap_get_client_block(request_rec *r, char *buffer,
- apr_size_t bufsiz)
-{
- apr_status_t rv;
- apr_bucket_brigade *bb;
-
- if (r->remaining < 0 || (!r->read_chunked && r->remaining == 0)) {
- return 0;
- }
-
- bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
- if (bb == NULL) {
- r->connection->keepalive = AP_CONN_CLOSE;
- return -1;
- }
-
- rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES,
- APR_BLOCK_READ, bufsiz);
-
- /* We lose the failure code here. This is why ap_get_client_block should
- * not be used.
- */
- if (rv != APR_SUCCESS) {
- /* if we actually fail here, we want to just return and
- * stop trying to read data from the client.
- */
- r->connection->keepalive = AP_CONN_CLOSE;
- apr_brigade_destroy(bb);
- return -1;
- }
-
- /* If this fails, it means that a filter is written incorrectly and that
- * it needs to learn how to properly handle APR_BLOCK_READ requests by
- * returning data when requested.
- */
- AP_DEBUG_ASSERT(!APR_BRIGADE_EMPTY(bb));
-
- /* Check to see if EOS in the brigade.
- *
- * If so, we have to leave a nugget for the *next* ap_get_client_block
- * call to return 0.
- */
- if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) {
- if (r->read_chunked) {
- r->remaining = -1;
- }
- else {
- r->remaining = 0;
- }
- }
-
- rv = apr_brigade_flatten(bb, buffer, &bufsiz);
- if (rv != APR_SUCCESS) {
- apr_brigade_destroy(bb);
- return -1;
- }
-
- /* XXX yank me? */
- r->read_length += bufsiz;
-
- apr_brigade_destroy(bb);
- return bufsiz;
-}
-
-/* In HTTP/1.1, any method can have a body. However, most GET handlers
- * wouldn't know what to do with a request body if they received one.
- * This helper routine tests for and reads any message body in the request,
- * simply discarding whatever it receives. We need to do this because
- * failing to read the request body would cause it to be interpreted
- * as the next request on a persistent connection.
- *
- * Since we return an error status if the request is malformed, this
- * routine should be called at the beginning of a no-body handler, e.g.,
- *
- * if ((retval = ap_discard_request_body(r)) != OK) {
- * return retval;
- * }
- */
-AP_DECLARE(int) ap_discard_request_body(request_rec *r)
-{
- apr_bucket_brigade *bb;
- int rv, seen_eos;
-
- /* Sometimes we'll get in a state where the input handling has
- * detected an error where we want to drop the connection, so if
- * that's the case, don't read the data as that is what we're trying
- * to avoid.
- *
- * This function is also a no-op on a subrequest.
- */
- if (r->main || r->connection->keepalive == AP_CONN_CLOSE ||
- ap_status_drops_connection(r->status)) {
- return OK;
- }
-
- bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
- seen_eos = 0;
- do {
- apr_bucket *bucket;
-
- rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES,
- APR_BLOCK_READ, HUGE_STRING_LEN);
-
- if (rv != APR_SUCCESS) {
- /* FIXME: If we ever have a mapping from filters (apr_status_t)
- * to HTTP error codes, this would be a good place for them.
- *
- * If we received the special case AP_FILTER_ERROR, it means
- * that the filters have already handled this error.
- * Otherwise, we should assume we have a bad request.
- */
- if (rv == AP_FILTER_ERROR) {
- apr_brigade_destroy(bb);
- return rv;
- }
- else {
- apr_brigade_destroy(bb);
- return HTTP_BAD_REQUEST;
- }
- }
-
- for (bucket = APR_BRIGADE_FIRST(bb);
- bucket != APR_BRIGADE_SENTINEL(bb);
- bucket = APR_BUCKET_NEXT(bucket))
- {
- const char *data;
- apr_size_t len;
-
- if (APR_BUCKET_IS_EOS(bucket)) {
- seen_eos = 1;
- break;
- }
-
- /* These are metadata buckets. */
- if (bucket->length == 0) {
- continue;
- }
-
- /* We MUST read because in case we have an unknown-length
- * bucket or one that morphs, we want to exhaust it.
- */
- rv = apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);
- if (rv != APR_SUCCESS) {
- apr_brigade_destroy(bb);
- return HTTP_BAD_REQUEST;
- }
- }
- apr_brigade_cleanup(bb);
- } while (!seen_eos);
-
- return OK;
-}
-
-static const char *add_optional_notes(request_rec *r,
- const char *prefix,
- const char *key,
- const char *suffix)
-{
- const char *notes, *result;
-
- if ((notes = apr_table_get(r->notes, key)) == NULL) {
- result = apr_pstrcat(r->pool, prefix, suffix, NULL);
- }
- else {
- result = apr_pstrcat(r->pool, prefix, notes, suffix, NULL);
- }
-
- return result;
-}
-
-/* construct and return the default error message for a given
- * HTTP defined error code
- */
-static const char *get_canned_error_string(int status,
- request_rec *r,
- const char *location)
-{
- apr_pool_t *p = r->pool;
- const char *error_notes, *h1, *s1;
-
- switch (status) {
- case HTTP_MOVED_PERMANENTLY:
- case HTTP_MOVED_TEMPORARILY:
- case HTTP_TEMPORARY_REDIRECT:
- return(apr_pstrcat(p,
- "The document has moved pool, location),
- "\">here.
\n",
- NULL));
- case HTTP_SEE_OTHER:
- return(apr_pstrcat(p,
- "The answer to your request is located "
- "pool, location),
- "\">here.
\n",
- NULL));
- case HTTP_USE_PROXY:
- return(apr_pstrcat(p,
- "This resource is only accessible "
- "through the proxy\n",
- ap_escape_html(r->pool, location),
- "
\nYou will need to configure "
- "your client to use that proxy.
\n",
- NULL));
- case HTTP_PROXY_AUTHENTICATION_REQUIRED:
- case HTTP_UNAUTHORIZED:
- return("This server could not verify that you\n"
- "are authorized to access the document\n"
- "requested. Either you supplied the wrong\n"
- "credentials (e.g., bad password), or your\n"
- "browser doesn't understand how to supply\n"
- "the credentials required.
\n");
- case HTTP_BAD_REQUEST:
- return(add_optional_notes(r,
- "Your browser sent a request that "
- "this server could not understand.
\n",
- "error-notes",
- "
\n"));
- case HTTP_FORBIDDEN:
- return(apr_pstrcat(p,
- "You don't have permission to access ",
- ap_escape_html(r->pool, r->uri),
- "\non this server.
\n",
- NULL));
- case HTTP_NOT_FOUND:
- return(apr_pstrcat(p,
- "The requested URL ",
- ap_escape_html(r->pool, r->uri),
- " was not found on this server.
\n",
- NULL));
- case HTTP_METHOD_NOT_ALLOWED:
- return(apr_pstrcat(p,
- "The requested method ", r->method,
- " is not allowed for the URL ",
- ap_escape_html(r->pool, r->uri),
- ".
\n",
- NULL));
- case HTTP_NOT_ACCEPTABLE:
- s1 = apr_pstrcat(p,
- "An appropriate representation of the "
- "requested resource ",
- ap_escape_html(r->pool, r->uri),
- " could not be found on this server.
\n",
- NULL);
- return(add_optional_notes(r, s1, "variant-list", ""));
- case HTTP_MULTIPLE_CHOICES:
- return(add_optional_notes(r, "", "variant-list", ""));
- case HTTP_LENGTH_REQUIRED:
- s1 = apr_pstrcat(p,
- "A request of the requested method ",
- r->method,
- " requires a valid Content-length.
\n",
- NULL);
- return(add_optional_notes(r, s1, "error-notes", "
\n"));
- case HTTP_PRECONDITION_FAILED:
- return(apr_pstrcat(p,
- "The precondition on the request "
- "for the URL ",
- ap_escape_html(r->pool, r->uri),
- " evaluated to false.
\n",
- NULL));
- case HTTP_NOT_IMPLEMENTED:
- s1 = apr_pstrcat(p,
- "",
- ap_escape_html(r->pool, r->method), " to ",
- ap_escape_html(r->pool, r->uri),
- " not supported.
\n",
- NULL);
- return(add_optional_notes(r, s1, "error-notes", "
\n"));
- case HTTP_BAD_GATEWAY:
- s1 = "The proxy server received an invalid" CRLF
- "response from an upstream server.
" CRLF;
- return(add_optional_notes(r, s1, "error-notes", "
\n"));
- case HTTP_VARIANT_ALSO_VARIES:
- return(apr_pstrcat(p,
- "A variant for the requested "
- "resource\n
\n",
- ap_escape_html(r->pool, r->uri),
- "\n
\nis itself a negotiable resource. "
- "This indicates a configuration error.\n",
- NULL));
- case HTTP_REQUEST_TIME_OUT:
- return("Server timeout waiting for the HTTP request from the client.
\n");
- case HTTP_GONE:
- return(apr_pstrcat(p,
- "The requested resource
",
- ap_escape_html(r->pool, r->uri),
- "
\nis no longer available on this server "
- "and there is no forwarding address.\n"
- "Please remove all references to this "
- "resource.
\n",
- NULL));
- case HTTP_REQUEST_ENTITY_TOO_LARGE:
- return(apr_pstrcat(p,
- "The requested resource
",
- ap_escape_html(r->pool, r->uri), "
\n",
- "does not allow request data with ",
- r->method,
- " requests, or the amount of data provided in\n"
- "the request exceeds the capacity limit.\n",
- NULL));
- case HTTP_REQUEST_URI_TOO_LARGE:
- s1 = "The requested URL's length exceeds the capacity\n"
- "limit for this server.
\n";
- return(add_optional_notes(r, s1, "error-notes", "
\n"));
- case HTTP_UNSUPPORTED_MEDIA_TYPE:
- return("The supplied request data is not in a format\n"
- "acceptable for processing by this resource.
\n");
- case HTTP_RANGE_NOT_SATISFIABLE:
- return("None of the range-specifier values in the Range\n"
- "request-header field overlap the current extent\n"
- "of the selected resource.
\n");
- case HTTP_EXPECTATION_FAILED:
- return(apr_pstrcat(p,
- "The expectation given in the Expect "
- "request-header"
- "\nfield could not be met by this server.
\n"
- "The client sent
\n Expect: ",
- apr_table_get(r->headers_in, "Expect"),
- "\n
\n"
- "but we only allow the 100-continue "
- "expectation.\n",
- NULL));
- case HTTP_UNPROCESSABLE_ENTITY:
- return("The server understands the media type of the\n"
- "request entity, but was unable to process the\n"
- "contained instructions.
\n");
- case HTTP_LOCKED:
- return("The requested resource is currently locked.\n"
- "The lock must be released or proper identification\n"
- "given before the method can be applied.
\n");
- case HTTP_FAILED_DEPENDENCY:
- return("The method could not be performed on the resource\n"
- "because the requested action depended on another\n"
- "action and that other action failed.
\n");
- case HTTP_UPGRADE_REQUIRED:
- return("The requested resource can only be retrieved\n"
- "using SSL. The server is willing to upgrade the current\n"
- "connection to SSL, but your client doesn't support it.\n"
- "Either upgrade your client, or try requesting the page\n"
- "using https://\n");
- case HTTP_INSUFFICIENT_STORAGE:
- return("
The method could not be performed on the resource\n"
- "because the server is unable to store the\n"
- "representation needed to successfully complete the\n"
- "request. There is insufficient free space left in\n"
- "your storage allocation.
\n");
- case HTTP_SERVICE_UNAVAILABLE:
- return("The server is temporarily unable to service your\n"
- "request due to maintenance downtime or capacity\n"
- "problems. Please try again later.
\n");
- case HTTP_GATEWAY_TIME_OUT:
- return("The proxy server did not receive a timely response\n"
- "from the upstream server.
\n");
- case HTTP_NOT_EXTENDED:
- return("A mandatory extension policy in the request is not\n"
- "accepted by the server for this resource.
\n");
- default: /* HTTP_INTERNAL_SERVER_ERROR */
- /*
- * This comparison to expose error-notes could be modified to
- * use a configuration directive and export based on that
- * directive. For now "*" is used to designate an error-notes
- * that is totally safe for any user to see (ie lacks paths,
- * database passwords, etc.)
- */
- if (((error_notes = apr_table_get(r->notes,
- "error-notes")) != NULL)
- && (h1 = apr_table_get(r->notes, "verbose-error-to")) != NULL
- && (strcmp(h1, "*") == 0)) {
- return(apr_pstrcat(p, error_notes, "\n", NULL));
- }
- else {
- return(apr_pstrcat(p,
- "The server encountered an internal "
- "error or\n"
- "misconfiguration and was unable to complete\n"
- "your request.
\n"
- "Please contact the server "
- "administrator,\n ",
- ap_escape_html(r->pool,
- r->server->server_admin),
- " and inform them of the time the "
- "error occurred,\n"
- "and anything you might have done that "
- "may have\n"
- "caused the error.
\n"
- "More information about this error "
- "may be available\n"
- "in the server error log.
\n",
- NULL));
- }
- /*
- * It would be nice to give the user the information they need to
- * fix the problem directly since many users don't have access to
- * the error_log (think University sites) even though they can easily
- * get this error by misconfiguring an htaccess file. However, the
- * e error notes tend to include the real file pathname in this case,
- * which some people consider to be a breach of privacy. Until we
- * can figure out a way to remove the pathname, leave this commented.
- *
- * if ((error_notes = apr_table_get(r->notes,
- * "error-notes")) != NULL) {
- * return(apr_pstrcat(p, error_notes, "\n", NULL);
- * }
- * else {
- * return "";
- * }
- */
- }
-}
-
-/* We should have named this send_canned_response, since it is used for any
- * response that can be generated by the server from the request record.
- * This includes all 204 (no content), 3xx (redirect), 4xx (client error),
- * and 5xx (server error) messages that have not been redirected to another
- * handler via the ErrorDocument feature.
- */
-AP_DECLARE(void) ap_send_error_response(request_rec *r, int recursive_error)
-{
- int status = r->status;
- int idx = ap_index_of_response(status);
- char *custom_response;
- const char *location = apr_table_get(r->headers_out, "Location");
-
- /* At this point, we are starting the response over, so we have to reset
- * this value.
- */
- r->eos_sent = 0;
-
- /* and we need to get rid of any RESOURCE filters that might be lurking
- * around, thinking they are in the middle of the original request
- */
-
- r->output_filters = r->proto_output_filters;
-
- ap_run_insert_error_filter(r);
-
- /*
- * It's possible that the Location field might be in r->err_headers_out
- * instead of r->headers_out; use the latter if possible, else the
- * former.
- */
- if (location == NULL) {
- location = apr_table_get(r->err_headers_out, "Location");
- }
- /* We need to special-case the handling of 204 and 304 responses,
- * since they have specific HTTP requirements and do not include a
- * message body. Note that being assbackwards here is not an option.
- */
- if (status == HTTP_NOT_MODIFIED) {
- ap_finalize_request_protocol(r);
- return;
- }
-
- if (status == HTTP_NO_CONTENT) {
- ap_finalize_request_protocol(r);
- return;
- }
-
- if (!r->assbackwards) {
- apr_table_t *tmp = r->headers_out;
-
- /* For all HTTP/1.x responses for which we generate the message,
- * we need to avoid inheriting the "normal status" header fields
- * that may have been set by the request handler before the
- * error or redirect, except for Location on external redirects.
- */
- r->headers_out = r->err_headers_out;
- r->err_headers_out = tmp;
- apr_table_clear(r->err_headers_out);
-
- if (ap_is_HTTP_REDIRECT(status) || (status == HTTP_CREATED)) {
- if ((location != NULL) && *location) {
- apr_table_setn(r->headers_out, "Location", location);
- }
- else {
- location = ""; /* avoids coredump when printing, below */
- }
- }
-
- r->content_languages = NULL;
- r->content_encoding = NULL;
- r->clength = 0;
- ap_set_content_type(r, "text/html; charset=iso-8859-1");
-
- if ((status == HTTP_METHOD_NOT_ALLOWED)
- || (status == HTTP_NOT_IMPLEMENTED)) {
- apr_table_setn(r->headers_out, "Allow", make_allow(r));
- }
-
- if (r->header_only) {
- ap_finalize_request_protocol(r);
- return;
- }
- }
-
- if ((custom_response = ap_response_code_string(r, idx))) {
- /*
- * We have a custom response output. This should only be
- * a text-string to write back. But if the ErrorDocument
- * was a local redirect and the requested resource failed
- * for any reason, the custom_response will still hold the
- * redirect URL. We don't really want to output this URL
- * as a text message, so first check the custom response
- * string to ensure that it is a text-string (using the
- * same test used in ap_die(), i.e. does it start with a ").
- *
- * If it's not a text string, we've got a recursive error or
- * an external redirect. If it's a recursive error, ap_die passes
- * us the second error code so we can write both, and has already
- * backed up to the original error. If it's an external redirect,
- * it hasn't happened yet; we may never know if it fails.
- */
- if (custom_response[0] == '\"') {
- ap_rputs(custom_response + 1, r);
- ap_finalize_request_protocol(r);
- return;
- }
- }
- {
- const char *title = status_lines[idx];
- const char *h1;
-
- /* Accept a status_line set by a module, but only if it begins
- * with the 3 digit status code
- */
- if (r->status_line != NULL
- && strlen(r->status_line) > 4 /* long enough */
- && apr_isdigit(r->status_line[0])
- && apr_isdigit(r->status_line[1])
- && apr_isdigit(r->status_line[2])
- && apr_isspace(r->status_line[3])
- && apr_isalnum(r->status_line[4])) {
- title = r->status_line;
- }
-
- /* folks decided they didn't want the error code in the H1 text */
- h1 = &title[4];
-
- /* can't count on a charset filter being in place here,
- * so do ebcdic->ascii translation explicitly (if needed)
- */
-
- ap_rvputs_proto_in_ascii(r,
- DOCTYPE_HTML_2_0
- "\n", title,
- "\n\n", h1, "
\n",
- NULL);
-
- ap_rvputs_proto_in_ascii(r,
- get_canned_error_string(status, r, location),
- NULL);
-
- if (recursive_error) {
- ap_rvputs_proto_in_ascii(r, "Additionally, a ",
- status_lines[ap_index_of_response(recursive_error)],
- "\nerror was encountered while trying to use an "
- "ErrorDocument to handle the request.
\n", NULL);
- }
- ap_rvputs_proto_in_ascii(r, ap_psignature("
\n", r), NULL);
- ap_rvputs_proto_in_ascii(r, "\n", NULL);
- }
- ap_finalize_request_protocol(r);
-}
-
-/*
- * Create a new method list with the specified number of preallocated
- * extension slots.
- */
-AP_DECLARE(ap_method_list_t *) ap_make_method_list(apr_pool_t *p, int nelts)
-{
- ap_method_list_t *ml;
-
- ml = (ap_method_list_t *) apr_palloc(p, sizeof(ap_method_list_t));
- ml->method_mask = 0;
- ml->method_list = apr_array_make(p, nelts, sizeof(char *));
- return ml;
-}
-
-/*
- * Make a copy of a method list (primarily for subrequests that may
- * subsequently change it; don't want them changing the parent's, too!).
- */
-AP_DECLARE(void) ap_copy_method_list(ap_method_list_t *dest,
- ap_method_list_t *src)
-{
- int i;
- char **imethods;
- char **omethods;
-
- dest->method_mask = src->method_mask;
- imethods = (char **) src->method_list->elts;
- for (i = 0; i < src->method_list->nelts; ++i) {
- omethods = (char **) apr_array_push(dest->method_list);
- *omethods = apr_pstrdup(dest->method_list->pool, imethods[i]);
- }
-}
-
-/*
- * Invoke a callback routine for each method in the specified list.
- */
-AP_DECLARE_NONSTD(void) ap_method_list_do(int (*comp) (void *urec,
- const char *mname,
- int mnum),
- void *rec,
- const ap_method_list_t *ml, ...)
-{
- va_list vp;
- va_start(vp, ml);
- ap_method_list_vdo(comp, rec, ml, vp);
- va_end(vp);
-}
-
-AP_DECLARE(void) ap_method_list_vdo(int (*comp) (void *mrec,
- const char *mname,
- int mnum),
- void *rec, const ap_method_list_t *ml,
- va_list vp)
-{
-
-}
-
-/*
- * Return true if the specified HTTP method is in the provided
- * method list.
- */
-AP_DECLARE(int) ap_method_in_list(ap_method_list_t *l, const char *method)
-{
- int methnum;
- int i;
- char **methods;
-
- /*
- * If it's one of our known methods, use the shortcut and check the
- * bitmask.
- */
- methnum = ap_method_number_of(method);
- if (methnum != M_INVALID) {
- return !!(l->method_mask & (AP_METHOD_BIT << methnum));
- }
- /*
- * Otherwise, see if the method name is in the array or string names
- */
- if ((l->method_list == NULL) || (l->method_list->nelts == 0)) {
- return 0;
- }
- methods = (char **)l->method_list->elts;
- for (i = 0; i < l->method_list->nelts; ++i) {
- if (strcmp(method, methods[i]) == 0) {
- return 1;
- }
- }
- return 0;
-}
-
-/*
- * Add the specified method to a method list (if it isn't already there).
- */
-AP_DECLARE(void) ap_method_list_add(ap_method_list_t *l, const char *method)
-{
- int methnum;
- int i;
- const char **xmethod;
- char **methods;
-
- /*
- * If it's one of our known methods, use the shortcut and use the
- * bitmask.
- */
- methnum = ap_method_number_of(method);
- l->method_mask |= (AP_METHOD_BIT << methnum);
- if (methnum != M_INVALID) {
- return;
- }
- /*
- * Otherwise, see if the method name is in the array of string names.
- */
- if (l->method_list->nelts != 0) {
- methods = (char **)l->method_list->elts;
- for (i = 0; i < l->method_list->nelts; ++i) {
- if (strcmp(method, methods[i]) == 0) {
- return;
- }
- }
- }
- xmethod = (const char **) apr_array_push(l->method_list);
- *xmethod = method;
-}
-
-/*
- * Remove the specified method from a method list.
- */
-AP_DECLARE(void) ap_method_list_remove(ap_method_list_t *l,
- const char *method)
-{
- int methnum;
- char **methods;
+#include "apr_strings.h"
+#include "apr_thread_proc.h" /* for RLIMIT stuff */
- /*
- * If it's a known methods, either builtin or registered
- * by a module, use the bitmask.
- */
- methnum = ap_method_number_of(method);
- l->method_mask |= ~(AP_METHOD_BIT << methnum);
- if (methnum != M_INVALID) {
- return;
- }
- /*
- * Otherwise, see if the method name is in the array of string names.
- */
- if (l->method_list->nelts != 0) {
- register int i, j, k;
- methods = (char **)l->method_list->elts;
- for (i = 0; i < l->method_list->nelts; ) {
- if (strcmp(method, methods[i]) == 0) {
- for (j = i, k = i + 1; k < l->method_list->nelts; ++j, ++k) {
- methods[j] = methods[k];
- }
- --l->method_list->nelts;
- }
- else {
- ++i;
- }
- }
- }
-}
+#define APR_WANT_STRFUNC
+#include "apr_want.h"
-/*
- * Reset a method list to be completely empty.
- */
-AP_DECLARE(void) ap_clear_method_list(ap_method_list_t *l)
-{
- l->method_mask = 0;
- l->method_list->nelts = 0;
-}
+#define CORE_PRIVATE
+#include "httpd.h"
+#include "http_config.h"
+#include "http_connection.h"
+#include "http_core.h"
+#include "http_protocol.h" /* For index_of_response(). Grump. */
+#include "http_request.h"
/* Generate the human-readable hex representation of an unsigned long
* (basically a faster version of 'sprintf("%lx")')
@@ -2784,337 +217,4 @@
}
apr_table_setn(r->headers_out, "ETag", etag);
-}
-
-static int parse_byterange(char *range, apr_off_t clength,
- apr_off_t *start, apr_off_t *end)
-{
- char *dash = strchr(range, '-');
- char *errp;
- apr_off_t number;
-
- if (!dash) {
- return 0;
- }
-
- if ((dash == range)) {
- /* In the form "-5" */
- if (apr_strtoff(&number, dash+1, &errp, 10) || *errp) {
- return 0;
- }
- *start = clength - number;
- *end = clength - 1;
- }
- else {
- *dash++ = '\0';
- if (apr_strtoff(&number, range, &errp, 10) || *errp) {
- return 0;
- }
- *start = number;
- if (*dash) {
- if (apr_strtoff(&number, dash, &errp, 10) || *errp) {
- return 0;
- }
- *end = number;
- }
- else { /* "5-" */
- *end = clength - 1;
- }
- }
-
- if (*start < 0) {
- *start = 0;
- }
-
- if (*end >= clength) {
- *end = clength - 1;
- }
-
- if (*start > *end) {
- return -1;
- }
-
- return (*start > 0 || *end < clength);
-}
-
-static int ap_set_byterange(request_rec *r);
-
-typedef struct byterange_ctx {
- apr_bucket_brigade *bb;
- int num_ranges;
- char *boundary;
- char *bound_head;
-} byterange_ctx;
-
-/*
- * Here we try to be compatible with clients that want multipart/x-byteranges
- * instead of multipart/byteranges (also see above), as per HTTP/1.1. We
- * look for the Request-Range header (e.g. Netscape 2 and 3) as an indication
- * that the browser supports an older protocol. We also check User-Agent
- * for Microsoft Internet Explorer 3, which needs this as well.
- */
-static int use_range_x(request_rec *r)
-{
- const char *ua;
- return (apr_table_get(r->headers_in, "Request-Range")
- || ((ua = apr_table_get(r->headers_in, "User-Agent"))
- && ap_strstr_c(ua, "MSIE 3")));
-}
-
-#define BYTERANGE_FMT "%" APR_OFF_T_FMT "-%" APR_OFF_T_FMT "/%" APR_OFF_T_FMT
-#define PARTITION_ERR_FMT "apr_brigade_partition() failed " \
- "[%" APR_OFF_T_FMT ",%" APR_OFF_T_FMT "]"
-
-AP_CORE_DECLARE_NONSTD(apr_status_t) ap_byterange_filter(ap_filter_t *f,
- apr_bucket_brigade *bb)
-{
-#define MIN_LENGTH(len1, len2) ((len1 > len2) ? len2 : len1)
- request_rec *r = f->r;
- conn_rec *c = r->connection;
- byterange_ctx *ctx = f->ctx;
- apr_bucket *e;
- apr_bucket_brigade *bsend;
- apr_off_t range_start;
- apr_off_t range_end;
- char *current;
- apr_off_t bb_length;
- apr_off_t clength = 0;
- apr_status_t rv;
- int found = 0;
-
- if (!ctx) {
- int num_ranges = ap_set_byterange(r);
-
- /* We have nothing to do, get out of the way. */
- if (num_ranges == 0) {
- ap_remove_output_filter(f);
- return ap_pass_brigade(f->next, bb);
- }
-
- ctx = f->ctx = apr_pcalloc(r->pool, sizeof(*ctx));
- ctx->num_ranges = num_ranges;
- /* create a brigade in case we never call ap_save_brigade() */
- ctx->bb = apr_brigade_create(r->pool, c->bucket_alloc);
-
- if (ctx->num_ranges > 1) {
- /* Is ap_make_content_type required here? */
- const char *orig_ct = ap_make_content_type(r, r->content_type);
- ctx->boundary = apr_psprintf(r->pool, "%" APR_UINT64_T_HEX_FMT "%lx",
- (apr_uint64_t)r->request_time, (long) getpid());
-
- ap_set_content_type(r, apr_pstrcat(r->pool, "multipart",
- use_range_x(r) ? "/x-" : "/",
- "byteranges; boundary=",
- ctx->boundary, NULL));
-
- ctx->bound_head = apr_pstrcat(r->pool,
- CRLF "--", ctx->boundary,
- CRLF "Content-type: ",
- orig_ct,
- CRLF "Content-range: bytes ",
- NULL);
- ap_xlate_proto_to_ascii(ctx->bound_head, strlen(ctx->bound_head));
- }
- }
-
- /* We can't actually deal with byte-ranges until we have the whole brigade
- * because the byte-ranges can be in any order, and according to the RFC,
- * we SHOULD return the data in the same order it was requested.
- *
- * XXX: We really need to dump all bytes prior to the start of the earliest
- * range, and only slurp up to the end of the latest range. By this we
- * mean that we should peek-ahead at the lowest first byte of any range,
- * and the highest last byte of any range.
- */
- if (!APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) {
- ap_save_brigade(f, &ctx->bb, &bb, r->pool);
- return APR_SUCCESS;
- }
-
- /* Prepend any earlier saved brigades. */
- APR_BRIGADE_PREPEND(bb, ctx->bb);
-
- /* It is possible that we won't have a content length yet, so we have to
- * compute the length before we can actually do the byterange work.
- */
- apr_brigade_length(bb, 1, &bb_length);
- clength = (apr_off_t)bb_length;
-
- /* this brigade holds what we will be sending */
- bsend = apr_brigade_create(r->pool, c->bucket_alloc);
-
- while ((current = ap_getword(r->pool, &r->range, ','))
- && (rv = parse_byterange(current, clength, &range_start,
- &range_end))) {
- apr_bucket *e2;
- apr_bucket *ec;
-
- if (rv == -1) {
- continue;
- }
-
- /* these calls to apr_brigade_partition() should theoretically
- * never fail because of the above call to apr_brigade_length(),
- * but what the heck, we'll check for an error anyway */
- if ((rv = apr_brigade_partition(bb, range_start, &ec)) != APR_SUCCESS) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
- PARTITION_ERR_FMT, range_start, clength);
- continue;
- }
- if ((rv = apr_brigade_partition(bb, range_end+1, &e2)) != APR_SUCCESS) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
- PARTITION_ERR_FMT, range_end+1, clength);
- continue;
- }
-
- found = 1;
-
- /* For single range requests, we must produce Content-Range header.
- * Otherwise, we need to produce the multipart boundaries.
- */
- if (ctx->num_ranges == 1) {
- apr_table_setn(r->headers_out, "Content-Range",
- apr_psprintf(r->pool, "bytes " BYTERANGE_FMT,
- range_start, range_end, clength));
- }
- else {
- char *ts;
-
- e = apr_bucket_pool_create(ctx->bound_head, strlen(ctx->bound_head),
- r->pool, c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bsend, e);
-
- ts = apr_psprintf(r->pool, BYTERANGE_FMT CRLF CRLF,
- range_start, range_end, clength);
- ap_xlate_proto_to_ascii(ts, strlen(ts));
- e = apr_bucket_pool_create(ts, strlen(ts), r->pool,
- c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bsend, e);
- }
-
- do {
- apr_bucket *foo;
- const char *str;
- apr_size_t len;
-
- if (apr_bucket_copy(ec, &foo) != APR_SUCCESS) {
- /* this shouldn't ever happen due to the call to
- * apr_brigade_length() above which normalizes
- * indeterminate-length buckets. just to be sure,
- * though, this takes care of uncopyable buckets that
- * do somehow manage to slip through.
- */
- /* XXX: check for failure? */
- apr_bucket_read(ec, &str, &len, APR_BLOCK_READ);
- apr_bucket_copy(ec, &foo);
- }
- APR_BRIGADE_INSERT_TAIL(bsend, foo);
- ec = APR_BUCKET_NEXT(ec);
- } while (ec != e2);
- }
-
- if (found == 0) {
- ap_remove_output_filter(f);
- r->status = HTTP_OK;
- /* bsend is assumed to be empty if we get here. */
- e = ap_bucket_error_create(HTTP_RANGE_NOT_SATISFIABLE, NULL,
- r->pool, c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bsend, e);
- e = apr_bucket_eos_create(c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bsend, e);
- return ap_pass_brigade(f->next, bsend);
- }
-
- if (ctx->num_ranges > 1) {
- char *end;
-
- /* add the final boundary */
- end = apr_pstrcat(r->pool, CRLF "--", ctx->boundary, "--" CRLF, NULL);
- ap_xlate_proto_to_ascii(end, strlen(end));
- e = apr_bucket_pool_create(end, strlen(end), r->pool, c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bsend, e);
- }
-
- e = apr_bucket_eos_create(c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bsend, e);
-
- /* we're done with the original content - all of our data is in bsend. */
- apr_brigade_destroy(bb);
-
- /* send our multipart output */
- return ap_pass_brigade(f->next, bsend);
-}
-
-static int ap_set_byterange(request_rec *r)
-{
- const char *range;
- const char *if_range;
- const char *match;
- const char *ct;
- int num_ranges;
-
- if (r->assbackwards) {
- return 0;
- }
-
- /* Check for Range request-header (HTTP/1.1) or Request-Range for
- * backwards-compatibility with second-draft Luotonen/Franks
- * byte-ranges (e.g. Netscape Navigator 2-3).
- *
- * We support this form, with Request-Range, and (farther down) we
- * send multipart/x-byteranges instead of multipart/byteranges for
- * Request-Range based requests to work around a bug in Netscape
- * Navigator 2-3 and MSIE 3.
- */
-
- if (!(range = apr_table_get(r->headers_in, "Range"))) {
- range = apr_table_get(r->headers_in, "Request-Range");
- }
-
- if (!range || strncasecmp(range, "bytes=", 6) || r->status != HTTP_OK) {
- return 0;
- }
-
- /* is content already a single range? */
- if (apr_table_get(r->headers_out, "Content-Range")) {
- return 0;
- }
-
- /* is content already a multiple range? */
- if ((ct = apr_table_get(r->headers_out, "Content-Type"))
- && (!strncasecmp(ct, "multipart/byteranges", 20)
- || !strncasecmp(ct, "multipart/x-byteranges", 22))) {
- return 0;
- }
-
- /* Check the If-Range header for Etag or Date.
- * Note that this check will return false (as required) if either
- * of the two etags are weak.
- */
- if ((if_range = apr_table_get(r->headers_in, "If-Range"))) {
- if (if_range[0] == '"') {
- if (!(match = apr_table_get(r->headers_out, "Etag"))
- || (strcmp(if_range, match) != 0)) {
- return 0;
- }
- }
- else if (!(match = apr_table_get(r->headers_out, "Last-Modified"))
- || (strcmp(if_range, match) != 0)) {
- return 0;
- }
- }
-
- if (!ap_strchr_c(range, ',')) {
- /* a single range */
- num_ranges = 1;
- }
- else {
- /* a multiple range */
- num_ranges = 2;
- }
-
- r->status = HTTP_PARTIAL_CONTENT;
- r->range = range + 6;
-
- return num_ranges;
}
Copied: httpd/httpd/trunk/modules/http/http_filters.c (from r106679, httpd/httpd/trunk/modules/http/http_protocol.c)
Url: http://svn.apache.org/viewcvs/httpd/httpd/trunk/modules/http/http_filters.c?view=diff&rev=106692&p1=httpd/httpd/trunk/modules/http/http_protocol.c&r1=106679&p2=httpd/httpd/trunk/modules/http/http_filters.c&r2=106692
==============================================================================
--- httpd/httpd/trunk/modules/http/http_protocol.c (original)
+++ httpd/httpd/trunk/modules/http/http_filters.c Sat Nov 27 00:07:44 2004
@@ -14,10 +14,7 @@
*/
/*
- * http_protocol.c --- routines which directly communicate with the client.
- *
- * Code originally by Rob McCool; much redone by Robert S. Thau
- * and the Apache Software Foundation.
+ * http_filter.c --- HTTP routines which either filters or deal with filters.
*/
#include "apr.h"
@@ -57,651 +54,6 @@
#include
#endif
-/* New Apache routine to map status codes into array indicies
- * e.g. 100 -> 0, 101 -> 1, 200 -> 2 ...
- * The number of status lines must equal the value of RESPONSE_CODES (httpd.h)
- * and must be listed in order.
- */
-
-#ifdef UTS21
-/* The second const triggers an assembler bug on UTS 2.1.
- * Another workaround is to move some code out of this file into another,
- * but this is easier. Dave Dykstra, 3/31/99
- */
-static const char * status_lines[RESPONSE_CODES] =
-#else
-static const char * const status_lines[RESPONSE_CODES] =
-#endif
-{
- "100 Continue",
- "101 Switching Protocols",
- "102 Processing",
-#define LEVEL_200 3
- "200 OK",
- "201 Created",
- "202 Accepted",
- "203 Non-Authoritative Information",
- "204 No Content",
- "205 Reset Content",
- "206 Partial Content",
- "207 Multi-Status",
-#define LEVEL_300 11
- "300 Multiple Choices",
- "301 Moved Permanently",
- "302 Found",
- "303 See Other",
- "304 Not Modified",
- "305 Use Proxy",
- "306 unused",
- "307 Temporary Redirect",
-#define LEVEL_400 19
- "400 Bad Request",
- "401 Authorization Required",
- "402 Payment Required",
- "403 Forbidden",
- "404 Not Found",
- "405 Method Not Allowed",
- "406 Not Acceptable",
- "407 Proxy Authentication Required",
- "408 Request Time-out",
- "409 Conflict",
- "410 Gone",
- "411 Length Required",
- "412 Precondition Failed",
- "413 Request Entity Too Large",
- "414 Request-URI Too Large",
- "415 Unsupported Media Type",
- "416 Requested Range Not Satisfiable",
- "417 Expectation Failed",
- "418 unused",
- "419 unused",
- "420 unused",
- "421 unused",
- "422 Unprocessable Entity",
- "423 Locked",
- "424 Failed Dependency",
- /* This is a hack, but it is required for ap_index_of_response
- * to work with 426.
- */
- "425 No code",
- "426 Upgrade Required",
-#define LEVEL_500 46
- "500 Internal Server Error",
- "501 Method Not Implemented",
- "502 Bad Gateway",
- "503 Service Temporarily Unavailable",
- "504 Gateway Time-out",
- "505 HTTP Version Not Supported",
- "506 Variant Also Negotiates",
- "507 Insufficient Storage",
- "508 unused",
- "509 unused",
- "510 Not Extended"
-};
-
-APR_HOOK_STRUCT(
- APR_HOOK_LINK(insert_error_filter)
-)
-
-AP_IMPLEMENT_HOOK_VOID(insert_error_filter, (request_rec *r), (r))
-
-/* The index of the first bit field that is used to index into a limit
- * bitmask. M_INVALID + 1 to METHOD_NUMBER_LAST.
- */
-#define METHOD_NUMBER_FIRST (M_INVALID + 1)
-
-/* The max method number. Method numbers are used to shift bitmasks,
- * so this cannot exceed 63, and all bits high is equal to -1, which is a
- * special flag, so the last bit used has index 62.
- */
-#define METHOD_NUMBER_LAST 62
-
-
-AP_DECLARE(int) ap_set_keepalive(request_rec *r)
-{
- int ka_sent = 0;
- int wimpy = ap_find_token(r->pool,
- apr_table_get(r->headers_out, "Connection"),
- "close");
- const char *conn = apr_table_get(r->headers_in, "Connection");
-
- /* The following convoluted conditional determines whether or not
- * the current connection should remain persistent after this response
- * (a.k.a. HTTP Keep-Alive) and whether or not the output message
- * body should use the HTTP/1.1 chunked transfer-coding. In English,
- *
- * IF we have not marked this connection as errored;
- * and the response body has a defined length due to the status code
- * being 304 or 204, the request method being HEAD, already
- * having defined Content-Length or Transfer-Encoding: chunked, or
- * the request version being HTTP/1.1 and thus capable of being set
- * as chunked [we know the (r->chunked = 1) side-effect is ugly];
- * and the server configuration enables keep-alive;
- * and the server configuration has a reasonable inter-request timeout;
- * and there is no maximum # requests or the max hasn't been reached;
- * and the response status does not require a close;
- * and the response generator has not already indicated close;
- * and the client did not request non-persistence (Connection: close);
- * and we haven't been configured to ignore the buggy twit
- * or they're a buggy twit coming through a HTTP/1.1 proxy
- * and the client is requesting an HTTP/1.0-style keep-alive
- * or the client claims to be HTTP/1.1 compliant (perhaps a proxy);
- * THEN we can be persistent, which requires more headers be output.
- *
- * Note that the condition evaluation order is extremely important.
- */
- if ((r->connection->keepalive != AP_CONN_CLOSE)
- && ((r->status == HTTP_NOT_MODIFIED)
- || (r->status == HTTP_NO_CONTENT)
- || r->header_only
- || apr_table_get(r->headers_out, "Content-Length")
- || ap_find_last_token(r->pool,
- apr_table_get(r->headers_out,
- "Transfer-Encoding"),
- "chunked")
- || ((r->proto_num >= HTTP_VERSION(1,1))
- && (r->chunked = 1))) /* THIS CODE IS CORRECT, see above. */
- && r->server->keep_alive
- && (r->server->keep_alive_timeout > 0)
- && ((r->server->keep_alive_max == 0)
- || (r->server->keep_alive_max > r->connection->keepalives))
- && !ap_status_drops_connection(r->status)
- && !wimpy
- && !ap_find_token(r->pool, conn, "close")
- && (!apr_table_get(r->subprocess_env, "nokeepalive")
- || apr_table_get(r->headers_in, "Via"))
- && ((ka_sent = ap_find_token(r->pool, conn, "keep-alive"))
- || (r->proto_num >= HTTP_VERSION(1,1)))) {
- int left = r->server->keep_alive_max - r->connection->keepalives;
-
- r->connection->keepalive = AP_CONN_KEEPALIVE;
- r->connection->keepalives++;
-
- /* If they sent a Keep-Alive token, send one back */
- if (ka_sent) {
- if (r->server->keep_alive_max) {
- apr_table_setn(r->headers_out, "Keep-Alive",
- apr_psprintf(r->pool, "timeout=%d, max=%d",
- (int)apr_time_sec(r->server->keep_alive_timeout),
- left));
- }
- else {
- apr_table_setn(r->headers_out, "Keep-Alive",
- apr_psprintf(r->pool, "timeout=%d",
- (int)apr_time_sec(r->server->keep_alive_timeout)));
- }
- apr_table_mergen(r->headers_out, "Connection", "Keep-Alive");
- }
-
- return 1;
- }
-
- /* Otherwise, we need to indicate that we will be closing this
- * connection immediately after the current response.
- *
- * We only really need to send "close" to HTTP/1.1 clients, but we
- * always send it anyway, because a broken proxy may identify itself
- * as HTTP/1.0, but pass our request along with our HTTP/1.1 tag
- * to a HTTP/1.1 client. Better safe than sorry.
- */
- if (!wimpy) {
- apr_table_mergen(r->headers_out, "Connection", "close");
- }
-
- r->connection->keepalive = AP_CONN_CLOSE;
-
- return 0;
-}
-
-AP_DECLARE(int) ap_meets_conditions(request_rec *r)
-{
- const char *etag;
- const char *if_match, *if_modified_since, *if_unmodified, *if_nonematch;
- apr_time_t tmp_time;
- apr_int64_t mtime;
-
- /* Check for conditional requests --- note that we only want to do
- * this if we are successful so far and we are not processing a
- * subrequest or an ErrorDocument.
- *
- * The order of the checks is important, since ETag checks are supposed
- * to be more accurate than checks relative to the modification time.
- * However, not all documents are guaranteed to *have* ETags, and some
- * might have Last-Modified values w/o ETags, so this gets a little
- * complicated.
- */
-
- if (!ap_is_HTTP_SUCCESS(r->status) || r->no_local_copy) {
- return OK;
- }
-
- etag = apr_table_get(r->headers_out, "ETag");
-
- /* All of our comparisons must be in seconds, because that's the
- * highest time resolution the HTTP specification allows.
- */
- /* XXX: we should define a "time unset" constant */
- tmp_time = ((r->mtime != 0) ? r->mtime : apr_time_now());
- mtime = apr_time_sec(tmp_time);
-
- /* If an If-Match request-header field was given
- * AND the field value is not "*" (meaning match anything)
- * AND if our strong ETag does not match any entity tag in that field,
- * respond with a status of 412 (Precondition Failed).
- */
- if ((if_match = apr_table_get(r->headers_in, "If-Match")) != NULL) {
- if (if_match[0] != '*'
- && (etag == NULL || etag[0] == 'W'
- || !ap_find_list_item(r->pool, if_match, etag))) {
- return HTTP_PRECONDITION_FAILED;
- }
- }
- else {
- /* Else if a valid If-Unmodified-Since request-header field was given
- * AND the requested resource has been modified since the time
- * specified in this field, then the server MUST
- * respond with a status of 412 (Precondition Failed).
- */
- if_unmodified = apr_table_get(r->headers_in, "If-Unmodified-Since");
- if (if_unmodified != NULL) {
- apr_time_t ius = apr_date_parse_http(if_unmodified);
-
- if ((ius != APR_DATE_BAD) && (mtime > apr_time_sec(ius))) {
- return HTTP_PRECONDITION_FAILED;
- }
- }
- }
-
- /* If an If-None-Match request-header field was given
- * AND the field value is "*" (meaning match anything)
- * OR our ETag matches any of the entity tags in that field, fail.
- *
- * If the request method was GET or HEAD, failure means the server
- * SHOULD respond with a 304 (Not Modified) response.
- * For all other request methods, failure means the server MUST
- * respond with a status of 412 (Precondition Failed).
- *
- * GET or HEAD allow weak etag comparison, all other methods require
- * strong comparison. We can only use weak if it's not a range request.
- */
- if_nonematch = apr_table_get(r->headers_in, "If-None-Match");
- if (if_nonematch != NULL) {
- if (r->method_number == M_GET) {
- if (if_nonematch[0] == '*') {
- return HTTP_NOT_MODIFIED;
- }
- if (etag != NULL) {
- if (apr_table_get(r->headers_in, "Range")) {
- if (etag[0] != 'W'
- && ap_find_list_item(r->pool, if_nonematch, etag)) {
- return HTTP_NOT_MODIFIED;
- }
- }
- else if (ap_strstr_c(if_nonematch, etag)) {
- return HTTP_NOT_MODIFIED;
- }
- }
- }
- else if (if_nonematch[0] == '*'
- || (etag != NULL
- && ap_find_list_item(r->pool, if_nonematch, etag))) {
- return HTTP_PRECONDITION_FAILED;
- }
- }
- /* Else if a valid If-Modified-Since request-header field was given
- * AND it is a GET or HEAD request
- * AND the requested resource has not been modified since the time
- * specified in this field, then the server MUST
- * respond with a status of 304 (Not Modified).
- * A date later than the server's current request time is invalid.
- */
- else if ((r->method_number == M_GET)
- && ((if_modified_since =
- apr_table_get(r->headers_in,
- "If-Modified-Since")) != NULL)) {
- apr_time_t ims_time;
- apr_int64_t ims, reqtime;
-
- ims_time = apr_date_parse_http(if_modified_since);
- ims = apr_time_sec(ims_time);
- reqtime = apr_time_sec(r->request_time);
-
- if ((ims >= mtime) && (ims <= reqtime)) {
- return HTTP_NOT_MODIFIED;
- }
- }
- return OK;
-}
-
-/**
- * Singleton registry of additional methods. This maps new method names
- * such as "MYGET" to methnums, which are int offsets into bitmasks.
- *
- * This follows the same technique as standard M_GET, M_POST, etc. These
- * are dynamically assigned when modules are loaded and
- * directives are processed.
- */
-static apr_hash_t *methods_registry = NULL;
-static int cur_method_number = METHOD_NUMBER_FIRST;
-
-/* internal function to register one method/number pair */
-static void register_one_method(apr_pool_t *p, const char *methname,
- int methnum)
-{
- int *pnum = apr_palloc(p, sizeof(*pnum));
-
- *pnum = methnum;
- apr_hash_set(methods_registry, methname, APR_HASH_KEY_STRING, pnum);
-}
-
-/* This internal function is used to clear the method registry
- * and reset the cur_method_number counter.
- */
-static apr_status_t ap_method_registry_destroy(void *notused)
-{
- methods_registry = NULL;
- cur_method_number = METHOD_NUMBER_FIRST;
- return APR_SUCCESS;
-}
-
-AP_DECLARE(void) ap_method_registry_init(apr_pool_t *p)
-{
- methods_registry = apr_hash_make(p);
- apr_pool_cleanup_register(p, NULL,
- ap_method_registry_destroy,
- apr_pool_cleanup_null);
-
- /* put all the standard methods into the registry hash to ease the
- mapping operations between name and number */
- register_one_method(p, "GET", M_GET);
- register_one_method(p, "PUT", M_PUT);
- register_one_method(p, "POST", M_POST);
- register_one_method(p, "DELETE", M_DELETE);
- register_one_method(p, "CONNECT", M_CONNECT);
- register_one_method(p, "OPTIONS", M_OPTIONS);
- register_one_method(p, "TRACE", M_TRACE);
- register_one_method(p, "PATCH", M_PATCH);
- register_one_method(p, "PROPFIND", M_PROPFIND);
- register_one_method(p, "PROPPATCH", M_PROPPATCH);
- register_one_method(p, "MKCOL", M_MKCOL);
- register_one_method(p, "COPY", M_COPY);
- register_one_method(p, "MOVE", M_MOVE);
- register_one_method(p, "LOCK", M_LOCK);
- register_one_method(p, "UNLOCK", M_UNLOCK);
- register_one_method(p, "VERSION-CONTROL", M_VERSION_CONTROL);
- register_one_method(p, "CHECKOUT", M_CHECKOUT);
- register_one_method(p, "UNCHECKOUT", M_UNCHECKOUT);
- register_one_method(p, "CHECKIN", M_CHECKIN);
- register_one_method(p, "UPDATE", M_UPDATE);
- register_one_method(p, "LABEL", M_LABEL);
- register_one_method(p, "REPORT", M_REPORT);
- register_one_method(p, "MKWORKSPACE", M_MKWORKSPACE);
- register_one_method(p, "MKACTIVITY", M_MKACTIVITY);
- register_one_method(p, "BASELINE-CONTROL", M_BASELINE_CONTROL);
- register_one_method(p, "MERGE", M_MERGE);
-}
-
-AP_DECLARE(int) ap_method_register(apr_pool_t *p, const char *methname)
-{
- int *methnum;
-
- if (methods_registry == NULL) {
- ap_method_registry_init(p);
- }
-
- if (methname == NULL) {
- return M_INVALID;
- }
-
- /* Check if the method was previously registered. If it was
- * return the associated method number.
- */
- methnum = (int *)apr_hash_get(methods_registry, methname,
- APR_HASH_KEY_STRING);
- if (methnum != NULL)
- return *methnum;
-
- if (cur_method_number > METHOD_NUMBER_LAST) {
- /* The method registry has run out of dynamically
- * assignable method numbers. Log this and return M_INVALID.
- */
- ap_log_perror(APLOG_MARK, APLOG_ERR, 0, p,
- "Maximum new request methods %d reached while "
- "registering method %s.",
- METHOD_NUMBER_LAST, methname);
- return M_INVALID;
- }
-
- register_one_method(p, methname, cur_method_number);
- return cur_method_number++;
-}
-
-#define UNKNOWN_METHOD (-1)
-
-static int lookup_builtin_method(const char *method, apr_size_t len)
-{
- /* Note: the following code was generated by the "shilka" tool from
- the "cocom" parsing/compilation toolkit. It is an optimized lookup
- based on analysis of the input keywords. Postprocessing was done
- on the shilka output, but the basic structure and analysis is
- from there. Should new HTTP methods be added, then manual insertion
- into this code is fine, or simply re-running the shilka tool on
- the appropriate input. */
-
- /* Note: it is also quite reasonable to just use our method_registry,
- but I'm assuming (probably incorrectly) we want more speed here
- (based on the optimizations the previous code was doing). */
-
- switch (len)
- {
- case 3:
- switch (method[0])
- {
- case 'P':
- return (method[1] == 'U'
- && method[2] == 'T'
- ? M_PUT : UNKNOWN_METHOD);
- case 'G':
- return (method[1] == 'E'
- && method[2] == 'T'
- ? M_GET : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
-
- case 4:
- switch (method[0])
- {
- case 'H':
- return (method[1] == 'E'
- && method[2] == 'A'
- && method[3] == 'D'
- ? M_GET : UNKNOWN_METHOD);
- case 'P':
- return (method[1] == 'O'
- && method[2] == 'S'
- && method[3] == 'T'
- ? M_POST : UNKNOWN_METHOD);
- case 'M':
- return (method[1] == 'O'
- && method[2] == 'V'
- && method[3] == 'E'
- ? M_MOVE : UNKNOWN_METHOD);
- case 'L':
- return (method[1] == 'O'
- && method[2] == 'C'
- && method[3] == 'K'
- ? M_LOCK : UNKNOWN_METHOD);
- case 'C':
- return (method[1] == 'O'
- && method[2] == 'P'
- && method[3] == 'Y'
- ? M_COPY : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
-
- case 5:
- switch (method[2])
- {
- case 'T':
- return (memcmp(method, "PATCH", 5) == 0
- ? M_PATCH : UNKNOWN_METHOD);
- case 'R':
- return (memcmp(method, "MERGE", 5) == 0
- ? M_MERGE : UNKNOWN_METHOD);
- case 'C':
- return (memcmp(method, "MKCOL", 5) == 0
- ? M_MKCOL : UNKNOWN_METHOD);
- case 'B':
- return (memcmp(method, "LABEL", 5) == 0
- ? M_LABEL : UNKNOWN_METHOD);
- case 'A':
- return (memcmp(method, "TRACE", 5) == 0
- ? M_TRACE : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
-
- case 6:
- switch (method[0])
- {
- case 'U':
- switch (method[5])
- {
- case 'K':
- return (memcmp(method, "UNLOCK", 6) == 0
- ? M_UNLOCK : UNKNOWN_METHOD);
- case 'E':
- return (memcmp(method, "UPDATE", 6) == 0
- ? M_UPDATE : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
- case 'R':
- return (memcmp(method, "REPORT", 6) == 0
- ? M_REPORT : UNKNOWN_METHOD);
- case 'D':
- return (memcmp(method, "DELETE", 6) == 0
- ? M_DELETE : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
-
- case 7:
- switch (method[1])
- {
- case 'P':
- return (memcmp(method, "OPTIONS", 7) == 0
- ? M_OPTIONS : UNKNOWN_METHOD);
- case 'O':
- return (memcmp(method, "CONNECT", 7) == 0
- ? M_CONNECT : UNKNOWN_METHOD);
- case 'H':
- return (memcmp(method, "CHECKIN", 7) == 0
- ? M_CHECKIN : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
-
- case 8:
- switch (method[0])
- {
- case 'P':
- return (memcmp(method, "PROPFIND", 8) == 0
- ? M_PROPFIND : UNKNOWN_METHOD);
- case 'C':
- return (memcmp(method, "CHECKOUT", 8) == 0
- ? M_CHECKOUT : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
-
- case 9:
- return (memcmp(method, "PROPPATCH", 9) == 0
- ? M_PROPPATCH : UNKNOWN_METHOD);
-
- case 10:
- switch (method[0])
- {
- case 'U':
- return (memcmp(method, "UNCHECKOUT", 10) == 0
- ? M_UNCHECKOUT : UNKNOWN_METHOD);
- case 'M':
- return (memcmp(method, "MKACTIVITY", 10) == 0
- ? M_MKACTIVITY : UNKNOWN_METHOD);
- default:
- return UNKNOWN_METHOD;
- }
-
- case 11:
- return (memcmp(method, "MKWORKSPACE", 11) == 0
- ? M_MKWORKSPACE : UNKNOWN_METHOD);
-
- case 15:
- return (memcmp(method, "VERSION-CONTROL", 15) == 0
- ? M_VERSION_CONTROL : UNKNOWN_METHOD);
-
- case 16:
- return (memcmp(method, "BASELINE-CONTROL", 16) == 0
- ? M_BASELINE_CONTROL : UNKNOWN_METHOD);
-
- default:
- return UNKNOWN_METHOD;
- }
-
- /* NOTREACHED */
-}
-
-/* Get the method number associated with the given string, assumed to
- * contain an HTTP method. Returns M_INVALID if not recognized.
- *
- * This is the first step toward placing method names in a configurable
- * list. Hopefully it (and other routines) can eventually be moved to
- * something like a mod_http_methods.c, complete with config stuff.
- */
-AP_DECLARE(int) ap_method_number_of(const char *method)
-{
- int len = strlen(method);
- int which = lookup_builtin_method(method, len);
-
- if (which != UNKNOWN_METHOD)
- return which;
-
- /* check if the method has been dynamically registered */
- if (methods_registry != NULL) {
- int *methnum = apr_hash_get(methods_registry, method, len);
-
- if (methnum != NULL) {
- return *methnum;
- }
- }
-
- return M_INVALID;
-}
-
-/*
- * Turn a known method number into a name.
- */
-AP_DECLARE(const char *) ap_method_name_of(apr_pool_t *p, int methnum)
-{
- apr_hash_index_t *hi = apr_hash_first(p, methods_registry);
-
- /* scan through the hash table, looking for a value that matches
- the provided method number. */
- for (; hi; hi = apr_hash_next(hi)) {
- const void *key;
- void *val;
-
- apr_hash_this(hi, &key, NULL, &val);
- if (*(int *)val == methnum)
- return key;
- }
-
- /* it wasn't found in the hash */
- return NULL;
-}
-
static long get_chunk_size(char *);
typedef struct http_filter_ctx {
@@ -768,7 +120,7 @@
ctx->state = BODY_LENGTH;
errno = 0;
-
+
/* Protects against over/underflow, non-digit chars in the
* string (excluding leading space) (the endstr checks)
* and a negative number. */
@@ -789,7 +141,7 @@
ctx->eos_sent = 1;
return ap_pass_brigade(f->r->output_filters, bb);
}
-
+
/* If we have a limit in effect and we know the C-L ahead of
* time, stop it here if it is invalid.
*/
@@ -837,7 +189,7 @@
apr_bucket_brigade *bb;
tmp = apr_pstrcat(f->r->pool, AP_SERVER_PROTOCOL, " ",
- status_lines[0], CRLF CRLF, NULL);
+ ap_get_status_line(100), CRLF CRLF, NULL);
bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
e = apr_bucket_pool_create(tmp, strlen(tmp), f->r->pool,
f->c->bucket_alloc);
@@ -1033,40 +385,48 @@
return APR_SUCCESS;
}
-/* The index is found by its offset from the x00 code of each level.
- * Although this is fast, it will need to be replaced if some nutcase
- * decides to define a high-numbered code before the lower numbers.
- * If that sad event occurs, replace the code below with a linear search
- * from status_lines[shortcut[i]] to status_lines[shortcut[i+1]-1];
+/**
+ * Parse a chunk extension, detect overflow.
+ * There are two error cases:
+ * 1) If the conversion would require too many bits, a -1 is returned.
+ * 2) If the conversion used the correct number of bits, but an overflow
+ * caused only the sign bit to flip, then that negative number is
+ * returned.
+ * In general, any negative number can be considered an overflow error.
*/
-AP_DECLARE(int) ap_index_of_response(int status)
+static long get_chunk_size(char *b)
{
- static int shortcut[6] = {0, LEVEL_200, LEVEL_300, LEVEL_400,
- LEVEL_500, RESPONSE_CODES};
- int i, pos;
-
- if (status < 100) { /* Below 100 is illegal for HTTP status */
- return LEVEL_500;
+ long chunksize = 0;
+ size_t chunkbits = sizeof(long) * 8;
+
+ /* Skip leading zeros */
+ while (*b == '0') {
+ ++b;
}
- for (i = 0; i < 5; i++) {
- status -= 100;
- if (status < 100) {
- pos = (status + shortcut[i]);
- if (pos < shortcut[i + 1]) {
- return pos;
- }
- else {
- return LEVEL_500; /* status unknown (falls in gap) */
- }
+ while (apr_isxdigit(*b) && (chunkbits > 0)) {
+ int xvalue = 0;
+
+ if (*b >= '0' && *b <= '9') {
+ xvalue = *b - '0';
}
+ else if (*b >= 'A' && *b <= 'F') {
+ xvalue = *b - 'A' + 0xa;
+ }
+ else if (*b >= 'a' && *b <= 'f') {
+ xvalue = *b - 'a' + 0xa;
+ }
+
+ chunksize = (chunksize << 4) | xvalue;
+ chunkbits -= 4;
+ ++b;
+ }
+ if (apr_isxdigit(*b) && (chunkbits <= 0)) {
+ /* overflow */
+ return -1;
}
- return LEVEL_500; /* 600 or above is also illegal */
-}
-AP_DECLARE(const char *) ap_get_status_line(int status)
-{
- return status_lines[ap_index_of_response(status)];
+ return chunksize;
}
typedef struct header_struct {
@@ -1123,60 +483,138 @@
return 1;
}
-/* Send a request's HTTP response headers to the client.
+/* This routine is called by apr_table_do and merges all instances of
+ * the passed field values into a single array that will be further
+ * processed by some later routine. Originally intended to help split
+ * and recombine multiple Vary fields, though it is generic to any field
+ * consisting of comma/space-separated tokens.
*/
-static apr_status_t send_all_header_fields(header_struct *h,
- const request_rec *r)
+static int uniq_field_values(void *d, const char *key, const char *val)
{
- const apr_array_header_t *elts;
- const apr_table_entry_t *t_elt;
- const apr_table_entry_t *t_end;
- struct iovec *vec;
- struct iovec *vec_next;
+ apr_array_header_t *values;
+ char *start;
+ char *e;
+ char **strpp;
+ int i;
- elts = apr_table_elts(r->headers_out);
- if (elts->nelts == 0) {
- return APR_SUCCESS;
- }
- t_elt = (const apr_table_entry_t *)(elts->elts);
- t_end = t_elt + elts->nelts;
- vec = (struct iovec *)apr_palloc(h->pool, 4 * elts->nelts *
- sizeof(struct iovec));
- vec_next = vec;
+ values = (apr_array_header_t *)d;
- /* For each field, generate
- * name ": " value CRLF
- */
- do {
- vec_next->iov_base = (void*)(t_elt->key);
- vec_next->iov_len = strlen(t_elt->key);
- vec_next++;
- vec_next->iov_base = ": ";
- vec_next->iov_len = sizeof(": ") - 1;
- vec_next++;
- vec_next->iov_base = (void*)(t_elt->val);
- vec_next->iov_len = strlen(t_elt->val);
- vec_next++;
- vec_next->iov_base = CRLF;
- vec_next->iov_len = sizeof(CRLF) - 1;
- vec_next++;
- t_elt++;
- } while (t_elt < t_end);
+ e = apr_pstrdup(values->pool, val);
-#if APR_CHARSET_EBCDIC
- {
- apr_size_t len;
- char *tmp = apr_pstrcatv(r->pool, vec, vec_next - vec, &len);
- ap_xlate_proto_to_ascii(tmp, len);
- return apr_brigade_write(h->bb, NULL, NULL, tmp, len);
- }
-#else
- return apr_brigade_writev(h->bb, NULL, NULL, vec, vec_next - vec);
-#endif
-}
+ do {
+ /* Find a non-empty fieldname */
-/*
- * Determine the protocol to use for the response. Potentially downgrade
+ while (*e == ',' || apr_isspace(*e)) {
+ ++e;
+ }
+ if (*e == '\0') {
+ break;
+ }
+ start = e;
+ while (*e != '\0' && *e != ',' && !apr_isspace(*e)) {
+ ++e;
+ }
+ if (*e != '\0') {
+ *e++ = '\0';
+ }
+
+ /* Now add it to values if it isn't already represented.
+ * Could be replaced by a ap_array_strcasecmp() if we had one.
+ */
+ for (i = 0, strpp = (char **) values->elts; i < values->nelts;
+ ++i, ++strpp) {
+ if (*strpp && strcasecmp(*strpp, start) == 0) {
+ break;
+ }
+ }
+ if (i == values->nelts) { /* if not found */
+ *(char **)apr_array_push(values) = start;
+ }
+ } while (*e != '\0');
+
+ return 1;
+}
+
+/*
+ * Since some clients choke violently on multiple Vary fields, or
+ * Vary fields with duplicate tokens, combine any multiples and remove
+ * any duplicates.
+ */
+static void fixup_vary(request_rec *r)
+{
+ apr_array_header_t *varies;
+
+ varies = apr_array_make(r->pool, 5, sizeof(char *));
+
+ /* Extract all Vary fields from the headers_out, separate each into
+ * its comma-separated fieldname values, and then add them to varies
+ * if not already present in the array.
+ */
+ apr_table_do((int (*)(void *, const char *, const char *))uniq_field_values,
+ (void *) varies, r->headers_out, "Vary", NULL);
+
+ /* If we found any, replace old Vary fields with unique-ified value */
+
+ if (varies->nelts > 0) {
+ apr_table_setn(r->headers_out, "Vary",
+ apr_array_pstrcat(r->pool, varies, ','));
+ }
+}
+
+/* Send a request's HTTP response headers to the client.
+ */
+static apr_status_t send_all_header_fields(header_struct *h,
+ const request_rec *r)
+{
+ const apr_array_header_t *elts;
+ const apr_table_entry_t *t_elt;
+ const apr_table_entry_t *t_end;
+ struct iovec *vec;
+ struct iovec *vec_next;
+
+ elts = apr_table_elts(r->headers_out);
+ if (elts->nelts == 0) {
+ return APR_SUCCESS;
+ }
+ t_elt = (const apr_table_entry_t *)(elts->elts);
+ t_end = t_elt + elts->nelts;
+ vec = (struct iovec *)apr_palloc(h->pool, 4 * elts->nelts *
+ sizeof(struct iovec));
+ vec_next = vec;
+
+ /* For each field, generate
+ * name ": " value CRLF
+ */
+ do {
+ vec_next->iov_base = (void*)(t_elt->key);
+ vec_next->iov_len = strlen(t_elt->key);
+ vec_next++;
+ vec_next->iov_base = ": ";
+ vec_next->iov_len = sizeof(": ") - 1;
+ vec_next++;
+ vec_next->iov_base = (void*)(t_elt->val);
+ vec_next->iov_len = strlen(t_elt->val);
+ vec_next++;
+ vec_next->iov_base = CRLF;
+ vec_next->iov_len = sizeof(CRLF) - 1;
+ vec_next++;
+ t_elt++;
+ } while (t_elt < t_end);
+
+#if APR_CHARSET_EBCDIC
+ {
+ apr_size_t len;
+ char *tmp = apr_pstrcatv(r->pool, vec, vec_next - vec, &len);
+ ap_xlate_proto_to_ascii(tmp, len);
+ return apr_brigade_write(h->bb, NULL, NULL, tmp, len);
+ }
+#else
+ return apr_brigade_writev(h->bb, NULL, NULL, vec, vec_next - vec);
+#endif
+}
+
+/*
+ * Determine the protocol to use for the response. Potentially downgrade
* to HTTP/1.0 in some situations and/or turn off keepalives.
*
* also prepare r->status_line.
@@ -1190,7 +628,7 @@
}
if (!r->status_line) {
- r->status_line = status_lines[ap_index_of_response(r->status)];
+ r->status_line = ap_get_status_line(r->status);
}
/* Note that we must downgrade before checking for force responses. */
@@ -1316,56 +754,6 @@
apr_brigade_write(bb, NULL, NULL, crlf, buflen);
}
-/* Build the Allow field-value from the request handler method mask.
- * Note that we always allow TRACE, since it is handled below.
- */
-static char *make_allow(request_rec *r)
-{
- char *list;
- apr_int64_t mask;
- apr_array_header_t *allow = apr_array_make(r->pool, 10, sizeof(char *));
- apr_hash_index_t *hi = apr_hash_first(r->pool, methods_registry);
-
- mask = r->allowed_methods->method_mask;
-
- for (; hi; hi = apr_hash_next(hi)) {
- const void *key;
- void *val;
-
- apr_hash_this(hi, &key, NULL, &val);
- if ((mask & (AP_METHOD_BIT << *(int *)val)) != 0) {
- *(const char **)apr_array_push(allow) = key;
-
- /* the M_GET method actually refers to two methods */
- if (*(int *)val == M_GET)
- *(const char **)apr_array_push(allow) = "HEAD";
- }
- }
-
- /* TRACE is always allowed */
- *(const char **)apr_array_push(allow) = "TRACE";
-
- list = apr_array_pstrcat(r->pool, allow, ',');
-
- /* ### this is rather annoying. we should enforce registration of
- ### these methods */
- if ((mask & (AP_METHOD_BIT << M_INVALID))
- && (r->allowed_methods->method_list != NULL)
- && (r->allowed_methods->method_list->nelts != 0)) {
- int i;
- char **xmethod = (char **) r->allowed_methods->method_list->elts;
-
- /*
- * Append all of the elements of r->allowed_methods->method_list
- */
- for (i = 0; i < r->allowed_methods->method_list->nelts; ++i) {
- list = apr_pstrcat(r->pool, list, ",", xmethod[i], NULL);
- }
- }
-
- return list;
-}
-
AP_DECLARE_NONSTD(int) ap_send_http_trace(request_rec *r)
{
int rv;
@@ -1401,116 +789,6 @@
return DONE;
}
-AP_DECLARE(int) ap_send_http_options(request_rec *r)
-{
- if (r->assbackwards) {
- return DECLINED;
- }
-
- apr_table_setn(r->headers_out, "Allow", make_allow(r));
-
- /* the request finalization will send an EOS, which will flush all
- * the headers out (including the Allow header)
- */
-
- return OK;
-}
-
-/* This routine is called by apr_table_do and merges all instances of
- * the passed field values into a single array that will be further
- * processed by some later routine. Originally intended to help split
- * and recombine multiple Vary fields, though it is generic to any field
- * consisting of comma/space-separated tokens.
- */
-static int uniq_field_values(void *d, const char *key, const char *val)
-{
- apr_array_header_t *values;
- char *start;
- char *e;
- char **strpp;
- int i;
-
- values = (apr_array_header_t *)d;
-
- e = apr_pstrdup(values->pool, val);
-
- do {
- /* Find a non-empty fieldname */
-
- while (*e == ',' || apr_isspace(*e)) {
- ++e;
- }
- if (*e == '\0') {
- break;
- }
- start = e;
- while (*e != '\0' && *e != ',' && !apr_isspace(*e)) {
- ++e;
- }
- if (*e != '\0') {
- *e++ = '\0';
- }
-
- /* Now add it to values if it isn't already represented.
- * Could be replaced by a ap_array_strcasecmp() if we had one.
- */
- for (i = 0, strpp = (char **) values->elts; i < values->nelts;
- ++i, ++strpp) {
- if (*strpp && strcasecmp(*strpp, start) == 0) {
- break;
- }
- }
- if (i == values->nelts) { /* if not found */
- *(char **)apr_array_push(values) = start;
- }
- } while (*e != '\0');
-
- return 1;
-}
-
-/*
- * Since some clients choke violently on multiple Vary fields, or
- * Vary fields with duplicate tokens, combine any multiples and remove
- * any duplicates.
- */
-static void fixup_vary(request_rec *r)
-{
- apr_array_header_t *varies;
-
- varies = apr_array_make(r->pool, 5, sizeof(char *));
-
- /* Extract all Vary fields from the headers_out, separate each into
- * its comma-separated fieldname values, and then add them to varies
- * if not already present in the array.
- */
- apr_table_do((int (*)(void *, const char *, const char *))uniq_field_values,
- (void *) varies, r->headers_out, "Vary", NULL);
-
- /* If we found any, replace old Vary fields with unique-ified value */
-
- if (varies->nelts > 0) {
- apr_table_setn(r->headers_out, "Vary",
- apr_array_pstrcat(r->pool, varies, ','));
- }
-}
-
-AP_DECLARE(void) ap_set_content_type(request_rec *r, const char *ct)
-{
- if (!ct) {
- r->content_type = NULL;
- }
- else if (!r->content_type || strcmp(r->content_type, ct)) {
- r->content_type = ct;
-
- /* Insert filters requested by the AddOutputFiltersByType
- * configuration directive. Content-type filters must be
- * inserted after the content handlers have run because
- * only then, do we reliably know the content-type.
- */
- ap_add_output_filters_by_type(r);
- }
-}
-
typedef struct header_filter_ctx {
int headers_sent;
} header_filter_ctx;
@@ -1700,30 +978,119 @@
return ap_pass_brigade(f->next, b);
}
-/* Here we deal with getting the request message body from the client.
- * Whether or not the request contains a body is signaled by the presence
- * of a non-zero Content-Length or by a Transfer-Encoding: chunked.
- *
- * Note that this is more complicated than it was in Apache 1.1 and prior
- * versions, because chunked support means that the module does less.
- *
- * The proper procedure is this:
- *
- * 1. Call setup_client_block() near the beginning of the request
- * handler. This will set up all the necessary properties, and will
- * return either OK, or an error code. If the latter, the module should
- * return that error code. The second parameter selects the policy to
- * apply if the request message indicates a body, and how a chunked
- * transfer-coding should be interpreted. Choose one of
- *
- * REQUEST_NO_BODY Send 413 error if message has any body
- * REQUEST_CHUNKED_ERROR Send 411 error if body without Content-Length
- * REQUEST_CHUNKED_DECHUNK If chunked, remove the chunks for me.
+/* In HTTP/1.1, any method can have a body. However, most GET handlers
+ * wouldn't know what to do with a request body if they received one.
+ * This helper routine tests for and reads any message body in the request,
+ * simply discarding whatever it receives. We need to do this because
+ * failing to read the request body would cause it to be interpreted
+ * as the next request on a persistent connection.
*
- * In order to use the last two options, the caller MUST provide a buffer
- * large enough to hold a chunk-size line, including any extensions.
+ * Since we return an error status if the request is malformed, this
+ * routine should be called at the beginning of a no-body handler, e.g.,
*
- * 2. When you are ready to read a body (if any), call should_client_block().
+ * if ((retval = ap_discard_request_body(r)) != OK) {
+ * return retval;
+ * }
+ */
+AP_DECLARE(int) ap_discard_request_body(request_rec *r)
+{
+ apr_bucket_brigade *bb;
+ int rv, seen_eos;
+
+ /* Sometimes we'll get in a state where the input handling has
+ * detected an error where we want to drop the connection, so if
+ * that's the case, don't read the data as that is what we're trying
+ * to avoid.
+ *
+ * This function is also a no-op on a subrequest.
+ */
+ if (r->main || r->connection->keepalive == AP_CONN_CLOSE ||
+ ap_status_drops_connection(r->status)) {
+ return OK;
+ }
+
+ bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+ seen_eos = 0;
+ do {
+ apr_bucket *bucket;
+
+ rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES,
+ APR_BLOCK_READ, HUGE_STRING_LEN);
+
+ if (rv != APR_SUCCESS) {
+ /* FIXME: If we ever have a mapping from filters (apr_status_t)
+ * to HTTP error codes, this would be a good place for them.
+ *
+ * If we received the special case AP_FILTER_ERROR, it means
+ * that the filters have already handled this error.
+ * Otherwise, we should assume we have a bad request.
+ */
+ if (rv == AP_FILTER_ERROR) {
+ apr_brigade_destroy(bb);
+ return rv;
+ }
+ else {
+ apr_brigade_destroy(bb);
+ return HTTP_BAD_REQUEST;
+ }
+ }
+
+ for (bucket = APR_BRIGADE_FIRST(bb);
+ bucket != APR_BRIGADE_SENTINEL(bb);
+ bucket = APR_BUCKET_NEXT(bucket))
+ {
+ const char *data;
+ apr_size_t len;
+
+ if (APR_BUCKET_IS_EOS(bucket)) {
+ seen_eos = 1;
+ break;
+ }
+
+ /* These are metadata buckets. */
+ if (bucket->length == 0) {
+ continue;
+ }
+
+ /* We MUST read because in case we have an unknown-length
+ * bucket or one that morphs, we want to exhaust it.
+ */
+ rv = apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);
+ if (rv != APR_SUCCESS) {
+ apr_brigade_destroy(bb);
+ return HTTP_BAD_REQUEST;
+ }
+ }
+ apr_brigade_cleanup(bb);
+ } while (!seen_eos);
+
+ return OK;
+}
+
+/* Here we deal with getting the request message body from the client.
+ * Whether or not the request contains a body is signaled by the presence
+ * of a non-zero Content-Length or by a Transfer-Encoding: chunked.
+ *
+ * Note that this is more complicated than it was in Apache 1.1 and prior
+ * versions, because chunked support means that the module does less.
+ *
+ * The proper procedure is this:
+ *
+ * 1. Call setup_client_block() near the beginning of the request
+ * handler. This will set up all the necessary properties, and will
+ * return either OK, or an error code. If the latter, the module should
+ * return that error code. The second parameter selects the policy to
+ * apply if the request message indicates a body, and how a chunked
+ * transfer-coding should be interpreted. Choose one of
+ *
+ * REQUEST_NO_BODY Send 413 error if message has any body
+ * REQUEST_CHUNKED_ERROR Send 411 error if body without Content-Length
+ * REQUEST_CHUNKED_DECHUNK If chunked, remove the chunks for me.
+ *
+ * In order to use the last two options, the caller MUST provide a buffer
+ * large enough to hold a chunk-size line, including any extensions.
+ *
+ * 2. When you are ready to read a body (if any), call should_client_block().
* This will tell the module whether or not to read input. If it is 0,
* the module should assume that there is no message body to read.
* This step also sends a 100 Continue response to HTTP/1.1 clients,
@@ -1804,50 +1171,6 @@
return 1;
}
-/**
- * Parse a chunk extension, detect overflow.
- * There are two error cases:
- * 1) If the conversion would require too many bits, a -1 is returned.
- * 2) If the conversion used the correct number of bits, but an overflow
- * caused only the sign bit to flip, then that negative number is
- * returned.
- * In general, any negative number can be considered an overflow error.
- */
-static long get_chunk_size(char *b)
-{
- long chunksize = 0;
- size_t chunkbits = sizeof(long) * 8;
-
- /* Skip leading zeros */
- while (*b == '0') {
- ++b;
- }
-
- while (apr_isxdigit(*b) && (chunkbits > 0)) {
- int xvalue = 0;
-
- if (*b >= '0' && *b <= '9') {
- xvalue = *b - '0';
- }
- else if (*b >= 'A' && *b <= 'F') {
- xvalue = *b - 'A' + 0xa;
- }
- else if (*b >= 'a' && *b <= 'f') {
- xvalue = *b - 'a' + 0xa;
- }
-
- chunksize = (chunksize << 4) | xvalue;
- chunkbits -= 4;
- ++b;
- }
- if (apr_isxdigit(*b) && (chunkbits <= 0)) {
- /* overflow */
- return -1;
- }
-
- return chunksize;
-}
-
/* get_client_block is called in a loop to get the request message body.
* This is quite simple if the client includes a content-length
* (the normal case), but gets messy if the body is chunked. Note that
@@ -1922,1199 +1245,3 @@
return bufsiz;
}
-/* In HTTP/1.1, any method can have a body. However, most GET handlers
- * wouldn't know what to do with a request body if they received one.
- * This helper routine tests for and reads any message body in the request,
- * simply discarding whatever it receives. We need to do this because
- * failing to read the request body would cause it to be interpreted
- * as the next request on a persistent connection.
- *
- * Since we return an error status if the request is malformed, this
- * routine should be called at the beginning of a no-body handler, e.g.,
- *
- * if ((retval = ap_discard_request_body(r)) != OK) {
- * return retval;
- * }
- */
-AP_DECLARE(int) ap_discard_request_body(request_rec *r)
-{
- apr_bucket_brigade *bb;
- int rv, seen_eos;
-
- /* Sometimes we'll get in a state where the input handling has
- * detected an error where we want to drop the connection, so if
- * that's the case, don't read the data as that is what we're trying
- * to avoid.
- *
- * This function is also a no-op on a subrequest.
- */
- if (r->main || r->connection->keepalive == AP_CONN_CLOSE ||
- ap_status_drops_connection(r->status)) {
- return OK;
- }
-
- bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
- seen_eos = 0;
- do {
- apr_bucket *bucket;
-
- rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES,
- APR_BLOCK_READ, HUGE_STRING_LEN);
-
- if (rv != APR_SUCCESS) {
- /* FIXME: If we ever have a mapping from filters (apr_status_t)
- * to HTTP error codes, this would be a good place for them.
- *
- * If we received the special case AP_FILTER_ERROR, it means
- * that the filters have already handled this error.
- * Otherwise, we should assume we have a bad request.
- */
- if (rv == AP_FILTER_ERROR) {
- apr_brigade_destroy(bb);
- return rv;
- }
- else {
- apr_brigade_destroy(bb);
- return HTTP_BAD_REQUEST;
- }
- }
-
- for (bucket = APR_BRIGADE_FIRST(bb);
- bucket != APR_BRIGADE_SENTINEL(bb);
- bucket = APR_BUCKET_NEXT(bucket))
- {
- const char *data;
- apr_size_t len;
-
- if (APR_BUCKET_IS_EOS(bucket)) {
- seen_eos = 1;
- break;
- }
-
- /* These are metadata buckets. */
- if (bucket->length == 0) {
- continue;
- }
-
- /* We MUST read because in case we have an unknown-length
- * bucket or one that morphs, we want to exhaust it.
- */
- rv = apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);
- if (rv != APR_SUCCESS) {
- apr_brigade_destroy(bb);
- return HTTP_BAD_REQUEST;
- }
- }
- apr_brigade_cleanup(bb);
- } while (!seen_eos);
-
- return OK;
-}
-
-static const char *add_optional_notes(request_rec *r,
- const char *prefix,
- const char *key,
- const char *suffix)
-{
- const char *notes, *result;
-
- if ((notes = apr_table_get(r->notes, key)) == NULL) {
- result = apr_pstrcat(r->pool, prefix, suffix, NULL);
- }
- else {
- result = apr_pstrcat(r->pool, prefix, notes, suffix, NULL);
- }
-
- return result;
-}
-
-/* construct and return the default error message for a given
- * HTTP defined error code
- */
-static const char *get_canned_error_string(int status,
- request_rec *r,
- const char *location)
-{
- apr_pool_t *p = r->pool;
- const char *error_notes, *h1, *s1;
-
- switch (status) {
- case HTTP_MOVED_PERMANENTLY:
- case HTTP_MOVED_TEMPORARILY:
- case HTTP_TEMPORARY_REDIRECT:
- return(apr_pstrcat(p,
- "The document has moved pool, location),
- "\">here.
\n",
- NULL));
- case HTTP_SEE_OTHER:
- return(apr_pstrcat(p,
- "The answer to your request is located "
- "pool, location),
- "\">here.
\n",
- NULL));
- case HTTP_USE_PROXY:
- return(apr_pstrcat(p,
- "This resource is only accessible "
- "through the proxy\n",
- ap_escape_html(r->pool, location),
- "
\nYou will need to configure "
- "your client to use that proxy.
\n",
- NULL));
- case HTTP_PROXY_AUTHENTICATION_REQUIRED:
- case HTTP_UNAUTHORIZED:
- return("This server could not verify that you\n"
- "are authorized to access the document\n"
- "requested. Either you supplied the wrong\n"
- "credentials (e.g., bad password), or your\n"
- "browser doesn't understand how to supply\n"
- "the credentials required.
\n");
- case HTTP_BAD_REQUEST:
- return(add_optional_notes(r,
- "Your browser sent a request that "
- "this server could not understand.
\n",
- "error-notes",
- "
\n"));
- case HTTP_FORBIDDEN:
- return(apr_pstrcat(p,
- "You don't have permission to access ",
- ap_escape_html(r->pool, r->uri),
- "\non this server.
\n",
- NULL));
- case HTTP_NOT_FOUND:
- return(apr_pstrcat(p,
- "The requested URL ",
- ap_escape_html(r->pool, r->uri),
- " was not found on this server.
\n",
- NULL));
- case HTTP_METHOD_NOT_ALLOWED:
- return(apr_pstrcat(p,
- "The requested method ", r->method,
- " is not allowed for the URL ",
- ap_escape_html(r->pool, r->uri),
- ".
\n",
- NULL));
- case HTTP_NOT_ACCEPTABLE:
- s1 = apr_pstrcat(p,
- "An appropriate representation of the "
- "requested resource ",
- ap_escape_html(r->pool, r->uri),
- " could not be found on this server.
\n",
- NULL);
- return(add_optional_notes(r, s1, "variant-list", ""));
- case HTTP_MULTIPLE_CHOICES:
- return(add_optional_notes(r, "", "variant-list", ""));
- case HTTP_LENGTH_REQUIRED:
- s1 = apr_pstrcat(p,
- "A request of the requested method ",
- r->method,
- " requires a valid Content-length.
\n",
- NULL);
- return(add_optional_notes(r, s1, "error-notes", "
\n"));
- case HTTP_PRECONDITION_FAILED:
- return(apr_pstrcat(p,
- "The precondition on the request "
- "for the URL ",
- ap_escape_html(r->pool, r->uri),
- " evaluated to false.
\n",
- NULL));
- case HTTP_NOT_IMPLEMENTED:
- s1 = apr_pstrcat(p,
- "",
- ap_escape_html(r->pool, r->method), " to ",
- ap_escape_html(r->pool, r->uri),
- " not supported.
\n",
- NULL);
- return(add_optional_notes(r, s1, "error-notes", "
\n"));
- case HTTP_BAD_GATEWAY:
- s1 = "The proxy server received an invalid" CRLF
- "response from an upstream server.
" CRLF;
- return(add_optional_notes(r, s1, "error-notes", "
\n"));
- case HTTP_VARIANT_ALSO_VARIES:
- return(apr_pstrcat(p,
- "A variant for the requested "
- "resource\n
\n",
- ap_escape_html(r->pool, r->uri),
- "\n
\nis itself a negotiable resource. "
- "This indicates a configuration error.\n",
- NULL));
- case HTTP_REQUEST_TIME_OUT:
- return("Server timeout waiting for the HTTP request from the client.
\n");
- case HTTP_GONE:
- return(apr_pstrcat(p,
- "The requested resource
",
- ap_escape_html(r->pool, r->uri),
- "
\nis no longer available on this server "
- "and there is no forwarding address.\n"
- "Please remove all references to this "
- "resource.
\n",
- NULL));
- case HTTP_REQUEST_ENTITY_TOO_LARGE:
- return(apr_pstrcat(p,
- "The requested resource
",
- ap_escape_html(r->pool, r->uri), "
\n",
- "does not allow request data with ",
- r->method,
- " requests, or the amount of data provided in\n"
- "the request exceeds the capacity limit.\n",
- NULL));
- case HTTP_REQUEST_URI_TOO_LARGE:
- s1 = "The requested URL's length exceeds the capacity\n"
- "limit for this server.
\n";
- return(add_optional_notes(r, s1, "error-notes", "
\n"));
- case HTTP_UNSUPPORTED_MEDIA_TYPE:
- return("The supplied request data is not in a format\n"
- "acceptable for processing by this resource.
\n");
- case HTTP_RANGE_NOT_SATISFIABLE:
- return("None of the range-specifier values in the Range\n"
- "request-header field overlap the current extent\n"
- "of the selected resource.
\n");
- case HTTP_EXPECTATION_FAILED:
- return(apr_pstrcat(p,
- "The expectation given in the Expect "
- "request-header"
- "\nfield could not be met by this server.
\n"
- "The client sent
\n Expect: ",
- apr_table_get(r->headers_in, "Expect"),
- "\n
\n"
- "but we only allow the 100-continue "
- "expectation.\n",
- NULL));
- case HTTP_UNPROCESSABLE_ENTITY:
- return("The server understands the media type of the\n"
- "request entity, but was unable to process the\n"
- "contained instructions.
\n");
- case HTTP_LOCKED:
- return("The requested resource is currently locked.\n"
- "The lock must be released or proper identification\n"
- "given before the method can be applied.
\n");
- case HTTP_FAILED_DEPENDENCY:
- return("The method could not be performed on the resource\n"
- "because the requested action depended on another\n"
- "action and that other action failed.
\n");
- case HTTP_UPGRADE_REQUIRED:
- return("The requested resource can only be retrieved\n"
- "using SSL. The server is willing to upgrade the current\n"
- "connection to SSL, but your client doesn't support it.\n"
- "Either upgrade your client, or try requesting the page\n"
- "using https://\n");
- case HTTP_INSUFFICIENT_STORAGE:
- return("
The method could not be performed on the resource\n"
- "because the server is unable to store the\n"
- "representation needed to successfully complete the\n"
- "request. There is insufficient free space left in\n"
- "your storage allocation.
\n");
- case HTTP_SERVICE_UNAVAILABLE:
- return("The server is temporarily unable to service your\n"
- "request due to maintenance downtime or capacity\n"
- "problems. Please try again later.
\n");
- case HTTP_GATEWAY_TIME_OUT:
- return("The proxy server did not receive a timely response\n"
- "from the upstream server.
\n");
- case HTTP_NOT_EXTENDED:
- return("A mandatory extension policy in the request is not\n"
- "accepted by the server for this resource.
\n");
- default: /* HTTP_INTERNAL_SERVER_ERROR */
- /*
- * This comparison to expose error-notes could be modified to
- * use a configuration directive and export based on that
- * directive. For now "*" is used to designate an error-notes
- * that is totally safe for any user to see (ie lacks paths,
- * database passwords, etc.)
- */
- if (((error_notes = apr_table_get(r->notes,
- "error-notes")) != NULL)
- && (h1 = apr_table_get(r->notes, "verbose-error-to")) != NULL
- && (strcmp(h1, "*") == 0)) {
- return(apr_pstrcat(p, error_notes, "\n", NULL));
- }
- else {
- return(apr_pstrcat(p,
- "The server encountered an internal "
- "error or\n"
- "misconfiguration and was unable to complete\n"
- "your request.
\n"
- "Please contact the server "
- "administrator,\n ",
- ap_escape_html(r->pool,
- r->server->server_admin),
- " and inform them of the time the "
- "error occurred,\n"
- "and anything you might have done that "
- "may have\n"
- "caused the error.
\n"
- "More information about this error "
- "may be available\n"
- "in the server error log.
\n",
- NULL));
- }
- /*
- * It would be nice to give the user the information they need to
- * fix the problem directly since many users don't have access to
- * the error_log (think University sites) even though they can easily
- * get this error by misconfiguring an htaccess file. However, the
- * e error notes tend to include the real file pathname in this case,
- * which some people consider to be a breach of privacy. Until we
- * can figure out a way to remove the pathname, leave this commented.
- *
- * if ((error_notes = apr_table_get(r->notes,
- * "error-notes")) != NULL) {
- * return(apr_pstrcat(p, error_notes, "\n", NULL);
- * }
- * else {
- * return "";
- * }
- */
- }
-}
-
-/* We should have named this send_canned_response, since it is used for any
- * response that can be generated by the server from the request record.
- * This includes all 204 (no content), 3xx (redirect), 4xx (client error),
- * and 5xx (server error) messages that have not been redirected to another
- * handler via the ErrorDocument feature.
- */
-AP_DECLARE(void) ap_send_error_response(request_rec *r, int recursive_error)
-{
- int status = r->status;
- int idx = ap_index_of_response(status);
- char *custom_response;
- const char *location = apr_table_get(r->headers_out, "Location");
-
- /* At this point, we are starting the response over, so we have to reset
- * this value.
- */
- r->eos_sent = 0;
-
- /* and we need to get rid of any RESOURCE filters that might be lurking
- * around, thinking they are in the middle of the original request
- */
-
- r->output_filters = r->proto_output_filters;
-
- ap_run_insert_error_filter(r);
-
- /*
- * It's possible that the Location field might be in r->err_headers_out
- * instead of r->headers_out; use the latter if possible, else the
- * former.
- */
- if (location == NULL) {
- location = apr_table_get(r->err_headers_out, "Location");
- }
- /* We need to special-case the handling of 204 and 304 responses,
- * since they have specific HTTP requirements and do not include a
- * message body. Note that being assbackwards here is not an option.
- */
- if (status == HTTP_NOT_MODIFIED) {
- ap_finalize_request_protocol(r);
- return;
- }
-
- if (status == HTTP_NO_CONTENT) {
- ap_finalize_request_protocol(r);
- return;
- }
-
- if (!r->assbackwards) {
- apr_table_t *tmp = r->headers_out;
-
- /* For all HTTP/1.x responses for which we generate the message,
- * we need to avoid inheriting the "normal status" header fields
- * that may have been set by the request handler before the
- * error or redirect, except for Location on external redirects.
- */
- r->headers_out = r->err_headers_out;
- r->err_headers_out = tmp;
- apr_table_clear(r->err_headers_out);
-
- if (ap_is_HTTP_REDIRECT(status) || (status == HTTP_CREATED)) {
- if ((location != NULL) && *location) {
- apr_table_setn(r->headers_out, "Location", location);
- }
- else {
- location = ""; /* avoids coredump when printing, below */
- }
- }
-
- r->content_languages = NULL;
- r->content_encoding = NULL;
- r->clength = 0;
- ap_set_content_type(r, "text/html; charset=iso-8859-1");
-
- if ((status == HTTP_METHOD_NOT_ALLOWED)
- || (status == HTTP_NOT_IMPLEMENTED)) {
- apr_table_setn(r->headers_out, "Allow", make_allow(r));
- }
-
- if (r->header_only) {
- ap_finalize_request_protocol(r);
- return;
- }
- }
-
- if ((custom_response = ap_response_code_string(r, idx))) {
- /*
- * We have a custom response output. This should only be
- * a text-string to write back. But if the ErrorDocument
- * was a local redirect and the requested resource failed
- * for any reason, the custom_response will still hold the
- * redirect URL. We don't really want to output this URL
- * as a text message, so first check the custom response
- * string to ensure that it is a text-string (using the
- * same test used in ap_die(), i.e. does it start with a ").
- *
- * If it's not a text string, we've got a recursive error or
- * an external redirect. If it's a recursive error, ap_die passes
- * us the second error code so we can write both, and has already
- * backed up to the original error. If it's an external redirect,
- * it hasn't happened yet; we may never know if it fails.
- */
- if (custom_response[0] == '\"') {
- ap_rputs(custom_response + 1, r);
- ap_finalize_request_protocol(r);
- return;
- }
- }
- {
- const char *title = status_lines[idx];
- const char *h1;
-
- /* Accept a status_line set by a module, but only if it begins
- * with the 3 digit status code
- */
- if (r->status_line != NULL
- && strlen(r->status_line) > 4 /* long enough */
- && apr_isdigit(r->status_line[0])
- && apr_isdigit(r->status_line[1])
- && apr_isdigit(r->status_line[2])
- && apr_isspace(r->status_line[3])
- && apr_isalnum(r->status_line[4])) {
- title = r->status_line;
- }
-
- /* folks decided they didn't want the error code in the H1 text */
- h1 = &title[4];
-
- /* can't count on a charset filter being in place here,
- * so do ebcdic->ascii translation explicitly (if needed)
- */
-
- ap_rvputs_proto_in_ascii(r,
- DOCTYPE_HTML_2_0
- "\n", title,
- "\n\n", h1, "
\n",
- NULL);
-
- ap_rvputs_proto_in_ascii(r,
- get_canned_error_string(status, r, location),
- NULL);
-
- if (recursive_error) {
- ap_rvputs_proto_in_ascii(r, "Additionally, a ",
- status_lines[ap_index_of_response(recursive_error)],
- "\nerror was encountered while trying to use an "
- "ErrorDocument to handle the request.
\n", NULL);
- }
- ap_rvputs_proto_in_ascii(r, ap_psignature("
\n", r), NULL);
- ap_rvputs_proto_in_ascii(r, "\n", NULL);
- }
- ap_finalize_request_protocol(r);
-}
-
-/*
- * Create a new method list with the specified number of preallocated
- * extension slots.
- */
-AP_DECLARE(ap_method_list_t *) ap_make_method_list(apr_pool_t *p, int nelts)
-{
- ap_method_list_t *ml;
-
- ml = (ap_method_list_t *) apr_palloc(p, sizeof(ap_method_list_t));
- ml->method_mask = 0;
- ml->method_list = apr_array_make(p, nelts, sizeof(char *));
- return ml;
-}
-
-/*
- * Make a copy of a method list (primarily for subrequests that may
- * subsequently change it; don't want them changing the parent's, too!).
- */
-AP_DECLARE(void) ap_copy_method_list(ap_method_list_t *dest,
- ap_method_list_t *src)
-{
- int i;
- char **imethods;
- char **omethods;
-
- dest->method_mask = src->method_mask;
- imethods = (char **) src->method_list->elts;
- for (i = 0; i < src->method_list->nelts; ++i) {
- omethods = (char **) apr_array_push(dest->method_list);
- *omethods = apr_pstrdup(dest->method_list->pool, imethods[i]);
- }
-}
-
-/*
- * Invoke a callback routine for each method in the specified list.
- */
-AP_DECLARE_NONSTD(void) ap_method_list_do(int (*comp) (void *urec,
- const char *mname,
- int mnum),
- void *rec,
- const ap_method_list_t *ml, ...)
-{
- va_list vp;
- va_start(vp, ml);
- ap_method_list_vdo(comp, rec, ml, vp);
- va_end(vp);
-}
-
-AP_DECLARE(void) ap_method_list_vdo(int (*comp) (void *mrec,
- const char *mname,
- int mnum),
- void *rec, const ap_method_list_t *ml,
- va_list vp)
-{
-
-}
-
-/*
- * Return true if the specified HTTP method is in the provided
- * method list.
- */
-AP_DECLARE(int) ap_method_in_list(ap_method_list_t *l, const char *method)
-{
- int methnum;
- int i;
- char **methods;
-
- /*
- * If it's one of our known methods, use the shortcut and check the
- * bitmask.
- */
- methnum = ap_method_number_of(method);
- if (methnum != M_INVALID) {
- return !!(l->method_mask & (AP_METHOD_BIT << methnum));
- }
- /*
- * Otherwise, see if the method name is in the array or string names
- */
- if ((l->method_list == NULL) || (l->method_list->nelts == 0)) {
- return 0;
- }
- methods = (char **)l->method_list->elts;
- for (i = 0; i < l->method_list->nelts; ++i) {
- if (strcmp(method, methods[i]) == 0) {
- return 1;
- }
- }
- return 0;
-}
-
-/*
- * Add the specified method to a method list (if it isn't already there).
- */
-AP_DECLARE(void) ap_method_list_add(ap_method_list_t *l, const char *method)
-{
- int methnum;
- int i;
- const char **xmethod;
- char **methods;
-
- /*
- * If it's one of our known methods, use the shortcut and use the
- * bitmask.
- */
- methnum = ap_method_number_of(method);
- l->method_mask |= (AP_METHOD_BIT << methnum);
- if (methnum != M_INVALID) {
- return;
- }
- /*
- * Otherwise, see if the method name is in the array of string names.
- */
- if (l->method_list->nelts != 0) {
- methods = (char **)l->method_list->elts;
- for (i = 0; i < l->method_list->nelts; ++i) {
- if (strcmp(method, methods[i]) == 0) {
- return;
- }
- }
- }
- xmethod = (const char **) apr_array_push(l->method_list);
- *xmethod = method;
-}
-
-/*
- * Remove the specified method from a method list.
- */
-AP_DECLARE(void) ap_method_list_remove(ap_method_list_t *l,
- const char *method)
-{
- int methnum;
- char **methods;
-
- /*
- * If it's a known methods, either builtin or registered
- * by a module, use the bitmask.
- */
- methnum = ap_method_number_of(method);
- l->method_mask |= ~(AP_METHOD_BIT << methnum);
- if (methnum != M_INVALID) {
- return;
- }
- /*
- * Otherwise, see if the method name is in the array of string names.
- */
- if (l->method_list->nelts != 0) {
- register int i, j, k;
- methods = (char **)l->method_list->elts;
- for (i = 0; i < l->method_list->nelts; ) {
- if (strcmp(method, methods[i]) == 0) {
- for (j = i, k = i + 1; k < l->method_list->nelts; ++j, ++k) {
- methods[j] = methods[k];
- }
- --l->method_list->nelts;
- }
- else {
- ++i;
- }
- }
- }
-}
-
-/*
- * Reset a method list to be completely empty.
- */
-AP_DECLARE(void) ap_clear_method_list(ap_method_list_t *l)
-{
- l->method_mask = 0;
- l->method_list->nelts = 0;
-}
-
-/* Generate the human-readable hex representation of an unsigned long
- * (basically a faster version of 'sprintf("%lx")')
- */
-#define HEX_DIGITS "0123456789abcdef"
-static char *etag_ulong_to_hex(char *next, unsigned long u)
-{
- int printing = 0;
- int shift = sizeof(unsigned long) * 8 - 4;
- do {
- unsigned long next_digit = ((u >> shift) & (unsigned long)0xf);
- if (next_digit) {
- *next++ = HEX_DIGITS[next_digit];
- printing = 1;
- }
- else if (printing) {
- *next++ = HEX_DIGITS[next_digit];
- }
- shift -= 4;
- } while (shift);
- *next++ = HEX_DIGITS[u & (unsigned long)0xf];
- return next;
-}
-
-#define ETAG_WEAK "W/"
-#define CHARS_PER_UNSIGNED_LONG (sizeof(unsigned long) * 2)
-/*
- * Construct an entity tag (ETag) from resource information. If it's a real
- * file, build in some of the file characteristics. If the modification time
- * is newer than (request-time minus 1 second), mark the ETag as weak - it
- * could be modified again in as short an interval. We rationalize the
- * modification time we're given to keep it from being in the future.
- */
-AP_DECLARE(char *) ap_make_etag(request_rec *r, int force_weak)
-{
- char *weak;
- apr_size_t weak_len;
- char *etag;
- char *next;
- core_dir_config *cfg;
- etag_components_t etag_bits;
- etag_components_t bits_added;
-
- cfg = (core_dir_config *)ap_get_module_config(r->per_dir_config,
- &core_module);
- etag_bits = (cfg->etag_bits & (~ cfg->etag_remove)) | cfg->etag_add;
-
- /*
- * If it's a file (or we wouldn't be here) and no ETags
- * should be set for files, return an empty string and
- * note it for the header-sender to ignore.
- */
- if (etag_bits & ETAG_NONE) {
- apr_table_setn(r->notes, "no-etag", "omit");
- return "";
- }
-
- if (etag_bits == ETAG_UNSET) {
- etag_bits = ETAG_BACKWARD;
- }
- /*
- * Make an ETag header out of various pieces of information. We use
- * the last-modified date and, if we have a real file, the
- * length and inode number - note that this doesn't have to match
- * the content-length (i.e. includes), it just has to be unique
- * for the file.
- *
- * If the request was made within a second of the last-modified date,
- * we send a weak tag instead of a strong one, since it could
- * be modified again later in the second, and the validation
- * would be incorrect.
- */
- if ((r->request_time - r->mtime > (1 * APR_USEC_PER_SEC)) &&
- !force_weak) {
- weak = NULL;
- weak_len = 0;
- }
- else {
- weak = ETAG_WEAK;
- weak_len = sizeof(ETAG_WEAK);
- }
-
- if (r->finfo.filetype != 0) {
- /*
- * ETag gets set to [W/]"inode-size-mtime", modulo any
- * FileETag keywords.
- */
- etag = apr_palloc(r->pool, weak_len + sizeof("\"--\"") +
- 3 * CHARS_PER_UNSIGNED_LONG + 1);
- next = etag;
- if (weak) {
- while (*weak) {
- *next++ = *weak++;
- }
- }
- *next++ = '"';
- bits_added = 0;
- if (etag_bits & ETAG_INODE) {
- next = etag_ulong_to_hex(next, (unsigned long)r->finfo.inode);
- bits_added |= ETAG_INODE;
- }
- if (etag_bits & ETAG_SIZE) {
- if (bits_added != 0) {
- *next++ = '-';
- }
- next = etag_ulong_to_hex(next, (unsigned long)r->finfo.size);
- bits_added |= ETAG_SIZE;
- }
- if (etag_bits & ETAG_MTIME) {
- if (bits_added != 0) {
- *next++ = '-';
- }
- next = etag_ulong_to_hex(next, (unsigned long)r->mtime);
- }
- *next++ = '"';
- *next = '\0';
- }
- else {
- /*
- * Not a file document, so just use the mtime: [W/]"mtime"
- */
- etag = apr_palloc(r->pool, weak_len + sizeof("\"\"") +
- CHARS_PER_UNSIGNED_LONG + 1);
- next = etag;
- if (weak) {
- while (*weak) {
- *next++ = *weak++;
- }
- }
- *next++ = '"';
- next = etag_ulong_to_hex(next, (unsigned long)r->mtime);
- *next++ = '"';
- *next = '\0';
- }
-
- return etag;
-}
-
-AP_DECLARE(void) ap_set_etag(request_rec *r)
-{
- char *etag;
- char *variant_etag, *vlv;
- int vlv_weak;
-
- if (!r->vlist_validator) {
- etag = ap_make_etag(r, 0);
-
- /* If we get a blank etag back, don't set the header. */
- if (!etag[0]) {
- return;
- }
- }
- else {
- /* If we have a variant list validator (vlv) due to the
- * response being negotiated, then we create a structured
- * entity tag which merges the variant etag with the variant
- * list validator (vlv). This merging makes revalidation
- * somewhat safer, ensures that caches which can deal with
- * Vary will (eventually) be updated if the set of variants is
- * changed, and is also a protocol requirement for transparent
- * content negotiation.
- */
-
- /* if the variant list validator is weak, we make the whole
- * structured etag weak. If we would not, then clients could
- * have problems merging range responses if we have different
- * variants with the same non-globally-unique strong etag.
- */
-
- vlv = r->vlist_validator;
- vlv_weak = (vlv[0] == 'W');
-
- variant_etag = ap_make_etag(r, vlv_weak);
-
- /* If we get a blank etag back, don't append vlv and stop now. */
- if (!variant_etag[0]) {
- return;
- }
-
- /* merge variant_etag and vlv into a structured etag */
- variant_etag[strlen(variant_etag) - 1] = '\0';
- if (vlv_weak) {
- vlv += 3;
- }
- else {
- vlv++;
- }
- etag = apr_pstrcat(r->pool, variant_etag, ";", vlv, NULL);
- }
-
- apr_table_setn(r->headers_out, "ETag", etag);
-}
-
-static int parse_byterange(char *range, apr_off_t clength,
- apr_off_t *start, apr_off_t *end)
-{
- char *dash = strchr(range, '-');
- char *errp;
- apr_off_t number;
-
- if (!dash) {
- return 0;
- }
-
- if ((dash == range)) {
- /* In the form "-5" */
- if (apr_strtoff(&number, dash+1, &errp, 10) || *errp) {
- return 0;
- }
- *start = clength - number;
- *end = clength - 1;
- }
- else {
- *dash++ = '\0';
- if (apr_strtoff(&number, range, &errp, 10) || *errp) {
- return 0;
- }
- *start = number;
- if (*dash) {
- if (apr_strtoff(&number, dash, &errp, 10) || *errp) {
- return 0;
- }
- *end = number;
- }
- else { /* "5-" */
- *end = clength - 1;
- }
- }
-
- if (*start < 0) {
- *start = 0;
- }
-
- if (*end >= clength) {
- *end = clength - 1;
- }
-
- if (*start > *end) {
- return -1;
- }
-
- return (*start > 0 || *end < clength);
-}
-
-static int ap_set_byterange(request_rec *r);
-
-typedef struct byterange_ctx {
- apr_bucket_brigade *bb;
- int num_ranges;
- char *boundary;
- char *bound_head;
-} byterange_ctx;
-
-/*
- * Here we try to be compatible with clients that want multipart/x-byteranges
- * instead of multipart/byteranges (also see above), as per HTTP/1.1. We
- * look for the Request-Range header (e.g. Netscape 2 and 3) as an indication
- * that the browser supports an older protocol. We also check User-Agent
- * for Microsoft Internet Explorer 3, which needs this as well.
- */
-static int use_range_x(request_rec *r)
-{
- const char *ua;
- return (apr_table_get(r->headers_in, "Request-Range")
- || ((ua = apr_table_get(r->headers_in, "User-Agent"))
- && ap_strstr_c(ua, "MSIE 3")));
-}
-
-#define BYTERANGE_FMT "%" APR_OFF_T_FMT "-%" APR_OFF_T_FMT "/%" APR_OFF_T_FMT
-#define PARTITION_ERR_FMT "apr_brigade_partition() failed " \
- "[%" APR_OFF_T_FMT ",%" APR_OFF_T_FMT "]"
-
-AP_CORE_DECLARE_NONSTD(apr_status_t) ap_byterange_filter(ap_filter_t *f,
- apr_bucket_brigade *bb)
-{
-#define MIN_LENGTH(len1, len2) ((len1 > len2) ? len2 : len1)
- request_rec *r = f->r;
- conn_rec *c = r->connection;
- byterange_ctx *ctx = f->ctx;
- apr_bucket *e;
- apr_bucket_brigade *bsend;
- apr_off_t range_start;
- apr_off_t range_end;
- char *current;
- apr_off_t bb_length;
- apr_off_t clength = 0;
- apr_status_t rv;
- int found = 0;
-
- if (!ctx) {
- int num_ranges = ap_set_byterange(r);
-
- /* We have nothing to do, get out of the way. */
- if (num_ranges == 0) {
- ap_remove_output_filter(f);
- return ap_pass_brigade(f->next, bb);
- }
-
- ctx = f->ctx = apr_pcalloc(r->pool, sizeof(*ctx));
- ctx->num_ranges = num_ranges;
- /* create a brigade in case we never call ap_save_brigade() */
- ctx->bb = apr_brigade_create(r->pool, c->bucket_alloc);
-
- if (ctx->num_ranges > 1) {
- /* Is ap_make_content_type required here? */
- const char *orig_ct = ap_make_content_type(r, r->content_type);
- ctx->boundary = apr_psprintf(r->pool, "%" APR_UINT64_T_HEX_FMT "%lx",
- (apr_uint64_t)r->request_time, (long) getpid());
-
- ap_set_content_type(r, apr_pstrcat(r->pool, "multipart",
- use_range_x(r) ? "/x-" : "/",
- "byteranges; boundary=",
- ctx->boundary, NULL));
-
- ctx->bound_head = apr_pstrcat(r->pool,
- CRLF "--", ctx->boundary,
- CRLF "Content-type: ",
- orig_ct,
- CRLF "Content-range: bytes ",
- NULL);
- ap_xlate_proto_to_ascii(ctx->bound_head, strlen(ctx->bound_head));
- }
- }
-
- /* We can't actually deal with byte-ranges until we have the whole brigade
- * because the byte-ranges can be in any order, and according to the RFC,
- * we SHOULD return the data in the same order it was requested.
- *
- * XXX: We really need to dump all bytes prior to the start of the earliest
- * range, and only slurp up to the end of the latest range. By this we
- * mean that we should peek-ahead at the lowest first byte of any range,
- * and the highest last byte of any range.
- */
- if (!APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) {
- ap_save_brigade(f, &ctx->bb, &bb, r->pool);
- return APR_SUCCESS;
- }
-
- /* Prepend any earlier saved brigades. */
- APR_BRIGADE_PREPEND(bb, ctx->bb);
-
- /* It is possible that we won't have a content length yet, so we have to
- * compute the length before we can actually do the byterange work.
- */
- apr_brigade_length(bb, 1, &bb_length);
- clength = (apr_off_t)bb_length;
-
- /* this brigade holds what we will be sending */
- bsend = apr_brigade_create(r->pool, c->bucket_alloc);
-
- while ((current = ap_getword(r->pool, &r->range, ','))
- && (rv = parse_byterange(current, clength, &range_start,
- &range_end))) {
- apr_bucket *e2;
- apr_bucket *ec;
-
- if (rv == -1) {
- continue;
- }
-
- /* these calls to apr_brigade_partition() should theoretically
- * never fail because of the above call to apr_brigade_length(),
- * but what the heck, we'll check for an error anyway */
- if ((rv = apr_brigade_partition(bb, range_start, &ec)) != APR_SUCCESS) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
- PARTITION_ERR_FMT, range_start, clength);
- continue;
- }
- if ((rv = apr_brigade_partition(bb, range_end+1, &e2)) != APR_SUCCESS) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
- PARTITION_ERR_FMT, range_end+1, clength);
- continue;
- }
-
- found = 1;
-
- /* For single range requests, we must produce Content-Range header.
- * Otherwise, we need to produce the multipart boundaries.
- */
- if (ctx->num_ranges == 1) {
- apr_table_setn(r->headers_out, "Content-Range",
- apr_psprintf(r->pool, "bytes " BYTERANGE_FMT,
- range_start, range_end, clength));
- }
- else {
- char *ts;
-
- e = apr_bucket_pool_create(ctx->bound_head, strlen(ctx->bound_head),
- r->pool, c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bsend, e);
-
- ts = apr_psprintf(r->pool, BYTERANGE_FMT CRLF CRLF,
- range_start, range_end, clength);
- ap_xlate_proto_to_ascii(ts, strlen(ts));
- e = apr_bucket_pool_create(ts, strlen(ts), r->pool,
- c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bsend, e);
- }
-
- do {
- apr_bucket *foo;
- const char *str;
- apr_size_t len;
-
- if (apr_bucket_copy(ec, &foo) != APR_SUCCESS) {
- /* this shouldn't ever happen due to the call to
- * apr_brigade_length() above which normalizes
- * indeterminate-length buckets. just to be sure,
- * though, this takes care of uncopyable buckets that
- * do somehow manage to slip through.
- */
- /* XXX: check for failure? */
- apr_bucket_read(ec, &str, &len, APR_BLOCK_READ);
- apr_bucket_copy(ec, &foo);
- }
- APR_BRIGADE_INSERT_TAIL(bsend, foo);
- ec = APR_BUCKET_NEXT(ec);
- } while (ec != e2);
- }
-
- if (found == 0) {
- ap_remove_output_filter(f);
- r->status = HTTP_OK;
- /* bsend is assumed to be empty if we get here. */
- e = ap_bucket_error_create(HTTP_RANGE_NOT_SATISFIABLE, NULL,
- r->pool, c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bsend, e);
- e = apr_bucket_eos_create(c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bsend, e);
- return ap_pass_brigade(f->next, bsend);
- }
-
- if (ctx->num_ranges > 1) {
- char *end;
-
- /* add the final boundary */
- end = apr_pstrcat(r->pool, CRLF "--", ctx->boundary, "--" CRLF, NULL);
- ap_xlate_proto_to_ascii(end, strlen(end));
- e = apr_bucket_pool_create(end, strlen(end), r->pool, c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bsend, e);
- }
-
- e = apr_bucket_eos_create(c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bsend, e);
-
- /* we're done with the original content - all of our data is in bsend. */
- apr_brigade_destroy(bb);
-
- /* send our multipart output */
- return ap_pass_brigade(f->next, bsend);
-}
-
-static int ap_set_byterange(request_rec *r)
-{
- const char *range;
- const char *if_range;
- const char *match;
- const char *ct;
- int num_ranges;
-
- if (r->assbackwards) {
- return 0;
- }
-
- /* Check for Range request-header (HTTP/1.1) or Request-Range for
- * backwards-compatibility with second-draft Luotonen/Franks
- * byte-ranges (e.g. Netscape Navigator 2-3).
- *
- * We support this form, with Request-Range, and (farther down) we
- * send multipart/x-byteranges instead of multipart/byteranges for
- * Request-Range based requests to work around a bug in Netscape
- * Navigator 2-3 and MSIE 3.
- */
-
- if (!(range = apr_table_get(r->headers_in, "Range"))) {
- range = apr_table_get(r->headers_in, "Request-Range");
- }
-
- if (!range || strncasecmp(range, "bytes=", 6) || r->status != HTTP_OK) {
- return 0;
- }
-
- /* is content already a single range? */
- if (apr_table_get(r->headers_out, "Content-Range")) {
- return 0;
- }
-
- /* is content already a multiple range? */
- if ((ct = apr_table_get(r->headers_out, "Content-Type"))
- && (!strncasecmp(ct, "multipart/byteranges", 20)
- || !strncasecmp(ct, "multipart/x-byteranges", 22))) {
- return 0;
- }
-
- /* Check the If-Range header for Etag or Date.
- * Note that this check will return false (as required) if either
- * of the two etags are weak.
- */
- if ((if_range = apr_table_get(r->headers_in, "If-Range"))) {
- if (if_range[0] == '"') {
- if (!(match = apr_table_get(r->headers_out, "Etag"))
- || (strcmp(if_range, match) != 0)) {
- return 0;
- }
- }
- else if (!(match = apr_table_get(r->headers_out, "Last-Modified"))
- || (strcmp(if_range, match) != 0)) {
- return 0;
- }
- }
-
- if (!ap_strchr_c(range, ',')) {
- /* a single range */
- num_ranges = 1;
- }
- else {
- /* a multiple range */
- num_ranges = 2;
- }
-
- r->status = HTTP_PARTIAL_CONTENT;
- r->range = range + 6;
-
- return num_ranges;
-}
Modified: httpd/httpd/trunk/modules/http/http_protocol.c
Url: http://svn.apache.org/viewcvs/httpd/httpd/trunk/modules/http/http_protocol.c?view=diff&rev=106692&p1=httpd/httpd/trunk/modules/http/http_protocol.c&r1=106691&p2=httpd/httpd/trunk/modules/http/http_protocol.c&r2=106692
==============================================================================
--- httpd/httpd/trunk/modules/http/http_protocol.c (original)
+++ httpd/httpd/trunk/modules/http/http_protocol.c Sat Nov 27 00:07:44 2004
@@ -702,337 +702,6 @@
return NULL;
}
-static long get_chunk_size(char *);
-
-typedef struct http_filter_ctx {
- apr_off_t remaining;
- apr_off_t limit;
- apr_off_t limit_used;
- enum {
- BODY_NONE,
- BODY_LENGTH,
- BODY_CHUNK
- } state;
- int eos_sent;
-} http_ctx_t;
-
-/* This is the HTTP_INPUT filter for HTTP requests and responses from
- * proxied servers (mod_proxy). It handles chunked and content-length
- * bodies. This can only be inserted/used after the headers
- * are successfully parsed.
- */
-apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b,
- ap_input_mode_t mode, apr_read_type_e block,
- apr_off_t readbytes)
-{
- apr_bucket *e;
- http_ctx_t *ctx = f->ctx;
- apr_status_t rv;
- apr_off_t totalread;
-
- /* just get out of the way of things we don't want. */
- if (mode != AP_MODE_READBYTES && mode != AP_MODE_GETLINE) {
- return ap_get_brigade(f->next, b, mode, block, readbytes);
- }
-
- if (!ctx) {
- const char *tenc, *lenp;
- f->ctx = ctx = apr_palloc(f->r->pool, sizeof(*ctx));
- ctx->state = BODY_NONE;
- ctx->remaining = 0;
- ctx->limit_used = 0;
- ctx->eos_sent = 0;
-
- /* LimitRequestBody does not apply to proxied responses.
- * Consider implementing this check in its own filter.
- * Would adding a directive to limit the size of proxied
- * responses be useful?
- */
- if (!f->r->proxyreq) {
- ctx->limit = ap_get_limit_req_body(f->r);
- }
- else {
- ctx->limit = 0;
- }
-
- tenc = apr_table_get(f->r->headers_in, "Transfer-Encoding");
- lenp = apr_table_get(f->r->headers_in, "Content-Length");
-
- if (tenc) {
- if (!strcasecmp(tenc, "chunked")) {
- ctx->state = BODY_CHUNK;
- }
- }
- else if (lenp) {
- char *endstr;
-
- ctx->state = BODY_LENGTH;
- errno = 0;
-
- /* Protects against over/underflow, non-digit chars in the
- * string (excluding leading space) (the endstr checks)
- * and a negative number. */
- if (apr_strtoff(&ctx->remaining, lenp, &endstr, 10)
- || endstr == lenp || *endstr || ctx->remaining < 0) {
- apr_bucket_brigade *bb;
-
- ctx->remaining = 0;
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r,
- "Invalid Content-Length");
-
- bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
- e = ap_bucket_error_create(HTTP_REQUEST_ENTITY_TOO_LARGE, NULL,
- f->r->pool, f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
- ctx->eos_sent = 1;
- return ap_pass_brigade(f->r->output_filters, bb);
- }
-
- /* If we have a limit in effect and we know the C-L ahead of
- * time, stop it here if it is invalid.
- */
- if (ctx->limit && ctx->limit < ctx->remaining) {
- apr_bucket_brigade *bb;
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r,
- "Requested content-length of %" APR_OFF_T_FMT
- " is larger than the configured limit"
- " of %" APR_OFF_T_FMT, ctx->remaining, ctx->limit);
- bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
- e = ap_bucket_error_create(HTTP_REQUEST_ENTITY_TOO_LARGE, NULL,
- f->r->pool, f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
- ctx->eos_sent = 1;
- return ap_pass_brigade(f->r->output_filters, bb);
- }
- }
-
- /* If we don't have a request entity indicated by the headers, EOS.
- * (BODY_NONE is a valid intermediate state due to trailers,
- * but it isn't a valid starting state.)
- *
- * RFC 2616 Section 4.4 note 5 states that connection-close
- * is invalid for a request entity - request bodies must be
- * denoted by C-L or T-E: chunked.
- *
- * Note that since the proxy uses this filter to handle the
- * proxied *response*, proxy responses MUST be exempt.
- */
- if (ctx->state == BODY_NONE && f->r->proxyreq != PROXYREQ_RESPONSE) {
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(b, e);
- ctx->eos_sent = 1;
- return APR_SUCCESS;
- }
-
- /* Since we're about to read data, send 100-Continue if needed.
- * Only valid on chunked and C-L bodies where the C-L is > 0. */
- if ((ctx->state == BODY_CHUNK ||
- (ctx->state == BODY_LENGTH && ctx->remaining > 0)) &&
- f->r->expecting_100 && f->r->proto_num >= HTTP_VERSION(1,1)) {
- char *tmp;
- apr_bucket_brigade *bb;
-
- tmp = apr_pstrcat(f->r->pool, AP_SERVER_PROTOCOL, " ",
- status_lines[0], CRLF CRLF, NULL);
- bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
- e = apr_bucket_pool_create(tmp, strlen(tmp), f->r->pool,
- f->c->bucket_alloc);
- APR_BRIGADE_INSERT_HEAD(bb, e);
- e = apr_bucket_flush_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
-
- ap_pass_brigade(f->c->output_filters, bb);
- }
-
- /* We can't read the chunk until after sending 100 if required. */
- if (ctx->state == BODY_CHUNK) {
- char line[30];
- apr_bucket_brigade *bb;
- apr_size_t len = 30;
- apr_off_t brigade_length;
-
- bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
-
- rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE,
- APR_BLOCK_READ, 0);
-
- if (rv == APR_SUCCESS) {
- /* We have to check the length of the brigade we got back.
- * We will not accept partial lines.
- */
- rv = apr_brigade_length(bb, 1, &brigade_length);
- if (rv == APR_SUCCESS
- && brigade_length > f->r->server->limit_req_line) {
- rv = APR_ENOSPC;
- }
- if (rv == APR_SUCCESS) {
- rv = apr_brigade_flatten(bb, line, &len);
- if (rv == APR_SUCCESS) {
- ctx->remaining = get_chunk_size(line);
- }
- }
- }
- apr_brigade_cleanup(bb);
-
- /* Detect chunksize error (such as overflow) */
- if (rv != APR_SUCCESS || ctx->remaining < 0) {
- ctx->remaining = 0; /* Reset it in case we have to
- * come back here later */
- e = ap_bucket_error_create(HTTP_REQUEST_ENTITY_TOO_LARGE, NULL,
- f->r->pool,
- f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
- ctx->eos_sent = 1;
- return ap_pass_brigade(f->r->output_filters, bb);
- }
-
- if (!ctx->remaining) {
- /* Handle trailers by calling ap_get_mime_headers again! */
- ctx->state = BODY_NONE;
- ap_get_mime_headers(f->r);
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(b, e);
- ctx->eos_sent = 1;
- return APR_SUCCESS;
- }
- }
- }
-
- if (ctx->eos_sent) {
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(b, e);
- return APR_SUCCESS;
- }
-
- if (!ctx->remaining) {
- switch (ctx->state) {
- case BODY_NONE:
- break;
- case BODY_LENGTH:
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(b, e);
- ctx->eos_sent = 1;
- return APR_SUCCESS;
- case BODY_CHUNK:
- {
- char line[30];
- apr_bucket_brigade *bb;
- apr_size_t len = 30;
-
- bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
-
- /* We need to read the CRLF after the chunk. */
- rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE,
- APR_BLOCK_READ, 0);
- apr_brigade_cleanup(bb);
-
- if (rv == APR_SUCCESS) {
- /* Read the real chunk line. */
- rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE,
- APR_BLOCK_READ, 0);
- if (rv == APR_SUCCESS) {
- rv = apr_brigade_flatten(bb, line, &len);
- if (rv == APR_SUCCESS) {
- ctx->remaining = get_chunk_size(line);
- }
- }
- apr_brigade_cleanup(bb);
- }
-
- /* Detect chunksize error (such as overflow) */
- if (rv != APR_SUCCESS || ctx->remaining < 0) {
- ctx->remaining = 0; /* Reset it in case we have to
- * come back here later */
- e = ap_bucket_error_create(HTTP_REQUEST_ENTITY_TOO_LARGE,
- NULL, f->r->pool,
- f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
- ctx->eos_sent = 1;
- return ap_pass_brigade(f->r->output_filters, bb);
- }
-
- if (!ctx->remaining) {
- /* Handle trailers by calling ap_get_mime_headers again! */
- ctx->state = BODY_NONE;
- ap_get_mime_headers(f->r);
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(b, e);
- ctx->eos_sent = 1;
- return APR_SUCCESS;
- }
- }
- break;
- }
- }
-
- /* Ensure that the caller can not go over our boundary point. */
- if (ctx->state == BODY_LENGTH || ctx->state == BODY_CHUNK) {
- if (ctx->remaining < readbytes) {
- readbytes = ctx->remaining;
- }
- AP_DEBUG_ASSERT(readbytes > 0);
- }
-
- rv = ap_get_brigade(f->next, b, mode, block, readbytes);
-
- if (rv != APR_SUCCESS) {
- return rv;
- }
-
- /* How many bytes did we just read? */
- apr_brigade_length(b, 0, &totalread);
-
- /* If this happens, we have a bucket of unknown length. Die because
- * it means our assumptions have changed. */
- AP_DEBUG_ASSERT(totalread >= 0);
-
- if (ctx->state != BODY_NONE) {
- ctx->remaining -= totalread;
- }
-
- /* If we have no more bytes remaining on a C-L request,
- * save the callter a roundtrip to discover EOS.
- */
- if (ctx->state == BODY_LENGTH && ctx->remaining == 0) {
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(b, e);
- }
-
- /* We have a limit in effect. */
- if (ctx->limit) {
- /* FIXME: Note that we might get slightly confused on chunked inputs
- * as we'd need to compensate for the chunk lengths which may not
- * really count. This seems to be up for interpretation. */
- ctx->limit_used += totalread;
- if (ctx->limit < ctx->limit_used) {
- apr_bucket_brigade *bb;
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r,
- "Read content-length of %" APR_OFF_T_FMT
- " is larger than the configured limit"
- " of %" APR_OFF_T_FMT, ctx->limit_used, ctx->limit);
- bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
- e = ap_bucket_error_create(HTTP_REQUEST_ENTITY_TOO_LARGE, NULL,
- f->r->pool,
- f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
- ctx->eos_sent = 1;
- return ap_pass_brigade(f->r->output_filters, bb);
- }
- }
-
- return APR_SUCCESS;
-}
-
/* The index is found by its offset from the x00 code of each level.
* Although this is fast, it will need to be replaced if some nutcase
* decides to define a high-numbered code before the lower numbers.
@@ -1069,253 +738,6 @@
return status_lines[ap_index_of_response(status)];
}
-typedef struct header_struct {
- apr_pool_t *pool;
- apr_bucket_brigade *bb;
-} header_struct;
-
-/* Send a single HTTP header field to the client. Note that this function
- * is used in calls to table_do(), so their interfaces are co-dependent.
- * In other words, don't change this one without checking table_do in alloc.c.
- * It returns true unless there was a write error of some kind.
- */
-static int form_header_field(header_struct *h,
- const char *fieldname, const char *fieldval)
-{
-#if APR_CHARSET_EBCDIC
- char *headfield;
- apr_size_t len;
- apr_size_t name_len;
- apr_size_t val_len;
- char *next;
-
- name_len = strlen(fieldname);
- val_len = strlen(fieldval);
- len = name_len + val_len + 4; /* 4 for ": " plus CRLF */
- headfield = (char *)apr_palloc(h->pool, len + 1);
- memcpy(headfield, fieldname, name_len);
- next = headfield + name_len;
- *next++ = ':';
- *next++ = ' ';
- memcpy(next, fieldval, val_len);
- next += val_len;
- *next++ = CR;
- *next++ = LF;
- *next = 0;
- ap_xlate_proto_to_ascii(headfield, len);
- apr_brigade_write(h->bb, NULL, NULL, headfield, len);
-#else
- struct iovec vec[4];
- struct iovec *v = vec;
- v->iov_base = (void *)fieldname;
- v->iov_len = strlen(fieldname);
- v++;
- v->iov_base = ": ";
- v->iov_len = sizeof(": ") - 1;
- v++;
- v->iov_base = (void *)fieldval;
- v->iov_len = strlen(fieldval);
- v++;
- v->iov_base = CRLF;
- v->iov_len = sizeof(CRLF) - 1;
- apr_brigade_writev(h->bb, NULL, NULL, vec, 4);
-#endif /* !APR_CHARSET_EBCDIC */
- return 1;
-}
-
-/* Send a request's HTTP response headers to the client.
- */
-static apr_status_t send_all_header_fields(header_struct *h,
- const request_rec *r)
-{
- const apr_array_header_t *elts;
- const apr_table_entry_t *t_elt;
- const apr_table_entry_t *t_end;
- struct iovec *vec;
- struct iovec *vec_next;
-
- elts = apr_table_elts(r->headers_out);
- if (elts->nelts == 0) {
- return APR_SUCCESS;
- }
- t_elt = (const apr_table_entry_t *)(elts->elts);
- t_end = t_elt + elts->nelts;
- vec = (struct iovec *)apr_palloc(h->pool, 4 * elts->nelts *
- sizeof(struct iovec));
- vec_next = vec;
-
- /* For each field, generate
- * name ": " value CRLF
- */
- do {
- vec_next->iov_base = (void*)(t_elt->key);
- vec_next->iov_len = strlen(t_elt->key);
- vec_next++;
- vec_next->iov_base = ": ";
- vec_next->iov_len = sizeof(": ") - 1;
- vec_next++;
- vec_next->iov_base = (void*)(t_elt->val);
- vec_next->iov_len = strlen(t_elt->val);
- vec_next++;
- vec_next->iov_base = CRLF;
- vec_next->iov_len = sizeof(CRLF) - 1;
- vec_next++;
- t_elt++;
- } while (t_elt < t_end);
-
-#if APR_CHARSET_EBCDIC
- {
- apr_size_t len;
- char *tmp = apr_pstrcatv(r->pool, vec, vec_next - vec, &len);
- ap_xlate_proto_to_ascii(tmp, len);
- return apr_brigade_write(h->bb, NULL, NULL, tmp, len);
- }
-#else
- return apr_brigade_writev(h->bb, NULL, NULL, vec, vec_next - vec);
-#endif
-}
-
-/*
- * Determine the protocol to use for the response. Potentially downgrade
- * to HTTP/1.0 in some situations and/or turn off keepalives.
- *
- * also prepare r->status_line.
- */
-static void basic_http_header_check(request_rec *r,
- const char **protocol)
-{
- if (r->assbackwards) {
- /* no such thing as a response protocol */
- return;
- }
-
- if (!r->status_line) {
- r->status_line = status_lines[ap_index_of_response(r->status)];
- }
-
- /* Note that we must downgrade before checking for force responses. */
- if (r->proto_num > HTTP_VERSION(1,0)
- && apr_table_get(r->subprocess_env, "downgrade-1.0")) {
- r->proto_num = HTTP_VERSION(1,0);
- }
-
- /* kludge around broken browsers when indicated by force-response-1.0
- */
- if (r->proto_num == HTTP_VERSION(1,0)
- && apr_table_get(r->subprocess_env, "force-response-1.0")) {
- *protocol = "HTTP/1.0";
- r->connection->keepalive = AP_CONN_CLOSE;
- }
- else {
- *protocol = AP_SERVER_PROTOCOL;
- }
-
-}
-
-/* fill "bb" with a barebones/initial HTTP response header */
-static void basic_http_header(request_rec *r, apr_bucket_brigade *bb,
- const char *protocol)
-{
- char *date;
- const char *server;
- header_struct h;
- struct iovec vec[4];
-
- if (r->assbackwards) {
- /* there are no headers to send */
- return;
- }
-
- /* Output the HTTP/1.x Status-Line and the Date and Server fields */
-
- vec[0].iov_base = (void *)protocol;
- vec[0].iov_len = strlen(protocol);
- vec[1].iov_base = (void *)" ";
- vec[1].iov_len = sizeof(" ") - 1;
- vec[2].iov_base = (void *)(r->status_line);
- vec[2].iov_len = strlen(r->status_line);
- vec[3].iov_base = (void *)CRLF;
- vec[3].iov_len = sizeof(CRLF) - 1;
-#if APR_CHARSET_EBCDIC
- {
- char *tmp;
- apr_size_t len;
- tmp = apr_pstrcatv(r->pool, vec, 4, &len);
- ap_xlate_proto_to_ascii(tmp, len);
- apr_brigade_write(bb, NULL, NULL, tmp, len);
- }
-#else
- apr_brigade_writev(bb, NULL, NULL, vec, 4);
-#endif
-
- date = apr_palloc(r->pool, APR_RFC822_DATE_LEN);
- ap_recent_rfc822_date(date, r->request_time);
-
- h.pool = r->pool;
- h.bb = bb;
- form_header_field(&h, "Date", date);
-
- /* keep the set-by-proxy server header, otherwise
- * generate a new server header */
- if (r->proxyreq != PROXYREQ_NONE) {
- server = apr_table_get(r->headers_out, "Server");
- if (server) {
- form_header_field(&h, "Server", server);
- }
- }
- else {
- form_header_field(&h, "Server", ap_get_server_version());
- }
-
- /* unset so we don't send them again */
- apr_table_unset(r->headers_out, "Date"); /* Avoid bogosity */
- apr_table_unset(r->headers_out, "Server");
-}
-
-AP_DECLARE(void) ap_basic_http_header(request_rec *r, apr_bucket_brigade *bb)
-{
- const char *protocol;
-
- basic_http_header_check(r, &protocol);
- basic_http_header(r, bb, protocol);
-}
-
-/* Navigator versions 2.x, 3.x and 4.0 betas up to and including 4.0b2
- * have a header parsing bug. If the terminating \r\n occur starting
- * at offset 256, 257 or 258 of output then it will not properly parse
- * the headers. Curiously it doesn't exhibit this problem at 512, 513.
- * We are guessing that this is because their initial read of a new request
- * uses a 256 byte buffer, and subsequent reads use a larger buffer.
- * So the problem might exist at different offsets as well.
- *
- * This should also work on keepalive connections assuming they use the
- * same small buffer for the first read of each new request.
- *
- * At any rate, we check the bytes written so far and, if we are about to
- * tickle the bug, we instead insert a bogus padding header. Since the bug
- * manifests as a broken image in Navigator, users blame the server. :(
- * It is more expensive to check the User-Agent than it is to just add the
- * bytes, so we haven't used the BrowserMatch feature here.
- */
-static void terminate_header(apr_bucket_brigade *bb)
-{
- char tmp[] = "X-Pad: avoid browser bug" CRLF;
- char crlf[] = CRLF;
- apr_off_t len;
- apr_size_t buflen;
-
- (void) apr_brigade_length(bb, 1, &len);
-
- if (len >= 255 && len <= 257) {
- buflen = strlen(tmp);
- ap_xlate_proto_to_ascii(tmp, buflen);
- apr_brigade_write(bb, NULL, NULL, tmp, buflen);
- }
- buflen = strlen(crlf);
- ap_xlate_proto_to_ascii(crlf, buflen);
- apr_brigade_write(bb, NULL, NULL, crlf, buflen);
-}
-
/* Build the Allow field-value from the request handler method mask.
* Note that we always allow TRACE, since it is handled below.
*/
@@ -1366,41 +788,6 @@
return list;
}
-AP_DECLARE_NONSTD(int) ap_send_http_trace(request_rec *r)
-{
- int rv;
- apr_bucket_brigade *b;
- header_struct h;
-
- if (r->method_number != M_TRACE) {
- return DECLINED;
- }
-
- /* Get the original request */
- while (r->prev) {
- r = r->prev;
- }
-
- if ((rv = ap_setup_client_block(r, REQUEST_NO_BODY))) {
- return rv;
- }
-
- ap_set_content_type(r, "message/http");
-
- /* Now we recreate the request, and echo it back */
-
- b = apr_brigade_create(r->pool, r->connection->bucket_alloc);
- apr_brigade_putstrs(b, NULL, NULL, r->the_request, CRLF, NULL);
- h.pool = r->pool;
- h.bb = b;
- apr_table_do((int (*) (void *, const char *, const char *))
- form_header_field, (void *) &h, r->headers_in, NULL);
- apr_brigade_puts(b, NULL, NULL, CRLF);
- ap_pass_brigade(r->output_filters, b);
-
- return DONE;
-}
-
AP_DECLARE(int) ap_send_http_options(request_rec *r)
{
if (r->assbackwards) {
@@ -1416,84 +803,6 @@
return OK;
}
-/* This routine is called by apr_table_do and merges all instances of
- * the passed field values into a single array that will be further
- * processed by some later routine. Originally intended to help split
- * and recombine multiple Vary fields, though it is generic to any field
- * consisting of comma/space-separated tokens.
- */
-static int uniq_field_values(void *d, const char *key, const char *val)
-{
- apr_array_header_t *values;
- char *start;
- char *e;
- char **strpp;
- int i;
-
- values = (apr_array_header_t *)d;
-
- e = apr_pstrdup(values->pool, val);
-
- do {
- /* Find a non-empty fieldname */
-
- while (*e == ',' || apr_isspace(*e)) {
- ++e;
- }
- if (*e == '\0') {
- break;
- }
- start = e;
- while (*e != '\0' && *e != ',' && !apr_isspace(*e)) {
- ++e;
- }
- if (*e != '\0') {
- *e++ = '\0';
- }
-
- /* Now add it to values if it isn't already represented.
- * Could be replaced by a ap_array_strcasecmp() if we had one.
- */
- for (i = 0, strpp = (char **) values->elts; i < values->nelts;
- ++i, ++strpp) {
- if (*strpp && strcasecmp(*strpp, start) == 0) {
- break;
- }
- }
- if (i == values->nelts) { /* if not found */
- *(char **)apr_array_push(values) = start;
- }
- } while (*e != '\0');
-
- return 1;
-}
-
-/*
- * Since some clients choke violently on multiple Vary fields, or
- * Vary fields with duplicate tokens, combine any multiples and remove
- * any duplicates.
- */
-static void fixup_vary(request_rec *r)
-{
- apr_array_header_t *varies;
-
- varies = apr_array_make(r->pool, 5, sizeof(char *));
-
- /* Extract all Vary fields from the headers_out, separate each into
- * its comma-separated fieldname values, and then add them to varies
- * if not already present in the array.
- */
- apr_table_do((int (*)(void *, const char *, const char *))uniq_field_values,
- (void *) varies, r->headers_out, "Vary", NULL);
-
- /* If we found any, replace old Vary fields with unique-ified value */
-
- if (varies->nelts > 0) {
- apr_table_setn(r->headers_out, "Vary",
- apr_array_pstrcat(r->pool, varies, ','));
- }
-}
-
AP_DECLARE(void) ap_set_content_type(request_rec *r, const char *ct)
{
if (!ct) {
@@ -1511,506 +820,6 @@
}
}
-typedef struct header_filter_ctx {
- int headers_sent;
-} header_filter_ctx;
-
-AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f,
- apr_bucket_brigade *b)
-{
- request_rec *r = f->r;
- conn_rec *c = r->connection;
- const char *clheader;
- const char *protocol;
- apr_bucket *e;
- apr_bucket_brigade *b2;
- header_struct h;
- header_filter_ctx *ctx = f->ctx;
-
- AP_DEBUG_ASSERT(!r->main);
-
- if (r->header_only) {
- if (!ctx) {
- ctx = f->ctx = apr_pcalloc(r->pool, sizeof(header_filter_ctx));
- }
- else if (ctx->headers_sent) {
- apr_brigade_destroy(b);
- return OK;
- }
- }
-
- for (e = APR_BRIGADE_FIRST(b);
- e != APR_BRIGADE_SENTINEL(b);
- e = APR_BUCKET_NEXT(e))
- {
- if (e->type == &ap_bucket_type_error) {
- ap_bucket_error *eb = e->data;
-
- ap_die(eb->status, r);
- return AP_FILTER_ERROR;
- }
- }
-
- if (r->assbackwards) {
- r->sent_bodyct = 1;
- ap_remove_output_filter(f);
- return ap_pass_brigade(f->next, b);
- }
-
- /*
- * Now that we are ready to send a response, we need to combine the two
- * header field tables into a single table. If we don't do this, our
- * later attempts to set or unset a given fieldname might be bypassed.
- */
- if (!apr_is_empty_table(r->err_headers_out)) {
- r->headers_out = apr_table_overlay(r->pool, r->err_headers_out,
- r->headers_out);
- }
-
- /*
- * Remove the 'Vary' header field if the client can't handle it.
- * Since this will have nasty effects on HTTP/1.1 caches, force
- * the response into HTTP/1.0 mode.
- *
- * Note: the force-response-1.0 should come before the call to
- * basic_http_header_check()
- */
- if (apr_table_get(r->subprocess_env, "force-no-vary") != NULL) {
- apr_table_unset(r->headers_out, "Vary");
- r->proto_num = HTTP_VERSION(1,0);
- apr_table_set(r->subprocess_env, "force-response-1.0", "1");
- }
- else {
- fixup_vary(r);
- }
-
- /*
- * Now remove any ETag response header field if earlier processing
- * says so (such as a 'FileETag None' directive).
- */
- if (apr_table_get(r->notes, "no-etag") != NULL) {
- apr_table_unset(r->headers_out, "ETag");
- }
-
- /* determine the protocol and whether we should use keepalives. */
- basic_http_header_check(r, &protocol);
- ap_set_keepalive(r);
-
- if (r->chunked) {
- apr_table_mergen(r->headers_out, "Transfer-Encoding", "chunked");
- apr_table_unset(r->headers_out, "Content-Length");
- }
-
- apr_table_setn(r->headers_out, "Content-Type",
- ap_make_content_type(r, r->content_type));
-
- if (r->content_encoding) {
- apr_table_setn(r->headers_out, "Content-Encoding",
- r->content_encoding);
- }
-
- if (!apr_is_empty_array(r->content_languages)) {
- int i;
- char **languages = (char **)(r->content_languages->elts);
- for (i = 0; i < r->content_languages->nelts; ++i) {
- apr_table_mergen(r->headers_out, "Content-Language", languages[i]);
- }
- }
-
- /*
- * Control cachability for non-cachable responses if not already set by
- * some other part of the server configuration.
- */
- if (r->no_cache && !apr_table_get(r->headers_out, "Expires")) {
- char *date = apr_palloc(r->pool, APR_RFC822_DATE_LEN);
- ap_recent_rfc822_date(date, r->request_time);
- apr_table_addn(r->headers_out, "Expires", date);
- }
-
- /* This is a hack, but I can't find anyway around it. The idea is that
- * we don't want to send out 0 Content-Lengths if it is a head request.
- * This happens when modules try to outsmart the server, and return
- * if they see a HEAD request. Apache 1.3 handlers were supposed to
- * just return in that situation, and the core handled the HEAD. In
- * 2.0, if a handler returns, then the core sends an EOS bucket down
- * the filter stack, and the content-length filter computes a C-L of
- * zero and that gets put in the headers, and we end up sending a
- * zero C-L to the client. We can't just remove the C-L filter,
- * because well behaved 2.0 handlers will send their data down the stack,
- * and we will compute a real C-L for the head request. RBB
- */
- if (r->header_only
- && (clheader = apr_table_get(r->headers_out, "Content-Length"))
- && !strcmp(clheader, "0")) {
- apr_table_unset(r->headers_out, "Content-Length");
- }
-
- b2 = apr_brigade_create(r->pool, c->bucket_alloc);
- basic_http_header(r, b2, protocol);
-
- h.pool = r->pool;
- h.bb = b2;
-
- if (r->status == HTTP_NOT_MODIFIED) {
- apr_table_do((int (*)(void *, const char *, const char *)) form_header_field,
- (void *) &h, r->headers_out,
- "Connection",
- "Keep-Alive",
- "ETag",
- "Content-Location",
- "Expires",
- "Cache-Control",
- "Vary",
- "Warning",
- "WWW-Authenticate",
- "Proxy-Authenticate",
- "Set-Cookie",
- "Set-Cookie2",
- NULL);
- }
- else {
- send_all_header_fields(&h, r);
- }
-
- terminate_header(b2);
-
- ap_pass_brigade(f->next, b2);
-
- if (r->header_only) {
- apr_brigade_destroy(b);
- ctx->headers_sent = 1;
- return OK;
- }
-
- r->sent_bodyct = 1; /* Whatever follows is real body stuff... */
-
- if (r->chunked) {
- /* We can't add this filter until we have already sent the headers.
- * If we add it before this point, then the headers will be chunked
- * as well, and that is just wrong.
- */
- ap_add_output_filter("CHUNK", NULL, r, r->connection);
- }
-
- /* Don't remove this filter until after we have added the CHUNK filter.
- * Otherwise, f->next won't be the CHUNK filter and thus the first
- * brigade won't be chunked properly.
- */
- ap_remove_output_filter(f);
- return ap_pass_brigade(f->next, b);
-}
-
-/* Here we deal with getting the request message body from the client.
- * Whether or not the request contains a body is signaled by the presence
- * of a non-zero Content-Length or by a Transfer-Encoding: chunked.
- *
- * Note that this is more complicated than it was in Apache 1.1 and prior
- * versions, because chunked support means that the module does less.
- *
- * The proper procedure is this:
- *
- * 1. Call setup_client_block() near the beginning of the request
- * handler. This will set up all the necessary properties, and will
- * return either OK, or an error code. If the latter, the module should
- * return that error code. The second parameter selects the policy to
- * apply if the request message indicates a body, and how a chunked
- * transfer-coding should be interpreted. Choose one of
- *
- * REQUEST_NO_BODY Send 413 error if message has any body
- * REQUEST_CHUNKED_ERROR Send 411 error if body without Content-Length
- * REQUEST_CHUNKED_DECHUNK If chunked, remove the chunks for me.
- *
- * In order to use the last two options, the caller MUST provide a buffer
- * large enough to hold a chunk-size line, including any extensions.
- *
- * 2. When you are ready to read a body (if any), call should_client_block().
- * This will tell the module whether or not to read input. If it is 0,
- * the module should assume that there is no message body to read.
- * This step also sends a 100 Continue response to HTTP/1.1 clients,
- * so should not be called until the module is *definitely* ready to
- * read content. (otherwise, the point of the 100 response is defeated).
- * Never call this function more than once.
- *
- * 3. Finally, call get_client_block in a loop. Pass it a buffer and its size.
- * It will put data into the buffer (not necessarily a full buffer), and
- * return the length of the input block. When it is done reading, it will
- * return 0 if EOF, or -1 if there was an error.
- * If an error occurs on input, we force an end to keepalive.
- */
-
-AP_DECLARE(int) ap_setup_client_block(request_rec *r, int read_policy)
-{
- const char *tenc = apr_table_get(r->headers_in, "Transfer-Encoding");
- const char *lenp = apr_table_get(r->headers_in, "Content-Length");
-
- r->read_body = read_policy;
- r->read_chunked = 0;
- r->remaining = 0;
-
- if (tenc) {
- if (strcasecmp(tenc, "chunked")) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "Unknown Transfer-Encoding %s", tenc);
- return HTTP_NOT_IMPLEMENTED;
- }
- if (r->read_body == REQUEST_CHUNKED_ERROR) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "chunked Transfer-Encoding forbidden: %s", r->uri);
- return (lenp) ? HTTP_BAD_REQUEST : HTTP_LENGTH_REQUIRED;
- }
-
- r->read_chunked = 1;
- }
- else if (lenp) {
- char *endstr;
-
- if (apr_strtoff(&r->remaining, lenp, &endstr, 10)
- || *endstr || r->remaining < 0) {
- r->remaining = 0;
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "Invalid Content-Length");
- return HTTP_BAD_REQUEST;
- }
- }
-
- if ((r->read_body == REQUEST_NO_BODY)
- && (r->read_chunked || (r->remaining > 0))) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "%s with body is not allowed for %s", r->method, r->uri);
- return HTTP_REQUEST_ENTITY_TOO_LARGE;
- }
-
-#ifdef AP_DEBUG
- {
- /* Make sure ap_getline() didn't leave any droppings. */
- core_request_config *req_cfg =
- (core_request_config *)ap_get_module_config(r->request_config,
- &core_module);
- AP_DEBUG_ASSERT(APR_BRIGADE_EMPTY(req_cfg->bb));
- }
-#endif
-
- return OK;
-}
-
-AP_DECLARE(int) ap_should_client_block(request_rec *r)
-{
- /* First check if we have already read the request body */
-
- if (r->read_length || (!r->read_chunked && (r->remaining <= 0))) {
- return 0;
- }
-
- return 1;
-}
-
-/**
- * Parse a chunk extension, detect overflow.
- * There are two error cases:
- * 1) If the conversion would require too many bits, a -1 is returned.
- * 2) If the conversion used the correct number of bits, but an overflow
- * caused only the sign bit to flip, then that negative number is
- * returned.
- * In general, any negative number can be considered an overflow error.
- */
-static long get_chunk_size(char *b)
-{
- long chunksize = 0;
- size_t chunkbits = sizeof(long) * 8;
-
- /* Skip leading zeros */
- while (*b == '0') {
- ++b;
- }
-
- while (apr_isxdigit(*b) && (chunkbits > 0)) {
- int xvalue = 0;
-
- if (*b >= '0' && *b <= '9') {
- xvalue = *b - '0';
- }
- else if (*b >= 'A' && *b <= 'F') {
- xvalue = *b - 'A' + 0xa;
- }
- else if (*b >= 'a' && *b <= 'f') {
- xvalue = *b - 'a' + 0xa;
- }
-
- chunksize = (chunksize << 4) | xvalue;
- chunkbits -= 4;
- ++b;
- }
- if (apr_isxdigit(*b) && (chunkbits <= 0)) {
- /* overflow */
- return -1;
- }
-
- return chunksize;
-}
-
-/* get_client_block is called in a loop to get the request message body.
- * This is quite simple if the client includes a content-length
- * (the normal case), but gets messy if the body is chunked. Note that
- * r->remaining is used to maintain state across calls and that
- * r->read_length is the total number of bytes given to the caller
- * across all invocations. It is messy because we have to be careful not
- * to read past the data provided by the client, since these reads block.
- * Returns 0 on End-of-body, -1 on error or premature chunk end.
- *
- */
-AP_DECLARE(long) ap_get_client_block(request_rec *r, char *buffer,
- apr_size_t bufsiz)
-{
- apr_status_t rv;
- apr_bucket_brigade *bb;
-
- if (r->remaining < 0 || (!r->read_chunked && r->remaining == 0)) {
- return 0;
- }
-
- bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
- if (bb == NULL) {
- r->connection->keepalive = AP_CONN_CLOSE;
- return -1;
- }
-
- rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES,
- APR_BLOCK_READ, bufsiz);
-
- /* We lose the failure code here. This is why ap_get_client_block should
- * not be used.
- */
- if (rv != APR_SUCCESS) {
- /* if we actually fail here, we want to just return and
- * stop trying to read data from the client.
- */
- r->connection->keepalive = AP_CONN_CLOSE;
- apr_brigade_destroy(bb);
- return -1;
- }
-
- /* If this fails, it means that a filter is written incorrectly and that
- * it needs to learn how to properly handle APR_BLOCK_READ requests by
- * returning data when requested.
- */
- AP_DEBUG_ASSERT(!APR_BRIGADE_EMPTY(bb));
-
- /* Check to see if EOS in the brigade.
- *
- * If so, we have to leave a nugget for the *next* ap_get_client_block
- * call to return 0.
- */
- if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) {
- if (r->read_chunked) {
- r->remaining = -1;
- }
- else {
- r->remaining = 0;
- }
- }
-
- rv = apr_brigade_flatten(bb, buffer, &bufsiz);
- if (rv != APR_SUCCESS) {
- apr_brigade_destroy(bb);
- return -1;
- }
-
- /* XXX yank me? */
- r->read_length += bufsiz;
-
- apr_brigade_destroy(bb);
- return bufsiz;
-}
-
-/* In HTTP/1.1, any method can have a body. However, most GET handlers
- * wouldn't know what to do with a request body if they received one.
- * This helper routine tests for and reads any message body in the request,
- * simply discarding whatever it receives. We need to do this because
- * failing to read the request body would cause it to be interpreted
- * as the next request on a persistent connection.
- *
- * Since we return an error status if the request is malformed, this
- * routine should be called at the beginning of a no-body handler, e.g.,
- *
- * if ((retval = ap_discard_request_body(r)) != OK) {
- * return retval;
- * }
- */
-AP_DECLARE(int) ap_discard_request_body(request_rec *r)
-{
- apr_bucket_brigade *bb;
- int rv, seen_eos;
-
- /* Sometimes we'll get in a state where the input handling has
- * detected an error where we want to drop the connection, so if
- * that's the case, don't read the data as that is what we're trying
- * to avoid.
- *
- * This function is also a no-op on a subrequest.
- */
- if (r->main || r->connection->keepalive == AP_CONN_CLOSE ||
- ap_status_drops_connection(r->status)) {
- return OK;
- }
-
- bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
- seen_eos = 0;
- do {
- apr_bucket *bucket;
-
- rv = ap_get_brigade(r->input_filters, bb, AP_MODE_READBYTES,
- APR_BLOCK_READ, HUGE_STRING_LEN);
-
- if (rv != APR_SUCCESS) {
- /* FIXME: If we ever have a mapping from filters (apr_status_t)
- * to HTTP error codes, this would be a good place for them.
- *
- * If we received the special case AP_FILTER_ERROR, it means
- * that the filters have already handled this error.
- * Otherwise, we should assume we have a bad request.
- */
- if (rv == AP_FILTER_ERROR) {
- apr_brigade_destroy(bb);
- return rv;
- }
- else {
- apr_brigade_destroy(bb);
- return HTTP_BAD_REQUEST;
- }
- }
-
- for (bucket = APR_BRIGADE_FIRST(bb);
- bucket != APR_BRIGADE_SENTINEL(bb);
- bucket = APR_BUCKET_NEXT(bucket))
- {
- const char *data;
- apr_size_t len;
-
- if (APR_BUCKET_IS_EOS(bucket)) {
- seen_eos = 1;
- break;
- }
-
- /* These are metadata buckets. */
- if (bucket->length == 0) {
- continue;
- }
-
- /* We MUST read because in case we have an unknown-length
- * bucket or one that morphs, we want to exhaust it.
- */
- rv = apr_bucket_read(bucket, &data, &len, APR_BLOCK_READ);
- if (rv != APR_SUCCESS) {
- apr_brigade_destroy(bb);
- return HTTP_BAD_REQUEST;
- }
- }
- apr_brigade_cleanup(bb);
- } while (!seen_eos);
-
- return OK;
-}
-
static const char *add_optional_notes(request_rec *r,
const char *prefix,
const char *key,
@@ -2594,527 +1403,3 @@
l->method_list->nelts = 0;
}
-/* Generate the human-readable hex representation of an unsigned long
- * (basically a faster version of 'sprintf("%lx")')
- */
-#define HEX_DIGITS "0123456789abcdef"
-static char *etag_ulong_to_hex(char *next, unsigned long u)
-{
- int printing = 0;
- int shift = sizeof(unsigned long) * 8 - 4;
- do {
- unsigned long next_digit = ((u >> shift) & (unsigned long)0xf);
- if (next_digit) {
- *next++ = HEX_DIGITS[next_digit];
- printing = 1;
- }
- else if (printing) {
- *next++ = HEX_DIGITS[next_digit];
- }
- shift -= 4;
- } while (shift);
- *next++ = HEX_DIGITS[u & (unsigned long)0xf];
- return next;
-}
-
-#define ETAG_WEAK "W/"
-#define CHARS_PER_UNSIGNED_LONG (sizeof(unsigned long) * 2)
-/*
- * Construct an entity tag (ETag) from resource information. If it's a real
- * file, build in some of the file characteristics. If the modification time
- * is newer than (request-time minus 1 second), mark the ETag as weak - it
- * could be modified again in as short an interval. We rationalize the
- * modification time we're given to keep it from being in the future.
- */
-AP_DECLARE(char *) ap_make_etag(request_rec *r, int force_weak)
-{
- char *weak;
- apr_size_t weak_len;
- char *etag;
- char *next;
- core_dir_config *cfg;
- etag_components_t etag_bits;
- etag_components_t bits_added;
-
- cfg = (core_dir_config *)ap_get_module_config(r->per_dir_config,
- &core_module);
- etag_bits = (cfg->etag_bits & (~ cfg->etag_remove)) | cfg->etag_add;
-
- /*
- * If it's a file (or we wouldn't be here) and no ETags
- * should be set for files, return an empty string and
- * note it for the header-sender to ignore.
- */
- if (etag_bits & ETAG_NONE) {
- apr_table_setn(r->notes, "no-etag", "omit");
- return "";
- }
-
- if (etag_bits == ETAG_UNSET) {
- etag_bits = ETAG_BACKWARD;
- }
- /*
- * Make an ETag header out of various pieces of information. We use
- * the last-modified date and, if we have a real file, the
- * length and inode number - note that this doesn't have to match
- * the content-length (i.e. includes), it just has to be unique
- * for the file.
- *
- * If the request was made within a second of the last-modified date,
- * we send a weak tag instead of a strong one, since it could
- * be modified again later in the second, and the validation
- * would be incorrect.
- */
- if ((r->request_time - r->mtime > (1 * APR_USEC_PER_SEC)) &&
- !force_weak) {
- weak = NULL;
- weak_len = 0;
- }
- else {
- weak = ETAG_WEAK;
- weak_len = sizeof(ETAG_WEAK);
- }
-
- if (r->finfo.filetype != 0) {
- /*
- * ETag gets set to [W/]"inode-size-mtime", modulo any
- * FileETag keywords.
- */
- etag = apr_palloc(r->pool, weak_len + sizeof("\"--\"") +
- 3 * CHARS_PER_UNSIGNED_LONG + 1);
- next = etag;
- if (weak) {
- while (*weak) {
- *next++ = *weak++;
- }
- }
- *next++ = '"';
- bits_added = 0;
- if (etag_bits & ETAG_INODE) {
- next = etag_ulong_to_hex(next, (unsigned long)r->finfo.inode);
- bits_added |= ETAG_INODE;
- }
- if (etag_bits & ETAG_SIZE) {
- if (bits_added != 0) {
- *next++ = '-';
- }
- next = etag_ulong_to_hex(next, (unsigned long)r->finfo.size);
- bits_added |= ETAG_SIZE;
- }
- if (etag_bits & ETAG_MTIME) {
- if (bits_added != 0) {
- *next++ = '-';
- }
- next = etag_ulong_to_hex(next, (unsigned long)r->mtime);
- }
- *next++ = '"';
- *next = '\0';
- }
- else {
- /*
- * Not a file document, so just use the mtime: [W/]"mtime"
- */
- etag = apr_palloc(r->pool, weak_len + sizeof("\"\"") +
- CHARS_PER_UNSIGNED_LONG + 1);
- next = etag;
- if (weak) {
- while (*weak) {
- *next++ = *weak++;
- }
- }
- *next++ = '"';
- next = etag_ulong_to_hex(next, (unsigned long)r->mtime);
- *next++ = '"';
- *next = '\0';
- }
-
- return etag;
-}
-
-AP_DECLARE(void) ap_set_etag(request_rec *r)
-{
- char *etag;
- char *variant_etag, *vlv;
- int vlv_weak;
-
- if (!r->vlist_validator) {
- etag = ap_make_etag(r, 0);
-
- /* If we get a blank etag back, don't set the header. */
- if (!etag[0]) {
- return;
- }
- }
- else {
- /* If we have a variant list validator (vlv) due to the
- * response being negotiated, then we create a structured
- * entity tag which merges the variant etag with the variant
- * list validator (vlv). This merging makes revalidation
- * somewhat safer, ensures that caches which can deal with
- * Vary will (eventually) be updated if the set of variants is
- * changed, and is also a protocol requirement for transparent
- * content negotiation.
- */
-
- /* if the variant list validator is weak, we make the whole
- * structured etag weak. If we would not, then clients could
- * have problems merging range responses if we have different
- * variants with the same non-globally-unique strong etag.
- */
-
- vlv = r->vlist_validator;
- vlv_weak = (vlv[0] == 'W');
-
- variant_etag = ap_make_etag(r, vlv_weak);
-
- /* If we get a blank etag back, don't append vlv and stop now. */
- if (!variant_etag[0]) {
- return;
- }
-
- /* merge variant_etag and vlv into a structured etag */
- variant_etag[strlen(variant_etag) - 1] = '\0';
- if (vlv_weak) {
- vlv += 3;
- }
- else {
- vlv++;
- }
- etag = apr_pstrcat(r->pool, variant_etag, ";", vlv, NULL);
- }
-
- apr_table_setn(r->headers_out, "ETag", etag);
-}
-
-static int parse_byterange(char *range, apr_off_t clength,
- apr_off_t *start, apr_off_t *end)
-{
- char *dash = strchr(range, '-');
- char *errp;
- apr_off_t number;
-
- if (!dash) {
- return 0;
- }
-
- if ((dash == range)) {
- /* In the form "-5" */
- if (apr_strtoff(&number, dash+1, &errp, 10) || *errp) {
- return 0;
- }
- *start = clength - number;
- *end = clength - 1;
- }
- else {
- *dash++ = '\0';
- if (apr_strtoff(&number, range, &errp, 10) || *errp) {
- return 0;
- }
- *start = number;
- if (*dash) {
- if (apr_strtoff(&number, dash, &errp, 10) || *errp) {
- return 0;
- }
- *end = number;
- }
- else { /* "5-" */
- *end = clength - 1;
- }
- }
-
- if (*start < 0) {
- *start = 0;
- }
-
- if (*end >= clength) {
- *end = clength - 1;
- }
-
- if (*start > *end) {
- return -1;
- }
-
- return (*start > 0 || *end < clength);
-}
-
-static int ap_set_byterange(request_rec *r);
-
-typedef struct byterange_ctx {
- apr_bucket_brigade *bb;
- int num_ranges;
- char *boundary;
- char *bound_head;
-} byterange_ctx;
-
-/*
- * Here we try to be compatible with clients that want multipart/x-byteranges
- * instead of multipart/byteranges (also see above), as per HTTP/1.1. We
- * look for the Request-Range header (e.g. Netscape 2 and 3) as an indication
- * that the browser supports an older protocol. We also check User-Agent
- * for Microsoft Internet Explorer 3, which needs this as well.
- */
-static int use_range_x(request_rec *r)
-{
- const char *ua;
- return (apr_table_get(r->headers_in, "Request-Range")
- || ((ua = apr_table_get(r->headers_in, "User-Agent"))
- && ap_strstr_c(ua, "MSIE 3")));
-}
-
-#define BYTERANGE_FMT "%" APR_OFF_T_FMT "-%" APR_OFF_T_FMT "/%" APR_OFF_T_FMT
-#define PARTITION_ERR_FMT "apr_brigade_partition() failed " \
- "[%" APR_OFF_T_FMT ",%" APR_OFF_T_FMT "]"
-
-AP_CORE_DECLARE_NONSTD(apr_status_t) ap_byterange_filter(ap_filter_t *f,
- apr_bucket_brigade *bb)
-{
-#define MIN_LENGTH(len1, len2) ((len1 > len2) ? len2 : len1)
- request_rec *r = f->r;
- conn_rec *c = r->connection;
- byterange_ctx *ctx = f->ctx;
- apr_bucket *e;
- apr_bucket_brigade *bsend;
- apr_off_t range_start;
- apr_off_t range_end;
- char *current;
- apr_off_t bb_length;
- apr_off_t clength = 0;
- apr_status_t rv;
- int found = 0;
-
- if (!ctx) {
- int num_ranges = ap_set_byterange(r);
-
- /* We have nothing to do, get out of the way. */
- if (num_ranges == 0) {
- ap_remove_output_filter(f);
- return ap_pass_brigade(f->next, bb);
- }
-
- ctx = f->ctx = apr_pcalloc(r->pool, sizeof(*ctx));
- ctx->num_ranges = num_ranges;
- /* create a brigade in case we never call ap_save_brigade() */
- ctx->bb = apr_brigade_create(r->pool, c->bucket_alloc);
-
- if (ctx->num_ranges > 1) {
- /* Is ap_make_content_type required here? */
- const char *orig_ct = ap_make_content_type(r, r->content_type);
- ctx->boundary = apr_psprintf(r->pool, "%" APR_UINT64_T_HEX_FMT "%lx",
- (apr_uint64_t)r->request_time, (long) getpid());
-
- ap_set_content_type(r, apr_pstrcat(r->pool, "multipart",
- use_range_x(r) ? "/x-" : "/",
- "byteranges; boundary=",
- ctx->boundary, NULL));
-
- ctx->bound_head = apr_pstrcat(r->pool,
- CRLF "--", ctx->boundary,
- CRLF "Content-type: ",
- orig_ct,
- CRLF "Content-range: bytes ",
- NULL);
- ap_xlate_proto_to_ascii(ctx->bound_head, strlen(ctx->bound_head));
- }
- }
-
- /* We can't actually deal with byte-ranges until we have the whole brigade
- * because the byte-ranges can be in any order, and according to the RFC,
- * we SHOULD return the data in the same order it was requested.
- *
- * XXX: We really need to dump all bytes prior to the start of the earliest
- * range, and only slurp up to the end of the latest range. By this we
- * mean that we should peek-ahead at the lowest first byte of any range,
- * and the highest last byte of any range.
- */
- if (!APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) {
- ap_save_brigade(f, &ctx->bb, &bb, r->pool);
- return APR_SUCCESS;
- }
-
- /* Prepend any earlier saved brigades. */
- APR_BRIGADE_PREPEND(bb, ctx->bb);
-
- /* It is possible that we won't have a content length yet, so we have to
- * compute the length before we can actually do the byterange work.
- */
- apr_brigade_length(bb, 1, &bb_length);
- clength = (apr_off_t)bb_length;
-
- /* this brigade holds what we will be sending */
- bsend = apr_brigade_create(r->pool, c->bucket_alloc);
-
- while ((current = ap_getword(r->pool, &r->range, ','))
- && (rv = parse_byterange(current, clength, &range_start,
- &range_end))) {
- apr_bucket *e2;
- apr_bucket *ec;
-
- if (rv == -1) {
- continue;
- }
-
- /* these calls to apr_brigade_partition() should theoretically
- * never fail because of the above call to apr_brigade_length(),
- * but what the heck, we'll check for an error anyway */
- if ((rv = apr_brigade_partition(bb, range_start, &ec)) != APR_SUCCESS) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
- PARTITION_ERR_FMT, range_start, clength);
- continue;
- }
- if ((rv = apr_brigade_partition(bb, range_end+1, &e2)) != APR_SUCCESS) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
- PARTITION_ERR_FMT, range_end+1, clength);
- continue;
- }
-
- found = 1;
-
- /* For single range requests, we must produce Content-Range header.
- * Otherwise, we need to produce the multipart boundaries.
- */
- if (ctx->num_ranges == 1) {
- apr_table_setn(r->headers_out, "Content-Range",
- apr_psprintf(r->pool, "bytes " BYTERANGE_FMT,
- range_start, range_end, clength));
- }
- else {
- char *ts;
-
- e = apr_bucket_pool_create(ctx->bound_head, strlen(ctx->bound_head),
- r->pool, c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bsend, e);
-
- ts = apr_psprintf(r->pool, BYTERANGE_FMT CRLF CRLF,
- range_start, range_end, clength);
- ap_xlate_proto_to_ascii(ts, strlen(ts));
- e = apr_bucket_pool_create(ts, strlen(ts), r->pool,
- c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bsend, e);
- }
-
- do {
- apr_bucket *foo;
- const char *str;
- apr_size_t len;
-
- if (apr_bucket_copy(ec, &foo) != APR_SUCCESS) {
- /* this shouldn't ever happen due to the call to
- * apr_brigade_length() above which normalizes
- * indeterminate-length buckets. just to be sure,
- * though, this takes care of uncopyable buckets that
- * do somehow manage to slip through.
- */
- /* XXX: check for failure? */
- apr_bucket_read(ec, &str, &len, APR_BLOCK_READ);
- apr_bucket_copy(ec, &foo);
- }
- APR_BRIGADE_INSERT_TAIL(bsend, foo);
- ec = APR_BUCKET_NEXT(ec);
- } while (ec != e2);
- }
-
- if (found == 0) {
- ap_remove_output_filter(f);
- r->status = HTTP_OK;
- /* bsend is assumed to be empty if we get here. */
- e = ap_bucket_error_create(HTTP_RANGE_NOT_SATISFIABLE, NULL,
- r->pool, c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bsend, e);
- e = apr_bucket_eos_create(c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bsend, e);
- return ap_pass_brigade(f->next, bsend);
- }
-
- if (ctx->num_ranges > 1) {
- char *end;
-
- /* add the final boundary */
- end = apr_pstrcat(r->pool, CRLF "--", ctx->boundary, "--" CRLF, NULL);
- ap_xlate_proto_to_ascii(end, strlen(end));
- e = apr_bucket_pool_create(end, strlen(end), r->pool, c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bsend, e);
- }
-
- e = apr_bucket_eos_create(c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bsend, e);
-
- /* we're done with the original content - all of our data is in bsend. */
- apr_brigade_destroy(bb);
-
- /* send our multipart output */
- return ap_pass_brigade(f->next, bsend);
-}
-
-static int ap_set_byterange(request_rec *r)
-{
- const char *range;
- const char *if_range;
- const char *match;
- const char *ct;
- int num_ranges;
-
- if (r->assbackwards) {
- return 0;
- }
-
- /* Check for Range request-header (HTTP/1.1) or Request-Range for
- * backwards-compatibility with second-draft Luotonen/Franks
- * byte-ranges (e.g. Netscape Navigator 2-3).
- *
- * We support this form, with Request-Range, and (farther down) we
- * send multipart/x-byteranges instead of multipart/byteranges for
- * Request-Range based requests to work around a bug in Netscape
- * Navigator 2-3 and MSIE 3.
- */
-
- if (!(range = apr_table_get(r->headers_in, "Range"))) {
- range = apr_table_get(r->headers_in, "Request-Range");
- }
-
- if (!range || strncasecmp(range, "bytes=", 6) || r->status != HTTP_OK) {
- return 0;
- }
-
- /* is content already a single range? */
- if (apr_table_get(r->headers_out, "Content-Range")) {
- return 0;
- }
-
- /* is content already a multiple range? */
- if ((ct = apr_table_get(r->headers_out, "Content-Type"))
- && (!strncasecmp(ct, "multipart/byteranges", 20)
- || !strncasecmp(ct, "multipart/x-byteranges", 22))) {
- return 0;
- }
-
- /* Check the If-Range header for Etag or Date.
- * Note that this check will return false (as required) if either
- * of the two etags are weak.
- */
- if ((if_range = apr_table_get(r->headers_in, "If-Range"))) {
- if (if_range[0] == '"') {
- if (!(match = apr_table_get(r->headers_out, "Etag"))
- || (strcmp(if_range, match) != 0)) {
- return 0;
- }
- }
- else if (!(match = apr_table_get(r->headers_out, "Last-Modified"))
- || (strcmp(if_range, match) != 0)) {
- return 0;
- }
- }
-
- if (!ap_strchr_c(range, ',')) {
- /* a single range */
- num_ranges = 1;
- }
- else {
- /* a multiple range */
- num_ranges = 2;
- }
-
- r->status = HTTP_PARTIAL_CONTENT;
- r->range = range + 6;
-
- return num_ranges;
-}
Modified: httpd/httpd/trunk/server/Makefile.in
Url: http://svn.apache.org/viewcvs/httpd/httpd/trunk/server/Makefile.in?view=diff&rev=106692&p1=httpd/httpd/trunk/server/Makefile.in&r1=106691&p2=httpd/httpd/trunk/server/Makefile.in&r2=106692
==============================================================================
--- httpd/httpd/trunk/server/Makefile.in (original)
+++ httpd/httpd/trunk/server/Makefile.in Sat Nov 27 00:07:44 2004
@@ -14,7 +14,7 @@
mpm_common.c util_charset.c util_debug.c util_xml.c \
util_filter.c exports.c buildmark.c \
scoreboard.c error_bucket.c protocol.c core.c request.c provider.c \
- eoc_bucket.c
+ eoc_bucket.c core_filters.c
TARGETS = delete-exports $(LTLIBRARY_NAME) $(CORE_IMPLIB_FILE) export_vars.h httpd.exp
Modified: httpd/httpd/trunk/server/core.c
Url: http://svn.apache.org/viewcvs/httpd/httpd/trunk/server/core.c?view=diff&rev=106692&p1=httpd/httpd/trunk/server/core.c&r1=106691&p2=httpd/httpd/trunk/server/core.c&r2=106692
==============================================================================
--- httpd/httpd/trunk/server/core.c (original)
+++ httpd/httpd/trunk/server/core.c Sat Nov 27 00:07:44 2004
@@ -93,6 +93,12 @@
AP_DECLARE_DATA ap_filter_rec_t *ap_net_time_filter_handle;
AP_DECLARE_DATA ap_filter_rec_t *ap_core_input_filter_handle;
+extern int core_input_filter(ap_filter_t *, apr_bucket_brigade *,
+ ap_input_mode_t, apr_read_type_e, apr_off_t);
+extern int net_time_filter(ap_filter_t *, apr_bucket_brigade *,
+ ap_input_mode_t, apr_read_type_e, apr_off_t);
+extern apr_status_t core_output_filter(ap_filter_t *, apr_bucket_brigade *);
+
/* magic pointer for ErrorDocument xxx "default" */
static char errordocument_default;
@@ -3008,218 +3014,6 @@
return;
}
-static apr_status_t writev_it_all(apr_socket_t *s,
- struct iovec *vec, int nvec,
- apr_size_t len, apr_size_t *nbytes)
-{
- apr_size_t bytes_written = 0;
- apr_status_t rv;
- apr_size_t n = len;
- int i = 0;
-
- *nbytes = 0;
-
- /* XXX handle checking for non-blocking socket */
- while (bytes_written != len) {
- rv = apr_socket_sendv(s, vec + i, nvec - i, &n);
- *nbytes += n;
- bytes_written += n;
- if (rv != APR_SUCCESS)
- return rv;
-
- /* If the write did not complete, adjust the iovecs and issue
- * apr_socket_sendv again
- */
- if (bytes_written < len) {
- /* Skip over the vectors that have already been written */
- apr_size_t cnt = vec[i].iov_len;
- while (n >= cnt && i + 1 < nvec) {
- i++;
- cnt += vec[i].iov_len;
- }
-
- if (n < cnt) {
- /* Handle partial write of vec i */
- vec[i].iov_base = (char *) vec[i].iov_base +
- (vec[i].iov_len - (cnt - n));
- vec[i].iov_len = cnt -n;
- }
- }
-
- n = len - bytes_written;
- }
-
- return APR_SUCCESS;
-}
-
-/* sendfile_it_all()
- * send the entire file using sendfile()
- * handle partial writes
- * return only when all bytes have been sent or an error is encountered.
- */
-
-#if APR_HAS_SENDFILE
-static apr_status_t sendfile_it_all(core_net_rec *c,
- apr_file_t *fd,
- apr_hdtr_t *hdtr,
- 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_interval_time_t timeout = 0;
-#endif
-
- 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_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 */
- }
-
- AP_DEBUG_ASSERT(total_bytes_left > 0 && tmplen > 0);
-
- /* partial write, oooh noooo...
- * Skip over any header data which was written
- */
- while (tmplen && hdtr->numheaders) {
- if (tmplen >= hdtr->headers[0].iov_len) {
- tmplen -= hdtr->headers[0].iov_len;
- --hdtr->numheaders;
- ++hdtr->headers;
- }
- else {
- char *iov_base = (char *)hdtr->headers[0].iov_base;
-
- hdtr->headers[0].iov_len -= tmplen;
- iov_base += tmplen;
- hdtr->headers[0].iov_base = iov_base;
- tmplen = 0;
- }
- }
-
- /* Skip over any file data which was written */
-
- if (tmplen <= file_bytes_left) {
- file_offset += tmplen;
- file_bytes_left -= tmplen;
- continue;
- }
-
- tmplen -= file_bytes_left;
- file_bytes_left = 0;
- file_offset = 0;
-
- /* Skip over any trailer data which was written */
-
- while (tmplen && hdtr->numtrailers) {
- if (tmplen >= hdtr->trailers[0].iov_len) {
- tmplen -= hdtr->trailers[0].iov_len;
- --hdtr->numtrailers;
- ++hdtr->trailers;
- }
- else {
- char *iov_base = (char *)hdtr->trailers[0].iov_base;
-
- hdtr->trailers[0].iov_len -= tmplen;
- iov_base += tmplen;
- hdtr->trailers[0].iov_base = iov_base;
- tmplen = 0;
- }
- }
- } while (1);
-}
-#endif
-
-/*
- * emulate_sendfile()
- * Sends the contents of file fd along with header/trailer bytes, if any,
- * to the network. emulate_sendfile will return only when all the bytes have been
- * sent (i.e., it handles partial writes) or on a network error condition.
- */
-static apr_status_t emulate_sendfile(core_net_rec *c, apr_file_t *fd,
- apr_hdtr_t *hdtr, apr_off_t offset,
- apr_size_t length, apr_size_t *nbytes)
-{
- apr_status_t rv = APR_SUCCESS;
- apr_size_t togo; /* Remaining number of bytes in the file to send */
- apr_size_t sendlen = 0;
- apr_size_t bytes_sent;
- apr_int32_t i;
- apr_off_t o; /* Track the file offset for partial writes */
- char buffer[8192];
-
- *nbytes = 0;
-
- /* Send the headers
- * writev_it_all handles partial writes.
- * XXX: optimization... if headers are less than MIN_WRITE_SIZE, copy
- * them into buffer
- */
- if (hdtr && hdtr->numheaders > 0 ) {
- for (i = 0; i < hdtr->numheaders; i++) {
- sendlen += hdtr->headers[i].iov_len;
- }
-
- rv = writev_it_all(c->client_socket, hdtr->headers, hdtr->numheaders,
- sendlen, &bytes_sent);
- *nbytes += bytes_sent; /* track total bytes sent */
- }
-
- /* Seek the file to 'offset' */
- if (offset >= 0 && rv == APR_SUCCESS) {
- rv = apr_file_seek(fd, APR_SET, &offset);
- }
-
- /* Send the file, making sure to handle partial writes */
- togo = length;
- while (rv == APR_SUCCESS && togo) {
- sendlen = togo > sizeof(buffer) ? sizeof(buffer) : togo;
- o = 0;
- rv = apr_file_read(fd, buffer, &sendlen);
- while (rv == APR_SUCCESS && sendlen) {
- bytes_sent = sendlen;
- rv = apr_socket_send(c->client_socket, &buffer[o], &bytes_sent);
- *nbytes += 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 */
- togo -= bytes_sent; /* track how much of the file we've sent */
- }
- }
- }
-
- /* Send the trailers
- * XXX: optimization... if it will fit, send this on the last send in the
- * loop above
- */
- sendlen = 0;
- if ( rv == APR_SUCCESS && hdtr && hdtr->numtrailers > 0 ) {
- for (i = 0; i < hdtr->numtrailers; i++) {
- sendlen += hdtr->trailers[i].iov_len;
- }
- rv = writev_it_all(c->client_socket, hdtr->trailers, hdtr->numtrailers,
- sendlen, &bytes_sent);
- *nbytes += bytes_sent;
- }
-
- return rv;
-}
-
/* Note --- ErrorDocument will now work from .htaccess files.
* The AllowOverride of Fileinfo allows webmasters to turn it off
*/
@@ -3574,8 +3368,6 @@
return OK;
}
-
-
static int default_handler(request_rec *r)
{
conn_rec *c = r->connection;
@@ -3733,664 +3525,10 @@
}
}
-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;
-
- 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 (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_socket_timeout_set(ctx->csd, f->c->base_server->timeout);
- }
- }
- }
- return ap_get_brigade(f->next, b, mode, block, readbytes);
-}
-
-/**
- * Remove all zero length buckets from the brigade.
- */
-#define BRIGADE_NORMALIZE(b) \
-do { \
- apr_bucket *e = APR_BRIGADE_FIRST(b); \
- do { \
- if (e->length == 0 && !APR_BUCKET_IS_METADATA(e)) { \
- apr_bucket *d; \
- d = APR_BUCKET_NEXT(e); \
- apr_bucket_delete(e); \
- e = d; \
- } \
- e = APR_BUCKET_NEXT(e); \
- } while (!APR_BRIGADE_EMPTY(b) && (e != APR_BRIGADE_SENTINEL(b))); \
-} while (0)
-
-static int core_input_filter(ap_filter_t *f, apr_bucket_brigade *b,
- ap_input_mode_t mode, apr_read_type_e block,
- apr_off_t readbytes)
-{
- apr_bucket *e;
- apr_status_t rv;
- core_net_rec *net = f->ctx;
- core_ctx_t *ctx = net->in_ctx;
- const char *str;
- apr_size_t len;
-
- if (mode == AP_MODE_INIT) {
- /*
- * this mode is for filters that might need to 'initialize'
- * a connection before reading request data from a client.
- * NNTP over SSL for example needs to handshake before the
- * server sends the welcome message.
- * such filters would have changed the mode before this point
- * is reached. however, protocol modules such as NNTP should
- * not need to know anything about SSL. given the example, if
- * SSL is not in the filter chain, AP_MODE_INIT is a noop.
- */
- return APR_SUCCESS;
- }
-
- if (!ctx)
- {
- ctx = apr_pcalloc(f->c->pool, sizeof(*ctx));
- ctx->b = apr_brigade_create(f->c->pool, f->c->bucket_alloc);
-
- /* seed the brigade with the client socket. */
- e = apr_bucket_socket_create(net->client_socket, f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(ctx->b, e);
- net->in_ctx = ctx;
- }
- else if (APR_BRIGADE_EMPTY(ctx->b)) {
- return APR_EOF;
- }
-
- /* ### This is bad. */
- BRIGADE_NORMALIZE(ctx->b);
-
- /* check for empty brigade again *AFTER* BRIGADE_NORMALIZE()
- * If we have lost our socket bucket (see above), we are EOF.
- *
- * Ideally, this should be returning SUCCESS with EOS bucket, but
- * some higher-up APIs (spec. read_request_line via ap_rgetline)
- * want an error code. */
- if (APR_BRIGADE_EMPTY(ctx->b)) {
- 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. */
- if (mode == AP_MODE_EATCRLF) {
- apr_bucket *e;
- const char *c;
-
- /* The purpose of this loop is to ignore any CRLF (or LF) at the end
- * of a request. Many browsers send extra lines at the end of POST
- * requests. We use the PEEK method to determine if there is more
- * data on the socket, so that we know if we should delay sending the
- * end of one request until we have served the second request in a
- * pipelined situation. We don't want to actually delay sending a
- * response if the server finds a CRLF (or LF), becuause that doesn't
- * mean that there is another request, just a blank line.
- */
- while (1) {
- if (APR_BRIGADE_EMPTY(ctx->b))
- return APR_EOF;
-
- e = APR_BRIGADE_FIRST(ctx->b);
-
- rv = apr_bucket_read(e, &str, &len, APR_NONBLOCK_READ);
-
- if (rv != APR_SUCCESS)
- return rv;
-
- c = str;
- while (c < str + len) {
- if (*c == APR_ASCII_LF)
- c++;
- else if (*c == APR_ASCII_CR && *(c + 1) == APR_ASCII_LF)
- c += 2;
- else
- return APR_SUCCESS;
- }
-
- /* If we reach here, we were a bucket just full of CRLFs, so
- * just toss the bucket. */
- /* 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
- * of the brigade, which in this case means the end of the socket.
- * To do this, we attach the brigade that has currently been setaside to
- * the brigade that was passed down, and send that brigade back.
- *
- * NOTE: This is VERY dangerous to use, and should only be done with
- * extreme caution. However, the Perchild MPM needs this feature
- * if it is ever going to work correctly again. With this, the Perchild
- * MPM can easily request the socket and all data that has been read,
- * which means that it can pass it to the correct child process.
- */
- if (mode == AP_MODE_EXHAUSTIVE) {
- apr_bucket *e;
-
- /* Tack on any buckets that were set aside. */
- APR_BRIGADE_CONCAT(b, ctx->b);
-
- /* Since we've just added all potential buckets (which will most
- * likely simply be the socket bucket) we know this is the end,
- * so tack on an EOS too. */
- /* We have read until the brigade was empty, so we know that we
- * must be EOS. */
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(b, e);
- return APR_SUCCESS;
- }
-
- /* read up to the amount they specified. */
- if (mode == AP_MODE_READBYTES || mode == AP_MODE_SPECULATIVE) {
- apr_bucket *e;
- apr_bucket_brigade *newbb;
-
- AP_DEBUG_ASSERT(readbytes > 0);
-
- e = APR_BRIGADE_FIRST(ctx->b);
- rv = apr_bucket_read(e, &str, &len, block);
-
- if (APR_STATUS_IS_EAGAIN(rv)) {
- return APR_SUCCESS;
- }
- else if (rv != APR_SUCCESS) {
- return rv;
- }
- else if (block == APR_BLOCK_READ && len == 0) {
- /* We wanted to read some bytes in blocking mode. We read
- * 0 bytes. Hence, we now assume we are EOS.
- *
- * When we are in normal mode, return an EOS bucket to the
- * caller.
- * When we are in speculative mode, leave ctx->b empty, so
- * that the next call returns an EOS bucket.
- */
- apr_bucket_delete(e);
-
- if (mode == AP_MODE_READBYTES) {
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(b, e);
- }
- return APR_SUCCESS;
- }
-
- /* We can only return at most what we read. */
- if (len < readbytes) {
- readbytes = len;
- }
-
- rv = apr_brigade_partition(ctx->b, readbytes, &e);
- if (rv != APR_SUCCESS) {
- return rv;
- }
-
- /* Must do split before CONCAT */
- newbb = apr_brigade_split(ctx->b, e);
-
- if (mode == AP_MODE_READBYTES) {
- APR_BRIGADE_CONCAT(b, ctx->b);
- }
- else if (mode == AP_MODE_SPECULATIVE) {
- apr_bucket *copy_bucket;
-
- for (e = APR_BRIGADE_FIRST(ctx->b);
- e != APR_BRIGADE_SENTINEL(ctx->b);
- e = APR_BUCKET_NEXT(e))
- {
- rv = apr_bucket_copy(e, ©_bucket);
- if (rv != APR_SUCCESS) {
- return rv;
- }
- APR_BRIGADE_INSERT_TAIL(b, copy_bucket);
- }
- }
-
- /* Take what was originally there and place it back on ctx->b */
- APR_BRIGADE_CONCAT(ctx->b, newbb);
- }
- return APR_SUCCESS;
-}
-
-#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));
- net->out_ctx = ctx;
- }
-
- /* If we have a saved brigade, concatenate the new brigade to it */
- if (ctx->b) {
- APR_BRIGADE_CONCAT(ctx->b, b);
- b = ctx->b;
- ctx->b = NULL;
- }
-
- /* Perform multiple passes over the brigade, sending batches of output
- to the connection. */
- while (b && !APR_BRIGADE_EMPTY(b)) {
- apr_size_t nbytes = 0;
- apr_bucket *last_e = NULL; /* initialized for debugging */
- apr_bucket *e;
-
- /* one group of iovecs per pass over the brigade */
- apr_size_t nvec = 0;
- apr_size_t nvec_trailers = 0;
- struct iovec vec[MAX_IOVEC_TO_WRITE];
- struct iovec vec_trailers[MAX_IOVEC_TO_WRITE];
-
- /* one file per pass over the brigade */
- apr_file_t *fd = NULL;
- apr_size_t flen = 0;
- apr_off_t foffset = 0;
-
- /* keep track of buckets that we've concatenated
- * to avoid small writes
- */
- 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 */
- for (e = APR_BRIGADE_FIRST(b);
- e != APR_BRIGADE_SENTINEL(b);
- e = APR_BUCKET_NEXT(e))
- {
- /* keep track of the last bucket processed */
- last_e = e;
- if (APR_BUCKET_IS_EOS(e) || AP_BUCKET_IS_EOC(e)) {
- break;
- }
- else if (APR_BUCKET_IS_FLUSH(e)) {
- if (e != APR_BRIGADE_LAST(b)) {
- more = apr_brigade_split(b, APR_BUCKET_NEXT(e));
- }
- break;
- }
-
- /* It doesn't make any sense to use sendfile for a file bucket
- * that represents 10 bytes.
- */
- else if (APR_BUCKET_IS_FILE(e)
- && (e->length >= AP_MIN_SENDFILE_BYTES)) {
- apr_bucket_file *a = e->data;
-
- /* We can't handle more than one file bucket at a time
- * so we split here and send the file we have already
- * found.
- */
- if (fd) {
- more = apr_brigade_split(b, e);
- break;
- }
-
- fd = a->fd;
- flen = e->length;
- foffset = e->start;
- }
- else {
- const char *str;
- apr_size_t n;
-
- 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) {
- /* woah! too many. buffer them up, for use later. */
- apr_bucket *temp, *next;
- apr_bucket_brigade *temp_brig;
-
- if (nbytes >= AP_MIN_BYTES_TO_WRITE) {
- /* We have enough data in the iovec
- * to justify doing a writev
- */
- more = apr_brigade_split(b, e);
- break;
- }
-
- /* Create a temporary brigade as a means
- * of concatenating a bunch of buckets together
- */
- if (last_merged_bucket) {
- /* If we've concatenated together small
- * buckets already in a previous pass,
- * the initial buckets in this brigade
- * are heap buckets that may have extra
- * space left in them (because they
- * were created by apr_brigade_write()).
- * We can take advantage of this by
- * building the new temp brigade out of
- * these buckets, so that the content
- * in them doesn't have to be copied again.
- */
- apr_bucket_brigade *bb;
- bb = apr_brigade_split(b,
- APR_BUCKET_NEXT(last_merged_bucket));
- temp_brig = b;
- b = bb;
- }
- else {
- temp_brig = apr_brigade_create(f->c->pool,
- f->c->bucket_alloc);
- }
-
- temp = APR_BRIGADE_FIRST(b);
- while (temp != e) {
- apr_bucket *d;
- rv = apr_bucket_read(temp, &str, &n, APR_BLOCK_READ);
- apr_brigade_write(temp_brig, NULL, NULL, str, n);
- d = temp;
- temp = APR_BUCKET_NEXT(temp);
- apr_bucket_delete(d);
- }
-
- nvec = 0;
- nbytes = 0;
- temp = APR_BRIGADE_FIRST(temp_brig);
- APR_BUCKET_REMOVE(temp);
- APR_BRIGADE_INSERT_HEAD(b, temp);
- apr_bucket_read(temp, &str, &n, APR_BLOCK_READ);
- vec[nvec].iov_base = (char*) str;
- vec[nvec].iov_len = n;
- nvec++;
-
- /* Just in case the temporary brigade has
- * multiple buckets, recover the rest of
- * them and put them in the brigade that
- * we're sending.
- */
- for (next = APR_BRIGADE_FIRST(temp_brig);
- next != APR_BRIGADE_SENTINEL(temp_brig);
- next = APR_BRIGADE_FIRST(temp_brig)) {
- APR_BUCKET_REMOVE(next);
- APR_BUCKET_INSERT_AFTER(temp, next);
- temp = next;
- apr_bucket_read(next, &str, &n,
- APR_BLOCK_READ);
- vec[nvec].iov_base = (char*) str;
- vec[nvec].iov_len = n;
- nvec++;
- }
-
- apr_brigade_destroy(temp_brig);
-
- last_merged_bucket = temp;
- e = temp;
- last_e = e;
- }
- else {
- vec[nvec].iov_base = (char*) str;
- vec[nvec].iov_len = n;
- nvec++;
- }
- }
- else {
- /* The bucket is a trailer to a file bucket */
-
- if (nvec_trailers == MAX_IOVEC_TO_WRITE) {
- /* woah! too many. stop now. */
- more = apr_brigade_split(b, e);
- break;
- }
-
- vec_trailers[nvec_trailers].iov_base = (char*) str;
- vec_trailers[nvec_trailers].iov_len = n;
- nvec_trailers++;
- }
-
- nbytes += n;
- }
- }
- }
-
-
- /* Completed iterating over the brigade, now determine if we want
- * to buffer the brigade or send the brigade out on the network.
- *
- * Save if we haven't accumulated enough bytes to send, the connection
- * is not about to be closed, and:
- *
- * 1) we didn't see a file, we don't have more passes over the
- * 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 (nbytes + flen < AP_MIN_BYTES_TO_WRITE
- && !AP_BUCKET_IS_EOC(last_e)
- && ((!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;
- 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_cerror(APLOG_MARK, APLOG_ERR, rv,
- c, "core_output_filter:"
- " Error reading from bucket.");
- return HTTP_INTERNAL_SERVER_ERROR;
- }
- }
- }
- }
- if (!ctx->deferred_write_pool) {
- apr_pool_create(&ctx->deferred_write_pool, c->pool);
- apr_pool_tag(ctx->deferred_write_pool, "deferred_write");
- }
- 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
-
- memset(&hdtr, '\0', sizeof(hdtr));
- if (nvec) {
- hdtr.numheaders = nvec;
- hdtr.headers = vec;
- }
-
- if (nvec_trailers) {
- hdtr.numtrailers = nvec_trailers;
- hdtr.trailers = vec_trailers;
- }
-
-#if APR_HAS_SENDFILE
- if (apr_file_flags_get(fd) & APR_SENDFILE_ENABLED) {
-
- 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 */
- }
- else
-#endif
- {
- rv = emulate_sendfile(net, fd, &hdtr, foffset, flen,
- &bytes_sent);
- }
-
- if (logio_add_bytes_out && bytes_sent > 0)
- logio_add_bytes_out(c, bytes_sent);
-
- fd = NULL;
- }
- else {
- apr_size_t bytes_sent;
-
- rv = writev_it_all(net->client_socket,
- vec, nvec,
- 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_cerror(APLOG_MARK, APLOG_INFO, rv, c,
- "core_output_filter: writing data to the network");
-
- if (more)
- apr_brigade_destroy(more);
-
- /* No need to check for SUCCESS, we did that above. */
- if (!APR_STATUS_IS_EAGAIN(rv)) {
- c->aborted = 1;
- }
-
- /* The client has aborted, but the request was successful. We
- * will report success, and leave it to the access and error
- * logs to note that the connection was aborted.
- */
- return APR_SUCCESS;
- }
-
- b = more;
- more = NULL;
- } /* end while () */
-
- return APR_SUCCESS;
-}
+APR_OPTIONAL_FN_TYPE(ap_logio_add_bytes_out) *logio_add_bytes_out;
static int core_post_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
{
Copied: httpd/httpd/trunk/server/core_filters.c (from r106679, httpd/httpd/trunk/server/core.c)
Url: http://svn.apache.org/viewcvs/httpd/httpd/trunk/server/core_filters.c?view=diff&rev=106692&p1=httpd/httpd/trunk/server/core.c&r1=106679&p2=httpd/httpd/trunk/server/core_filters.c&r2=106692
==============================================================================
--- httpd/httpd/trunk/server/core.c (original)
+++ httpd/httpd/trunk/server/core_filters.c Sat Nov 27 00:07:44 2004
@@ -13,3731 +13,55 @@
* limitations under the License.
*/
-#include "apr.h"
-#include "apr_strings.h"
-#include "apr_lib.h"
-#include "apr_fnmatch.h"
-#include "apr_hash.h"
-#include "apr_thread_proc.h" /* for RLIMIT stuff */
-#include "apr_hooks.h"
-
-#define APR_WANT_IOVEC
-#define APR_WANT_STRFUNC
-#define APR_WANT_MEMFUNC
-#include "apr_want.h"
-
-#define CORE_PRIVATE
-#include "ap_config.h"
-#include "httpd.h"
-#include "http_config.h"
-#include "http_core.h"
-#include "http_protocol.h" /* For index_of_response(). Grump. */
-#include "http_request.h"
-#include "http_vhost.h"
-#include "http_main.h" /* For the default_handler below... */
-#include "http_log.h"
-#include "util_md5.h"
-#include "http_connection.h"
-#include "apr_buckets.h"
-#include "util_filter.h"
-#include "util_ebcdic.h"
-#include "mpm.h"
-#include "mpm_common.h"
-#include "scoreboard.h"
-#include "mod_core.h"
-#include "mod_proxy.h"
-#include "ap_listen.h"
-
-#include "mod_so.h" /* for ap_find_loaded_module_symbol */
-
-/* LimitRequestBody handling */
-#define AP_LIMIT_REQ_BODY_UNSET ((apr_off_t) -1)
-#define AP_DEFAULT_LIMIT_REQ_BODY ((apr_off_t) 0)
-
-/* LimitXMLRequestBody handling */
-#define AP_LIMIT_UNSET ((long) -1)
-#define AP_DEFAULT_LIMIT_XML_BODY ((size_t)1000000)
-
-#define AP_MIN_SENDFILE_BYTES (256)
-
-/* maximum include nesting level */
-#ifndef AP_MAX_INCLUDE_DEPTH
-#define AP_MAX_INCLUDE_DEPTH (128)
-#endif
-
-APR_HOOK_STRUCT(
- APR_HOOK_LINK(get_mgmt_items)
-)
-
-AP_IMPLEMENT_HOOK_RUN_ALL(int, get_mgmt_items,
- (apr_pool_t *p, const char *val, apr_hash_t *ht),
- (p, val, ht), OK, DECLINED)
-
-/* 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.
- *
- * The core module also defines handlers, etc., do handle just enough
- * to allow a server with the core module ONLY to actually serve documents
- * (though it slaps DefaultType on all of 'em); this was useful in testing,
- * but may not be worth preserving.
- *
- * This file could almost be mod_core.c, except for the stuff which affects
- * the http_conf_globals.
- */
-
-/* Handles for core filters */
-AP_DECLARE_DATA ap_filter_rec_t *ap_subreq_core_filter_handle;
-AP_DECLARE_DATA ap_filter_rec_t *ap_core_output_filter_handle;
-AP_DECLARE_DATA ap_filter_rec_t *ap_content_length_filter_handle;
-AP_DECLARE_DATA ap_filter_rec_t *ap_net_time_filter_handle;
-AP_DECLARE_DATA ap_filter_rec_t *ap_core_input_filter_handle;
-
-/* magic pointer for ErrorDocument xxx "default" */
-static char errordocument_default;
-
-static void *create_core_dir_config(apr_pool_t *a, char *dir)
-{
- core_dir_config *conf;
- int i;
-
- conf = (core_dir_config *)apr_pcalloc(a, sizeof(core_dir_config));
-
- /* conf->r and conf->d[_*] are initialized by dirsection() or left NULL */
-
- conf->opts = dir ? OPT_UNSET : OPT_UNSET|OPT_ALL;
- conf->opts_add = conf->opts_remove = OPT_NONE;
- conf->override = dir ? OR_UNSET : OR_UNSET|OR_ALL;
- conf->override_opts = OPT_UNSET | OPT_ALL | OPT_INCNOEXEC | OPT_SYM_OWNER
- | OPT_MULTI;
-
- conf->content_md5 = 2;
- conf->accept_path_info = 3;
-
- conf->use_canonical_name = USE_CANONICAL_NAME_UNSET;
-
- conf->hostname_lookups = HOSTNAME_LOOKUP_UNSET;
- conf->satisfy = apr_palloc(a, sizeof(*conf->satisfy) * METHODS);
- for (i = 0; i < METHODS; ++i) {
- conf->satisfy[i] = SATISFY_NOSPEC;
- }
-
-#ifdef RLIMIT_CPU
- conf->limit_cpu = NULL;
-#endif
-#if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS)
- conf->limit_mem = NULL;
-#endif
-#ifdef RLIMIT_NPROC
- conf->limit_nproc = NULL;
-#endif
-
- conf->limit_req_body = AP_LIMIT_REQ_BODY_UNSET;
- conf->limit_xml_body = AP_LIMIT_UNSET;
- conf->sec_file = apr_array_make(a, 2, sizeof(ap_conf_vector_t *));
-
- conf->server_signature = srv_sig_unset;
-
- conf->add_default_charset = ADD_DEFAULT_CHARSET_UNSET;
- conf->add_default_charset_name = DEFAULT_ADD_DEFAULT_CHARSET_NAME;
-
- /* Overriding all negotiation
- */
- conf->mime_type = NULL;
- conf->handler = NULL;
- conf->output_filters = NULL;
- conf->input_filters = NULL;
-
- /*
- * Flag for use of inodes in ETags.
- */
- conf->etag_bits = ETAG_UNSET;
- conf->etag_add = ETAG_UNSET;
- 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;
-}
-
-/*
- * Overlay one hash table of ct_output_filters onto another
- */
-static void *merge_ct_filters(apr_pool_t *p,
- const void *key,
- apr_ssize_t klen,
- const void *overlay_val,
- const void *base_val,
- const void *data)
-{
- ap_filter_rec_t *cur;
- const ap_filter_rec_t *overlay_info = (const ap_filter_rec_t *)overlay_val;
- const ap_filter_rec_t *base_info = (const ap_filter_rec_t *)base_val;
-
- cur = NULL;
-
- while (overlay_info) {
- ap_filter_rec_t *new;
-
- new = apr_pcalloc(p, sizeof(ap_filter_rec_t));
- new->name = apr_pstrdup(p, overlay_info->name);
- new->next = cur;
- cur = new;
- overlay_info = overlay_info->next;
- }
-
- while (base_info) {
- ap_filter_rec_t *f;
- int found = 0;
-
- /* We can't have dups. */
- f = cur;
- while (f) {
- if (!strcasecmp(base_info->name, f->name)) {
- found = 1;
- break;
- }
-
- f = f->next;
- }
-
- if (!found) {
- f = apr_pcalloc(p, sizeof(ap_filter_rec_t));
- f->name = apr_pstrdup(p, base_info->name);
- f->next = cur;
- cur = f;
- }
-
- base_info = base_info->next;
- }
-
- return cur;
-}
-
-static void *merge_core_dir_configs(apr_pool_t *a, void *basev, void *newv)
-{
- core_dir_config *base = (core_dir_config *)basev;
- core_dir_config *new = (core_dir_config *)newv;
- core_dir_config *conf;
- int i;
-
- /* Create this conf by duplicating the base, replacing elements
- * (or creating copies for merging) where new-> values exist.
- */
- conf = (core_dir_config *)apr_palloc(a, sizeof(core_dir_config));
- memcpy(conf, base, sizeof(core_dir_config));
-
- conf->d = new->d;
- conf->d_is_fnmatch = new->d_is_fnmatch;
- conf->d_components = new->d_components;
- conf->r = new->r;
-
- if (new->opts & OPT_UNSET) {
- /* there was no explicit setting of new->opts, so we merge
- * preserve the invariant (opts_add & opts_remove) == 0
- */
- conf->opts_add = (conf->opts_add & ~new->opts_remove) | new->opts_add;
- conf->opts_remove = (conf->opts_remove & ~new->opts_add)
- | new->opts_remove;
- conf->opts = (conf->opts & ~conf->opts_remove) | conf->opts_add;
- if ((base->opts & OPT_INCNOEXEC) && (new->opts & OPT_INCLUDES)) {
- conf->opts = (conf->opts & ~OPT_INCNOEXEC) | OPT_INCLUDES;
- }
- }
- else {
- /* otherwise we just copy, because an explicit opts setting
- * overrides all earlier +/- modifiers
- */
- conf->opts = new->opts;
- conf->opts_add = new->opts_add;
- conf->opts_remove = new->opts_remove;
- }
-
- if (!(new->override & OR_UNSET)) {
- conf->override = new->override;
- }
-
- if (!(new->override_opts & OPT_UNSET)) {
- conf->override_opts = new->override_opts;
- }
-
- if (new->ap_default_type) {
- conf->ap_default_type = new->ap_default_type;
- }
-
- if (new->ap_auth_type) {
- conf->ap_auth_type = new->ap_auth_type;
- }
-
- if (new->ap_auth_name) {
- conf->ap_auth_name = new->ap_auth_name;
- }
-
- if (new->ap_requires) {
- conf->ap_requires = new->ap_requires;
- }
-
- if (conf->response_code_strings == NULL) {
- conf->response_code_strings = new->response_code_strings;
- }
- else if (new->response_code_strings != NULL) {
- /* If we merge, the merge-result must have it's own array
- */
- conf->response_code_strings = apr_palloc(a,
- sizeof(*conf->response_code_strings) * RESPONSE_CODES);
- memcpy(conf->response_code_strings, base->response_code_strings,
- sizeof(*conf->response_code_strings) * RESPONSE_CODES);
-
- for (i = 0; i < RESPONSE_CODES; ++i) {
- if (new->response_code_strings[i] != NULL) {
- conf->response_code_strings[i] = new->response_code_strings[i];
- }
- }
- }
- /* Otherwise we simply use the base->response_code_strings array
- */
-
- if (new->hostname_lookups != HOSTNAME_LOOKUP_UNSET) {
- conf->hostname_lookups = new->hostname_lookups;
- }
-
- if ((new->content_md5 & 2) == 0) {
- conf->content_md5 = new->content_md5;
- }
-
- if (new->accept_path_info != 3) {
- conf->accept_path_info = new->accept_path_info;
- }
-
- if (new->use_canonical_name != USE_CANONICAL_NAME_UNSET) {
- conf->use_canonical_name = new->use_canonical_name;
- }
-
-#ifdef RLIMIT_CPU
- if (new->limit_cpu) {
- conf->limit_cpu = new->limit_cpu;
- }
-#endif
-
-#if defined(RLIMIT_DATA) || defined(RLIMIT_VMEM) || defined(RLIMIT_AS)
- if (new->limit_mem) {
- conf->limit_mem = new->limit_mem;
- }
-#endif
-
-#ifdef RLIMIT_NPROC
- if (new->limit_nproc) {
- conf->limit_nproc = new->limit_nproc;
- }
-#endif
-
- if (new->limit_req_body != AP_LIMIT_REQ_BODY_UNSET) {
- conf->limit_req_body = new->limit_req_body;
- }
-
- if (new->limit_xml_body != AP_LIMIT_UNSET)
- conf->limit_xml_body = new->limit_xml_body;
- else
- conf->limit_xml_body = base->limit_xml_body;
-
- if (!conf->sec_file) {
- conf->sec_file = new->sec_file;
- }
- else if (new->sec_file) {
- /* If we merge, the merge-result must have it's own array
- */
- conf->sec_file = apr_array_append(a, base->sec_file, new->sec_file);
- }
- /* Otherwise we simply use the base->sec_file array
- */
-
- /* use a separate ->satisfy[] array either way */
- conf->satisfy = apr_palloc(a, sizeof(*conf->satisfy) * METHODS);
- for (i = 0; i < METHODS; ++i) {
- if (new->satisfy[i] != SATISFY_NOSPEC) {
- conf->satisfy[i] = new->satisfy[i];
- } else {
- conf->satisfy[i] = base->satisfy[i];
- }
- }
-
- if (new->server_signature != srv_sig_unset) {
- conf->server_signature = new->server_signature;
- }
-
- if (new->add_default_charset != ADD_DEFAULT_CHARSET_UNSET) {
- conf->add_default_charset = new->add_default_charset;
- conf->add_default_charset_name = new->add_default_charset_name;
- }
-
- /* Overriding all negotiation
- */
- if (new->mime_type) {
- conf->mime_type = new->mime_type;
- }
-
- if (new->handler) {
- conf->handler = new->handler;
- }
-
- if (new->output_filters) {
- conf->output_filters = new->output_filters;
- }
-
- if (new->input_filters) {
- conf->input_filters = new->input_filters;
- }
-
- if (conf->ct_output_filters && new->ct_output_filters) {
- conf->ct_output_filters = apr_hash_merge(a,
- new->ct_output_filters,
- conf->ct_output_filters,
- merge_ct_filters,
- NULL);
- }
- else if (new->ct_output_filters) {
- conf->ct_output_filters = apr_hash_copy(a, new->ct_output_filters);
- }
- else if (conf->ct_output_filters) {
- /* That memcpy above isn't enough. */
- conf->ct_output_filters = apr_hash_copy(a, base->ct_output_filters);
- }
-
- /*
- * Now merge the setting of the FileETag directive.
- */
- if (new->etag_bits == ETAG_UNSET) {
- conf->etag_add =
- (conf->etag_add & (~ new->etag_remove)) | new->etag_add;
- conf->etag_remove =
- (conf->opts_remove & (~ new->etag_add)) | new->etag_remove;
- conf->etag_bits =
- (conf->etag_bits & (~ conf->etag_remove)) | conf->etag_add;
- }
- else {
- conf->etag_bits = new->etag_bits;
- conf->etag_add = new->etag_add;
- conf->etag_remove = new->etag_remove;
- }
-
- if (conf->etag_bits != ETAG_NONE) {
- conf->etag_bits &= (~ ETAG_NONE);
- }
-
- if (new->enable_mmap != ENABLE_MMAP_UNSET) {
- 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;
-}
-
-static void *create_core_server_config(apr_pool_t *a, server_rec *s)
-{
- core_server_config *conf;
- int is_virtual = s->is_virtual;
-
- conf = (core_server_config *)apr_pcalloc(a, sizeof(core_server_config));
-
-#ifdef GPROF
- conf->gprof_dir = NULL;
-#endif
-
- conf->access_name = is_virtual ? NULL : DEFAULT_ACCESS_FNAME;
- conf->ap_document_root = is_virtual ? NULL : DOCUMENT_LOCATION;
- 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;
-}
-
-static void *merge_core_server_configs(apr_pool_t *p, void *basev, void *virtv)
-{
- core_server_config *base = (core_server_config *)basev;
- core_server_config *virt = (core_server_config *)virtv;
- core_server_config *conf;
-
- conf = (core_server_config *)apr_palloc(p, sizeof(core_server_config));
- memcpy(conf, virt, sizeof(core_server_config));
-
- if (!conf->access_name) {
- conf->access_name = base->access_name;
- }
-
- if (!conf->ap_document_root) {
- conf->ap_document_root = base->ap_document_root;
- }
-
- 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;
-}
-
-/* Add per-directory configuration entry (for section);
- * these are part of the core server config.
- */
-
-AP_CORE_DECLARE(void) ap_add_per_dir_conf(server_rec *s, void *dir_config)
-{
- core_server_config *sconf = ap_get_module_config(s->module_config,
- &core_module);
- void **new_space = (void **)apr_array_push(sconf->sec_dir);
-
- *new_space = dir_config;
-}
-
-AP_CORE_DECLARE(void) ap_add_per_url_conf(server_rec *s, void *url_config)
-{
- core_server_config *sconf = ap_get_module_config(s->module_config,
- &core_module);
- void **new_space = (void **)apr_array_push(sconf->sec_url);
-
- *new_space = url_config;
-}
-
-AP_CORE_DECLARE(void) ap_add_file_conf(core_dir_config *conf, void *url_config)
-{
- void **new_space = (void **)apr_array_push(conf->sec_file);
-
- *new_space = url_config;
-}
-
-/* We need to do a stable sort, qsort isn't stable. So to make it stable
- * we'll be maintaining the original index into the list, and using it
- * as the minor key during sorting. The major key is the number of
- * components (where the root component is zero).
- */
-struct reorder_sort_rec {
- ap_conf_vector_t *elt;
- int orig_index;
-};
-
-static int reorder_sorter(const void *va, const void *vb)
-{
- const struct reorder_sort_rec *a = va;
- const struct reorder_sort_rec *b = vb;
- core_dir_config *core_a;
- core_dir_config *core_b;
-
- core_a = ap_get_module_config(a->elt, &core_module);
- core_b = ap_get_module_config(b->elt, &core_module);
-
- /* a regex always sorts after a non-regex
- */
- if (!core_a->r && core_b->r) {
- return -1;
- }
- else if (core_a->r && !core_b->r) {
- return 1;
- }
-
- /* we always sort next by the number of components
- */
- if (core_a->d_components < core_b->d_components) {
- return -1;
- }
- else if (core_a->d_components > core_b->d_components) {
- return 1;
- }
-
- /* They have the same number of components, we now have to compare
- * the minor key to maintain the original order (from the config.)
- */
- return a->orig_index - b->orig_index;
-}
-
-void ap_core_reorder_directories(apr_pool_t *p, server_rec *s)
-{
- core_server_config *sconf;
- apr_array_header_t *sec_dir;
- struct reorder_sort_rec *sortbin;
- int nelts;
- ap_conf_vector_t **elts;
- int i;
- apr_pool_t *tmp;
-
- sconf = ap_get_module_config(s->module_config, &core_module);
- sec_dir = sconf->sec_dir;
- nelts = sec_dir->nelts;
- elts = (ap_conf_vector_t **)sec_dir->elts;
-
- if (!nelts) {
- /* simple case of already being sorted... */
- /* We're not checking this condition to be fast... we're checking
- * it to avoid trying to palloc zero bytes, which can trigger some
- * memory debuggers to barf
- */
- return;
- }
-
- /* we have to allocate tmp space to do a stable sort */
- apr_pool_create(&tmp, p);
- sortbin = apr_palloc(tmp, sec_dir->nelts * sizeof(*sortbin));
- for (i = 0; i < nelts; ++i) {
- sortbin[i].orig_index = i;
- sortbin[i].elt = elts[i];
- }
-
- qsort(sortbin, nelts, sizeof(*sortbin), reorder_sorter);
-
- /* and now copy back to the original array */
- for (i = 0; i < nelts; ++i) {
- elts[i] = sortbin[i].elt;
- }
-
- apr_pool_destroy(tmp);
-}
-
-/*****************************************************************
- *
- * There are some elements of the core config structures in which
- * other modules have a legitimate interest (this is ugly, but necessary
- * to preserve NCSA back-compatibility). So, we have a bunch of accessors
- * here...
- */
-
-AP_DECLARE(int) ap_allow_options(request_rec *r)
-{
- core_dir_config *conf =
- (core_dir_config *)ap_get_module_config(r->per_dir_config, &core_module);
-
- return conf->opts;
-}
-
-AP_DECLARE(int) ap_allow_overrides(request_rec *r)
-{
- core_dir_config *conf;
- conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
- &core_module);
-
- return conf->override;
-}
-
-AP_DECLARE(const char *) ap_auth_type(request_rec *r)
-{
- core_dir_config *conf;
-
- conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
- &core_module);
-
- return conf->ap_auth_type;
-}
-
-AP_DECLARE(const char *) ap_auth_name(request_rec *r)
-{
- core_dir_config *conf;
-
- conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
- &core_module);
-
- return conf->ap_auth_name;
-}
-
-AP_DECLARE(const char *) ap_default_type(request_rec *r)
-{
- core_dir_config *conf;
-
- conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
- &core_module);
-
- return conf->ap_default_type
- ? conf->ap_default_type
- : DEFAULT_CONTENT_TYPE;
-}
-
-AP_DECLARE(const char *) ap_document_root(request_rec *r) /* Don't use this! */
-{
- core_server_config *conf;
-
- conf = (core_server_config *)ap_get_module_config(r->server->module_config,
- &core_module);
-
- return conf->ap_document_root;
-}
-
-AP_DECLARE(const apr_array_header_t *) ap_requires(request_rec *r)
-{
- core_dir_config *conf;
-
- conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
- &core_module);
-
- return conf->ap_requires;
-}
-
-AP_DECLARE(int) ap_satisfies(request_rec *r)
-{
- core_dir_config *conf;
-
- conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
- &core_module);
-
- return conf->satisfy[r->method_number];
-}
-
-/* Should probably just get rid of this... the only code that cares is
- * part of the core anyway (and in fact, it isn't publicised to other
- * modules).
- */
-
-char *ap_response_code_string(request_rec *r, int error_index)
-{
- core_dir_config *dirconf;
- core_request_config *reqconf;
-
- /* check for string registered via ap_custom_response() first */
- reqconf = (core_request_config *)ap_get_module_config(r->request_config,
- &core_module);
- if (reqconf->response_code_strings != NULL &&
- reqconf->response_code_strings[error_index] != NULL) {
- return reqconf->response_code_strings[error_index];
- }
-
- /* check for string specified via ErrorDocument */
- dirconf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
- &core_module);
-
- if (dirconf->response_code_strings == NULL) {
- return NULL;
- }
-
- if (dirconf->response_code_strings[error_index] == &errordocument_default) {
- return NULL;
- }
-
- return dirconf->response_code_strings[error_index];
-}
-
-
-/* Code from Harald Hanche-Olsen */
-static APR_INLINE void do_double_reverse (conn_rec *conn)
-{
- apr_sockaddr_t *sa;
- apr_status_t rv;
-
- if (conn->double_reverse) {
- /* already done */
- return;
- }
-
- if (conn->remote_host == NULL || conn->remote_host[0] == '\0') {
- /* single reverse failed, so don't bother */
- conn->double_reverse = -1;
- return;
- }
-
- rv = apr_sockaddr_info_get(&sa, conn->remote_host, APR_UNSPEC, 0, 0, conn->pool);
- if (rv == APR_SUCCESS) {
- while (sa) {
- if (apr_sockaddr_equal(sa, conn->remote_addr)) {
- conn->double_reverse = 1;
- return;
- }
-
- sa = sa->next;
- }
- }
-
- conn->double_reverse = -1;
-}
-
-AP_DECLARE(const char *) ap_get_remote_host(conn_rec *conn, void *dir_config,
- int type, int *str_is_ip)
-{
- int hostname_lookups;
- int ignored_str_is_ip;
-
- if (!str_is_ip) { /* caller doesn't want to know */
- str_is_ip = &ignored_str_is_ip;
- }
- *str_is_ip = 0;
-
- /* If we haven't checked the host name, and we want to */
- if (dir_config) {
- hostname_lookups =
- ((core_dir_config *)ap_get_module_config(dir_config, &core_module))
- ->hostname_lookups;
-
- if (hostname_lookups == HOSTNAME_LOOKUP_UNSET) {
- hostname_lookups = HOSTNAME_LOOKUP_OFF;
- }
- }
- else {
- /* the default */
- hostname_lookups = HOSTNAME_LOOKUP_OFF;
- }
-
- if (type != REMOTE_NOLOOKUP
- && conn->remote_host == NULL
- && (type == REMOTE_DOUBLE_REV
- || hostname_lookups != HOSTNAME_LOOKUP_OFF)) {
-
- if (apr_getnameinfo(&conn->remote_host, conn->remote_addr, 0)
- == APR_SUCCESS) {
- ap_str_tolower(conn->remote_host);
-
- if (hostname_lookups == HOSTNAME_LOOKUP_DOUBLE) {
- do_double_reverse(conn);
- if (conn->double_reverse != 1) {
- conn->remote_host = NULL;
- }
- }
- }
-
- /* if failed, set it to the NULL string to indicate error */
- if (conn->remote_host == NULL) {
- conn->remote_host = "";
- }
- }
-
- if (type == REMOTE_DOUBLE_REV) {
- do_double_reverse(conn);
- if (conn->double_reverse == -1) {
- return NULL;
- }
- }
-
- /*
- * Return the desired information; either the remote DNS name, if found,
- * or either NULL (if the hostname was requested) or the IP address
- * (if any identifier was requested).
- */
- if (conn->remote_host != NULL && conn->remote_host[0] != '\0') {
- return conn->remote_host;
- }
- else {
- if (type == REMOTE_HOST || type == REMOTE_DOUBLE_REV) {
- return NULL;
- }
- else {
- *str_is_ip = 1;
- return conn->remote_ip;
- }
- }
-}
-
-/*
- * 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)
-{
- if (r->connection->remote_logname != NULL) {
- return r->connection->remote_logname;
- }
-
- if (ident_lookup) {
- return ident_lookup(r);
- }
-
- return NULL;
-}
-
-/* There are two options regarding what the "name" of a server is. The
- * "canonical" name as defined by ServerName and Port, or the "client's
- * name" as supplied by a possible Host: header or full URI. We never
- * trust the port passed in the client's headers, we always use the
- * port of the actual socket.
- *
- * The DNS option to UseCanonicalName causes this routine to do a
- * reverse lookup on the local IP address of the connection and use
- * that for the ServerName. This makes its value more reliable while
- * at the same time allowing Demon's magic virtual hosting to work.
- * The assumption is that DNS lookups are sufficiently quick...
- * -- fanf 1998-10-03
- */
-AP_DECLARE(const char *) ap_get_server_name(request_rec *r)
-{
- conn_rec *conn = r->connection;
- core_dir_config *d;
-
- d = (core_dir_config *)ap_get_module_config(r->per_dir_config,
- &core_module);
-
- if (d->use_canonical_name == USE_CANONICAL_NAME_OFF) {
- return r->hostname ? r->hostname : r->server->server_hostname;
- }
-
- if (d->use_canonical_name == USE_CANONICAL_NAME_DNS) {
- if (conn->local_host == NULL) {
- if (apr_getnameinfo(&conn->local_host,
- conn->local_addr, 0) != APR_SUCCESS)
- conn->local_host = apr_pstrdup(conn->pool,
- r->server->server_hostname);
- else {
- ap_str_tolower(conn->local_host);
- }
- }
-
- return conn->local_host;
- }
-
- /* default */
- 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;
- core_dir_config *d =
- (core_dir_config *)ap_get_module_config(r->per_dir_config, &core_module);
-
- if (d->use_canonical_name == USE_CANONICAL_NAME_OFF
- || d->use_canonical_name == USE_CANONICAL_NAME_DNS) {
-
- /* With UseCanonicalName off Apache will form self-referential
- * URLs using the hostname and port supplied by the client if
- * any are supplied (otherwise it will use the canonical name).
- */
- port = r->parsed_uri.port_str ? r->parsed_uri.port :
- r->connection->local_addr->port ? r->connection->local_addr->port :
- r->server->port ? r->server->port :
- ap_default_port(r);
- }
- else { /* d->use_canonical_name == USE_CANONICAL_NAME_ON */
-
- /* With UseCanonicalName on (and in all versions prior to 1.3)
- * Apache will use the hostname and port specified in the
- * ServerName directive to construct a canonical name for the
- * server. (If no port was specified in the ServerName
- * directive, Apache uses the port supplied by the client if
- * any is supplied, and finally the default port for the protocol
- * used.
- */
- port = r->server->port ? r->server->port :
- r->connection->local_addr->port ? r->connection->local_addr->port :
- ap_default_port(r);
- }
-
- /* default */
- return port;
-}
-
-AP_DECLARE(char *) ap_construct_url(apr_pool_t *p, const char *uri,
- request_rec *r)
-{
- unsigned port = ap_get_server_port(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 apr_psprintf(p, "%s://%s:%u%s", ap_http_method(r), host, port, uri);
-}
-
-AP_DECLARE(apr_off_t) ap_get_limit_req_body(const request_rec *r)
-{
- core_dir_config *d =
- (core_dir_config *)ap_get_module_config(r->per_dir_config, &core_module);
-
- if (d->limit_req_body == AP_LIMIT_REQ_BODY_UNSET) {
- return AP_DEFAULT_LIMIT_REQ_BODY;
- }
-
- return d->limit_req_body;
-}
-
-
-/*****************************************************************
- *
- * Commands... this module handles almost all of the NCSA httpd.conf
- * commands, but most of the old srm.conf is in the the modules.
- */
-
-
-/* returns a parent if it matches the given directive */
-static const ap_directive_t * find_parent(const ap_directive_t *dirp,
- const char *what)
-{
- while (dirp->parent != NULL) {
- dirp = dirp->parent;
-
- /* ### it would be nice to have atom-ized directives */
- if (strcasecmp(dirp->directive, what) == 0)
- return dirp;
- }
-
- return NULL;
-}
-
-AP_DECLARE(const char *) ap_check_cmd_context(cmd_parms *cmd,
- unsigned forbidden)
-{
- const char *gt = (cmd->cmd->name[0] == '<'
- && cmd->cmd->name[strlen(cmd->cmd->name)-1] != '>')
- ? ">" : "";
- const ap_directive_t *found;
-
- if ((forbidden & NOT_IN_VIRTUALHOST) && cmd->server->is_virtual) {
- return apr_pstrcat(cmd->pool, cmd->cmd->name, gt,
- " cannot occur within section", NULL);
- }
-
- if ((forbidden & NOT_IN_LIMIT) && cmd->limited != -1) {
- return apr_pstrcat(cmd->pool, cmd->cmd->name, gt,
- " cannot occur within section", NULL);
- }
-
- if ((forbidden & NOT_IN_DIR_LOC_FILE) == NOT_IN_DIR_LOC_FILE) {
- if (cmd->path != NULL) {
- return apr_pstrcat(cmd->pool, cmd->cmd->name, gt,
- " cannot occur within "
- "section", NULL);
- }
- if (cmd->cmd->req_override & EXEC_ON_READ) {
- /* EXEC_ON_READ must be NOT_IN_DIR_LOC_FILE, if not, it will
- * (deliberately) segfault below in the individual tests...
- */
- return NULL;
- }
- }
-
- if (((forbidden & NOT_IN_DIRECTORY)
- && ((found = find_parent(cmd->directive, "directive, "directive, "directive, "directive, "directive, "pool, cmd->cmd->name, gt,
- " cannot occur within ", found->directive,
- "> section", NULL);
- }
-
- return NULL;
-}
-
-static const char *set_access_name(cmd_parms *cmd, void *dummy,
- const char *arg)
-{
- void *sconf = cmd->server->module_config;
- core_server_config *conf = ap_get_module_config(sconf, &core_module);
-
- const char *err = ap_check_cmd_context(cmd,
- NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
- if (err != NULL) {
- return err;
- }
-
- conf->access_name = apr_pstrdup(cmd->pool, arg);
- return NULL;
-}
-
-#ifdef GPROF
-static const char *set_gprof_dir(cmd_parms *cmd, void *dummy, const char *arg)
-{
- void *sconf = cmd->server->module_config;
- core_server_config *conf = ap_get_module_config(sconf, &core_module);
-
- const char *err = ap_check_cmd_context(cmd,
- NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
- if (err != NULL) {
- return err;
- }
-
- conf->gprof_dir = apr_pstrdup(cmd->pool, arg);
- return NULL;
-}
-#endif /*GPROF*/
-
-static const char *set_add_default_charset(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, "Off")) {
- d->add_default_charset = ADD_DEFAULT_CHARSET_OFF;
- }
- else if (!strcasecmp(arg, "On")) {
- d->add_default_charset = ADD_DEFAULT_CHARSET_ON;
- d->add_default_charset_name = DEFAULT_ADD_DEFAULT_CHARSET_NAME;
- }
- else {
- d->add_default_charset = ADD_DEFAULT_CHARSET_ON;
- d->add_default_charset_name = arg;
- }
-
- return NULL;
-}
-
-static const char *set_document_root(cmd_parms *cmd, void *dummy,
- const char *arg)
-{
- void *sconf = cmd->server->module_config;
- core_server_config *conf = ap_get_module_config(sconf, &core_module);
-
- const char *err = ap_check_cmd_context(cmd,
- NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
- if (err != NULL) {
- return err;
- }
-
- /* Make it absolute, relative to ServerRoot */
- arg = ap_server_root_relative(cmd->pool, arg);
-
- /* TODO: ap_configtestonly && ap_docrootcheck && */
- if (apr_filepath_merge((char**)&conf->ap_document_root, NULL, arg,
- APR_FILEPATH_TRUENAME, cmd->pool) != APR_SUCCESS
- || !ap_is_directory(cmd->pool, arg)) {
- if (cmd->server->is_virtual) {
- ap_log_perror(APLOG_MARK, APLOG_STARTUP, 0,
- cmd->pool,
- "Warning: DocumentRoot [%s] does not exist",
- arg);
- conf->ap_document_root = arg;
- }
- else {
- return "DocumentRoot must be a directory";
- }
- }
- return NULL;
-}
-
-AP_DECLARE(void) ap_custom_response(request_rec *r, int status,
- const char *string)
-{
- core_request_config *conf =
- ap_get_module_config(r->request_config, &core_module);
- int idx;
-
- if (conf->response_code_strings == NULL) {
- conf->response_code_strings =
- apr_pcalloc(r->pool,
- sizeof(*conf->response_code_strings) * RESPONSE_CODES);
- }
-
- idx = ap_index_of_response(status);
-
- conf->response_code_strings[idx] =
- ((ap_is_url(string) || (*string == '/')) && (*string != '"')) ?
- apr_pstrdup(r->pool, string) : apr_pstrcat(r->pool, "\"", string, NULL);
-}
-
-static const char *set_error_document(cmd_parms *cmd, void *conf_,
- const char *errno_str, const char *msg)
-{
- core_dir_config *conf = conf_;
- int error_number, index_number, idx500;
- enum { MSG, LOCAL_PATH, REMOTE_PATH } what = MSG;
-
- const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
- if (err != NULL) {
- return err;
- }
-
- /* 1st parameter should be a 3 digit number, which we recognize;
- * convert it into an array index
- */
- error_number = atoi(errno_str);
- idx500 = ap_index_of_response(HTTP_INTERNAL_SERVER_ERROR);
-
- if (error_number == HTTP_INTERNAL_SERVER_ERROR) {
- index_number = idx500;
- }
- else if ((index_number = ap_index_of_response(error_number)) == idx500) {
- return apr_pstrcat(cmd->pool, "Unsupported HTTP response code ",
- errno_str, NULL);
- }
-
- /* Heuristic to determine second argument. */
- if (ap_strchr_c(msg,' '))
- what = MSG;
- else if (msg[0] == '/')
- what = LOCAL_PATH;
- else if (ap_is_url(msg))
- what = REMOTE_PATH;
- else
- what = MSG;
-
- /* The entry should be ignored if it is a full URL for a 401 error */
-
- if (error_number == 401 && what == REMOTE_PATH) {
- ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, cmd->server,
- "cannot use a full URL in a 401 ErrorDocument "
- "directive --- ignoring!");
- }
- else { /* Store it... */
- if (conf->response_code_strings == NULL) {
- conf->response_code_strings =
- apr_pcalloc(cmd->pool,
- sizeof(*conf->response_code_strings) *
- RESPONSE_CODES);
- }
-
- if (strcmp(msg, "default") == 0) {
- /* special case: ErrorDocument 404 default restores the
- * canned server error response
- */
- conf->response_code_strings[index_number] = &errordocument_default;
- }
- else {
- /* hack. Prefix a " if it is a msg; as that is what
- * http_protocol.c relies on to distinguish between
- * a msg and a (local) path.
- */
- conf->response_code_strings[index_number] = (what == MSG) ?
- apr_pstrcat(cmd->pool, "\"",msg,NULL) :
- apr_pstrdup(cmd->pool, msg);
- }
- }
-
- return NULL;
-}
-
-static const char *set_allow_opts(cmd_parms *cmd, allow_options_t *opts,
- const char *l)
-{
- allow_options_t opt;
- int first = 1;
-
- char *w, *p = (char *) l;
- char *tok_state;
-
- while ((w = apr_strtok(p, ",", &tok_state)) != NULL) {
-
- if (first) {
- p = NULL;
- *opts = OPT_NONE;
- first = 0;
- }
-
- if (!strcasecmp(w, "Indexes")) {
- opt = OPT_INDEXES;
- }
- else if (!strcasecmp(w, "Includes")) {
- opt = OPT_INCLUDES;
- }
- else if (!strcasecmp(w, "IncludesNOEXEC")) {
- opt = (OPT_INCLUDES | OPT_INCNOEXEC);
- }
- else if (!strcasecmp(w, "FollowSymLinks")) {
- opt = OPT_SYM_LINKS;
- }
- else if (!strcasecmp(w, "SymLinksIfOwnerMatch")) {
- opt = OPT_SYM_OWNER;
- }
- else if (!strcasecmp(w, "ExecCGI")) {
- opt = OPT_EXECCGI;
- }
- else if (!strcasecmp(w, "MultiViews")) {
- opt = OPT_MULTI;
- }
- else if (!strcasecmp(w, "RunScripts")) { /* AI backcompat. Yuck */
- opt = OPT_MULTI|OPT_EXECCGI;
- }
- else if (!strcasecmp(w, "None")) {
- opt = OPT_NONE;
- }
- else if (!strcasecmp(w, "All")) {
- opt = OPT_ALL;
- }
- else {
- return apr_pstrcat(cmd->pool, "Illegal option ", w, NULL);
- }
-
- *opts |= opt;
- }
-
- (*opts) &= (~OPT_UNSET);
-
- return NULL;
-}
-
-static const char *set_override(cmd_parms *cmd, void *d_, const char *l)
-{
- core_dir_config *d = d_;
- char *w;
- char *k, *v;
-
- const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
- if (err != NULL) {
- return err;
- }
-
- /* Throw a warning if we're in or */
- 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);
-
- k = w;
- v = strchr(k, '=');
- if (v) {
- *v++ = '\0';
- }
-
- if (!strcasecmp(w, "Limit")) {
- d->override |= OR_LIMIT;
- }
- else if (!strcasecmp(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")) {
- d->override |= OR_FILEINFO;
- }
- else if (!strcasecmp(w, "AuthConfig")) {
- d->override |= OR_AUTHCFG;
- }
- else if (!strcasecmp(w, "Indexes")) {
- d->override |= OR_INDEXES;
- }
- else if (!strcasecmp(w, "None")) {
- d->override = OR_NONE;
- }
- else if (!strcasecmp(w, "All")) {
- d->override = OR_ALL;
- }
- else {
- return apr_pstrcat(cmd->pool, "Illegal override option ", w, NULL);
- }
-
- d->override &= ~OR_UNSET;
- }
-
- return NULL;
-}
-
-static const char *set_options(cmd_parms *cmd, void *d_, const char *l)
-{
- core_dir_config *d = d_;
- allow_options_t opt;
- int first = 1;
- char action;
-
- while (l[0]) {
- char *w = ap_getword_conf(cmd->pool, &l);
- action = '\0';
-
- if (*w == '+' || *w == '-') {
- action = *(w++);
- }
- else if (first) {
- d->opts = OPT_NONE;
- first = 0;
- }
-
- if (!strcasecmp(w, "Indexes")) {
- opt = OPT_INDEXES;
- }
- else if (!strcasecmp(w, "Includes")) {
- opt = OPT_INCLUDES;
- }
- else if (!strcasecmp(w, "IncludesNOEXEC")) {
- opt = (OPT_INCLUDES | OPT_INCNOEXEC);
- }
- else if (!strcasecmp(w, "FollowSymLinks")) {
- opt = OPT_SYM_LINKS;
- }
- else if (!strcasecmp(w, "SymLinksIfOwnerMatch")) {
- opt = OPT_SYM_OWNER;
- }
- else if (!strcasecmp(w, "ExecCGI")) {
- opt = OPT_EXECCGI;
- }
- else if (!strcasecmp(w, "MultiViews")) {
- opt = OPT_MULTI;
- }
- else if (!strcasecmp(w, "RunScripts")) { /* AI backcompat. Yuck */
- opt = OPT_MULTI|OPT_EXECCGI;
- }
- else if (!strcasecmp(w, "None")) {
- opt = OPT_NONE;
- }
- else if (!strcasecmp(w, "All")) {
- opt = OPT_ALL;
- }
- else {
- return apr_pstrcat(cmd->pool, "Illegal option ", w, NULL);
- }
-
- if (!(cmd->override_opts & opt) && opt != OPT_NONE) {
- return apr_pstrcat(cmd->pool, "Option ", w, " not allowed here", NULL);
- }
- else if (action == '-') {
- /* we ensure the invariant (d->opts_add & d->opts_remove) == 0 */
- d->opts_remove |= opt;
- d->opts_add &= ~opt;
- d->opts &= ~opt;
- }
- else if (action == '+') {
- d->opts_add |= opt;
- d->opts_remove &= ~opt;
- d->opts |= opt;
- }
- else {
- d->opts |= opt;
- }
- }
-
- return NULL;
-}
-
-/*
- * Note what data should be used when forming file ETag values.
- * It would be nicer to do this as an ITERATE, but then we couldn't
- * remember the +/- state properly.
- */
-static const char *set_etag_bits(cmd_parms *cmd, void *mconfig,
- const char *args_p)
-{
- core_dir_config *cfg;
- etag_components_t bit;
- char action;
- char *token;
- const char *args;
- int valid;
- int first;
- int explicit;
-
- cfg = (core_dir_config *)mconfig;
-
- args = args_p;
- first = 1;
- explicit = 0;
- while (args[0] != '\0') {
- action = '*';
- bit = ETAG_UNSET;
- valid = 1;
- token = ap_getword_conf(cmd->pool, &args);
- if ((*token == '+') || (*token == '-')) {
- action = *token;
- token++;
- }
- else {
- /*
- * The occurrence of an absolute setting wipes
- * out any previous relative ones. The first such
- * occurrence forgets any inherited ones, too.
- */
- if (first) {
- cfg->etag_bits = ETAG_UNSET;
- cfg->etag_add = ETAG_UNSET;
- cfg->etag_remove = ETAG_UNSET;
- first = 0;
- }
- }
-
- if (strcasecmp(token, "None") == 0) {
- if (action != '*') {
- valid = 0;
- }
- else {
- cfg->etag_bits = bit = ETAG_NONE;
- explicit = 1;
- }
- }
- else if (strcasecmp(token, "All") == 0) {
- if (action != '*') {
- valid = 0;
- }
- else {
- explicit = 1;
- cfg->etag_bits = bit = ETAG_ALL;
- }
- }
- else if (strcasecmp(token, "Size") == 0) {
- bit = ETAG_SIZE;
- }
- else if ((strcasecmp(token, "LMTime") == 0)
- || (strcasecmp(token, "MTime") == 0)
- || (strcasecmp(token, "LastModified") == 0)) {
- bit = ETAG_MTIME;
- }
- else if (strcasecmp(token, "INode") == 0) {
- bit = ETAG_INODE;
- }
- else {
- return apr_pstrcat(cmd->pool, "Unknown keyword '",
- token, "' for ", cmd->cmd->name,
- " directive", NULL);
- }
-
- if (! valid) {
- return apr_pstrcat(cmd->pool, cmd->cmd->name, " keyword '",
- token, "' cannot be used with '+' or '-'",
- NULL);
- }
-
- if (action == '+') {
- /*
- * Make sure it's in the 'add' list and absent from the
- * 'subtract' list.
- */
- cfg->etag_add |= bit;
- cfg->etag_remove &= (~ bit);
- }
- else if (action == '-') {
- cfg->etag_remove |= bit;
- cfg->etag_add &= (~ bit);
- }
- else {
- /*
- * Non-relative values wipe out any + or - values
- * accumulated so far.
- */
- cfg->etag_bits |= bit;
- cfg->etag_add = ETAG_UNSET;
- cfg->etag_remove = ETAG_UNSET;
- explicit = 1;
- }
- }
-
- /*
- * Any setting at all will clear the 'None' and 'Unset' bits.
- */
-
- if (cfg->etag_add != ETAG_UNSET) {
- cfg->etag_add &= (~ ETAG_UNSET);
- }
-
- if (cfg->etag_remove != ETAG_UNSET) {
- cfg->etag_remove &= (~ ETAG_UNSET);
- }
-
- if (explicit) {
- cfg->etag_bits &= (~ ETAG_UNSET);
-
- if ((cfg->etag_bits & ETAG_NONE) != ETAG_NONE) {
- cfg->etag_bits &= (~ ETAG_NONE);
- }
- }
-
- return NULL;
-}
-
-static const char *set_enable_mmap(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_mmap = ENABLE_MMAP_ON;
- }
- else if (strcasecmp(arg, "off") == 0) {
- d->enable_mmap = ENABLE_MMAP_OFF;
- }
- else {
- return "parameter must be 'on' or 'off'";
- }
-
- 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_;
- int satisfy = SATISFY_NOSPEC;
- int i;
-
- if (!strcasecmp(arg, "all")) {
- satisfy = SATISFY_ALL;
- }
- else if (!strcasecmp(arg, "any")) {
- satisfy = SATISFY_ANY;
- }
- else {
- return "Satisfy either 'any' or 'all'.";
- }
-
- for (i = 0; i < METHODS; ++i) {
- if (cmd->limited & (AP_METHOD_BIT << i)) {
- c->satisfy[i] = satisfy;
- }
- }
-
- return NULL;
-}
-
-static const char *require(cmd_parms *cmd, void *c_, const char *arg)
-{
- require_line *r;
- core_dir_config *c = c_;
-
- if (!c->ap_requires) {
- c->ap_requires = apr_array_make(cmd->pool, 2, sizeof(require_line));
- }
-
- r = (require_line *)apr_array_push(c->ap_requires);
- r->requirement = apr_pstrdup(cmd->pool, arg);
- r->method_mask = cmd->limited;
-
- return NULL;
-}
-
-/*
- * Report a missing-'>' syntax error.
- */
-static char *unclosed_directive(cmd_parms *cmd)
-{
- return apr_pstrcat(cmd->pool, cmd->cmd->name,
- "> directive missing closing '>'", NULL);
-}
-
-AP_CORE_DECLARE_NONSTD(const char *) ap_limit_section(cmd_parms *cmd,
- void *dummy,
- const char *arg)
-{
- const char *endp = ap_strrchr_c(arg, '>');
- const char *limited_methods;
- void *tog = cmd->cmd->cmd_data;
- apr_int64_t limited = 0;
- const char *errmsg;
-
- const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
- if (err != NULL) {
- return err;
- }
-
- if (endp == NULL) {
- return unclosed_directive(cmd);
- }
-
- limited_methods = apr_pstrndup(cmd->pool, arg, endp - arg);
-
- while (limited_methods[0]) {
- char *method = ap_getword_conf(cmd->pool, &limited_methods);
- int methnum;
-
- /* check for builtin or module registered method number */
- methnum = ap_method_number_of(method);
-
- if (methnum == M_TRACE && !tog) {
- return "TRACE cannot be controlled by ";
- }
- else if (methnum == M_INVALID) {
- /* method has not been registered yet, but resorce restriction
- * is always checked before method handling, so register it.
- */
- methnum = ap_method_register(cmd->pool, method);
- }
-
- limited |= (AP_METHOD_BIT << methnum);
- }
-
- /* Killing two features with one function,
- * if (tog == NULL) , else
- */
- cmd->limited = tog ? ~limited : limited;
-
- errmsg = ap_walk_config(cmd->directive->first_child, cmd, cmd->context);
-
- cmd->limited = -1;
-
- return errmsg;
-}
-
-/* XXX: Bogus - need to do this differently (at least OS2/Netware suffer
- * the same problem!!!
- * We use this in and , to ensure that
- * people don't get bitten by wrong-cased regex matches
- */
-
-#ifdef WIN32
-#define USE_ICASE REG_ICASE
-#else
-#define USE_ICASE 0
-#endif
-
-static const char *dirsection(cmd_parms *cmd, void *mconfig, const char *arg)
-{
- const char *errmsg;
- const char *endp = ap_strrchr_c(arg, '>');
- int old_overrides = cmd->override;
- char *old_path = cmd->path;
- core_dir_config *conf;
- ap_conf_vector_t *new_dir_conf = ap_create_per_dir_config(cmd->pool);
- regex_t *r = NULL;
- const command_rec *thiscmd = cmd->cmd;
-
- const char *err = ap_check_cmd_context(cmd,
- NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
- if (err != NULL) {
- return err;
- }
-
- if (endp == NULL) {
- return unclosed_directive(cmd);
- }
-
- arg = apr_pstrndup(cmd->pool, arg, endp - arg);
-
- if (!arg) {
- if (thiscmd->cmd_data)
- return " block must specify a path";
- else
- return " block must specify a path";
- }
-
- cmd->path = ap_getword_conf(cmd->pool, &arg);
- cmd->override = OR_ALL|ACCESS_CONF;
-
- if (!strcmp(cmd->path, "~")) {
- cmd->path = ap_getword_conf(cmd->pool, &arg);
- if (!cmd->path)
- return " block must specify a path";
- r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED|USE_ICASE);
- if (!r) {
- return "Regex could not be compiled";
- }
- }
- else if (thiscmd->cmd_data) { /* */
- r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED|USE_ICASE);
- if (!r) {
- return "Regex could not be compiled";
- }
- }
- else if (!strcmp(cmd->path, "/") == 0)
- {
- char *newpath;
-
- /*
- * Ensure that the pathname is canonical, and append the trailing /
- */
- 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, "path,
- "\"> path is invalid.", NULL);
- }
-
- cmd->path = newpath;
- if (cmd->path[strlen(cmd->path) - 1] != '/')
- cmd->path = apr_pstrcat(cmd->pool, cmd->path, "/", NULL);
- }
-
- /* initialize our config and fetch it */
- conf = ap_set_config_vectors(cmd->server, new_dir_conf, cmd->path,
- &core_module, cmd->pool);
-
- errmsg = ap_walk_config(cmd->directive->first_child, cmd, new_dir_conf);
- if (errmsg != NULL)
- return errmsg;
-
- conf->r = r;
- conf->d = cmd->path;
- conf->d_is_fnmatch = (apr_fnmatch_test(conf->d) != 0);
-
- /* Make this explicit - the "/" root has 0 elements, that is, we
- * will always merge it, and it will always sort and merge first.
- * All others are sorted and tested by the number of slashes.
- */
- if (strcmp(conf->d, "/") == 0)
- conf->d_components = 0;
- else
- conf->d_components = ap_count_dirs(conf->d);
-
- ap_add_per_dir_conf(cmd->server, new_dir_conf);
-
- if (*arg != '\0') {
- return apr_pstrcat(cmd->pool, "Multiple ", thiscmd->name,
- "> arguments not (yet) supported.", NULL);
- }
-
- cmd->path = old_path;
- cmd->override = old_overrides;
-
- return NULL;
-}
-
-static const char *urlsection(cmd_parms *cmd, void *mconfig, const char *arg)
-{
- const char *errmsg;
- const char *endp = ap_strrchr_c(arg, '>');
- int old_overrides = cmd->override;
- char *old_path = cmd->path;
- core_dir_config *conf;
- regex_t *r = NULL;
- const command_rec *thiscmd = cmd->cmd;
- ap_conf_vector_t *new_url_conf = ap_create_per_dir_config(cmd->pool);
- const char *err = ap_check_cmd_context(cmd,
- NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
- if (err != NULL) {
- return err;
- }
-
- if (endp == NULL) {
- return unclosed_directive(cmd);
- }
-
- arg = apr_pstrndup(cmd->pool, arg, endp - arg);
-
- cmd->path = ap_getword_conf(cmd->pool, &arg);
- cmd->override = OR_ALL|ACCESS_CONF;
-
- if (thiscmd->cmd_data) { /* */
- r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED);
- if (!r) {
- return "Regex could not be compiled";
- }
- }
- else if (!strcmp(cmd->path, "~")) {
- cmd->path = ap_getword_conf(cmd->pool, &arg);
- r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED);
- if (!r) {
- return "Regex could not be compiled";
- }
- }
-
- /* initialize our config and fetch it */
- conf = ap_set_config_vectors(cmd->server, new_url_conf, cmd->path,
- &core_module, cmd->pool);
-
- errmsg = ap_walk_config(cmd->directive->first_child, cmd, new_url_conf);
- if (errmsg != NULL)
- return errmsg;
-
- conf->d = apr_pstrdup(cmd->pool, cmd->path); /* No mangling, please */
- conf->d_is_fnmatch = apr_fnmatch_test(conf->d) != 0;
- conf->r = r;
-
- ap_add_per_url_conf(cmd->server, new_url_conf);
-
- if (*arg != '\0') {
- return apr_pstrcat(cmd->pool, "Multiple ", thiscmd->name,
- "> arguments not (yet) supported.", NULL);
- }
-
- cmd->path = old_path;
- cmd->override = old_overrides;
-
- return NULL;
-}
-
-static const char *filesection(cmd_parms *cmd, void *mconfig, const char *arg)
-{
- const char *errmsg;
- const char *endp = ap_strrchr_c(arg, '>');
- int old_overrides = cmd->override;
- char *old_path = cmd->path;
- core_dir_config *conf;
- regex_t *r = NULL;
- const command_rec *thiscmd = cmd->cmd;
- core_dir_config *c = mconfig;
- ap_conf_vector_t *new_file_conf = ap_create_per_dir_config(cmd->pool);
- const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT|NOT_IN_LOCATION);
-
- if (err != NULL) {
- return err;
- }
-
- if (endp == NULL) {
- return unclosed_directive(cmd);
- }
-
- arg = apr_pstrndup(cmd->pool, arg, endp - arg);
-
- cmd->path = ap_getword_conf(cmd->pool, &arg);
- /* Only if not an .htaccess file */
- if (!old_path) {
- cmd->override = OR_ALL|ACCESS_CONF;
- }
-
- if (thiscmd->cmd_data) { /* */
- r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED|USE_ICASE);
- if (!r) {
- return "Regex could not be compiled";
- }
- }
- else if (!strcmp(cmd->path, "~")) {
- cmd->path = ap_getword_conf(cmd->pool, &arg);
- r = ap_pregcomp(cmd->pool, cmd->path, REG_EXTENDED|USE_ICASE);
- if (!r) {
- return "Regex could not be compiled";
- }
- }
- else {
- char *newpath;
- /* Ensure that the pathname is canonical, but we
- * can't test the case/aliases without a fixed path */
- if (apr_filepath_merge(&newpath, "", cmd->path,
- 0, cmd->pool) != APR_SUCCESS)
- return apr_pstrcat(cmd->pool, "path,
- "\"> is invalid.", NULL);
- cmd->path = newpath;
- }
-
- /* initialize our config and fetch it */
- conf = ap_set_config_vectors(cmd->server, new_file_conf, cmd->path,
- &core_module, cmd->pool);
-
- errmsg = ap_walk_config(cmd->directive->first_child, cmd, new_file_conf);
- if (errmsg != NULL)
- return errmsg;
-
- conf->d = cmd->path;
- conf->d_is_fnmatch = apr_fnmatch_test(conf->d) != 0;
- conf->r = r;
-
- ap_add_file_conf(c, new_file_conf);
-
- if (*arg != '\0') {
- return apr_pstrcat(cmd->pool, "Multiple ", thiscmd->name,
- "> arguments not (yet) supported.", NULL);
- }
-
- cmd->path = old_path;
- cmd->override = old_overrides;
-
- return NULL;
-}
-
-static const char *start_ifmod(cmd_parms *cmd, void *mconfig, const char *arg)
-{
- const char *endp = ap_strrchr_c(arg, '>');
- int not = (arg[0] == '!');
- module *found;
-
- if (endp == NULL) {
- return unclosed_directive(cmd);
- }
-
- arg = apr_pstrndup(cmd->pool, arg, endp - arg);
-
- if (not) {
- arg++;
- }
-
- found = ap_find_linked_module(arg);
-
- /* search prelinked stuff */
- if (!found) {
- ap_module_symbol_t *current = ap_prelinked_module_symbols;
-
- for (; current->name; ++current) {
- if (!strcmp(current->name, arg)) {
- found = current->modp;
- break;
- }
- }
- }
-
- /* search dynamic stuff */
- if (!found) {
- APR_OPTIONAL_FN_TYPE(ap_find_loaded_module_symbol) *check_symbol =
- APR_RETRIEVE_OPTIONAL_FN(ap_find_loaded_module_symbol);
-
- if (check_symbol) {
- found = check_symbol(cmd->server, arg);
- }
- }
-
- if ((!not && found) || (not && !found)) {
- ap_directive_t *parent = NULL;
- ap_directive_t *current = NULL;
- const char *retval;
-
- retval = ap_build_cont_config(cmd->pool, cmd->temp_pool, cmd,
- ¤t, &parent, "elts;
- for (i = 0; i < ap_server_config_defines->nelts; i++) {
- if (strcmp(defines[i], name) == 0) {
- return 1;
- }
- }
-
- return 0;
-}
-
-static const char *start_ifdefine(cmd_parms *cmd, void *dummy, const char *arg)
-{
- const char *endp;
- int defined;
- int not = 0;
-
- endp = ap_strrchr_c(arg, '>');
- if (endp == NULL) {
- return unclosed_directive(cmd);
- }
-
- arg = apr_pstrndup(cmd->pool, arg, endp - arg);
-
- if (arg[0] == '!') {
- not = 1;
- arg++;
- }
-
- defined = ap_exists_config_define(arg);
- if ((!not && defined) || (not && !defined)) {
- ap_directive_t *parent = NULL;
- ap_directive_t *current = NULL;
- const char *retval;
-
- retval = ap_build_cont_config(cmd->pool, cmd->temp_pool, cmd,
- ¤t, &parent, " business */
-
-static const char *virtualhost_section(cmd_parms *cmd, void *dummy,
- const char *arg)
-{
- server_rec *main_server = cmd->server, *s;
- const char *errmsg;
- const char *endp = ap_strrchr_c(arg, '>');
- apr_pool_t *p = cmd->pool;
-
- const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
- if (err != NULL) {
- return err;
- }
-
- if (endp == NULL) {
- return unclosed_directive(cmd);
- }
-
- arg = apr_pstrndup(cmd->pool, arg, endp - arg);
-
- /* FIXME: There's another feature waiting to happen here -- since you
- can now put multiple addresses/names on a single
- you might want to use it to group common definitions and then
- define other "subhosts" with their individual differences. But
- personally I'd rather just do it with a macro preprocessor. -djg */
- if (main_server->is_virtual) {
- return " doesn't nest!";
- }
-
- errmsg = ap_init_virtual_host(p, arg, main_server, &s);
- if (errmsg) {
- return errmsg;
- }
-
- s->next = main_server->next;
- main_server->next = s;
-
- s->defn_name = cmd->directive->filename;
- s->defn_line_number = cmd->directive->line_num;
-
- cmd->server = s;
-
- errmsg = ap_walk_config(cmd->directive->first_child, cmd,
- s->lookup_defaults);
-
- cmd->server = main_server;
-
- return errmsg;
-}
-
-static const char *set_server_alias(cmd_parms *cmd, void *dummy,
- const char *arg)
-{
- if (!cmd->server->names) {
- return "ServerAlias only used in ";
- }
-
- while (*arg) {
- char **item, *name = ap_getword_conf(cmd->pool, &arg);
-
- if (ap_is_matchexp(name)) {
- item = (char **)apr_array_push(cmd->server->wild_names);
- }
- else {
- item = (char **)apr_array_push(cmd->server->names);
- }
-
- *item = name;
- }
-
- return NULL;
-}
-
-static const char *set_server_string_slot(cmd_parms *cmd, void *dummy,
- const char *arg)
-{
- /* This one's pretty generic... */
-
- int offset = (int)(long)cmd->info;
- char *struct_ptr = (char *)cmd->server;
-
- const char *err = ap_check_cmd_context(cmd,
- NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
- if (err != NULL) {
- return err;
- }
-
- *(const char **)(struct_ptr + offset) = arg;
- return NULL;
-}
-
-static const char *server_hostname_port(cmd_parms *cmd, void *dummy, const char *arg)
-{
- const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
- const char *portstr;
- int port;
-
- if (err != NULL) {
- return err;
- }
-
- portstr = ap_strchr_c(arg, ':');
- if (portstr) {
- cmd->server->server_hostname = apr_pstrndup(cmd->pool, arg,
- portstr - arg);
- portstr++;
- port = atoi(portstr);
- if (port <= 0 || port >= 65536) { /* 65536 == 1<<16 */
- return apr_pstrcat(cmd->temp_pool, "The port number \"", arg,
- "\" is outside the appropriate range "
- "(i.e., 1..65535).", NULL);
- }
- }
- else {
- cmd->server->server_hostname = apr_pstrdup(cmd->pool, arg);
- port = 0;
- }
-
- cmd->server->port = port;
- return NULL;
-}
-
-static const char *set_signature_flag(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->server_signature = srv_sig_on;
- }
- else if (strcasecmp(arg, "Off") == 0) {
- d->server_signature = srv_sig_off;
- }
- else if (strcasecmp(arg, "EMail") == 0) {
- d->server_signature = srv_sig_withmail;
- }
- else {
- return "ServerSignature: use one of: off | on | email";
- }
-
- return NULL;
-}
-
-static const char *set_server_root(cmd_parms *cmd, void *dummy,
- const char *arg)
-{
- const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
-
- if (err != NULL) {
- return err;
- }
-
- if ((apr_filepath_merge((char**)&ap_server_root, NULL, arg,
- APR_FILEPATH_TRUENAME, cmd->pool) != APR_SUCCESS)
- || !ap_is_directory(cmd->pool, ap_server_root)) {
- return "ServerRoot must be a valid directory";
- }
-
- return NULL;
-}
-
-static const char *set_timeout(cmd_parms *cmd, void *dummy, const char *arg)
-{
- const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
-
- if (err != NULL) {
- return err;
- }
-
- cmd->server->timeout = apr_time_from_sec(atoi(arg));
- return NULL;
-}
-
-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);
-
- if (err != NULL) {
- return err;
- }
-
- d->allow_encoded_slashes = arg != 0;
- return NULL;
-}
-
-static const char *set_hostname_lookups(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")) {
- d->hostname_lookups = HOSTNAME_LOOKUP_ON;
- }
- else if (!strcasecmp(arg, "off")) {
- d->hostname_lookups = HOSTNAME_LOOKUP_OFF;
- }
- else if (!strcasecmp(arg, "double")) {
- d->hostname_lookups = HOSTNAME_LOOKUP_DOUBLE;
- }
- else {
- return "parameter must be 'on', 'off', or 'double'";
- }
-
- return NULL;
-}
-
-static const char *set_serverpath(cmd_parms *cmd, void *dummy,
- const char *arg)
-{
- const char *err = ap_check_cmd_context(cmd, NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
-
- if (err != NULL) {
- return err;
- }
-
- cmd->server->path = arg;
- cmd->server->pathlen = (int)strlen(arg);
- return NULL;
-}
-
-static const char *set_content_md5(cmd_parms *cmd, void *d_, int arg)
-{
- core_dir_config *d = d_;
- const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
-
- if (err != NULL) {
- return err;
- }
-
- d->content_md5 = arg != 0;
- return NULL;
-}
-
-static const char *set_accept_path_info(cmd_parms *cmd, void *d_, const char *arg)
-{
- core_dir_config *d = d_;
-
- if (strcasecmp(arg, "on") == 0) {
- d->accept_path_info = AP_REQ_ACCEPT_PATH_INFO;
- }
- else if (strcasecmp(arg, "off") == 0) {
- d->accept_path_info = AP_REQ_REJECT_PATH_INFO;
- }
- else if (strcasecmp(arg, "default") == 0) {
- d->accept_path_info = AP_REQ_DEFAULT_PATH_INFO;
- }
- else {
- return "AcceptPathInfo must be set to on, off or default";
- }
-
- return NULL;
-}
-
-static const char *set_use_canonical_name(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->use_canonical_name = USE_CANONICAL_NAME_ON;
- }
- else if (strcasecmp(arg, "off") == 0) {
- d->use_canonical_name = USE_CANONICAL_NAME_OFF;
- }
- else if (strcasecmp(arg, "dns") == 0) {
- d->use_canonical_name = USE_CANONICAL_NAME_DNS;
- }
- else {
- return "parameter must be 'on', 'off', or 'dns'";
- }
-
- return NULL;
-}
-
-
-static const char *include_config (cmd_parms *cmd, void *dummy,
- const char *name)
-{
- ap_directive_t *conftree = NULL;
- const char* conffile, *error;
- unsigned *recursion;
- void *data;
-
- apr_pool_userdata_get(&data, "ap_include_sentinel", cmd->pool);
- if (data) {
- recursion = data;
- }
- else {
- data = recursion = apr_palloc(cmd->pool, sizeof(*recursion));
- *recursion = 0;
- apr_pool_userdata_setn(data, "ap_include_sentinel", NULL, cmd->pool);
- }
-
- if (++*recursion > AP_MAX_INCLUDE_DEPTH) {
- *recursion = 0;
- return apr_psprintf(cmd->pool, "Exceeded maximum include depth of %u. "
- "You have probably a recursion somewhere.",
- AP_MAX_INCLUDE_DEPTH);
- }
-
- conffile = ap_server_root_relative(cmd->pool, name);
- if (!conffile) {
- *recursion = 0;
- return apr_pstrcat(cmd->pool, "Invalid Include path ",
- name, NULL);
- }
-
- error = ap_process_resource_config(cmd->server, conffile,
- &conftree, cmd->pool, cmd->temp_pool);
- if (error) {
- *recursion = 0;
- return error;
- }
-
- *(ap_directive_t **)dummy = conftree;
-
- /* recursion level done */
- if (*recursion) {
- --*recursion;
- }
-
- return NULL;
-}
-
-static const char *set_loglevel(cmd_parms *cmd, void *dummy, const char *arg)
-{
- char *str;
-
- const char *err = ap_check_cmd_context(cmd,
- NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
- if (err != NULL) {
- return err;
- }
-
- if ((str = ap_getword_conf(cmd->pool, &arg))) {
- if (!strcasecmp(str, "emerg")) {
- cmd->server->loglevel = APLOG_EMERG;
- }
- else if (!strcasecmp(str, "alert")) {
- cmd->server->loglevel = APLOG_ALERT;
- }
- else if (!strcasecmp(str, "crit")) {
- cmd->server->loglevel = APLOG_CRIT;
- }
- else if (!strcasecmp(str, "error")) {
- cmd->server->loglevel = APLOG_ERR;
- }
- else if (!strcasecmp(str, "warn")) {
- cmd->server->loglevel = APLOG_WARNING;
- }
- else if (!strcasecmp(str, "notice")) {
- cmd->server->loglevel = APLOG_NOTICE;
- }
- else if (!strcasecmp(str, "info")) {
- cmd->server->loglevel = APLOG_INFO;
- }
- else if (!strcasecmp(str, "debug")) {
- cmd->server->loglevel = APLOG_DEBUG;
- }
- else {
- return "LogLevel requires level keyword: one of "
- "emerg/alert/crit/error/warn/notice/info/debug";
- }
- }
- else {
- return "LogLevel requires level keyword";
- }
-
- return NULL;
-}
-
-AP_DECLARE(const char *) ap_psignature(const char *prefix, request_rec *r)
-{
- char sport[20];
- core_dir_config *conf;
-
- conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
- &core_module);
- if ((conf->server_signature == srv_sig_off)
- || (conf->server_signature == srv_sig_unset)) {
- return "";
- }
-
- 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, "",
- ap_get_server_version(),
- " Server at server->server_admin) ? "" : "mailto:",
- ap_escape_html(r->pool, r->server->server_admin),
- "\">",
- ap_escape_html(r->pool, ap_get_server_name(r)),
- " Port ", sport,
- "\n", NULL);
- }
-
- return apr_pstrcat(r->pool, prefix, "", ap_get_server_version(),
- " Server at ",
- ap_escape_html(r->pool, ap_get_server_name(r)),
- " Port ", sport,
- "\n", NULL);
-}
-
-/*
- * Load an authorisation realm into our location configuration, applying the
- * usual rules that apply to realms.
- */
-static const char *set_authname(cmd_parms *cmd, void *mconfig,
- const char *word1)
-{
- core_dir_config *aconfig = (core_dir_config *)mconfig;
-
- aconfig->ap_auth_name = ap_escape_quotes(cmd->pool, word1);
- return NULL;
-}
-
-/*
- * Handle a request to include the server's OS platform in the Server
- * response header field (the ServerTokens directive). Unfortunately
- * this requires a new global in order to communicate the setting back to
- * http_main so it can insert the information in the right place in the
- * string.
- */
-
-static char *server_version = NULL;
-static int version_locked = 0;
-
-enum server_token_type {
- 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;
-
-static apr_status_t reset_version(void *dummy)
-{
- version_locked = 0;
- ap_server_tokens = SrvTk_FULL;
- server_version = NULL;
- return APR_SUCCESS;
-}
-
-AP_DECLARE(void) ap_get_server_revision(ap_version_t *version)
-{
- version->major = AP_SERVER_MAJORVERSION_NUMBER;
- version->minor = AP_SERVER_MINORVERSION_NUMBER;
- version->patch = AP_SERVER_PATCHLEVEL_NUMBER;
- version->add_string = AP_SERVER_ADD_STRING;
-}
-
-AP_DECLARE(const char *) ap_get_server_version(void)
-{
- return (server_version ? server_version : AP_SERVER_BASEVERSION);
-}
-
-AP_DECLARE(void) ap_add_version_component(apr_pool_t *pconf, const char *component)
-{
- if (! version_locked) {
- /*
- * If the version string is null, register our cleanup to reset the
- * pointer on pool destruction. We also know that, if NULL,
- * we are adding the original SERVER_BASEVERSION string.
- */
- if (server_version == NULL) {
- apr_pool_cleanup_register(pconf, NULL, reset_version,
- apr_pool_cleanup_null);
- server_version = apr_pstrdup(pconf, component);
- }
- else {
- /*
- * Tack the given component identifier to the end of
- * the existing string.
- */
- server_version = apr_pstrcat(pconf, server_version, " ",
- component, NULL);
- }
- }
-}
-
-/*
- * This routine adds the real server base identity to the version string,
- * and then locks out changes until the next reconfig.
- */
-static void ap_set_version(apr_pool_t *pconf)
-{
- if (ap_server_tokens == SrvTk_PRODUCT_ONLY) {
- ap_add_version_component(pconf, AP_SERVER_BASEPRODUCT);
- }
- 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 ")");
- }
-
- /*
- * Lock the server_version string if we're not displaying
- * the full set of tokens
- */
- if (ap_server_tokens != SrvTk_FULL) {
- version_locked++;
- }
-}
-
-static const char *set_serv_tokens(cmd_parms *cmd, void *dummy,
- const char *arg)
-{
- const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
-
- if (err != NULL) {
- return err;
- }
-
- if (!strcasecmp(arg, "OS")) {
- ap_server_tokens = SrvTk_OS;
- }
- else if (!strcasecmp(arg, "Min") || !strcasecmp(arg, "Minimal")) {
- 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;
- }
- else {
- ap_server_tokens = SrvTk_FULL;
- }
-
- return NULL;
-}
-
-static const char *set_limit_req_line(cmd_parms *cmd, void *dummy,
- const char *arg)
-{
- const char *err = ap_check_cmd_context(cmd,
- NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
- int lim;
-
- if (err != NULL) {
- return err;
- }
-
- lim = atoi(arg);
- if (lim < 0) {
- return apr_pstrcat(cmd->temp_pool, "LimitRequestLine \"", arg,
- "\" must be a non-negative integer", NULL);
- }
-
- cmd->server->limit_req_line = lim;
- return NULL;
-}
-
-static const char *set_limit_req_fieldsize(cmd_parms *cmd, void *dummy,
- const char *arg)
-{
- const char *err = ap_check_cmd_context(cmd,
- NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
- int lim;
-
- if (err != NULL) {
- return err;
- }
-
- lim = atoi(arg);
- if (lim < 0) {
- return apr_pstrcat(cmd->temp_pool, "LimitRequestFieldsize \"", arg,
- "\" must be a non-negative integer (0 = no limit)",
- NULL);
- }
-
- if (lim > DEFAULT_LIMIT_REQUEST_FIELDSIZE) {
- return apr_psprintf(cmd->temp_pool, "LimitRequestFieldsize \"%s\" "
- "must not exceed the precompiled maximum of %d",
- arg, DEFAULT_LIMIT_REQUEST_FIELDSIZE);
- }
-
- cmd->server->limit_req_fieldsize = lim;
- return NULL;
-}
-
-static const char *set_limit_req_fields(cmd_parms *cmd, void *dummy,
- const char *arg)
-{
- const char *err = ap_check_cmd_context(cmd,
- NOT_IN_DIR_LOC_FILE|NOT_IN_LIMIT);
- int lim;
-
- if (err != NULL) {
- return err;
- }
-
- lim = atoi(arg);
- if (lim < 0) {
- return apr_pstrcat(cmd->temp_pool, "LimitRequestFields \"", arg,
- "\" must be a non-negative integer (0 = no limit)",
- NULL);
- }
-
- cmd->server->limit_req_fields = lim;
- return NULL;
-}
-
-static const char *set_limit_req_body(cmd_parms *cmd, void *conf_,
- const char *arg)
-{
- core_dir_config *conf = conf_;
- const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
- char *errp;
-
- if (err != NULL) {
- return err;
- }
-
- if (APR_SUCCESS != apr_strtoff(&conf->limit_req_body, arg, &errp, 10)) {
- return "LimitRequestBody argument is not parsable.";
- }
- if (*errp || conf->limit_req_body < 0) {
- return "LimitRequestBody requires a non-negative integer.";
- }
-
- return NULL;
-}
-
-static const char *set_limit_xml_req_body(cmd_parms *cmd, void *conf_,
- const char *arg)
-{
- core_dir_config *conf = conf_;
- const char *err = ap_check_cmd_context(cmd, NOT_IN_LIMIT);
-
- if (err != NULL) {
- return err;
- }
-
- conf->limit_xml_body = atol(arg);
- if (conf->limit_xml_body < 0)
- return "LimitXMLRequestBody requires a non-negative integer.";
-
- return NULL;
-}
-
-AP_DECLARE(size_t) ap_get_limit_xml_body(const request_rec *r)
-{
- core_dir_config *conf;
-
- conf = ap_get_module_config(r->per_dir_config, &core_module);
- if (conf->limit_xml_body == AP_LIMIT_UNSET)
- return AP_DEFAULT_LIMIT_XML_BODY;
-
- return (size_t)conf->limit_xml_body;
-}
-
-#if !defined (RLIMIT_CPU) || !(defined (RLIMIT_DATA) || defined (RLIMIT_VMEM) || defined(RLIMIT_AS)) || !defined (RLIMIT_NPROC)
-static const char *no_set_limit(cmd_parms *cmd, void *conf_,
- const char *arg, const char *arg2)
-{
- ap_log_error(APLOG_MARK, APLOG_ERR, 0, cmd->server,
- "%s not supported on this platform", cmd->cmd->name);
-
- return NULL;
-}
-#endif
-
-#ifdef RLIMIT_CPU
-static const char *set_limit_cpu(cmd_parms *cmd, void *conf_,
- const char *arg, const char *arg2)
-{
- core_dir_config *conf = conf_;
-
- unixd_set_rlimit(cmd, &conf->limit_cpu, arg, arg2, RLIMIT_CPU);
- return NULL;
-}
-#endif
-
-#if defined (RLIMIT_DATA) || defined (RLIMIT_VMEM) || defined(RLIMIT_AS)
-static const char *set_limit_mem(cmd_parms *cmd, void *conf_,
- const char *arg, const char * arg2)
-{
- core_dir_config *conf = conf_;
-
-#if defined(RLIMIT_AS)
- unixd_set_rlimit(cmd, &conf->limit_mem, arg, arg2 ,RLIMIT_AS);
-#elif defined(RLIMIT_DATA)
- unixd_set_rlimit(cmd, &conf->limit_mem, arg, arg2, RLIMIT_DATA);
-#elif defined(RLIMIT_VMEM)
- unixd_set_rlimit(cmd, &conf->limit_mem, arg, arg2, RLIMIT_VMEM);
-#endif
-
- return NULL;
-}
-#endif
-
-#ifdef RLIMIT_NPROC
-static const char *set_limit_nproc(cmd_parms *cmd, void *conf_,
- const char *arg, const char * arg2)
-{
- core_dir_config *conf = conf_;
-
- unixd_set_rlimit(cmd, &conf->limit_nproc, arg, arg2, RLIMIT_NPROC);
- return NULL;
-}
-#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 = NULL;
- const char *filter_name;
-
- if (!conf->ct_output_filters) {
- conf->ct_output_filters = apr_hash_make(cmd->pool);
- old = NULL;
- }
- 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;
- }
- }
- }
-
- 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) {
- old->next = new;
- }
- else {
- apr_hash_set(conf->ct_output_filters, arg2,
- APR_HASH_KEY_STRING, new);
- }
- old = new;
- }
-
- if (!new) {
- return "invalid filter name";
- }
-
- return NULL;
-}
-/*
- * 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 reliably know the content-type.
- */
-void ap_add_output_filters_by_type(request_rec *r)
-{
- core_dir_config *conf;
- const char *ctype;
-
- conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
- &core_module);
-
- /* We can't do anything with proxy requests, no content-types or if
- * we don't have a filter configured.
- */
- if (r->proxyreq != PROXYREQ_NONE || !r->content_type ||
- !conf->ct_output_filters) {
- return;
- }
-
- /* 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);
- while (ct_filter) {
- ap_add_output_filter(ct_filter->name, NULL, r, r->connection);
- ct_filter = ct_filter->next;
- }
- }
-
- return;
-}
-
-static apr_status_t writev_it_all(apr_socket_t *s,
- struct iovec *vec, int nvec,
- apr_size_t len, apr_size_t *nbytes)
-{
- apr_size_t bytes_written = 0;
- apr_status_t rv;
- apr_size_t n = len;
- int i = 0;
-
- *nbytes = 0;
-
- /* XXX handle checking for non-blocking socket */
- while (bytes_written != len) {
- rv = apr_socket_sendv(s, vec + i, nvec - i, &n);
- *nbytes += n;
- bytes_written += n;
- if (rv != APR_SUCCESS)
- return rv;
-
- /* If the write did not complete, adjust the iovecs and issue
- * apr_socket_sendv again
- */
- if (bytes_written < len) {
- /* Skip over the vectors that have already been written */
- apr_size_t cnt = vec[i].iov_len;
- while (n >= cnt && i + 1 < nvec) {
- i++;
- cnt += vec[i].iov_len;
- }
-
- if (n < cnt) {
- /* Handle partial write of vec i */
- vec[i].iov_base = (char *) vec[i].iov_base +
- (vec[i].iov_len - (cnt - n));
- vec[i].iov_len = cnt -n;
- }
- }
-
- n = len - bytes_written;
- }
-
- return APR_SUCCESS;
-}
-
-/* sendfile_it_all()
- * send the entire file using sendfile()
- * handle partial writes
- * return only when all bytes have been sent or an error is encountered.
- */
-
-#if APR_HAS_SENDFILE
-static apr_status_t sendfile_it_all(core_net_rec *c,
- apr_file_t *fd,
- apr_hdtr_t *hdtr,
- 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_interval_time_t timeout = 0;
-#endif
-
- 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_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 */
- }
-
- AP_DEBUG_ASSERT(total_bytes_left > 0 && tmplen > 0);
-
- /* partial write, oooh noooo...
- * Skip over any header data which was written
- */
- while (tmplen && hdtr->numheaders) {
- if (tmplen >= hdtr->headers[0].iov_len) {
- tmplen -= hdtr->headers[0].iov_len;
- --hdtr->numheaders;
- ++hdtr->headers;
- }
- else {
- char *iov_base = (char *)hdtr->headers[0].iov_base;
-
- hdtr->headers[0].iov_len -= tmplen;
- iov_base += tmplen;
- hdtr->headers[0].iov_base = iov_base;
- tmplen = 0;
- }
- }
-
- /* Skip over any file data which was written */
-
- if (tmplen <= file_bytes_left) {
- file_offset += tmplen;
- file_bytes_left -= tmplen;
- continue;
- }
-
- tmplen -= file_bytes_left;
- file_bytes_left = 0;
- file_offset = 0;
-
- /* Skip over any trailer data which was written */
-
- while (tmplen && hdtr->numtrailers) {
- if (tmplen >= hdtr->trailers[0].iov_len) {
- tmplen -= hdtr->trailers[0].iov_len;
- --hdtr->numtrailers;
- ++hdtr->trailers;
- }
- else {
- char *iov_base = (char *)hdtr->trailers[0].iov_base;
-
- hdtr->trailers[0].iov_len -= tmplen;
- iov_base += tmplen;
- hdtr->trailers[0].iov_base = iov_base;
- tmplen = 0;
- }
- }
- } while (1);
-}
-#endif
-
-/*
- * emulate_sendfile()
- * Sends the contents of file fd along with header/trailer bytes, if any,
- * to the network. emulate_sendfile will return only when all the bytes have been
- * sent (i.e., it handles partial writes) or on a network error condition.
- */
-static apr_status_t emulate_sendfile(core_net_rec *c, apr_file_t *fd,
- apr_hdtr_t *hdtr, apr_off_t offset,
- apr_size_t length, apr_size_t *nbytes)
-{
- apr_status_t rv = APR_SUCCESS;
- apr_size_t togo; /* Remaining number of bytes in the file to send */
- apr_size_t sendlen = 0;
- apr_size_t bytes_sent;
- apr_int32_t i;
- apr_off_t o; /* Track the file offset for partial writes */
- char buffer[8192];
-
- *nbytes = 0;
-
- /* Send the headers
- * writev_it_all handles partial writes.
- * XXX: optimization... if headers are less than MIN_WRITE_SIZE, copy
- * them into buffer
- */
- if (hdtr && hdtr->numheaders > 0 ) {
- for (i = 0; i < hdtr->numheaders; i++) {
- sendlen += hdtr->headers[i].iov_len;
- }
-
- rv = writev_it_all(c->client_socket, hdtr->headers, hdtr->numheaders,
- sendlen, &bytes_sent);
- *nbytes += bytes_sent; /* track total bytes sent */
- }
-
- /* Seek the file to 'offset' */
- if (offset >= 0 && rv == APR_SUCCESS) {
- rv = apr_file_seek(fd, APR_SET, &offset);
- }
-
- /* Send the file, making sure to handle partial writes */
- togo = length;
- while (rv == APR_SUCCESS && togo) {
- sendlen = togo > sizeof(buffer) ? sizeof(buffer) : togo;
- o = 0;
- rv = apr_file_read(fd, buffer, &sendlen);
- while (rv == APR_SUCCESS && sendlen) {
- bytes_sent = sendlen;
- rv = apr_socket_send(c->client_socket, &buffer[o], &bytes_sent);
- *nbytes += 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 */
- togo -= bytes_sent; /* track how much of the file we've sent */
- }
- }
- }
-
- /* Send the trailers
- * XXX: optimization... if it will fit, send this on the last send in the
- * loop above
- */
- sendlen = 0;
- if ( rv == APR_SUCCESS && hdtr && hdtr->numtrailers > 0 ) {
- for (i = 0; i < hdtr->numtrailers; i++) {
- sendlen += hdtr->trailers[i].iov_len;
- }
- rv = writev_it_all(c->client_socket, hdtr->trailers, hdtr->numtrailers,
- sendlen, &bytes_sent);
- *nbytes += bytes_sent;
- }
-
- return rv;
-}
-
-/* Note --- ErrorDocument will now work from .htaccess files.
- * The AllowOverride of Fileinfo allows webmasters to turn it off
- */
-
-static const command_rec core_cmds[] = {
-
-/* Old access config file commands */
-
-AP_INIT_RAW_ARGS("server->module_config;
- core_server_config *conf = ap_get_module_config(sconf, &core_module);
- apr_status_t rv;
-
- /* XXX this seems too specific, this should probably become
- * some general-case test
- */
- if (r->proxyreq) {
- return HTTP_FORBIDDEN;
- }
- if (!r->uri || ((r->uri[0] != '/') && strcmp(r->uri, "*"))) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "Invalid URI in request %s", r->the_request);
- return HTTP_BAD_REQUEST;
- }
-
- if (r->server->path
- && !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'))
- {
- /* 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 ((rv = apr_filepath_merge(&r->filename, conf->ap_document_root, path,
- APR_FILEPATH_TRUENAME
- | APR_FILEPATH_SECUREROOT, r->pool))
- != APR_SUCCESS) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
- "Cannot map %s to file", r->the_request);
- return HTTP_FORBIDDEN;
- }
- r->canonical_filename = r->filename;
- }
- else {
- /*
- * Make sure that we do not mess up the translation by adding two
- * /'s in a row. This happens under windows when the document
- * root ends with a /
- */
- /* 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 ((rv = apr_filepath_merge(&r->filename, conf->ap_document_root, path,
- APR_FILEPATH_TRUENAME
- | APR_FILEPATH_SECUREROOT, r->pool))
- != APR_SUCCESS) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
- "Cannot map %s to file", r->the_request);
- return HTTP_FORBIDDEN;
- }
- r->canonical_filename = r->filename;
- }
-
- return OK;
-}
-
-/*****************************************************************
- *
- * Test the filesystem name through directory_walk and file_walk
+/*
+ * core_filters.c --- Core input/output network filters.
*/
-static int core_map_to_storage(request_rec *r)
-{
- int access_status;
-
- if ((access_status = ap_directory_walk(r))) {
- return access_status;
- }
-
- if ((access_status = ap_file_walk(r))) {
- return access_status;
- }
-
- return OK;
-}
-
-
-static int do_nothing(request_rec *r) { return OK; }
-
-
-static int core_override_type(request_rec *r)
-{
- core_dir_config *conf =
- (core_dir_config *)ap_get_module_config(r->per_dir_config,
- &core_module);
-
- /* Check for overrides with ForceType / SetHandler
- */
- if (conf->mime_type && strcmp(conf->mime_type, "none"))
- ap_set_content_type(r, (char*) conf->mime_type);
-
- if (conf->handler && strcmp(conf->handler, "none"))
- r->handler = conf->handler;
-
- /* Deal with the poor soul who is trying to force path_info to be
- * accepted within the core_handler, where they will let the subreq
- * address its contents. This is toggled by the user in the very
- * beginning of the fixup phase, so modules should override the user's
- * discretion in their own module fixup phase. It is tristate, if
- * the user doesn't specify, the result is 2 (which the module may
- * interpret to its own customary behavior.) It won't be touched
- * if the value is no longer undefined (2), so any module changing
- * the value prior to the fixup phase OVERRIDES the user's choice.
- */
- if ((r->used_path_info == AP_REQ_DEFAULT_PATH_INFO)
- && (conf->accept_path_info != 3)) {
- r->used_path_info = conf->accept_path_info;
- }
-
- return OK;
-}
-
-
-
-static int default_handler(request_rec *r)
-{
- conn_rec *c = r->connection;
- apr_bucket_brigade *bb;
- apr_bucket *e;
- core_dir_config *d;
- int errstatus;
- apr_file_t *fd = NULL;
- apr_status_t status;
- /* XXX if/when somebody writes a content-md5 filter we either need to
- * remove this support or coordinate when to use the filter vs.
- * when to use this code
- * The current choice of when to compute the md5 here matches the 1.3
- * support fairly closely (unlike 1.3, we don't handle computing md5
- * when the charset is translated).
- */
- int bld_content_md5;
-
- d = (core_dir_config *)ap_get_module_config(r->per_dir_config,
- &core_module);
- bld_content_md5 = (d->content_md5 & 1)
- && r->output_filters->frec->ftype != AP_FTYPE_RESOURCE;
-
- ap_allow_standard_methods(r, MERGE_ALLOW, M_GET, M_OPTIONS, M_POST, -1);
-
- /* If filters intend to consume the request body, they must
- * register an InputFilter to slurp the contents of the POST
- * data from the POST input stream. It no longer exists when
- * the output filters are invoked by the default handler.
- */
- if ((errstatus = ap_discard_request_body(r)) != OK) {
- return errstatus;
- }
-
- if (r->method_number == M_GET || r->method_number == M_POST) {
- if (r->finfo.filetype == 0) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "File does not exist: %s", r->filename);
- return HTTP_NOT_FOUND;
- }
-
- /* Don't try to serve a dir. Some OSs do weird things with
- * raw I/O on a dir.
- */
- if (r->finfo.filetype == APR_DIR) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "Attempt to serve directory: %s", r->filename);
- return HTTP_NOT_FOUND;
- }
-
- if ((r->used_path_info != AP_REQ_ACCEPT_PATH_INFO) &&
- r->path_info && *r->path_info)
- {
- /* default to reject */
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "File does not exist: %s",
- apr_pstrcat(r->pool, r->filename, r->path_info, NULL));
- return HTTP_NOT_FOUND;
- }
-
- /* 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;
- }
-
- ap_update_mtime(r, r->finfo.mtime);
- ap_set_last_modified(r);
- ap_set_etag(r);
- apr_table_setn(r->headers_out, "Accept-Ranges", "bytes");
- ap_set_content_length(r, r->finfo.size);
- if ((errstatus = ap_meets_conditions(r)) != OK) {
- apr_file_close(fd);
- return errstatus;
- }
-
- if (bld_content_md5) {
- apr_table_setn(r->headers_out, "Content-MD5",
- ap_md5digest(r->pool, fd));
- }
+#include "apr.h"
+#include "apr_strings.h"
+#include "apr_lib.h"
+#include "apr_fnmatch.h"
+#include "apr_hash.h"
+#include "apr_thread_proc.h" /* for RLIMIT stuff */
+#include "apr_hooks.h"
- bb = apr_brigade_create(r->pool, c->bucket_alloc);
+#define APR_WANT_IOVEC
+#define APR_WANT_STRFUNC
+#define APR_WANT_MEMFUNC
+#include "apr_want.h"
- /* For platforms where the size of the file may be larger than
- * that which can be stored in a single bucket (where the
- * length field is an apr_size_t), split it into several
- * buckets: */
- if (sizeof(apr_off_t) > sizeof(apr_size_t)
- && r->finfo.size > AP_MAX_SENDFILE) {
- apr_off_t fsize = r->finfo.size;
- e = apr_bucket_file_create(fd, 0, AP_MAX_SENDFILE, r->pool,
- c->bucket_alloc);
- while (fsize > AP_MAX_SENDFILE) {
- apr_bucket *ce;
- apr_bucket_copy(e, &ce);
- APR_BRIGADE_INSERT_TAIL(bb, ce);
- e->start += AP_MAX_SENDFILE;
- fsize -= AP_MAX_SENDFILE;
- }
- e->length = (apr_size_t)fsize; /* Resize just the last bucket */
- }
- else
- e = apr_bucket_file_create(fd, 0, (apr_size_t)r->finfo.size,
- r->pool, c->bucket_alloc);
-
-#if APR_HAS_MMAP
- if (d->enable_mmap == ENABLE_MMAP_OFF) {
- (void)apr_bucket_file_enable_mmap(e, 0);
- }
-#endif
- APR_BRIGADE_INSERT_TAIL(bb, e);
- e = apr_bucket_eos_create(c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(bb, e);
+#define CORE_PRIVATE
+#include "ap_config.h"
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_protocol.h" /* For index_of_response(). Grump. */
+#include "http_request.h"
+#include "http_vhost.h"
+#include "http_main.h" /* For the default_handler below... */
+#include "http_log.h"
+#include "util_md5.h"
+#include "http_connection.h"
+#include "apr_buckets.h"
+#include "util_filter.h"
+#include "util_ebcdic.h"
+#include "mpm.h"
+#include "mpm_common.h"
+#include "scoreboard.h"
+#include "mod_core.h"
+#include "mod_proxy.h"
+#include "ap_listen.h"
- return ap_pass_brigade(r->output_filters, bb);
- }
- else { /* unusual method (not GET or POST) */
- if (r->method_number == M_INVALID) {
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
- "Invalid method in request %s", r->the_request);
- return HTTP_NOT_IMPLEMENTED;
- }
+#include "mod_so.h" /* for ap_find_loaded_module_symbol */
- if (r->method_number == M_OPTIONS) {
- return ap_send_http_options(r);
- }
- return HTTP_METHOD_NOT_ALLOWED;
- }
-}
+#define AP_MIN_SENDFILE_BYTES (256)
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,
+
+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)
{
@@ -3784,7 +108,7 @@
} while (!APR_BRIGADE_EMPTY(b) && (e != APR_BRIGADE_SENTINEL(b))); \
} while (0)
-static int core_input_filter(ap_filter_t *f, apr_bucket_brigade *b,
+int core_input_filter(ap_filter_t *f, apr_bucket_brigade *b,
ap_input_mode_t mode, apr_read_type_e block,
apr_off_t readbytes)
{
@@ -3921,75 +245,287 @@
return APR_SUCCESS;
}
- /* read up to the amount they specified. */
- if (mode == AP_MODE_READBYTES || mode == AP_MODE_SPECULATIVE) {
- apr_bucket *e;
- apr_bucket_brigade *newbb;
+ /* read up to the amount they specified. */
+ if (mode == AP_MODE_READBYTES || mode == AP_MODE_SPECULATIVE) {
+ apr_bucket *e;
+ apr_bucket_brigade *newbb;
+
+ AP_DEBUG_ASSERT(readbytes > 0);
+
+ e = APR_BRIGADE_FIRST(ctx->b);
+ rv = apr_bucket_read(e, &str, &len, block);
+
+ if (APR_STATUS_IS_EAGAIN(rv)) {
+ return APR_SUCCESS;
+ }
+ else if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ else if (block == APR_BLOCK_READ && len == 0) {
+ /* We wanted to read some bytes in blocking mode. We read
+ * 0 bytes. Hence, we now assume we are EOS.
+ *
+ * When we are in normal mode, return an EOS bucket to the
+ * caller.
+ * When we are in speculative mode, leave ctx->b empty, so
+ * that the next call returns an EOS bucket.
+ */
+ apr_bucket_delete(e);
+
+ if (mode == AP_MODE_READBYTES) {
+ e = apr_bucket_eos_create(f->c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(b, e);
+ }
+ return APR_SUCCESS;
+ }
+
+ /* We can only return at most what we read. */
+ if (len < readbytes) {
+ readbytes = len;
+ }
+
+ rv = apr_brigade_partition(ctx->b, readbytes, &e);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+
+ /* Must do split before CONCAT */
+ newbb = apr_brigade_split(ctx->b, e);
+
+ if (mode == AP_MODE_READBYTES) {
+ APR_BRIGADE_CONCAT(b, ctx->b);
+ }
+ else if (mode == AP_MODE_SPECULATIVE) {
+ apr_bucket *copy_bucket;
+
+ for (e = APR_BRIGADE_FIRST(ctx->b);
+ e != APR_BRIGADE_SENTINEL(ctx->b);
+ e = APR_BUCKET_NEXT(e))
+ {
+ rv = apr_bucket_copy(e, ©_bucket);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ APR_BRIGADE_INSERT_TAIL(b, copy_bucket);
+ }
+ }
+
+ /* Take what was originally there and place it back on ctx->b */
+ APR_BRIGADE_CONCAT(ctx->b, newbb);
+ }
+ return APR_SUCCESS;
+}
+
+static apr_status_t writev_it_all(apr_socket_t *s,
+ struct iovec *vec, int nvec,
+ apr_size_t len, apr_size_t *nbytes)
+{
+ apr_size_t bytes_written = 0;
+ apr_status_t rv;
+ apr_size_t n = len;
+ int i = 0;
+
+ *nbytes = 0;
+
+ /* XXX handle checking for non-blocking socket */
+ while (bytes_written != len) {
+ rv = apr_socket_sendv(s, vec + i, nvec - i, &n);
+ *nbytes += n;
+ bytes_written += n;
+ if (rv != APR_SUCCESS)
+ return rv;
+
+ /* If the write did not complete, adjust the iovecs and issue
+ * apr_socket_sendv again
+ */
+ if (bytes_written < len) {
+ /* Skip over the vectors that have already been written */
+ apr_size_t cnt = vec[i].iov_len;
+ while (n >= cnt && i + 1 < nvec) {
+ i++;
+ cnt += vec[i].iov_len;
+ }
+
+ if (n < cnt) {
+ /* Handle partial write of vec i */
+ vec[i].iov_base = (char *) vec[i].iov_base +
+ (vec[i].iov_len - (cnt - n));
+ vec[i].iov_len = cnt -n;
+ }
+ }
+
+ n = len - bytes_written;
+ }
+
+ return APR_SUCCESS;
+}
+
+/* sendfile_it_all()
+ * send the entire file using sendfile()
+ * handle partial writes
+ * return only when all bytes have been sent or an error is encountered.
+ */
+
+#if APR_HAS_SENDFILE
+static apr_status_t sendfile_it_all(core_net_rec *c,
+ apr_file_t *fd,
+ apr_hdtr_t *hdtr,
+ 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_interval_time_t timeout = 0;
+#endif
+
+ AP_DEBUG_ASSERT((apr_socket_timeout_get(c->client_socket, &timeout)
+ == APR_SUCCESS)
+ && timeout > 0); /* socket must be in timeout mode */
- AP_DEBUG_ASSERT(readbytes > 0);
+ /* Reset the bytes_sent field */
+ *bytes_sent = 0;
- e = APR_BRIGADE_FIRST(ctx->b);
- rv = apr_bucket_read(e, &str, &len, block);
+ do {
+ apr_size_t tmplen = file_bytes_left;
- if (APR_STATUS_IS_EAGAIN(rv)) {
- return APR_SUCCESS;
- }
- else if (rv != APR_SUCCESS) {
- return rv;
+ 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 */
}
- else if (block == APR_BLOCK_READ && len == 0) {
- /* We wanted to read some bytes in blocking mode. We read
- * 0 bytes. Hence, we now assume we are EOS.
- *
- * When we are in normal mode, return an EOS bucket to the
- * caller.
- * When we are in speculative mode, leave ctx->b empty, so
- * that the next call returns an EOS bucket.
- */
- apr_bucket_delete(e);
- if (mode == AP_MODE_READBYTES) {
- e = apr_bucket_eos_create(f->c->bucket_alloc);
- APR_BRIGADE_INSERT_TAIL(b, e);
+ AP_DEBUG_ASSERT(total_bytes_left > 0 && tmplen > 0);
+
+ /* partial write, oooh noooo...
+ * Skip over any header data which was written
+ */
+ while (tmplen && hdtr->numheaders) {
+ if (tmplen >= hdtr->headers[0].iov_len) {
+ tmplen -= hdtr->headers[0].iov_len;
+ --hdtr->numheaders;
+ ++hdtr->headers;
+ }
+ else {
+ char *iov_base = (char *)hdtr->headers[0].iov_base;
+
+ hdtr->headers[0].iov_len -= tmplen;
+ iov_base += tmplen;
+ hdtr->headers[0].iov_base = iov_base;
+ tmplen = 0;
}
- return APR_SUCCESS;
}
- /* We can only return at most what we read. */
- if (len < readbytes) {
- readbytes = len;
+ /* Skip over any file data which was written */
+
+ if (tmplen <= file_bytes_left) {
+ file_offset += tmplen;
+ file_bytes_left -= tmplen;
+ continue;
}
- rv = apr_brigade_partition(ctx->b, readbytes, &e);
- if (rv != APR_SUCCESS) {
- return rv;
+ tmplen -= file_bytes_left;
+ file_bytes_left = 0;
+ file_offset = 0;
+
+ /* Skip over any trailer data which was written */
+
+ while (tmplen && hdtr->numtrailers) {
+ if (tmplen >= hdtr->trailers[0].iov_len) {
+ tmplen -= hdtr->trailers[0].iov_len;
+ --hdtr->numtrailers;
+ ++hdtr->trailers;
+ }
+ else {
+ char *iov_base = (char *)hdtr->trailers[0].iov_base;
+
+ hdtr->trailers[0].iov_len -= tmplen;
+ iov_base += tmplen;
+ hdtr->trailers[0].iov_base = iov_base;
+ tmplen = 0;
+ }
}
+ } while (1);
+}
+#endif
- /* Must do split before CONCAT */
- newbb = apr_brigade_split(ctx->b, e);
+/*
+ * emulate_sendfile()
+ * Sends the contents of file fd along with header/trailer bytes, if any,
+ * to the network. emulate_sendfile will return only when all the bytes have been
+ * sent (i.e., it handles partial writes) or on a network error condition.
+ */
+static apr_status_t emulate_sendfile(core_net_rec *c, apr_file_t *fd,
+ apr_hdtr_t *hdtr, apr_off_t offset,
+ apr_size_t length, apr_size_t *nbytes)
+{
+ apr_status_t rv = APR_SUCCESS;
+ apr_size_t togo; /* Remaining number of bytes in the file to send */
+ apr_size_t sendlen = 0;
+ apr_size_t bytes_sent;
+ apr_int32_t i;
+ apr_off_t o; /* Track the file offset for partial writes */
+ char buffer[8192];
- if (mode == AP_MODE_READBYTES) {
- APR_BRIGADE_CONCAT(b, ctx->b);
+ *nbytes = 0;
+
+ /* Send the headers
+ * writev_it_all handles partial writes.
+ * XXX: optimization... if headers are less than MIN_WRITE_SIZE, copy
+ * them into buffer
+ */
+ if (hdtr && hdtr->numheaders > 0 ) {
+ for (i = 0; i < hdtr->numheaders; i++) {
+ sendlen += hdtr->headers[i].iov_len;
}
- else if (mode == AP_MODE_SPECULATIVE) {
- apr_bucket *copy_bucket;
- for (e = APR_BRIGADE_FIRST(ctx->b);
- e != APR_BRIGADE_SENTINEL(ctx->b);
- e = APR_BUCKET_NEXT(e))
- {
- rv = apr_bucket_copy(e, ©_bucket);
- if (rv != APR_SUCCESS) {
- return rv;
- }
- APR_BRIGADE_INSERT_TAIL(b, copy_bucket);
+ rv = writev_it_all(c->client_socket, hdtr->headers, hdtr->numheaders,
+ sendlen, &bytes_sent);
+ *nbytes += bytes_sent; /* track total bytes sent */
+ }
+
+ /* Seek the file to 'offset' */
+ if (offset >= 0 && rv == APR_SUCCESS) {
+ rv = apr_file_seek(fd, APR_SET, &offset);
+ }
+
+ /* Send the file, making sure to handle partial writes */
+ togo = length;
+ while (rv == APR_SUCCESS && togo) {
+ sendlen = togo > sizeof(buffer) ? sizeof(buffer) : togo;
+ o = 0;
+ rv = apr_file_read(fd, buffer, &sendlen);
+ while (rv == APR_SUCCESS && sendlen) {
+ bytes_sent = sendlen;
+ rv = apr_socket_send(c->client_socket, &buffer[o], &bytes_sent);
+ *nbytes += 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 */
+ togo -= bytes_sent; /* track how much of the file we've sent */
}
}
+ }
- /* Take what was originally there and place it back on ctx->b */
- APR_BRIGADE_CONCAT(ctx->b, newbb);
+ /* Send the trailers
+ * XXX: optimization... if it will fit, send this on the last send in the
+ * loop above
+ */
+ sendlen = 0;
+ if ( rv == APR_SUCCESS && hdtr && hdtr->numtrailers > 0 ) {
+ for (i = 0; i < hdtr->numtrailers; i++) {
+ sendlen += hdtr->trailers[i].iov_len;
+ }
+ rv = writev_it_all(c->client_socket, hdtr->trailers, hdtr->numtrailers,
+ sendlen, &bytes_sent);
+ *nbytes += bytes_sent;
}
- return APR_SUCCESS;
+
+ return rv;
}
#define MAX_IOVEC_TO_WRITE 16
@@ -3997,9 +533,9 @@
/* 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;
+extern 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 core_output_filter(ap_filter_t *f, apr_bucket_brigade *b)
{
apr_status_t rv;
apr_bucket_brigade *more;
@@ -4391,248 +927,3 @@
return APR_SUCCESS;
}
-
-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 void core_insert_filter(request_rec *r)
-{
- core_dir_config *conf = (core_dir_config *)
- ap_get_module_config(r->per_dir_config,
- &core_module);
- const char *filter, *filters = conf->output_filters;
-
- if (filters) {
- while (*filters && (filter = ap_getword(r->pool, &filters, ';'))) {
- ap_add_output_filter(filter, NULL, r, r->connection);
- }
- }
-
- filters = conf->input_filters;
- if (filters) {
- while (*filters && (filter = ap_getword(r->pool, &filters, ';'))) {
- ap_add_input_filter(filter, NULL, r, r->connection);
- }
- }
-}
-
-static apr_size_t num_request_notes = AP_NUM_STD_NOTES;
-
-static apr_status_t reset_request_notes(void *dummy)
-{
- num_request_notes = AP_NUM_STD_NOTES;
- return APR_SUCCESS;
-}
-
-AP_DECLARE(apr_size_t) ap_register_request_note(void)
-{
- apr_pool_cleanup_register(apr_hook_global_pool, NULL, reset_request_notes,
- apr_pool_cleanup_null);
- return num_request_notes++;
-}
-
-AP_DECLARE(void **) ap_get_request_note(request_rec *r, apr_size_t note_num)
-{
- core_request_config *req_cfg;
-
- if (note_num >= num_request_notes) {
- return NULL;
- }
-
- req_cfg = (core_request_config *)
- ap_get_module_config(r->request_config, &core_module);
-
- if (!req_cfg) {
- return NULL;
- }
-
- return &(req_cfg->notes[note_num]);
-}
-
-static int core_create_req(request_rec *r)
-{
- /* Alloc the config struct and the array of request notes in
- * a single block for efficiency
- */
- core_request_config *req_cfg;
-
- 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);
- req_cfg->bb = main_req_cfg->bb;
- }
- else {
- req_cfg->bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
- if (!r->prev) {
- ap_add_input_filter_handle(ap_net_time_filter_handle,
- NULL, r, r->connection);
- }
- }
-
- ap_set_module_config(r->request_config, &core_module, req_cfg);
-
- /* Begin by presuming any module can make its own path_info assumptions,
- * until some module interjects and changes the value.
- */
- r->used_path_info = AP_REQ_DEFAULT_PATH_INFO;
-
- return OK;
-}
-
-static int core_create_proxy_req(request_rec *r, request_rec *pr)
-{
- return core_create_req(pr);
-}
-
-static conn_rec *core_create_conn(apr_pool_t *ptrans, server_rec *server,
- apr_socket_t *csd, long id, void *sbh,
- apr_bucket_alloc_t *alloc)
-{
- apr_status_t rv;
- 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);
-
- /* Got a connection structure, so initialize what fields we can
- * (the rest are zeroed out by pcalloc).
- */
- 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,
- "apr_socket_addr_get(APR_LOCAL)");
- apr_socket_close(csd);
- return NULL;
- }
-
- apr_sockaddr_ip_get(&c->local_ip, c->local_addr);
- if ((rv = apr_socket_addr_get(&c->remote_addr, APR_REMOTE, csd))
- != APR_SUCCESS) {
- ap_log_error(APLOG_MARK, APLOG_INFO, rv, server,
- "apr_socket_addr_get(APR_REMOTE)");
- apr_socket_close(csd);
- return NULL;
- }
-
- apr_sockaddr_ip_get(&c->remote_ip, c->remote_addr);
- c->base_server = server;
-
- c->id = id;
- c->bucket_alloc = alloc;
-
- return c;
-}
-
-static int core_pre_connection(conn_rec *c, void *csd)
-{
- core_net_rec *net = apr_palloc(c->pool, sizeof(*net));
-
-#ifdef AP_MPM_DISABLE_NAGLE_ACCEPTED_SOCK
- apr_status_t rv;
-
- /* 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
- * persistent connections and Nagle's algorithm that have very severe
- * performance penalties. (Failing to disable Nagle is not much of a
- * problem with simple HTTP.)
- */
- rv = apr_socket_opt_set(csd, APR_TCP_NODELAY, 1);
- if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) {
- /* expected cause is that the client disconnected already,
- * hence the debug level
- */
- ap_log_cerror(APLOG_MARK, APLOG_DEBUG, rv, c,
- "apr_socket_opt_set(APR_TCP_NODELAY)");
- }
-#endif
- net->c = c;
- net->in_ctx = NULL;
- net->out_ctx = NULL;
- net->client_socket = csd;
-
- ap_set_module_config(net->c->conn_config, &core_module, csd);
- ap_add_input_filter_handle(ap_core_input_filter_handle, net, NULL, net->c);
- ap_add_output_filter_handle(ap_core_output_filter_handle, net, NULL, net->c);
- return DONE;
-}
-
-static void register_hooks(apr_pool_t *p)
-{
- /* create_connection and install_transport_filters are
- * hooks that should always be APR_HOOK_REALLY_LAST to give other
- * modules the opportunity to install alternate network transports
- * and stop other functions from being run.
- */
- ap_hook_create_connection(core_create_conn, NULL, NULL,
- APR_HOOK_REALLY_LAST);
- ap_hook_pre_connection(core_pre_connection, NULL, NULL,
- APR_HOOK_REALLY_LAST);
-
- 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(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);
- ap_hook_fixups(core_override_type,NULL,NULL,APR_HOOK_REALLY_FIRST);
- ap_hook_access_checker(do_nothing,NULL,NULL,APR_HOOK_REALLY_LAST);
- ap_hook_create_request(core_create_req, NULL, NULL, APR_HOOK_MIDDLE);
- APR_OPTIONAL_HOOK(proxy, create_req, core_create_proxy_req, NULL, NULL,
- APR_HOOK_MIDDLE);
- ap_hook_pre_mpm(ap_create_scoreboard, NULL, NULL, APR_HOOK_MIDDLE);
-
- /* register the core's insert_filter hook and register core-provided
- * filters
- */
- ap_hook_insert_filter(core_insert_filter, NULL, NULL, APR_HOOK_MIDDLE);
-
- ap_core_input_filter_handle =
- ap_register_input_filter("CORE_IN", core_input_filter,
- NULL, AP_FTYPE_NETWORK);
- ap_net_time_filter_handle =
- ap_register_input_filter("NET_TIME", net_time_filter,
- NULL, AP_FTYPE_PROTOCOL);
- ap_content_length_filter_handle =
- ap_register_output_filter("CONTENT_LENGTH", ap_content_length_filter,
- NULL, AP_FTYPE_PROTOCOL);
- ap_core_output_filter_handle =
- ap_register_output_filter("CORE", core_output_filter,
- NULL, AP_FTYPE_NETWORK);
- ap_subreq_core_filter_handle =
- ap_register_output_filter("SUBREQ_CORE", ap_sub_req_output_filter,
- NULL, AP_FTYPE_CONTENT_SET);
- ap_old_write_func =
- ap_register_output_filter("OLD_WRITE", ap_old_write_filter,
- NULL, AP_FTYPE_RESOURCE - 10);
-}
-
-AP_DECLARE_DATA module core_module = {
- STANDARD20_MODULE_STUFF,
- create_core_dir_config, /* create per-directory config structure */
- merge_core_dir_configs, /* merge per-directory config structures */
- create_core_server_config, /* create per-server config structure */
- merge_core_server_configs, /* merge per-server config structures */
- core_cmds, /* command apr_table_t */
- register_hooks /* register hooks */
-};
-