diff --git a/qse/configure b/qse/configure index eb649c18..3ebfb13b 100755 --- a/qse/configure +++ b/qse/configure @@ -16179,12 +16179,13 @@ _ACEOF fi done -for ac_func in posix_spawn +for ac_func in fork vfork posix_spawn do : - ac_fn_c_check_func "$LINENO" "posix_spawn" "ac_cv_func_posix_spawn" -if test "x$ac_cv_func_posix_spawn" = xyes; then : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF -#define HAVE_POSIX_SPAWN 1 +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi diff --git a/qse/configure.ac b/qse/configure.ac index 6735822b..2286d723 100644 --- a/qse/configure.ac +++ b/qse/configure.ac @@ -106,7 +106,7 @@ AC_CHECK_FUNCS([utime utimes]) AC_CHECK_FUNCS([sysconf]) AC_CHECK_FUNCS([backtrace backtrace_symbols]) AC_CHECK_FUNCS([fdopendir]) -AC_CHECK_FUNCS([posix_spawn]) +AC_CHECK_FUNCS([fork vfork posix_spawn]) OLDLIBS="$LIBS" LIBS="$LIBM $LIBS" diff --git a/qse/include/qse/config.h.in b/qse/include/qse/config.h.in index 452a52e2..5d1c67da 100644 --- a/qse/include/qse/config.h.in +++ b/qse/include/qse/config.h.in @@ -94,6 +94,9 @@ /* Define to 1 if you have the `fmodl' function. */ #undef HAVE_FMODL +/* Define to 1 if you have the `fork' function. */ +#undef HAVE_FORK + /* Define to 1 if you have the `fstat64' function. */ #undef HAVE_FSTAT64 @@ -321,6 +324,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_UTIME_H +/* Define to 1 if you have the `vfork' function. */ +#undef HAVE_VFORK + /* Define to 1 if you have the header file. */ #undef HAVE_WCHAR_H diff --git a/qse/lib/awk/std.c b/qse/lib/awk/std.c index 5f158bf4..4a5b789e 100644 --- a/qse/lib/awk/std.c +++ b/qse/lib/awk/std.c @@ -1453,25 +1453,25 @@ void* qse_awk_rtx_getxtnstd (qse_awk_rtx_t* rtx) return (void*)((rxtn_t*)QSE_XTN(rtx) + 1); } -static int fnc_rand (qse_awk_rtx_t* run, const qse_cstr_t* fnm) +static int fnc_rand (qse_awk_rtx_t* rtx, const qse_cstr_t* fnm) { qse_awk_val_t* r; /* rxtn_t* rxtn; - rxtn = (rxtn_t*) QSE_XTN (run); + rxtn = (rxtn_t*) QSE_XTN (rtx); r = qse_awk_rtx_makefltval ( - run, (qse_flt_t)(rand_r(rxtn->seed) % RAND_MAX) / RAND_MAX ); + rtx, (qse_flt_t)(rand_r(rxtn->seed) % RAND_MAX) / RAND_MAX ); */ r = qse_awk_rtx_makefltval ( - run, (qse_flt_t)(rand() % RAND_MAX) / RAND_MAX); + rtx, (qse_flt_t)(rand() % RAND_MAX) / RAND_MAX); if (r == QSE_NULL) return -1; - qse_awk_rtx_setretval (run, r); + qse_awk_rtx_setretval (rtx, r); return 0; } -static int fnc_srand (qse_awk_rtx_t* run, const qse_cstr_t* fnm) +static int fnc_srand (qse_awk_rtx_t* rtx, const qse_cstr_t* fnm) { qse_size_t nargs; qse_awk_val_t* a0; @@ -1481,16 +1481,16 @@ static int fnc_srand (qse_awk_rtx_t* run, const qse_cstr_t* fnm) unsigned int prev; rxtn_t* rxtn; - rxtn = (rxtn_t*) QSE_XTN (run); - nargs = qse_awk_rtx_getnargs (run); + rxtn = (rxtn_t*) QSE_XTN (rtx); + nargs = qse_awk_rtx_getnargs (rtx); QSE_ASSERT (nargs == 0 || nargs == 1); prev = rxtn->seed; if (nargs == 1) { - a0 = qse_awk_rtx_getarg (run, 0); - n = qse_awk_rtx_valtolong (run, a0, &lv); + a0 = qse_awk_rtx_getarg (rtx, 0); + n = qse_awk_rtx_valtolong (rtx, a0, &lv); if (n <= -1) return -1; rxtn->seed = lv; @@ -1504,10 +1504,10 @@ static int fnc_srand (qse_awk_rtx_t* run, const qse_cstr_t* fnm) srand (rxtn->seed); - r = qse_awk_rtx_makeintval (run, prev); + r = qse_awk_rtx_makeintval (rtx, prev); if (r == QSE_NULL) return -1; - qse_awk_rtx_setretval (run, r); + qse_awk_rtx_setretval (rtx, r); return 0; } @@ -1572,6 +1572,20 @@ skip_system: return 0; } +static int fnc_time (qse_awk_rtx_t* rtx, const qse_cstr_t* fnm) +{ + qse_awk_val_t* r; + qse_ntime_t now; + + if (qse_gettime (&now) <= -1) now = 0; + + r = qse_awk_rtx_makeintval (rtx, now); + if (r == QSE_NULL) return -1; + + qse_awk_rtx_setretval (rtx, r); + return 0; +} + #if defined(QSE_CHAR_IS_WCHAR) static qse_cmgr_t* getcmgr_from_cmgrtab ( qse_awk_rtx_t* rtx, const qse_char_t* ioname) @@ -1707,6 +1721,7 @@ static int add_functions (qse_awk_t* awk) ADDFNC (awk, QSE_T("rand"), 0, 0, fnc_rand, 0); ADDFNC (awk, QSE_T("srand"), 0, 1, fnc_srand, 0); ADDFNC (awk, QSE_T("system"), 1, 1, fnc_system, 0); + ADDFNC (awk, QSE_T("time"), 0, 0, fnc_time, 0); #if defined(QSE_CHAR_IS_WCHAR) ADDFNC (awk, QSE_T("setenc"), 2, 2, fnc_setenc, QSE_AWK_RIO); ADDFNC (awk, QSE_T("unsetenc"), 1, 1, fnc_unsetenc, QSE_AWK_RIO); diff --git a/qse/lib/cmn/pio.c b/qse/lib/cmn/pio.c index ffd7e2b6..11c1dee7 100644 --- a/qse/lib/cmn/pio.c +++ b/qse/lib/cmn/pio.c @@ -550,13 +550,23 @@ int qse_pio_init ( /* DOS not multi-processed. can't support pio */ -#elif defined(HAVE_POSIX_SPAWN) +#elif defined(HAVE_POSIX_SPAWN) && !(defined(QSE_SYSCALL0) && defined(SYS_vfork)) posix_spawn_file_actions_t fa; int fa_inited = 0; int pserr; +#if defined(__linux) + posix_spawnattr_t psattr; +#endif qse_pio_pid_t pid; param_t param; extern char** environ; +#elif defined(QSE_SYSCALL0) && defined(SYS_vfork) + qse_pio_pid_t pid; + param_t param; + extern char** environ; + char** envarr; + int highest_fd; + int dummy; #else qse_pio_pid_t pid; param_t param; @@ -1178,7 +1188,7 @@ create_process: pio->errnum = QSE_PIO_ENOIMPL; return -1; -#elif defined(HAVE_POSIX_SPAWN) +#elif defined(HAVE_POSIX_SPAWN) && !(defined(QSE_SYSCALL0) && defined(SYS_vfork)) if (flags & QSE_PIO_WRITEIN) { if (QSE_PIPE(&handle[0]) <= -1) @@ -1379,9 +1389,22 @@ create_process: goto oops; } +#if defined(__linux) + posix_spawnattr_init (&psattr); +#if !defined(POSIX_SPAWN_USEVFORK) +# define POSIX_SPAWN_USEVFORK 0x40 +#endif + posix_spawnattr_setflags (&psattr, POSIX_SPAWN_USEVFORK); +#endif + pserr = posix_spawn( - &pid, param.argv[0], &fa, QSE_NULL, param.argv, + &pid, param.argv[0], &fa, &psattr, param.argv, (env? qse_env_getarr(env): environ)); + +#if defined(__linux) + posix_spawnattr_destroy (&psattr); +#endif + free_param (pio, ¶m); if (fa_inited) { @@ -1411,6 +1434,210 @@ create_process: handle[5] = QSE_PIO_HND_NIL; } +#elif defined(QSE_SYSCALL0) && defined(SYS_vfork) + if (flags & QSE_PIO_WRITEIN) + { + if (QSE_PIPE(&handle[0]) <= -1) + { + pio->errnum = syserr_to_errnum (errno); + goto oops; + } + minidx = 0; maxidx = 1; + } + + if (flags & QSE_PIO_READOUT) + { + if (QSE_PIPE(&handle[2]) <= -1) + { + pio->errnum = syserr_to_errnum (errno); + goto oops; + } + if (minidx == -1) minidx = 2; + maxidx = 3; + } + + if (flags & QSE_PIO_READERR) + { + if (QSE_PIPE(&handle[4]) <= -1) + { + pio->errnum = syserr_to_errnum (errno); + goto oops; + } + if (minidx == -1) minidx = 4; + maxidx = 5; + } + + if (maxidx == -1) + { + pio->errnum = QSE_PIO_EINVAL; + goto oops; + } + + if (make_param (pio, cmd, flags, ¶m) <= -1) goto oops; + + /* check if the command(the command requested or /bin/sh) is + * exectuable to return an error without trying to execute it + * though this check alone isn't sufficient */ + if (assert_executable (pio, param.argv[0]) <= -1) + { + free_param (pio, ¶m); + goto oops; + } + + if (!(flags & QSE_PIO_NOCLOEXEC)) + highest_fd = get_highest_fd (); + envarr = env? qse_env_getarr(env): environ; + + QSE_SYSCALL0 (pid, SYS_vfork); + if (pid <= -1) + { + pio->errnum = QSE_PIO_EINVAL; + free_param (pio, ¶m); + goto oops; + } + + if (pid == 0) + { + /* child */ + qse_pio_hnd_t devnull = -1; + + if (!(flags & QSE_PIO_NOCLOEXEC)) + { + int fd = 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_SYSCALL1 (dummy, SYS_close, fd); + } + } + } + + if (flags & QSE_PIO_WRITEIN) + { + /* child should read */ + QSE_SYSCALL1 (dummy, SYS_close, handle[1]); + /*handle[1] = QSE_PIO_HND_NIL;*/ + QSE_SYSCALL2 (dummy, SYS_dup2, handle[0], 0); + if (dummy <= -1) goto child_oops; + QSE_SYSCALL1 (dummy, SYS_close, handle[0]); + /*handle[0] = QSE_PIO_HND_NIL;*/ + } + + if (flags & QSE_PIO_READOUT) + { + /* child should write */ + QSE_SYSCALL1 (dummy, SYS_close, handle[2]); + /*handle[2] = QSE_PIO_HND_NIL;*/ + QSE_SYSCALL2 (dummy, SYS_dup2, handle[3], 1); + if (dummy <= -1) goto child_oops; + + if (flags & QSE_PIO_ERRTOOUT) + { + QSE_SYSCALL2 (dummy, SYS_dup2, handle[3], 2); + if (dummy <= -1) goto child_oops; + } + + QSE_SYSCALL1 (dummy, SYS_close, handle[3]); + /*handle[3] = QSE_PIO_HND_NIL;*/ + } + + if (flags & QSE_PIO_READERR) + { + /* child should write */ + QSE_SYSCALL1 (dummy, SYS_close, handle[4]); + /*handle[4] = QSE_PIO_HND_NIL;*/ + QSE_SYSCALL2 (dummy, SYS_dup2, handle[5], 2); + if (dummy <= -1) goto child_oops; + + if (flags & QSE_PIO_OUTTOERR) + { + QSE_SYSCALL2 (dummy, SYS_dup2, handle[5], 1); + if (dummy <= -1) goto child_oops; + } + + QSE_SYSCALL1 (dummy, SYS_close, handle[5]); + /*handle[5] = QSE_PIO_HND_NIL;*/ + } + + if ((flags & QSE_PIO_INTONUL) || + (flags & QSE_PIO_OUTTONUL) || + (flags & QSE_PIO_ERRTONUL)) + { + #if defined(O_LARGEFILE) + QSE_SYSCALL3 (devnull, SYS_open, QSE_MT("/dev/null"), O_RDWR|O_LARGEFILE, 0); + #else + QSE_SYSCALL3 (devnull, SYS_open, QSE_MT("/dev/null"), O_RDWR, 0); + #endif + if (devnull <= -1) goto child_oops; + } + + if (flags & QSE_PIO_INTONUL) + { + QSE_SYSCALL2 (dummy, SYS_dup2, devnull, 0); + if (dummy <= -1) goto child_oops; + } + if (flags & QSE_PIO_OUTTONUL) + { + QSE_SYSCALL2 (dummy, SYS_dup2, devnull, 1); + if (dummy <= -1) goto child_oops; + } + if (flags & QSE_PIO_ERRTONUL) + { + QSE_SYSCALL2 (dummy, SYS_dup2, devnull, 2); + if (dummy <= -1) goto child_oops; + } + + if ((flags & QSE_PIO_INTONUL) || + (flags & QSE_PIO_OUTTONUL) || + (flags & QSE_PIO_ERRTONUL)) + { + QSE_SYSCALL1 (dummy, SYS_close, devnull); + devnull = -1; + } + + if (flags & QSE_PIO_DROPIN) QSE_SYSCALL1 (dummy, SYS_close, 0); + if (flags & QSE_PIO_DROPOUT) QSE_SYSCALL1 (dummy, SYS_close, 1); + if (flags & QSE_PIO_DROPERR) QSE_SYSCALL1 (dummy, SYS_close, 2); + + QSE_SYSCALL3 (dummy, SYS_execve, param.argv[0], param.argv, envarr); + if (dummy == -1) + { +printf ("hello\n"); + } + /*free_param (pio, ¶m); */ + + child_oops: + if (devnull >= 0) QSE_SYSCALL1 (dummy, SYS_close, devnull); + QSE_SYSCALL1 (dummy, SYS_exit, 128); + } + + /* parent */ + free_param (pio, ¶m); + pio->child = pid; + + if (flags & QSE_PIO_WRITEIN) + { + QSE_CLOSE (handle[0]); + handle[0] = QSE_PIO_HND_NIL; + } + + if (flags & QSE_PIO_READOUT) + { + QSE_CLOSE (handle[3]); + handle[3] = QSE_PIO_HND_NIL; + } + + if (flags & QSE_PIO_READERR) + { + QSE_CLOSE (handle[5]); + handle[5] = QSE_PIO_HND_NIL; + } #else if (flags & QSE_PIO_WRITEIN) @@ -1485,7 +1712,10 @@ create_process: { if (fd != handle[0] && fd != handle[1] && fd != handle[2] && fd != handle[3] && - fd != handle[4] && fd != handle[5]) QSE_CLOSE (fd); + fd != handle[4] && fd != handle[5]) + { + QSE_CLOSE (fd); + } } } @@ -1703,7 +1933,7 @@ oops: #elif defined(__DOS__) /* DOS not multi-processed. can't support pio */ -#elif defined(HAVE_POSIX_SPAWN) +#elif defined(HAVE_POSIX_SPAWN) && !(defined(QSE_SYSCALL0) && defined(SYS_vfork)) if (fa_inited) { posix_spawn_file_actions_destroy (&fa); @@ -1713,6 +1943,11 @@ oops: { 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]); + } #else for (i = minidx; i < maxidx; i++) { diff --git a/qse/lib/cmn/syscall.h b/qse/lib/cmn/syscall.h index eab18ecf..ddba3e3f 100644 --- a/qse/lib/cmn/syscall.h +++ b/qse/lib/cmn/syscall.h @@ -173,6 +173,12 @@ # define QSE_FORK() fork() #endif +#if defined(SYS_vfork) +# define QSE_VFORK() syscall(SYS_vfork) +#else +# define QSE_VFORK() vfork() +#endif + #if defined(SYS_execve) # define QSE_EXECVE(path,argv,envp) syscall(SYS_execve,path,argv,envp) #else @@ -349,4 +355,110 @@ # define QSE_UTIMES(path,t) utimes(path,t) #endif +/* ------------------------------------------------------------------------ */ + +#if defined(__linux) && defined(__GNUC__) && defined(__x86_64) + +#include + +/* +#define QSE_SYSCALL0(ret,num) \ + __asm__ volatile ( \ + "movq %1, %%rax\n\t" \ + "syscall\n": \ + "=&a"(ret): \ + "g"((qse_uint64_t)num): \ + "%rcx", "%r11") + +#define QSE_SYSCALL1(ret,num,arg1) \ + __asm__ volatile ( \ + "movq %1, %%rax\n\t" \ + "movq %2, %%rdi\n\t" \ + "syscall\n": \ + "=&a"(ret): \ + "g"((qse_uint64_t)num), "g"((qse_uint64_t)arg1): \ + "%rdi", "%rcx", "%r11") + +#define QSE_SYSCALL2(ret,num,arg1,arg2) \ + __asm__ volatile ( \ + "movq %1, %%rax\n\t" \ + "movq %2, %%rdi\n\t" \ + "movq %3, %%rsi\n\t" \ + "syscall\n": \ + "=&a"(ret): \ + "g"((qse_uint64_t)num), "g"((qse_uint64_t)arg1), "g"((qse_uint64_t)arg2): \ + "%rdi", "%rsi", "%rcx", "%r11") + +#define QSE_SYSCALL3(ret,num,arg1,arg2,arg3) \ + __asm__ volatile ( \ + "movq %1, %%rax\n\t" \ + "movq %2, %%rdi\n\t" \ + "movq %3, %%rsi\n\t" \ + "movq %4, %%rdx\n\t" \ + "syscall\n": \ + "=&a"(ret): \ + "g"((qse_uint64_t)num), "g"((qse_uint64_t)arg1), "g"((qse_uint64_t)arg2), "g"((qse_uint64_t)arg3): \ + "%rdi", "%rsi", "%rdx", "%rcx", "%r11") +*/ + +#define QSE_SYSCALL0(ret,num) \ + __asm__ volatile ( \ + "syscall\n": \ + "=a"(ret): \ + "a"((qse_uint64_t)num) : \ + "%rcx", "%r11") + +#define QSE_SYSCALL1(ret,num,arg1) \ + __asm__ volatile ( \ + "syscall\n": \ + "=a"(ret): \ + "a"((qse_uint64_t)num), "D"((qse_uint64_t)arg1): \ + "%rcx", "%r11") + +#define QSE_SYSCALL2(ret,num,arg1,arg2) \ + __asm__ volatile ( \ + "syscall\n": \ + "=a"(ret): \ + "a"((qse_uint64_t)num), "D"((qse_uint64_t)arg1), "S"((qse_uint64_t)arg2): \ + "%rcx", "%r11") + +#define QSE_SYSCALL3(ret,num,arg1,arg2,arg3) \ + __asm__ volatile ( \ + "syscall\n": \ + "=a"(ret): \ + "a"((qse_uint64_t)num), "D"((qse_uint64_t)arg1), "S"((qse_uint64_t)arg2), "d"((qse_uint64_t)arg3): \ + "%rcx", "%r11") + +#elif defined(__linux) && defined(__GNUC__) && defined(__i386) + +#include + +#define QSE_SYSCALL0(ret,num) \ + __asm__ volatile ( \ + "int $0x80\n": \ + "=a"(ret): \ + "a"((qse_uint32_t)num)) + +#define QSE_SYSCALL1(ret,num,arg1) \ + __asm__ volatile ( \ + "int $0x80\n": \ + "=a"(ret): \ + "a"((qse_uint32_t)num), "b"((qse_uint32_t)arg1)) + +#define QSE_SYSCALL2(ret,num,arg1,arg2) \ + __asm__ volatile ( \ + "int $0x80\n": \ + "=a"(ret): \ + "a"((qse_uint32_t)num), "b"((qse_uint32_t)arg1), "c"((qse_uint32_t)arg2)) + +#define QSE_SYSCALL3(ret,num,arg1,arg2,arg3) \ + __asm__ volatile ( \ + "int $0x80\n": \ + "=a"(ret): \ + "a"((qse_uint32_t)num), "b"((qse_uint32_t)arg1), "c"((qse_uint32_t)arg2), "d"((qse_uint32_t)arg3)) + +#endif + +/* ------------------------------------------------------------------------ */ + #endif diff --git a/qse/lib/net/httpd-task.c b/qse/lib/net/httpd-task.c index 489ffeef..68277d53 100644 --- a/qse/lib/net/httpd-task.c +++ b/qse/lib/net/httpd-task.c @@ -1572,8 +1572,8 @@ static int cgi_snatch_content ( task = (qse_httpd_task_t*)ctx; cgi = (task_cgi_t*)task->ctx; -if (ptr) qse_printf (QSE_T("!!!SNATCHING [%.*hs]\n"), len, ptr); -else qse_printf (QSE_T("!!!SNATCHING DONE\n")); +if (ptr) qse_printf (QSE_T("!!!CGI SNATCHING [%.*hs]\n"), len, ptr); +else qse_printf (QSE_T("!!!CGI SNATCHING DONE\n")); if (ptr == QSE_NULL) { @@ -1614,7 +1614,7 @@ else qse_printf (QSE_T("!!!SNATCHING DONE\n")); { return -1; } -qse_printf (QSE_T("!!!SNATCHED [%.*hs]\n"), len, ptr); +qse_printf (QSE_T("!!!CGI SNATCHED [%.*hs]\n"), len, ptr); } return 0; @@ -1800,8 +1800,10 @@ done: cgi->env = makecgienv (httpd, client, arg->req, arg->path, content_length); if (cgi->env == QSE_NULL) goto oops; - /* i don't set triggers yet. triggers will be set task_main_cgi(). - * this way, task_main_cgi() is called regardless of data availability */ + /* no triggers yet since the main loop doesn't allow me to set + * triggers in the task initializer. however the main task handler + * will be invoked so long as the client handle is writable by + * the main loop. */ task->ctx = cgi; return 0; @@ -2349,10 +2351,278 @@ qse_httpd_task_t* qse_httpd_entasknph ( /*------------------------------------------------------------------------*/ -#if 0 +typedef struct task_proxy_arg_t task_proxy_arg_t; +struct task_proxy_arg_t +{ + const qse_mchar_t* host; + qse_htre_t* req; + int nph; +}; + typedef struct task_proxy_t task_proxy_t; struct task_proxy_t { + int init_failed; + + const qse_mchar_t* host; + qse_http_version_t version; + int keepalive; /* taken from the request */ + + qse_htrd_t* htrd; + + qse_htre_t* req; /* original request associated with this */ + qse_mbs_t* reqfwdbuf; /* content from the request */ + int reqfwderr; + + qse_mbs_t* res; + qse_mchar_t* res_ptr; + qse_size_t res_left; +}; + +typedef struct proxy_htrd_xtn_t proxy_htrd_xtn_t; +struct proxy_htrd_xtn_t +{ + task_proxy_t* proxy; +}; + +static int proxy_snatch_content ( + qse_htre_t* req, const qse_mchar_t* ptr, qse_size_t len, void* ctx) +{ + qse_httpd_task_t* task; + task_proxy_t* proxy; + + task = (qse_httpd_task_t*)ctx; + proxy = (task_proxy_t*)task->ctx; + +if (ptr) qse_printf (QSE_T("!!!PROXY SNATCHING [%.*hs]\n"), len, ptr); +else qse_printf (QSE_T("!!!PROXY SNATCHING DONE\n")); + + if (ptr == QSE_NULL) + { + /* + * this callback is called with ptr of QSE_NULL + * when the request is completed or discarded. + * and this indicates that there's nothing more to read + * from the client side. this can happen when the end of + * a request is seen or when an error occurs + */ + QSE_ASSERT (len == 0); + + /* mark the there's nothing to read form the client side */ + proxy->req = QSE_NULL; + + /* since there is no more to read from the client side. + * the relay trigger is not needed any more. */ + task->trigger[2].mask = 0; + +#if 0 + if (QSE_MBS_LEN(proxy->reqfwdbuf) > 0 && proxy->pio_inited && + !(task->trigger[1].mask & QSE_HTTPD_TASK_TRIGGER_WRITE)) + { + /* there's nothing more to read from the client side. + * there's something to forward in the forwarding buffer. + * 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 (&proxy->pio, QSE_PIO_IN); + } +#endif + } + else if (!proxy->reqfwderr) + { + /* we can write to the child process if a forwarding error + * didn't occur previously. we store data from the client side + * to the forwaring buffer only if there's no such previous + * error. if an error occurred, we simply drop the data. */ + if (qse_mbs_ncat (proxy->reqfwdbuf, ptr, len) == (qse_size_t)-1) + { + return -1; + } +qse_printf (QSE_T("!!!PROXY SNATCHED [%.*hs]\n"), len, ptr); + } + + return 0; +} + +static int proxy_htrd_peek_request (qse_htrd_t* htrd, qse_htre_t* req) +{ + return -1; +} + +static qse_htrd_recbs_t proxy_htrd_cbs = +{ + proxy_htrd_peek_request, + QSE_NULL /* not needed for proxy */ +}; + +static int task_init_proxy ( + qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) +{ + task_proxy_t* proxy; + task_proxy_arg_t* arg; + qse_size_t content_length; + qse_size_t len; + const qse_mchar_t* ptr; + + proxy = (task_proxy_t*)qse_httpd_gettaskxtn (httpd, task); + arg = (task_proxy_arg_t*)task->ctx; + +/* TODO: can content length be a different type??? + * maybe qse_uintmax_t.... it thinks the data size can be larger than the max pointer size + * qse_htre_t and qse_htrd_t also needs changes to support it + */ + + QSE_MEMSET (proxy, 0, QSE_SIZEOF(*proxy)); + qse_mbscpy ((qse_mchar_t*)(proxy + 1), arg->host); + proxy->host = (qse_mchar_t*)(proxy + 1); + proxy->version = *qse_htre_getversion(arg->req); + proxy->keepalive = arg->req->attr.keepalive; + + if (arg->req->state & QSE_HTRE_DISCARDED) + { +qse_printf (QSE_T("XXXXXXXXXXXXXXXXX\n")); + content_length = 0; + goto done; + } + + len = qse_htre_getcontentlen(arg->req); + if ((arg->req->state & QSE_HTRE_COMPLETED) && len <= 0) + { +qse_printf (QSE_T("YYYYYYYYYYYYYYYYy\n")); + /* the content part is completed and no content + * in the content buffer. there is nothing to forward */ + content_length = 0; + goto done; + } + + if (!(arg->req->state & QSE_HTRE_COMPLETED) && + !arg->req->attr.content_length_set) + { +qse_printf (QSE_T("ZZZZZZZZZZZZZZZ\n")); + /* if the request is not completed and doesn't have + * content-length set, it's not really possible to + * pass the content. this function, however, allows + * such a request to entask a proxy script dropping the + * content */ + qse_htre_discardcontent (arg->req); + content_length = 0; + } + else + { + /* create a buffer to hold request content from the client + * and copy content received already */ + proxy->reqfwdbuf = qse_mbs_open (httpd->mmgr, 0, (len < 512? 512: len)); + if (proxy->reqfwdbuf == QSE_NULL) goto oops; + + ptr = qse_htre_getcontentptr(arg->req); + if (qse_mbs_ncpy (proxy->reqfwdbuf, ptr, len) == (qse_size_t)-1) + { + qse_mbs_close (proxy->reqfwdbuf); + proxy->reqfwdbuf = QSE_NULL; + goto oops; + } + + if (arg->req->state & QSE_HTRE_COMPLETED) + { + /* no furthur forwarding is needed. + * even a chunked request entaksed when completed + * should reach here. if content-length is set + * the length should match len. */ + QSE_ASSERT (len > 0); + QSE_ASSERT (!arg->req->attr.content_length_set || + (arg->req->attr.content_length_set && arg->req->attr.content_length == len)); +qse_printf (QSE_T("HHHHHHHHHHHHHHHHhh %d\n"), (int)len); + content_length = len; + } + else + { + /* CGI entasking is invoked probably from the peek handler + * that was triggered after the request header is received. + * you can know this because the request is not completed. + * In this case, arrange to forward content + * bypassing the buffer in the request object itself. */ + +/* TODO: callback chain instead of a single pointer??? + if the request is already set up with a callback, something will go wrong. +*/ + /* 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. */ + proxy->req = arg->req; + qse_htre_setconcb (proxy->req, proxy_snatch_content, task); + + QSE_ASSERT (arg->req->attr.content_length_set); + content_length = arg->req->attr.content_length; +qse_printf (QSE_T("TTTTTTTTTTTTTTTTTTTT %d\n"), (int)content_length); + } + } + +done: + /* no triggers yet since the main loop doesn't allow me to set + * triggers in the task initializer. however the main task handler + * will be invoked so long as the client handle is writable by + * the main loop. */ + + task->ctx = proxy; + return 0; + +oops: + /* since a new task can't be added in the initializer, + * i mark that initialization failed and let task_main_proxy() + * add an error task */ + proxy->init_failed = 1; + task->ctx = proxy; + return 0; +} + +static void task_fini_proxy ( + qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) +{ +} + +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; + int http_errnum = 500; + + if (proxy->init_failed) goto oops; + + 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); + xtn->proxy = proxy; + qse_htrd_setrecbs (proxy->htrd, &proxy_htrd_cbs); + qse_htrd_setoption ( + proxy->htrd, + QSE_HTRD_SKIPINITIALLINE | + QSE_HTRD_PEEKONLY | + QSE_HTRD_REQUEST + ); + + proxy->res = qse_mbs_open (httpd->mmgr, 0, 256); + if (proxy->res == QSE_NULL) goto oops; + +/////////////////////// + + return 1; + +oops: + if (proxy->res) + { + qse_mbs_close (proxy->res); + proxy->res = QSE_NULL; + } + if (proxy->htrd) + { + qse_htrd_close (proxy->htrd); + proxy->htrd = QSE_NULL; + } + + return (entask_error ( + httpd, client, task, http_errnum, + &proxy->version, proxy->keepalive) == QSE_NULL)? -1: 0; } qse_httpd_task_t* qse_httpd_entaskproxy ( @@ -2362,9 +2632,23 @@ qse_httpd_task_t* qse_httpd_entaskproxy ( const qse_mchar_t* host, const qse_htre_t* req) { - + qse_httpd_task_t task; + task_proxy_arg_t arg; + + arg.host = host; + arg.req = req; + + QSE_MEMSET (&task, 0, QSE_SIZEOF(task)); + task.init = task_init_proxy; + task.fini = task_fini_proxy; + task.main = task_main_proxy; + task.ctx = &arg; + + return qse_httpd_entask ( + httpd, client, pred, &task, + QSE_SIZEOF(task_proxy_t) + ((qse_mbslen(host) + 1) * QSE_SIZEOF(*host)) + ); } -#endif /*------------------------------------------------------------------------*/ diff --git a/qse/lib/net/httpd.c b/qse/lib/net/httpd.c index e78593cf..6b1d2d8d 100644 --- a/qse/lib/net/httpd.c +++ b/qse/lib/net/httpd.c @@ -775,10 +775,6 @@ static int invoke_client_task ( } } - /* locate an active client to the tail of the client list */ - qse_gettime (&client->last_active); /* TODO: error check??? */ - move_client_to_tail (httpd, client); - n = task->main (httpd, client, task); qse_printf (QSE_T("task returend %d\n"), n); if (n <= -1) return -1; @@ -943,6 +939,10 @@ static int perform_client_task ( } else { + /* locate an active client to the tail of the client list */ + qse_gettime (&client->last_active); /* TODO: error check??? */ + move_client_to_tail (httpd, client); + if (invoke_client_task (httpd, client, handle, mask) <= -1) goto oops; } diff --git a/qse/samples/net/http01.c b/qse/samples/net/http01.c index 6be06c74..447b7b38 100644 --- a/qse/samples/net/http01.c +++ b/qse/samples/net/http01.c @@ -430,10 +430,6 @@ struct mux_t struct mux_ev_t* ptr; qse_size_t capa; } mev; - -#if 0 - qse_fma_t* fma; -#endif }; #define MUX_EV_ALIGN 64 @@ -455,13 +451,6 @@ static void* mux_open (qse_httpd_t* httpd) return QSE_NULL; } -#if 0 - mux->fma = qse_fma_open (qse_getmmgr(httpd), QSE_NULL); - if (mux->fma == QSE_NULL) - { - } -#endif - return mux; } @@ -486,28 +475,22 @@ static int mux_addhnd ( if (mask & QSE_HTTPD_MUX_READ) ev.events |= EPOLLIN; if (mask & QSE_HTTPD_MUX_WRITE) ev.events |= EPOLLOUT; - if (ev.events == 0) + if (ev.events == 0 || handle.i <= -1) { qse_httpd_seterrnum (httpd, QSE_HTTPD_EINVAL); return -1; } -#if 0 - mev = qse_httpd_allocmem (httpd, QSE_SIZEOF(*mev)); - if (mev == QSE_NULL) return -1; -#endif - if (handle.i >= mux->mev.capa) { struct mux_ev_t* tmp; qse_size_t tmpcapa; - - tmpcapa = (((handle.i + MUX_EV_ALIGN - 1) / MUX_EV_ALIGN) * MUX_EV_ALIGN) + 1; + + tmpcapa = (((handle.i + MUX_EV_ALIGN) / MUX_EV_ALIGN) * MUX_EV_ALIGN); -/* TODO: allocate this from fma ... */ tmp = qse_httpd_reallocmem ( httpd, mux->mev.ptr, - QSE_SIZEOF(*mux->mev.ptr) * tmpcapa); /* TODO: round up handle.i ... */ + QSE_SIZEOF(*mux->mev.ptr) * tmpcapa); if (tmp == QSE_NULL) return -1; mux->mev.ptr = tmp; @@ -521,11 +504,7 @@ static int mux_addhnd ( tmp = qse_httpd_reallocmem ( httpd, mux->ee.ptr, QSE_SIZEOF(*mux->ee.ptr) * (mux->ee.capa + 1) * 2); - if (tmp == QSE_NULL) - { - /*qse_httpd_freemem (httpd, mev);*/ - return -1; - } + if (tmp == QSE_NULL) return -1; mux->ee.ptr = tmp; mux->ee.capa = (mux->ee.capa + 1) * 2; @@ -544,7 +523,6 @@ static int mux_addhnd ( { /* don't rollback ee.ptr */ qse_httpd_seterrnum (httpd, syserr_to_errnum(errno)); - /*qse_httpd_freemem (httpd, mev);*/ return -1; } @@ -556,8 +534,6 @@ static int mux_delhnd (qse_httpd_t* httpd, void* vmux, qse_ubi_t handle) { struct mux_t* mux = (struct mux_t*)vmux; -/* TODO: delete mev associated with handle.i */ - if (epoll_ctl (mux->fd, EPOLL_CTL_DEL, handle.i, QSE_NULL) <= -1) { qse_httpd_seterrnum (httpd, syserr_to_errnum(errno)); @@ -597,8 +573,6 @@ static int mux_poll (qse_httpd_t* httpd, void* vmux, qse_ntime_t timeout) } mev->cbfun (httpd, mux, mev->handle, mask, mev->cbarg); - -//if (cbfun fails and the client is deleted???) other pending events should also be dropped??? } return 0; } @@ -971,7 +945,6 @@ if (qse_htre_getcontentlen(req) > 0) qse_printf (QSE_T("CONTENT after discard = [%.*S]\n"), (int)qse_htre_getcontentlen(req), qse_htre_getcontentptr(req)); } - if (method == QSE_HTTP_GET || method == QSE_HTTP_POST) { const qse_mchar_t* qpath = qse_htre_getqpathptr(req); @@ -1089,6 +1062,80 @@ oops: return -1; } +#if 0 +static int proxy_request ( + qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req, int peek) +{ + const qse_mchar_t* qpath; + + qpath = qse_htre_qpathptr (eq); + if (qpath[0] == QSE_MT('/')) + { + host = qse_htre_getheaderval (req, QSE_MT("Host")); + if (host == QSE_NULL) + { +qse_printf (QSE_T("Host not included....\n")); + goto oops; + } + } + else + { + const qse_mchar_t* host; + qse_parseuri (); + } + + +#if 0 + if (peek) + { + if (req->attr.expect && + (req->version.major > 1 || + (req->version.major == 1 && req->version.minor >= 1)) && + !content_received) + { +/* TODO: check method.... */ + /* "expect" in the header, version 1.1 or higher, + * and no content received yet */ + + if (qse_mbscasecmp(req->attr.expect, QSE_MT("100-continue")) != 0) + { + if (qse_httpd_entaskerror ( + httpd, client, QSE_NULL, 417, req) == QSE_NULL) return -1; + if (qse_httpd_entaskdisconnect ( + httpd, client, QSE_NULL) == QSE_NULL) return -1; + } + else + { + /* TODO: determine if to return 100-continue or other errors */ + if (qse_httpd_entaskcontinue ( + httpd, client, QSE_NULL, req) == QSE_NULL) return -1; + } + } + } +#endif + +if (qse_htre_getqparamlen(req) > 0) qse_printf (QSE_T("PARAMS ==> [%hs]\n"), qse_htre_getqparamptr(req)); + + + task = qse_httpd_entaskproxy (httpd, client, QSE_NULL, qpath, req); + if (task == QSE_NULL) goto oops; + + if (!req->attr.keepalive) + { + if (!peek) + { + task = qse_httpd_entaskdisconnect (httpd, client, QSE_NULL); + if (task == QSE_NULL) goto oops; + } + } + + return 0; + +oops: + return -1; +} +#endif + static int peek_request ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req) {