improved httpd

This commit is contained in:
hyung-hwan 2012-09-18 13:03:57 +00:00
parent 99f1b5453b
commit fdba865863
9 changed files with 438 additions and 221 deletions

View File

@ -65,6 +65,8 @@ enum qse_httpd_option_t
typedef struct qse_httpd_stat_t qse_httpd_stat_t;
struct qse_httpd_stat_t
{
qse_long_t dev;
qse_long_t ino;
qse_foff_t size;
qse_ntime_t mtime;
const qse_mchar_t* mime;
@ -556,14 +558,9 @@ qse_httpd_task_t* qse_httpd_entaskcgi (
qse_httpd_client_t* client,
qse_httpd_task_t* pred,
const qse_mchar_t* path,
qse_htre_t* req
);
qse_httpd_task_t* qse_httpd_entasknph (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
qse_httpd_task_t* pred,
const qse_mchar_t* path,
const qse_mchar_t* script,
const qse_mchar_t* suffix,
int nph,
qse_htre_t* req
);

View File

@ -64,39 +64,39 @@
# include <sys/syscall.h>
#endif
#if defined(SYS_open)
#if defined(SYS_open) && defined(QSE_USE_SYSCALL)
# define QSE_OPEN(path,flags,mode) syscall(SYS_open,path,flags,mode)
#else
# define QSE_OPEN(path,flags,mode) open(path,flags,mode)
#endif
#if defined(SYS_close)
#if defined(SYS_close) && defined(QSE_USE_SYSCALL)
# define QSE_CLOSE(handle) syscall(SYS_close,handle)
#else
# define QSE_CLOSE(handle) close(handle)
#endif
#if defined(SYS_read)
#if defined(SYS_read) && defined(QSE_USE_SYSCALL)
# define QSE_READ(handle,buf,size) syscall(SYS_read,handle,buf,size)
#else
# define QSE_READ(handle,buf,size) read(handle,buf,size)
#endif
#if defined(SYS_write)
#if defined(SYS_write) && defined(QSE_USE_SYSCALL)
# define QSE_WRITE(handle,buf,size) syscall(SYS_write,handle,buf,size)
#else
# define QSE_WRITE(handle,buf,size) write(handle,buf,size)
#endif
#if !defined(_LP64) && (QSE_SIZEOF_VOID_P<8) && defined(SYS__llseek)
#if !defined(_LP64) && (QSE_SIZEOF_VOID_P<8) && defined(SYS__llseek) && defined(QSE_USE_SYSCALL)
# define QSE_LLSEEK(handle,hoffset,loffset,out,whence) syscall(SYS__llseek,handle,hoffset,loffset,out,whence)
#elif !defined(_LP64) && (QSE_SIZEOF_VOID_P<8) && defined(HAVE__LLSEEK)
#elif !defined(_LP64) && (QSE_SIZEOF_VOID_P<8) && defined(HAVE__LLSEEK) && defined(QSE_USE_SYSCALL)
# define QSE_LLSEEK(handle,hoffset,loffset,out,whence) _llseek(handle,hoffset,loffset,out,whence)
#endif
#if !defined(_LP64) && (QSE_SIZEOF_VOID_P<8) && defined(SYS_lseek64)
#if !defined(_LP64) && (QSE_SIZEOF_VOID_P<8) && defined(SYS_lseek64) && defined(QSE_USE_SYSCALL)
# define QSE_LSEEK(handle,offset,whence) syscall(SYS_lseek64,handle,offset,whence)
#elif defined(SYS_lseek)
#elif defined(SYS_lseek) && defined(QSE_USE_SYSCALL)
# define QSE_LSEEK(handle,offset,whence) syscall(SYS_lseek,handle,offset,whence)
#elif !defined(_LP64) && (QSE_SIZEOF_VOID_P<8) && defined(HAVE_LSEEK64)
# define QSE_LSEEK(handle,offset,whence) lseek64(handle,offset,whence)
@ -104,10 +104,10 @@
# define QSE_LSEEK(handle,offset,whence) lseek(handle,offset,whence)
#endif
#if !defined(_LP64) && (QSE_SIZEOF_VOID_P<8) && defined(SYS_fstat64)
#if !defined(_LP64) && (QSE_SIZEOF_VOID_P<8) && defined(SYS_fstat64) && defined(QSE_USE_SYSCALL)
# define QSE_FSTAT(handle,stbuf) syscall(SYS_fstat64,handle,stbuf)
typedef struct stat64 qse_fstat_t;
#elif defined(SYS_fstat)
#elif defined(SYS_fstat) && defined(QSE_USE_SYSCALL)
# 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)
@ -118,9 +118,9 @@
typedef struct stat qse_fstat_t;
#endif
#if !defined(_LP64) && (QSE_SIZEOF_VOID_P<8) && defined(SYS_ftruncate64)
#if !defined(_LP64) && (QSE_SIZEOF_VOID_P<8) && defined(SYS_ftruncate64) && defined(QSE_USE_SYSCALL)
# define QSE_FTRUNCATE(handle,size) syscall(SYS_ftruncate64,handle,size)
#elif defined(SYS_ftruncate)
#elif defined(SYS_ftruncate) && defined(QSE_USE_SYSCALL)
# define QSE_FTRUNCATE(handle,size) syscall(SYS_ftruncate,handle,size)
#elif !defined(_LP64) && (QSE_SIZEOF_VOID_P<8) && defined(HAVE_FTRUNCATE64)
# define QSE_FTRUNCATE(handle,size) ftruncate64(handle,size)
@ -128,127 +128,127 @@
# define QSE_FTRUNCATE(handle,size) ftruncate(handle,size)
#endif
#if defined(SYS_fchmod)
#if defined(SYS_fchmod) && defined(QSE_USE_SYSCALL)
# define QSE_FCHMOD(handle,mode) syscall(SYS_fchmod,handle,mode)
#else
# define QSE_FCHMOD(handle,mode) fchmod(handle,mode)
#endif
#if defined(SYS_fchown)
#if defined(SYS_fchown) && defined(QSE_USE_SYSCALL)
# define QSE_FCHOWN(handle,owner,group) syscall(SYS_fchown,handle,owner,group)
#else
# define QSE_FCHOWN(handle,owner,group) fchown(handle,owner,group)
#endif
#if defined(SYS_fsync)
#if defined(SYS_fsync) && defined(QSE_USE_SYSCALL)
# define QSE_FSYNC(handle) syscall(SYS_fsync,handle)
#else
# define QSE_FSYNC(handle) fsync(handle)
#endif
#if defined(SYS_fcntl)
#if defined(SYS_fcntl) && defined(QSE_USE_SYSCALL)
# define QSE_FCNTL(handle,cmd,arg) syscall(SYS_fcntl,handle,cmd,arg)
#else
# define QSE_FCNTL(handle,cmd,arg) fcntl(handle,cmd,arg)
#endif
#if defined(SYS_dup2)
#if defined(SYS_dup2) && defined(QSE_USE_SYSCALL)
# define QSE_DUP2(ofd,nfd) syscall(SYS_dup2,ofd,nfd)
#else
# define QSE_DUP2(ofd,nfd) dup2(ofd,nfd)
#endif
#if defined(SYS_pipe)
#if defined(SYS_pipe) && defined(QSE_USE_SYSCALL)
# define QSE_PIPE(pfds) syscall(SYS_pipe,pfds)
#else
# define QSE_PIPE(pfds) pipe(pfds)
#endif
#if defined(SYS_exit)
#if defined(SYS_exit) && defined(QSE_USE_SYSCALL)
# define QSE_EXIT(code) syscall(SYS_exit,code)
#else
# define QSE_EXIT(code) _exit(code)
#endif
#if defined(SYS_fork)
#if defined(SYS_fork) && defined(QSE_USE_SYSCALL)
# define QSE_FORK() syscall(SYS_fork)
#else
# define QSE_FORK() fork()
#endif
#if defined(SYS_vfork)
#if defined(SYS_vfork) && defined(QSE_USE_SYSCALL)
# define QSE_VFORK() syscall(SYS_vfork)
#else
# define QSE_VFORK() vfork()
#endif
#if defined(SYS_execve)
#if defined(SYS_execve) && defined(QSE_USE_SYSCALL)
# define QSE_EXECVE(path,argv,envp) syscall(SYS_execve,path,argv,envp)
#else
# define QSE_EXECVE(path,argv,envp) execve(path,argv,envp)
#endif
#if defined(SYS_waitpid)
#if defined(SYS_waitpid) && defined(QSE_USE_SYSCALL)
# define QSE_WAITPID(pid,status,options) syscall(SYS_waitpid,pid,status,options)
#else
# define QSE_WAITPID(pid,status,options) waitpid(pid,status,options)
#endif
#if defined(SYS_kill)
#if defined(SYS_kill) && defined(QSE_USE_SYSCALL)
# define QSE_KILL(pid,sig) syscall(SYS_kill,pid,sig)
#else
# define QSE_KILL(pid,sig) kill(pid,sig)
#endif
#if defined(SYS_getpid)
#if defined(SYS_getpid) && defined(QSE_USE_SYSCALL)
# define QSE_GETPID() syscall(SYS_getpid)
#else
# define QSE_GETPID() getpid()
#endif
#if defined(SYS_getuid)
#if defined(SYS_getuid) && defined(QSE_USE_SYSCALL)
# define QSE_GETUID() syscall(SYS_getuid)
#else
# define QSE_GETUID() getuid()
#endif
#if defined(SYS_geteuid)
#if defined(SYS_geteuid) && defined(QSE_USE_SYSCALL)
# define QSE_GETEUID() syscall(SYS_geteuid)
#else
# define QSE_GETEUID() geteuid()
#endif
#if defined(SYS_getgid)
#if defined(SYS_getgid) && defined(QSE_USE_SYSCALL)
# define QSE_GETGID() syscall(SYS_getgid)
#else
# define QSE_GETGID() getgid()
#endif
#if defined(SYS_getegid)
#if defined(SYS_getegid) && defined(QSE_USE_SYSCALL)
# define QSE_GETEGID() syscall(SYS_getegid)
#else
# define QSE_GETEGID() getegid()
#endif
#if defined(SYS_gettimeofday)
#if defined(SYS_gettimeofday) && defined(QSE_USE_SYSCALL)
# define QSE_GETTIMEOFDAY(tv,tz) syscall(SYS_gettimeofday,tv,tz)
#else
# define QSE_GETTIMEOFDAY(tv,tz) gettimeofday(tv,tz)
#endif
#if defined(SYS_settimeofday)
#if defined(SYS_settimeofday) && defined(QSE_USE_SYSCALL)
# define QSE_SETTIMEOFDAY(tv,tz) syscall(SYS_settimeofday,tv,tz)
#else
# define QSE_SETTIMEOFDAY(tv,tz) settimeofday(tv,tz)
#endif
#if defined(SYS_getrlimit)
#if defined(SYS_getrlimit) && defined(QSE_USE_SYSCALL)
# define QSE_GETRLIMIT(res,lim) syscall(SYS_getrlimit,res,lim)
#else
# define QSE_GETRLIMIT(res,lim) getrlimit(res,lim)
#endif
#if defined(SYS_setrlimit)
#if defined(SYS_setrlimit) && defined(QSE_USE_SYSCALL)
# define QSE_SETRLIMIT(res,lim) syscall(SYS_setrlimit,res,lim)
#else
# define QSE_SETRLIMIT(res,lim) setrlimit(res,lim)
@ -257,40 +257,40 @@
/* ===== FILE SYSTEM CALLS ===== */
#if defined(SYS_chmod)
#if defined(SYS_chmod) && defined(QSE_USE_SYSCALL)
# define QSE_CHMOD(path,mode) syscall(SYS_chmod,path,mode)
#else
# define QSE_CHMOD(path,mode) chmod(path,mode)
#endif
#if defined(SYS_chown)
#if defined(SYS_chown) && defined(QSE_USE_SYSCALL)
# define QSE_CHOWN(path,owner,group) syscall(SYS_chown,path,owner,group)
#else
# define QSE_CHOWN(path,owner,group) chown(path,owner,group)
#endif
#if defined(SYS_chroot)
#if defined(SYS_chroot) && defined(QSE_USE_SYSCALL)
# define QSE_CHROOT(path) syscall(SYS_chroot,path)
#else
# define QSE_CHROOT(path) chroot(path)
#endif
#if defined(SYS_lchown)
#if defined(SYS_lchown) && defined(QSE_USE_SYSCALL)
# define QSE_LCHOWN(path,owner,group) syscall(SYS_lchown,path,owner,group)
#else
# define QSE_LCHOWN(path,owner,group) lchown(path,owner,group)
#endif
#if defined(SYS_link)
#if defined(SYS_link) && defined(QSE_USE_SYSCALL)
# define QSE_LINK(oldpath,newpath) syscall(SYS_link,oldpath,newpath)
#else
# define QSE_LINK(oldpath,newpath) link(oldpath,newpath)
#endif
#if !defined(_LP64) && (QSE_SIZEOF_VOID_P<8) && defined(SYS_lstat64)
#if !defined(_LP64) && (QSE_SIZEOF_VOID_P<8) && defined(SYS_lstat64) && defined(QSE_USE_SYSCALL)
# define QSE_LSTAT(path,stbuf) syscall(SYS_lstat,path,stbuf)
typedef struct stat64 qse_lstat_t;
#elif defined(SYS_lstat)
#elif defined(SYS_lstat) && defined(QSE_USE_SYSCALL)
# define QSE_LSTAT(path,stbuf) syscall(SYS_lstat,path,stbuf)
typedef struct stat qse_lstat_t;
#elif !defined(_LP64) && (QSE_SIZEOF_VOID_P<8) && defined(HAVE_LSTAT64)
@ -301,29 +301,29 @@
typedef struct stat qse_lstat_t;
#endif
#if defined(SYS_access)
#if defined(SYS_access) && defined(QSE_USE_SYSCALL)
# define QSE_ACCESS(path,mode) syscall(SYS_access,path,mode)
#else
# define QSE_ACCESS(path,mode) access(path,mode)
#endif
#if defined(SYS_rename)
#if defined(SYS_rename) && defined(QSE_USE_SYSCALL)
# define QSE_RENAME(oldpath,newpath) syscall(SYS_rename,oldpath,newpath)
#else
int rename(const char *oldpath, const char *newpath); /* not to include stdio.h */
# define QSE_RENAME(oldpath,newpath) rename(oldpath,newpath)
#endif
#if defined(SYS_rmdir)
#if defined(SYS_rmdir) && defined(QSE_USE_SYSCALL)
# define QSE_RMDIR(path) syscall(SYS_rmdir,path)
#else
# define QSE_RMDIR(path) rmdir(path)
#endif
#if !defined(_LP64) && (QSE_SIZEOF_VOID_P<8) && defined(SYS_stat64)
#if !defined(_LP64) && (QSE_SIZEOF_VOID_P<8) && defined(SYS_stat64) && defined(QSE_USE_SYSCALL)
# define QSE_STAT(path,stbuf) syscall(SYS_stat64,path,stbuf)
typedef struct stat64 qse_stat_t;
#elif defined(SYS_stat)
#elif defined(SYS_stat) && defined(QSE_USE_SYSCALL)
# define QSE_STAT(path,stbuf) syscall(SYS_stat,path,stbuf)
typedef struct stat qse_stat_t;
#elif !defined(_LP64) && (QSE_SIZEOF_VOID_P<8) && defined(HAVE_STAT64)
@ -334,25 +334,25 @@
typedef struct stat qse_stat_t;
#endif
#if defined(SYS_symlink)
#if defined(SYS_symlink) && defined(QSE_USE_SYSCALL)
# define QSE_SYMLINK(oldpath,newpath) syscall(SYS_symlink,oldpath,newpath)
#else
# define QSE_SYMLINK(oldpath,newpath) symlink(oldpath,newpath)
#endif
#if defined(SYS_unlink)
#if defined(SYS_unlink) && defined(QSE_USE_SYSCALL)
# define QSE_UNLINK(path) syscall(SYS_unlink,path)
#else
# define QSE_UNLINK(path) unlink(path)
#endif
#if defined(SYS_utime)
#if defined(SYS_utime) && defined(QSE_USE_SYSCALL)
# define QSE_UTIME(path,t) syscall(SYS_utime,path,t)
#else
# define QSE_UTIME(path,t) utime(path,t)
#endif
#if defined(SYS_utimes)
#if defined(SYS_utimes) && defined(QSE_USE_SYSCALL)
# define QSE_UTIMES(path,t) syscall(SYS_utimes,path,t)
#else
# define QSE_UTIMES(path,t) utimes(path,t)

View File

@ -18,11 +18,6 @@
License along with QSE. If not, see <htrd://www.gnu.org/licenses/>.
*/
#if defined(_WIN32) || defined(__DOS__) || defined(__OS2__)
/* UNSUPPORTED YET.. */
/* TODO: IMPLEMENT THIS */
#else
#include "httpd.h"
#include "../cmn/mem.h"
#include <qse/cmn/str.h>
@ -34,9 +29,11 @@
typedef struct task_cgi_arg_t task_cgi_arg_t;
struct task_cgi_arg_t
{
const qse_mchar_t* path;
qse_htre_t* req;
qse_mcstr_t path;
qse_mcstr_t script;
qse_mcstr_t suffix;
int nph;
qse_htre_t* req;
};
typedef struct task_cgi_t task_cgi_t;
@ -46,6 +43,8 @@ struct task_cgi_t
qse_httpd_t* httpd;
const qse_mchar_t* path;
const qse_mchar_t* script;
const qse_mchar_t* suffix;
qse_http_version_t version;
int keepalive; /* taken from the request */
int nph;
@ -407,8 +406,13 @@ static qse_htrd_recbs_t cgi_script_htrd_cbs =
static int cgi_add_env (
qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_env_t* env, qse_htre_t* req, const
qse_mchar_t* path, qse_size_t length, int chunked)
qse_env_t* env, qse_htre_t* req,
const qse_mchar_t* path,
const qse_mchar_t* script,
const qse_mchar_t* suffix,
const qse_mchar_t* content_type,
qse_size_t content_length,
int chunked)
{
/* TODO: error check */
cgi_client_req_hdr_ctx_t ctx;
@ -430,22 +434,21 @@ static int cgi_add_env (
snprintf (buf, QSE_COUNTOF(buf), QSE_MT("HTTP/%d.%d"), (int)v->major, (int)v->minor);
qse_env_insertmbs (env, QSE_MT("SERVER_PROTOCOL"), buf);
{
qse_env_insertmbs (env, QSE_MT("SCRIPT_FILENAME"), path);
qse_env_insertmbs (env, QSE_MT("SCRIPT_NAME"), script);
if (suffix && suffix[0] != QSE_MT('\0'))
qse_env_insertmbs (env, QSE_MT("PATH_INFO"), suffix);
/* TODO: corrent all these name??? */
//qse_env_insertmbs (env, QSE_MT("SCRIPT_NAME"), qse_htre_getqpath(req));
//qse_env_insertmbs (env, QSE_MT("PATH_INFO"), qse_htre_getqpath(req));
//qse_env_insertmbs (env, QSE_MT("PATH_TRANSLATED"), qse_htre_getqpath(req));
//qse_env_insertmbs (env, QSE_MT("DOCUMENT_ROOT"), QSE_MT("/"));
}
//qse_env_insertmbs (env, QSE_MT("SCRIPT_FILENAME"), path);
qse_env_insertmbs (env, QSE_MT("REQUEST_METHOD"), qse_htre_getqmethodname(req));
qse_env_insertmbs (env, QSE_MT("REQUEST_URI"), qse_htre_getqpath(req));
if (qparam) qse_env_insertmbs (env, QSE_MT("QUERY_STRING"), qparam);
qse_fmtuintmaxtombs (buf, QSE_COUNTOF(buf), length, 10, -1, QSE_MT('\0'), QSE_NULL);
qse_fmtuintmaxtombs (buf, QSE_COUNTOF(buf), content_length, 10, -1, QSE_MT('\0'), QSE_NULL);
qse_env_insertmbs (env, QSE_MT("CONTENT_LENGTH"), buf);
if (content_type) qse_env_insertmbs (env, QSE_MT("CONTENT_TYPE"), content_type);
if (chunked) qse_env_insertmbs (env, QSE_MT("TRANSFER_ENCODING"), QSE_MT("chunked"));
@ -459,6 +462,7 @@ static int cgi_add_env (
qse_env_insertmbs (env, QSE_MT("REMOTE_ADDR"), buf);
#if 0
//qse_env_insertmbs (env, QSE_MT("DOCUMENT_ROOT"), QSE_MT("/"));
qse_env_insertmbs (env, "SERVER_NAME",
qse_env_insertmbs (env, "SERVER_ROOT",
qse_env_insertmbs (env, "DOCUMENT_ROOT",
@ -680,7 +684,8 @@ static int task_init_cgi (
qse_size_t content_length;
qse_size_t len;
const qse_mchar_t* ptr;
const qse_htre_hdrval_t* tmp;
cgi = (task_cgi_t*)qse_httpd_gettaskxtn (httpd, task);
arg = (task_cgi_arg_t*)task->ctx;
@ -692,8 +697,13 @@ static int task_init_cgi (
QSE_MEMSET (cgi, 0, QSE_SIZEOF(*cgi));
cgi->httpd = httpd;
qse_mbscpy ((qse_mchar_t*)(cgi + 1), arg->path);
cgi->path = (qse_mchar_t*)(cgi + 1);
cgi->script = cgi->path + arg->path.len + 1;
cgi->suffix = cgi->script + arg->script.len + 1;
qse_mbscpy ((qse_mchar_t*)cgi->path, arg->path.ptr);
qse_mbscpy ((qse_mchar_t*)cgi->script, arg->script.ptr);
qse_mbscpy ((qse_mchar_t*)cgi->suffix, arg->suffix.ptr);
cgi->version = *qse_htre_getversion(arg->req);
cgi->keepalive = (arg->req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE);
cgi->nph = arg->nph;
@ -826,8 +836,14 @@ done:
goto oops;
}
/* get the content type header value */
tmp = qse_htre_getheaderval(arg->req, QSE_MT("Content-Type"));
if (tmp) while (tmp->next) tmp = tmp->next; /* get the last value */
if (cgi_add_env (
httpd, client, cgi->env, arg->req, arg->path, content_length,
httpd, client, cgi->env, arg->req,
cgi->path, cgi->script, cgi->suffix,
(tmp? tmp->ptr: QSE_NULL), content_length,
(cgi->reqflags & CGI_REQ_FWDCHUNKED)) <= -1)
{
goto oops;
@ -1434,15 +1450,28 @@ oops:
* non-blocking pio read ...
*/
static QSE_INLINE qse_httpd_task_t* entask_cgi (
qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_httpd_task_t* pred, const qse_mchar_t* path,
qse_htre_t* req, int nph)
qse_httpd_task_t* qse_httpd_entaskcgi (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
qse_httpd_task_t* pred,
const qse_mchar_t* path,
const qse_mchar_t* script,
const qse_mchar_t* suffix,
int nph,
qse_htre_t* req)
{
qse_httpd_task_t task;
task_cgi_arg_t arg;
arg.path = path;
if (script == QSE_NULL) script = qse_htre_getqpath(req);
if (suffix == QSE_NULL) suffix = QSE_MT("");
arg.path.ptr = path;
arg.path.len = qse_mbslen(path);
arg.script.ptr = script;
arg.script.len = qse_mbslen(script);
arg.suffix.ptr = suffix;
arg.suffix.len = qse_mbslen(suffix);
arg.req = req;
arg.nph = nph;
@ -1454,22 +1483,10 @@ static QSE_INLINE qse_httpd_task_t* entask_cgi (
return qse_httpd_entask (
httpd, client, pred, &task,
QSE_SIZEOF(task_cgi_t) + ((qse_mbslen(path) + 1) * QSE_SIZEOF(*path))
QSE_SIZEOF(task_cgi_t) +
((arg.path.len + 1) * QSE_SIZEOF(*path)) +
((arg.script.len + 1) * QSE_SIZEOF(*script)) +
((arg.suffix.len + 1) * QSE_SIZEOF(*suffix))
);
}
qse_httpd_task_t* qse_httpd_entaskcgi (
qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_httpd_task_t* pred, const qse_mchar_t* path, qse_htre_t* req)
{
return entask_cgi (httpd, client, pred, path, req, 0);
}
qse_httpd_task_t* qse_httpd_entasknph (
qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_httpd_task_t* pred, const qse_mchar_t* path, qse_htre_t* req)
{
return entask_cgi (httpd, client, pred, path, req, 1);
}
#endif

View File

@ -27,11 +27,14 @@
#include <qse/cmn/time.h>
#include <qse/cmn/stdio.h> /* TODO: remove this */
#define ETAG_LEN_MAX 127
typedef struct task_file_t task_file_t;
struct task_file_t
{
const qse_mchar_t* path;
qse_http_range_t range;
qse_mchar_t if_none_match[ETAG_LEN_MAX + 1];
qse_ntime_t if_modified_since;
qse_http_version_t version;
int keepalive;
@ -178,6 +181,8 @@ qse_printf (QSE_T("opening file %hs\n"), file->path);
if (file->range.type != QSE_HTTP_RANGE_NONE)
{
qse_mchar_t tmp[4][64];
qse_mchar_t etag[ETAG_LEN_MAX + 1];
qse_size_t etag_len;
if (file->range.type == QSE_HTTP_RANGE_SUFFIX)
{
@ -201,9 +206,17 @@ qse_printf (QSE_T("opening file %hs\n"), file->path);
qse_fmtuintmaxtombs (tmp[2], QSE_COUNTOF(tmp[2]), file->range.to, 10, -1, QSE_MT('\0'), QSE_NULL);
qse_fmtuintmaxtombs (tmp[3], QSE_COUNTOF(tmp[3]), st.size, 10, -1, QSE_MT('\0'), QSE_NULL);
etag_len = qse_fmtuintmaxtombs (&etag[0], QSE_COUNTOF(etag) - etag_len, st.mtime, 16, -1, QSE_MT('\0'), QSE_NULL);
etag[etag_len++] = QSE_MT('-');
etag_len += qse_fmtuintmaxtombs (&etag[etag_len], QSE_COUNTOF(etag) - etag_len, st.size, 16, -1, QSE_MT('\0'), QSE_NULL);
etag[etag_len++] = QSE_MT('-');
etag_len += qse_fmtuintmaxtombs (&etag[etag_len], QSE_COUNTOF(etag) - etag_len, st.ino, 16, -1, QSE_MT('\0'), QSE_NULL);
etag[etag_len++] = QSE_MT('-');
etag_len += qse_fmtuintmaxtombs (&etag[etag_len], QSE_COUNTOF(etag) - etag_len, st.dev, 16, -1, QSE_MT('\0'), QSE_NULL);
x = qse_httpd_entaskformat (
httpd, client, x,
QSE_MT("HTTP/%d.%d 206 Partial Content\r\nServer: %s\r\nDate: %s\r\nConnection: %s\r\n%s%s%sContent-Length: %s\r\nContent-Range: bytes %s-%s/%s\r\nAccept-Ranges: bytes\r\nLast-Modified: %s\r\n\r\n"),
QSE_MT("HTTP/%d.%d 206 Partial Content\r\nServer: %s\r\nDate: %s\r\nConnection: %s\r\n%s%s%sContent-Length: %s\r\nContent-Range: bytes %s-%s/%s\r\nAccept-Ranges: bytes\r\nLast-Modified: %s\r\nETag: %s\r\n\r\n"),
file->version.major, file->version.minor,
qse_httpd_getname (httpd),
qse_httpd_fmtgmtimetobb (httpd, QSE_NULL, 0),
@ -211,7 +224,7 @@ qse_printf (QSE_T("opening file %hs\n"), file->path);
(st.mime? QSE_MT("Content-Type: "): QSE_MT("")),
(st.mime? st.mime: QSE_MT("")),
(st.mime? QSE_MT("\r\n"): QSE_MT("")),
tmp[0], tmp[1], tmp[2], tmp[3]
tmp[0], tmp[1], tmp[2], tmp[3], etag
);
if (x)
{
@ -226,13 +239,24 @@ qse_printf (QSE_T("opening file %hs\n"), file->path);
else
{
qse_mchar_t b_fsize[64];
qse_mchar_t etag[ETAG_LEN_MAX + 1];
qse_size_t etag_len;
if (file->if_modified_since > 0 &&
QSE_MSEC_TO_SEC(st.mtime) <= QSE_MSEC_TO_SEC(file->if_modified_since))
etag_len = qse_fmtuintmaxtombs (&etag[0], QSE_COUNTOF(etag) - etag_len, st.mtime, 16, -1, QSE_MT('\0'), QSE_NULL);
etag[etag_len++] = QSE_MT('-');
etag_len += qse_fmtuintmaxtombs (&etag[etag_len], QSE_COUNTOF(etag) - etag_len, st.size, 16, -1, QSE_MT('\0'), QSE_NULL);
etag[etag_len++] = QSE_MT('-');
etag_len += qse_fmtuintmaxtombs (&etag[etag_len], QSE_COUNTOF(etag) - etag_len, st.ino, 16, -1, QSE_MT('\0'), QSE_NULL);
etag[etag_len++] = QSE_MT('-');
etag_len += qse_fmtuintmaxtombs (&etag[etag_len], QSE_COUNTOF(etag) - etag_len, st.dev, 16, -1, QSE_MT('\0'), QSE_NULL);
if ((file->if_none_match[0] != QSE_MT('\0') && qse_mbscmp (etag, file->if_none_match) == 0) ||
(file->if_modified_since > 0 && QSE_MSEC_TO_SEC(st.mtime) <= QSE_MSEC_TO_SEC(file->if_modified_since)))
{
/* i've converted milliseconds to seconds before comparison
/* i've converted milliseconds to seconds before timestamp comparison
* because st.mtime has the actual milliseconds less than 1 second
* while if_modified_since doesn't have such small milliseconds */
x = qse_httpd_entaskformat (
httpd, client, x,
QSE_MT("HTTP/%d.%d 304 Not Modified\r\nServer: %s\r\nDate: %s\r\nConnection: %s\r\nContent-Length: 0\r\n\r\n"),
@ -252,7 +276,7 @@ qse_printf (QSE_T("opening file %hs\n"), file->path);
x = qse_httpd_entaskformat (
httpd, client, x,
QSE_MT("HTTP/%d.%d 200 OK\r\nServer: %s\r\nDate: %s\r\nConnection: %s\r\n%s%s%sContent-Length: %s\r\nAccept-Ranges: bytes\r\nLast-Modified: %s\r\n\r\n"),
QSE_MT("HTTP/%d.%d 200 OK\r\nServer: %s\r\nDate: %s\r\nConnection: %s\r\n%s%s%sContent-Length: %s\r\nAccept-Ranges: bytes\r\nLast-Modified: %s\r\nETag: %s\r\n\r\n"),
file->version.major, file->version.minor,
qse_httpd_getname (httpd),
qse_httpd_fmtgmtimetobb (httpd, QSE_NULL, 0),
@ -261,7 +285,8 @@ qse_printf (QSE_T("opening file %hs\n"), file->path);
(st.mime? st.mime: QSE_MT("")),
(st.mime? QSE_MT("\r\n"): QSE_MT("")),
b_fsize,
qse_httpd_fmtgmtimetobb (httpd, &st.mtime, 1)
qse_httpd_fmtgmtimetobb (httpd, &st.mtime, 1),
etag
);
if (x) x = entask_file_segment (httpd, client, x, handle, 0, st.size);
}
@ -305,14 +330,31 @@ qse_httpd_task_t* qse_httpd_entaskfile (
data.range.type = QSE_HTTP_RANGE_NONE;
}
/* TODO: support Etag and If-None-Match */
data.if_modified_since = 0; /* 0 should be old enough */
tmp = qse_htre_getheaderval(req, QSE_MT("If-Modified-Since"));
tmp = qse_htre_getheaderval(req, QSE_MT("If-None-Match"));
if (tmp)
{
while (tmp->next) tmp = tmp->next; /* get the last value */
if (qse_parsehttptime (tmp->ptr, &data.if_modified_since) <= -1)
data.if_modified_since = 0;
qse_mbsxcpy (data.if_none_match, QSE_COUNTOF(data.if_none_match), tmp->ptr);
}
if (data.if_none_match[0] == QSE_MT('\0'))
{
/* Both ETag and Last-Modified are included in the reply.
* If the client understand ETag, it can choose to include
* If-None-Match in the request. If it understands Last-Modified,
* it can choose to include If-Modified-Since. I don't care
* the client understands both and include both of them
* in the request.
*
* I check If-None-Match if it's included.
* I check If-Modified-Since if If-None-Match is not included.
*/
tmp = qse_htre_getheaderval(req, QSE_MT("If-Modified-Since"));
if (tmp)
{
while (tmp->next) tmp = tmp->next; /* get the last value */
if (qse_parsehttptime (tmp->ptr, &data.if_modified_since) <= -1)
data.if_modified_since = 0;
}
}
QSE_MEMSET (&task, 0, QSE_SIZEOF(task));

View File

@ -155,6 +155,13 @@ static qse_httpd_errnum_t syserr_to_errnum (int e)
return QSE_HTTPD_EACCES;
case ENOENT:
case ENOTDIR:
/* ENOTDIR can be returned in this situation.
* i want to access /tmp/t1.cgi/abc/def
* while /tmp/t1.cgi is an existing file.
* I'm not sure if it is really good to translate
* ENOTDIR to QSE_HTTPD_ENOENT.
*/
return QSE_HTTPD_ENOENT;
case EEXIST:
@ -328,6 +335,7 @@ static int init_xtn_ssl (
/* TODO: CRYPTO_set_id_callback ();
TODO: CRYPTO_set_locking_callback ();*/
SSL_CTX_set_read_ahead (ctx, 0);
xtn->ssl_ctx = ctx;
return 0;
}
@ -987,6 +995,8 @@ static int file_stat (
QSE_MEMSET (hst, 0, QSE_SIZEOF(*hst));
hst->dev = st.st_dev;
hst->ino = st.st_ino;
hst->size = st.st_size;
#if defined(HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC)
hst->mtime = QSE_SECNSEC_TO_MSEC(st.st_mtim.tv_sec,st.st_mtim.tv_nsec);
@ -1110,6 +1120,12 @@ static qse_ssize_t client_recv (
else
qse_httpd_seterrnum (httpd, QSE_HTTPD_ESYSERR);
}
if (SSL_pending (client->handle2.ptr) > 0)
client->status |= CLIENT_PENDING;
else
client->status &= ~CLIENT_PENDING;
return ret;
#else
return -1;
@ -1209,6 +1225,8 @@ qse_fflush (QSE_STDOUT);
* will free it */
return -1;
}
SSL_set_read_ahead (ssl, 0);
}
ret = SSL_accept (ssl);
@ -1224,7 +1242,10 @@ qse_fflush (QSE_STDOUT);
/* SSL_free (ssl); */
return -1;
}
qse_printf (QSE_T("SSL ACCEPTED %d\n"), client->handle.i);
qse_fflush (QSE_STDOUT);
#else
qse_fprintf (QSE_STDERR, QSE_T("Error: NO SSL SUPPORT\n"));
return -1;
#endif
}
@ -1260,6 +1281,178 @@ qse_printf (QSE_T("HEADER OK %d[%hs] %d[%hs]\n"), (int)QSE_HTB_KLEN(pair), QSE_
return QSE_HTB_WALK_FORWARD;
}
typedef struct target_t target_t;
struct target_t
{
enum
{
TARGET_DIR,
TARGET_FILE,
TARGET_CGI
} type;
union
{
const qse_mchar_t* dir;
const qse_mchar_t* file;
struct
{
qse_mchar_t* path;
qse_mchar_t* script;
qse_mchar_t* suffix;
int nph;
} cgi;
} u;
};
static void dispose_target (qse_httpd_t* httpd, target_t* target)
{
if (target->type == TARGET_CGI)
{
if (target->u.cgi.suffix) QSE_MMGR_FREE (httpd->mmgr, target->u.cgi.suffix);
if (target->u.cgi.script) QSE_MMGR_FREE (httpd->mmgr, target->u.cgi.script);
if (target->u.cgi.path) QSE_MMGR_FREE (httpd->mmgr, target->u.cgi.path);
}
}
static int resolve_target (
qse_httpd_t* httpd, qse_htre_t* req, target_t* target)
{
static struct extinfo_t
{
const qse_mchar_t* ptr;
qse_size_t len;
int nph;
} extinfo[] =
{
{ QSE_MT(".cgi"), 4, 0 },
{ QSE_MT(".nph"), 4, 1 }
};
qse_size_t i;
const qse_mchar_t* qpath;
qse_stat_t st;
qpath = qse_htre_getqpath(req);
QSE_MEMSET (target, 0, QSE_SIZEOF(*target));
if (QSE_STAT (qpath, &st) == 0 && S_ISDIR(st.st_mode))
{
/* TODO: attempt the index file like index.html, index.cgi, etc. */
/* it is a directory */
target->type = TARGET_DIR;
target->u.dir = qpath;
}
else
{
/* TODO: attempt other segments if qpath is like
* /abc/x.cgi/y.cgi/ttt. currently, it tries x.cgi only.
* x.cgi could be a directory name .
*/
for (i = 0; i < QSE_COUNTOF(extinfo); i++)
{
const qse_mchar_t* ext;
ext = qse_mbsstr (qpath, extinfo[i].ptr);
if (ext && (ext[extinfo[i].len] == QSE_MT('/') ||
ext[extinfo[i].len] == QSE_MT('\0')))
{
target->type = TARGET_CGI;
target->u.cgi.nph = extinfo[i].nph;
if (ext[extinfo[i].len] == QSE_MT('/'))
{
/* TODO: combine path with document root */
target->u.cgi.path = qse_mbsxdup (qpath, ext - qpath + extinfo[i].len, httpd->mmgr);
target->u.cgi.script = qse_mbsxdup (qpath, ext - qpath + extinfo[i].len, httpd->mmgr);
target->u.cgi.suffix = qse_mbsdup (&ext[extinfo[i].len], httpd->mmgr);
if (target->u.cgi.path == QSE_NULL ||
target->u.cgi.script == QSE_NULL ||
target->u.cgi.suffix == QSE_NULL)
{
dispose_target (httpd, target);
return -1;
}
}
else
{
/* TODO: combine path with document root */
target->u.cgi.path = qse_mbsdup (qpath, httpd->mmgr);
target->u.cgi.script = qse_mbsdup (qpath, httpd->mmgr);
if (target->u.cgi.path == QSE_NULL ||
target->u.cgi.script == QSE_NULL)
{
dispose_target (httpd, target);
return -1;
}
}
return 0;
}
}
target->type = TARGET_FILE;
target->u.file = qpath;
}
return 0;
}
static qse_httpd_task_t* entask_target (
qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_htre_t* req, target_t* target)
{
qse_httpd_task_t* task;
switch (target->type)
{
case TARGET_DIR:
qse_httpd_discardcontent (httpd, req);
task = qse_httpd_entaskdir (httpd, client, QSE_NULL, target->u.dir, req);
break;
case TARGET_FILE:
qse_httpd_discardcontent (httpd, req);
task = qse_httpd_entaskfile (httpd, client, QSE_NULL, target->u.file, req);
break;
case TARGET_CGI:
if (qse_htre_getqmethodtype(req) == QSE_HTTP_POST &&
!(req->attr.flags & QSE_HTRE_ATTR_LENGTH) &&
!(req->attr.flags & QSE_HTRE_ATTR_CHUNKED))
{
req->attr.flags &= ~QSE_HTRE_ATTR_KEEPALIVE;
qse_httpd_discardcontent (httpd, req);
task = qse_httpd_entaskerror (httpd, client, QSE_NULL, 411, req);
if (task)
{
/* 411 Length Required - can't keep alive */
task = qse_httpd_entaskdisconnect (httpd, client, QSE_NULL);
}
}
else
{
task = qse_httpd_entaskcgi (
httpd, client, QSE_NULL, target->u.cgi.path,
target->u.cgi.script, target->u.cgi.suffix,
target->u.cgi.nph, req);
}
break;
default:
task = QSE_NULL;
break;
}
return task;
}
static int process_request (
qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_htre_t* req, int peek)
@ -1315,7 +1508,7 @@ if (qse_htre_getcontentlen(req) > 0)
/* "expect" in the header, version 1.1 or higher,
* and no content received yet */
/* TODO: determine if to return 100-continue or other errors */
/* TODO: determine if to return 100-continue or other errors */
{
qse_ntime_t now;
qse_gettime (&now);
@ -1333,117 +1526,60 @@ if (qse_htre_getcontentlen(req) > 0)
if (method == QSE_HTTP_GET || method == QSE_HTTP_POST)
{
const qse_mchar_t* qpath = qse_htre_getqpath(req);
const qse_mchar_t* dot = qse_mbsrchr (qpath, QSE_MT('.'));
if (dot && qse_mbscmp (dot, QSE_MT(".cgi")) == 0)
if (peek)
{
if (peek)
{
/* cgi */
#if 0
if (req->attr.flags & QSE_HTRE_ATTR_CHUNKED)
{
qse_printf (QSE_T("chunked cgi... delaying until contents are received\n"));
#if 0
req->attr.keepalive = 0;
task = qse_httpd_entaskerror (
httpd, client, QSE_NULL, 411, req);
/* 411 can't keep alive */
if (task) qse_httpd_entaskdisconnect (httpd, client, QSE_NULL);
#endif
}
else
#endif
target_t target;
/*if (method == QSE_HTTP_POST && !(req->attr.flags & QSE_HTRE_ATTR_LENGTH))*/
if (method == QSE_HTTP_POST &&
!(req->attr.flags & QSE_HTRE_ATTR_LENGTH) &&
!(req->attr.flags & QSE_HTRE_ATTR_CHUNKED))
{
req->attr.flags &= ~QSE_HTRE_ATTR_KEEPALIVE;
task = qse_httpd_entaskerror (
httpd, client, QSE_NULL, 411, req);
/* 411 can't keep alive */
if (task) qse_httpd_entaskdisconnect (httpd, client, QSE_NULL);
}
else
{
task = qse_httpd_entaskcgi (
httpd, client, QSE_NULL, qpath, req);
if (task == QSE_NULL) goto oops;
}
}
#if 0
else
{
/* to support the chunked request,
* i need to wait until it's completed and invoke cgi */
if (req->attr.flags & QSE_HTRE_ATTR_CHUNKED)
{
qse_printf (QSE_T("Entasking chunked CGI...\n"));
task = qse_httpd_entaskcgi (
httpd, client, QSE_NULL, qpath, req);
if (task == QSE_NULL) goto oops;
}
}
#endif
return 0;
}
else if (dot && qse_mbscmp (dot, QSE_MT(".nph")) == 0)
{
if (peek)
{
const qse_htre_hdrval_t* auth;
int authorized = 0;
auth = qse_htre_getheaderval (req, QSE_MT("Authorization"));
if (auth)
{
/* TODO: PERFORM authorization... */
/* BASE64 decode... */
while (auth->next) auth = auth->next;
authorized = 1;
}
if (authorized)
{
/* nph-cgi */
task = qse_httpd_entasknph (
httpd, client, QSE_NULL, qpath, req);
}
else
{
task = qse_httpd_entaskauth (
httpd, client, QSE_NULL, QSE_MT("Secure Area"), req);
}
if (task == QSE_NULL) goto oops;
}
return 0;
}
else
{
#if 0
if (!peek)
{
/* file or directory */
task = qse_httpd_entaskfile (
httpd, client, QSE_NULL, qpath, req);
if (task == QSE_NULL) goto oops;
}
#else
if (peek)
if (resolve_target (httpd, req, &target) <= -1)
{
qse_httpd_discardcontent (httpd, req);
task = qse_httpd_entaskpath (httpd, client, QSE_NULL, qpath, req);
if (task == QSE_NULL) goto oops;
task = qse_httpd_entaskerror (httpd, client, QSE_NULL, 500, req);
}
#endif
else
{
task = entask_target (httpd, client, req, &target);
dispose_target (httpd, &target);
}
if (task == QSE_NULL) goto oops;
}
#if 0
if (peek)
{
const qse_htre_hdrval_t* auth;
int authorized = 0;
auth = qse_htre_getheaderval (req, QSE_MT("Authorization"));
if (auth)
{
/* TODO: PERFORM authorization... */
/* BASE64 decode... */
while (auth->next) auth = auth->next;
authorized = 1;
}
if (authorized)
{
/* nph-cgi */
task = qse_httpd_entasknph (
httpd, client, QSE_NULL, qpath, req);
}
else
{
task = qse_httpd_entaskauth (
httpd, client, QSE_NULL, QSE_MT("Secure Area"), req);
}
if (task == QSE_NULL) goto oops;
}
#endif
}
else
{
if (!peek)
if (peek)
{
qse_httpd_discardcontent (httpd, req);
}
else
{
task = qse_httpd_entaskerror (httpd, client, QSE_NULL, 405, req);
if (task == QSE_NULL) goto oops;

View File

@ -446,6 +446,7 @@ qse_httpd_task_t* qse_httpd_entaskauth (
}
/*------------------------------------------------------------------------*/
qse_httpd_task_t* qse_httpd_entaskpath (
qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_httpd_task_t* pred, const qse_mchar_t* name, qse_htre_t* req)
@ -453,13 +454,16 @@ qse_httpd_task_t* qse_httpd_entaskpath (
qse_stat_t st;
qse_httpd_task_t* task;
if (QSE_LSTAT (name, &st) == 0 && S_ISDIR(st.st_mode))
/* TODO: LSTAT or STAT? should i not follow the symbolic link? */
/* if (QSE_LSTAT (name, &st) == 0 && S_ISDIR(st.st_mode))*/
if (QSE_STAT (name, &st) == 0 && S_ISDIR(st.st_mode))
task = qse_httpd_entaskdir (httpd, client, pred, name, req);
else
task = qse_httpd_entaskfile (httpd, client, pred, name, req);
return task;
}
/*------------------------------------------------------------------------*/
#if 0

View File

@ -758,6 +758,25 @@ qse_printf (QSE_T("]\n"));
return -1;
}
qse_printf (QSE_T("!!!!!FEEDING OK OK OK OK %d from %d\n"), (int)m, (int)client->handle.i);
if (client->status & CLIENT_PENDING)
{
/* this CLIENT_PENDING thing is a dirty hack for SSL.
* In SSL, data is transmitted in a record. a record can be
* as large as 16K bytes since its length field is 2 bytes.
* If SSL_read() has record a record but it's given a
* smaller buffer than the actuaal record, the next call
* to select() won't return. there is no data to read
* at the socket layer. SSL_pending() can tell you the
* amount of data in the SSL buffer. I try to consume
* the pending data if the client.recv handler set CLIENT_PENDING.
*
* TODO: Investigate if there is any starvation issues.
* What if a single client never stops sending?
*/
goto reread;
}
return 0;
}
@ -771,6 +790,7 @@ static int invoke_client_task (
int n, trigger_fired, client_handle_writable;
/* TODO: handle comparison callback ... */
qse_printf (QSE_T("INVOKE CLIENT TASK..........\n"));
if (handle.i == client->handle.i && (mask & QSE_HTTPD_MUX_READ)) /* TODO: no direct comparision */
{
if (!(client->status & CLIENT_MUTE) &&
@ -899,7 +919,7 @@ qse_printf (QSE_T("REMOVING XXXXX FROM READING NO MORE TASK....\n"));
{
/* the code here is pretty fragile. there is a high chance
* that something can go wrong if the task handler plays
* with the trigger field in an unexpected mannger.
* with the trigger field in an unexpected manner.
*/
for (i = 0; i < QSE_COUNTOF(task->trigger); i++)

View File

@ -74,12 +74,13 @@ struct qse_httpd_t
#define CLIENT_BAD (1 << 0)
#define CLIENT_READY (1 << 1)
#define CLIENT_SECURE (1 << 2)
#define CLIENT_MUTE (1 << 3)
#define CLIENT_MUTE_DELETED (1 << 4)
#define CLIENT_HANDLE_READ_IN_MUX (1 << 5)
#define CLIENT_HANDLE_WRITE_IN_MUX (1 << 6)
#define CLIENT_PENDING (1 << 3)
#define CLIENT_MUTE (1 << 4)
#define CLIENT_MUTE_DELETED (1 << 5)
#define CLIENT_HANDLE_READ_IN_MUX (1 << 6)
#define CLIENT_HANDLE_WRITE_IN_MUX (1 << 7)
#define CLIENT_HANDLE_IN_MUX (CLIENT_HANDLE_READ_IN_MUX|CLIENT_HANDLE_WRITE_IN_MUX)
#define CLIENT_TASK_TRIGGER_IN_MUX(i) (1 << ((i) + 7))
#define CLIENT_TASK_TRIGGER_IN_MUX(i) (1 << ((i) + 8))
#ifdef __cplusplus
extern "C" {

View File

@ -111,7 +111,7 @@ if (qse_htre_getqparam(req))
else
{
task = qse_httpd_entaskcgi (
httpd, client, QSE_NULL, qpath, req);
httpd, client, QSE_NULL, qpath, QSE_NULL, QSE_NULL, 0, req);
if (task == QSE_NULL) goto oops;
}
}