enhanced httpd a bit

This commit is contained in:
2012-09-12 15:47:41 +00:00
parent b9a0863fff
commit adb9f387f9
19 changed files with 1144 additions and 420 deletions

View File

@ -20,63 +20,67 @@
#include <qse/cmn/path.h>
/* ------------------------------------------------------------------ */
/* MBS IMPLEMENTATION */
/* ------------------------------------------------------------------ */
#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__)
# define IS_SEP(c) ((c) == QSE_T('/') || (c) == QSE_T('\\'))
# define IS_MSEP(c) ((c) == QSE_MT('/') || (c) == QSE_MT('\\'))
#else
# define IS_SEP(c) ((c) == QSE_T('/'))
# define IS_MSEP(c) ((c) == QSE_MT('/'))
#endif
#define IS_NIL(c) ((c) == QSE_T('\0'))
#define IS_SEP_OR_NIL(c) (IS_SEP(c) || IS_NIL(c))
#define IS_MNIL(c) ((c) == QSE_MT('\0'))
#define IS_MSEP_OR_MNIL(c) (IS_MSEP(c) || IS_MNIL(c))
#define ISDRIVE(s) \
(((s[0] >= QSE_T('A') && s[0] <= QSE_T('Z')) || \
(s[0] >= QSE_T('a') && s[0] <= QSE_T('z'))) && \
s[1] == QSE_T(':'))
#define IS_MDRIVE(s) \
(((s[0] >= QSE_MT('A') && s[0] <= QSE_MT('Z')) || \
(s[0] >= QSE_MT('a') && s[0] <= QSE_MT('z'))) && \
s[1] == QSE_MT(':'))
int qse_isabspath (const qse_char_t* path)
int qse_ismbsabspath (const qse_mchar_t* path)
{
if (IS_SEP(path[0])) return 1;
if (IS_MSEP(path[0])) return 1;
#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__)
/* a drive like c:tmp is absolute in positioning the drive.
* but the path within the drive is kind of relative */
if (ISDRIVE(path)) return 1;
if (IS_MDRIVE(path)) return 1;
#endif
return 0;
}
int qse_isdrivepath (const qse_char_t* path)
int qse_ismbsdrivepath (const qse_mchar_t* path)
{
#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__)
if (ISDRIVE(path)) return 1;
if (IS_MDRIVE(path)) return 1;
#endif
return 0;
}
int qse_isdrivecurpath (const qse_char_t* path)
int qse_ismbsdrivecurpath (const qse_mchar_t* path)
{
#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__)
if (ISDRIVE(path) && path[2] == QSE_T('\0')) return 1;
if (IS_MDRIVE(path) && path[2] == QSE_MT('\0')) return 1;
#endif
return 0;
}
qse_size_t qse_canonpath (const qse_char_t* path, qse_char_t* canon, int flags)
qse_size_t qse_canonmbspath (const qse_mchar_t* path, qse_mchar_t* canon, int flags)
{
const qse_char_t* ptr;
qse_char_t* dst;
qse_char_t* non_root_start;
const qse_mchar_t* ptr;
qse_mchar_t* dst;
qse_mchar_t* non_root_start;
int has_root = 0;
#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__)
int is_drive = 0;
#endif
qse_size_t canon_len;
if (path[0] == QSE_T('\0'))
if (path[0] == QSE_MT('\0'))
{
/* if the source is empty, no translation is needed */
canon[0] = QSE_T('\0');
canon[0] = QSE_MT('\0');
return 0;
}
@ -84,31 +88,31 @@ qse_size_t qse_canonpath (const qse_char_t* path, qse_char_t* canon, int flags)
dst = canon;
#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__)
if (ISDRIVE(ptr))
if (IS_MDRIVE(ptr))
{
/* handle drive letter */
*dst++ = *ptr++; /* drive letter */
*dst++ = *ptr++; /* colon */
is_drive = 1;
if (IS_SEP(*ptr))
if (IS_MSEP(*ptr))
{
*dst++ = *ptr++; /* root directory */
has_root = 1;
}
}
else if (IS_SEP(*ptr))
else if (IS_MSEP(*ptr))
{
*dst++ = *ptr++; /* root directory */
has_root = 1;
#if defined(_WIN32)
/* handle UNC path for Windows */
if (IS_SEP(*ptr))
if (IS_MSEP(*ptr))
{
*dst++ = *ptr++;
if (IS_SEP_OR_NIL(*ptr))
if (IS_MSEP_OR_MNIL(*ptr))
{
/* if there is another separator after \\,
* it's not an UNC path. */
@ -117,14 +121,14 @@ qse_size_t qse_canonpath (const qse_char_t* path, qse_char_t* canon, int flags)
else
{
/* if it starts with \\, process host name */
do { *dst++ = *ptr++; } while (!IS_SEP_OR_NIL(*ptr));
if (IS_SEP(*ptr)) *dst++ = *ptr++;
do { *dst++ = *ptr++; } while (!IS_MSEP_OR_MNIL(*ptr));
if (IS_MSEP(*ptr)) *dst++ = *ptr++;
}
}
#endif
}
#else
if (IS_SEP(*ptr))
if (IS_MSEP(*ptr))
{
*dst++ = *ptr++; /* root directory */
has_root = 1;
@ -137,30 +141,30 @@ qse_size_t qse_canonpath (const qse_char_t* path, qse_char_t* canon, int flags)
do
{
const qse_char_t* seg;
const qse_mchar_t* seg;
qse_size_t seglen;
/* skip duplicate separators */
while (IS_SEP(*ptr)) ptr++;
while (IS_MSEP(*ptr)) ptr++;
/* end of path reached */
if (*ptr == QSE_T('\0')) break;
if (*ptr == QSE_MT('\0')) break;
/* find the next segment */
seg = ptr;
while (!IS_SEP_OR_NIL(*ptr)) ptr++;
while (!IS_MSEP_OR_MNIL(*ptr)) ptr++;
seglen = ptr - seg;
/* handle the segment */
if (seglen == 1 && seg[0] == QSE_T('.'))
if (seglen == 1 && seg[0] == QSE_MT('.'))
{
/* eat up . */
}
else if (!(flags & QSE_CANONPATH_KEEPDOUBLEDOTS) &&
seglen == 2 && seg[0] == QSE_T('.') && seg[1] == QSE_T('.'))
seglen == 2 && seg[0] == QSE_MT('.') && seg[1] == QSE_MT('.'))
{
/* eat up the previous segment */
qse_char_t* tmp;
qse_mchar_t* tmp;
tmp = dst;
if (tmp > non_root_start)
@ -173,7 +177,7 @@ qse_size_t qse_canonpath (const qse_char_t* path, qse_char_t* canon, int flags)
while (tmp > non_root_start)
{
tmp--;
if (IS_SEP(*tmp))
if (IS_MSEP(*tmp))
{
tmp++; /* position it next to the separator */
break;
@ -212,7 +216,7 @@ qse_size_t qse_canonpath (const qse_char_t* path, qse_char_t* canon, int flags)
goto normal;
}
if (prevlen == 3 && tmp[0] == QSE_T('.') && tmp[1] == QSE_T('.'))
if (prevlen == 3 && tmp[0] == QSE_MT('.') && tmp[1] == QSE_MT('.'))
{
/* nothing to eat away because the previous segment is ../
*
@ -234,7 +238,7 @@ qse_size_t qse_canonpath (const qse_char_t* path, qse_char_t* canon, int flags)
{
normal:
while (seg < ptr) *dst++ = *seg++;
if (IS_SEP(*ptr))
if (IS_MSEP(*ptr))
{
/* this segment ended with a separator */
*dst++ = *seg++; /* copy the separator */
@ -244,8 +248,8 @@ qse_size_t qse_canonpath (const qse_char_t* path, qse_char_t* canon, int flags)
}
while (1);
if (dst > non_root_start && IS_SEP(dst[-1]) &&
((flags & QSE_CANONPATH_DROPTRAILINGSEP) || !IS_SEP(ptr[-1])))
if (dst > non_root_start && IS_MSEP(dst[-1]) &&
((flags & QSE_CANONPATH_DROPTRAILINGSEP) || !IS_MSEP(ptr[-1])))
{
/* if the canoncal path composed so far ends with a separator
* and the original path didn't end with the separator, delete
@ -255,18 +259,18 @@ qse_size_t qse_canonpath (const qse_char_t* path, qse_char_t* canon, int flags)
* dst > non_root_start:
* there is at least 1 character after the root directory
* part.
* IS_SEP(dst[-1]):
* IS_MSEP(dst[-1]):
* the canonical path ends with a separator.
* IS_SEP(ptr[-1]):
* IS_MSEP(ptr[-1]):
* the origial path ends with a separator.
*/
dst[-1] = QSE_T('\0');
dst[-1] = QSE_MT('\0');
canon_len = dst - canon - 1;
}
else
{
/* just null-terminate the canonical path normally */
dst[0] = QSE_T('\0');
dst[0] = QSE_MT('\0');
canon_len = dst - canon;
}
@ -276,8 +280,8 @@ qse_size_t qse_canonpath (const qse_char_t* path, qse_char_t* canon, int flags)
{
/* when resolving to a single dot, a trailing separator is not
* retained though the orignal path name contains it. */
canon[0] = QSE_T('.');
canon[1] = QSE_T('\0');
canon[0] = QSE_MT('.');
canon[1] = QSE_MT('\0');
canon_len = 1;
}
}
@ -302,21 +306,328 @@ qse_size_t qse_canonpath (const qse_char_t* path, qse_char_t* canon, int flags)
/* i don't have to retain a trailing separator
* if the last segment is double slashes because
* the double slahses indicate a directory obviously */
if (canon[canon_len-3] == QSE_T('.') &&
canon[canon_len-2] == QSE_T('.') &&
IS_SEP(canon[canon_len-1]))
if (canon[canon_len-3] == QSE_MT('.') &&
canon[canon_len-2] == QSE_MT('.') &&
IS_MSEP(canon[canon_len-1]))
{
canon[--canon_len] = QSE_T('\0');
canon[--canon_len] = QSE_MT('\0');
}
}
else if (canon_len > adj_base_len)
{
if (IS_SEP(canon[canon_len-4]) &&
canon[canon_len-3] == QSE_T('.') &&
canon[canon_len-2] == QSE_T('.') &&
IS_SEP(canon[canon_len-1]))
if (IS_MSEP(canon[canon_len-4]) &&
canon[canon_len-3] == QSE_MT('.') &&
canon[canon_len-2] == QSE_MT('.') &&
IS_MSEP(canon[canon_len-1]))
{
canon[--canon_len] = QSE_T('\0');
canon[--canon_len] = QSE_MT('\0');
}
}
}
return canon_len;
}
/* ------------------------------------------------------------------ */
/* WCS IMPLEMENTATION */
/* ------------------------------------------------------------------ */
#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__)
# define IS_WSEP(c) ((c) == QSE_WT('/') || (c) == QSE_WT('\\'))
#else
# define IS_WSEP(c) ((c) == QSE_WT('/'))
#endif
#define IS_WNIL(c) ((c) == QSE_WT('\0'))
#define IS_WSEP_OR_WNIL(c) (IS_WSEP(c) || IS_WNIL(c))
#define IS_WDRIVE(s) \
(((s[0] >= QSE_WT('A') && s[0] <= QSE_WT('Z')) || \
(s[0] >= QSE_WT('a') && s[0] <= QSE_WT('z'))) && \
s[1] == QSE_WT(':'))
int qse_iswcsabspath (const qse_wchar_t* path)
{
if (IS_WSEP(path[0])) return 1;
#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__)
/* a drive like c:tmp is absolute in positioning the drive.
* but the path within the drive is kind of relative */
if (IS_WDRIVE(path)) return 1;
#endif
return 0;
}
int qse_iswcsdrivepath (const qse_wchar_t* path)
{
#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__)
if (IS_WDRIVE(path)) return 1;
#endif
return 0;
}
int qse_iswcsdrivecurpath (const qse_wchar_t* path)
{
#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__)
if (IS_WDRIVE(path) && path[2] == QSE_WT('\0')) return 1;
#endif
return 0;
}
qse_size_t qse_canonwcspath (const qse_wchar_t* path, qse_wchar_t* canon, int flags)
{
const qse_wchar_t* ptr;
qse_wchar_t* dst;
qse_wchar_t* non_root_start;
int has_root = 0;
#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__)
int is_drive = 0;
#endif
qse_size_t canon_len;
if (path[0] == QSE_WT('\0'))
{
/* if the source is empty, no translation is needed */
canon[0] = QSE_WT('\0');
return 0;
}
ptr = path;
dst = canon;
#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__)
if (IS_WDRIVE(ptr))
{
/* handle drive letter */
*dst++ = *ptr++; /* drive letter */
*dst++ = *ptr++; /* colon */
is_drive = 1;
if (IS_WSEP(*ptr))
{
*dst++ = *ptr++; /* root directory */
has_root = 1;
}
}
else if (IS_WSEP(*ptr))
{
*dst++ = *ptr++; /* root directory */
has_root = 1;
#if defined(_WIN32)
/* handle UNC path for Windows */
if (IS_WSEP(*ptr))
{
*dst++ = *ptr++;
if (IS_WSEP_OR_WNIL(*ptr))
{
/* if there is another separator after \\,
* it's not an UNC path. */
dst--;
}
else
{
/* if it starts with \\, process host name */
do { *dst++ = *ptr++; } while (!IS_WSEP_OR_WNIL(*ptr));
if (IS_WSEP(*ptr)) *dst++ = *ptr++;
}
}
#endif
}
#else
if (IS_WSEP(*ptr))
{
*dst++ = *ptr++; /* root directory */
has_root = 1;
}
#endif
/* non_root_start points to the beginning of the canonicalized
* path excluding the root directory part. */
non_root_start = dst;
do
{
const qse_wchar_t* seg;
qse_size_t seglen;
/* skip duplicate separators */
while (IS_WSEP(*ptr)) ptr++;
/* end of path reached */
if (*ptr == QSE_WT('\0')) break;
/* find the next segment */
seg = ptr;
while (!IS_WSEP_OR_WNIL(*ptr)) ptr++;
seglen = ptr - seg;
/* handle the segment */
if (seglen == 1 && seg[0] == QSE_WT('.'))
{
/* eat up . */
}
else if (!(flags & QSE_CANONPATH_KEEPDOUBLEDOTS) &&
seglen == 2 && seg[0] == QSE_WT('.') && seg[1] == QSE_WT('.'))
{
/* eat up the previous segment */
qse_wchar_t* tmp;
tmp = dst;
if (tmp > non_root_start)
{
/* there is a previous segment. */
tmp--; /* skip the separator just before .. */
/* find the beginning of the previous segment */
while (tmp > non_root_start)
{
tmp--;
if (IS_WSEP(*tmp))
{
tmp++; /* position it next to the separator */
break;
}
}
}
if (has_root)
{
/*
* Eat up the previous segment if it exists.
*
* If it doesn't exist, tmp == dst so dst = tmp
* keeps dst unchanged. If it exists,
* tmp != dst. so dst = tmp changes dst.
*
* path /abc/def/..
* ^ ^
* seg ptr
*
* canon /abc/def/
* ^ ^
* tmp dst
*/
dst = tmp;
}
else
{
qse_size_t prevlen;
prevlen = dst - tmp;
if (/*tmp == non_root_start &&*/ prevlen == 0)
{
/* there is no previous segment */
goto normal;
}
if (prevlen == 3 && tmp[0] == QSE_WT('.') && tmp[1] == QSE_WT('.'))
{
/* nothing to eat away because the previous segment is ../
*
* path ../../
* ^ ^
* seg ptr
*
* canon ../
* ^ ^
* tmp dst
*/
goto normal;
}
dst = tmp;
}
}
else
{
normal:
while (seg < ptr) *dst++ = *seg++;
if (IS_WSEP(*ptr))
{
/* this segment ended with a separator */
*dst++ = *seg++; /* copy the separator */
ptr++; /* move forward the pointer */
}
}
}
while (1);
if (dst > non_root_start && IS_WSEP(dst[-1]) &&
((flags & QSE_CANONPATH_DROPTRAILINGSEP) || !IS_WSEP(ptr[-1])))
{
/* if the canoncal path composed so far ends with a separator
* and the original path didn't end with the separator, delete
* the ending separator.
* also delete it if QSE_CANONPATH_DROPTRAILINGSEP is set.
*
* dst > non_root_start:
* there is at least 1 character after the root directory
* part.
* IS_WSEP(dst[-1]):
* the canonical path ends with a separator.
* IS_WSEP(ptr[-1]):
* the origial path ends with a separator.
*/
dst[-1] = QSE_WT('\0');
canon_len = dst - canon - 1;
}
else
{
/* just null-terminate the canonical path normally */
dst[0] = QSE_WT('\0');
canon_len = dst - canon;
}
if (canon_len <= 0)
{
if (!(flags & QSE_CANONPATH_EMPTYSINGLEDOT))
{
/* when resolving to a single dot, a trailing separator is not
* retained though the orignal path name contains it. */
canon[0] = QSE_WT('.');
canon[1] = QSE_WT('\0');
canon_len = 1;
}
}
else
{
/* drop a traling separator if the last segment is
* double slashes */
int adj_base_len = 3;
#if defined(_WIN32) || defined(__OS2__) || defined(__DOS__)
if (is_drive && !has_root)
{
/* A path like A:..\\\ need some adjustment for
* finalization below. */
adj_base_len += 2;
}
#endif
if (canon_len == adj_base_len)
{
/* i don't have to retain a trailing separator
* if the last segment is double slashes because
* the double slahses indicate a directory obviously */
if (canon[canon_len-3] == QSE_WT('.') &&
canon[canon_len-2] == QSE_WT('.') &&
IS_WSEP(canon[canon_len-1]))
{
canon[--canon_len] = QSE_WT('\0');
}
}
else if (canon_len > adj_base_len)
{
if (IS_WSEP(canon[canon_len-4]) &&
canon[canon_len-3] == QSE_WT('.') &&
canon[canon_len-2] == QSE_WT('.') &&
IS_WSEP(canon[canon_len-1]))
{
canon[--canon_len] = QSE_WT('\0');
}
}
}

View File

@ -389,6 +389,9 @@ static qse_mchar_t* parse_initial_line (
htrd->re.u.q.param = QSE_NULL;
}
#endif
if (htrd->option & QSE_HTRD_CANONQPATH)
qse_canonmbspath (htrd->re.u.q.path, htrd->re.u.q.path, 0);
/* skip spaces after the url part */
do { p++; } while (is_space_octet(*p));

View File

@ -22,6 +22,7 @@
#include <qse/cmn/str.h>
#include <qse/cmn/chr.h>
#include <qse/cmn/htb.h>
#include "../cmn/mem.h"
int qse_comparehttpversions (
const qse_http_version_t* v1,
@ -224,3 +225,57 @@ qse_size_t qse_perdechttpstr (const qse_mchar_t* str, qse_mchar_t* buf)
*out = QSE_MT('\0');
return out - buf;
}
#define IS_UNRESERVED(c) \
(((c) >= QSE_MT('A') && (c) <= QSE_MT('Z')) || \
((c) >= QSE_MT('a') && (c) <= QSE_MT('z')) || \
(c) == QSE_MT('-') || (c) == QSE_T('_') || \
(c) == QSE_MT('.') || (c) == QSE_T('~'))
#define TO_HEX(v) (QSE_MT("0123456789ABCDEF")[(v) & 15])
qse_size_t qse_perenchttpstr (const qse_mchar_t* str, qse_mchar_t* buf)
{
const qse_mchar_t* p = str;
qse_mchar_t* out = buf;
while (*p != QSE_T('\0'))
{
if (IS_UNRESERVED(*p)) *out++ = *p;
else
{
*out++ = QSE_MT('%');
*out++ = TO_HEX (*p >> 4);
*out++ = TO_HEX (*p & 15);
}
p++;
}
*out = QSE_MT('\0');
return out - buf;
}
qse_mchar_t* qse_perenchttpstrdup (const qse_mchar_t* str, qse_mmgr_t* mmgr)
{
qse_mchar_t* buf;
qse_size_t len = 0;
qse_size_t count = 0;
/* count the number of characters that should be encoded */
for (len = 0; str[len] != QSE_T('\0'); len++)
{
if (!IS_UNRESERVED(str[len])) count++;
}
/* if there are no characters to escape, just return the original string */
if (count <= 0) return (qse_mchar_t*)str;
/* allocate a buffer of an optimal size for escaping, otherwise */
buf = QSE_MMGR_ALLOC (mmgr, (len + (count * 2) + 1) * QSE_SIZEOF(*buf));
if (buf == QSE_NULL) return QSE_NULL;
/* perform actual escaping */
qse_perenchttpstr (str, buf);
return buf;
}

View File

@ -577,7 +577,7 @@ qse_printf (QSE_T("FORWARD: CLEARING REQCON FOR ERROR\n"));
if (writable) goto forward;
n = httpd->cbs->mux.writable (
n = httpd->scb->mux.writable (
httpd, qse_pio_gethandleasubi (&cgi->pio, QSE_PIO_IN), 0);
if (n >= 1)
{
@ -886,7 +886,7 @@ static QSE_INLINE qse_ssize_t cgi_write_script_output_to_client (
qse_ssize_t n;
httpd->errnum = QSE_HTTPD_ENOERR;
n = httpd->cbs->client.send (httpd, client, cgi->buf, cgi->buflen);
n = httpd->scb->client.send (httpd, client, cgi->buf, cgi->buflen);
if (n > 0)
{
QSE_MEMCPY (&cgi->buf[0], &cgi->buf[n], cgi->buflen - n);
@ -1140,7 +1140,7 @@ qse_printf (QSE_T("[cgi_3 sending %d bytes]\n"), (int)count);
if (count > 0)
{
httpd->errnum = QSE_HTTPD_ENOERR;
n = httpd->cbs->client.send (httpd, client, cgi->res_ptr, count);
n = httpd->scb->client.send (httpd, client, cgi->res_ptr, count);
if (n <= -1)
{

View File

@ -22,6 +22,7 @@
#include "../cmn/mem.h"
#include "../cmn/syscall.h"
#include <qse/cmn/str.h>
#include <qse/cmn/path.h>
#include <qse/cmn/stdio.h> /* TODO: remove this */
typedef struct task_dir_t task_dir_t;
@ -35,15 +36,24 @@ struct task_dir_t
typedef struct task_dseg_t task_dseg_t;
struct task_dseg_t
{
qse_http_version_t version;
int keepalive;
int chunked;
const qse_mchar_t* path;
qse_dir_t* handle;
qse_dirent_t* dent;
int header_added;
int footer_pending;
#define HEADER_ADDED (1 << 0)
#define FOOTER_ADDED (1 << 1)
#define FOOTER_PENDING (1 << 2)
#define DIRENT_PENDING (1 << 3)
int state;
/*qse_mchar_t buf[4096];*/
qse_mchar_t buf[512]; /* TOOD: increate size */
qse_size_t tcount; /* total directory entries */
qse_size_t dcount; /* the number of items in the buffer */
qse_mchar_t buf[4096];
qse_size_t bufpos;
qse_size_t buflen;
qse_size_t bufrem;
@ -70,14 +80,88 @@ static void task_fini_dseg (
QSE_CLOSEDIR (ctx->handle);
}
static int task_main_dseg_chunked (
#define SIZE_CHLEN 4 /* the space size to hold the hexadecimal chunk length */
#define SIZE_CHLENCRLF 2 /* the space size to hold CRLF after the chunk length */
#define SIZE_CHENDCRLF 2 /* the sapce size to hold CRLF after the chunk data */
static QSE_INLINE void close_chunk_data (task_dseg_t* ctx, qse_size_t len)
{
ctx->chunklen = len;
/* CHENDCRLF - there is always space for these two.
* reserved with SIZE_CHENDCRLF */
ctx->buf[ctx->buflen++] = QSE_MT('\r');
ctx->buf[ctx->buflen++] = QSE_MT('\n');
}
static void fill_chunk_length (task_dseg_t* ctx)
{
int x;
/* 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)));
/* i don't check the error of snprintf because i've secured the
* suffient space for the chunk length at the beginning of the buffer */
/* CHLENCRLF */
ctx->buf[x] = QSE_MT('\r');
ctx->buf[x+1] = QSE_MT('\n');
/* skip leading space padding */
QSE_ASSERT (ctx->bufpos == 0);
while (ctx->buf[ctx->bufpos] == QSE_MT(' ')) ctx->bufpos++;
}
static int add_footer (task_dseg_t* ctx)
{
int x;
if (ctx->chunked)
{
x = snprintf (
&ctx->buf[ctx->buflen], ctx->bufrem,
QSE_MT("</ul>Total %lu entries</body></html>\r\n0\r\n"), (unsigned long)ctx->tcount);
}
else
{
x = snprintf (
&ctx->buf[ctx->buflen], ctx->bufrem,
QSE_MT("</ul>Total %lu entries</body></html>"), (unsigned long)ctx->tcount);
}
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->bufrem -= x;
/* -5 for \r\n0\r\n added above */
if (ctx->chunked) close_chunk_data (ctx, ctx->buflen - 5);
return 0;
}
static int task_main_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_ssize_t n;
int x;
if (ctx->bufpos < ctx->buflen) goto send_dirlist;
if (ctx->bufpos < ctx->buflen)
{
/* buffer contents not fully sent yet */
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
@ -103,122 +187,149 @@ static int task_main_dseg_chunked (
* 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)
/* initialize buffer */
ctx->dcount = 0; /* reset the entry counter */
ctx->bufpos = 0;
if (ctx->chunked)
{
x = snprintf (
&ctx->buf[ctx->buflen],
ctx->bufrem,
QSE_MT("</ul></body></html>\r\n0\r\n"));
if (x == -1 || x >= ctx->bufrem)
/* 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;
}
else
{
ctx->buflen = 0;
ctx->bufrem = QSE_COUNTOF(ctx->buf);
}
if (ctx->state & FOOTER_PENDING)
{
/* only footers yet to be sent */
if (add_footer (ctx) <= -1)
{
/* return an error if the buffer is too small to hold the
* trailing footer. you need to increate the buffer size */
httpd->errnum = QSE_HTTPD_EINTERN;
return -1;
}
ctx->buflen += x;
ctx->chunklen = ctx->buflen - 5; /* -5 for \r\n0\r\n added above */
ctx->state &= ~FOOTER_PENDING;
ctx->state |= FOOTER_ADDED;
/* CHENDCRLF */
ctx->buf[ctx->buflen++] = QSE_MT('\r');
ctx->buf[ctx->buflen++] = QSE_MT('\n');
goto set_chunklen;
if (ctx->chunked) fill_chunk_length (ctx);
goto send_dirlist;
}
if (!ctx->header_added)
if (!(ctx->state & HEADER_ADDED))
{
/* compose the header since this is the first time. */
int is_root;
is_root = (qse_mbscmp (ctx->path, QSE_MT("/")) == 0);
/* compose the header since this is the first time. */
/* TODO: page encoding?? utf-8??? or derive name from cmgr or current locale??? */
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
&ctx->buf[ctx->buflen], ctx->bufrem,
QSE_MT("<html><head></head><body><b>%s</b><ul>%s"),
ctx->path, (is_root? QSE_MT(""): QSE_MT("<li><a href='../'>..</a></li>"))
);
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 */
* you need to increate the buffer size. or i have make the buffer
* dynamic. */
httpd->errnum = QSE_HTTPD_EINTERN;
return -1;
}
ctx->buflen += x;
ctx->bufrem -= x;
ctx->header_added = 1;
ctx->state |= HEADER_ADDED;
ctx->dcount++;
}
if (!ctx->dent)
/*if (!ctx->dent) ctx->dent = QSE_READDIR (ctx->handle); */
if (ctx->state & DIRENT_PENDING)
ctx->state &= ~DIRENT_PENDING;
else
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)
/* TODO: check if errno has changed from before QSE_READDIR().
and return -1 if so. */
if (add_footer (ctx) <= -1)
{
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');
/* failed to add the footer part */
if (ctx->chunked)
{
close_chunk_data (ctx, ctx->buflen);
fill_chunk_length (ctx);
}
ctx->state |= FOOTER_PENDING;
}
else if (ctx->chunked) fill_chunk_length (ctx);
break;
}
else if (qse_mbscmp (ctx->dent->d_name, QSE_MT(".")) != 0 &&
qse_mbscmp (ctx->dent->d_name, QSE_MT("..")) != 0)
{
qse_mchar_t* encname;
/* TODO: better buffer management in case there are
* a lot of file names to escape. */
encname = qse_perenchttpstrdup (ctx->dent->d_name, httpd->mmgr);
if (encname == QSE_NULL)
{
httpd->errnum = QSE_HTTPD_ENOMEM;
return -1;
}
x = snprintf (
&ctx->buf[ctx->buflen],
ctx->bufrem,
QSE_MT("<li><a href='%s%s'>%s%s</a></li>"),
ctx->dent->d_name,
encname,
(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 (encname != ctx->dent->d_name) QSE_MMGR_FREE (httpd->mmgr, encname);
if (x == -1 || x >= ctx->bufrem)
{
/* buffer not large enough to hold this entry */
ctx->chunklen = ctx->buflen;
if (ctx->dcount <= 0)
{
/* neither directory entry nor the header
* has been added to the buffer so far. and
* this attempt has failed. the buffer size must
* be too small. you must increase it */
httpd->errnum = QSE_HTTPD_EINTERN;
return -1;
}
/* CHENDCRLF */
ctx->buf[ctx->buflen++] = QSE_MT('\r');
ctx->buf[ctx->buflen++] = QSE_MT('\n');
if (ctx->chunked)
{
close_chunk_data (ctx, ctx->buflen);
fill_chunk_length (ctx);
}
ctx->state |= DIRENT_PENDING;
break;
}
else
{
ctx->buflen += x;
ctx->bufrem -= x;
ctx->dcount++;
ctx->tcount++;
}
}
@ -226,167 +337,41 @@ static int task_main_dseg_chunked (
}
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);
n = httpd->scb->client.send (
httpd, client, &ctx->buf[ctx->bufpos], ctx->buflen - ctx->bufpos);
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;
return (ctx->bufpos < ctx->buflen || (ctx->state & 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* pred, qse_dir_t* handle, task_dir_t* dir)
{
qse_httpd_task_t task;
task_dseg_t data;
QSE_MEMSET (&data, 0, QSE_SIZEOF(data));
data.handle = handle;
data.path = path;
data.version = dir->version;
data.keepalive = dir->keepalive;
data.chunked = dir->keepalive;
data.path = dir->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.main = task_main_dseg;
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);
return qse_httpd_entask (httpd, client, pred, &task, QSE_SIZEOF(data) + qse_mbslen(data.path) + 1);
}
/*------------------------------------------------------------------------*/
@ -398,6 +383,7 @@ static int task_init_dir (
/* 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);
@ -429,7 +415,7 @@ static QSE_INLINE int task_main_dir (
(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) x = entask_directory_segment (httpd, client, x, handle, dir);
if (x) return 0;
QSE_CLOSEDIR (handle);

View File

@ -22,6 +22,7 @@
#include "../cmn/mem.h"
#include "../cmn/syscall.h"
#include <qse/cmn/str.h>
#include <qse/cmn/path.h>
#include <qse/cmn/stdio.h> /* TODO: remove this */
typedef struct task_file_t task_file_t;
@ -54,7 +55,7 @@ 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);
httpd->scb->file.close (httpd, ctx->handle);
}
static int task_main_fseg (
@ -68,7 +69,7 @@ static int task_main_fseg (
if (count >= ctx->left) count = ctx->left;
/* TODO: more adjustment needed for OS with different sendfile semantics... */
n = httpd->cbs->client.sendfile (
n = httpd->scb->client.sendfile (
httpd, client, ctx->handle, &ctx->offset, count);
if (n <= -1)
{
@ -147,7 +148,7 @@ static QSE_INLINE int task_main_file (
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)
if (httpd->scb->file.stat (httpd, file->path, &st) <= -1)
{
int http_errnum;
http_errnum = (httpd->errnum == QSE_HTTPD_ENOENT)? 404:
@ -159,7 +160,7 @@ qse_printf (QSE_T("opening file %hs\n"), file->path);
}
httpd->errnum = QSE_HTTPD_ENOERR;
if (httpd->cbs->file.ropen (httpd, file->path, &handle) <= -1)
if (httpd->scb->file.ropen (httpd, file->path, &handle) <= -1)
{
int http_errnum;
http_errnum = (httpd->errnum == QSE_HTTPD_ENOENT)? 404:
@ -266,11 +267,11 @@ qse_printf (QSE_T("opening file %hs\n"), file->path);
}
if (x) return 0;
httpd->cbs->file.close (httpd, handle);
httpd->scb->file.close (httpd, handle);
return -1;
no_file_send:
if (fileopen) httpd->cbs->file.close (httpd, handle);
if (fileopen) httpd->scb->file.close (httpd, handle);
return (x == QSE_NULL)? -1: 0;
}

View File

@ -608,7 +608,7 @@ qse_printf (QSE_T("FORWARD: CLEARING REQCON FOR ERROR\n"));
if (writable) goto forward;
n = httpd->cbs->mux.writable (httpd, proxy->peer.handle, 0);
n = httpd->scb->mux.writable (httpd, proxy->peer.handle, 0);
if (n == 0) qse_printf (QSE_T("PROXY FORWARD: @@@@@@@@@NOT WRITABLE\n"));
if (n >= 1)
{
@ -617,7 +617,7 @@ if (n == 0) qse_printf (QSE_T("PROXY FORWARD: @@@@@@@@@NOT WRITABLE\n"));
qse_printf (QSE_T("PROXY FORWARD: @@@@@@@@@@WRITING[%.*hs]\n"),
(int)QSE_MBS_LEN(proxy->reqfwdbuf),
QSE_MBS_PTR(proxy->reqfwdbuf));
n = httpd->cbs->peer.send (
n = httpd->scb->peer.send (
httpd, &proxy->peer,
QSE_MBS_PTR(proxy->reqfwdbuf),
QSE_MBS_LEN(proxy->reqfwdbuf)
@ -866,7 +866,7 @@ static void task_fini_proxy (
task_proxy_t* proxy = (task_proxy_t*)task->ctx;
if (proxy->peer_status & PROXY_PEER_OPEN)
httpd->cbs->peer.close (httpd, &proxy->peer);
httpd->scb->peer.close (httpd, &proxy->peer);
if (proxy->res) qse_mbs_close (proxy->res);
if (proxy->peer_htrd) qse_htrd_close (proxy->peer_htrd);
@ -902,7 +902,7 @@ qse_printf (QSE_T("task_main_proxy_5 trigger[0].mask=%d trigger[1].mask=%d trigg
{
/* TODO: check if proxy outputs more than content-length if it is set... */
httpd->errnum = QSE_HTTPD_ENOERR;
n = httpd->cbs->client.send (httpd, client, proxy->buf, proxy->buflen);
n = httpd->scb->client.send (httpd, client, proxy->buf, proxy->buflen);
if (n <= -1)
{
/* can't return internal server error any more... */
@ -950,7 +950,7 @@ qse_printf (QSE_T("task_main_proxy_4 about to read from PEER...\n"));
{
qse_printf (QSE_T("task_main_proxy_4 reading from PEER... %d %d\n"), (int)proxy->peer_output_length, (int)proxy->peer_output_received);
httpd->errnum = QSE_HTTPD_ENOERR;
n = httpd->cbs->peer.recv (
n = httpd->scb->peer.recv (
httpd, &proxy->peer,
&proxy->buf[proxy->buflen],
QSE_SIZEOF(proxy->buf) - proxy->buflen
@ -1005,7 +1005,7 @@ qse_printf (QSE_T("task_main_proxy_4 read from PEER...%d\n"), (int)n);
* side is writable. it should be safe to write whenever
* this task function is called. */
httpd->errnum = QSE_HTTPD_ENOERR;
n = httpd->cbs->client.send (httpd, client, proxy->buf, proxy->buflen);
n = httpd->scb->client.send (httpd, client, proxy->buf, proxy->buflen);
if (n <= -1)
{
/* can't return internal server error any more... */
@ -1057,7 +1057,7 @@ qse_printf (QSE_T("[PROXY-----3 SENDING XXXXX]\n"));
{
qse_printf (QSE_T("[proxy_3 sending %d bytes]\n"), (int)count);
httpd->errnum = QSE_HTTPD_ENOERR;
n = httpd->cbs->client.send (
n = httpd->scb->client.send (
httpd, client,
&QSE_MBS_CHAR(proxy->res,proxy->res_consumed),
count
@ -1150,7 +1150,7 @@ for (i = 0; i < count; i++) qse_printf (QSE_T("%hc"), QSE_MBS_CHAR(proxy->res,pr
qse_printf (QSE_T("]\n"));
httpd->errnum = QSE_HTTPD_ENOERR;
n = httpd->cbs->client.send (
n = httpd->scb->client.send (
httpd, client,
QSE_MBS_CPTR(proxy->res,proxy->res_consumed),
count
@ -1181,7 +1181,7 @@ qse_printf (QSE_T("[proxy-2 send failure....\n"));
/* there is something to read from peer */
httpd->errnum = QSE_HTTPD_ENOERR;
n = httpd->cbs->peer.recv (
n = httpd->scb->peer.recv (
httpd, &proxy->peer,
&proxy->buf[proxy->buflen],
QSE_SIZEOF(proxy->buf) - proxy->buflen
@ -1307,7 +1307,7 @@ qse_printf (QSE_T("task_main_proxy_1....\n"));
int n;
httpd->errnum = QSE_HTTPD_ENOERR;
n = httpd->cbs->peer.connected (httpd, &proxy->peer);
n = httpd->scb->peer.connected (httpd, &proxy->peer);
if (n <= -1)
{
/* improve error conversion */
@ -1378,7 +1378,7 @@ qse_printf (QSE_T("task_main_proxy....\n"));
proxy->res_pending = 0;
httpd->errnum = QSE_HTTPD_ENOERR;
n = httpd->cbs->peer.open (httpd, &proxy->peer);
n = httpd->scb->peer.open (httpd, &proxy->peer);
if (n <= -1)
{
/* TODO: translate error code to http error... */

View File

@ -34,8 +34,6 @@
#else
# include "../cmn/syscall.h"
# include <errno.h>
# include <sys/stat.h>
# include <sys/socket.h>
# include <netinet/in.h>
# if defined(HAVE_SYS_SENDFILE_H)
@ -49,8 +47,6 @@
# endif
#endif
#if defined(HAVE_SSL)
# include <openssl/ssl.h>
# include <openssl/err.h>
@ -543,13 +539,13 @@ static int server_open (qse_httpd_t* httpd, qse_httpd_server_t* server)
oops:
qse_httpd_seterrnum (httpd, syserr_to_errnum(errno));
if (fd >= 0) close (fd);
if (fd >= 0) QSE_CLOSE (fd);
return -1;
}
static void server_close (qse_httpd_t* httpd, qse_httpd_server_t* server)
{
close (server->handle.i);
QSE_CLOSE (server->handle.i);
}
static int server_accept (
@ -578,7 +574,7 @@ static int server_accept (
{
qse_fprintf (QSE_STDERR, QSE_T("Error: too many client?\n"));
/*TODO: qse_httpd_seterrnum (httpd, QSE_HTTPD_EXXXXX);*/
close (fd);
QSE_CLOSE (fd);
return -1;
}
#endif
@ -659,13 +655,13 @@ static int peer_open (qse_httpd_t* httpd, qse_httpd_peer_t* peer)
oops:
qse_httpd_seterrnum (httpd, syserr_to_errnum(errno));
if (fd >= 0) close (fd);
if (fd >= 0) QSE_CLOSE (fd);
return -1;
}
static void peer_close (qse_httpd_t* httpd, qse_httpd_peer_t* peer)
{
close (peer->handle.i);
QSE_CLOSE (peer->handle.i);
}
static int peer_connected (qse_httpd_t* httpd, qse_httpd_peer_t* peer)
@ -782,7 +778,7 @@ static void mux_close (qse_httpd_t* httpd, void* vmux)
if (mux->mev.ptr[i]) qse_httpd_freemem (httpd, mux->mev.ptr[i]);
qse_httpd_freemem (httpd, mux->mev.ptr);
}
close (mux->fd);
QSE_CLOSE (mux->fd);
qse_httpd_freemem (httpd, mux);
}
@ -998,7 +994,10 @@ static int file_stat (
qse_mbsend (path, QSE_MT(".txt"))? QSE_MT("text/plain"):
qse_mbsend (path, QSE_MT(".jpg"))? QSE_MT("image/jpeg"):
qse_mbsend (path, QSE_MT(".mp4"))? QSE_MT("video/mp4"):
qse_mbsend (path, QSE_MT(".mp3"))? QSE_MT("audio/mpeg"): QSE_NULL;
qse_mbsend (path, QSE_MT(".mp3"))? QSE_MT("audio/mpeg"):
qse_mbsend (path, QSE_MT(".c"))? QSE_MT("text/plain"):
qse_mbsend (path, QSE_MT(".h"))? QSE_MT("text/plain"):
QSE_NULL;
return 0;
}
@ -1014,7 +1013,7 @@ static int file_ropen (
#endif
qse_printf (QSE_T("opening file [%hs] for reading\n"), path);
fd = open (path, flags, 0);
fd = QSE_OPEN (path, flags, 0);
if (fd <= -1)
{
qse_httpd_seterrnum (httpd, syserr_to_errnum(errno));
@ -1042,7 +1041,7 @@ static int file_wopen (
#endif
qse_printf (QSE_T("opening file [%hs] for writing\n"), path);
fd = open (path, flags, 0644);
fd = QSE_OPEN (path, flags, 0644);
if (fd <= -1)
{
qse_httpd_seterrnum (httpd, syserr_to_errnum(errno));
@ -1056,28 +1055,28 @@ qse_printf (QSE_T("opening file [%hs] for writing\n"), path);
static void file_close (qse_httpd_t* httpd, qse_ubi_t handle)
{
qse_printf (QSE_T("closing file %d\n"), handle.i);
close (handle.i);
QSE_CLOSE (handle.i);
}
static qse_ssize_t file_read (
qse_httpd_t* httpd, qse_ubi_t handle,
qse_mchar_t* buf, qse_size_t len)
{
return read (handle.i, buf, len);
return QSE_READ (handle.i, buf, len);
}
static qse_ssize_t file_write (
qse_httpd_t* httpd, qse_ubi_t handle,
const qse_mchar_t* buf, qse_size_t len)
{
return write (handle.i, buf, len);
return QSE_WRITE (handle.i, buf, len);
}
/* ------------------------------------------------------------------- */
static void client_close (
qse_httpd_t* httpd, qse_httpd_client_t* client)
{
close (client->handle.i);
QSE_CLOSE (client->handle.i);
}
static void client_shutdown (
@ -1264,8 +1263,10 @@ static int process_request (
/* percent-decode the query path to the original buffer
* since i'm not gonna need it in the original form
* any more */
qse_perdechttpstr (qse_htre_getqpath(req), qse_htre_getqpath(req));
* any more. once it's decoded in the peek mode,
* the decoded query path is made available in the
* non-peek mode as well */
if (peek) qse_perdechttpstr (qse_htre_getqpath(req), qse_htre_getqpath(req));
qse_printf (QSE_T("================================\n"));
qse_printf (QSE_T("[%lu] %hs REQUEST ==> [%hs] version[%d.%d %hs] method[%hs]\n"),
@ -1423,14 +1424,8 @@ qse_printf (QSE_T("Entasking chunked CGI...\n"));
#else
if (peek)
{
qse_stat_t st;
qse_httpd_discardcontent (httpd, 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);
task = qse_httpd_entaskpath (httpd, client, QSE_NULL, qpath, req);
if (task == QSE_NULL) goto oops;
}
#endif
@ -1578,7 +1573,7 @@ static int handle_request (
}
}
static qse_httpd_cbs_t httpd_standard_callbacks =
static qse_httpd_scb_t httpd_system_callbacks =
{
/* server */
{ server_open, server_close, server_accept },
@ -1617,14 +1612,17 @@ static qse_httpd_cbs_t httpd_standard_callbacks =
client_send,
client_sendfile,
client_accepted,
client_closed },
/* http request */
peek_request,
handle_request,
client_closed }
};
int qse_httpd_loopstd (qse_httpd_t* httpd, qse_ntime_t timeout)
static qse_httpd_rcb_t httpd_request_callbacks =
{
return qse_httpd_loop (httpd, &httpd_standard_callbacks, timeout);
peek_request,
handle_request
};
int qse_httpd_loopstd (qse_httpd_t* httpd, qse_httpd_rcb_t* rcb, qse_ntime_t timeout)
{
if (rcb == QSE_NULL) rcb = &httpd_request_callbacks;
return qse_httpd_loop (httpd, &httpd_system_callbacks, rcb, timeout);
}

View File

@ -18,20 +18,24 @@
License along with QSE. If not, see <htrd://www.gnu.org/licenses/>.
*/
#if defined(_WIN32) || defined(__DOS__) || defined(__OS2__)
/* UNSUPPORTED YET.. */
/* TODO: IMPLEMENT THIS */
#else
#include "httpd.h"
#include "../cmn/mem.h"
#include <qse/cmn/str.h>
#include <qse/cmn/fmt.h>
#include <qse/cmn/stdio.h> /* TODO: remove this */
#if defined(_WIN32)
/* TODO */
#elif defined(__DOS__)
/* TODO */
#elif defined(__OS2__)
/* TODO */
#else
# include "../cmn/syscall.h"
#endif
#include <stdarg.h>
#include <stdio.h>
#include <qse/cmn/stdio.h> /* TODO: remove this */
/* TODO:
* many functions in this file use qse_size_t.
@ -43,7 +47,7 @@
static int task_main_disconnect (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
httpd->cbs->client.shutdown (httpd, client);
httpd->scb->client.shutdown (httpd, client);
return 0;
}
@ -75,7 +79,7 @@ static int task_main_statictext (
}
/* TODO: do i need to add code to skip this send if count is 0? */
n = httpd->cbs->client.send (httpd, client, task->ctx, count);
n = httpd->scb->client.send (httpd, client, task->ctx, count);
if (n <= -1) return -1;
ptr = (const qse_mchar_t*)task->ctx + n;
@ -133,7 +137,7 @@ static int task_main_text (
if (count >= ctx->left) count = ctx->left;
/* TODO: do i need to add code to skip this send if count is 0? */
n = httpd->cbs->client.send (httpd, client, ctx->ptr, count);
n = httpd->scb->client.send (httpd, client, ctx->ptr, count);
if (n <= -1) return -1;
ctx->left -= n;
@ -206,7 +210,7 @@ static int task_main_format (
count = MAX_SEND_SIZE;
if (count >= ctx->left) count = ctx->left;
n = httpd->cbs->client.send (httpd, client, ctx->ptr, count);
n = httpd->scb->client.send (httpd, client, ctx->ptr, count);
if (n <= -1) return -1;
ctx->left -= n;
@ -437,6 +441,21 @@ qse_httpd_task_t* qse_httpd_entaskauth (
realm, (unsigned long)qse_mbslen(lmsg) + 4, lmsg);
}
/*------------------------------------------------------------------------*/
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_stat_t st;
qse_httpd_task_t* task;
if (QSE_LSTAT (name, &st) == 0 && S_ISDIR(st.st_mode))
task = qse_httpd_entaskdir (httpd, client, pred, name, req);
else
task = qse_httpd_entaskfile (httpd, client, pred, name, req);
return task;
}
/*------------------------------------------------------------------------*/
#if 0
@ -451,4 +470,3 @@ qse_httpd_task_t* qse_httpd_entaskconnect (
}
#endif
#endif

View File

@ -245,7 +245,7 @@ static QSE_INLINE int dequeue_task (
{
if (client->status & CLIENT_TASK_TRIGGER_IN_MUX(i))
{
httpd->cbs->mux.delhnd (httpd, httpd->mux, task->trigger[i].handle);
httpd->scb->mux.delhnd (httpd, httpd->mux, task->trigger[i].handle);
client->status &= ~CLIENT_TASK_TRIGGER_IN_MUX(i);
}
}
@ -281,13 +281,13 @@ static QSE_INLINE void purge_tasks (
static int htrd_peek_request (qse_htrd_t* htrd, qse_htre_t* req)
{
htrd_xtn_t* xtn = (htrd_xtn_t*) qse_htrd_getxtn (htrd);
return xtn->httpd->cbs->peek_request (xtn->httpd, xtn->client, req);
return xtn->httpd->rcb->peek_request (xtn->httpd, xtn->client, req);
}
static int htrd_handle_request (qse_htrd_t* htrd, qse_htre_t* req)
{
htrd_xtn_t* xtn = (htrd_xtn_t*) qse_htrd_getxtn (htrd);
return xtn->httpd->cbs->handle_request (xtn->httpd, xtn->client, req);
return xtn->httpd->rcb->handle_request (xtn->httpd, xtn->client, req);
}
static qse_htrd_recbs_t htrd_recbs =
@ -317,9 +317,9 @@ static qse_httpd_client_t* new_client (
return QSE_NULL;
}
qse_htrd_setoption (client->htrd, QSE_HTRD_REQUEST | QSE_HTRD_TRAILERS);
qse_htrd_setoption (client->htrd, QSE_HTRD_REQUEST | QSE_HTRD_TRAILERS | QSE_HTRD_CANONQPATH);
if (httpd->cbs->client.accepted == QSE_NULL)
if (httpd->scb->client.accepted == QSE_NULL)
client->status |= CLIENT_READY;
client->status = tmpl->status;
@ -351,16 +351,16 @@ qse_printf (QSE_T("Debug: CLOSING SOCKET %d\n"), client->handle.i);
if (client->status & CLIENT_HANDLE_IN_MUX)
{
httpd->cbs->mux.delhnd (httpd, httpd->mux, client->handle);
httpd->scb->mux.delhnd (httpd, httpd->mux, client->handle);
client->status &= ~CLIENT_HANDLE_IN_MUX;
}
/* note that client.closed is not a counterpart to client.accepted.
* so it is called even if client.close() failed. */
if (httpd->cbs->client.closed)
httpd->cbs->client.closed (httpd, client);
if (httpd->scb->client.closed)
httpd->scb->client.closed (httpd, client);
httpd->cbs->client.close (httpd, client);
httpd->scb->client.close (httpd, client);
qse_httpd_freemem (httpd, client);
}
@ -434,7 +434,7 @@ static int accept_client (
QSE_MEMSET (&clibuf, 0, QSE_SIZEOF(clibuf));
if (httpd->cbs->server.accept (httpd, server, &clibuf) <= -1)
if (httpd->scb->server.accept (httpd, server, &clibuf) <= -1)
{
/* TODO: proper logging */
qse_char_t tmp[128];
@ -451,12 +451,12 @@ qse_printf (QSE_T("failed to accept from server %s\n"), tmp);
client = new_client (httpd, &clibuf);
if (client == QSE_NULL)
{
httpd->cbs->client.close (httpd, &clibuf);
httpd->scb->client.close (httpd, &clibuf);
return -1;
}
qse_printf (QSE_T("MUX ADDHND CLIENT READ %d\n"), client->handle.i);
if (httpd->cbs->mux.addhnd (
if (httpd->scb->mux.addhnd (
httpd, mux, client->handle, QSE_HTTPD_MUX_READ,
perform_client_task, client) <= -1)
{
@ -540,8 +540,8 @@ static void deactivate_servers (qse_httpd_t* httpd)
{
if (server->active)
{
httpd->cbs->mux.delhnd (httpd, httpd->mux, server->handle);
httpd->cbs->server.close (httpd, server);
httpd->scb->mux.delhnd (httpd, httpd->mux, server->handle);
httpd->scb->server.close (httpd, server);
server->active = 0;
httpd->server.nactive--;
}
@ -554,7 +554,7 @@ static int activate_servers (qse_httpd_t* httpd)
for (server = httpd->server.list; server; server = server->next)
{
if (httpd->cbs->server.open (httpd, server) <= -1)
if (httpd->scb->server.open (httpd, server) <= -1)
{
qse_char_t buf[64];
qse_nwadtostr (&server->nwad, buf, QSE_COUNTOF(buf), QSE_NWADTOSTR_ALL);
@ -563,12 +563,12 @@ qse_printf (QSE_T("FAILED TO ACTIVATE SERVER....[%s]\n"), buf);
}
qse_printf (QSE_T("MUX ADDHND SERVER %d\n"), server->handle.i);
if (httpd->cbs->mux.addhnd (
if (httpd->scb->mux.addhnd (
httpd, httpd->mux, server->handle, QSE_HTTPD_MUX_READ,
accept_client, server) <= -1)
{
qse_printf (QSE_T("FAILED TO ADD SERVER HANDLE TO MUX....\n"));
httpd->cbs->server.close (httpd, server);
httpd->scb->server.close (httpd, server);
continue;
}
@ -585,7 +585,7 @@ static void free_server_list (qse_httpd_t* httpd, qse_httpd_server_t* server)
{
qse_httpd_server_t* next = server->next;
httpd->cbs->server.close (httpd, server);
httpd->scb->server.close (httpd, server);
qse_httpd_freemem (httpd, server);
httpd->server.navail--;
@ -689,11 +689,11 @@ static int read_from_client (qse_httpd_t* httpd, qse_httpd_client_t* client)
qse_mchar_t buf[4096]; /* TODO: adjust this buffer size */
qse_ssize_t m;
QSE_ASSERT (httpd->cbs->client.recv != QSE_NULL);
QSE_ASSERT (httpd->scb->client.recv != QSE_NULL);
reread:
httpd->errnum = QSE_HTTPD_ENOERR;
m = httpd->cbs->client.recv (httpd, client, buf, QSE_SIZEOF(buf));
m = httpd->scb->client.recv (httpd, client, buf, QSE_SIZEOF(buf));
if (m <= -1)
{
if (httpd->errnum == QSE_HTTPD_EAGAIN)
@ -840,7 +840,7 @@ qse_printf (QSE_T("ERROR: mute client got no more task [%d] failed...\n"), (int)
{
/* the task is invoked for triggers.
* check if the client handle is writable */
if (httpd->cbs->mux.writable (httpd, client->handle, 0) <= 0)
if (httpd->scb->mux.writable (httpd, client->handle, 0) <= 0)
{
/* it is not writable yet. so just skip
* performing the actual task */
@ -891,12 +891,12 @@ qse_printf (QSE_T("REMOVING XXXXX FROM READING NO MORE TASK....\n"));
if ((client->status & CLIENT_HANDLE_IN_MUX) !=
(mux_status & CLIENT_HANDLE_IN_MUX))
{
httpd->cbs->mux.delhnd (httpd, httpd->mux, client->handle);
httpd->scb->mux.delhnd (httpd, httpd->mux, client->handle);
client->status &= ~CLIENT_HANDLE_IN_MUX;
if (mux_status)
{
if (httpd->cbs->mux.addhnd (
if (httpd->scb->mux.addhnd (
httpd, httpd->mux, client->handle,
mux_mask, perform_client_task, client) <= -1)
{
@ -937,7 +937,7 @@ qse_printf (QSE_T("REMOVING XXXXX FROM READING NO MORE TASK....\n"));
{
if (client->status & CLIENT_TASK_TRIGGER_IN_MUX(i))
{
httpd->cbs->mux.delhnd (httpd, httpd->mux, client->trigger[i].handle);
httpd->scb->mux.delhnd (httpd, httpd->mux, client->trigger[i].handle);
client->status &= ~CLIENT_TASK_TRIGGER_IN_MUX(i);
}
}
@ -983,7 +983,7 @@ qse_printf (QSE_T("REMOVING XXXXX FROM READING NO MORE TASK....\n"));
}
else
{
if (httpd->cbs->mux.addhnd (
if (httpd->scb->mux.addhnd (
httpd, httpd->mux, task->trigger[i].handle,
trigger_mux_mask, perform_client_task, client) <= -1)
{
@ -1012,12 +1012,12 @@ qse_printf (QSE_T("REMOVING XXXXX FROM READING NO MORE TASK....\n"));
if ((client->status & CLIENT_HANDLE_IN_MUX) !=
(client_handle_mux_status & CLIENT_HANDLE_IN_MUX))
{
httpd->cbs->mux.delhnd (httpd, httpd->mux, client->handle);
httpd->scb->mux.delhnd (httpd, httpd->mux, client->handle);
client->status &= ~CLIENT_HANDLE_IN_MUX;
if (client_handle_mux_mask)
{
if (httpd->cbs->mux.addhnd (
if (httpd->scb->mux.addhnd (
httpd, httpd->mux, client->handle,
client_handle_mux_mask, perform_client_task, client) <= -1)
{
@ -1045,7 +1045,7 @@ static int perform_client_task (
if (!(client->status & CLIENT_READY))
{
int x;
x = httpd->cbs->client.accepted (httpd, client);
x = httpd->scb->client.accepted (httpd, client);
if (x <= -1) goto oops;
if (x >= 1)
{
@ -1139,11 +1139,11 @@ qse_httpd_task_t* qse_httpd_entask (
/* arrange to invokde this task so long as
* the client-side handle is writable. */
QSE_ASSERT (client->status & CLIENT_HANDLE_IN_MUX);
httpd->cbs->mux.delhnd (httpd, httpd->mux, client->handle);
httpd->scb->mux.delhnd (httpd, httpd->mux, client->handle);
client->status &= ~CLIENT_HANDLE_IN_MUX;
qse_printf (QSE_T("MUX ADDHND CLIENT RW(ENTASK) %d\n"), client->handle.i);
if (httpd->cbs->mux.addhnd (
if (httpd->scb->mux.addhnd (
httpd, httpd->mux, client->handle,
QSE_HTTPD_MUX_READ | QSE_HTTPD_MUX_WRITE,
perform_client_task, client) <= -1)
@ -1158,30 +1158,28 @@ qse_printf (QSE_T("MUX ADDHND CLIENT RW(ENTASK) %d\n"), client->handle.i);
return new_task;
}
int qse_httpd_loop (qse_httpd_t* httpd, qse_httpd_cbs_t* cbs, qse_ntime_t timeout)
int qse_httpd_loop (qse_httpd_t* httpd, qse_httpd_scb_t* scb, qse_httpd_rcb_t* rcb, qse_ntime_t timeout)
{
httpd->stopreq = 0;
httpd->cbs = cbs;
QSE_ASSERTX (httpd->server.list != QSE_NULL,
"Add listeners before calling qse_httpd_loop()");
QSE_ASSERTX (httpd->client.list.head == QSE_NULL,
"No client should exist when this loop is started");
QSE_ASSERTX (httpd->cbs != QSE_NULL,
"Set httpd callbacks before calling qse_httpd_loop()");
if (httpd->server.list == QSE_NULL)
if (scb == QSE_NULL || rcb == QSE_NULL ||
httpd->server.list == QSE_NULL)
{
/* no listener specified */
httpd->errnum = QSE_HTTPD_EINVAL;
return -1;
}
httpd->stopreq = 0;
httpd->scb = scb;
httpd->rcb = rcb;
QSE_ASSERT (httpd->server.navail > 0);
httpd->mux = httpd->cbs->mux.open (httpd);
httpd->mux = httpd->scb->mux.open (httpd);
if (httpd->mux == QSE_NULL)
{
qse_printf (QSE_T("can't open mux....\n"));
@ -1190,13 +1188,13 @@ qse_printf (QSE_T("can't open mux....\n"));
if (activate_servers (httpd) <= -1)
{
httpd->cbs->mux.close (httpd, httpd->mux);
httpd->scb->mux.close (httpd, httpd->mux);
return -1;
}
if (httpd->server.nactive <= 0)
{
qse_printf (QSE_T("no servers are active....\n"));
httpd->cbs->mux.close (httpd, httpd->mux);
httpd->scb->mux.close (httpd, httpd->mux);
return -1;
}
@ -1204,7 +1202,7 @@ qse_printf (QSE_T("no servers are active....\n"));
{
int count;
count = httpd->cbs->mux.poll (httpd, httpd->mux, timeout);
count = httpd->scb->mux.poll (httpd, httpd->mux, timeout);
if (count <= -1)
{
httpd->errnum = QSE_HTTPD_EIOMUX;
@ -1220,7 +1218,7 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: mux returned failure\n"));
purge_client_list (httpd);
deactivate_servers (httpd);
httpd->cbs->mux.close (httpd, httpd->mux);
httpd->scb->mux.close (httpd, httpd->mux);
return 0;
}

View File

@ -30,7 +30,8 @@ struct qse_httpd_t
QSE_DEFINE_COMMON_FIELDS (httpd)
qse_httpd_errnum_t errnum;
qse_httpd_ecb_t* ecb; /* event callbacks */
qse_httpd_cbs_t* cbs;
qse_httpd_scb_t* scb; /* system callbacks */
qse_httpd_rcb_t* rcb; /* request callbacks */
int option;
int stopreq;