]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
Correct proxy to be able to handle the unexpected 100-continue
authorGraham Leggett <minfrin@apache.org>
Fri, 19 Apr 2002 11:15:19 +0000 (11:15 +0000)
committerGraham Leggett <minfrin@apache.org>
Fri, 19 Apr 2002 11:15:19 +0000 (11:15 +0000)
reponses sent during PUT or POST requests. (ie Hotmail works now)
PR:
Obtained from:
Submitted by:
Reviewed by:

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/1.3.x@94707 13f79535-47bb-0310-9956-ffa450edef68

src/CHANGES
src/modules/proxy/proxy_http.c
src/modules/proxy/proxy_util.c

index cd7814164b92582668b12a972c98a8b56b38cddb..eada8612136442e86f70043aa536f1a000926840 100644 (file)
@@ -1,5 +1,8 @@
 Changes with Apache 1.3.25
 
+  *) Correct proxy to be able to handle the unexpected 100-continue
+     reponses sent during PUT or POST requests. [Graham Leggett]
+
   *) Correct a timeout problem within proxy which would force long
      or slow POST requests to close after 300 seconds.
      [Martin Lichtin <martin@lichtin.net>, Brian Bothwell
index 6d3788a1d2b2aea88026fb811dc5c752263891a1..65f4207a327f6af7ab57d88fad9b51f02b3a6699 100644 (file)
@@ -168,6 +168,7 @@ int ap_proxy_http_handler(request_rec *r, cache_req *c, char *url,
     char *destportstr = NULL;
     const char *urlptr = NULL;
     const char *datestr, *urlstr;
+    int result, major, minor;
     const char *content_length;
 
     void *sconf = r->server->module_config;
@@ -386,70 +387,76 @@ int ap_proxy_http_handler(request_rec *r, cache_req *c, char *url,
     /* 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.
+    /* read the request data, and pass it to the backend.
+     * we 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.
      */
-    ap_hard_timeout("proxy receive", r);
-
-    len = ap_bgets(buffer, sizeof buffer - 1, f);
-    if (len == -1) {
-        ap_bclose(f);
+    {
+        /* 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);
-        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);
+
+
+        /* 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);
-        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/#.# ###*")) {
-        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;
+        /* trap any errors */
+        if (result != OK) {
+            ap_bclose(f);
+            return result;
         }
 
-        /* If not an HTTP/1 message or if the status line was > 8192 bytes */
-        if (buffer[5] != '1' || buffer[len - 1] != '\n') {
-            ap_bclose(f);
+        /* 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';
+    }
+
 
-        buffer[12] = '\0';
-        r->status = atoi(&buffer[9]);
-        buffer[12] = ' ';
-        r->status_line = ap_pstrdup(p, &buffer[9]);
+    /*
+     * 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);
+
+    /*
+     * 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)",
@@ -489,17 +496,15 @@ int ap_proxy_http_handler(request_rec *r, cache_req *c, char *url,
         ap_proxy_clear_connection(p, resp_hdrs);
 
         content_length = ap_table_get(resp_hdrs, "Content-Length");
-        if (content_length != NULL)
+        if (content_length != NULL) {
             c->len = strtol(content_length, NULL, 10);
+        }
 
         /* Now add out bound headers set by other modules */
         resp_hdrs = ap_overlay_tables(r->pool, r->err_headers_out, resp_hdrs);
     }
     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);
index 712d62b7e65552a06b8c50e89c06ff0030ab28aa..402b1e4ac5600062e70a85ed96b30961f3a0f8ef 100644 (file)
@@ -1554,6 +1554,85 @@ int ap_proxy_table_replace(table *base, table *overlay)
     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;