From 681e50822c9dac27d404656ab42b540e4273641e Mon Sep 17 00:00:00 2001 From: hyung-hwan Date: Thu, 2 Feb 2012 14:48:06 +0000 Subject: [PATCH] made some enhancements to httpd --- qse/include/qse/cmn/pio.h | 33 +++++--- qse/include/qse/net/httpd.h | 21 ++++- qse/lib/cmn/pio.c | 63 +++++++++------ qse/lib/cmn/syscall.h | 14 +++- qse/lib/net/httpd.c | 154 +++++++++++++++++++++++++++++++----- qse/lib/net/httpd.h | 1 + qse/lib/net/httpd_task.c | 84 +++++++++++++++----- qse/samples/cmn/pio.c | 18 ++++- qse/samples/net/http01.c | 2 +- 9 files changed, 307 insertions(+), 83 deletions(-) diff --git a/qse/include/qse/cmn/pio.h b/qse/include/qse/cmn/pio.h index 5f35995d..b7b93414 100644 --- a/qse/include/qse/cmn/pio.h +++ b/qse/include/qse/cmn/pio.h @@ -43,38 +43,47 @@ enum qse_pio_flag_t QSE_PIO_NOAUTOFLUSH = (1 << 2), /** execute the command via a system shell - * (/bin/sh on *nix, cmd.exe on windows) */ + * (/bin/sh on unix/linux, cmd.exe on windows and os2) */ QSE_PIO_SHELL = (1 << 3), /** indicate that the command to qse_pio_open() is a multi-byte string. * it is useful if #QSE_CHAR_IS_WCHAR is defined. */ QSE_PIO_MBSCMD = (1 << 4), + /** don't attempt to close open file descriptors unknown to pio. + * it is useful only on a unix-like systems where file descriptors + * not set with FD_CLOEXEC are inherited by a child process. + * you're advised to set this option if all normal file descriptors + * in your application are open with FD_CLOEXEC set. it can skip + * checking a bunch of file descriptors and arranging to close + * them to prevent inheritance. */ + QSE_PIO_NOCLOEXEC = (1 << 5), + /** write to stdin of a child process */ - QSE_PIO_WRITEIN = (1 << 8), + QSE_PIO_WRITEIN = (1 << 8), /** read stdout of a child process */ - QSE_PIO_READOUT = (1 << 9), + QSE_PIO_READOUT = (1 << 9), /** read stderr of a child process */ - QSE_PIO_READERR = (1 << 10), + 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), + QSE_PIO_OUTTOERR = (1 << 12), /** redirect stdin to the null device (/dev/null, >NUL) */ - QSE_PIO_ERRTONUL = (1 << 14), + QSE_PIO_ERRTONUL = (1 << 14), /** redirect stderr to the null device (2>/dev/null, 2>NUL) */ - QSE_PIO_OUTTONUL = (1 << 15), + QSE_PIO_OUTTONUL = (1 << 15), /** drop stdin */ - QSE_PIO_DROPIN = (1 << 16), + QSE_PIO_DROPIN = (1 << 16), /** drop stdout */ - QSE_PIO_DROPOUT = (1 << 17), + QSE_PIO_DROPOUT = (1 << 17), /** drop stderr */ - QSE_PIO_DROPERR = (1 << 18) + QSE_PIO_DROPERR = (1 << 18) }; /** diff --git a/qse/include/qse/net/httpd.h b/qse/include/qse/net/httpd.h index 62951418..9a8b0f3e 100644 --- a/qse/include/qse/net/httpd.h +++ b/qse/include/qse/net/httpd.h @@ -43,6 +43,11 @@ enum qse_httpd_errnum_t }; typedef enum qse_httpd_errnum_t qse_httpd_errnum_t; +enum qse_httpd_option_t +{ + QSE_HTTPD_CGINOCLOEXEC = (1 << 0) +}; + typedef struct qse_httpd_cbs_t qse_httpd_cbs_t; struct qse_httpd_cbs_t { @@ -77,10 +82,15 @@ typedef int (*qse_httpd_task_main_t) ( struct qse_httpd_task_t { - /* you must not call another entask functions from within initailizer */ + /* you must not call another entask functions from within + * an initailizer. you can call entask functions from within + * a finalizer and a main function. */ qse_httpd_task_init_t init; qse_httpd_task_fini_t fini; qse_httpd_task_main_t main; + + qse_ubi_t trigger; + void* ctx; }; @@ -105,6 +115,15 @@ void qse_httpd_close ( qse_httpd_t* httpd ); +int qse_httpd_getoption ( + qse_httpd_t* httpd +); + +void qse_httpd_setoption ( + qse_httpd_t* httpd, + int option +); + const qse_httpd_cbs_t* qse_httpd_getcbs ( qse_httpd_t* httpd ); diff --git a/qse/lib/cmn/pio.c b/qse/lib/cmn/pio.c index ab5b9232..5df82a85 100644 --- a/qse/lib/cmn/pio.c +++ b/qse/lib/cmn/pio.c @@ -270,9 +270,10 @@ static int make_param ( } #if defined(QSE_CHAR_IS_MCHAR) - if (mcmd && mcmd != cmd) param->mcmd = mcmd; + if (mcmd && mcmd != (qse_mchar_t*)cmd) param->mcmd = mcmd; #else - if (mcmd && mcmd != cmd && mcmd != param->fixed_mbuf) param->mcmd = mcmd; + if (mcmd && mcmd != (qse_mchar_t*)cmd && + mcmd != param->fixed_mbuf) param->mcmd = mcmd; #endif return 0; @@ -280,7 +281,8 @@ oops: #if defined(QSE_CHAR_IS_MCHAR) if (mcmd && mcmd != cmd) QSE_MMGR_FREE (pio->mmgr, mcmd); #else - if (mcmd && mcmd != cmd && mcmd != param->fixed_mbuf) QSE_MMGR_FREE (pio->mmgr, mcmd); + if (mcmd && mcmd != (qse_mchar_t*)cmd && + mcmd != param->fixed_mbuf) QSE_MMGR_FREE (pio->mmgr, mcmd); if (wcmd) QSE_MMGR_FREE (pio->mmgr, wcmd); #endif return -1; @@ -288,7 +290,18 @@ oops: static QSE_INLINE int is_fd_valid (int fd) { - return fcntl (fd, F_GETFL) != -1 || errno != EBADF; + return fcntl (fd, F_GETFD) != -1 || errno != EBADF; +} + +static QSE_INLINE int is_fd_valid_and_nocloexec (int fd) +{ + int flags = fcntl (fd, F_GETFD); + if (flags == -1) + { + if (errno == EBADF) return 0; /* invalid. return false */ + return -1; /* unknown. true but negative to indicate unknown */ + } + return !(flags & FD_CLOEXEC)? 1: 0; } static int get_highest_fd (void) @@ -962,21 +975,21 @@ int qse_pio_init ( if ((flags & QSE_PIO_DROPERR) && is_fd_valid(2) && posix_spawn_file_actions_addclose (&fa, 2) != 0) goto oops; + if (!(flags & QSE_PIO_NOCLOEXEC)) { int fd = get_highest_fd (); while (--fd > 2) { - if (fd != handle[0] && - fd != handle[1] && - fd != handle[2] && - fd != handle[3] && - fd != handle[4] && - fd != handle[5]) - { - /* closing attempt on a best-effort basis */ - if (is_fd_valid(fd) && - posix_spawn_file_actions_addclose (&fa, fd) != 0) goto oops; - } + if (fd == handle[0] || fd == handle[1] || + fd == handle[2] || fd == handle[3] || + fd == handle[4] || fd == handle[5]) continue; + + /* closing attempt on a best-effort basis. + * posix_spawn() fails if the file descriptor added + * with addclose() is closed before posix_spawn(). + * addclose() if no FD_CLOEXEC is set or it's unknown. */ + if (is_fd_valid_and_nocloexec(fd) && + posix_spawn_file_actions_addclose (&fa, fd) != 0) goto oops; } } @@ -1044,17 +1057,19 @@ int qse_pio_init ( { /* child */ qse_pio_hnd_t devnull = -1; - int fd = get_highest_fd (); - /* don't close stdin/out/err and the pipes */ - while (--fd > 2) + if (!(flags & QSE_PIO_NOCLOEXEC)) { - if (fd != handle[0] && - fd != handle[1] && - fd != handle[2] && - fd != handle[3] && - fd != handle[4] && - fd != handle[5]) QSE_CLOSE (fd); + int fd = get_highest_fd (); + + /* close all other unknown open handles except + * stdin/out/err and the pipes. */ + while (--fd > 2) + { + if (fd != handle[0] && fd != handle[1] && + fd != handle[2] && fd != handle[3] && + fd != handle[4] && fd != handle[5]) QSE_CLOSE (fd); + } } if (flags & QSE_PIO_WRITEIN) diff --git a/qse/lib/cmn/syscall.h b/qse/lib/cmn/syscall.h index 04ac1e1c..91b569cc 100644 --- a/qse/lib/cmn/syscall.h +++ b/qse/lib/cmn/syscall.h @@ -99,16 +99,16 @@ #endif #if !defined(_LP64) && (QSE_SIZEOF_VOID_P<8) && defined(SYS_fstat64) -# define QSE_FSTAT(path,stbuf) syscall(SYS_fstat64,path,stbuf) +# define QSE_FSTAT(handle,stbuf) syscall(SYS_fstat64,handle,stbuf) typedef struct stat64 qse_fstat_t; #elif defined(SYS_fstat) -# define QSE_FSTAT(path,stbuf) syscall(SYS_fstat,path,stbuf) +# define QSE_FSTAT(handle,stbuf) syscall(SYS_fstat,handle,stbuf) typedef struct stat qse_fstat_t; #elif !defined(_LP64) && (QSE_SIZEOF_VOID_P<8) && defined(HAVE_FSTAT64) -# define QSE_FSTAT(path,stbuf) fstat64(path,stbuf) +# define QSE_FSTAT(handle,stbuf) fstat64(handle,stbuf) typedef struct stat64 qse_fstat_t; #else -# define QSE_FSTAT(path,stbuf) fstat(path,stbuf) +# define QSE_FSTAT(handle,stbuf) fstat(handle,stbuf) typedef struct stat qse_fstat_t; #endif @@ -289,6 +289,12 @@ typedef struct stat qse_lstat_t; #endif +#if defined(SYS_access) +# define QSE_ACCESS(path,mode) syscall(SYS_access,path,mode) +#else +# define QSE_ACCESS(path,mode) access(path,mode) +#endif + #if defined(SYS_rename) # define QSE_RENAME(oldpath,newpath) syscall(SYS_rename,oldpath,newpath) #else diff --git a/qse/lib/net/httpd.c b/qse/lib/net/httpd.c index bc1a0511..b69ffefe 100644 --- a/qse/lib/net/httpd.c +++ b/qse/lib/net/httpd.c @@ -111,6 +111,16 @@ void qse_httpd_stop (qse_httpd_t* httpd) httpd->stopreq = 1; } +int qse_httpd_getoption (qse_httpd_t* httpd) +{ + return httpd->option; +} + +void qse_httpd_setoption (qse_httpd_t* httpd, int option) +{ + httpd->option = option; +} + const qse_httpd_cbs_t* qse_httpd_getcbs (qse_httpd_t* httpd) { return httpd->cbs; @@ -573,13 +583,14 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: too many client?\n")); * arrival for wrong checksum, for example. */ flag = fcntl (c, F_GETFL); if (flag >= 0) fcntl (c, F_SETFL, flag | O_NONBLOCK); - fcntl (c, F_SETFD, FD_CLOEXEC); + + flag = fcntl (c, F_GETFD); + if (flag >= 0) fcntl (c, F_SETFD, flag | FD_CLOEXEC); #if defined(HAVE_PTHREAD) if (httpd->threaded) pthread_mutex_lock (&httpd->client.mutex); #endif client = insert_into_client_array (httpd, c, &addr); - #if defined(HAVE_PTHREAD) if (httpd->threaded) pthread_mutex_unlock (&httpd->client.mutex); #endif @@ -614,12 +625,12 @@ httpd->cbs.on_error (httpd, l).... */ } static int make_fd_set_from_client_array ( - qse_httpd_t* httpd, fd_set* r, fd_set* w) + qse_httpd_t* httpd, fd_set* r, fd_set* w, int for_rdwr) { int fd, max = -1; client_array_t* ca = &httpd->client.array; - if (r) + if (r && for_rdwr) { max = httpd->listener.max; *r = httpd->listener.set; @@ -635,15 +646,48 @@ static int make_fd_set_from_client_array ( { if (r && !ca->data[fd].bad) { - FD_SET (ca->data[fd].handle.i, r); - if (ca->data[fd].handle.i > max) max = ca->data[fd].handle.i; + if (for_rdwr) + { + /* add a client-side handle to the read set */ + FD_SET (ca->data[fd].handle.i, r); + if (ca->data[fd].handle.i > max) max = ca->data[fd].handle.i; + } + + if (ca->data[fd].task.queue.head && + ca->data[fd].task.queue.head->task.trigger.i >= 0) + { + /* if a trigger is available, add it to the read set also */ + FD_SET (ca->data[fd].task.queue.head->task.trigger.i, r); + if (ca->data[fd].task.queue.head->task.trigger.i > max) + max = ca->data[fd].task.queue.head->task.trigger.i; + } } +#if 0 if (w && (ca->data[fd].task.queue.count > 0 || ca->data[fd].bad)) { /* add it to the set if it has a response to send */ FD_SET (ca->data[fd].handle.i, w); if (ca->data[fd].handle.i > max) max = ca->data[fd].handle.i; } +#endif + if (w) + { + if (ca->data[fd].bad || + (ca->data[fd].task.queue.head && + ca->data[fd].task.queue.head->task.trigger.i <= -1)) + { + /* add a client-side handle to the write set + * if the client is already marked bad or + * the current task enqueued didn't specify a trigger. + * + * if the task doesn't have a trigger, i perform + * the task so long as the client side-handle is + * available for writing in the main loop. + */ + FD_SET (ca->data[fd].handle.i, w); + if (ca->data[fd].handle.i > max) max = ca->data[fd].handle.i; + } + } } } @@ -680,14 +724,14 @@ static void* response_thread (void* arg) while (!httpd->stopreq) { int n, max, fd; - fd_set w; + fd_set r, w; struct timeval tv; tv.tv_sec = 1; tv.tv_usec = 0; pthread_mutex_lock (&httpd->client.mutex); - max = make_fd_set_from_client_array (httpd, QSE_NULL, &w); + max = make_fd_set_from_client_array (httpd, &r, &w, 0); pthread_mutex_unlock (&httpd->client.mutex); while (max == -1 && !httpd->stopreq) @@ -702,14 +746,14 @@ static void* response_thread (void* arg) timeout.tv_nsec = now.tv_usec * 1000; pthread_cond_timedwait (&httpd->client.cond, &httpd->client.mutex, &timeout); - max = make_fd_set_from_client_array (httpd, QSE_NULL, &w); + max = make_fd_set_from_client_array (httpd, &r, &w, 0); pthread_mutex_unlock (&httpd->client.mutex); } if (httpd->stopreq) break; - n = select (max + 1, QSE_NULL, &w, QSE_NULL, &tv); + n = select (max + 1, &r, &w, QSE_NULL, &tv); if (n <= -1) { /*if (errno == EINTR) continue; */ @@ -728,21 +772,45 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: select returned failure - %hs\n"), strerr if (!client->htrd) continue; + /* ---------------------------------------------- */ +#if 0 if (FD_ISSET(client->handle.i, &w)) { if (client->bad) { - /*send (client->handle, i, "INTERNAL SERVER ERROR..", ...);*/ - /*shutdown (client->handle.i, SHUT_RDWR);*/ - pthread_mutex_lock (&httpd->client.mutex); - delete_from_client_array (httpd, fd); - pthread_mutex_unlock (&httpd->client.mutex); } else if (client->task.queue.count > 0) { - perform_task (httpd, client); + if (client->task.queue.head->task.trigger.i <= -1 || + FD_ISSET(client->task.queue.head->task.trigger.i, &r)) + { + perform_task (httpd, client); + } } } +#endif + if (client->bad) + { + /*shutdown (client->handle.i, SHUT_RDWR);*/ + pthread_mutex_lock (&httpd->client.mutex); + delete_from_client_array (httpd, fd); + pthread_mutex_unlock (&httpd->client.mutex); + } + else + { + if (client->task.queue.head->task.trigger.i <= -1 || + FD_ISSET(client->task.queue.head->task.trigger.i, &r)) + { + tv.tv_sec = 0; + tv.tv_usec = 0; + FD_ZERO (&w); + FD_SET (client->handle.i, &w); + n = select (max + 1, QSE_NULL, &w, QSE_NULL, &tv); + if (n > 0 && FD_ISSET(client->handle.i, &w)) + perform_task (httpd, client); + } + } + /* ---------------------------------------------- */ } } @@ -760,7 +828,13 @@ reread: m = read (client->handle.i, buf, QSE_SIZEOF(buf)); if (m <= -1) { - if (errno != EINTR) + if (errno == EAGAIN || errno == EWOULDBLOCK) + { + /* nothing to read yet. */ +qse_fprintf (QSE_STDERR, QSE_T("Warning: Nothing to read from a client %d\n"), client->handle.i); + return 0; /* return ok */ + } + else if (errno != EINTR) { httpd->errnum = QSE_HTTPD_ESOCKET; qse_fprintf (QSE_STDERR, QSE_T("Error: failed to read from a client %d\n"), client->handle.i); @@ -856,11 +930,12 @@ int qse_httpd_loop (qse_httpd_t* httpd, int threaded) #if defined(HAVE_PTHREAD) if (httpd->threaded) pthread_mutex_lock (&httpd->client.mutex); #endif - max = make_fd_set_from_client_array (httpd, &r, &w); + max = make_fd_set_from_client_array (httpd, &r, &w, 1); #if defined(HAVE_PTHREAD) if (httpd->threaded) pthread_mutex_unlock (&httpd->client.mutex); #endif + /*n = select (max + 1, &r, &w, QSE_NULL, &tv);*/ n = select (max + 1, &r, &w, QSE_NULL, &tv); if (n <= -1) { @@ -871,7 +946,10 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: select returned failure\n")); /* break; */ continue; } - if (n == 0) continue; + if (n == 0) + { + continue; + } /* check the listener activity */ accept_client_from_listeners (httpd, &r); @@ -905,6 +983,9 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: select returned failure\n")); } } + + /*----------------------------------------------------------*/ +#if 0 if (!httpd->threaded && FD_ISSET(client->handle.i, &w)) { /* output is handled in the main loop if and @@ -920,10 +1001,43 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: select returned failure\n")); } else if (client->task.queue.count > 0) { - perform_task (httpd, client); + if (client->task.queue.head->task.trigger.i <= -1 || + FD_ISSET(client->task.queue.head->task.trigger.i, &r)) + { + perform_task (httpd, client); + } + } + } +#endif + if (!httpd->threaded) + { + if (client->bad) + { + /*shutdown (client->handle.i, SHUT_RDWR);*/ + /*pthread_mutex_lock (&httpd->client.mutex);*/ + delete_from_client_array (httpd, fd); + /*pthread_mutex_unlock (&httpd->client.mutex);*/ + } + else if (client->task.queue.count > 0) + { + if (client->task.queue.head->task.trigger.i <= -1 || + FD_ISSET(client->task.queue.head->task.trigger.i, &r)) + { + tv.tv_sec = 0; + tv.tv_usec = 0; + FD_ZERO (&w); + FD_SET (client->handle.i, &w); + n = select (max + 1, QSE_NULL, &w, QSE_NULL, &tv); + + /* TODO: logging if n == -1 */ + + if (n > 0 && FD_ISSET(client->handle.i, &w)) + perform_task (httpd, client); + } } } + /*----------------------------------------------------------*/ } } diff --git a/qse/lib/net/httpd.h b/qse/lib/net/httpd.h index 2d2f8fa7..2e4605c3 100644 --- a/qse/lib/net/httpd.h +++ b/qse/lib/net/httpd.h @@ -112,6 +112,7 @@ struct qse_httpd_t qse_httpd_errnum_t errnum; qse_httpd_cbs_t* cbs; + int option; int stopreq; int threaded; diff --git a/qse/lib/net/httpd_task.c b/qse/lib/net/httpd_task.c index 06ee02f7..f4343b5a 100644 --- a/qse/lib/net/httpd_task.c +++ b/qse/lib/net/httpd_task.c @@ -25,12 +25,12 @@ #include "httpd.h" #include "../cmn/mem.h" +#include "../cmn/syscall.h" #include #include #include #include -#include #include #include #include @@ -131,6 +131,7 @@ qse_httpd_task_t* qse_httpd_entaskdisconnect ( QSE_MEMSET (&task, 0, QSE_SIZEOF(task)); task.main = task_main_disconnect; + task.trigger.i = -1; return qse_httpd_entask (httpd, client, pred, &task, 0); } @@ -177,6 +178,7 @@ qse_httpd_task_t* qse_httpd_entaskstatictext ( QSE_MEMSET (&task, 0, QSE_SIZEOF(task)); task.main = task_main_statictext; task.ctx = (void*)text; + task.trigger.i = -1; return qse_httpd_entask (httpd, client, pred, &task, 0); } @@ -247,6 +249,7 @@ qse_httpd_task_t* qse_httpd_entasktext ( task.init = task_init_text; task.main = task_main_text; task.ctx = &data; + task.trigger.i = -1; return qse_httpd_entask ( httpd, client, pred, &task, QSE_SIZEOF(data) + data.left); @@ -397,8 +400,9 @@ qse_httpd_task_t* qse_httpd_entaskformat ( task.fini = task_fini_format; task.main = task_main_format; task.ctx = &data; + task.trigger.i = -1; -qse_printf (QSE_T("SEND: [%.*S]\n"), (int)l, buf); +qse_printf (QSE_T("SEND: [%.*hs]\n"), (int)l, buf); return qse_httpd_entask ( httpd, client, pred, &task, QSE_SIZEOF(data)); } @@ -502,7 +506,7 @@ static void task_fini_file ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) { task_file_t* ctx = (task_file_t*)task->ctx; - close (ctx->handle.i); + QSE_CLOSE (ctx->handle.i); } static int task_main_file ( @@ -567,6 +571,7 @@ qse_printf (QSE_T("Debug: sending file to %d\n"), client->handle.i); task.main = task_main_file; task.fini = task_fini_file; task.ctx = &data; + task.trigger.i = -1; return qse_httpd_entask (httpd, client, pred, &task, QSE_SIZEOF(data)); } @@ -581,7 +586,8 @@ struct task_dir_t int footer_pending; struct dirent* dent; - qse_mchar_t buf[512]; + /*qse_mchar_t buf[4096];*/ + qse_mchar_t buf[512]; /* TOOD: increate size */ qse_size_t bufpos; qse_size_t buflen; qse_size_t bufrem; @@ -909,16 +915,13 @@ qse_httpd_task_t* qse_httpd_entaskdir ( task.main = chunked? task_main_dir: task_main_dir_nochunk; task.fini = task_fini_dir; task.ctx = &handle; + task.trigger.i = -1; return qse_httpd_entask (httpd, client, pred, &task, QSE_SIZEOF(task_dir_t)); } /*------------------------------------------------------------------------*/ -#include -#include -#include - typedef struct task_path_t task_path_t; struct task_path_t { @@ -945,19 +948,27 @@ static QSE_INLINE int task_main_path_file ( task_path_t* data = (task_path_t*)task->ctx; qse_httpd_task_t* x = task; qse_ubi_t handle; + int oflags; /* when it comes to the file size, using fstat after opening * can be more accurate. but this function uses information * set into the task context before the call to this function */ qse_printf (QSE_T("opening file %hs\n"), data->name); - handle.i = open (data->name, O_RDONLY); + + oflags = O_RDONLY; +#if defined(O_LARGEFILE) + oflags |= O_LARGEFILE; +#endif + handle.i = QSE_OPEN (data->name, oflags, 0); if (handle.i <= -1) { x = entask_error (httpd, client, x, 404, &data->version, data->keepalive); goto no_file_send; } - fcntl (handle.i, F_SETFD, FD_CLOEXEC); + oflags = QSE_FCNTL (handle.i, F_GETFD, 0); + if (oflags >= 0) + QSE_FCNTL (handle.i, F_SETFD, oflags | FD_CLOEXEC); if (data->range.type != QSE_HTTP_RANGE_NONE) { @@ -1147,9 +1158,9 @@ static int task_main_path ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) { task_path_t* data = (task_path_t*)task->ctx; - struct stat st; + qse_lstat_t st; - if (lstat (data->name, &st) <= -1) + if (QSE_LSTAT (data->name, &st) <= -1) { return (entask_error (httpd, client, task, 404, &data->version, data->keepalive) == QSE_NULL)? -1: 0; } @@ -1198,6 +1209,7 @@ qse_httpd_task_t* qse_httpd_entaskpath ( task.init = task_init_path; task.main = task_main_path; task.ctx = &data; + task.trigger.i = -1; return qse_httpd_entask (httpd, client, pred, &task, QSE_SIZEOF(task_path_t) + qse_mbslen(name) + 1); @@ -1476,7 +1488,13 @@ static void task_fini_cgi ( { task_cgi_t* cgi = (task_cgi_t*)task->ctx; if (cgi->env) qse_env_close (cgi->env); - if (cgi->pio) qse_pio_close (cgi->pio); + if (cgi->pio) + { + /* kill cgi in case it is still alive. + * qse_pio_wait() in qse_pio_close() can block. */ + qse_pio_kill (cgi->pio); + qse_pio_close (cgi->pio); + } if (cgi->res) qse_mbs_close (cgi->res); if (cgi->htrd) qse_htrd_close (cgi->htrd); qse_printf (QSE_T("task_fini_cgi\n")); @@ -1604,6 +1622,7 @@ qse_printf (QSE_T("CGI FUCKED UP...RETURNING TOO MUCH DATA\n")); return -1; } +qse_printf (QSE_T("CGI SEND [%.*hs]\n"), (int)cgi->buflen, cgi->buf); n = send (client->handle.i, cgi->buf, cgi->buflen, 0); if (n <= -1) { @@ -1615,6 +1634,7 @@ qse_printf (QSE_T("CGI FUCKED UP...RETURNING TOO MUCH DATA\n")); QSE_MEMCPY (&cgi->buf[0], &cgi->buf[n], cgi->buflen - n); cgi->buflen -= n; +qse_printf (QSE_T("CGI SEND DONE\n")); return 1; } @@ -1684,7 +1704,6 @@ qse_printf (QSE_T("[cgi_2 ]\n")); /* end of output from cgi before it has seen a header. * the cgi script must be crooked. */ /* TODO: logging */ - qse_pio_kill (cgi->pio); return -1; } @@ -1723,9 +1742,17 @@ static int task_main_cgi ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) { task_cgi_t* cgi = (task_cgi_t*)task->ctx; + int pio_options; + int error_code = 500; if (cgi->init_failed) goto oops; + if (QSE_ACCESS (cgi->path, X_OK) == -1) + { + error_code = (errno == EACCES)? 403: 404; + goto oops; + } + if (cgi->nph) { /* i cannot know how long the content will be @@ -1753,12 +1780,21 @@ static int task_main_cgi ( if (cgi->res == QSE_NULL) goto oops; } + pio_options = QSE_PIO_READOUT | QSE_PIO_WRITEIN | + QSE_PIO_ERRTONUL | QSE_PIO_MBSCMD; + if (httpd->option & QSE_HTTPD_CGINOCLOEXEC) + pio_options |= QSE_PIO_NOCLOEXEC; + cgi->pio = qse_pio_open ( - httpd->mmgr, 0, (const qse_char_t*)cgi->path, cgi->env, - QSE_PIO_READOUT | QSE_PIO_WRITEIN | QSE_PIO_ERRTONUL | QSE_PIO_MBSCMD + httpd->mmgr, 0, (const qse_char_t*)cgi->path, + cgi->env, pio_options ); if (cgi->pio == QSE_NULL) goto oops; + /* set the trigger that the main loop can use this + * handle for multiplexing */ + task->trigger.i = qse_pio_gethandle (cgi->pio, QSE_PIO_OUT); + if (cgi->nph) { /* skip various header processing */ @@ -1783,7 +1819,7 @@ oops: cgi->htrd = QSE_NULL; } - return (entask_error (httpd, client, task, 500, &cgi->version, cgi->keepalive) == QSE_NULL)? -1: 0; + return (entask_error (httpd, client, task, error_code, &cgi->version, cgi->keepalive) == QSE_NULL)? -1: 0; } /* TODO: global option or individual paramter for max cgi lifetime @@ -1809,6 +1845,7 @@ qse_httpd_task_t* qse_httpd_entaskcgi ( task.fini = task_fini_cgi; task.main = task_main_cgi; task.ctx = &arg; + task.trigger.i = -1; return qse_httpd_entask ( httpd, client, pred, &task, @@ -1835,6 +1872,7 @@ qse_httpd_task_t* qse_httpd_entasknph ( task.fini = task_fini_cgi; task.main = task_main_cgi; task.ctx = &arg; + task.trigger.i = -1; return qse_httpd_entask ( httpd, client, pred, &task, @@ -1845,16 +1883,22 @@ qse_httpd_task_t* qse_httpd_entasknph ( /*------------------------------------------------------------------------*/ -/* +#if 0 typedef struct task_proxy_t task_proxy_t; struct task_proxy_t { } -qse_httpd_task_t* qse_httpd_entaskproxy (...) +qse_httpd_task_t* qse_httpd_entaskproxy ( + qse_httpd_t* httpd, + qse_httpd_client_t* client, + const qse_httpd_task_t* pred, + const qse_mchar_t* host, + const qse_htre_t* req) { + } -*/ +#endif /*------------------------------------------------------------------------*/ diff --git a/qse/samples/cmn/pio.c b/qse/samples/cmn/pio.c index b531a26f..4a0360a8 100644 --- a/qse/samples/cmn/pio.c +++ b/qse/samples/cmn/pio.c @@ -282,6 +282,21 @@ static int test10 (void) } static int test11 (void) +{ + return pio1 ( +#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__) + (const qse_char_t*)"dir /a", +#else + (const qse_char_t*)"ls -laF", +#endif + QSE_NULL, + QSE_PIO_MBSCMD|QSE_PIO_READOUT|QSE_PIO_WRITEIN|QSE_PIO_DROPERR|QSE_PIO_INTONUL|QSE_PIO_SHELL, + QSE_PIO_OUT + ); +} + + +static int test12 (void) { qse_env_t* env; int n; @@ -307,7 +322,7 @@ static int test11 (void) return n; } -static int test12 (void) +static int test13 (void) { qse_pio_t* pio; int x; @@ -406,6 +421,7 @@ int main () R (test10); R (test11); R (test12); + R (test13); return 0; } diff --git a/qse/samples/net/http01.c b/qse/samples/net/http01.c index 8d430c2b..d4bbb70e 100644 --- a/qse/samples/net/http01.c +++ b/qse/samples/net/http01.c @@ -178,7 +178,7 @@ int httpd_main (int argc, qse_char_t* argv[]) signal (SIGINT, sigint); signal (SIGPIPE, SIG_IGN); - n = qse_httpd_loop (httpd, 0); + n = qse_httpd_loop (httpd, 1); signal (SIGINT, SIG_DFL); signal (SIGPIPE, SIG_DFL);