From 654003e06d93e4aa68be7abbbf214ab6f55d3420 Mon Sep 17 00:00:00 2001
From: hyung-hwan <hyunghwan.chung@gmail.com>
Date: Mon, 9 Apr 2012 15:29:33 +0000
Subject: [PATCH] fixed a bug of sending http/1.0 taken from the peer while
 chunking. enhanced proxy handling in general

---
 qse/include/qse/net/htrd.h |  14 +++--
 qse/include/qse/net/htre.h |   7 +--
 qse/lib/net/htrd.c         |  93 +++++++++++++++++++++++++++------
 qse/lib/net/httpd-task.c   | 104 +++++++++++++++++++++++--------------
 qse/lib/net/httpd.c        |  10 ++--
 qse/samples/net/http01.c   |  64 +++++++++++++++--------
 6 files changed, 206 insertions(+), 86 deletions(-)

diff --git a/qse/include/qse/net/htrd.h b/qse/include/qse/net/htrd.h
index 2bd316bf..dadb70c0 100644
--- a/qse/include/qse/net/htrd.h
+++ b/qse/include/qse/net/htrd.h
@@ -74,6 +74,8 @@ struct qse_htrd_t
 	{
 		struct
 		{
+			int flags;
+
 			int crlf; /* crlf status */
 			qse_size_t plen; /* raw request length excluding crlf */
 			qse_size_t need; /* number of octets needed for contents */
@@ -86,8 +88,7 @@ struct qse_htrd_t
 			} chunk;
 		} s; /* state */
 
-
-		/* buffers needed to for processing a request */
+		/* buffers needed for processing a request */
 		struct
 		{
 			qse_htob_t raw; /* buffer to hold raw octets */
@@ -163,8 +164,13 @@ int qse_htrd_feed (
 	qse_size_t         len   /**< number of octets */
 );
 
-int qse_htrd_read (
-	qse_htrd_t* htrd /**< htrd */
+/**
+ * The qse_htrd_halt() function indicates the end of feeeding
+ * if the current response should be processed until the 
+ * connection is closed.
+ */ 
+int qse_htrd_halt (
+	qse_htrd_t* htrd
 );
 
 int qse_htrd_scanqparam (
diff --git a/qse/include/qse/net/htre.h b/qse/include/qse/net/htre.h
index e801b114..07460c1b 100644
--- a/qse/include/qse/net/htre.h
+++ b/qse/include/qse/net/htre.h
@@ -81,10 +81,11 @@ struct qse_htre_t
 	/* special attributes derived from the header */
 	struct
 	{
-		int chunked;		
-		int content_length_set;
+#define QSE_HTRE_ATTR_CHUNKED   (1 << 0)
+#define QSE_HTRE_ATTR_LENGTH    (1 << 1)
+#define QSE_HTRE_ATTR_KEEPALIVE (1 << 2)
+		int flags;
 		qse_size_t content_length;
-		int keepalive;
 		const qse_mchar_t* expect;
 		const qse_mchar_t* status;
 	} attr;
diff --git a/qse/lib/net/htrd.c b/qse/lib/net/htrd.c
index 247ec259..b75eb93f 100644
--- a/qse/lib/net/htrd.c
+++ b/qse/lib/net/htrd.c
@@ -26,6 +26,8 @@ QSE_IMPLEMENT_COMMON_FUNCTIONS (htrd)
 
 static const qse_mchar_t NUL = QSE_MT('\0');
 
+#define CONSUME_UNTIL_CLOSE (1 << 0)
+
 static QSE_INLINE int is_whspace_octet (qse_mchar_t c)
 {
 	return c == QSE_MT(' ') || c == QSE_MT('\t') || c == QSE_MT('\r') || c == QSE_MT('\n');
@@ -425,7 +427,7 @@ static qse_mchar_t* parse_initial_line (
 	if (htrd->re.version.major > 1 || 
 	    (htrd->re.version.major == 1 && htrd->re.version.minor >= 1))
 	{
-		htrd->re.attr.keepalive = 1;
+		htrd->re.attr.flags |= QSE_HTRE_ATTR_KEEPALIVE;
 	}
 
 	return ++p;
@@ -469,7 +471,7 @@ static int capture_connection (qse_htrd_t* htrd, qse_htb_pair_t* pair)
 		"close", 5);
 	if (n == 0)
 	{
-		htrd->re.attr.keepalive = 0;
+		htrd->re.attr.flags &= ~QSE_HTRE_ATTR_KEEPALIVE;
 		return 0;
 	}
 
@@ -478,7 +480,7 @@ static int capture_connection (qse_htrd_t* htrd, qse_htb_pair_t* pair)
 		"keep-alive", 10);
 	if (n == 0)
 	{
-		htrd->re.attr.keepalive = 1;
+		htrd->re.attr.flags |= QSE_HTRE_ATTR_KEEPALIVE;
 		return 0;
 	}
 
@@ -494,7 +496,7 @@ static int capture_connection (qse_htrd_t* htrd, qse_htb_pair_t* pair)
 	if (htrd->re.version.major < 1  || 
 	    (htrd->re.version.major == 1 && htrd->re.version.minor <= 0))
 	{
-		htrd->re.attr.keepalive = 0;
+		htrd->re.attr.flags &= ~QSE_HTRE_ATTR_KEEPALIVE;
 	}
 	return 0;
 }
@@ -533,7 +535,7 @@ static int capture_content_length (qse_htrd_t* htrd, qse_htb_pair_t* pair)
 		return -1;
 	}
 
-	if (htrd->re.attr.chunked && len > 0)
+	if ((htrd->re.attr.flags & QSE_HTRE_ATTR_CHUNKED) && len > 0)
 	{
 		/* content-length is greater than 0 
 		 * while transfer-encoding: chunked is specified. */
@@ -541,8 +543,8 @@ static int capture_content_length (qse_htrd_t* htrd, qse_htb_pair_t* pair)
 		return -1;
 	}
 
+	htrd->re.attr.flags |= QSE_HTRE_ATTR_LENGTH;
 	htrd->re.attr.content_length = len;
-	htrd->re.attr.content_length_set = 1;
 	return 0;
 }
 
@@ -567,13 +569,13 @@ static int capture_transfer_encoding (qse_htrd_t* htrd, qse_htb_pair_t* pair)
 	if (n == 0)
 	{
 		/* if (htrd->re.attr.content_length > 0) */
-		if (htrd->re.attr.content_length_set)
+		if (htrd->re.attr.flags & QSE_HTRE_ATTR_LENGTH)
 		{
 			/* both content-length and 'transfer-encoding: chunked' are specified. */
 			goto badre;
 		}
 
-		htrd->re.attr.chunked = 1;
+		htrd->re.attr.flags |= QSE_HTRE_ATTR_CHUNKED;
 		return 0;
 	}
 
@@ -1111,8 +1113,7 @@ int qse_htrd_feed (qse_htrd_t* htrd, const qse_mchar_t* req, qse_size_t len)
 					/* reset the raw request length */
 					htrd->fed.s.plen = 0;
 	
-					if (parse_initial_line_and_headers (htrd, req, ptr - req) <= -1)
-						return -1;
+					if (parse_initial_line_and_headers (htrd, req, ptr - req) <= -1) return -1;
 
 					/* compelete request header is received */
 					header_completed_during_this_feed = 1;
@@ -1149,10 +1150,10 @@ int qse_htrd_feed (qse_htrd_t* htrd, const qse_mchar_t* req, qse_size_t len)
 					}
 
 					/* carry on processing content body fed together with the header */
-					if (htrd->re.attr.chunked)
+					if (htrd->re.attr.flags & QSE_HTRE_ATTR_CHUNKED)
 					{
 						/* transfer-encoding: chunked */
-						QSE_ASSERT (!htrd->re.attr.content_length_set);
+						QSE_ASSERT (!(htrd->re.attr.flags & QSE_HTRE_ATTR_LENGTH));
 	
 					dechunk_start:
 						htrd->fed.s.chunk.phase = GET_CHUNK_LEN;
@@ -1195,7 +1196,31 @@ int qse_htrd_feed (qse_htrd_t* htrd, const qse_mchar_t* req, qse_size_t len)
 					{
 						/* we need to read as many octets as
 						 * Content-Length */
-						htrd->fed.s.need = htrd->re.attr.content_length;
+						if ((htrd->option & QSE_HTRD_RESPONSE) && 
+						    !(htrd->re.attr.flags & QSE_HTRE_ATTR_LENGTH) &&
+						    !(htrd->re.attr.flags & QSE_HTRE_ATTR_KEEPALIVE))
+						{
+							/* for a response, no content-length and 
+							 * no chunk are specified and 'connection' 
+							 * is to close. i must read until the 
+							 * connection is closed. however, there isn't 
+							 * any good way to know when to stop from 
+							 * within this function. so the caller
+							 * can call qse_htrd_halt() for this. */
+
+							/* set this to the maximum in a type safe way
+							 * assuming it's unsigned. the problem of
+							 * the current implementation is that 
+							 * it can't receive more than  */
+							htrd->fed.s.need = 0;
+							htrd->fed.s.need = ~htrd->fed.s.need; 
+							htrd->fed.s.flags |= CONSUME_UNTIL_CLOSE;
+						}
+						else
+						{
+							htrd->fed.s.need = htrd->re.attr.content_length;
+						}
+				
 					}
 
 					if (htrd->fed.s.need > 0)
@@ -1209,7 +1234,17 @@ int qse_htrd_feed (qse_htrd_t* htrd, const qse_mchar_t* req, qse_size_t len)
 						{
 							/* the data is not as large as needed */
 							if (push_content (htrd, ptr, avail) <= -1) return -1;
-							htrd->fed.s.need -= avail;
+
+							if (!(htrd->fed.s.flags & CONSUME_UNTIL_CLOSE)) 
+							{
+								/* i don't decrement htrd->fed.s.need
+								 * if i should read until connection is closed.
+								 * well, unless set your own callback,
+								 * push_content() above will fail 
+								 * if too much has been received already */
+								htrd->fed.s.need -= avail;
+							}
+
 							/* we didn't get a complete content yet */
 							goto feedme_more; 
 						}
@@ -1218,7 +1253,8 @@ int qse_htrd_feed (qse_htrd_t* htrd, const qse_mchar_t* req, qse_size_t len)
 							/* we got all or more than needed */
 							if (push_content (htrd, ptr, htrd->fed.s.need) <= -1) return -1;
 							ptr += htrd->fed.s.need;
-							htrd->fed.s.need = 0;
+							if (!(htrd->fed.s.flags & CONSUME_UNTIL_CLOSE)) 
+								htrd->fed.s.need = 0;
 						}
 					}
 	
@@ -1361,6 +1397,33 @@ feedme_more:
 	return 0;
 }
 
+int qse_htrd_halt (qse_htrd_t* htrd)
+{
+	if (htrd->fed.s.flags & CONSUME_UNTIL_CLOSE)
+	{
+		qse_htre_completecontent (&htrd->re);
+
+		if (htrd->recbs->handle)
+		{
+			int n;
+			htrd->errnum = QSE_HTRD_ENOERR;
+			n = htrd->recbs->handle (htrd, &htrd->re);
+			if (n <= -1)
+			{
+				if (htrd->errnum == QSE_HTRD_ENOERR)
+					htrd->errnum = QSE_HTRD_ERECBS;	
+				/* need to clear request on error? 
+				clear_feed (htrd); */
+				return -1;
+			}
+		}
+
+		clear_feed (htrd);
+	}
+
+	return 0;
+}
+
 #if 0
 int qse_htrd_scanqparam (qse_htrd_t* htrd, const qse_mcstr_t* cstr)
 {
diff --git a/qse/lib/net/httpd-task.c b/qse/lib/net/httpd-task.c
index e8742952..73f6e7ee 100644
--- a/qse/lib/net/httpd-task.c
+++ b/qse/lib/net/httpd-task.c
@@ -36,6 +36,11 @@
 
 #define MAX_SEND_SIZE 4096
 
+/* TODO:
+ * many functions in this file use qse_size_t.
+ * so the size data transfers is limited by this type.
+ * break this barrier... */
+
 static qse_http_version_t http_v11 = { 1, 1 };
 /*------------------------------------------------------------------------*/
 
@@ -400,7 +405,8 @@ qse_httpd_task_t* qse_httpd_entaskerror (
 {
 	return entask_error (
 		httpd, client, pred, code,
-		qse_htre_getversion(req), req->attr.keepalive);
+		qse_htre_getversion(req), 
+		(req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE));
 }
 
 /*------------------------------------------------------------------------*/
@@ -431,7 +437,7 @@ qse_httpd_task_t* qse_httpd_entaskauth (
 		httpd, client, pred,
 		QSE_MT("HTTP/%d.%d 401 Unauthorized\r\nConnection: %s\r\nWWW-Authenticate: Basic realm=\"%s\"\r\nContent-Type: text/html\r\nContent-Length: %lu\r\n\r\n%s\r\n\r\n"),
 		version->major, version->minor, 
-		(req->attr.keepalive? QSE_MT("keep-alive"): QSE_MT("close")),
+		((req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE)? QSE_MT("keep-alive"): QSE_MT("close")),
 		realm, (unsigned long)qse_mbslen(lmsg) + 4, lmsg);
 }
 
@@ -1196,7 +1202,7 @@ qse_httpd_task_t* qse_httpd_entaskfile (
 	QSE_MEMSET (&data, 0, QSE_SIZEOF(data));
 	data.path = path;
 	data.version = *qse_htre_getversion(req);
-	data.keepalive = req->attr.keepalive;
+	data.keepalive = (req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE);
 
 	tmp = qse_htre_getheaderval(req, QSE_MT("Range"));
 	if (tmp) 
@@ -1414,7 +1420,7 @@ static int cgi_htrd_peek_script_output (qse_htrd_t* htrd, qse_htre_t* req)
 	}
 
 	keepalive = cgi->keepalive;
-	if (req->attr.content_length_set) 
+	if (req->attr.flags & QSE_HTRE_ATTR_LENGTH)
 	{
 		cgi->resflags |= CGI_RES_SCRIPT_LENGTH;
 		cgi->script_output_length = req->attr.content_length;
@@ -1805,7 +1811,7 @@ static int task_init_cgi (
 	qse_mbscpy ((qse_mchar_t*)(cgi + 1), arg->path);
 	cgi->path = (qse_mchar_t*)(cgi + 1);
 	cgi->version = *qse_htre_getversion(arg->req);
-	cgi->keepalive = arg->req->attr.keepalive;
+	cgi->keepalive = (arg->req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE);
 	cgi->nph = arg->nph;
 	cgi->req = QSE_NULL;	
 
@@ -1826,7 +1832,7 @@ static int task_init_cgi (
 	}
 
 	if (!(arg->req->state & QSE_HTRE_COMPLETED) &&
-	    !arg->req->attr.content_length_set)
+	    !(arg->req->attr.flags & QSE_HTRE_ATTR_LENGTH))
 	{
 		/* if the request is not completed and doesn't have
 		 * content-length set, it's not really possible to
@@ -1859,8 +1865,9 @@ static int task_init_cgi (
 			 * should reach here. if content-length is set
 			 * the length should match len. */
 			QSE_ASSERT (len > 0);
-			QSE_ASSERT (!arg->req->attr.content_length_set ||
-			            (arg->req->attr.content_length_set && arg->req->attr.content_length == len));
+			QSE_ASSERT (!(arg->req->attr.flags & QSE_HTRE_ATTR_LENGTH) ||
+			            ((arg->req->attr.flags & QSE_HTRE_ATTR_LENGTH) && 
+			             arg->req->attr.content_length == len));
 			cgi->reqflags |= CGI_REQ_GOTALL;
 			content_length = len;
 		}
@@ -1885,7 +1892,7 @@ static int task_init_cgi (
 			cgi->req = arg->req; 
 			qse_htre_setconcb (cgi->req, cgi_snatch_client_input, task);
 
-			QSE_ASSERT (arg->req->attr.content_length_set);
+			QSE_ASSERT (arg->req->attr.flags & QSE_HTRE_ATTR_LENGTH);
 			content_length = arg->req->attr.content_length;
 		}
 	}
@@ -2663,7 +2670,6 @@ static int proxy_snatch_peer_output (
 	 * the client should be chunked */
 	QSE_ASSERT (proxy->resflags & PROXY_RES_CLIENT_CHUNK);
 
-
 	/* TODO: better condition for compaction??? */
 	if (proxy->res_pending > 0 && proxy->res_consumed > 0)
 	{
@@ -2740,6 +2746,7 @@ static int proxy_htrd_peek_peer_output (qse_htrd_t* htrd, qse_htre_t* res)
 		/* this peek handler is being called again. 
 		 * this can happen if qse_htrd_feed() is fed with
 		 * multiple responses in task_main_proxy_2 (). */
+qse_printf (QSE_T("XXXXXXXXXXXXXXXXXXXXXXxx\n"));
 		proxy->httpd->errnum = QSE_HTTPD_EINVAL;
 		return -1;
 	}
@@ -2772,17 +2779,9 @@ qse_printf (QSE_T("10000000000000000000000000000 CONTINUE 1000000000000000000000
 		proxy->resflags |= PROXY_RES_RECEIVED_RESHDR;
 
 qse_printf (QSE_T("NORMAL REPLY 222222222222222222222 NORMAL REPLY\n"));
-		/* begin initial line */
-		if (qse_mbs_cat (proxy->res, qse_htre_getverstr(res)) == (qse_size_t)-1) return -1;
-		if (qse_mbs_cat (proxy->res, QSE_MT(" ")) == (qse_size_t)-1) return -1;;
-		if (qse_mbs_cat (proxy->res, qse_htre_getscodestr(res)) == (qse_size_t)-1) return -1;
-		if (qse_mbs_cat (proxy->res, QSE_MT(" ")) == (qse_size_t)-1) return -1;
-		if (qse_mbs_cat (proxy->res, qse_htre_getsmesg(res)) == (qse_size_t)-1) return -1;
-		if (qse_mbs_cat (proxy->res, QSE_MT("\r\n")) == (qse_size_t)-1) return -1; 
-		/* end initial line */
 
 		keepalive = proxy->keepalive;
-		if (res->attr.content_length_set) 
+		if (res->attr.flags & QSE_HTRE_ATTR_LENGTH)
 		{
 			/* the response from the peer is length based */
 			proxy->resflags |= PROXY_RES_PEER_LENGTH;
@@ -2797,13 +2796,7 @@ qse_printf (QSE_T("NORMAL REPLY 222222222222222222222 NORMAL REPLY\n"));
 				/* chunk response when writing back to client */
 				proxy->resflags |= PROXY_RES_CLIENT_CHUNK;
 
-				if (qse_mbs_cat (proxy->res, QSE_MT("Transfer-Encoding: chunked\r\n")) == (qse_size_t)-1) 
-				{
-					proxy->httpd->errnum = QSE_HTTPD_ENOMEM;
-					return -1;
-				}
-
-				if (res->attr.chunked)
+				if (res->attr.flags & QSE_HTRE_ATTR_CHUNKED)
 				{
 					/* mark the peer output is chunked */
 					proxy->resflags |= PROXY_RES_PEER_CHUNK;
@@ -2822,13 +2815,42 @@ qse_printf (QSE_T("NORMAL REPLY 222222222222222222222 NORMAL REPLY\n"));
 				proxy->resflags |= PROXY_RES_CLIENT_DISCON;
 				if (qse_httpd_entaskdisconnect (proxy->httpd, xtn->client, xtn->task) == QSE_NULL) return -1;
 
-				if (res->attr.chunked)
+				if (res->attr.flags & QSE_HTRE_ATTR_CHUNKED)
 					proxy->resflags |= PROXY_RES_PEER_CHUNK;
 				else
 					proxy->resflags |= PROXY_RES_PEER_CLOSE;
 			}
 		}
 
+		/* begin initial line */
+		if (proxy->resflags & PROXY_RES_CLIENT_CHUNK &&
+		    qse_comparehttpversions (&res->version, &http_v11) < 0)
+		{
+			qse_mchar_t vbuf[64];
+			snprintf (vbuf, QSE_COUNTOF(vbuf), QSE_MT("HTTP/%d.%d"), 
+				(int)proxy->version.major, (int)proxy->version.minor);
+			if (qse_mbs_cat (proxy->res, vbuf) == (qse_size_t)-1) return -1;
+		}
+		else
+		{
+			if (qse_mbs_cat (proxy->res, qse_htre_getverstr(res)) == (qse_size_t)-1) return -1;
+		}
+		if (qse_mbs_cat (proxy->res, QSE_MT(" ")) == (qse_size_t)-1) return -1;;
+		if (qse_mbs_cat (proxy->res, qse_htre_getscodestr(res)) == (qse_size_t)-1) return -1;
+		if (qse_mbs_cat (proxy->res, QSE_MT(" ")) == (qse_size_t)-1) return -1;
+		if (qse_mbs_cat (proxy->res, qse_htre_getsmesg(res)) == (qse_size_t)-1) return -1;
+		if (qse_mbs_cat (proxy->res, QSE_MT("\r\n")) == (qse_size_t)-1) return -1; 
+		/* end initial line */
+
+		if (proxy->resflags & PROXY_RES_CLIENT_CHUNK)
+		{
+			if (qse_mbs_cat (proxy->res, QSE_MT("Transfer-Encoding: chunked\r\n")) == (qse_size_t)-1) 
+			{
+				proxy->httpd->errnum = QSE_HTTPD_ENOMEM;
+				return -1;
+			}
+		}
+
 		if (qse_mbs_cat (proxy->res, (keepalive? QSE_MT("Connection: keep-alive\r\n"): QSE_MT("Connection: close\r\n"))) == (qse_size_t)-1) 
 		{
 			proxy->httpd->errnum = QSE_HTTPD_ENOMEM;
@@ -2883,6 +2905,7 @@ qse_printf (QSE_T("PROXY PEER FUCKED - RETURNING TOO MUCH...\n"));
 			/* arrange to store further contents received to proxy->res */
 			qse_htre_setconcb (res, proxy_snatch_peer_output, xtn->task);
 		}
+qse_printf (QSE_T("NORMAL REPLY 222222222222222222222 NORMAL REPLY OK\n"));
 	}
 
 	proxy->res_pending = QSE_MBS_LEN(proxy->res) - proxy->res_consumed;
@@ -3000,7 +3023,7 @@ static int task_init_proxy (
 	proxy->httpd = httpd;
 
 	proxy->version = *qse_htre_getversion(arg->req);
-	proxy->keepalive = arg->req->attr.keepalive;
+	proxy->keepalive = (arg->req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE);
 	proxy->peer.nwad = arg->peer_nwad;
 	proxy->req = QSE_NULL;
 
@@ -3051,7 +3074,7 @@ len = 1024;
 	}
 
 	if (!(arg->req->state & QSE_HTRE_COMPLETED) &&
-	    !arg->req->attr.content_length_set)
+	    !(arg->req->attr.flags & QSE_HTRE_ATTR_LENGTH))
 	{
 		/* if the request is not completed and doesn't have
 		 * content-length set, it's not really possible to
@@ -3074,8 +3097,9 @@ len = 1024;
 			 * should reach here. if content-length is set
 			 * the length should match len. */
 			QSE_ASSERT (len > 0);
-			QSE_ASSERT (!arg->req->attr.content_length_set ||
-			            (arg->req->attr.content_length_set && arg->req->attr.content_length == len));
+			QSE_ASSERT (!(arg->req->attr.flags & QSE_HTRE_ATTR_LENGTH) ||
+			            ((arg->req->attr.flags & QSE_HTRE_ATTR_LENGTH) && 
+			             arg->req->attr.content_length == len));
 		}
 		else
 		{
@@ -3093,7 +3117,7 @@ len = 1024;
 			 * htrd calls invokes this callback. */
 			proxy->req = arg->req;
 			qse_htre_setconcb (proxy->req, proxy_snatch_client_input, task);
-			QSE_ASSERT (arg->req->attr.content_length_set);
+			QSE_ASSERT (arg->req->attr.flags & QSE_HTRE_ATTR_LENGTH);
 		}
 	}
 
@@ -3449,20 +3473,20 @@ qse_printf (QSE_T("#####PREMATURE EOF FROM PEER\n"));
 			}
 			else 
 			{
-qse_printf (QSE_T("#####PREMATURE EOF FROM PEER CLIENT CHUNK\n"));
 				QSE_ASSERT (proxy->resflags & PROXY_RES_CLIENT_CHUNK);
 
 				if (proxy->resflags & PROXY_RES_PEER_CLOSE)
 				{
-					/* i should compelte the content manually
-					 * since the end of content is indicated by
-					 * close in this case. */
-					qse_htre_completecontent (&proxy->peer_htrd->re);
+					/* i should stop the reader manually since the 
+					 * end of content is indicated by close in this
+					 * case. call qse_htrd_halt() for this. */
+					qse_htrd_halt (proxy->peer_htrd);
 					task->main = task_main_proxy_3;
 					task->trigger[2].mask |= QSE_HTTPD_TASK_TRIGGER_WRITE;
 					return 1;
 				}
 
+qse_printf (QSE_T("#####PREMATURE EOF FROM PEER CLIENT CHUNK\n"));
 				return -1;
 			}
 		}
@@ -3550,6 +3574,7 @@ static int task_main_proxy_1 (
 			/* improve error conversion */
 			if (httpd->errnum == QSE_HTTPD_ENOENT) http_errnum = 404;
 			else if (httpd->errnum == QSE_HTTPD_EACCES) http_errnum = 403;
+qse_printf (QSE_T("task_main_proxy_1.... ERROR \n"));
 			goto oops;
 		}
 
@@ -3589,11 +3614,13 @@ static int task_main_proxy (
 	proxy_peer_htrd_xtn_t* xtn;
 	int http_errnum = 500;
 	int n;
+qse_printf (QSE_T("task_main_proxy....\n"));
 
 	if (proxy->init_failed) goto oops;
 
 	/* set up a http reader to read a response from the peer */
-	proxy->peer_htrd = qse_htrd_open (httpd->mmgr, QSE_SIZEOF(proxy_peer_htrd_xtn_t));
+	proxy->peer_htrd = qse_htrd_open (
+		httpd->mmgr, QSE_SIZEOF(proxy_peer_htrd_xtn_t));
 	if (proxy->peer_htrd == QSE_NULL) goto oops;
 	xtn = (proxy_peer_htrd_xtn_t*) qse_htrd_getxtn (proxy->peer_htrd);
 	xtn->proxy = proxy;
@@ -3614,6 +3641,7 @@ static int task_main_proxy (
 /* TODO: translate error code to http error... */
 		if (httpd->errnum == QSE_HTTPD_ENOENT) http_errnum = 404;
 		else if (httpd->errnum == QSE_HTTPD_EACCES) http_errnum = 403;
+qse_printf (QSE_T("caanot open peer....\n"));
 		goto oops;
 	}
 
diff --git a/qse/lib/net/httpd.c b/qse/lib/net/httpd.c
index 3696eac2..4ac6a2c1 100644
--- a/qse/lib/net/httpd.c
+++ b/qse/lib/net/httpd.c
@@ -302,8 +302,9 @@ static qse_httpd_client_t* new_client (
 	client->status = tmpl->status;
 	client->handle = tmpl->handle;
 	client->handle2 = tmpl->handle2;
-	client->local_addr = tmpl->local_addr;
 	client->remote_addr = tmpl->remote_addr;
+	client->local_addr = tmpl->local_addr;
+	client->orgdst_addr = tmpl->orgdst_addr;
 
 	xtn = (htrd_xtn_t*)qse_htrd_getxtn (client->htrd);	
 	xtn->httpd = httpd;
@@ -458,10 +459,11 @@ qse_printf (QSE_T("MUX ADDHND CLIENT READ %d\n"), client->handle.i);
 
 {
 /* TODO: proper logging */
-qse_char_t tmp[128], tmp2[128];
+qse_char_t tmp[128], tmp2[128], tmp3[128];
 qse_nwadtostr (&client->local_addr, tmp, QSE_COUNTOF(tmp), QSE_NWADTOSTR_ALL);
-qse_nwadtostr (&client->remote_addr, tmp2, QSE_COUNTOF(tmp2), QSE_NWADTOSTR_ALL);
-qse_printf (QSE_T("connection %d accepted %s from %s\n"), client->handle.i, tmp, tmp2);
+qse_nwadtostr (&client->orgdst_addr, tmp2, QSE_COUNTOF(tmp2), QSE_NWADTOSTR_ALL);
+qse_nwadtostr (&client->remote_addr, tmp3, QSE_COUNTOF(tmp3), QSE_NWADTOSTR_ALL);
+qse_printf (QSE_T("connection %d accepted %s(%s from %s\n"), client->handle.i, tmp, tmp2, tmp3);
 }
 	}
 	return 0;
diff --git a/qse/samples/net/http01.c b/qse/samples/net/http01.c
index daf45e78..e74ffdc3 100644
--- a/qse/samples/net/http01.c
+++ b/qse/samples/net/http01.c
@@ -141,6 +141,7 @@ static qse_httpd_errnum_t syserr_to_errnum (int e)
 			return QSE_HTTPD_EINVAL;
 
 		case EACCES:
+		case ECONNREFUSED:
 			return QSE_HTTPD_EACCES;
 
 		case ENOENT:
@@ -414,12 +415,6 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: too many client?\n"));
 	}
 #endif
 
-{
-qse_char_t buf[100];
-qse_nwadtostr (&client->orgdst_addr, buf, QSE_COUNTOF(buf), QSE_NWADTOSTR_ALL);
-qse_printf (QSE_T("ORGDST address : (%s)\n"), buf);
-}
-		
 	client->handle.i = fd;
 	return 0;
 }
@@ -487,7 +482,11 @@ static int peer_connected (qse_httpd_t* httpd, qse_httpd_peer_t* peer)
 	if (getsockopt (peer->handle.i, SOL_SOCKET, SO_ERROR, &ret, &len) <= -1) return -1;
 
 	if (ret == EINPROGRESS) return 0;
-	if (ret != 0) return -1;
+	if (ret != 0) 
+	{
+		qse_httpd_seterrnum (httpd, syserr_to_errnum (ret));
+		return -1;
+	}
 
 	return 1; /* connection completed */
 }
@@ -533,8 +532,8 @@ struct mux_t
 
 	struct
 	{
-		struct mux_ev_t* ptr;
-		qse_size_t       capa;
+		struct mux_ev_t** ptr;
+		qse_size_t        capa;
 	} mev;
 };
 
@@ -564,7 +563,13 @@ static void mux_close (qse_httpd_t* httpd, void* vmux)
 {
 	struct mux_t* mux = (struct mux_t*)vmux;
 	if (mux->ee.ptr) qse_httpd_freemem (httpd, mux->ee.ptr);
-	if (mux->mev.ptr) qse_httpd_freemem (httpd, mux->mev.ptr);
+	if (mux->mev.ptr) 
+	{
+		qse_size_t i;
+		for (i = 0; i < mux->mev.capa; i++)
+			if (mux->mev.ptr[i]) qse_httpd_freemem (httpd, mux->mev.ptr[i]);
+		qse_httpd_freemem (httpd, mux->mev.ptr);
+	}
 	close (mux->fd);
 	qse_httpd_freemem (httpd, mux);
 }
@@ -589,20 +594,33 @@ static int mux_addhnd (
 
 	if (handle.i >= mux->mev.capa)
 	{
-		struct mux_ev_t* tmp;
-		qse_size_t tmpcapa;
+		struct mux_ev_t** tmp;
+		qse_size_t tmpcapa, i;
 	
 		tmpcapa = (((handle.i + MUX_EV_ALIGN) / MUX_EV_ALIGN) * MUX_EV_ALIGN);
 
-		tmp = qse_httpd_reallocmem (
+		tmp = (struct mux_ev_t**) qse_httpd_reallocmem (
 			httpd, mux->mev.ptr, 
 			QSE_SIZEOF(*mux->mev.ptr) * tmpcapa); 
 		if (tmp == QSE_NULL) return -1;
 
+		for (i = mux->mev.capa; i < tmpcapa; i++) tmp[i] = QSE_NULL;
 		mux->mev.ptr = tmp;
 		mux->mev.capa = tmpcapa;
 	}
 
+	if (mux->mev.ptr[handle.i] == QSE_NULL) 
+	{
+		/* the location of the data passed to epoll_ctl()
+		 * must not change unless i update the info with epoll()
+		 * whenever there is reallocation. so i simply
+		 * make mux-mev.ptr reallocatable but auctual
+		 * data fixed once allocated. */
+		mux->mev.ptr[handle.i] = qse_httpd_allocmem (
+			httpd, QSE_SIZEOF(*mux->mev.ptr[handle.i]));
+		if (mux->mev.ptr[handle.i] == QSE_NULL) return -1;
+	}	
+
 	if (mux->ee.len >= mux->ee.capa)
 	{
 		struct epoll_event* tmp;
@@ -616,8 +634,7 @@ static int mux_addhnd (
 		mux->ee.capa = (mux->ee.capa + 1) * 2;
 	}
 
-	mev = &mux->mev.ptr[handle.i];
-
+	mev = mux->mev.ptr[handle.i];
 	mev->handle = handle;
 	mev->reqmask = mask;
 	mev->cbfun = cbfun;
@@ -1070,7 +1087,7 @@ if (qse_htre_getcontentlen(req) > 0)
 			if (peek)
 			{
 				/* cgi */
-				if (req->attr.chunked)
+				if (req->attr.flags & QSE_HTRE_ATTR_CHUNKED)
 				{
 qse_printf (QSE_T("chunked cgi... delaying until contents are received\n"));
 				#if 0
@@ -1081,10 +1098,9 @@ qse_printf (QSE_T("chunked cgi... delaying until contents are received\n"));
 					if (task) qse_httpd_entaskdisconnect (httpd, client, QSE_NULL);
 				#endif
 				}
-				else if (method == QSE_HTTP_POST && 
-				         !req->attr.content_length_set)
+				else if (method == QSE_HTTP_POST && !(req->attr.flags & QSE_HTRE_ATTR_LENGTH))
 				{
-					req->attr.keepalive = 0;
+					req->attr.flags &= ~QSE_HTRE_ATTR_KEEPALIVE;
 					task = qse_httpd_entaskerror (
 						httpd, client, QSE_NULL, 411, req);
 					/* 411 can't keep alive */
@@ -1101,7 +1117,7 @@ qse_printf (QSE_T("chunked cgi... delaying until contents are received\n"));
 			{
 				/* to support the chunked request,
 				 * i need to wait until it's completed and invoke cgi */
-				if (req->attr.chunked)
+				if (req->attr.flags & QSE_HTRE_ATTR_CHUNKED)
 				{
 qse_printf (QSE_T("Entasking chunked CGI...\n"));
 					task = qse_httpd_entaskcgi (
@@ -1161,7 +1177,7 @@ qse_printf (QSE_T("Entasking chunked CGI...\n"));
 		}
 	}
 
-	if (!req->attr.keepalive)
+	if (!(req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE))
 	{
 		if (!peek)
 		{
@@ -1236,6 +1252,7 @@ qse_printf (QSE_T("Host not included....\n"));
 	{
 		qse_nwad_t nwad;
 
+#if 0
 		if (qse_nwadequal (&client->local_addr, &client->orgdst_addr))
 		{
 			//qse_strtonwad (QSE_T("192.168.1.55:9000"), &nwad);
@@ -1243,13 +1260,16 @@ qse_printf (QSE_T("Host not included....\n"));
 		}
 		else
 		{
+#endif
 			nwad = client->orgdst_addr;
+#if 0
 		}
+#endif
 		task = qse_httpd_entaskproxy (httpd, client, QSE_NULL, &nwad, req);
 		if (task == QSE_NULL) goto oops;
 	}
 
-	if (!req->attr.keepalive)
+	if (!(req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE))
 	{
 		if (!peek)
 		{