enhanced httpd
This commit is contained in:
parent
17d05bb1e0
commit
517107ab79
@ -106,46 +106,41 @@ qse_env_char_t** qse_env_getarr (
|
|||||||
qse_env_t* env
|
qse_env_t* env
|
||||||
);
|
);
|
||||||
|
|
||||||
int qse_env_insertw (
|
/**
|
||||||
qse_env_t* env,
|
* 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* name,
|
||||||
const qse_wchar_t* value
|
const qse_wchar_t* value
|
||||||
);
|
);
|
||||||
|
|
||||||
int qse_env_insertm (
|
int qse_env_insertmbs (
|
||||||
qse_env_t* env,
|
qse_env_t* env,
|
||||||
const qse_mchar_t* name,
|
const qse_mchar_t* name,
|
||||||
const qse_mchar_t* value
|
const qse_mchar_t* value
|
||||||
);
|
);
|
||||||
|
|
||||||
int qse_env_deletew (
|
int qse_env_deletewcs (
|
||||||
qse_env_t* env,
|
qse_env_t* env,
|
||||||
const qse_wchar_t* name
|
const qse_wchar_t* name
|
||||||
);
|
);
|
||||||
|
|
||||||
int qse_env_deletem (
|
int qse_env_deletembs (
|
||||||
qse_env_t* env,
|
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 (
|
|
||||||
qse_env_t* env,
|
|
||||||
const qse_mchar_t* name
|
const qse_mchar_t* name
|
||||||
);
|
);
|
||||||
|
|
||||||
#if defined(QSE_CHAR_IS_WCHAR)
|
#if defined(QSE_CHAR_IS_WCHAR)
|
||||||
# define qse_env_insert(env,name,value) qse_env_insertw(env,name,value)
|
# define qse_env_insert(env,name,value) qse_env_insertwcs(env,name,value)
|
||||||
# define qse_env_delete(env,name) qse_env_deletew(env,name)
|
# define qse_env_delete(env,name) qse_env_deletewcs(env,name)
|
||||||
# define qse_env_insertsys(env,name) qse_env_insertsysw(env,name)
|
|
||||||
#else
|
#else
|
||||||
# define qse_env_insert(env,name,value) qse_env_insertm(env,name,value)
|
# define qse_env_insert(env,name,value) qse_env_insertmbs(env,name,value)
|
||||||
# define qse_env_delete(env,name) qse_env_deletem(env,name)
|
# define qse_env_delete(env,name) qse_env_deletembs(env,name)
|
||||||
# define qse_env_insertsys(env,name) qse_env_insertsysm(env,name)
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
@ -32,7 +32,8 @@ enum qse_htrd_errnum_t
|
|||||||
QSE_HTRD_ENOMEM,
|
QSE_HTRD_ENOMEM,
|
||||||
QSE_HTRD_EBADRE,
|
QSE_HTRD_EBADRE,
|
||||||
QSE_HTRD_EBADHDR,
|
QSE_HTRD_EBADHDR,
|
||||||
QSE_HTRD_ERECBS
|
QSE_HTRD_ERECBS,
|
||||||
|
QSE_HTRD_ECONCB
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef enum qse_htrd_errnum_t qse_htrd_errnum_t;
|
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_SKIPEMPTYLINES = (1 << 0), /**< skip leading empty lines before the initial line */
|
||||||
QSE_HTRD_SKIPINITIALLINE = (1 << 1), /**< skip processing an 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_REQUEST = (1 << 3), /**< parse input as a request */
|
||||||
QSE_HTRD_RESPONSE = (1 << 4) /**< parse input as a response */
|
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
|
struct qse_htrd_recbs_t
|
||||||
{
|
{
|
||||||
int (*request) (qse_htrd_t* htrd, qse_htre_t* req);
|
int (*peek) (qse_htrd_t* htrd, qse_htre_t* re);
|
||||||
int (*expect_continue) (qse_htrd_t* htrd, qse_htre_t* req);
|
int (*handle) (qse_htrd_t* htrd, qse_htre_t* re);
|
||||||
int (*response) (qse_htrd_t* htrd, qse_htre_t* res);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct qse_htrd_t
|
struct qse_htrd_t
|
||||||
|
@ -27,6 +27,20 @@
|
|||||||
|
|
||||||
/* header and contents of request/response */
|
/* header and contents of request/response */
|
||||||
typedef struct qse_htre_t qse_htre_t;
|
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
|
struct qse_htre_t
|
||||||
{
|
{
|
||||||
qse_mmgr_t* mmgr;
|
qse_mmgr_t* mmgr;
|
||||||
@ -45,10 +59,7 @@ struct qse_htre_t
|
|||||||
int content_length_set;
|
int content_length_set;
|
||||||
qse_size_t content_length;
|
qse_size_t content_length;
|
||||||
int keepalive;
|
int keepalive;
|
||||||
int expect_continue;
|
const qse_mchar_t* expect;
|
||||||
|
|
||||||
/* indicates if the content has been filled */
|
|
||||||
int hurried;
|
|
||||||
} attr;
|
} attr;
|
||||||
|
|
||||||
/* header table */
|
/* header table */
|
||||||
@ -57,8 +68,12 @@ struct qse_htre_t
|
|||||||
/* content octets */
|
/* content octets */
|
||||||
qse_mbs_t content;
|
qse_mbs_t content;
|
||||||
|
|
||||||
/* if set, the rest of the contents are discarded */
|
/* content callback */
|
||||||
int discard;
|
qse_htre_concb_t concb;
|
||||||
|
void* concb_ctx;
|
||||||
|
|
||||||
|
/* ORed of qse_htre_flag_t */
|
||||||
|
int flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define qse_htre_getversion(re) (&((re)->version))
|
#define qse_htre_getversion(re) (&((re)->version))
|
||||||
@ -111,13 +126,12 @@ struct qse_htre_t
|
|||||||
#define qse_htre_setsmessagefromxstr(re,v) \
|
#define qse_htre_setsmessagefromxstr(re,v) \
|
||||||
qse_htre_setstrfromxstr((re),qse_htre_getsmessage(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) \
|
#define qse_htre_setcontentfromcstr(re,v) \
|
||||||
qse_htre_setstrfromcstr((re),qse_htre_getcontent(re),(v))
|
qse_htre_setstrfromcstr((re),qse_htre_getcontent(re),(v))
|
||||||
#define qse_htre_setcontentfromxstr(re,v) \
|
#define qse_htre_setcontentfromxstr(re,v) \
|
||||||
qse_htre_setstrfromxstr((re),qse_htre_getcontent(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) (
|
typedef int (*qse_htre_header_walker_t) (
|
||||||
qse_htre_t* re,
|
qse_htre_t* re,
|
||||||
const qse_mchar_t* key,
|
const qse_mchar_t* key,
|
||||||
@ -165,6 +179,26 @@ int qse_htre_walkheaders (
|
|||||||
void* ctx
|
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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -51,9 +51,33 @@ enum qse_httpd_option_t
|
|||||||
typedef struct qse_httpd_cbs_t qse_httpd_cbs_t;
|
typedef struct qse_httpd_cbs_t qse_httpd_cbs_t;
|
||||||
struct qse_httpd_cbs_t
|
struct qse_httpd_cbs_t
|
||||||
{
|
{
|
||||||
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);
|
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);
|
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);
|
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
|
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
|
struct qse_httpd_task_t
|
||||||
{
|
{
|
||||||
/* you must not call another entask functions from within
|
/* 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_fini_t fini;
|
||||||
qse_httpd_task_main_t main;
|
qse_httpd_task_main_t main;
|
||||||
|
|
||||||
qse_ubi_t trigger;
|
int trigger_mask;
|
||||||
|
qse_ubi_t trigger[2];
|
||||||
|
|
||||||
void* ctx;
|
void* ctx;
|
||||||
};
|
};
|
||||||
@ -143,8 +177,9 @@ void qse_httpd_setcbs (
|
|||||||
* specify the number of output threads.
|
* specify the number of output threads.
|
||||||
*/
|
*/
|
||||||
int qse_httpd_loop (
|
int qse_httpd_loop (
|
||||||
qse_httpd_t* httpd,
|
qse_httpd_t* httpd,
|
||||||
int threaded
|
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_t* httpd,
|
||||||
qse_httpd_client_t* client
|
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))
|
#define qse_httpd_gettaskxtn(httpd,task) ((void*)(task+1))
|
||||||
|
|
||||||
qse_httpd_task_t* qse_httpd_entask (
|
qse_httpd_task_t* qse_httpd_entask (
|
||||||
@ -235,6 +275,13 @@ qse_httpd_task_t* qse_httpd_entaskerror (
|
|||||||
const qse_htre_t* req
|
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_task_t* qse_httpd_entaskpath (
|
||||||
qse_httpd_t* httpd,
|
qse_httpd_t* httpd,
|
||||||
qse_httpd_client_t* client,
|
qse_httpd_client_t* client,
|
||||||
|
@ -34,6 +34,8 @@
|
|||||||
QSE_IMPLEMENT_COMMON_FUNCTIONS(env)
|
QSE_IMPLEMENT_COMMON_FUNCTIONS(env)
|
||||||
|
|
||||||
static int load_curenv (qse_env_t* 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)
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#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)
|
qse_env_t* env, const qse_wchar_t* name, const qse_wchar_t* value)
|
||||||
{
|
{
|
||||||
#if defined(QSE_ENV_CHAR_IS_WCHAR)
|
#if defined(QSE_ENV_CHAR_IS_WCHAR)
|
||||||
@ -328,9 +329,9 @@ int qse_env_insertw (
|
|||||||
qse_mchar_t* namedup, * valuedup;
|
qse_mchar_t* namedup, * valuedup;
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
namedup = qse_wcstombsdup (name, env->mmgr);
|
namedup = qse_wcstombsdup (name, env->mmgr); /* TODO: ignore mbwcerr */
|
||||||
if (namedup == QSE_NULL) return -1;
|
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)
|
if (valuedup == QSE_NULL)
|
||||||
{
|
{
|
||||||
QSE_MMGR_FREE (env->mmgr, namedup);
|
QSE_MMGR_FREE (env->mmgr, namedup);
|
||||||
@ -344,7 +345,7 @@ int qse_env_insertw (
|
|||||||
#endif
|
#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)
|
qse_env_t* env, const qse_mchar_t* name, const qse_mchar_t* value)
|
||||||
{
|
{
|
||||||
#if defined(QSE_ENV_CHAR_IS_WCHAR)
|
#if defined(QSE_ENV_CHAR_IS_WCHAR)
|
||||||
@ -352,9 +353,9 @@ int qse_env_insertm (
|
|||||||
qse_wchar_t* namedup, * valuedup;
|
qse_wchar_t* namedup, * valuedup;
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
namedup = qse_mbstowcsdup (name, env->mmgr); /* TODO: ignroe mbwcerr */
|
namedup = qse_mbstowcsalldup (name, env->mmgr);
|
||||||
if (namedup == QSE_NULL) return -1;
|
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)
|
if (valuedup == QSE_NULL)
|
||||||
{
|
{
|
||||||
QSE_MMGR_FREE (env->mmgr, namedup);
|
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)
|
#if defined(_WIN32)
|
||||||
static qse_char_t* get_env (qse_env_t* env, const qse_char_t* name, int* free)
|
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
|
#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)
|
#if defined(QSE_ENV_CHAR_IS_WCHAR)
|
||||||
qse_wchar_t* v;
|
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;
|
qse_mchar_t* namedup;
|
||||||
int ret = -1;
|
int ret = -1;
|
||||||
|
|
||||||
namedup = qse_wcstombsdup (name, env->mmgr);
|
namedup = qse_wcstombsdup (name, env->mmgr); /* TODO: ignore mbwcerr */
|
||||||
if (namedup)
|
if (namedup)
|
||||||
{
|
{
|
||||||
ret = qse_env_insertsysm (env, namedup);
|
ret = insert_sys_mbs (env, namedup);
|
||||||
QSE_MMGR_FREE (env->mmgr, 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
|
#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)
|
#if defined(QSE_ENV_CHAR_IS_WCHAR)
|
||||||
/* convert mchar to 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 */
|
namedup = qse_mbstowcsdup (name, env->mmgr); /* TODO: ignroe mbwcerr */
|
||||||
if (namedup)
|
if (namedup)
|
||||||
{
|
{
|
||||||
ret = qse_env_insertsysw (env, namedup);
|
ret = insert_sys_wcs (env, namedup);
|
||||||
QSE_MMGR_FREE (env->mmgr, namedup);
|
QSE_MMGR_FREE (env->mmgr, namedup);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -653,3 +616,56 @@ done:
|
|||||||
#endif
|
#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
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -93,6 +93,17 @@ static QSE_INLINE int push_to_buffer (
|
|||||||
return 0;
|
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
|
||||||
{
|
{
|
||||||
struct hdr_cmb_t* next;
|
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)
|
static int capture_expect (qse_htrd_t* htrd, qse_htb_pair_t* pair)
|
||||||
{
|
{
|
||||||
int n;
|
htrd->re.attr.expect = QSE_HTB_VPTR(pair);
|
||||||
|
|
||||||
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 */
|
|
||||||
return 0;
|
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* end = req + len;
|
||||||
const qse_mchar_t* ptr = req;
|
const qse_mchar_t* ptr = req;
|
||||||
|
int header_completed_during_this_feed = 0;
|
||||||
|
qse_size_t avail;
|
||||||
|
|
||||||
/* does this goto drop code maintainability? */
|
/* does this goto drop code maintainability? */
|
||||||
if (htrd->fed.s.need > 0)
|
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
|
* => 2nd CR before LF
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* we got a complete request. */
|
/* we got a complete request header. */
|
||||||
QSE_ASSERT (htrd->fed.s.crlf <= 3);
|
QSE_ASSERT (htrd->fed.s.crlf <= 3);
|
||||||
|
|
||||||
/* reset the crlf state */
|
/* 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)
|
if (parse_initial_line_and_headers (htrd, req, ptr - req) <= -1)
|
||||||
return -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;
|
/* 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. */
|
||||||
|
|
||||||
/* it pushes any trailing data into the content in this mode.
|
/* i don't really know if it is really completed
|
||||||
* so the handler knows if there is contents fed to this reader. */
|
* with content. QSE_HTRD_PEEKONLY is not compatible
|
||||||
if (push_to_buffer (htrd, &htrd->re.content, ptr, end - ptr) <= -1)
|
* with the completed flag. */
|
||||||
return -1;
|
htrd->re.flags &= QSE_HTRE_COMPLETED;
|
||||||
|
goto feedme_more;
|
||||||
|
|
||||||
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?
|
|
||||||
*/
|
|
||||||
|
|
||||||
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;
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* carry on processing content body fed together with the header */
|
||||||
if (htrd->re.attr.chunked)
|
if (htrd->re.attr.chunked)
|
||||||
{
|
{
|
||||||
/* transfer-encoding: 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
|
/* content-length or chunked data length
|
||||||
* specified */
|
* specified */
|
||||||
|
|
||||||
qse_size_t avail;
|
|
||||||
|
|
||||||
content_resume:
|
content_resume:
|
||||||
avail = end - ptr;
|
avail = end - ptr;
|
||||||
|
|
||||||
if (avail < htrd->fed.s.need)
|
if (avail < htrd->fed.s.need)
|
||||||
{
|
{
|
||||||
/* the data is not as large as needed */
|
/* 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;
|
htrd->fed.s.need -= avail;
|
||||||
/* we didn't get a complete content yet */
|
/* we didn't get a complete content yet */
|
||||||
goto feedme_more;
|
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
|
else
|
||||||
{
|
{
|
||||||
/* we got all or more than needed */
|
/* we got all or more than needed */
|
||||||
if (push_to_buffer (
|
if (!(htrd->re.flags & QSE_HTRE_DISCARDED) &&
|
||||||
htrd, &htrd->re.content, ptr,
|
push_content (htrd, ptr, htrd->fed.s.need) <= -1) return -1;
|
||||||
htrd->fed.s.need) <= -1) return -1;
|
|
||||||
ptr += htrd->fed.s.need;
|
ptr += htrd->fed.s.need;
|
||||||
htrd->fed.s.need = 0;
|
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;
|
int n;
|
||||||
|
htrd->re.flags |= QSE_HTRE_COMPLETED;
|
||||||
htrd->re.attr.hurried = 0;
|
|
||||||
htrd->errnum = QSE_HTRD_ENOERR;
|
htrd->errnum = QSE_HTRD_ENOERR;
|
||||||
|
n = htrd->recbs->peek (htrd, &htrd->re);
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (n <= -1)
|
if (n <= -1)
|
||||||
{
|
{
|
||||||
if (htrd->errnum == QSE_HTRD_ENOERR)
|
if (htrd->errnum == QSE_HTRD_ENOERR)
|
||||||
htrd->errnum = QSE_HTRD_ERECBS;
|
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?
|
/* need to clear request on error?
|
||||||
clear_feed (htrd); */
|
clear_feed (htrd); */
|
||||||
return -1;
|
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);
|
clear_feed (htrd);
|
||||||
|
|
||||||
/* let ptr point to the next character to LF or
|
/* 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:
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -45,6 +45,12 @@ void qse_htre_fini (qse_htre_t* re)
|
|||||||
|
|
||||||
void qse_htre_clear (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->version, 0, QSE_SIZEOF(re->version));
|
||||||
QSE_MEMSET (&re->attr, 0, QSE_SIZEOF(re->attr));
|
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->content);
|
||||||
qse_mbs_clear (&re->qpath_or_smesg);
|
qse_mbs_clear (&re->qpath_or_smesg);
|
||||||
qse_mbs_clear (&re->qparam);
|
qse_mbs_clear (&re->qparam);
|
||||||
|
re->flags = 0;
|
||||||
re->discard = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int qse_htre_setstrfromcstr (
|
int qse_htre_setstrfromcstr (
|
||||||
@ -109,3 +114,29 @@ int qse_htre_walkheaders (
|
|||||||
return hwctx.ret;
|
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);
|
||||||
|
}
|
||||||
|
@ -296,27 +296,26 @@ static void purge_tasks_locked (qse_httpd_t* httpd, qse_httpd_client_t* client)
|
|||||||
#endif
|
#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)
|
static int htrd_handle_request (qse_htrd_t* htrd, qse_htre_t* req)
|
||||||
{
|
{
|
||||||
htrd_xtn_t* xtn = (htrd_xtn_t*) qse_htrd_getxtn (htrd);
|
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);
|
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 =
|
static qse_htrd_recbs_t htrd_recbs =
|
||||||
{
|
{
|
||||||
htrd_handle_request,
|
htrd_peek_request,
|
||||||
htrd_handle_expect_continue,
|
htrd_handle_request
|
||||||
|
|
||||||
/* The response handler is not needed as QSE_HTRD_RESPONSE is truned off */
|
|
||||||
QSE_NULL
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static void deactivate_listener (qse_httpd_t* httpd, listener_t* l)
|
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;
|
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;
|
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)
|
switch (l->family)
|
||||||
{
|
{
|
||||||
case AF_INET:
|
case AF_INET:
|
||||||
{
|
{
|
||||||
addr.in4.sin_family = l->family;
|
addr->in4.sin_family = l->family;
|
||||||
addr.in4.sin_addr = l->addr.in4;
|
addr->in4.sin_addr = l->addr.in4;
|
||||||
addr.in4.sin_port = htons (l->port);
|
addr->in4.sin_port = htons (l->port);
|
||||||
addrsize = QSE_SIZEOF(addr.in4);
|
addrsize = QSE_SIZEOF(addr->in4);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef AF_INET6
|
#ifdef AF_INET6
|
||||||
case AF_INET6:
|
case AF_INET6:
|
||||||
{
|
{
|
||||||
addr.in6.sin6_family = l->family;
|
addr->in6.sin6_family = l->family;
|
||||||
addr.in6.sin6_addr = l->addr.in6;
|
addr->in6.sin6_addr = l->addr.in6;
|
||||||
addr.in6.sin6_port = htons (l->port);
|
addr->in6.sin6_port = htons (l->port);
|
||||||
/* TODO: addr.in6.sin6_scope_id */
|
/* TODO: addr.in6.sin6_scope_id */
|
||||||
addrsize = QSE_SIZEOF(addr.in6);
|
addrsize = QSE_SIZEOF(addr->in6);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
#endif
|
#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
|
/* Solaris 8 returns EINVAL if QSE_SIZEOF(addr) is passed in as the
|
||||||
* address size for AF_INET. */
|
* address size for AF_INET. */
|
||||||
/*if (bind (s, (struct sockaddr*)&addr, QSE_SIZEOF(addr)) <= -1) goto oops_esocket;*/
|
/*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);
|
if (flag >= 0) fcntl (s, F_SETFL, flag | O_NONBLOCK);
|
||||||
fcntl (s, F_SETFD, FD_CLOEXEC);
|
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;
|
l->handle = s;
|
||||||
s = -1;
|
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);
|
qse_htrd_close (array->data[fd].htrd);
|
||||||
array->data[fd].htrd = QSE_NULL;
|
array->data[fd].htrd = QSE_NULL;
|
||||||
qse_fprintf (QSE_STDERR, QSE_T("Debug: closing socket %d\n"), array->data[fd].handle.i);
|
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);
|
close (array->data[fd].handle.i);
|
||||||
array->size--;
|
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;
|
htrd_xtn_t* xtn;
|
||||||
client_array_t* array = &httpd->client.array;
|
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)
|
if (fd >= array->capa)
|
||||||
{
|
{
|
||||||
#define ALIGN 512
|
#define ALIGN 512
|
||||||
qse_httpd_client_t* tmp;
|
qse_httpd_client_t* tmp;
|
||||||
qse_size_t capa = ((fd + ALIGN) / ALIGN) * ALIGN;
|
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;
|
if (tmp == QSE_NULL) return QSE_NULL;
|
||||||
|
|
||||||
QSE_MEMSET (&tmp[array->capa], 0,
|
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;
|
opt &= ~QSE_HTRD_RESPONSE;
|
||||||
qse_htrd_setoption (array->data[fd].htrd, opt);
|
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].bad = 0;
|
||||||
array->data[fd].handle.i = fd;
|
array->data[fd].secure = client->secure;
|
||||||
array->data[fd].addr = *addr;
|
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 defined(HAVE_PTHREAD)
|
||||||
if (httpd->threaded)
|
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)
|
static int accept_client_from_listener (qse_httpd_t* httpd, listener_t* l)
|
||||||
{
|
{
|
||||||
int flag, c;
|
int flag;
|
||||||
sockaddr_t addr;
|
|
||||||
#ifdef HAVE_SOCKLEN_T
|
#ifdef HAVE_SOCKLEN_T
|
||||||
socklen_t addrlen = QSE_SIZEOF(addr);
|
socklen_t addrlen;
|
||||||
#else
|
#else
|
||||||
int addrlen = QSE_SIZEOF(addr);
|
int addrlen;
|
||||||
#endif
|
#endif
|
||||||
|
qse_httpd_client_t clibuf;
|
||||||
qse_httpd_client_t* client;
|
qse_httpd_client_t* client;
|
||||||
|
|
||||||
/* TODO:
|
QSE_MEMSET (&clibuf, 0, QSE_SIZEOF(clibuf));
|
||||||
if (l->secure)
|
clibuf.secure = l->secure;
|
||||||
{
|
|
||||||
SSL_Accept
|
|
||||||
....
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
c = accept (l->handle, (struct sockaddr*)&addr, &addrlen);
|
addrlen = QSE_SIZEOF(clibuf.remote_addr);
|
||||||
if (c <= -1)
|
clibuf.handle.i = accept (
|
||||||
|
l->handle, (struct sockaddr*)&clibuf.remote_addr, &addrlen);
|
||||||
|
if (clibuf.handle.i <= -1)
|
||||||
{
|
{
|
||||||
httpd->errnum = QSE_HTTPD_ESOCKET;
|
httpd->errnum = QSE_HTTPD_ESOCKET;
|
||||||
qse_fprintf (QSE_STDERR, QSE_T("Error: accept returned failure\n"));
|
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
|
/* select() uses a fixed-size array so the file descriptor can not
|
||||||
* exceeded FD_SETSIZE */
|
* 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"));
|
qse_fprintf (QSE_STDERR, QSE_T("Error: too many client?\n"));
|
||||||
/* httpd->errnum = QSE_HTTPD_EOVERFLOW; */
|
/* httpd->errnum = QSE_HTTPD_EOVERFLOW; */
|
||||||
goto oops;
|
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
|
/* set the nonblock flag in case read() after select() blocks
|
||||||
* for various reasons - data received may be dropped after
|
* for various reasons - data received may be dropped after
|
||||||
* arrival for wrong checksum, for example. */
|
* arrival for wrong checksum, for example. */
|
||||||
flag = fcntl (c, F_GETFL);
|
flag = fcntl (clibuf.handle.i, F_GETFL);
|
||||||
if (flag >= 0) fcntl (c, F_SETFL, flag | O_NONBLOCK);
|
if (flag >= 0) fcntl (clibuf.handle.i, F_SETFL, flag | O_NONBLOCK);
|
||||||
|
|
||||||
flag = fcntl (c, F_GETFD);
|
flag = fcntl (clibuf.handle.i, F_GETFD);
|
||||||
if (flag >= 0) fcntl (c, F_SETFD, flag | FD_CLOEXEC);
|
if (flag >= 0) fcntl (clibuf.handle.i, F_SETFD, flag | FD_CLOEXEC);
|
||||||
|
|
||||||
#if defined(HAVE_PTHREAD)
|
#if defined(HAVE_PTHREAD)
|
||||||
if (httpd->threaded) pthread_mutex_lock (&httpd->client.mutex);
|
if (httpd->threaded) pthread_mutex_lock (&httpd->client.mutex);
|
||||||
#endif
|
#endif
|
||||||
client = insert_into_client_array (httpd, c, &addr);
|
client = insert_into_client_array (httpd, &clibuf);
|
||||||
#if defined(HAVE_PTHREAD)
|
#if defined(HAVE_PTHREAD)
|
||||||
if (httpd->threaded) pthread_mutex_unlock (&httpd->client.mutex);
|
if (httpd->threaded) pthread_mutex_unlock (&httpd->client.mutex);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (client == QSE_NULL)
|
if (client == QSE_NULL)
|
||||||
{
|
{
|
||||||
close (c);
|
close (clibuf.handle.i);
|
||||||
goto oops;
|
goto oops;
|
||||||
}
|
}
|
||||||
|
|
||||||
qse_printf (QSE_T("connection %d accepted\n"), c);
|
qse_printf (QSE_T("connection %d accepted\n"), clibuf.handle.i);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
@ -683,26 +695,39 @@ static int make_fd_set_from_client_array (
|
|||||||
|
|
||||||
if (!httpd->threaded || !for_rdwr)
|
if (!httpd->threaded || !for_rdwr)
|
||||||
{
|
{
|
||||||
/* a trigger is a handle to monitor to check
|
/* trigger[0] is a handle to monitor to check
|
||||||
* if there is data avaiable to write back to the client.
|
* if there is data avaiable to read to write back to
|
||||||
* if it is not threaded, qse_httpd_loop() needs to
|
* the client. if it is not threaded, qse_httpd_loop()
|
||||||
* monitor trigger handles. if it is threaded,
|
* needs to monitor trigger handles. if it is threaded,
|
||||||
* response_thread() needs to monitor these handles */
|
* response_thread() needs to monitor these handles.
|
||||||
|
*
|
||||||
if (ca->data[fd].task.queue.head &&
|
* trigger[1] is a user-defined handle to monitor to
|
||||||
ca->data[fd].task.queue.head->task.trigger.i >= 0)
|
* check if httpd can post data to. but this is not
|
||||||
|
* a client-side handle.
|
||||||
|
*/
|
||||||
|
if (ca->data[fd].task.queue.head)
|
||||||
{
|
{
|
||||||
/* if a trigger is available, add it to the read set also. */
|
qse_httpd_task_t* task = &ca->data[fd].task.queue.head->task;
|
||||||
FD_SET (ca->data[fd].task.queue.head->task.trigger.i, r);
|
if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_READ)
|
||||||
if (ca->data[fd].task.queue.head->task.trigger.i > max)
|
{
|
||||||
max = ca->data[fd].task.queue.head->task.trigger.i;
|
/* if a trigger is available, add it to the read set also. */
|
||||||
|
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 ||
|
if (ca->data[fd].bad ||
|
||||||
(ca->data[fd].task.queue.head &&
|
(ca->data[fd].task.queue.head && !(ca->data[fd].task.queue.head->task.trigger_mask & QSE_HTTPD_TASK_TRIGGER_READ)))
|
||||||
ca->data[fd].task.queue.head->task.trigger.i <= -1))
|
|
||||||
{
|
{
|
||||||
/* add a client-side handle to the write set
|
/* add a client-side handle to the write set
|
||||||
* if the client is already marked bad or
|
* 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)
|
else if (client->task.queue.head)
|
||||||
{
|
{
|
||||||
if (client->task.queue.head->task.trigger.i <= -1 ||
|
qse_httpd_task_t* task;
|
||||||
FD_ISSET(client->task.queue.head->task.trigger.i, &r))
|
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_sec = 0;
|
||||||
tv.tv_usec = 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_mchar_t buf[1024];
|
||||||
qse_ssize_t m;
|
qse_ssize_t m;
|
||||||
|
|
||||||
|
QSE_ASSERT (httpd->cbs->client.recv != QSE_NULL);
|
||||||
|
|
||||||
reread:
|
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)
|
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)
|
if (errno == EAGAIN || errno == EWOULDBLOCK)
|
||||||
{
|
{
|
||||||
/* nothing to read yet. */
|
/* 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
|
* that's because we don't know how many valid requests
|
||||||
* are included in 'buf' */
|
* are included in 'buf' */
|
||||||
qse_fprintf (QSE_STDERR, QSE_T("Debug: read from a client %d\n"), client->handle.i);
|
qse_fprintf (QSE_STDERR, QSE_T("Debug: read from a client %d\n"), client->handle.i);
|
||||||
|
|
||||||
httpd->errnum = QSE_HTTPD_ENOERR;
|
httpd->errnum = QSE_HTTPD_ENOERR;
|
||||||
if (qse_htrd_feed (client->htrd, buf, m) <= -1)
|
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;
|
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)
|
#if defined(HAVE_PTHREAD)
|
||||||
pthread_t response_thread_id;
|
pthread_t response_thread_id;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
httpd->stopreq = 0;
|
|
||||||
httpd->threaded = 0;
|
httpd->threaded = 0;
|
||||||
|
httpd->stopreq = 0;
|
||||||
|
httpd->cbs = cbs;
|
||||||
|
|
||||||
QSE_ASSERTX (httpd->listener.list != QSE_NULL,
|
QSE_ASSERTX (httpd->listener.list != QSE_NULL,
|
||||||
"Add listeners before calling qse_httpd_loop()"
|
"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_mutex_init (&httpd->client.mutex, QSE_NULL);
|
||||||
pthread_cond_init (&httpd->client.cond, 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 (
|
if (pthread_create (
|
||||||
&response_thread_id, QSE_NULL,
|
&response_thread_id, QSE_NULL,
|
||||||
response_thread, httpd) != 0)
|
response_thread, httpd) != 0)
|
||||||
{
|
{
|
||||||
|
httpd->threaded = 0;
|
||||||
pthread_cond_destroy (&httpd->client.cond);
|
pthread_cond_destroy (&httpd->client.cond);
|
||||||
pthread_mutex_destroy (&httpd->client.mutex);
|
pthread_mutex_destroy (&httpd->client.mutex);
|
||||||
QSE_CLOSE (httpd->client.pfd[1]);
|
QSE_CLOSE (httpd->client.pfd[1]);
|
||||||
QSE_CLOSE (httpd->client.pfd[0]);
|
QSE_CLOSE (httpd->client.pfd[0]);
|
||||||
}
|
}
|
||||||
else httpd->threaded = 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -994,26 +1067,38 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: select returned failure\n"));
|
|||||||
if (FD_ISSET(client->handle.i, &r))
|
if (FD_ISSET(client->handle.i, &r))
|
||||||
{
|
{
|
||||||
/* got input */
|
/* got input */
|
||||||
if (read_from_client (httpd, client) <= -1)
|
if (!client->ready)
|
||||||
{
|
{
|
||||||
if (httpd->threaded)
|
/* 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)
|
||||||
{
|
{
|
||||||
/* let the writing part handle it,
|
bad_client:
|
||||||
* probably in the next iteration */
|
if (httpd->threaded)
|
||||||
qse_httpd_markclientbad (httpd, client);
|
{
|
||||||
shutdown (client->handle.i, SHUT_RDWR);
|
/* let the writing part handle it,
|
||||||
}
|
* probably in the next iteration */
|
||||||
else
|
qse_httpd_markbadclient (httpd, client);
|
||||||
{
|
shutdown (client->handle.i, SHUT_RDWR);
|
||||||
/*pthread_mutex_lock (&httpd->client.mutex);*/
|
}
|
||||||
delete_from_client_array (httpd, fd);
|
else
|
||||||
/*pthread_mutex_unlock (&httpd->client.mutex);*/
|
{
|
||||||
continue; /* don't need to go to the writing part */
|
/*pthread_mutex_lock (&httpd->client.mutex);*/
|
||||||
|
delete_from_client_array (httpd, fd);
|
||||||
|
/*pthread_mutex_unlock (&httpd->client.mutex);*/
|
||||||
|
continue; /* don't need to go to the writing part */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (!httpd->threaded)
|
if (!httpd->threaded)
|
||||||
{
|
{
|
||||||
if (client->bad)
|
if (client->bad)
|
||||||
@ -1025,8 +1110,41 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: select returned failure\n"));
|
|||||||
}
|
}
|
||||||
else if (client->task.queue.head)
|
else if (client->task.queue.head)
|
||||||
{
|
{
|
||||||
if (client->task.queue.head->task.trigger.i <= -1 ||
|
qse_httpd_task_t* task;
|
||||||
FD_ISSET(client->task.queue.head->task.trigger.i, &r))
|
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_sec = 0;
|
||||||
tv.tv_usec = 0;
|
tv.tv_usec = 0;
|
||||||
@ -1305,7 +1423,7 @@ qse_httpd_task_t* qse_httpd_entask (
|
|||||||
return ret;
|
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.
|
/* mark that something is wrong in processing requests from this client.
|
||||||
* this client could be bad... or the system could encounter some errors
|
* 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;
|
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
|
#endif
|
||||||
|
@ -61,8 +61,13 @@ struct task_queue_node_t
|
|||||||
struct qse_httpd_client_t
|
struct qse_httpd_client_t
|
||||||
{
|
{
|
||||||
qse_ubi_t handle;
|
qse_ubi_t handle;
|
||||||
|
qse_ubi_t handle2;
|
||||||
|
|
||||||
|
int ready;
|
||||||
int bad;
|
int bad;
|
||||||
sockaddr_t addr;
|
int secure;
|
||||||
|
sockaddr_t local_addr;
|
||||||
|
sockaddr_t remote_addr;
|
||||||
qse_htrd_t* htrd;
|
qse_htrd_t* htrd;
|
||||||
|
|
||||||
struct
|
struct
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -106,11 +106,11 @@ static int test3 (void)
|
|||||||
|
|
||||||
env = qse_env_open (QSE_MMGR_GETDFL(), 0, 0);
|
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 PATH => %d\n"), qse_env_insert (env, QSE_T("PATH"), QSE_NULL));
|
||||||
qse_printf (QSE_T("inserting HOME => %d\n"), qse_env_insertsysm (env, QSE_MT("HOME")));
|
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_insertsysw (env, QSE_WT("USER")));
|
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_insertsys (env, QSE_T("WHAT")));
|
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_insertsys (env, QSE_T("")));
|
qse_printf (QSE_T("inserting an empty string => %d\n"), qse_env_insert (env, QSE_T(""), QSE_NULL));
|
||||||
|
|
||||||
dump (env);
|
dump (env);
|
||||||
|
|
||||||
|
@ -304,7 +304,7 @@ static int test12 (void)
|
|||||||
env = qse_env_open (QSE_MMGR_GETDFL(), 0, 0);
|
env = qse_env_open (QSE_MMGR_GETDFL(), 0, 0);
|
||||||
if (env == QSE_NULL) return -1;
|
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"));
|
qse_env_insert (env, QSE_T("HELLO"), QSE_T("WORLD"));
|
||||||
|
|
||||||
n = pio1 (
|
n = pio1 (
|
||||||
|
@ -8,7 +8,7 @@ AM_CPPFLAGS = \
|
|||||||
bin_PROGRAMS = http01
|
bin_PROGRAMS = http01
|
||||||
|
|
||||||
LDFLAGS += -L../../lib/cmn -L../../lib/net
|
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
|
http01_SOURCES = http01.c
|
||||||
|
|
||||||
|
@ -226,7 +226,7 @@ AM_CPPFLAGS = \
|
|||||||
-I$(top_srcdir)/include \
|
-I$(top_srcdir)/include \
|
||||||
-I$(includedir)
|
-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
|
http01_SOURCES = http01.c
|
||||||
all: all-am
|
all: all-am
|
||||||
|
|
||||||
|
@ -12,52 +12,347 @@
|
|||||||
# include <windows.h>
|
# include <windows.h>
|
||||||
#endif
|
#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;
|
typedef struct httpd_xtn_t httpd_xtn_t;
|
||||||
struct 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)
|
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));
|
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;
|
return QSE_HTB_WALK_FORWARD;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int handle_request (
|
static int process_request (
|
||||||
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req)
|
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req, int peek)
|
||||||
{
|
{
|
||||||
int method;
|
int method;
|
||||||
qse_httpd_task_t* x;
|
qse_httpd_task_t* task;
|
||||||
|
int content_received;
|
||||||
|
|
||||||
#if 0
|
method = qse_htre_getqmethod(req);
|
||||||
httpd_xtn_t* xtn = (httpd_xtn_t*) qse_httpd_getxtn (httpd);
|
content_received = (qse_htre_getcontentlen(req) > 0);
|
||||||
return xtn->orgcbs->handle_request (httpd, client, req);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
qse_printf (QSE_T("================================\n"));
|
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),
|
(unsigned long)time(NULL),
|
||||||
|
(peek? QSE_MT("PEEK"): QSE_MT("HANDLE")),
|
||||||
qse_htre_getqpathptr(req),
|
qse_htre_getqpathptr(req),
|
||||||
qse_htre_getmajorversion(req),
|
qse_htre_getmajorversion(req),
|
||||||
qse_htre_getminorversion(req),
|
qse_htre_getminorversion(req),
|
||||||
qse_htre_getqmethod(req)
|
method
|
||||||
);
|
);
|
||||||
if (qse_htre_getqparamlen(req) > 0)
|
if (qse_htre_getqparamlen(req) > 0) qse_printf (QSE_T("PARAMS ==> [%hs]\n"), qse_htre_getqparamptr(req));
|
||||||
{
|
|
||||||
qse_printf (QSE_T("PARAMS ==> [%hs]\n"), qse_htre_getqparamptr(req));
|
|
||||||
}
|
|
||||||
|
|
||||||
qse_htb_walk (&req->hdrtab, walk, QSE_NULL);
|
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"),
|
qse_printf (QSE_T("CONTENT before discard = [%.*S]\n"), (int)qse_htre_getcontentlen(req), qse_htre_getcontentptr(req));
|
||||||
(int)QSE_MBS_LEN(&req->content),
|
|
||||||
QSE_MBS_PTR(&req->content));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
if (method == QSE_HTTP_GET || method == QSE_HTTP_POST)
|
||||||
{
|
{
|
||||||
@ -66,50 +361,72 @@ qse_printf (QSE_T("content = [%.*S]\n"),
|
|||||||
|
|
||||||
if (dot && qse_mbscmp (dot, QSE_MT(".cgi")) == 0)
|
if (dot && qse_mbscmp (dot, QSE_MT(".cgi")) == 0)
|
||||||
{
|
{
|
||||||
/* cgi */
|
if (peek)
|
||||||
x = qse_httpd_entaskcgi (
|
{
|
||||||
httpd, client, QSE_NULL, qpath, req);
|
/* cgi */
|
||||||
if (x == QSE_NULL) goto oops;
|
task = qse_httpd_entaskcgi (
|
||||||
|
httpd, client, QSE_NULL, qpath, req);
|
||||||
|
if (task == QSE_NULL) goto oops;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
else if (dot && qse_mbscmp (dot, QSE_MT(".nph")) == 0)
|
else if (dot && qse_mbscmp (dot, QSE_MT(".nph")) == 0)
|
||||||
{
|
{
|
||||||
/* nph-cgi */
|
if (peek)
|
||||||
x = qse_httpd_entasknph (
|
{
|
||||||
httpd, client, QSE_NULL, qpath, req);
|
/* nph-cgi */
|
||||||
if (x == QSE_NULL) goto oops;
|
task = qse_httpd_entasknph (
|
||||||
|
httpd, client, QSE_NULL, qpath, req);
|
||||||
|
if (task == QSE_NULL) goto oops;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* file or directory */
|
if (!peek)
|
||||||
x = qse_httpd_entaskpath (
|
{
|
||||||
httpd, client, QSE_NULL, qpath, req);
|
/* file or directory */
|
||||||
if (x == QSE_NULL) goto oops;
|
task = qse_httpd_entaskpath (
|
||||||
|
httpd, client, QSE_NULL, qpath, req);
|
||||||
|
if (task == QSE_NULL) goto oops;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
x = qse_httpd_entaskerror (httpd, client, QSE_NULL, 405, req);
|
if (!peek)
|
||||||
if (x == QSE_NULL) goto oops;
|
{
|
||||||
|
task = qse_httpd_entaskerror (httpd, client, QSE_NULL, 405, req);
|
||||||
|
if (task == QSE_NULL) goto oops;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!req->attr.keepalive)
|
if (!req->attr.keepalive)
|
||||||
{
|
{
|
||||||
x = qse_httpd_entaskdisconnect (httpd, client, QSE_NULL);
|
if (!peek)
|
||||||
if (x == QSE_NULL) goto oops;
|
{
|
||||||
|
task = qse_httpd_entaskdisconnect (httpd, client, QSE_NULL);
|
||||||
|
if (task == QSE_NULL) goto oops;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
oops:
|
oops:
|
||||||
/*qse_httpd_markclientbad (httpd, client);*/
|
/*qse_httpd_markbadclient (httpd, client);*/
|
||||||
return 0;
|
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)
|
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 process_request (httpd, client, req, 1);
|
||||||
return xtn->orgcbs->handle_expect_continue (httpd, client, req);
|
}
|
||||||
|
|
||||||
|
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)
|
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;
|
return 404;
|
||||||
}
|
}
|
||||||
|
|
||||||
static qse_httpd_t* httpd = NULL;
|
|
||||||
|
|
||||||
static void sigint (int sig)
|
|
||||||
{
|
|
||||||
qse_httpd_stop (httpd);
|
|
||||||
}
|
|
||||||
|
|
||||||
static qse_httpd_cbs_t httpd_cbs =
|
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_request,
|
||||||
handle_expect_continue,
|
|
||||||
get_mime_type,
|
get_mime_type,
|
||||||
list_directory
|
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 httpd_main (int argc, qse_char_t* argv[])
|
||||||
{
|
{
|
||||||
int n;
|
qse_httpd_t* httpd = QSE_NULL;
|
||||||
httpd_xtn_t* xtn;
|
httpd_xtn_t* xtn;
|
||||||
|
int ret = -1, i;
|
||||||
|
int ssl_xtn_inited = 0;
|
||||||
|
|
||||||
if (argc <= 1)
|
if (argc <= 1)
|
||||||
{
|
{
|
||||||
qse_fprintf (QSE_STDERR, QSE_T("Usage: %s <listener_uri> ...\n"), argv[0]);
|
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));
|
httpd = qse_httpd_open (QSE_MMGR_GETDFL(), QSE_SIZEOF(httpd_xtn_t));
|
||||||
if (httpd == QSE_NULL)
|
if (httpd == QSE_NULL)
|
||||||
{
|
{
|
||||||
qse_fprintf (QSE_STDERR, QSE_T("Cannot open httpd\n"));
|
qse_fprintf (QSE_STDERR, QSE_T("Cannot open httpd\n"));
|
||||||
return -1;
|
goto oops;
|
||||||
}
|
}
|
||||||
|
|
||||||
xtn = (httpd_xtn_t*)qse_httpd_getxtn (httpd);
|
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_fprintf (QSE_STDERR,
|
||||||
QSE_T("Failed to add httpd listener - %s\n"), argv[n]);
|
QSE_T("Failed to add httpd listener - %s\n"), argv[i]);
|
||||||
qse_httpd_close (httpd);
|
goto oops;
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
qse_httpd_setcbs (httpd, &httpd_cbs);
|
g_httpd = httpd;
|
||||||
|
|
||||||
signal (SIGINT, sigint);
|
signal (SIGINT, sigint);
|
||||||
signal (SIGPIPE, SIG_IGN);
|
signal (SIGPIPE, SIG_IGN);
|
||||||
|
|
||||||
n = qse_httpd_loop (httpd, 1);
|
ret = qse_httpd_loop (httpd, &httpd_cbs, 0);
|
||||||
|
|
||||||
signal (SIGINT, SIG_DFL);
|
signal (SIGINT, SIG_DFL);
|
||||||
signal (SIGPIPE, SIG_DFL);
|
signal (SIGPIPE, SIG_DFL);
|
||||||
|
g_httpd = QSE_NULL;
|
||||||
|
|
||||||
if (n <= -1)
|
if (ret <= -1) qse_fprintf (QSE_STDERR, QSE_T("Httpd error\n"));
|
||||||
{
|
|
||||||
qse_fprintf (QSE_STDERR, QSE_T("Httpd error\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
qse_httpd_close (httpd);
|
oops:
|
||||||
return n;
|
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[])
|
int qse_main (int argc, qse_achar_t* argv[])
|
||||||
|
Loading…
x
Reference in New Issue
Block a user