diff --git a/qse/include/qse/net/htre.h b/qse/include/qse/net/htre.h index 9b72cfea..74d2f7da 100644 --- a/qse/include/qse/net/htre.h +++ b/qse/include/qse/net/htre.h @@ -86,6 +86,7 @@ struct qse_htre_t qse_size_t content_length; int keepalive; const qse_mchar_t* expect; + const qse_mchar_t* status; } attr; /* header table */ diff --git a/qse/lib/net/htrd.c b/qse/lib/net/htrd.c index dc3b0522..41cbaa04 100644 --- a/qse/lib/net/htrd.c +++ b/qse/lib/net/htrd.c @@ -552,6 +552,12 @@ static int capture_expect (qse_htrd_t* htrd, qse_htb_pair_t* pair) return 0; } +static int capture_status (qse_htrd_t* htrd, qse_htb_pair_t* pair) +{ + htrd->re.attr.status = QSE_HTB_VPTR(pair); + return 0; +} + static int capture_transfer_encoding (qse_htrd_t* htrd, qse_htb_pair_t* pair) { int n; @@ -590,6 +596,7 @@ static QSE_INLINE int capture_key_header ( { "Connection", 10, capture_connection }, { "Content-Length", 14, capture_content_length }, { "Expect", 6, capture_expect }, + { "Status", 6, capture_status }, { "Transfer-Encoding", 17, capture_transfer_encoding } }; diff --git a/qse/lib/net/httpd-task.c b/qse/lib/net/httpd-task.c index db317024..8a07c9ac 100644 --- a/qse/lib/net/httpd-task.c +++ b/qse/lib/net/httpd-task.c @@ -373,6 +373,11 @@ static qse_httpd_task_t* entask_error ( smsg = QSE_MT("Service Unavailable"); lmsg = QSE_MT("Service UnavailableSERVICE UNAVAILABLE"); break; + + case 504: + smsg = QSE_MT("Gateway Timeout"); + lmsg = QSE_MT("Gateway TimeoutGATEWAY TIMEOUT"); + break; default: smsg = QSE_MT("Unknown"); @@ -1244,55 +1249,58 @@ struct task_cgi_t int keepalive; /* taken from the request */ int nph; - qse_htrd_t* htrd; + qse_htrd_t* script_htrd; qse_env_t* env; 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) + int reqflags; qse_htre_t* req; /* original request associated with this */ qse_mbs_t* reqfwdbuf; /* content from the request */ - int reqfwderr; +#define CGI_RES_CLIENT_DISCON (1 << 0) +#define CGI_RES_CLIENT_CHUNK (1 << 1) +#define CGI_RES_SCRIPT_OUTPUT_LENGTH (1 << 2) + int resflags; qse_mbs_t* res; qse_mchar_t* res_ptr; qse_size_t res_left; - /* if true, close connection after response is sent out */ - int disconnect; - /* if true, the content of response is chunked */ - int chunk_res; - - /* if true, content_length is set. */ - int content_length_set; /* content-length that CGI returned */ - qse_size_t content_length; - /* the number of octets in the contents received */ - qse_size_t content_received; + qse_size_t script_output_length; /* TODO: a script maybe be able to output more than the maximum value of qse_size_t */ + /* the number of octets received from the script */ + qse_size_t script_output_received; qse_mchar_t buf[MAX_SEND_SIZE]; qse_size_t buflen; }; -typedef struct cgi_htrd_xtn_t cgi_htrd_xtn_t; -struct cgi_htrd_xtn_t +typedef struct cgi_script_output_htrd_xtn_t cgi_script_output_htrd_xtn_t; +struct cgi_script_output_htrd_xtn_t { task_cgi_t* cgi; + qse_httpd_client_t* client; + qse_httpd_task_t* task; }; -typedef struct cgi_req_hdr_ctx_t cgi_req_hdr_ctx_t; -struct cgi_req_hdr_ctx_t +typedef struct cgi_client_req_hdr_ctx_t cgi_client_req_hdr_ctx_t; +struct cgi_client_req_hdr_ctx_t { qse_httpd_t* httpd; qse_env_t* env; }; -static int walk_req_headers (qse_htre_t* req, const qse_mchar_t* key, const qse_mchar_t* val, void* ctx) +static int cgi_walk_client_req_header ( + qse_htre_t* req, const qse_mchar_t* key, const qse_mchar_t* val, void* ctx) { - cgi_req_hdr_ctx_t* hdrctx; + cgi_client_req_hdr_ctx_t* hdrctx; qse_mchar_t* http_key; int ret; - hdrctx = (cgi_req_hdr_ctx_t*)ctx; + hdrctx = (cgi_client_req_hdr_ctx_t*)ctx; /* convert value to uppercase, change - to _ */ http_key = qse_mbsdup2 (QSE_MT("HTTP_"), key, req->mmgr); @@ -1307,11 +1315,14 @@ static int walk_req_headers (qse_htre_t* req, const qse_mchar_t* key, const qse_ return ret; } -static int walk_cgi_headers (qse_htre_t* req, const qse_mchar_t* key, const qse_mchar_t* val, void* ctx) +static int cgi_capture_script_header (qse_htre_t* req, const qse_mchar_t* key, const qse_mchar_t* val, void* ctx) { task_cgi_t* cgi = (task_cgi_t*)ctx; - if (qse_mbscmp (key, QSE_MT("Status")) != 0) + /* capture a header excluding Status and Connection */ + if (qse_mbscmp (key, QSE_MT("Status")) != 0 && + qse_mbscmp (key, QSE_MT("Connection")) != 0 && + qse_mbscmp (key, QSE_MT("Transfer-Encoding")) != 0) { if (qse_mbs_cat (cgi->res, key) == (qse_size_t)-1) return -1; if (qse_mbs_cat (cgi->res, QSE_MT(": ")) == (qse_size_t)-1) return -1; @@ -1322,21 +1333,25 @@ static int walk_cgi_headers (qse_htre_t* req, const qse_mchar_t* key, const qse_ return 0; } -static int cgi_htrd_peek_request (qse_htrd_t* htrd, qse_htre_t* req) +static int cgi_htrd_peek_script_output (qse_htrd_t* htrd, qse_htre_t* req) { - cgi_htrd_xtn_t* xtn = (cgi_htrd_xtn_t*) qse_htrd_getxtn (htrd); - task_cgi_t* cgi = xtn->cgi; - const qse_mchar_t* status; + cgi_script_output_htrd_xtn_t* xtn; + task_cgi_t* cgi; + int keepalive; - status = qse_htre_getheaderval (req, QSE_MT("Status")); - if (status) + xtn = (cgi_script_output_htrd_xtn_t*) qse_htrd_getxtn (htrd); + cgi = xtn->cgi; + + QSE_ASSERT (!cgi->nph); + + if (req->attr.status) { qse_mchar_t buf[128]; int nstatus; qse_mchar_t* endptr; -/* TODO: check the syntax of status value??? */ - QSE_MSTRTONUM (nstatus, status, &endptr, 10); +/* TODO: check the syntax of status value??? if not numeric??? */ + QSE_MSTRTONUM (nstatus, req->attr.status, &endptr, 10); snprintf (buf, QSE_COUNTOF(buf), QSE_MT("HTTP/%d.%d %d "), @@ -1377,7 +1392,7 @@ static int cgi_htrd_peek_request (qse_htrd_t* htrd, qse_htre_t* req) return -1; } - /* the actual Location hearer is added by + /* the actual Location header is added by * qse_htre_walkheaders() below */ } else @@ -1394,43 +1409,65 @@ static int cgi_htrd_peek_request (qse_htrd_t* htrd, qse_htre_t* req) } } + keepalive = cgi->keepalive; if (req->attr.content_length_set) { - cgi->content_length_set = 1; - cgi->content_length = req->attr.content_length; + cgi->resflags |= CGI_RES_SCRIPT_OUTPUT_LENGTH; + cgi->script_output_length = req->attr.content_length; } else { /* no Content-Length returned by CGI. */ if (qse_comparehttpversions (&cgi->version, &http_v11) >= 0) { - cgi->chunk_res = 1; + cgi->resflags |= CGI_RES_CLIENT_CHUNK; + if (qse_mbs_cat (cgi->res, QSE_MT("Transfer-Encoding: chunked\r\n")) == (qse_size_t)-1) + { + cgi->httpd->errnum = QSE_HTTPD_ENOMEM; + return -1; + } } else { - /* If CGI doesn't specify Content-Length, + /* If the CGI script doesn't specify Content-Length, * i can't honor cgi->keepalive in HTTP/1.0 * or earlier. Closing the connection is the * only way to specify the content length */ - cgi->disconnect = 1; + cgi->resflags |= CGI_RES_CLIENT_DISCON; + keepalive = 0; + + if (qse_httpd_entaskdisconnect ( + cgi->httpd, xtn->client, xtn->task) == QSE_NULL) + { + return -1; + } } } - if (cgi->chunk_res && - qse_mbs_cat (cgi->res, QSE_MT("Transfer-Encoding: chunked\r\n")) == (qse_size_t)-1) + /* NOTE: + * an explicit 'disconnect' task is added only if + * the orignal keep-alive request can't be honored. + * so if a client specifies keep-alive but doesn't + * close connection, the connection will stay alive + * until it's cleaned up for an error or idle timeout. + */ + + if (qse_mbs_cat (cgi->res, (keepalive? QSE_MT("Connection: keep-alive\r\n"): QSE_MT("Connection: close\r\n"))) == (qse_size_t)-1) { cgi->httpd->errnum = QSE_HTTPD_ENOMEM; return -1; } - if (qse_htre_walkheaders (req, walk_cgi_headers, cgi) <= -1) return -1; + /* add all other header fields excluding + * Status, Connection, Transfer-Encoding */ + if (qse_htre_walkheaders (req, cgi_capture_script_header, cgi) <= -1) return -1; /* end of header */ if (qse_mbs_cat (cgi->res, QSE_MT("\r\n")) == (qse_size_t)-1) return -1; /* content body begins here */ - cgi->content_received = qse_htre_getcontentlen(req); - if (cgi->content_length_set && - cgi->content_received > cgi->content_length) + cgi->script_output_received = qse_htre_getcontentlen(req); + if ((cgi->resflags & CGI_RES_SCRIPT_OUTPUT_LENGTH) && + cgi->script_output_received > cgi->script_output_length) { /* TODO: cgi returning too much data... something is wrong in CGI */ qse_printf (QSE_T("CGI SCRIPT FUCKED - RETURNING TOO MUCH...\n")); @@ -1438,15 +1475,15 @@ qse_printf (QSE_T("CGI SCRIPT FUCKED - RETURNING TOO MUCH...\n")); return -1; } - if (cgi->content_received > 0) + if (cgi->script_output_received > 0) { /* the initial part of the content body has been received * along with the header. it need to be added to the result * buffer. */ - if (cgi->chunk_res) + if (cgi->resflags & CGI_RES_CLIENT_CHUNK) { qse_mchar_t buf[64]; - snprintf (buf, QSE_COUNTOF(buf), QSE_MT("%lX\r\n"), (unsigned long)cgi->content_received); + snprintf (buf, QSE_COUNTOF(buf), QSE_MT("%lX\r\n"), (unsigned long)cgi->script_output_received); if (qse_mbs_cat (cgi->res, buf) == (qse_size_t)-1) { cgi->httpd->errnum = QSE_HTTPD_ENOMEM; @@ -1460,7 +1497,7 @@ qse_printf (QSE_T("CGI SCRIPT FUCKED - RETURNING TOO MUCH...\n")); return -1; } - if (cgi->chunk_res) + if (cgi->resflags & CGI_RES_CLIENT_CHUNK) { if (qse_mbs_ncat (cgi->res, QSE_MT("\r\n"), 2) == (qse_size_t)-1) { @@ -1473,9 +1510,9 @@ qse_printf (QSE_T("CGI SCRIPT FUCKED - RETURNING TOO MUCH...\n")); return 0; } -static qse_htrd_recbs_t cgi_htrd_cbs = +static qse_htrd_recbs_t cgi_script_output_htrd_cbs = { - cgi_htrd_peek_request, + cgi_htrd_peek_script_output, QSE_NULL /* not needed for CGI */ }; @@ -1563,7 +1600,7 @@ static qse_env_t* makecgienv ( #if 0 ctx.httpd = httpd; ctx.env = env; - if (qse_htre_walkheaders (req, walk_req_headers, &ctx) <= -1) return -1; + if (qse_htre_walkheaders (req, cgi_walk_client_req_header, &ctx) <= -1) return -1; #endif { @@ -1592,7 +1629,7 @@ oops: return QSE_NULL; } -static int cgi_snatch_content ( +static int cgi_snatch_client_input ( qse_htre_t* req, const qse_mchar_t* ptr, qse_size_t len, void* ctx) { qse_httpd_task_t* task; @@ -1604,6 +1641,9 @@ static int cgi_snatch_content ( if (ptr) qse_printf (QSE_T("!!!CGI SNATCHING [%.*hs]\n"), len, ptr); else qse_printf (QSE_T("!!!CGI SNATCHING DONE\n")); + QSE_ASSERT (cgi->req); + QSE_ASSERT (!(cgi->reqflags & CGI_REQ_GOTALL)); + if (ptr == QSE_NULL) { /* @@ -1616,7 +1656,11 @@ else qse_printf (QSE_T("!!!CGI SNATCHING DONE\n")); QSE_ASSERT (len == 0); /* mark the there's nothing to read form the client side */ - cgi->req = QSE_NULL; + cgi->reqflags |= CGI_REQ_GOTALL; + + /* this request object can be reused regardless of this task */ + qse_htre_unsetconcb (cgi->req); + cgi->req = QSE_NULL; /* since there is no more to read from the client side. * the relay trigger is not needed any more. */ @@ -1630,10 +1674,9 @@ else qse_printf (QSE_T("!!!CGI SNATCHING DONE\n")); * but no write trigger is set. add the write trigger * for task invocation. */ task->trigger[1].mask = QSE_HTTPD_TASK_TRIGGER_WRITE; - task->trigger[1].handle = qse_pio_gethandleasubi (&cgi->pio, QSE_PIO_IN); } } - else if (!cgi->reqfwderr) + else if (!(cgi->reqflags & CGI_REQ_FWDERR)) { /* we can write to the child process if a forwarding error * didn't occur previously. we store data from the client side @@ -1649,7 +1692,7 @@ qse_printf (QSE_T("!!!CGI SNATCHED [%.*hs]\n"), len, ptr); return 0; } -static void cgi_forward_content ( +static void cgi_forward_client_input_to_script ( qse_httpd_t* httpd, qse_httpd_task_t* task, int writable) { task_cgi_t* cgi = (task_cgi_t*)task->ctx; @@ -1660,7 +1703,7 @@ static void cgi_forward_content ( { /* there is something to forward in the forwarding buffer. */ - if (cgi->reqfwderr) + if (cgi->reqflags & CGI_REQ_FWDERR) { /* a forwarding error has occurred previously. * clear the forwarding buffer */ @@ -1676,7 +1719,6 @@ qse_printf (QSE_T("FORWARD: CLEARING REQCON FOR ERROR\n")); n = httpd->cbs->mux.writable ( httpd, qse_pio_gethandleasubi (&cgi->pio, QSE_PIO_IN), 0); -if (n == 0) qse_printf (QSE_T("FORWARD: @@@@@@@@@NOT WRITABLE\n")); if (n >= 1) { forward: @@ -1689,28 +1731,38 @@ qse_printf (QSE_T("FORWARD: @@@@@@@@@@WRITING[%.*hs]\n"), QSE_MBS_PTR(cgi->reqfwdbuf), QSE_MBS_LEN(cgi->reqfwdbuf) ); + if (n > 0) + { /* 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 (cgi->reqfwdbuf, 0, n); + qse_mbs_del (cgi->reqfwdbuf, 0, n); + } } if (n <= -1) { qse_printf (QSE_T("FORWARD: @@@@@@@@WRITE TO CGI FAILED\n")); /* TODO: logging ... */ - cgi->reqfwderr = 1; + cgi->reqflags |= CGI_REQ_FWDERR; qse_mbs_clear (cgi->reqfwdbuf); - if (cgi->req) + + if (!(cgi->reqflags & CGI_REQ_GOTALL)) { + QSE_ASSERT (cgi->req); qse_htre_discardcontent (cgi->req); - /* NOTE: cgi->req may be set to QSE_NULL - * in cgi_snatch_content() triggered by - * qse_htre_discardcontent() */ + + /* NOTE: + * this qse_htre_discardcontent() invokes + * cgi_snatch_client_input() + * which sets cgi->req to QSE_NULL + * and toggles on CGI_REQ_GOTALL. */ + QSE_ASSERT (!cgi->req); + QSE_ASSERT (cgi->reqflags & CGI_REQ_GOTALL); } } } } - else if (cgi->req == QSE_NULL) + else if (!(cgi->reqflags & CGI_REQ_GOTALL)) { /* there is nothing to read from the client side and * there is nothing more to forward in the forwarding buffer. @@ -1747,6 +1799,7 @@ static int task_init_cgi ( cgi->version = *qse_htre_getversion(arg->req); cgi->keepalive = arg->req->attr.keepalive; cgi->nph = arg->nph; + cgi->req = QSE_NULL; if (arg->req->state & QSE_HTRE_DISCARDED) { @@ -1759,6 +1812,7 @@ 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; } @@ -1772,6 +1826,7 @@ static int task_init_cgi ( * such a request to entask a cgi script dropping the * content */ qse_htre_discardcontent (arg->req); + cgi->reqflags |= CGI_REQ_GOTALL; content_length = 0; } else @@ -1798,6 +1853,7 @@ static int task_init_cgi ( QSE_ASSERT (len > 0); QSE_ASSERT (!arg->req->attr.content_length_set || (arg->req->attr.content_length_set && arg->req->attr.content_length == len)); + cgi->reqflags |= CGI_REQ_GOTALL; content_length = len; } else @@ -1814,8 +1870,12 @@ static int task_init_cgi ( /* set up a callback to be called when the request content * is fed to the htrd reader. qse_htre_addcontent() that * htrd calls invokes this callback. */ - cgi->req = arg->req; - qse_htre_setconcb (cgi->req, cgi_snatch_content, task); + + /* remember this request pointer if and only if the content + * is not fully read. so reading into this request object + * is guaranteed not to break integrity of this object */ + cgi->req = arg->req; + qse_htre_setconcb (cgi->req, cgi_snatch_client_input, task); QSE_ASSERT (arg->req->attr.content_length_set); content_length = arg->req->attr.content_length; @@ -1856,70 +1916,138 @@ static void task_fini_cgi ( qse_pio_fini (&cgi->pio); } if (cgi->res) qse_mbs_close (cgi->res); - if (cgi->htrd) qse_htrd_close (cgi->htrd); + if (cgi->script_htrd) qse_htrd_close (cgi->script_htrd); if (cgi->reqfwdbuf) qse_mbs_close (cgi->reqfwdbuf); if (cgi->req) { /* this task is destroyed but the request * associated is still alive. so clear the * callback to prevent the callback call. */ + QSE_ASSERT (!(cgi->reqflags & CGI_REQ_GOTALL)); qse_htre_unsetconcb (cgi->req); } qse_printf (QSE_T("task_fini_cgi\n")); } +static QSE_INLINE qse_ssize_t cgi_read_script_output_to_buffer ( + qse_httpd_t* httpd, qse_httpd_client_t* client, task_cgi_t* cgi) +{ + qse_ssize_t n; + + n = qse_pio_read ( + &cgi->pio, QSE_PIO_OUT, + &cgi->buf[cgi->buflen], + QSE_SIZEOF(cgi->buf) - cgi->buflen + ); + if (n > 0) cgi->buflen += n; + return n; +} + +static QSE_INLINE qse_ssize_t cgi_write_script_output_to_client ( + qse_httpd_t* httpd, qse_httpd_client_t* client, task_cgi_t* cgi) +{ + qse_ssize_t n; + + httpd->errnum = QSE_HTTPD_ENOERR; + n = httpd->cbs->client.send (httpd, client, cgi->buf, cgi->buflen); + if (n > 0) + { + QSE_MEMCPY (&cgi->buf[0], &cgi->buf[n], cgi->buflen - n); + cgi->buflen -= n; + } + return n; +} + static int task_main_cgi_5 ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) { task_cgi_t* cgi = (task_cgi_t*)task->ctx; - qse_ssize_t n; QSE_ASSERT (cgi->pio_inited); if (task->trigger[2].mask & QSE_HTTPD_TASK_TRIGGER_READABLE) { - cgi_forward_content (httpd, task, 0); + cgi_forward_client_input_to_script (httpd, task, 0); } else if (task->trigger[1].mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE) { - cgi_forward_content (httpd, task, 1); + cgi_forward_client_input_to_script (httpd, task, 1); } if (!(task->trigger[2].mask & QSE_HTTPD_TASK_TRIGGER_WRITE) || (task->trigger[2].mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE)) { -qse_printf (QSE_T("task_main_cgi_5\n")); +qse_printf (QSE_T("task_main_cgi_5 about to write %d bytes\n"), (int)cgi->buflen); if (cgi->buflen > 0) { -/* TODO: check if cgi outputs more than content-length if it is set... */ - httpd->errnum = QSE_HTTPD_ENOERR; - n = httpd->cbs->client.send (httpd, client, cgi->buf, cgi->buflen); - if (n <= -1) + if (cgi_write_script_output_to_client (httpd, client, cgi) <= -1) { /* can't return internal server error any more... */ /* TODO: logging ... */ return -1; } - - QSE_MEMCPY (&cgi->buf[0], &cgi->buf[n], cgi->buflen - n); - cgi->buflen -= n; } } /* if forwarding didn't finish, something is not really right... * so long as the output from CGI is finished, no more forwarding * is performed */ - return (cgi->buflen > 0 || cgi->req || + return (cgi->buflen > 0 || !(cgi->reqflags & CGI_REQ_GOTALL) || (cgi->reqfwdbuf && QSE_MBS_LEN(cgi->reqfwdbuf) > 0))? 1: 0; } +static int task_main_cgi_4_nph ( + qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) +{ + task_cgi_t* cgi = (task_cgi_t*)task->ctx; + qse_ssize_t n; + + QSE_ASSERT (cgi->nph); + + if (task->trigger[2].mask & QSE_HTTPD_TASK_TRIGGER_READABLE) + { + cgi_forward_client_input_to_script (httpd, task, 0); + } + else if (task->trigger[1].mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE) + { + cgi_forward_client_input_to_script (httpd, task, 1); + } + + if (task->trigger[0].mask & QSE_HTTPD_TASK_TRIGGER_READABLE) + { + if (cgi->buflen < QSE_SIZEOF(cgi->buf)) + { + n = cgi_read_script_output_to_buffer (httpd, client, cgi); + if (n <= -1) return -1; /* TODO: logging */ + if (n == 0) + { + /* switch to the next phase */ + task->main = task_main_cgi_5; + task->trigger[0].mask = 0; + task->trigger[2].mask |= QSE_HTTPD_TASK_TRIGGER_WRITE; + return 1; + } + } + + QSE_ASSERT (cgi->buflen > 0); + if (cgi_write_script_output_to_client (httpd, client, cgi) <= -1) + { +/* TODO: logging ... */ + return -1; + } + } + + return 1; +} + static int task_main_cgi_4 ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) { task_cgi_t* cgi = (task_cgi_t*)task->ctx; qse_ssize_t n; + QSE_ASSERT (!cgi->nph); QSE_ASSERT (cgi->pio_inited); qse_printf (QSE_T("task_main_cgi_4 trigger[0].mask=%d trigger[1].mask=%d trigger[2].mask=%d\n"), @@ -1927,17 +2055,17 @@ qse_printf (QSE_T("task_main_cgi_4 trigger[0].mask=%d trigger[1].mask=%d trigger if (task->trigger[2].mask & QSE_HTTPD_TASK_TRIGGER_READABLE) { - cgi_forward_content (httpd, task, 0); + cgi_forward_client_input_to_script (httpd, task, 0); } else if (task->trigger[1].mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE) { - cgi_forward_content (httpd, task, 1); + cgi_forward_client_input_to_script (httpd, task, 1); } if (task->trigger[0].mask & QSE_HTTPD_TASK_TRIGGER_READABLE) { qse_printf (QSE_T("TASK_MAIN_CGI_4\n")); - if (cgi->chunk_res) + if (cgi->resflags & CGI_RES_CLIENT_CHUNK) { qse_size_t count, extra; qse_mchar_t chunklen[7]; @@ -1958,7 +2086,6 @@ qse_printf (QSE_T("TASK_MAIN_CGI_4\n")); ); if (n <= -1) { - /* can't return internal server error any more... */ /* TODO: logging ... */ return -1; } @@ -1988,10 +2115,10 @@ qse_printf (QSE_T("TASK_MAIN_CGI_4\n")); cgi->buf[cgi->buflen++] = QSE_MT('\r'); cgi->buf[cgi->buflen++] = QSE_MT('\n'); - cgi->content_received += n; + cgi->script_output_received += n; - if (cgi->content_length_set && - cgi->content_received > cgi->content_length) + if ((cgi->resflags & CGI_RES_SCRIPT_OUTPUT_LENGTH) && + cgi->script_output_received > cgi->script_output_length) { /* TODO: cgi returning too much data... something is wrong in CGI */ qse_printf (QSE_T("CGI FUCKED UP...RETURNING TOO MUCH DATA\n")); @@ -2004,31 +2131,22 @@ qse_printf (QSE_T("TASK_MAIN_CGI_4\n")); qse_printf (QSE_T("READING IN NON-CHUNKED MODE...\n")); if (cgi->buflen < QSE_SIZEOF(cgi->buf)) { - n = qse_pio_read ( - &cgi->pio, QSE_PIO_OUT, - &cgi->buf[cgi->buflen], - QSE_SIZEOF(cgi->buf) - cgi->buflen - ); - if (n <= -1) - { - /* can't return internal server error any more... */ - /* TODO: loggig ... */ - return -1; - } - if (n == 0) + n = cgi_read_script_output_to_buffer (httpd, client, cgi); + if (n <= -1) return -1; /* TODO: logging */ + if (n == 0) { + /* switch to the next phase */ task->main = task_main_cgi_5; task->trigger[0].mask = 0; task->trigger[2].mask |= QSE_HTTPD_TASK_TRIGGER_WRITE; return 1; } - - cgi->buflen += n; - cgi->content_received += n; - if (cgi->content_length_set && - cgi->content_received > cgi->content_length) + cgi->script_output_received += n; + if ((cgi->resflags & CGI_RES_SCRIPT_OUTPUT_LENGTH) && + cgi->script_output_received > cgi->script_output_length) { + /* TODO: logging */ /* TODO: cgi returning too much data... something is wrong in CGI */ qse_printf (QSE_T("CGI FUCKED UP...RETURNING TOO MUCH DATA\n")); return -1; @@ -2039,19 +2157,12 @@ qse_printf (QSE_T("TASK_MAIN_CGI_4\n")); /* the main loop invokes the task function only if the client * side is writable. it should be safe to write whenever * this task function is called. */ - - httpd->errnum = QSE_HTTPD_ENOERR; - n = httpd->cbs->client.send (httpd, client, cgi->buf, cgi->buflen); - if (n <= -1) + QSE_ASSERT (cgi->buflen > 0); + if (cgi_write_script_output_to_client (httpd, client, cgi) <= -1) { - /* can't return internal server error any more... */ - /* TODO: logging ... */ - qse_printf (QSE_T("CGI SEND FAILURE\n")); + /* TODO: logging ... */ return -1; } - - QSE_MEMCPY (&cgi->buf[0], &cgi->buf[n], cgi->buflen - n); - cgi->buflen -= n; } return 1; @@ -2067,13 +2178,15 @@ static int task_main_cgi_3 ( qse_ssize_t n; qse_size_t count; + QSE_ASSERT (!cgi->nph); + if (task->trigger[2].mask & QSE_HTTPD_TASK_TRIGGER_READABLE) { - cgi_forward_content (httpd, task, 0); + cgi_forward_client_input_to_script (httpd, task, 0); } else if (task->trigger[1].mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE) { - cgi_forward_content (httpd, task, 1); + cgi_forward_client_input_to_script (httpd, task, 1); } /* send the partial reponse received with the initial line and headers @@ -2104,8 +2217,8 @@ qse_printf (QSE_T("[cgi-3 send failure....\n")); { qse_mbs_clear (cgi->res); - if (cgi->content_length_set && - cgi->content_received >= cgi->content_length) + if ((cgi->resflags & CGI_RES_SCRIPT_OUTPUT_LENGTH) && + cgi->script_output_received >= cgi->script_output_length) { qse_printf (QSE_T("[switching to cgi-5....\n")); /* if a cgi script specified the content length @@ -2140,10 +2253,10 @@ static int task_main_cgi_2 ( * it is possible that some contents are also read in. * The callback function to qse_htrd_feed() handles this also. */ - task_cgi_t* cgi = (task_cgi_t*)task->ctx; qse_ssize_t n; + QSE_ASSERT (!cgi->nph); QSE_ASSERT (cgi->pio_inited); { @@ -2154,17 +2267,16 @@ qse_printf (QSE_T("[cgi_2 at %lld]\n"), now); if (task->trigger[2].mask & QSE_HTTPD_TASK_TRIGGER_READABLE) { qse_printf (QSE_T("[cgi_2 write]\n")); - cgi_forward_content (httpd, task, 0); + cgi_forward_client_input_to_script (httpd, task, 0); } else if (task->trigger[1].mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE) { - cgi_forward_content (httpd, task, 1); + cgi_forward_client_input_to_script (httpd, task, 1); } if (task->trigger[0].mask & QSE_HTTPD_TASK_TRIGGER_READABLE) { qse_printf (QSE_T("[cgi_2 read]\n")); - /* <- can i make it non-block?? or use select??? pio_tryread()? */ n = qse_pio_read ( &cgi->pio, QSE_PIO_OUT, &cgi->buf[cgi->buflen], @@ -2188,7 +2300,7 @@ qse_printf (QSE_T("#####PREMATURE EOF FROM CHILD\n")); cgi->buflen += n; qse_printf (QSE_T("#####CGI FEED [%.*hs]\n"), (int)cgi->buflen, cgi->buf); - if (qse_htrd_feed (cgi->htrd, cgi->buf, cgi->buflen) <= -1) + if (qse_htrd_feed (cgi->script_htrd, cgi->buf, cgi->buflen) <= -1) { /* TODO: logging */ qse_printf (QSE_T("#####INVALID HEADER FROM FROM CHILD [%.*hs]\n"), (int)cgi->buflen, cgi->buf); @@ -2204,17 +2316,10 @@ qse_printf (QSE_T("#####INVALID HEADER FROM FROM CHILD [%.*hs]\n"), (int)cgi->bu * some contents could be in cgi->res, though. * * qse_htrd_feed() must have executed the peek handler - * (cgi_htrd_peek_request()) which handled the request header. - * so i won't call qse_htrd_feed() any more. intead, i'll - * simply read directly from the pipe. + * (cgi_htrd_peek_script_output()) which handled the + * request header. so i won't call qse_htrd_feed() any more. + * intead, i'll simply read directly from the pipe. */ - - if (cgi->disconnect && - qse_httpd_entaskdisconnect (httpd, client, task) == QSE_NULL) - { - goto oops; - } - cgi->res_ptr = QSE_MBS_PTR(cgi->res); cgi->res_left = QSE_MBS_LEN(cgi->res); @@ -2253,14 +2358,16 @@ static int task_main_cgi ( } else { - cgi_htrd_xtn_t* xtn; - cgi->htrd = qse_htrd_open (httpd->mmgr, QSE_SIZEOF(cgi_htrd_xtn_t)); - if (cgi->htrd == QSE_NULL) goto oops; - xtn = (cgi_htrd_xtn_t*) qse_htrd_getxtn (cgi->htrd); + cgi_script_output_htrd_xtn_t* xtn; + cgi->script_htrd = qse_htrd_open (httpd->mmgr, QSE_SIZEOF(cgi_script_output_htrd_xtn_t)); + if (cgi->script_htrd == QSE_NULL) goto oops; + xtn = (cgi_script_output_htrd_xtn_t*) qse_htrd_getxtn (cgi->script_htrd); xtn->cgi = cgi; - qse_htrd_setrecbs (cgi->htrd, &cgi_htrd_cbs); + xtn->task = task; + xtn->client = client; + qse_htrd_setrecbs (cgi->script_htrd, &cgi_script_output_htrd_cbs); qse_htrd_setoption ( - cgi->htrd, + cgi->script_htrd, QSE_HTRD_SKIPINITIALLINE | QSE_HTRD_PEEKONLY | QSE_HTRD_REQUEST @@ -2301,6 +2408,9 @@ static int task_main_cgi ( * checking the data availability from the child process */ task->trigger[0].mask = QSE_HTTPD_TASK_TRIGGER_READ; task->trigger[0].handle = qse_pio_gethandleasubi (&cgi->pio, QSE_PIO_OUT); + task->trigger[1].handle = qse_pio_gethandleasubi (&cgi->pio, QSE_PIO_IN); + task->trigger[2].handle = client->handle; + if (cgi->reqfwdbuf) { /* the existence of the forwarding buffer leads to a trigger @@ -2311,7 +2421,6 @@ static int task_main_cgi ( /* there are still things to forward from the client-side. * i can rely on this relay trigger for task invocation. */ task->trigger[2].mask = QSE_HTTPD_TASK_TRIGGER_READ; - task->trigger[2].handle = client->handle; } if (QSE_MBS_LEN(cgi->reqfwdbuf) > 0) @@ -2322,7 +2431,7 @@ static int task_main_cgi ( * between the initializer function (task_init_cgi()) and * this function. so let's forward it initially. */ qse_printf (QSE_T("FORWARDING INITIAL PART OF CONTENT...\n")); - cgi_forward_content (httpd, task, 0); + cgi_forward_client_input_to_script (httpd, task, 0); /* if the initial forwarding clears the forwarding * buffer, there is nothing more to forward. @@ -2337,12 +2446,11 @@ qse_printf (QSE_T("FORWARDING INITIAL PART OF CONTENT...\n")); * invoke this task so long as it is able to write * to the child process */ task->trigger[1].mask = QSE_HTTPD_TASK_TRIGGER_WRITE; - task->trigger[1].handle = qse_pio_gethandleasubi (&cgi->pio, QSE_PIO_IN); } } } - task->main = cgi->nph? task_main_cgi_4: task_main_cgi_2; + task->main = cgi->nph? task_main_cgi_4_nph: task_main_cgi_2; return 1; oops: @@ -2351,10 +2459,10 @@ oops: qse_mbs_close (cgi->res); cgi->res = QSE_NULL; } - if (cgi->htrd) + if (cgi->script_htrd) { - qse_htrd_close (cgi->htrd); - cgi->htrd = QSE_NULL; + qse_htrd_close (cgi->script_htrd); + cgi->script_htrd = QSE_NULL; } return (entask_error ( @@ -2424,14 +2532,14 @@ struct task_proxy_t qse_http_version_t version; int keepalive; /* taken from the request */ - qse_htrd_t* htrd; + qse_htrd_t* peer_htrd; qse_httpd_peer_t peer; -#define PEER_OPEN (1 << 0) -#define PEER_CONNECTED (1 << 1) +#define PROXY_PEER_OPEN (1 << 0) +#define PROXY_PEER_CONNECTED (1 << 1) int peer_status; - qse_htre_t* req; /* original request associated with this */ + qse_htre_t* req; /* original client request associated with this */ qse_mbs_t* reqfwdbuf; /* content from the request */ int reqfwderr; @@ -2439,32 +2547,40 @@ struct task_proxy_t qse_size_t res_consumed; qse_size_t res_pending; +#define AWAIT_100 (1 << 0) +#define AWAIT_RES (1 << 1) +#define AWAIT_CON (1 << 2) +#define RECEIVED_100 (1 << 3) +#define RECEIVED_RES (1 << 4) +#define RECEIVED_CON (1 << 5) int expect_100; -#define PROXY_RES_DISCONNECT (1 << 0) -#define PROXY_RES_CHUNK (1 << 1) -#define PROXY_RES_CHUNK_GOTALL (1 << 2) - int res_status; +#define PROXY_RES_CLIENT_DISCON (1 << 0) +#define PROXY_RES_CLIENT_CHUNK (1 << 1) - /* if true, content_length is set. */ - int content_length_set; - /* content-length that CGI returned */ - qse_size_t content_length; - /* the number of octets in the contents received */ - qse_size_t content_received; +#define PROXY_RES_PEER_CLOSE (1 << 2) /* read until close */ +#define PROXY_RES_PEER_CLOSED (1 << 3) + +#define PROXY_RES_PEER_CHUNK (1 << 4) +#define PROXY_RES_PEER_OUTPUT_LENGTH (1 << 6) /* content-length is set */ + int resflags; + + qse_size_t peer_output_length; + qse_size_t peer_output_received; qse_mchar_t buf[MAX_SEND_SIZE]; qse_size_t buflen; }; -typedef struct proxy_htrd_xtn_t proxy_htrd_xtn_t; -struct proxy_htrd_xtn_t +typedef struct proxy_peer_htrd_xtn_t proxy_peer_htrd_xtn_t; +struct proxy_peer_htrd_xtn_t { task_proxy_t* proxy; + qse_httpd_client_t* client; qse_httpd_task_t* task; }; -static int proxy_snatch_client_content ( +static int proxy_snatch_client_input ( qse_htre_t* req, const qse_mchar_t* ptr, qse_size_t len, void* ctx) { qse_httpd_task_t* task; @@ -2488,6 +2604,7 @@ else qse_printf (QSE_T("!!!PROXY SNATCHING DONE\n")); QSE_ASSERT (len == 0); /* mark the there's nothing to read form the client side */ + qse_htre_unsetconcb (proxy->req); proxy->req = QSE_NULL; /* since there is no more to read from the client side. @@ -2495,7 +2612,7 @@ else qse_printf (QSE_T("!!!PROXY SNATCHING DONE\n")); task->trigger[2].mask = 0; if (QSE_MBS_LEN(proxy->reqfwdbuf) > 0 && - (proxy->peer_status & PEER_CONNECTED) && + (proxy->peer_status & PROXY_PEER_CONNECTED) && !(task->trigger[0].mask & QSE_HTTPD_TASK_TRIGGER_WRITE)) { /* there's nothing more to read from the client side. @@ -2521,11 +2638,11 @@ qse_printf (QSE_T("!!!PROXY SNATCHED [%.*hs]\n"), len, ptr); return 0; } -static int proxy_snatch_peer_content ( +static int proxy_snatch_peer_output ( qse_htre_t* req, const qse_mchar_t* ptr, qse_size_t len, void* ctx) { /* this is a content callback function called by the peer - * response reader (proxy->htrd). */ + * response reader (proxy->peer_htrd). */ qse_httpd_task_t* task; task_proxy_t* proxy; @@ -2533,14 +2650,14 @@ static int proxy_snatch_peer_content ( task = (qse_httpd_task_t*)ctx; proxy = (task_proxy_t*)task->ctx; - QSE_ASSERT (proxy->res_status & PROXY_RES_CHUNK); + QSE_ASSERT (proxy->resflags & PROXY_RES_CLIENT_CHUNK); if (ptr == QSE_NULL) { /* content completed */ qse_printf (QSE_T("PROXY GOT ALL RESPONSE>>>>>>>\n")); - if (proxy->res_status & PROXY_RES_CHUNK) /*TODO: remove this check */ + if (proxy->resflags & PROXY_RES_CLIENT_CHUNK) /*TODO: remove this check */ { qse_printf (QSE_T("ADDING CHUNK END>>>>>>>\n")); if (qse_mbs_cat (proxy->res, QSE_MT("0\r\n\r\n")) == (qse_size_t)-1) @@ -2550,12 +2667,15 @@ qse_printf (QSE_T("ADDING CHUNK END>>>>>>>\n")); } } - proxy->res_status |= PROXY_RES_CHUNK_GOTALL; +/* TODO: include trailers into proxy->res if any. for this htrd and htre need to be enhanced as well */ + + proxy->expect_100 &= ~AWAIT_CON; + proxy->expect_100 |= RECEIVED_CON; } else { /* append the peer response content to the response buffer */ - if (proxy->res_status & PROXY_RES_CHUNK) + if (proxy->resflags & PROXY_RES_CLIENT_CHUNK) /* TODO: remove this check */ { qse_mchar_t buf[64]; snprintf (buf, QSE_COUNTOF(buf), QSE_MT("%lX\r\n"), (unsigned long)len); @@ -2572,7 +2692,7 @@ qse_printf (QSE_T("ADDING CHUNK END>>>>>>>\n")); return -1; } - if (proxy->res_status & PROXY_RES_CHUNK) + if (proxy->resflags & PROXY_RES_CLIENT_CHUNK) /* TODO: remove this check */ { if (qse_mbs_cat (proxy->res, QSE_MT("\r\n")) == (qse_size_t)-1) { @@ -2590,105 +2710,149 @@ static int add_header_to_proxy_resbuf (qse_htre_t* req, const qse_mchar_t* key, { task_proxy_t* proxy = (task_proxy_t*)ctx; - if (qse_mbs_cat (proxy->res, key) == (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, val) == (qse_size_t)-1) return -1; - if (qse_mbs_cat (proxy->res, QSE_MT("\r\n")) == (qse_size_t)-1) return -1; + if (qse_mbscmp (key, QSE_MT("Transfer-Encoding")) != 0) + { + if (qse_mbs_cat (proxy->res, key) == (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, val) == (qse_size_t)-1) return -1; + if (qse_mbs_cat (proxy->res, QSE_MT("\r\n")) == (qse_size_t)-1) return -1; + } return 0; } -static int proxy_htrd_peek_request (qse_htrd_t* htrd, qse_htre_t* req) +static int proxy_htrd_peek_peer_output (qse_htrd_t* htrd, qse_htre_t* res) { - proxy_htrd_xtn_t* xtn = (proxy_htrd_xtn_t*) qse_htrd_getxtn (htrd); - task_proxy_t* proxy = xtn->proxy; + proxy_peer_htrd_xtn_t* xtn; + task_proxy_t* proxy; - if (proxy->expect_100 > 0 && qse_htre_getscodeval(req) == 100) + xtn = (proxy_peer_htrd_xtn_t*) qse_htrd_getxtn (htrd); + proxy = xtn->proxy; + + if (proxy->expect_100 & RECEIVED_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 (). */ + proxy->httpd->errnum = QSE_HTTPD_EINVAL; + return -1; + } + + if ((proxy->expect_100 & AWAIT_100) && qse_htre_getscodeval(res) == 100) { /* TODO: check if the request contained Expect... */ qse_printf (QSE_T("10000000000000000000000000000 CONTINUE 10000000000000000000000000000000\n")); - proxy->expect_100 = 0; + proxy->expect_100 &= ~AWAIT_100; + proxy->expect_100 |= RECEIVED_100; - if (qse_mbs_cat (proxy->res, qse_htre_getverstr(req)) == (qse_size_t)-1) return -1; + 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(req)) == (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(req)) == (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\r\n")) == (qse_size_t)-1) return -1; - /* '100 continue' should not include contents. drop them if any */ - qse_htre_discardcontent (req); + /* i don't relay any headers and contents in '100 continue' + * back to the client */ + qse_htre_discardcontent (res); } else { + int keepalive; + /* add initial line and headers to proxy->res */ - proxy->expect_100 = -1; + proxy->expect_100 &= ~AWAIT_100; + proxy->expect_100 &= ~AWAIT_RES; + proxy->expect_100 |= RECEIVED_RES; qse_printf (QSE_T("NORMAL REPLY 222222222222222222222 NORMAL REPLY\n")); /* begin initial line */ - if (qse_mbs_cat (proxy->res, qse_htre_getverstr(req)) == (qse_size_t)-1) return -1; + 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(req)) == (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(req)) == (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 */ - proxy->content_received = qse_htre_getcontentlen(req); - if (req->attr.content_length_set) + keepalive = proxy->keepalive; + if (res->attr.content_length_set) { - proxy->content_length_set = 1; - proxy->content_length = req->attr.content_length; + /* the response from the peer is length based */ + proxy->resflags |= PROXY_RES_PEER_OUTPUT_LENGTH; + proxy->peer_output_length = res->attr.content_length; } else { - /* no Content-Length returned by the peer. */ if (qse_comparehttpversions (&proxy->version, &http_v11) >= 0) { - proxy->res_status |= PROXY_RES_CHUNK; + /* client supports chunking */ + + /* 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) + { + /* mark the peer output is chunked */ + proxy->resflags |= PROXY_RES_PEER_CHUNK; + } + else + { + /* no chunk, no size */ + proxy->resflags |= PROXY_RES_PEER_CLOSE; + } } - else + else { - /* If the peer doesn't specify Content-Length, - * i can't honor proxy->keepalive in HTTP/1.0 - * or earlier. Closing the connection is the - * only way to specify the content length */ - proxy->res_status |= PROXY_RES_DISCONNECT; + /* client doesn't support chunking */ + proxy->resflags |= PROXY_RES_CLIENT_DISCON; + if (qse_httpd_entaskdisconnect (proxy->httpd, xtn->client, xtn->task) == QSE_NULL) return -1; + + if (res->attr.chunked) + { + proxy->resflags |= PROXY_RES_PEER_CHUNK; + } + else + { + /* read peer until it closes */ + proxy->resflags |= PROXY_RES_PEER_CLOSE; + } } } - /* begin headers */ - if (proxy->res_status & PROXY_RES_CHUNK && - qse_mbs_cat (proxy->res, QSE_MT("Transfer-Encoding: chunked\r\n")) == (qse_size_t)-1) - { - proxy->httpd->errnum = QSE_HTTPD_ENOMEM; - return -1; - } +/* TODO: add Conntion here like in cgi handler??? depending on keepalive??? */ - if (qse_htre_walkheaders (req, add_header_to_proxy_resbuf, proxy) <= -1) return -1; + if (qse_htre_walkheaders (res, add_header_to_proxy_resbuf, proxy) <= -1) return -1; + /* end of headers */ if (qse_mbs_cat (proxy->res, QSE_MT("\r\n")) == (qse_size_t)-1) return -1; - /* end headers */ + /* content body begins here */ - proxy->content_received = qse_htre_getcontentlen(req); - if (proxy->content_length_set && - proxy->content_received > proxy->content_length) + proxy->peer_output_received = qse_htre_getcontentlen(res); + if ((proxy->resflags & PROXY_RES_PEER_OUTPUT_LENGTH) && + proxy->peer_output_received > proxy->peer_output_length) { /* TODO: proxy returning too much data... something is wrong in CGI */ -qse_printf (QSE_T("PROXY SCRIPT FUCKED - RETURNING TOO MUCH...\n")); +qse_printf (QSE_T("PROXY PEER FUCKED - RETURNING TOO MUCH...\n")); proxy->httpd->errnum = QSE_HTTPD_EINVAL; /* TODO: change it to a better error code */ return -1; } - if (proxy->content_received > 0) + if (proxy->peer_output_received > 0) { /* the initial part of the content body has been received - * along with the header. it need to be added to the result + * along with the header. it needs to be added to the result * buffer. */ - if (proxy->res_status & PROXY_RES_CHUNK) + if (proxy->resflags & PROXY_RES_CLIENT_CHUNK) { qse_mchar_t buf[64]; - snprintf (buf, QSE_COUNTOF(buf), QSE_MT("%lX\r\n"), (unsigned long)proxy->content_received); + snprintf (buf, QSE_COUNTOF(buf), QSE_MT("%lX\r\n"), (unsigned long)proxy->peer_output_received); if (qse_mbs_cat (proxy->res, buf) == (qse_size_t)-1) { proxy->httpd->errnum = QSE_HTTPD_ENOMEM; @@ -2696,13 +2860,13 @@ qse_printf (QSE_T("PROXY SCRIPT FUCKED - RETURNING TOO MUCH...\n")); } } - if (qse_mbs_ncat (proxy->res, qse_htre_getcontentptr(req), qse_htre_getcontentlen(req)) == (qse_size_t)-1) + if (qse_mbs_ncat (proxy->res, qse_htre_getcontentptr(res), qse_htre_getcontentlen(res)) == (qse_size_t)-1) { proxy->httpd->errnum = QSE_HTTPD_ENOMEM; return -1; } - if (proxy->res_status & PROXY_RES_CHUNK) + if (proxy->resflags & PROXY_RES_CLIENT_CHUNK) { if (qse_mbs_ncat (proxy->res, QSE_MT("\r\n"), 2) == (qse_size_t)-1) { @@ -2712,10 +2876,10 @@ qse_printf (QSE_T("PROXY SCRIPT FUCKED - RETURNING TOO MUCH...\n")); } } - if (proxy->res_status & PROXY_RES_CHUNK) + if (proxy->resflags & PROXY_RES_CLIENT_CHUNK) { /* arrange to store further contents received to proxy->res */ - qse_htre_setconcb (req, proxy_snatch_peer_content, xtn->task); + qse_htre_setconcb (res, proxy_snatch_peer_output, xtn->task); } } @@ -2723,19 +2887,19 @@ qse_printf (QSE_T("PROXY SCRIPT FUCKED - RETURNING TOO MUCH...\n")); return 0; } -static int proxy_htrd_handle_request (qse_htrd_t* htrd, qse_htre_t* req) +static int proxy_htrd_handle_peer_output (qse_htrd_t* htrd, qse_htre_t* req) { -qse_printf (QSE_T("FINISHED READING REQUEST...\n")); +qse_printf (QSE_T("FINISHED READING RESPONSE...\n")); return 0; } static qse_htrd_recbs_t proxy_htrd_cbs = { - proxy_htrd_peek_request, - proxy_htrd_handle_request + proxy_htrd_peek_peer_output, + proxy_htrd_handle_peer_output }; -static void proxy_forward_content ( +static void proxy_forward_client_input_to_peer ( qse_httpd_t* httpd, qse_httpd_task_t* task, int writable) { task_proxy_t* proxy = (task_proxy_t*)task->ctx; @@ -2790,7 +2954,7 @@ qse_printf (QSE_T("PROXY FORWARD: @@@@@@@@WRITE TO PROXY FAILED\n")); { qse_htre_discardcontent (proxy->req); /* NOTE: proxy->req may be set to QSE_NULL - * in proxy_snatch_client_content() triggered by + * in proxy_snatch_client_input() triggered by * qse_htre_discardcontent() */ } } @@ -2824,7 +2988,6 @@ static int task_init_proxy ( { task_proxy_t* proxy; task_proxy_arg_t* arg; - qse_size_t content_length; qse_size_t len; const qse_mchar_t* ptr; @@ -2837,6 +3000,7 @@ static int task_init_proxy ( proxy->version = *qse_htre_getversion(arg->req); proxy->keepalive = arg->req->attr.keepalive; proxy->peer.nwad = arg->peer_nwad; + proxy->req = QSE_NULL; /* -------------------------------------------------------------------- * TODO: compose headers to send to peer and push them to fwdbuf... @@ -2863,28 +3027,24 @@ len = 1024; if (qse_htre_walkheaders (arg->req, add_header_to_proxy_fwdbuf, proxy) <= -1) goto oops; if (qse_mbs_cat (proxy->reqfwdbuf, QSE_MT("\r\n")) == (qse_size_t)-1) goto oops; + proxy->expect_100 |= AWAIT_RES; if (arg->req->attr.expect && (arg->req->version.major > 1 || (arg->req->version.major == 1 && arg->req->version.minor >= 1))) { if (qse_mbscasecmp(arg->req->attr.expect, QSE_MT("100-continue")) == 0) { - proxy->expect_100 = 1; + proxy->expect_100 |= AWAIT_100; } } - if (arg->req->state & QSE_HTRE_DISCARDED) - { - content_length = 0; - goto done; - } + if (arg->req->state & QSE_HTRE_DISCARDED) goto done; len = qse_htre_getcontentlen(arg->req); if ((arg->req->state & QSE_HTRE_COMPLETED) && len <= 0) { /* the content part is completed and no content * in the content buffer. there is nothing to forward */ - content_length = 0; goto done; } @@ -2897,7 +3057,6 @@ len = 1024; * such a request to entask a proxy script dropping the * content */ qse_htre_discardcontent (arg->req); - content_length = 0; } else { @@ -2915,7 +3074,6 @@ len = 1024; QSE_ASSERT (len > 0); QSE_ASSERT (!arg->req->attr.content_length_set || (arg->req->attr.content_length_set && arg->req->attr.content_length == len)); - content_length = len; } else { @@ -2932,10 +3090,8 @@ len = 1024; * is fed to the htrd reader. qse_htre_addcontent() that * htrd calls invokes this callback. */ proxy->req = arg->req; - qse_htre_setconcb (proxy->req, proxy_snatch_client_content, task); - + qse_htre_setconcb (proxy->req, proxy_snatch_client_input, task); QSE_ASSERT (arg->req->attr.content_length_set); - content_length = arg->req->attr.content_length; } } @@ -2968,10 +3124,11 @@ static void task_fini_proxy ( { task_proxy_t* proxy = (task_proxy_t*)task->ctx; - if (proxy->peer_status & PEER_OPEN) + if (proxy->peer_status & PROXY_PEER_OPEN) httpd->cbs->peer.close (httpd, &proxy->peer); + if (proxy->res) qse_mbs_close (proxy->res); - if (proxy->htrd) qse_htrd_close (proxy->htrd); + if (proxy->peer_htrd) qse_htrd_close (proxy->peer_htrd); if (proxy->reqfwdbuf) qse_mbs_close (proxy->reqfwdbuf); if (proxy->req) qse_htre_unsetconcb (proxy->req); } @@ -2986,11 +3143,11 @@ static int task_main_proxy_5 ( if (task->trigger[2].mask & QSE_HTTPD_TASK_TRIGGER_READABLE) { - proxy_forward_content (httpd, task, 0); + proxy_forward_client_input_to_peer (httpd, task, 0); } else if (task->trigger[0].mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE) { - proxy_forward_content (httpd, task, 1); + proxy_forward_client_input_to_peer (httpd, task, 1); } qse_printf (QSE_T("task_main_proxy_5\n")); @@ -3034,11 +3191,11 @@ qse_printf (QSE_T("task_main_proxy_4 trigger[0].mask=%d trigger[1].mask=%d trigg if (task->trigger[2].mask & QSE_HTTPD_TASK_TRIGGER_READABLE) { - proxy_forward_content (httpd, task, 0); + proxy_forward_client_input_to_peer (httpd, task, 0); } else if (task->trigger[0].mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE) { - proxy_forward_content (httpd, task, 1); + proxy_forward_client_input_to_peer (httpd, task, 1); } qse_printf (QSE_T("task_main_proxy_4 about to read from PEER...\n")); @@ -3048,7 +3205,7 @@ qse_printf (QSE_T("task_main_proxy_4 about to read from PEER...\n")); if (proxy->buflen < QSE_SIZEOF(proxy->buf)) { -qse_printf (QSE_T("task_main_proxy_4 reading from PEER... %d %d\n"), (int)proxy->content_length, (int)proxy->content_received); +qse_printf (QSE_T("task_main_proxy_4 reading from PEER... %d %d\n"), (int)proxy->peer_output_length, (int)proxy->peer_output_received); httpd->errnum = QSE_HTTPD_ENOERR; n = httpd->cbs->peer.recv ( httpd, &proxy->peer, @@ -3064,6 +3221,15 @@ qse_printf (QSE_T("task_main_proxy_4 read from PEER...%d\n"), (int)n); } if (n == 0) { + if (proxy->resflags & PROXY_RES_PEER_OUTPUT_LENGTH) + { + if (proxy->peer_output_received < proxy->peer_output_length) + { + qse_printf (QSE_T("PROXY FUCKED UP...PEER CLOSING PREMATURELY\n")); + return -1; + } + } + task->main = task_main_proxy_5; task->trigger[0].mask = 0; task->trigger[2].mask |= QSE_HTTPD_TASK_TRIGGER_WRITE; @@ -3071,24 +3237,24 @@ qse_printf (QSE_T("task_main_proxy_4 read from PEER...%d\n"), (int)n); } proxy->buflen += n; - proxy->content_received += n; + proxy->peer_output_received += n; - if (proxy->content_length_set && - proxy->content_received > proxy->content_length) + if (proxy->resflags & PROXY_RES_PEER_OUTPUT_LENGTH) { + if (proxy->peer_output_received > proxy->peer_output_length) + { /* TODO: proxy returning too much data... something is wrong in PROXY */ qse_printf (QSE_T("PROXY FUCKED UP...RETURNING TOO MUCH DATA\n")); - return -1; - } - - if (proxy->content_length_set && - proxy->content_received == proxy->content_length) - { + return -1; + } + else if (proxy->peer_output_received == proxy->peer_output_length) + { qse_printf (QSE_T("PROXY DONE READING\n")); - task->main = task_main_proxy_5; - task->trigger[0].mask = 0; - task->trigger[2].mask |= QSE_HTTPD_TASK_TRIGGER_WRITE; - return 1; + task->main = task_main_proxy_5; + task->trigger[0].mask = 0; + task->trigger[2].mask |= QSE_HTTPD_TASK_TRIGGER_WRITE; + return 1; + } } } @@ -3122,11 +3288,11 @@ static int task_main_proxy_3 ( if (task->trigger[2].mask & QSE_HTTPD_TASK_TRIGGER_READABLE) { - proxy_forward_content (httpd, task, 0); + proxy_forward_client_input_to_peer (httpd, task, 0); } else if (task->trigger[0].mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE) { - proxy_forward_content (httpd, task, 1); + proxy_forward_client_input_to_peer (httpd, task, 1); } qse_printf (QSE_T("[PROXY-----3]\n")); @@ -3165,8 +3331,8 @@ qse_printf (QSE_T("[proxy-3 send failure....\n")); { qse_mbs_clear (proxy->res); - if ((proxy->res_status & PROXY_RES_CHUNK_GOTALL) || - (proxy->content_length_set && proxy->content_received >= proxy->content_length)) + if ((proxy->resflags & PROXY_RES_CLIENT_CHUNK) || + ((proxy->resflags & PROXY_RES_PEER_OUTPUT_LENGTH) && proxy->peer_output_received >= proxy->peer_output_length)) { task->main = task_main_proxy_5; task->trigger[2].mask |= QSE_HTTPD_TASK_TRIGGER_WRITE; @@ -3188,14 +3354,15 @@ static int task_main_proxy_2 ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) { task_proxy_t* proxy = (task_proxy_t*)task->ctx; + int http_errnum = 0; if (task->trigger[2].mask & QSE_HTTPD_TASK_TRIGGER_READABLE) { - proxy_forward_content (httpd, task, 0); + proxy_forward_client_input_to_peer (httpd, task, 0); } else if (task->trigger[0].mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE) { - proxy_forward_content (httpd, task, 1); + proxy_forward_client_input_to_peer (httpd, task, 1); } if (!(task->trigger[2].mask & QSE_HTTPD_TASK_TRIGGER_WRITE) || @@ -3214,7 +3381,7 @@ static int task_main_proxy_2 ( qse_ssize_t n; qse_size_t count; - QSE_ASSERT (proxy->expect_100 == -2 || (proxy->res_status & PROXY_RES_CHUNK)); + QSE_ASSERT ((proxy->expect_100 & AWAIT_RES) || (proxy->resflags & PROXY_RES_CLIENT_CHUNK)); count = proxy->res_pending; if (count > MAX_SEND_SIZE) count = MAX_SEND_SIZE; @@ -3265,30 +3432,44 @@ qse_printf (QSE_T("[proxy-2 send failure....\n")); } if (n == 0) { - /* end of output from peer before it has seen a header. - * the proxy script must be crooked. */ - if (proxy->res_status & PROXY_RES_CHUNK_GOTALL) + if (!(proxy->expect_100 & RECEIVED_RES)) { - task->main = task_main_proxy_3; - task->trigger[2].mask |= QSE_HTTPD_TASK_TRIGGER_WRITE; - return 1; - } - + /* end of output from peer before it has seen a header. + * the proxy script must be crooked. */ /* TODO: logging */ qse_printf (QSE_T("#####PREMATURE EOF FROM PEER\n")); - goto oops; + if (!(proxy->expect_100 & RECEIVED_100)) + { + http_errnum = 502; + goto oops; + } + + return -1; + } + 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); + task->main = task_main_proxy_3; + task->trigger[2].mask |= QSE_HTTPD_TASK_TRIGGER_WRITE; + return 1; + } + + return -1; + } } proxy->buflen += n; - if (proxy->res_status & PROXY_RES_CHUNK_GOTALL) - { -qse_printf (QSE_T("#####PROXY RETURNING TOO MUCH\n")); - return -1; - } - qse_printf (QSE_T("#####PROXY FEEDING [%.*hs]\n"), (int)proxy->buflen, proxy->buf); - if (qse_htrd_feed (proxy->htrd, proxy->buf, proxy->buflen) <= -1) + if (qse_htrd_feed (proxy->peer_htrd, proxy->buf, proxy->buflen) <= -1) { /* TODO: logging */ qse_printf (QSE_T("#####INVALID HEADER FROM PEER [%.*hs]\n"), (int)proxy->buflen, proxy->buf); @@ -3299,51 +3480,45 @@ qse_printf (QSE_T("#####INVALID HEADER FROM PEER [%.*hs]\n"), (int)proxy->buflen if (QSE_MBS_LEN(proxy->res) > 0) { -qse_printf (QSE_T("proxy->expect_100 %d\n"), proxy->expect_100); - if (proxy->expect_100 == -1) + if (proxy->expect_100 & RECEIVED_CON) { - /* the peek handler for proxy->htrd sets - * proxy->expect_100 to either -1 or 0. - * it's set to 0 if it's called with - * '100 Continue', -1 with other responses. - * But at this point, the handler could - * be called with both '100 Continue' and - * another response for a single feed. - * if it's already set to -1, proxy->res - * should contains at least the part of - * the actual reply. so i can switch to the - * next phase */ - - if (proxy->res_status & PROXY_RES_CHUNK) + QSE_ASSERT (proxy->resflags & PROXY_RES_CLIENT_CHUNK); + task->main = task_main_proxy_3; + task->trigger[2].mask |= QSE_HTTPD_TASK_TRIGGER_WRITE; + } + else if (proxy->expect_100 & AWAIT_CON) + { + QSE_ASSERT (proxy->resflags & PROXY_RES_CLIENT_CHUNK); + task->trigger[2].mask |= QSE_HTTPD_TASK_TRIGGER_WRITE; + } + else if (proxy->expect_100 & RECEIVED_RES) + { + /* the actual response header has been received + * with or without '100 continue'. you can + * check it with proxy->expect_100 & RECEIVED_100 */ + if (proxy->resflags & PROXY_RES_CLIENT_CHUNK) { + proxy->expect_100 |= AWAIT_CON; task->trigger[2].mask |= QSE_HTTPD_TASK_TRIGGER_WRITE; - return 1; } else { - if ((proxy->res_status & PROXY_RES_DISCONNECT) && - qse_httpd_entaskdisconnect (httpd, client, task) == QSE_NULL) - { - goto oops; - } - qse_printf (QSE_T("TRAILING DATA=[%hs]\n"), &QSE_MBS_CHAR(proxy->res,proxy->res_consumed)); /* switch to the next phase */ task->main = task_main_proxy_3; task->trigger[2].mask |= QSE_HTTPD_TASK_TRIGGER_WRITE; - return 1; } } - else if (proxy->expect_100 == 0) + else if (proxy->expect_100 & RECEIVED_100) { - /* if it's set to 0, it's seen '100 Continue' - * without an actual response. so i need to - * arrange to relay '100 Continue' while - * waiting for at least the header of an - * actual reponse */ - proxy->expect_100 = -2; + /* 100 continue has been received but + * the actual response has not. */ task->trigger[2].mask |= QSE_HTTPD_TASK_TRIGGER_WRITE; } + else + { + /* anything to do? */ + } } } @@ -3351,37 +3526,45 @@ qse_printf (QSE_T("TRAILING DATA=[%hs]\n"), &QSE_MBS_CHAR(proxy->res,proxy->res_ return 1; oops: -/* TODO: if it's before sending anything i can send 500 otherwise just return -1; */ - return (entask_error (httpd, client, task, 500, &proxy->version, proxy->keepalive) == QSE_NULL)? -1: 0; + return (entask_error (httpd, client, task, http_errnum, &proxy->version, proxy->keepalive) == QSE_NULL)? -1: 0; } static int task_main_proxy_1 ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) { task_proxy_t* proxy = (task_proxy_t*)task->ctx; - int n; + int http_errnum = 500; /* wait for peer to get connected */ if (task->trigger[0].mask & QSE_HTTPD_TASK_TRIGGER_READABLE || task->trigger[0].mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE) { + int n; + + httpd->errnum = QSE_HTTPD_ENOERR; n = httpd->cbs->peer.connected (httpd, &proxy->peer); - if (n <= -1) return -1; + if (n <= -1) + { + /* improve error conversion */ + if (httpd->errnum == QSE_HTTPD_ENOENT) http_errnum = 404; + else if (httpd->errnum == QSE_HTTPD_EACCES) http_errnum = 403; + goto oops; + } + if (n >= 1) { - proxy->peer_status |= PEER_CONNECTED; + proxy->peer_status |= PROXY_PEER_CONNECTED; if (proxy->req) { task->trigger[2].mask = QSE_HTTPD_TASK_TRIGGER_READ; - task->trigger[2].handle = client->handle; } task->trigger[0].mask &= ~QSE_HTTPD_TASK_TRIGGER_WRITE; if (QSE_MBS_LEN(proxy->reqfwdbuf) > 0) { - proxy_forward_content (httpd, task, 0); + proxy_forward_client_input_to_peer (httpd, task, 0); if (QSE_MBS_LEN(proxy->reqfwdbuf) > 0) { task->trigger[0].mask |= QSE_HTTPD_TASK_TRIGGER_WRITE; @@ -3393,26 +3576,30 @@ qse_printf (QSE_T("FINALLY connected to peer ...............................\n") } return 1; + +oops: + return (entask_error (httpd, client, task, http_errnum, &proxy->version, proxy->keepalive) == QSE_NULL)? -1: 0; } static int task_main_proxy ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) { task_proxy_t* proxy = (task_proxy_t*)task->ctx; - proxy_htrd_xtn_t* xtn; + proxy_peer_htrd_xtn_t* xtn; int http_errnum = 500; int n; if (proxy->init_failed) goto oops; /* set up a http reader to read a response from the peer */ - proxy->htrd = qse_htrd_open (httpd->mmgr, QSE_SIZEOF(proxy_htrd_xtn_t)); - if (proxy->htrd == QSE_NULL) goto oops; - xtn = (proxy_htrd_xtn_t*) qse_htrd_getxtn (proxy->htrd); + 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; + xtn->client = client; xtn->task = task; - qse_htrd_setrecbs (proxy->htrd, &proxy_htrd_cbs); - qse_htrd_setoption (proxy->htrd, QSE_HTRD_RESPONSE); + qse_htrd_setrecbs (proxy->peer_htrd, &proxy_htrd_cbs); + qse_htrd_setoption (proxy->peer_htrd, QSE_HTRD_RESPONSE); proxy->res = qse_mbs_open (httpd->mmgr, 0, 256); if (proxy->res == QSE_NULL) goto oops; @@ -3429,9 +3616,10 @@ static int task_main_proxy ( goto oops; } - proxy->peer_status |= PEER_OPEN; + proxy->peer_status |= PROXY_PEER_OPEN; task->trigger[0].mask = QSE_HTTPD_TASK_TRIGGER_READ; task->trigger[0].handle = proxy->peer.handle; + task->trigger[2].handle = client->handle; if (n == 0) { @@ -3442,16 +3630,15 @@ static int task_main_proxy ( else { /* peer connected already */ - proxy->peer_status |= PEER_CONNECTED; + proxy->peer_status |= PROXY_PEER_CONNECTED; if (proxy->req) { task->trigger[2].mask = QSE_HTTPD_TASK_TRIGGER_READ; - task->trigger[2].handle = client->handle; } if (QSE_MBS_LEN(proxy->reqfwdbuf) > 0) { - proxy_forward_content (httpd, task, 0); + proxy_forward_client_input_to_peer (httpd, task, 0); if (QSE_MBS_LEN(proxy->reqfwdbuf) > 0) { task->trigger[0].mask |= QSE_HTTPD_TASK_TRIGGER_WRITE; @@ -3468,10 +3655,10 @@ oops: qse_mbs_close (proxy->res); proxy->res = QSE_NULL; } - if (proxy->htrd) + if (proxy->peer_htrd) { - qse_htrd_close (proxy->htrd); - proxy->htrd = QSE_NULL; + qse_htrd_close (proxy->peer_htrd); + proxy->peer_htrd = QSE_NULL; } return (entask_error ( diff --git a/qse/lib/net/httpd.c b/qse/lib/net/httpd.c index 4ec5b8c2..3696eac2 100644 --- a/qse/lib/net/httpd.c +++ b/qse/lib/net/httpd.c @@ -163,7 +163,8 @@ static qse_httpd_task_t* enqueue_task ( qse_httpd_allocmem (httpd, QSE_SIZEOF(*new_task) + xtnsize); if (new_task == QSE_NULL) return QSE_NULL; - QSE_MEMCPY (new_task, task, QSE_SIZEOF(*new_task)); + QSE_MEMSET (new_task, 0, QSE_SIZEOF(*new_task) + xtnsize); + *new_task = *task; if (new_task->init) { @@ -430,6 +431,7 @@ qse_printf (QSE_T("failed to accept from server %s\n"), tmp); return -1; } +qse_printf (QSE_T("MUX ADDHND CLIENT READ %d\n"), client->handle.i); if (httpd->cbs->mux.addhnd ( httpd, mux, client->handle, QSE_HTTPD_MUX_READ, perform_client_task, client) <= -1) @@ -533,6 +535,7 @@ qse_printf (QSE_T("FAILED TO ACTIVATE SERVER....\n")); continue; } +qse_printf (QSE_T("MUX ADDHND SERVER %d\n"), server->handle.i); if (httpd->cbs->mux.addhnd ( httpd, httpd->mux, server->handle, QSE_HTTPD_MUX_READ, accept_client, server) <= -1) @@ -1001,8 +1004,14 @@ qse_httpd_task_t* qse_httpd_entask ( { qse_httpd_task_t* new_task; + if (client->status & CLIENT_BAD) return QSE_NULL; + new_task = enqueue_task (httpd, client, pred, task, xtnsize); - if (new_task == QSE_NULL) purge_client (httpd, client); + if (new_task == QSE_NULL) + { + /*purge_client (httpd, client);*/ + client->status |= CLIENT_BAD; + } else if (new_task->prev == QSE_NULL) { /* this new task is the first task for a client */ @@ -1014,12 +1023,14 @@ qse_httpd_task_t* qse_httpd_entask ( httpd->cbs->mux.delhnd (httpd, httpd->mux, client->handle); client->status &= ~CLIENT_HANDLE_IN_MUX; +qse_printf (QSE_T("MUX ADDHND CLIENT RW(ENTASK) %d\n"), client->handle.i); if (httpd->cbs->mux.addhnd ( httpd, httpd->mux, client->handle, QSE_HTTPD_MUX_READ | QSE_HTTPD_MUX_WRITE, perform_client_task, client) <= -1) { - purge_client (httpd, client); + /*purge_client (httpd, client);*/ + client->status |= CLIENT_BAD; new_task = QSE_NULL; } client->status |= CLIENT_HANDLE_IN_MUX; /* READ | WRITE */ diff --git a/qse/samples/net/http01.c b/qse/samples/net/http01.c index 4f1a2e33..81fe0db0 100644 --- a/qse/samples/net/http01.c +++ b/qse/samples/net/http01.c @@ -605,7 +605,6 @@ static int mux_addhnd ( ev.data.ptr = mev; -qse_printf (QSE_T("MUX ADDING %d\n"), (int)handle.i); if (epoll_ctl (mux->fd, EPOLL_CTL_ADD, handle.i, &ev) <= -1) { /* don't rollback ee.ptr */ @@ -627,7 +626,6 @@ static int mux_delhnd (qse_httpd_t* httpd, void* vmux, qse_ubi_t handle) return -1; } -qse_printf (QSE_T("MUX DELETING %d\n"), (int)handle.i); mux->ee.len--; return 0; } @@ -660,7 +658,6 @@ static int mux_poll (qse_httpd_t* httpd, void* vmux, qse_ntime_t timeout) if (mev->reqmask & QSE_HTTPD_MUX_WRITE) mask |= QSE_HTTPD_MUX_WRITE; } -qse_printf (QSE_T("MUX EXEC %d\n"), (int)mev->handle.i); mev->cbfun (httpd, mux, mev->handle, mask, mev->cbarg); } return 0;