components/lighttpd/patches/lighttpd_fix_slow_request_dos.patch
changeset 331 5001b63ddc8a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/components/lighttpd/patches/lighttpd_fix_slow_request_dos.patch	Wed Jun 22 14:10:22 2011 -0700
@@ -0,0 +1,209 @@
+diff -u -r lighttpd-1.4.23-orig/src/base.h lighttpd-1.4.23/src/base.h
+--- src/base.h	Thu Jun 11 02:44:17 2009
++++ src/base.h	Tue Aug 24 04:16:29 2010
+@@ -421,7 +421,6 @@
+ 
+ #ifdef USE_OPENSSL
+ 	SSL *ssl;
+-	buffer *ssl_error_want_reuse_buffer;
+ #endif
+ 	/* etag handling */
+ 	etag_flags_t etag_flags;
+Only in lighttpd-1.4.23/src: base.h.orig
+diff -u -r lighttpd-1.4.23-orig/src/chunk.c lighttpd-1.4.23/src/chunk.c
+--- src/chunk.c	Mon Mar 30 15:16:59 2009
++++ src/chunk.c	Tue Aug 24 04:12:50 2010
+@@ -197,8 +197,6 @@
+ int chunkqueue_append_buffer_weak(chunkqueue *cq, buffer *mem) {
+ 	chunk *c;
+ 
+-	if (mem->used == 0) return 0;
+-
+ 	c = chunkqueue_get_unused_chunk(cq);
+ 	c->type = MEM_CHUNK;
+ 	c->offset = 0;
+diff -u -r lighttpd-1.4.23-orig/src/connections.c lighttpd-1.4.23/src/connections.c
+--- src/connections.c	Thu Jun 11 06:54:30 2009
++++ src/connections.c	Tue Aug 24 04:12:50 2010
+@@ -192,40 +192,42 @@
+ 
+ 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 @@
+ 		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 @@
+ 
+ 		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(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 @@
+ 		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;
+@@ -841,13 +854,6 @@
+ 	/* 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;
+ 
+@@ -1131,8 +1137,15 @@
+ 			} 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;