diff --git a/qse/include/qse/net/httpd.h b/qse/include/qse/net/httpd.h index 8ecc7028..fc06819e 100644 --- a/qse/include/qse/net/httpd.h +++ b/qse/include/qse/net/httpd.h @@ -58,9 +58,10 @@ 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_CGINOCHUNKED = (1 << 2) + QSE_HTTPD_MUTECLIENT = (1 << 0), + QSE_HTTPD_CGIERRTONUL = (1 << 1), + QSE_HTTPD_CGINOCLOEXEC = (1 << 2), + QSE_HTTPD_CGINOCHUNKED = (1 << 3) }; typedef struct qse_httpd_stat_t qse_httpd_stat_t; @@ -240,12 +241,9 @@ typedef int (*qse_httpd_task_main_t) ( enum qse_httpd_task_trigger_mask_t { QSE_HTTPD_TASK_TRIGGER_READ = (1 << 0), - QSE_HTTPD_TASK_TRIGGER_RELAY = (1 << 1), - QSE_HTTPD_TASK_TRIGGER_WRITE = (1 << 2), - QSE_HTTPD_TASK_TRIGGER_READABLE = (1 << 3), - QSE_HTTPD_TASK_TRIGGER_RELAYABLE = (1 << 4), - QSE_HTTPD_TASK_TRIGGER_WRITABLE = (1 << 5) - + QSE_HTTPD_TASK_TRIGGER_WRITE = (1 << 1), + QSE_HTTPD_TASK_TRIGGER_READABLE = (1 << 2), + QSE_HTTPD_TASK_TRIGGER_WRITABLE = (1 << 3) }; typedef struct qse_httpd_task_trigger_t qse_httpd_task_trigger_t; diff --git a/qse/lib/net/htrd.c b/qse/lib/net/htrd.c index aa12389c..b29a9494 100644 --- a/qse/lib/net/htrd.c +++ b/qse/lib/net/htrd.c @@ -1419,7 +1419,7 @@ feedme_more: int qse_htrd_halt (qse_htrd_t* htrd) { - if (htrd->fed.s.flags & CONSUME_UNTIL_CLOSE) + if (htrd->fed.s.flags & CONSUME_UNTIL_CLOSE || !htrd->clean) { qse_htre_completecontent (&htrd->re); diff --git a/qse/lib/net/httpd-task.c b/qse/lib/net/httpd-task.c index 392254e2..c7f686f4 100644 --- a/qse/lib/net/httpd-task.c +++ b/qse/lib/net/httpd-task.c @@ -2701,6 +2701,7 @@ struct task_proxy_t #define PROXY_RES_PEER_LENGTH (1 << 5) /* peer's output is set with * the content-length */ #define PROXY_RES_PEER_LENGTH_FAKE (1 << 6) /* peer_output_length is fake */ +#define PROXY_RES_EVER_SENTBACK (1 << 7) /* any single byte sent back to a client */ #define PROXY_RES_AWAIT_100 (1 << 10) /* waiting for 100 continue */ #define PROXY_RES_AWAIT_RESHDR (1 << 11) /* waiting for response header */ #define PROXY_RES_AWAIT_RESCON (1 << 12) /* waiting for response content. @@ -3652,11 +3653,16 @@ qse_printf (QSE_T("task_main_proxy_4 read from PEER...%d\n"), (int)n); static int task_main_proxy_3 ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) { - /* send the http initial line and headers built using the headers - * returned by peer. it may include some contents as well */ + /* let's send up the http initial line and headers before + * attempting to read the reset of content. it may already + * include some contents as well received together with + * the header. */ task_proxy_t* proxy = (task_proxy_t*)task->ctx; +qse_printf (QSE_T("task_main_proxy_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) { proxy_forward_client_input_to_peer (httpd, task, 0); @@ -3666,7 +3672,6 @@ static int task_main_proxy_3 ( proxy_forward_client_input_to_peer (httpd, task, 1); } -qse_printf (QSE_T("[PROXY-----3]\n")); if (!(task->trigger[2].mask & QSE_HTTPD_TASK_TRIGGER_WRITE) || (task->trigger[2].mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE)) { @@ -3693,6 +3698,7 @@ qse_printf (QSE_T("[proxy-3 send failure....\n")); return -1; } + proxy->resflags |= PROXY_RES_EVER_SENTBACK; proxy->res_consumed += n; proxy->res_pending -= n; } @@ -3713,7 +3719,7 @@ qse_printf (QSE_T("SWITINCG TO 55555555555555555555555555 %d %d %d %d\n"), } else { -qse_printf (QSE_T("SWITINCG TO 4444444444444444444444444444\n")); +qse_printf (QSE_T("SWITICHING TO 4444444444444444444444444444\n")); task->main = task_main_proxy_4; task->trigger[2].mask &= ~QSE_HTTPD_TASK_TRIGGER_WRITE; } @@ -3729,7 +3735,7 @@ 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; + int http_errnum = 500; 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); @@ -3782,9 +3788,10 @@ qse_printf (QSE_T("]\n")); if (n <= -1) { qse_printf (QSE_T("[proxy-2 send failure....\n")); - return -1; + goto oops; } + proxy->resflags |= PROXY_RES_EVER_SENTBACK; proxy->res_consumed += n; proxy->res_pending -= n; @@ -3822,13 +3829,8 @@ qse_printf (QSE_T("[proxy-2 send failure....\n")); * the proxy script must be crooked. */ /* TODO: logging */ qse_printf (QSE_T("#####PREMATURE EOF FROM PEER\n")); - if (!(proxy->resflags & PROXY_RES_RECEIVED_100)) - { - http_errnum = 502; - goto oops; - } - - return -1; + if (!(proxy->resflags & PROXY_RES_RECEIVED_100)) http_errnum = 502; + goto oops; } else { @@ -3846,7 +3848,7 @@ qse_printf (QSE_T("#####PREMATURE EOF FROM PEER\n")); } qse_printf (QSE_T("#####PREMATURE EOF FROM PEER CLIENT CHUNK\n")); - return -1; + goto oops; } } @@ -3893,7 +3895,7 @@ qse_printf (QSE_T("#####INVALID HEADER FROM PEER [%.*hs]\n"), (int)proxy->buflen } else { -qse_printf (QSE_T("TRAILING DATA=[%hs]\n"), QSE_MBS_CPTR(proxy->res,proxy->res_consumed)); +qse_printf (QSE_T("TRAILING DATA=%d, [%hs]\n"), (int)QSE_MBS_LEN(proxy->res), QSE_MBS_CPTR(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; @@ -3916,6 +3918,7 @@ qse_printf (QSE_T("TRAILING DATA=[%hs]\n"), QSE_MBS_CPTR(proxy->res,proxy->res_c return 1; oops: + if (proxy->resflags & PROXY_RES_EVER_SENTBACK) return -1; return (entask_error (httpd, client, task, http_errnum, &proxy->version, proxy->keepalive) == QSE_NULL)? -1: 0; } diff --git a/qse/lib/net/httpd.c b/qse/lib/net/httpd.c index 2bee58c9..237efce7 100644 --- a/qse/lib/net/httpd.c +++ b/qse/lib/net/httpd.c @@ -693,15 +693,25 @@ 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 && client->htrd->clean) + /* reading from the client returned 0. this typically + * happens when the client closes the connection or + * shutdown the writing half of the socket. it's + * not really easy to determine one from the other. + * if QSE_HTTPD_MUTECLIENT is on, attempt to handle + * it as a half-close under a certain condition. */ + + if (httpd->option & QSE_HTTPD_MUTECLIENT && + client->task.head && client->htrd->clean) { /* there is still more tasks to finish and * http reader is not waiting for any more feeds. */ client->status |= CLIENT_MUTE; +qse_printf (QSE_T(">>>>> Marking client %d as MUTE\n"), client->handle.i); return 0; } else { +qse_printf (QSE_T(">>>>> Returning failure for client %d\n"), client->handle.i); httpd->errnum = QSE_HTTPD_EDISCON; return -1; } @@ -820,7 +830,7 @@ qse_printf (QSE_T("task returend %d\n"), n); int mux_mask; int mux_status; - /* the current task is over. remove remove the task + /* the current task is over. remove the task * from the queue. dequeue_task() clears task triggers * from the mux. so i don't clear them explicitly here */ diff --git a/qse/samples/net/http01.c b/qse/samples/net/http01.c index e0ee89ed..5968909f 100644 --- a/qse/samples/net/http01.c +++ b/qse/samples/net/http01.c @@ -699,13 +699,17 @@ static int mux_poll (qse_httpd_t* httpd, void* vmux, qse_ntime_t timeout) mask = 0; - if (mux->ee.ptr[i].events & EPOLLIN) mask |= QSE_HTTPD_MUX_READ; - if (mux->ee.ptr[i].events & EPOLLOUT) mask |= QSE_HTTPD_MUX_WRITE; + if (mux->ee.ptr[i].events & EPOLLIN) + mask |= QSE_HTTPD_MUX_READ; + if (mux->ee.ptr[i].events & EPOLLOUT) + mask |= QSE_HTTPD_MUX_WRITE; if (mux->ee.ptr[i].events & EPOLLHUP) { - if (mev->reqmask & QSE_HTTPD_MUX_READ) mask |= QSE_HTTPD_MUX_READ; - if (mev->reqmask & QSE_HTTPD_MUX_WRITE) mask |= QSE_HTTPD_MUX_WRITE; + if (mev->reqmask & QSE_HTTPD_MUX_READ) + mask |= QSE_HTTPD_MUX_READ; + if (mev->reqmask & QSE_HTTPD_MUX_WRITE) + mask |= QSE_HTTPD_MUX_WRITE; } mev->cbfun (httpd, mux, mev->handle, mask, mev->cbarg);