http_protocol.c revision 6af61679f5d25e77f0d34112d714f9ae4cc666e3
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher/* ====================================================================
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * The Apache Software License, Version 1.1
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher *
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * Copyright (c) 2000-2001 The Apache Software Foundation. All rights
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * reserved.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher *
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * Redistribution and use in source and binary forms, with or without
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * modification, are permitted provided that the following conditions
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * are met:
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher *
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * 1. Redistributions of source code must retain the above copyright
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * notice, this list of conditions and the following disclaimer.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher *
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * 2. Redistributions in binary form must reproduce the above copyright
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * notice, this list of conditions and the following disclaimer in
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * the documentation and/or other materials provided with the
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * distribution.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher *
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * 3. The end-user documentation included with the redistribution,
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * if any, must include the following acknowledgment:
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * "This product includes software developed by the
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * Apache Software Foundation (http://www.apache.org/)."
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * Alternately, this acknowledgment may appear in the software itself,
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * if and wherever such third-party acknowledgments normally appear.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher *
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * 4. The names "Apache" and "Apache Software Foundation" must
e0404de84c31d2387bb244d018a5cac8d01f8b19Simo Sorce * not be used to endorse or promote products derived from this
e0404de84c31d2387bb244d018a5cac8d01f8b19Simo Sorce * software without prior written permission. For written
e0404de84c31d2387bb244d018a5cac8d01f8b19Simo Sorce * permission, please contact apache@apache.org.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher *
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * 5. Products derived from this software may not be called "Apache",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * nor may "Apache" appear in their name, without prior written
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * permission of the Apache Software Foundation.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher *
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * SUCH DAMAGE.
e0404de84c31d2387bb244d018a5cac8d01f8b19Simo Sorce * ====================================================================
e0404de84c31d2387bb244d018a5cac8d01f8b19Simo Sorce *
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * This software consists of voluntary contributions made by many
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * individuals on behalf of the Apache Software Foundation. For more
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * information on the Apache Software Foundation, please see
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * <http://www.apache.org/>.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher *
e0404de84c31d2387bb244d018a5cac8d01f8b19Simo Sorce * Portions of this software are based upon public domain software
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * originally written at the National Center for Supercomputing Applications,
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * University of Illinois, Urbana-Champaign.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher/*
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * http_protocol.c --- routines which directly communicate with the client.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher *
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * Code originally by Rob McCool; much redone by Robert S. Thau
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * and the Apache Software Foundation.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher#include "apr.h"
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher#include "apr_strings.h"
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher#include "apr_buckets.h"
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher#include "apr_lib.h"
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher#include "apr_signal.h"
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher#define APR_WANT_STDIO /* for sscanf */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher#define APR_WANT_STRFUNC
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher#define APR_WANT_MEMFUNC
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher#include "apr_want.h"
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher#define CORE_PRIVATE
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher#include "util_filter.h"
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher#include "ap_config.h"
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher#include "httpd.h"
e0404de84c31d2387bb244d018a5cac8d01f8b19Simo Sorce#include "http_config.h"
e0404de84c31d2387bb244d018a5cac8d01f8b19Simo Sorce#include "http_core.h"
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher#include "http_protocol.h"
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher#include "http_main.h"
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher#include "http_request.h"
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher#include "http_vhost.h"
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher#include "http_log.h" /* For errors detected in basic auth common
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * support code... */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher#include "util_date.h" /* For parseHTTPdate and BAD_DATE */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher#include "util_charset.h"
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher#include "util_ebcdic.h"
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher#include "mod_core.h"
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher#if APR_HAVE_STDARG_H
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher#include <stdarg.h>
e0404de84c31d2387bb244d018a5cac8d01f8b19Simo Sorce#endif
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher#if APR_HAVE_UNISTD_H
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher#include <unistd.h>
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher#endif
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen GallagherAP_DECLARE(int) ap_set_keepalive(request_rec *r)
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher{
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher int ka_sent = 0;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher int wimpy = ap_find_token(r->pool,
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher apr_table_get(r->headers_out, "Connection"), "close");
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher const char *conn = apr_table_get(r->headers_in, "Connection");
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher /* The following convoluted conditional determines whether or not
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * the current connection should remain persistent after this response
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * (a.k.a. HTTP Keep-Alive) and whether or not the output message
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * body should use the HTTP/1.1 chunked transfer-coding. In English,
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher *
e0404de84c31d2387bb244d018a5cac8d01f8b19Simo Sorce * IF we have not marked this connection as errored;
21d485184df986e1a123f70c689517386e51a5ceMichal Zidek * and the response body has a defined length due to the status code
21d485184df986e1a123f70c689517386e51a5ceMichal Zidek * being 304 or 204, the request method being HEAD, already
21d485184df986e1a123f70c689517386e51a5ceMichal Zidek * having defined Content-Length or Transfer-Encoding: chunked, or
21d485184df986e1a123f70c689517386e51a5ceMichal Zidek * the request version being HTTP/1.1 and thus capable of being set
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * as chunked [we know the (r->chunked = 1) side-effect is ugly];
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * and the server configuration enables keep-alive;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * and the server configuration has a reasonable inter-request timeout;
e0404de84c31d2387bb244d018a5cac8d01f8b19Simo Sorce * and there is no maximum # requests or the max hasn't been reached;
e0404de84c31d2387bb244d018a5cac8d01f8b19Simo Sorce * and the response status does not require a close;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * and the response generator has not already indicated close;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * and the client did not request non-persistence (Connection: close);
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * and we haven't been configured to ignore the buggy twit
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * or they're a buggy twit coming through a HTTP/1.1 proxy
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * and the client is requesting an HTTP/1.0-style keep-alive
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * or the client claims to be HTTP/1.1 compliant (perhaps a proxy);
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * THEN we can be persistent, which requires more headers be output.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher *
e0404de84c31d2387bb244d018a5cac8d01f8b19Simo Sorce * Note that the condition evaluation order is extremely important.
e0404de84c31d2387bb244d018a5cac8d01f8b19Simo Sorce */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if ((r->connection->keepalive != -1)
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher && ((r->status == HTTP_NOT_MODIFIED)
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher || (r->status == HTTP_NO_CONTENT)
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher || r->header_only
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher || apr_table_get(r->headers_out, "Content-Length")
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher || ap_find_last_token(r->pool,
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher apr_table_get(r->headers_out,
e0404de84c31d2387bb244d018a5cac8d01f8b19Simo Sorce "Transfer-Encoding"),
21d485184df986e1a123f70c689517386e51a5ceMichal Zidek "chunked")
21d485184df986e1a123f70c689517386e51a5ceMichal Zidek || ((r->proto_num >= HTTP_VERSION(1,1))
21d485184df986e1a123f70c689517386e51a5ceMichal Zidek && (r->chunked = 1))) /* THIS CODE IS CORRECT, see comment above. */
21d485184df986e1a123f70c689517386e51a5ceMichal Zidek && r->server->keep_alive
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher && (r->server->keep_alive_timeout > 0)
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher && ((r->server->keep_alive_max == 0)
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher || (r->server->keep_alive_max > r->connection->keepalives))
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher && !ap_status_drops_connection(r->status)
e0404de84c31d2387bb244d018a5cac8d01f8b19Simo Sorce && !wimpy
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher && !ap_find_token(r->pool, conn, "close")
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher && (!apr_table_get(r->subprocess_env, "nokeepalive")
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher || apr_table_get(r->headers_in, "Via"))
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher && ((ka_sent = ap_find_token(r->pool, conn, "keep-alive"))
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher || (r->proto_num >= HTTP_VERSION(1,1)))) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher int left = r->server->keep_alive_max - r->connection->keepalives;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher r->connection->keepalive = 1;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher r->connection->keepalives++;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher /* If they sent a Keep-Alive token, send one back */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (ka_sent) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (r->server->keep_alive_max)
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher apr_table_setn(r->headers_out, "Keep-Alive",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher apr_psprintf(r->pool, "timeout=%d, max=%d",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher r->server->keep_alive_timeout, left));
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher else
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher apr_table_setn(r->headers_out, "Keep-Alive",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher apr_psprintf(r->pool, "timeout=%d",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher r->server->keep_alive_timeout));
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher apr_table_mergen(r->headers_out, "Connection", "Keep-Alive");
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher return 1;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher /* Otherwise, we need to indicate that we will be closing this
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * connection immediately after the current response.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher *
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * We only really need to send "close" to HTTP/1.1 clients, but we
3d8a87081a6cd197acbd355b5a39111669ec2aa6Jakub Hrozek * always send it anyway, because a broken proxy may identify itself
3d8a87081a6cd197acbd355b5a39111669ec2aa6Jakub Hrozek * as HTTP/1.0, but pass our request along with our HTTP/1.1 tag
3d8a87081a6cd197acbd355b5a39111669ec2aa6Jakub Hrozek * to a HTTP/1.1 client. Better safe than sorry.
3d8a87081a6cd197acbd355b5a39111669ec2aa6Jakub Hrozek */
3d8a87081a6cd197acbd355b5a39111669ec2aa6Jakub Hrozek if (!wimpy)
3d8a87081a6cd197acbd355b5a39111669ec2aa6Jakub Hrozek apr_table_mergen(r->headers_out, "Connection", "close");
3d8a87081a6cd197acbd355b5a39111669ec2aa6Jakub Hrozek
3d8a87081a6cd197acbd355b5a39111669ec2aa6Jakub Hrozek r->connection->keepalive = 0;
3d8a87081a6cd197acbd355b5a39111669ec2aa6Jakub Hrozek
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher return 0;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher}
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen GallagherAP_DECLARE(int) ap_meets_conditions(request_rec *r)
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher{
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher const char *etag = apr_table_get(r->headers_out, "ETag");
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher const char *if_match, *if_modified_since, *if_unmodified, *if_nonematch;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher apr_time_t mtime;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher /* Check for conditional requests --- note that we only want to do
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * this if we are successful so far and we are not processing a
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * subrequest or an ErrorDocument.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher *
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * The order of the checks is important, since ETag checks are supposed
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * to be more accurate than checks relative to the modification time.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * However, not all documents are guaranteed to *have* ETags, and some
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * might have Last-Modified values w/o ETags, so this gets a little
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * complicated.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (!ap_is_HTTP_SUCCESS(r->status) || r->no_local_copy) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher return OK;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher /* XXX: we should define a "time unset" constant */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher mtime = (r->mtime != 0) ? r->mtime : apr_time_now();
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher /* If an If-Match request-header field was given
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * AND the field value is not "*" (meaning match anything)
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * AND if our strong ETag does not match any entity tag in that field,
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * respond with a status of 412 (Precondition Failed).
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if ((if_match = apr_table_get(r->headers_in, "If-Match")) != NULL) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (if_match[0] != '*'
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher && (etag == NULL || etag[0] == 'W'
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher || !ap_find_list_item(r->pool, if_match, etag))) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher return HTTP_PRECONDITION_FAILED;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher else {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher /* Else if a valid If-Unmodified-Since request-header field was given
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * AND the requested resource has been modified since the time
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * specified in this field, then the server MUST
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * respond with a status of 412 (Precondition Failed).
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if_unmodified = apr_table_get(r->headers_in, "If-Unmodified-Since");
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (if_unmodified != NULL) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher apr_time_t ius = ap_parseHTTPdate(if_unmodified);
4a6a5421113ab662a665c62ed6a24b61a5a36950Jakub Hrozek
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if ((ius != BAD_DATE) && (mtime > ius)) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher return HTTP_PRECONDITION_FAILED;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher /* If an If-None-Match request-header field was given
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * AND the field value is "*" (meaning match anything)
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * OR our ETag matches any of the entity tags in that field, fail.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher *
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * If the request method was GET or HEAD, failure means the server
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * SHOULD respond with a 304 (Not Modified) response.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * For all other request methods, failure means the server MUST
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * respond with a status of 412 (Precondition Failed).
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher *
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * GET or HEAD allow weak etag comparison, all other methods require
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * strong comparison. We can only use weak if it's not a range request.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if_nonematch = apr_table_get(r->headers_in, "If-None-Match");
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (if_nonematch != NULL) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (r->method_number == M_GET) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (if_nonematch[0] == '*') {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher return HTTP_NOT_MODIFIED;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (etag != NULL) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (apr_table_get(r->headers_in, "Range")) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (etag[0] != 'W'
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher && ap_find_list_item(r->pool, if_nonematch, etag)) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher return HTTP_NOT_MODIFIED;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher else if (ap_strstr_c(if_nonematch, etag)) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher return HTTP_NOT_MODIFIED;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher else if (if_nonematch[0] == '*'
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher || (etag != NULL
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher && ap_find_list_item(r->pool, if_nonematch, etag))) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher return HTTP_PRECONDITION_FAILED;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher /* Else if a valid If-Modified-Since request-header field was given
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * AND it is a GET or HEAD request
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * AND the requested resource has not been modified since the time
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * specified in this field, then the server MUST
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * respond with a status of 304 (Not Modified).
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * A date later than the server's current request time is invalid.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher else if ((r->method_number == M_GET)
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher && ((if_modified_since =
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher apr_table_get(r->headers_in,
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "If-Modified-Since")) != NULL)) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher apr_time_t ims = ap_parseHTTPdate(if_modified_since);
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if ((ims >= mtime) && (ims <= r->request_time)) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher return HTTP_NOT_MODIFIED;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher return OK;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher}
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher/* Get the method number associated with the given string, assumed to
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * contain an HTTP method. Returns M_INVALID if not recognized.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher *
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * This is the first step toward placing method names in a configurable
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * list. Hopefully it (and other routines) can eventually be moved to
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * something like a mod_http_methods.c, complete with config stuff.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen GallagherAP_DECLARE(int) ap_method_number_of(const char *method)
044868b388b4e47499f12a9105310b247bbe1ce2Simo Sorce{
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher switch (*method) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher case 'H':
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (strcmp(method, "HEAD") == 0)
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher return M_GET; /* see header_only in request_rec */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher break;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher case 'G':
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (strcmp(method, "GET") == 0)
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher return M_GET;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher break;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher case 'P':
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (strcmp(method, "POST") == 0)
770896b194b7b66b09c2a30545b4d091fd86b1f4Simo Sorce return M_POST;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (strcmp(method, "PUT") == 0)
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher return M_PUT;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (strcmp(method, "PATCH") == 0)
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher return M_PATCH;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (strcmp(method, "PROPFIND") == 0)
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher return M_PROPFIND;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (strcmp(method, "PROPPATCH") == 0)
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher return M_PROPPATCH;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher break;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher case 'D':
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (strcmp(method, "DELETE") == 0)
770896b194b7b66b09c2a30545b4d091fd86b1f4Simo Sorce return M_DELETE;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher break;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher case 'C':
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (strcmp(method, "CONNECT") == 0)
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher return M_CONNECT;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (strcmp(method, "COPY") == 0)
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher return M_COPY;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher break;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher case 'M':
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (strcmp(method, "MKCOL") == 0)
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher return M_MKCOL;
6fb75e297bf7fc83e3db1f5ae8560624656ef319Jan Zeleny if (strcmp(method, "MOVE") == 0)
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher return M_MOVE;
770896b194b7b66b09c2a30545b4d091fd86b1f4Simo Sorce break;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher case 'O':
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (strcmp(method, "OPTIONS") == 0)
6fb75e297bf7fc83e3db1f5ae8560624656ef319Jan Zeleny return M_OPTIONS;
6fb75e297bf7fc83e3db1f5ae8560624656ef319Jan Zeleny break;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher case 'T':
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (strcmp(method, "TRACE") == 0)
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher return M_TRACE;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher break;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher case 'L':
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (strcmp(method, "LOCK") == 0)
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher return M_LOCK;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher break;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher case 'U':
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (strcmp(method, "UNLOCK") == 0)
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher return M_UNLOCK;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher break;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher return M_INVALID;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher}
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher/*
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * Turn a known method number into a name. Doesn't work for
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * extension methods, obviously.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen GallagherAP_DECLARE(const char *) ap_method_name_of(int methnum)
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher{
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher static const char *AP_HTTP_METHODS[METHODS] = { NULL };
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher /*
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * This is ugly, but the previous incantation made Windows C
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * varf. I'm not even sure it was ANSI C. However, ugly as it
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * is, this works, and we only have to do it once.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (AP_HTTP_METHODS[0] == NULL) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher AP_HTTP_METHODS[M_GET] = "GET";
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher AP_HTTP_METHODS[M_PUT] = "PUT";
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher AP_HTTP_METHODS[M_POST] = "POST";
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher AP_HTTP_METHODS[M_DELETE] = "DELETE";
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher AP_HTTP_METHODS[M_CONNECT] = "CONNECT";
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher AP_HTTP_METHODS[M_OPTIONS] = "OPTIONS";
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher AP_HTTP_METHODS[M_TRACE] = "TRACE";
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher AP_HTTP_METHODS[M_PATCH] = "PATCH";
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher AP_HTTP_METHODS[M_PROPFIND] = "PROPFIND";
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher AP_HTTP_METHODS[M_PROPPATCH] = "PROPPATCH";
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher AP_HTTP_METHODS[M_MKCOL] = "MKCOL";
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher AP_HTTP_METHODS[M_COPY] = "COPY";
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher AP_HTTP_METHODS[M_MOVE] = "MOVE";
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher AP_HTTP_METHODS[M_LOCK] = "LOCK";
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher AP_HTTP_METHODS[M_UNLOCK] = "UNLOCK";
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher AP_HTTP_METHODS[M_INVALID] = NULL;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher /*
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * Since we're using symbolic names, make sure we only do
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * this once by forcing a value into the first slot IFF it's
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * still NULL.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (AP_HTTP_METHODS[0] == NULL) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher AP_HTTP_METHODS[0] = "INVALID";
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
2ce00e0d3896bb42db169d1e79553a81ca837a22Simo Sorce
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if ((methnum == M_INVALID) || (methnum >= METHODS)) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher return NULL;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher return AP_HTTP_METHODS[methnum];
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher}
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagherstruct dechunk_ctx {
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher apr_size_t chunk_size;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher apr_size_t bytes_delivered;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher enum {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher WANT_HDR /* must have value zero */,
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher WANT_BODY,
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher WANT_TRL
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher } state;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher};
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagherstatic long get_chunk_size(char *);
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagherapr_status_t ap_dechunk_filter(ap_filter_t *f, apr_bucket_brigade *bb,
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher ap_input_mode_t mode, apr_size_t *readbytes)
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher{
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher apr_status_t rv;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher struct dechunk_ctx *ctx = f->ctx;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher apr_bucket *b;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher const char *buf;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher apr_size_t len;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (!ctx) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(struct dechunk_ctx));
94a66f84bd3c28fcabffeb84c682dccf89d89c2bSumit Bose }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher do {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (ctx->chunk_size == ctx->bytes_delivered) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher /* Time to read another chunk header or trailer... ap_http_filter() is
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * the next filter in line and it knows how to return a brigade with
1ea2e8bd370e0dc2f2c3fa09232cf67082ef748dStephen Gallagher * one line.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher char line[30];
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if ((rv = ap_getline(line, sizeof(line), f->r,
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher 0 /* readline */)) < 0) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher return rv;
94a66f84bd3c28fcabffeb84c682dccf89d89c2bSumit Bose }
94a66f84bd3c28fcabffeb84c682dccf89d89c2bSumit Bose switch(ctx->state) {
94a66f84bd3c28fcabffeb84c682dccf89d89c2bSumit Bose case WANT_HDR:
94a66f84bd3c28fcabffeb84c682dccf89d89c2bSumit Bose ctx->chunk_size = get_chunk_size(line);
94a66f84bd3c28fcabffeb84c682dccf89d89c2bSumit Bose ctx->bytes_delivered = 0;
94a66f84bd3c28fcabffeb84c682dccf89d89c2bSumit Bose if (ctx->chunk_size == 0) {
94a66f84bd3c28fcabffeb84c682dccf89d89c2bSumit Bose ctx->state = WANT_TRL;
94a66f84bd3c28fcabffeb84c682dccf89d89c2bSumit Bose }
94a66f84bd3c28fcabffeb84c682dccf89d89c2bSumit Bose else {
2ce00e0d3896bb42db169d1e79553a81ca837a22Simo Sorce ctx->state = WANT_BODY;
94a66f84bd3c28fcabffeb84c682dccf89d89c2bSumit Bose }
94a66f84bd3c28fcabffeb84c682dccf89d89c2bSumit Bose break;
b860f8b6b6b03982c80268e9f6fd35f6455b6b37Simo Sorce case WANT_TRL:
94a66f84bd3c28fcabffeb84c682dccf89d89c2bSumit Bose /* XXX sanity check end chunk here */
94a66f84bd3c28fcabffeb84c682dccf89d89c2bSumit Bose if (strlen(line)) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher /* bad trailer */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (ctx->chunk_size == 0) { /* we just finished the last chunk? */
b860f8b6b6b03982c80268e9f6fd35f6455b6b37Simo Sorce /* ### woah... ap_http_filter() is doing this, too */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher /* append eos bucket and get out */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher b = apr_bucket_eos_create();
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher APR_BRIGADE_INSERT_TAIL(bb, b);
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher return APR_SUCCESS;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
9b72b00ebcfd6225a4e139619c8e18d44a448f87Stephen Gallagher ctx->state = WANT_HDR;
9b72b00ebcfd6225a4e139619c8e18d44a448f87Stephen Gallagher break;
9b72b00ebcfd6225a4e139619c8e18d44a448f87Stephen Gallagher default:
9b72b00ebcfd6225a4e139619c8e18d44a448f87Stephen Gallagher ap_assert(ctx->state == WANT_HDR || ctx->state == WANT_TRL);
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
9b72b00ebcfd6225a4e139619c8e18d44a448f87Stephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher } while (ctx->state != WANT_BODY);
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (ctx->state == WANT_BODY) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher /* Tell ap_http_filter() how many bytes to deliver. */
b860f8b6b6b03982c80268e9f6fd35f6455b6b37Simo Sorce apr_size_t chunk_bytes = ctx->chunk_size - ctx->bytes_delivered;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if ((rv = ap_get_brigade(f->next, bb, mode,
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher &chunk_bytes)) != APR_SUCCESS) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher return rv;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher /* Walk through the body, accounting for bytes, and removing an eos
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * bucket if ap_http_filter() delivered the entire chunk.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher *
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * ### this shouldn't be necessary. 1) ap_http_filter shouldn't be
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * ### adding EOS buckets. 2) it shouldn't return more bytes than
b860f8b6b6b03982c80268e9f6fd35f6455b6b37Simo Sorce * ### we requested, therefore the total len can be found with a
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * ### simple call to apr_brigade_length(). no further munging
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * ### would be needed.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher b = APR_BRIGADE_FIRST(bb);
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher while (b != APR_BRIGADE_SENTINEL(bb) && !APR_BUCKET_IS_EOS(b)) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher apr_bucket_read(b, &buf, &len, mode);
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher AP_DEBUG_ASSERT(len <= ctx->chunk_size - ctx->bytes_delivered);
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher ctx->bytes_delivered += len;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher b = APR_BUCKET_NEXT(b);
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (ctx->bytes_delivered == ctx->chunk_size) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher AP_DEBUG_ASSERT(APR_BUCKET_IS_EOS(b));
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher apr_bucket_delete(b);
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher ctx->state = WANT_TRL;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
2ce00e0d3896bb42db169d1e79553a81ca837a22Simo Sorce }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher return APR_SUCCESS;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher}
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallaghertypedef struct http_filter_ctx {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher apr_bucket_brigade *b;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher} http_ctx_t;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagherapr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b, ap_input_mode_t mode, apr_size_t *readbytes)
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher{
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher apr_bucket *e;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher char *buff;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher apr_size_t len;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher char *pos;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher http_ctx_t *ctx = f->ctx;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher apr_status_t rv;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (!ctx) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher f->ctx = ctx = apr_pcalloc(f->c->pool, sizeof(*ctx));
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher ctx->b = apr_brigade_create(f->c->pool);
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (mode == AP_MODE_PEEK) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher apr_bucket *e;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher const char *str;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher apr_size_t length;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher /* The purpose of this loop is to ignore any CRLF (or LF) at the end
2ce00e0d3896bb42db169d1e79553a81ca837a22Simo Sorce * of a request. Many browsers send extra lines at the end of POST
2ce00e0d3896bb42db169d1e79553a81ca837a22Simo Sorce * requests. We use the PEEK method to determine if there is more
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * data on the socket, so that we know if we should delay sending the
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * end of one request until we have served the second request in a
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * pipelined situation. We don't want to actually delay sending a
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * response if the server finds a CRLF (or LF), becuause that doesn't
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * mean that there is another request, just a blank line.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher while (1) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (APR_BRIGADE_EMPTY(ctx->b)) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher e = NULL;
3d8a87081a6cd197acbd355b5a39111669ec2aa6Jakub Hrozek }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher else {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher e = APR_BRIGADE_FIRST(ctx->b);
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (!e || apr_bucket_read(e, &str, &length, APR_NONBLOCK_READ) != APR_SUCCESS) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher return APR_EOF;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher else {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher const char *c = str;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher while (c - str < length) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (*c == APR_ASCII_LF)
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher c++;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher else if (*c == APR_ASCII_CR && *(c + 1) == APR_ASCII_LF)
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher c += 2;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher else return APR_SUCCESS;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher apr_bucket_delete(e);
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (APR_BRIGADE_EMPTY(ctx->b)) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if ((rv = ap_get_brigade(f->next, ctx->b, mode, readbytes)) != APR_SUCCESS) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher return rv;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher /* readbytes == 0 is "read a single line". otherwise, read a block. */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (*readbytes) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher /* ### the code below, which moves bytes from one brigade to the
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher ### other is probably bogus. presuming the next filter down was
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher ### working properly, it should not have returned more than
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher ### READBYTES bytes, and we wouldn't have to do any work. further,
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher ### we could probably just use brigade_partition() in here.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher while (!APR_BRIGADE_EMPTY(ctx->b)) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher const char *ignore;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher e = APR_BRIGADE_FIRST(ctx->b);
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if ((rv = apr_bucket_read(e, &ignore, &len, mode)) != APR_SUCCESS) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher /* probably APR_IS_EAGAIN(rv); socket state isn't correct;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * remove log once we get this squared away */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher ap_log_error(APLOG_MARK, APLOG_ERR, rv, f->c->base_server,
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "apr_bucket_read");
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher return rv;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (len) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher /* note: this can sometimes insert empty buckets into the
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * brigade, or the data might come in a few characters at
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * a time - don't assume that one call to apr_bucket_read()
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher * will return the full string.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (*readbytes < len) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher apr_bucket_split(e, *readbytes);
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher *readbytes = 0;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher else {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher *readbytes -= len;
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher APR_BUCKET_REMOVE(e);
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher APR_BRIGADE_INSERT_TAIL(b, e);
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher break; /* once we've gotten some data, deliver it to caller */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher apr_bucket_delete(e);
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher /* ### this is a hack. it is saying, "if we have read everything
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher ### that was requested, then we are at the end of the request."
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher ### it presumes that the next filter up will *only* call us
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher ### with readbytes set to the Content-Length of the request.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher ### that may not always be true, and this code is *definitely*
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher ### too presumptive of the caller's intent. the point is: this
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher ### filter is not the guy that is parsing the headers or the
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher ### chunks to determine where the end of the request is, so we
de526c8425886ca3bed8f07a0f092ba5ac325654Simo Sorce ### shouldn't be monkeying with EOS buckets.
de526c8425886ca3bed8f07a0f092ba5ac325654Simo Sorce */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (*readbytes == 0) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher apr_bucket *eos = apr_bucket_eos_create();
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher APR_BRIGADE_INSERT_TAIL(b, eos);
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher return APR_SUCCESS;
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher }
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher /* we are reading a single line, e.g. the HTTP headers */
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher while (!APR_BRIGADE_EMPTY(ctx->b)) {
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher e = APR_BRIGADE_FIRST(ctx->b);
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher if ((rv = apr_bucket_read(e, (const char **)&buff, &len, mode)) != APR_SUCCESS) {
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher return rv;
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher }
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher
3d8a87081a6cd197acbd355b5a39111669ec2aa6Jakub Hrozek pos = memchr(buff, APR_ASCII_LF, len);
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher if (pos != NULL) {
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher apr_bucket_split(e, pos - buff + 1);
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher APR_BUCKET_REMOVE(e);
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher APR_BRIGADE_INSERT_TAIL(b, e);
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher return APR_SUCCESS;
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher APR_BUCKET_REMOVE(e);
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher APR_BRIGADE_INSERT_TAIL(b, e);
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher return APR_SUCCESS;
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher}
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher/* New Apache routine to map status codes into array indicies
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher * e.g. 100 -> 0, 101 -> 1, 200 -> 2 ...
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher * The number of status lines must equal the value of RESPONSE_CODES (httpd.h)
3d8a87081a6cd197acbd355b5a39111669ec2aa6Jakub Hrozek * and must be listed in order.
c3d09c0095a45de1973f320ce2045ac74d4e4f83Jakub Hrozek */
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher#ifdef UTS21
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher/* The second const triggers an assembler bug on UTS 2.1.
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher * Another workaround is to move some code out of this file into another,
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * but this is easier. Dave Dykstra, 3/31/99
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagherstatic const char * status_lines[RESPONSE_CODES] =
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher#else
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagherstatic const char * const status_lines[RESPONSE_CODES] =
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher#endif
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher{
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher "100 Continue",
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher "101 Switching Protocols",
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher "102 Processing",
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher#define LEVEL_200 3
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher "200 OK",
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher "201 Created",
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher "202 Accepted",
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher "203 Non-Authoritative Information",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "204 No Content",
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher "205 Reset Content",
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher "206 Partial Content",
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher "207 Multi-Status",
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher#define LEVEL_300 11
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "300 Multiple Choices",
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher "301 Moved Permanently",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "302 Found",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "303 See Other",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "304 Not Modified",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "305 Use Proxy",
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher "306 unused",
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher "307 Temporary Redirect",
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher#define LEVEL_400 19
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "400 Bad Request",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "401 Authorization Required",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "402 Payment Required",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "403 Forbidden",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "404 Not Found",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "405 Method Not Allowed",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "406 Not Acceptable",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "407 Proxy Authentication Required",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "408 Request Time-out",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "409 Conflict",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "410 Gone",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "411 Length Required",
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher "412 Precondition Failed",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "413 Request Entity Too Large",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "414 Request-URI Too Large",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "415 Unsupported Media Type",
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher "416 Requested Range Not Satisfiable",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "417 Expectation Failed",
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher "418 unused",
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher "419 unused",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "420 unused",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "421 unused",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "422 Unprocessable Entity",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "423 Locked",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "424 Failed Dependency",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher#define LEVEL_500 44
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "500 Internal Server Error",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "501 Method Not Implemented",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "502 Bad Gateway",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "503 Service Temporarily Unavailable",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "504 Gateway Time-out",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "505 HTTP Version Not Supported",
9b72b00ebcfd6225a4e139619c8e18d44a448f87Stephen Gallagher "506 Variant Also Negotiates",
9b72b00ebcfd6225a4e139619c8e18d44a448f87Stephen Gallagher "507 Insufficient Storage",
9b72b00ebcfd6225a4e139619c8e18d44a448f87Stephen Gallagher "508 unused",
9b72b00ebcfd6225a4e139619c8e18d44a448f87Stephen Gallagher "509 unused",
3d8a87081a6cd197acbd355b5a39111669ec2aa6Jakub Hrozek "510 Not Extended"
9b72b00ebcfd6225a4e139619c8e18d44a448f87Stephen Gallagher};
9b72b00ebcfd6225a4e139619c8e18d44a448f87Stephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher/* The index is found by its offset from the x00 code of each level.
de526c8425886ca3bed8f07a0f092ba5ac325654Simo Sorce * Although this is fast, it will need to be replaced if some nutcase
de526c8425886ca3bed8f07a0f092ba5ac325654Simo Sorce * decides to define a high-numbered code before the lower numbers.
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher * If that sad event occurs, replace the code below with a linear search
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * from status_lines[shortcut[i]] to status_lines[shortcut[i+1]-1];
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen GallagherAP_DECLARE(int) ap_index_of_response(int status)
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher{
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher static int shortcut[6] = {0, LEVEL_200, LEVEL_300, LEVEL_400,
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher LEVEL_500, RESPONSE_CODES};
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher int i, pos;
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher if (status < 100) /* Below 100 is illegal for HTTP status */
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher return LEVEL_500;
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher for (i = 0; i < 5; i++) {
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher status -= 100;
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher if (status < 100) {
3d8a87081a6cd197acbd355b5a39111669ec2aa6Jakub Hrozek pos = (status + shortcut[i]);
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher if (pos < shortcut[i + 1]) {
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher return pos;
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher }
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher else {
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher return LEVEL_500; /* status unknown (falls in gap) */
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher return LEVEL_500; /* 600 or above is also illegal */
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher}
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen GallagherAP_DECLARE(const char *) ap_get_status_line(int status)
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher{
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher return status_lines[ap_index_of_response(status)];
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher}
3d8a87081a6cd197acbd355b5a39111669ec2aa6Jakub Hrozek
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallaghertypedef struct header_struct {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher apr_pool_t *pool;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher apr_bucket_brigade *bb;
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher} header_struct;
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher/* Send a single HTTP header field to the client. Note that this function
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * is used in calls to table_do(), so their interfaces are co-dependent.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * In other words, don't change this one without checking table_do in alloc.c.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * It returns true unless there was a write error of some kind.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher */
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagherstatic int form_header_field(header_struct *h,
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher const char *fieldname, const char *fieldval)
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher{
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher char *headfield;
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher headfield = apr_pstrcat(h->pool, fieldname, ": ", fieldval, CRLF, NULL);
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher ap_xlate_proto_to_ascii(headfield, strlen(headfield));
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher apr_brigade_puts(h->bb, NULL, NULL, headfield);
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher return 1;
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher}
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher/*
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher * Determine the protocol to use for the response. Potentially downgrade
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher * to HTTP/1.0 in some situations and/or turn off keepalives.
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher *
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * also prepare r->status_line.
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagherstatic void basic_http_header_check(request_rec *r,
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher const char **protocol)
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher{
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (r->assbackwards) {
9b72b00ebcfd6225a4e139619c8e18d44a448f87Stephen Gallagher /* no such thing as a response protocol */
9b72b00ebcfd6225a4e139619c8e18d44a448f87Stephen Gallagher return;
9b72b00ebcfd6225a4e139619c8e18d44a448f87Stephen Gallagher }
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (!r->status_line)
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher r->status_line = status_lines[ap_index_of_response(r->status)];
/* kluge around broken browsers when indicated by force-response-1.0
*/
if (r->proto_num == HTTP_VERSION(1,0)
&& apr_table_get(r->subprocess_env, "force-response-1.0")) {
*protocol = "HTTP/1.0";
r->connection->keepalive = -1;
}
else {
*protocol = AP_SERVER_PROTOCOL;
}
}
/* fill "bb" with a barebones/initial HTTP response header */
static void basic_http_header(request_rec *r, apr_bucket_brigade *bb,
const char *protocol)
{
char *date = NULL;
char *tmp;
header_struct h;
if (r->assbackwards) {
/* there are no headers to send */
return;
}
/* Output the HTTP/1.x Status-Line and the Date and Server fields */
tmp = apr_pstrcat(r->pool, protocol, " ", r->status_line, CRLF, NULL);
ap_xlate_proto_to_ascii(tmp, strlen(tmp));
apr_brigade_puts(bb, NULL, NULL, tmp);
date = apr_palloc(r->pool, APR_RFC822_DATE_LEN);
apr_rfc822_date(date, r->request_time);
h.pool = r->pool;
h.bb = bb;
form_header_field(&h, "Date", date);
form_header_field(&h, "Server", ap_get_server_version());
apr_table_unset(r->headers_out, "Date"); /* Avoid bogosity */
apr_table_unset(r->headers_out, "Server");
}
AP_DECLARE(void) ap_basic_http_header(request_rec *r, apr_bucket_brigade *bb)
{
const char *protocol;
basic_http_header_check(r, &protocol);
basic_http_header(r, bb, protocol);
}
/* Navigator versions 2.x, 3.x and 4.0 betas up to and including 4.0b2
* have a header parsing bug. If the terminating \r\n occur starting
* at offset 256, 257 or 258 of output then it will not properly parse
* the headers. Curiously it doesn't exhibit this problem at 512, 513.
* We are guessing that this is because their initial read of a new request
* uses a 256 byte buffer, and subsequent reads use a larger buffer.
* So the problem might exist at different offsets as well.
*
* This should also work on keepalive connections assuming they use the
* same small buffer for the first read of each new request.
*
* At any rate, we check the bytes written so far and, if we are about to
* tickle the bug, we instead insert a bogus padding header. Since the bug
* manifests as a broken image in Navigator, users blame the server. :(
* It is more expensive to check the User-Agent than it is to just add the
* bytes, so we haven't used the BrowserMatch feature here.
*/
static void terminate_header(apr_bucket_brigade *bb)
{
char tmp[] = "X-Pad: avoid browser bug" CRLF;
char crlf[] = CRLF;
apr_ssize_t len;
(void) apr_brigade_length(bb, 1, &len);
if (len >= 255 && len <= 257) {
ap_xlate_proto_to_ascii(tmp, strlen(tmp));
apr_brigade_puts(bb, NULL, NULL, tmp);
}
ap_xlate_proto_to_ascii(crlf, strlen(crlf));
apr_brigade_puts(bb, NULL, NULL, crlf);
}
/* Build the Allow field-value from the request handler method mask.
* Note that we always allow TRACE, since it is handled below.
*/
static char *make_allow(request_rec *r)
{
char *list;
int mask;
mask = r->allowed_methods->method_mask;
list = apr_pstrcat(r->pool,
(mask & (1 << M_GET)) ? ", GET, HEAD" : "",
(mask & (1 << M_POST)) ? ", POST" : "",
(mask & (1 << M_PUT)) ? ", PUT" : "",
(mask & (1 << M_DELETE)) ? ", DELETE" : "",
(mask & (1 << M_CONNECT)) ? ", CONNECT" : "",
(mask & (1 << M_OPTIONS)) ? ", OPTIONS" : "",
(mask & (1 << M_PATCH)) ? ", PATCH" : "",
(mask & (1 << M_PROPFIND)) ? ", PROPFIND" : "",
(mask & (1 << M_PROPPATCH)) ? ", PROPPATCH" : "",
(mask & (1 << M_MKCOL)) ? ", MKCOL" : "",
(mask & (1 << M_COPY)) ? ", COPY" : "",
(mask & (1 << M_MOVE)) ? ", MOVE" : "",
(mask & (1 << M_LOCK)) ? ", LOCK" : "",
(mask & (1 << M_UNLOCK)) ? ", UNLOCK" : "",
", TRACE",
NULL);
if ((mask & (1 << M_INVALID))
&& (r->allowed_methods->method_list != NULL)
&& (r->allowed_methods->method_list->nelts != 0)) {
int i;
char **xmethod = (char **) r->allowed_methods->method_list->elts;
/*
* Append all of the elements of r->allowed_methods->method_list
*/
for (i = 0; i < r->allowed_methods->method_list->nelts; ++i) {
list = apr_pstrcat(r->pool, list, ", ", xmethod[i], NULL);
}
}
/*
* Space past the leading ", ". Wastes two bytes, but that's better
* than futzing around to find the actual length.
*/
return list + 2;
}
AP_DECLARE(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";
/* Now we recreate the request, and echo it back */
ap_rvputs(r, r->the_request, CRLF, NULL);
apr_table_do((int (*) (void *, const char *, const char *))
form_header_field, (void *) r, r->headers_in, NULL);
ap_rputs(CRLF, r);
return OK;
}
int ap_send_http_options(request_rec *r)
{
if (r->assbackwards)
return DECLINED;
apr_table_setn(r->headers_out, "Allow", make_allow(r));
/* the request finalization will send an EOS, which will flush all
the headers out (including the Allow header) */
return OK;
}
/* This routine is called by apr_table_do and merges all instances of
* the passed field values into a single array that will be further
* processed by some later routine. Originally intended to help split
* and recombine multiple Vary fields, though it is generic to any field
* consisting of comma/space-separated tokens.
*/
static int uniq_field_values(void *d, const char *key, const char *val)
{
apr_array_header_t *values;
char *start;
char *e;
char **strpp;
int i;
values = (apr_array_header_t *)d;
e = apr_pstrdup(values->cont, val);
do {
/* Find a non-empty fieldname */
while (*e == ',' || apr_isspace(*e)) {
++e;
}
if (*e == '\0') {
break;
}
start = e;
while (*e != '\0' && *e != ',' && !apr_isspace(*e)) {
++e;
}
if (*e != '\0') {
*e++ = '\0';
}
/* Now add it to values if it isn't already represented.
* Could be replaced by a ap_array_strcasecmp() if we had one.
*/
for (i = 0, strpp = (char **) values->elts; i < values->nelts;
++i, ++strpp) {
if (*strpp && strcasecmp(*strpp, start) == 0) {
break;
}
}
if (i == values->nelts) { /* if not found */
*(char **)apr_array_push(values) = start;
}
} while (*e != '\0');
return 1;
}
/*
* Since some clients choke violently on multiple Vary fields, or
* Vary fields with duplicate tokens, combine any multiples and remove
* any duplicates.
*/
static void fixup_vary(request_rec *r)
{
apr_array_header_t *varies;
varies = apr_array_make(r->pool, 5, sizeof(char *));
/* Extract all Vary fields from the headers_out, separate each into
* its comma-separated fieldname values, and then add them to varies
* if not already present in the array.
*/
apr_table_do((int (*)(void *, const char *, const char *))uniq_field_values,
(void *) varies, r->headers_out, "Vary", NULL);
/* If we found any, replace old Vary fields with unique-ified value */
if (varies->nelts > 0) {
apr_table_setn(r->headers_out, "Vary",
apr_array_pstrcat(r->pool, varies, ','));
}
}
AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(
ap_filter_t *f,
apr_bucket_brigade *b)
{
int i;
char *date = NULL;
request_rec *r = f->r;
const char *clheader;
const char *protocol;
apr_bucket *e;
apr_bucket_brigade *b2;
header_struct h;
AP_DEBUG_ASSERT(!r->main);
APR_BRIGADE_FOREACH(e, b) {
if (e->type == &ap_bucket_type_error) {
ap_bucket_error *eb = e->data;
ap_die(eb->status, r);
return AP_FILTER_ERROR;
}
}
if (r->assbackwards) {
r->bytes_sent = 0;
r->sent_bodyct = 1;
ap_remove_output_filter(f);
return ap_pass_brigade(f->next, b);
}
/*
* Now that we are ready to send a response, we need to combine the two
* header field tables into a single table. If we don't do this, our
* later attempts to set or unset a given fieldname might be bypassed.
*/
if (!apr_is_empty_table(r->err_headers_out))
r->headers_out = apr_table_overlay(r->pool, r->err_headers_out,
r->headers_out);
/*
* Remove the 'Vary' header field if the client can't handle it.
* Since this will have nasty effects on HTTP/1.1 caches, force
* the response into HTTP/1.0 mode.
*
* Note: the force-response-1.0 should come before the call to
* basic_http_header_check()
*/
if (apr_table_get(r->subprocess_env, "force-no-vary") != NULL) {
apr_table_unset(r->headers_out, "Vary");
r->proto_num = HTTP_VERSION(1,0);
apr_table_set(r->subprocess_env, "force-response-1.0", "1");
}
else {
fixup_vary(r);
}
/* determine the protocol and whether we should use keepalives. */
basic_http_header_check(r, &protocol);
ap_set_keepalive(r);
if (r->chunked) {
apr_table_mergen(r->headers_out, "Transfer-Encoding", "chunked");
apr_table_unset(r->headers_out, "Content-Length");
}
apr_table_setn(r->headers_out, "Content-Type", ap_make_content_type(r,
r->content_type));
if (r->content_encoding) {
apr_table_setn(r->headers_out, "Content-Encoding",
r->content_encoding);
}
if (r->content_languages && r->content_languages->nelts) {
for (i = 0; i < r->content_languages->nelts; ++i) {
apr_table_mergen(r->headers_out, "Content-Language",
((char **) (r->content_languages->elts))[i]);
}
}
else if (r->content_language) {
apr_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 && !apr_table_get(r->headers_out, "Expires")) {
date = apr_palloc(r->pool, APR_RFC822_DATE_LEN);
apr_rfc822_date(date, r->request_time);
apr_table_addn(r->headers_out, "Expires", date);
}
/* This is a hack, but I can't find anyway around it. The idea is that
* we don't want to send out 0 Content-Lengths if it is a head request.
* This happens when modules try to outsmart the server, and return
* if they see a HEAD request. Apache 1.3 handlers were supposed to
* just return in that situation, and the core handled the HEAD. In
* 2.0, if a handler returns, then the core sends an EOS bucket down
* the filter stack, and the content-length filter computes a C-L of
* zero and that gets put in the headers, and we end up sending a
* zero C-L to the client. We can't just remove the C-L filter,
* because well behaved 2.0 handlers will send their data down the stack,
* and we will compute a real C-L for the head request. RBB
*/
if (r->header_only &&
(clheader = apr_table_get(r->headers_out, "Content-Length")) &&
!strcmp(clheader, "0")) {
apr_table_unset(r->headers_out, "Content-Length");
}
b2 = apr_brigade_create(r->pool);
basic_http_header(r, b2, protocol);
h.pool = r->pool;
h.bb = b2;
if (r->status == HTTP_NOT_MODIFIED) {
apr_table_do((int (*)(void *, const char *, const char *)) form_header_field,
(void *) &h, r->headers_out,
"Connection",
"Keep-Alive",
"ETag",
"Content-Location",
"Expires",
"Cache-Control",
"Vary",
"Warning",
"WWW-Authenticate",
"Proxy-Authenticate",
NULL);
}
else {
apr_table_do((int (*) (void *, const char *, const char *)) form_header_field,
(void *) &h, r->headers_out, NULL);
}
terminate_header(b2);
r->sent_bodyct = 1; /* Whatever follows is real body stuff... */
ap_pass_brigade(f->next, b2);
if (r->header_only) {
apr_brigade_destroy(b);
return OK;
}
if (r->chunked) {
/* We can't add this filter until we have already sent the headers.
* If we add it before this point, then the headers will be chunked
* as well, and that is just wrong.
*/
ap_add_output_filter("CHUNK", NULL, r, r->connection);
}
/* Don't remove this filter until after we have added the CHUNK filter.
* Otherwise, f->next won't be the CHUNK filter and thus the first
* brigade won't be chunked properly.
*/
ap_remove_output_filter(f);
return ap_pass_brigade(f->next, b);
}
/* Here we deal with getting the request message body from the client.
* Whether or not the request contains a body is signaled by the presence
* of a non-zero Content-Length or by a Transfer-Encoding: chunked.
*
* Note that this is more complicated than it was in Apache 1.1 and prior
* versions, because chunked support means that the module does less.
*
* The proper procedure is this:
*
* 1. Call setup_client_block() near the beginning of the request
* handler. This will set up all the necessary properties, and will
* return either OK, or an error code. If the latter, the module should
* return that error code. The second parameter selects the policy to
* apply if the request message indicates a body, and how a chunked
* transfer-coding should be interpreted. Choose one of
*
* REQUEST_NO_BODY Send 413 error if message has any body
* REQUEST_CHUNKED_ERROR Send 411 error if body without Content-Length
* REQUEST_CHUNKED_DECHUNK If chunked, remove the chunks for me.
*
* In order to use the last two options, the caller MUST provide a buffer
* large enough to hold a chunk-size line, including any extensions.
*
* 2. When you are ready to read a body (if any), call should_client_block().
* This will tell the module whether or not to read input. If it is 0,
* the module should assume that there is no message body to read.
* This step also sends a 100 Continue response to HTTP/1.1 clients,
* so should not be called until the module is *definitely* ready to
* read content. (otherwise, the point of the 100 response is defeated).
* Never call this function more than once.
*
* 3. Finally, call get_client_block in a loop. Pass it a buffer and its size.
* It will put data into the buffer (not necessarily a full buffer), and
* return the length of the input block. When it is done reading, it will
* return 0 if EOF, or -1 if there was an error.
* If an error occurs on input, we force an end to keepalive.
*/
AP_DECLARE(int) ap_setup_client_block(request_rec *r, int read_policy)
{
const char *tenc = apr_table_get(r->headers_in, "Transfer-Encoding");
const char *lenp = apr_table_get(r->headers_in, "Content-Length");
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;
ap_add_input_filter("DECHUNK", NULL, r, r->connection);
}
else if (lenp) {
const char *pos = lenp;
while (apr_isdigit(*pos) || apr_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;
}
#ifdef AP_DEBUG
{
/* Make sure ap_getline() didn't leave any droppings. */
core_request_config *req_cfg =
(core_request_config *)ap_get_module_config(r->request_config,
&core_module);
AP_DEBUG_ASSERT(APR_BRIGADE_EMPTY(req_cfg->bb));
}
#endif
return OK;
}
AP_DECLARE(int) ap_should_client_block(request_rec *r)
{
/* First check if we have already read the request body */
if (r->read_length || (!r->read_chunked && (r->remaining <= 0)))
return 0;
if (r->expecting_100 && r->proto_num >= HTTP_VERSION(1,1)) {
char *tmp;
apr_bucket *e;
apr_bucket_brigade *bb;
/* sending 100 Continue interim response */
tmp = apr_pstrcat(r->pool, AP_SERVER_PROTOCOL, " ", status_lines[0],
CRLF CRLF, NULL);
bb = apr_brigade_create(r->pool);
e = apr_bucket_pool_create(tmp, strlen(tmp), r->pool);
APR_BRIGADE_INSERT_HEAD(bb, e);
e = apr_bucket_flush_create();
APR_BRIGADE_INSERT_TAIL(bb, e);
ap_pass_brigade(r->connection->output_filters, bb);
}
return 1;
}
static long get_chunk_size(char *b)
{
long chunksize = 0;
while (apr_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.
*/
AP_DECLARE(long) ap_get_client_block(request_rec *r, char *buffer, int bufsiz)
{
apr_size_t len_read, total;
apr_status_t rv;
apr_bucket *b, *old;
const char *tempbuf;
core_request_config *req_cfg =
(core_request_config *)ap_get_module_config(r->request_config,
&core_module);
apr_bucket_brigade *bb = req_cfg->bb;
do {
if (APR_BRIGADE_EMPTY(bb)) {
if (ap_get_brigade(r->input_filters, bb, AP_MODE_BLOCKING,
&r->remaining) != APR_SUCCESS) {
/* if we actually fail here, we want to just return and
* stop trying to read data from the client.
*/
r->connection->keepalive = -1;
apr_brigade_destroy(bb);
return -1;
}
}
} while (APR_BRIGADE_EMPTY(bb));
b = APR_BRIGADE_FIRST(bb);
if (APR_BUCKET_IS_EOS(b)) { /* reached eos on previous invocation */
apr_bucket_delete(b);
return 0;
}
/* ### it would be nice to replace the code below with "consume N bytes
### from this brigade, placing them into that buffer." there are
### other places where we do the same...
###
### alternatively, we could partition the brigade, then call a
### function which serializes a given brigade into a buffer. that
### semantic is used elsewhere, too...
*/
total = 0;
while (total < bufsiz
&& b != APR_BRIGADE_SENTINEL(bb)
&& !APR_BUCKET_IS_EOS(b)) {
if ((rv = apr_bucket_read(b, &tempbuf, &len_read, APR_BLOCK_READ)) != APR_SUCCESS) {
return -1;
}
if (total + len_read > bufsiz) {
apr_bucket_split(b, bufsiz - total);
len_read = bufsiz - total;
}
memcpy(buffer, tempbuf, len_read);
buffer += len_read;
total += len_read;
/* XXX the next two fields shouldn't be mucked with here, as they are in terms
* of bytes in the unfiltered body; gotta see if anybody else actually uses
* these
*/
r->read_length += len_read; /* XXX yank me? */
old = b;
b = APR_BUCKET_NEXT(b);
apr_bucket_delete(old);
}
return total;
}
/* In HTTP/1.1, any method can have a body. However, most GET handlers
* wouldn't know what to do with a request body if they received one.
* This helper routine tests for and reads any message body in the request,
* simply discarding whatever it receives. We need to do this because
* failing to read the request body would cause it to be interpreted
* as the next request on a persistent connection.
*
* Since we return an error status if the request is malformed, this
* routine should be called at the beginning of a no-body handler, e.g.,
*
* if ((retval = ap_discard_request_body(r)) != OK)
* return retval;
*/
AP_DECLARE(int) ap_discard_request_body(request_rec *r)
{
int rv;
if (r->read_length == 0) { /* if not read already */
if ((rv = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK)))
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;
}
static const char *add_optional_notes(request_rec *r,
const char *prefix,
const char *key,
const char *suffix)
{
const char *notes, *result;
if ((notes = apr_table_get(r->notes, key)) == NULL) {
result = prefix;
}
else {
result = apr_pstrcat(r->pool, prefix, notes, suffix, NULL);
}
return result;
}
static const char *get_canned_error_string(int status,
request_rec *r,
const char *location)
/* construct and return the default error message for a given
* HTTP defined error code
*/
{
apr_pool_t *p = r->pool;
const char *error_notes, *h1, *s1;
switch (status) {
case HTTP_MOVED_PERMANENTLY:
case HTTP_MOVED_TEMPORARILY:
case HTTP_TEMPORARY_REDIRECT:
return(apr_pstrcat(p,
"The document has moved <A HREF=\"",
ap_escape_html(r->pool, location),
"\">here</A>.<P>\n",
NULL));
case HTTP_SEE_OTHER:
return(apr_pstrcat(p,
"The answer to your request is located <A HREF=\"",
ap_escape_html(r->pool, location),
"\">here</A>.<P>\n",
NULL));
case HTTP_USE_PROXY:
return(apr_pstrcat(p,
"This resource is only accessible "
"through the proxy\n",
ap_escape_html(r->pool, location),
"<BR>\nYou will need to "
"configure your client to use that proxy.<P>\n",
NULL));
case HTTP_PROXY_AUTHENTICATION_REQUIRED:
case HTTP_UNAUTHORIZED:
return("This server could not verify that you\n"
"are authorized to access the document\n"
"requested. Either you supplied the wrong\n"
"credentials (e.g., bad password), or your\n"
"browser doesn't understand how to supply\n"
"the credentials required.<P>\n");
case HTTP_BAD_REQUEST:
return(add_optional_notes(r,
"Your browser sent a request that "
"this server could not understand.<P>\n",
"error-notes",
"<P>\n"));
case HTTP_FORBIDDEN:
return(apr_pstrcat(p,
"You don't have permission to access ",
ap_escape_html(r->pool, r->uri),
"\non this server.<P>\n",
NULL));
case HTTP_NOT_FOUND:
return(apr_pstrcat(p,
"The requested URL ",
ap_escape_html(r->pool, r->uri),
" was not found on this server.<P>\n",
NULL));
case HTTP_METHOD_NOT_ALLOWED:
return(apr_pstrcat(p,
"The requested method ", r->method,
" is not allowed for the URL ",
ap_escape_html(r->pool, r->uri),
".<P>\n",
NULL));
case HTTP_NOT_ACCEPTABLE:
s1 = apr_pstrcat(p,
"An appropriate representation of the "
"requested resource ",
ap_escape_html(r->pool, r->uri),
" could not be found on this server.<P>\n",
NULL);
return(add_optional_notes(r, s1, "variant-list", ""));
case HTTP_MULTIPLE_CHOICES:
return(add_optional_notes(r, "", "variant-list", ""));
case HTTP_LENGTH_REQUIRED:
s1 = apr_pstrcat(p,
"A request of the requested method ",
r->method,
" requires a valid Content-length.<P>\n",
NULL);
return(add_optional_notes(r, s1, "error-notes", "<P>\n"));
case HTTP_PRECONDITION_FAILED:
return(apr_pstrcat(p,
"The precondition on the request for the URL ",
ap_escape_html(r->pool, r->uri),
" evaluated to false.<P>\n",
NULL));
case HTTP_NOT_IMPLEMENTED:
s1 = apr_pstrcat(p,
ap_escape_html(r->pool, r->method), " to ",
ap_escape_html(r->pool, r->uri),
" not supported.<P>\n",
NULL);
return(add_optional_notes(r, s1, "error-notes", "<P>\n"));
case HTTP_BAD_GATEWAY:
s1 = "The proxy server received an invalid" CRLF
"response from an upstream server.<P>" CRLF;
return(add_optional_notes(r, s1, "error-notes", "<P>\n"));
case HTTP_VARIANT_ALSO_VARIES:
return(apr_pstrcat(p,
"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));
case HTTP_REQUEST_TIME_OUT:
return("I'm tired of waiting for your request.\n");
case HTTP_GONE:
return(apr_pstrcat(p,
"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));
case HTTP_REQUEST_ENTITY_TOO_LARGE:
return(apr_pstrcat(p,
"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));
case HTTP_REQUEST_URI_TOO_LARGE:
s1 = "The requested URL's length exceeds the capacity\n"
"limit for this server.<P>\n";
return(add_optional_notes(r, s1, "error-notes", "<P>\n"));
case HTTP_UNSUPPORTED_MEDIA_TYPE:
return("The supplied request data is not in a format\n"
"acceptable for processing by this resource.\n");
case HTTP_RANGE_NOT_SATISFIABLE:
return("None of the range-specifier values in the Range\n"
"request-header field overlap the current extent\n"
"of the selected resource.\n");
case HTTP_EXPECTATION_FAILED:
return(apr_pstrcat(p,
"The expectation given in the Expect request-header"
"\nfield could not be met by this server.<P>\n"
"The client sent<PRE>\n Expect: ",
apr_table_get(r->headers_in, "Expect"), "\n</PRE>\n"
"but we only allow the 100-continue expectation.\n",
NULL));
case HTTP_UNPROCESSABLE_ENTITY:
return("The server understands the media type of the\n"
"request entity, but was unable to process the\n"
"contained instructions.\n");
case HTTP_LOCKED:
return("The requested resource is currently locked.\n"
"The lock must be released or proper identification\n"
"given before the method can be applied.\n");
case HTTP_FAILED_DEPENDENCY:
return("The method could not be performed on the resource\n"
"because the requested action depended on another\n"
"action and that other action failed.\n");
case HTTP_INSUFFICIENT_STORAGE:
return("The method could not be performed on the resource\n"
"because the server is unable to store the\n"
"representation needed to successfully complete the\n"
"request. There is insufficient free space left in\n"
"your storage allocation.\n");
case HTTP_SERVICE_UNAVAILABLE:
return("The server is temporarily unable to service your\n"
"request due to maintenance downtime or capacity\n"
"problems. Please try again later.\n");
case HTTP_GATEWAY_TIME_OUT:
return("The proxy server did not receive a timely response\n"
"from the upstream server.\n");
case HTTP_NOT_EXTENDED:
return("A mandatory extension policy in the request is not\n"
"accepted by the server for this resource.\n");
default: /* HTTP_INTERNAL_SERVER_ERROR */
/*
* This comparison to expose error-notes could be modified to
* use a configuration directive and export based on that
* directive. For now "*" is used to designate an error-notes
* that is totally safe for any user to see (ie lacks paths,
* database passwords, etc.)
*/
if (((error_notes = apr_table_get(r->notes, "error-notes")) != NULL)
&& (h1 = apr_table_get(r->notes, "verbose-error-to")) != NULL
&& (strcmp(h1, "*") == 0)) {
return(apr_pstrcat(p, error_notes, "<P>\n", NULL));
}
else {
return(apr_pstrcat(p,
"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
e error notes tend to include the real file pathname in this case,
* which some people consider to be a breach of privacy. Until we
* can figure out a way to remove the pathname, leave this commented.
*
* if ((error_notes = apr_table_get(r->notes, "error-notes")) != NULL) {
* return(apr_pstrcat(p, error_notes, "<P>\n", NULL);
* }
* else {
* return "";
* }
*/
}
}
static void reset_filters(request_rec *r)
{
ap_filter_t *f = r->output_filters;
while (f) {
if (!strcasecmp(f->frec->name, "CORE") ||
!strcasecmp(f->frec->name, "CONTENT_LENGTH") ||
!strcasecmp(f->frec->name, "HTTP_HEADER")) {
f = f->next;
continue;
}
else {
ap_remove_output_filter(f);
f = f->next;
}
}
}
/* We should have named this send_canned_response, since it is used for any
* response that can be generated by the server from the request record.
* This includes all 204 (no content), 3xx (redirect), 4xx (client error),
* and 5xx (server error) messages that have not been redirected to another
* handler via the ErrorDocument feature.
*/
AP_DECLARE(void) ap_send_error_response(request_rec *r, int recursive_error)
{
int status = r->status;
int idx = ap_index_of_response(status);
char *custom_response;
const char *location = apr_table_get(r->headers_out, "Location");
/* At this point, we are starting the response over, so we have to reset
* this value.
*/
r->eos_sent = 0;
reset_filters(r);
/*
* It's possible that the Location field might be in r->err_headers_out
* instead of r->headers_out; use the latter if possible, else the
* former.
*/
if (location == NULL) {
location = apr_table_get(r->err_headers_out, "Location");
}
/* We need to special-case the handling of 204 and 304 responses,
* since they have specific HTTP requirements and do not include a
* message body. Note that being assbackwards here is not an option.
*/
if (status == HTTP_NOT_MODIFIED) {
ap_finalize_request_protocol(r);
return;
}
if (status == HTTP_NO_CONTENT) {
ap_finalize_request_protocol(r);
return;
}
if (!r->assbackwards) {
apr_table_t *tmp = r->headers_out;
/* For all HTTP/1.x responses for which we generate the message,
* we need to avoid inheriting the "normal status" header fields
* that may have been set by the request handler before the
* error or redirect, except for Location on external redirects.
*/
r->headers_out = r->err_headers_out;
r->err_headers_out = tmp;
apr_table_clear(r->err_headers_out);
if (ap_is_HTTP_REDIRECT(status) || (status == HTTP_CREATED)) {
if ((location != NULL) && *location) {
apr_table_setn(r->headers_out, "Location", location);
}
else {
location = ""; /* avoids coredump when printing, below */
}
}
r->content_language = NULL;
r->content_languages = NULL;
r->content_encoding = NULL;
r->clength = 0;
r->content_type = "text/html; charset=iso-8859-1";
if ((status == HTTP_METHOD_NOT_ALLOWED)
|| (status == HTTP_NOT_IMPLEMENTED)) {
apr_table_setn(r->headers_out, "Allow", make_allow(r));
}
if (r->header_only) {
ap_finalize_request_protocol(r);
return;
}
}
if ((custom_response = ap_response_code_string(r, idx))) {
/*
* We have a custom response output. This should only be
* a text-string to write back. But if the ErrorDocument
* was a local redirect and the requested resource failed
* for any reason, the custom_response will still hold the
* redirect URL. We don't really want to output this URL
* as a text message, so first check the custom response
* string to ensure that it is a text-string (using the
* same test used in ap_die(), i.e. does it start with a ").
* If it 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);
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;
/* XXX This is a major hack that should be fixed cleanly. The
* problem is that we have the information we need in a previous
* request, but the text of the page must be sent down the last
* request_rec's filter stack. rbb
*/
request_rec *rlast = r;
while (rlast->next) {
rlast = rlast->next;
}
/* Accept a status_line set by a module, but only if it begins
* with the 3 digit status code
*/
if (r->status_line != NULL
&& strlen(r->status_line) > 4 /* long enough */
&& apr_isdigit(r->status_line[0])
&& apr_isdigit(r->status_line[1])
&& apr_isdigit(r->status_line[2])
&& apr_isspace(r->status_line[3])
&& apr_isalnum(r->status_line[4])) {
title = r->status_line;
}
/* folks decided they didn't want the error code in the H1 text */
h1 = &title[4];
/* can't count on a charset filter being in place here,
* so do ebcdic->ascii translation explicitly (if needed)
*/
ap_rvputs_proto_in_ascii(rlast,
DOCTYPE_HTML_2_0
"<HTML><HEAD>\n<TITLE>", title,
"</TITLE>\n</HEAD><BODY>\n<H1>", h1, "</H1>\n",
NULL);
ap_rvputs_proto_in_ascii(rlast,
get_canned_error_string(status, r, location),
NULL);
if (recursive_error) {
ap_rvputs_proto_in_ascii(rlast, "<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_rvputs_proto_in_ascii(rlast, ap_psignature("<HR>\n", r), NULL);
ap_rvputs_proto_in_ascii(rlast, "</BODY></HTML>\n", NULL);
}
ap_finalize_request_protocol(r);
}
/*
* Create a new method list with the specified number of preallocated
* extension slots.
*/
AP_DECLARE(ap_method_list_t *) ap_make_method_list(apr_pool_t *p, int nelts)
{
ap_method_list_t *ml;
ml = (ap_method_list_t *) apr_palloc(p, sizeof(ap_method_list_t));
ml->method_mask = 0;
ml->method_list = apr_array_make(p, sizeof(char *), nelts);
return ml;
}
/*
* Make a copy of a method list (primarily for subrequests that may
* subsequently change it; don't want them changing the parent's, too!).
*/
AP_DECLARE(void) ap_copy_method_list(ap_method_list_t *dest,
ap_method_list_t *src)
{
int i;
char **imethods;
char **omethods;
dest->method_mask = src->method_mask;
imethods = (char **) src->method_list->elts;
for (i = 0; i < src->method_list->nelts; ++i) {
omethods = (char **) apr_array_push(dest->method_list);
*omethods = apr_pstrdup(dest->method_list->cont, imethods[i]);
}
}
/*
* Invoke a callback routine for each method in the specified list.
*/
AP_DECLARE_NONSTD(void) ap_method_list_do(int (*comp) (void *urec, const char *mname,
int mnum),
void *rec,
const ap_method_list_t *ml, ...)
{
va_list vp;
va_start(vp, ml);
ap_method_list_vdo(comp, rec, ml, vp);
va_end(vp);
}
AP_DECLARE(void) ap_method_list_vdo(int (*comp) (void *mrec,
const char *mname,
int mnum),
void *rec, const ap_method_list_t *ml,
va_list vp)
{
}
/*
* Return true if the specified HTTP method is in the provided
* method list.
*/
AP_DECLARE(int) ap_method_in_list(ap_method_list_t *l, const char *method)
{
int methnum;
int i;
char **methods;
/*
* If it's one of our known methods, use the shortcut and check the
* bitmask.
*/
methnum = ap_method_number_of(method);
if (methnum != M_INVALID) {
return (l->method_mask & (1 << methnum));
}
/*
* Otherwise, see if the method name is in the array or string names
*/
if ((l->method_list == NULL) || (l->method_list->nelts == 0)) {
return 0;
}
methods = (char **)l->method_list->elts;
for (i = 0; i < l->method_list->nelts; ++i) {
if (strcmp(method, methods[i]) == 0) {
return 1;
}
}
return 0;
}
/*
* Add the specified method to a method list (if it isn't already there).
*/
AP_DECLARE(void) ap_method_list_add(ap_method_list_t *l, const char *method)
{
int methnum;
int i;
const char **xmethod;
char **methods;
/*
* If it's one of our known methods, use the shortcut and use the
* bitmask.
*/
methnum = ap_method_number_of(method);
l->method_mask |= (1 << methnum);
if (methnum != M_INVALID) {
return;
}
/*
* Otherwise, see if the method name is in the array of string names.
*/
if (l->method_list->nelts != 0) {
methods = (char **)l->method_list->elts;
for (i = 0; i < l->method_list->nelts; ++i) {
if (strcmp(method, methods[i]) == 0) {
return;
}
}
}
xmethod = (const char **) apr_array_push(l->method_list);
*xmethod = method;
}
/*
* Remove the specified method from a method list.
*/
AP_DECLARE(void) ap_method_list_remove(ap_method_list_t *l,
const char *method)
{
int methnum;
char **methods;
/*
* If it's one of our known methods, use the shortcut and use the
* bitmask.
*/
methnum = ap_method_number_of(method);
l->method_mask |= ~(1 << methnum);
if (methnum != M_INVALID) {
return;
}
/*
* Otherwise, see if the method name is in the array of string names.
*/
if (l->method_list->nelts != 0) {
register int i, j, k;
methods = (char **)l->method_list->elts;
for (i = 0; i < l->method_list->nelts; ) {
if (strcmp(method, methods[i]) == 0) {
for (j = i, k = i + 1; k < l->method_list->nelts; ++j, ++k) {
methods[j] = methods[k];
}
--l->method_list->nelts;
}
else {
++i;
}
}
}
}
/*
* Reset a method list to be completely empty.
*/
AP_DECLARE(void) ap_clear_method_list(ap_method_list_t *l)
{
l->method_mask = 0;
l->method_list->nelts = 0;
}
/*
* Construct an entity tag (ETag) from resource information. If it's a real
* file, build in some of the file characteristics. If the modification time
* is newer than (request-time minus 1 second), mark the ETag as weak - it
* could be modified again in as short an interval. We rationalize the
* modification time we're given to keep it from being in the future.
*/
AP_DECLARE(char *) ap_make_etag(request_rec *r, int force_weak)
{
char *etag;
char *weak;
/*
* Make an ETag header out of various pieces of information. We use
* the last-modified date and, if we have a real file, the
* length and inode number - note that this doesn't have to match
* the content-length (i.e. includes), it just has to be unique
* for the file.
*
* If the request was made within a second of the last-modified date,
* we send a weak tag instead of a strong one, since it could
* be modified again later in the second, and the validation
* would be incorrect.
*/
weak = ((r->request_time - r->mtime > APR_USEC_PER_SEC)
&& !force_weak) ? "" : "W/";
if (r->finfo.filetype != 0) {
etag = apr_psprintf(r->pool,
"%s\"%lx-%lx-%lx\"", weak,
(unsigned long) r->finfo.inode,
(unsigned long) r->finfo.size,
(unsigned long) r->mtime);
}
else {
etag = apr_psprintf(r->pool, "%s\"%lx\"", weak,
(unsigned long) r->mtime);
}
return etag;
}
AP_DECLARE(void) ap_set_etag(request_rec *r)
{
char *etag;
char *variant_etag, *vlv;
int vlv_weak;
if (!r->vlist_validator) {
etag = ap_make_etag(r, 0);
}
else {
/* If we have a variant list validator (vlv) due to the
* response being negotiated, then we create a structured
* entity tag which merges the variant etag with the variant
* list validator (vlv). This merging makes revalidation
* somewhat safer, ensures that caches which can deal with
* Vary will (eventually) be updated if the set of variants is
* changed, and is also a protocol requirement for transparent
* content negotiation.
*/
/* if the variant list validator is weak, we make the whole
* structured etag weak. If we would not, then clients could
* have problems merging range responses if we have different
* variants with the same non-globally-unique strong etag.
*/
vlv = r->vlist_validator;
vlv_weak = (vlv[0] == 'W');
variant_etag = ap_make_etag(r, vlv_weak);
/* merge variant_etag and vlv into a structured etag */
variant_etag[strlen(variant_etag) - 1] = '\0';
if (vlv_weak)
vlv += 3;
else
vlv++;
etag = apr_pstrcat(r->pool, variant_etag, ";", vlv, NULL);
}
apr_table_setn(r->headers_out, "ETag", etag);
}
static int parse_byterange(char *range, apr_off_t clength,
apr_off_t *start, apr_off_t *end)
{
char *dash = strchr(range, '-');
if (!dash)
return 0;
if ((dash == range)) {
/* In the form "-5" */
*start = clength - atol(dash + 1);
*end = clength - 1;
}
else {
*dash = '\0';
dash++;
*start = atol(range);
if (*dash)
*end = atol(dash);
else /* "5-" */
*end = clength - 1;
}
if (*start < 0)
*start = 0;
if (*end >= clength)
*end = clength - 1;
if (*start > *end)
return -1;
return (*start > 0 || *end < clength);
}
static int ap_set_byterange(request_rec *r);
typedef struct byterange_ctx {
apr_bucket_brigade *bb;
int num_ranges;
const char *orig_ct;
} byterange_ctx;
/*
* Here we try to be compatible with clients that want multipart/x-byteranges
* instead of multipart/byteranges (also see above), as per HTTP/1.1. We
* look for the Request-Range header (e.g. Netscape 2 and 3) as an indication
* that the browser supports an older protocol. We also check User-Agent
* for Microsoft Internet Explorer 3, which needs this as well.
*/
static int use_range_x(request_rec *r)
{
const char *ua;
return (apr_table_get(r->headers_in, "Request-Range") ||
((ua = apr_table_get(r->headers_in, "User-Agent"))
&& ap_strstr_c(ua, "MSIE 3")));
}
#define BYTERANGE_FMT "%" APR_OFF_T_FMT "-%" APR_OFF_T_FMT "/%" APR_OFF_T_FMT
AP_CORE_DECLARE_NONSTD(apr_status_t) ap_byterange_filter(
ap_filter_t *f,
apr_bucket_brigade *bb)
{
#define MIN_LENGTH(len1, len2) ((len1 > len2) ? len2 : len1)
request_rec *r = f->r;
byterange_ctx *ctx = f->ctx;
apr_bucket *e;
apr_bucket_brigade *bsend;
apr_off_t range_start;
apr_off_t range_end;
char *current;
char *bound_head;
apr_ssize_t bb_length;
apr_off_t clength = 0;
apr_status_t rv;
int found = 0;
if (!ctx) {
int num_ranges = ap_set_byterange(r);
if (num_ranges == -1) {
ap_remove_output_filter(f);
bsend = apr_brigade_create(r->pool);
e = ap_bucket_error_create(HTTP_RANGE_NOT_SATISFIABLE, NULL, r->pool);
APR_BRIGADE_INSERT_TAIL(bsend, e);
e = apr_bucket_eos_create();
APR_BRIGADE_INSERT_TAIL(bsend, e);
return ap_pass_brigade(f->next, bsend);
}
if (num_ranges == 0) {
ap_remove_output_filter(f);
return ap_pass_brigade(f->next, bb);
}
ctx = f->ctx = apr_pcalloc(r->pool, sizeof(*ctx));
ctx->num_ranges = num_ranges;
if (num_ranges > 1) {
ctx->orig_ct = r->content_type;
r->content_type =
apr_pstrcat(r->pool, "multipart", use_range_x(r) ? "/x-" : "/",
"byteranges; boundary=", r->boundary, NULL);
}
/* create a brigade in case we never call ap_save_brigade() */
ctx->bb = apr_brigade_create(r->pool);
}
/* We can't actually deal with byte-ranges until we have the whole brigade
* because the byte-ranges can be in any order, and according to the RFC,
* we SHOULD return the data in the same order it was requested.
*/
if (!APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) {
ap_save_brigade(f, &ctx->bb, &bb);
return APR_SUCCESS;
}
/* compute this once (it is an invariant) */
bound_head = apr_pstrcat(r->pool,
CRLF "--", r->boundary,
CRLF "Content-type: ",
ap_make_content_type(r, ctx->orig_ct),
CRLF "Content-range: bytes ",
NULL);
ap_xlate_proto_to_ascii(bound_head, strlen(bound_head));
/* If we have a saved brigade from a previous run, concat the passed
* brigade with our saved brigade. Otherwise just continue.
*/
if (ctx->bb) {
APR_BRIGADE_CONCAT(ctx->bb, bb);
bb = ctx->bb;
ctx->bb = NULL; /* ### strictly necessary? call brigade_destroy? */
}
/* It is possible that we won't have a content length yet, so we have to
* compute the length before we can actually do the byterange work.
*/
(void) apr_brigade_length(bb, 1, &bb_length);
clength = (apr_off_t)bb_length;
/* this brigade holds what we will be sending */
bsend = apr_brigade_create(r->pool);
while ((current = ap_getword(r->pool, &r->range, ',')) &&
(rv = parse_byterange(current, clength, &range_start, &range_end))) {
apr_bucket *e2;
apr_bucket *ec;
if (rv == -1) {
continue;
}
else {
found = 1;
}
if (ctx->num_ranges > 1) {
char *ts;
e = apr_bucket_pool_create(bound_head,
strlen(bound_head), r->pool);
APR_BRIGADE_INSERT_TAIL(bsend, e);
ts = apr_psprintf(r->pool, BYTERANGE_FMT CRLF CRLF,
range_start, range_end, clength);
ap_xlate_proto_to_ascii(ts, strlen(ts));
e = apr_bucket_pool_create(ts, strlen(ts), r->pool);
APR_BRIGADE_INSERT_TAIL(bsend, e);
}
e = apr_brigade_partition(bb, range_start);
e2 = apr_brigade_partition(bb, range_end + 1);
ec = e;
do {
apr_bucket *foo;
const char *str;
apr_size_t len;
if (apr_bucket_copy(ec, &foo) != APR_SUCCESS) {
apr_bucket_read(ec, &str, &len, APR_BLOCK_READ);
foo = apr_bucket_heap_create(str, len, 0, NULL);
}
APR_BRIGADE_INSERT_TAIL(bsend, foo);
ec = APR_BUCKET_NEXT(ec);
} while (ec != e2);
}
if (found == 0) {
ap_remove_output_filter(f);
r->status = HTTP_OK;
return HTTP_RANGE_NOT_SATISFIABLE;
}
if (ctx->num_ranges > 1) {
char *end;
/* add the final boundary */
end = apr_pstrcat(r->pool, CRLF "--", r->boundary, "--" CRLF, NULL);
ap_xlate_proto_to_ascii(end, strlen(end));
e = apr_bucket_pool_create(end, strlen(end), r->pool);
APR_BRIGADE_INSERT_TAIL(bsend, e);
}
e = apr_bucket_eos_create();
APR_BRIGADE_INSERT_TAIL(bsend, e);
/* we're done with the original content */
apr_brigade_destroy(bb);
/* send our multipart output */
return ap_pass_brigade(f->next, bsend);
}
static int ap_set_byterange(request_rec *r)
{
const char *range;
const char *if_range;
const char *match;
const char *ct;
apr_off_t range_start;
apr_off_t range_end;
int num_ranges;
if (r->assbackwards)
return 0;
/* is content already a single range? */
if (apr_table_get(r->headers_out, "Content-Range")) {
return 0;
}
/* is content already a multiple range? */
if ((ct = apr_table_get(r->headers_out, "Content-Type")) &&
(!strncasecmp(ct, "multipart/byteranges", 20) ||
!strncasecmp(ct, "multipart/x-byteranges", 22))) {
return 0;
}
/* Check for Range request-header (HTTP/1.1) or Request-Range for
* backwards-compatibility with second-draft Luotonen/Franks
* byte-ranges (e.g. Netscape Navigator 2-3).
*
* We support this form, with Request-Range, and (farther down) we
* send multipart/x-byteranges instead of multipart/byteranges for
* Request-Range based requests to work around a bug in Netscape
* Navigator 2-3 and MSIE 3.
*/
if (!(range = apr_table_get(r->headers_in, "Range")))
range = apr_table_get(r->headers_in, "Request-Range");
if (!range || strncasecmp(range, "bytes=", 6)) {
return 0;
}
/* Check the If-Range header for Etag or Date.
* Note that this check will return false (as required) if either
* of the two etags are weak.
*/
if ((if_range = apr_table_get(r->headers_in, "If-Range"))) {
if (if_range[0] == '"') {
if (!(match = apr_table_get(r->headers_out, "Etag")) ||
(strcmp(if_range, match) != 0))
return 0;
}
else if (!(match = apr_table_get(r->headers_out, "Last-Modified")) ||
(strcmp(if_range, match) != 0))
return 0;
}
/* would be nice to pick this up from f->ctx */
ct = ap_make_content_type(r, r->content_type);
if (!ap_strchr_c(range, ',')) {
int rv;
/* A single range */
/* rvarse_byterange() modifies the contents, so make a copy */
if ((rv = parse_byterange(apr_pstrdup(r->pool, range + 6), r->clength,
&range_start, &range_end)) <= 0) {
return rv;
}
apr_table_setn(r->headers_out, "Content-Range",
apr_psprintf(r->pool, "bytes " BYTERANGE_FMT,
range_start, range_end, r->clength));
apr_table_setn(r->headers_out, "Content-Type", ct);
num_ranges = 1;
}
else {
/* a multiple range */
num_ranges = 2;
/* ### it would be nice if r->boundary was in f->ctx */
r->boundary = apr_psprintf(r->pool, "%qx%lx",
r->request_time, (long) getpid());
apr_table_setn(r->headers_out, "Content-Type",
apr_pstrcat(r->pool,
"multipart", use_range_x(r) ? "/x-" : "/",
"byteranges; boundary=", r->boundary,
NULL));
}
r->status = HTTP_PARTIAL_CONTENT;
r->range = range + 6;
return num_ranges;
}