fielding 96/11/25 03:22:06
Modified: src httpd.h http_config.h http_protocol.h http_protocol.c mod_cgi.c mod_fastcgi.c util_script.c src/modules/proxy mod_proxy.c proxy_http.c Log: Added ability for modules to select the policy for reading a request message body as part of the call to setup_client_block(). The code can now reject a message body, require Content-Length if there is a body, transparently dechunk a chunked body, or pass the chunks to the module. Added read_body (to remember the policy) and read_length (to keep track of the amount of bytes read) to the request_rec structure. Added code to force an end to keepalive if the client input is errored, since this might not be caught by a CGI script and might result in a hanging connection til timeout, or a double response to the remaining input as if it were an additional request. Fixed a bug in get_client_block not checking for a negative (error) result from the read routines, and made the corresponding changes to the calls from mod_cgi, mod_fastcgi, and mod_proxy. Removed the method-dependent code from the three module input routines, since the question of whether or not the request message contains a body is solely determined by the presence of a non-zero Content-Length or Transfer-Encoding field. This is an interface change, so the API version has been bumped. Fixed a bug in outgoing CGI responses, due to not checking for a script sending Content-Length or Transfer-Encoding, that was causing keepalive to end in 1.1.1 and an invalid combination of Content-Length and Transfer-Encoding in the current version. Fixed a bug in the proxy in regards to how it was clearing the Connection header field and fields indicated by it. Reviewed by: Randy Terbush, Brian Behlendorf, Jim Jagielski Revision Changes Path 1.63 +14 -1 apache/src/httpd.h Index: httpd.h =================================================================== RCS file: /export/home/cvs/apache/src/httpd.h,v retrieving revision 1.62 retrieving revision 1.63 diff -C3 -r1.62 -r1.63 *** httpd.h 1996/11/23 23:18:52 1.62 --- httpd.h 1996/11/25 11:21:58 1.63 *************** *** 341,346 **** --- 341,357 ---- #define LF 10 #define CR 13 + /* Possible values for request_rec.read_body (set by handling module): + * REQUEST_NO_BODY Send 413 error if message has any body + * REQUEST_CHUNKED_ERROR Send 411 error if body without Content-Length + * REQUEST_CHUNKED_DECHUNK If chunked, remove the chunks for me. + * REQUEST_CHUNKED_PASS Pass the chunks to me without removal. + */ + #define REQUEST_NO_BODY 0 + #define REQUEST_CHUNKED_ERROR 1 + #define REQUEST_CHUNKED_DECHUNK 2 + #define REQUEST_CHUNKED_PASS 3 + /* Things which may vary per file-lookup WITHIN a request --- * e.g., state of MIME config. Basically, the name of an object, info * about the object, and any other info we may ahve which may need to *************** *** 426,432 **** char *range; /* The Range: header */ long clength; /* The "real" content length */ ! long int remaining; /* bytes left to read */ int read_chunked; /* reading chunked transfer-coding */ /* MIME header environments, in and out. Also, an array containing --- 437,445 ---- char *range; /* The Range: header */ long clength; /* The "real" content length */ ! long remaining; /* bytes left to read */ ! long read_length; /* bytes that have been read */ ! int read_body; /* how the request body should be read */ int read_chunked; /* reading chunked transfer-coding */ /* MIME header environments, in and out. Also, an array containing 1.22 +1 -1 apache/src/http_config.h Index: http_config.h =================================================================== RCS file: /export/home/cvs/apache/src/http_config.h,v retrieving revision 1.21 retrieving revision 1.22 diff -C3 -r1.21 -r1.22 *** http_config.h 1996/11/12 05:22:03 1.21 --- http_config.h 1996/11/25 11:21:58 1.22 *************** *** 225,231 **** * handle it back-compatibly, or at least signal an error). */ ! #define MODULE_MAGIC_NUMBER 19961007 #define STANDARD_MODULE_STUFF MODULE_MAGIC_NUMBER, -1, __FILE__, NULL /* Generic accessors for other modules to get at their own module-specific --- 225,231 ---- * handle it back-compatibly, or at least signal an error). */ ! #define MODULE_MAGIC_NUMBER 19961125 #define STANDARD_MODULE_STUFF MODULE_MAGIC_NUMBER, -1, __FILE__, NULL /* Generic accessors for other modules to get at their own module-specific 1.13 +1 -1 apache/src/http_protocol.h Index: http_protocol.h =================================================================== RCS file: /export/home/cvs/apache/src/http_protocol.h,v retrieving revision 1.12 retrieving revision 1.13 diff -C3 -r1.12 -r1.13 *** http_protocol.h 1996/10/20 18:03:32 1.12 --- http_protocol.h 1996/11/25 11:21:59 1.13 *************** *** 124,130 **** /* Reading a block of data from the client connection (e.g., POST arg) */ ! int setup_client_block (request_rec *r); int should_client_block (request_rec *r); long get_client_block (request_rec *r, char *buffer, int bufsiz); --- 124,130 ---- /* Reading a block of data from the client connection (e.g., POST arg) */ ! int setup_client_block (request_rec *r, int read_policy); int should_client_block (request_rec *r); long get_client_block (request_rec *r, char *buffer, int bufsiz); 1.77 +214 -90 apache/src/http_protocol.c Index: http_protocol.c =================================================================== RCS file: /export/home/cvs/apache/src/http_protocol.c,v retrieving revision 1.76 retrieving revision 1.77 diff -C3 -r1.76 -r1.77 *** http_protocol.c 1996/11/23 20:51:45 1.76 --- http_protocol.c 1996/11/25 11:21:59 1.77 *************** *** 206,218 **** int set_keepalive(request_rec *r) { ! char *conn = table_get (r->headers_in, "Connection"); ! char *length = table_get (r->headers_out, "Content-length"); int ka_sent; ! if ((r->server->keep_alive > r->connection->keepalives) && (r->server->keep_alive_timeout > 0) && ! (r->header_only || length || ((r->proto_num >= 1001) && (r->byterange > 1 || (r->chunked = 1)))) && (!find_token(r->pool, conn, "close")) && ((ka_sent = find_token(r->pool, conn, "keep-alive")) || --- 206,221 ---- int set_keepalive(request_rec *r) { ! char *conn = table_get(r->headers_in, "Connection"); ! char *length = table_get(r->headers_out, "Content-Length"); ! char *tenc = table_get(r->headers_out, "Transfer-Encoding"); int ka_sent; ! if (r->connection->keepalive == -1) /* Did we get bad input? */ ! r->connection->keepalive = 0; ! else if ((r->server->keep_alive > r->connection->keepalives) && (r->server->keep_alive_timeout > 0) && ! (r->header_only || length || tenc || ((r->proto_num >= 1001) && (r->byterange > 1 || (r->chunked = 1)))) && (!find_token(r->pool, conn, "close")) && ((ka_sent = find_token(r->pool, conn, "keep-alive")) || *************** *** 633,638 **** --- 636,644 ---- r->per_dir_config = r->server->lookup_defaults; /* For now. */ r->sent_bodyct = 0; /* bytect isn't for body */ + + r->read_length = 0; + r->read_body = REQUEST_NO_BODY; r->status = HTTP_OK; /* Until further notice. * Only changed by die(), or (bletch!) *************** *** 703,708 **** --- 709,717 ---- rnew->err_headers_out = make_table (rnew->pool, 5); rnew->notes = make_table (rnew->pool, 5); + rnew->read_length = r->read_length; + rnew->read_body = REQUEST_NO_BODY; + rnew->main = (request_rec *)r; } *************** *** 1056,1112 **** } ! /* Here we deal with getting input from the client. This can be in the ! * form of POST or PUT (other methods can be added later), and may be ! * transmitted in either a fixed content-length or via chunked ! * transfer-coding. * * Note that this is more complicated than it was in Apache 1.1 and prior * versions, because chunked support means that the module does less. * * The proper procedure is this: * 1. Call setup_client_block() near the beginning of the request ! * handler. This will set up all the neccessary properties, and ! * will return either OK, or an error code. If the latter, ! * the module should return that error code. * ! * 2. When you are ready to possibly accept input, call should_client_block(). ! * This will tell the module whether or not to read input. If it is 0, ! * the module should assume that the input is of a non-entity type ! * (e.g. a GET request). This step also sends a 100 Continue response ! * to HTTP/1.1 clients, so should not be called until the module ! * is *definitely* ready to read content. (otherwise, the point of the ! * 100 response is defeated). Never call this function more than once. * ! * 3. Finally, call get_client_block in a loop. Pass it a buffer and its ! * size. It will put data into the buffer (not neccessarily the full ! * buffer, in the case of chunked inputs), and return the length of ! * the input block. When it is done reading, it will return 0. * */ ! int setup_client_block (request_rec *r) { ! char *tenc = table_get (r->headers_in, "Transfer-Encoding"); ! char *lenp = table_get (r->headers_in, "Content-length"); ! if ((r->method_number != M_POST) && (r->method_number != M_PUT)) ! return OK; if (tenc) { ! if (strcasecmp(tenc, "chunked")) { ! log_printf(r->server, "Unknown Transfer-Encoding %s", tenc); ! return BAD_REQUEST; ! } ! r->read_chunked = 1; ! r->remaining = 0; } ! else { ! if (!lenp) { ! log_reason("POST or PUT without Content-length:", r->filename, r); ! return LENGTH_REQUIRED; ! } ! r->remaining = atol(lenp); } return OK; --- 1065,1147 ---- } ! /* Here we deal with getting the request message body from the client. ! * Whether or not the request contains a body is signaled by the presence ! * of a non-zero Content-Length or by a Transfer-Encoding: chunked. * * Note that this is more complicated than it was in Apache 1.1 and prior * versions, because chunked support means that the module does less. * * The proper procedure is this: + * * 1. Call setup_client_block() near the beginning of the request ! * handler. This will set up all the necessary properties, and will ! * return either OK, or an error code. If the latter, the module should ! * return that error code. The second parameter selects the policy to ! * apply if the request message indicates a body, and how a chunked ! * transfer-coding should be interpreted. Choose one of * ! * REQUEST_NO_BODY Send 413 error if message has any body ! * REQUEST_CHUNKED_ERROR Send 411 error if body without Content-Length ! * REQUEST_CHUNKED_DECHUNK If chunked, remove the chunks for me. ! * REQUEST_CHUNKED_PASS Pass the chunks to me without removal. * ! * In order to use the last two options, the caller MUST provide a buffer ! * large enough to hold a chunk-size line, including any extensions. * + * 2. When you are ready to read a body (if any), call should_client_block(). + * This will tell the module whether or not to read input. If it is 0, + * the module should assume that there is no message body to read. + * This step also sends a 100 Continue response to HTTP/1.1 clients, + * so should not be called until the module is *definitely* ready to + * read content. (otherwise, the point of the 100 response is defeated). + * Never call this function more than once. + * + * 3. Finally, call get_client_block in a loop. Pass it a buffer and its size. + * It will put data into the buffer (not necessarily a full buffer), and + * return the length of the input block. When it is done reading, it will + * return 0 if EOF, or -1 if there was an error. + * If an error occurs on input, we force an end to keepalive. */ ! int setup_client_block (request_rec *r, int read_policy) { ! char *tenc = table_get(r->headers_in, "Transfer-Encoding"); ! char *lenp = table_get(r->headers_in, "Content-length"); ! r->read_body = read_policy; ! r->read_chunked = 0; ! r->remaining = 0; if (tenc) { ! if (strcasecmp(tenc, "chunked")) { ! log_printf(r->server, "Unknown Transfer-Encoding %s", tenc); ! return HTTP_BAD_REQUEST; ! } ! if (r->read_body == REQUEST_CHUNKED_ERROR) { ! log_reason("chunked Transfer-Encoding forbidden", r->uri, r); ! return (lenp) ? HTTP_BAD_REQUEST : HTTP_LENGTH_REQUIRED; ! } ! ! r->read_chunked = 1; } ! else if (lenp) { ! char *pos = lenp; ! ! while (isdigit(*pos) || isspace(*pos)) ++pos; ! if (*pos != '\0') { ! log_printf(r->server, "Invalid Content-Length %s", lenp); ! return HTTP_BAD_REQUEST; ! } ! ! r->remaining = atol(lenp); ! } ! ! if ((r->read_body == REQUEST_NO_BODY) && ! (r->read_chunked || (r->remaining > 0))) { ! log_printf(r->server, "%s with body is not allowed for %s", ! r->method, r->uri); ! return HTTP_REQUEST_ENTITY_TOO_LARGE; } return OK; *************** *** 1114,1124 **** int should_client_block (request_rec *r) { ! /* The following should involve a test of whether the request message ! * included a Content-Length or Transfer-Encoding header field, since ! * methods are supposed to be extensible. However, this'll do for now. ! */ ! if (r->method_number != M_POST && r->method_number != M_PUT) return 0; if (r->proto_num >= 1001) { /* sending 100 Continue interim response */ --- 1149,1155 ---- int should_client_block (request_rec *r) { ! if (!r->read_chunked && (r->remaining <= 0)) return 0; if (r->proto_num >= 1001) { /* sending 100 Continue interim response */ *************** *** 1130,1196 **** return 1; } ! static int rd_chunk_size (BUFF *b) { ! int chunksize = 0; ! int c; ! while ((c = bgetc (b)) != EOF && isxdigit (c)) { int xvalue = 0; ! if (c >= '0' && c <= '9') xvalue = c - '0'; ! else if (c >= 'A' && c <= 'F') xvalue = c - 'A' + 0xa; ! else if (c >= 'a' && c <= 'f') xvalue = c - 'a' + 0xa; chunksize = (chunksize << 4) | xvalue; } ! /* Skip to end of line, bypassing chunk options, if present */ ! ! while (c != '\n' && c != EOF) ! c = bgetc (b); ! ! return (c == EOF) ? -1 : chunksize; } long get_client_block (request_rec *r, char *buffer, int bufsiz) { ! long c, len_read, len_to_read = r->remaining; ! if (!r->read_chunked) { /* Content-length read */ ! if (len_to_read > bufsiz) ! len_to_read = bufsiz; ! len_read = bread(r->connection->client, buffer, len_to_read); ! r->remaining -= len_read; ! return len_read; ! } ! ! /* Handle chunked reading */ ! if (len_to_read == 0) { ! len_to_read = rd_chunk_size(r->connection->client); ! if (len_to_read == 0) { ! /* Read any footers - the module may not notice them, ! * but they're there, and so we read them */ ! get_mime_headers(r); ! return 0; ! } } ! if (len_to_read > bufsiz) { ! r->remaining = len_to_read - bufsiz; ! len_to_read = bufsiz; } ! else ! r->remaining = 0; len_read = bread(r->connection->client, buffer, len_to_read); ! if (r->remaining == 0) { ! /* Read the newline at the end of the chunk ! * (and any other garbage that might be present) */ ! do c = bgetc (r->connection->client); ! while (c != '\n' && c != EOF); } ! return len_read; } long send_fd(FILE *f, request_rec *r) { return send_fd_length(f, r, -1); } --- 1161,1318 ---- return 1; } ! static long get_chunk_size (char *b) { ! long chunksize = 0; ! while (isxdigit(*b)) { int xvalue = 0; ! if (*b >= '0' && *b <= '9') xvalue = *b - '0'; ! else if (*b >= 'A' && *b <= 'F') xvalue = *b - 'A' + 0xa; ! else if (*b >= 'a' && *b <= 'f') xvalue = *b - 'a' + 0xa; chunksize = (chunksize << 4) | xvalue; + ++b; } ! return chunksize; } + /* get_client_block is called in a loop to get the request message body. + * This is quite simple if the client includes a content-length + * (the normal case), but gets messy if the body is chunked. Note that + * r->remaining is used to maintain state across calls and that + * r->read_length is the total number of bytes given to the caller + * across all invocations. It is messy because we have to be careful not + * to read past the data provided by the client, since these reads block. + * Returns 0 on End-of-body, -1 on error or premature chunk end. + * + * Reading the chunked encoding requires a buffer size large enough to + * hold a chunk-size line, including any extensions. For now, we'll leave + * that to the caller, at least until we can come up with a better solution. + */ long get_client_block (request_rec *r, char *buffer, int bufsiz) { ! int c; ! long len_read, len_to_read; ! long chunk_start = 0; ! if (!r->read_chunked) { /* Content-length read */ ! len_to_read = (r->remaining > bufsiz) ? bufsiz : r->remaining; ! len_read = bread(r->connection->client, buffer, len_to_read); ! if (len_read <= 0) { ! if (len_read < 0) r->connection->keepalive = -1; ! return len_read; ! } ! r->read_length += len_read; ! r->remaining -= len_read; ! return len_read; } ! ! /* Handle chunked reading ! * Note: we are careful to shorten the input bufsiz so that there ! * will always be enough space for us to add a CRLF (if necessary). ! */ ! if (r->read_body == REQUEST_CHUNKED_PASS) ! bufsiz -= 2; ! if (bufsiz <= 0) ! return -1; /* Cannot read chunked with a small buffer */ ! ! if (r->remaining == 0) { /* Start of new chunk */ ! ! chunk_start = getline(buffer, bufsiz, r->connection->client, 0); ! if ((chunk_start <= 0) || (chunk_start >= (bufsiz - 1)) ! || !isxdigit(*buffer)) { ! r->connection->keepalive = -1; ! return -1; ! } ! ! len_to_read = get_chunk_size(buffer); ! ! if (len_to_read == 0) { /* Last chunk indicated, get footers */ ! if (r->read_body == REQUEST_CHUNKED_DECHUNK) { ! get_mime_headers(r); ! sprintf(buffer, "%ld", r->read_length); ! table_unset(r->headers_in, "Transfer-Encoding"); ! table_set(r->headers_in, "Content-Length", buffer); ! return 0; ! } ! r->remaining = -1; /* Indicate footers in-progress */ ! } ! else { ! r->remaining = len_to_read; ! } ! if (r->read_body == REQUEST_CHUNKED_PASS) { ! buffer[chunk_start++] = CR; /* Restore chunk-size line end */ ! buffer[chunk_start++] = LF; ! buffer += chunk_start; /* and pass line on to caller */ ! bufsiz -= chunk_start; ! } } ! /* When REQUEST_CHUNKED_PASS, we are */ ! if (r->remaining == -1) { /* reading footers until empty line */ ! len_read = chunk_start; ! ! while ((bufsiz > 1) && ((len_read = ! getline(buffer, bufsiz, r->connection->client, 1)) > 0)) { ! ! if (len_read != (bufsiz - 1)) { ! buffer[len_read++] = CR; /* Restore footer line end */ ! buffer[len_read++] = LF; ! } ! chunk_start += len_read; ! buffer += len_read; ! bufsiz -= len_read; ! } ! if (len_read < 0) { ! r->connection->keepalive = -1; ! return -1; ! } ! ! if (len_read == 0) { /* Indicates an empty line */ ! buffer[0] = CR; ! buffer[1] = LF; ! chunk_start += 2; ! r->remaining = -2; ! } ! r->read_length += chunk_start; ! return chunk_start; ! } ! /* When REQUEST_CHUNKED_PASS, we */ ! if (r->remaining == -2) { /* finished footers when last called */ ! r->remaining = 0; /* so now we must signal EOF */ ! return 0; ! } ! ! /* Otherwise, we are in the midst of reading a chunk of data */ ! ! len_to_read = (r->remaining > bufsiz) ? bufsiz : r->remaining; len_read = bread(r->connection->client, buffer, len_to_read); ! if (len_read <= 0) { ! r->connection->keepalive = -1; ! return -1; } ! r->remaining -= len_read; ! ! if (r->remaining == 0) { /* End of chunk, get trailing CRLF */ ! if ((c = bgetc(r->connection->client)) == CR) { ! c = bgetc(r->connection->client); ! } ! if (c != LF) { ! r->connection->keepalive = -1; ! return -1; ! } ! if (r->read_body == REQUEST_CHUNKED_PASS) { ! buffer[len_read++] = CR; ! buffer[len_read++] = LF; ! } ! } ! r->read_length += (chunk_start + len_read); ! ! return (chunk_start + len_read); } long send_fd(FILE *f, request_rec *r) { return send_fd_length(f, r, -1); } *************** *** 1469,1478 **** "Please remove all references to this resource.\n", NULL); break; case HTTP_REQUEST_ENTITY_TOO_LARGE: ! bputs("The supplied request data exceeds the capacity\n", fd); ! bputs("limit placed on this resource. The request data \n", fd); ! bputs("must be reduced before the request can proceed.\n", fd); ! break; case HTTP_REQUEST_URI_TOO_LARGE: bputs("The requested URL's length exceeds the capacity\n", fd); bputs("limit for this server.\n", fd); --- 1591,1602 ---- "Please remove all references to this resource.\n", NULL); break; case HTTP_REQUEST_ENTITY_TOO_LARGE: ! bvputs(fd, "The requested resource<BR>", ! escape_html(r->pool, r->uri), "<BR>\n", ! "does not allow request data with ", r->method, ! " requests, or the amount of data provided in\n", ! "the request exceeds the capacity limit.\n", NULL); ! break; case HTTP_REQUEST_URI_TOO_LARGE: bputs("The requested URL's length exceeds the capacity\n", fd); bputs("limit for this server.\n", fd); 1.21 +9 -4 apache/src/mod_cgi.c Index: mod_cgi.c =================================================================== RCS file: /export/home/cvs/apache/src/mod_cgi.c,v retrieving revision 1.20 retrieving revision 1.21 diff -C3 -r1.20 -r1.21 *** mod_cgi.c 1996/10/20 18:03:34 1.20 --- mod_cgi.c 1996/11/25 11:22:00 1.21 *************** *** 381,387 **** return log_scripterror(r, conf, FORBIDDEN, "file permissions deny server execution"); ! if ((retval = setup_client_block(r))) return retval; add_common_vars (r); --- 381,387 ---- return log_scripterror(r, conf, FORBIDDEN, "file permissions deny server execution"); ! if ((retval = setup_client_block(r, REQUEST_CHUNKED_ERROR))) return retval; add_common_vars (r); *************** *** 422,437 **** hard_timeout ("copy script args", r); handler = signal (SIGPIPE, SIG_IGN); ! while ((len_read = get_client_block (r, argsbuffer, HUGE_STRING_LEN))) { - if (fwrite (argsbuffer, 1, len_read, script_out) == 0) - break; if (conf->logname) { if ((dbpos + len_read) > conf->bufbytes) dbsize = conf->bufbytes - dbpos; else dbsize = len_read; strncpy(dbuf + dbpos, argsbuffer, dbsize); dbpos += dbsize; } } --- 422,442 ---- hard_timeout ("copy script args", r); handler = signal (SIGPIPE, SIG_IGN); ! while ((len_read = ! get_client_block(r, argsbuffer, HUGE_STRING_LEN)) > 0) { if (conf->logname) { if ((dbpos + len_read) > conf->bufbytes) dbsize = conf->bufbytes - dbpos; else dbsize = len_read; strncpy(dbuf + dbpos, argsbuffer, dbsize); dbpos += dbsize; + } + if (fwrite(argsbuffer, 1, len_read, script_out) < len_read) { + /* silly script stopped reading, soak up remaining message */ + while (get_client_block(r, argsbuffer, HUGE_STRING_LEN) > 0) + ; /* dump it */ + break; } } 1.3 +1 -1 apache/src/mod_fastcgi.c Index: mod_fastcgi.c =================================================================== RCS file: /export/home/cvs/apache/src/mod_fastcgi.c,v retrieving revision 1.2 retrieving revision 1.3 diff -C3 -r1.2 -r1.3 *** mod_fastcgi.c 1996/10/22 20:38:11 1.2 --- mod_fastcgi.c 1996/11/25 11:22:00 1.3 *************** *** 3673,3679 **** reqPtr->filename, reqPtr); return NOT_FOUND; } ! status = setup_client_block(reqPtr); if(status != OK) { return status; } --- 3673,3679 ---- reqPtr->filename, reqPtr); return NOT_FOUND; } ! status = setup_client_block(reqPtr, REQUEST_CHUNKED_ERROR); if(status != OK) { return status; } 1.27 +6 -0 apache/src/util_script.c Index: util_script.c =================================================================== RCS file: /export/home/cvs/apache/src/util_script.c,v retrieving revision 1.26 retrieving revision 1.27 diff -C3 -r1.26 -r1.27 *** util_script.c 1996/11/12 06:02:54 1.26 --- util_script.c 1996/11/25 11:22:01 1.27 *************** *** 327,332 **** --- 327,338 ---- else if(!strcasecmp(w,"Location")) { table_set (r->headers_out, w, l); } + else if(!strcasecmp(w,"Content-Length")) { + table_set (r->headers_out, w, l); + } + else if(!strcasecmp(w,"Transfer-Encoding")) { + table_set (r->headers_out, w, l); + } /* The HTTP specification says that it is legal to merge duplicate * headers into one. Some browsers that support Cookies don't like 1.6 +1 -1 apache/src/modules/proxy/mod_proxy.c Index: mod_proxy.c =================================================================== RCS file: /export/home/cvs/apache/src/modules/proxy/mod_proxy.c,v retrieving revision 1.5 retrieving revision 1.6 diff -C3 -r1.5 -r1.6 *** mod_proxy.c 1996/10/20 23:58:59 1.5 --- mod_proxy.c 1996/11/25 11:22:05 1.6 *************** *** 196,202 **** if (strncmp(r->filename, "proxy:", 6) != 0) return DECLINED; ! if ((rc = setup_client_block(r))) return rc; url = r->filename + 6; --- 196,202 ---- if (strncmp(r->filename, "proxy:", 6) != 0) return DECLINED; ! if ((rc = setup_client_block(r, REQUEST_CHUNKED_ERROR))) return rc; url = r->filename + 6; 1.6 +23 -2 apache/src/modules/proxy/proxy_http.c Index: proxy_http.c =================================================================== RCS file: /export/home/cvs/apache/src/modules/proxy/proxy_http.c,v retrieving revision 1.5 retrieving revision 1.6 diff -C3 -r1.5 -r1.6 *** proxy_http.c 1996/10/27 18:29:57 1.5 --- proxy_http.c 1996/11/25 11:22:05 1.6 *************** *** 108,113 **** --- 108,133 ---- return OK; } + /* Clear all connection-based headers from the incoming headers table */ + static void clear_connection (table *headers) + { + char *name; + char *next = table_get(headers, "Connection"); + + if (!next) return; + + while (*next) { + name = next; + while (*next && !isspace(*next) && (*next != ',')) ++next; + while (*next && (isspace(*next) || (*next == ','))) { + *next = '\0'; + ++next; + } + table_unset(headers, name); + } + table_unset(headers, "Connection"); + } + /* * This handles http:// URLs, and other URLs using a remote proxy over http * If proxyhost is NULL, then contact the server directly, otherwise *************** *** 193,198 **** --- 213,220 ---- else return proxyerror(r, "Could not connect to remote machine"); } + clear_connection(r->headers_in); /* Strip connection-based headers */ + f = bcreate(pool, B_RDWR); bpushfd(f, sock, sock); *************** *** 204,210 **** for (i=0; i < reqhdrs_arr->nelts; i++) { if (reqhdrs[i].key == NULL || reqhdrs[i].val == NULL) continue; - if (!strcasecmp(reqhdrs[i].key, "Connection")) continue; bvputs(f, reqhdrs[i].key, ": ", reqhdrs[i].val, "\015\012", NULL); } --- 226,231 ---- *************** *** 213,219 **** if (should_client_block(r)) { ! while ((i = get_client_block (r, buffer, HUGE_STRING_LEN))) bwrite(f, buffer, i); } bflush(f); --- 234,240 ---- if (should_client_block(r)) { ! while ((i = get_client_block(r, buffer, HUGE_STRING_LEN)) > 0) bwrite(f, buffer, i); } bflush(f);