Hi all,

One piece of the HTTP/1.1 spec that was not handled in the original 1.1
patch for proxy was 100-continue handling. As a result of this, certain
sites (such as Hotmail) break when you access them.

This patch adds 100-continue support. It has been tested against
HTTP/1.1 servers and seems to work for me. I tried to test it against
the HTTP/1.0 Tomcat connector, but it turns out the connector is broken
:(

Can someone with access to a HTTP/1.0 server test it for me?

Regards,
Graham
-- 
-----------------------------------------
[EMAIL PROTECTED]                "There's a moon
                                        over Bourbon Street
                                                tonight..."
diff -u -r --exclude=CVS 
/home/minfrin/src/apache/pristine/apache-1.3/src/main/http_main.c src/main/http_main.c
--- /home/minfrin/src/apache/pristine/apache-1.3/src/main/http_main.c   Sun Mar 24 
11:59:48 2002
+++ src/main/http_main.c        Wed Apr 10 09:50:37 2002
@@ -1714,6 +1714,20 @@
     ap_set_callback_and_alarm(timeout, r->server->timeout);
 }
 
+/* a short timeout for handling possible 100-continue responses
+ * in proxy.
+ */
+#define SHORT_TIMEOUT  5
+
+API_EXPORT(void) ap_soft_short_timeout(char *name)
+{
+#ifdef NETWARE
+    get_tsd
+#endif
+    timeout_name = name;
+    ap_set_callback_and_alarm(timeout, SHORT_TIMEOUT);
+}
+
 API_EXPORT(void) ap_kill_timeout(request_rec *dummy)
 {
 #ifdef NETWARE
diff -u -r --exclude=CVS 
/home/minfrin/src/apache/pristine/apache-1.3/src/modules/proxy/proxy_http.c 
src/modules/proxy/proxy_http.c
--- /home/minfrin/src/apache/pristine/apache-1.3/src/modules/proxy/proxy_http.c Tue 
Apr  9 10:41:02 2002
+++ src/modules/proxy/proxy_http.c      Wed Apr 10 10:18:25 2002
@@ -168,6 +168,7 @@
     char *destportstr = NULL;
     const char *urlptr = NULL;
     const char *datestr, *urlstr;
+    int result, major, minor;
 
     void *sconf = r->server->module_config;
     proxy_server_conf *conf =
@@ -385,70 +386,140 @@
     /* the obligatory empty line to mark the end of the headers */
     ap_bputs(CRLF, f);
 
-    /* send the request data, if any. */
-    if (ap_should_client_block(r)) {
-        while ((i = ap_get_client_block(r, buffer, sizeof buffer)) > 0) {
-            ap_reset_timeout(r);
-            ap_bwrite(f, buffer, i);
-        }
-    }
+    /* and flush the above away */
     ap_bflush(f);
+
+    /* and kill the send timeout */
     ap_kill_timeout(r);
 
 
-    /*
-     * Right - now it's time to listen for a response.
+    /* at this point, if an Expect header was included in the request,
+     * we need to listen for a possible 100-continue response before
+     * we send the request data.
+     * We will get one of three responses from the backend server:
+     * - A 100-continue response: return the 100-continue back up to the
+     *                            client, read and pass on the request data,
+     *                            and listen for the second response.
+     *                            (AKA ap_should_client_block())
+     * - Another response: ignore the request data, return the response
+     *                     to the client, and switch off keepalive (to be safe).
+     * - A delay: if we hear nothing for a specified period, assume we're
+     *            connecting to an HTTP/1.0 server and pretend we received a
+     *            100-continue response, and start reading and passing
+     *            the request data.
      */
-    ap_hard_timeout("proxy receive", r);
+    if (r->expecting_100) {
 
-    len = ap_bgets(buffer, sizeof buffer - 1, f);
-    if (len == -1) {
-        ap_bclose(f);
-        ap_kill_timeout(r);
-        ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
-                      "ap_bgets() - proxy receive - Error reading from remote server 
%s (length %d)",
-                      proxyhost ? proxyhost : desthost, len);
-        return ap_proxyerror(r, HTTP_BAD_GATEWAY,
-                             "Error reading from remote server");
-    }
-    else if (len == 0) {
-        ap_bclose(f);
+        ap_soft_short_timeout("proxy waiting for 100-continue");
+        result = ap_proxy_read_response_line(f, r, buffer, sizeof(buffer)-1, 0, 
+&backasswards, &major, &minor);
         ap_kill_timeout(r);
-        return ap_proxyerror(r, HTTP_BAD_GATEWAY,
-                             "Document contains no data");
+
+        /* trap any errors */
+        if (result != OK) {
+            ap_bclose(f);
+            return result;
+        }
+
+        /* timeout/100-continue happened */
+        if (r->status == 100) {
+
+            /* send the request data, if any. */
+            ap_hard_timeout("proxy receive request data", r);
+            if (ap_should_client_block(r)) {
+                while ((i = ap_get_client_block(r, buffer, sizeof buffer)) > 0) {
+                    ap_reset_timeout(r);
+                    ap_bwrite(f, buffer, i);
+                }
+            }
+            ap_bflush(f);
+            ap_kill_timeout(r);
+
+
+            /* then, read a response line */
+            ap_hard_timeout("proxy receive response status line", r);
+            result = ap_proxy_read_response_line(f, r, buffer, sizeof(buffer)-1, 1, 
+&backasswards, &major, &minor);
+            ap_kill_timeout(r);
+
+            /* trap any errors */
+            if (result != OK) {
+                ap_bclose(f);
+                return result;
+            }
+
+        }
+
+        /* otherwise another status code happened, don't read or send any request
+         * data and switch off keepalives.
+         */
+        else {
+            r->connection->keepalive = -1;
+        }
     }
 
-    /*
-     * Is it an HTTP/1 response? Do some sanity checks on the response. (This
-     * is buggy if we ever see an HTTP/1.10)
+    /* if no 100-continue behavior is needed, handle the response in the normal
+     * way.
+     * we still might encounter a stray 100-continue reponse from a PUT or POST,
+     * if this happens we ignore the 100 continue status line and read the
+     * response again.
      */
-    if (ap_checkmask(buffer, "HTTP/#.# ###*")) {
-        int major, minor;
-        if (2 != sscanf(buffer, "HTTP/%u.%u", &major, &minor)) {
-            /* if no response, default to HTTP/1.1 - is this correct? */
-            major = 1;
-            minor = 1;
+    else {
+        /* send the request data, if any. */
+        ap_hard_timeout("proxy receive request data", r);
+        if (ap_should_client_block(r)) {
+            while ((i = ap_get_client_block(r, buffer, sizeof buffer)) > 0) {
+                ap_reset_timeout(r);
+                ap_bwrite(f, buffer, i);
+            }
         }
+        ap_bflush(f);
+        ap_kill_timeout(r);
+
+
+        /* then, read a response line */
+        ap_hard_timeout("proxy receive response status line", r);
+        result = ap_proxy_read_response_line(f, r, buffer, sizeof(buffer)-1, 1, 
+&backasswards, &major, &minor);
+        ap_kill_timeout(r);
 
-        /* If not an HTTP/1 message or if the status line was > 8192 bytes */
-        if (buffer[5] != '1' || buffer[len - 1] != '\n') {
+        /* trap any errors */
+        if (result != OK) {
             ap_bclose(f);
+            return result;
+        }
+
+        /* if this response was 100-continue, a stray response has been caught.
+         * read the line again for the real response
+         */
+        if (r->status == 100) {
+            ap_hard_timeout("proxy receive response status line", r);
+            result = ap_proxy_read_response_line(f, r, buffer, sizeof(buffer)-1, 1, 
+&backasswards, &major, &minor);
             ap_kill_timeout(r);
-            return HTTP_BAD_GATEWAY;
+
+            /* trap any errors */
+            if (result != OK) {
+                ap_bclose(f);
+                return result;
+            }
         }
-        backasswards = 0;
-        buffer[--len] = '\0';
+    }
+
+
+    /*
+     * We have our response status line from the convoluted code above,
+     * now we read the headers to continue.
+     */
+    ap_hard_timeout("proxy receive response headers", r);
 
-        buffer[12] = '\0';
-        r->status = atoi(&buffer[9]);
-        buffer[12] = ' ';
-        r->status_line = ap_pstrdup(p, &buffer[9]);
+    /*
+     * Is it an HTTP/1 response? Do some sanity checks on the response. (This
+     * is buggy if we ever see an HTTP/1.10)
+     */
+    if (backasswards == 0) {
 
         /* read the response headers. */
         /* N.B. for HTTP/1.0 clients, we have to fold line-wrapped headers */
         /* Also, take care with headers with multiple occurences. */
 
-        resp_hdrs = ap_proxy_read_headers(r, buffer, HUGE_STRING_LEN, f);
+        resp_hdrs = ap_proxy_read_headers(r, buffer, sizeof(buffer), f);
         if (resp_hdrs == NULL) {
             ap_log_error(APLOG_MARK, APLOG_WARNING | APLOG_NOERRNO, r->server,
                          "proxy: Bad HTTP/%d.%d header returned by %s (%s)",
@@ -491,9 +562,6 @@
     }
     else {
         /* an http/0.9 response */
-        backasswards = 1;
-        r->status = 200;
-        r->status_line = "200 OK";
 
         /* no headers */
         resp_hdrs = ap_make_table(p, 20);
diff -u -r --exclude=CVS 
/home/minfrin/src/apache/pristine/apache-1.3/src/modules/proxy/proxy_util.c 
src/modules/proxy/proxy_util.c
--- /home/minfrin/src/apache/pristine/apache-1.3/src/modules/proxy/proxy_util.c Tue 
Apr  9 10:41:07 2002
+++ src/modules/proxy/proxy_util.c      Wed Apr 10 10:29:59 2002
@@ -1529,6 +1529,85 @@
     return q;
 }
 
+/* read the response line
+ * This function reads a single line of response from the server,
+ * and returns a status code.
+ * The timeout flag if non-zero means we return BAD_GATEWAY on timeout
+ * errors, otherwise we silently return to handle 100-continue.
+ * It also populates the request_rec with the resultant status, and
+ * returns backasswards status (HTTP/0.9).
+ */
+int ap_proxy_read_response_line(BUFF *f, request_rec *r, char *buffer, int size, int 
+timeout, int *backasswards, int *major, int *minor) {
+
+    long len;
+
+    len = ap_getline(buffer, size-1, f, 0);
+    if (len == -1) {
+        if (!timeout && errno == ETIMEDOUT) {
+            /* emulate 100-continue */
+            r->status = 100;
+            r->status_line = "100 Continue";
+            return OK;
+        }
+        ap_bclose(f);
+        ap_kill_timeout(r);
+        return ap_proxyerror(r, HTTP_BAD_GATEWAY,
+                             "Error reading from remote server");
+    }
+    else if (len == 0) {
+        ap_bclose(f);
+        ap_kill_timeout(r);
+        return ap_proxyerror(r, HTTP_BAD_GATEWAY,
+                             "Document contains no data");
+    }
+
+    /*
+     * Is it an HTTP/1 response? Do some sanity checks on the response. (This
+     * is buggy if we ever see an HTTP/1.10)
+     */
+    if (ap_checkmask(buffer, "HTTP/#.# ###*")) {
+
+        if (2 != sscanf(buffer, "HTTP/%u.%u", major, minor)) {
+            /* if no response, default to HTTP/1.1 - is this correct? */
+            *major = 1;
+            *minor = 1;
+        }
+
+        /* If not an HTTP/1 message */
+        if (*major < 1) {
+            ap_bclose(f);
+            ap_kill_timeout(r);
+            return HTTP_BAD_GATEWAY;
+        }
+        *backasswards = 0;
+
+        buffer[12] = '\0';
+        r->status = atoi(&buffer[9]);
+        buffer[12] = ' ';
+        r->status_line = ap_pstrdup(r->pool, &buffer[9]);
+
+        /* if the response was 100 continue, soak up any headers */
+        if (r->status == 100) {
+            ap_proxy_read_headers(r, buffer, size, f);
+        }
+
+    }
+    else {
+
+        /* an http/0.9 response */
+        *backasswards = 1;
+        r->status = 200;
+        r->status_line = "200 OK";
+        *major = 0;
+        *minor = 9;
+
+    }
+
+    return OK;
+
+}
+
+
 #if defined WIN32
 
 static DWORD tls_index;

Attachment: smime.p7s
Description: S/MIME Cryptographic Signature

Reply via email to