added qse_httpd_entaskdir()

This commit is contained in:
hyung-hwan 2012-09-11 16:19:52 +00:00
parent 9cc8d6f76d
commit b9a0863fff
16 changed files with 905 additions and 880 deletions

4
qse/configure vendored
View File

@ -16057,7 +16057,7 @@ $as_echo "#define STDC_HEADERS 1" >>confdefs.h
fi fi
for ac_header in stddef.h wchar.h wctype.h errno.h signal.h fcntl.h for ac_header in stddef.h wchar.h wctype.h errno.h signal.h fcntl.h dirent.h
do : do :
as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` 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" ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default"
@ -16177,7 +16177,7 @@ _ACEOF
fi fi
done done
for ac_func in lseek64 stat64 fstat64 lstat64 ftruncate64 for ac_func in lseek64 stat64 fstat64 lstat64 ftruncate64 readdir64
do : do :
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"

View File

@ -82,7 +82,7 @@ AC_SUBST(LIBM, $LIBM)
dnl check header files. dnl check header files.
AC_HEADER_STDC AC_HEADER_STDC
AC_CHECK_HEADERS([stddef.h wchar.h wctype.h errno.h signal.h fcntl.h]) AC_CHECK_HEADERS([stddef.h wchar.h wctype.h errno.h signal.h fcntl.h dirent.h])
AC_CHECK_HEADERS([time.h sys/time.h utime.h spawn.h execinfo.h]) AC_CHECK_HEADERS([time.h sys/time.h utime.h spawn.h execinfo.h])
AC_CHECK_HEADERS([sys/resource.h sys/wait.h sys/syscall.h sys/sendfile.h sys/epoll.h]) AC_CHECK_HEADERS([sys/resource.h sys/wait.h sys/syscall.h sys/sendfile.h sys/epoll.h])
AC_CHECK_HEADERS([net/if.h]) AC_CHECK_HEADERS([net/if.h])
@ -100,7 +100,7 @@ AC_CHECK_FUNCS([mbrlen mbrtowc wcrtomb])
AC_CHECK_FUNCS([mbsnrtowcs mbsrtowcs wcsnrtombs wcsrtombs]) AC_CHECK_FUNCS([mbsnrtowcs mbsrtowcs wcsnrtombs wcsrtombs])
AC_CHECK_FUNCS([wctype iswctype wctrans towctrans]) AC_CHECK_FUNCS([wctype iswctype wctrans towctrans])
AC_CHECK_FUNCS([isblank iswblank]) AC_CHECK_FUNCS([isblank iswblank])
AC_CHECK_FUNCS([lseek64 stat64 fstat64 lstat64 ftruncate64]) AC_CHECK_FUNCS([lseek64 stat64 fstat64 lstat64 ftruncate64 readdir64])
AC_CHECK_FUNCS([timegm timelocal]) AC_CHECK_FUNCS([timegm timelocal])
AC_CHECK_FUNCS([utime utimes]) AC_CHECK_FUNCS([utime utimes])
AC_CHECK_FUNCS([sysconf]) AC_CHECK_FUNCS([sysconf])

View File

@ -199,16 +199,22 @@ QSEAWK implements the language described in the book
<a class="el" href="http://cm.bell-labs.com/cm/cs/awkbook/"> <a class="el" href="http://cm.bell-labs.com/cm/cs/awkbook/">
The AWK Proramming Language</a> with various @ref awk_ext "extensions". The AWK Proramming Language</a> with various @ref awk_ext "extensions".
An AWK program, at the top level, can composed of the following elements shown below. Each language element requires the option in the second column to be on. An AWK program can be composed of the following elements shown below.
Each language element requires the option in the second column to be on.
<table> <table>
<tr><th>Element </th><th>Option </th></tr> <tr><th>Element </th><th>Option </th></tr>
<tr><td>Comment </td><td> </td></tr>
<tr><td>Global variable declaration</td><td>#QSE_AWK_EXPLICIT </td></tr> <tr><td>Global variable declaration</td><td>#QSE_AWK_EXPLICIT </td></tr>
<tr><td>Pattern-action block </td><td>#QSE_AWK_PABLOCK </td></tr> <tr><td>Pattern-action block </td><td>#QSE_AWK_PABLOCK </td></tr>
<tr><td>User-defined function </td><td> </td></tr> <tr><td>User-defined function </td><td> </td></tr>
<tr><td>\@include </td><td>#QSE_AWK_INCLUDE </td></tr> <tr><td>\@include </td><td>#QSE_AWK_INCLUDE </td></tr>
</table> </table>
Single line comments begin with the '#' letter and end at the end of the
same line. The C style multi-line comments are supported as well.
Comments are ignored.
- pattern-action-block := pattern action-block - pattern-action-block := pattern action-block
- pattern := BEGIN | END | expression | expression-range - pattern := BEGIN | END | expression | expression-range
- expression-range := expression , expression - expression-range := expression , expression

View File

@ -202,6 +202,9 @@
/* Have PTHREAD_PRIO_INHERIT. */ /* Have PTHREAD_PRIO_INHERIT. */
#undef HAVE_PTHREAD_PRIO_INHERIT #undef HAVE_PTHREAD_PRIO_INHERIT
/* Define to 1 if you have the `readdir64' function. */
#undef HAVE_READDIR64
/* Define to 1 if you have the `round' function. */ /* Define to 1 if you have the `round' function. */
#undef HAVE_ROUND #undef HAVE_ROUND

View File

@ -104,6 +104,12 @@ typedef int (*qse_httpd_muxcb_t) (
void* cbarg void* cbarg
); );
typedef struct qse_httpd_dirent_t qse_httpd_dirent_t;
struct qse_httpd_dirent_t
{
};
typedef struct qse_httpd_cbs_t qse_httpd_cbs_t; typedef struct qse_httpd_cbs_t qse_httpd_cbs_t;
struct qse_httpd_cbs_t struct qse_httpd_cbs_t
{ {
@ -211,8 +217,6 @@ struct qse_httpd_cbs_t
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req); qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req);
int (*handle_request) ( int (*handle_request) (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req); qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req);
int (*listdir) (qse_httpd_t* httpd, const qse_mchar_t* path);
}; };
typedef struct qse_httpd_task_t qse_httpd_task_t; typedef struct qse_httpd_task_t qse_httpd_task_t;
@ -502,6 +506,14 @@ qse_httpd_task_t* qse_httpd_entaskfile (
qse_htre_t* req qse_htre_t* req
); );
qse_httpd_task_t* qse_httpd_entaskdir (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
qse_httpd_task_t* pred,
const qse_mchar_t* name,
qse_htre_t* req
);
qse_httpd_task_t* qse_httpd_entaskcgi ( qse_httpd_task_t* qse_httpd_entaskcgi (
qse_httpd_t* httpd, qse_httpd_t* httpd,
qse_httpd_client_t* client, qse_httpd_client_t* client,

View File

@ -20,7 +20,7 @@
#include "awk.h" #include "awk.h"
const qse_char_t* qse_awk_dflerrstr (qse_awk_t* awk, qse_awk_errnum_t errnum) const qse_char_t* qse_awk_dflerrstr (const qse_awk_t* awk, qse_awk_errnum_t errnum)
{ {
static const qse_char_t* errstr[] = static const qse_char_t* errstr[] =
{ {

View File

@ -25,7 +25,7 @@
extern "C" { extern "C" {
#endif #endif
const qse_char_t* qse_awk_dflerrstr (qse_awk_t* awk, qse_awk_errnum_t errnum); const qse_char_t* qse_awk_dflerrstr (const qse_awk_t* awk, qse_awk_errnum_t errnum);
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@ -1268,6 +1268,7 @@ qse_long_t qse_awk_rtx_hashval (qse_awk_rtx_t* rtx, qse_awk_val_t* v)
return -1; return -1;
} }
/* turn off the sign bit */
return hv & ~(((qse_ulong_t)1) << ((QSE_SIZEOF(qse_ulong_t) * 8) - 1)); return hv & ~(((qse_ulong_t)1) << ((QSE_SIZEOF(qse_ulong_t) * 8) - 1));
} }

View File

@ -34,9 +34,7 @@
# include <dos.h> # include <dos.h>
# include <errno.h> # include <errno.h>
#else #else
# include <dirent.h> # include "syscall.h"
# include <sys/stat.h>
# include <unistd.h>
#endif #endif
#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__) #if defined(_WIN32) || defined(__OS2__) || defined(__DOS__)
@ -326,40 +324,41 @@ static int get_next_segment (glob_t* g, segment_t* seg)
#if defined(_WIN32) #if defined(_WIN32)
struct DIR struct qse_dir_t
{ {
HANDLE h; HANDLE h;
WIN32_FIND_DATA wfd; WIN32_FIND_DATA wfd;
int done; int done;
}; };
typedef struct DIR DIR; typedef struct qse_dir_t qse_dir_t;
#elif defined(__OS2__) #elif defined(__OS2__)
struct DIR struct qse_dir_t
{ {
HDIR h; HDIR h;
FILEFINDBUF3L ffb; FILEFINDBUF3L ffb;
ULONG count; ULONG count;
}; };
typedef struct DIR DIR; typedef struct qse_dir_t qse_dir_t;
#elif defined(__DOS__) #elif defined(__DOS__)
struct DIR
struct qse_dir_t
{ {
struct find_t f; struct find_t f;
int done; int done;
}; };
typedef struct DIR DIR; typedef struct qse_dir_t qse_dir_t;
#endif #endif
static DIR* xopendir (glob_t* g, const qse_cstr_t* path) static qse_dir_t* xopendir (glob_t* g, const qse_cstr_t* path)
{ {
#if defined(_WIN32) #if defined(_WIN32)
/* ------------------------------------------------------------------- */ /* ------------------------------------------------------------------- */
DIR* dp; qse_dir_t* dp;
dp = QSE_MMGR_ALLOC (g->mmgr, QSE_SIZEOF(*dp)); dp = QSE_MMGR_ALLOC (g->mmgr, QSE_SIZEOF(*dp));
if (dp == QSE_NULL) return QSE_NULL; if (dp == QSE_NULL) return QSE_NULL;
@ -400,7 +399,7 @@ static DIR* xopendir (glob_t* g, const qse_cstr_t* path)
#elif defined(__OS2__) #elif defined(__OS2__)
/* ------------------------------------------------------------------- */ /* ------------------------------------------------------------------- */
DIR* dp; qse_dir_t* dp;
APIRET rc; APIRET rc;
qse_mchar_t* mptr; qse_mchar_t* mptr;
@ -463,7 +462,7 @@ static DIR* xopendir (glob_t* g, const qse_cstr_t* path)
#elif defined(__DOS__) #elif defined(__DOS__)
/* ------------------------------------------------------------------- */ /* ------------------------------------------------------------------- */
DIR* dp; qse_dir_t* dp;
unsigned int rc; unsigned int rc;
qse_mchar_t* mptr; qse_mchar_t* mptr;
qse_size_t wl, ml; qse_size_t wl, ml;
@ -519,13 +518,12 @@ static DIR* xopendir (glob_t* g, const qse_cstr_t* path)
#else #else
/* ------------------------------------------------------------------- */ /* ------------------------------------------------------------------- */
#if defined(QSE_CHAR_IS_MCHAR) #if defined(QSE_CHAR_IS_MCHAR)
return opendir ((path->len <= 0)? QSE_T("."): path->ptr); return QSE_OPENDIR ((path->len <= 0)? QSE_T("."): path->ptr);
#else #else
if (path->len <= 0) if (path->len <= 0)
{ {
return opendir (QSE_MT(".")); return QSE_OPENDIR (QSE_MT("."));
} }
else else
{ {
@ -534,7 +532,7 @@ static DIR* xopendir (glob_t* g, const qse_cstr_t* path)
mptr = wcs_to_mbuf (g, path->ptr, &g->mbuf); mptr = wcs_to_mbuf (g, path->ptr, &g->mbuf);
if (mptr == QSE_NULL) return QSE_NULL; if (mptr == QSE_NULL) return QSE_NULL;
return opendir (mptr); return QSE_OPENDIR (mptr);
} }
#endif #endif
/* ------------------------------------------------------------------- */ /* ------------------------------------------------------------------- */
@ -542,7 +540,7 @@ static DIR* xopendir (glob_t* g, const qse_cstr_t* path)
#endif #endif
} }
static int xreaddir (glob_t* g, DIR* dp, qse_str_t* path) static int xreaddir (glob_t* g, qse_dir_t* dp, qse_str_t* path)
{ {
#if defined(_WIN32) #if defined(_WIN32)
@ -615,14 +613,14 @@ static int xreaddir (glob_t* g, DIR* dp, qse_str_t* path)
#else #else
/* ------------------------------------------------------------------- */ /* ------------------------------------------------------------------- */
struct dirent* de; qse_dirent_t* de;
#if defined(QSE_CHAR_IS_MCHAR) #if defined(QSE_CHAR_IS_MCHAR)
/* nothing */ /* nothing */
#else #else
qse_size_t ml, wl, tmp; qse_size_t ml, wl, tmp;
#endif #endif
de = readdir (dp); de = QSE_READDIR (dp);
if (de == NULL) return 0; if (de == NULL) return 0;
#if defined(QSE_CHAR_IS_MCHAR) #if defined(QSE_CHAR_IS_MCHAR)
@ -640,7 +638,7 @@ static int xreaddir (glob_t* g, DIR* dp, qse_str_t* path)
#endif #endif
} }
static void xclosedir (glob_t* g, DIR* dp) static void xclosedir (glob_t* g, qse_dir_t* dp)
{ {
#if defined(_WIN32) #if defined(_WIN32)
FindClose (dp->h); FindClose (dp->h);
@ -652,7 +650,7 @@ static void xclosedir (glob_t* g, DIR* dp)
_dos_findclose (&dp->f); _dos_findclose (&dp->f);
QSE_MMGR_FREE (g->mmgr, dp); QSE_MMGR_FREE (g->mmgr, dp);
#else #else
closedir (dp); QSE_CLOSEDIR (dp);
#endif #endif
} }
@ -722,7 +720,7 @@ struct stack_node_t
{ {
qse_size_t tmp; qse_size_t tmp;
qse_size_t tmp2; qse_size_t tmp2;
DIR* dp; qse_dir_t* dp;
segment_t seg; segment_t seg;
stack_node_t* next; stack_node_t* next;
@ -731,7 +729,7 @@ struct stack_node_t
static int search (glob_t* g, segment_t* seg) static int search (glob_t* g, segment_t* seg)
{ {
DIR* dp; qse_dir_t* dp;
qse_size_t tmp, tmp2; qse_size_t tmp, tmp2;
#if defined(NO_RECURSION) #if defined(NO_RECURSION)

View File

@ -23,39 +23,42 @@
/* This file defines unix/linux system calls */ /* This file defines unix/linux system calls */
#ifdef HAVE_SYS_TYPES_H #if defined(HAVE_SYS_TYPES_H)
# include <sys/types.h> # include <sys/types.h>
#endif #endif
#ifdef HAVE_UNISTD_H #if defined(HAVE_UNISTD_H)
# include <unistd.h> # include <unistd.h>
#endif #endif
#ifdef HAVE_SYS_WAIT_H #if defined(HAVE_SYS_WAIT_H)
# include <sys/wait.h> # include <sys/wait.h>
#endif #endif
#ifdef HAVE_SIGNAL_H #if defined(HAVE_SIGNAL_H)
# include <signal.h> # include <signal.h>
#endif #endif
#ifdef HAVE_ERRNO_H #if defined(HAVE_ERRNO_H)
# include <errno.h> # include <errno.h>
#endif #endif
#ifdef HAVE_FCNTL_H #if defined(HAVE_FCNTL_H)
# include <fcntl.h> # include <fcntl.h>
#endif #endif
#ifdef HAVE_TIME_H #if defined(HAVE_TIME_H)
# include <time.h> # include <time.h>
#endif #endif
#ifdef HAVE_SYS_TIME_H #if defined(HAVE_SYS_TIME_H)
# include <sys/time.h> # include <sys/time.h>
#endif #endif
#ifdef HAVE_UTIME_H #if defined(HAVE_UTIME_H)
# include <utime.h> # include <utime.h>
#endif #endif
#ifdef HAVE_SYS_RESOURCE_H #if defined(HAVE_SYS_RESOURCE_H)
# include <sys/resource.h> # include <sys/resource.h>
#endif #endif
#ifdef HAVE_SYS_STAT_H #if defined(HAVE_SYS_STAT_H)
# include <sys/stat.h> # include <sys/stat.h>
#endif #endif
#if defined(HAVE_DIRENT_H)
# include <dirent.h>
#endif
#if defined(QSE_USE_SYSCALL) && defined(HAVE_SYS_SYSCALL_H) #if defined(QSE_USE_SYSCALL) && defined(HAVE_SYS_SYSCALL_H)
# include <sys/syscall.h> # include <sys/syscall.h>
@ -355,6 +358,19 @@
# define QSE_UTIMES(path,t) utimes(path,t) # define QSE_UTIMES(path,t) utimes(path,t)
#endif #endif
/* ===== DIRECTORY - not really system calls ===== */
typedef DIR qse_dir_t;
#define QSE_OPENDIR(name) opendir(name)
#define QSE_CLOSEDIR(name) closedir(name)
#if defined(HAVE_READDIR64)
typedef struct dirent64 qse_dirent_t;
# define QSE_READDIR(x) readdir64(x)
#else
typedef struct dirent qse_dirent_t;
# define QSE_READDIR(x) readdir(x)
#endif
/* ------------------------------------------------------------------------ */ /* ------------------------------------------------------------------------ */
#if defined(__linux) && defined(__GNUC__) && defined(__x86_64) #if defined(__linux) && defined(__GNUC__) && defined(__x86_64)

View File

@ -14,6 +14,8 @@ libqsenet_la_SOURCES = \
htrd.c \ htrd.c \
httpd.c \ httpd.c \
httpd-cgi.c \ httpd-cgi.c \
httpd-dir.c \
httpd-file.c \
httpd-proxy.c \ httpd-proxy.c \
httpd-resol.c \ httpd-resol.c \
httpd-std.c \ httpd-std.c \

View File

@ -79,8 +79,8 @@ am__installdirs = "$(DESTDIR)$(libdir)"
LTLIBRARIES = $(lib_LTLIBRARIES) LTLIBRARIES = $(lib_LTLIBRARIES)
libqsenet_la_DEPENDENCIES = libqsenet_la_DEPENDENCIES =
am_libqsenet_la_OBJECTS = http.lo htre.lo htrd.lo httpd.lo \ am_libqsenet_la_OBJECTS = http.lo htre.lo htrd.lo httpd.lo \
httpd-cgi.lo httpd-proxy.lo httpd-resol.lo httpd-std.lo \ httpd-cgi.lo httpd-dir.lo httpd-file.lo httpd-proxy.lo \
httpd-task.lo upxd.lo httpd-resol.lo httpd-std.lo httpd-task.lo upxd.lo
libqsenet_la_OBJECTS = $(am_libqsenet_la_OBJECTS) libqsenet_la_OBJECTS = $(am_libqsenet_la_OBJECTS)
libqsenet_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \ libqsenet_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
@ -271,6 +271,8 @@ libqsenet_la_SOURCES = \
htrd.c \ htrd.c \
httpd.c \ httpd.c \
httpd-cgi.c \ httpd-cgi.c \
httpd-dir.c \
httpd-file.c \
httpd-proxy.c \ httpd-proxy.c \
httpd-resol.c \ httpd-resol.c \
httpd-std.c \ httpd-std.c \
@ -357,6 +359,8 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/htre.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/htre.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/httpd-cgi.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/httpd-cgi.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/httpd-dir.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/httpd-file.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/httpd-proxy.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/httpd-proxy.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/httpd-resol.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/httpd-resol.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/httpd-std.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/httpd-std.Plo@am__quote@

487
qse/lib/net/httpd-dir.c Normal file
View File

@ -0,0 +1,487 @@
/*
* $Id$
*
Copyright 2006-2012 Chung, Hyung-Hwan.
This file is part of QSE.
QSE is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
QSE is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with QSE. If not, see <htrd://www.gnu.org/licenses/>.
*/
#include "httpd.h"
#include "../cmn/mem.h"
#include "../cmn/syscall.h"
#include <qse/cmn/str.h>
#include <qse/cmn/stdio.h> /* TODO: remove this */
typedef struct task_dir_t task_dir_t;
struct task_dir_t
{
const qse_mchar_t* path;
qse_http_version_t version;
int keepalive;
};
typedef struct task_dseg_t task_dseg_t;
struct task_dseg_t
{
const qse_mchar_t* path;
qse_dir_t* handle;
qse_dirent_t* dent;
int header_added;
int footer_pending;
/*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;
qse_size_t chunklen;
};
static int task_init_dseg (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_dseg_t* xtn = qse_httpd_gettaskxtn (httpd, task);
QSE_MEMCPY (xtn, task->ctx, QSE_SIZEOF(*xtn));
qse_mbscpy ((qse_mchar_t*)(xtn + 1), xtn->path);
xtn->path = (qse_mchar_t*)(xtn + 1);
task->ctx = xtn;
return 0;
}
static void task_fini_dseg (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_dseg_t* ctx = (task_dseg_t*)task->ctx;
QSE_CLOSEDIR (ctx->handle);
}
static int task_main_dseg_chunked (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_dseg_t* ctx = (task_dseg_t*)task->ctx;
qse_ssize_t n;
int x;
if (ctx->bufpos < ctx->buflen) goto send_dirlist;
/* the buffer size is fixed to QSE_COUNTOF(ctx->buf).
* the number of digits need to hold the the size converted to
* a hexadecimal notation is roughly (log16(QSE_COUNTOF(ctx->buf) + 1).
* it should be safter to use ceil(log16(QSE_COUNTOF(ctx->buf)) + 1
* for precision issues.
*
* 16**X = QSE_COUNTOF(ctx->buf).
* X = log16(QSE_COUNTOF(ctx->buf).
* X + 1 is a required number of digits.
*
* Since log16 is not provided, we should use a natural log function
* whose base is the constant e (2.718).
*
* log16(n) = log(n) / log(16)
*
* The final fomula is here.
*
* X = ceil((log(QSE_COUNTOF(ctx->buf)) / log(16))) + 1;
*
* However, i won't use these floating-point opertions.
* instead i'll reserve a hardcoded size. so when you change
* the size of the buffer arrray, you should check this size.
*/
#define SIZE_CHLEN 4
#define SIZE_CHLENCRLF 2
#define SIZE_CHENDCRLF 2
/* reserve space to fill with the chunk length
* 4 for the actual chunk length and +2 for \r\n */
ctx->buflen = SIZE_CHLEN + SIZE_CHLENCRLF;
/* free space remaing in the buffer for the chunk data */
ctx->bufrem = QSE_COUNTOF(ctx->buf) - ctx->buflen - SIZE_CHENDCRLF;
if (ctx->footer_pending)
{
x = snprintf (
&ctx->buf[ctx->buflen],
ctx->bufrem,
QSE_MT("</ul></body></html>\r\n0\r\n"));
if (x == -1 || x >= ctx->bufrem)
{
/* return an error if the buffer is too small to hold the
* trailing footer. you need to increate the buffer size */
return -1;
}
ctx->buflen += x;
ctx->chunklen = ctx->buflen - 5; /* -5 for \r\n0\r\n added above */
/* CHENDCRLF */
ctx->buf[ctx->buflen++] = QSE_MT('\r');
ctx->buf[ctx->buflen++] = QSE_MT('\n');
goto set_chunklen;
}
if (!ctx->header_added)
{
/* compose the header since this is the first time. */
x = snprintf (
&ctx->buf[ctx->buflen],
ctx->bufrem,
QSE_MT("<html><head><title>Directory Listing</title></head><body><b>%s</b><ul><li><a href='../'>..</a></li>"),
ctx->path
);
if (x == -1 || x >= ctx->bufrem)
{
/* return an error if the buffer is too small to hold the header.
* you need to increate the buffer size */
return -1;
}
ctx->buflen += x;
ctx->bufrem -= x;
ctx->header_added = 1;
}
if (!ctx->dent)
ctx->dent = QSE_READDIR (ctx->handle);
do
{
if (!ctx->dent)
{
// TODO: check if errno has changed from before QSE_READDIR().
// and return -1 if so.
x = snprintf (
&ctx->buf[ctx->buflen],
ctx->bufrem,
QSE_MT("</ul></body></html>\r\n0\r\n"));
if (x == -1 || x >= ctx->bufrem)
{
ctx->footer_pending = 1;
ctx->chunklen = ctx->buflen;
/* CHENDCRLF */
ctx->buf[ctx->buflen++] = QSE_MT('\r');
ctx->buf[ctx->buflen++] = QSE_MT('\n');
}
else
{
ctx->buflen += x;
ctx->chunklen = ctx->buflen - 5;
/* CHENDCRLF */
ctx->buf[ctx->buflen++] = QSE_MT('\r');
ctx->buf[ctx->buflen++] = QSE_MT('\n');
}
break;
}
else if (qse_mbscmp (ctx->dent->d_name, QSE_MT(".")) != 0 &&
qse_mbscmp (ctx->dent->d_name, QSE_MT("..")) != 0)
{
x = snprintf (
&ctx->buf[ctx->buflen],
ctx->bufrem,
QSE_MT("<li><a href='%s%s'>%s%s</a></li>"),
ctx->dent->d_name,
(ctx->dent->d_type == DT_DIR? QSE_MT("/"): QSE_MT("")),
ctx->dent->d_name,
(ctx->dent->d_type == DT_DIR? QSE_MT("/"): QSE_MT(""))
);
if (x == -1 || x >= ctx->bufrem)
{
/* buffer not large enough to hold this entry */
ctx->chunklen = ctx->buflen;
/* CHENDCRLF */
ctx->buf[ctx->buflen++] = QSE_MT('\r');
ctx->buf[ctx->buflen++] = QSE_MT('\n');
break;
}
else
{
ctx->buflen += x;
ctx->bufrem -= x;
}
}
ctx->dent = QSE_READDIR (ctx->handle);
}
while (1);
set_chunklen:
/* right alignment with space padding on the left */
/* TODO: change snprintf to qse_fmtuintmaxtombs() */
x = snprintf (
ctx->buf, (SIZE_CHLEN + SIZE_CHLENCRLF) - 1,
QSE_MT("%*lX"), (int)(SIZE_CHLEN + SIZE_CHLENCRLF - 2),
(unsigned long)(ctx->chunklen - (SIZE_CHLEN + SIZE_CHLENCRLF)));
/* CHLENCRLF */
ctx->buf[x] = QSE_MT('\r');
ctx->buf[x+1] = QSE_MT('\n');
/* skip leading space padding */
for (x = 0; ctx->buf[x] == QSE_MT(' '); x++) ctx->buflen--;
ctx->bufpos = x;
send_dirlist:
httpd->errnum = QSE_HTTPD_ENOERR;
n = httpd->cbs->client.send (
httpd, client, &ctx->buf[ctx->bufpos], ctx->buflen);
if (n <= -1) return -1;
/* NOTE if (n == 0), it will enter an infinite loop */
ctx->bufpos += n;
ctx->buflen -= n;
return (ctx->bufpos < ctx->buflen || ctx->footer_pending || ctx->dent)? 1: 0;
}
static int task_main_dseg_nochunk (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_dseg_t* ctx = (task_dseg_t*)task->ctx;
qse_ssize_t n;
int x;
if (ctx->bufpos < ctx->buflen) goto send_dirlist;
ctx->bufpos = 0;
ctx->buflen = 0;
ctx->bufrem = QSE_COUNTOF(ctx->buf);
if (ctx->footer_pending)
{
x = snprintf (
&ctx->buf[ctx->buflen],
ctx->bufrem,
"</ul></body></html>");
if (x == -1 || x >= ctx->bufrem)
{
/* return an error if the buffer is too small to hold the
* trailing footer. you need to increate the buffer size */
return -1;
}
ctx->buflen += x;
goto send_dirlist;
}
if (!ctx->header_added)
{
/* compose the header since this is the first time. */
x = snprintf (
&ctx->buf[ctx->buflen],
ctx->bufrem,
QSE_MT("<html><head><title>Directory Listing</title></head><body><b>%s</b><ul><li><a href='../'>..</a></li>"),
ctx->path
);
if (x == -1 || x >= ctx->bufrem)
{
/* return an error if the buffer is too small to hold the header.
* you need to increate the buffer size */
return -1;
}
ctx->buflen += x;
ctx->bufrem -= x;
ctx->header_added = 1;
}
if (ctx->dent == QSE_NULL)
ctx->dent = QSE_READDIR (ctx->handle);
do
{
if (ctx->dent == QSE_NULL)
{
// TODO: check if errno has changed from before QSE_READDIR().
// and return -1 if so.
x = snprintf (
&ctx->buf[ctx->buflen],
ctx->bufrem,
"</ul></body></html>");
if (x == -1 || x >= ctx->bufrem)
{
ctx->footer_pending = 1;
}
else
{
ctx->buflen += x;
}
break;
}
else if (qse_mbscmp (ctx->dent->d_name, QSE_MT(".")) != 0 &&
qse_mbscmp (ctx->dent->d_name, QSE_MT("..")) != 0)
{
x = snprintf (
&ctx->buf[ctx->buflen],
ctx->bufrem,
"<li><a href='%s%s'>%s%s</a></li>",
ctx->dent->d_name,
(ctx->dent->d_type == DT_DIR? "/": ""),
ctx->dent->d_name,
(ctx->dent->d_type == DT_DIR? "/": "")
);
if (x == -1 || x >= ctx->bufrem)
{
/* buffer not large enough to hold this entry */
break;
}
else
{
ctx->buflen += x;
ctx->bufrem -= x;
}
}
ctx->dent = QSE_READDIR (ctx->handle);
}
while (1);
send_dirlist:
httpd->errnum = QSE_HTTPD_ENOERR;
n = httpd->cbs->client.send (
httpd, client, &ctx->buf[ctx->bufpos], ctx->buflen);
if (n <= -1) return -1;
ctx->bufpos += n;
ctx->buflen -= n;
return (ctx->bufpos < ctx->buflen || ctx->footer_pending || ctx->dent)? 1: 0;
}
static qse_httpd_task_t* entask_directory_segment (
qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_httpd_task_t* pred, qse_dir_t* handle, const qse_mchar_t* path, int keepalive)
{
qse_httpd_task_t task;
task_dseg_t data;
QSE_MEMSET (&data, 0, QSE_SIZEOF(data));
data.handle = handle;
data.path = path;
QSE_MEMSET (&task, 0, QSE_SIZEOF(task));
task.init = task_init_dseg;
task.main = (keepalive)? task_main_dseg_chunked: task_main_dseg_nochunk;
task.fini = task_fini_dseg;
task.ctx = &data;
qse_printf (QSE_T("Debug: entasking directory segment (%d)\n"), client->handle.i);
return qse_httpd_entask (httpd, client, pred, &task, QSE_SIZEOF(data) + qse_mbslen(path) + 1);
}
/*------------------------------------------------------------------------*/
static int task_init_dir (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_dir_t* xtn = qse_httpd_gettaskxtn (httpd, task);
/* deep-copy the context data to the extension area */
QSE_MEMCPY (xtn, task->ctx, QSE_SIZEOF(*xtn));
qse_mbscpy ((qse_mchar_t*)(xtn + 1), xtn->path);
xtn->path = (qse_mchar_t*)(xtn + 1);
/* switch the context to the extension area */
task->ctx = xtn;
return 0;
}
static QSE_INLINE int task_main_dir (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_dir_t* dir;
qse_httpd_task_t* x;
qse_dir_t* handle = QSE_NULL;
dir = (task_dir_t*)task->ctx;
x = task;
if (qse_mbsend (dir->path, QSE_MT("/")))
{
handle = QSE_OPENDIR (dir->path);
if (handle)
{
x = qse_httpd_entaskformat (
httpd, client, x,
QSE_MT("HTTP/%d.%d 200 OK\r\nConnection: %s\r\nContent-Type: text/html\r\n%s\r\n"),
dir->version.major, dir->version.minor,
(dir->keepalive? QSE_MT("keep-alive"): QSE_MT("close")),
(dir->keepalive? QSE_MT("Transfer-Encoding: chunked\r\n"): QSE_MT(""))
);
if (x) x = entask_directory_segment (httpd, client, x, handle, dir->path, dir->keepalive);
if (x) return 0;
QSE_CLOSEDIR (handle);
return -1;
}
else
{
int http_errnum;
http_errnum = (errno == ENOENT)? 404:
(errno == EACCES)? 403: 500;
x = qse_httpd_entask_error (
httpd, client, x, http_errnum,
&dir->version, dir->keepalive);
QSE_CLOSEDIR (handle);
return (x == QSE_NULL)? -1: 0;
}
}
else
{
x = qse_httpd_entaskformat (
httpd, client, x,
QSE_MT("HTTP/%d.%d 301 Moved Permanently\r\nContent-Length: 0\r\nConnection: %s\r\nLocation: %s/\r\n\r\n"),
dir->version.major, dir->version.minor,
(dir->keepalive? QSE_MT("keep-alive"): QSE_MT("close")),
dir->path
);
return (x == QSE_NULL)? -1: 0;
}
}
qse_httpd_task_t* qse_httpd_entaskdir (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
qse_httpd_task_t* pred,
const qse_mchar_t* path,
qse_htre_t* req)
{
qse_httpd_task_t task;
task_dir_t data;
QSE_MEMSET (&data, 0, QSE_SIZEOF(data));
data.path = path;
data.version = *qse_htre_getversion(req);
data.keepalive = (req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE);
QSE_MEMSET (&task, 0, QSE_SIZEOF(task));
task.init = task_init_dir;
task.main = task_main_dir;
task.ctx = &data;
return qse_httpd_entask (httpd, client, pred, &task,
QSE_SIZEOF(task_dir_t) + qse_mbslen(path) + 1);
}

323
qse/lib/net/httpd-file.c Normal file
View File

@ -0,0 +1,323 @@
/*
* $Id$
*
Copyright 2006-2012 Chung, Hyung-Hwan.
This file is part of QSE.
QSE is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
QSE is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with QSE. If not, see <htrd://www.gnu.org/licenses/>.
*/
#include "httpd.h"
#include "../cmn/mem.h"
#include "../cmn/syscall.h"
#include <qse/cmn/str.h>
#include <qse/cmn/stdio.h> /* TODO: remove this */
typedef struct task_file_t task_file_t;
struct task_file_t
{
const qse_mchar_t* path;
qse_http_range_t range;
qse_http_version_t version;
int keepalive;
};
typedef struct task_fseg_t task_fseg_t;
struct task_fseg_t
{
qse_ubi_t handle;
qse_foff_t left;
qse_foff_t offset;
};
static int task_init_fseg (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_fseg_t* xtn = qse_httpd_gettaskxtn (httpd, task);
QSE_MEMCPY (xtn, task->ctx, QSE_SIZEOF(*xtn));
task->ctx = xtn;
return 0;
}
static void task_fini_fseg (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_fseg_t* ctx = (task_fseg_t*)task->ctx;
httpd->cbs->file.close (httpd, ctx->handle);
}
static int task_main_fseg (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
qse_ssize_t n;
qse_size_t count;
task_fseg_t* ctx = (task_fseg_t*)task->ctx;
count = MAX_SEND_SIZE;
if (count >= ctx->left) count = ctx->left;
/* TODO: more adjustment needed for OS with different sendfile semantics... */
n = httpd->cbs->client.sendfile (
httpd, client, ctx->handle, &ctx->offset, count);
if (n <= -1)
{
/* HANDLE EGAIN specially??? */
return -1; /* TODO: any logging */
}
if (n == 0 && count > 0)
{
/* The file could be truncated when this condition is set.
* The content-length sent in the header can't be fulfilled.
* So let's return an error here so that the main loop abort
* the connection. */
/* TODO: any logging....??? */
return -1;
}
ctx->left -= n;
if (ctx->left <= 0) return 0;
return 1; /* more work to do */
}
static qse_httpd_task_t* entask_file_segment (
qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_httpd_task_t* pred,
qse_ubi_t handle, qse_foff_t offset, qse_foff_t size)
{
qse_httpd_task_t task;
task_fseg_t data;
QSE_MEMSET (&data, 0, QSE_SIZEOF(data));
data.handle = handle;
data.offset = offset;
data.left = size;
QSE_MEMSET (&task, 0, QSE_SIZEOF(task));
task.init = task_init_fseg;
task.main = task_main_fseg;
task.fini = task_fini_fseg;
task.ctx = &data;
qse_printf (QSE_T("Debug: entasking file segment (%d)\n"), client->handle.i);
return qse_httpd_entask (httpd, client, pred, &task, QSE_SIZEOF(data));
}
/*------------------------------------------------------------------------*/
static int task_init_file (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_file_t* xtn = qse_httpd_gettaskxtn (httpd, task);
QSE_MEMCPY (xtn, task->ctx, QSE_SIZEOF(*xtn));
qse_mbscpy ((qse_mchar_t*)(xtn + 1), xtn->path);
xtn->path = (qse_mchar_t*)(xtn + 1);
task->ctx = xtn;
return 0;
}
static QSE_INLINE int task_main_file (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_file_t* file;
qse_httpd_task_t* x;
qse_ubi_t handle;
int fileopen = 0;
qse_httpd_stat_t st;
file = (task_file_t*)task->ctx;
x = task;
/* TODO: if you should deal with files on a network-mounted drive,
setting a trigger or non-blocking I/O are needed. */
qse_printf (QSE_T("opening file %hs\n"), file->path);
httpd->errnum = QSE_HTTPD_ENOERR;
if (httpd->cbs->file.stat (httpd, file->path, &st) <= -1)
{
int http_errnum;
http_errnum = (httpd->errnum == QSE_HTTPD_ENOENT)? 404:
(httpd->errnum == QSE_HTTPD_EACCES)? 403: 500;
x = qse_httpd_entask_error (
httpd, client, x, http_errnum,
&file->version, file->keepalive);
goto no_file_send;
}
httpd->errnum = QSE_HTTPD_ENOERR;
if (httpd->cbs->file.ropen (httpd, file->path, &handle) <= -1)
{
int http_errnum;
http_errnum = (httpd->errnum == QSE_HTTPD_ENOENT)? 404:
(httpd->errnum == QSE_HTTPD_EACCES)? 403: 500;
x = qse_httpd_entask_error (
httpd, client, x, http_errnum,
&file->version, file->keepalive);
goto no_file_send;
}
fileopen = 1;
if (file->range.type != QSE_HTTP_RANGE_NONE)
{
if (file->range.type == QSE_HTTP_RANGE_SUFFIX)
{
if (file->range.to > st.size) file->range.to = st.size;
file->range.from = st.size - file->range.to;
file->range.to = file->range.to + file->range.from;
if (st.size > 0) file->range.to--;
}
if (file->range.from >= st.size)
{
x = qse_httpd_entask_error (
httpd, client, x, 416, &file->version, file->keepalive);
goto no_file_send;
}
if (file->range.to >= st.size) file->range.to = st.size - 1;
#if (QSE_SIZEOF_LONG_LONG > 0)
x = qse_httpd_entaskformat (
httpd, client, x,
QSE_MT("HTTP/%d.%d 206 Partial Content\r\nConnection: %s\r\n%s%s%sContent-Length: %llu\r\nContent-Range: bytes %llu-%llu/%llu\r\n\r\n"),
file->version.major,
file->version.minor,
(file->keepalive? QSE_MT("keep-alive"): QSE_MT("close")),
(st.mime? QSE_MT("Content-Type: "): QSE_MT("")),
(st.mime? st.mime: QSE_MT("")),
(st.mime? QSE_MT("\r\n"): QSE_MT("")),
(unsigned long long)(file->range.to - file->range.from + 1),
(unsigned long long)file->range.from,
(unsigned long long)file->range.to,
(unsigned long long)st.size
);
#else
x = qse_httpd_entaskformat (
httpd, client, x,
QSE_MT("HTTP/%d.%d 206 Partial Content\r\nConnection: %s\r\n%s%s%sContent-Length: %lu\r\nContent-Range: bytes %lu-%lu/%lu\r\n\r\n"),
file->version.major,
file->version.minor,
(file->keepalive? QSE_MT("keep-alive"): QSE_MT("close")),
(st.mime? QSE_MT("Content-Type: "): QSE_MT("")),
(st.mime? st.mime: QSE_MT("")),
(st.mime? QSE_MT("\r\n"): QSE_MT("")),
(unsigned long)(file->range.to - file->range.from + 1),
(unsigned long)file->range.from,
(unsigned long)file->range.to,
(unsigned long)st.size
);
#endif
if (x)
{
x = entask_file_segment (
httpd, client, x,
handle,
file->range.from,
(file->range.to - file->range.from + 1)
);
}
}
else
{
/* TODO: int64 format.... don't hard code it llu */
/* wget 1.8.2 set 'Connection: keep-alive' in the http 1.0 header.
* if the reply doesn't contain 'Connection: keep-alive', it didn't
* close connection.*/
#if (QSE_SIZEOF_LONG_LONG > 0)
x = qse_httpd_entaskformat (
httpd, client, x,
QSE_MT("HTTP/%d.%d 200 OK\r\nConnection: %s\r\n%s%s%sContent-Length: %llu\r\n\r\n"),
file->version.major, file->version.minor,
(file->keepalive? QSE_MT("keep-alive"): QSE_MT("close")),
(st.mime? QSE_MT("Content-Type: "): QSE_MT("")),
(st.mime? st.mime: QSE_MT("")),
(st.mime? QSE_MT("\r\n"): QSE_MT("")),
(unsigned long long)st.size
);
#else
x = qse_httpd_entaskformat (
httpd, client, x,
QSE_MT("HTTP/%d.%d 200 OK\r\nConnection: %s\r\n%s%s%sContent-Length: %lu\r\n\r\n"),
file->version.major,
file->version.minor,
(file->keepalive? QSE_MT("keep-alive"): QSE_MT("close")),
(st.mime? QSE_MT("Content-Type: "): QSE_MT("")),
(st.mime? st.mime: QSE_MT("")),
(st.mime? QSE_MT("\r\n"): QSE_MT("")),
(unsigned long)st.size
);
#endif
if (x) x = entask_file_segment (httpd, client, x, handle, 0, st.size);
}
if (x) return 0;
httpd->cbs->file.close (httpd, handle);
return -1;
no_file_send:
if (fileopen) httpd->cbs->file.close (httpd, handle);
return (x == QSE_NULL)? -1: 0;
}
qse_httpd_task_t* qse_httpd_entaskfile (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
qse_httpd_task_t* pred,
const qse_mchar_t* path,
qse_htre_t* req)
{
qse_httpd_task_t task;
task_file_t data;
const qse_htre_hdrval_t* tmp;
QSE_MEMSET (&data, 0, QSE_SIZEOF(data));
data.path = path;
data.version = *qse_htre_getversion(req);
data.keepalive = (req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE);
tmp = qse_htre_getheaderval(req, QSE_MT("Range"));
if (tmp)
{
while (tmp->next) tmp = tmp->next; /* get the last value */
if (qse_parsehttprange (tmp->ptr, &data.range) <= -1)
{
return qse_httpd_entaskerror (httpd, client, pred, 416, req);
}
}
else
{
data.range.type = QSE_HTTP_RANGE_NONE;
}
/*
TODO: If-Modified-Since...
tmp = qse_htre_getheaderval(req, QSE_MT("If-Modified-Since"));
if (tmp)
{
}
*/
QSE_MEMSET (&task, 0, QSE_SIZEOF(task));
task.init = task_init_file;
task.main = task_main_file;
task.ctx = &data;
return qse_httpd_entask (httpd, client, pred, &task,
QSE_SIZEOF(task_file_t) + qse_mbslen(path) + 1);
}

View File

@ -1423,9 +1423,14 @@ qse_printf (QSE_T("Entasking chunked CGI...\n"));
#else #else
if (peek) if (peek)
{ {
qse_stat_t st;
qse_httpd_discardcontent (httpd, req); qse_httpd_discardcontent (httpd, req);
task = qse_httpd_entaskfile (
httpd, client, QSE_NULL, qpath, req); if (QSE_LSTAT (qpath, &st) == 0 && S_ISDIR(st.st_mode))
task = qse_httpd_entaskdir (httpd, client, QSE_NULL, qpath, req);
else
task = qse_httpd_entaskfile (httpd, client, QSE_NULL, qpath, req);
if (task == QSE_NULL) goto oops; if (task == QSE_NULL) goto oops;
} }
#endif #endif
@ -1573,11 +1578,6 @@ static int handle_request (
} }
} }
int list_directory (qse_httpd_t* httpd, const qse_mchar_t* path)
{
return 404;
}
static qse_httpd_cbs_t httpd_standard_callbacks = static qse_httpd_cbs_t httpd_standard_callbacks =
{ {
/* server */ /* server */
@ -1622,8 +1622,6 @@ static qse_httpd_cbs_t httpd_standard_callbacks =
/* http request */ /* http request */
peek_request, peek_request,
handle_request, handle_request,
list_directory
}; };
int qse_httpd_loopstd (qse_httpd_t* httpd, qse_ntime_t timeout) int qse_httpd_loopstd (qse_httpd_t* httpd, qse_ntime_t timeout)

View File

@ -32,7 +32,6 @@
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
#include <dirent.h>
/* TODO: /* TODO:
* many functions in this file use qse_size_t. * many functions in this file use qse_size_t.
@ -440,830 +439,6 @@ qse_httpd_task_t* qse_httpd_entaskauth (
/*------------------------------------------------------------------------*/ /*------------------------------------------------------------------------*/
#if 0
typedef struct task_dir_t task_dir_t;
struct task_dir_t
{
qse_ubi_t handle;
int header_added;
int footer_pending;
struct dirent* dent;
/*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;
qse_size_t chunklen;
};
static int task_init_dir (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_dir_t* xtn = qse_httpd_gettaskxtn (httpd, task);
QSE_MEMSET (xtn, 0, QSE_SIZEOF(*xtn));
xtn->handle = *(qse_ubi_t*)task->ctx;
task->ctx = xtn;
return 0;
}
static void task_fini_dir (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_dir_t* ctx = (task_dir_t*)task->ctx;
closedir (ctx->handle.ptr);
}
static int task_main_dir (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_dir_t* ctx = (task_dir_t*)task->ctx;
qse_ssize_t n;
int x;
if (ctx->bufpos < ctx->buflen) goto send_dirlist;
/* the buffer size is fixed to QSE_COUNTOF(ctx->buf).
* the number of digits need to hold the the size converted to
* a hexadecimal notation is roughly (log16(QSE_COUNTOF(ctx->buf) + 1).
* it should be safter to use ceil(log16(QSE_COUNTOF(ctx->buf)) + 1
* for precision issues.
*
* 16**X = QSE_COUNTOF(ctx->buf).
* X = log16(QSE_COUNTOF(ctx->buf).
* X + 1 is a required number of digits.
*
* Since log16 is not provided, we should use a natural log function
* whose base is the constant e (2.718).
*
* log16(n) = log(n) / log(16)
*
* The final fomula is here.
*
* X = ceil((log(QSE_COUNTOF(ctx->buf)) / log(16))) + 1;
*
* However, i won't use these floating-point opertions.
* instead i'll reserve a hardcoded size. so when you change
* the size of the buffer arrray, you should check this size.
*/
#define SIZE_CHLEN 4
#define SIZE_CHLENCRLF 2
#define SIZE_CHENDCRLF 2
/* reserve space to fill with the chunk length
* 4 for the actual chunk length and +2 for \r\n */
ctx->buflen = SIZE_CHLEN + SIZE_CHLENCRLF;
/* free space remaing in the buffer for the chunk data */
ctx->bufrem = QSE_COUNTOF(ctx->buf) - ctx->buflen - SIZE_CHENDCRLF;
if (ctx->footer_pending)
{
x = snprintf (
&ctx->buf[ctx->buflen],
ctx->bufrem,
QSE_MT("</ul></body></html>\r\n0\r\n"));
if (x == -1 || x >= ctx->bufrem)
{
/* return an error if the buffer is too small to hold the
* trailing footer. you need to increate the buffer size */
return -1;
}
ctx->buflen += x;
ctx->chunklen = ctx->buflen - 5; /* -5 for \r\n0\r\n added above */
/* CHENDCRLF */
ctx->buf[ctx->buflen++] = QSE_MT('\r');
ctx->buf[ctx->buflen++] = QSE_MT('\n');
goto set_chunklen;
}
if (!ctx->header_added)
{
/* compose the header since this is the first time. */
/* TODO: get the actual path ... and use it in the body or title. */
x = snprintf (
&ctx->buf[ctx->buflen],
ctx->bufrem,
QSE_MT("<html><head><title>Directory Listing</title></head><body>index of xxxx<ul>")
);
if (x == -1 || x >= ctx->bufrem)
{
/* return an error if the buffer is too small to hold the header.
* you need to increate the buffer size */
return -1;
}
ctx->buflen += x;
ctx->bufrem -= x;
ctx->header_added = 1;
}
if (ctx->dent == QSE_NULL)
ctx->dent = readdir (ctx->handle.ptr);
do
{
if (ctx->dent == QSE_NULL)
{
// TODO: check if errno has changed from before readdir().
// and return -1 if so.
x = snprintf (
&ctx->buf[ctx->buflen],
ctx->bufrem,
QSE_MT("</ul></body></html>\r\n0\r\n"));
if (x == -1 || x >= ctx->bufrem)
{
ctx->footer_pending = 1;
ctx->chunklen = ctx->buflen;
/* CHENDCRLF */
ctx->buf[ctx->buflen++] = QSE_MT('\r');
ctx->buf[ctx->buflen++] = QSE_MT('\n');
}
else
{
ctx->buflen += x;
ctx->chunklen = ctx->buflen - 5;
/* CHENDCRLF */
ctx->buf[ctx->buflen++] = QSE_MT('\r');
ctx->buf[ctx->buflen++] = QSE_MT('\n');
}
break;
}
else
{
x = snprintf (
&ctx->buf[ctx->buflen],
ctx->bufrem,
QSE_MT("<li><a href='%s%s'>%s%s</a></li>"),
ctx->dent->d_name,
(ctx->dent->d_type == DT_DIR? QSE_MT("/"): QSE_MT("")),
ctx->dent->d_name,
(ctx->dent->d_type == DT_DIR? QSE_MT("/"): QSE_MT(""))
);
if (x == -1 || x >= ctx->bufrem)
{
/* buffer not large enough to hold this entry */
ctx->chunklen = ctx->buflen;
/* CHENDCRLF */
ctx->buf[ctx->buflen++] = QSE_MT('\r');
ctx->buf[ctx->buflen++] = QSE_MT('\n');
break;
}
else
{
ctx->buflen += x;
ctx->bufrem -= x;
}
}
ctx->dent = readdir (ctx->handle.ptr);
}
while (1);
set_chunklen:
/* right alignment with space padding on the left */
/* TODO: change snprintf to qse_fmtuintmaxtombs() */
x = snprintf (
ctx->buf, (SIZE_CHLEN + SIZE_CHLENCRLF) - 1,
QSE_MT("%*lX"), (int)(SIZE_CHLEN + SIZE_CHLENCRLF - 2),
(unsigned long)(ctx->chunklen - (SIZE_CHLEN + SIZE_CHLENCRLF)));
/* CHLENCRLF */
ctx->buf[x] = QSE_MT('\r');
ctx->buf[x+1] = QSE_MT('\n');
/* skip leading space padding */
for (x = 0; ctx->buf[x] == QSE_MT(' '); x++) ctx->buflen--;
ctx->bufpos = x;
send_dirlist:
httpd->errnum = QSE_HTTPD_ENOERR;
n = httpd->cbs->client.send (
httpd, client, &ctx->buf[ctx->bufpos], ctx->buflen);
if (n <= -1) return -1;
/* NOTE if (n == 0), it will enter an infinite loop */
ctx->bufpos += n;
ctx->buflen -= n;
return (ctx->bufpos < ctx->buflen || ctx->footer_pending || ctx->dent)? 1: 0;
}
static int task_main_dir_nochunk (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_dir_t* ctx = (task_dir_t*)task->ctx;
qse_ssize_t n;
int x;
if (ctx->bufpos < ctx->buflen) goto send_dirlist;
ctx->bufpos = 0;
ctx->buflen = 0;
ctx->bufrem = QSE_COUNTOF(ctx->buf);
if (ctx->footer_pending)
{
x = snprintf (
&ctx->buf[ctx->buflen],
ctx->bufrem,
"</ul></body></html>");
if (x == -1 || x >= ctx->bufrem)
{
/* return an error if the buffer is too small to hold the
* trailing footer. you need to increate the buffer size */
return -1;
}
ctx->buflen += x;
goto send_dirlist;
}
if (!ctx->header_added)
{
/* compose the header since this is the first time. */
x = snprintf (
&ctx->buf[ctx->buflen],
ctx->bufrem,
"<html><head><title>Directory Listing</title></head><body>index of xxxx<ul>"
);
if (x == -1 || x >= ctx->bufrem)
{
/* return an error if the buffer is too small to hold the header.
* you need to increate the buffer size */
return -1;
}
ctx->buflen += x;
ctx->bufrem -= x;
ctx->header_added = 1;
}
if (ctx->dent == QSE_NULL)
ctx->dent = readdir (ctx->handle.ptr);
do
{
if (ctx->dent == QSE_NULL)
{
// TODO: check if errno has changed from before readdir().
// and return -1 if so.
x = snprintf (
&ctx->buf[ctx->buflen],
ctx->bufrem,
"</ul></body></html>");
if (x == -1 || x >= ctx->bufrem)
{
ctx->footer_pending = 1;
}
else
{
ctx->buflen += x;
}
break;
}
else
{
x = snprintf (
&ctx->buf[ctx->buflen],
ctx->bufrem,
"<li><a href='%s%s'>%s%s</a></li>",
ctx->dent->d_name,
(ctx->dent->d_type == DT_DIR? "/": ""),
ctx->dent->d_name,
(ctx->dent->d_type == DT_DIR? "/": "")
);
if (x == -1 || x >= ctx->bufrem)
{
/* buffer not large enough to hold this entry */
break;
}
else
{
ctx->buflen += x;
ctx->bufrem -= x;
}
}
ctx->dent = readdir (ctx->handle.ptr);
}
while (1);
send_dirlist:
httpd->errnum = QSE_HTTPD_ENOERR;
n = httpd->cbs->client.send (
httpd, client, &ctx->buf[ctx->bufpos], ctx->buflen);
if (n <= -1) return -1;
ctx->bufpos += n;
ctx->buflen -= n;
return (ctx->bufpos < ctx->buflen || ctx->footer_pending || ctx->dent)? 1: 0;
}
qse_httpd_task_t* qse_httpd_entaskdir (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
qse_httpd_task_t* pred,
qse_ubi_t handle, int chunked)
{
qse_httpd_task_t task;
QSE_MEMSET (&task, 0, QSE_SIZEOF(task));
task.init = task_init_dir;
task.main = chunked? task_main_dir: task_main_dir_nochunk;
task.fini = task_fini_dir;
task.ctx = &handle;
return qse_httpd_entask (httpd, client, pred, &task, QSE_SIZEOF(task_dir_t));
}
/*------------------------------------------------------------------------*/
typedef struct task_path_t task_path_t;
struct task_path_t
{
const qse_mchar_t* name;
qse_http_range_t range;
qse_http_version_t version;
int keepalive;
};
static int task_init_path (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_path_t* xtn = qse_httpd_gettaskxtn (httpd, task);
QSE_MEMCPY (xtn, task->ctx, QSE_SIZEOF(*xtn));
qse_mbscpy ((qse_mchar_t*)(xtn + 1), xtn->name);
xtn->name = (qse_mchar_t*)(xtn + 1);
task->ctx = xtn;
return 0;
}
static QSE_INLINE int task_main_path_dir (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_path_t* data = (task_path_t*)task->ctx;
qse_httpd_task_t* x = task;
qse_ubi_t handle;
if (qse_mbsend (data->name, QSE_MT("/")))
{
handle.ptr = opendir (data->name);
if (handle.ptr)
{
if (data->version.major < 1 ||
(data->version.major == 1 && data->version.minor == 0))
{
data->keepalive = 0;
}
if (data->keepalive)
{
x = qse_httpd_entaskformat (
httpd, client, x,
QSE_MT("HTTP/%d.%d 200 OK\r\nConnection: keep-alive\r\nContent-Type: text/html\r\nTransfer-Encoding: chunked\r\n\r\n"),
data->version.major, data->version.minor
);
if (x) x = qse_httpd_entaskdir (httpd, client, x, handle, data->keepalive);
}
else
{
x = qse_httpd_entaskformat (
httpd, client, x,
QSE_MT("HTTP/%d.%d 200 OK\r\nConnection: close\r\nContent-Type: text/html\r\n\r\n"),
data->version.major, data->version.minor
);
if (x)
{
x = qse_httpd_entaskdir (httpd, client, x, handle, data->keepalive);
if (x) x = qse_httpd_entaskdisconnect (httpd, client, x);
}
}
if (!x) closedir (handle.ptr);
}
else
{
x = qse_httpd_entask_error (httpd, client, x, 403, &data->version, data->keepalive);
}
}
else
{
x = qse_httpd_entaskformat (
httpd, client, x,
QSE_MT("HTTP/%d.%d 301 Moved Permanently\r\nContent-Length: 0\r\nConnection: %s\r\nLocation: %s/\r\n\r\n"),
data->version.major, data->version.minor,
(data->keepalive? QSE_MT("keep-alive"): QSE_MT("close")),
data->name
);
}
return (x == QSE_NULL)? -1: 0;
}
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;
qse_lstat_t st;
if (QSE_LSTAT (data->name, &st) <= -1)
{
return (qse_httpd_entask_error (httpd, client, task, 404, &data->version, data->keepalive) == QSE_NULL)? -1: 0;
}
if (S_ISDIR(st.st_mode))
{
return task_main_path_dir (httpd, client, task);
}
else
{
if (st.st_size < 0) st.st_size = 0; /* can this happen? */
return task_main_path_file (httpd, client, task, st.st_size);
}
}
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)
{
qse_httpd_task_t task;
task_path_t data;
const qse_htre_hdrval_t* tmp;
QSE_MEMSET (&data, 0, QSE_SIZEOF(data));
data.name = name;
data.version = *qse_htre_getversion(req);
data.keepalive = req->attr.keepalive;
tmp = qse_htre_getheaderval(req, QSE_MT("Range"));
if (tmp)
{
while (tmp->next) tmp = tmp->next; /* get the last value */
if (qse_parsehttprange (tmp->ptr, &data.range) <= -1)
{
return qse_httpd_entask_error (httpd, client, pred, 416, &data.version, data.keepalive);
}
}
else
{
data.range.type = QSE_HTTP_RANGE_NONE;
}
QSE_MEMSET (&task, 0, QSE_SIZEOF(task));
task.init = task_init_path;
task.main = task_main_path;
task.ctx = &data;
return qse_httpd_entask (httpd, client, pred, &task,
QSE_SIZEOF(task_path_t) + qse_mbslen(name) + 1);
}
/*------------------------------------------------------------------------*/
#endif
typedef struct task_file_t task_file_t;
struct task_file_t
{
const qse_mchar_t* path;
qse_http_range_t range;
qse_http_version_t version;
int keepalive;
};
typedef struct task_fseg_t task_fseg_t;
struct task_fseg_t
{
qse_ubi_t handle;
qse_foff_t left;
qse_foff_t offset;
};
static int task_init_fseg (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_fseg_t* xtn = qse_httpd_gettaskxtn (httpd, task);
QSE_MEMCPY (xtn, task->ctx, QSE_SIZEOF(*xtn));
task->ctx = xtn;
return 0;
}
static void task_fini_fseg (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_fseg_t* ctx = (task_fseg_t*)task->ctx;
httpd->cbs->file.close (httpd, ctx->handle);
}
static int task_main_fseg (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
qse_ssize_t n;
qse_size_t count;
task_fseg_t* ctx = (task_fseg_t*)task->ctx;
count = MAX_SEND_SIZE;
if (count >= ctx->left) count = ctx->left;
/* TODO: more adjustment needed for OS with different sendfile semantics... */
n = httpd->cbs->client.sendfile (
httpd, client, ctx->handle, &ctx->offset, count);
if (n <= -1)
{
/* HANDLE EGAIN specially??? */
return -1; /* TODO: any logging */
}
if (n == 0 && count > 0)
{
/* The file could be truncated when this condition is set.
* The content-length sent in the header can't be fulfilled.
* So let's return an error here so that the main loop abort
* the connection. */
/* TODO: any logging....??? */
return -1;
}
ctx->left -= n;
if (ctx->left <= 0) return 0;
return 1; /* more work to do */
}
static qse_httpd_task_t* entask_file_segment (
qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_httpd_task_t* pred,
qse_ubi_t handle, qse_foff_t offset, qse_foff_t size)
{
qse_httpd_task_t task;
task_fseg_t data;
QSE_MEMSET (&data, 0, QSE_SIZEOF(data));
data.handle = handle;
data.offset = offset;
data.left = size;
QSE_MEMSET (&task, 0, QSE_SIZEOF(task));
task.init = task_init_fseg;
task.main = task_main_fseg;
task.fini = task_fini_fseg;
task.ctx = &data;
qse_printf (QSE_T("Debug: entasking file segment (%d)\n"), client->handle.i);
return qse_httpd_entask (httpd, client, pred, &task, QSE_SIZEOF(data));
}
/*------------------------------------------------------------------------*/
static int task_init_file (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_file_t* xtn = qse_httpd_gettaskxtn (httpd, task);
QSE_MEMCPY (xtn, task->ctx, QSE_SIZEOF(*xtn));
qse_mbscpy ((qse_mchar_t*)(xtn + 1), xtn->path);
xtn->path = (qse_mchar_t*)(xtn + 1);
task->ctx = xtn;
return 0;
}
static QSE_INLINE int task_main_file (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_file_t* file;
qse_httpd_task_t* x;
qse_ubi_t handle;
int fileopen = 0;
qse_httpd_stat_t st;
file = (task_file_t*)task->ctx;
x = task;
/* TODO: if you should deal with files on a network-mounted drive,
setting a trigger or non-blocking I/O are needed. */
qse_printf (QSE_T("opening file %hs\n"), file->path);
httpd->errnum = QSE_HTTPD_ENOERR;
if (httpd->cbs->file.stat (httpd, file->path, &st) <= -1)
{
int http_errnum;
http_errnum = (httpd->errnum == QSE_HTTPD_ENOENT)? 404:
(httpd->errnum == QSE_HTTPD_EACCES)? 403: 500;
x = qse_httpd_entask_error (
httpd, client, x, http_errnum,
&file->version, file->keepalive);
goto no_file_send;
}
httpd->errnum = QSE_HTTPD_ENOERR;
if (httpd->cbs->file.ropen (httpd, file->path, &handle) <= -1)
{
int http_errnum;
http_errnum = (httpd->errnum == QSE_HTTPD_ENOENT)? 404:
(httpd->errnum == QSE_HTTPD_EACCES)? 403: 500;
x = qse_httpd_entask_error (
httpd, client, x, http_errnum,
&file->version, file->keepalive);
goto no_file_send;
}
fileopen = 1;
if (file->range.type != QSE_HTTP_RANGE_NONE)
{
if (file->range.type == QSE_HTTP_RANGE_SUFFIX)
{
if (file->range.to > st.size) file->range.to = st.size;
file->range.from = st.size - file->range.to;
file->range.to = file->range.to + file->range.from;
if (st.size > 0) file->range.to--;
}
if (file->range.from >= st.size)
{
x = qse_httpd_entask_error (
httpd, client, x, 416, &file->version, file->keepalive);
goto no_file_send;
}
if (file->range.to >= st.size) file->range.to = st.size - 1;
#if (QSE_SIZEOF_LONG_LONG > 0)
x = qse_httpd_entaskformat (
httpd, client, x,
QSE_MT("HTTP/%d.%d 206 Partial Content\r\nConnection: %s\r\n%s%s%sContent-Length: %llu\r\nContent-Range: bytes %llu-%llu/%llu\r\n\r\n"),
file->version.major,
file->version.minor,
(file->keepalive? QSE_MT("keep-alive"): QSE_MT("close")),
(st.mime? QSE_MT("Content-Type: "): QSE_MT("")),
(st.mime? st.mime: QSE_MT("")),
(st.mime? QSE_MT("\r\n"): QSE_MT("")),
(unsigned long long)(file->range.to - file->range.from + 1),
(unsigned long long)file->range.from,
(unsigned long long)file->range.to,
(unsigned long long)st.size
);
#else
x = qse_httpd_entaskformat (
httpd, client, x,
QSE_MT("HTTP/%d.%d 206 Partial Content\r\nConnection: %s\r\n%s%s%sContent-Length: %lu\r\nContent-Range: bytes %lu-%lu/%lu\r\n\r\n"),
file->version.major,
file->version.minor,
(file->keepalive? QSE_MT("keep-alive"): QSE_MT("close")),
(st.mime? QSE_MT("Content-Type: "): QSE_MT("")),
(st.mime? st.mime: QSE_MT("")),
(st.mime? QSE_MT("\r\n"): QSE_MT("")),
(unsigned long)(file->range.to - file->range.from + 1),
(unsigned long)file->range.from,
(unsigned long)file->range.to,
(unsigned long)st.size
);
#endif
if (x)
{
x = entask_file_segment (
httpd, client, x,
handle,
file->range.from,
(file->range.to - file->range.from + 1)
);
}
}
else
{
/* TODO: int64 format.... don't hard code it llu */
/* wget 1.8.2 set 'Connection: keep-alive' in the http 1.0 header.
* if the reply doesn't contain 'Connection: keep-alive', it didn't
* close connection.*/
#if (QSE_SIZEOF_LONG_LONG > 0)
x = qse_httpd_entaskformat (
httpd, client, x,
QSE_MT("HTTP/%d.%d 200 OK\r\nConnection: %s\r\n%s%s%sContent-Length: %llu\r\n\r\n"),
file->version.major, file->version.minor,
(file->keepalive? QSE_MT("keep-alive"): QSE_MT("close")),
(st.mime? QSE_MT("Content-Type: "): QSE_MT("")),
(st.mime? st.mime: QSE_MT("")),
(st.mime? QSE_MT("\r\n"): QSE_MT("")),
(unsigned long long)st.size
);
#else
x = qse_httpd_entaskformat (
httpd, client, x,
QSE_MT("HTTP/%d.%d 200 OK\r\nConnection: %s\r\n%s%s%sContent-Length: %lu\r\n\r\n"),
file->version.major,
file->version.minor,
(file->keepalive? QSE_MT("keep-alive"): QSE_MT("close")),
(st.mime? QSE_MT("Content-Type: "): QSE_MT("")),
(st.mime? st.mime: QSE_MT("")),
(st.mime? QSE_MT("\r\n"): QSE_MT("")),
(unsigned long)st.size
);
#endif
if (x)
{
x = entask_file_segment (httpd, client, x, handle, 0, st.size);
}
}
return (x == QSE_NULL)? -1: 0;
no_file_send:
if (fileopen) httpd->cbs->file.close (httpd, handle);
return (x == QSE_NULL)? -1: 0;
}
qse_httpd_task_t* qse_httpd_entaskfile (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
qse_httpd_task_t* pred,
const qse_mchar_t* path,
qse_htre_t* req)
{
qse_httpd_task_t task;
task_file_t data;
const qse_htre_hdrval_t* tmp;
QSE_MEMSET (&data, 0, QSE_SIZEOF(data));
data.path = path;
data.version = *qse_htre_getversion(req);
data.keepalive = (req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE);
tmp = qse_htre_getheaderval(req, QSE_MT("Range"));
if (tmp)
{
while (tmp->next) tmp = tmp->next; /* get the last value */
if (qse_parsehttprange (tmp->ptr, &data.range) <= -1)
{
return qse_httpd_entaskerror (httpd, client, pred, 416, req);
}
}
else
{
data.range.type = QSE_HTTP_RANGE_NONE;
}
/*
TODO: If-Modified-Since...
tmp = qse_htre_getheaderval(req, QSE_MT("If-Modified-Since"));
if (tmp)
{
}
*/
QSE_MEMSET (&task, 0, QSE_SIZEOF(task));
task.init = task_init_file;
task.main = task_main_file;
task.ctx = &data;
return qse_httpd_entask (httpd, client, pred, &task,
QSE_SIZEOF(task_file_t) + qse_mbslen(path) + 1);
}
/*------------------------------------------------------------------------*/
#if 0
qse_httpd_task_t* qse_httpd_entaskresol (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
qse_httpd_task_t* pred,
const qse_mchar_t* host,
qse_htre_t* req)
{
qse_httpd_task_t task;
task_resol_arg_t arg;
arg.peer_nwad = *nwad;
arg.req = req;
QSE_MEMSET (&task, 0, QSE_SIZEOF(task));
task.init = task_init_resol;
task.fini = task_fini_resol;
task.main = task_main_resol;
task.ctx = &arg;
return qse_httpd_entask (
httpd, client, pred, &task, QSE_SIZEOF(task_resol_t)
);
}
#endif
/*------------------------------------------------------------------------*/
#if 0 #if 0
qse_httpd_task_t* qse_httpd_entaskconnect ( qse_httpd_task_t* qse_httpd_entaskconnect (
qse_httpd_t* httpd, qse_httpd_t* httpd,