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
|
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"
|
||||||
|
@ -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])
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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,
|
||||||
|
@ -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[] =
|
||||||
{
|
{
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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 \
|
||||||
|
@ -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
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
|
#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)
|
||||||
|
@ -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,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user