From 05e0476f97e2fffa42e3e36e7cc9bc5f5353001b Mon Sep 17 00:00:00 2001 From: hyung-hwan Date: Fri, 13 Apr 2012 15:21:36 +0000 Subject: [PATCH] added a state field to htrd for determining if it's waiting for more feeds. enhanced how it can determine client's half-close changed how to compose cgi environment variables fixed a few bugs for various edge cases. --- qse/include/qse/net/htrd.h | 1 + qse/include/qse/net/httpd.h | 3 +- qse/lib/net/htrd.c | 58 ++-- qse/lib/net/httpd-task.c | 534 ++++++++++++++++++++++-------------- qse/lib/net/httpd.c | 10 +- qse/samples/net/http01.c | 46 +++- 6 files changed, 420 insertions(+), 232 deletions(-) diff --git a/qse/include/qse/net/htrd.h b/qse/include/qse/net/htrd.h index 1dfb7e09..f234e61b 100644 --- a/qse/include/qse/net/htrd.h +++ b/qse/include/qse/net/htrd.h @@ -97,6 +97,7 @@ struct qse_htrd_t } fed; qse_htre_t re; + int clean; }; #ifdef __cplusplus diff --git a/qse/include/qse/net/httpd.h b/qse/include/qse/net/httpd.h index 9a18b94d..8ecc7028 100644 --- a/qse/include/qse/net/httpd.h +++ b/qse/include/qse/net/httpd.h @@ -59,7 +59,8 @@ typedef enum qse_httpd_errnum_t qse_httpd_errnum_t; enum qse_httpd_option_t { QSE_HTTPD_CGIERRTONUL = (1 << 0), - QSE_HTTPD_CGINOCLOEXEC = (1 << 1) + QSE_HTTPD_CGINOCLOEXEC = (1 << 1), + QSE_HTTPD_CGINOCHUNKED = (1 << 2) }; typedef struct qse_httpd_stat_t qse_httpd_stat_t; diff --git a/qse/lib/net/htrd.c b/qse/lib/net/htrd.c index bc8f6201..aa12389c 100644 --- a/qse/lib/net/htrd.c +++ b/qse/lib/net/htrd.c @@ -98,6 +98,8 @@ static QSE_INLINE int push_to_buffer ( static QSE_INLINE int push_content ( qse_htrd_t* htrd, const qse_mchar_t* ptr, qse_size_t len) { + QSE_ASSERT (len > 0); + if (qse_htre_addcontent (&htrd->re, ptr, len) <= -1) { htrd->errnum = QSE_HTRD_ENOMEM; @@ -109,15 +111,11 @@ static QSE_INLINE int push_content ( return 0; } -struct hdr_cmb_t -{ - struct hdr_cmb_t* next; -}; - static QSE_INLINE void clear_feed (qse_htrd_t* htrd) { /* clear necessary part of the request/response before * reading the next request/response */ + htrd->clean = 1; qse_htre_clear (&htrd->re); qse_mbs_clear (&htrd->fed.b.tra); @@ -126,10 +124,6 @@ static QSE_INLINE void clear_feed (qse_htrd_t* htrd) QSE_MEMSET (&htrd->fed.s, 0, QSE_SIZEOF(htrd->fed.s)); } -#define QSE_HTRD_STATE_REQ 1 -#define QSE_HTRD_STATE_HDR 2 -#define QSE_HTRD_STATE_POST 3 - qse_htrd_t* qse_htrd_open (qse_mmgr_t* mmgr, qse_size_t xtnsize) { qse_htrd_t* htrd; @@ -176,6 +170,7 @@ int qse_htrd_init (qse_htrd_t* htrd, qse_mmgr_t* mmgr) return -1; } + htrd->clean = 1; return 0; } @@ -849,9 +844,11 @@ static QSE_INLINE int parse_initial_line_and_headers ( p = QSE_MBS_PTR(&htrd->fed.b.raw); +#if 0 if (htrd->option & QSE_HTRD_SKIPEMPTYLINES) while (is_whspace_octet(*p)) p++; else +#endif while (is_space_octet(*p)) p++; QSE_ASSERT (*p != '\0'); @@ -895,7 +892,6 @@ static const qse_mchar_t* getchunklen (qse_htrd_t* htrd, const qse_mchar_t* ptr, /* this function must be called in the GET_CHUNK_LEN context */ QSE_ASSERT (htrd->fed.s.chunk.phase == GET_CHUNK_LEN); -/*qse_printf (QSE_T("CALLING getchunklen [%hs]\n"), ptr);*/ if (htrd->fed.s.chunk.count <= 0) { /* skip leading spaces if the first character of @@ -925,7 +921,6 @@ static const qse_mchar_t* getchunklen (qse_htrd_t* htrd, const qse_mchar_t* ptr, if (htrd->fed.s.chunk.count <= 0) { /* empty line - no more chunk */ -/*qse_printf (QSE_T("empty line chunk done....\n"));*/ htrd->fed.s.chunk.phase = GET_CHUNK_DONE; } else if (htrd->fed.s.chunk.len <= 0) @@ -933,13 +928,11 @@ static const qse_mchar_t* getchunklen (qse_htrd_t* htrd, const qse_mchar_t* ptr, /* length explicity specified to 0 get trailing headers .... */ htrd->fed.s.chunk.phase = GET_CHUNK_TRAILERS; -/*qse_printf (QSE_T("SWITCH TO GET_CHUNK_TRAILERS....\n"));*/ } else { /* ready to read the chunk data... */ htrd->fed.s.chunk.phase = GET_CHUNK_DATA; -/*qse_printf (QSE_T("SWITCH TO GET_CHUNK_DATA....\n"));*/ } htrd->fed.s.need = htrd->fed.s.chunk.len; @@ -947,7 +940,6 @@ static const qse_mchar_t* getchunklen (qse_htrd_t* htrd, const qse_mchar_t* ptr, } else { -/*qse_printf (QSE_T("XXXXXXXXXXXXXXXXXxxx [%c]\n"), *ptr);*/ htrd->errnum = QSE_HTRD_EBADRE; return QSE_NULL; } @@ -964,7 +956,6 @@ static const qse_mchar_t* get_trailing_headers ( while (ptr < end) { register qse_mchar_t b = *ptr++; -/*qse_printf (QSE_T("[%hc], %d\n"), b, htrd->fed.s.crlf);*/ switch (b) { case '\0': @@ -1043,6 +1034,8 @@ int qse_htrd_feed (qse_htrd_t* htrd, const qse_mchar_t* req, qse_size_t len) int header_completed_during_this_feed = 0; qse_size_t avail; + QSE_ASSERT (len > 0); + /* does this goto drop code maintainability? */ if (htrd->fed.s.need > 0) { @@ -1069,10 +1062,13 @@ int qse_htrd_feed (qse_htrd_t* htrd, const qse_mchar_t* req, qse_size_t len) goto dechunk_get_trailers; } + htrd->clean = 0; /* mark that the htrd is in need of some data */ + while (ptr < end) { register qse_mchar_t b = *ptr++; +#if 0 if (htrd->option & QSE_HTRD_SKIPEMPTYLINES && htrd->fed.s.plen <= 0 && is_whspace_octet(b)) { @@ -1081,6 +1077,7 @@ int qse_htrd_feed (qse_htrd_t* htrd, const qse_mchar_t* req, qse_size_t len) req++; continue; } +#endif switch (b) { @@ -1142,16 +1139,17 @@ int qse_htrd_feed (qse_htrd_t* htrd, const qse_mchar_t* req, qse_size_t len) * reading CGI outputs. So it comes with * awkwardity described above. */ - if (push_content (htrd, ptr, end - ptr) <= -1) return -1; - /* this jump is only to invoke the peek - * callback. this function should not be fed - * more. */ + if (ptr < end && push_content (htrd, ptr, end - ptr) <= -1) return -1; /* i don't really know if it is really completed * with content. QSE_HTRD_PEEKONLY is not compatible * with the completed state. anyway, let me complete * it. */ qse_htre_completecontent (&htrd->re); + + /* this jump is only to invoke the peek + * callback. this function should not be fed + * more. */ goto feedme_more; } @@ -1169,7 +1167,7 @@ int qse_htrd_feed (qse_htrd_t* htrd, const qse_mchar_t* req, qse_size_t len) dechunk_resume: ptr = getchunklen (htrd, ptr, end - ptr); if (ptr == QSE_NULL) return -1; - + if (htrd->fed.s.chunk.phase == GET_CHUNK_LEN) { /* still in the GET_CHUNK_LEN state. @@ -1235,8 +1233,17 @@ int qse_htrd_feed (qse_htrd_t* htrd, const qse_mchar_t* req, qse_size_t len) * specified */ content_resume: avail = end - ptr; - - if (avail < htrd->fed.s.need) + if (avail <= 0) + { + /* we didn't get a complete content yet */ + + /* avail can be 0 if data fed ends with + * a chunk length withtout actual data. + * so i check if avail is greater than 0 + * in order not to push empty content. */ + goto feedme_more; + } + else if (avail < htrd->fed.s.need) { /* the data is not as large as needed */ if (push_content (htrd, ptr, avail) <= -1) return -1; @@ -1355,10 +1362,17 @@ qse_printf (QSE_T("CONTENT_LENGTH %d, RAW HEADER LENGTH %d\n"), #endif clear_feed (htrd); + if (ptr >= end) return 0; /* no more feeds to handle */ /* let ptr point to the next character to LF or * the optional contents */ req = ptr; + + /* since there are more to handle, i mark that + * htrd is in need of some data. this may + * not be really compatible with SKIPEMPTYLINES. + * SHOULD I simply remove the option? */ + htrd->clean = 0; } break; } diff --git a/qse/lib/net/httpd-task.c b/qse/lib/net/httpd-task.c index f776d0c0..392254e2 100644 --- a/qse/lib/net/httpd-task.c +++ b/qse/lib/net/httpd-task.c @@ -1263,9 +1263,10 @@ struct task_cgi_t qse_pio_t pio; int pio_inited; -#define CGI_REQ_GOTALL (1 << 0) -#define CGI_REQ_FWDALL (1 << 1) -#define CGI_REQ_FWDERR (1 << 2) +#define CGI_REQ_GOTALL (1 << 0) +#define CGI_REQ_FWDALL (1 << 1) +#define CGI_REQ_FWDERR (1 << 2) +#define CGI_REQ_FWDCHUNKED (1 << 3) int reqflags; qse_htre_t* req; /* original request associated with this */ qse_mbs_t* reqfwdbuf; /* content from the request */ @@ -1306,31 +1307,82 @@ static int cgi_capture_client_header ( qse_htre_t* req, const qse_mchar_t* key, const qse_htre_hdrval_t* val, void* ctx) { cgi_client_req_hdr_ctx_t* hdrctx; - qse_mchar_t* http_key; - int ret; hdrctx = (cgi_client_req_hdr_ctx_t*)ctx; -/* convert value to uppercase, change - to _ */ - http_key = qse_mbsdup2 (QSE_MT("HTTP_"), key, req->mmgr); - if (http_key == QSE_NULL) + if (qse_mbscasecmp (key, QSE_MT("Connection")) != 0 && + qse_mbscasecmp (key, QSE_MT("Transfer-Encoding")) != 0 && + qse_mbscasecmp (key, QSE_MT("Content-Length")) != 0) { - hdrctx->httpd->errnum = QSE_HTTPD_ENOMEM; - return -1; + qse_mchar_t* http_key, * ptr; + int ret; + + http_key = qse_mbsdup2 (QSE_MT("HTTP_"), key, req->mmgr); + if (http_key == QSE_NULL) + { + hdrctx->httpd->errnum = QSE_HTTPD_ENOMEM; + return -1; + } + + for (ptr = http_key; *ptr; ptr++) + { + *ptr = QSE_TOMUPPER((unsigned)*ptr); + if (*ptr == QSE_MT('-')) *ptr = '_'; + } + + /* insert the last value only */ + while (val->next) val = val->next; + ret = qse_env_insertmbs (hdrctx->env, http_key, val->ptr); + if (ret <= -1) + { + QSE_MMGR_FREE (req->mmgr, http_key); + hdrctx->httpd->errnum = QSE_HTTPD_ENOMEM; + return -1; + } + + QSE_MMGR_FREE (req->mmgr, http_key); } -/* TODO EXCLUDE VARIOUS FIELDS like transfer-encoding or should i let cgi handle transfer-encoding: chunked??? */ -/* TODO: special handling for Cookie??? */ + return 0; +} + +static int cgi_add_header_to_buffer ( + task_cgi_t* cgi, qse_mbs_t* buf, const qse_mchar_t* key, const qse_htre_hdrval_t* val) +{ + QSE_ASSERT (val != QSE_NULL); + do { - ret = qse_env_insertmbs (hdrctx->env, http_key, val->ptr); - if (ret <= -1) break; + if (qse_mbs_cat (buf, key) == (qse_size_t)-1 || + qse_mbs_cat (buf, QSE_MT(": ")) == (qse_size_t)-1 || + qse_mbs_cat (buf, val->ptr) == (qse_size_t)-1 || + qse_mbs_cat (buf, QSE_MT("\r\n")) == (qse_size_t)-1) + { + /* multiple items with the same keys are also + * copied back to the response buffer */ + cgi->httpd->errnum = QSE_HTTPD_ENOMEM; + return -1; + } + val = val->next; } while (val); - QSE_MMGR_FREE (req->mmgr, http_key); - return ret; + return 0; +} + +static int cgi_capture_client_trailer (qse_htre_t* req, const qse_mchar_t* key, const qse_htre_hdrval_t* val, void* ctx) +{ + task_cgi_t* cgi = (task_cgi_t*)ctx; + + if (qse_mbscasecmp (key, QSE_MT("Content-Length")) != 0 && + qse_mbscasecmp (key, QSE_MT("Connection")) != 0 && + qse_mbscasecmp (key, QSE_MT("Transfer-Encoding")) != 0) + { + return cgi_add_header_to_buffer (cgi, cgi->reqfwdbuf, key, val); + } + + return 0; } static int cgi_capture_script_header (qse_htre_t* req, const qse_mchar_t* key, const qse_htre_hdrval_t* val, void* ctx) @@ -1342,22 +1394,7 @@ static int cgi_capture_script_header (qse_htre_t* req, const qse_mchar_t* key, c qse_mbscasecmp (key, QSE_MT("Connection")) != 0 && qse_mbscasecmp (key, QSE_MT("Transfer-Encoding")) != 0) { - /* multiple items with the same keys are also - * copied back to the response buffer */ - do - { - if (qse_mbs_cat (cgi->res, key) == (qse_size_t)-1 || - qse_mbs_cat (cgi->res, QSE_MT(": ")) == (qse_size_t)-1 || - qse_mbs_cat (cgi->res, val->ptr) == (qse_size_t)-1 || - qse_mbs_cat (cgi->res, QSE_MT("\r\n")) == (qse_size_t)-1) - { - cgi->httpd->errnum = QSE_HTTPD_ENOMEM; - return -1; - } - - val = val->next; - } - while (val); + return cgi_add_header_to_buffer (cgi, cgi->res, key, val); } return 0; @@ -1553,15 +1590,19 @@ static qse_htrd_recbs_t cgi_script_htrd_cbs = QSE_NULL /* not needed for CGI */ }; -static qse_env_t* makecgienv ( +static int cgi_add_env ( qse_httpd_t* httpd, qse_httpd_client_t* client, - const qse_htre_t* req, const qse_mchar_t* path, qse_size_t content_length) + qse_env_t* env, qse_htre_t* req, const + qse_mchar_t* path, qse_size_t length, int chunked) { /* TODO: error check */ - qse_env_t* env; + cgi_client_req_hdr_ctx_t ctx; + qse_mchar_t buf[128]; + const qse_http_version_t* v; + const qse_mchar_t* qparam; - env = qse_env_open (httpd->mmgr, 0, 0); - if (env == QSE_NULL) goto oops; + v = qse_htre_getversion(req); + qparam = qse_htre_getqparam(req); #ifdef _WIN32 qse_env_insert (env, QSE_T("PATH"), QSE_NULL); @@ -1571,6 +1612,8 @@ static qse_env_t* makecgienv ( #endif qse_env_insertmbs (env, QSE_MT("GATEWAY_INTERFACE"), QSE_MT("CGI/1.1")); + snprintf (buf, QSE_COUNTOF(buf), QSE_MT("HTTP/%d.%d"), (int)v->major, (int)v->minor); + qse_env_insertmbs (env, QSE_MT("SERVER_PROTOCOL"), buf); { /* TODO: corrent all these name??? */ @@ -1582,110 +1625,38 @@ static qse_env_t* makecgienv ( //qse_env_insertmbs (env, QSE_MT("SCRIPT_FILENAME"), path); + qse_env_insertmbs (env, QSE_MT("REQUEST_METHOD"), qse_htre_getqmethodname(req)); qse_env_insertmbs (env, QSE_MT("REQUEST_URI"), qse_htre_getqpath(req)); + if (qparam) qse_env_insertmbs (env, QSE_MT("QUERY_STRING"), qparam); - { - const qse_mchar_t* tmp = qse_htre_getqparam(req); - if (tmp) qse_env_insertmbs (env, QSE_MT("QUERY_STRING"), tmp); - } + qse_fmtuintmaxtombs (buf, QSE_COUNTOF(buf), length, 10, -1, QSE_MT('\0'), QSE_NULL); + qse_env_insertmbs (env, QSE_MT("CONTENT_LENGTH"), buf); - qse_env_insertmbs ( - env, QSE_MT("REQUEST_METHOD"), qse_htre_getqmethodname(req)); + if (chunked) qse_env_insertmbs (env, QSE_MT("TRANSFER_ENCODING"), QSE_MT("chunked")); - { - qse_mchar_t tmp[64]; - qse_fmtuintmaxtombs ( - tmp, QSE_COUNTOF(tmp), content_length, - 10, -1, QSE_MT('\0'), QSE_NULL); - qse_env_insertmbs (env, QSE_MT("CONTENT_LENGTH"), tmp); - } - - { - qse_mchar_t addr[128]; - qse_nwadtombs (&client->local_addr, - addr, QSE_COUNTOF(addr), QSE_NWADTOMBS_PORT); - qse_env_insertmbs (env, QSE_MT("SERVER_PORT"), addr); - qse_nwadtombs (&client->local_addr, - addr, QSE_COUNTOF(addr), QSE_NWADTOMBS_ADDR); - qse_env_insertmbs (env, QSE_MT("SERVER_ADDR"), addr); - qse_nwadtombs (&client->remote_addr, - addr, QSE_COUNTOF(addr), QSE_NWADTOMBS_PORT); - qse_env_insertmbs (env, QSE_MT("REMOTE_PORT"), addr); - qse_nwadtombs (&client->remote_addr, - addr, QSE_COUNTOF(addr), QSE_NWADTOMBS_ADDR); - qse_env_insertmbs (env, QSE_MT("REMOTE_ADDR"), addr); - } - - { - qse_mchar_t proto[32]; - const qse_http_version_t* v = qse_htre_getversion(req); - snprintf (proto, QSE_COUNTOF(proto), - QSE_MT("HTTP/%d.%d"), (int)v->major, (int)v->minor); - qse_env_insertmbs (env, QSE_MT("SERVER_PROTOCOL"), proto); - } -// TODO: HTTP_ headers. -// TODO: REMOTE_USER ... + qse_nwadtombs (&client->local_addr, buf, QSE_COUNTOF(buf), QSE_NWADTOMBS_PORT); + qse_env_insertmbs (env, QSE_MT("SERVER_PORT"), buf); + qse_nwadtombs (&client->local_addr, buf, QSE_COUNTOF(buf), QSE_NWADTOMBS_ADDR); + qse_env_insertmbs (env, QSE_MT("SERVER_ADDR"), buf); + qse_nwadtombs (&client->remote_addr, buf, QSE_COUNTOF(buf), QSE_NWADTOMBS_PORT); + qse_env_insertmbs (env, QSE_MT("REMOTE_PORT"), buf); + qse_nwadtombs (&client->remote_addr, buf, QSE_COUNTOF(buf), QSE_NWADTOMBS_ADDR); + qse_env_insertmbs (env, QSE_MT("REMOTE_ADDR"), buf); #if 0 qse_env_insertmbs (env, "SERVER_NAME", qse_env_insertmbs (env, "SERVER_ROOT", qse_env_insertmbs (env, "DOCUMENT_ROOT", qse_env_insertmbs (env, "REMOTE_PORT", - qse_env_insertmbs (env, "REQUEST_URI", + qse_env_insertmbs (env, "REMOTE_USER", #endif -#if 0 ctx.httpd = httpd; ctx.env = env; if (qse_htre_walkheaders (req, cgi_capture_client_header, &ctx) <= -1) return -1; -#endif + if (qse_htre_walktrailers (req, cgi_capture_client_header, &ctx) <= -1) return -1; -/* TODO: memory error check */ - { - const qse_htre_hdrval_t* tmp; - - tmp = qse_htre_getheaderval(req, QSE_MT("Content-Type")); - if (tmp) - { - while (tmp->next) tmp = tmp->next; - qse_env_insertmbs (env, QSE_MT("CONTENT_TYPE"), tmp->ptr); - } - - tmp = qse_htre_getheaderval(req, QSE_MT("Cookie")); - if (tmp) - { -/* TODO: should cookie be combined into 1??? */ - while (tmp->next) tmp = tmp->next; - qse_env_insertmbs (env, QSE_MT("HTTP_COOKIE"), tmp->ptr); - } - - tmp = qse_htre_getheaderval(req, QSE_MT("Host")); - if (tmp) - { - while (tmp->next) tmp = tmp->next; - qse_env_insertmbs (env, QSE_MT("HTTP_HOST"), tmp->ptr); - } - - tmp = qse_htre_getheaderval(req, QSE_MT("Referer")); - if (tmp) - { - while (tmp->next) tmp = tmp->next; - qse_env_insertmbs (env, QSE_MT("HTTP_REFERER"), tmp->ptr); - } - - tmp = qse_htre_getheaderval(req, QSE_MT("User-Agent")); - if (tmp) - { - while (tmp->next) tmp = tmp->next; - qse_env_insertmbs (env, QSE_MT("HTTP_USER_AGENT"), tmp->ptr); - } - } - - return env; - -oops: - if (env) qse_env_close (env); - return QSE_NULL; + return 0; } static int cgi_snatch_client_input ( @@ -1714,6 +1685,18 @@ else qse_printf (QSE_T("!!!CGI SNATCHING DONE\n")); */ QSE_ASSERT (len == 0); + if (cgi->reqflags & CGI_REQ_FWDCHUNKED) + { + /* add the 0-sized chunk and trailers */ + if (qse_mbs_cat (cgi->reqfwdbuf, QSE_MT("0\r\n")) == (qse_size_t)-1 || + qse_htre_walktrailers (req, cgi_capture_client_trailer, cgi) <= -1 || + qse_mbs_cat (cgi->reqfwdbuf, QSE_MT("\r\n")) == (qse_size_t)-1) + { + cgi->httpd->errnum = QSE_HTTPD_ENOMEM; + return -1; + } + } + /* mark the there's nothing to read form the client side */ cgi->reqflags |= CGI_REQ_GOTALL; @@ -1743,11 +1726,34 @@ else qse_printf (QSE_T("!!!CGI SNATCHING DONE\n")); * didn't occur previously. we store data from the client side * to the forwaring buffer only if there's no such previous * error. if an error occurred, we simply drop the data. */ - if (qse_mbs_ncat (cgi->reqfwdbuf, ptr, len) == (qse_size_t)-1) + if (cgi->reqflags & CGI_REQ_FWDCHUNKED) { - cgi->httpd->errnum = QSE_HTTPD_ENOMEM; - return -1; + qse_mchar_t buf[64]; + qse_fmtuintmaxtombs ( + buf, QSE_COUNTOF(buf), len, + 16 | QSE_FMTUINTMAXTOMBS_UPPERCASE, + -1, QSE_MT('\0'), QSE_NULL); + + if (qse_mbs_cat (cgi->reqfwdbuf, buf) == (qse_size_t)-1 || + qse_mbs_cat (cgi->reqfwdbuf, QSE_MT("\r\n")) == (qse_size_t)-1 || + qse_mbs_ncat (cgi->reqfwdbuf, ptr, len) == (qse_size_t)-1 || + qse_mbs_cat (cgi->reqfwdbuf, QSE_MT("\r\n")) == (qse_size_t)-1) + { + cgi->httpd->errnum = QSE_HTTPD_ENOMEM; + return -1; + } } + else + { + if (qse_mbs_ncat (cgi->reqfwdbuf, ptr, len) == (qse_size_t)-1) + { + cgi->httpd->errnum = QSE_HTTPD_ENOMEM; + return -1; + } + } + + /* output pipe to child */ + task->trigger[1].mask = QSE_HTTPD_TASK_TRIGGER_WRITE; qse_printf (QSE_T("!!!CGI SNATCHED [%.*hs]\n"), len, ptr); } @@ -1798,6 +1804,11 @@ qse_printf (QSE_T("FORWARD: @@@@@@@@@@WRITING[%.*hs]\n"), /* TODO: improve performance.. instead of copying the remaing part to the head all the time.. grow the buffer to a certain limit. */ qse_mbs_del (cgi->reqfwdbuf, 0, n); + if (QSE_MBS_LEN(cgi->reqfwdbuf) <= 0) + { + if (cgi->reqflags & CGI_REQ_GOTALL) goto done; + else task->trigger[1].mask = 0; /* pipe output to child */ + } } } @@ -1821,17 +1832,26 @@ qse_printf (QSE_T("FORWARD: @@@@@@@@WRITE TO CGI FAILED\n")); QSE_ASSERT (!cgi->req); QSE_ASSERT (cgi->reqflags & CGI_REQ_GOTALL); } + + /* mark the end of input to the child explicitly. */ + qse_pio_end (&cgi->pio, QSE_PIO_IN); + task->trigger[1].mask = 0; /* pipe output to child */ } } } else if (cgi->reqflags & CGI_REQ_GOTALL) { + done: /* there is nothing to read from the client side and * there is nothing more to forward in the forwarding buffer. * clear the relay and write triggers for the time being. */ qse_printf (QSE_T("FORWARD: @@@@@@@@NOTHING MORE TO WRITE TO CGI\n")); QSE_ASSERT (cgi->req == QSE_NULL); + + /* mark the end of input to the child explicitly. */ + qse_pio_end (&cgi->pio, QSE_PIO_IN); + task->trigger[1].mask = 0; /* pipe output to child */ task->trigger[2].mask = 0; /* client-side */ } @@ -1864,11 +1884,8 @@ static int task_init_cgi ( cgi->nph = arg->nph; cgi->req = QSE_NULL; - if (arg->req->state & QSE_HTRE_DISCARDED) - { - content_length = 0; - goto done; - } + content_length = 0; + if (arg->req->state & QSE_HTRE_DISCARDED) goto done; len = qse_htre_getcontentlen(arg->req); if ((arg->req->state & QSE_HTRE_COMPLETED) && len <= 0) @@ -1876,7 +1893,6 @@ static int task_init_cgi ( /* the content part is completed and no content * in the content buffer. there is nothing to forward */ cgi->reqflags |= CGI_REQ_GOTALL; - content_length = 0; goto done; } @@ -1888,22 +1904,63 @@ static int task_init_cgi ( * pass the content. this function, however, allows * such a request to entask a cgi script dropping the * content */ - qse_htre_discardcontent (arg->req); - cgi->reqflags |= CGI_REQ_GOTALL; - content_length = 0; + + if (httpd->option & QSE_HTTPD_CGINOCHUNKED) + { + qse_htre_discardcontent (arg->req); + cgi->reqflags |= CGI_REQ_GOTALL; + } + else + { + /* do chunking to cgi */ + cgi->reqfwdbuf = qse_mbs_open (httpd->mmgr, 0, (len < 512? 512: len)); + if (cgi->reqfwdbuf == QSE_NULL) + { + httpd->errnum = QSE_HTTPD_ENOMEM; + goto oops; + } + + if (len > 0) + { + qse_mchar_t buf[64]; + + qse_fmtuintmaxtombs ( + buf, QSE_COUNTOF(buf), len, + 16 | QSE_FMTUINTMAXTOMBS_UPPERCASE, + -1, QSE_MT('\0'), QSE_NULL); + + ptr = qse_htre_getcontentptr(arg->req); + if (qse_mbs_cat (cgi->reqfwdbuf, buf) == (qse_size_t)-1 || + qse_mbs_cat (cgi->reqfwdbuf, QSE_MT("\r\n")) == (qse_size_t)-1 || + qse_mbs_ncat (cgi->reqfwdbuf, ptr, len) == (qse_size_t)-1 || + qse_mbs_cat (cgi->reqfwdbuf, QSE_MT("\r\n")) == (qse_size_t)-1) + { + httpd->errnum = QSE_HTTPD_ENOMEM; + goto oops; + } + + } + + cgi->reqflags |= CGI_REQ_FWDCHUNKED; + cgi->req = arg->req; + qse_htre_setconcb (cgi->req, cgi_snatch_client_input, task); + } } else { /* create a buffer to hold request content from the client * and copy content received already */ cgi->reqfwdbuf = qse_mbs_open (httpd->mmgr, 0, (len < 512? 512: len)); - if (cgi->reqfwdbuf == QSE_NULL) goto oops; + if (cgi->reqfwdbuf == QSE_NULL) + { + httpd->errnum = QSE_HTTPD_ENOMEM; + goto oops; + } ptr = qse_htre_getcontentptr(arg->req); if (qse_mbs_ncpy (cgi->reqfwdbuf, ptr, len) == (qse_size_t)-1) { - qse_mbs_close (cgi->reqfwdbuf); - cgi->reqfwdbuf = QSE_NULL; + httpd->errnum = QSE_HTTPD_ENOMEM; goto oops; } @@ -1947,8 +2004,19 @@ static int task_init_cgi ( } done: - cgi->env = makecgienv (httpd, client, arg->req, arg->path, content_length); - if (cgi->env == QSE_NULL) goto oops; + cgi->env = qse_env_open (httpd->mmgr, 0, 0); + if (cgi->env == QSE_NULL) + { + httpd->errnum = QSE_HTTPD_ENOMEM; + goto oops; + } + + if (cgi_add_env ( + httpd, client, cgi->env, arg->req, arg->path, content_length, + (cgi->reqflags & CGI_REQ_FWDCHUNKED)) <= -1) + { + goto oops; + } /* no triggers yet since the main loop doesn't allow me to set * triggers in the task initializer. however the main task handler @@ -1964,6 +2032,18 @@ oops: * add an error task */ cgi->init_failed = 1; task->ctx = cgi; + + if (cgi->env) + { + qse_env_close (cgi->env); + cgi->env = QSE_NULL; + } + if (cgi->reqfwdbuf) + { + qse_mbs_close (cgi->reqfwdbuf); + cgi->reqfwdbuf = QSE_NULL; + } + return 0; } @@ -2245,6 +2325,8 @@ static int task_main_cgi_3 ( QSE_ASSERT (!cgi->nph); +qse_printf (QSE_T("task_main_cgi_3 trigger[0].mask=%d trigger[1].mask=%d trigger[2].mask=%d\n"), + task->trigger[0].mask, task->trigger[1].mask, task->trigger[2].mask); if (task->trigger[2].mask & QSE_HTTPD_TASK_TRIGGER_READABLE) { cgi_forward_client_input_to_script (httpd, task, 0); @@ -2324,11 +2406,9 @@ static int task_main_cgi_2 ( QSE_ASSERT (!cgi->nph); QSE_ASSERT (cgi->pio_inited); -{ -qse_ntime_t now; -qse_gettime(&now); -qse_printf (QSE_T("[cgi_2 at %lld]\n"), now); -} +qse_printf (QSE_T("task_main_cgi_2 trigger[0].mask=%d trigger[1].mask=%d trigger[2].mask=%d\n"), + task->trigger[0].mask, task->trigger[1].mask, task->trigger[2].mask); + if (task->trigger[2].mask & QSE_HTTPD_TASK_TRIGGER_READABLE) { qse_printf (QSE_T("[cgi_2 write]\n")); @@ -2604,11 +2684,11 @@ struct task_proxy_t #define PROXY_PEER_CONNECTED (1 << 1) int peer_status; -#define PROXY_REQ_CHUNKED (1 << 0) +#define PROXY_REQ_FWDERR (1 << 0) +#define PROXY_REQ_FWDCHUNKED (1 << 1) int reqflags; qse_htre_t* req; /* original client request associated with this */ qse_mbs_t* reqfwdbuf; /* content from the request */ - int reqfwderr; qse_mbs_t* res; qse_size_t res_consumed; @@ -2748,7 +2828,7 @@ else qse_printf (QSE_T("!!!PROXY SNATCHING DONE\n")); */ QSE_ASSERT (len == 0); - if (proxy->reqflags & PROXY_REQ_CHUNKED) + if (proxy->reqflags & PROXY_REQ_FWDCHUNKED) { /* add the 0-sized chunk and trailers */ if (qse_mbs_cat (proxy->reqfwdbuf, QSE_MT("0\r\n")) == (qse_size_t)-1 || @@ -2778,15 +2858,14 @@ else qse_printf (QSE_T("!!!PROXY SNATCHING DONE\n")); * for task invocation. */ task->trigger[0].mask |= QSE_HTTPD_TASK_TRIGGER_WRITE; } - } - else if (!proxy->reqfwderr) + else if (!(proxy->reqflags & PROXY_REQ_FWDERR)) { /* we can write to the peer if a forwarding error * didn't occur previously. we store data from the client side * to the forwaring buffer only if there's no such previous * error. if an error occurred, we simply drop the data. */ - if (proxy->reqflags & PROXY_REQ_CHUNKED) + if (proxy->reqflags & PROXY_REQ_FWDCHUNKED) { qse_mchar_t buf[64]; qse_fmtuintmaxtombs ( @@ -2811,6 +2890,8 @@ else qse_printf (QSE_T("!!!PROXY SNATCHING DONE\n")); return -1; } } + + task->trigger[0].mask |= QSE_HTTPD_TASK_TRIGGER_WRITE; qse_printf (QSE_T("!!!PROXY SNATCHED [%.*hs]\n"), len, ptr); } @@ -2897,7 +2978,6 @@ 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; } @@ -3142,7 +3222,7 @@ static void proxy_forward_client_input_to_peer ( { /* there is something to forward in the forwarding buffer. */ - if (proxy->reqfwderr) + if (proxy->reqflags & PROXY_REQ_FWDERR) { /* a forwarding error has occurred previously. * clear the forwarding buffer */ @@ -3173,14 +3253,22 @@ qse_printf (QSE_T("PROXY FORWARD: @@@@@@@@@@WRITING[%.*hs]\n"), /* TODO: improve performance.. instead of copying the remaing part to the head all the time.. grow the buffer to a certain limit. */ - if (n > 0) qse_mbs_del (proxy->reqfwdbuf, 0, n); + if (n > 0) + { + qse_mbs_del (proxy->reqfwdbuf, 0, n); + if (QSE_MBS_LEN(proxy->reqfwdbuf) <= 0) + { + if (proxy->req == QSE_NULL) goto done; + else task->trigger[0].mask &= ~QSE_HTTPD_TASK_TRIGGER_WRITE; + } + } } if (n <= -1) { qse_printf (QSE_T("PROXY FORWARD: @@@@@@@@WRITE TO PROXY FAILED\n")); /* TODO: logging ... */ - proxy->reqfwderr = 1; + proxy->reqflags |= PROXY_REQ_FWDERR; qse_mbs_clear (proxy->reqfwdbuf); if (proxy->req) { @@ -3189,17 +3277,21 @@ qse_printf (QSE_T("PROXY FORWARD: @@@@@@@@WRITE TO PROXY FAILED\n")); * in proxy_snatch_client_input() triggered by * qse_htre_discardcontent() */ } + + task->trigger[0].mask &= ~QSE_HTTPD_TASK_TRIGGER_WRITE; /* peer */ } } } else if (proxy->req == QSE_NULL) { + done: /* there is nothing to read from the client side and * there is nothing more to forward in the forwarding buffer. - * clear the relay and write triggers. + * clear the read and write triggers. */ qse_printf (QSE_T("FORWARD: @@@@@@@@NOTHING MORE TO WRITE TO PROXY\n")); - task->trigger[2].mask = 0; + task->trigger[0].mask &= ~QSE_HTTPD_TASK_TRIGGER_WRITE; /* peer */ + task->trigger[2].mask = 0; /* client-side */ } } @@ -3233,19 +3325,19 @@ len = 1024; proxy->reqfwdbuf = qse_mbs_open (httpd->mmgr, 0, (len < 512? 512: len)); if (proxy->reqfwdbuf == QSE_NULL) goto oops; - if (qse_mbs_cat (proxy->reqfwdbuf, qse_htre_getqmethodname(arg->req)) == (qse_size_t)-1) goto oops; - if (qse_mbs_cat (proxy->reqfwdbuf, QSE_MT(" ")) == (qse_size_t)-1) goto oops; - if (qse_mbs_cat (proxy->reqfwdbuf, qse_htre_getqpath(arg->req)) == (qse_size_t)-1) goto oops; + if (qse_mbs_cat (proxy->reqfwdbuf, qse_htre_getqmethodname(arg->req)) == (qse_size_t)-1 || + qse_mbs_cat (proxy->reqfwdbuf, QSE_MT(" ")) == (qse_size_t)-1 || + qse_mbs_cat (proxy->reqfwdbuf, qse_htre_getqpath(arg->req)) == (qse_size_t)-1) goto oops; + if (qse_htre_getqparam(arg->req)) { - if (qse_mbs_cat (proxy->reqfwdbuf, QSE_MT("?")) == (qse_size_t)-1) goto oops; - if (qse_mbs_cat (proxy->reqfwdbuf, qse_htre_getqparam(arg->req)) == (qse_size_t)-1) goto oops; + if (qse_mbs_cat (proxy->reqfwdbuf, QSE_MT("?")) == (qse_size_t)-1 || + qse_mbs_cat (proxy->reqfwdbuf, qse_htre_getqparam(arg->req)) == (qse_size_t)-1) goto oops; } - if (qse_mbs_cat (proxy->reqfwdbuf, QSE_MT(" ")) == (qse_size_t)-1) goto oops; - if (qse_mbs_cat (proxy->reqfwdbuf, qse_htre_getverstr(arg->req)) == (qse_size_t)-1) goto oops; - - if (qse_mbs_cat (proxy->reqfwdbuf, QSE_MT("\r\n")) == (qse_size_t)-1) goto oops; - if (qse_htre_walkheaders (arg->req, proxy_capture_client_header, proxy) <= -1) goto oops; + if (qse_mbs_cat (proxy->reqfwdbuf, QSE_MT(" ")) == (qse_size_t)-1 || + qse_mbs_cat (proxy->reqfwdbuf, qse_htre_getverstr(arg->req)) == (qse_size_t)-1 || + qse_mbs_cat (proxy->reqfwdbuf, QSE_MT("\r\n")) == (qse_size_t)-1 || + qse_htre_walkheaders (arg->req, proxy_capture_client_header, proxy) <= -1) goto oops; proxy->resflags |= PROXY_RES_AWAIT_RESHDR; if ((arg->req->attr.flags & QSE_HTRE_ATTR_EXPECT100) && @@ -3259,20 +3351,20 @@ len = 1024; if (arg->req->state & QSE_HTRE_DISCARDED) { /* no content to add */ - /*if (qse_mbs_cat (proxy->reqfwdbuf, QSE_MT("Content-Length: 0\r\n")) == (qse_size_t)-1) goto oops;*/ - - /* i don't also add chunk traiers if the - * request content has been discarded */ - - /* end of header */ - if (qse_mbs_cat (proxy->reqfwdbuf, QSE_MT("\r\n")) == (qse_size_t)-1) goto oops; + if ((arg->req->attr.flags & QSE_HTRE_ATTR_LENGTH) || + (arg->req->attr.flags & QSE_HTRE_ATTR_CHUNKED)) + { + /* i don't add chunk traiers if the + * request content has been discarded */ + if (qse_mbs_cat (proxy->reqfwdbuf, QSE_MT("Content-Length: 0\r\n\r\n")) == (qse_size_t)-1) goto oops; + } + else + { + if (qse_mbs_cat (proxy->reqfwdbuf, QSE_MT("\r\n")) == (qse_size_t)-1) goto oops; + } } else if (arg->req->state & QSE_HTRE_COMPLETED) { - qse_mchar_t buf[64]; - - len = qse_htre_getcontentlen(arg->req); - if (arg->req->attr.flags & QSE_HTRE_ATTR_CHUNKED) { /* add trailers if any */ @@ -3280,21 +3372,32 @@ len = 1024; arg->req, proxy_capture_client_trailer, proxy) <= -1) goto oops; } - qse_fmtuintmaxtombs ( - buf, QSE_COUNTOF(buf), len, - 10, -1, QSE_MT('\0'), QSE_NULL); - - /* force-insert content-length. content-length is added - * even if the original request dones't contain it */ - if (qse_mbs_cat (proxy->reqfwdbuf, QSE_MT("Content-Length: ")) == (qse_size_t)-1 || - qse_mbs_cat (proxy->reqfwdbuf, buf) == (qse_size_t)-1 || - qse_mbs_cat (proxy->reqfwdbuf, QSE_MT("\r\n\r\n")) == (qse_size_t)-1) goto oops; - - if (len > 0) + len = qse_htre_getcontentlen(arg->req); + if (len > 0 || (arg->req->attr.flags & QSE_HTRE_ATTR_LENGTH) || + (arg->req->attr.flags & QSE_HTRE_ATTR_CHUNKED)) { - /* content */ - ptr = qse_htre_getcontentptr(arg->req); - if (qse_mbs_ncat (proxy->reqfwdbuf, ptr, len) == (qse_size_t)-1) goto oops; + qse_mchar_t buf[64]; + + qse_fmtuintmaxtombs ( + buf, QSE_COUNTOF(buf), len, + 10, -1, QSE_MT('\0'), QSE_NULL); + + /* force-insert content-length. content-length is added + * even if the original request dones't contain it */ + if (qse_mbs_cat (proxy->reqfwdbuf, QSE_MT("Content-Length: ")) == (qse_size_t)-1 || + qse_mbs_cat (proxy->reqfwdbuf, buf) == (qse_size_t)-1 || + qse_mbs_cat (proxy->reqfwdbuf, QSE_MT("\r\n\r\n")) == (qse_size_t)-1) goto oops; + + if (len > 0) + { + /* content */ + ptr = qse_htre_getcontentptr(arg->req); + if (qse_mbs_ncat (proxy->reqfwdbuf, ptr, len) == (qse_size_t)-1) goto oops; + } + } + else + { + if (qse_mbs_cat (proxy->reqfwdbuf, QSE_MT("\r\n")) == (qse_size_t)-1) goto oops; } } else if (arg->req->attr.flags & QSE_HTRE_ATTR_LENGTH) @@ -3321,9 +3424,12 @@ len = 1024; } else { + /* if this request is not chunked nor not length based, + * the state should be QSE_HTRE_COMPLETED. so only a + * chunked request should reach here */ QSE_ASSERT (arg->req->attr.flags & QSE_HTRE_ATTR_CHUNKED); - proxy->reqflags |= PROXY_REQ_CHUNKED; + proxy->reqflags |= PROXY_REQ_FWDCHUNKED; if (qse_mbs_cat (proxy->reqfwdbuf, QSE_MT("Transfer-Encoding: chunked\r\n")) == (qse_size_t)-1 || qse_mbs_cat (proxy->reqfwdbuf, QSE_MT("\r\n")) == (qse_size_t)-1 /* end of header */) goto oops; @@ -3332,12 +3438,13 @@ len = 1024; { qse_mchar_t buf[64]; - ptr = qse_htre_getcontentptr(arg->req); qse_fmtuintmaxtombs ( buf, QSE_COUNTOF(buf), len, 16 | QSE_FMTUINTMAXTOMBS_UPPERCASE, -1, QSE_MT('\0'), QSE_NULL); + ptr = qse_htre_getcontentptr(arg->req); + /* chunk length and chunk content */ if (qse_mbs_cat (proxy->reqfwdbuf, buf) == (qse_size_t)-1 || qse_mbs_cat (proxy->reqfwdbuf, QSE_MT("\r\n")) == (qse_size_t)-1 || @@ -3402,17 +3509,20 @@ static int task_main_proxy_5 ( qse_ssize_t n; //QSE_ASSERT (proxy->pio_inited); +qse_printf (QSE_T("task_main_proxy_5 trigger[0].mask=%d trigger[1].mask=%d trigger[2].mask=%d\n"), + task->trigger[0].mask, task->trigger[1].mask, task->trigger[2].mask); if (task->trigger[2].mask & QSE_HTTPD_TASK_TRIGGER_READABLE) { + /* if the client side is readable */ proxy_forward_client_input_to_peer (httpd, task, 0); } else if (task->trigger[0].mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE) { + /* if the peer side is writable */ proxy_forward_client_input_to_peer (httpd, task, 1); } -qse_printf (QSE_T("task_main_proxy_5\n")); if (!(task->trigger[2].mask & QSE_HTTPD_TASK_TRIGGER_WRITE) || (task->trigger[2].mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE)) { @@ -3621,7 +3731,9 @@ static int task_main_proxy_2 ( task_proxy_t* proxy = (task_proxy_t*)task->ctx; int http_errnum = 0; -qse_printf (QSE_T("task_main_proxy_2....\n")); +qse_printf (QSE_T("task_main_proxy_2 trigger[0].mask=%d trigger[1].mask=%d trigger[2].mask=%d\n"), + task->trigger[0].mask, task->trigger[1].mask, task->trigger[2].mask); + if (task->trigger[2].mask & QSE_HTTPD_TASK_TRIGGER_READABLE) { proxy_forward_client_input_to_peer (httpd, task, 0); @@ -3740,7 +3852,13 @@ qse_printf (QSE_T("#####PREMATURE EOF FROM PEER CLIENT CHUNK\n")); proxy->buflen += n; -qse_printf (QSE_T("#####PROXY FEEDING %d [%.*hs]\n"), (int)proxy->buflen, (int)proxy->buflen, proxy->buf); +qse_printf (QSE_T("#####PROXY FEEDING %d [\n"), (int)proxy->buflen); +{ +int i; +for (i = 0; i < proxy->buflen; i++) qse_printf (QSE_T("%hc"), proxy->buf[i]); +} +qse_printf (QSE_T("]\n")); + if (qse_htrd_feed (proxy->peer_htrd, proxy->buf, proxy->buflen) <= -1) { /* TODO: logging */ @@ -3829,6 +3947,7 @@ qse_printf (QSE_T("task_main_proxy_1.... ERROR \n")); if (n >= 1) { proxy->peer_status |= PROXY_PEER_CONNECTED; +qse_printf (QSE_T("FINALLY connected to peer ...............................\n")); if (proxy->req) { @@ -3838,13 +3957,16 @@ qse_printf (QSE_T("task_main_proxy_1.... ERROR \n")); task->trigger[0].mask &= ~QSE_HTTPD_TASK_TRIGGER_WRITE; if (QSE_MBS_LEN(proxy->reqfwdbuf) > 0) { + /* forward the initial part of the input to the peer */ proxy_forward_client_input_to_peer (httpd, task, 0); if (QSE_MBS_LEN(proxy->reqfwdbuf) > 0) { + /* there are still more to forward in the buffer + * request the task invocation when the peer + * is writable */ task->trigger[0].mask |= QSE_HTTPD_TASK_TRIGGER_WRITE; } } -qse_printf (QSE_T("FINALLY connected to peer ...............................\n")); task->main = task_main_proxy_2; } } @@ -3969,4 +4091,16 @@ qse_httpd_task_t* qse_httpd_entaskproxy ( /*------------------------------------------------------------------------*/ +#if 0 +qse_httpd_task_t* qse_httpd_entaskconnect ( + qse_httpd_t* httpd, + qse_httpd_client_t* client, + qse_httpd_task_t* pred, + const qse_nwad_t* nwad, + qse_htre_t* req) +{ + return -1; +} +#endif + #endif diff --git a/qse/lib/net/httpd.c b/qse/lib/net/httpd.c index 9f65493c..2bee58c9 100644 --- a/qse/lib/net/httpd.c +++ b/qse/lib/net/httpd.c @@ -46,6 +46,7 @@ QSE_IMPLEMENT_COMMON_FUNCTIONS (httpd) #define DEFAULT_PORT 80 #define DEFAULT_SECURE_PORT 443 +/* client->status */ #define CLIENT_BAD (1 << 0) #define CLIENT_READY (1 << 1) #define CLIENT_SECURE (1 << 2) @@ -531,7 +532,9 @@ static int activate_servers (qse_httpd_t* httpd) { if (httpd->cbs->server.open (httpd, server) <= -1) { -qse_printf (QSE_T("FAILED TO ACTIVATE SERVER....\n")); +qse_char_t buf[64]; +qse_nwadtostr (&server->nwad, buf, QSE_COUNTOF(buf), QSE_NWADTOSTR_ALL); +qse_printf (QSE_T("FAILED TO ACTIVATE SERVER....[%s]\n"), buf); continue; } @@ -690,9 +693,10 @@ qse_printf (QSE_T("Error: failed to read from a client %d\n"), client->handle.i) else if (m == 0) { qse_printf (QSE_T("Debug: connection closed %d - errno %d\n"), client->handle.i, errno); - if (client->task.head) + if (client->task.head && client->htrd->clean) { - /* there is still more tasks to finish */ + /* there is still more tasks to finish and + * http reader is not waiting for any more feeds. */ client->status |= CLIENT_MUTE; return 0; } diff --git a/qse/samples/net/http01.c b/qse/samples/net/http01.c index 777ae501..e0ee89ed 100644 --- a/qse/samples/net/http01.c +++ b/qse/samples/net/http01.c @@ -335,7 +335,20 @@ static int server_open (qse_httpd_t* httpd, qse_httpd_server_t* server) /* Solaris 8 returns EINVAL if QSE_SIZEOF(addr) is passed in as the * address size for AF_INET. */ /*if (bind (s, (struct sockaddr*)&addr, QSE_SIZEOF(addr)) <= -1) goto oops_esocket;*/ - if (bind (fd, (struct sockaddr*)&addr, addrsize) <= -1) goto oops; + if (bind (fd, (struct sockaddr*)&addr, addrsize) <= -1) + { +#ifdef IPV6_V6ONLY + if (errno == EADDRINUSE && addr.ss_family == AF_INET6) + { + int on = 1; + setsockopt (fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); + if (bind (fd, (struct sockaddr*)&addr, addrsize) <= -1) goto oops; + } + else goto oops; +#else + goto oops; +#endif + } if (listen (fd, 10) <= -1) goto oops; flag = fcntl (fd, F_GETFL); @@ -1084,6 +1097,7 @@ if (qse_htre_getcontentlen(req) > 0) if (peek) { /* cgi */ +#if 0 if (req->attr.flags & QSE_HTRE_ATTR_CHUNKED) { qse_printf (QSE_T("chunked cgi... delaying until contents are received\n")); @@ -1095,7 +1109,13 @@ 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.flags & QSE_HTRE_ATTR_LENGTH)) + else +#endif + + /*if (method == QSE_HTTP_POST && !(req->attr.flags & QSE_HTRE_ATTR_LENGTH))*/ + if (method == QSE_HTTP_POST && + !(req->attr.flags & QSE_HTRE_ATTR_LENGTH) && + !(req->attr.flags & QSE_HTRE_ATTR_CHUNKED)) { req->attr.flags &= ~QSE_HTRE_ATTR_KEEPALIVE; task = qse_httpd_entaskerror ( @@ -1110,6 +1130,7 @@ qse_printf (QSE_T("chunked cgi... delaying until contents are received\n")); if (task == QSE_NULL) goto oops; } } +#if 0 else { /* to support the chunked request, @@ -1122,6 +1143,7 @@ qse_printf (QSE_T("Entasking chunked CGI...\n")); if (task == QSE_NULL) goto oops; } } +#endif return 0; } else if (dot && qse_mbscmp (dot, QSE_MT(".nph")) == 0) @@ -1285,15 +1307,27 @@ oops: static int peek_request ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req) { - //return process_request (httpd, client, req, 1); - return proxy_request (httpd, client, req, 1); + if (memcmp (&client->local_addr, &client->orgdst_addr, sizeof(client->orgdst_addr)) == 0) + { + return process_request (httpd, client, req, 1); + } + else + { + return proxy_request (httpd, client, req, 1); + } } static int handle_request ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req) { - //return process_request (httpd, client, req, 0); - return proxy_request (httpd, client, req, 0); + if (memcmp (&client->local_addr, &client->orgdst_addr, sizeof(client->orgdst_addr)) == 0) + { + return process_request (httpd, client, req, 0); + } + else + { + return proxy_request (httpd, client, req, 0); + } } int list_directory (qse_httpd_t* httpd, const qse_mchar_t* path)