added qse_httpd_entaskdir()
This commit is contained in:
parent
9cc8d6f76d
commit
b9a0863fff
4
qse/configure
vendored
4
qse/configure
vendored
@ -16057,7 +16057,7 @@ $as_echo "#define STDC_HEADERS 1" >>confdefs.h
|
||||
|
||||
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 :
|
||||
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"
|
||||
@ -16177,7 +16177,7 @@ _ACEOF
|
||||
fi
|
||||
done
|
||||
|
||||
for ac_func in lseek64 stat64 fstat64 lstat64 ftruncate64
|
||||
for ac_func in lseek64 stat64 fstat64 lstat64 ftruncate64 readdir64
|
||||
do :
|
||||
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
|
||||
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
|
||||
|
@ -82,7 +82,7 @@ AC_SUBST(LIBM, $LIBM)
|
||||
|
||||
dnl check header files.
|
||||
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([sys/resource.h sys/wait.h sys/syscall.h sys/sendfile.h sys/epoll.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([wctype iswctype wctrans towctrans])
|
||||
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([utime utimes])
|
||||
AC_CHECK_FUNCS([sysconf])
|
||||
|
@ -199,16 +199,22 @@ QSEAWK implements the language described in the book
|
||||
<a class="el" href="http://cm.bell-labs.com/cm/cs/awkbook/">
|
||||
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>
|
||||
<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>Pattern-action block </td><td>#QSE_AWK_PABLOCK </td></tr>
|
||||
<tr><td>User-defined function </td><td> </td></tr>
|
||||
<tr><td>\@include </td><td>#QSE_AWK_INCLUDE </td></tr>
|
||||
</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 := BEGIN | END | expression | expression-range
|
||||
- expression-range := expression , expression
|
||||
|
@ -202,6 +202,9 @@
|
||||
/* 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. */
|
||||
#undef HAVE_ROUND
|
||||
|
||||
|
@ -104,6 +104,12 @@ typedef int (*qse_httpd_muxcb_t) (
|
||||
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;
|
||||
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);
|
||||
int (*handle_request) (
|
||||
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;
|
||||
@ -502,6 +506,14 @@ qse_httpd_task_t* qse_httpd_entaskfile (
|
||||
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_t* httpd,
|
||||
qse_httpd_client_t* client,
|
||||
|
@ -20,7 +20,7 @@
|
||||
|
||||
#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[] =
|
||||
{
|
||||
|
@ -25,7 +25,7 @@
|
||||
extern "C" {
|
||||
#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
|
||||
}
|
||||
|
@ -1268,6 +1268,7 @@ qse_long_t qse_awk_rtx_hashval (qse_awk_rtx_t* rtx, qse_awk_val_t* v)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* turn off the sign bit */
|
||||
return hv & ~(((qse_ulong_t)1) << ((QSE_SIZEOF(qse_ulong_t) * 8) - 1));
|
||||
}
|
||||
|
||||
|
@ -34,9 +34,7 @@
|
||||
# include <dos.h>
|
||||
# include <errno.h>
|
||||
#else
|
||||
# include <dirent.h>
|
||||
# include <sys/stat.h>
|
||||
# include <unistd.h>
|
||||
# include "syscall.h"
|
||||
#endif
|
||||
|
||||
#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)
|
||||
|
||||
struct DIR
|
||||
struct qse_dir_t
|
||||
{
|
||||
HANDLE h;
|
||||
WIN32_FIND_DATA wfd;
|
||||
int done;
|
||||
};
|
||||
typedef struct DIR DIR;
|
||||
typedef struct qse_dir_t qse_dir_t;
|
||||
|
||||
#elif defined(__OS2__)
|
||||
|
||||
struct DIR
|
||||
struct qse_dir_t
|
||||
{
|
||||
HDIR h;
|
||||
FILEFINDBUF3L ffb;
|
||||
ULONG count;
|
||||
};
|
||||
typedef struct DIR DIR;
|
||||
typedef struct qse_dir_t qse_dir_t;
|
||||
|
||||
#elif defined(__DOS__)
|
||||
struct DIR
|
||||
|
||||
struct qse_dir_t
|
||||
{
|
||||
struct find_t f;
|
||||
int done;
|
||||
};
|
||||
typedef struct DIR DIR;
|
||||
typedef struct qse_dir_t qse_dir_t;
|
||||
|
||||
#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)
|
||||
|
||||
/* ------------------------------------------------------------------- */
|
||||
DIR* dp;
|
||||
qse_dir_t* dp;
|
||||
|
||||
dp = QSE_MMGR_ALLOC (g->mmgr, QSE_SIZEOF(*dp));
|
||||
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__)
|
||||
|
||||
/* ------------------------------------------------------------------- */
|
||||
DIR* dp;
|
||||
qse_dir_t* dp;
|
||||
APIRET rc;
|
||||
qse_mchar_t* mptr;
|
||||
|
||||
@ -463,7 +462,7 @@ static DIR* xopendir (glob_t* g, const qse_cstr_t* path)
|
||||
#elif defined(__DOS__)
|
||||
|
||||
/* ------------------------------------------------------------------- */
|
||||
DIR* dp;
|
||||
qse_dir_t* dp;
|
||||
unsigned int rc;
|
||||
qse_mchar_t* mptr;
|
||||
qse_size_t wl, ml;
|
||||
@ -519,13 +518,12 @@ static DIR* xopendir (glob_t* g, const qse_cstr_t* path)
|
||||
#else
|
||||
|
||||
/* ------------------------------------------------------------------- */
|
||||
|
||||
#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
|
||||
if (path->len <= 0)
|
||||
{
|
||||
return opendir (QSE_MT("."));
|
||||
return QSE_OPENDIR (QSE_MT("."));
|
||||
}
|
||||
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);
|
||||
if (mptr == QSE_NULL) return QSE_NULL;
|
||||
|
||||
return opendir (mptr);
|
||||
return QSE_OPENDIR (mptr);
|
||||
}
|
||||
#endif
|
||||
/* ------------------------------------------------------------------- */
|
||||
@ -542,7 +540,7 @@ static DIR* xopendir (glob_t* g, const qse_cstr_t* path)
|
||||
#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)
|
||||
|
||||
@ -615,14 +613,14 @@ static int xreaddir (glob_t* g, DIR* dp, qse_str_t* path)
|
||||
#else
|
||||
|
||||
/* ------------------------------------------------------------------- */
|
||||
struct dirent* de;
|
||||
qse_dirent_t* de;
|
||||
#if defined(QSE_CHAR_IS_MCHAR)
|
||||
/* nothing */
|
||||
#else
|
||||
qse_size_t ml, wl, tmp;
|
||||
#endif
|
||||
|
||||
de = readdir (dp);
|
||||
de = QSE_READDIR (dp);
|
||||
if (de == NULL) return 0;
|
||||
|
||||
#if defined(QSE_CHAR_IS_MCHAR)
|
||||
@ -640,7 +638,7 @@ static int xreaddir (glob_t* g, DIR* dp, qse_str_t* path)
|
||||
#endif
|
||||
}
|
||||
|
||||
static void xclosedir (glob_t* g, DIR* dp)
|
||||
static void xclosedir (glob_t* g, qse_dir_t* dp)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
FindClose (dp->h);
|
||||
@ -652,7 +650,7 @@ static void xclosedir (glob_t* g, DIR* dp)
|
||||
_dos_findclose (&dp->f);
|
||||
QSE_MMGR_FREE (g->mmgr, dp);
|
||||
#else
|
||||
closedir (dp);
|
||||
QSE_CLOSEDIR (dp);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -722,7 +720,7 @@ struct stack_node_t
|
||||
{
|
||||
qse_size_t tmp;
|
||||
qse_size_t tmp2;
|
||||
DIR* dp;
|
||||
qse_dir_t* dp;
|
||||
segment_t seg;
|
||||
|
||||
stack_node_t* next;
|
||||
@ -731,7 +729,7 @@ struct stack_node_t
|
||||
|
||||
static int search (glob_t* g, segment_t* seg)
|
||||
{
|
||||
DIR* dp;
|
||||
qse_dir_t* dp;
|
||||
qse_size_t tmp, tmp2;
|
||||
|
||||
#if defined(NO_RECURSION)
|
||||
|
@ -23,39 +23,42 @@
|
||||
|
||||
/* This file defines unix/linux system calls */
|
||||
|
||||
#ifdef HAVE_SYS_TYPES_H
|
||||
#if defined(HAVE_SYS_TYPES_H)
|
||||
# include <sys/types.h>
|
||||
#endif
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#if defined(HAVE_UNISTD_H)
|
||||
# include <unistd.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_WAIT_H
|
||||
#if defined(HAVE_SYS_WAIT_H)
|
||||
# include <sys/wait.h>
|
||||
#endif
|
||||
#ifdef HAVE_SIGNAL_H
|
||||
#if defined(HAVE_SIGNAL_H)
|
||||
# include <signal.h>
|
||||
#endif
|
||||
#ifdef HAVE_ERRNO_H
|
||||
#if defined(HAVE_ERRNO_H)
|
||||
# include <errno.h>
|
||||
#endif
|
||||
#ifdef HAVE_FCNTL_H
|
||||
#if defined(HAVE_FCNTL_H)
|
||||
# include <fcntl.h>
|
||||
#endif
|
||||
#ifdef HAVE_TIME_H
|
||||
#if defined(HAVE_TIME_H)
|
||||
# include <time.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_TIME_H
|
||||
#if defined(HAVE_SYS_TIME_H)
|
||||
# include <sys/time.h>
|
||||
#endif
|
||||
#ifdef HAVE_UTIME_H
|
||||
#if defined(HAVE_UTIME_H)
|
||||
# include <utime.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_RESOURCE_H
|
||||
#if defined(HAVE_SYS_RESOURCE_H)
|
||||
# include <sys/resource.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_STAT_H
|
||||
#if defined(HAVE_SYS_STAT_H)
|
||||
# include <sys/stat.h>
|
||||
#endif
|
||||
#if defined(HAVE_DIRENT_H)
|
||||
# include <dirent.h>
|
||||
#endif
|
||||
|
||||
#if defined(QSE_USE_SYSCALL) && defined(HAVE_SYS_SYSCALL_H)
|
||||
# include <sys/syscall.h>
|
||||
@ -355,6 +358,19 @@
|
||||
# define QSE_UTIMES(path,t) utimes(path,t)
|
||||
#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)
|
||||
|
@ -14,6 +14,8 @@ libqsenet_la_SOURCES = \
|
||||
htrd.c \
|
||||
httpd.c \
|
||||
httpd-cgi.c \
|
||||
httpd-dir.c \
|
||||
httpd-file.c \
|
||||
httpd-proxy.c \
|
||||
httpd-resol.c \
|
||||
httpd-std.c \
|
||||
|
@ -79,8 +79,8 @@ am__installdirs = "$(DESTDIR)$(libdir)"
|
||||
LTLIBRARIES = $(lib_LTLIBRARIES)
|
||||
libqsenet_la_DEPENDENCIES =
|
||||
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-task.lo upxd.lo
|
||||
httpd-cgi.lo httpd-dir.lo httpd-file.lo httpd-proxy.lo \
|
||||
httpd-resol.lo httpd-std.lo httpd-task.lo upxd.lo
|
||||
libqsenet_la_OBJECTS = $(am_libqsenet_la_OBJECTS)
|
||||
libqsenet_la_LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) \
|
||||
$(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
|
||||
@ -271,6 +271,8 @@ libqsenet_la_SOURCES = \
|
||||
htrd.c \
|
||||
httpd.c \
|
||||
httpd-cgi.c \
|
||||
httpd-dir.c \
|
||||
httpd-file.c \
|
||||
httpd-proxy.c \
|
||||
httpd-resol.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)/http.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-resol.Plo@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/httpd-std.Plo@am__quote@
|
||||
|
487
qse/lib/net/httpd-dir.c
Normal file
487
qse/lib/net/httpd-dir.c
Normal 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
323
qse/lib/net/httpd-file.c
Normal 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);
|
||||
}
|
||||
|
@ -1423,9 +1423,14 @@ qse_printf (QSE_T("Entasking chunked CGI...\n"));
|
||||
#else
|
||||
if (peek)
|
||||
{
|
||||
qse_stat_t st;
|
||||
|
||||
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;
|
||||
}
|
||||
#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 =
|
||||
{
|
||||
/* server */
|
||||
@ -1622,8 +1622,6 @@ static qse_httpd_cbs_t httpd_standard_callbacks =
|
||||
/* http request */
|
||||
peek_request,
|
||||
handle_request,
|
||||
|
||||
list_directory
|
||||
};
|
||||
|
||||
int qse_httpd_loopstd (qse_httpd_t* httpd, qse_ntime_t timeout)
|
||||
|
@ -32,7 +32,6 @@
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <dirent.h>
|
||||
|
||||
/* TODO:
|
||||
* 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
|
||||
qse_httpd_task_t* qse_httpd_entaskconnect (
|
||||
qse_httpd_t* httpd,
|
||||
|
Loading…
x
Reference in New Issue
Block a user