enhanced httpd

This commit is contained in:
hyung-hwan 2012-02-08 12:59:59 +00:00
parent 17d05bb1e0
commit 517107ab79
15 changed files with 1473 additions and 706 deletions

View File

@ -106,46 +106,41 @@ qse_env_char_t** qse_env_getarr (
qse_env_t* env
);
int qse_env_insertw (
/**
* The qse_env_insertwcs() function adds a new environment variable
* @a name with the @a value. If the @a value is #QSE_NULL, it takes
* the actual value from the system environment
*
* @return 0 on success, -1 on failure
*/
int qse_env_insertwcs (
qse_env_t* env,
const qse_wchar_t* name,
const qse_wchar_t* value
);
int qse_env_insertm (
int qse_env_insertmbs (
qse_env_t* env,
const qse_mchar_t* name,
const qse_mchar_t* value
);
int qse_env_deletew (
int qse_env_deletewcs (
qse_env_t* env,
const qse_wchar_t* name
);
int qse_env_deletem (
qse_env_t* env,
const qse_mchar_t* name
);
int qse_env_insertsysw (
qse_env_t* env,
const qse_wchar_t* name
);
int qse_env_insertsysm (
int qse_env_deletembs (
qse_env_t* env,
const qse_mchar_t* name
);
#if defined(QSE_CHAR_IS_WCHAR)
# define qse_env_insert(env,name,value) qse_env_insertw(env,name,value)
# define qse_env_delete(env,name) qse_env_deletew(env,name)
# define qse_env_insertsys(env,name) qse_env_insertsysw(env,name)
# define qse_env_insert(env,name,value) qse_env_insertwcs(env,name,value)
# define qse_env_delete(env,name) qse_env_deletewcs(env,name)
#else
# define qse_env_insert(env,name,value) qse_env_insertm(env,name,value)
# define qse_env_delete(env,name) qse_env_deletem(env,name)
# define qse_env_insertsys(env,name) qse_env_insertsysm(env,name)
# define qse_env_insert(env,name,value) qse_env_insertmbs(env,name,value)
# define qse_env_delete(env,name) qse_env_deletembs(env,name)
#endif
#ifdef __cplusplus

View File

@ -32,7 +32,8 @@ enum qse_htrd_errnum_t
QSE_HTRD_ENOMEM,
QSE_HTRD_EBADRE,
QSE_HTRD_EBADHDR,
QSE_HTRD_ERECBS
QSE_HTRD_ERECBS,
QSE_HTRD_ECONCB
};
typedef enum qse_htrd_errnum_t qse_htrd_errnum_t;
@ -45,7 +46,7 @@ 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_HURRIED = (1 << 2), /**< trigger a callback also after headers without processing contents */
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 */
};
@ -56,9 +57,8 @@ typedef struct qse_htrd_recbs_t qse_htrd_recbs_t;
struct qse_htrd_recbs_t
{
int (*request) (qse_htrd_t* htrd, qse_htre_t* req);
int (*expect_continue) (qse_htrd_t* htrd, qse_htre_t* req);
int (*response) (qse_htrd_t* htrd, qse_htre_t* res);
int (*peek) (qse_htrd_t* htrd, qse_htre_t* re);
int (*handle) (qse_htrd_t* htrd, qse_htre_t* re);
};
struct qse_htrd_t

View File

@ -27,6 +27,20 @@
/* header and contents of request/response */
typedef struct qse_htre_t qse_htre_t;
enum qse_htre_flag_t
{
QSE_HTRE_DISCARDED = (1 << 0), /** content has been discarded */
QSE_HTRE_COMPLETED = (1 << 1) /** complete content has been seen */
};
typedef int (*qse_htre_concb_t) (
qse_htre_t* re,
const qse_mchar_t* ptr,
qse_size_t len,
void* ctx
);
struct qse_htre_t
{
qse_mmgr_t* mmgr;
@ -45,10 +59,7 @@ struct qse_htre_t
int content_length_set;
qse_size_t content_length;
int keepalive;
int expect_continue;
/* indicates if the content has been filled */
int hurried;
const qse_mchar_t* expect;
} attr;
/* header table */
@ -57,8 +68,12 @@ struct qse_htre_t
/* content octets */
qse_mbs_t content;
/* if set, the rest of the contents are discarded */
int discard;
/* content callback */
qse_htre_concb_t concb;
void* concb_ctx;
/* ORed of qse_htre_flag_t */
int flags;
};
#define qse_htre_getversion(re) (&((re)->version))
@ -111,13 +126,12 @@ struct qse_htre_t
#define qse_htre_setsmessagefromxstr(re,v) \
qse_htre_setstrfromxstr((re),qse_htre_getsmessage(re),(v))
/* NOTE: setcontent() doesn't execute concb. use this with care */
#define qse_htre_setcontentfromcstr(re,v) \
qse_htre_setstrfromcstr((re),qse_htre_getcontent(re),(v))
#define qse_htre_setcontentfromxstr(re,v) \
qse_htre_setstrfromxstr((re),qse_htre_getcontent(re),(v))
#define qse_htre_setdiscard(re,v) QSE_BLOCK((re)->discard = (v);)
typedef int (*qse_htre_header_walker_t) (
qse_htre_t* re,
const qse_mchar_t* key,
@ -165,6 +179,26 @@ int qse_htre_walkheaders (
void* ctx
);
int qse_htre_addcontent (
qse_htre_t* re,
const qse_mchar_t* ptr,
qse_size_t len
);
void qse_htre_unsetconcb (
qse_htre_t* re
);
void qse_htre_setconcb (
qse_htre_t* re,
qse_htre_concb_t concb,
void* ctx
);
const qse_mchar_t* qse_htre_getqmethodname (
qse_htre_t* re
);
#ifdef __cplusplus
}
#endif

View File

@ -51,9 +51,33 @@ enum qse_httpd_option_t
typedef struct qse_httpd_cbs_t qse_httpd_cbs_t;
struct qse_httpd_cbs_t
{
int (*handle_request) (
struct
{
/* action */
int (*recv) (qse_httpd_t* httpd,
qse_httpd_client_t* client,
qse_mchar_t* buf, qse_size_t bufsize);
int (*send) (qse_httpd_t* httpd,
qse_httpd_client_t* client,
const qse_mchar_t* buf, qse_size_t bufsize);
int (*sendfile) (qse_httpd_t* httpd,
qse_httpd_client_t* client,
qse_ubi_t handle, qse_foff_t* offset, qse_size_t count);
/* event notification */
int (*accepted) (
qse_httpd_t* httpd,
qse_httpd_client_t* client); /* optional */
void (*closed) (
qse_httpd_t* httpd,
qse_httpd_client_t* client); /* optional */
} client;
int (*peek_request) (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req);
int (*handle_expect_continue) (
int (*handle_request) (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req);
const qse_mchar_t* (*getmimetype) (qse_httpd_t* httpd, const qse_mchar_t* path);
@ -80,6 +104,15 @@ typedef int (*qse_httpd_task_main_t) (
qse_httpd_task_t* task
);
enum qse_httpd_task_trigger_mask_t
{
QSE_HTTPD_TASK_TRIGGER_READ = (1 << 0),
QSE_HTTPD_TASK_TRIGGER_WRITE = (1 << 1),
QSE_HTTPD_TASK_TRIGGER_READABLE = (1 << 2),
QSE_HTTPD_TASK_TRIGGER_WRITABLE = (1 << 3)
};
struct qse_httpd_task_t
{
/* you must not call another entask functions from within
@ -89,7 +122,8 @@ struct qse_httpd_task_t
qse_httpd_task_fini_t fini;
qse_httpd_task_main_t main;
qse_ubi_t trigger;
int trigger_mask;
qse_ubi_t trigger[2];
void* ctx;
};
@ -144,6 +178,7 @@ void qse_httpd_setcbs (
*/
int qse_httpd_loop (
qse_httpd_t* httpd,
qse_httpd_cbs_t* cbs,
int threaded
);
@ -161,11 +196,16 @@ int qse_httpd_addlistener (
);
void qse_httpd_markclientbad (
void qse_httpd_markbadclient (
qse_httpd_t* httpd,
qse_httpd_client_t* client
);
void qse_httpd_discardcontent (
qse_httpd_t* httpd,
qse_htre_t* req
);
#define qse_httpd_gettaskxtn(httpd,task) ((void*)(task+1))
qse_httpd_task_t* qse_httpd_entask (
@ -235,6 +275,13 @@ qse_httpd_task_t* qse_httpd_entaskerror (
const qse_htre_t* req
);
qse_httpd_task_t* qse_httpd_entaskcontinue (
qse_httpd_t* httpd,
qse_httpd_client_t* client,
const qse_httpd_task_t* task,
const qse_htre_t* req
);
qse_httpd_task_t* qse_httpd_entaskpath (
qse_httpd_t* httpd,
qse_httpd_client_t* client,

View File

@ -34,6 +34,8 @@
QSE_IMPLEMENT_COMMON_FUNCTIONS(env)
static int load_curenv (qse_env_t* env);
static int insert_sys_wcs (qse_env_t* env, const qse_wchar_t* name);
static int insert_sys_mbs (qse_env_t* env, const qse_mchar_t* name);
qse_env_t* qse_env_open (qse_mmgr_t* mmgr, qse_size_t xtnsize, int fromcurenv)
{
@ -314,10 +316,9 @@ static int deletem (qse_env_t* env, const qse_mchar_t* name)
return -1;
}
#endif
int qse_env_insertw (
static QSE_INLINE int insert_wcs (
qse_env_t* env, const qse_wchar_t* name, const qse_wchar_t* value)
{
#if defined(QSE_ENV_CHAR_IS_WCHAR)
@ -328,9 +329,9 @@ int qse_env_insertw (
qse_mchar_t* namedup, * valuedup;
int n;
namedup = qse_wcstombsdup (name, env->mmgr);
namedup = qse_wcstombsdup (name, env->mmgr); /* TODO: ignore mbwcerr */
if (namedup == QSE_NULL) return -1;
valuedup = qse_wcstombsdup (value, env->mmgr);
valuedup = qse_wcstombsdup (value, env->mmgr); /* TODO: ignore mbwcerr */
if (valuedup == QSE_NULL)
{
QSE_MMGR_FREE (env->mmgr, namedup);
@ -344,7 +345,7 @@ int qse_env_insertw (
#endif
}
int qse_env_insertm (
static QSE_INLINE int insert_mbs (
qse_env_t* env, const qse_mchar_t* name, const qse_mchar_t* value)
{
#if defined(QSE_ENV_CHAR_IS_WCHAR)
@ -352,9 +353,9 @@ int qse_env_insertm (
qse_wchar_t* namedup, * valuedup;
int n;
namedup = qse_mbstowcsdup (name, env->mmgr); /* TODO: ignroe mbwcerr */
namedup = qse_mbstowcsalldup (name, env->mmgr);
if (namedup == QSE_NULL) return -1;
valuedup = qse_mbstowcsdup (value, env->mmgr); /* TODO: ignroe mbwcerr */
valuedup = qse_mbstowcsalldup (value, env->mmgr);
if (valuedup == QSE_NULL)
{
QSE_MMGR_FREE (env->mmgr, namedup);
@ -372,44 +373,6 @@ int qse_env_insertm (
}
int qse_env_deletew (qse_env_t* env, const qse_wchar_t* name)
{
#if defined(QSE_ENV_CHAR_IS_WCHAR)
return deletew (env, name);
#else
/* convert wchar to mchar */
qse_mchar_t* namedup;
int n;
namedup = qse_wcstombsdup (name, env->mmgr);
if (namedup == QSE_NULL) return -1;
n = deletem (env, namedup);
QSE_MMGR_FREE (env->mmgr, namedup);
return n;
#endif
}
int qse_env_deletem (qse_env_t* env, const qse_mchar_t* name)
{
#if defined(QSE_ENV_CHAR_IS_WCHAR)
/* convert mchar to wchar */
qse_wchar_t* namedup;
int n;
namedup = qse_mbstowcsdup (name, env->mmgr); /* TODO: ignroe mbwcerr */
if (namedup == QSE_NULL) return -1;
n = deletew (env, namedup);
QSE_MMGR_FREE (env->mmgr, namedup);
return n;
#else
return deletem (env, name);
#endif
}
#if defined(_WIN32)
static qse_char_t* get_env (qse_env_t* env, const qse_char_t* name, int* free)
{
@ -507,7 +470,7 @@ static qse_mchar_t* get_env (qse_env_t* env, const qse_mchar_t* name, int* free)
}
#endif
int qse_env_insertsysw (qse_env_t* env, const qse_wchar_t* name)
static int insert_sys_wcs (qse_env_t* env, const qse_wchar_t* name)
{
#if defined(QSE_ENV_CHAR_IS_WCHAR)
qse_wchar_t* v;
@ -526,10 +489,10 @@ int qse_env_insertsysw (qse_env_t* env, const qse_wchar_t* name)
qse_mchar_t* namedup;
int ret = -1;
namedup = qse_wcstombsdup (name, env->mmgr);
namedup = qse_wcstombsdup (name, env->mmgr); /* TODO: ignore mbwcerr */
if (namedup)
{
ret = qse_env_insertsysm (env, namedup);
ret = insert_sys_mbs (env, namedup);
QSE_MMGR_FREE (env->mmgr, namedup);
}
@ -537,7 +500,7 @@ int qse_env_insertsysw (qse_env_t* env, const qse_wchar_t* name)
#endif
}
int qse_env_insertsysm (qse_env_t* env, const qse_mchar_t* name)
static insert_sys_mbs (qse_env_t* env, const qse_mchar_t* name)
{
#if defined(QSE_ENV_CHAR_IS_WCHAR)
/* convert mchar to wchar */
@ -547,7 +510,7 @@ int qse_env_insertsysm (qse_env_t* env, const qse_mchar_t* name)
namedup = qse_mbstowcsdup (name, env->mmgr); /* TODO: ignroe mbwcerr */
if (namedup)
{
ret = qse_env_insertsysw (env, namedup);
ret = insert_sys_wcs (env, namedup);
QSE_MMGR_FREE (env->mmgr, namedup);
}
@ -653,3 +616,56 @@ done:
#endif
}
/* ------------------------------------------------------------------- */
int qse_env_insertwcs (
qse_env_t* env, const qse_wchar_t* name, const qse_wchar_t* value)
{
return value? insert_wcs (env, name, value): insert_sys_wcs (env, name);
}
int qse_env_insertmbs (
qse_env_t* env, const qse_mchar_t* name, const qse_mchar_t* value)
{
return value? insert_mbs (env, name, value): insert_sys_mbs (env, name);
}
int qse_env_deletewcs (qse_env_t* env, const qse_wchar_t* name)
{
#if defined(QSE_ENV_CHAR_IS_WCHAR)
return deletew (env, name);
#else
/* convert wchar to mchar */
qse_mchar_t* namedup;
int n;
namedup = qse_wcstombsdup (name, env->mmgr); /* TODO: ignore mbwcerr */
if (namedup == QSE_NULL) return -1;
n = deletem (env, namedup);
QSE_MMGR_FREE (env->mmgr, namedup);
return n;
#endif
}
int qse_env_deletembs (qse_env_t* env, const qse_mchar_t* name)
{
#if defined(QSE_ENV_CHAR_IS_WCHAR)
/* convert mchar to wchar */
qse_wchar_t* namedup;
int n;
namedup = qse_mbstowcsalldup (name, env->mmgr);
if (namedup == QSE_NULL) return -1;
n = deletew (env, namedup);
QSE_MMGR_FREE (env->mmgr, namedup);
return n;
#else
return deletem (env, name);
#endif
}

View File

@ -93,6 +93,17 @@ static QSE_INLINE int push_to_buffer (
return 0;
}
static QSE_INLINE int push_content (
qse_htrd_t* htrd, const qse_mchar_t* ptr, qse_size_t len)
{
if (qse_htre_addcontent (&htrd->re, ptr, len) <= -1)
{
htrd->errnum = QSE_HTRD_ENOMEM;
return -1;
}
return 0;
}
struct hdr_cmb_t
{
struct hdr_cmb_t* next;
@ -515,18 +526,7 @@ static int capture_content_length (qse_htrd_t* htrd, qse_htb_pair_t* pair)
static int capture_expect (qse_htrd_t* htrd, qse_htb_pair_t* pair)
{
int n;
n = qse_mbsxncasecmp (
QSE_HTB_VPTR(pair), QSE_HTB_VLEN(pair), "100-continue", 12);
if (n == 0)
{
htrd->re.attr.expect_continue = 1;
return 0;
}
/* don't care about other values */
htrd->re.attr.expect = QSE_HTB_VPTR(pair);
return 0;
}
@ -998,6 +998,8 @@ int qse_htrd_feed (qse_htrd_t* htrd, const qse_mchar_t* req, qse_size_t len)
{
const qse_mchar_t* end = req + len;
const qse_mchar_t* ptr = req;
int header_completed_during_this_feed = 0;
qse_size_t avail;
/* does this goto drop code maintainability? */
if (htrd->fed.s.need > 0)
@ -1067,7 +1069,7 @@ int qse_htrd_feed (qse_htrd_t* htrd, const qse_mchar_t* req, qse_size_t len)
* => 2nd CR before LF
*/
/* we got a complete request. */
/* we got a complete request header. */
QSE_ASSERT (htrd->fed.s.crlf <= 3);
/* reset the crlf state */
@ -1078,86 +1080,41 @@ int qse_htrd_feed (qse_htrd_t* htrd, const qse_mchar_t* req, qse_size_t len)
if (parse_initial_line_and_headers (htrd, req, ptr - req) <= -1)
return -1;
if (htrd->option & QSE_HTRD_HURRIED)
/* compelete request header is received */
header_completed_during_this_feed = 1;
if (htrd->option & QSE_HTRD_PEEKONLY)
{
int n;
/* it pushes any trailing data into the content in this mode.
* so the handler knows if there is contents fed to this reader. */
if (push_to_buffer (htrd, &htrd->re.content, ptr, end - ptr) <= -1)
return -1;
htrd->re.attr.hurried = 1;
htrd->errnum = QSE_HTRD_ENOERR;
if (htrd->retype == QSE_HTRD_RETYPE_S)
{
QSE_ASSERTX (
htrd->recbs->response != QSE_NULL,
"set response callback before feeding"
);
n = htrd->recbs->response (htrd, &htrd->re);
}
else
{
QSE_ASSERTX (
htrd->recbs->request != QSE_NULL,
"set request callback before feeding"
);
n = htrd->recbs->request (htrd, &htrd->re);
}
/* qse_mbs_clear (&htrd->re.content); */
if (n <= -1)
{
if (htrd->errnum == QSE_HTRD_ENOERR)
htrd->errnum = QSE_HTRD_ERECBS;
/* need to clear request on error?
clear_feed (htrd); */
return -1;
}
/* if QSE_HTRD_HURRIED is set, we do not handle expect_continue */
/* if QSE_HTRD_HURRIED is set, we handle a single request only */
return 0;
}
if (htrd->retype == QSE_HTRD_RETYPE_Q &&
htrd->re.attr.expect_continue &&
htrd->recbs->expect_continue && ptr >= end)
{
int n;
/* the 'ptr >= end' check is for not executing the
* callback if the message body has been seen already.
* however, this is not perfect as it is based on
* the current feed. what if it has been received but
* not fed here?
/* when QSE_HTRD_PEEKONCE is set,
* the peek callback is invoked once
* a complete header is seen. the caller
* should not feed more data by calling
* this function again once the callback is
* invoked. the trailing data is appended
* to the content buffer.
*
* NOTE: if the current feed that completed
* the header contains the next request,
* the next request is treated as if it
* belongs to the current request.
*
* In priciple, this option was added for
* reading CGI outputs. So it comes with
* awkwardity described above.
*/
if (!(htrd->re.flags & QSE_HTRE_DISCARDED) &&
push_content (htrd, ptr, end - ptr) <= -1) return -1;
/* this jump is only to invoke the peek
* callback. this function should not be fed
* more. */
htrd->re.attr.hurried = 0;
htrd->errnum = QSE_HTRD_ENOERR;
n = htrd->recbs->expect_continue (htrd, &htrd->re);
if (n <= -1)
{
if (htrd->errnum == QSE_HTRD_ENOERR)
htrd->errnum = QSE_HTRD_ERECBS;
/* need to clear request on error?
clear_feed (htrd); */
return -1;
}
/* the expect_continue handler can set discard to non-zero
* to force discard the contents body
htrd->re.discard = 1;
*/
/* i don't really know if it is really completed
* with content. QSE_HTRD_PEEKONLY is not compatible
* with the completed flag. */
htrd->re.flags &= QSE_HTRE_COMPLETED;
goto feedme_more;
}
/* carry on processing content body fed together with the header */
if (htrd->re.attr.chunked)
{
/* transfer-encoding: chunked */
@ -1203,16 +1160,14 @@ int qse_htrd_feed (qse_htrd_t* htrd, const qse_mchar_t* req, qse_size_t len)
{
/* content-length or chunked data length
* specified */
qse_size_t avail;
content_resume:
avail = end - ptr;
if (avail < htrd->fed.s.need)
{
/* the data is not as large as needed */
if (push_to_buffer (htrd, &htrd->re.content, ptr, avail) <= -1) return -1;
if (!(htrd->re.flags & QSE_HTRE_DISCARDED) &&
push_content (htrd, ptr, avail) <= -1) return -1;
htrd->fed.s.need -= avail;
/* we didn't get a complete content yet */
goto feedme_more;
@ -1220,9 +1175,8 @@ int qse_htrd_feed (qse_htrd_t* htrd, const qse_mchar_t* req, qse_size_t len)
else
{
/* we got all or more than needed */
if (push_to_buffer (
htrd, &htrd->re.content, ptr,
htrd->fed.s.need) <= -1) return -1;
if (!(htrd->re.flags & QSE_HTRE_DISCARDED) &&
push_content (htrd, ptr, htrd->fed.s.need) <= -1) return -1;
ptr += htrd->fed.s.need;
htrd->fed.s.need = 0;
}
@ -1272,43 +1226,51 @@ int qse_htrd_feed (qse_htrd_t* htrd, const qse_mchar_t* req, qse_size_t len)
}
}
if (!htrd->re.discard)
if (header_completed_during_this_feed && htrd->recbs->peek)
{
/* the peek handler has not been executed.
* this can happen if this function is fed with
* at least the ending part of a complete header
* plus complete content body and the header
* of the next request. */
int n;
htrd->re.attr.hurried = 0;
htrd->re.flags |= QSE_HTRE_COMPLETED;
htrd->errnum = QSE_HTRD_ENOERR;
if (htrd->retype == QSE_HTRD_RETYPE_S)
{
QSE_ASSERTX (
htrd->recbs->response != QSE_NULL,
"set response callback before feeding"
);
n = htrd->recbs->response (htrd, &htrd->re);
}
else
{
QSE_ASSERTX (
htrd->recbs->request != QSE_NULL,
"set request callback before feeding"
);
n = htrd->recbs->request (htrd, &htrd->re);
}
n = htrd->recbs->peek (htrd, &htrd->re);
if (n <= -1)
{
if (htrd->errnum == QSE_HTRD_ENOERR)
htrd->errnum = QSE_HTRD_ERECBS;
/* need to clear request on error?
clear_feed (htrd); */
return -1;
}
header_completed_during_this_feed = 0;
}
if (htrd->recbs->handle)
{
int n;
htrd->re.flags |= QSE_HTRE_COMPLETED;
htrd->errnum = QSE_HTRD_ENOERR;
n = htrd->recbs->handle (htrd, &htrd->re);
if (n <= -1)
{
if (htrd->errnum == QSE_HTRD_ENOERR)
htrd->errnum = QSE_HTRD_ERECBS;
/* need to clear request on error?
clear_feed (htrd); */
return -1;
}
}
#if 0
qse_printf (QSE_T("CONTENT_LENGTH %d, RAW HEADER LENGTH %d\n"),
(int)QSE_MBS_LEN(&htrd->re.content),
(int)QSE_MBS_LEN(&htrd->fed.b.raw));
#endif
clear_feed (htrd);
/* let ptr point to the next character to LF or
@ -1340,6 +1302,21 @@ int qse_htrd_feed (qse_htrd_t* htrd, const qse_mchar_t* req, qse_size_t len)
}
feedme_more:
if (header_completed_during_this_feed && htrd->recbs->peek)
{
int n;
htrd->errnum = QSE_HTRD_ENOERR;
n = htrd->recbs->peek (htrd, &htrd->re);
if (n <= -1)
{
if (htrd->errnum == QSE_HTRD_ENOERR)
htrd->errnum = QSE_HTRD_ERECBS;
/* need to clear request on error?
clear_feed (htrd); */
return -1;
}
}
return 0;
}

View File

@ -45,6 +45,12 @@ void qse_htre_fini (qse_htre_t* re)
void qse_htre_clear (qse_htre_t* re)
{
if (re->concb)
{
re->concb (re, QSE_NULL, 0, re->concb_ctx); /* indicate end of content */
qse_htre_unsetconcb (re);
}
QSE_MEMSET (&re->version, 0, QSE_SIZEOF(re->version));
QSE_MEMSET (&re->attr, 0, QSE_SIZEOF(re->attr));
@ -53,8 +59,7 @@ void qse_htre_clear (qse_htre_t* re)
qse_mbs_clear (&re->content);
qse_mbs_clear (&re->qpath_or_smesg);
qse_mbs_clear (&re->qparam);
re->discard = 0;
re->flags = 0;
}
int qse_htre_setstrfromcstr (
@ -109,3 +114,29 @@ int qse_htre_walkheaders (
return hwctx.ret;
}
int qse_htre_addcontent (
qse_htre_t* re, const qse_mchar_t* ptr, qse_size_t len)
{
/* if the callback is set, the content goes to the callback. */
if (re->concb) return re->concb (re, ptr, len, re->concb_ctx);
/* if the callback is not set, the contents goes to the internal buffer */
if (qse_mbs_ncat (&re->content, ptr, len) == (qse_size_t)-1) return -1;
return 0;
}
void qse_htre_unsetconcb (qse_htre_t* re)
{
re->concb = QSE_NULL;
re->concb_ctx = QSE_NULL;
}
void qse_htre_setconcb (qse_htre_t* re, qse_htre_concb_t concb, void* ctx)
{
re->concb = concb;
re->concb_ctx = ctx;
}
const qse_mchar_t* qse_htre_getqmethodname (qse_htre_t* re)
{
return qse_gethttpmethodname (re->qmethod_or_sstatus);
}

View File

@ -296,27 +296,26 @@ static void purge_tasks_locked (qse_httpd_t* httpd, qse_httpd_client_t* client)
#endif
}
static int htrd_peek_request (qse_htrd_t* htrd, qse_htre_t* req)
{
htrd_xtn_t* xtn = (htrd_xtn_t*) qse_htrd_getxtn (htrd);
qse_httpd_client_t* client =
&xtn->httpd->client.array.data[xtn->client_index];
return xtn->httpd->cbs->peek_request (xtn->httpd, 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);
qse_httpd_client_t* client = &xtn->httpd->client.array.data[xtn->client_index];
qse_httpd_client_t* client =
&xtn->httpd->client.array.data[xtn->client_index];
return xtn->httpd->cbs->handle_request (xtn->httpd, client, req);
}
static int htrd_handle_expect_continue (qse_htrd_t* htrd, qse_htre_t* req)
{
htrd_xtn_t* xtn = (htrd_xtn_t*) qse_htrd_getxtn (htrd);
qse_httpd_client_t* client = &xtn->httpd->client.array.data[xtn->client_index];
return xtn->httpd->cbs->handle_expect_continue (xtn->httpd, client, req);
}
static qse_htrd_recbs_t htrd_recbs =
{
htrd_handle_request,
htrd_handle_expect_continue,
/* The response handler is not needed as QSE_HTRD_RESPONSE is truned off */
QSE_NULL
htrd_peek_request,
htrd_handle_request
};
static void deactivate_listener (qse_httpd_t* httpd, listener_t* l)
@ -329,41 +328,31 @@ static void deactivate_listener (qse_httpd_t* httpd, listener_t* l)
l->handle = -1;
}
static int activate_listener (qse_httpd_t* httpd, listener_t* l)
static int get_listener_sockaddr (const listener_t* l, sockaddr_t* addr)
{
/* TODO: suport https... */
sockaddr_t addr;
int s = -1, flag;
int addrsize;
QSE_ASSERT (l->handle <= -1);
QSE_MEMSET (addr, 0, QSE_SIZEOF(*addr));
s = socket (l->family, SOCK_STREAM, IPPROTO_TCP);
if (s <= -1) goto oops_esocket;
flag = 1;
setsockopt (s, SOL_SOCKET, SO_REUSEADDR, &flag, QSE_SIZEOF(flag));
QSE_MEMSET (&addr, 0, QSE_SIZEOF(addr));
switch (l->family)
{
case AF_INET:
{
addr.in4.sin_family = l->family;
addr.in4.sin_addr = l->addr.in4;
addr.in4.sin_port = htons (l->port);
addrsize = QSE_SIZEOF(addr.in4);
addr->in4.sin_family = l->family;
addr->in4.sin_addr = l->addr.in4;
addr->in4.sin_port = htons (l->port);
addrsize = QSE_SIZEOF(addr->in4);
break;
}
#ifdef AF_INET6
case AF_INET6:
{
addr.in6.sin6_family = l->family;
addr.in6.sin6_addr = l->addr.in6;
addr.in6.sin6_port = htons (l->port);
addr->in6.sin6_family = l->family;
addr->in6.sin6_addr = l->addr.in6;
addr->in6.sin6_port = htons (l->port);
/* TODO: addr.in6.sin6_scope_id */
addrsize = QSE_SIZEOF(addr.in6);
addrsize = QSE_SIZEOF(addr->in6);
break;
}
#endif
@ -374,6 +363,26 @@ static int activate_listener (qse_httpd_t* httpd, listener_t* l)
}
}
return addrsize;
}
static int activate_listener (qse_httpd_t* httpd, listener_t* l)
{
/* TODO: suport https... */
int s = -1, flag;
sockaddr_t addr;
int addrsize;
QSE_ASSERT (l->handle <= -1);
s = socket (l->family, SOCK_STREAM, IPPROTO_TCP);
if (s <= -1) goto oops_esocket;
flag = 1;
setsockopt (s, SOL_SOCKET, SO_REUSEADDR, &flag, QSE_SIZEOF(flag));
addrsize = get_listener_sockaddr (l, &addr);
/* Solaris 8 returns EINVAL if QSE_SIZEOF(addr) is passed in as the
* address size for AF_INET. */
/*if (bind (s, (struct sockaddr*)&addr, QSE_SIZEOF(addr)) <= -1) goto oops_esocket;*/
@ -384,18 +393,6 @@ static int activate_listener (qse_httpd_t* httpd, listener_t* l)
if (flag >= 0) fcntl (s, F_SETFL, flag | O_NONBLOCK);
fcntl (s, F_SETFD, FD_CLOEXEC);
#if 0
/* TODO: */
if (l->secure)
{
SSL_CTX* ctx;
SSL* ssl;
ctx = SSL_ctx_new (SSLv3_method());
ssl = SSL_new (ctx);
}
#endif
l->handle = s;
s = -1;
@ -470,6 +467,12 @@ static void delete_from_client_array (qse_httpd_t* httpd, int fd)
qse_htrd_close (array->data[fd].htrd);
array->data[fd].htrd = QSE_NULL;
qse_fprintf (QSE_STDERR, QSE_T("Debug: closing socket %d\n"), array->data[fd].handle.i);
/* note that client.closed is not a counterpart to client.accepted.
* so it is called even if client.closed failed. */
if (httpd->cbs->client.closed)
httpd->cbs->client.closed (httpd, &array->data[fd]);
close (array->data[fd].handle.i);
array->size--;
}
@ -492,19 +495,23 @@ static void fini_client_array (qse_httpd_t* httpd)
}
}
static qse_httpd_client_t* insert_into_client_array (qse_httpd_t* httpd, int fd, sockaddr_t* addr)
static qse_httpd_client_t* insert_into_client_array (
qse_httpd_t* httpd, qse_httpd_client_t* client)
{
htrd_xtn_t* xtn;
client_array_t* array = &httpd->client.array;
int opt;
int opt, fd = client->handle.i;
/* TODO: is an array is the best???
* i do use an array for direct access by fd. */
if (fd >= array->capa)
{
#define ALIGN 512
qse_httpd_client_t* tmp;
qse_size_t capa = ((fd + ALIGN) / ALIGN) * ALIGN;
tmp = qse_httpd_reallocmem (httpd, array->data, capa * QSE_SIZEOF(*tmp));
tmp = qse_httpd_reallocmem (
httpd, array->data, capa * QSE_SIZEOF(*tmp));
if (tmp == QSE_NULL) return QSE_NULL;
QSE_MEMSET (&tmp[array->capa], 0,
@ -523,9 +530,13 @@ static qse_httpd_client_t* insert_into_client_array (qse_httpd_t* httpd, int fd,
opt &= ~QSE_HTRD_RESPONSE;
qse_htrd_setoption (array->data[fd].htrd, opt);
array->data[fd].ready = httpd->cbs->client.accepted? 0 : 1;
array->data[fd].bad = 0;
array->data[fd].handle.i = fd;
array->data[fd].addr = *addr;
array->data[fd].secure = client->secure;
array->data[fd].handle = client->handle;
array->data[fd].handle2 = client->handle2;
array->data[fd].local_addr = client->local_addr;
array->data[fd].remote_addr = client->remote_addr;
#if defined(HAVE_PTHREAD)
if (httpd->threaded)
@ -543,25 +554,23 @@ static qse_httpd_client_t* insert_into_client_array (qse_httpd_t* httpd, int fd,
static int accept_client_from_listener (qse_httpd_t* httpd, listener_t* l)
{
int flag, c;
sockaddr_t addr;
int flag;
#ifdef HAVE_SOCKLEN_T
socklen_t addrlen = QSE_SIZEOF(addr);
socklen_t addrlen;
#else
int addrlen = QSE_SIZEOF(addr);
int addrlen;
#endif
qse_httpd_client_t clibuf;
qse_httpd_client_t* client;
/* TODO:
if (l->secure)
{
SSL_Accept
....
}
*/
QSE_MEMSET (&clibuf, 0, QSE_SIZEOF(clibuf));
clibuf.secure = l->secure;
c = accept (l->handle, (struct sockaddr*)&addr, &addrlen);
if (c <= -1)
addrlen = QSE_SIZEOF(clibuf.remote_addr);
clibuf.handle.i = accept (
l->handle, (struct sockaddr*)&clibuf.remote_addr, &addrlen);
if (clibuf.handle.i <= -1)
{
httpd->errnum = QSE_HTTPD_ESOCKET;
qse_fprintf (QSE_STDERR, QSE_T("Error: accept returned failure\n"));
@ -570,39 +579,42 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: accept returned failure\n"));
/* select() uses a fixed-size array so the file descriptor can not
* exceeded FD_SETSIZE */
if (c >= FD_SETSIZE)
if (clibuf.handle.i >= FD_SETSIZE)
{
close (c);
close (clibuf.handle.i);
qse_fprintf (QSE_STDERR, QSE_T("Error: too many client?\n"));
/* httpd->errnum = QSE_HTTPD_EOVERFLOW; */
goto oops;
}
addrlen = QSE_SIZEOF(clibuf.local_addr);
if (getsockname (clibuf.handle.i, (struct sockaddr*)&clibuf.local_addr, &addrlen) <= -1)
get_listener_sockaddr (l, &clibuf.local_addr);
/* set the nonblock flag in case read() after select() blocks
* for various reasons - data received may be dropped after
* arrival for wrong checksum, for example. */
flag = fcntl (c, F_GETFL);
if (flag >= 0) fcntl (c, F_SETFL, flag | O_NONBLOCK);
flag = fcntl (clibuf.handle.i, F_GETFL);
if (flag >= 0) fcntl (clibuf.handle.i, F_SETFL, flag | O_NONBLOCK);
flag = fcntl (c, F_GETFD);
if (flag >= 0) fcntl (c, F_SETFD, flag | FD_CLOEXEC);
flag = fcntl (clibuf.handle.i, F_GETFD);
if (flag >= 0) fcntl (clibuf.handle.i, F_SETFD, flag | FD_CLOEXEC);
#if defined(HAVE_PTHREAD)
if (httpd->threaded) pthread_mutex_lock (&httpd->client.mutex);
#endif
client = insert_into_client_array (httpd, c, &addr);
client = insert_into_client_array (httpd, &clibuf);
#if defined(HAVE_PTHREAD)
if (httpd->threaded) pthread_mutex_unlock (&httpd->client.mutex);
#endif
if (client == QSE_NULL)
{
close (c);
close (clibuf.handle.i);
goto oops;
}
qse_printf (QSE_T("connection %d accepted\n"), c);
qse_printf (QSE_T("connection %d accepted\n"), clibuf.handle.i);
return 0;
@ -683,26 +695,39 @@ static int make_fd_set_from_client_array (
if (!httpd->threaded || !for_rdwr)
{
/* a trigger is a handle to monitor to check
* if there is data avaiable to write back to the client.
* if it is not threaded, qse_httpd_loop() needs to
* monitor trigger handles. if it is threaded,
* response_thread() needs to monitor these handles */
if (ca->data[fd].task.queue.head &&
ca->data[fd].task.queue.head->task.trigger.i >= 0)
/* trigger[0] is a handle to monitor to check
* if there is data avaiable to read to write back to
* the client. if it is not threaded, qse_httpd_loop()
* needs to monitor trigger handles. if it is threaded,
* response_thread() needs to monitor these handles.
*
* trigger[1] is a user-defined handle to monitor to
* check if httpd can post data to. but this is not
* a client-side handle.
*/
if (ca->data[fd].task.queue.head)
{
qse_httpd_task_t* task = &ca->data[fd].task.queue.head->task;
if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_READ)
{
/* if a trigger is available, add it to the read set also. */
FD_SET (ca->data[fd].task.queue.head->task.trigger.i, r);
if (ca->data[fd].task.queue.head->task.trigger.i > max)
max = ca->data[fd].task.queue.head->task.trigger.i;
qse_printf (QSE_T(">>>>ADDING TRIGGER[0] %d\n"), task->trigger[0].i);
FD_SET (task->trigger[0].i, r);
if (task->trigger[0].i > max) max = task->trigger[0].i;
}
if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_WRITE)
{
/* if a trigger is available, add it to the read set also. */
qse_printf (QSE_T(">>>>ADDING TRIGGER[1] %d\n"), task->trigger[1].i);
FD_SET (task->trigger[1].i, w);
if (task->trigger[1].i > max) max = task->trigger[1].i;
}
}
}
}
if (ca->data[fd].bad ||
(ca->data[fd].task.queue.head &&
ca->data[fd].task.queue.head->task.trigger.i <= -1))
(ca->data[fd].task.queue.head && !(ca->data[fd].task.queue.head->task.trigger_mask & QSE_HTTPD_TASK_TRIGGER_READ)))
{
/* add a client-side handle to the write set
* if the client is already marked bad or
@ -815,8 +840,42 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: select returned failure - %hs\n"), strerr
}
else if (client->task.queue.head)
{
if (client->task.queue.head->task.trigger.i <= -1 ||
FD_ISSET(client->task.queue.head->task.trigger.i, &r))
qse_httpd_task_t* task;
int perform = 0;
task = &client->task.queue.head->task;
task->trigger_mask &=
~(QSE_HTTPD_TASK_TRIGGER_READABLE |
QSE_HTTPD_TASK_TRIGGER_WRITABLE);
if (!(task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_READ) &&
!(task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_WRITE))
{
/* no trigger set. set the flag to
* non-readable and non-writable */
perform = 1;
}
else
{
if ((task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_READ) &&
FD_ISSET(task->trigger[0].i, &r))
{
/* set the flag to readable */
task->trigger_mask |= QSE_HTTPD_TASK_TRIGGER_READABLE;
perform = 1;
}
if ((task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_WRITE) &&
FD_ISSET(task->trigger[1].i, &w))
{
/* set the flag to writable */
task->trigger_mask |= QSE_HTTPD_TASK_TRIGGER_WRITABLE;
perform = 1;
}
}
if (perform)
{
tv.tv_sec = 0;
tv.tv_usec = 0;
@ -842,10 +901,16 @@ static int read_from_client (qse_httpd_t* httpd, qse_httpd_client_t* client)
qse_mchar_t buf[1024];
qse_ssize_t m;
QSE_ASSERT (httpd->cbs->client.recv != QSE_NULL);
reread:
m = read (client->handle.i, buf, QSE_SIZEOF(buf));
httpd->errnum = QSE_HTTPD_ENOERR;
m = httpd->cbs->client.recv (httpd, client, buf, QSE_SIZEOF(buf));
if (m <= -1)
{
// TODO: handle errno in the callback... and devise a new return value
// to indicate no data at this momemnt (EAGAIN, EWOULDBLOCK)...
// EINTR to be hnalded inside callback if needed...
if (errno == EAGAIN || errno == EWOULDBLOCK)
{
/* nothing to read yet. */
@ -871,6 +936,7 @@ qse_fprintf (QSE_STDERR, QSE_T("Debug: connection closed %d\n"), client->handle.
* that's because we don't know how many valid requests
* are included in 'buf' */
qse_fprintf (QSE_STDERR, QSE_T("Debug: read from a client %d\n"), client->handle.i);
httpd->errnum = QSE_HTTPD_ENOERR;
if (qse_htrd_feed (client->htrd, buf, m) <= -1)
{
@ -889,14 +955,15 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: http error while processing \n"));
return 0;
}
int qse_httpd_loop (qse_httpd_t* httpd, int threaded)
int qse_httpd_loop (qse_httpd_t* httpd, qse_httpd_cbs_t* cbs, int threaded)
{
#if defined(HAVE_PTHREAD)
pthread_t response_thread_id;
#endif
httpd->stopreq = 0;
httpd->threaded = 0;
httpd->stopreq = 0;
httpd->cbs = cbs;
QSE_ASSERTX (httpd->listener.list != QSE_NULL,
"Add listeners before calling qse_httpd_loop()"
@ -934,16 +1001,22 @@ int qse_httpd_loop (qse_httpd_t* httpd, int threaded)
pthread_mutex_init (&httpd->client.mutex, QSE_NULL);
pthread_cond_init (&httpd->client.cond, QSE_NULL);
/* set this before creating a thread
* because this is accessed in a thread.
* if i set this after pthread_create, a thread
* function may still see 0. */
httpd->threaded = 1;
if (pthread_create (
&response_thread_id, QSE_NULL,
response_thread, httpd) != 0)
{
httpd->threaded = 0;
pthread_cond_destroy (&httpd->client.cond);
pthread_mutex_destroy (&httpd->client.mutex);
QSE_CLOSE (httpd->client.pfd[1]);
QSE_CLOSE (httpd->client.pfd[0]);
}
else httpd->threaded = 1;
}
}
#endif
@ -994,13 +1067,25 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: select returned failure\n"));
if (FD_ISSET(client->handle.i, &r))
{
/* got input */
if (!client->ready)
{
/* if client.accepted() returns 0, it is called
* again next time. */
QSE_ASSERT (httpd->cbs->client.accepted != QSE_NULL);
int x = httpd->cbs->client.accepted (httpd, client); /* is this correct???? what if ssl handshaking got stalled because writing failed in SSL_accept()? */
if (x >= 1) client->ready = 1;
else if (x <= -1) goto bad_client;
}
else
{
if (read_from_client (httpd, client) <= -1)
{
bad_client:
if (httpd->threaded)
{
/* let the writing part handle it,
* probably in the next iteration */
qse_httpd_markclientbad (httpd, client);
qse_httpd_markbadclient (httpd, client);
shutdown (client->handle.i, SHUT_RDWR);
}
else
@ -1012,7 +1097,7 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: select returned failure\n"));
}
}
}
}
if (!httpd->threaded)
{
@ -1025,8 +1110,41 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: select returned failure\n"));
}
else if (client->task.queue.head)
{
if (client->task.queue.head->task.trigger.i <= -1 ||
FD_ISSET(client->task.queue.head->task.trigger.i, &r))
qse_httpd_task_t* task;
int perform = 0;
task = &client->task.queue.head->task;
task->trigger_mask &=
~(QSE_HTTPD_TASK_TRIGGER_READABLE |
QSE_HTTPD_TASK_TRIGGER_WRITABLE);
if (!(task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_READ) &&
!(task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_WRITE))
{
/* no trigger set. set the flag to
* non-readable and non-writable */
perform = 1;
}
else
{
if ((task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_READ) &&
FD_ISSET(task->trigger[0].i, &r))
{
/* set the flag to readable */
task->trigger_mask |= QSE_HTTPD_TASK_TRIGGER_READABLE;
perform = 1;
}
if ((task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_WRITE) &&
FD_ISSET(task->trigger[1].i, &w))
{
/* set the flag to writable */
task->trigger_mask |= QSE_HTTPD_TASK_TRIGGER_WRITABLE;
perform = 1;
}
}
if (perform)
{
tv.tv_sec = 0;
tv.tv_usec = 0;
@ -1305,7 +1423,7 @@ qse_httpd_task_t* qse_httpd_entask (
return ret;
}
void qse_httpd_markclientbad (qse_httpd_t* httpd, qse_httpd_client_t* client)
void qse_httpd_markbadclient (qse_httpd_t* httpd, qse_httpd_client_t* client)
{
/* mark that something is wrong in processing requests from this client.
* this client could be bad... or the system could encounter some errors
@ -1313,4 +1431,12 @@ void qse_httpd_markclientbad (qse_httpd_t* httpd, qse_httpd_client_t* client)
client->bad = 1;
}
void qse_httpd_discardcontent (qse_httpd_t* httpd, qse_htre_t* req)
{
req->flags |= QSE_HTRE_DISCARDED;
/* clear the content buffer in case it has received contents
* partially already */
qse_mbs_clear (&req->content);
}
#endif

View File

@ -61,8 +61,13 @@ struct task_queue_node_t
struct qse_httpd_client_t
{
qse_ubi_t handle;
qse_ubi_t handle2;
int ready;
int bad;
sockaddr_t addr;
int secure;
sockaddr_t local_addr;
sockaddr_t remote_addr;
qse_htrd_t* htrd;
struct

View File

@ -29,6 +29,7 @@
#include <qse/cmn/str.h>
#include <qse/cmn/chr.h>
#include <qse/cmn/pio.h>
#include <qse/cmn/fmt.h>
#include <fcntl.h>
#include <stdarg.h>
@ -37,82 +38,6 @@
#define MAX_SEND_SIZE 4096
#if defined(HAVE_SYS_SENDFILE_H)
# include <sys/sendfile.h>
#endif
#if defined(HAVE_SENDFILE) && defined(HAVE_SENDFILE64)
# if !defined(_LP64) && (QSE_SIZEOF_VOID_P<8) && defined(HAVE_SENDFILE64)
# define xsendfile sendfile64
# else
# define xsendfile sendfile
# endif
#elif defined(HAVE_SENDFILE)
# define xsendfile sendfile
#elif defined(HAVE_SENDFILE64)
# define xsendfile sendfile64
#elif defined(HAVE_SENDFILEV) || defined(HAVE_SENDFILEV64)
static qse_ssize_t xsendfile (
int out_fd, int in_fd, qse_foff_t* offset, qse_size_t count)
{
#if !defined(_LP64) && (QSE_SIZEOF_VOID_P<8) && defined(HAVE_SENDFILE64)
struct sendfilevec64 vec;
#else
struct sendfilevec vec;
#endif
size_t xfer;
ssize_t n;
vec.sfv_fd = in_fd;
vec.sfv_flag = 0;
if (offset)
{
vec.sfv_off = *offset;
}
else
{
vec.sfv_off = lseek (in_fd, 0, SEEK_CUR); /* TODO: lseek64 or llseek.. */
if (vec.sfv_off == (off_t)-1) return (qse_ssize_t)-1;
}
vec.sfv_len = count;
#if !defined(_LP64) && (QSE_SIZEOF_VOID_P<8) && defined(HAVE_SENDFILE64)
n = sendfilev64 (out_fd, &vec, 1, &xfer);
#else
n = sendfilev (out_fd, &vec, 1, &xfer);
#endif
if (offset) *offset = *offset + xfer;
/* TODO: xfer contains number of byte written even on failure
on success xfer == n.
on failure xfer != n.
*/
return n;
}
#else
static qse_ssize_t xsendfile (
int out_fd, int in_fd, qse_foff_t* offset, qse_size_t count)
{
qse_mchar_t buf[MAX_SEND_SIZE];
qse_ssize_t n;
if (offset && lseek (in_fd, *offset, SEEK_SET) != *offset) //* 64bit version of lseek...
return (qse_ssize_t)-1;
if (count > QSE_COUNTOF(buf)) count = QSE_COUNTOF(buf);
n = read (in_fd, buf, count);
if (n == (qse_ssize_t)-1 || n == 0) return n;
n = send (out_fd, buf, n, 0);
if (n > 0 && offset) *offset = *offset + n;
return n;
}
#endif
/*------------------------------------------------------------------------*/
static int task_main_disconnect (
@ -131,7 +56,6 @@ qse_httpd_task_t* qse_httpd_entaskdisconnect (
QSE_MEMSET (&task, 0, QSE_SIZEOF(task));
task.main = task_main_disconnect;
task.trigger.i = -1;
return qse_httpd_entask (httpd, client, pred, &task, 0);
}
@ -151,13 +75,7 @@ static int task_main_statictext (
}
/* TODO: do i need to add code to skip this send if count is 0? */
n = send (
client->handle.i,
task->ctx,
count,
0
);
n = httpd->cbs->client.send (httpd, client, task->ctx, count);
if (n <= -1) return -1;
ptr = (const qse_mchar_t*)task->ctx + n;
@ -178,7 +96,6 @@ qse_httpd_task_t* qse_httpd_entaskstatictext (
QSE_MEMSET (&task, 0, QSE_SIZEOF(task));
task.main = task_main_statictext;
task.ctx = (void*)text;
task.trigger.i = -1;
return qse_httpd_entask (httpd, client, pred, &task, 0);
}
@ -216,13 +133,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 = send (
client->handle.i,
ctx->ptr,
count,
0
);
n = httpd->cbs->client.send (httpd, client, ctx->ptr, count);
if (n <= -1) return -1;
ctx->left -= n;
@ -249,7 +160,6 @@ qse_httpd_task_t* qse_httpd_entasktext (
task.init = task_init_text;
task.main = task_main_text;
task.ctx = &data;
task.trigger.i = -1;
return qse_httpd_entask (
httpd, client, pred, &task, QSE_SIZEOF(data) + data.left);
@ -296,13 +206,7 @@ static int task_main_format (
count = MAX_SEND_SIZE;
if (count >= ctx->left) count = ctx->left;
n = send (
client->handle.i,
ctx->ptr,
count,
0
);
n = httpd->cbs->client.send (httpd, client, ctx->ptr, count);
if (n <= -1) return -1;
ctx->left -= n;
@ -400,7 +304,6 @@ qse_httpd_task_t* qse_httpd_entaskformat (
task.fini = task_fini_format;
task.main = task_main_format;
task.ctx = &data;
task.trigger.i = -1;
qse_printf (QSE_T("SEND: [%.*hs]\n"), (int)l, buf);
return qse_httpd_entask (
@ -436,11 +339,21 @@ static qse_httpd_task_t* entask_error (
lmsg = QSE_MT("<html><head><title>Method Not Allowed</title></head><body><b>REQUESTED METHOD NOT ALLOWED<b></body></html>");
break;
case 411:
smsg = QSE_MT("Length Required");
lmsg = QSE_MT("<html><head><title>Length Required</title></head><body><b>LENGTH REQUIRED<b></body></html>");
break;
case 416:
smsg = QSE_MT("Requested Range Not Satisfiable");
lmsg = QSE_MT("<html><head><title>Requested Range Not Satsfiable</title></head><body><b>REQUESTED RANGE NOT SATISFIABLE<b></body></html>");
break;
case 417:
smsg = QSE_MT("Expectation Failed");
lmsg = QSE_MT("<html><head><title>Expectation Failed</title></head><body><b>EXPECTATION FAILED<b></body></html>");
break;
case 500:
smsg = QSE_MT("Internal Server Error");
lmsg = QSE_MT("<html><head><title>Internal Server Error</title></head><body><b>INTERNAL SERVER ERROR<b></body></html>");
@ -483,6 +396,18 @@ qse_httpd_task_t* qse_httpd_entaskerror (
return entask_error (httpd, client, task, code, qse_htre_getversion(req), req->attr.keepalive);
}
/*------------------------------------------------------------------------*/
qse_httpd_task_t* qse_httpd_entaskcontinue (
qse_httpd_t* httpd, qse_httpd_client_t* client,
const qse_httpd_task_t* task, const qse_htre_t* req)
{
qse_http_version_t* version = qse_htre_getversion(req);
return qse_httpd_entaskformat (
httpd, client, task,
QSE_MT("HTTP/%d.%d 100 Continue\r\n\r\n"),
version->major, version->minor);
}
/*------------------------------------------------------------------------*/
typedef struct task_file_t task_file_t;
@ -506,6 +431,7 @@ static void task_fini_file (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_file_t* ctx = (task_file_t*)task->ctx;
qse_printf (QSE_T("closing file %d\n"), ctx->handle.i);
QSE_CLOSE (ctx->handle.i);
}
@ -520,13 +446,8 @@ static int task_main_file (
if (count >= ctx->left) count = ctx->left;
/* TODO: more adjustment needed for OS with different sendfile semantics... */
n = xsendfile (
client->handle.i,
ctx->handle.i,
&ctx->offset,
count
);
n = httpd->cbs->client.sendfile (
httpd, client, ctx->handle, &ctx->offset, count);
if (n <= -1)
{
// HANDLE EGAIN specially???
@ -560,7 +481,6 @@ qse_httpd_task_t* qse_httpd_entaskfile (
qse_httpd_task_t task;
task_file_t data;
qse_printf (QSE_T("Debug: sending file to %d\n"), client->handle.i);
QSE_MEMSET (&data, 0, QSE_SIZEOF(data));
data.handle = handle;
data.offset = offset;
@ -571,8 +491,8 @@ qse_printf (QSE_T("Debug: sending file to %d\n"), client->handle.i);
task.main = task_main_file;
task.fini = task_fini_file;
task.ctx = &data;
task.trigger.i = -1;
qse_printf (QSE_T("Debug: entasking file (%d)\n"), client->handle.i);
return qse_httpd_entask (httpd, client, pred, &task, QSE_SIZEOF(data));
}
@ -783,7 +703,9 @@ set_chunklen:
ctx->bufpos = x;
send_dirlist:
n = send (client->handle.i, &ctx->buf[ctx->bufpos], ctx->buflen, 0);
httpd->errnum = QSE_HTTPD_ENOERR;
n = httpd->cbs->client.send (
httpd, client, &ctx->buf[ctx->bufpos], ctx->buflen);
if (n <= -1) return -1;
/* NOTE if (n == 0), it will enter an infinite loop */
@ -894,7 +816,9 @@ static int task_main_dir_nochunk (
while (1);
send_dirlist:
n = send (client->handle.i, &ctx->buf[ctx->bufpos], ctx->buflen, 0);
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;
@ -915,7 +839,6 @@ qse_httpd_task_t* qse_httpd_entaskdir (
task.main = chunked? task_main_dir: task_main_dir_nochunk;
task.fini = task_fini_dir;
task.ctx = &handle;
task.trigger.i = -1;
return qse_httpd_entask (httpd, client, pred, &task, QSE_SIZEOF(task_dir_t));
}
@ -1209,7 +1132,6 @@ qse_httpd_task_t* qse_httpd_entaskpath (
task.init = task_init_path;
task.main = task_main_path;
task.ctx = &data;
task.trigger.i = -1;
return qse_httpd_entask (httpd, client, pred, &task,
QSE_SIZEOF(task_path_t) + qse_mbslen(name) + 1);
@ -1239,6 +1161,10 @@ struct task_cgi_t
qse_pio_t* pio;
qse_htrd_t* htrd;
qse_htre_t* req; /* original request associated with this */
qse_mbs_t* reqcon; /* content from the request */
int reqfwderr;
qse_mbs_t* res;
qse_mchar_t* res_ptr;
qse_size_t res_left;
@ -1264,6 +1190,35 @@ struct cgi_htrd_xtn_t
task_cgi_t* cgi;
};
typedef struct cgi_req_hdr_ctx_t cgi_req_hdr_ctx_t;
struct cgi_req_hdr_ctx_t
{
qse_httpd_t* httpd;
qse_env_t* env;
};
int walk_req_headers (qse_htre_t* req, const qse_mchar_t* key, const qse_mchar_t* val, void* ctx)
{
cgi_req_hdr_ctx_t* cgi;
qse_mchar_t* http_key;
int ret;
cgi = (cgi_req_hdr_ctx_t*)ctx;
/* convert value to uppercase, change - to _ */
http_key = qse_mbsdup2 (QSE_MT("HTTP_"), key, req->mmgr);
if (http_key == QSE_NULL)
{
cgi->httpd->errnum = QSE_HTTPD_ENOMEM;
return -1;
}
ret = qse_env_insertmbs (cgi->env, http_key, val);
QSE_MMGR_FREE (req->mmgr, http_key);
return ret;
}
int walk_cgi_headers (qse_htre_t* req, const qse_mchar_t* key, const qse_mchar_t* val, void* ctx)
{
task_cgi_t* cgi = (task_cgi_t*)ctx;
@ -1279,17 +1234,14 @@ int walk_cgi_headers (qse_htre_t* req, const qse_mchar_t* key, const qse_mchar_t
return 0;
}
static int cgi_htrd_handle_request (qse_htrd_t* htrd, qse_htre_t* req)
static int cgi_htrd_peek_request (qse_htrd_t* htrd, qse_htre_t* req)
{
cgi_htrd_xtn_t* xtn = (cgi_htrd_xtn_t*) qse_htrd_getxtn (htrd);
task_cgi_t* cgi = xtn->cgi;
const qse_mchar_t* status;
static qse_http_version_t v11 = { 1, 1 };
QSE_ASSERT (req->attr.hurried);
status = qse_htre_getheaderval (req, QSE_MT("Status"));
if (status)
{
qse_mchar_t buf[128];
@ -1383,6 +1335,9 @@ static int cgi_htrd_handle_request (qse_htrd_t* htrd, qse_htre_t* req)
if (cgi->content_received > 0)
{
/* the initial part of the content body has been received
* along with the header. it need to be added to the result
* buffer. */
if (cgi->content_chunked)
{
qse_mchar_t buf[64];
@ -1403,59 +1358,143 @@ static int cgi_htrd_handle_request (qse_htrd_t* htrd, qse_htre_t* req)
static qse_htrd_recbs_t cgi_htrd_cbs =
{
cgi_htrd_handle_request,
QSE_NULL, /* not needed for CGI */
cgi_htrd_peek_request,
QSE_NULL /* not needed for CGI */
};
static qse_env_t* makecgienv (
qse_httpd_t* httpd, qse_httpd_client_t* client, const qse_htre_t* req)
qse_httpd_t* httpd, qse_httpd_client_t* client,
const qse_htre_t* req, const qse_mchar_t* path)
{
/* TODO: error check */
qse_env_t* env;
cgi_req_hdr_ctx_t ctx;
env = qse_env_open (httpd->mmgr, 0, 0);
if (env == QSE_NULL) goto oops;
#ifdef _WIN32
qse_env_insertsys (env, QSE_T("PATH"));
qse_env_insert (env, QSE_T("PATH"), QSE_NULL);
#else
qse_env_insertmbs (env, QSE_MT("LANG"), QSE_NULL);
qse_env_insertmbs (env, QSE_MT("PATH"), QSE_NULL);
#endif
qse_env_insertmbs (env, QSE_MT("GATEWAY_INTERFACE"), QSE_MT("CGI/1.1"));
{
qse_char_t proto[32];
qse_http_version_t* v = qse_htre_getversion(req);
snprintf (proto, QSE_COUNTOF(proto),
QSE_T("HTTP/%d.%d"), (int)v->major, (int)v->minor);
qse_env_insert (env, QSE_T("SERVER_PROTOCOL"), proto);
/* TODO: corrent all these name??? */
qse_mchar_t tmp[1024];
qse_mbsxncpy (tmp, QSE_COUNTOF(tmp), qse_htre_getqpathptr(req), qse_htre_getqpathlen(req));
//qse_env_insertmbs (env, QSE_MT("SCRIPT_NAME"), tmp);
//qse_env_insertmbs (env, QSE_MT("PATH_INFO"), tmp);
//qse_env_insertmbs (env, QSE_MT("PATH_TRANSLATED"), tmp);
//qse_env_insertmbs (env, QSE_MT("DOCUMENT_ROOT"), QSE_MT("/"));
}
#else
qse_env_insertsysm (env, QSE_MT("LANG"));
qse_env_insertsysm (env, QSE_MT("PATH"));
//qse_env_insertm (env, QSE_MT("SERVER_PORT"), );
//qse_env_insertmbs (env, QSE_MT("SCRIPT_FILENAME"), path);
qse_env_insertmbs (env, QSE_MT("REQUEST_URI"), qse_htre_getqpathptr(req));
{
qse_mchar_t* tmp = qse_htre_getqparamptr(req);
if (tmp) qse_env_insertmbs (env, QSE_MT("QUERY_STRING"), tmp);
}
qse_env_insertmbs (
env, QSE_MT("REQUEST_METHOD"), qse_htre_getqmethodname(req));
if (req->attr.content_length_set)
{
qse_mchar_t tmp[64];
qse_fmtuintmaxtombs (
tmp, QSE_COUNTOF(tmp),
req->attr.content_length, 10,
-1, QSE_MT('\0'), QSE_NULL);
qse_env_insertmbs (env, QSE_MT("CONTENT_LENGTH"), tmp);
}
// TODO: SERVER_HOST, REMOTE_HOST,
{
qse_mchar_t port[16];
snprintf (port, QSE_COUNTOF(port),
QSE_MT("%d"), (int)ntohs(client->addr.in4.sin_port));
qse_env_insertm (env, QSE_MT("REMOTE_PORT"), port);
QSE_MT("%d"), (int)ntohs(client->local_addr.in4.sin_port));
qse_env_insertmbs (env, QSE_MT("SERVER_PORT"), port);
snprintf (port, QSE_COUNTOF(port),
QSE_MT("%d"), (int)ntohs(client->remote_addr.in4.sin_port));
qse_env_insertmbs (env, QSE_MT("REMOTE_PORT"), port);
}
if (client->local_addr.in4.sin_family == AF_INET)
{
qse_mchar_t ipaddr[128];
inet_ntop (client->local_addr.in4.sin_family, &client->local_addr.in4.sin_addr, ipaddr, QSE_COUNTOF(ipaddr));
qse_env_insertmbs (env, QSE_MT("SERVER_ADDR"), ipaddr);
}
else
{
qse_mchar_t ipaddr[128];
inet_ntop (client->local_addr.in6.sin6_family, &client->local_addr.in6.sin6_addr, ipaddr, QSE_COUNTOF(ipaddr));
qse_env_insertmbs (env, QSE_MT("SERVER_ADDR"), ipaddr);
}
if (client->remote_addr.in4.sin_family == AF_INET)
{
qse_mchar_t ipaddr[128];
inet_ntop (client->remote_addr.in4.sin_family, &client->remote_addr.in4.sin_addr, ipaddr, QSE_COUNTOF(ipaddr));
qse_env_insertmbs (env, QSE_MT("REMOTE_ADDR"), ipaddr);
}
else
{
qse_mchar_t ipaddr[128];
inet_ntop (client->remote_addr.in6.sin6_family, &client->remote_addr.in6.sin6_addr, ipaddr, QSE_COUNTOF(ipaddr));
qse_env_insertmbs (env, QSE_MT("REMOTE_ADDR"), ipaddr);
}
{
qse_mchar_t proto[32];
qse_http_version_t* v = qse_htre_getversion(req);
const qse_http_version_t* v = qse_htre_getversion(req);
snprintf (proto, QSE_COUNTOF(proto),
QSE_MT("HTTP/%d.%d"), (int)v->major, (int)v->minor);
qse_env_insertm (env, QSE_MT("SERVER_PROTOCOL"), proto);
qse_env_insertmbs (env, QSE_MT("SERVER_PROTOCOL"), proto);
}
//qse_env_insertm (env, QSE_MT("REMOTE_ADDR"), QSE_MT("what the hell"));
// TODO: HTTP_ headers.
#if 0
qse_env_insertmbs (env, "SERVER_NAME",
qse_env_insertmbs (env, "SERVER_ROOT",
qse_env_insertmbs (env, "DOCUMENT_ROOT",
qse_env_insertmbs (env, "REMOTE_PORT",
qse_env_insertmbs (env, "REQUEST_URI",
#endif
#if 0
qse_env_insertm (env, "SERVER_NAME",
qse_env_insertm (env, "SERVER_ROOT",
qse_env_insertm (env, "DOCUMENT_ROOT",
qse_env_insertm (env, "REMOTE_PORT",
qse_env_insertm (env, "REQUEST_URI",
ctx.httpd = httpd;
ctx.env = env;
if (qse_htre_walkheaders (req, walk_req_headers, &ctx) <= -1) return -1;
#endif
{
const qse_mchar_t* tmp;
tmp = qse_htre_getheaderval(req, QSE_MT("Content-Type"));
if (tmp) qse_env_insertmbs (env, QSE_MT("CONTENT_TYPE"), tmp);
tmp = qse_htre_getheaderval(req, QSE_MT("Cookie"));
if (tmp) qse_env_insertmbs (env, QSE_MT("HTTP_COOKIE"), tmp);
tmp = qse_htre_getheaderval(req, QSE_MT("Host"));
if (tmp) qse_env_insertmbs (env, QSE_MT("HTTP_HOST"), tmp);
tmp = qse_htre_getheaderval(req, QSE_MT("Referer"));
if (tmp) qse_env_insertmbs (env, QSE_MT("HTTP_REFERER"), tmp);
tmp = qse_htre_getheaderval(req, QSE_MT("User-Agent"));
if (tmp) qse_env_insertmbs (env, QSE_MT("HTTP_USER_AGENT"), tmp);
}
return env;
oops:
@ -1463,23 +1502,126 @@ oops:
return QSE_NULL;
}
static int cgi_snatch_content (
qse_htre_t* req, const qse_mchar_t* ptr, qse_size_t len, void* ctx)
{
task_cgi_t* cgi = (task_cgi_t*)ctx;
if (ptr == QSE_NULL)
{
/* request ended. this could be a real end or
* abortion for an error */
QSE_ASSERT (len == 0);
cgi->req = QSE_NULL;
}
else
{
/* push the contents to the own buffer */
if (qse_mbs_ncat (cgi->reqcon, ptr, len) == (qse_size_t)-1)
{
return -1;
}
}
return 0;
}
static int cgi_forward_content (qse_httpd_task_t* task)
{
task_cgi_t* cgi = (task_cgi_t*)task->ctx;
QSE_ASSERT (cgi->reqcon != QSE_NULL);
if (QSE_MBS_LEN(cgi->reqcon) > 0)
{
qse_ssize_t n;
if (!cgi->reqfwderr)
{
qse_printf (QSE_T("@@@@@@@WRITING %d bytes TO CGI\n"), (int)QSE_MBS_LEN(cgi->reqcon));
n = qse_pio_write (
cgi->pio, QSE_PIO_IN,
QSE_MBS_PTR(cgi->reqcon),
QSE_MBS_LEN(cgi->reqcon)
);
if (n <= -1)
{
qse_printf (QSE_T("@@@@@@@@WRITE TO CGI FAILED\n"));
/* TODO: logging ... */
cgi->reqfwderr = 1;
if (cgi->req) cgi->req->flags |= QSE_HTRE_DISCARDED;
}
}
/* TODO: performance improvement...
deleting keeps on moving contents to the head...
can't we grow the buffer to a certain limit?
if the limit is reached, copy the tail to the head... */
/* can write return 0? */
qse_mbs_del (cgi->reqcon, 0, n);
}
else if (cgi->req == QSE_NULL)
{
/* no more request content */
qse_printf (QSE_T("@@@@@@@@NOTHING MORE TO WRITE TO CGI\n"));
task->trigger_mask &=
~(QSE_HTTPD_TASK_TRIGGER_WRITE |
QSE_HTTPD_TASK_TRIGGER_WRITABLE);
}
return 0;
}
static int task_init_cgi (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_cgi_t* xtn = (task_cgi_t*)qse_httpd_gettaskxtn (httpd, task);
task_cgi_t* cgi = (task_cgi_t*)qse_httpd_gettaskxtn (httpd, task);
task_cgi_arg_t* arg = (task_cgi_arg_t*)task->ctx;
QSE_MEMSET (xtn, 0, QSE_SIZEOF(*xtn));
qse_mbscpy ((qse_mchar_t*)(xtn + 1), arg->path);
xtn->path = (qse_mchar_t*)(xtn + 1);
xtn->version = *qse_htre_getversion(arg->req);
xtn->keepalive = arg->req->attr.keepalive;
xtn->nph = arg->nph;
QSE_MEMSET (cgi, 0, QSE_SIZEOF(*cgi));
qse_mbscpy ((qse_mchar_t*)(cgi + 1), arg->path);
cgi->path = (qse_mchar_t*)(cgi + 1);
cgi->version = *qse_htre_getversion(arg->req);
cgi->keepalive = arg->req->attr.keepalive;
cgi->nph = arg->nph;
xtn->env = makecgienv (httpd, client, arg->req);
if (xtn->env == QSE_NULL) xtn->init_failed = 1;
if (!(arg->req->flags & QSE_HTRE_DISCARDED))
{
/* CGI entasking is invoked probably from the peek handler
* that was triggered after the request header is received
* in principle. In that case, arrange to forward content
* bypassing the buffer in the request object itself. */
const qse_mchar_t* ptr = qse_htre_getcontentptr(arg->req);
qse_size_t len = qse_htre_getcontentlen(arg->req);
task->ctx = xtn;
/* create a buffer to hold request content from the client
* and copy content received already */
cgi->reqcon = qse_mbs_open (httpd->mmgr, 0, (len < 512? 512: len));
if (cgi->reqcon == QSE_NULL ||
qse_mbs_ncpy (cgi->reqcon, ptr, len) == (qse_size_t)-1)
{
cgi->init_failed = 1;
}
else if (!(arg->req->flags & QSE_HTRE_COMPLETED))
{
/* TODO: callback chain instead of a single pointer???
if the request is already set up with a callback, something will go wrong.
*/
/* set up a callback to be called when the request content
* is fed to the htrd reader */
cgi->req = (qse_htre_t*)arg->req;
qse_htre_setconcb (cgi->req, cgi_snatch_content, cgi);
}
}
if (!cgi->init_failed)
{
cgi->env = makecgienv (httpd, client, arg->req, arg->path);
if (cgi->env == QSE_NULL) cgi->init_failed = 1;
}
task->ctx = cgi;
return 0;
}
@ -1497,6 +1639,15 @@ static void task_fini_cgi (
}
if (cgi->res) qse_mbs_close (cgi->res);
if (cgi->htrd) qse_htrd_close (cgi->htrd);
if (cgi->reqcon) qse_mbs_close (cgi->reqcon);
if (cgi->req)
{
/* this task is destroyed but the request
* associated is still alive. so clear the
* callback to prevent the callback call. */
qse_htre_unsetconcb (cgi->req);
}
qse_printf (QSE_T("task_fini_cgi\n"));
}
@ -1508,10 +1659,18 @@ static int task_main_cgi_5 (
QSE_ASSERT (cgi->pio != QSE_NULL);
qse_printf (QSE_T("task_main_cgi_5\n"));
if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE)
{
if (cgi_forward_content (task) <= -1) return -1;
/* if forwarding didn't finish, something is not really right...
* so long as the output from CGI is finished, no more forwarding
* is performed */
}
qse_printf (QSE_T("task_main_cgi_5\n"));
/* TODO: check if cgi outputs more than content-length if it is set... */
n = send (client->handle.i, cgi->buf, cgi->buflen, 0);
httpd->errnum = QSE_HTTPD_ENOERR;
n = httpd->cbs->client.send (httpd, client, cgi->buf, cgi->buflen);
if (n <= -1)
{
/* can't return internal server error any more... */
@ -1533,6 +1692,13 @@ static int task_main_cgi_4 (
QSE_ASSERT (cgi->pio != QSE_NULL);
if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE)
{
if (cgi_forward_content (task) <= -1) return -1;
}
if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_READABLE)
{
/* this function assumes that the chunk length does not exceeded
* 4 hexadecimal digits. */
QSE_ASSERT (QSE_SIZEOF(cgi->buf) <= 0xFFFF);
@ -1566,6 +1732,11 @@ qse_printf (QSE_T("READING CHUNKED MODE...\n"));
}
if (n == 0)
{
/* the cgi script closed the output */
task->trigger_mask &=
~(QSE_HTTPD_TASK_TRIGGER_READ |
QSE_HTTPD_TASK_TRIGGER_READABLE);
cgi->buf[cgi->buflen++] = QSE_MT('0');
cgi->buf[cgi->buflen++] = QSE_MT('\r');
cgi->buf[cgi->buflen++] = QSE_MT('\n');
@ -1606,6 +1777,9 @@ qse_printf (QSE_T("READING IN NON-CHUNKED MODE...\n"));
}
if (n == 0)
{
task->trigger_mask &=
~(QSE_HTTPD_TASK_TRIGGER_READ |
QSE_HTTPD_TASK_TRIGGER_READABLE);
task->main = task_main_cgi_5;
return task_main_cgi_5 (httpd, client, task);
}
@ -1623,9 +1797,10 @@ qse_printf (QSE_T("CGI FUCKED UP...RETURNING TOO MUCH DATA\n"));
}
#if 0
qse_printf (QSE_T("CGI SEND [%.*hs]\n"), (int)cgi->buflen, cgi->buf);
qse_printf (QSE_T("CGI_4 SEND [%.*hs]\n"), (int)cgi->buflen, cgi->buf);
#endif
n = send (client->handle.i, cgi->buf, cgi->buflen, 0);
httpd->errnum = QSE_HTTPD_ENOERR;
n = httpd->cbs->client.send (httpd, client, cgi->buf, cgi->buflen);
if (n <= -1)
{
/* can't return internal server error any more... */
@ -1639,6 +1814,8 @@ qse_printf (QSE_T("CGI SEND [%.*hs]\n"), (int)cgi->buflen, cgi->buf);
#if 0
qse_printf (QSE_T("CGI SEND DONE\n"));
#endif
}
return 1;
}
@ -1652,16 +1829,19 @@ static int task_main_cgi_3 (
qse_ssize_t n;
qse_size_t count;
qse_printf (QSE_T("cgi_3\n"));
if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE)
{
if (cgi_forward_content (task) <= -1) return -1;
}
if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_READABLE)
{
count = MAX_SEND_SIZE;
if (count >= cgi->res_left) count = cgi->res_left;
n = send (
client->handle.i,
cgi->res_ptr,
count,
0
);
qse_printf (QSE_T("[cgi_3 sending %d bytes]\n"), (int)count);
httpd->errnum = QSE_HTTPD_ENOERR;
n = httpd->cbs->client.send (httpd, client, cgi->res_ptr, count);
if (n <= -1) return -1;
@ -1669,10 +1849,16 @@ qse_printf (QSE_T("cgi_3\n"));
if (cgi->res_left <= 0)
{
task->main = task_main_cgi_4;
return task_main_cgi_4 (httpd, client, task);
/* don't chain-call task_main_cgi_4 since it has another send
* and it has already been sent here. so the writability must
* be checked again in the main loop.
* => return task_main_cgi_4 (httpd, client, task);*/
return 1;
}
cgi->res_ptr += n;
}
return 1; /* more work to do */
}
@ -1691,6 +1877,15 @@ static int task_main_cgi_2 (
QSE_ASSERT (cgi->pio != QSE_NULL);
qse_printf (QSE_T("[cgi_2 ]\n"));
if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE)
{
qse_printf (QSE_T("[cgi_2 write]\n"));
if (cgi_forward_content (task) <= -1) return -1;
}
if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_READABLE)
{
qse_printf (QSE_T("[cgi_2 read]\n"));
/* <- can i make it non-block?? or use select??? pio_tryread()? */
n = qse_pio_read (
cgi->pio, QSE_PIO_OUT,
@ -1708,6 +1903,9 @@ qse_printf (QSE_T("[cgi_2 ]\n"));
/* end of output from cgi before it has seen a header.
* the cgi script must be crooked. */
/* TODO: logging */
task->trigger_mask &=
~(QSE_HTTPD_TASK_TRIGGER_READ |
QSE_HTTPD_TASK_TRIGGER_READABLE);
return -1;
}
@ -1726,6 +1924,11 @@ qse_printf (QSE_T("[cgi_2 ]\n"));
/* the htrd handler composed some response.
* this means that at least it finished processing CGI headers.
* some contents could be in cgi->res, though.
*
* qse_htrd_feed() must have executed the peek handler
* (cgi_htrd_peek_request()) which handled the request header.
* so i won't call qse_htrd_feed() any more. intead, i'll
* simply read directly from the pipe.
*/
if (cgi->disconnect &&
@ -1734,10 +1937,13 @@ qse_printf (QSE_T("[cgi_2 ]\n"));
cgi->res_ptr = QSE_MBS_PTR(cgi->res);
cgi->res_left = QSE_MBS_LEN(cgi->res);
qse_printf (QSE_T("TRAILING DATA=[%.*hs]\n"), (int)QSE_MBS_LEN(cgi->res), QSE_MBS_PTR(cgi->res));
task->main = task_main_cgi_3;
return task_main_cgi_3 (httpd, client, task);
}
}
/* complete headers not seen yet. i need to be called again */
return 1;
}
@ -1776,7 +1982,7 @@ static int task_main_cgi (
qse_htrd_setoption (
cgi->htrd,
QSE_HTRD_SKIPINITIALLINE |
QSE_HTRD_HURRIED |
QSE_HTRD_PEEKONLY |
QSE_HTRD_REQUEST
);
@ -1795,21 +2001,24 @@ static int task_main_cgi (
);
if (cgi->pio == QSE_NULL) goto oops;
/* TODO: use a different field for different OS???
HANDLE for win32???
*/
/* set the trigger that the main loop can use this
* handle for multiplexing */
task->trigger.i = qse_pio_gethandle (cgi->pio, QSE_PIO_OUT);
task->trigger_mask = QSE_HTTPD_TASK_TRIGGER_READ;
task->trigger[0].i = qse_pio_gethandle (cgi->pio, QSE_PIO_OUT);
if (cgi->reqcon)
{
task->trigger_mask |= QSE_HTTPD_TASK_TRIGGER_WRITE;
task->trigger[1].i = qse_pio_gethandle (cgi->pio, QSE_PIO_IN);
}
if (cgi->nph)
{
/* skip various header processing */
task->main = task_main_cgi_4;
return task_main_cgi_4 (httpd, client, task);
}
else
{
task->main = task_main_cgi_2; /* cause this function to be called subsequently */
return task_main_cgi_2 (httpd, client, task); /* let me call it here once */
}
task->main = cgi->nph? task_main_cgi_4: task_main_cgi_2;
/* no chain call since readability and writability needs
* to be checked in the main loop
return task->main (httpd, client, task); */
return 1;
oops:
if (cgi->res)
@ -1849,7 +2058,6 @@ qse_httpd_task_t* qse_httpd_entaskcgi (
task.fini = task_fini_cgi;
task.main = task_main_cgi;
task.ctx = &arg;
task.trigger.i = -1;
return qse_httpd_entask (
httpd, client, pred, &task,
@ -1876,7 +2084,6 @@ qse_httpd_task_t* qse_httpd_entasknph (
task.fini = task_fini_cgi;
task.main = task_main_cgi;
task.ctx = &arg;
task.trigger.i = -1;
return qse_httpd_entask (
httpd, client, pred, &task,

View File

@ -106,11 +106,11 @@ static int test3 (void)
env = qse_env_open (QSE_MMGR_GETDFL(), 0, 0);
qse_printf (QSE_T("inserting PATH => %d\n"), qse_env_insertsys (env, QSE_T("PATH")));
qse_printf (QSE_T("inserting HOME => %d\n"), qse_env_insertsysm (env, QSE_MT("HOME")));
qse_printf (QSE_T("inserting USER => %d\n"), qse_env_insertsysw (env, QSE_WT("USER")));
qse_printf (QSE_T("inserting WHAT => %d\n"), qse_env_insertsys (env, QSE_T("WHAT")));
qse_printf (QSE_T("inserting an empty string => %d\n"), qse_env_insertsys (env, QSE_T("")));
qse_printf (QSE_T("inserting PATH => %d\n"), qse_env_insert (env, QSE_T("PATH"), QSE_NULL));
qse_printf (QSE_T("inserting HOME => %d\n"), qse_env_insertmbs (env, QSE_MT("HOME"), QSE_NULL));
qse_printf (QSE_T("inserting USER => %d\n"), qse_env_insertwcs (env, QSE_WT("USER"), QSE_NULL));
qse_printf (QSE_T("inserting WHAT => %d\n"), qse_env_insert (env, QSE_T("WHAT"), QSE_NULL));
qse_printf (QSE_T("inserting an empty string => %d\n"), qse_env_insert (env, QSE_T(""), QSE_NULL));
dump (env);

View File

@ -304,7 +304,7 @@ static int test12 (void)
env = qse_env_open (QSE_MMGR_GETDFL(), 0, 0);
if (env == QSE_NULL) return -1;
qse_env_insertsys (env, QSE_T("PATH"));
qse_env_insert (env, QSE_T("PATH"), QSE_NULL);
qse_env_insert (env, QSE_T("HELLO"), QSE_T("WORLD"));
n = pio1 (

View File

@ -8,7 +8,7 @@ AM_CPPFLAGS = \
bin_PROGRAMS = http01
LDFLAGS += -L../../lib/cmn -L../../lib/net
LDADD = -lqsenet -lqsecmn $(PTHREAD_LIBS) $(SOCKET_LIBS) $(SENDFILE_LIBS)
LDADD = -lqsenet -lqsecmn $(PTHREAD_LIBS) $(SOCKET_LIBS) $(SENDFILE_LIBS) -lssl
http01_SOURCES = http01.c

View File

@ -226,7 +226,7 @@ AM_CPPFLAGS = \
-I$(top_srcdir)/include \
-I$(includedir)
LDADD = -lqsenet -lqsecmn $(PTHREAD_LIBS) $(SOCKET_LIBS) $(SENDFILE_LIBS)
LDADD = -lqsenet -lqsecmn $(PTHREAD_LIBS) $(SOCKET_LIBS) $(SENDFILE_LIBS) -lssl
http01_SOURCES = http01.c
all: all-am

View File

@ -12,52 +12,347 @@
# include <windows.h>
#endif
#define MAX_SENDFILE_SIZE 4096
#include <openssl/ssl.h>
// TODO: remove this and export structured needed like qse_httpd_client_t
#include "../../lib/net/httpd.h"
/* ------------------------------------------------------------------- */
#define MAX_SEND_SIZE 4096
#if defined(HAVE_SYS_SENDFILE_H)
# include <sys/sendfile.h>
#endif
#include <unistd.h>
#if defined(HAVE_SENDFILE) && defined(HAVE_SENDFILE64)
# if !defined(_LP64) && (QSE_SIZEOF_VOID_P<8) && defined(HAVE_SENDFILE64)
# define xsendfile sendfile64
# else
# define xsendfile sendfile
# endif
#elif defined(HAVE_SENDFILE)
# define xsendfile sendfile
#elif defined(HAVE_SENDFILE64)
# define xsendfile sendfile64
#elif defined(HAVE_SENDFILEV) || defined(HAVE_SENDFILEV64)
static qse_ssize_t xsendfile (
int out_fd, int in_fd, qse_foff_t* offset, qse_size_t count)
{
#if !defined(_LP64) && (QSE_SIZEOF_VOID_P<8) && defined(HAVE_SENDFILE64)
struct sendfilevec64 vec;
#else
struct sendfilevec vec;
#endif
size_t xfer;
ssize_t n;
vec.sfv_fd = in_fd;
vec.sfv_flag = 0;
if (offset)
{
vec.sfv_off = *offset;
}
else
{
vec.sfv_off = lseek (in_fd, 0, SEEK_CUR); /* TODO: lseek64 or llseek.. */
if (vec.sfv_off == (off_t)-1) return (qse_ssize_t)-1;
}
vec.sfv_len = count;
#if !defined(_LP64) && (QSE_SIZEOF_VOID_P<8) && defined(HAVE_SENDFILE64)
n = sendfilev64 (out_fd, &vec, 1, &xfer);
#else
n = sendfilev (out_fd, &vec, 1, &xfer);
#endif
if (offset) *offset = *offset + xfer;
/* TODO: xfer contains number of byte written even on failure
on success xfer == n.
on failure xfer != n.
*/
return n;
}
#else
static qse_ssize_t xsendfile (
int out_fd, int in_fd, qse_foff_t* offset, qse_size_t count)
{
qse_mchar_t buf[MAX_SEND_SIZE];
qse_ssize_t n;
if (offset && lseek (in_fd, *offset, SEEK_SET) != *offset) //* 64bit version of lseek...
return (qse_ssize_t)-1;
if (count > QSE_COUNTOF(buf)) count = QSE_COUNTOF(buf);
n = read (in_fd, buf, count);
if (n == (qse_ssize_t)-1 || n == 0) return n;
n = send (out_fd, buf, n, 0);
if (n > 0 && offset) *offset = *offset + n;
return n;
}
#endif
static qse_ssize_t xsendfile_ssl (
SSL* out, int in_fd, qse_foff_t* offset, qse_size_t count)
{
qse_mchar_t buf[MAX_SEND_SIZE];
qse_ssize_t n;
if (offset && lseek (in_fd, *offset, SEEK_SET) != *offset) //* 64bit version of lseek...
return (qse_ssize_t)-1;
if (count > QSE_COUNTOF(buf)) count = QSE_COUNTOF(buf);
n = read (in_fd, buf, count);
if (n == (qse_ssize_t)-1 || n == 0) return n;
n = SSL_write (out, buf, count);
if (n > 0 && offset) *offset = *offset + n;
return n;
}
/* ------------------------------------------------------------------- */
typedef struct httpd_xtn_t httpd_xtn_t;
struct httpd_xtn_t
{
const qse_httpd_cbs_t* orgcbs;
SSL_CTX* ssl_ctx;
};
/* ------------------------------------------------------------------- */
static int init_xtn_ssl (
httpd_xtn_t* xtn,
const qse_mchar_t* pemfile,
const qse_mchar_t* keyfile/*,
const qse_mchar_t* chainfile*/)
{
SSL_CTX* ctx;
SSL_library_init ();
SSL_load_error_strings ();
/*SSLeay_add_ssl_algorithms();*/
ctx = SSL_CTX_new (SSLv23_server_method());
if (ctx == QSE_NULL) return -1;
/*SSL_CTX_set_info_callback(ctx,ssl_info_callback);*/
if (SSL_CTX_use_certificate_file (ctx, pemfile, SSL_FILETYPE_PEM) == 0 ||
SSL_CTX_use_PrivateKey_file (ctx, keyfile, SSL_FILETYPE_PEM) == 0 ||
SSL_CTX_check_private_key (ctx) == 0 /*||
SSL_CTX_use_certificate_chain_file (ctx, chainfile) == 0*/)
{
qse_mchar_t buf[128];
ERR_error_string_n(ERR_get_error(), buf, QSE_COUNTOF(buf));
qse_fprintf (QSE_STDERR, QSE_T("Error: %hs\n"), buf);
SSL_CTX_free (ctx);
return -1;
}
// TODO: CRYPTO_set_id_callback ();
// TODO: CRYPTO_set_locking_callback ();
xtn->ssl_ctx = ctx;
return 0;
}
static void fini_xtn_ssl (httpd_xtn_t* xtn)
{
// TODO: CRYPTO_set_id_callback (QSE_NULL);
// TODO: CRYPTO_set_locking_callback (QSE_NULL);
SSL_CTX_free (xtn->ssl_ctx);
// ERR_remove_state ();
ENGINE_cleanup ();
ERR_free_strings ();
EVP_cleanup ();
CRYPTO_cleanup_all_ex_data ();
}
/* ------------------------------------------------------------------- */
static qse_ssize_t client_recv (
qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_mchar_t* buf, qse_size_t bufsize)
{
if (client->secure)
{
return SSL_read (client->handle2.ptr, buf, bufsize);
}
else
{
return read (client->handle.i, buf, bufsize);
}
}
static qse_ssize_t client_send (
qse_httpd_t* httpd, qse_httpd_client_t* client,
const qse_mchar_t* buf, qse_size_t bufsize)
{
if (client->secure)
{
return SSL_write (client->handle2.ptr, buf, bufsize);
}
else
{
return write (client->handle.i, buf, bufsize);
}
}
static qse_ssize_t client_sendfile (
qse_httpd_t* httpd, qse_httpd_client_t* client,
qse_ubi_t handle, qse_foff_t* offset, qse_size_t count)
{
if (client->secure)
{
return xsendfile_ssl (client->handle2.ptr, handle.i, offset, count);
}
else
{
return xsendfile (client->handle.i, handle.i, offset, count);
}
}
static int client_accepted (qse_httpd_t* httpd, qse_httpd_client_t* client)
{
httpd_xtn_t* xtn = (httpd_xtn_t*) qse_httpd_getxtn (httpd);
if (client->secure)
{
int ret;
SSL* ssl;
if (client->handle2.ptr)
{
ssl = client->handle2.ptr;
}
else
{
ssl = SSL_new (xtn->ssl_ctx);
if (ssl == QSE_NULL) return -1;
client->handle2.ptr = ssl;
qse_printf (QSE_T("SSL ACCEPTING %d\n"), client->handle.i);
qse_fflush (QSE_STDOUT);
if (SSL_set_fd (ssl, client->handle.i) == 0)
{
/* don't free ssl here since client_closed()
* will be closed */
return -1;
}
}
ret = SSL_accept (ssl);
if (ret <= 0)
{
if (SSL_get_error(ssl,ret) == SSL_ERROR_WANT_READ)
{
/* handshaking isn't complete. */
return 0;
}
qse_fprintf (QSE_STDERR, QSE_T("Error: SSL ACCEPT ERROR\n"));
/* SSL_free (ssl); */
return -1;
}
}
return 1; /* accept completed */
}
static void client_closed (qse_httpd_t* httpd, qse_httpd_client_t* client)
{
if (client->secure)
{
if (client->handle2.ptr)
{
SSL_shutdown ((SSL*)client->handle2.ptr); /* is this needed? */
SSL_free ((SSL*)client->handle2.ptr);
}
}
}
/* ------------------------------------------------------------------- */
static qse_htb_walk_t walk (qse_htb_t* htb, qse_htb_pair_t* pair, void* ctx)
{
qse_printf (QSE_T("HEADER OK %d[%hs] %d[%hs]\n"), (int)QSE_HTB_KLEN(pair), QSE_HTB_KPTR(pair), (int)QSE_HTB_VLEN(pair), QSE_HTB_VPTR(pair));
return QSE_HTB_WALK_FORWARD;
}
static int handle_request (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req)
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* x;
qse_httpd_task_t* task;
int content_received;
#if 0
httpd_xtn_t* xtn = (httpd_xtn_t*) qse_httpd_getxtn (httpd);
return xtn->orgcbs->handle_request (httpd, client, req);
#endif
method = qse_htre_getqmethod(req);
content_received = (qse_htre_getcontentlen(req) > 0);
qse_printf (QSE_T("================================\n"));
qse_printf (QSE_T("[%lu] REQUEST ==> [%hs] version[%d.%d] method[%d]\n"),
qse_printf (QSE_T("[%lu] %hs REQUEST ==> [%hs] version[%d.%d] method[%d]\n"),
(unsigned long)time(NULL),
(peek? QSE_MT("PEEK"): QSE_MT("HANDLE")),
qse_htre_getqpathptr(req),
qse_htre_getmajorversion(req),
qse_htre_getminorversion(req),
qse_htre_getqmethod(req)
method
);
if (qse_htre_getqparamlen(req) > 0)
{
qse_printf (QSE_T("PARAMS ==> [%hs]\n"), qse_htre_getqparamptr(req));
}
if (qse_htre_getqparamlen(req) > 0) qse_printf (QSE_T("PARAMS ==> [%hs]\n"), qse_htre_getqparamptr(req));
qse_htb_walk (&req->hdrtab, walk, QSE_NULL);
if (QSE_MBS_LEN(&req->content) > 0)
if (qse_htre_getcontentlen(req) > 0)
{
qse_printf (QSE_T("content = [%.*S]\n"),
(int)QSE_MBS_LEN(&req->content),
QSE_MBS_PTR(&req->content));
qse_printf (QSE_T("CONTENT before discard = [%.*S]\n"), (int)qse_htre_getcontentlen(req), qse_htre_getcontentptr(req));
}
method = qse_htre_getqmethod (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.expect &&
(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 */
if (qse_mbscasecmp(req->attr.expect, QSE_MT("100-continue")) != 0)
{
if (qse_httpd_entaskerror (
httpd, client, QSE_NULL, 417, req) == QSE_NULL) return -1;
if (qse_httpd_entaskdisconnect (
httpd, client, QSE_NULL) == QSE_NULL) return -1;
}
else
{
/* TODO: determine if to return 100-continue or other errors */
if (qse_httpd_entaskcontinue (
httpd, client, QSE_NULL, req) == QSE_NULL) return -1;
}
}
}
if (qse_htre_getcontentlen(req) > 0)
{
qse_printf (QSE_T("CONTENT after discard = [%.*S]\n"), (int)qse_htre_getcontentlen(req), qse_htre_getcontentptr(req));
}
if (method == QSE_HTTP_GET || method == QSE_HTTP_POST)
{
@ -65,51 +360,73 @@ qse_printf (QSE_T("content = [%.*S]\n"),
const qse_mchar_t* dot = qse_mbsrchr (qpath, QSE_MT('.'));
if (dot && qse_mbscmp (dot, QSE_MT(".cgi")) == 0)
{
if (peek)
{
/* cgi */
x = qse_httpd_entaskcgi (
task = qse_httpd_entaskcgi (
httpd, client, QSE_NULL, qpath, req);
if (x == QSE_NULL) goto oops;
if (task == QSE_NULL) goto oops;
}
return 0;
}
else if (dot && qse_mbscmp (dot, QSE_MT(".nph")) == 0)
{
if (peek)
{
/* nph-cgi */
x = qse_httpd_entasknph (
task = qse_httpd_entasknph (
httpd, client, QSE_NULL, qpath, req);
if (x == QSE_NULL) goto oops;
if (task == QSE_NULL) goto oops;
}
return 0;
}
else
{
if (!peek)
{
/* file or directory */
x = qse_httpd_entaskpath (
task = qse_httpd_entaskpath (
httpd, client, QSE_NULL, qpath, req);
if (x == QSE_NULL) goto oops;
if (task == QSE_NULL) goto oops;
}
}
}
else
{
x = qse_httpd_entaskerror (httpd, client, QSE_NULL, 405, req);
if (x == QSE_NULL) goto oops;
if (!peek)
{
task = qse_httpd_entaskerror (httpd, client, QSE_NULL, 405, req);
if (task == QSE_NULL) goto oops;
}
}
if (!req->attr.keepalive)
{
x = qse_httpd_entaskdisconnect (httpd, client, QSE_NULL);
if (x == QSE_NULL) goto oops;
if (!peek)
{
task = qse_httpd_entaskdisconnect (httpd, client, QSE_NULL);
if (task == QSE_NULL) goto oops;
}
}
return 0;
oops:
/*qse_httpd_markclientbad (httpd, client);*/
return 0;
/*qse_httpd_markbadclient (httpd, client);*/
return 0; /* TODO: return failure??? */
}
static int handle_expect_continue (
static int peek_request (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req)
{
httpd_xtn_t* xtn = (httpd_xtn_t*) qse_httpd_getxtn (httpd);
return xtn->orgcbs->handle_expect_continue (httpd, client, 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);
}
const qse_mchar_t* get_mime_type (qse_httpd_t* httpd, const qse_mchar_t* path)
@ -127,70 +444,82 @@ int list_directory (qse_httpd_t* httpd, const qse_mchar_t* path)
return 404;
}
static qse_httpd_t* httpd = NULL;
static void sigint (int sig)
{
qse_httpd_stop (httpd);
}
static qse_httpd_cbs_t httpd_cbs =
{
/* client connection */
{ client_recv, client_send, client_sendfile,
client_accepted, client_closed },
/* http request */
peek_request,
handle_request,
handle_expect_continue,
get_mime_type,
list_directory
};
static qse_httpd_t* g_httpd = QSE_NULL;
static void sigint (int sig)
{
if (g_httpd) qse_httpd_stop (g_httpd);
}
int httpd_main (int argc, qse_char_t* argv[])
{
int n;
qse_httpd_t* httpd = QSE_NULL;
httpd_xtn_t* xtn;
int ret = -1, i;
int ssl_xtn_inited = 0;
if (argc <= 1)
{
qse_fprintf (QSE_STDERR, QSE_T("Usage: %s <listener_uri> ...\n"), argv[0]);
return -1;
goto oops;
}
httpd = qse_httpd_open (QSE_MMGR_GETDFL(), QSE_SIZEOF(httpd_xtn_t));
if (httpd == QSE_NULL)
{
qse_fprintf (QSE_STDERR, QSE_T("Cannot open httpd\n"));
return -1;
goto oops;
}
xtn = (httpd_xtn_t*)qse_httpd_getxtn (httpd);
xtn->orgcbs = qse_httpd_getcbs (httpd);
for (n = 1; n < argc; n++)
if (init_xtn_ssl (xtn, "http01.pem", "http01.key") <= -1)
{
if (qse_httpd_addlistener (httpd, argv[n]) <= -1)
qse_fprintf (QSE_STDERR, QSE_T("Cannot open httpd\n"));
goto oops;
}
ssl_xtn_inited = 1;
for (i = 1; i < argc; i++)
{
if (qse_httpd_addlistener (httpd, argv[i]) <= -1)
{
qse_fprintf (QSE_STDERR,
QSE_T("Failed to add httpd listener - %s\n"), argv[n]);
qse_httpd_close (httpd);
return -1;
QSE_T("Failed to add httpd listener - %s\n"), argv[i]);
goto oops;
}
}
qse_httpd_setcbs (httpd, &httpd_cbs);
g_httpd = httpd;
signal (SIGINT, sigint);
signal (SIGPIPE, SIG_IGN);
n = qse_httpd_loop (httpd, 1);
ret = qse_httpd_loop (httpd, &httpd_cbs, 0);
signal (SIGINT, SIG_DFL);
signal (SIGPIPE, SIG_DFL);
g_httpd = QSE_NULL;
if (n <= -1)
{
qse_fprintf (QSE_STDERR, QSE_T("Httpd error\n"));
}
if (ret <= -1) qse_fprintf (QSE_STDERR, QSE_T("Httpd error\n"));
qse_httpd_close (httpd);
return n;
oops:
if (ssl_xtn_inited) fini_xtn_ssl (xtn);
if (httpd) qse_httpd_close (httpd);
return ret;
}
int qse_main (int argc, qse_achar_t* argv[])