http_protocol.c revision 1b21d7b3d97def358b2e923655edeb16613a1c31
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek/* ====================================================================
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * The Apache Software License, Version 1.1
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * Copyright (c) 2000 The Apache Software Foundation. All rights
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * Redistribution and use in source and binary forms, with or without
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * modification, are permitted provided that the following conditions
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * 1. Redistributions of source code must retain the above copyright
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * notice, this list of conditions and the following disclaimer.
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * 2. Redistributions in binary form must reproduce the above copyright
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * notice, this list of conditions and the following disclaimer in
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * the documentation and/or other materials provided with the
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * distribution.
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * 3. The end-user documentation included with the redistribution,
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * if any, must include the following acknowledgment:
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * "This product includes software developed by the
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * Apache Software Foundation (http://www.apache.org/)."
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * Alternately, this acknowledgment may appear in the software itself,
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * if and wherever such third-party acknowledgments normally appear.
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * 4. The names "Apache" and "Apache Software Foundation" must
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * not be used to endorse or promote products derived from this
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * software without prior written permission. For written
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * permission, please contact apache@apache.org.
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * 5. Products derived from this software may not be called "Apache",
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * nor may "Apache" appear in their name, without prior written
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * permission of the Apache Software Foundation.
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * SUCH DAMAGE.
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * ====================================================================
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * This software consists of voluntary contributions made by many
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * individuals on behalf of the Apache Software Foundation. For more
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * information on the Apache Software Foundation, please see
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * Portions of this software are based upon public domain software
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * originally written at the National Center for Supercomputing Applications,
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * University of Illinois, Urbana-Champaign.
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * http_protocol.c --- routines which directly communicate with the client.
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * Code originally by Rob McCool; much redone by Robert S. Thau
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * and the Apache Software Foundation.
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek#include "http_log.h" /* For errors detected in basic auth common
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * support code... */
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek#include "util_date.h" /* For parseHTTPdate and BAD_DATE */
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * Builds the content-type that should be sent to the client from the
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * content-type specified. The following rules are followed:
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * - if type is NULL, type is set to ap_default_type(r)
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * - if charset adding is disabled, stop processing and return type.
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * - then, if there are no parameters on type, add the default charset
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * - return type
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozekstatic const char *make_content_type(request_rec *r, const char *type)
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek static const char *needcset[] = {
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek const char **pcset;
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek (core_dir_config *)ap_get_module_config(r->per_dir_config,
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek if (conf->add_default_charset != ADD_DEFAULT_CHARSET_ON) {
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek /* already has parameter, do nothing */
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek /* XXX we don't check the validity */
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek /* see if it makes sense to add the charset. At present,
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * we only add it if the Content-type is one of needcset[]
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek type = apr_pstrcat(r->pool, type, "; charset=",
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozekstatic int parse_byterange(char *range, apr_off_t clength,
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek /* In the form "-5" */
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek else /* "5-" */
7f5de490e24f1389501b939b742a9e5675f1c41dJakub Hrozektypedef struct byterange_ctx {
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * Here we try to be compatible with clients that want multipart/x-byteranges
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * instead of multipart/byteranges (also see above), as per HTTP/1.1. We
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * look for the Request-Range header (e.g. Netscape 2 and 3) as an indication
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * that the browser supports an older protocol. We also check User-Agent
7f5de490e24f1389501b939b742a9e5675f1c41dJakub Hrozek * for Microsoft Internet Explorer 3, which needs this as well.
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek const char *ua;
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek return (apr_table_get(r->headers_in, "Request-Range") ||
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek ((ua = apr_table_get(r->headers_in, "User-Agent"))
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek#define BYTERANGE_FMT "%" APR_OFF_T_FMT "-%" APR_OFF_T_FMT "/%" APR_OFF_T_FMT
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub HrozekAP_CORE_DECLARE_NONSTD(apr_status_t) ap_byterange_filter(
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek#define MIN_LENGTH(len1, len2) ((len1 > len2) ? len2 : len1)
7f5de490e24f1389501b939b742a9e5675f1c41dJakub Hrozek e = ap_bucket_create_error(HTTP_RANGE_NOT_SATISFIABLE, NULL, r->pool);
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek ctx = f->ctx = apr_pcalloc(r->pool, sizeof(*ctx));
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek apr_pstrcat(r->pool, "multipart", use_range_x(r) ? "/x-" : "/",
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek /* create a brigade in case we never call ap_save_brigade() */
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek /* We can't actually deal with byte-ranges until we have the whole brigade
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * because the byte-ranges can be in any order, and according to the RFC,
7f5de490e24f1389501b939b742a9e5675f1c41dJakub Hrozek * we SHOULD return the data in the same order it was requested.
7f5de490e24f1389501b939b742a9e5675f1c41dJakub Hrozek if (!APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) {
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek /* compute this once (it is an invariant) */
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek ap_xlate_proto_to_ascii(bound_head, strlen(bound_head));
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek /* concat the passed brigade with our saved brigade */
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek ctx->bb = NULL; /* ### strictly necessary? call brigade_destroy? */
7f5de490e24f1389501b939b742a9e5675f1c41dJakub Hrozek /* It is possible that we won't have a content length yet, so we have to
7f5de490e24f1389501b939b742a9e5675f1c41dJakub Hrozek * compute the length before we can actually do the byterange work.
7f5de490e24f1389501b939b742a9e5675f1c41dJakub Hrozek /* this brigade holds what we will be sending */
7f5de490e24f1389501b939b742a9e5675f1c41dJakub Hrozek while ((current = ap_getword(r->pool, &r->range, ',')) &&
7f5de490e24f1389501b939b742a9e5675f1c41dJakub Hrozek (rv = parse_byterange(current, clength, &range_start, &range_end))) {
7f5de490e24f1389501b939b742a9e5675f1c41dJakub Hrozek ts = apr_psprintf(r->pool, BYTERANGE_FMT CRLF CRLF,
7f5de490e24f1389501b939b742a9e5675f1c41dJakub Hrozek e = apr_bucket_pool_create(ts, strlen(ts), r->pool);
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek const char *str;
7f5de490e24f1389501b939b742a9e5675f1c41dJakub Hrozek if (apr_bucket_copy(ec, &foo) != APR_SUCCESS) {
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek apr_bucket_read(ec, &str, &len, APR_BLOCK_READ);
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek foo = apr_bucket_heap_create(str, len, 0, NULL);
7f5de490e24f1389501b939b742a9e5675f1c41dJakub Hrozek /* add the final boundary */
7f5de490e24f1389501b939b742a9e5675f1c41dJakub Hrozek end = apr_pstrcat(r->pool, CRLF "--", r->boundary, "--" CRLF, NULL);
7f5de490e24f1389501b939b742a9e5675f1c41dJakub Hrozek e = apr_bucket_pool_create(end, strlen(end), r->pool);
7f5de490e24f1389501b939b742a9e5675f1c41dJakub Hrozek /* we're done with the original content */
7f5de490e24f1389501b939b742a9e5675f1c41dJakub Hrozek /* send our multipart output */
7f5de490e24f1389501b939b742a9e5675f1c41dJakub HrozekAP_DECLARE(void) ap_set_content_length(request_rec *r, apr_off_t clength)
7f5de490e24f1389501b939b742a9e5675f1c41dJakub Hrozek apr_table_setn(r->headers_out, "Content-Length",
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek apr_psprintf(r->pool, "%" APR_OFF_T_FMT, clength));
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub HrozekAP_DECLARE(int) ap_set_keepalive(request_rec *r)
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek apr_table_get(r->headers_out, "Connection"), "close");
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek const char *conn = apr_table_get(r->headers_in, "Connection");
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek /* The following convoluted conditional determines whether or not
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * the current connection should remain persistent after this response
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * (a.k.a. HTTP Keep-Alive) and whether or not the output message
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * body should use the HTTP/1.1 chunked transfer-coding. In English,
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * IF we have not marked this connection as errored;
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * and the response body has a defined length due to the status code
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * being 304 or 204, the request method being HEAD, already
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * having defined Content-Length or Transfer-Encoding: chunked, or
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * the request version being HTTP/1.1 and thus capable of being set
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * as chunked [we know the (r->chunked = 1) side-effect is ugly];
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * and the server configuration enables keep-alive;
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * and the server configuration has a reasonable inter-request timeout;
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * and there is no maximum # requests or the max hasn't been reached;
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * and the response status does not require a close;
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * and the response generator has not already indicated close;
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * and the client did not request non-persistence (Connection: close);
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * and we haven't been configured to ignore the buggy twit
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * or they're a buggy twit coming through a HTTP/1.1 proxy
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * and the client is requesting an HTTP/1.0-style keep-alive
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * or the client claims to be HTTP/1.1 compliant (perhaps a proxy);
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * THEN we can be persistent, which requires more headers be output.
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * Note that the condition evaluation order is extremely important.
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek || apr_table_get(r->headers_out, "Content-Length")
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek "Transfer-Encoding"),
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek && (r->chunked = 1))) /* THIS CODE IS CORRECT, see comment above. */
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek || (r->server->keep_alive_max > r->connection->keepalives))
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek && (!apr_table_get(r->subprocess_env, "nokeepalive")
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek && ((ka_sent = ap_find_token(r->pool, conn, "keep-alive"))
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek int left = r->server->keep_alive_max - r->connection->keepalives;
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek /* If they sent a Keep-Alive token, send one back */
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek apr_table_mergen(r->headers_out, "Connection", "Keep-Alive");
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek /* Otherwise, we need to indicate that we will be closing this
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * connection immediately after the current response.
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * We only really need to send "close" to HTTP/1.1 clients, but we
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * always send it anyway, because a broken proxy may identify itself
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * as HTTP/1.0, but pass our request along with our HTTP/1.1 tag
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * to a HTTP/1.1 client. Better safe than sorry.
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek apr_table_mergen(r->headers_out, "Connection", "close");
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * Return the latest rational time from a request/mtime (modification time)
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * pair. We return the mtime unless it's in the future, in which case we
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * return the current time. We use the request time as a reference in order
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * to limit the number of calls to time(). We don't check for futurosity
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * unless the mtime is at least as new as the reference.
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub HrozekAP_DECLARE(apr_time_t) ap_rationalize_mtime(request_rec *r, apr_time_t mtime)
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek /* For all static responses, it's almost certain that the file was
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * last modified before the beginning of the request. So there's
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * no reason to call time(NULL) again. But if the response has been
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * created on demand, then it might be newer than the time the request
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * started. In this event we really have to call time(NULL) again
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * so that we can give the clients the most accurate Last-Modified. If we
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * were given a time in the future, we return the current time - the
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * Last-Modified can't be in the future.
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek now = (mtime < r->request_time) ? r->request_time : apr_time_now();
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub HrozekAP_DECLARE(int) ap_meets_conditions(request_rec *r)
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek const char *etag = apr_table_get(r->headers_out, "ETag");
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek const char *if_match, *if_modified_since, *if_unmodified, *if_nonematch;
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek /* Check for conditional requests --- note that we only want to do
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * this if we are successful so far and we are not processing a
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * subrequest or an ErrorDocument.
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * The order of the checks is important, since ETag checks are supposed
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * to be more accurate than checks relative to the modification time.
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * However, not all documents are guaranteed to *have* ETags, and some
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * might have Last-Modified values w/o ETags, so this gets a little
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * complicated.
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek if (!ap_is_HTTP_SUCCESS(r->status) || r->no_local_copy) {
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek /* XXX: we should define a "time unset" constant */
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek mtime = (r->mtime != 0) ? r->mtime : apr_time_now();
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek /* If an If-Match request-header field was given
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * AND the field value is not "*" (meaning match anything)
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * AND if our strong ETag does not match any entity tag in that field,
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * respond with a status of 412 (Precondition Failed).
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek if ((if_match = apr_table_get(r->headers_in, "If-Match")) != NULL) {
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek || !ap_find_list_item(r->pool, if_match, etag))) {
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek /* Else if a valid If-Unmodified-Since request-header field was given
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * AND the requested resource has been modified since the time
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * specified in this field, then the server MUST
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * respond with a status of 412 (Precondition Failed).
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek if_unmodified = apr_table_get(r->headers_in, "If-Unmodified-Since");
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek apr_time_t ius = ap_parseHTTPdate(if_unmodified);
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek /* If an If-None-Match request-header field was given
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * AND the field value is "*" (meaning match anything)
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * OR our ETag matches any of the entity tags in that field, fail.
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * If the request method was GET or HEAD, failure means the server
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * SHOULD respond with a 304 (Not Modified) response.
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * For all other request methods, failure means the server MUST
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * respond with a status of 412 (Precondition Failed).
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * GET or HEAD allow weak etag comparison, all other methods require
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * strong comparison. We can only use weak if it's not a range request.
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek if_nonematch = apr_table_get(r->headers_in, "If-None-Match");
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek && ap_find_list_item(r->pool, if_nonematch, etag)) {
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek && ap_find_list_item(r->pool, if_nonematch, etag))) {
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek /* Else if a valid If-Modified-Since request-header field was given
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * AND it is a GET or HEAD request
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * AND the requested resource has not been modified since the time
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * specified in this field, then the server MUST
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * respond with a status of 304 (Not Modified).
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * A date later than the server's current request time is invalid.
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek apr_time_t ims = ap_parseHTTPdate(if_modified_since);
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek if ((ims >= mtime) && (ims <= r->request_time)) {
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * Construct an entity tag (ETag) from resource information. If it's a real
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * file, build in some of the file characteristics. If the modification time
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * is newer than (request-time minus 1 second), mark the ETag as weak - it
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * could be modified again in as short an interval. We rationalize the
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * modification time we're given to keep it from being in the future.
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub HrozekAP_DECLARE(char *) ap_make_etag(request_rec *r, int force_weak)
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * Make an ETag header out of various pieces of information. We use
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * the last-modified date and, if we have a real file, the
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * length and inode number - note that this doesn't have to match
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * the content-length (i.e. includes), it just has to be unique
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * for the file.
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * If the request was made within a second of the last-modified date,
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * we send a weak tag instead of a strong one, since it could
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * be modified again later in the second, and the validation
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * would be incorrect.
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek weak = ((r->request_time - r->mtime > APR_USEC_PER_SEC)
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek (unsigned long) r->mtime);
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek etag = apr_psprintf(r->pool, "%s\"%lx\"", weak,
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek (unsigned long) r->mtime);
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek /* If we have a variant list validator (vlv) due to the
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * response being negotiated, then we create a structured
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * entity tag which merges the variant etag with the variant
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * list validator (vlv). This merging makes revalidation
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * somewhat safer, ensures that caches which can deal with
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * Vary will (eventually) be updated if the set of variants is
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * changed, and is also a protocol requirement for transparent
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * content negotiation.
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek /* if the variant list validator is weak, we make the whole
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * structured etag weak. If we would not, then clients could
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * have problems merging range responses if we have different
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * variants with the same non-globally-unique strong etag.
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek /* merge variant_etag and vlv into a structured etag */
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek etag = apr_pstrcat(r->pool, variant_etag, ";", vlv, NULL);
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * This function sets the Last-Modified output header field to the value
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * of the mtime field in the request structure - rationalized to keep it from
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * being in the future.
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub HrozekAP_DECLARE(void) ap_set_last_modified(request_rec *r)
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek apr_time_t mod_time = ap_rationalize_mtime(r, r->mtime);
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek char *datestr = apr_palloc(r->pool, APR_RFC822_DATE_LEN);
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek apr_table_setn(r->headers_out, "Last-Modified", datestr);
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek/* Get the method number associated with the given string, assumed to
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * contain an HTTP method. Returns M_INVALID if not recognized.
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * This is the first step toward placing method names in a configurable
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * list. Hopefully it (and other routines) can eventually be moved to
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * something like a mod_http_methods.c, complete with config stuff.
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub HrozekAP_DECLARE(int) ap_method_number_of(const char *method)
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek return M_GET; /* see header_only in request_rec */
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * Turn a known method number into a name. Doesn't work for
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * extension methods, obviously.
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub HrozekAP_DECLARE(const char *) ap_method_name_of(int methnum)
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek static const char *AP_HTTP_METHODS[METHODS] = { NULL };
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * This is ugly, but the previous incantation made Windows C
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * varf. I'm not even sure it was ANSI C. However, ugly as it
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * is, this works, and we only have to do it once.
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * Since we're using symbolic names, make sure we only do
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * this once by forcing a value into the first slot IFF it's
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * still NULL.
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek if ((methnum == M_INVALID) || (methnum >= METHODS)) {
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek enum {WANT_HDR /* must have value zero */, WANT_BODY, WANT_TRL} state;
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozekstatic long get_chunk_size(char *);
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozekapr_status_t ap_dechunk_filter(ap_filter_t *f, apr_bucket_brigade *bb,
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek const char *buf;
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(struct dechunk_ctx));
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek /* Time to read another chunk header or trailer... ap_http_filter() is
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * the next filter in line and it knows how to return a brigade with
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek if ((rv = ap_getline(line, sizeof(line), f->r, 0)) < 0) {
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek /* XXX sanity check end chunk here */
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek /* bad trailer */
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek if (ctx->chunk_size == 0) { /* we just finished the last chunk? */
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek /* append eos bucket and get out */
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek ap_assert(ctx->state == WANT_HDR || ctx->state == WANT_TRL);
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek /* Tell ap_http_filter() how many bytes to deliver. */
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek f->c->remain = ctx->chunk_size - ctx->bytes_delivered;
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek if ((rv = ap_get_brigade(f->next, bb, mode)) != APR_SUCCESS) {
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek /* Walk through the body, accounting for bytes, and removing an eos bucket if
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek * ap_http_filter() delivered the entire chunk.
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek while (b != APR_BRIGADE_SENTINEL(bb) && !APR_BUCKET_IS_EOS(b)) {
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozek AP_DEBUG_ASSERT(len <= ctx->chunk_size - ctx->bytes_delivered);
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozektypedef struct http_filter_ctx {
cc84fd46f356c4a36a721ab135a33ec77c93e34dJakub Hrozekapr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b, ap_input_mode_t mode)
apr_bucket *e;
char *buff;
char *pos;
if (!ctx) {
apr_bucket *e;
const char *str;
e = NULL;
return APR_EOF;
const char *c = str;
if (*c == ASCII_LF)
else return APR_SUCCESS;
return rv;
if (f->c->remain) {
const char *ignore;
return rv;
if (len) {
f->c->remain = 0;
APR_BRIGADE_INSERT_TAIL(b, e);
old = e;
e = APR_BUCKET_NEXT(e);
if (f->c->remain == 0) {
return APR_SUCCESS;
return rv;
APR_BRIGADE_INSERT_TAIL(b, e);
return APR_SUCCESS;
APR_BRIGADE_INSERT_TAIL(b, e);
return APR_SUCCESS;
char *pos = s;
char *last_char;
char *beyond_buff = s + n;
const char *temp;
int retval;
int total = 0;
int looking_ahead = 0;
apr_bucket *e;
if (APR_BRIGADE_EMPTY(b)) {
APR_BRIGADE_EMPTY(b)) {
e = APR_BRIGADE_FIRST(b);
if (e->length == 0) {
/* no LF yet...character mode client (telnet)...keep going
return total;
* This will prevent "http://www.wherever.com/..\..\/" from
const char *ll = l;
const char *uri;
int len;
char *value;
char *copy;
int len;
int fields_read = 0;
++value;
request_rec *r;
apr_pool_t *p;
const char *expect;
int access_status;
r->pool = p;
r->read_length = 0;
if (!read_request_line(r)) {
ap_send_error_response(r, 0);
return NULL;
if (!r->assbackwards) {
get_mime_headers(r);
ap_send_error_response(r, 0);
if (r->header_only) {
r->uri);
r->header_only = 0;
ap_send_error_response(r, 0);
ap_send_error_response(r, 0);
ap_send_error_response(r, 0);
(void) ap_discard_request_body(r);
return NULL;
apr_bucket *b;
b = apr_bucket_eos_create();
NULL));
return DECLINED;
if (!ap_auth_name(r)) {
return HTTP_INTERNAL_SERVER_ERROR;
if (!auth_line) {
return HTTP_UNAUTHORIZED;
return HTTP_UNAUTHORIZED;
auth_line++;
*pw = t;
return OK;
* The number of status lines must equal the value of RESPONSE_CODES (httpd.h)
#ifdef UTS21
int i, pos;
return LEVEL_500;
return pos;
typedef struct header_struct {
* In other words, don't change this one without checking table_do in alloc.c.
char *headfield;
const char **protocol)
if (r->assbackwards)
if (!r->status_line)
if (r->proxyreq
char *tmp;
if (r->assbackwards)
const char *protocol;
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 *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;
ap_set_keepalive(r);
r->bytes_sent = 0;
return OK;
* consisting of comma/space-separated tokens.
char *start;
char **strpp;
start = e;
++i, ++strpp) {
struct content_length_ctx {
int compute_len;
request_rec *r = f->r;
apr_bucket *e;
int send_it = 0;
APR_BRIGADE_FOREACH(e, b) {
const char *ignored;
return rv;
if (!send_it) {
return APR_SUCCESS;
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;
typedef struct header_filter_cts {
int headers_sent;
request_rec *r = f->r;
const char *clheader;
const char *protocol;
apr_bucket *e;
if (!ctx) {
return OK;
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) {
while (r->next) {
r = r->next;
if (!r->eos_sent) {
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;
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;
apr_bucket *b;
return rv;
#if APR_HAS_MMAP
apr_bucket *b;
char *buf;
char *cur;
if (nbyte != 0) {
ap_filter_t *f;
if (len == 0)
return APR_SUCCESS;
if (f == NULL) {
return APR_SUCCESS;
return status;
return APR_SUCCESS;
char c2 = (char)c;
return len;
return nbyte;
return written;
if (s == NULL)
return written;
apr_bucket *b;
b = apr_bucket_flush_create();
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);
if (recursive_error) {
(const request_rec *r),(r),0)