added qse_httpd_entasknph()

enhanced qse_pio_t with posix_spawn()
This commit is contained in:
hyung-hwan 2012-02-01 14:32:34 +00:00
parent 7a7127b89f
commit a782229fa2
7 changed files with 591 additions and 216 deletions

26
qse/configure vendored
View File

@ -16000,7 +16000,20 @@ fi
done
for ac_header in time.h sys/time.h utime.h sys/resource.h sys/syscall.h sys/sendfile.h
for ac_header in time.h sys/time.h utime.h spawn.h
do :
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
if eval test \"x\$"$as_ac_Header"\" = x"yes"; then :
cat >>confdefs.h <<_ACEOF
#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1
_ACEOF
fi
done
for ac_header in sys/resource.h sys/syscall.h sys/sendfile.h
do :
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh`
ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
@ -16164,6 +16177,17 @@ _ACEOF
fi
done
for ac_func in 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 :
cat >>confdefs.h <<_ACEOF
#define HAVE_POSIX_SPAWN 1
_ACEOF
fi
done
OLDLIBS="$LIBS"
LIBS="$LIBM $LIBS"

View File

@ -79,7 +79,8 @@ AC_SUBST(LIBM, $LIBM)
dnl check header files.
AC_HEADER_STDC
AC_CHECK_HEADERS([stddef.h wchar.h wctype.h errno.h signal.h])
AC_CHECK_HEADERS([time.h sys/time.h utime.h sys/resource.h sys/syscall.h sys/sendfile.h])
AC_CHECK_HEADERS([time.h sys/time.h utime.h spawn.h])
AC_CHECK_HEADERS([sys/resource.h sys/syscall.h sys/sendfile.h])
AC_CHECK_HEADERS([execinfo.h])
dnl check data types
@ -101,6 +102,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])
OLDLIBS="$LIBS"
LIBS="$LIBM $LIBS"

View File

@ -154,6 +154,9 @@
/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
#undef HAVE_NDIR_H
/* Define to 1 if you have the `posix_spawn' function. */
#undef HAVE_POSIX_SPAWN
/* Define to 1 if you have the `pow' function. */
#undef HAVE_POW
@ -205,6 +208,9 @@
/* Define it socklen_t typedef is in sys/socket.h. */
#undef HAVE_SOCKLEN_T
/* Define to 1 if you have the <spawn.h> header file. */
#undef HAVE_SPAWN_H
/* Define to 1 if you have the `sqrt' function. */
#undef HAVE_SQRT

View File

@ -232,6 +232,14 @@ qse_httpd_task_t* qse_httpd_entaskcgi (
const qse_htre_t* req
);
qse_httpd_task_t* qse_httpd_entasknph (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
const qse_httpd_task_t* pred,
const qse_mchar_t* path,
const qse_htre_t* req
);
/* -------------------------------------------- */
void* qse_httpd_allocmem (

View File

@ -36,6 +36,9 @@
# include "syscall.h"
# include <fcntl.h>
# include <sys/wait.h>
# if defined(HAVE_SPAWN_H)
# include <spawn.h>
# endif
#endif
QSE_IMPLEMENT_COMMON_FUNCTIONS (pio)
@ -67,6 +70,254 @@ void qse_pio_close (qse_pio_t* pio)
QSE_MMGR_FREE (pio->mmgr, pio);
}
#if !defined(_WIN32) && !defined(__OS2__) && !defined(__DOS__)
struct param_t
{
qse_mchar_t* mcmd;
qse_mchar_t* fixed_argv[4];
qse_mchar_t** argv;
#if defined(QSE_CHAR_IS_MCHAR)
/* nothign extra */
#else
qse_mchar_t fixed_mbuf[64];
#endif
};
typedef struct param_t param_t;
static void free_param (qse_pio_t* pio, param_t* param)
{
if (param->argv && param->argv != param->fixed_argv)
QSE_MMGR_FREE (pio->mmgr, param->argv);
if (param->mcmd) QSE_MMGR_FREE (pio->mmgr, param->mcmd);
}
static int make_param (
qse_pio_t* pio, const qse_char_t* cmd, int flags, param_t* param)
{
#if defined(QSE_CHAR_IS_MCHAR)
qse_mchar_t* mcmd = QSE_NULL;
#else
qse_mchar_t* mcmd = QSE_NULL;
qse_char_t* wcmd = QSE_NULL;
#endif
int fcnt = 0;
QSE_MEMSET (param, 0, QSE_SIZEOF(*param));
#if defined(QSE_CHAR_IS_MCHAR)
if (flags & QSE_PIO_SHELL) mcmd = (qse_char_t*)cmd;
else
{
mcmd = qse_strdup (cmd, pio->mmgr);
if (mcmd == QSE_NULL)
{
pio->errnum = QSE_PIO_ENOMEM;
goto oops;
}
fcnt = qse_strspl (mcmd, QSE_T(""),
QSE_T('\"'), QSE_T('\"'), QSE_T('\\'));
if (fcnt <= 0)
{
/* no field or an error */
pio->errnum = QSE_PIO_EINVAL;
goto oops;
}
}
#else
if (flags & QSE_PIO_MBSCMD)
{
/* the cmd is flagged to be of qse_mchar_t
* while the default character type is qse_wchar_t. */
if (flags & QSE_PIO_SHELL) mcmd = (qse_mchar_t*)cmd;
else
{
mcmd = qse_mbsdup ((const qse_mchar_t*)cmd, pio->mmgr);
if (mcmd == QSE_NULL)
{
pio->errnum = QSE_PIO_ENOMEM;
goto oops;
}
fcnt = qse_mbsspl (mcmd, QSE_MT(""),
QSE_MT('\"'), QSE_MT('\"'), QSE_MT('\\'));
if (fcnt <= 0)
{
/* no field or an error */
pio->errnum = QSE_PIO_EINVAL;
goto oops;
}
}
}
else
{
qse_size_t n, mn, wl;
if (flags & QSE_PIO_SHELL)
{
if (qse_wcstombs (cmd, &wl, QSE_NULL, &mn) <= -1)
{
/* cmd has illegal sequence */
pio->errnum = QSE_PIO_EINVAL;
goto oops;
}
}
else
{
wcmd = qse_strdup (cmd, pio->mmgr);
if (wcmd == QSE_NULL)
{
pio->errnum = QSE_PIO_ENOMEM;
goto oops;
}
fcnt = qse_strspl (wcmd, QSE_T(""),
QSE_T('\"'), QSE_T('\"'), QSE_T('\\'));
if (fcnt <= 0)
{
/* no field or an error */
pio->errnum = QSE_PIO_EINVAL;
goto oops;
}
/* calculate the length of the string after splitting */
for (wl = 0, n = fcnt; n > 0; )
{
if (wcmd[wl++] == QSE_T('\0')) n--;
}
if (qse_wcsntombsn (wcmd, &wl, QSE_NULL, &mn) <= -1)
{
pio->errnum = QSE_PIO_EINVAL;
goto oops;
}
}
/* prepare to reserve 1 more slot for the terminating '\0'
* by incrementing mn by 1. */
mn = mn + 1;
if (mn <= QSE_COUNTOF(param->fixed_mbuf))
{
mcmd = param->fixed_mbuf;
mn = QSE_COUNTOF(param->fixed_mbuf);
}
else
{
mcmd = QSE_MMGR_ALLOC (pio->mmgr, mn * QSE_SIZEOF(*mcmd));
if (mcmd == QSE_NULL)
{
pio->errnum = QSE_PIO_ENOMEM;
goto oops;
}
}
if (flags & QSE_PIO_SHELL)
{
QSE_ASSERT (wcmd == QSE_NULL);
/* qse_wcstombs() should succeed as
* it was successful above */
qse_wcstombs (cmd, &wl, mcmd, &mn);
/* qse_wcstombs() null-terminate mcmd */
}
else
{
QSE_ASSERT (wcmd != QSE_NULL);
/* qse_wcsntombsn() should succeed as
* it was was successful above */
qse_wcsntombsn (wcmd, &wl, mcmd, &mn);
/* qse_wcsntombsn() doesn't null-terminate mcmd */
mcmd[mn] = QSE_MT('\0');
QSE_MMGR_FREE (pio->mmgr, wcmd);
wcmd = QSE_NULL;
}
}
#endif
if (flags & QSE_PIO_SHELL)
{
param->argv = param->fixed_argv;
param->argv[0] = QSE_MT("/bin/sh");
param->argv[1] = QSE_MT("-c");
param->argv[2] = mcmd;
param->argv[3] = QSE_NULL;
}
else
{
int i;
qse_mchar_t** argv;
qse_mchar_t* mcmdptr;
param->argv = QSE_MMGR_ALLOC (
pio->mmgr, (fcnt + 1) * QSE_SIZEOF(argv[0]));
if (param->argv == QSE_NULL)
{
pio->errnum = QSE_PIO_ENOMEM;
goto oops;
}
mcmdptr = mcmd;
for (i = 0; i < fcnt; i++)
{
param->argv[i] = mcmdptr;
while (*mcmdptr != QSE_MT('\0')) mcmdptr++;
mcmdptr++;
}
param->argv[i] = QSE_NULL;
}
#if defined(QSE_CHAR_IS_MCHAR)
if (mcmd && mcmd != cmd) param->mcmd = mcmd;
#else
if (mcmd && mcmd != cmd && mcmd != param->fixed_mbuf) param->mcmd = mcmd;
#endif
return 0;
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 (wcmd) QSE_MMGR_FREE (pio->mmgr, wcmd);
#endif
return -1;
}
static QSE_INLINE int is_fd_valid (int fd)
{
return fcntl (fd, F_GETFL) != -1 || errno != EBADF;
}
static int get_highest_fd (void)
{
/* TODO: consider if reading from /proc/self/fd is
* a better idea. */
struct rlimit rlim;
int fd = -1;
#if defined(F_MAXFD)
fd = fcntl (0, F_MAXFD);
#endif
if (fd == -1)
{
if (QSE_GETRLIMIT (RLIMIT_NOFILE, &rlim) <= -1 ||
rlim.rlim_max == RLIM_INFINITY)
{
#if defined(HAVE_SYSCONF)
fd = sysconf (_SC_OPEN_MAX);
#endif
}
else fd = rlim.rlim_max;
}
if (fd == -1) fd = 1024; /* fallback */
return fd;
}
#endif
int qse_pio_init (
qse_pio_t* pio, qse_mmgr_t* mmgr, const qse_char_t* cmd,
qse_env_t* env, int flags)
@ -113,8 +364,17 @@ int qse_pio_init (
/* DOS not multi-processed. can't support pio */
#elif defined(HAVE_POSIX_SPAWN)
posix_spawn_file_actions_t fa;
int fa_inited = 0;
int spawn_ret;
qse_pio_pid_t pid;
param_t param;
extern char** environ;
#else
qse_pio_pid_t pid;
param_t param;
extern char** environ;
#endif
QSE_MEMSET (pio, 0, QSE_SIZEOF(*pio));
@ -618,6 +878,137 @@ int qse_pio_init (
/* DOS not multi-processed. can't support pio */
return -1;
#elif defined(HAVE_POSIX_SPAWN)
if (flags & QSE_PIO_WRITEIN)
{
if (QSE_PIPE(&handle[0]) <= -1) goto oops;
minidx = 0; maxidx = 1;
}
if (flags & QSE_PIO_READOUT)
{
if (QSE_PIPE(&handle[2]) <= -1) goto oops;
if (minidx == -1) minidx = 2;
maxidx = 3;
}
if (flags & QSE_PIO_READERR)
{
if (QSE_PIPE(&handle[4]) <= -1) goto oops;
if (minidx == -1) minidx = 4;
maxidx = 5;
}
if (maxidx == -1)
{
pio->errnum = QSE_PIO_EINVAL;
goto oops;
}
if (posix_spawn_file_actions_init (&fa) != 0) goto oops;
fa_inited = 1;
if (flags & QSE_PIO_WRITEIN)
{
/* child should read */
if (posix_spawn_file_actions_addclose (&fa, handle[1]) != 0) goto oops;
if (posix_spawn_file_actions_adddup2 (&fa, handle[0], 0) != 0) goto oops;
if (posix_spawn_file_actions_addclose (&fa, handle[0]) != 0) goto oops;
}
if (flags & QSE_PIO_READOUT)
{
/* child should write */
if (posix_spawn_file_actions_addclose (&fa, handle[2]) != 0) goto oops;
if (posix_spawn_file_actions_adddup2 (&fa, handle[3], 1) != 0) goto oops;
if ((flags & QSE_PIO_ERRTOOUT) &&
posix_spawn_file_actions_adddup2 (&fa, handle[3], 2) != 0) goto oops;
if (posix_spawn_file_actions_addclose (&fa, handle[3]) != 0) goto oops;
}
if (flags & QSE_PIO_READERR)
{
/* child should write */
if (posix_spawn_file_actions_addclose (&fa, handle[4]) != 0) goto oops;
if (posix_spawn_file_actions_adddup2 (&fa, handle[5], 2) != 0) goto oops;
if ((flags & QSE_PIO_OUTTOERR) &&
posix_spawn_file_actions_adddup2 (&fa, handle[5], 1) != 0) goto oops;
if (posix_spawn_file_actions_addclose (&fa, handle[5]) != 0) goto oops;
}
{
int oflags = O_RDWR;
#if defined(O_LARGEFILE)
oflags |= O_LARGEFILE;
#endif
if ((flags & QSE_PIO_INTONUL) &&
posix_spawn_file_actions_addopen (&fa, 0, QSE_MT("/dev/null"), oflags, 0) != 0) goto oops;
if ((flags & QSE_PIO_OUTTONUL) &&
posix_spawn_file_actions_addopen (&fa, 1, QSE_MT("/dev/null"), oflags, 0) != 0) goto oops;
if ((flags & QSE_PIO_ERRTONUL) &&
posix_spawn_file_actions_addopen (&fa, 2, QSE_MT("/dev/null"), oflags, 0) != 0) goto oops;
}
/* there remains the chance of race condition that
* 0, 1, 2 can be closed between addclose() and posix_spawn().
* so checking the file descriptors with is_fd_valid() is
* just on the best-effort basis.
*/
if ((flags & QSE_PIO_DROPIN) && is_fd_valid(0) &&
posix_spawn_file_actions_addclose (&fa, 0) != 0) goto oops;
if ((flags & QSE_PIO_DROPOUT) && is_fd_valid(1) &&
posix_spawn_file_actions_addclose (&fa, 1) != 0) goto oops;
if ((flags & QSE_PIO_DROPERR) && is_fd_valid(2) &&
posix_spawn_file_actions_addclose (&fa, 2) != 0) goto oops;
{
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 (make_param (pio, cmd, flags, &param) <= -1) goto oops;
spawn_ret = posix_spawn(
&pid, param.argv[0], &fa, QSE_NULL, param.argv,
(env? qse_env_getarr(env): environ));
free_param (pio, &param);
if (fa_inited)
{
posix_spawn_file_actions_destroy (&fa);
fa_inited = 0;
}
if (spawn_ret != 0) goto oops;
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)
@ -652,31 +1043,10 @@ int qse_pio_init (
if (pid == 0)
{
/* child */
qse_pio_hnd_t devnull = -1;
int fd = get_highest_fd ();
qse_pio_hnd_t devnull;
qse_mchar_t* mcmd;
int fcnt = 0;
#ifndef QSE_CHAR_IS_MCHAR
qse_mchar_t buf[64];
#endif
/* TODO: consider if reading from /proc/self/fd is
* a better idea. */
struct rlimit rlim;
int fd;
if (QSE_GETRLIMIT (RLIMIT_NOFILE, &rlim) <= -1 ||
rlim.rlim_max == RLIM_INFINITY)
{
#ifdef HAVE_SYSCONF
fd = sysconf (_SC_OPEN_MAX);
if (fd <= 0)
#endif
fd = 1024;
}
else fd = rlim.rlim_max;
/* don't close stdin/out/err and the pipes */
while (--fd > 2)
{
if (fd != handle[0] &&
@ -733,7 +1103,7 @@ int qse_pio_init (
(flags & QSE_PIO_OUTTONUL) ||
(flags & QSE_PIO_ERRTONUL))
{
#ifdef O_LARGEFILE
#if defined(O_LARGEFILE)
devnull = QSE_OPEN (QSE_MT("/dev/null"), O_RDWR|O_LARGEFILE, 0);
#else
devnull = QSE_OPEN (QSE_MT("/dev/null"), O_RDWR, 0);
@ -750,162 +1120,22 @@ int qse_pio_init (
if ((flags & QSE_PIO_INTONUL) ||
(flags & QSE_PIO_OUTTONUL) ||
(flags & QSE_PIO_ERRTONUL)) QSE_CLOSE (devnull);
(flags & QSE_PIO_ERRTONUL))
{
QSE_CLOSE (devnull);
devnull = -1;
}
if (flags & QSE_PIO_DROPIN) QSE_CLOSE(0);
if (flags & QSE_PIO_DROPOUT) QSE_CLOSE(1);
if (flags & QSE_PIO_DROPERR) QSE_CLOSE(2);
#ifdef QSE_CHAR_IS_MCHAR
if (flags & QSE_PIO_SHELL) mcmd = (qse_char_t*)cmd;
else
{
mcmd = qse_strdup (cmd, pio->mmgr);
if (mcmd == QSE_NULL) goto child_oops;
fcnt = qse_strspl (mcmd, QSE_T(""),
QSE_T('\"'), QSE_T('\"'), QSE_T('\\'));
if (fcnt <= 0)
{
/* no field or an error */
goto child_oops;
}
}
#else
if (flags & QSE_PIO_MBSCMD)
{
/* the cmd is flagged to be of qse_mchar_t
* while the default character type is qse_wchar_t. */
if (flags & QSE_PIO_SHELL) mcmd = (qse_mchar_t*)cmd;
else
{
mcmd = qse_mbsdup ((const qse_mchar_t*)cmd, pio->mmgr);
if (mcmd == QSE_NULL) goto child_oops;
fcnt = qse_mbsspl (mcmd, QSE_MT(""),
QSE_MT('\"'), QSE_MT('\"'), QSE_MT('\\'));
if (fcnt <= 0)
{
/* no field or an error */
goto child_oops;
}
}
}
else
{
qse_size_t n, mn, wl;
qse_char_t* wcmd = QSE_NULL;
if (flags & QSE_PIO_SHELL)
{
if (qse_wcstombs (cmd, &wl, QSE_NULL, &mn) <= -1)
{
/* cmd has illegal sequence */
goto child_oops;
}
}
else
{
wcmd = qse_strdup (cmd, pio->mmgr);
if (wcmd == QSE_NULL) goto child_oops;
fcnt = qse_strspl (wcmd, QSE_T(""),
QSE_T('\"'), QSE_T('\"'), QSE_T('\\'));
if (fcnt <= 0)
{
/* no field or an error */
goto child_oops;
}
/* calculate the length of the string after splitting */
for (wl = 0, n = fcnt; n > 0; )
{
if (wcmd[wl++] == QSE_T('\0')) n--;
}
#if 0
n = qse_wcsntombsnlen (wcmd, wl, &mn);
if (n != wl) goto child_oops;
#endif
if (qse_wcsntombsn (wcmd, &wl, QSE_NULL, &mn) <= -1) goto child_oops;
}
/* prepare to reserve 1 more slot for the terminating '\0'
* by incrementing mn by 1. */
mn = mn + 1;
if (mn <= QSE_COUNTOF(buf))
{
mcmd = buf;
mn = QSE_COUNTOF(buf);
}
else
{
mcmd = QSE_MMGR_ALLOC (
pio->mmgr, mn*QSE_SIZEOF(*mcmd));
if (mcmd == QSE_NULL) goto child_oops;
}
if (flags & QSE_PIO_SHELL)
{
/* qse_wcstombs() should succeed as
* it was successful above */
qse_wcstombs (cmd, &wl, mcmd, &mn);
/* qse_wcstombs() null-terminate mcmd */
}
else
{
QSE_ASSERT (wcmd != QSE_NULL);
/* qse_wcsntombsn() should succeed as
* it was was successful above */
qse_wcsntombsn (wcmd, &wl, mcmd, &mn);
/* qse_wcsntombsn() doesn't null-terminate mcmd */
mcmd[mn] = QSE_MT('\0');
}
}
#endif
if (flags & QSE_PIO_SHELL)
{
const qse_mchar_t* argv[4];
extern char** environ;
argv[0] = QSE_MT("/bin/sh");
argv[1] = QSE_MT("-c");
argv[2] = mcmd;
argv[3] = QSE_NULL;
QSE_EXECVE (
QSE_MT("/bin/sh"),
(qse_mchar_t*const*)argv,
(env? qse_env_getarr(env): environ)
);
}
else
{
int i;
qse_mchar_t** argv;
extern char** environ;
argv = QSE_MMGR_ALLOC (pio->mmgr, (fcnt+1)*QSE_SIZEOF(argv[0]));
if (argv == QSE_NULL) goto child_oops;
for (i = 0; i < fcnt; i++)
{
argv[i] = mcmd;
while (*mcmd != QSE_MT('\0')) mcmd++;
mcmd++;
}
argv[i] = QSE_NULL;
QSE_EXECVE (argv[0], argv, (env? qse_env_getarr(env): environ));
/* this won't be reached if execve succeeds */
QSE_MMGR_FREE (pio->mmgr, argv);
}
if (make_param (pio, cmd, flags, &param) <= -1) goto child_oops;
QSE_EXECVE (param.argv[0], param.argv, (env? qse_env_getarr(env): environ));
free_param (pio, &param);
child_oops:
if (devnull >= 0) QSE_CLOSE (devnull);
QSE_EXIT (128);
}
@ -995,8 +1225,7 @@ int qse_pio_init (
return 0;
oops:
if (pio->errnum == QSE_PIO_ENOERR)
pio->errnum = QSE_PIO_ESUBSYS;
if (pio->errnum == QSE_PIO_ENOERR) pio->errnum = QSE_PIO_ESUBSYS;
#if defined(_WIN32)
if (windevnul != INVALID_HANDLE_VALUE) CloseHandle (windevnul);
@ -1036,7 +1265,16 @@ oops:
#elif defined(__DOS__)
/* DOS not multi-processed. can't support pio */
#elif defined(HAVE_POSIX_SPAWN)
if (fa_inited)
{
posix_spawn_file_actions_destroy (&fa);
fa_inited = 0;
}
for (i = minidx; i < maxidx; i++)
{
if (handle[i] != QSE_PIO_HND_NIL) QSE_CLOSE (handle[i]);
}
#else
for (i = minidx; i < maxidx; i++)
{

View File

@ -1210,6 +1210,7 @@ struct task_cgi_arg_t
{
const qse_mchar_t* path;
const qse_htre_t* req;
int nph;
};
typedef struct task_cgi_t task_cgi_t;
@ -1220,6 +1221,7 @@ struct task_cgi_t
const qse_mchar_t* path;
qse_http_version_t version;
int keepalive; /* taken from the request */
int nph;
qse_env_t* env;
qse_pio_t* pio;
@ -1254,7 +1256,7 @@ int walk_cgi_headers (qse_htre_t* req, const qse_mchar_t* key, const qse_mchar_t
{
task_cgi_t* cgi = (task_cgi_t*)ctx;
if (qse_mbscmp (key, "Status") != 0)
if (qse_mbscmp (key, QSE_MT("Status")) != 0)
{
if (qse_mbs_cat (cgi->res, key) == (qse_size_t)-1) return -1;
if (qse_mbs_cat (cgi->res, QSE_MT(": ")) == (qse_size_t)-1) return -1;
@ -1275,6 +1277,7 @@ static int cgi_htrd_handle_request (qse_htrd_t* htrd, qse_htre_t* req)
QSE_ASSERT (req->attr.hurried);
status = qse_htre_getheaderval (req, QSE_MT("Status"));
if (status)
{
qse_mchar_t buf[128];
@ -1282,8 +1285,7 @@ static int cgi_htrd_handle_request (qse_htrd_t* htrd, qse_htre_t* req)
qse_mchar_t* endptr;
/* TODO: check the syntax of status value??? */
QSE_MSTRTONUM (nstatus,status,&endptr,10);
QSE_MSTRTONUM (nstatus, status, &endptr, 10);
snprintf (buf, QSE_COUNTOF(buf),
QSE_MT("HTTP/%d.%d %d "),
@ -1304,12 +1306,29 @@ static int cgi_htrd_handle_request (qse_htrd_t* htrd, qse_htre_t* req)
}
else
{
qse_mchar_t* location;
qse_mchar_t buf[128];
snprintf (buf, QSE_COUNTOF(buf),
QSE_MT("HTTP/%d.%d 200 OK\r\n"),
cgi->version.major, cgi->version.minor
);
if (qse_mbs_cat (cgi->res, buf) == (qse_size_t)-1) return -1;
location = qse_htre_getheaderval (req, QSE_MT("Location"));
if (location)
{
snprintf (buf, QSE_COUNTOF(buf),
QSE_MT("HTTP/%d.%d 301 Moved Permanently\r\n"),
cgi->version.major, cgi->version.minor
);
if (qse_mbs_cat (cgi->res, buf) == (qse_size_t)-1) return -1;
/* the actual Location hearer is added by
* qse_htre_walkheaders() below */
}
else
{
snprintf (buf, QSE_COUNTOF(buf),
QSE_MT("HTTP/%d.%d 200 OK\r\n"),
cgi->version.major, cgi->version.minor
);
if (qse_mbs_cat (cgi->res, buf) == (qse_size_t)-1) return -1;
}
}
if (req->attr.content_length_set)
@ -1319,20 +1338,29 @@ static int cgi_htrd_handle_request (qse_htrd_t* htrd, qse_htre_t* req)
}
else
{
/* no Content-Length returned by CGI */
/* no Content-Length returned by CGI. */
if (qse_comparehttpversions (&cgi->version, &v11) >= 0)
{
cgi->content_chunked = 1;
else cgi->disconnect = 1;
}
else
{
/* If CGI doesn't specify Content-Length, i can't
* honor cgi->keepalive in HTTP/1.0 or earlier.
* Closing the connection is the only way to
* specify how long the content is */
cgi->disconnect = 1;
}
}
if (cgi->content_chunked)
{
if (qse_mbs_cat (cgi->res, QSE_MT("Transfer-Encoding: chunked\r\n")) == (qse_size_t)-1) return -1;
}
if (cgi->content_chunked &&
qse_mbs_cat (cgi->res, QSE_MT("Transfer-Encoding: chunked\r\n")) == (qse_size_t)-1) return -1;
if (qse_htre_walkheaders (req, walk_cgi_headers, cgi) <= -1) return -1;
if (qse_mbs_ncat (cgi->res, QSE_MT("\r\n"), 2) == (qse_size_t)-1) return -1;
/* end of header */
if (qse_mbs_cat (cgi->res, QSE_MT("\r\n")) == (qse_size_t)-1) return -1;
/* content body begins here */
cgi->content_received = qse_htre_getcontentlen(req);
if (cgi->content_length_set &&
cgi->content_received > cgi->content_length)
@ -1379,6 +1407,14 @@ static qse_env_t* makecgienv (
#ifdef _WIN32
qse_env_insertsys (env, QSE_T("PATH"));
{
qse_char_t proto[32];
qse_http_version_t* v = qse_htre_getversion(req);
snprintf (proto, QSE_COUNTOF(proto),
QSE_T("HTTP/%d.%d"), (int)v->major, (int)v->minor);
qse_env_insert (env, QSE_T("SERVER_PROTOCOL"), proto);
}
#else
qse_env_insertsysm (env, QSE_MT("LANG"));
qse_env_insertsysm (env, QSE_MT("PATH"));
@ -1387,9 +1423,17 @@ static qse_env_t* makecgienv (
{
qse_mchar_t port[16];
snprintf (port, QSE_COUNTOF(port),
"%d", (int)ntohs(client->addr.in4.sin_port));
QSE_MT("%d"), (int)ntohs(client->addr.in4.sin_port));
qse_env_insertm (env, QSE_MT("REMOTE_PORT"), port);
}
{
qse_mchar_t proto[32];
qse_http_version_t* v = qse_htre_getversion(req);
snprintf (proto, QSE_COUNTOF(proto),
QSE_MT("HTTP/%d.%d"), (int)v->major, (int)v->minor);
qse_env_insertm (env, QSE_MT("SERVER_PROTOCOL"), proto);
}
//qse_env_insertm (env, QSE_MT("REMOTE_ADDR"), QSE_MT("what the hell"));
#endif
@ -1418,6 +1462,7 @@ static int task_init_cgi (
xtn->path = (qse_mchar_t*)(xtn + 1);
xtn->version = *qse_htre_getversion(arg->req);
xtn->keepalive = arg->req->attr.keepalive;
xtn->nph = arg->nph;
xtn->env = makecgienv (httpd, client, arg->req);
if (xtn->env == QSE_NULL) xtn->init_failed = 1;
@ -1678,24 +1723,35 @@ 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;
cgi_htrd_xtn_t* xtn;
if (cgi->init_failed) goto oops;
cgi->htrd = qse_htrd_open (httpd->mmgr, QSE_SIZEOF(cgi_htrd_xtn_t));
if (cgi->htrd == QSE_NULL) goto oops;
xtn = (cgi_htrd_xtn_t*) qse_htrd_getxtn (cgi->htrd);
xtn->cgi = cgi;
qse_htrd_setrecbs (cgi->htrd, &cgi_htrd_cbs);
qse_htrd_setoption (
cgi->htrd,
QSE_HTRD_SKIPINITIALLINE |
QSE_HTRD_HURRIED |
QSE_HTRD_REQUEST
);
if (cgi->nph)
{
/* i cannot know how long the content will be
* since i don't parse the header. so i have to close
* the connection regardless of content-length or transfer-encoding
* in the actual header. */
if (qse_httpd_entaskdisconnect (httpd, client, task) == QSE_NULL) return -1;
}
else
{
cgi_htrd_xtn_t* xtn;
cgi->htrd = qse_htrd_open (httpd->mmgr, QSE_SIZEOF(cgi_htrd_xtn_t));
if (cgi->htrd == QSE_NULL) goto oops;
xtn = (cgi_htrd_xtn_t*) qse_htrd_getxtn (cgi->htrd);
xtn->cgi = cgi;
qse_htrd_setrecbs (cgi->htrd, &cgi_htrd_cbs);
qse_htrd_setoption (
cgi->htrd,
QSE_HTRD_SKIPINITIALLINE |
QSE_HTRD_HURRIED |
QSE_HTRD_REQUEST
);
cgi->res = qse_mbs_open (httpd->mmgr, 0, 256);
if (cgi->res == QSE_NULL) goto oops;
cgi->res = qse_mbs_open (httpd->mmgr, 0, 256);
if (cgi->res == QSE_NULL) goto oops;
}
cgi->pio = qse_pio_open (
httpd->mmgr, 0, (const qse_char_t*)cgi->path, cgi->env,
@ -1703,8 +1759,17 @@ static int task_main_cgi (
);
if (cgi->pio == QSE_NULL) goto oops;
task->main = task_main_cgi_2; /* cause this function to be called subsequently */
return task_main_cgi_2 (httpd, client, task); /* let me call it here once */
if (cgi->nph)
{
/* skip various header processing */
task->main = task_main_cgi_4;
return task_main_cgi_4 (httpd, client, task);
}
else
{
task->main = task_main_cgi_2; /* cause this function to be called subsequently */
return task_main_cgi_2 (httpd, client, task); /* let me call it here once */
}
oops:
if (cgi->res)
@ -1721,6 +1786,10 @@ oops:
return (entask_error (httpd, client, task, 500, &cgi->version, cgi->keepalive) == QSE_NULL)? -1: 0;
}
/* TODO: global option or individual paramter for max cgi lifetime
* non-blocking pio read ...
*/
qse_httpd_task_t* qse_httpd_entaskcgi (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
@ -1733,6 +1802,33 @@ qse_httpd_task_t* qse_httpd_entaskcgi (
arg.path = path;
arg.req = req;
arg.nph = 0;
QSE_MEMSET (&task, 0, QSE_SIZEOF(task));
task.init = task_init_cgi;
task.fini = task_fini_cgi;
task.main = task_main_cgi;
task.ctx = &arg;
return qse_httpd_entask (
httpd, client, pred, &task,
QSE_SIZEOF(task_cgi_t) + ((qse_mbslen(path) + 1) * QSE_SIZEOF(*path))
);
}
qse_httpd_task_t* qse_httpd_entasknph (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
const qse_httpd_task_t* pred,
const qse_mchar_t* path,
const qse_htre_t* req)
{
qse_httpd_task_t task;
task_cgi_arg_t arg;
arg.path = path;
arg.req = req;
arg.nph = 1;
QSE_MEMSET (&task, 0, QSE_SIZEOF(task));
task.init = task_init_cgi;

View File

@ -69,12 +69,13 @@ qse_printf (QSE_T("content = [%.*S]\n"),
x = qse_httpd_entaskcgi (
httpd, client, QSE_NULL, qpath, req);
if (x == QSE_NULL) goto oops;
#if 0
x = qse_httpd_entasknphcgi (
}
else if (dot && qse_mbscmp (dot, QSE_MT(".nph")) == 0)
{
/* nph-cgi */
x = qse_httpd_entasknph (
httpd, client, QSE_NULL, qpath, req);
if (x == QSE_NULL) goto oops;
#endif
}
else
{