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;
smime.p7s
Description: S/MIME Cryptographic Signature
