fixed a bug of sending http/1.0 taken from the peer while chunking.
enhanced proxy handling in general
This commit is contained in:
		@ -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 (
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -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;
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
		{
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user