http_protocol.c revision ebc2d0583ce25c4d7894da8392ca57ccc608ce49
08cb74ca432a8c24e39f17dedce527e6a47b8001jerenkrantz/* ====================================================================
08cb74ca432a8c24e39f17dedce527e6a47b8001jerenkrantz * Copyright (c) 1995-1999 The Apache Group. All rights reserved.
7e79e8fd53348f9fc6e8009a4a2522425ab6f08ffielding *
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd * Redistribution and use in source and binary forms, with or without
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd * modification, are permitted provided that the following conditions
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd * are met:
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding *
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd * 1. Redistributions of source code must retain the above copyright
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * notice, this list of conditions and the following disclaimer.
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd *
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd * 2. Redistributions in binary form must reproduce the above copyright
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd * notice, this list of conditions and the following disclaimer in
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd * the documentation and/or other materials provided with the
ce9621257ef9e54c1bbe5ad8a5f445a1f211c2dcnd * distribution.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding *
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * 3. All advertising materials mentioning features or use of this
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * software must display the following acknowledgment:
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * "This product includes software developed by the Apache Group
785be1b6298010956622771c870ab3cd8ca57a2faaron * for use in the Apache HTTP server project (http://www.apache.org/)."
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding *
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * 4. The names "Apache Server" and "Apache Group" must not be used to
1b21d7b3d97def358b2e923655edeb16613a1c31gstein * endorse or promote products derived from this software without
1b21d7b3d97def358b2e923655edeb16613a1c31gstein * prior written permission. For written permission, please contact
1b21d7b3d97def358b2e923655edeb16613a1c31gstein * apache@apache.org.
1b21d7b3d97def358b2e923655edeb16613a1c31gstein *
1b21d7b3d97def358b2e923655edeb16613a1c31gstein * 5. Products derived from this software may not be called "Apache"
1b21d7b3d97def358b2e923655edeb16613a1c31gstein * nor may "Apache" appear in their names without prior written
1b21d7b3d97def358b2e923655edeb16613a1c31gstein * permission of the Apache Group.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding *
2d71630471d1c23f0137309e3c3957c633ecbfd6rbb * 6. Redistributions of any form whatsoever must retain the following
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * acknowledgment:
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * "This product includes software developed by the Apache Group
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * for use in the Apache HTTP server project (http://www.apache.org/)."
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding *
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
51af95bb51b5084e883bad250b2afa2838e9ceebfielding * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
d4f1d9c1ff112a8ab9bee31f196973761329b236rbb * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
7fae9cc4639013f3c04c085547256c68814aee8ftrawick * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
7fae9cc4639013f3c04c085547256c68814aee8ftrawick * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
7fae9cc4639013f3c04c085547256c68814aee8ftrawick * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
7fae9cc4639013f3c04c085547256c68814aee8ftrawick * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * OF THE POSSIBILITY OF SUCH DAMAGE.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * ====================================================================
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding *
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * This software consists of voluntary contributions made by many
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * individuals on behalf of the Apache Group and was originally based
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * on public domain software written at the National Center for
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Supercomputing Applications, University of Illinois, Urbana-Champaign.
785be1b6298010956622771c870ab3cd8ca57a2faaron * For more information on the Apache Group and the Apache HTTP server
785be1b6298010956622771c870ab3cd8ca57a2faaron * project, please see <http://www.apache.org/>.
785be1b6298010956622771c870ab3cd8ca57a2faaron *
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding/*
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * http_protocol.c --- routines which directly communicate with the client.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding *
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Code originally by Rob McCool; much redone by Robert S. Thau
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * and the Apache Group.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding */
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron#define CORE_PRIVATE
785be1b6298010956622771c870ab3cd8ca57a2faaron#include "httpd.h"
785be1b6298010956622771c870ab3cd8ca57a2faaron#include "http_config.h"
785be1b6298010956622771c870ab3cd8ca57a2faaron#include "http_core.h"
785be1b6298010956622771c870ab3cd8ca57a2faaron#include "http_protocol.h"
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#include "http_main.h"
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#include "http_request.h"
f4b96a996afbc46872f57ad1450e6ee1c8f13707jorton#include "http_vhost.h"
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#include "http_log.h" /* For errors detected in basic auth common
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * support code... */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#include "util_date.h" /* For parseHTTPdate and BAD_DATE */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#include "mpm_status.h"
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#include <stdarg.h>
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingHOOK_STRUCT(
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding HOOK_LINK(post_read_request)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding HOOK_LINK(log_transaction)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding HOOK_LINK(http_method)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding HOOK_LINK(default_port)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#define SET_BYTES_SENT(r) \
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding do { if (r->sent_bodyct) \
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_bgetopt (r->connection->client, BO_BYTECT, &r->bytes_sent); \
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding } while (0)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingstatic int parse_byterange(char *range, long clength, long *start, long *end)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding{
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding char *dash = strchr(range, '-');
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (!dash)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return 0;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if ((dash == range)) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* In the form "-5" */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding *start = clength - atol(dash + 1);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding *end = clength - 1;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding else {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding *dash = '\0';
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding dash++;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding *start = atol(range);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (*dash)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding *end = atol(dash);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding else /* "5-" */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding *end = clength - 1;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (*start < 0)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding *start = 0;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (*end >= clength)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding *end = clength - 1;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (*start > *end)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return 0;
45acd673a68181802b112e97e84fa3813ddd3ec1stoddard
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return (*start > 0 || *end < clength - 1);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding}
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingstatic int internal_byterange(int, long *, request_rec *, const char **,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_off_t *, long *);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingAPI_EXPORT(int) ap_set_byterange(request_rec *r)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding{
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding const char *range, *if_range, *match;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding long range_start, range_end;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (!r->clength || r->assbackwards)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return 0;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* Check for Range request-header (HTTP/1.1) or Request-Range for
1ccd992d37d62c8cb2056126f2234f64ec189bfddougm * backwards-compatibility with second-draft Luotonen/Franks
785be1b6298010956622771c870ab3cd8ca57a2faaron * byte-ranges (e.g. Netscape Navigator 2-3).
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick *
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * We support this form, with Request-Range, and (farther down) we
8a261a9f7d18d1e862d63f68e93f288d3e1f0d94trawick * send multipart/x-byteranges instead of multipart/byteranges for
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Request-Range based requests to work around a bug in Netscape
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick * Navigator 2-3 and MSIE 3.
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick */
58fd79b56eb624bf011772994e9761d3c2e228c1orlikowski
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick if (!(range = ap_table_get(r->headers_in, "Range")))
8a261a9f7d18d1e862d63f68e93f288d3e1f0d94trawick range = ap_table_get(r->headers_in, "Request-Range");
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick if (!range || strncasecmp(range, "bytes=", 6)) {
785be1b6298010956622771c870ab3cd8ca57a2faaron return 0;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick /* Check the If-Range header for Etag or Date.
e160b861b50a3a8dcc013b8cd3ef849fe777e52fgregames * Note that this check will return false (as required) if either
e160b861b50a3a8dcc013b8cd3ef849fe777e52fgregames * of the two etags are weak.
e160b861b50a3a8dcc013b8cd3ef849fe777e52fgregames */
e160b861b50a3a8dcc013b8cd3ef849fe777e52fgregames if ((if_range = ap_table_get(r->headers_in, "If-Range"))) {
e160b861b50a3a8dcc013b8cd3ef849fe777e52fgregames if (if_range[0] == '"') {
e160b861b50a3a8dcc013b8cd3ef849fe777e52fgregames if (!(match = ap_table_get(r->headers_out, "Etag")) ||
e160b861b50a3a8dcc013b8cd3ef849fe777e52fgregames (strcmp(if_range, match) != 0))
e160b861b50a3a8dcc013b8cd3ef849fe777e52fgregames return 0;
e160b861b50a3a8dcc013b8cd3ef849fe777e52fgregames }
e160b861b50a3a8dcc013b8cd3ef849fe777e52fgregames else if (!(match = ap_table_get(r->headers_out, "Last-Modified")) ||
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick (strcmp(if_range, match) != 0))
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick return 0;
560f6ac786d611b858b2bad932713d9e971f0716trawick }
560f6ac786d611b858b2bad932713d9e971f0716trawick
560f6ac786d611b858b2bad932713d9e971f0716trawick if (!strchr(range, ',')) {
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick /* A single range */
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick if (!parse_byterange(ap_pstrdup(r->pool, range + 6), r->clength,
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick &range_start, &range_end))
7bf77d70b6830636bc36e6b76a228c301be23ff7brianp return 0;
7bf77d70b6830636bc36e6b76a228c301be23ff7brianp
7bf77d70b6830636bc36e6b76a228c301be23ff7brianp r->byterange = 1;
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick ap_table_setn(r->headers_out, "Content-Range",
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick ap_psprintf(r->pool, "bytes %ld-%ld/%ld",
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick range_start, range_end, r->clength));
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick ap_table_setn(r->headers_out, "Content-Length",
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_psprintf(r->pool, "%ld", range_end - range_start + 1));
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
eae32ab3fb398ca408bc2d45b22adf1b67a75471rbb else {
39b76a07959a0a332366c735a23894d9e8ed6872trawick /* a multiple range */
39b76a07959a0a332366c735a23894d9e8ed6872trawick const char *r_range = ap_pstrdup(r->pool, range + 6);
785be1b6298010956622771c870ab3cd8ca57a2faaron long tlength = 0;
785be1b6298010956622771c870ab3cd8ca57a2faaron
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick r->byterange = 2;
39b76a07959a0a332366c735a23894d9e8ed6872trawick r->boundary = ap_psprintf(r->pool, "%lx%lx",
8a261a9f7d18d1e862d63f68e93f288d3e1f0d94trawick r->request_time, (long) getpid());
785be1b6298010956622771c870ab3cd8ca57a2faaron while (internal_byterange(0, &tlength, r, &r_range, NULL, NULL));
785be1b6298010956622771c870ab3cd8ca57a2faaron ap_table_setn(r->headers_out, "Content-Length",
066877f1a045103acfdd376d48cdd473c33f409bdougm ap_psprintf(r->pool, "%ld", tlength));
8a261a9f7d18d1e862d63f68e93f288d3e1f0d94trawick }
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick r->status = PARTIAL_CONTENT;
74b39333600dee3260355ad3a06e36ef6c61c8f1dreid r->range = range + 6;
74b39333600dee3260355ad3a06e36ef6c61c8f1dreid
74b39333600dee3260355ad3a06e36ef6c61c8f1dreid return 1;
74b39333600dee3260355ad3a06e36ef6c61c8f1dreid}
97c78987224dcd037076d393aad1867c26b2c8cftrawick
97c78987224dcd037076d393aad1867c26b2c8cftrawickAPI_EXPORT(int) ap_each_byterange(request_rec *r, ap_off_t *offset,
97c78987224dcd037076d393aad1867c26b2c8cftrawick long *length)
97c78987224dcd037076d393aad1867c26b2c8cftrawick{
97c78987224dcd037076d393aad1867c26b2c8cftrawick return internal_byterange(1, NULL, r, &r->range, offset, length);
97c78987224dcd037076d393aad1867c26b2c8cftrawick}
97c78987224dcd037076d393aad1867c26b2c8cftrawick
97c78987224dcd037076d393aad1867c26b2c8cftrawick/* If this function is called with realreq=1, it will spit out
97c78987224dcd037076d393aad1867c26b2c8cftrawick * the correct headers for a byterange chunk, and set offset and
97c78987224dcd037076d393aad1867c26b2c8cftrawick * length to the positions they should be.
97c78987224dcd037076d393aad1867c26b2c8cftrawick *
97c78987224dcd037076d393aad1867c26b2c8cftrawick * If it is called with realreq=0, it will add to tlength the length
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * it *would* have used with realreq=1.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding *
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Either case will return 1 if it should be called again, and 0
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * when done.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding */
785be1b6298010956622771c870ab3cd8ca57a2faaronstatic int internal_byterange(int realreq, long *tlength, request_rec *r,
785be1b6298010956622771c870ab3cd8ca57a2faaron const char **r_range, ap_off_t *offset,
785be1b6298010956622771c870ab3cd8ca57a2faaron long *length)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding{
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding long range_start, range_end;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding char *range;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (!**r_range) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (r->byterange > 1) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (realreq)
785be1b6298010956622771c870ab3cd8ca57a2faaron ap_rvputs(r, "\015\012--", r->boundary, "--\015\012", NULL);
785be1b6298010956622771c870ab3cd8ca57a2faaron else
785be1b6298010956622771c870ab3cd8ca57a2faaron *tlength += 4 + strlen(r->boundary) + 4;
785be1b6298010956622771c870ab3cd8ca57a2faaron }
785be1b6298010956622771c870ab3cd8ca57a2faaron return 0;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding range = ap_getword(r->pool, r_range, ',');
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (!parse_byterange(range, r->clength, &range_start, &range_end))
785be1b6298010956622771c870ab3cd8ca57a2faaron /* Skip this one */
785be1b6298010956622771c870ab3cd8ca57a2faaron return internal_byterange(realreq, tlength, r, r_range, offset,
785be1b6298010956622771c870ab3cd8ca57a2faaron length);
785be1b6298010956622771c870ab3cd8ca57a2faaron
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (r->byterange > 1) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding const char *ct = r->content_type ? r->content_type : ap_default_type(r);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding char ts[MAX_STRING_LEN];
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_snprintf(ts, sizeof(ts), "%ld-%ld/%ld", range_start, range_end,
785be1b6298010956622771c870ab3cd8ca57a2faaron r->clength);
785be1b6298010956622771c870ab3cd8ca57a2faaron if (realreq)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_rvputs(r, "\015\012--", r->boundary, "\015\012Content-type: ",
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ct, "\015\012Content-range: bytes ", ts, "\015\012\015\012",
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding NULL);
785be1b6298010956622771c870ab3cd8ca57a2faaron else
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding *tlength += 4 + strlen(r->boundary) + 16 + strlen(ct) + 23 +
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding strlen(ts) + 4;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
f4b96a996afbc46872f57ad1450e6ee1c8f13707jorton
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (realreq) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding *offset = range_start;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding *length = range_end - range_start + 1;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding else {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding *tlength += range_end - range_start + 1;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return 1;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding}
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingAPI_EXPORT(int) ap_set_content_length(request_rec *r, long clength)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding{
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding r->clength = clength;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_table_setn(r->headers_out, "Content-Length", ap_psprintf(r->pool, "%ld", clength));
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return 0;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding}
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingAPI_EXPORT(int) ap_set_keepalive(request_rec *r)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding{
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding int ka_sent = 0;
785be1b6298010956622771c870ab3cd8ca57a2faaron int wimpy = ap_find_token(r->pool,
785be1b6298010956622771c870ab3cd8ca57a2faaron ap_table_get(r->headers_out, "Connection"), "close");
785be1b6298010956622771c870ab3cd8ca57a2faaron const char *conn = ap_table_get(r->headers_in, "Connection");
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron /* The following convoluted conditional determines whether or not
785be1b6298010956622771c870ab3cd8ca57a2faaron * the current connection should remain persistent after this response
785be1b6298010956622771c870ab3cd8ca57a2faaron * (a.k.a. HTTP Keep-Alive) and whether or not the output message
785be1b6298010956622771c870ab3cd8ca57a2faaron * body should use the HTTP/1.1 chunked transfer-coding. In English,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding *
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * IF we have not marked this connection as errored;
1ccd992d37d62c8cb2056126f2234f64ec189bfddougm * and the response body has a defined length due to the status code
785be1b6298010956622771c870ab3cd8ca57a2faaron * being 304 or 204, the request method being HEAD, already
785be1b6298010956622771c870ab3cd8ca57a2faaron * having defined Content-Length or Transfer-Encoding: chunked, or
785be1b6298010956622771c870ab3cd8ca57a2faaron * the request version being HTTP/1.1 and thus capable of being set
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * as chunked [we know the (r->chunked = 1) side-effect is ugly];
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * and the server configuration enables keep-alive;
785be1b6298010956622771c870ab3cd8ca57a2faaron * and the server configuration has a reasonable inter-request timeout;
785be1b6298010956622771c870ab3cd8ca57a2faaron * and there is no maximum # requests or the max hasn't been reached;
785be1b6298010956622771c870ab3cd8ca57a2faaron * and the response status does not require a close;
785be1b6298010956622771c870ab3cd8ca57a2faaron * and the response generator has not already indicated close;
785be1b6298010956622771c870ab3cd8ca57a2faaron * and the client did not request non-persistence (Connection: close);
785be1b6298010956622771c870ab3cd8ca57a2faaron * and we haven't been configured to ignore the buggy twit
785be1b6298010956622771c870ab3cd8ca57a2faaron * or they're a buggy twit coming through a HTTP/1.1 proxy
785be1b6298010956622771c870ab3cd8ca57a2faaron * and the client is requesting an HTTP/1.0-style keep-alive
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * or the client claims to be HTTP/1.1 compliant (perhaps a proxy);
1ccd992d37d62c8cb2056126f2234f64ec189bfddougm * THEN we can be persistent, which requires more headers be output.
785be1b6298010956622771c870ab3cd8ca57a2faaron *
2d399cd7535887fceaa9f8f116eb98ce68ddd602trawick * Note that the condition evaluation order is extremely important.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if ((r->connection->keepalive != -1) &&
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ((r->status == HTTP_NOT_MODIFIED) ||
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding (r->status == HTTP_NO_CONTENT) ||
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding r->header_only ||
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_table_get(r->headers_out, "Content-Length") ||
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_find_last_token(r->pool,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_table_get(r->headers_out, "Transfer-Encoding"),
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding "chunked") ||
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ((r->proto_num >= HTTP_VERSION(1,1)) &&
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding (r->chunked = 1))) && /* THIS CODE IS CORRECT, see comment above. */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding r->server->keep_alive &&
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* ZZZ change to APR keepalive timeout defined value */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding (r->server->keep_alive_timeout > 0) &&
a7ed9c525f9460187f327cea953bf90ecf1bdc51gstein ((r->server->keep_alive_max == 0) ||
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding (r->server->keep_alive_max > r->connection->keepalives)) &&
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding !ap_status_drops_connection(r->status) &&
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding !wimpy &&
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding !ap_find_token(r->pool, conn, "close") &&
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding (!ap_table_get(r->subprocess_env, "nokeepalive") ||
a7ed9c525f9460187f327cea953bf90ecf1bdc51gstein ap_table_get(r->headers_in, "Via")) &&
17f3ba69f65182426ad4e568bb2d6f192ccd2ed5trawick ((ka_sent = ap_find_token(r->pool, conn, "keep-alive")) ||
17f3ba69f65182426ad4e568bb2d6f192ccd2ed5trawick (r->proto_num >= HTTP_VERSION(1,1)))
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ) {
17f3ba69f65182426ad4e568bb2d6f192ccd2ed5trawick int left = r->server->keep_alive_max - r->connection->keepalives;
17f3ba69f65182426ad4e568bb2d6f192ccd2ed5trawick
17f3ba69f65182426ad4e568bb2d6f192ccd2ed5trawick r->connection->keepalive = 1;
17f3ba69f65182426ad4e568bb2d6f192ccd2ed5trawick r->connection->keepalives++;
17f3ba69f65182426ad4e568bb2d6f192ccd2ed5trawick
17f3ba69f65182426ad4e568bb2d6f192ccd2ed5trawick /* If they sent a Keep-Alive token, send one back */
17f3ba69f65182426ad4e568bb2d6f192ccd2ed5trawick if (ka_sent) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (r->server->keep_alive_max)
1ccd992d37d62c8cb2056126f2234f64ec189bfddougm ap_table_setn(r->headers_out, "Keep-Alive",
785be1b6298010956622771c870ab3cd8ca57a2faaron ap_psprintf(r->pool, "timeout=%d, max=%d",
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding r->server->keep_alive_timeout, left));
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding else
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_table_setn(r->headers_out, "Keep-Alive",
1ccd992d37d62c8cb2056126f2234f64ec189bfddougm ap_psprintf(r->pool, "timeout=%d",
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding r->server->keep_alive_timeout));
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_table_mergen(r->headers_out, "Connection", "Keep-Alive");
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return 1;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* Otherwise, we need to indicate that we will be closing this
785be1b6298010956622771c870ab3cd8ca57a2faaron * connection immediately after the current response.
785be1b6298010956622771c870ab3cd8ca57a2faaron *
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * We only really need to send "close" to HTTP/1.1 clients, but we
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * always send it anyway, because a broken proxy may identify itself
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * as HTTP/1.0, but pass our request along with our HTTP/1.1 tag
1ccd992d37d62c8cb2056126f2234f64ec189bfddougm * to a HTTP/1.1 client. Better safe than sorry.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (!wimpy)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_table_mergen(r->headers_out, "Connection", "close");
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding r->connection->keepalive = 0;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return 0;
a7ed9c525f9460187f327cea953bf90ecf1bdc51gstein}
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding/*
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Return the latest rational time from a request/mtime (modification time)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * pair. We return the mtime unless it's in the future, in which case we
f4b96a996afbc46872f57ad1450e6ee1c8f13707jorton * return the current time. We use the request time as a reference in order
17f3ba69f65182426ad4e568bb2d6f192ccd2ed5trawick * to limit the number of calls to time(). We don't check for futurosity
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * unless the mtime is at least as new as the reference.
785be1b6298010956622771c870ab3cd8ca57a2faaron */
17f3ba69f65182426ad4e568bb2d6f192ccd2ed5trawickAPI_EXPORT(time_t) ap_rationalize_mtime(request_rec *r, time_t mtime)
17f3ba69f65182426ad4e568bb2d6f192ccd2ed5trawick{
39b76a07959a0a332366c735a23894d9e8ed6872trawick time_t now;
8fd7c5046d164fb0959222497e5925dfc6a52ff3trawick
17f3ba69f65182426ad4e568bb2d6f192ccd2ed5trawick /* For all static responses, it's almost certain that the file was
17f3ba69f65182426ad4e568bb2d6f192ccd2ed5trawick * last modified before the beginning of the request. So there's
17f3ba69f65182426ad4e568bb2d6f192ccd2ed5trawick * no reason to call time(NULL) again. But if the response has been
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * created on demand, then it might be newer than the time the request
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * started. In this event we really have to call time(NULL) again
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * so that we can give the clients the most accurate Last-Modified. If we
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * were given a time in the future, we return the current time - the
8f8ec0957334f50b7ac11359f90490ee467258eedreid * Last-Modified can't be in the future.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* ZZZ Change time call to use time AP time thread functions. */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding now = (mtime < r->request_time) ? r->request_time : time(NULL);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return (mtime > now) ? now : mtime;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding}
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingAPI_EXPORT(int) ap_meets_conditions(request_rec *r)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding{
785be1b6298010956622771c870ab3cd8ca57a2faaron const char *etag = ap_table_get(r->headers_out, "ETag");
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding const char *if_match, *if_modified_since, *if_unmodified, *if_nonematch;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding time_t mtime;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* Check for conditional requests --- note that we only want to do
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * this if we are successful so far and we are not processing a
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf * subrequest or an ErrorDocument.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding *
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * The order of the checks is important, since ETag checks are supposed
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf * to be more accurate than checks relative to the modification time.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * However, not all documents are guaranteed to *have* ETags, and some
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick * might have Last-Modified values w/o ETags, so this gets a little
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * complicated.
39b76a07959a0a332366c735a23894d9e8ed6872trawick */
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick
785be1b6298010956622771c870ab3cd8ca57a2faaron if (!ap_is_HTTP_SUCCESS(r->status) || r->no_local_copy) {
785be1b6298010956622771c870ab3cd8ca57a2faaron return OK;
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf }
39b76a07959a0a332366c735a23894d9e8ed6872trawick
6b38fca3ec543a0f72efd5683e91a0b30fc752d1trawick /* ZZZ We are changing time(NULL) to AP time thread functions. */
785be1b6298010956622771c870ab3cd8ca57a2faaron mtime = (r->mtime != 0) ? r->mtime : time(NULL);
785be1b6298010956622771c870ab3cd8ca57a2faaron
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf /* If an If-Match request-header field was given
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf * AND the field value is not "*" (meaning match anything)
785be1b6298010956622771c870ab3cd8ca57a2faaron * AND if our strong ETag does not match any entity tag in that field,
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf * respond with a status of 412 (Precondition Failed).
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf */
785be1b6298010956622771c870ab3cd8ca57a2faaron if ((if_match = ap_table_get(r->headers_in, "If-Match")) != NULL) {
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf if (if_match[0] != '*' &&
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf (etag == NULL || etag[0] == 'W' ||
785be1b6298010956622771c870ab3cd8ca57a2faaron !ap_find_list_item(r->pool, if_match, etag))) {
785be1b6298010956622771c870ab3cd8ca57a2faaron return HTTP_PRECONDITION_FAILED;
785be1b6298010956622771c870ab3cd8ca57a2faaron }
785be1b6298010956622771c870ab3cd8ca57a2faaron }
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf else {
066877f1a045103acfdd376d48cdd473c33f409bdougm /* Else if a valid If-Unmodified-Since request-header field was given
785be1b6298010956622771c870ab3cd8ca57a2faaron * AND the requested resource has been modified since the time
785be1b6298010956622771c870ab3cd8ca57a2faaron * specified in this field, then the server MUST
785be1b6298010956622771c870ab3cd8ca57a2faaron * respond with a status of 412 (Precondition Failed).
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf */
785be1b6298010956622771c870ab3cd8ca57a2faaron if_unmodified = ap_table_get(r->headers_in, "If-Unmodified-Since");
785be1b6298010956622771c870ab3cd8ca57a2faaron if (if_unmodified != NULL) {
785be1b6298010956622771c870ab3cd8ca57a2faaron time_t ius = ap_parseHTTPdate(if_unmodified);
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron if ((ius != BAD_DATE) && (mtime > ius)) {
785be1b6298010956622771c870ab3cd8ca57a2faaron return HTTP_PRECONDITION_FAILED;
785be1b6298010956622771c870ab3cd8ca57a2faaron }
785be1b6298010956622771c870ab3cd8ca57a2faaron }
785be1b6298010956622771c870ab3cd8ca57a2faaron }
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf /* If an If-None-Match request-header field was given
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf * AND the field value is "*" (meaning match anything)
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf * OR our ETag matches any of the entity tags in that field, fail.
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf *
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf * If the request method was GET or HEAD, failure means the server
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf * SHOULD respond with a 304 (Not Modified) response.
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf * For all other request methods, failure means the server MUST
066877f1a045103acfdd376d48cdd473c33f409bdougm * respond with a status of 412 (Precondition Failed).
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding *
785be1b6298010956622771c870ab3cd8ca57a2faaron * GET or HEAD allow weak etag comparison, all other methods require
785be1b6298010956622771c870ab3cd8ca57a2faaron * strong comparison. We can only use weak if it's not a range request.
785be1b6298010956622771c870ab3cd8ca57a2faaron */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if_nonematch = ap_table_get(r->headers_in, "If-None-Match");
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (if_nonematch != NULL) {
785be1b6298010956622771c870ab3cd8ca57a2faaron if (r->method_number == M_GET) {
785be1b6298010956622771c870ab3cd8ca57a2faaron if (if_nonematch[0] == '*')
785be1b6298010956622771c870ab3cd8ca57a2faaron return HTTP_NOT_MODIFIED;
785be1b6298010956622771c870ab3cd8ca57a2faaron if (etag != NULL) {
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf if (ap_table_get(r->headers_in, "Range")) {
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf if (etag[0] != 'W' &&
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf ap_find_list_item(r->pool, if_nonematch, etag)) {
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf return HTTP_NOT_MODIFIED;
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf }
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf }
785be1b6298010956622771c870ab3cd8ca57a2faaron else if (strstr(if_nonematch, etag)) {
785be1b6298010956622771c870ab3cd8ca57a2faaron return HTTP_NOT_MODIFIED;
785be1b6298010956622771c870ab3cd8ca57a2faaron }
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf }
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf }
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf else if (if_nonematch[0] == '*' ||
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf (etag != NULL &&
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf ap_find_list_item(r->pool, if_nonematch, etag))) {
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf return HTTP_PRECONDITION_FAILED;
785be1b6298010956622771c870ab3cd8ca57a2faaron }
785be1b6298010956622771c870ab3cd8ca57a2faaron }
785be1b6298010956622771c870ab3cd8ca57a2faaron /* Else if a valid If-Modified-Since request-header field was given
785be1b6298010956622771c870ab3cd8ca57a2faaron * AND it is a GET or HEAD request
785be1b6298010956622771c870ab3cd8ca57a2faaron * AND the requested resource has not been modified since the time
785be1b6298010956622771c870ab3cd8ca57a2faaron * specified in this field, then the server MUST
2d399cd7535887fceaa9f8f116eb98ce68ddd602trawick * respond with a status of 304 (Not Modified).
785be1b6298010956622771c870ab3cd8ca57a2faaron * A date later than the server's current request time is invalid.
785be1b6298010956622771c870ab3cd8ca57a2faaron */
785be1b6298010956622771c870ab3cd8ca57a2faaron else if ((r->method_number == M_GET)
785be1b6298010956622771c870ab3cd8ca57a2faaron && ((if_modified_since =
785be1b6298010956622771c870ab3cd8ca57a2faaron ap_table_get(r->headers_in, "If-Modified-Since")) != NULL)) {
785be1b6298010956622771c870ab3cd8ca57a2faaron time_t ims = ap_parseHTTPdate(if_modified_since);
785be1b6298010956622771c870ab3cd8ca57a2faaron
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf if ((ims >= mtime) && (ims <= r->request_time)) {
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf return HTTP_NOT_MODIFIED;
785be1b6298010956622771c870ab3cd8ca57a2faaron }
785be1b6298010956622771c870ab3cd8ca57a2faaron }
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf return OK;
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf}
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf/*
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf * Construct an entity tag (ETag) from resource information. If it's a real
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf * file, build in some of the file characteristics. If the modification time
785be1b6298010956622771c870ab3cd8ca57a2faaron * is newer than (request-time minus 1 second), mark the ETag as weak - it
785be1b6298010956622771c870ab3cd8ca57a2faaron * could be modified again in as short an interval. We rationalize the
785be1b6298010956622771c870ab3cd8ca57a2faaron * modification time we're given to keep it from being in the future.
2d399cd7535887fceaa9f8f116eb98ce68ddd602trawick */
785be1b6298010956622771c870ab3cd8ca57a2faaronAPI_EXPORT(char *) ap_make_etag(request_rec *r, int force_weak)
785be1b6298010956622771c870ab3cd8ca57a2faaron{
785be1b6298010956622771c870ab3cd8ca57a2faaron char *etag;
785be1b6298010956622771c870ab3cd8ca57a2faaron char *weak;
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron /*
785be1b6298010956622771c870ab3cd8ca57a2faaron * Make an ETag header out of various pieces of information. We use
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * the last-modified date and, if we have a real file, the
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * length and inode number - note that this doesn't have to match
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * the content-length (i.e. includes), it just has to be unique
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * for the file.
ebc18d48bea83ee5ed7a1b4e30007e5192539829wrowe *
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * If the request was made within a second of the last-modified date,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * we send a weak tag instead of a strong one, since it could
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * be modified again later in the second, and the validation
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * would be incorrect.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding weak = ((r->request_time - r->mtime > 1) && !force_weak) ? "" : "W/";
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (r->finfo.st_mode != 0) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding etag = ap_psprintf(r->pool,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding "%s\"%lx-%lx-%lx\"", weak,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding (unsigned long) r->finfo.st_ino,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding (unsigned long) r->finfo.st_size,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding (unsigned long) r->mtime);
785be1b6298010956622771c870ab3cd8ca57a2faaron }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding else {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding etag = ap_psprintf(r->pool, "%s\"%lx\"", weak,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding (unsigned long) r->mtime);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
785be1b6298010956622771c870ab3cd8ca57a2faaron
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return etag;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding}
f4b96a996afbc46872f57ad1450e6ee1c8f13707jorton
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingAPI_EXPORT(void) ap_set_etag(request_rec *r)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding{
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding char *etag;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding char *variant_etag, *vlv;
5f08a022a210f4e511561e89f500621a15e6177dtrawick int vlv_weak;
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron if (!r->vlist_validator) {
785be1b6298010956622771c870ab3cd8ca57a2faaron etag = ap_make_etag(r, 0);
5f08a022a210f4e511561e89f500621a15e6177dtrawick }
5f08a022a210f4e511561e89f500621a15e6177dtrawick else {
785be1b6298010956622771c870ab3cd8ca57a2faaron /* If we have a variant list validator (vlv) due to the
785be1b6298010956622771c870ab3cd8ca57a2faaron * response being negotiated, then we create a structured
785be1b6298010956622771c870ab3cd8ca57a2faaron * entity tag which merges the variant etag with the variant
785be1b6298010956622771c870ab3cd8ca57a2faaron * list validator (vlv). This merging makes revalidation
785be1b6298010956622771c870ab3cd8ca57a2faaron * somewhat safer, ensures that caches which can deal with
785be1b6298010956622771c870ab3cd8ca57a2faaron * Vary will (eventually) be updated if the set of variants is
785be1b6298010956622771c870ab3cd8ca57a2faaron * changed, and is also a protocol requirement for transparent
785be1b6298010956622771c870ab3cd8ca57a2faaron * content negotiation.
785be1b6298010956622771c870ab3cd8ca57a2faaron */
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron /* if the variant list validator is weak, we make the whole
785be1b6298010956622771c870ab3cd8ca57a2faaron * structured etag weak. If we would not, then clients could
785be1b6298010956622771c870ab3cd8ca57a2faaron * have problems merging range responses if we have different
785be1b6298010956622771c870ab3cd8ca57a2faaron * variants with the same non-globally-unique strong etag.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
f4b96a996afbc46872f57ad1450e6ee1c8f13707jorton vlv = r->vlist_validator;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding vlv_weak = (vlv[0] == 'W');
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding variant_etag = ap_make_etag(r, vlv_weak);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* merge variant_etag and vlv into a structured etag */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
785be1b6298010956622771c870ab3cd8ca57a2faaron variant_etag[strlen(variant_etag) - 1] = '\0';
785be1b6298010956622771c870ab3cd8ca57a2faaron if (vlv_weak)
785be1b6298010956622771c870ab3cd8ca57a2faaron vlv += 3;
900127764fb985c340ee4979cac97146a330c694trawick else
785be1b6298010956622771c870ab3cd8ca57a2faaron vlv++;
900127764fb985c340ee4979cac97146a330c694trawick etag = ap_pstrcat(r->pool, variant_etag, ";", vlv, NULL);
900127764fb985c340ee4979cac97146a330c694trawick }
900127764fb985c340ee4979cac97146a330c694trawick
785be1b6298010956622771c870ab3cd8ca57a2faaron ap_table_setn(r->headers_out, "ETag", etag);
785be1b6298010956622771c870ab3cd8ca57a2faaron}
785be1b6298010956622771c870ab3cd8ca57a2faaron
2d399cd7535887fceaa9f8f116eb98ce68ddd602trawick/*
785be1b6298010956622771c870ab3cd8ca57a2faaron * This function sets the Last-Modified output header field to the value
785be1b6298010956622771c870ab3cd8ca57a2faaron * of the mtime field in the request structure - rationalized to keep it from
785be1b6298010956622771c870ab3cd8ca57a2faaron * being in the future.
785be1b6298010956622771c870ab3cd8ca57a2faaron */
785be1b6298010956622771c870ab3cd8ca57a2faaronAPI_EXPORT(void) ap_set_last_modified(request_rec *r)
785be1b6298010956622771c870ab3cd8ca57a2faaron{
785be1b6298010956622771c870ab3cd8ca57a2faaron time_t mod_time = ap_rationalize_mtime(r, r->mtime);
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron ap_table_setn(r->headers_out, "Last-Modified",
785be1b6298010956622771c870ab3cd8ca57a2faaron ap_gm_timestr_822(r->pool, mod_time));
785be1b6298010956622771c870ab3cd8ca57a2faaron}
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron/* Get the method number associated with the given string, assumed to
785be1b6298010956622771c870ab3cd8ca57a2faaron * contain an HTTP method. Returns M_INVALID if not recognized.
785be1b6298010956622771c870ab3cd8ca57a2faaron *
785be1b6298010956622771c870ab3cd8ca57a2faaron * This is the first step toward placing method names in a configurable
785be1b6298010956622771c870ab3cd8ca57a2faaron * list. Hopefully it (and other routines) can eventually be moved to
785be1b6298010956622771c870ab3cd8ca57a2faaron * something like a mod_http_methods.c, complete with config stuff.
785be1b6298010956622771c870ab3cd8ca57a2faaron */
785be1b6298010956622771c870ab3cd8ca57a2faaronAPI_EXPORT(int) ap_method_number_of(const char *method)
785be1b6298010956622771c870ab3cd8ca57a2faaron{
785be1b6298010956622771c870ab3cd8ca57a2faaron switch (*method) {
2d399cd7535887fceaa9f8f116eb98ce68ddd602trawick case 'H':
785be1b6298010956622771c870ab3cd8ca57a2faaron if (strcmp(method, "HEAD") == 0)
785be1b6298010956622771c870ab3cd8ca57a2faaron return M_GET; /* see header_only in request_rec */
785be1b6298010956622771c870ab3cd8ca57a2faaron break;
785be1b6298010956622771c870ab3cd8ca57a2faaron case 'G':
785be1b6298010956622771c870ab3cd8ca57a2faaron if (strcmp(method, "GET") == 0)
785be1b6298010956622771c870ab3cd8ca57a2faaron return M_GET;
785be1b6298010956622771c870ab3cd8ca57a2faaron break;
785be1b6298010956622771c870ab3cd8ca57a2faaron case 'P':
785be1b6298010956622771c870ab3cd8ca57a2faaron if (strcmp(method, "POST") == 0)
785be1b6298010956622771c870ab3cd8ca57a2faaron return M_POST;
785be1b6298010956622771c870ab3cd8ca57a2faaron if (strcmp(method, "PUT") == 0)
785be1b6298010956622771c870ab3cd8ca57a2faaron return M_PUT;
785be1b6298010956622771c870ab3cd8ca57a2faaron if (strcmp(method, "PATCH") == 0)
785be1b6298010956622771c870ab3cd8ca57a2faaron return M_PATCH;
785be1b6298010956622771c870ab3cd8ca57a2faaron if (strcmp(method, "PROPFIND") == 0)
785be1b6298010956622771c870ab3cd8ca57a2faaron return M_PROPFIND;
785be1b6298010956622771c870ab3cd8ca57a2faaron if (strcmp(method, "PROPPATCH") == 0)
785be1b6298010956622771c870ab3cd8ca57a2faaron return M_PROPPATCH;
785be1b6298010956622771c870ab3cd8ca57a2faaron break;
785be1b6298010956622771c870ab3cd8ca57a2faaron case 'D':
785be1b6298010956622771c870ab3cd8ca57a2faaron if (strcmp(method, "DELETE") == 0)
785be1b6298010956622771c870ab3cd8ca57a2faaron return M_DELETE;
785be1b6298010956622771c870ab3cd8ca57a2faaron break;
785be1b6298010956622771c870ab3cd8ca57a2faaron case 'C':
785be1b6298010956622771c870ab3cd8ca57a2faaron if (strcmp(method, "CONNECT") == 0)
785be1b6298010956622771c870ab3cd8ca57a2faaron return M_CONNECT;
785be1b6298010956622771c870ab3cd8ca57a2faaron if (strcmp(method, "COPY") == 0)
785be1b6298010956622771c870ab3cd8ca57a2faaron return M_COPY;
785be1b6298010956622771c870ab3cd8ca57a2faaron break;
785be1b6298010956622771c870ab3cd8ca57a2faaron case 'M':
8a261a9f7d18d1e862d63f68e93f288d3e1f0d94trawick if (strcmp(method, "MKCOL") == 0)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return M_MKCOL;
8a261a9f7d18d1e862d63f68e93f288d3e1f0d94trawick if (strcmp(method, "MOVE") == 0)
8a261a9f7d18d1e862d63f68e93f288d3e1f0d94trawick return M_MOVE;
8a261a9f7d18d1e862d63f68e93f288d3e1f0d94trawick break;
785be1b6298010956622771c870ab3cd8ca57a2faaron case 'O':
785be1b6298010956622771c870ab3cd8ca57a2faaron if (strcmp(method, "OPTIONS") == 0)
785be1b6298010956622771c870ab3cd8ca57a2faaron return M_OPTIONS;
785be1b6298010956622771c870ab3cd8ca57a2faaron break;
8a261a9f7d18d1e862d63f68e93f288d3e1f0d94trawick case 'T':
8a261a9f7d18d1e862d63f68e93f288d3e1f0d94trawick if (strcmp(method, "TRACE") == 0)
066877f1a045103acfdd376d48cdd473c33f409bdougm return M_TRACE;
785be1b6298010956622771c870ab3cd8ca57a2faaron break;
8a261a9f7d18d1e862d63f68e93f288d3e1f0d94trawick case 'L':
8a261a9f7d18d1e862d63f68e93f288d3e1f0d94trawick if (strcmp(method, "LOCK") == 0)
8a261a9f7d18d1e862d63f68e93f288d3e1f0d94trawick return M_LOCK;
8a261a9f7d18d1e862d63f68e93f288d3e1f0d94trawick break;
785be1b6298010956622771c870ab3cd8ca57a2faaron case 'U':
785be1b6298010956622771c870ab3cd8ca57a2faaron if (strcmp(method, "UNLOCK") == 0)
785be1b6298010956622771c870ab3cd8ca57a2faaron return M_UNLOCK;
785be1b6298010956622771c870ab3cd8ca57a2faaron break;
785be1b6298010956622771c870ab3cd8ca57a2faaron }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return M_INVALID;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding}
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding/* Get a line of protocol input, including any continuation lines
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * caused by MIME folding (or broken clients) if fold != 0, and place it
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * in the buffer s, of size n bytes, without the ending newline.
785be1b6298010956622771c870ab3cd8ca57a2faaron *
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Returns -1 on error, or the length of s.
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf *
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Note: Because bgets uses 1 char for newline and 1 char for NUL,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * the most we can get is (n - 2) actual characters if it
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * was ended by a newline, or (n - 1) characters if the line
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * length exceeded (n - 1). So, if the result == (n - 1),
bbbf8f0e622ad5a37ccf70f35660fc755575278arbb * then the actual input line exceeded the buffer length,
1ccd992d37d62c8cb2056126f2234f64ec189bfddougm * and it would be a good idea for the caller to puke 400 or 414.
066877f1a045103acfdd376d48cdd473c33f409bdougm */
785be1b6298010956622771c870ab3cd8ca57a2faaronstatic int getline(char *s, int n, BUFF *in, int fold)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding{
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding char *pos, next;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding int retval;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding int total = 0;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding pos = s;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding do {
3926b3b7716683a1241c1ff6f8dd2f9c5073665afanf retval = ap_bgets(pos, n, in);
3926b3b7716683a1241c1ff6f8dd2f9c5073665afanf /* retval == -1 if error, 0 if EOF */
91583d2e9c0550f539ea6f4dedf051979ad1ad88fanf
91583d2e9c0550f539ea6f4dedf051979ad1ad88fanf if (retval <= 0)
91583d2e9c0550f539ea6f4dedf051979ad1ad88fanf return ((retval < 0) && (total == 0)) ? -1 : total;
91583d2e9c0550f539ea6f4dedf051979ad1ad88fanf
91583d2e9c0550f539ea6f4dedf051979ad1ad88fanf /* retval is the number of characters read, not including NUL */
91583d2e9c0550f539ea6f4dedf051979ad1ad88fanf
91583d2e9c0550f539ea6f4dedf051979ad1ad88fanf n -= retval; /* Keep track of how much of s is full */
91583d2e9c0550f539ea6f4dedf051979ad1ad88fanf pos += (retval - 1); /* and where s ends */
91583d2e9c0550f539ea6f4dedf051979ad1ad88fanf total += retval; /* and how long s has become */
91583d2e9c0550f539ea6f4dedf051979ad1ad88fanf
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (*pos == '\n') { /* Did we get a full line of input? */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /*
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Trim any extra trailing spaces or tabs except for the first
3c48210f662a2ab8ed90708989e04c09aae33cb2trawick * space or tab at the beginning of a blank string. This makes
3926b3b7716683a1241c1ff6f8dd2f9c5073665afanf * it much easier to check field values for exact matches, and
3c48210f662a2ab8ed90708989e04c09aae33cb2trawick * saves memory as well. Terminate string at end of line.
3c48210f662a2ab8ed90708989e04c09aae33cb2trawick */
3926b3b7716683a1241c1ff6f8dd2f9c5073665afanf while (pos > (s + 1) && (*(pos - 1) == ' ' || *(pos - 1) == '\t')) {
9c518951a46c7a12e20876827bb2e84ef87d3c11jerenkrantz --pos; /* trim extra trailing spaces or tabs */
9c518951a46c7a12e20876827bb2e84ef87d3c11jerenkrantz --total; /* but not one at the beginning of line */
9c518951a46c7a12e20876827bb2e84ef87d3c11jerenkrantz ++n;
9c518951a46c7a12e20876827bb2e84ef87d3c11jerenkrantz }
9c518951a46c7a12e20876827bb2e84ef87d3c11jerenkrantz *pos = '\0';
3c48210f662a2ab8ed90708989e04c09aae33cb2trawick --total;
3c48210f662a2ab8ed90708989e04c09aae33cb2trawick ++n;
3c48210f662a2ab8ed90708989e04c09aae33cb2trawick }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding else
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return total; /* if not, input line exceeded buffer size */
0db1b9810f06c0e3c537e0e0dfbc30160c308526trawick
0db1b9810f06c0e3c537e0e0dfbc30160c308526trawick /* Continue appending if line folding is desired and
0db1b9810f06c0e3c537e0e0dfbc30160c308526trawick * the last line was not empty and we have room in the buffer and
0db1b9810f06c0e3c537e0e0dfbc30160c308526trawick * the next line begins with a continuation character.
0db1b9810f06c0e3c537e0e0dfbc30160c308526trawick */
d40fc22d54d5969fa330e4983cf07f450f7029cebrianp } while (fold && (retval != 1) && (n > 1)
0db1b9810f06c0e3c537e0e0dfbc30160c308526trawick && (next = ap_blookc(in))
644be6f54749d2d9950d2c4d2ac448f7af016d26martin && ((next == ' ') || (next == '\t')));
644be6f54749d2d9950d2c4d2ac448f7af016d26martin
644be6f54749d2d9950d2c4d2ac448f7af016d26martin return total;
644be6f54749d2d9950d2c4d2ac448f7af016d26martin}
644be6f54749d2d9950d2c4d2ac448f7af016d26martin
644be6f54749d2d9950d2c4d2ac448f7af016d26martin/* parse_uri: break apart the uri
644be6f54749d2d9950d2c4d2ac448f7af016d26martin * Side Effects:
d40fc22d54d5969fa330e4983cf07f450f7029cebrianp * - sets r->args to rest after '?' (or NULL if no '?')
644be6f54749d2d9950d2c4d2ac448f7af016d26martin * - sets r->uri to request uri (without r->args part)
0db1b9810f06c0e3c537e0e0dfbc30160c308526trawick * - sets r->hostname (if not set already) from request (scheme://host:port)
3c48210f662a2ab8ed90708989e04c09aae33cb2trawick */
3c48210f662a2ab8ed90708989e04c09aae33cb2trawickCORE_EXPORT(void) ap_parse_uri(request_rec *r, const char *uri)
3c48210f662a2ab8ed90708989e04c09aae33cb2trawick{
3c48210f662a2ab8ed90708989e04c09aae33cb2trawick int status = HTTP_OK;
71715c646d5231de578431f8961e711764b899d3fanf
3c8b3749225668f06abbb2b023a833a2cef46931brianp r->unparsed_uri = ap_pstrdup(r->pool, uri);
3c8b3749225668f06abbb2b023a833a2cef46931brianp
3c8b3749225668f06abbb2b023a833a2cef46931brianp if (r->method_number == M_CONNECT) {
3c8b3749225668f06abbb2b023a833a2cef46931brianp status = ap_parse_hostinfo_components(r->pool, uri, &r->parsed_uri);
ccad4fdc6c75a352157a413bd3bbaf4d0c8a6f72brianp } else {
785be1b6298010956622771c870ab3cd8ca57a2faaron /* Simple syntax Errors in URLs are trapped by parse_uri_components(). */
ccad4fdc6c75a352157a413bd3bbaf4d0c8a6f72brianp status = ap_parse_uri_components(r->pool, uri, &r->parsed_uri);
785be1b6298010956622771c870ab3cd8ca57a2faaron }
3c8b3749225668f06abbb2b023a833a2cef46931brianp
3c8b3749225668f06abbb2b023a833a2cef46931brianp if (ap_is_HTTP_SUCCESS(status)) {
3c8b3749225668f06abbb2b023a833a2cef46931brianp /* if it has a scheme we may need to do absoluteURI vhost stuff */
785be1b6298010956622771c870ab3cd8ca57a2faaron if (r->parsed_uri.scheme
785be1b6298010956622771c870ab3cd8ca57a2faaron && !strcasecmp(r->parsed_uri.scheme, ap_http_method(r))) {
785be1b6298010956622771c870ab3cd8ca57a2faaron r->hostname = r->parsed_uri.hostname;
3c48210f662a2ab8ed90708989e04c09aae33cb2trawick } else if (r->method_number == M_CONNECT) {
3c48210f662a2ab8ed90708989e04c09aae33cb2trawick r->hostname = r->parsed_uri.hostname;
3c48210f662a2ab8ed90708989e04c09aae33cb2trawick }
3c48210f662a2ab8ed90708989e04c09aae33cb2trawick r->args = r->parsed_uri.query;
3c48210f662a2ab8ed90708989e04c09aae33cb2trawick r->uri = r->parsed_uri.path ? r->parsed_uri.path
3c48210f662a2ab8ed90708989e04c09aae33cb2trawick : ap_pstrdup(r->pool, "/");
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#if defined(OS2) || defined(WIN32)
3926b3b7716683a1241c1ff6f8dd2f9c5073665afanf /* Handle path translations for OS/2 and plug security hole.
3926b3b7716683a1241c1ff6f8dd2f9c5073665afanf * This will prevent "http://www.wherever.com/..\..\/" from
3926b3b7716683a1241c1ff6f8dd2f9c5073665afanf * returning a directory for the root drive.
3926b3b7716683a1241c1ff6f8dd2f9c5073665afanf */
2d399cd7535887fceaa9f8f116eb98ce68ddd602trawick {
785be1b6298010956622771c870ab3cd8ca57a2faaron char *x;
3926b3b7716683a1241c1ff6f8dd2f9c5073665afanf
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding for (x = r->uri; (x = strchr(x, '\\')) != NULL; )
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding *x = '/';
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#endif /* OS2 || WIN32 */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding else {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding r->args = NULL;
1ccd992d37d62c8cb2056126f2234f64ec189bfddougm r->hostname = NULL;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding r->status = status; /* set error status */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding r->uri = ap_pstrdup(r->pool, uri);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
785be1b6298010956622771c870ab3cd8ca57a2faaron}
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingstatic int read_request_line(request_rec *r)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding{
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding char l[DEFAULT_LIMIT_REQUEST_LINE + 2]; /* getline's two extra for \n\0 */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding const char *ll = l;
785be1b6298010956622771c870ab3cd8ca57a2faaron const char *uri;
785be1b6298010956622771c870ab3cd8ca57a2faaron conn_rec *conn = r->connection;
785be1b6298010956622771c870ab3cd8ca57a2faaron int major = 1, minor = 0; /* Assume HTTP/1.0 if non-"HTTP" protocol */
785be1b6298010956622771c870ab3cd8ca57a2faaron int len;
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron /* Read past empty lines until we get a real request line,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * a read error, the connection closes (EOF), or we timeout.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding *
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * We skip empty lines because browsers have to tack a CRLF on to the end
785be1b6298010956622771c870ab3cd8ca57a2faaron * of POSTs to support old CERN webservers. But note that we may not
785be1b6298010956622771c870ab3cd8ca57a2faaron * have flushed any previous response completely to the client yet.
785be1b6298010956622771c870ab3cd8ca57a2faaron * We delay the flush as long as possible so that we can improve
785be1b6298010956622771c870ab3cd8ca57a2faaron * performance for clients that are pipelining requests. If a request
785be1b6298010956622771c870ab3cd8ca57a2faaron * is pipelined then we won't block during the (implicit) read() below.
785be1b6298010956622771c870ab3cd8ca57a2faaron * If the requests aren't pipelined, then the client is still waiting
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * for the final buffer flush from us, and we will block in the implicit
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * read(). B_SAFEREAD ensures that the BUFF layer flushes if it will
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * have to block during a read.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* TODO: reimplement SAFEREAD external to BUFF using a layer */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* //ap_bsetflag(conn->client, B_SAFEREAD, 1); */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_bflush(conn->client);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding while ((len = getline(l, sizeof(l), conn->client, 0)) <= 0) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if ((len < 0) || ap_bgetflag(conn->client, B_EOF)) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* //ap_bsetflag(conn->client, B_SAFEREAD, 0); */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* this is a hack to make sure that request time is set,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * it's not perfect, but it's better than nothing
3d96ee83babeec32482c9082c9426340cee8c44dwrowe */
785be1b6298010956622771c870ab3cd8ca57a2faaron r->request_time = time(0);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return 0;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* we've probably got something to do, ignore graceful restart requests */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* XXX - sigwait doesn't work if the signal has been SIG_IGNed (under
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * linux 2.0 w/ glibc 2.0, anyway), and this step isn't necessary when
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * we're running a sigwait thread anyway. If/when unthreaded mode is
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * put back in, we should make sure to ignore this signal iff a sigwait
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * thread isn't used. - mvsk
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#ifdef SIGWINCH
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding signal(SIGWINCH, SIG_IGN);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#endif
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* //ap_bsetflag(conn->client, B_SAFEREAD, 0); */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding r->request_time = time(NULL);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding r->the_request = ap_pstrdup(r->pool, l);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding r->method = ap_getword_white(r->pool, &ll);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_update_connection_status(conn->id, "Method", r->method);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding uri = ap_getword_white(r->pool, &ll);
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron /* Provide quick information about the request method as soon as known */
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron r->method_number = ap_method_number_of(r->method);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (r->method_number == M_GET && r->method[0] == 'H') {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding r->header_only = 1;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_parse_uri(r, uri);
785be1b6298010956622771c870ab3cd8ca57a2faaron
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* getline returns (size of max buffer - 1) if it fills up the
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * buffer before finding the end-of-line. This is only going to
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * happen if it exceeds the configured limit for a request-line.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (len > r->server->limit_req_line) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding r->status = HTTP_REQUEST_URI_TOO_LARGE;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding r->proto_num = HTTP_VERSION(1,0);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding r->protocol = ap_pstrdup(r->pool, "HTTP/1.0");
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return 0;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding r->assbackwards = (ll[0] == '\0');
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding r->protocol = ap_pstrdup(r->pool, ll[0] ? ll : "HTTP/0.9");
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_update_connection_status(conn->id, "Protocol", r->protocol);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (2 == sscanf(r->protocol, "HTTP/%u.%u", &major, &minor)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding && minor < HTTP_VERSION(1,0)) /* don't allow HTTP/0.1000 */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding r->proto_num = HTTP_VERSION(major, minor);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding else
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding r->proto_num = HTTP_VERSION(1,0);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return 1;
dad234382d8424e1c5a30af2838e172aec9d6d1bdreid}
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fieldingstatic void get_mime_headers(request_rec *r)
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding{
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding char field[DEFAULT_LIMIT_REQUEST_FIELDSIZE + 2]; /* getline's two extra */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding conn_rec *c = r->connection;
e6cc28a5eb3371ba0c38e941855e71ff0054f50erbb char *value;
c1b808d160bfb5c849263be8d4acff600853a328trawick char *copy;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding int len;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding unsigned int fields_read = 0;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_table_t *tmp_headers;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* We'll use ap_overlap_tables later to merge these into r->headers_in. */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding tmp_headers = ap_make_table(r->pool, 50);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /*
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Read header lines until we get the empty separator line, a read error,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * the connection closes (EOF), reach the server limit, or we timeout.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding while ((len = getline(field, sizeof(field), c->client, 1)) > 0) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
785be1b6298010956622771c870ab3cd8ca57a2faaron if (r->server->limit_req_fields &&
785be1b6298010956622771c870ab3cd8ca57a2faaron (++fields_read > r->server->limit_req_fields)) {
785be1b6298010956622771c870ab3cd8ca57a2faaron r->status = HTTP_BAD_REQUEST;
785be1b6298010956622771c870ab3cd8ca57a2faaron ap_table_setn(r->notes, "error-notes",
785be1b6298010956622771c870ab3cd8ca57a2faaron "The number of request header fields exceeds "
785be1b6298010956622771c870ab3cd8ca57a2faaron "this server's limit.<P>\n");
785be1b6298010956622771c870ab3cd8ca57a2faaron return;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* getline returns (size of max buffer - 1) if it fills up the
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * buffer before finding the end-of-line. This is only going to
785be1b6298010956622771c870ab3cd8ca57a2faaron * happen if it exceeds the configured limit for a field size.
785be1b6298010956622771c870ab3cd8ca57a2faaron */
785be1b6298010956622771c870ab3cd8ca57a2faaron if (len > r->server->limit_req_fieldsize) {
785be1b6298010956622771c870ab3cd8ca57a2faaron r->status = HTTP_BAD_REQUEST;
785be1b6298010956622771c870ab3cd8ca57a2faaron ap_table_setn(r->notes, "error-notes", ap_pstrcat(r->pool,
785be1b6298010956622771c870ab3cd8ca57a2faaron "Size of a request header field exceeds server limit.<P>\n"
785be1b6298010956622771c870ab3cd8ca57a2faaron "<PRE>\n", field, "</PRE>\n", NULL));
785be1b6298010956622771c870ab3cd8ca57a2faaron return;
785be1b6298010956622771c870ab3cd8ca57a2faaron }
785be1b6298010956622771c870ab3cd8ca57a2faaron copy = ap_palloc(r->pool, len + 1);
785be1b6298010956622771c870ab3cd8ca57a2faaron memcpy(copy, field, len + 1);
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron if (!(value = strchr(copy, ':'))) { /* Find the colon separator */
785be1b6298010956622771c870ab3cd8ca57a2faaron r->status = HTTP_BAD_REQUEST; /* or abort the bad request */
785be1b6298010956622771c870ab3cd8ca57a2faaron ap_table_setn(r->notes, "error-notes", ap_pstrcat(r->pool,
785be1b6298010956622771c870ab3cd8ca57a2faaron "Request header field is missing colon separator.<P>\n"
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding "<PRE>\n", copy, "</PRE>\n", NULL));
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding *value = '\0';
51af95bb51b5084e883bad250b2afa2838e9ceebfielding ++value;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding while (*value == ' ' || *value == '\t')
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ++value; /* Skip to start of value */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_table_addn(tmp_headers, copy, value);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_overlap_tables(r->headers_in, tmp_headers, AP_OVERLAP_TABLES_MERGE);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding}
dad234382d8424e1c5a30af2838e172aec9d6d1bdreid
382fa07a63096c4a1aabfed36433ea5ac9c40ad0trawickrequest_rec *ap_read_request(conn_rec *conn)
c1b808d160bfb5c849263be8d4acff600853a328trawick{
e6cc28a5eb3371ba0c38e941855e71ff0054f50erbb request_rec *r;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_context_t *p;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding const char *expect;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding int access_status;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_create_context(&p, conn->pool);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding r = ap_pcalloc(p, sizeof(request_rec));
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding r->pool = p;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding r->connection = conn;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding r->server = conn->base_server;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
785be1b6298010956622771c870ab3cd8ca57a2faaron conn->keptalive = conn->keepalive == 1;
785be1b6298010956622771c870ab3cd8ca57a2faaron conn->keepalive = 0;
785be1b6298010956622771c870ab3cd8ca57a2faaron
785be1b6298010956622771c870ab3cd8ca57a2faaron r->user = NULL;
785be1b6298010956622771c870ab3cd8ca57a2faaron r->ap_auth_type = NULL;
785be1b6298010956622771c870ab3cd8ca57a2faaron
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding r->headers_in = ap_make_table(r->pool, 50);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding r->subprocess_env = ap_make_table(r->pool, 50);
785be1b6298010956622771c870ab3cd8ca57a2faaron r->headers_out = ap_make_table(r->pool, 12);
785be1b6298010956622771c870ab3cd8ca57a2faaron r->err_headers_out = ap_make_table(r->pool, 5);
785be1b6298010956622771c870ab3cd8ca57a2faaron r->notes = ap_make_table(r->pool, 5);
785be1b6298010956622771c870ab3cd8ca57a2faaron
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding r->request_config = ap_create_request_config(r->pool);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding r->per_dir_config = r->server->lookup_defaults;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding r->sent_bodyct = 0; /* bytect isn't for body */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
51af95bb51b5084e883bad250b2afa2838e9ceebfielding r->read_length = 0;
785be1b6298010956622771c870ab3cd8ca57a2faaron r->read_body = REQUEST_NO_BODY;
785be1b6298010956622771c870ab3cd8ca57a2faaron
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding r->status = HTTP_REQUEST_TIME_OUT; /* Until we get a request */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding r->the_request = NULL;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#ifdef CHARSET_EBCDIC
45acd673a68181802b112e97e84fa3813ddd3ec1stoddard ap_bsetflag(r->connection->client, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 1);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding#endif
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
1ccd992d37d62c8cb2056126f2234f64ec189bfddougm ap_bsetopt(conn->client, BO_TIMEOUT,
785be1b6298010956622771c870ab3cd8ca57a2faaron conn->keptalive
785be1b6298010956622771c870ab3cd8ca57a2faaron ? &r->server->keep_alive_timeout
785be1b6298010956622771c870ab3cd8ca57a2faaron : &r->server->timeout);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding /* Get the request... */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (!read_request_line(r)) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding if (r->status == HTTP_REQUEST_URI_TOO_LARGE) {
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding "request failed: URI too long");
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_send_error_response(r, 0);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_run_log_transaction(r);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return r;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna return NULL;
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna }
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna if (r->connection->keptalive) {
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna ap_bsetopt(r->connection->client, BO_TIMEOUT,
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna &r->server->timeout);
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna }
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna if (!r->assbackwards) {
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna get_mime_headers(r);
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna if (r->status != HTTP_REQUEST_TIME_OUT) {
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna "request failed: error reading the headers");
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna ap_send_error_response(r, 0);
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna ap_run_log_transaction(r);
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna return r;
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna }
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna }
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna else {
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna if (r->header_only) {
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna /*
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna * Client asked for headers only with HTTP/0.9, which doesn't send
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna * headers! Have to dink things just to make sure the error message
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna * comes through...
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna */
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna "client sent invalid HTTP/0.9 request: HEAD %s",
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna r->uri);
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna r->header_only = 0;
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna r->status = HTTP_BAD_REQUEST;
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna ap_send_error_response(r, 0);
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna ap_run_log_transaction(r);
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna return r;
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna }
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna }
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna r->status = HTTP_OK; /* Until further notice. */
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna /* update what we think the virtual host is based on the headers we've
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna * now read
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna */
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna ap_update_vhost_from_headers(r);
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna /* we may have switched to another server */
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna r->per_dir_config = r->server->lookup_defaults;
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna conn->keptalive = 0; /* We now have a request to play with */
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna if ((!r->hostname && (r->proto_num >= HTTP_VERSION(1,1))) ||
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna ((r->proto_num == HTTP_VERSION(1,1)) &&
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna !ap_table_get(r->headers_in, "Host"))) {
20f1b1a67eef5ab0f3295608c89964a7dca4fdd1pquerna /*
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * Client sent us an HTTP/1.1 or later request without telling us the
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * hostname, either with a full URL or a Host: header. We therefore
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * need to (as per the 1.1 spec) send an error. As a special case,
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * HTTP/1.1 mentions twice (S9, S14.23) that a request MUST contain
45acd673a68181802b112e97e84fa3813ddd3ec1stoddard * a Host: header, and the server MUST respond with 400 if it doesn't.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding */
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding r->status = HTTP_BAD_REQUEST;
8f8ec0957334f50b7ac11359f90490ee467258eedreid ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
382fa07a63096c4a1aabfed36433ea5ac9c40ad0trawick "client sent HTTP/1.1 request without hostname "
f4b96a996afbc46872f57ad1450e6ee1c8f13707jorton "(see RFC2068 section 9, and 14.23): %s", r->uri);
17f3ba69f65182426ad4e568bb2d6f192ccd2ed5trawick ap_send_error_response(r, 0);
e6cc28a5eb3371ba0c38e941855e71ff0054f50erbb ap_run_log_transaction(r);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding return r;
785be1b6298010956622771c870ab3cd8ca57a2faaron }
785be1b6298010956622771c870ab3cd8ca57a2faaron if (((expect = ap_table_get(r->headers_in, "Expect")) != NULL) &&
785be1b6298010956622771c870ab3cd8ca57a2faaron (expect[0] != '\0')) {
785be1b6298010956622771c870ab3cd8ca57a2faaron /*
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * The Expect header field was added to HTTP/1.1 after RFC 2068
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding * as a means to signal when a 100 response is desired and,
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf * unfortunately, to signal a poor man's mandatory extension that
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf * the server must understand or return 417 Expectation Failed.
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding */
c1b808d160bfb5c849263be8d4acff600853a328trawick if (strcasecmp(expect, "100-continue") == 0) {
e6cc28a5eb3371ba0c38e941855e71ff0054f50erbb r->expecting_100 = 1;
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding else {
785be1b6298010956622771c870ab3cd8ca57a2faaron r->status = HTTP_EXPECTATION_FAILED;
785be1b6298010956622771c870ab3cd8ca57a2faaron ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 0, r,
785be1b6298010956622771c870ab3cd8ca57a2faaron "client sent an unrecognized expectation value of "
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding "Expect: %s", expect);
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding ap_send_error_response(r, 0);
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf (void) ap_discard_request_body(r);
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf ap_run_log_transaction(r);
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf return r;
b88f887ed5554d9050d97f9a56a89ae62bdbd906fanf }
09fe0b69d3d1e8c8041c9ce99ee77b8b44b5e3b1fielding }
if ((access_status = ap_run_post_read_request(r))) {
ap_die(access_status, r);
ap_run_log_transaction(r);
return NULL;
}
return r;
}
/*
* A couple of other functions which initialize some of the fields of
* a request structure, as appropriate for adjuncts of one kind or another
* to a request in progress. Best here, rather than elsewhere, since
* *someone* has to set the protocol-specific fields...
*/
void ap_set_sub_req_protocol(request_rec *rnew, const request_rec *r)
{
rnew->the_request = r->the_request; /* Keep original request-line */
rnew->assbackwards = 1; /* Don't send headers from this. */
rnew->no_local_copy = 1; /* Don't try to send USE_LOCAL_COPY for a
* fragment. */
rnew->method = "GET";
rnew->method_number = M_GET;
rnew->protocol = "INCLUDED";
rnew->status = HTTP_OK;
rnew->headers_in = r->headers_in;
rnew->subprocess_env = ap_copy_table(rnew->pool, r->subprocess_env);
rnew->headers_out = ap_make_table(rnew->pool, 5);
rnew->err_headers_out = ap_make_table(rnew->pool, 5);
rnew->notes = ap_make_table(rnew->pool, 5);
rnew->expecting_100 = r->expecting_100;
rnew->read_length = r->read_length;
rnew->read_body = REQUEST_NO_BODY;
rnew->main = (request_rec *) r;
}
void ap_finalize_sub_req_protocol(request_rec *sub)
{
SET_BYTES_SENT(sub->main);
}
/*
* Support for the Basic authentication protocol, and a bit for Digest.
*/
API_EXPORT(void) ap_note_auth_failure(request_rec *r)
{
if (!strcasecmp(ap_auth_type(r), "Basic"))
ap_note_basic_auth_failure(r);
else if (!strcasecmp(ap_auth_type(r), "Digest"))
ap_note_digest_auth_failure(r);
}
API_EXPORT(void) ap_note_basic_auth_failure(request_rec *r)
{
if (strcasecmp(ap_auth_type(r), "Basic"))
ap_note_auth_failure(r);
else
ap_table_setn(r->err_headers_out,
r->proxyreq ? "Proxy-Authenticate" : "WWW-Authenticate",
ap_pstrcat(r->pool, "Basic realm=\"", ap_auth_name(r), "\"",
NULL));
}
API_EXPORT(void) ap_note_digest_auth_failure(request_rec *r)
{
ap_table_setn(r->err_headers_out,
r->proxyreq ? "Proxy-Authenticate" : "WWW-Authenticate",
ap_psprintf(r->pool, "Digest realm=\"%s\", nonce=\"%lu\"",
ap_auth_name(r), r->request_time));
}
API_EXPORT(int) ap_get_basic_auth_pw(request_rec *r, const char **pw)
{
const char *auth_line = ap_table_get(r->headers_in,
r->proxyreq ? "Proxy-Authorization"
: "Authorization");
const char *t;
if (!(t = ap_auth_type(r)) || strcasecmp(t, "Basic"))
return DECLINED;
if (!ap_auth_name(r)) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR,
0, r, "need AuthName: %s", r->uri);
return SERVER_ERROR;
}
if (!auth_line) {
ap_note_basic_auth_failure(r);
return AUTH_REQUIRED;
}
if (strcasecmp(ap_getword(r->pool, &auth_line, ' '), "Basic")) {
/* Client tried to authenticate using wrong auth scheme */
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
"client used wrong authentication scheme: %s", r->uri);
ap_note_basic_auth_failure(r);
return AUTH_REQUIRED;
}
/* CHARSET_EBCDIC Issue's here ?!? Compare with 32/9 instead
* as we are operating on an octed stream ?
*/
while (*auth_line== ' ' || *auth_line== '\t')
auth_line++;
t = ap_pbase64decode(r->pool, auth_line);
/* Note that this allocation has to be made from r->connection->pool
* because it has the lifetime of the connection. The other allocations
* are temporary and can be tossed away any time.
*/
r->user = ap_getword_nulls (r->pool, &t, ':');
r->ap_auth_type = "Basic";
*pw = t;
return OK;
}
/* 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",
#define LEVEL_500 44
"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"
};
/* 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];
*/
API_EXPORT(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 */
}
/* 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.
*/
API_EXPORT_NONSTD(int) ap_send_header_field(request_rec *r,
const char *fieldname, const char *fieldval)
{
return (0 < ap_rvputs(r, fieldname, ": ", fieldval, "\015\012", NULL));
}
API_EXPORT(void) ap_basic_http_header(request_rec *r)
{
char *protocol;
#ifdef CHARSET_EBCDIC
int convert = ap_bgetflag(r->connection->client, B_EBCDIC2ASCII);
#endif /*CHARSET_EBCDIC*/
if (r->assbackwards)
return;
if (!r->status_line)
r->status_line = status_lines[ap_index_of_response(r->status)];
/* mod_proxy is only HTTP/1.0, so avoid sending HTTP/1.1 error response;
* kluge around broken browsers when indicated by force-response-1.0
*/
if (r->proxyreq
|| (r->proto_num == HTTP_VERSION(1,0)
&& ap_table_get(r->subprocess_env, "force-response-1.0"))) {
protocol = "HTTP/1.0";
r->connection->keepalive = -1;
}
else
protocol = SERVER_PROTOCOL;
#ifdef CHARSET_EBCDIC
ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, 1);
#endif /*CHARSET_EBCDIC*/
/* Output the HTTP/1.x Status-Line and the Date and Server fields */
ap_rvputs(r, protocol, " ", r->status_line, "\015\012", NULL);
ap_send_header_field(r, "Date", ap_gm_timestr_822(r->pool, r->request_time));
ap_send_header_field(r, "Server", ap_get_server_version());
ap_table_unset(r->headers_out, "Date"); /* Avoid bogosity */
ap_table_unset(r->headers_out, "Server");
#ifdef CHARSET_EBCDIC
if (!convert)
ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, convert);
#endif /*CHARSET_EBCDIC*/
}
/* 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(request_rec *r)
{
long int bs;
ap_bgetopt(r->connection->client, BO_BYTECT, &bs);
if (bs >= 255 && bs <= 257)
ap_rputs("X-Pad: avoid browser bug\015\012", r);
ap_rputs("\015\012", r); /* Send the terminating empty line */
}
/* 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)
{
return 2 + ap_pstrcat(r->pool,
(r->allowed & (1 << M_GET)) ? ", GET, HEAD" : "",
(r->allowed & (1 << M_POST)) ? ", POST" : "",
(r->allowed & (1 << M_PUT)) ? ", PUT" : "",
(r->allowed & (1 << M_DELETE)) ? ", DELETE" : "",
(r->allowed & (1 << M_CONNECT)) ? ", CONNECT" : "",
(r->allowed & (1 << M_OPTIONS)) ? ", OPTIONS" : "",
(r->allowed & (1 << M_PATCH)) ? ", PATCH" : "",
(r->allowed & (1 << M_PROPFIND)) ? ", PROPFIND" : "",
(r->allowed & (1 << M_PROPPATCH)) ? ", PROPPATCH" : "",
(r->allowed & (1 << M_MKCOL)) ? ", MKCOL" : "",
(r->allowed & (1 << M_COPY)) ? ", COPY" : "",
(r->allowed & (1 << M_MOVE)) ? ", MOVE" : "",
(r->allowed & (1 << M_LOCK)) ? ", LOCK" : "",
(r->allowed & (1 << M_UNLOCK)) ? ", UNLOCK" : "",
", TRACE",
NULL);
}
API_EXPORT(int) ap_send_http_trace(request_rec *r)
{
int rv;
/* Get the original request */
while (r->prev)
r = r->prev;
if ((rv = ap_setup_client_block(r, REQUEST_NO_BODY)))
return rv;
r->content_type = "message/http";
ap_send_http_header(r);
/* Now we recreate the request, and echo it back */
ap_rvputs(r, r->the_request, "\015\012", NULL);
ap_table_do((int (*) (void *, const char *, const char *))
ap_send_header_field, (void *) r, r->headers_in, NULL);
ap_rputs("\015\012", r);
return OK;
}
int ap_send_http_options(request_rec *r)
{
const long int zero = 0L;
if (r->assbackwards)
return DECLINED;
ap_basic_http_header(r);
ap_table_setn(r->headers_out, "Content-Length", "0");
ap_table_setn(r->headers_out, "Allow", make_allow(r));
ap_set_keepalive(r);
ap_table_do((int (*) (void *, const char *, const char *)) ap_send_header_field,
(void *) r, r->headers_out, NULL);
terminate_header(r);
ap_bsetopt(r->connection->client, BO_BYTECT, &zero);
return OK;
}
/*
* 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 (ap_table_get(r->headers_in, "Request-Range") ||
((ua = ap_table_get(r->headers_in, "User-Agent"))
&& strstr(ua, "MSIE 3")));
}
/* This routine is called by ap_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)
{
ap_array_header_t *values;
char *start;
char *e;
char **strpp;
int i;
values = (ap_array_header_t *)d;
e = ap_pstrdup(values->cont, val);
do {
/* Find a non-empty fieldname */
while (*e == ',' || ap_isspace(*e)) {
++e;
}
if (*e == '\0') {
break;
}
start = e;
while (*e != '\0' && *e != ',' && !ap_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 **)ap_push_array(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)
{
ap_array_header_t *varies;
varies = ap_make_array(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.
*/
ap_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) {
ap_table_setn(r->headers_out, "Vary",
ap_array_pstrcat(r->pool, varies, ','));
}
}
API_EXPORT(void) ap_send_http_header(request_rec *r)
{
int i;
const long int zero = 0L;
#ifdef CHARSET_EBCDIC
int convert = ap_bgetflag(r->connection->client, B_EBCDIC2ASCII);
#endif /*CHARSET_EBCDIC*/
if (r->assbackwards) {
if (!r->main)
ap_bsetopt(r->connection->client, BO_BYTECT, &zero);
r->sent_bodyct = 1;
return;
}
/*
* 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 (!ap_is_empty_table(r->err_headers_out))
r->headers_out = ap_overlay_tables(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.
*/
if (ap_table_get(r->subprocess_env, "force-no-vary") != NULL) {
ap_table_unset(r->headers_out, "Vary");
r->proto_num = HTTP_VERSION(1,0);
ap_table_set(r->subprocess_env, "force-response-1.0", "1");
}
else {
fixup_vary(r);
}
ap_basic_http_header(r);
#ifdef CHARSET_EBCDIC
ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, 1);
#endif /*CHARSET_EBCDIC*/
ap_set_keepalive(r);
if (r->chunked) {
ap_table_mergen(r->headers_out, "Transfer-Encoding", "chunked");
ap_table_unset(r->headers_out, "Content-Length");
}
if (r->byterange > 1)
ap_table_setn(r->headers_out, "Content-Type",
ap_pstrcat(r->pool, "multipart", use_range_x(r) ? "/x-" : "/",
"byteranges; boundary=", r->boundary, NULL));
else if (r->content_type)
ap_table_setn(r->headers_out, "Content-Type", r->content_type);
else
ap_table_setn(r->headers_out, "Content-Type", ap_default_type(r));
if (r->content_encoding)
ap_table_setn(r->headers_out, "Content-Encoding", r->content_encoding);
if (r->content_languages && r->content_languages->nelts) {
for (i = 0; i < r->content_languages->nelts; ++i) {
ap_table_mergen(r->headers_out, "Content-Language",
((char **) (r->content_languages->elts))[i]);
}
}
else if (r->content_language)
ap_table_setn(r->headers_out, "Content-Language", r->content_language);
/*
* Control cachability for non-cachable responses if not already set by
* some other part of the server configuration.
*/
if (r->no_cache && !ap_table_get(r->headers_out, "Expires"))
ap_table_addn(r->headers_out, "Expires",
ap_gm_timestr_822(r->pool, r->request_time));
/* Send the entire ap_table_t of header fields, terminated by an empty line. */
ap_table_do((int (*) (void *, const char *, const char *)) ap_send_header_field,
(void *) r, r->headers_out, NULL);
terminate_header(r);
ap_bsetopt(r->connection->client, BO_BYTECT, &zero);
r->sent_bodyct = 1; /* Whatever follows is real body stuff... */
/* Set buffer flags for the body */
if (r->chunked)
ap_bsetflag(r->connection->client, B_CHUNK, 1);
#ifdef CHARSET_EBCDIC
if (!convert)
ap_bsetflag(r->connection->client, B_EBCDIC2ASCII, convert);
#endif /*CHARSET_EBCDIC*/
}
/* finalize_request_protocol is called at completion of sending the
* response. It's sole purpose is to send the terminating protocol
* information for any wrappers around the response message body
* (i.e., transfer encodings). It should have been named finalize_response.
*/
API_EXPORT(void) ap_finalize_request_protocol(request_rec *r)
{
if (r->chunked && !r->connection->aborted) {
/*
* Turn off chunked encoding --- we can only do this once.
*/
r->chunked = 0;
ap_bsetflag(r->connection->client, B_CHUNK, 0);
ap_rputs("0\015\012", r);
/* If we had footer "headers", we'd send them now */
ap_rputs("\015\012", r);
}
}
/* 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.
* REQUEST_CHUNKED_PASS Pass the chunks to me without removal.
*
* 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.
*/
API_EXPORT(int) ap_setup_client_block(request_rec *r, int read_policy)
{
const char *tenc = ap_table_get(r->headers_in, "Transfer-Encoding");
const char *lenp = ap_table_get(r->headers_in, "Content-Length");
unsigned long max_body;
r->read_body = read_policy;
r->read_chunked = 0;
r->remaining = 0;
if (tenc) {
if (strcasecmp(tenc, "chunked")) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|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_NOERRNO|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) {
const char *pos = lenp;
while (ap_isdigit(*pos) || ap_isspace(*pos))
++pos;
if (*pos != '\0') {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
"Invalid Content-Length %s", lenp);
return HTTP_BAD_REQUEST;
}
r->remaining = atol(lenp);
}
if ((r->read_body == REQUEST_NO_BODY) &&
(r->read_chunked || (r->remaining > 0))) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
"%s with body is not allowed for %s", r->method, r->uri);
return HTTP_REQUEST_ENTITY_TOO_LARGE;
}
max_body = ap_get_limit_req_body(r);
if (max_body && (r->remaining > max_body)) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
"Request content-length of %s is larger than the configured "
"limit of %lu", lenp, max_body);
return HTTP_REQUEST_ENTITY_TOO_LARGE;
}
return OK;
}
API_EXPORT(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;
if (r->expecting_100 && r->proto_num >= HTTP_VERSION(1,1)) {
/* sending 100 Continue interim response */
ap_rvputs(r, SERVER_PROTOCOL, " ", status_lines[0], "\015\012\015\012",
NULL);
ap_rflush(r);
}
return 1;
}
static long get_chunk_size(char *b)
{
long chunksize = 0;
while (ap_isxdigit(*b)) {
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;
++b;
}
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.
*
* Reading the chunked encoding requires a buffer size large enough to
* hold a chunk-size line, including any extensions. For now, we'll leave
* that to the caller, at least until we can come up with a better solution.
*/
API_EXPORT(long) ap_get_client_block(request_rec *r, char *buffer, int bufsiz)
{
int c;
ap_size_t len_to_read;
ap_ssize_t len_read;
long chunk_start = 0;
unsigned long max_body;
ap_status_t rv;
if (!r->read_chunked) { /* Content-length read */
len_to_read = (r->remaining > bufsiz) ? bufsiz : r->remaining;
rv = ap_bread(r->connection->client, buffer, len_to_read, &len_read);
if (len_read == 0) { /* error or eof */
if (rv != APR_SUCCESS) {
r->connection->keepalive = -1;
return -1;
}
return 0;
}
r->read_length += len_read;
r->remaining -= len_read;
return len_read;
}
/*
* Handle chunked reading Note: we are careful to shorten the input
* bufsiz so that there will always be enough space for us to add a CRLF
* (if necessary).
*/
if (r->read_body == REQUEST_CHUNKED_PASS)
bufsiz -= 2;
if (bufsiz <= 0)
return -1; /* Cannot read chunked with a small buffer */
/* Check to see if we have already read too much request data.
* For efficiency reasons, we only check this at the top of each
* caller read pass, since the limit exists just to stop infinite
* length requests and nobody cares if it goes over by one buffer.
*/
max_body = ap_get_limit_req_body(r);
if (max_body && (r->read_length > max_body)) {
ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r,
"Chunked request body is larger than the configured limit of %lu",
max_body);
r->connection->keepalive = -1;
return -1;
}
if (r->remaining == 0) { /* Start of new chunk */
chunk_start = getline(buffer, bufsiz, r->connection->client, 0);
if ((chunk_start <= 0) || (chunk_start >= (bufsiz - 1))
|| !ap_isxdigit(*buffer)) {
r->connection->keepalive = -1;
return -1;
}
len_to_read = get_chunk_size(buffer);
if (len_to_read == 0) { /* Last chunk indicated, get footers */
if (r->read_body == REQUEST_CHUNKED_DECHUNK) {
get_mime_headers(r);
ap_snprintf(buffer, bufsiz, "%ld", r->read_length);
ap_table_unset(r->headers_in, "Transfer-Encoding");
ap_table_setn(r->headers_in, "Content-Length",
ap_pstrdup(r->pool, buffer));
return 0;
}
r->remaining = -1; /* Indicate footers in-progress */
}
else {
r->remaining = len_to_read;
}
if (r->read_body == REQUEST_CHUNKED_PASS) {
buffer[chunk_start++] = CR; /* Restore chunk-size line end */
buffer[chunk_start++] = LF;
buffer += chunk_start; /* and pass line on to caller */
bufsiz -= chunk_start;
}
else {
/* REQUEST_CHUNKED_DECHUNK -- do not include the length of the
* header in the return value
*/
chunk_start = 0;
}
}
/* When REQUEST_CHUNKED_PASS, we are */
if (r->remaining == -1) { /* reading footers until empty line */
len_read = chunk_start;
while ((bufsiz > 1) && ((len_read =
getline(buffer, bufsiz, r->connection->client, 1)) > 0)) {
if (len_read != (bufsiz - 1)) {
buffer[len_read++] = CR; /* Restore footer line end */
buffer[len_read++] = LF;
}
chunk_start += len_read;
buffer += len_read;
bufsiz -= len_read;
}
if (len_read < 0) {
r->connection->keepalive = -1;
return -1;
}
if (len_read == 0) { /* Indicates an empty line */
buffer[0] = CR;
buffer[1] = LF;
chunk_start += 2;
r->remaining = -2;
}
r->read_length += chunk_start;
return chunk_start;
}
/* When REQUEST_CHUNKED_PASS, we */
if (r->remaining == -2) { /* finished footers when last called */
r->remaining = 0; /* so now we must signal EOF */
return 0;
}
/* Otherwise, we are in the midst of reading a chunk of data */
len_to_read = (r->remaining > bufsiz) ? bufsiz : r->remaining;
(void) ap_bread(r->connection->client, buffer, len_to_read, &len_read);
if (len_read == 0) { /* error or eof */
r->connection->keepalive = -1;
return -1;
}
r->remaining -= len_read;
if (r->remaining == 0) { /* End of chunk, get trailing CRLF */
if ((c = ap_bgetc(r->connection->client)) == CR) {
c = ap_bgetc(r->connection->client);
}
if (c != LF) {
r->connection->keepalive = -1;
return -1;
}
if (r->read_body == REQUEST_CHUNKED_PASS) {
buffer[len_read++] = CR;
buffer[len_read++] = LF;
}
}
r->read_length += (chunk_start + len_read);
return (chunk_start + len_read);
}
/* 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;
*/
API_EXPORT(int) ap_discard_request_body(request_rec *r)
{
int rv;
if ((rv = ap_setup_client_block(r, REQUEST_CHUNKED_PASS)))
return rv;
/* In order to avoid sending 100 Continue when we already know the
* final response status, and yet not kill the connection if there is
* no request body to be read, we need to duplicate the test from
* ap_should_client_block() here negated rather than call it directly.
*/
if ((r->read_length == 0) && (r->read_chunked || (r->remaining > 0))) {
char dumpbuf[HUGE_STRING_LEN];
if (r->expecting_100) {
r->connection->keepalive = -1;
return OK;
}
while ((rv = ap_get_client_block(r, dumpbuf, HUGE_STRING_LEN)) > 0)
continue;
if (rv < 0)
return HTTP_BAD_REQUEST;
}
return OK;
}
/*
* Send the body of a response to the client.
*/
API_EXPORT(long) ap_send_fd(ap_file_t *fd, request_rec *r)
{
return ap_send_fd_length(fd, r, -1);
}
API_EXPORT(long) ap_send_fd_length(ap_file_t *fd, request_rec *r, long length)
{
char buf[IOBUFSIZE];
long total_bytes_sent = 0;
register int o;
ap_ssize_t w;
ap_ssize_t n;
ap_status_t rv;
if (length == 0)
return 0;
while (!ap_is_aborted(r->connection)) {
if ((length > 0) && (total_bytes_sent + IOBUFSIZE) > length)
o = length - total_bytes_sent;
else
o = IOBUFSIZE;
n = o;
do {
rv = ap_read(fd, buf, &n);
} while (rv == APR_EINTR && !ap_is_aborted(r->connection) &&
(n < 1));
if (n < 1) {
break;
}
o = 0;
while (n && !ap_is_aborted(r->connection)) {
rv = ap_bwrite(r->connection->client, &buf[o], n, &w);
if (w > 0) {
total_bytes_sent += w;
n -= w;
o += w;
}
else if (rv != APR_SUCCESS) {
if (!ap_is_aborted(r->connection)) {
ap_log_rerror(APLOG_MARK, APLOG_INFO, rv, r,
"client stopped connection before send body completed");
ap_bsetflag(r->connection->client, B_EOUT, 1);
r->connection->aborted = 1;
}
break;
}
}
}
SET_BYTES_SENT(r);
return total_bytes_sent;
}
/*
* Send the body of a response to the client.
*/
API_EXPORT(long) ap_send_fb(BUFF *fb, request_rec *r)
{
return ap_send_fb_length(fb, r, -1);
}
API_EXPORT(long) ap_send_fb_length(BUFF *fb, request_rec *r, long length)
{
char buf[IOBUFSIZE];
long total_bytes_sent = 0;
long zero_timeout = 0;
register int o;
ap_ssize_t w;
ap_ssize_t n;
ap_status_t rv;
if (length == 0) {
return 0;
}
/* This function tries to as much as possible through non-blocking
* reads so that it can do writes while waiting for the CGI to
* produce more data. This way, the CGI's output gets to the client
* as soon as possible */
ap_bsetopt(fb, BO_TIMEOUT, &zero_timeout);
while (!ap_is_aborted(r->connection)) {
rv = ap_bread(fb, buf, sizeof(buf), &n);
if (n == 0) {
if (rv == APR_SUCCESS) { /* eof */
(void) ap_rflush(r);
break;
}
if (rv != APR_EAGAIN) {
r->connection->aborted = 1;
break;
}
/* next read will block, so flush the client now */
if (ap_rflush(r) == EOF) {
break;
}
ap_bsetopt(fb, BO_TIMEOUT, &r->server->timeout);
rv = ap_bread(fb, buf, sizeof(buf), &n);
if (n == 0) {
if (rv == APR_SUCCESS) { /* eof */
(void) ap_rflush(r);
}
r->connection->aborted = 1;
break;
}
ap_bsetopt(fb, BO_TIMEOUT, &zero_timeout);
}
o = 0;
while (n && !ap_is_aborted(r->connection)) {
rv = ap_bwrite(r->connection->client, &buf[o], n, &w);
if (w > 0) {
total_bytes_sent += w;
n -= w;
o += w;
}
else if (rv != APR_SUCCESS) {
if (!ap_is_aborted(r->connection)) {
ap_log_rerror(APLOG_MARK, APLOG_INFO, rv, r,
"client stopped connection before rflush completed");
ap_bsetflag(r->connection->client, B_EOUT, 1);
r->connection->aborted = 1;
}
break;
}
}
}
SET_BYTES_SENT(r);
return total_bytes_sent;
}
/* The code writes MMAP_SEGMENT_SIZE bytes at a time. This is due to Apache's
* timeout model, which is a timeout per-write rather than a time for the
* entire transaction to complete. Essentially this should be small enough
* so that in one Timeout period, your slowest clients should be reasonably
* able to receive this many bytes.
*
* To take advantage of zero-copy TCP under Solaris 2.6 this should be a
* multiple of 16k. (And you need a SunATM2.0 network card.)
*/
#ifndef MMAP_SEGMENT_SIZE
#define MMAP_SEGMENT_SIZE 32768
#endif
/* send data from an in-memory buffer */
API_EXPORT(size_t) ap_send_mmap(void *mm, request_rec *r, size_t offset,
size_t length)
{
size_t total_bytes_sent = 0;
int n;
ap_ssize_t w;
ap_status_t rv;
if (length == 0)
return 0;
length += offset;
while (!r->connection->aborted && offset < length) {
if (length - offset > MMAP_SEGMENT_SIZE) {
n = MMAP_SEGMENT_SIZE;
}
else {
n = length - offset;
}
while (n && !r->connection->aborted) {
rv = ap_bwrite(r->connection->client, (char *) mm + offset, n, &w);
if (w > 0) {
total_bytes_sent += w;
n -= w;
offset += w;
}
else if (rv != APR_SUCCESS) {
if (r->connection->aborted)
break;
else if (rv == EAGAIN)
continue;
else {
ap_log_rerror(APLOG_MARK, APLOG_INFO, rv, r,
"client stopped connection before send mmap completed");
ap_bsetflag(r->connection->client, B_EOUT, 1);
r->connection->aborted = 1;
break;
}
}
}
}
SET_BYTES_SENT(r);
return total_bytes_sent;
}
API_EXPORT(int) ap_rputc(int c, request_rec *r)
{
if (r->connection->aborted)
return EOF;
if (ap_bputc(c, r->connection->client) < 0) {
if (!r->connection->aborted) {
ap_log_rerror(APLOG_MARK, APLOG_INFO, errno, r,
"client stopped connection before rputc completed");
ap_bsetflag(r->connection->client, B_EOUT, 1);
r->connection->aborted = 1;
}
return EOF;
}
SET_BYTES_SENT(r);
return c;
}
API_EXPORT(int) ap_rputs(const char *str, request_rec *r)
{
int rcode;
if (r->connection->aborted)
return EOF;
rcode = ap_bputs(str, r->connection->client);
if (rcode < 0) {
if (!r->connection->aborted) {
ap_log_rerror(APLOG_MARK, APLOG_INFO, errno, r,
"client stopped connection before rputs completed");
ap_bsetflag(r->connection->client, B_EOUT, 1);
r->connection->aborted = 1;
}
return EOF;
}
SET_BYTES_SENT(r);
return rcode;
}
API_EXPORT(int) ap_rwrite(const void *buf, int nbyte, request_rec *r)
{
ap_ssize_t n;
ap_status_t rv;
if (r->connection->aborted)
return EOF;
rv = ap_bwrite(r->connection->client, buf, nbyte, &n);
if (n < 0) {
if (!r->connection->aborted) {
ap_log_rerror(APLOG_MARK, APLOG_INFO, rv, r,
"client stopped connection before rwrite completed");
ap_bsetflag(r->connection->client, B_EOUT, 1);
r->connection->aborted = 1;
}
return EOF;
}
SET_BYTES_SENT(r);
return n;
}
API_EXPORT(int) ap_vrprintf(request_rec *r, const char *fmt, va_list ap)
{
int n;
if (r->connection->aborted)
return -1;
n = ap_vbprintf(r->connection->client, fmt, ap);
if (n < 0) {
if (!r->connection->aborted) {
ap_log_rerror(APLOG_MARK, APLOG_INFO, errno, r,
"client stopped connection before vrprintf completed");
ap_bsetflag(r->connection->client, B_EOUT, 1);
r->connection->aborted = 1;
}
return -1;
}
SET_BYTES_SENT(r);
return n;
}
API_EXPORT(int) ap_rprintf(request_rec *r, const char *fmt,...)
{
va_list vlist;
int n;
if (r->connection->aborted)
return -1;
va_start(vlist, fmt);
n = ap_vbprintf(r->connection->client, fmt, vlist);
va_end(vlist);
if (n < 0) {
if (!r->connection->aborted) {
ap_log_rerror(APLOG_MARK, APLOG_INFO, errno, r,
"client stopped connection before rprintf completed");
ap_bsetflag(r->connection->client, B_EOUT, 1);
r->connection->aborted = 1;
}
return -1;
}
SET_BYTES_SENT(r);
return n;
}
API_EXPORT_NONSTD(int) ap_rvputs(request_rec *r,...)
{
va_list args;
int i, j, k;
const char *x;
BUFF *fb = r->connection->client;
ap_status_t rv;
if (r->connection->aborted)
return EOF;
va_start(args, r);
for (k = 0;;) {
x = va_arg(args, const char *);
if (x == NULL)
break;
j = strlen(x);
rv = ap_bwrite(fb, x, j, &i);
if (i != j) {
va_end(args);
if (!r->connection->aborted) {
ap_log_rerror(APLOG_MARK, APLOG_INFO, rv, r,
"client stopped connection before rvputs completed");
ap_bsetflag(r->connection->client, B_EOUT, 1);
r->connection->aborted = 1;
}
return EOF;
}
k += i;
}
va_end(args);
SET_BYTES_SENT(r);
return k;
}
API_EXPORT(int) ap_rflush(request_rec *r)
{
ap_status_t rv;
if ((rv = ap_bflush(r->connection->client)) != APR_SUCCESS) {
if (!ap_is_aborted(r->connection)) {
ap_log_rerror(APLOG_MARK, APLOG_INFO, rv, r,
"client stopped connection before rflush completed");
ap_bsetflag(r->connection->client, B_EOUT, 1);
r->connection->aborted = 1;
}
errno = rv;
return EOF;
}
return 0;
}
/* 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.
*/
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 = ap_table_get(r->headers_out, "Location");
/*
* 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 = ap_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) {
if (!ap_is_empty_table(r->err_headers_out))
r->headers_out = ap_overlay_tables(r->pool, r->err_headers_out,
r->headers_out);
ap_basic_http_header(r);
ap_set_keepalive(r);
ap_table_do((int (*)(void *, const char *, const char *)) ap_send_header_field,
(void *) r, r->headers_out,
"Connection",
"Keep-Alive",
"ETag",
"Content-Location",
"Expires",
"Cache-Control",
"Vary",
"Warning",
"WWW-Authenticate",
"Proxy-Authenticate",
NULL);
terminate_header(r);
return;
}
if (status == HTTP_NO_CONTENT) {
ap_send_http_header(r);
ap_finalize_request_protocol(r);
return;
}
if (!r->assbackwards) {
ap_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;
ap_clear_table(r->err_headers_out);
if (ap_is_HTTP_REDIRECT(status) || (status == HTTP_CREATED)) {
if ((location != NULL) && *location) {
ap_table_setn(r->headers_out, "Location", location);
}
else {
location = ""; /* avoids coredump when printing, below */
}
}
r->content_language = NULL;
r->content_languages = NULL;
r->content_encoding = NULL;
r->clength = 0;
r->content_type = "text/html";
if ((status == METHOD_NOT_ALLOWED) || (status == NOT_IMPLEMENTED))
ap_table_setn(r->headers_out, "Allow", make_allow(r));
ap_send_http_header(r);
if (r->header_only) {
ap_finalize_request_protocol(r);
ap_rflush(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 doesn't, we've got a recursive error, so find
* the original error and output that as well.
*/
if (custom_response[0] == '\"') {
ap_rputs(custom_response + 1, r);
ap_finalize_request_protocol(r);
ap_rflush(r);
return;
}
/*
* Redirect failed, so get back the original error
*/
while (r->prev && (r->prev->status != HTTP_OK))
r = r->prev;
}
{
const char *title = status_lines[idx];
const char *h1;
const char *error_notes;
/* 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 */
&& ap_isdigit(r->status_line[0])
&& ap_isdigit(r->status_line[1])
&& ap_isdigit(r->status_line[2])
&& ap_isspace(r->status_line[3])
&& ap_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];
ap_rvputs(r,
DOCTYPE_HTML_2_0
"<HTML><HEAD>\n<TITLE>", title,
"</TITLE>\n</HEAD><BODY>\n<H1>", h1, "</H1>\n",
NULL);
switch (status) {
case HTTP_MOVED_PERMANENTLY:
case HTTP_MOVED_TEMPORARILY:
case HTTP_TEMPORARY_REDIRECT:
ap_rvputs(r, "The document has moved <A HREF=\"",
ap_escape_html(r->pool, location), "\">here</A>.<P>\n",
NULL);
break;
case HTTP_SEE_OTHER:
ap_rvputs(r, "The answer to your request is located <A HREF=\"",
ap_escape_html(r->pool, location), "\">here</A>.<P>\n",
NULL);
break;
case HTTP_USE_PROXY:
ap_rvputs(r, "This resource is only accessible "
"through the proxy\n",
ap_escape_html(r->pool, location),
"<BR>\nYou will need to ",
"configure your client to use that proxy.<P>\n", NULL);
break;
case HTTP_PROXY_AUTHENTICATION_REQUIRED:
case AUTH_REQUIRED:
ap_rputs("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.<P>\n", r);
break;
case BAD_REQUEST:
ap_rputs("Your browser sent a request that "
"this server could not understand.<P>\n", r);
if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) {
ap_rvputs(r, error_notes, "<P>\n", NULL);
}
break;
case HTTP_FORBIDDEN:
ap_rvputs(r, "You don't have permission to access ",
ap_escape_html(r->pool, r->uri),
"\non this server.<P>\n", NULL);
break;
case NOT_FOUND:
ap_rvputs(r, "The requested URL ",
ap_escape_html(r->pool, r->uri),
" was not found on this server.<P>\n", NULL);
break;
case METHOD_NOT_ALLOWED:
ap_rvputs(r, "The requested method ", r->method,
" is not allowed "
"for the URL ", ap_escape_html(r->pool, r->uri),
".<P>\n", NULL);
break;
case NOT_ACCEPTABLE:
ap_rvputs(r,
"An appropriate representation of the "
"requested resource ",
ap_escape_html(r->pool, r->uri),
" could not be found on this server.<P>\n", NULL);
/* fall through */
case MULTIPLE_CHOICES:
{
const char *list;
if ((list = ap_table_get(r->notes, "variant-list")))
ap_rputs(list, r);
}
break;
case LENGTH_REQUIRED:
ap_rvputs(r, "A request of the requested method ", r->method,
" requires a valid Content-length.<P>\n", NULL);
if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) {
ap_rvputs(r, error_notes, "<P>\n", NULL);
}
break;
case PRECONDITION_FAILED:
ap_rvputs(r, "The precondition on the request for the URL ",
ap_escape_html(r->pool, r->uri),
" evaluated to false.<P>\n", NULL);
break;
case HTTP_NOT_IMPLEMENTED:
ap_rvputs(r, ap_escape_html(r->pool, r->method), " to ",
ap_escape_html(r->pool, r->uri),
" not supported.<P>\n", NULL);
if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) {
ap_rvputs(r, error_notes, "<P>\n", NULL);
}
break;
case BAD_GATEWAY:
ap_rputs("The proxy server received an invalid\015\012"
"response from an upstream server.<P>\015\012", r);
if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) {
ap_rvputs(r, error_notes, "<P>\n", NULL);
}
break;
case VARIANT_ALSO_VARIES:
ap_rvputs(r, "A variant for the requested resource\n<PRE>\n",
ap_escape_html(r->pool, r->uri),
"\n</PRE>\nis itself a negotiable resource. "
"This indicates a configuration error.<P>\n", NULL);
break;
case HTTP_REQUEST_TIME_OUT:
ap_rputs("I'm tired of waiting for your request.\n", r);
break;
case HTTP_GONE:
ap_rvputs(r, "The requested resource<BR>",
ap_escape_html(r->pool, r->uri),
"<BR>\nis no longer available on this server ",
"and there is no forwarding address.\n",
"Please remove all references to this resource.\n",
NULL);
break;
case HTTP_REQUEST_ENTITY_TOO_LARGE:
ap_rvputs(r, "The requested resource<BR>",
ap_escape_html(r->pool, r->uri), "<BR>\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);
break;
case HTTP_REQUEST_URI_TOO_LARGE:
ap_rputs("The requested URL's length exceeds the capacity\n"
"limit for this server.<P>\n", r);
if ((error_notes = ap_table_get(r->notes, "error-notes")) != NULL) {
ap_rvputs(r, error_notes, "<P>\n", NULL);
}
break;
case HTTP_UNSUPPORTED_MEDIA_TYPE:
ap_rputs("The supplied request data is not in a format\n"
"acceptable for processing by this resource.\n", r);
break;
case HTTP_RANGE_NOT_SATISFIABLE:
ap_rputs("None of the range-specifier values in the Range\n"
"request-header field overlap the current extent\n"
"of the selected resource.\n", r);
break;
case HTTP_EXPECTATION_FAILED:
ap_rvputs(r, "The expectation given in the Expect request-header"
"\nfield could not be met by this server.<P>\n"
"The client sent<PRE>\n Expect: ",
ap_table_get(r->headers_in, "Expect"), "\n</PRE>\n"
"but we only allow the 100-continue expectation.\n",
NULL);
break;
case HTTP_UNPROCESSABLE_ENTITY:
ap_rputs("The server understands the media type of the\n"
"request entity, but was unable to process the\n"
"contained instructions.\n", r);
break;
case HTTP_LOCKED:
ap_rputs("The requested resource is currently locked.\n"
"The lock must be released or proper identification\n"
"given before the method can be applied.\n", r);
break;
case HTTP_FAILED_DEPENDENCY:
ap_rputs("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", r);
break;
case HTTP_INSUFFICIENT_STORAGE:
ap_rputs("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", r);
break;
case HTTP_SERVICE_UNAVAILABLE:
ap_rputs("The server is temporarily unable to service your\n"
"request due to maintenance downtime or capacity\n"
"problems. Please try again later.\n", r);
break;
case HTTP_GATEWAY_TIME_OUT:
ap_rputs("The proxy server did not receive a timely response\n"
"from the upstream server.\n", r);
break;
case HTTP_NOT_EXTENDED:
ap_rputs("A mandatory extension policy in the request is not\n"
"accepted by the server for this resource.\n", r);
break;
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 = ap_table_get(r->notes, "error-notes")) != NULL)
&& (h1 = ap_table_get(r->notes, "verbose-error-to")) != NULL
&& (strcmp(h1, "*") == 0)) {
ap_rvputs(r, error_notes, "<P>\n", NULL);
}
else {
ap_rvputs(r, "The server encountered an internal error or\n"
"misconfiguration and was unable to complete\n"
"your request.<P>\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.<P>\n"
"More information about this error may be available\n"
"in the server error log.<P>\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
* 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 = ap_table_get(r->notes, "error-notes")) != NULL) {
* ap_rvputs(r, error_notes, "<P>\n", NULL);
* }
*/
break;
}
if (recursive_error) {
ap_rvputs(r, "<P>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_rputs(ap_psignature("<HR>\n", r), r);
ap_rputs("</BODY></HTML>\n", r);
}
ap_finalize_request_protocol(r);
ap_rflush(r);
}
IMPLEMENT_HOOK_RUN_ALL(int,post_read_request,(request_rec *r),(r),OK,DECLINED)
IMPLEMENT_HOOK_RUN_ALL(int,log_transaction,(request_rec *r),(r),OK,DECLINED)
IMPLEMENT_HOOK_RUN_FIRST(const char *,http_method,(const request_rec *r),(r),
NULL)
IMPLEMENT_HOOK_RUN_FIRST(unsigned short,default_port,(const request_rec *r),
(r),0)