diff --git a/src/sbbs3/sbbs_ini.c b/src/sbbs3/sbbs_ini.c
index 271e9065579f45815c2de06e8b13ef82f9e83b15..6db54fa086188a5c234613dc645ea2870ad328b6 100644
--- a/src/sbbs3/sbbs_ini.c
+++ b/src/sbbs3/sbbs_ini.c
@@ -610,6 +610,10 @@ void sbbs_read_ini(
 		web->options
 			=iniGetBitField(list,section,strOptions,web_options
 				,BBS_OPT_NO_HOST_LOOKUP | WEB_OPT_HTTP_LOGGING);
+		web->outbuf_highwater_mark
+			=iniGetShortInt(list,section,"OutbufHighwaterMark",1024);
+		web->outbuf_drain_timeout
+			=iniGetShortInt(list,section,"OutbufDrainTimeout",10);
 
 		web->bind_retry_count=iniGetInteger(list,section,strBindRetryCount,global->bind_retry_count);
 		web->bind_retry_delay=iniGetInteger(list,section,strBindRetryDelay,global->bind_retry_delay);
@@ -1110,6 +1114,10 @@ BOOL sbbs_write_ini(
 			iniRemoveValue(lp,section,strBindRetryDelay);
 		else if(!iniSetInteger(lp,section,strBindRetryDelay,web->bind_retry_delay,&style))
 			break;
+		if(!iniSetShortInt(lp,section,"OutbufHighwaterMark",bbs->outbuf_highwater_mark,&style))
+			break;
+		if(!iniSetShortInt(lp,section,"OutbufDrainTimeout",bbs->outbuf_drain_timeout,&style))
+			break;
 	}
 
 	/***********************************************************************/
diff --git a/src/sbbs3/websrvr.c b/src/sbbs3/websrvr.c
index 46253e6e404aa75039acca0dd8b2f0f7bb035f1a..46b9c09da5dfa7eaee304f51bcf99a7cc5eddc06 100644
--- a/src/sbbs3/websrvr.c
+++ b/src/sbbs3/websrvr.c
@@ -82,6 +82,7 @@ static const char*	unknown="<unknown>";
 										   (Including terminator )*/
 #define MAX_REDIR_LOOPS			20		/* Max. times to follow internal redirects for a single request */
 #define MAX_POST_LEN			1048576	/* Max size of body for POSTS */
+#define	OUTBUF_LEN				20480	/* Size of output thread ring buffer */
 
 enum {
 	 CLEANUP_SSJS_TMP_FILE
@@ -197,6 +198,10 @@ typedef struct  {
 	js_branch_t		js_branch;
 	subscan_t		*subscan;
 
+	/* Ring Buffer Stuff */
+	RingBuf			outbuf;
+	sem_t			output_thread_terminated;
+
 	/* Client info */
 	client_t		client;
 } http_session_t;
@@ -297,7 +302,7 @@ static BOOL js_setup(http_session_t* session);
 static char *find_last_slash(char *str);
 static BOOL check_extra_path(http_session_t * session);
 static BOOL exec_ssjs(http_session_t* session, char* script);
-static BOOL ssjs_send_headers(http_session_t* session);
+static BOOL ssjs_send_headers(http_session_t* session, int chunked);
 
 static time_t
 sub_mkgmt(struct tm *tm)
@@ -416,6 +421,22 @@ static int lprintf(int level, char *fmt, ...)
     return(startup->lputs(startup->cbdata,level,sbuf));
 }
 
+static int writebuf(http_session_t	*session, const char *buf, size_t len)
+{
+	int		sent=0;
+	int		avail;
+
+	while(!terminate_server && sent < len) {
+		avail=RingBufFree(&session->outbuf);
+		if(!avail)
+			SLEEP(1);
+		if(avail > len-sent)
+			avail=len-sent;
+		sent=RingBufWrite(&(session->outbuf), ((char *)buf)+sent, avail);
+	}
+	return(sent);
+}
+
 static int sock_sendbuf(SOCKET sock, const char *buf, size_t len, BOOL *failed)
 {
 	size_t sent=0;
@@ -556,37 +577,12 @@ static void init_enviro(http_session_t *session)  {
  * Sends string str to socket sock... returns number of bytes written, or 0 on an error
  * Can not close the socket since it can not set it to INVALID_SOCKET
  */
-static int sockprint(SOCKET sock, const char *str)
+static int bufprint(http_session_t *session, const char *str)
 {
 	int len;
-	int	result;
-	int written=0;
-	BOOL	wr;
 
-	if(sock==INVALID_SOCKET)
-		return(0);
-	if(startup->options&WEB_OPT_DEBUG_TX)
-		lprintf(LOG_DEBUG,"%04d TX: %s", sock, str);
 	len=strlen(str);
-
-	while(socket_check(sock,NULL,&wr,startup->max_inactivity*1000) && wr && written<len)  {
-		result=sendsocket(sock,str+written,len-written);
-		if(result==SOCKET_ERROR) {
-			if(ERROR_VALUE==ECONNRESET) 
-				lprintf(LOG_NOTICE,"%04d Connection reset by peer on send",sock);
-			else if(ERROR_VALUE==ECONNABORTED) 
-				lprintf(LOG_NOTICE,"%04d Connection aborted by peer on send",sock);
-			else
-				lprintf(LOG_WARNING,"%04d !ERROR %d sending on socket",sock,ERROR_VALUE);
-			return(0);
-		}
-		written+=result;
-	}
-	if(written != len) {
-		lprintf(LOG_WARNING,"%04d !ERROR %d sending on socket",sock,ERROR_VALUE);
-		return(0);
-	}
-	return(len);
+	return(writebuf(session,str,len));
 }
 
 /**********************************************************/
@@ -741,14 +737,20 @@ static void close_request(http_session_t * session)
 	int			i;
 
 	if(session->req.write_chunked) {
-		sock_sendbuf(session->socket, "0\r\n",3,NULL);
+		while(RingBufFull(&session->outbuf))
+			SLEEP(1);
+		session->req.write_chunked=0;
+		writebuf(session,"0\r\n",3);
 		if(session->req.dynamic==IS_SSJS)
-			ssjs_send_headers(session);
+			ssjs_send_headers(session,FALSE);
 		else
 			/* Non-ssjs isn't capable of generating headers during execution */
-			sock_sendbuf(session->socket, newline,2,NULL);
+			writebuf(session, newline, 2);
 	}
 
+	/* Force the output thread to go NOW */
+	sem_post(&(session->outbuf.highwater_sem));
+
 	if(session->req.ld!=NULL) {
 		now=time(NULL);
 		localtime_r(&now,&session->req.ld->completed);
@@ -886,7 +888,7 @@ static void safecat(char *dst, const char *append, size_t maxlen) {
 /* Sends headers for the reply.					 */
 /* HTTP/0.9 doesn't use headers, so just returns */
 /*************************************************/
-static BOOL send_headers(http_session_t *session, const char *status)
+static BOOL send_headers(http_session_t *session, const char *status, int chunked)
 {
 	int		ret;
 	BOOL	send_file=TRUE;
@@ -985,13 +987,13 @@ static BOOL send_headers(http_session_t *session, const char *status)
 			safecat(headers,header,MAX_HEADERS_SIZE);
 		}
 
-		if(session->req.write_chunked) {
+		if(chunked) {
 			safe_snprintf(header,sizeof(header),"%s: %s",get_header(HEAD_TRANSFER_ENCODING),"Chunked");
 			safecat(headers,header,MAX_HEADERS_SIZE);
 		}
 
 		/* DO NOT send a content-length for chunked */
-		if(session->req.keep_alive && session->req.dynamic!=IS_CGI && (!session->req.write_chunked)) {
+		if(session->req.keep_alive && session->req.dynamic!=IS_CGI && (!chunked)) {
 			if(ret)  {
 				safe_snprintf(header,sizeof(header),"%s: %s",get_header(HEAD_LENGTH),"0");
 				safecat(headers,header,MAX_HEADERS_SIZE);
@@ -1024,26 +1026,28 @@ static BOOL send_headers(http_session_t *session, const char *status)
 	}
 
 	safecat(headers,"",MAX_HEADERS_SIZE);
-	send_file = (sockprint(session->socket,headers) && send_file);
+	send_file = (bufprint(session,headers) && send_file);
 	FREE_AND_NULL(headers);
+	while(RingBufFull(&session->outbuf))
+		SLEEP(1);
+	session->req.write_chunked=chunked;
 	return(send_file);
 }
 
-static int sock_sendfile(SOCKET socket,char *path)
+static int sock_sendfile(http_session_t *session,char *path)
 {
 	int		file;
-	long	offset=0;
 	int		ret=0;
+	int		i;
+	char	buf[2048];		/* Input buffer */
 
 	if(startup->options&WEB_OPT_DEBUG_TX)
-		lprintf(LOG_DEBUG,"%04d Sending %s",socket,path);
+		lprintf(LOG_DEBUG,"%04d Sending %s",session->socket,path);
 	if((file=open(path,O_RDONLY|O_BINARY))==-1)
-		lprintf(LOG_WARNING,"%04d !ERROR %d opening %s",socket,errno,path);
+		lprintf(LOG_WARNING,"%04d !ERROR %d opening %s",session->socket,errno,path);
 	else {
-		if((ret=sendfilesocket(socket, file, &offset, 0)) < 0) {
-			lprintf(LOG_DEBUG,"%04d !ERROR %d sending %s"
-				, socket, errno, path);
-			ret=0;
+		while((i=read(file, buf, sizeof(buf)))>0) {
+			writebuf(session,buf,i);
 		}
 		close(file);
 	}
@@ -1086,7 +1090,7 @@ static void send_error(http_session_t * session, const char* message)
 					int	snt=0;
 
 					lprintf(LOG_INFO,"%04d Sending generated error page",session->socket);
-					snt=sock_sendfile(session->socket,session->req.physical_path);
+					snt=sock_sendfile(session,session->req.physical_path);
 					if(snt<0)
 						snt=0;
 					if(session->req.ld!=NULL)
@@ -1102,10 +1106,10 @@ static void send_error(http_session_t * session, const char* message)
 	if(!sent_ssjs) {
 		sprintf(session->req.physical_path,"%s%s.html",session->req.error_dir?session->req.error_dir:error_dir,error_code);
 		session->req.mime_type=get_mime_type(strrchr(session->req.physical_path,'.'));
-		send_headers(session,message);
+		send_headers(session,message,FALSE);
 		if(!stat(session->req.physical_path,&sb)) {
 			int	snt=0;
-			snt=sock_sendfile(session->socket,session->req.physical_path);
+			snt=sock_sendfile(session,session->req.physical_path);
 			if(snt<0)
 				snt=0;
 			if(session->req.ld!=NULL)
@@ -1121,7 +1125,7 @@ static void send_error(http_session_t * session, const char* message)
 				"please notify <a href=\"mailto:sysop@%s\">"
 				"%s</a></BODY></HTML>"
 				,error_code,error_code,error_code,scfg.sys_inetaddr,scfg.sys_op);
-			sockprint(session->socket,sbuf);
+			bufprint(session,sbuf);
 			if(session->req.ld!=NULL)
 				session->req.ld->size=strlen(sbuf);
 		}
@@ -2329,6 +2333,7 @@ static BOOL exec_cgi(http_session_t *session)
 	str_list_t	tmpbuf;
 	size_t	tmpbuflen=0;
 	BOOL	no_chunked=FALSE;
+	BOOL	set_chunked=FALSE;
 
 	SAFECOPY(cmdline,session->req.physical_path);
 
@@ -2433,16 +2438,10 @@ static BOOL exec_cgi(http_session_t *session)
 						int snt=0;
 						start=time(NULL);
 						if(session->req.method!=HTTP_HEAD) {
-							if(session->req.write_chunked) {
-								sprintf(header,"%X\r\n",i);
-								sock_sendbuf(session->socket,header,strlen(header),NULL);
-							}
-							snt=sock_sendbuf(session->socket,buf,i,NULL);
+							snt=writebuf(session,buf,i);
 							if(session->req.ld!=NULL) {
 								session->req.ld->size+=snt;
 							}
-							if(session->req.write_chunked)
-								sock_sendbuf(session->socket,newline,2,NULL);
 						}
 					}
 					else
@@ -2509,13 +2508,13 @@ static BOOL exec_cgi(http_session_t *session)
 					else  {
 						if(!no_chunked && session->http_ver>=HTTP_1_1) {
 							session->req.keep_alive=orig_keep;
-							session->req.write_chunked=TRUE;
+							set_chunked=TRUE;
 						}
 						if(got_valid_headers)  {
 							session->req.dynamic=IS_CGI;
 							if(cgi_status[0]==0)
 								SAFECOPY(cgi_status,session->req.status);
-							send_headers(session,cgi_status);
+							send_headers(session,cgi_status,set_chunked);
 						}
 						else {
 							/* Invalid headers... send 'er all as plain-text */
@@ -2533,34 +2532,22 @@ static BOOL exec_cgi(http_session_t *session)
 							/* Add the content-type header (REQUIRED) */
 							SAFEPRINTF2(content_type,"%s: %s",get_header(HEAD_TYPE),startup->default_cgi_content);
 							strListPush(&session->req.dynamic_heads,content_type);
-							send_headers(session,cgi_status);
+							send_headers(session,cgi_status,FALSE);
 
 							/* Now send the tmpbuf */
 							for(i=0; tmpbuf != NULL && tmpbuf[i] != NULL; i++) {
 								if(strlen(tmpbuf[i])>0) {
-									if(session->req.write_chunked) {
-										sprintf(header,"%X\r\n",strlen(tmpbuf[i]));
-										sock_sendbuf(session->socket,header,strlen(header),NULL);
-									}
-									snt=sock_sendbuf(session->socket,tmpbuf[i],strlen(tmpbuf[i]),NULL);
-									if(session->req.write_chunked)
-										sock_sendbuf(session->socket,newline,2,NULL);
+									snt=writebuf(session,tmpbuf[i],strlen(tmpbuf[i]));
 									if(session->req.ld!=NULL) {
 										session->req.ld->size+=snt;
 									}
 								}
 							}
 							if(strlen(fbuf)>0) {
-								if(session->req.write_chunked) {
-									sprintf(header,"%X\r\n",strlen(fbuf));
-									sock_sendbuf(session->socket,header,strlen(header),NULL);
-								}
-								snt=sock_sendbuf(session->socket,fbuf,strlen(fbuf),NULL);
+								snt=writebuf(session,fbuf,strlen(fbuf));
 								if(session->req.ld!=NULL && snt>0) {
 									session->req.ld->size+=snt;
 								}
-								if(session->req.write_chunked)
-									sock_sendbuf(session->socket,newline,2,NULL);
 							}
 							got_valid_headers=TRUE;
 						}
@@ -2630,16 +2617,10 @@ static BOOL exec_cgi(http_session_t *session)
 				int snt=0;
 				start=time(NULL);
 				if(session->req.method!=HTTP_HEAD) {
-					if(session->req.write_chunked) {
-						sprintf(header,"%X\r\n",i);
-						sock_sendbuf(session->socket,header,strlen(header),NULL);
-					}
-					snt=sock_sendbuf(session->socket,buf,i,NULL);
+					snt=writebuf(session,buf,i);
 					if(session->req.ld!=NULL) {
 						session->req.ld->size+=snt;
 					}
-					if(session->req.write_chunked)
-						sock_sendbuf(session->socket,newline,2,NULL);
 				}
 			}
 		}
@@ -2687,6 +2668,7 @@ static BOOL exec_cgi(http_session_t *session)
 	char	*value=NULL;
 	time_t	start;
 	BOOL	no_chunked=FALSE;
+	int		set_chunked=FALSE;
 
 	/* Win32-specific */
 	char*	env_block;
@@ -2881,24 +2863,18 @@ static BOOL exec_cgi(http_session_t *session)
 			session->req.dynamic=IS_CGI;
 			if(!no_chunked && session->http_ver>=HTTP_1_1) {
 				session->req.keep_alive=orig_keep;
-				session->req.write_chunked=TRUE;
+				set_chunked=TRUE;
 			}
 			strListPush(&session->req.dynamic_heads,content_type);
-			send_headers(session,cgi_status);
+			send_headers(session,cgi_status,set_chunked);
 		}
 		if(msglen) {
-			if(session->req.write_chunked) {
-				sprintf(header,"%X\r\n",msglen);
-				sock_sendbuf(session->socket,header,strlen(header),NULL);
-			}
 			lprintf(LOG_DEBUG,"%04d Sending %d bytes: %.*s"
 				,session->socket,msglen,msglen,buf);
-			wr=sock_sendbuf(session->socket,buf,msglen,NULL);
+			wr=writebuf(session,session->socket,buf,msglen,NULL);
 			/* log actual bytes sent */
 			if(session->req.ld!=NULL && wr>0)
 				session->req.ld->size+=wr;	
-			if(session->req.write_chunked)
-				sock_sendbuf(session->socket,newline,2,NULL);
 		}
 	}
 
@@ -3069,8 +3045,7 @@ js_writefunc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval,
 
 	if((!session->req.prev_write) && (!session->req.sent_headers)) {
 		if(session->http_ver>=HTTP_1_1 && session->req.keep_alive) {
-			session->req.write_chunked=TRUE;
-			if(!ssjs_send_headers(session))
+			if(!ssjs_send_headers(session,TRUE))
 				return(JS_FALSE);
 		}
 		else {
@@ -3082,7 +3057,7 @@ js_writefunc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval,
 			JS_GetProperty(cx, reply, "fast", &val);
 			if(JSVAL_IS_BOOLEAN(val) && JSVAL_TO_BOOLEAN(val)) {
 				session->req.keep_alive=FALSE;
-				if(!ssjs_send_headers(session))
+				if(!ssjs_send_headers(session,FALSE))
 					return(JS_FALSE);
 			}
 		}
@@ -3097,16 +3072,9 @@ js_writefunc(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval,
 			continue;
 		if(session->req.sent_headers) {
 			if(session->req.method!=HTTP_HEAD && session->req.method!=HTTP_OPTIONS) {
-				if(session->req.write_chunked) {
-					char	chstr[12];
-					sprintf(chstr,"%X\r\n", JS_GetStringLength(str)+(writeln?2:0));
-					sock_sendbuf(session->socket, chstr, strlen(chstr),NULL);
-				}
-				sock_sendbuf(session->socket, JS_GetStringBytes(str), JS_GetStringLength(str),NULL);
+				writebuf(session,JS_GetStringBytes(str), JS_GetStringLength(str));
 				if(writeln)
 					sock_sendbuf(session->socket, newline, 2,NULL);
-				if(session->req.write_chunked)
-					sock_sendbuf(session->socket, newline, 2,NULL);
 			}
 		}
 		else {
@@ -3369,7 +3337,7 @@ static BOOL js_setup(http_session_t* session)
 	return(TRUE);
 }
 
-static BOOL ssjs_send_headers(http_session_t* session)
+static BOOL ssjs_send_headers(http_session_t* session,int chunked)
 {
 	jsval		val;
 	JSObject*	reply;
@@ -3397,7 +3365,7 @@ static BOOL ssjs_send_headers(http_session_t* session)
 		}
 		JS_ClearScope(session->js_cx, headers);
 	}
-	return(send_headers(session,session->req.status));
+	return(send_headers(session,session->req.status,chunked));
 }
 
 static BOOL exec_ssjs(http_session_t* session, char* script)  {
@@ -3459,7 +3427,7 @@ static BOOL exec_ssjs(http_session_t* session, char* script)  {
 
 	/* Read http_reply object */
 	if(!session->req.sent_headers) {
-		retval=ssjs_send_headers(session);
+		retval=ssjs_send_headers(session,FALSE);
 	}
 
 	/* Free up temporary resources here */
@@ -3476,7 +3444,7 @@ static void respond(http_session_t * session)
 	BOOL		send_file=TRUE;
 
 	if(session->req.method==HTTP_OPTIONS) {
-		send_headers(session,session->req.status);
+		send_headers(session,session->req.status,FALSE);
 	}
 	else {
 		if(session->req.dynamic==IS_CGI)  {
@@ -3498,7 +3466,7 @@ static void respond(http_session_t * session)
 		}
 		else {
 			session->req.mime_type=get_mime_type(strrchr(session->req.physical_path,'.'));
-			send_file=send_headers(session,session->req.status);
+			send_file=send_headers(session,session->req.status,FALSE);
 		}
 	}
 	if(session->req.method==HTTP_HEAD || session->req.method==HTTP_OPTIONS)
@@ -3507,7 +3475,7 @@ static void respond(http_session_t * session)
 		int snt=0;
 		lprintf(LOG_INFO,"%04d Sending file: %s (%u bytes)"
 			,session->socket, session->req.physical_path, flength(session->req.physical_path));
-		snt=sock_sendfile(session->socket,session->req.physical_path);
+		snt=sock_sendfile(session,session->req.physical_path);
 		if(session->req.ld!=NULL) {
 			if(snt<0)
 				snt=0;
@@ -3594,6 +3562,75 @@ int read_post_data(http_session_t * session)
 	return(TRUE);
 }
 
+void http_output_thread(void *arg)
+{
+	http_session_t	*session=(http_session_t *)arg;
+	RingBuf	*obuf;
+	char	buf[OUTBUF_LEN+12];						/* *MUST* be large enough to hold the buffer,
+														the size of the buffer in hex, and four extra bytes. */
+	char	*bufdata;
+	int		failed=0;
+	int		len;
+	int		avail;
+	int		chunked;
+	int		i;
+
+	obuf=&(session->outbuf);
+
+	thread_up(TRUE /* setuid */);
+    while(session->socket!=INVALID_SOCKET && !terminate_server) {
+        /* Wait for something to output in the RingBuffer */
+        if(sem_trywait_block(&obuf->sem,1000))
+            continue;
+
+        /* Check for spurious sem post... */
+        if(!RingBufFull(obuf))
+            continue;
+
+        /* Wait for full buffer or drain timeout */
+        if(obuf->highwater_mark)
+            sem_trywait_block(&obuf->highwater_sem,startup->outbuf_drain_timeout);
+
+        /*
+         * At this point, there's something to send and,
+         * if the highwater mark is set, the timeout has
+         * passed or we've hit highwater.  Read ring buffer
+         * into linear buffer.
+         */
+        len=avail=RingBufFull(obuf);
+        if(avail>sizeof(buf)-12)
+            len=avail=sizeof(buf);
+
+		/* 
+		 * Read the current value of write_chunked... since we wait until the
+		 * ring buffer is empty before fiddling with it.
+		 */
+		chunked=session->req.write_chunked;
+
+		bufdata=buf;
+		if(chunked) {
+			i=sprintf(buf, "%X\r\n", avail);
+			bufdata+=i;
+			len+=i;
+		}
+
+        RingBufRead(obuf, bufdata, avail);
+		if(chunked) {
+			bufdata+=avail;
+			*(bufdata++)='\r';
+			*(bufdata++)='\n';
+			len+=2;
+		}
+
+		if(failed)
+			continue;
+
+		sock_sendbuf(session->socket, buf, len, &failed);
+    }
+	thread_down();
+	sem_post(&session->output_thread_terminated);
+}
+
 void http_session_thread(void* arg)
 {
 	int				i;
@@ -3622,6 +3659,17 @@ void http_session_thread(void* arg)
 	thread_up(TRUE /* setuid */);
 	session.finished=FALSE;
 
+	/* Start up the output buffer */
+	if(RingBufInit(&(session.outbuf), OUTBUF_LEN)) {
+		lprintf(LOG_ERR,"%04d Canot create output ringbuffer!", session.socket);
+		close_socket(session.socket);
+		thread_down();
+		return;
+	}
+
+	sem_init(&session.output_thread_terminated,0,0);
+	_beginthread(http_output_thread, 0, &session);
+
 	sbbs_srand();	/* Seed random number generator */
 
 	if(startup->options&BBS_OPT_NO_HOST_LOOKUP)
@@ -3644,6 +3692,9 @@ void http_session_thread(void* arg)
 			lprintf(LOG_INFO,"%04d HostAlias: %s", session.socket, host->h_aliases[i]);
 		if(trashcan(&scfg,host_name,"host")) {
 			close_socket(session.socket);
+			session.socket=INVALID_SOCKET;
+			sem_wait(&session.output_thread_terminated);
+			RingBufDispose(&session.outbuf);
 			lprintf(LOG_NOTICE,"%04d !CLIENT BLOCKED in host.can: %s", session.socket, host_name);
 			thread_down();
 			return;
@@ -3653,6 +3704,9 @@ void http_session_thread(void* arg)
 	/* host_ip wasn't defined in http_session_thread */
 	if(trashcan(&scfg,session.host_ip,"ip")) {
 		close_socket(session.socket);
+		session.socket=INVALID_SOCKET;
+		sem_wait(&session.output_thread_terminated);
+		RingBufDispose(&session.outbuf);
 		lprintf(LOG_NOTICE,"%04d !CLIENT BLOCKED in ip.can: %s", session.socket, session.host_ip);
 		thread_down();
 		return;
@@ -3762,6 +3816,9 @@ void http_session_thread(void* arg)
 #endif
 
 	close_socket(session.socket);
+	session.socket=INVALID_SOCKET;
+	sem_wait(&session.output_thread_terminated);
+	RingBufDispose(&session.outbuf);
 
 	active_clients--;
 	update_clients();
diff --git a/src/sbbs3/websrvr.h b/src/sbbs3/websrvr.h
index 55064b2582fa6b40c95637e4ac046bbb9a9299be..49f22f76181f6c237544ae4d699ec4e9a3e9e2b9 100644
--- a/src/sbbs3/websrvr.h
+++ b/src/sbbs3/websrvr.h
@@ -90,6 +90,8 @@ typedef struct {
 	uint	bind_retry_count;		/* Number of times to retry bind() calls */
 	uint	bind_retry_delay;		/* Time to wait between each bind() retry */
 	char	default_cgi_content[128];
+	WORD	outbuf_highwater_mark;	/* output block size control */
+	WORD	outbuf_drain_timeout;
 
 	/* JavaScript operating parameters */
 	js_startup_t js;