Apache and HTTP/1.1

Ben Laurie writes:
>Since no-one else from the Apache Group has mentioned it, I thought I'd better.
>I know that there's various things we are supposed to be looking at, but most
>of the Apache Group are spending most of their spare time trying to get 1.2
>out, and encountering some significant difficulties with HTTP/1.1. So, don't
>take silence from AG to indicate lack of interest! No doubt we'll be back soon!

Ummm, we never left.  Aside from the problems I already described
a few months ago, the only new HTTP/1.1 problem we have is with pipelining
and persistent connections and greedy/lazy browsers.  I haven't mentioned
it yet because we haven't nailed down all the causes, but here is a
summary.  I'm sure the other Apache people will chime-in if I've forgotten
something.  A more detailed discussion is at

   http://www.apache.org/docs/misc/fin_wait_2.html

Basically, what we are seeing is a lot of connections being left in
FIN_WAIT_2 state after the server has closed the connection (for whatever
reason, but usually due to a pre-request timeout).  The one known
source of the FIN_WAIT_2 state (wherein the server's TCB is waiting for
the client to send a FIN or RST) is due to current clients with keep-alive
enabled -- they open multiple connections to the server and then leave
them open, never checking to see that the connection has been closed,
and thus never closing their side of the connection.  That is a generic
problem with client/server applications, so the only real solution is
to either require all clients to be good (not likely) or include a
FIN_WAIT_2 timeout within the TCP implementation of the OS.

The other source of FIN_WAIT_2 states is still unknown, but is somehow
connected to the way we are lingering on a half-closed connection
in order to ensure the client has time to ACK the last response sent
to the server, and the server has enough time to receive the ACK before
it fully-closes the connection.  This lingering behavior exists to avoid
the TCP buffer reset problem discussed in several notes within the
HTTP/1.1 specification.  However, I believe that the cause of this
problem is either in our implementation or that of the OS implementation
of the shutdown() system call, and therefore not a problem with HTTP/1.1
per se.

One thing I should note is that we have not yet attempted to complete an
HTTP/1.1 proxy.  Our current proxy, which is a bit of a bastard child
for people who were desperately seeking an alternative to the old
CERN proxy, is suffering from a lack of volunteers to improve it.
If anybody reading this is using the Apache proxy and would like to
help make it HTTP/1.1-compliant, please let me know.

Oh yeah, one other thing... The BNF for the Allow header is 1#Method,
which is a problem if a resource is set to disallow all methods,
as might be the case for a resource made temporarily unavailable.
The simple solution is to change it to #Method, wherein no value
means no methods are allowed.

Ah, and a third thing (funny how things get remembered) ...
The order and precedence in which the various If-* conditional
header fields are checked is important, and I don't recall it
being detailed in the specification.  Anyway, the order should be
as follows:

===============================
    /* If an If-Match request-header field was given and
     * if our ETag does not match any of the entity tags in that field
     * and the field value is not "*" (meaning match anything), then
     *    respond with a status of 412 (Precondition Failed).
     */

    if (if_match) {
        if ((if_match[0] != '*') && !find_token(r->pool, if_match, etag))
            return HTTP_PRECONDITION_FAILED;
    }

    /* Else if a valid If-Unmodified-Since request-header field was given
     * and the requested resource has been modified since the time
     * specified in this field, then the server MUST
     *    respond with a status of 412 (Precondition Failed).
     */

    else if (if_unmodified) {
        time_t ius = parseHTTPdate(if_unmodified);

        if ((ius != BAD_DATE) && (mtime > ius))
            return HTTP_PRECONDITION_FAILED;
    }

    /* If an If-None-Match request-header field was given and
     * if our ETag matches any of the entity tags in that field or
     * if the field value is "*" (meaning match anything), then
     *    if the request method was GET or HEAD, the server SHOULD
     *       respond with a 304 (Not Modified) response.
     *    For all other request methods, the server MUST
     *       respond with a status of 412 (Precondition Failed).
     */

    if (if_nonematch) {
        if ((if_nonematch[0] == '*') || find_token(r->pool,if_nonematch,etag))
            return (r->method_number == M_GET) ? HTTP_NOT_MODIFIED
                                               : HTTP_PRECONDITION_FAILED;
    }

    /* Else if a valid If-Modified-Since request-header field was given
     * and it is a GET or HEAD request
     * and the requested resource has not been modified since the time
     * specified in this field, then the server MUST
     *    respond with a status of 304 (Not Modified).
     * A date later than the server's current request time is invalid.
     */

    else if (if_modified_since && (r->method_number == M_GET)) {
        time_t ims = parseHTTPdate(if_modified_since);

        if ((ims >= mtime) && (ims <= r->request_time))
            return HTTP_NOT_MODIFIED;
    }
===============================

The order is important because ETags have greater accuracy than pure
modification dates (if implemented correctly), and thus should be
given precedence in handling the conditionals.

 ...Roy T. Fielding
    Department of Information & Computer Science    (fielding@ics.uci.edu)
    University of California, Irvine, CA 92697-3425    fax:+1(714)824-4056
    http://www.ics.uci.edu/~fielding/

Received on Wednesday, 19 February 1997 10:38:54 UTC