http_protocol.c revision 6af61679f5d25e77f0d34112d714f9ae4cc666e3
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher/* ====================================================================
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * The Apache Software License, Version 1.1
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * Copyright (c) 2000-2001 The Apache Software Foundation. All rights
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 * 1. Redistributions of source code must retain the above copyright
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * notice, this list of conditions and the following disclaimer.
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 * 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 * 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 * 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 * 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 * ====================================================================
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
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 * http_protocol.c --- routines which directly communicate with the client.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * Code originally by Rob McCool; much redone by Robert S. Thau
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * and the Apache Software Foundation.
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 GallagherAP_DECLARE(int) ap_set_keepalive(request_rec *r)
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher apr_table_get(r->headers_out, "Connection"), "close");
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher const char *conn = apr_table_get(r->headers_in, "Connection");
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,
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.
e0404de84c31d2387bb244d018a5cac8d01f8b19Simo Sorce * Note that the condition evaluation order is extremely important.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher || apr_table_get(r->headers_out, "Content-Length")
e0404de84c31d2387bb244d018a5cac8d01f8b19Simo Sorce "Transfer-Encoding"),
21d485184df986e1a123f70c689517386e51a5ceMichal Zidek && (r->chunked = 1))) /* THIS CODE IS CORRECT, see comment above. */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher || (r->server->keep_alive_max > r->connection->keepalives))
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher && (!apr_table_get(r->subprocess_env, "nokeepalive")
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 /* If they sent a Keep-Alive token, send one back */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher apr_table_setn(r->headers_out, "Keep-Alive",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher apr_psprintf(r->pool, "timeout=%d, max=%d",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher apr_table_setn(r->headers_out, "Keep-Alive",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher apr_table_mergen(r->headers_out, "Connection", "Keep-Alive");
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher /* Otherwise, we need to indicate that we will be closing this
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * connection immediately after the current response.
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 apr_table_mergen(r->headers_out, "Connection", "close");
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen GallagherAP_DECLARE(int) ap_meets_conditions(request_rec *r)
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 /* 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 * 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 if (!ap_is_HTTP_SUCCESS(r->status) || r->no_local_copy) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher /* XXX: we should define a "time unset" constant */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher mtime = (r->mtime != 0) ? r->mtime : apr_time_now();
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 if ((if_match = apr_table_get(r->headers_in, "If-Match")) != NULL) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher || !ap_find_list_item(r->pool, if_match, etag))) {
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 if_unmodified = apr_table_get(r->headers_in, "If-Unmodified-Since");
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher apr_time_t ius = ap_parseHTTPdate(if_unmodified);
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 * 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 * 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 if_nonematch = apr_table_get(r->headers_in, "If-None-Match");
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (apr_table_get(r->headers_in, "Range")) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher && ap_find_list_item(r->pool, if_nonematch, etag)) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher else if (ap_strstr_c(if_nonematch, etag)) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher && ap_find_list_item(r->pool, if_nonematch, etag))) {
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 apr_time_t ims = ap_parseHTTPdate(if_modified_since);
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if ((ims >= mtime) && (ims <= r->request_time)) {
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 * 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 GallagherAP_DECLARE(int) ap_method_number_of(const char *method)
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher return M_GET; /* see header_only in request_rec */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * Turn a known method number into a name. Doesn't work for
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * extension methods, obviously.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen GallagherAP_DECLARE(const char *) ap_method_name_of(int methnum)
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher static const char *AP_HTTP_METHODS[METHODS] = { NULL };
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 AP_HTTP_METHODS[M_PROPPATCH] = "PROPPATCH";
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 if ((methnum == M_INVALID) || (methnum >= METHODS)) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagherstatic long get_chunk_size(char *);
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 f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(struct dechunk_ctx));
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
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if ((rv = ap_getline(line, sizeof(line), f->r,
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher 0 /* readline */)) < 0) {
94a66f84bd3c28fcabffeb84c682dccf89d89c2bSumit Bose /* XXX sanity check end chunk here */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher /* bad trailer */
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 */
9b72b00ebcfd6225a4e139619c8e18d44a448f87Stephen Gallagher ap_assert(ctx->state == WANT_HDR || ctx->state == WANT_TRL);
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 if ((rv = ap_get_brigade(f->next, bb, mode,
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 * ### 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 while (b != APR_BRIGADE_SENTINEL(bb) && !APR_BUCKET_IS_EOS(b)) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher AP_DEBUG_ASSERT(len <= ctx->chunk_size - ctx->bytes_delivered);
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if (ctx->bytes_delivered == ctx->chunk_size) {
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 f->ctx = ctx = apr_pcalloc(f->c->pool, sizeof(*ctx));
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 if (!e || apr_bucket_read(e, &str, &length, APR_NONBLOCK_READ) != APR_SUCCESS) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher const char *c = str;
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher else if (*c == APR_ASCII_CR && *(c + 1) == APR_ASCII_LF)
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher if ((rv = ap_get_brigade(f->next, ctx->b, mode, readbytes)) != APR_SUCCESS) {
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher /* readbytes == 0 is "read a single line". otherwise, read a block. */
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 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 /* 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.
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher break; /* once we've gotten some data, deliver it to caller */
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.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher apr_bucket *eos = apr_bucket_eos_create();
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher /* we are reading a single line, e.g. the HTTP headers */
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher if ((rv = apr_bucket_read(e, (const char **)&buff, &len, mode)) != APR_SUCCESS) {
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.
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 Gallagherstatic const char * status_lines[RESPONSE_CODES] =
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagherstatic const char * const status_lines[RESPONSE_CODES] =
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher "100 Continue",
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher "101 Switching Protocols",
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher "102 Processing",
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",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "300 Multiple Choices",
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher "301 Moved Permanently",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "303 See Other",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "304 Not Modified",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "305 Use Proxy",
f5e22261a2ff95f2a61f4f199fffb8de79668110Stephen Gallagher "307 Temporary Redirect",
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 "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",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "422 Unprocessable Entity",
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher "424 Failed Dependency",
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",
3d8a87081a6cd197acbd355b5a39111669ec2aa6Jakub Hrozek "510 Not Extended"
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 GallagherAP_DECLARE(int) ap_index_of_response(int status)
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher static int shortcut[6] = {0, LEVEL_200, LEVEL_300, LEVEL_400,
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher if (status < 100) /* Below 100 is illegal for HTTP status */
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher for (i = 0; i < 5; i++) {
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher return LEVEL_500; /* status unknown (falls in gap) */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher return LEVEL_500; /* 600 or above is also illegal */
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen GallagherAP_DECLARE(const char *) ap_get_status_line(int status)
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher return status_lines[ap_index_of_response(status)];
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.
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagherstatic int form_header_field(header_struct *h,
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher const char *fieldname, const char *fieldval)
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 * Determine the protocol to use for the response. Potentially downgrade
c1fcc832ccfc237caac8b99be238cf2d598f908cStephen Gallagher * to HTTP/1.0 in some situations and/or turn off keepalives.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher * also prepare r->status_line.
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagherstatic void basic_http_header_check(request_rec *r,
9b72b00ebcfd6225a4e139619c8e18d44a448f87Stephen Gallagher /* no such thing as a response protocol */
e134a6af42102c8d865e82bf89e0b8c5a40fb5faStephen Gallagher r->status_line = status_lines[ap_index_of_response(r->status)];
const char *protocol)
char *tmp;
if (r->assbackwards) {
const char *protocol;
char *list;
int mask;
NULL);
int rv;
while (r->prev)
r = r->prev;
return rv;
apr_table_do((int (*) (void *, const char *, const char *))
return OK;
if (r->assbackwards)
return DECLINED;
return OK;
* consisting of comma/space-separated tokens.
char *start;
char **strpp;
start = e;
++i, ++strpp) {
ap_filter_t *f,
request_rec *r = f->r;
const char *clheader;
const char *protocol;
apr_bucket *e;
APR_BRIGADE_FOREACH(e, b) {
return AP_FILTER_ERROR;
if (r->assbackwards) {
r->bytes_sent = 0;
r->headers_out);
fixup_vary(r);
ap_set_keepalive(r);
if (r->chunked) {
r->content_type));
if (r->content_encoding) {
r->content_encoding);
else if (r->content_language) {
r->content_language);
if (r->header_only &&
(void *) &h, r->headers_out,
NULL);
if (r->header_only) {
return OK;
if (r->chunked) {
long max_body;
r->read_chunked = 0;
r->remaining = 0;
if (tenc) {
return HTTP_NOT_IMPLEMENTED;
else if (lenp) {
++pos;
return HTTP_BAD_REQUEST;
return HTTP_REQUEST_ENTITY_TOO_LARGE;
return HTTP_REQUEST_ENTITY_TOO_LARGE;
#ifdef AP_DEBUG
&core_module);
return OK;
char *tmp;
apr_bucket *e;
e = apr_bucket_flush_create();
static long get_chunk_size(char *b)
long chunksize = 0;
while (apr_isxdigit(*b)) {
int xvalue = 0;
return chunksize;
const char *tempbuf;
&core_module);
total = 0;
&& !APR_BUCKET_IS_EOS(b)) {
old = b;
b = APR_BUCKET_NEXT(b);
return total;
int rv;
return rv;
if (r->expecting_100) {
return OK;
if (rv < 0)
return HTTP_BAD_REQUEST;
return OK;
const char *prefix,
const char *key,
const char *suffix)
return result;
request_rec *r,
const char *location)
switch (status) {
case HTTP_MOVED_PERMANENTLY:
case HTTP_MOVED_TEMPORARILY:
case HTTP_TEMPORARY_REDIRECT:
return(apr_pstrcat(p,
NULL));
case HTTP_SEE_OTHER:
return(apr_pstrcat(p,
NULL));
case HTTP_USE_PROXY:
return(apr_pstrcat(p,
NULL));
case HTTP_UNAUTHORIZED:
case HTTP_BAD_REQUEST:
return(add_optional_notes(r,
case HTTP_FORBIDDEN:
return(apr_pstrcat(p,
NULL));
case HTTP_NOT_FOUND:
return(apr_pstrcat(p,
NULL));
case HTTP_METHOD_NOT_ALLOWED:
return(apr_pstrcat(p,
NULL));
case HTTP_NOT_ACCEPTABLE:
NULL);
case HTTP_MULTIPLE_CHOICES:
case HTTP_LENGTH_REQUIRED:
r->method,
NULL);
case HTTP_PRECONDITION_FAILED:
return(apr_pstrcat(p,
NULL));
case HTTP_NOT_IMPLEMENTED:
NULL);
case HTTP_BAD_GATEWAY:
case HTTP_VARIANT_ALSO_VARIES:
return(apr_pstrcat(p,
NULL));
case HTTP_REQUEST_TIME_OUT:
case HTTP_GONE:
return(apr_pstrcat(p,
NULL));
return(apr_pstrcat(p,
r->method,
NULL));
case HTTP_EXPECTATION_FAILED:
return(apr_pstrcat(p,
NULL));
case HTTP_LOCKED:
case HTTP_FAILED_DEPENDENCY:
case HTTP_SERVICE_UNAVAILABLE:
case HTTP_GATEWAY_TIME_OUT:
case HTTP_NOT_EXTENDED:
return(apr_pstrcat(p,
NULL));
f = f->next;
f = f->next;
char *custom_response;
r->eos_sent = 0;
reset_filters(r);
if (!r->assbackwards) {
r->clength = 0;
if (r->header_only) {
r = r->prev;
const char *h1;
NULL);
NULL);
if (recursive_error) {
return ml;
char **imethods;
char **omethods;
int mnum),
void *rec,
const char *mname,
int mnum),
int methnum;
char **methods;
int methnum;
const char **xmethod;
char **methods;
const char *method)
int methnum;
char **methods;
l->method_mask = 0;
char *etag;
char *weak;
(unsigned long) r->mtime);
(unsigned long) r->mtime);
return etag;
char *etag;
int vlv_weak;
if (!r->vlist_validator) {
if (vlv_weak)
vlv++;
if (!dash)
dash++;
if (*dash)
if (*start < 0)
*start = 0;
typedef struct byterange_ctx {
int num_ranges;
const char *orig_ct;
* 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
const char *ua;
ap_filter_t *f,
request_rec *r = f->r;
apr_bucket *e;
char *current;
char *bound_head;
int found = 0;
if (!ctx) {
e = apr_bucket_eos_create();
if (num_ranges == 0) {
r->content_type =
return APR_SUCCESS;
NULL);
char *ts;
ec = e;
const char *str;
if (found == 0) {
return HTTP_RANGE_NOT_SATISFIABLE;
char *end;
e = apr_bucket_eos_create();
const char *range;
const char *if_range;
const char *match;
const char *ct;
int num_ranges;
if (r->assbackwards)
* send multipart/x-byteranges instead of multipart/byteranges for
int rv;
return rv;
NULL));
return num_ranges;