From: Graham Leggett Date: Fri, 19 Apr 2002 11:15:19 +0000 (+0000) Subject: Correct proxy to be able to handle the unexpected 100-continue X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2c3fee1faf9afbb893aa0cac2257df63fb9e7a66;p=thirdparty%2Fapache%2Fhttpd.git Correct proxy to be able to handle the unexpected 100-continue 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 --- diff --git a/src/CHANGES b/src/CHANGES index cd7814164b9..eada8612136 100644 --- a/src/CHANGES +++ b/src/CHANGES @@ -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 , Brian Bothwell diff --git a/src/modules/proxy/proxy_http.c b/src/modules/proxy/proxy_http.c index 6d3788a1d2b..65f4207a327 100644 --- a/src/modules/proxy/proxy_http.c +++ b/src/modules/proxy/proxy_http.c @@ -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); diff --git a/src/modules/proxy/proxy_util.c b/src/modules/proxy/proxy_util.c index 712d62b7e65..402b1e4ac56 100644 --- a/src/modules/proxy/proxy_util.c +++ b/src/modules/proxy/proxy_util.c @@ -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;