diff --git a/qse/include/qse/cmn/pio.h b/qse/include/qse/cmn/pio.h index b3a5d0a9..4a0d57d2 100644 --- a/qse/include/qse/cmn/pio.h +++ b/qse/include/qse/cmn/pio.h @@ -39,7 +39,7 @@ enum qse_pio_flag_t { /** enable text based I/O. */ QSE_PIO_TEXT = (1 << 0), - QSE_PIO_IGNOREMBWCERR = (1 << 1), + QSE_PIO_IGNOREMBWCERR = (1 << 1), QSE_PIO_NOAUTOFLUSH = (1 << 2), /** execute the command via a system shell @@ -67,7 +67,7 @@ enum qse_pio_flag_t QSE_PIO_READERR = (1 << 10), /** redirect stderr to stdout (2>&1, require #QSE_PIO_READOUT) */ - QSE_PIO_ERRTOOUT = (1 << 11), + QSE_PIO_ERRTOOUT = (1 << 11), /** redirect stdout to stderr (1>&2, require #QSE_PIO_READERR) */ QSE_PIO_OUTTOERR = (1 << 12), @@ -92,7 +92,14 @@ enum qse_pio_flag_t /** return immediately from qse_pio_wait() if a child has not exited */ QSE_PIO_WAITNOBLOCK = (1 << 23), /** do not wait again if waitpid has been interrupted */ - QSE_PIO_WAITNORETRY = (1 << 24) + QSE_PIO_WAITNORETRY = (1 << 24), + + /** put stdin to non-blocking mode (only on supported platforms) */ + QSE_PIO_INNOBLOCK = (1 << 25), + /** put stdout to non-blocking mode (only on supported platforms)*/ + QSE_PIO_OUTNOBLOCK = (1 << 26), + /** put stderr to non-blocking mode (only on supported platforms) */ + QSE_PIO_ERRNOBLOCK = (1 << 27) }; /** diff --git a/qse/lib/cmn/mux.c b/qse/lib/cmn/mux.c index 52e9957d..2830de89 100644 --- a/qse/lib/cmn/mux.c +++ b/qse/lib/cmn/mux.c @@ -438,6 +438,26 @@ qse_mux_errnum_t qse_mux_geterrnum (qse_mux_t* mux) int qse_mux_insert (qse_mux_t* mux, const qse_mux_evt_t* evt) { +#if defined(USE_SELECT) + /* nothing */ +#elif defined(USE_KQUEUE) + struct kevent chlist[2]; + int count = 0; +#elif defined(USE_EPOLL) + struct epoll_event ev; +#elif defined(__OS2__) + /* nothing */ +#else + /* nothing */ +#endif + + /* sanity check */ + if (!(evt->mask & (QSE_MUX_IN | QSE_MUX_OUT)) || evt->hnd < 0) + { + mux->errnum = QSE_MUX_EINVAL; + return -1; + } + #if defined(USE_SELECT) /* TODO: windows seems to return a pretty high file descriptors @@ -482,9 +502,6 @@ int qse_mux_insert (qse_mux_t* mux, const qse_mux_evt_t* evt) return 0; #elif defined(USE_KQUEUE) - struct kevent chlist[2]; - int count = 0; - /* TODO: study if it is better to put 'evt' to the udata * field of chlist? */ @@ -502,11 +519,7 @@ int qse_mux_insert (qse_mux_t* mux, const qse_mux_evt_t* evt) count++; } - if (count == 0 || evt->hnd < 0) - { - mux->errnum = QSE_MUX_EINVAL; - return -1; - } + QSE_ASSERT (count > 0); if (evt->hnd >= mux->me.ubound) { @@ -550,17 +563,12 @@ int qse_mux_insert (qse_mux_t* mux, const qse_mux_evt_t* evt) return 0; #elif defined(USE_EPOLL) - struct epoll_event ev; QSE_MEMSET (&ev, 0, QSE_SIZEOF(ev)); if (evt->mask & QSE_MUX_IN) ev.events |= EPOLLIN; if (evt->mask & QSE_MUX_OUT) ev.events |= EPOLLOUT; - if (ev.events == 0 || evt->hnd < 0) - { - mux->errnum = QSE_MUX_EINVAL; - return -1; - } + QSE_ASSERT (ev.events != 0); if (evt->hnd >= mux->me.ubound) { @@ -937,13 +945,13 @@ int qse_mux_poll (qse_mux_t* mux, const qse_ntime_t* tmout) if (mux->ee.ptr[i].events & EPOLLIN) xevt.mask |= QSE_MUX_IN; if (mux->ee.ptr[i].events & EPOLLOUT) xevt.mask |= QSE_MUX_OUT; - if (mux->ee.ptr[i].events & EPOLLHUP) + if (mux->ee.ptr[i].events & (EPOLLHUP | EPOLLERR)) { if (evt->mask & QSE_MUX_IN) xevt.mask |= QSE_MUX_IN; if (evt->mask & QSE_MUX_OUT) xevt.mask |= QSE_MUX_OUT; } - mux->evtfun (mux, &xevt); + if (xevt.mask > 0) mux->evtfun (mux, &xevt); } return nfds; diff --git a/qse/lib/cmn/pio.c b/qse/lib/cmn/pio.c index 8d4f580a..4cb07331 100644 --- a/qse/lib/cmn/pio.c +++ b/qse/lib/cmn/pio.c @@ -429,6 +429,22 @@ static int get_highest_fd (void) #endif +static int set_pipe_nonblock (qse_pio_t* pio, qse_pio_hnd_t fd, int enabled) +{ +#if defined(O_NONBLOCK) + + int flag = QSE_FCNTL (fd, F_GETFL, 0); + if (flag >= 0) flag = QSE_FCNTL (fd, F_SETFL, (enabled? (flag | O_NONBLOCK): (flag & ~O_NONBLOCK))); + if (flag <= -1) pio->errnum = syserr_to_errnum (errno); + return flag; + +#else + pio->errnum = QSE_PIO_ENOIMPL; + return -1; +#endif +} + + int qse_pio_init ( qse_pio_t* pio, qse_mmgr_t* mmgr, const qse_char_t* cmd, qse_env_t* env, int flags) @@ -1785,6 +1801,13 @@ create_process: #endif + if (((flags & QSE_PIO_INNOBLOCK) && set_pipe_nonblock(pio, handle[1], 1) <= -1) || + ((flags & QSE_PIO_OUTNOBLOCK) && set_pipe_nonblock(pio, handle[2], 1) <= -1) || + ((flags & QSE_PIO_ERRNOBLOCK) && set_pipe_nobnlock(pio, handle[4], 1) <= -1)) + { + goto oops; + } + /* store back references */ pio->pin[QSE_PIO_IN].self = pio; pio->pin[QSE_PIO_OUT].self = pio; @@ -1795,6 +1818,7 @@ create_process: pio->pin[QSE_PIO_OUT].handle = handle[2]; pio->pin[QSE_PIO_ERR].handle = handle[4]; + if (flags & QSE_PIO_TEXT) { int topt = 0; @@ -1882,17 +1906,17 @@ oops: } for (i = minidx; i < maxidx; i++) { - if (handle[i] != QSE_PIO_HND_NIL) QSE_CLOSE (handle[i]); + if (handle[i] != QSE_PIO_HND_NIL) QSE_CLOSE (handle[i]); } #elif defined(QSE_SYSCALL0) && defined(SYS_vfork) for (i = minidx; i < maxidx; i++) { - if (handle[i] != QSE_PIO_HND_NIL) QSE_CLOSE (handle[i]); + if (handle[i] != QSE_PIO_HND_NIL) QSE_CLOSE (handle[i]); } #else for (i = minidx; i < maxidx; i++) { - if (handle[i] != QSE_PIO_HND_NIL) QSE_CLOSE (handle[i]); + if (handle[i] != QSE_PIO_HND_NIL) QSE_CLOSE (handle[i]); } #endif diff --git a/qse/lib/http/httpd-cgi.c b/qse/lib/http/httpd-cgi.c index 56bad4be..fb8042bd 100644 --- a/qse/lib/http/httpd-cgi.c +++ b/qse/lib/http/httpd-cgi.c @@ -25,6 +25,16 @@ #include #include +#if defined(_WIN32) + /* nothing */ +#elif defined(__OS2__) + /* nothing */ +#elif defined(__DOS__) + /* nothing */ +#else +# include "../cmn/syscall.h" +#endif + #include /* TODO: remove this */ #if defined(_MSC_VER) || defined(__BORLANDC__) || (defined(__WATCOMC__) && (__WATCOMC__ < 1200)) # define snprintf _snprintf @@ -78,7 +88,7 @@ struct task_cgi_t int resflags; qse_mbs_t* res; qse_mchar_t* res_ptr; - qse_size_t res_left; + qse_size_t res_left; /* content-length that CGI returned */ qse_size_t script_output_length; /* TODO: a script maybe be able to output more than the maximum value of qse_size_t */ @@ -104,6 +114,8 @@ struct cgi_client_req_hdr_ctx_t qse_env_t* env; }; + + static int cgi_capture_client_header ( qse_htre_t* req, const qse_mchar_t* key, const qse_htre_hdrval_t* val, void* ctx) { @@ -637,57 +649,50 @@ static void cgi_forward_client_input_to_script ( /* normal forwarding */ qse_ssize_t n; - if (writable) goto forward; - - n = httpd->opt.scb.mux.writable ( - httpd, qse_pio_gethandleasubi (&cgi->pio, QSE_PIO_IN), 0); - if (n >= 1) - { - forward: - /* writable */ - n = qse_pio_write ( - &cgi->pio, QSE_PIO_IN, - 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. */ - 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.v[1].mask = 0; /* pipe output to child */ - } - } - } - + n = qse_pio_write ( + &cgi->pio, QSE_PIO_IN, + QSE_MBS_PTR(cgi->reqfwdbuf), + QSE_MBS_LEN(cgi->reqfwdbuf) + ); if (n <= -1) { - if (cgi->httpd->opt.trait & QSE_HTTPD_LOGACT) - log_cgi_script_error (cgi, QSE_MT("cgi pio write error - ")); - - cgi->reqflags |= CGI_REQ_FWDERR; - qse_mbs_clear (cgi->reqfwdbuf); - - if (!(cgi->reqflags & CGI_REQ_GOTALL)) + if (qse_pio_geterrnum(&cgi->pio) != QSE_PIO_EAGAIN) { - QSE_ASSERT (cgi->req); - qse_htre_discardcontent (cgi->req); + if (cgi->httpd->opt.trait & QSE_HTTPD_LOGACT) + log_cgi_script_error (cgi, QSE_MT("cgi pio write error - ")); - /* 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); + cgi->reqflags |= CGI_REQ_FWDERR; + qse_mbs_clear (cgi->reqfwdbuf); + + if (!(cgi->reqflags & CGI_REQ_GOTALL)) + { + QSE_ASSERT (cgi->req); + qse_htre_discardcontent (cgi->req); + + /* 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); + } + + /* mark the end of input to the child explicitly. */ + qse_pio_end (&cgi->pio, QSE_PIO_IN); + task->trigger.v[1].mask = 0; /* pipe output to child */ + } + } + else 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. */ + 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.v[1].mask = 0; /* pipe output to child */ } - - /* mark the end of input to the child explicitly. */ - qse_pio_end (&cgi->pio, QSE_PIO_IN); - task->trigger.v[1].mask = 0; /* pipe output to child */ } } } @@ -953,10 +958,18 @@ static QSE_INLINE qse_ssize_t cgi_read_script_output_to_buffer ( &cgi->buf[cgi->buflen], QSE_SIZEOF(cgi->buf) - cgi->buflen ); - if (n > 0) cgi->buflen += n; + if (n <= -1) + { + if (qse_pio_geterrnum(&cgi->pio) != QSE_PIO_EAGAIN) + { + if (cgi->httpd->opt.trait & QSE_HTTPD_LOGACT) + log_cgi_script_error (cgi, QSE_MT("cgi pio read error - ")); + return -1; + } - if (n <= -1 && cgi->httpd->opt.trait & QSE_HTTPD_LOGACT) - log_cgi_script_error (cgi, QSE_MT("cgi pio read error - ")); + n = -999; + } + else if (n > 0) cgi->buflen += n; return n; } @@ -966,16 +979,25 @@ static QSE_INLINE qse_ssize_t cgi_write_script_output_to_client ( { qse_ssize_t n; + QSE_ASSERT (cgi->buflen > 0); + + httpd->errnum = QSE_HTTPD_ENOERR; n = httpd->opt.scb.client.send (httpd, client, cgi->buf, cgi->buflen); - if (n > 0) + if (n <= -1) + { + if (httpd->errnum != QSE_HTTPD_EAGAIN) + { + if (cgi->httpd->opt.trait & QSE_HTTPD_LOGACT) + log_cgi_script_error (cgi, QSE_MT("cgi write error to client - ")); + } + else n = 0; + } + else if (n > 0) { QSE_MEMCPY (&cgi->buf[0], &cgi->buf[n], cgi->buflen - n); cgi->buflen -= n; } - if (n <= -1 && cgi->httpd->opt.trait & QSE_HTTPD_LOGACT) - log_cgi_script_error (cgi, QSE_MT("cgi write error to client - ")); - return n; } @@ -998,16 +1020,12 @@ static int task_main_cgi_5 ( } } - if (/*!(task->trigger.cmask & QSE_HTTPD_TASK_TRIGGER_WRITE) ||*/ - (task->trigger.cmask & QSE_HTTPD_TASK_TRIGGER_WRITABLE)) + if (/*(task->trigger.cmask & QSE_HTTPD_TASK_TRIGGER_WRITABLE) && */ cgi->buflen > 0) { - if (cgi->buflen > 0) + if (cgi_write_script_output_to_client (httpd, client, cgi) <= -1) { - if (cgi_write_script_output_to_client (httpd, client, cgi) <= -1) - { - /* can't return internal server error any more... */ - return -1; - } + /* can't return internal server error any more... */ + return -1; } } @@ -1042,9 +1060,21 @@ static int task_main_cgi_4_nph ( { 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) + n = qse_pio_read ( + &cgi->pio, QSE_PIO_OUT, + &cgi->buf[cgi->buflen], + QSE_SIZEOF(cgi->buf) - cgi->buflen + ); + if (n <= -1) + { + if (qse_pio_geterrnum(&cgi->pio) != QSE_PIO_EAGAIN) + { + if (cgi->httpd->opt.trait & QSE_HTTPD_LOGACT) + log_cgi_script_error (cgi, QSE_MT("cgi pio read error - ")); + return -1; + } + } + else if (n == 0) { /* switch to the next phase */ task->main = task_main_cgi_5; @@ -1052,10 +1082,10 @@ static int task_main_cgi_4_nph ( task->trigger.cmask |= QSE_HTTPD_TASK_TRIGGER_WRITE; return 1; } + else cgi->buflen += n; } - QSE_ASSERT (cgi->buflen > 0); - if (cgi_write_script_output_to_client (httpd, client, cgi) <= -1) return -1; + if (cgi->buflen > 0 && cgi_write_script_output_to_client (httpd, client, cgi) <= -1) return -1; } return 1; @@ -1110,11 +1140,14 @@ printf ("task_main_cgi_4 trigger[0].mask=%d trigger[1].mask=%d cmask=%d\n", ); if (n <= -1) { - if (cgi->httpd->opt.trait & QSE_HTTPD_LOGACT) - log_cgi_script_error (cgi, QSE_MT("cgi pio read error - ")); - return -1; + if (qse_pio_geterrnum(&cgi->pio) != QSE_PIO_EAGAIN) + { + if (cgi->httpd->opt.trait & QSE_HTTPD_LOGACT) + log_cgi_script_error (cgi, QSE_MT("cgi pio read error - ")); + return -1; + } } - if (n == 0) + else if (n == 0) { /* the cgi script closed the output */ cgi->buf[cgi->buflen++] = QSE_MT('0'); @@ -1128,34 +1161,36 @@ printf ("task_main_cgi_4 trigger[0].mask=%d trigger[1].mask=%d cmask=%d\n", task->trigger.cmask |= QSE_HTTPD_TASK_TRIGGER_WRITE; return 1; } - - /* set the chunk length. if the length string is less - * than 4 digits, the right side of the string is filled - * with space letters. for example, the chunk length line - * for the length 10 will be "A \r\n". */ - cgi->buflen += qse_fmtuintmaxtombs ( - &cgi->buf[cgi->buflen], CHLEN_RESERVE - 2 + 1, - n, 16 | QSE_FMTUINTMAXTOMBS_UPPERCASE | QSE_FMTUINTMAXTOMBS_FILLRIGHT, - -1, QSE_MT(' '), QSE_NULL - ); - cgi->buf[cgi->buflen++] = QSE_MT('\r'); - cgi->buf[cgi->buflen++] = QSE_MT('\n'); - - cgi->buflen += n; /* +n for the data read above */ - - /* set the trailing CR & LF for a chunk */ - cgi->buf[cgi->buflen++] = QSE_MT('\r'); - cgi->buf[cgi->buflen++] = QSE_MT('\n'); - - cgi->script_output_received += n; - - if ((cgi->resflags & CGI_RES_SCRIPT_LENGTH) && - cgi->script_output_received > cgi->script_output_length) + else { - /* cgi returning too much data... something is wrong in CGI */ - if (cgi->httpd->opt.trait & QSE_HTTPD_LOGACT) - log_cgi_script_error (cgi, QSE_MT("cgi redundant output - ")); - return -1; + /* set the chunk length. if the length string is less + * than 4 digits, the right side of the string is filled + * with space letters. for example, the chunk length line + * for the length 10 will be "A \r\n". */ + cgi->buflen += qse_fmtuintmaxtombs ( + &cgi->buf[cgi->buflen], CHLEN_RESERVE - 2 + 1, + n, 16 | QSE_FMTUINTMAXTOMBS_UPPERCASE | QSE_FMTUINTMAXTOMBS_FILLRIGHT, + -1, QSE_MT(' '), QSE_NULL + ); + cgi->buf[cgi->buflen++] = QSE_MT('\r'); + cgi->buf[cgi->buflen++] = QSE_MT('\n'); + + cgi->buflen += n; /* +n for the data read above */ + + /* set the trailing CR & LF for a chunk */ + cgi->buf[cgi->buflen++] = QSE_MT('\r'); + cgi->buf[cgi->buflen++] = QSE_MT('\n'); + + cgi->script_output_received += n; + + if ((cgi->resflags & CGI_RES_SCRIPT_LENGTH) && + cgi->script_output_received > cgi->script_output_length) + { + /* cgi returning too much data... something is wrong in CGI */ + if (cgi->httpd->opt.trait & QSE_HTTPD_LOGACT) + log_cgi_script_error (cgi, QSE_MT("cgi redundant output - ")); + return -1; + } } } } @@ -1163,9 +1198,21 @@ printf ("task_main_cgi_4 trigger[0].mask=%d trigger[1].mask=%d cmask=%d\n", { 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) + n = qse_pio_read ( + &cgi->pio, QSE_PIO_OUT, + &cgi->buf[cgi->buflen], + QSE_SIZEOF(cgi->buf) - cgi->buflen + ); + if (n <= -1) + { + if (qse_pio_geterrnum(&cgi->pio) != QSE_PIO_EAGAIN) + { + if (cgi->httpd->opt.trait & QSE_HTTPD_LOGACT) + log_cgi_script_error (cgi, QSE_MT("cgi pio read error - ")); + return -1; + } + } + else if (n == 0) { /* switch to the next phase */ task->main = task_main_cgi_5; @@ -1173,15 +1220,17 @@ printf ("task_main_cgi_4 trigger[0].mask=%d trigger[1].mask=%d cmask=%d\n", task->trigger.cmask |= QSE_HTTPD_TASK_TRIGGER_WRITE; return 1; } - - cgi->script_output_received += n; - if ((cgi->resflags & CGI_RES_SCRIPT_LENGTH) && - cgi->script_output_received > cgi->script_output_length) + else { - /* cgi returning too much data... something is wrong in CGI */ - if (cgi->httpd->opt.trait & QSE_HTTPD_LOGACT) - log_cgi_script_error (cgi, QSE_MT("cgi redundant output - ")); - return -1; + cgi->script_output_received += n; + if ((cgi->resflags & CGI_RES_SCRIPT_LENGTH) && + cgi->script_output_received > cgi->script_output_length) + { + /* cgi returning too much data... something is wrong in CGI */ + if (cgi->httpd->opt.trait & QSE_HTTPD_LOGACT) + log_cgi_script_error (cgi, QSE_MT("cgi redundant output - ")); + return -1; + } } } } @@ -1189,8 +1238,7 @@ printf ("task_main_cgi_4 trigger[0].mask=%d trigger[1].mask=%d cmask=%d\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. */ - QSE_ASSERT (cgi->buflen > 0); - if (cgi_write_script_output_to_client (httpd, client, cgi) <= -1) return -1; + if (cgi->buflen > 0 && cgi_write_script_output_to_client (httpd, client, cgi) <= -1) return -1; } return 1; @@ -1226,51 +1274,52 @@ printf ("task_main_cgi_3 trigger[0].mask=%d trigger[1].mask=%d cmask=%d\n", /* send the partial reponse received with the initial line and headers * so long as the client-side handle is writable... */ - if (/*!(task->trigger.cmask & QSE_HTTPD_TASK_TRIGGER_WRITE) ||*/ - (task->trigger.cmask & QSE_HTTPD_TASK_TRIGGER_WRITABLE)) + if (/*(task->trigger.cmask & QSE_HTTPD_TASK_TRIGGER_WRITABLE) && */ cgi->res_left > 0) { - count = MAX_SEND_SIZE; - if (count >= cgi->res_left) count = cgi->res_left; + count = cgi->res_left; + if (count >= MAX_SEND_SIZE) count = MAX_SEND_SIZE; - if (count > 0) + httpd->errnum = QSE_HTTPD_ENOERR; + n = httpd->opt.scb.client.send (httpd, client, cgi->res_ptr, count); + if (n <= -1) { - n = httpd->opt.scb.client.send (httpd, client, cgi->res_ptr, count); - if (n <= -1) + if (httpd->errnum != QSE_HTTPD_EAGAIN) { if (cgi->httpd->opt.trait & QSE_HTTPD_LOGACT) log_cgi_script_error (cgi, QSE_MT("cgi initial write error to client - ")); return -1; } - + } + else if (n > 0) + { cgi->res_ptr += n; cgi->res_left -= n; - } - if (cgi->res_left <= 0) - { - qse_mbs_clear (cgi->res); - - if ((cgi->resflags & CGI_RES_SCRIPT_LENGTH) && - cgi->script_output_received >= cgi->script_output_length) + if (cgi->res_left <= 0) { - /* if a cgi script specified the content length - * and it has emitted as much as the length, - * i don't wait for the script to finish. - * one potential side-effect is that the script - * can be killed prematurely if it wants to do - * something extra after having done so. - * however, a CGI script shouln't do that... */ - task->main = task_main_cgi_5; - task->trigger.cmask |= QSE_HTTPD_TASK_TRIGGER_WRITE; - } - else - { - task->main = task_main_cgi_4; - task->trigger.cmask &= ~QSE_HTTPD_TASK_TRIGGER_WRITE; - } - return 1; - } + qse_mbs_clear (cgi->res); + if ((cgi->resflags & CGI_RES_SCRIPT_LENGTH) && + cgi->script_output_received >= cgi->script_output_length) + { + /* if a cgi script specified the content length + * and it has emitted as much as the length, + * i don't wait for the script to finish. + * one potential side-effect is that the script + * can be killed prematurely if it wants to do + * something extra after having done so. + * however, a CGI script shouln't do that... */ + task->main = task_main_cgi_5; + task->trigger.cmask |= QSE_HTTPD_TASK_TRIGGER_WRITE; + } + else + { + task->main = task_main_cgi_4; + task->trigger.cmask &= ~QSE_HTTPD_TASK_TRIGGER_WRITE; + } + return 1; + } + } } return 1; /* more work to do */ @@ -1320,12 +1369,15 @@ printf ("task_main_cgi_2 trigger[0].mask=%d trigger[1].mask=%d cmask=%d\n", ); if (n <= -1) { - /* can't return internal server error any more... */ - if (cgi->httpd->opt.trait & QSE_HTTPD_LOGACT) - log_cgi_script_error (cgi, QSE_MT("cgi pio read error - ")); - goto oops; + if (qse_pio_geterrnum(&cgi->pio) != QSE_PIO_EAGAIN) + { + /* can't return internal server error any more... */ + if (cgi->httpd->opt.trait & QSE_HTTPD_LOGACT) + log_cgi_script_error (cgi, QSE_MT("cgi pio read error - ")); + goto oops; + } } - if (n == 0) + else if (n == 0) { /* end of output from cgi before it has seen a header. * the cgi script must be crooked. */ @@ -1333,16 +1385,22 @@ printf ("task_main_cgi_2 trigger[0].mask=%d trigger[1].mask=%d cmask=%d\n", log_cgi_script_error (cgi, QSE_MT("cgi premature eof - ")); goto oops; } - cgi->buflen += n; - - if (qse_htrd_feed (cgi->script_htrd, cgi->buf, cgi->buflen) <= -1) + else { - if (cgi->httpd->opt.trait & QSE_HTTPD_LOGACT) - log_cgi_script_error (cgi, QSE_MT("cgi feed error - ")); - goto oops; + cgi->buflen += n; } - cgi->buflen = 0; + if (cgi->buflen > 0) + { + if (qse_htrd_feed (cgi->script_htrd, cgi->buf, cgi->buflen) <= -1) + { + if (cgi->httpd->opt.trait & QSE_HTTPD_LOGACT) + log_cgi_script_error (cgi, QSE_MT("cgi feed error - ")); + goto oops; + } + + cgi->buflen = 0; + } if (QSE_MBS_LEN(cgi->res) > 0) { @@ -1410,7 +1468,11 @@ static int task_main_cgi ( if (cgi->res == QSE_NULL) goto oops; } - pio_options = QSE_PIO_READOUT | QSE_PIO_WRITEIN | QSE_PIO_MBSCMD; + /* <> + * QSE_PIO_INNOBLOCK and QSE_PIO_OUTNONBLOCK are not supported + * on non-unix/linux platforms. so the CGI task can only be + * used on unix/linux platforms */ + pio_options = QSE_PIO_READOUT | QSE_PIO_WRITEIN | QSE_PIO_MBSCMD | QSE_PIO_INNOBLOCK | QSE_PIO_OUTNOBLOCK; if (httpd->opt.trait & QSE_HTTPD_CGIERRTONUL) pio_options |= QSE_PIO_ERRTONUL; else @@ -1446,8 +1508,9 @@ static int task_main_cgi ( goto oops; } + cgi->pio_inited = 1; - + /* set the trigger that the main loop can use this * handle for multiplexing * diff --git a/qse/lib/http/httpd-file.c b/qse/lib/http/httpd-file.c index a7a57d1e..d63971ab 100644 --- a/qse/lib/http/httpd-file.c +++ b/qse/lib/http/httpd-file.c @@ -93,12 +93,19 @@ static int task_main_getfseg ( count = MAX_SEND_SIZE; if (count >= ctx->left) count = ctx->left; + httpd->errnum = QSE_HTTPD_ENOERR; n = httpd->opt.scb.client.sendfile ( httpd, client, ctx->handle, &ctx->offset, count); if (n <= -1) { /* HANDLE EGAIN specially??? */ - return -1; /* TODO: any logging */ + if (httpd->errnum != QSE_HTTPD_EAGAIN) + { + /* TODO: logging */ + return -1; + } + + goto more_work; } if (n == 0 && count > 0) @@ -108,12 +115,13 @@ static int task_main_getfseg ( * So let's return an error here so that the main loop abort * the connection. */ /* TODO: any logging....??? */ - return -1; + return -1; } ctx->left -= n; if (ctx->left <= 0) return 0; +more_work: return 1; /* more work to do */ } diff --git a/qse/lib/http/httpd-proxy.c b/qse/lib/http/httpd-proxy.c index b0cca619..56a07e27 100644 --- a/qse/lib/http/httpd-proxy.c +++ b/qse/lib/http/httpd-proxy.c @@ -658,8 +658,7 @@ static qse_htrd_recbs_t proxy_peer_htrd_cbs = proxy_htrd_handle_peer_output }; -static void proxy_forward_client_input_to_peer ( - qse_httpd_t* httpd, qse_httpd_task_t* task, int writable) +static void proxy_forward_client_input_to_peer (qse_httpd_t* httpd, qse_httpd_task_t* task) { task_proxy_t* proxy = (task_proxy_t*)task->ctx; @@ -680,56 +679,48 @@ static void proxy_forward_client_input_to_peer ( /* normal forwarding */ qse_ssize_t n; - if (writable) goto forward; - - n = httpd->opt.scb.mux.writable (httpd, proxy->peer.handle, 0); -#if 0 -if (n == 0) qse_printf (QSE_T("PROXY FORWARD: @@@@@@@@@NOT WRITABLE\n")); -#endif - if (n >= 1) - { - forward: - /* writable */ #if 0 qse_printf (QSE_T("PROXY FORWARD: @@@@@@@@@@WRITING[%.*hs]\n"), (int)QSE_MBS_LEN(proxy->reqfwdbuf), QSE_MBS_PTR(proxy->reqfwdbuf)); #endif - n = httpd->opt.scb.peer.send ( - httpd, &proxy->peer, - QSE_MBS_PTR(proxy->reqfwdbuf), - QSE_MBS_LEN(proxy->reqfwdbuf) - ); - -/* 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 (QSE_MBS_LEN(proxy->reqfwdbuf) <= 0) - { - if (proxy->req == QSE_NULL) goto done; - else task->trigger.v[0].mask &= ~QSE_HTTPD_TASK_TRIGGER_WRITE; - } - } - } + httpd->errnum = QSE_HTTPD_ENOERR; + n = httpd->opt.scb.peer.send ( + httpd, &proxy->peer, + QSE_MBS_PTR(proxy->reqfwdbuf), + QSE_MBS_LEN(proxy->reqfwdbuf) + ); if (n <= -1) { - if (httpd->opt.trait & QSE_HTTPD_LOGACT) - log_proxy_error (proxy, "proxy send-to-peer error - "); - - proxy->reqflags |= PROXY_REQ_FWDERR; - qse_mbs_clear (proxy->reqfwdbuf); - if (proxy->req) + if (httpd->errnum != QSE_HTTPD_EAGAIN) { - qse_htre_discardcontent (proxy->req); - /* NOTE: proxy->req may be set to QSE_NULL - * in proxy_snatch_client_input() triggered by - * qse_htre_discardcontent() */ - } + if (httpd->opt.trait & QSE_HTTPD_LOGACT) + log_proxy_error (proxy, "proxy send-to-peer error - "); - task->trigger.v[0].mask &= ~QSE_HTTPD_TASK_TRIGGER_WRITE; /* peer */ + proxy->reqflags |= PROXY_REQ_FWDERR; + qse_mbs_clear (proxy->reqfwdbuf); + if (proxy->req) + { + qse_htre_discardcontent (proxy->req); + /* NOTE: proxy->req may be set to QSE_NULL + * in proxy_snatch_client_input() triggered by + * qse_htre_discardcontent() */ + } + + task->trigger.v[0].mask &= ~QSE_HTTPD_TASK_TRIGGER_WRITE; /* peer */ + } + } + else 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. */ + qse_mbs_del (proxy->reqfwdbuf, 0, n); + if (QSE_MBS_LEN(proxy->reqfwdbuf) <= 0) + { + if (proxy->req == QSE_NULL) goto done; + else task->trigger.v[0].mask &= ~QSE_HTTPD_TASK_TRIGGER_WRITE; + } } } } @@ -1038,6 +1029,7 @@ printf ("task_main_proxy_5 trigger[0].mask=%d trigger[1].mask=%d trigger[2].mask task->trigger.v[0].mask, task->trigger.v[1].mask, task->trigger.cmask); #endif +#if 0 if (task->trigger.cmask & QSE_HTTPD_TASK_TRIGGER_READABLE) { /* if the client side is readable */ @@ -1048,23 +1040,30 @@ printf ("task_main_proxy_5 trigger[0].mask=%d trigger[1].mask=%d trigger[2].mask /* if the peer side is writable while the client side is not readable*/ proxy_forward_client_input_to_peer (httpd, task, 1); } +#endif + proxy_forward_client_input_to_peer (httpd, task); - if (/*!(task->trigger.cmask & QSE_HTTPD_TASK_TRIGGER_WRITE) ||*/ - (task->trigger.cmask & QSE_HTTPD_TASK_TRIGGER_WRITABLE)) + if (/*(task->trigger.cmask & QSE_HTTPD_TASK_TRIGGER_WRITABLE) && */ proxy->buflen > 0) { - if (proxy->buflen > 0) - { - /* TODO: check if proxy outputs more than content-length if it is set... */ + /* wrote to the client socket as long as there's something to + * write. it's safe to do so as the socket is non-blocking. + * i commented out the check in the 'if' condition above */ - n = httpd->opt.scb.client.send (httpd, client, proxy->buf, proxy->buflen); - if (n <= -1) + /* TODO: check if proxy outputs more than content-length if it is set... */ + httpd->errnum = QSE_HTTPD_ENOERR; + n = httpd->opt.scb.client.send (httpd, client, proxy->buf, proxy->buflen); + if (n <= -1) + { + if (httpd->errnum != QSE_HTTPD_EAGAIN) { /* can't return internal server error any more... */ if (httpd->opt.trait & QSE_HTTPD_LOGACT) log_proxy_error (proxy, "proxy send-to-client error - "); return -1; } - + } + else if (n > 0) + { QSE_MEMCPY (&proxy->buf[0], &proxy->buf[n], proxy->buflen - n); proxy->buflen -= n; } @@ -1087,6 +1086,8 @@ printf ("task_main_proxy_4 trigger[0].mask=%d trigger[1].mask=%d trigger[2].mask task->trigger.v[0].mask, task->trigger.v[1].mask, task->trigger.cmask); #endif + proxy_forward_client_input_to_peer (httpd, task); +/* if (task->trigger.cmask & QSE_HTTPD_TASK_TRIGGER_READABLE) { proxy_forward_client_input_to_peer (httpd, task, 0); @@ -1095,64 +1096,69 @@ printf ("task_main_proxy_4 trigger[0].mask=%d trigger[1].mask=%d trigger[2].mask { proxy_forward_client_input_to_peer (httpd, task, 1); } +*/ - if (task->trigger.v[0].mask & QSE_HTTPD_TASK_TRIGGER_READABLE) + if ((task->trigger.v[0].mask & QSE_HTTPD_TASK_TRIGGER_READABLE) && + proxy->buflen < QSE_SIZEOF(proxy->buf)) { qse_ssize_t n; - if (proxy->buflen < QSE_SIZEOF(proxy->buf)) + /* reading from the peer */ + httpd->errnum = QSE_HTTPD_ENOERR; + n = httpd->opt.scb.peer.recv ( + httpd, &proxy->peer, + &proxy->buf[proxy->buflen], + QSE_SIZEOF(proxy->buf) - proxy->buflen + ); + if (n <= -1) { - /* reading from the peer */ - - httpd->errnum = QSE_HTTPD_ENOERR; - n = httpd->opt.scb.peer.recv ( - httpd, &proxy->peer, - &proxy->buf[proxy->buflen], - QSE_SIZEOF(proxy->buf) - proxy->buflen - ); - if (n <= -1) + /* can't return internal server error any more... */ + if (httpd->errnum != QSE_HTTPD_EAGAIN) { - /* can't return internal server error any more... */ if (httpd->opt.trait & QSE_HTTPD_LOGACT) log_proxy_error (proxy, "proxy recv-from-peer error - "); return -1; } - if (n == 0) + + /* carry on as if recv was't called at all */ + } + else if (n == 0) + { + /* peer closed connection */ + if (proxy->resflags & PROXY_RES_PEER_LENGTH) { - /* peer closed connection */ - if (proxy->resflags & PROXY_RES_PEER_LENGTH) + QSE_ASSERT (!(proxy->flags & PROXY_RAW)); + + if (proxy->peer_output_received < proxy->peer_output_length) { - QSE_ASSERT (!(proxy->flags & PROXY_RAW)); - - if (proxy->peer_output_received < proxy->peer_output_length) - { - if (httpd->opt.trait & QSE_HTTPD_LOGACT) - log_proxy_error (proxy, "proxy premature eof - "); - return -1; - } + if (httpd->opt.trait & QSE_HTTPD_LOGACT) + log_proxy_error (proxy, "proxy premature eof - "); + return -1; } - - task->main = task_main_proxy_5; - - /* nothing to read from peer. set the mask to 0 */ - task->trigger.v[0].mask = 0; - - /* arrange to be called if the client side is writable */ - task->trigger.cmask |= QSE_HTTPD_TASK_TRIGGER_WRITE; - - if (proxy->flags & PROXY_RAW) - { - /* peer connection has been closed. - * so no more forwarding from the client to the peer - * is possible. get rid of the content callback on the - * client side. */ - qse_htre_unsetconcb (proxy->req); - proxy->req = QSE_NULL; - } - - return 1; } + task->main = task_main_proxy_5; + + /* nothing to read from peer. set the mask to 0 */ + task->trigger.v[0].mask = 0; + + /* arrange to be called if the client side is writable */ + task->trigger.cmask |= QSE_HTTPD_TASK_TRIGGER_WRITE; + + if (proxy->flags & PROXY_RAW) + { + /* peer connection has been closed. + * so no more forwarding from the client to the peer + * is possible. get rid of the content callback on the + * client side. */ + qse_htre_unsetconcb (proxy->req); + proxy->req = QSE_NULL; + } + + return 1; + } + else + { proxy->buflen += n; proxy->peer_output_received += n; @@ -1177,21 +1183,33 @@ printf ("task_main_proxy_4 trigger[0].mask=%d trigger[1].mask=%d trigger[2].mask } } } + } + if (proxy->buflen) + { /* 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. */ + * this task function is called. even if it's not writable, + * it should still be ok as the client socket is non-blocking. */ + qse_ssize_t n; + + httpd->errnum = QSE_HTTPD_ENOERR; n = httpd->opt.scb.client.send (httpd, client, proxy->buf, proxy->buflen); if (n <= -1) { - /* can't return internal server error any more... */ - if (httpd->opt.trait & QSE_HTTPD_LOGACT) - log_proxy_error (proxy, "proxy send-to-client error - "); - return -1; + if (httpd->errnum != QSE_HTTPD_EAGAIN) + { + /* can't return internal server error any more... */ + if (httpd->opt.trait & QSE_HTTPD_LOGACT) + log_proxy_error (proxy, "proxy send-to-client error - "); + return -1; + } + } + else if (n > 0) + { + QSE_MEMCPY (&proxy->buf[0], &proxy->buf[n], proxy->buflen - n); + proxy->buflen -= n; } - - QSE_MEMCPY (&proxy->buf[0], &proxy->buf[n], proxy->buflen - n); - proxy->buflen -= n; } return 1; @@ -1212,6 +1230,8 @@ qse_printf (QSE_T("task_main_proxy_3 trigger[0].mask=%d trigger[1].mask=%d trigg task->trigger.v[0].mask, task->trigger.v[1].mask, task->trigger.cmask); #endif + proxy_forward_client_input_to_peer (httpd, task); +/* if (task->trigger.cmask & QSE_HTTPD_TASK_TRIGGER_READABLE) { proxy_forward_client_input_to_peer (httpd, task, 0); @@ -1220,60 +1240,68 @@ qse_printf (QSE_T("task_main_proxy_3 trigger[0].mask=%d trigger[1].mask=%d trigg { proxy_forward_client_input_to_peer (httpd, task, 1); } +*/ - if (/*!(task->trigger.cmask & QSE_HTTPD_TASK_TRIGGER_WRITE) ||*/ - (task->trigger.cmask & QSE_HTTPD_TASK_TRIGGER_WRITABLE)) + if (/*(task->trigger.cmask & QSE_HTTPD_TASK_TRIGGER_WRITABLE) &&*/ proxy->res_pending > 0) { + /* the client socket is non-blocking. so attempt to send + * so long as there's something to send regardless of writability + * of the client socket. see the check commented out in the 'if' + * condition above.*/ + qse_ssize_t n; qse_size_t count; count = proxy->res_pending; if (count > MAX_SEND_SIZE) count = MAX_SEND_SIZE; - if (count > 0) + httpd->errnum = QSE_HTTPD_ENOERR; + n = httpd->opt.scb.client.send ( + httpd, client, + &QSE_MBS_CHAR(proxy->res,proxy->res_consumed), + count + ); + if (n <= -1) { - n = httpd->opt.scb.client.send ( - httpd, client, - &QSE_MBS_CHAR(proxy->res,proxy->res_consumed), - count - ); - if (n <= -1) + if (httpd->errnum != QSE_HTTPD_EAGAIN) { if (httpd->opt.trait & QSE_HTTPD_LOGACT) log_proxy_error (proxy, "proxy send-to-client error - "); return -1; } - + } + else if (n > 0) + { proxy->resflags |= PROXY_RES_EVER_SENTBACK; proxy->res_consumed += n; proxy->res_pending -= n; - } - if (proxy->res_pending <= 0) - { - /* all data received from the peer so far(including those injected) - * have been sent back to the client-side */ - - qse_mbs_clear (proxy->res); - proxy->res_consumed = 0; - - if ((proxy->resflags & PROXY_RES_CLIENT_CHUNK) || - ((proxy->resflags & PROXY_RES_PEER_LENGTH) && proxy->peer_output_received >= proxy->peer_output_length)) + if (proxy->res_pending <= 0) { - /* received all contents */ - task->main = task_main_proxy_5; - task->trigger.cmask |= QSE_HTTPD_TASK_TRIGGER_WRITE; + /* all data received from the peer so far(including those injected) + * have been sent back to the client-side */ + + qse_mbs_clear (proxy->res); + proxy->res_consumed = 0; + + if ((proxy->resflags & PROXY_RES_CLIENT_CHUNK) || + ((proxy->resflags & PROXY_RES_PEER_LENGTH) && proxy->peer_output_received >= proxy->peer_output_length)) + { + /* received all contents */ + task->main = task_main_proxy_5; + task->trigger.cmask |= QSE_HTTPD_TASK_TRIGGER_WRITE; + } + else + { + /* there are still more to read from the peer. + * arrange to read the remaining contents from the peer */ + task->main = task_main_proxy_4; + /* nothing to write in proxy->res. so clear WRITE from the + * client side */ + task->trigger.cmask &= ~QSE_HTTPD_TASK_TRIGGER_WRITE; + } + return 1; } - else - { - /* there are still more to read from the peer. - * arrange to read the remaining contents from the peer */ - task->main = task_main_proxy_4; - /* nothing to write in proxy->res. so clear WRITE from the - * client side */ - task->trigger.cmask &= ~QSE_HTTPD_TASK_TRIGGER_WRITE; - } - return 1; } } @@ -1291,6 +1319,8 @@ printf ("task_main_proxy_2 trigger[0].mask=%d trigger[1].mask=%d cmask=%d\n", task->trigger.v[0].mask, task->trigger.v[1].mask, task->trigger.cmask); #endif + proxy_forward_client_input_to_peer (httpd, task); +#if 0 if (task->trigger.cmask & QSE_HTTPD_TASK_TRIGGER_READABLE) { /* client is readable */ @@ -1301,41 +1331,46 @@ printf ("task_main_proxy_2 trigger[0].mask=%d trigger[1].mask=%d cmask=%d\n", /* client is not readable but peer is writable */ proxy_forward_client_input_to_peer (httpd, task, 1); } +#endif - if (/*!(task->trigger.cmask & QSE_HTTPD_TASK_TRIGGER_WRITE) ||*/ - (task->trigger.cmask & QSE_HTTPD_TASK_TRIGGER_WRITABLE)) + if (/*(task->trigger.cmask & QSE_HTTPD_TASK_TRIGGER_WRITABLE) && */ proxy->res_pending > 0) { - if (proxy->res_pending > 0) + /* the 'if' condition becomes true only if '100 Continue' + * is received without an actual reply in a previous call to + * qse_htrd_feed() far below. Since the actual reply is not + * received yet, i just want to read more while relaying + * '100 Continue' to the client. + * + * attempt to write to the client regardless of writability of + * the cleint socket as it is non-blocking. see the check commented + * in the 'if' condition above. */ + + qse_ssize_t n; + qse_size_t count; + + QSE_ASSERT ((proxy->resflags & PROXY_RES_AWAIT_RESHDR) || + (proxy->resflags & PROXY_RES_CLIENT_CHUNK)); + + count = proxy->res_pending; + if (count > MAX_SEND_SIZE) count = MAX_SEND_SIZE; + + httpd->errnum = QSE_HTTPD_ENOERR; + n = httpd->opt.scb.client.send ( + httpd, client, + QSE_MBS_CPTR(proxy->res,proxy->res_consumed), + count + ); + if (n <= -1) { - /* the 'if' condition becomes true only if '100 Continue' - * is received without an actual reply in a previous call to - * qse_htrd_feed() below. Since the actual reply is not - * received yet, i just want to read more while realying - * '100 Continue' to the client. this task handler is called - * only if the client side handle is writable. i can safely - * write to the client without a check. */ - - qse_ssize_t n; - qse_size_t count; - - QSE_ASSERT ((proxy->resflags & PROXY_RES_AWAIT_RESHDR) || - (proxy->resflags & PROXY_RES_CLIENT_CHUNK)); - - count = proxy->res_pending; - if (count > MAX_SEND_SIZE) count = MAX_SEND_SIZE; - - n = httpd->opt.scb.client.send ( - httpd, client, - QSE_MBS_CPTR(proxy->res,proxy->res_consumed), - count - ); - if (n <= -1) + if (httpd->errnum != QSE_HTTPD_EAGAIN) { if (httpd->opt.trait & QSE_HTTPD_LOGACT) log_proxy_error (proxy, "proxy send-to-client error - "); goto oops; } - + } + else if (n > 0) + { proxy->resflags |= PROXY_RES_EVER_SENTBACK; proxy->res_consumed += n; proxy->res_pending -= n; @@ -1367,16 +1402,19 @@ printf ("task_main_proxy_2 trigger[0].mask=%d trigger[1].mask=%d cmask=%d\n", ); if (n <= -1) { - if (httpd->opt.trait & QSE_HTTPD_LOGACT) - log_proxy_error (proxy, "proxy recv-from-peer error - "); - goto oops; + if (httpd->errnum != QSE_HTTPD_EAGAIN) + { + if (httpd->opt.trait & QSE_HTTPD_LOGACT) + log_proxy_error (proxy, "proxy recv-from-peer error - "); + goto oops; + } } - if (n == 0) + else if (n == 0) { if (!(proxy->resflags & PROXY_RES_RECEIVED_RESHDR)) { /* end of output from peer before it has seen a header. - * the proxy peer must be crooked. */ + * the proxy peer must be bad. */ if (httpd->opt.trait & QSE_HTTPD_LOGACT) log_proxy_error (proxy, "proxy premature eof - "); @@ -1405,8 +1443,10 @@ printf ("task_main_proxy_2 trigger[0].mask=%d trigger[1].mask=%d cmask=%d\n", goto oops; } } - - proxy->buflen += n; + else + { + proxy->buflen += n; + } #if 0 qse_printf (QSE_T("#####PROXY FEEDING %d [\n"), (int)proxy->buflen); @@ -1416,15 +1456,17 @@ for (i = 0; i < proxy->buflen; i++) qse_printf (QSE_T("%hc"), proxy->buf[i]); } qse_printf (QSE_T("]\n")); #endif - - if (qse_htrd_feed (proxy->peer_htrd, proxy->buf, proxy->buflen) <= -1) + if (proxy->buflen > 0) { - if (httpd->opt.trait & QSE_HTTPD_LOGACT) - log_proxy_error (proxy, "proxy feed error - "); - goto oops; - } + if (qse_htrd_feed (proxy->peer_htrd, proxy->buf, proxy->buflen) <= -1) + { + if (httpd->opt.trait & QSE_HTTPD_LOGACT) + log_proxy_error (proxy, "proxy feed error - "); + goto oops; + } - proxy->buflen = 0; + proxy->buflen = 0; + } if (QSE_MBS_LEN(proxy->res) > 0) { @@ -1530,7 +1572,7 @@ static int task_main_proxy_1 ( 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); + proxy_forward_client_input_to_peer (httpd, task); if (QSE_MBS_LEN(proxy->reqfwdbuf) > 0) { /* there are still more to forward in the buffer @@ -1697,7 +1739,7 @@ static int task_main_proxy ( if (QSE_MBS_LEN(proxy->reqfwdbuf) > 0) { - proxy_forward_client_input_to_peer (httpd, task, 0); + proxy_forward_client_input_to_peer (httpd, task); if (QSE_MBS_LEN(proxy->reqfwdbuf) > 0) { task->trigger.v[0].mask |= QSE_HTTPD_TASK_TRIGGER_WRITE; diff --git a/qse/lib/http/httpd-std.c b/qse/lib/http/httpd-std.c index 5e9fe188..386ce515 100644 --- a/qse/lib/http/httpd-std.c +++ b/qse/lib/http/httpd-std.c @@ -135,6 +135,9 @@ static qse_httpd_errnum_t skerr_to_errnum (DWORD e) case WSAEINTR: return QSE_HTTPD_EINTR; + case WASEWOULDBLOCK: + return QSE_HTTPD_EAGAIN; + case WSAECONNREFUSED: case WSAENETUNREACH: case WSAEHOSTUNREACH: @@ -180,6 +183,9 @@ static qse_httpd_errnum_t skerr_to_errnum (int e) case SOCEPIPE: return QSE_HTTPD_EPIPE; + case SOCEAGAIN: + return QSE_HTTPD_EAGAIN; + case SOCECONNREFUSED: case SOCENETUNREACH: case SOCEHOSTUNREACH: @@ -229,6 +235,9 @@ static qse_httpd_errnum_t skerr_to_errnum (int e) return QSE_HTTPD_EPIPE; case EAGAIN: +#if defined(EWEOULDBLOCK) && defined(EAGAIN) && EWOULDBLOCK != EAGAIN + case EWOULDBLOCK: +#endif return QSE_HTTPD_EAGAIN; #if defined(ECONNREFUSED) || defined(ENETUNREACH) || defined(EHOSTUNREACH) || defined(EHOSTDOWN) @@ -259,68 +268,68 @@ static qse_httpd_errnum_t skerr_to_errnum (int e) static qse_httpd_errnum_t muxerr_to_errnum (qse_mux_errnum_t e) { - switch (e) - { - case QSE_MUX_ENOMEM: - return QSE_HTTPD_ENOMEM; + switch (e) + { + case QSE_MUX_ENOMEM: + return QSE_HTTPD_ENOMEM; - case QSE_MUX_EINVAL: - return QSE_HTTPD_EINVAL; + case QSE_MUX_EINVAL: + return QSE_HTTPD_EINVAL; - case QSE_MUX_EACCES: - return QSE_HTTPD_EACCES; + case QSE_MUX_EACCES: + return QSE_HTTPD_EACCES; - case QSE_MUX_ENOENT: - return QSE_HTTPD_ENOENT; + case QSE_MUX_ENOENT: + return QSE_HTTPD_ENOENT; - case QSE_MUX_EEXIST: - return QSE_HTTPD_EEXIST; + case QSE_MUX_EEXIST: + return QSE_HTTPD_EEXIST; - case QSE_MUX_EINTR: - return QSE_HTTPD_EINTR; + case QSE_MUX_EINTR: + return QSE_HTTPD_EINTR; - case QSE_MUX_EPIPE: - return QSE_HTTPD_EPIPE; + case QSE_MUX_EPIPE: + return QSE_HTTPD_EPIPE; - case QSE_MUX_EAGAIN: - return QSE_HTTPD_EAGAIN; + case QSE_MUX_EAGAIN: + return QSE_HTTPD_EAGAIN; - default: - return QSE_HTTPD_ESYSERR; - } + default: + return QSE_HTTPD_ESYSERR; + } } static qse_httpd_errnum_t fioerr_to_errnum (qse_fio_errnum_t e) { - switch (e) - { - case QSE_FIO_ENOMEM: - return QSE_HTTPD_ENOMEM; + switch (e) + { + case QSE_FIO_ENOMEM: + return QSE_HTTPD_ENOMEM; - case QSE_FIO_EINVAL: - return QSE_HTTPD_EINVAL; + case QSE_FIO_EINVAL: + return QSE_HTTPD_EINVAL; - case QSE_FIO_EACCES: - return QSE_HTTPD_EACCES; + case QSE_FIO_EACCES: + return QSE_HTTPD_EACCES; - case QSE_FIO_ENOENT: - return QSE_HTTPD_ENOENT; + case QSE_FIO_ENOENT: + return QSE_HTTPD_ENOENT; - case QSE_FIO_EEXIST: - return QSE_HTTPD_EEXIST; + case QSE_FIO_EEXIST: + return QSE_HTTPD_EEXIST; - case QSE_FIO_EINTR: - return QSE_HTTPD_EINTR; + case QSE_FIO_EINTR: + return QSE_HTTPD_EINTR; - case QSE_FIO_EPIPE: - return QSE_HTTPD_EPIPE; + case QSE_FIO_EPIPE: + return QSE_HTTPD_EPIPE; - case QSE_FIO_EAGAIN: - return QSE_HTTPD_EAGAIN; + case QSE_FIO_EAGAIN: + return QSE_HTTPD_EAGAIN; - default: - return QSE_HTTPD_ESYSERR; - } + default: + return QSE_HTTPD_ESYSERR; + } } static qse_httpd_errnum_t direrr_to_errnum (qse_dir_errnum_t e) @@ -488,7 +497,7 @@ static QSE_INLINE qse_ssize_t __send_file_ssl ( if (offset && (foff = qse_fio_seek (in_fd.ptr, *offset, QSE_FIO_BEGIN)) != *offset) { if (foff == (qse_foff_t)-1) - qse_httpd_seterrnum (httpd, fioerr_to_errnum(qse_fio_geterrnum(in_fd.ptr))); + qse_httpd_seterrnum (httpd, fioerr_to_errnum(qse_fio_geterrnum(in_fd.ptr))); else qse_httpd_seterrnum (httpd, QSE_HTTPD_ESYSERR); return (qse_ssize_t)-1; @@ -514,7 +523,7 @@ static QSE_INLINE qse_ssize_t __send_file_ssl ( } else if (ret <= -1) { - qse_httpd_seterrnum (httpd, fioerr_to_errnum(qse_fio_geterrnum(in_fd.ptr))); + qse_httpd_seterrnum (httpd, fioerr_to_errnum(qse_fio_geterrnum(in_fd.ptr))); } return ret; @@ -640,13 +649,86 @@ void* qse_httpd_getxtnstd (qse_httpd_t* httpd) /* ------------------------------------------------------------------- */ +#if defined(_WIN32) + typedef SOCKET sock_t; +# define SOCK_INIT INVALID_SOCKET +#else + typedef int sock_t; +# define SOCK_INIT -1 +#endif +#if !defined(HAVE_SOCKLEN_T) + typedef int socklen_t; +#endif + +static QSE_INLINE int is_valid_socket (sock_t fd) +{ +#if defined(_WIN32) + return fd != INVALID_SOCKET; +#else + return fd >= 0; +#endif +} + +static QSE_INLINE void close_socket (sock_t fd) +{ +#if defined(_WIN32) + closesocket (fd); +#elif defined(__OS2__) + soclose (fd); +#elif defined(__DOS__) + /* TODO: */ +#else + QSE_CLOSE (fd); +#endif +} + +static int set_socket_nonblock (qse_httpd_t* httpd, sock_t fd, int enabled) +{ +#if defined(_WIN32) + if (ioctlsocket (fd, FIONBIO, &enabled) == SOCKET_ERROR) + { + qse_httpd_seterrnum (httpd, SKERR_TO_ERRNUM()); + return -1; + } + + return 0; + +#elif defined(__OS2__) + + if (ioctl (fd, FIONBIO, (char*)&enabled, sizeof(enabled)) <= -1) + { + qse_httpd_seterrnum (httpd, SKERR_TO_ERRNUM()); + return -1; + } + return 0; + +#elif defined(O_NONBLOCK) + + int flag = fcntl (fd, F_GETFL); + if (flag >= 0) flag = fcntl (fd, F_SETFL, (enabled? (flag | O_NONBLOCK): (flag & ~O_NONBLOCK))); + if (flag <= -1) + { + qse_httpd_seterrnum (httpd, SKERR_TO_ERRNUM()); + return -1; + } + return 0; +#else + + qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOIMPL); + return -1; +#endif + +} + +/* ------------------------------------------------------------------- */ + static int server_open (qse_httpd_t* httpd, qse_httpd_server_t* server) { #if defined(__DOS__) qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOIMPL); return -1; #else - int fd = -1, flag; + sock_t fd = SOCK_INIT, flag; qse_skad_t addr; int addrsize; @@ -658,7 +740,11 @@ static int server_open (qse_httpd_t* httpd, qse_httpd_server_t* server) } fd = socket (qse_skadfamily(&addr), SOCK_STREAM, IPPROTO_TCP); - if (fd <= -1) goto oops; + if (!is_valid_socket(fd)) + { + qse_httpd_seterrnum (httpd, SKERR_TO_ERRNUM()); + goto oops; + } #if defined(FD_CLOEXEC) flag = fcntl (fd, F_GETFD); @@ -720,6 +806,7 @@ static int server_open (qse_httpd_t* httpd, qse_httpd_server_t* server) if (len <= 0 || setsockopt (fd, SOL_SOCKET, SO_BINDTODEVICE, tmp, len) <= -1) { /* TODO: logging ... */ + qse_httpd_seterrnum (httpd, ((len <= 0)? QSE_HTTPD_EINVAL: SKERR_TO_ERRNUM())); goto oops; } #endif @@ -735,50 +822,44 @@ static int server_open (qse_httpd_t* httpd, qse_httpd_server_t* server) { int on = 1; setsockopt (fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof(on)); - if (bind (fd, (struct sockaddr*)&addr, addrsize) <= -1) goto oops; + if (bind (fd, (struct sockaddr*)&addr, addrsize) <= -1) + { + qse_httpd_seterrnum (httpd, SKERR_TO_ERRNUM()); + goto oops; + } + } + else + { + qse_httpd_seterrnum (httpd, SKERR_TO_ERRNUM()); + goto oops; } - else goto oops; #else + + qse_httpd_seterrnum (httpd, SKERR_TO_ERRNUM()); goto oops; #endif } - if (listen (fd, 10) <= -1) goto oops; + if (listen (fd, 10) <= -1) + { + qse_httpd_seterrnum (httpd, SKERR_TO_ERRNUM()); + goto oops; + } - #if defined(O_NONBLOCK) - flag = fcntl (fd, F_GETFL); - if (flag >= 0) fcntl (fd, F_SETFL, flag | O_NONBLOCK); - #endif + if (set_socket_nonblock (httpd, fd, 1) <= -1) goto oops; server->handle.i = fd; return 0; oops: - qse_httpd_seterrnum (httpd, SKERR_TO_ERRNUM()); - #if defined(_WIN32) - if (fd != INVALID_SOCKET) closesocket (fd); - #elif defined(__OS2__) - if (fd >= 0) soclose (fd); - #elif defined(__DOS__) - /* TODO: */ - #else - if (fd >= 0) QSE_CLOSE (fd); - #endif + if (is_valid_socket(fd)) close_socket (fd); return -1; #endif } static void server_close (qse_httpd_t* httpd, qse_httpd_server_t* server) { -#if defined(_WIN32) - closesocket (server->handle.i); -#elif defined(__OS2__) - soclose (server->handle.i); -#elif defined(__DOS__) - /* TODO: */ -#else - QSE_CLOSE (server->handle.i); -#endif + close_socket (server->handle.i); } static int server_accept ( @@ -790,29 +871,25 @@ static int server_accept ( #else qse_skad_t addr; - - #if defined(HAVE_SOCKLEN_T) socklen_t addrlen; - #else - int addrlen; - #endif - int fd, flag; + sock_t fd = SOCK_INIT; + int flag; addrlen = QSE_SIZEOF(addr); fd = accept (server->handle.i, (struct sockaddr*)&addr, &addrlen); - if (fd <= -1) + if (!is_valid_socket(fd)) { qse_httpd_seterrnum (httpd, SKERR_TO_ERRNUM()); - return -1; + goto oops; } #if 0 +/* TODO: implement maximum number of client per server??? */ if (fd >= FD_SETSIZE) { qse_fprintf (QSE_STDERR, QSE_T("Error: too many client?\n")); /*TODO: qse_httpd_seterrnum (httpd, QSE_HTTPD_EXXXXX);*/ - QSE_CLOSE (fd); - return -1; + goto oops; } #endif @@ -821,10 +898,7 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: too many client?\n")); if (flag >= 0) fcntl (fd, F_SETFD, flag | FD_CLOEXEC); #endif - #if defined(O_NONBLOCK) - flag = fcntl (fd, F_GETFL); - if (flag >= 0) fcntl (fd, F_SETFL, flag | O_NONBLOCK); - #endif + if (set_socket_nonblock (httpd, fd, 1) <= -1) goto oops; if (qse_skadtonwad (&addr, &client->remote_addr) <= -1) { @@ -867,6 +941,10 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: too many client?\n")); client->handle.i = fd; return 0; + +oops: + if (is_valid_socket(fd)) close_socket (fd); + return -1; #endif } @@ -885,17 +963,14 @@ static int peer_open (qse_httpd_t* httpd, qse_httpd_peer_t* peer) qse_skad_t connaddr, bindaddr; int connaddrsize, bindaddrsize; int connected = 1; + sock_t fd = SOCK_INIT; #if defined(_WIN32) - SOCKET fd = -1; unsigned long cmd; #elif defined(__OS2__) - int fd = -1; int cmd; #elif defined(__DOS__) - int fd = -1; int flag; #else - int fd = -1; int flag; #endif @@ -908,7 +983,11 @@ static int peer_open (qse_httpd_t* httpd, qse_httpd_peer_t* peer) } fd = socket (qse_skadfamily(&connaddr), SOCK_STREAM, IPPROTO_TCP); - if (fd <= -1) goto oops; + if (!is_valid_socket(fd)) + { + qse_httpd_seterrnum (httpd, SKERR_TO_ERRNUM()); + goto oops; + } #if defined(IP_TRANSPARENT) flag = 1; @@ -921,72 +1000,58 @@ static int peer_open (qse_httpd_t* httpd, qse_httpd_peer_t* peer) /* TODO: some logging for this failure though */ } -#if defined(_WIN32) - cmd = 1; - if (ioctlsocket(fd, FIONBIO, &cmd) == SOCKET_ERROR) goto oops; - - if (connect (fd, (struct sockaddr*)&connaddr, connaddrsize) <= -1) - { - if (WSAGetLastError() != WSAEWOULDBLOCK) goto oops; - connected = 0; - } - - cmd = 0; - if (ioctlsocket(fd, FIONBIO, &cmd) == SOCKET_ERROR) goto oops; - -#elif defined(__OS2__) - - cmd = 1; - if (ioctl(fd, FIONBIO, (char*)&cmd, QSE_SIZEOF(cmd)) == -1) goto oops; - - if (connect (fd, (struct sockaddr*)&connaddr, connaddrsize) == -1) - { - if (sock_errno() != SOCEINPROGRESS) goto oops; - connected = 0; - } - - cmd = 0; - if (ioctl(fd, FIONBIO, (char*)&cmd, QSE_SIZEOF(cmd)) == -1) goto oops; - -#elif defined(__DOS__) - - /* TODO: */ - -#else - #if defined(FD_CLOEXEC) flag = fcntl (fd, F_GETFD); if (flag >= 0) fcntl (fd, F_SETFD, flag | FD_CLOEXEC); #endif - flag = fcntl (fd, F_GETFL); - if (flag >= 0) fcntl (fd, F_SETFL, flag | O_NONBLOCK); + if (set_socket_nonblock (httpd, fd, 1) <= -1) goto oops; +#if defined(_WIN32) if (connect (fd, (struct sockaddr*)&connaddr, connaddrsize) <= -1) { - if (errno != EINPROGRESS) goto oops; + if (WSAGetLastError() != WSAEWOULDBLOCK) + { + qse_httpd_seterrnum (httpd, SKERR_TO_ERRNUM()); + goto oops; + } connected = 0; } - /* restore flags */ - if (fcntl (fd, F_SETFL, flag) <= -1) goto oops; +#elif defined(__OS2__) + if (connect (fd, (struct sockaddr*)&connaddr, connaddrsize) == -1) + { + if (sock_errno() != SOCEINPROGRESS) + { + qse_httpd_seterrnum (httpd, SKERR_TO_ERRNUM()); + goto oops; + } + connected = 0; + } +#elif defined(__DOS__) + /* TODO: */ + +#else + if (connect (fd, (struct sockaddr*)&connaddr, connaddrsize) <= -1) + { + if (errno != EINPROGRESS) + { + qse_httpd_seterrnum (httpd, SKERR_TO_ERRNUM()); + goto oops; + } + connected = 0; + } #endif + /*if (set_socket_nonblock (httpd, fd, 0) <= -1) goto oops;*/ + peer->handle.i = fd; return connected; oops: qse_httpd_seterrnum (httpd, SKERR_TO_ERRNUM()); -#if defined(_WIN32) - if (fd != INVALID_SOCKET) closesocket (fd); -#elif defined(__OS2__) - if (fd >= 0) soclose (fd); -#elif defined(__DOS__) - /* TODO: */ -#else - if (fd >= 0) QSE_CLOSE (fd); -#endif + if (is_valid_socket(fd)) close_socket (fd); return -1; /* -------------------------------------------------------------------- */ @@ -995,15 +1060,7 @@ oops: static void peer_close (qse_httpd_t* httpd, qse_httpd_peer_t* peer) { -#if defined(_WIN32) - closesocket (peer->handle.i); -#elif defined(__OS2__) - soclose (peer->handle.i); -#elif defined(__DOS__) - /* TODO: */ -#else - QSE_CLOSE (peer->handle.i); -#endif + close_socket (peer->handle.i); } static int peer_connected (qse_httpd_t* httpd, qse_httpd_peer_t* peer) @@ -1057,11 +1114,7 @@ static int peer_connected (qse_httpd_t* httpd, qse_httpd_peer_t* peer) #else - #if defined(HAVE_SOCKLEN_T) socklen_t len; - #else - int len; - #endif int ret; len = QSE_SIZEOF(ret); @@ -1747,26 +1800,22 @@ static int dir_read (qse_httpd_t* httpd, qse_ubi_t handle, qse_httpd_dirent_t* d # define SHUT_RDWR 2 #endif -static void client_close ( - qse_httpd_t* httpd, qse_httpd_client_t* client) +static void client_close (qse_httpd_t* httpd, qse_httpd_client_t* client) { #if defined(_WIN32) shutdown (client->handle.i, SHUT_RDWR); - closesocket (client->handle.i); #elif defined(__OS2__) shutdown (client->handle.i, SHUT_RDWR); - soclose (client->handle.i); #elif defined(__DOS__) /* TODO: */ #else shutdown (client->handle.i, SHUT_RDWR); - QSE_CLOSE (client->handle.i); #endif + close_socket (client->handle.i); } -static void client_shutdown ( - qse_httpd_t* httpd, qse_httpd_client_t* client) +static void client_shutdown (qse_httpd_t* httpd, qse_httpd_client_t* client) { #if defined(_WIN32) shutdown (client->handle.i, SHUT_RDWR); @@ -2201,7 +2250,8 @@ static int dns_open (qse_httpd_t* httpd, qse_httpd_dns_t* dns) qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOIMPL); return -1; #else - int fd = -1, flag; + sock_t fd = SOCK_INIT; + int flag; qse_nwad_t nwad; dns_ctx_t* dc; httpd_xtn_t* httpd_xtn; @@ -2276,7 +2326,11 @@ static int dns_open (qse_httpd_t* httpd, qse_httpd_dns_t* dns) } fd = socket (qse_skadfamily(&dc->skad), SOCK_DGRAM, IPPROTO_UDP); - if (fd <= -1) goto oops; + if (!is_valid_socket(fd)) + { + qse_httpd_seterrnum (httpd, SKERR_TO_ERRNUM()); + goto oops; + } #if defined(FD_CLOEXEC) flag = fcntl (fd, F_GETFD); @@ -2293,22 +2347,14 @@ static int dns_open (qse_httpd_t* httpd, qse_httpd_dns_t* dns) setsockopt (fd, SOL_SOCKET, SO_REUSEPORT, (void*)&flag, QSE_SIZEOF(flag)); #endif + if (set_socket_nonblock (httpd, fd, 1) <= -1) goto oops; + dns->handle.i = fd; dns->ctx = dc; return 0; oops: - if (fd >= 0) - { - #if defined(_WIN32) - closesocket (fd); - #elif defined(__OS2__) - soclose (fd); - #else - QSE_CLOSE (fd); - #endif - } - + if (is_valid_socket(fd)) close_socket (fd); if (dc) qse_httpd_freemem (httpd, dc); return -1; @@ -2447,7 +2493,7 @@ static int dns_recv (qse_httpd_t* httpd, qse_httpd_dns_t* dns) dns_ctx_t* dc = (dns_ctx_t*)dns->ctx; qse_skad_t fromaddr; - socklen_t fromlen; /* TODO: change type */ + socklen_t fromlen; qse_uint8_t buf[DNS_MAX_MSG_LEN]; qse_ssize_t len; @@ -2614,16 +2660,13 @@ static int dns_send (qse_httpd_t* httpd, qse_httpd_dns_t* dns, const qse_mchar_t qse_size_t name_len; dns_ans_t* ans; -printf ("finding answer in cache...\n"); ans = dns_get_answer_from_cache (dc, name); if (ans) { -printf ("found answer in cache...\n"); resol (httpd, name, ((ans->nwad.type == QSE_NWAD_NX)? QSE_NULL: &ans->nwad), ctx); return 0; } -printf ("found XXXXX in cache...\n"); seq = dc->seq; seq = (seq + 1) % QSE_COUNTOF(dc->reqs); dc->seq = seq; diff --git a/qse/lib/http/httpd-task.c b/qse/lib/http/httpd-task.c index 7632fdba..520ac06f 100644 --- a/qse/lib/http/httpd-task.c +++ b/qse/lib/http/httpd-task.c @@ -90,13 +90,19 @@ static int task_main_format ( count = MAX_SEND_SIZE; if (count >= ctx->left) count = ctx->left; + httpd->errnum = QSE_HTTPD_ENOERR; n = httpd->opt.scb.client.send (httpd, client, ctx->ptr, count); - if (n <= -1) return -1; + if (n <= -1) + { + if (httpd->errnum != QSE_HTTPD_EAGAIN) return -1; + } + else if (n > 0) + { + ctx->left -= n; + if (ctx->left <= 0) return 0; + ctx->ptr += n; + } - ctx->left -= n; - if (ctx->left <= 0) return 0; - - ctx->ptr += n; return 1; /* more work to do */ } diff --git a/qse/lib/http/httpd-text.c b/qse/lib/http/httpd-text.c index 4d74f273..e0a9db27 100644 --- a/qse/lib/http/httpd-text.c +++ b/qse/lib/http/httpd-text.c @@ -53,13 +53,18 @@ static int task_main_text ( if (count >= ctx->left) count = ctx->left; /* TODO: do i need to add code to skip this send if count is 0? */ + httpd->errnum = QSE_HTTPD_ENOERR; n = httpd->opt.scb.client.send (httpd, client, ctx->ptr, count); - if (n <= -1) return -1; - - ctx->left -= n; - if (ctx->left <= 0) return 0; - - ctx->ptr += n; + if (n <= -1) + { + if (httpd->errnum != QSE_HTTPD_EAGAIN) return -1; + } + else if (n > 0) + { + ctx->left -= n; + if (ctx->left <= 0) return 0; + ctx->ptr += n; + } return 1; /* more work to do */ } diff --git a/qse/lib/http/httpd.c b/qse/lib/http/httpd.c index 6f0d9b76..4f5db2c6 100644 --- a/qse/lib/http/httpd.c +++ b/qse/lib/http/httpd.c @@ -921,7 +921,7 @@ static int update_mux_for_current_task (qse_httpd_t* httpd, qse_httpd_client_t* { /* active to inactive */ -printf ("ACTIVE TO INACTIVE....\n"); +/*printf ("ACTIVE TO INACTIVE....\n");*/ for (i = 0; i < QSE_COUNTOF(task->trigger.v); i++) { if (client->status & CLIENT_TASK_TRIGGER_RW_IN_MUX(i)) @@ -942,21 +942,21 @@ printf ("ACTIVE TO INACTIVE....\n"); return 0; } -printf ("INACTIVE TO ACTIVE....\n"); +/*printf ("INACTIVE TO ACTIVE....\n");*/ /* inactive to active . go on*/ } else { if (task->trigger.flags & QSE_HTTPD_TASK_TRIGGER_INACTIVE) { -printf ("INACTIVE TO INACTIVE....\n"); +/*printf ("INACTIVE TO INACTIVE....\n");*/ /* inactive to inactive. * save the trigger as the trigger handle and masks could change */ client->trigger = task->trigger; return 0; } -printf ("ACTIVE TO ACTIVE....\n"); +/*printf ("ACTIVE TO ACTIVE....\n");*/ /* active to active. go on */ } @@ -1183,13 +1183,23 @@ static int invoke_client_task ( { if (mask & QSE_HTTPD_MUX_READ) { - QSE_ASSERT (task->trigger.v[i].mask & QSE_HTTPD_TASK_TRIGGER_READ); + /*QSE_ASSERT (task->trigger.v[i].mask & QSE_HTTPD_TASK_TRIGGER_READ);*/ + /* the assertion above may be false if a task for the same + * trigger set was called earlier by a qse_mux_poll() call. + * and the task has changed some masks. + * + * for instance, you put handle A and B in to a trigger. + * if the task is triggered for A but the task may change + * the mask for B. the task may get executed for B by + * the same qse_mux_poll() call. + */ + task->trigger.v[i].mask |= QSE_HTTPD_TASK_TRIGGER_READABLE; trigger_fired = 1; } if (mask & QSE_HTTPD_MUX_WRITE) { - QSE_ASSERT (task->trigger.v[i].mask & QSE_HTTPD_TASK_TRIGGER_WRITE); + /*QSE_ASSERT (task->trigger.v[i].mask & QSE_HTTPD_TASK_TRIGGER_WRITE);*/ task->trigger.v[i].mask |= QSE_HTTPD_TASK_TRIGGER_WRITABLE; trigger_fired = 1; }