$OpenBSD: patch-src_connections_c,v 1.13 2010/02/02 08:10:29 sthen Exp $
--- src/connections.c.orig	Mon Feb  1 19:21:56 2010
+++ src/connections.c	Mon Feb  1 19:21:46 2010
@@ -192,40 +192,42 @@ static void dump_packet(const unsigned char *data, siz
 
 static int connection_handle_read_ssl(server *srv, connection *con) {
 #ifdef USE_OPENSSL
-	int r, ssl_err, len, count = 0;
+	int r, ssl_err, len, count = 0, read_offset, toread;
 	buffer *b = NULL;
 
 	if (!con->conf.is_ssl) return -1;
 
-	/* don't resize the buffer if we were in SSL_ERROR_WANT_* */
-
 	ERR_clear_error();
 	do {
-		if (!con->ssl_error_want_reuse_buffer) {
-			b = buffer_init();
-			buffer_prepare_copy(b, SSL_pending(con->ssl) + (16 * 1024)); /* the pending bytes + 16kb */
+		if (NULL != con->read_queue->last) {
+			b = con->read_queue->last->mem;
+		}
 
+		if (NULL == b || b->size - b->used < 1024) {
+			b = chunkqueue_get_append_buffer(con->read_queue);
+			len = SSL_pending(con->ssl);
+			if (len < 4*1024) len = 4*1024; /* always alloc >= 4k buffer */
+			buffer_prepare_copy(b, len + 1);
+
 			/* overwrite everything with 0 */
 			memset(b->ptr, 0, b->size);
-		} else {
-			b = con->ssl_error_want_reuse_buffer;
 		}
 
-		len = SSL_read(con->ssl, b->ptr, b->size - 1);
-		con->ssl_error_want_reuse_buffer = NULL; /* reuse it only once */
+		read_offset = (b->used > 0) ? b->used - 1 : 0;
+		toread = b->size - 1 - read_offset;
 
+		len = SSL_read(con->ssl, b->ptr + read_offset, toread);
+
 		if (len > 0) {
-			b->used = len;
+			if (b->used > 0) b->used--;
+			b->used += len;
 			b->ptr[b->used++] = '\0';
 
-		       	/* we move the buffer to the chunk-queue, no need to free it */
+			con->bytes_read += len;
 
-			chunkqueue_append_buffer_weak(con->read_queue, b);
 			count += len;
-			con->bytes_read += len;
-			b = NULL;
 		}
-	} while (len > 0 && count < MAX_READ_LIMIT);
+	} while (len == toread && count < MAX_READ_LIMIT);
 
 
 	if (len < 0) {
@@ -234,11 +236,11 @@ static int connection_handle_read_ssl(server *srv, con
 		case SSL_ERROR_WANT_READ:
 		case SSL_ERROR_WANT_WRITE:
 			con->is_readable = 0;
-			con->ssl_error_want_reuse_buffer = b;
 
-			b = NULL;
+			/* the manual says we have to call SSL_read with the same arguments next time.
+			 * we ignore this restriction; no one has complained about it in 1.5 yet, so it probably works anyway.
+			 */
 
-			/* we have to steal the buffer from the queue-queue */
 			return 0;
 		case SSL_ERROR_SYSCALL:
 			/**
@@ -297,16 +299,11 @@ static int connection_handle_read_ssl(server *srv, con
 
 		connection_set_state(srv, con, CON_STATE_ERROR);
 
-		buffer_free(b);
-
 		return -1;
 	} else if (len == 0) {
 		con->is_readable = 0;
 		/* the other end close the connection -> KEEP-ALIVE */
 
-		/* pipelining */
-		buffer_free(b);
-
 		return -2;
 	}
 
@@ -321,26 +318,41 @@ static int connection_handle_read_ssl(server *srv, con
 static int connection_handle_read(server *srv, connection *con) {
 	int len;
 	buffer *b;
-	int toread;
+	int toread, read_offset;
 
 	if (con->conf.is_ssl) {
 		return connection_handle_read_ssl(srv, con);
 	}
 
+	b = (NULL != con->read_queue->last) ? con->read_queue->last->mem : NULL;
+
+	/* default size for chunks is 4kb; only use bigger chunks if FIONREAD tells
+	 *  us more than 4kb is available
+	 * if FIONREAD doesn't signal a big chunk we fill the previous buffer
+	 *  if it has >= 1kb free
+	 */
 #if defined(__WIN32)
-	b = chunkqueue_get_append_buffer(con->read_queue);
-	buffer_prepare_copy(b, 4 * 1024);
-	len = recv(con->fd, b->ptr, b->size - 1, 0);
-#else
-	if (ioctl(con->fd, FIONREAD, &toread) || toread == 0) {
+	if (NULL == b || b->size - b->used < 1024) {
 		b = chunkqueue_get_append_buffer(con->read_queue);
 		buffer_prepare_copy(b, 4 * 1024);
+	}
+
+	read_offset = (b->used == 0) ? 0 : b->used - 1;
+	len = recv(con->fd, b->ptr + read_offset, b->size - 1 - read_offset, 0);
+#else
+	if (ioctl(con->fd, FIONREAD, &toread) || toread == 0 || toread <= 4*1024) {
+		if (NULL == b || b->size - b->used < 1024) {
+			b = chunkqueue_get_append_buffer(con->read_queue);
+			buffer_prepare_copy(b, 4 * 1024);
+		}
 	} else {
 		if (toread > MAX_READ_LIMIT) toread = MAX_READ_LIMIT;
 		b = chunkqueue_get_append_buffer(con->read_queue);
 		buffer_prepare_copy(b, toread + 1);
 	}
-	len = read(con->fd, b->ptr, b->size - 1);
+
+	read_offset = (b->used == 0) ? 0 : b->used - 1;
+	len = read(con->fd, b->ptr + read_offset, b->size - 1 - read_offset);
 #endif
 
 	if (len < 0) {
@@ -374,7 +386,8 @@ static int connection_handle_read(server *srv, connect
 		con->is_readable = 0;
 	}
 
-	b->used = len;
+	if (b->used > 0) b->used--;
+	b->used += len;
 	b->ptr[b->used++] = '\0';
 
 	con->bytes_read += len;
@@ -850,13 +863,6 @@ int connection_reset(server *srv, connection *con) {
 	/* The cond_cache gets reset in response.c */
 	/* config_cond_cache_reset(srv, con); */
 
-#ifdef USE_OPENSSL
-	if (con->ssl_error_want_reuse_buffer) {
-		buffer_free(con->ssl_error_want_reuse_buffer);
-		con->ssl_error_want_reuse_buffer = NULL;
-	}
-#endif
-
 	con->header_len = 0;
 	con->in_error_handler = 0;
 
@@ -945,62 +951,50 @@ static int connection_handle_read_state(server *srv, c
 		last_chunk = NULL;
 		last_offset = 0;
 
-		for (c = cq->first; !last_chunk && c; c = c->next) {
+		for (c = cq->first; c; c = c->next) {
 			buffer b;
 			size_t i;
 
 			b.ptr = c->mem->ptr + c->offset;
 			b.used = c->mem->used - c->offset;
+			if (b.used > 0) b.used--; /* buffer "used" includes terminating zero */
 
-			for (i = 0; !last_chunk && i < b.used; i++) {
+			for (i = 0; i < b.used; i++) {
 				char ch = b.ptr[i];
-				size_t have_chars = 0;
 
-				switch (ch) {
-				case '\r':
-					/* we have to do a 4 char lookup */
-					have_chars = b.used - i - 1;
+				if ('\r' == ch) {
+					/* chec if \n\r\n follows */
+					size_t j = i+1;
+					chunk *cc = c;
+					const char header_end[] = "\r\n\r\n";
+					int header_end_match_pos = 1;
 
-					if (have_chars >= 4) {
-						/* all chars are in this buffer */
+					for ( ; cc; cc = cc->next, j = 0 ) {
+						buffer bb;
+						bb.ptr = cc->mem->ptr + cc->offset;
+						bb.used = cc->mem->used - cc->offset;
+						if (bb.used > 0) bb.used--; /* buffer "used" includes terminating zero */
 
-						if (0 == strncmp(b.ptr + i, "\r\n\r\n", 4)) {
-							/* found */
-							last_chunk = c;
-							last_offset = i + 4;
+						for ( ; j < bb.used; j++) {
+							ch = bb.ptr[j];
 
-							break;
-						}
-					} else {
-						chunk *lookahead_chunk = c->next;
-						size_t missing_chars;
-						/* looks like the following chars are not in the same chunk */
-
-						missing_chars = 4 - have_chars;
-
-						if (lookahead_chunk && lookahead_chunk->type == MEM_CHUNK) {
-							/* is the chunk long enough to contain the other chars ? */
-
-							if (lookahead_chunk->mem->used > missing_chars) {
-								if (0 == strncmp(b.ptr + i, "\r\n\r\n", have_chars) &&
-								    0 == strncmp(lookahead_chunk->mem->ptr, "\r\n\r\n" + have_chars, missing_chars)) {
-
-									last_chunk = lookahead_chunk;
-									last_offset = missing_chars;
-
-									break;
+							if (ch == header_end[header_end_match_pos]) {
+								header_end_match_pos++;
+								if (4 == header_end_match_pos) {
+									last_chunk = cc;
+									last_offset = j+1;
+									goto found_header_end;
 								}
 							} else {
-								/* a splited \r \n */
-								break;
+								goto reset_search;
 							}
 						}
 					}
-
-					break;
 				}
+reset_search: ;
 			}
 		}
+found_header_end:
 
 		/* found */
 		if (last_chunk) {
@@ -1140,8 +1134,15 @@ static int connection_handle_read_state(server *srv, c
 			} else {
 				buffer *b;
 
-				b = chunkqueue_get_append_buffer(dst_cq);
-				buffer_copy_string_len(b, c->mem->ptr + c->offset, toRead);
+				if (dst_cq->last &&
+				    dst_cq->last->type == MEM_CHUNK) {
+					b = dst_cq->last->mem;
+				} else {
+					b = chunkqueue_get_append_buffer(dst_cq);
+					/* prepare buffer size for remaining POST data; is < 64kb */
+					buffer_prepare_copy(b, con->request.content_length - dst_cq->bytes_in + 1);
+				}
+				buffer_append_string_len(b, c->mem->ptr + c->offset, toRead);
 			}
 
 			c->offset += toRead;
