httpd-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Roy T. Fielding" <field...@kiwi.ICS.UCI.EDU>
Subject Re: what to do about CGI and chunked
Date Sat, 23 Nov 1996 10:29:04 GMT
Here is my solution.  I've added a couple request_rec values for
determining how the message body should be read and how much has
been read.  This allows a module to select whatever level of
sophistication they want, while ensuring that the data being read
is complete and correct, and killing keepalive if it isn't (since
otherwise the server would interpret the leftover garbage as a
second request and send a second error response after the CGI response).
Normal CGI scripts are not affected by the changes (other than the bug fixes).

I tested all of the options by modifying the read_body value in
mod_cgi.  Unfortunately, I can't test the changes to mod_fastcgi
and mod_proxy (aside from compiling them). I'm not sure whether
fastcgi should require Content-Length or not, so I set it so that
it would dechunk and signal errors with -1.

It is difficult to see how valuable this change is without a module or
proxy that uses the more poweful chunking options.  All I can say is
that we can't build an HTTP/1.1 gateway or proxy without them.
It would also be nice if we had a configurable directive for limiting
the size of a request message, since this gives the ability to enforce it.

This is an API change, so we'll need to bump the version when this
is committed.  That's also why it *really* needs to be reviewed now
and not later.

.....Roy

Index: httpd.h
===================================================================
RCS file: /export/home/cvs/apache/src/httpd.h,v
retrieving revision 1.61
diff -c -r1.61 httpd.h
*** httpd.h	1996/11/14 07:24:42	1.61
--- httpd.h	1996/11/23 09:54:22
***************
*** 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
Index: http_protocol.c
===================================================================
RCS file: /export/home/cvs/apache/src/http_protocol.c,v
retrieving revision 1.75
diff -c -r1.75 http_protocol.c
*** http_protocol.c	1996/11/14 07:35:49	1.75
--- http_protocol.c	1996/11/23 09:54:22
***************
*** 210,216 ****
      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))))
&&
--- 210,218 ----
      char *length = table_get (r->headers_out, "Content-length");
      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 ||
  	 ((r->proto_num >= 1001) && (r->byterange > 1 || (r->chunked = 1))))
&&
***************
*** 633,638 ****
--- 635,645 ----
      r->per_dir_config = r->server->lookup_defaults; /* For now. */
  
      r->sent_bodyct = 0; /* bytect isn't for body */
+ 
+     r->remaining    = 0;
+     r->read_length  = 0;
+     r->read_body    = REQUEST_NO_BODY;
+     r->read_chunked = 0;
      
      r->status = HTTP_OK;	/* Until further notice.
  				 * Only changed by die(), or (bletch!)
***************
*** 703,708 ****
--- 710,718 ----
      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;
  }
  
***************
*** 1055,1111 ****
  
  }
  
! /* 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,1143 ----
  
  }
  
! /* 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. Set the request record's r->read_body field to select the policy
!  *    to apply if the request message indicates a body and how a chunked
!  *    tranfer-coding should be interpreted.  Set it to 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.
!  *
!  * 2. 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.
   *
!  * 3. 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.
   *
+  * 4. 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)
  {
!     char *tenc = table_get(r->headers_in, "Transfer-Encoding");
!     char *lenp = table_get(r->headers_in, "Content-length");
  
      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 HTTP_LENGTH_REQUIRED;
!         }
! 
!         r->read_chunked = 1;
!         r->remaining = 0;
      }
!     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;
***************
*** 1113,1123 ****
  
  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 */
--- 1145,1151 ----
  
  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 */
***************
*** 1129,1195 ****
      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); }
--- 1157,1309 ----
      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.
+  * Assumes that caller will never pass a bufsiz <= 2; that would be dumb.
+  * Returns 0 on End-of-body, -1 on error or premature chunk end.
+  */
  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 (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); }
***************
*** 1468,1477 ****
  	           "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);
--- 1582,1593 ----
  	           "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);
Index: mod_cgi.c
===================================================================
RCS file: /export/home/cvs/apache/src/mod_cgi.c,v
retrieving revision 1.20
diff -c -r1.20 mod_cgi.c
*** mod_cgi.c	1996/10/20 18:03:34	1.20
--- mod_cgi.c	1996/11/23 09:54:22
***************
*** 381,386 ****
--- 381,388 ----
  	return log_scripterror(r, conf, FORBIDDEN,
  			       "file permissions deny server execution");
      
+     r->read_body = REQUEST_CHUNKED_ERROR; /* Require Content-Length if body */
+ 
      if ((retval = setup_client_block(r)))
  	return retval;
  
***************
*** 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;
  	    }
  	}
  
--- 424,444 ----
          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;
  	    }
  	}
  
Index: mod_fastcgi.c
===================================================================
RCS file: /export/home/cvs/apache/src/mod_fastcgi.c,v
retrieving revision 1.2
diff -c -r1.2 mod_fastcgi.c
*** mod_fastcgi.c	1996/10/22 20:38:11	1.2
--- mod_fastcgi.c	1996/11/23 09:54:23
***************
*** 3673,3678 ****
--- 3673,3679 ----
                  reqPtr->filename, reqPtr);
          return NOT_FOUND;
      }
+     reqPtr->read_body = REQUEST_CHUNKED_DECHUNK;
      status = setup_client_block(reqPtr);
      if(status != OK) {
          return status;
Index: modules/proxy/mod_proxy.c
===================================================================
RCS file: /export/home/cvs/apache/src/modules/proxy/mod_proxy.c,v
retrieving revision 1.5
diff -c -r1.5 mod_proxy.c
*** mod_proxy.c	1996/10/20 23:58:59	1.5
--- mod_proxy.c	1996/11/23 09:54:23
***************
*** 196,201 ****
--- 196,203 ----
  
      if (strncmp(r->filename, "proxy:", 6) != 0) return DECLINED;
  
+     r->read_body = REQUEST_CHUNKED_ERROR; /* Require Content-Length if body */
+  
      if ((rc = setup_client_block(r)))
  	return rc;
  
Index: modules/proxy/proxy_http.c
===================================================================
RCS file: /export/home/cvs/apache/src/modules/proxy/proxy_http.c,v
retrieving revision 1.5
diff -c -r1.5 proxy_http.c
*** proxy_http.c	1996/10/27 18:29:57	1.5
--- proxy_http.c	1996/11/23 09:54:23
***************
*** 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);

Mime
View raw message