enhanced httpd a bit

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

View File

@ -70,34 +70,34 @@ const qse_wchar_t* qse_wcsbasename (
);
/**
* The qse_isabspath() function determines if a path name is absolute.
* The qse_ismbsabspath() function determines if a path name is absolute.
* A path name beginning with a segment separator is absolute.
* On Win32/OS2/DOS, it also returns 1 if a path name begins with a drive
* letter followed by a colon.
* @return 1 if absolute, 0 if not.
*/
int qse_isabspath (
const qse_char_t* path
int qse_ismbsabspath (
const qse_mchar_t* path
);
/**
* The qse_isdrivepath() function determines if a path name begins with
* The qse_ismbsdrivepath() function determines if a path name begins with
* a drive letter followed by a colon like A:.
*/
int qse_isdrivepath (
const qse_char_t* path
int qse_ismbsdrivepath (
const qse_mchar_t* path
);
/**
* The qse_isdrivecurpath() function determines if a path name is in the form
* The qse_ismbsdrivecurpath() function determines if a path name is in the form
* of a drive letter followed by a colon like A:, without any trailing path.
*/
int qse_isdrivecurpath (
const qse_char_t* path
int qse_ismbsdrivecurpath (
const qse_mchar_t* path
);
/**
* The qse_canonpath() function canonicalizes a path name @a path by deleting
* The qse_canonmbspath() function canonicalizes a path name @a path by deleting
* unnecessary path segments from it and stores the result to a memory buffer
* pointed to by @a canon. Canonicalization is purely performed on the path
* name without refering to actual file systems. It null-terminates the
@ -105,9 +105,9 @@ int qse_isdrivecurpath (
* the terminating null.
*
* @code
* qse_char_t buf[64];
* qse_canonpath ("/usr/local/../bin/sh", buf);
* qse_printf (QSE_T("%s\n")); // prints /usr/bin/sh
* qse_mchar_t buf[64];
* qse_canonmbspath (QSE_MT("/usr/local/../bin/sh"), buf);
* qse_printf (QSE_T("%hs\n")); // prints /usr/bin/sh
* @endcode
*
* If #QSE_CANONPATH_EMPTYSINGLEDOT is clear in the @a flags, a single dot
@ -129,12 +129,90 @@ int qse_isdrivecurpath (
* @return number of characters in the resulting canonical path excluding
* the terminating null.
*/
qse_size_t qse_canonpath (
const qse_char_t* path,
qse_char_t* canon,
qse_size_t qse_canonmbspath (
const qse_mchar_t* path,
qse_mchar_t* canon,
int flags
);
/**
* The qse_iswcsabspath() function determines if a path name is absolute.
* A path name beginning with a segment separator is absolute.
* On Win32/OS2/DOS, it also returns 1 if a path name begins with a drive
* letter followed by a colon.
* @return 1 if absolute, 0 if not.
*/
int qse_iswcsabspath (
const qse_wchar_t* path
);
/**
* The qse_iswcsdrivepath() function determines if a path name begins with
* a drive letter followed by a colon like A:.
*/
int qse_iswcsdrivepath (
const qse_wchar_t* path
);
/**
* The qse_iswcsdrivecurpath() function determines if a path name is in the form
* of a drive letter followed by a colon like A:, without any trailing path.
*/
int qse_iswcsdrivecurpath (
const qse_wchar_t* path
);
/**
* The qse_canonwcspath() function canonicalizes a path name @a path by deleting
* unnecessary path segments from it and stores the result to a memory buffer
* pointed to by @a canon. Canonicalization is purely performed on the path
* name without refering to actual file systems. It null-terminates the
* canonical path in @a canon and returns the number of characters excluding
* the terminating null.
*
* @code
* qse_wchar_t buf[64];
* qse_canonwcspath (QSE_WT("/usr/local/../bin/sh"), buf);
* qse_printf (QSE_T("%ls\n")); // prints /usr/bin/sh
* @endcode
*
* If #QSE_CANONPATH_EMPTYSINGLEDOT is clear in the @a flags, a single dot
* is produced if the input @path resolves to the current directory logically.
* For example, dir/.. is canonicalized to a single period; If it is set,
* an empty string is produced. Even a single period as an input produces
* an empty string if it is set.
*
* The output is empty returning 0 regardless of @a flags if the input
* @a path is empty.
*
* The caller must ensure that it is large enough to hold the resulting
* canonical path before calling because this function does not check the
* size of the memory buffer. Since the canonical path cannot be larger
* than the original path, you can simply ensure this by providing a memory
* buffer as long as the number of characters and a terminating null in
* the original path.
*
* @return number of characters in the resulting canonical path excluding
* the terminating null.
*/
qse_size_t qse_canonwcspath (
const qse_wchar_t* path,
qse_wchar_t* canon,
int flags
);
#if defined(QSE_CHAR_IS_MCHAR)
# define qse_isabspath(p) qse_ismbsabspath(p)
# define qse_isdrivepath(p) qse_ismbsdrivepath(p)
# define qse_isdrivecurpath(p) qse_ismbsdrivecurpath(p)
# define qse_canonpath(p,c,f) qse_canonmbspath(p,c,f)
#else
# define qse_isabspath(p) qse_iswcsabspath(p)
# define qse_isdrivepath(p) qse_iswcsdrivepath(p)
# define qse_isdrivecurpath(p) qse_iswcsdrivecurpath(p)
# define qse_canonpath(p,c,f) qse_canonwcspath(p,c,f)
#endif
#ifdef __cplusplus
}
#endif

View File

@ -51,11 +51,12 @@ enum qse_htrd_option_t
{
QSE_HTRD_SKIPEMPTYLINES = (1 << 0), /**< skip leading empty lines before the initial line */
QSE_HTRD_SKIPINITIALLINE = (1 << 1), /**< skip processing an initial line */
QSE_HTRD_PEEKONLY = (1 << 2), /**< trigger a peek callback after headers without processing contents */
QSE_HTRD_REQUEST = (1 << 3), /**< parse input as a request */
QSE_HTRD_RESPONSE = (1 << 4), /**< parse input as a response */
QSE_HTRD_TRAILERS = (1 << 5), /**< store trailers in a separate table */
QSE_HTRD_STRICT = (1 << 6) /**< be more picky */
QSE_HTRD_CANONQPATH = (1 << 2), /**< canonicalize the query path */
QSE_HTRD_PEEKONLY = (1 << 3), /**< trigger a peek callback after headers without processing contents */
QSE_HTRD_REQUEST = (1 << 4), /**< parse input as a request */
QSE_HTRD_RESPONSE = (1 << 5), /**< parse input as a response */
QSE_HTRD_TRAILERS = (1 << 6), /**< store trailers in a separate table */
QSE_HTRD_STRICT = (1 << 7) /**< be more picky */
};
typedef enum qse_htrd_option_t qse_htrd_option_t;

View File

@ -187,11 +187,24 @@ int qse_parsehttpdatetime (
);
*/
/* percent-decode a string */
qse_size_t qse_perdechttpstr (
const qse_mchar_t* str,
qse_mchar_t* buf
);
/* percent-encode a string */
qse_size_t qse_perenchttpstr (
const qse_mchar_t* str,
qse_mchar_t* buf
);
qse_mchar_t* qse_perenchttpstrdup (
const qse_mchar_t* str,
qse_mmgr_t* mmgr
);
#ifdef __cplusplus
}
#endif

View File

@ -104,14 +104,8 @@ typedef int (*qse_httpd_muxcb_t) (
void* cbarg
);
typedef struct qse_httpd_dirent_t qse_httpd_dirent_t;
struct qse_httpd_dirent_t
{
};
typedef struct qse_httpd_cbs_t qse_httpd_cbs_t;
struct qse_httpd_cbs_t
typedef struct qse_httpd_scb_t qse_httpd_scb_t;
struct qse_httpd_scb_t
{
struct
{
@ -212,7 +206,11 @@ struct qse_httpd_cbs_t
qse_httpd_t* httpd,
qse_httpd_client_t* client); /* optional */
} client;
};
typedef struct qse_httpd_rcb_t qse_httpd_rcb_t;
struct qse_httpd_rcb_t
{
int (*peek_request) (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req);
int (*handle_request) (
@ -335,6 +333,7 @@ struct qse_httpd_ecb_t
qse_httpd_ecb_t* next;
};
#ifdef __cplusplus
extern "C" {
#endif
@ -396,7 +395,8 @@ void qse_httpd_pushecb (
*/
int qse_httpd_loop (
qse_httpd_t* httpd,
qse_httpd_cbs_t* cbs,
qse_httpd_scb_t* scb,
qse_httpd_rcb_t* rcb,
qse_ntime_t timeout
);
@ -506,7 +506,7 @@ qse_httpd_task_t* qse_httpd_entaskfile (
qse_htre_t* req
);
qse_httpd_task_t* qse_httpd_entaskdir (
qse_httpd_task_t* qse_httpd_entaskpath (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
qse_httpd_task_t* pred,
@ -574,6 +574,7 @@ void* qse_httpd_getxtnstd (
int qse_httpd_loopstd (
qse_httpd_t* httpd,
qse_httpd_rcb_t* rcb,
qse_ntime_t timeout
);

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

@ -390,6 +390,9 @@ static qse_mchar_t* parse_initial_line (
}
#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
/* initialize buffer */
ctx->dcount = 0; /* reset the entry counter */
ctx->bufpos = 0;
if (ctx->chunked)
{
/* 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)
}
else
{
x = snprintf (
&ctx->buf[ctx->buflen],
ctx->bufrem,
QSE_MT("</ul></body></html>\r\n0\r\n"));
if (x == -1 || x >= ctx->bufrem)
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
/* failed to add the footer part */
if (ctx->chunked)
{
ctx->buflen += x;
ctx->chunklen = ctx->buflen - 5;
/* CHENDCRLF */
ctx->buf[ctx->buflen++] = QSE_MT('\r');
ctx->buf[ctx->buflen++] = QSE_MT('\n');
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;

View File

@ -5,7 +5,7 @@ AM_CPPFLAGS = \
-I$(top_srcdir)/include \
-I$(includedir)
bin_PROGRAMS = http01 upxd01
bin_PROGRAMS = httpd01 httpd02 upxd01
LDFLAGS += -L../../lib/cmn -L../../lib/net
LDADD = -lqsenet -lqsecmn $(PTHREAD_LIBS) $(SOCKET_LIBS) $(SENDFILE_LIBS)
@ -16,7 +16,9 @@ LDADD += $(UNICOWS_LIBS)
endif
endif
http01_SOURCES = http01.c
httpd01_SOURCES = httpd01.c
httpd02_SOURCES = httpd02.c
upxd01_SOURCES = upxd01.c
http01_LDADD = $(LDADD) $(SSL_LIBS)
httpd01_LDADD = $(LDADD) $(SSL_LIBS)
httpd02_LDADD = $(LDADD) $(SSL_LIBS)

View File

@ -34,7 +34,7 @@ PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
bin_PROGRAMS = http01$(EXEEXT) upxd01$(EXEEXT)
bin_PROGRAMS = httpd01$(EXEEXT) httpd02$(EXEEXT) upxd01$(EXEEXT)
@WCHAR_TRUE@@WIN32_TRUE@am__append_1 = $(UNICOWS_LIBS)
subdir = samples/net
DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in
@ -52,13 +52,16 @@ CONFIG_CLEAN_FILES =
CONFIG_CLEAN_VPATH_FILES =
am__installdirs = "$(DESTDIR)$(bindir)"
PROGRAMS = $(bin_PROGRAMS)
am_http01_OBJECTS = http01.$(OBJEXT)
http01_OBJECTS = $(am_http01_OBJECTS)
am_httpd01_OBJECTS = httpd01.$(OBJEXT)
httpd01_OBJECTS = $(am_httpd01_OBJECTS)
am__DEPENDENCIES_1 =
@WCHAR_TRUE@@WIN32_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1)
am__DEPENDENCIES_3 = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2)
http01_DEPENDENCIES = $(am__DEPENDENCIES_3) $(am__DEPENDENCIES_1)
httpd01_DEPENDENCIES = $(am__DEPENDENCIES_3) $(am__DEPENDENCIES_1)
am_httpd02_OBJECTS = httpd02.$(OBJEXT)
httpd02_OBJECTS = $(am_httpd02_OBJECTS)
httpd02_DEPENDENCIES = $(am__DEPENDENCIES_3) $(am__DEPENDENCIES_1)
am_upxd01_OBJECTS = upxd01.$(OBJEXT)
upxd01_OBJECTS = $(am_upxd01_OBJECTS)
upxd01_LDADD = $(LDADD)
@ -77,8 +80,8 @@ CCLD = $(CC)
LINK = $(LIBTOOL) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) \
--mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) \
$(LDFLAGS) -o $@
SOURCES = $(http01_SOURCES) $(upxd01_SOURCES)
DIST_SOURCES = $(http01_SOURCES) $(upxd01_SOURCES)
SOURCES = $(httpd01_SOURCES) $(httpd02_SOURCES) $(upxd01_SOURCES)
DIST_SOURCES = $(httpd01_SOURCES) $(httpd02_SOURCES) $(upxd01_SOURCES)
ETAGS = etags
CTAGS = ctags
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
@ -243,9 +246,11 @@ AM_CPPFLAGS = \
LDADD = -lqsenet -lqsecmn $(PTHREAD_LIBS) $(SOCKET_LIBS) \
$(SENDFILE_LIBS) $(am__append_1)
http01_SOURCES = http01.c
httpd01_SOURCES = httpd01.c
httpd02_SOURCES = httpd02.c
upxd01_SOURCES = upxd01.c
http01_LDADD = $(LDADD) $(SSL_LIBS)
httpd01_LDADD = $(LDADD) $(SSL_LIBS)
httpd02_LDADD = $(LDADD) $(SSL_LIBS)
all: all-am
.SUFFIXES:
@ -323,9 +328,12 @@ clean-binPROGRAMS:
list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
echo " rm -f" $$list; \
rm -f $$list
http01$(EXEEXT): $(http01_OBJECTS) $(http01_DEPENDENCIES) $(EXTRA_http01_DEPENDENCIES)
@rm -f http01$(EXEEXT)
$(LINK) $(http01_OBJECTS) $(http01_LDADD) $(LIBS)
httpd01$(EXEEXT): $(httpd01_OBJECTS) $(httpd01_DEPENDENCIES) $(EXTRA_httpd01_DEPENDENCIES)
@rm -f httpd01$(EXEEXT)
$(LINK) $(httpd01_OBJECTS) $(httpd01_LDADD) $(LIBS)
httpd02$(EXEEXT): $(httpd02_OBJECTS) $(httpd02_DEPENDENCIES) $(EXTRA_httpd02_DEPENDENCIES)
@rm -f httpd02$(EXEEXT)
$(LINK) $(httpd02_OBJECTS) $(httpd02_LDADD) $(LIBS)
upxd01$(EXEEXT): $(upxd01_OBJECTS) $(upxd01_DEPENDENCIES) $(EXTRA_upxd01_DEPENDENCIES)
@rm -f upxd01$(EXEEXT)
$(LINK) $(upxd01_OBJECTS) $(upxd01_LDADD) $(LIBS)
@ -336,7 +344,8 @@ mostlyclean-compile:
distclean-compile:
-rm -f *.tab.c
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/http01.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/httpd01.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/httpd02.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/upxd01.Po@am__quote@
.c.o:

View File

@ -33,7 +33,7 @@ static void sigint (int sig)
if (g_httpd) qse_httpd_stop (g_httpd);
}
int httpd_main (int argc, qse_char_t* argv[])
static int httpd_main (int argc, qse_char_t* argv[])
{
qse_httpd_t* httpd = QSE_NULL;
int ret = -1, i;
@ -66,7 +66,7 @@ int httpd_main (int argc, qse_char_t* argv[])
signal (SIGPIPE, SIG_IGN);
qse_httpd_setoption (httpd, QSE_HTTPD_CGIERRTONUL);
ret = qse_httpd_loopstd (httpd, 10000);
ret = qse_httpd_loopstd (httpd, QSE_NULL, 10000);
signal (SIGINT, SIG_DFL);
signal (SIGPIPE, SIG_DFL);

249
qse/samples/net/httpd02.c Normal file
View File

@ -0,0 +1,249 @@
#include <qse/net/httpd.h>
#include <qse/cmn/stdio.h>
#include <qse/cmn/main.h>
#include <qse/cmn/str.h>
#include <qse/cmn/mem.h>
#include <qse/cmn/mbwc.h>
#include <qse/cmn/time.h>
#include <signal.h>
#include <locale.h>
#if defined(_WIN32)
# include <windows.h>
# include <tchar.h>
# include <process.h>
#elif defined(__OS2__)
# define INCL_DOSPROCESS
# define INCL_DOSEXCEPTIONS
# define INCL_ERRORS
# include <os2.h>
#elif defined(__DOS__)
# include <dos.h>
#else
# include <unistd.h>
# include <errno.h>
#endif
/* --------------------------------------------------------------------- */
typedef struct xtn_t xtn_t;
struct xtn_t
{
qse_mchar_t basedir[4096];
};
static int process_request (
qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_htre_t* req, int peek)
{
int method;
qse_httpd_task_t* task;
int content_received;
xtn_t* xtn;
method = qse_htre_getqmethodtype(req);
content_received = (qse_htre_getcontentlen(req) > 0);
xtn = (xtn_t*) qse_httpd_getxtn (httpd);
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"),
(unsigned long)time(NULL),
(peek? QSE_MT("PEEK"): QSE_MT("HANDLE")),
qse_htre_getqpath(req),
qse_htre_getmajorversion(req),
qse_htre_getminorversion(req),
qse_htre_getverstr(req),
qse_htre_getqmethodname(req)
);
if (qse_htre_getqparam(req))
qse_printf (QSE_T("PARAMS ==> [%hs]\n"), qse_htre_getqparam(req));
if (peek)
{
if (method != QSE_HTTP_POST && method != QSE_HTTP_PUT)
{
/* i'll discard request contents if the method is none of
* post and put */
qse_httpd_discardcontent (httpd, req);
}
if ((req->attr.flags & QSE_HTRE_ATTR_EXPECT100) &&
(req->version.major > 1 ||
(req->version.major == 1 && req->version.minor >= 1)) &&
!content_received)
{
/* TODO: check method.... */
/* "expect" in the header, version 1.1 or higher,
* and no content received yet */
/* TODO: determine if to return 100-continue or other errors */
if (qse_httpd_entaskcontinue (
httpd, client, QSE_NULL, req) == QSE_NULL) return -1;
}
}
if (method == QSE_HTTP_GET || method == QSE_HTTP_POST)
{
const qse_mchar_t* qpath = qse_htre_getqpath(req);
const qse_mchar_t* dot = qse_mbsrchr (qpath, QSE_MT('.'));
if (dot && qse_mbscmp (dot, QSE_MT(".cgi")) == 0)
{
if (peek)
{
/* cgi */
if (method == QSE_HTTP_POST &&
!(req->attr.flags & QSE_HTRE_ATTR_LENGTH) &&
!(req->attr.flags & QSE_HTRE_ATTR_CHUNKED))
{
req->attr.flags &= ~QSE_HTRE_ATTR_KEEPALIVE;
task = qse_httpd_entaskerror (
httpd, client, QSE_NULL, 411, req);
/* 411 can't keep alive */
if (task) qse_httpd_entaskdisconnect (httpd, client, QSE_NULL);
}
else
{
task = qse_httpd_entaskcgi (
httpd, client, QSE_NULL, qpath, req);
if (task == QSE_NULL) goto oops;
}
}
return 0;
}
else
{
if (peek)
{
/* TODO: combine qpath with xtn->basedir */
qse_httpd_discardcontent (httpd, req);
task = qse_httpd_entaskpath (httpd, client, QSE_NULL, qpath, req);
if (task == QSE_NULL) goto oops;
}
}
}
else
{
if (!peek)
{
task = qse_httpd_entaskerror (httpd, client, QSE_NULL, 405, req);
if (task == QSE_NULL) goto oops;
}
}
if (!(req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE))
{
if (!peek)
{
task = qse_httpd_entaskdisconnect (httpd, client, QSE_NULL);
if (task == QSE_NULL) goto oops;
}
}
return 0;
oops:
/*qse_httpd_markbadclient (httpd, client);*/
return -1;
}
static int peek_request (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req)
{
return process_request (httpd, client, req, 1);
}
static int handle_request (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req)
{
return process_request (httpd, client, req, 0);
}
/* --------------------------------------------------------------------- */
static qse_httpd_t* g_httpd = QSE_NULL;
static void sigint (int sig)
{
if (g_httpd) qse_httpd_stop (g_httpd);
}
/* --------------------------------------------------------------------- */
static int httpd_main (int argc, qse_char_t* argv[])
{
qse_httpd_t* httpd = QSE_NULL;
int ret = -1, i;
static qse_httpd_rcb_t rcb = { peek_request, handle_request };
if (argc <= 1)
{
qse_fprintf (QSE_STDERR, QSE_T("Usage: %s <listener_uri> ...\n"), argv[0]);
goto oops;
}
httpd = qse_httpd_openstd (QSE_SIZEOF(xtn_t));
if (httpd == QSE_NULL)
{
qse_fprintf (QSE_STDERR, QSE_T("Cannot open httpd\n"));
goto oops;
}
for (i = 1; i < argc; i++)
{
if (qse_httpd_addserver (httpd, argv[i]) <= -1)
{
qse_fprintf (QSE_STDERR,
QSE_T("Failed to add httpd listener - %s\n"), argv[i]);
goto oops;
}
}
g_httpd = httpd;
signal (SIGINT, sigint);
signal (SIGPIPE, SIG_IGN);
qse_httpd_setoption (httpd, QSE_HTTPD_CGIERRTONUL);
ret = qse_httpd_loopstd (httpd, &rcb, 10000);
signal (SIGINT, SIG_DFL);
signal (SIGPIPE, SIG_DFL);
g_httpd = QSE_NULL;
if (ret <= -1) qse_fprintf (QSE_STDERR, QSE_T("Httpd error\n"));
oops:
if (httpd) qse_httpd_close (httpd);
return ret;
}
int qse_main (int argc, qse_achar_t* argv[])
{
#if defined(_WIN32)
char locale[100];
UINT codepage = GetConsoleOutputCP();
if (codepage == CP_UTF8)
{
/*SetConsoleOUtputCP (CP_UTF8);*/
qse_setdflcmgr (qse_utf8cmgr);
}
else
{
sprintf (locale, ".%u", (unsigned int)codepage);
setlocale (LC_ALL, locale);
qse_setdflcmgrbyid (QSE_CMGR_SLMB);
}
#else
setlocale (LC_ALL, "");
qse_setdflcmgrbyid (QSE_CMGR_SLMB);
#endif
return qse_runmain (argc, argv, httpd_main);
}