fixed various bugs in handling cgi
This commit is contained in:
parent
517107ab79
commit
1ab75927d2
@ -28,7 +28,7 @@
|
|||||||
/* 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
|
enum qse_htre_state_t
|
||||||
{
|
{
|
||||||
QSE_HTRE_DISCARDED = (1 << 0), /** content has been discarded */
|
QSE_HTRE_DISCARDED = (1 << 0), /** content has been discarded */
|
||||||
QSE_HTRE_COMPLETED = (1 << 1) /** complete content has been seen */
|
QSE_HTRE_COMPLETED = (1 << 1) /** complete content has been seen */
|
||||||
@ -72,8 +72,8 @@ struct qse_htre_t
|
|||||||
qse_htre_concb_t concb;
|
qse_htre_concb_t concb;
|
||||||
void* concb_ctx;
|
void* concb_ctx;
|
||||||
|
|
||||||
/* ORed of qse_htre_flag_t */
|
/* ORed of qse_htre_state_t */
|
||||||
int flags;
|
int state;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define qse_htre_getversion(re) (&((re)->version))
|
#define qse_htre_getversion(re) (&((re)->version))
|
||||||
@ -169,7 +169,7 @@ int qse_htre_setstrfromxstr (
|
|||||||
);
|
);
|
||||||
|
|
||||||
const qse_mchar_t* qse_htre_getheaderval (
|
const qse_mchar_t* qse_htre_getheaderval (
|
||||||
qse_htre_t* re,
|
const qse_htre_t* re,
|
||||||
const qse_mchar_t* key
|
const qse_mchar_t* key
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -179,12 +179,27 @@ int qse_htre_walkheaders (
|
|||||||
void* ctx
|
void* ctx
|
||||||
);
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The qse_htre_addcontent() function adds a content semgnet pointed to by
|
||||||
|
* @a ptr of @a len bytes to the content buffer. If @a re is already completed
|
||||||
|
* or discarded, this function returns 0 without adding the segment to the
|
||||||
|
* content buffer.
|
||||||
|
* @return 1 on success, -1 on failure, 0 if adding is skipped.
|
||||||
|
*/
|
||||||
int qse_htre_addcontent (
|
int qse_htre_addcontent (
|
||||||
qse_htre_t* re,
|
qse_htre_t* re,
|
||||||
const qse_mchar_t* ptr,
|
const qse_mchar_t* ptr,
|
||||||
qse_size_t len
|
qse_size_t len
|
||||||
);
|
);
|
||||||
|
|
||||||
|
void qse_htre_completecontent (
|
||||||
|
qse_htre_t* re
|
||||||
|
);
|
||||||
|
|
||||||
|
void qse_htre_discardcontent (
|
||||||
|
qse_htre_t* re
|
||||||
|
);
|
||||||
|
|
||||||
void qse_htre_unsetconcb (
|
void qse_htre_unsetconcb (
|
||||||
qse_htre_t* re
|
qse_htre_t* re
|
||||||
);
|
);
|
||||||
@ -196,7 +211,7 @@ void qse_htre_setconcb (
|
|||||||
);
|
);
|
||||||
|
|
||||||
const qse_mchar_t* qse_htre_getqmethodname (
|
const qse_mchar_t* qse_htre_getqmethodname (
|
||||||
qse_htre_t* re
|
const qse_htre_t* re
|
||||||
);
|
);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#include <qse/types.h>
|
#include <qse/types.h>
|
||||||
#include <qse/macros.h>
|
#include <qse/macros.h>
|
||||||
#include <qse/net/htre.h>
|
#include <qse/net/htre.h>
|
||||||
|
#include <qse/cmn/time.h>
|
||||||
|
|
||||||
typedef struct qse_httpd_t qse_httpd_t;
|
typedef struct qse_httpd_t qse_httpd_t;
|
||||||
typedef struct qse_httpd_client_t qse_httpd_client_t;
|
typedef struct qse_httpd_client_t qse_httpd_client_t;
|
||||||
@ -45,24 +46,36 @@ typedef enum qse_httpd_errnum_t qse_httpd_errnum_t;
|
|||||||
|
|
||||||
enum qse_httpd_option_t
|
enum qse_httpd_option_t
|
||||||
{
|
{
|
||||||
QSE_HTTPD_CGINOCLOEXEC = (1 << 0)
|
QSE_HTTPD_CGIERRTONUL = (1 << 0),
|
||||||
|
QSE_HTTPD_CGINOCLOEXEC = (1 << 1)
|
||||||
};
|
};
|
||||||
|
|
||||||
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
|
||||||
{
|
{
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
int (*readable) (qse_httpd_t* httpd, qse_ubi_t handle, qse_ntoff_t timeout);
|
||||||
|
int (*writable) (qse_httpd_t* httpd, qse_ubi_t handle, qse_ntoff_t timeout);
|
||||||
|
} mux;
|
||||||
|
|
||||||
|
struct
|
||||||
|
{
|
||||||
|
int (*executable) (qse_httpd_t* httpd, const qse_mchar_t* path);
|
||||||
|
} path;
|
||||||
|
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
/* action */
|
/* action */
|
||||||
int (*recv) (qse_httpd_t* httpd,
|
qse_ssize_t (*recv) (qse_httpd_t* httpd,
|
||||||
qse_httpd_client_t* client,
|
qse_httpd_client_t* client,
|
||||||
qse_mchar_t* buf, qse_size_t bufsize);
|
qse_mchar_t* buf, qse_size_t bufsize);
|
||||||
|
|
||||||
int (*send) (qse_httpd_t* httpd,
|
qse_ssize_t (*send) (qse_httpd_t* httpd,
|
||||||
qse_httpd_client_t* client,
|
qse_httpd_client_t* client,
|
||||||
const qse_mchar_t* buf, qse_size_t bufsize);
|
const qse_mchar_t* buf, qse_size_t bufsize);
|
||||||
|
|
||||||
int (*sendfile) (qse_httpd_t* httpd,
|
qse_ssize_t (*sendfile) (qse_httpd_t* httpd,
|
||||||
qse_httpd_client_t* client,
|
qse_httpd_client_t* client,
|
||||||
qse_ubi_t handle, qse_foff_t* offset, qse_size_t count);
|
qse_ubi_t handle, qse_foff_t* offset, qse_size_t count);
|
||||||
|
|
||||||
@ -108,9 +121,11 @@ typedef int (*qse_httpd_task_main_t) (
|
|||||||
enum qse_httpd_task_trigger_mask_t
|
enum qse_httpd_task_trigger_mask_t
|
||||||
{
|
{
|
||||||
QSE_HTTPD_TASK_TRIGGER_READ = (1 << 0),
|
QSE_HTTPD_TASK_TRIGGER_READ = (1 << 0),
|
||||||
QSE_HTTPD_TASK_TRIGGER_WRITE = (1 << 1),
|
QSE_HTTPD_TASK_TRIGGER_RELAY = (1 << 1),
|
||||||
QSE_HTTPD_TASK_TRIGGER_READABLE = (1 << 2),
|
QSE_HTTPD_TASK_TRIGGER_WRITE = (1 << 2),
|
||||||
QSE_HTTPD_TASK_TRIGGER_WRITABLE = (1 << 3)
|
QSE_HTTPD_TASK_TRIGGER_READABLE = (1 << 3),
|
||||||
|
QSE_HTTPD_TASK_TRIGGER_RELAYABLE = (1 << 4),
|
||||||
|
QSE_HTTPD_TASK_TRIGGER_WRITABLE = (1 << 5)
|
||||||
};
|
};
|
||||||
|
|
||||||
struct qse_httpd_task_t
|
struct qse_httpd_task_t
|
||||||
@ -123,7 +138,7 @@ struct qse_httpd_task_t
|
|||||||
qse_httpd_task_main_t main;
|
qse_httpd_task_main_t main;
|
||||||
|
|
||||||
int trigger_mask;
|
int trigger_mask;
|
||||||
qse_ubi_t trigger[2];
|
qse_ubi_t trigger[3];
|
||||||
|
|
||||||
void* ctx;
|
void* ctx;
|
||||||
};
|
};
|
||||||
@ -158,28 +173,12 @@ void qse_httpd_setoption (
|
|||||||
int option
|
int option
|
||||||
);
|
);
|
||||||
|
|
||||||
const qse_httpd_cbs_t* qse_httpd_getcbs (
|
|
||||||
qse_httpd_t* httpd
|
|
||||||
);
|
|
||||||
|
|
||||||
void qse_httpd_setcbs (
|
|
||||||
qse_httpd_t* httpd,
|
|
||||||
qse_httpd_cbs_t* cbs
|
|
||||||
);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The qse_httpd_loop() function starts a httpd server loop.
|
* The qse_httpd_loop() function starts a httpd server loop.
|
||||||
* If @a threaded is non-zero, it creates a separate output thread.
|
|
||||||
* If no thread support is available, it is ignored.
|
|
||||||
*
|
|
||||||
* @note
|
|
||||||
* In the future, the @a threaded parameter will be extended to
|
|
||||||
* specify the number of output threads.
|
|
||||||
*/
|
*/
|
||||||
int qse_httpd_loop (
|
int qse_httpd_loop (
|
||||||
qse_httpd_t* httpd,
|
qse_httpd_t* httpd,
|
||||||
qse_httpd_cbs_t* cbs,
|
qse_httpd_cbs_t* cbs
|
||||||
int threaded
|
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -195,7 +194,6 @@ int qse_httpd_addlistener (
|
|||||||
const qse_char_t* uri
|
const qse_char_t* uri
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
void qse_httpd_markbadclient (
|
void qse_httpd_markbadclient (
|
||||||
qse_httpd_t* httpd,
|
qse_httpd_t* httpd,
|
||||||
qse_httpd_client_t* client
|
qse_httpd_client_t* client
|
||||||
@ -206,6 +204,11 @@ void qse_httpd_discardcontent (
|
|||||||
qse_htre_t* req
|
qse_htre_t* req
|
||||||
);
|
);
|
||||||
|
|
||||||
|
void qse_httpd_completecontent (
|
||||||
|
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 (
|
||||||
@ -272,14 +275,14 @@ qse_httpd_task_t* qse_httpd_entaskerror (
|
|||||||
qse_httpd_client_t* client,
|
qse_httpd_client_t* client,
|
||||||
const qse_httpd_task_t* task,
|
const qse_httpd_task_t* task,
|
||||||
int code,
|
int code,
|
||||||
const qse_htre_t* req
|
qse_htre_t* req
|
||||||
);
|
);
|
||||||
|
|
||||||
qse_httpd_task_t* qse_httpd_entaskcontinue (
|
qse_httpd_task_t* qse_httpd_entaskcontinue (
|
||||||
qse_httpd_t* httpd,
|
qse_httpd_t* httpd,
|
||||||
qse_httpd_client_t* client,
|
qse_httpd_client_t* client,
|
||||||
const qse_httpd_task_t* task,
|
const qse_httpd_task_t* task,
|
||||||
const qse_htre_t* req
|
qse_htre_t* req
|
||||||
);
|
);
|
||||||
|
|
||||||
qse_httpd_task_t* qse_httpd_entaskpath (
|
qse_httpd_task_t* qse_httpd_entaskpath (
|
||||||
@ -287,7 +290,7 @@ qse_httpd_task_t* qse_httpd_entaskpath (
|
|||||||
qse_httpd_client_t* client,
|
qse_httpd_client_t* client,
|
||||||
const qse_httpd_task_t* pred,
|
const qse_httpd_task_t* pred,
|
||||||
const qse_mchar_t* name,
|
const qse_mchar_t* name,
|
||||||
const qse_htre_t* req
|
qse_htre_t* req
|
||||||
);
|
);
|
||||||
|
|
||||||
qse_httpd_task_t* qse_httpd_entaskcgi (
|
qse_httpd_task_t* qse_httpd_entaskcgi (
|
||||||
@ -295,7 +298,7 @@ qse_httpd_task_t* qse_httpd_entaskcgi (
|
|||||||
qse_httpd_client_t* client,
|
qse_httpd_client_t* client,
|
||||||
const qse_httpd_task_t* pred,
|
const qse_httpd_task_t* pred,
|
||||||
const qse_mchar_t* path,
|
const qse_mchar_t* path,
|
||||||
const qse_htre_t* req
|
qse_htre_t* req
|
||||||
);
|
);
|
||||||
|
|
||||||
qse_httpd_task_t* qse_httpd_entasknph (
|
qse_httpd_task_t* qse_httpd_entasknph (
|
||||||
@ -303,7 +306,7 @@ qse_httpd_task_t* qse_httpd_entasknph (
|
|||||||
qse_httpd_client_t* client,
|
qse_httpd_client_t* client,
|
||||||
const qse_httpd_task_t* pred,
|
const qse_httpd_task_t* pred,
|
||||||
const qse_mchar_t* path,
|
const qse_mchar_t* path,
|
||||||
const qse_htre_t* req
|
qse_htre_t* req
|
||||||
);
|
);
|
||||||
|
|
||||||
/* -------------------------------------------- */
|
/* -------------------------------------------- */
|
||||||
|
@ -101,6 +101,9 @@ static QSE_INLINE int push_content (
|
|||||||
htrd->errnum = QSE_HTRD_ENOMEM;
|
htrd->errnum = QSE_HTRD_ENOMEM;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* qse_htre_addcontent() returns 1 on full success and 0 if adding is
|
||||||
|
* skipped. i treat both as success */
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1101,16 +1104,16 @@ int qse_htrd_feed (qse_htrd_t* htrd, const qse_mchar_t* req, qse_size_t len)
|
|||||||
* reading CGI outputs. So it comes with
|
* reading CGI outputs. So it comes with
|
||||||
* awkwardity described above.
|
* awkwardity described above.
|
||||||
*/
|
*/
|
||||||
if (!(htrd->re.flags & QSE_HTRE_DISCARDED) &&
|
if (push_content (htrd, ptr, end - ptr) <= -1) return -1;
|
||||||
push_content (htrd, ptr, end - ptr) <= -1) return -1;
|
|
||||||
/* this jump is only to invoke the peek
|
/* this jump is only to invoke the peek
|
||||||
* callback. this function should not be fed
|
* callback. this function should not be fed
|
||||||
* more. */
|
* more. */
|
||||||
|
|
||||||
/* i don't really know if it is really completed
|
/* i don't really know if it is really completed
|
||||||
* with content. QSE_HTRD_PEEKONLY is not compatible
|
* with content. QSE_HTRD_PEEKONLY is not compatible
|
||||||
* with the completed flag. */
|
* with the completed state. anyway, let me complete
|
||||||
htrd->re.flags &= QSE_HTRE_COMPLETED;
|
* it. */
|
||||||
|
qse_htre_completecontent (&htrd->re);
|
||||||
goto feedme_more;
|
goto feedme_more;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1166,8 +1169,7 @@ int qse_htrd_feed (qse_htrd_t* htrd, const qse_mchar_t* req, qse_size_t len)
|
|||||||
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 (!(htrd->re.flags & QSE_HTRE_DISCARDED) &&
|
if (push_content (htrd, ptr, avail) <= -1) return -1;
|
||||||
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;
|
||||||
@ -1175,8 +1177,7 @@ 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 (!(htrd->re.flags & QSE_HTRE_DISCARDED) &&
|
if (push_content (htrd, ptr, htrd->fed.s.need) <= -1) return -1;
|
||||||
push_content (htrd, ptr, 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;
|
||||||
}
|
}
|
||||||
@ -1226,6 +1227,9 @@ int qse_htrd_feed (qse_htrd_t* htrd, const qse_mchar_t* req, qse_size_t len)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* the content has been received fully */
|
||||||
|
qse_htre_completecontent (&htrd->re);
|
||||||
|
|
||||||
if (header_completed_during_this_feed && htrd->recbs->peek)
|
if (header_completed_during_this_feed && htrd->recbs->peek)
|
||||||
{
|
{
|
||||||
/* the peek handler has not been executed.
|
/* the peek handler has not been executed.
|
||||||
@ -1234,7 +1238,6 @@ int qse_htrd_feed (qse_htrd_t* htrd, const qse_mchar_t* req, qse_size_t len)
|
|||||||
* plus complete content body and the header
|
* plus complete content body and the header
|
||||||
* of the next request. */
|
* of the next request. */
|
||||||
int n;
|
int n;
|
||||||
htrd->re.flags |= QSE_HTRE_COMPLETED;
|
|
||||||
htrd->errnum = QSE_HTRD_ENOERR;
|
htrd->errnum = QSE_HTRD_ENOERR;
|
||||||
n = htrd->recbs->peek (htrd, &htrd->re);
|
n = htrd->recbs->peek (htrd, &htrd->re);
|
||||||
if (n <= -1)
|
if (n <= -1)
|
||||||
@ -1252,7 +1255,6 @@ int qse_htrd_feed (qse_htrd_t* htrd, const qse_mchar_t* req, qse_size_t len)
|
|||||||
if (htrd->recbs->handle)
|
if (htrd->recbs->handle)
|
||||||
{
|
{
|
||||||
int n;
|
int n;
|
||||||
htrd->re.flags |= QSE_HTRE_COMPLETED;
|
|
||||||
htrd->errnum = QSE_HTRD_ENOERR;
|
htrd->errnum = QSE_HTRD_ENOERR;
|
||||||
n = htrd->recbs->handle (htrd, &htrd->re);
|
n = htrd->recbs->handle (htrd, &htrd->re);
|
||||||
if (n <= -1)
|
if (n <= -1)
|
||||||
|
@ -59,7 +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->state = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int qse_htre_setstrfromcstr (
|
int qse_htre_setstrfromcstr (
|
||||||
@ -75,7 +75,7 @@ int qse_htre_setstrfromxstr (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const qse_mchar_t* qse_htre_getheaderval (
|
const qse_mchar_t* qse_htre_getheaderval (
|
||||||
qse_htre_t* re, const qse_mchar_t* name)
|
const qse_htre_t* re, const qse_mchar_t* name)
|
||||||
{
|
{
|
||||||
qse_htb_pair_t* pair;
|
qse_htb_pair_t* pair;
|
||||||
pair = qse_htb_search (&re->hdrtab, name, qse_mbslen(name));
|
pair = qse_htb_search (&re->hdrtab, name, qse_mbslen(name));
|
||||||
@ -117,11 +117,25 @@ int qse_htre_walkheaders (
|
|||||||
int qse_htre_addcontent (
|
int qse_htre_addcontent (
|
||||||
qse_htre_t* re, const qse_mchar_t* ptr, qse_size_t len)
|
qse_htre_t* re, const qse_mchar_t* ptr, qse_size_t len)
|
||||||
{
|
{
|
||||||
|
if (re->state & (QSE_HTRE_COMPLETED | QSE_HTRE_DISCARDED)) return 0;
|
||||||
|
|
||||||
/* if the callback is set, the content goes to the callback. */
|
/* if the callback is set, the content goes to the callback. */
|
||||||
if (re->concb) return re->concb (re, ptr, len, re->concb_ctx);
|
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 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;
|
if (qse_mbs_ncat (&re->content, ptr, len) == (qse_size_t)-1) return -1;
|
||||||
return 0;
|
|
||||||
|
return 1; /* added successfully */
|
||||||
|
}
|
||||||
|
|
||||||
|
void qse_htre_completecontent (qse_htre_t* re)
|
||||||
|
{
|
||||||
|
re->state |= QSE_HTRE_COMPLETED;
|
||||||
|
}
|
||||||
|
|
||||||
|
void qse_htre_discardcontent (qse_htre_t* re)
|
||||||
|
{
|
||||||
|
re->state |= QSE_HTRE_DISCARDED;
|
||||||
|
qse_mbs_clear (&re->content);
|
||||||
}
|
}
|
||||||
|
|
||||||
void qse_htre_unsetconcb (qse_htre_t* re)
|
void qse_htre_unsetconcb (qse_htre_t* re)
|
||||||
@ -136,7 +150,7 @@ void qse_htre_setconcb (qse_htre_t* re, qse_htre_concb_t concb, void* ctx)
|
|||||||
re->concb_ctx = ctx;
|
re->concb_ctx = ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
const qse_mchar_t* qse_htre_getqmethodname (qse_htre_t* re)
|
const qse_mchar_t* qse_htre_getqmethodname (const qse_htre_t* re)
|
||||||
{
|
{
|
||||||
return qse_gethttpmethodname (re->qmethod_or_sstatus);
|
return qse_gethttpmethodname (re->qmethod_or_sstatus);
|
||||||
}
|
}
|
||||||
|
@ -35,14 +35,6 @@
|
|||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
|
|
||||||
#if defined(HAVE_PTHREAD)
|
|
||||||
#include <pthread.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
#include <openssl.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <qse/cmn/stdio.h>
|
#include <qse/cmn/stdio.h>
|
||||||
|
|
||||||
typedef struct htrd_xtn_t htrd_xtn_t;
|
typedef struct htrd_xtn_t htrd_xtn_t;
|
||||||
@ -90,19 +82,12 @@ int qse_httpd_init (qse_httpd_t* httpd, qse_mmgr_t* mmgr)
|
|||||||
httpd->mmgr = mmgr;
|
httpd->mmgr = mmgr;
|
||||||
httpd->listener.max = -1;
|
httpd->listener.max = -1;
|
||||||
|
|
||||||
#if defined(HAVE_PTHREAD)
|
|
||||||
pthread_mutex_init (&httpd->listener.mutex, QSE_NULL);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void qse_httpd_fini (qse_httpd_t* httpd)
|
void qse_httpd_fini (qse_httpd_t* httpd)
|
||||||
{
|
{
|
||||||
/* TODO */
|
/* TODO */
|
||||||
#if defined(HAVE_PTHREAD)
|
|
||||||
pthread_mutex_destroy (&httpd->listener.mutex);
|
|
||||||
#endif
|
|
||||||
free_listener_list (httpd, httpd->listener.list);
|
free_listener_list (httpd, httpd->listener.list);
|
||||||
httpd->listener.list = QSE_NULL;
|
httpd->listener.list = QSE_NULL;
|
||||||
}
|
}
|
||||||
@ -122,16 +107,6 @@ void qse_httpd_setoption (qse_httpd_t* httpd, int option)
|
|||||||
httpd->option = option;
|
httpd->option = option;
|
||||||
}
|
}
|
||||||
|
|
||||||
const qse_httpd_cbs_t* qse_httpd_getcbs (qse_httpd_t* httpd)
|
|
||||||
{
|
|
||||||
return httpd->cbs;
|
|
||||||
}
|
|
||||||
|
|
||||||
void qse_httpd_setcbs (qse_httpd_t* httpd, qse_httpd_cbs_t* cbs)
|
|
||||||
{
|
|
||||||
httpd->cbs = cbs;
|
|
||||||
}
|
|
||||||
|
|
||||||
QSE_INLINE void* qse_httpd_allocmem (qse_httpd_t* httpd, qse_size_t size)
|
QSE_INLINE void* qse_httpd_allocmem (qse_httpd_t* httpd, qse_size_t size)
|
||||||
{
|
{
|
||||||
void* ptr = QSE_MMGR_ALLOC (httpd->mmgr, size);
|
void* ptr = QSE_MMGR_ALLOC (httpd->mmgr, size);
|
||||||
@ -152,7 +127,7 @@ QSE_INLINE void qse_httpd_freemem (qse_httpd_t* httpd, void* ptr)
|
|||||||
QSE_MMGR_FREE (httpd->mmgr, ptr);
|
QSE_MMGR_FREE (httpd->mmgr, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static qse_httpd_task_t* enqueue_task_unlocked (
|
static qse_httpd_task_t* enqueue_task (
|
||||||
qse_httpd_t* httpd, qse_httpd_client_t* client,
|
qse_httpd_t* httpd, qse_httpd_client_t* client,
|
||||||
const qse_httpd_task_t* pred, const qse_httpd_task_t* task,
|
const qse_httpd_task_t* pred, const qse_httpd_task_t* task,
|
||||||
qse_size_t xtnsize)
|
qse_size_t xtnsize)
|
||||||
@ -215,30 +190,7 @@ static qse_httpd_task_t* enqueue_task_unlocked (
|
|||||||
return &node->task;
|
return &node->task;
|
||||||
}
|
}
|
||||||
|
|
||||||
static qse_httpd_task_t* enqueue_task_locked (
|
static QSE_INLINE int dequeue_task (
|
||||||
qse_httpd_t* httpd, qse_httpd_client_t* client,
|
|
||||||
const qse_httpd_task_t* pred, const qse_httpd_task_t* task,
|
|
||||||
qse_size_t xtnsize)
|
|
||||||
{
|
|
||||||
#if defined(HAVE_PTHREAD)
|
|
||||||
if (httpd->threaded)
|
|
||||||
{
|
|
||||||
qse_httpd_task_t* ret;
|
|
||||||
pthread_mutex_lock (&client->task.mutex);
|
|
||||||
ret = enqueue_task_unlocked (httpd, client, pred, task, xtnsize);
|
|
||||||
pthread_mutex_unlock (&client->task.mutex);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
#endif
|
|
||||||
return enqueue_task_unlocked (httpd, client, pred, task, xtnsize);
|
|
||||||
#if defined(HAVE_PTHREAD)
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static QSE_INLINE int dequeue_task_unlocked (
|
|
||||||
qse_httpd_t* httpd, qse_httpd_client_t* client)
|
qse_httpd_t* httpd, qse_httpd_client_t* client)
|
||||||
{
|
{
|
||||||
task_queue_node_t* node;
|
task_queue_node_t* node;
|
||||||
@ -265,35 +217,10 @@ static QSE_INLINE int dequeue_task_unlocked (
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int dequeue_task_locked (qse_httpd_t* httpd, qse_httpd_client_t* client)
|
static QSE_INLINE void purge_tasks (
|
||||||
|
qse_httpd_t* httpd, qse_httpd_client_t* client)
|
||||||
{
|
{
|
||||||
#if defined(HAVE_PTHREAD)
|
while (dequeue_task (httpd, client) == 0);
|
||||||
if (httpd->threaded)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
pthread_mutex_lock (&client->task.mutex);
|
|
||||||
ret = dequeue_task_unlocked (httpd, client);
|
|
||||||
pthread_mutex_unlock (&client->task.mutex);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
#endif
|
|
||||||
return dequeue_task_unlocked (httpd, client);
|
|
||||||
#if defined(HAVE_PTHREAD)
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
static void purge_tasks_locked (qse_httpd_t* httpd, qse_httpd_client_t* client)
|
|
||||||
{
|
|
||||||
#if defined(HAVE_PTHREAD)
|
|
||||||
if (httpd->threaded) pthread_mutex_lock (&client->task.mutex);
|
|
||||||
#endif
|
|
||||||
while (dequeue_task_unlocked (httpd, client) == 0);
|
|
||||||
#if defined(HAVE_PTHREAD)
|
|
||||||
if (httpd->threaded) pthread_mutex_unlock (&client->task.mutex);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int htrd_peek_request (qse_htrd_t* htrd, qse_htre_t* req)
|
static int htrd_peek_request (qse_htrd_t* htrd, qse_htre_t* req)
|
||||||
@ -458,11 +385,7 @@ static void delete_from_client_array (qse_httpd_t* httpd, int fd)
|
|||||||
client_array_t* array = &httpd->client.array;
|
client_array_t* array = &httpd->client.array;
|
||||||
if (array->data[fd].htrd)
|
if (array->data[fd].htrd)
|
||||||
{
|
{
|
||||||
purge_tasks_locked (httpd, &array->data[fd]);
|
purge_tasks (httpd, &array->data[fd]);
|
||||||
#if defined(HAVE_PTHREAD)
|
|
||||||
if (httpd->threaded)
|
|
||||||
pthread_mutex_destroy (&array->data[fd].task.mutex);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
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;
|
||||||
@ -538,11 +461,6 @@ static qse_httpd_client_t* insert_into_client_array (
|
|||||||
array->data[fd].local_addr = client->local_addr;
|
array->data[fd].local_addr = client->local_addr;
|
||||||
array->data[fd].remote_addr = client->remote_addr;
|
array->data[fd].remote_addr = client->remote_addr;
|
||||||
|
|
||||||
#if defined(HAVE_PTHREAD)
|
|
||||||
if (httpd->threaded)
|
|
||||||
pthread_mutex_init (&array->data[fd].task.mutex, QSE_NULL);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
xtn = (htrd_xtn_t*)qse_htrd_getxtn (array->data[fd].htrd);
|
xtn = (htrd_xtn_t*)qse_htrd_getxtn (array->data[fd].htrd);
|
||||||
xtn->client_index = fd;
|
xtn->client_index = fd;
|
||||||
xtn->httpd = httpd;
|
xtn->httpd = httpd;
|
||||||
@ -600,13 +518,7 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: too many client?\n"));
|
|||||||
flag = fcntl (clibuf.handle.i, F_GETFD);
|
flag = fcntl (clibuf.handle.i, F_GETFD);
|
||||||
if (flag >= 0) fcntl (clibuf.handle.i, F_SETFD, flag | FD_CLOEXEC);
|
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, &clibuf);
|
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)
|
if (client == QSE_NULL)
|
||||||
{
|
{
|
||||||
@ -638,109 +550,102 @@ httpd->cbs.on_error (httpd, l).... */
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int make_fd_set_from_client_array (
|
static int make_fd_set_from_client_array (
|
||||||
qse_httpd_t* httpd, fd_set* r, fd_set* w, int for_rdwr)
|
qse_httpd_t* httpd, fd_set* r, fd_set* w)
|
||||||
{
|
{
|
||||||
/* qse_http_loop() sets for_rdwr to true.
|
int fd, max;
|
||||||
* response_thread() sets for_rdwr to false.
|
client_array_t* ca;
|
||||||
*
|
qse_httpd_client_t* client;
|
||||||
* qse_http_loop()
|
|
||||||
* - accepts a new client connection
|
|
||||||
* - reads a client request
|
|
||||||
* - writes back a response to a client request if not threaded.
|
|
||||||
*
|
|
||||||
* response_thread()
|
|
||||||
* - writes back a response to a client request if threaded.
|
|
||||||
*/
|
|
||||||
|
|
||||||
int fd, max = -1;
|
ca = &httpd->client.array;
|
||||||
client_array_t* ca = &httpd->client.array;
|
|
||||||
|
|
||||||
if (for_rdwr)
|
/* qse_http_loop() needs to monitor listner handles
|
||||||
{
|
* to handle a new client connection. */
|
||||||
/* qse_http_loop() needs to monitor listner handles
|
max = httpd->listener.max;
|
||||||
* to handle a new client connection. */
|
*r = httpd->listener.set;
|
||||||
max = httpd->listener.max;
|
|
||||||
*r = httpd->listener.set;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
FD_ZERO (r);
|
|
||||||
|
|
||||||
#if defined(HAVE_PTHREAD)
|
|
||||||
/* select() in response_thread() needs to be aborted
|
|
||||||
* if it's blocking on a fd_set previously composed
|
|
||||||
* when a new task is enqueued. it can select() on new
|
|
||||||
* fd_set quickly.
|
|
||||||
*/
|
|
||||||
QSE_ASSERT (httpd->threaded);
|
|
||||||
FD_SET (httpd->client.pfd[0], r);
|
|
||||||
max = httpd->client.pfd[0];
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
FD_ZERO (w);
|
FD_ZERO (w);
|
||||||
|
|
||||||
for (fd = 0; fd < ca->capa; fd++)
|
for (fd = 0; fd < ca->capa; fd++)
|
||||||
{
|
{
|
||||||
if (ca->data[fd].htrd)
|
client = &ca->data[fd];
|
||||||
|
|
||||||
|
if (!client->htrd) continue;
|
||||||
|
|
||||||
|
if (client->bad)
|
||||||
{
|
{
|
||||||
if (!ca->data[fd].bad)
|
/* add a client-side handle to the write set
|
||||||
{
|
* if the client is already marked bad */
|
||||||
if (for_rdwr)
|
FD_SET (client->handle.i, w);
|
||||||
{
|
if (client->handle.i > max) max = client->handle.i;
|
||||||
/* add a client-side handle to the read set
|
}
|
||||||
* only for qse_httpd_loop(). */
|
else
|
||||||
FD_SET (ca->data[fd].handle.i, r);
|
{
|
||||||
if (ca->data[fd].handle.i > max) max = ca->data[fd].handle.i;
|
/* add a client-side handle to the read set */
|
||||||
}
|
FD_SET (client->handle.i, r);
|
||||||
|
if (client->handle.i > max) max = client->handle.i;
|
||||||
|
qse_printf (QSE_T(">>>>ADDING CLIENT HANDLE %d\n"), client->handle.i);
|
||||||
|
|
||||||
if (!httpd->threaded || !for_rdwr)
|
/* trigger[0] is a handle to monitor to check
|
||||||
|
* if there is data avaiable to read to write back to
|
||||||
|
* the client. qse_httpd_loop() needs to monitor
|
||||||
|
* trigger 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 (client->task.queue.head)
|
||||||
|
{
|
||||||
|
qse_httpd_task_t* task = &client->task.queue.head->task;
|
||||||
|
int has_trigger = 0;
|
||||||
|
|
||||||
|
if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_READ)
|
||||||
{
|
{
|
||||||
/* trigger[0] is a handle to monitor to check
|
/* if a trigger is available, add it to the read set also. */
|
||||||
* if there is data avaiable to read to write back to
|
qse_printf (QSE_T(">>>>%s ADDING TRIGGER[0] %d\n"),
|
||||||
* the client. if it is not threaded, qse_httpd_loop()
|
(task->trigger[0].i == client->handle.i? QSE_T("NOT"): QSE_T("")),
|
||||||
* needs to monitor trigger handles. if it is threaded,
|
task->trigger[0].i);
|
||||||
* response_thread() needs to monitor these handles.
|
if (task->trigger[0].i != client->handle.i)
|
||||||
*
|
|
||||||
* 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;
|
FD_SET (task->trigger[0].i, r);
|
||||||
if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_READ)
|
if (task->trigger[0].i > max) max = task->trigger[0].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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
has_trigger = 1;
|
||||||
|
}
|
||||||
|
if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_RELAY)
|
||||||
|
{
|
||||||
|
qse_printf (QSE_T(">>>>%s ADDING TRIGGER[1] %d\n"),
|
||||||
|
(task->trigger[1].i == client->handle.i? QSE_T("NOT"): QSE_T("")),
|
||||||
|
task->trigger[1].i);
|
||||||
|
if (task->trigger[1].i != client->handle.i)
|
||||||
|
{
|
||||||
|
FD_SET (task->trigger[1].i, r);
|
||||||
|
if (task->trigger[1].i > max) max = task->trigger[1].i;
|
||||||
|
}
|
||||||
|
has_trigger = 1;
|
||||||
|
}
|
||||||
|
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[2] %d\n"), task->trigger[2].i);
|
||||||
|
FD_SET (task->trigger[2].i, w);
|
||||||
|
if (task->trigger[2].i > max) max = task->trigger[2].i;
|
||||||
|
has_trigger = 1;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (ca->data[fd].bad ||
|
if (!has_trigger)
|
||||||
(ca->data[fd].task.queue.head && !(ca->data[fd].task.queue.head->task.trigger_mask & QSE_HTTPD_TASK_TRIGGER_READ)))
|
{
|
||||||
{
|
/* there is a task to perform. but no triggers
|
||||||
/* add a client-side handle to the write set
|
* were specified. if the client-side handle is
|
||||||
* if the client is already marked bad or
|
* available for writing, arrange to perform the
|
||||||
* the current task enqueued didn't specify a trigger.
|
* task in the main loop by adding the client-side
|
||||||
*
|
* handle to the write set. */
|
||||||
* if the task doesn't have a trigger, i perform
|
qse_printf (QSE_T(">>>>ADDING CLIENT CONNECTION %d TO WRITE\n"), client->handle.i);
|
||||||
* the task so long as the client side-handle is
|
FD_SET (client->handle.i, w);
|
||||||
* available for writing in the main loop.
|
if (client->handle.i > max) max = client->handle.i;
|
||||||
*/
|
}
|
||||||
FD_SET (ca->data[fd].handle.i, w);
|
|
||||||
if (ca->data[fd].handle.i > max) max = ca->data[fd].handle.i;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return max;
|
return max;
|
||||||
@ -758,144 +663,16 @@ static void perform_task (qse_httpd_t* httpd, qse_httpd_client_t* client)
|
|||||||
n = node->task.main (httpd, client, &node->task);
|
n = node->task.main (httpd, client, &node->task);
|
||||||
if (n <= -1)
|
if (n <= -1)
|
||||||
{
|
{
|
||||||
dequeue_task_locked (httpd, client);
|
dequeue_task (httpd, client);
|
||||||
/*shutdown (client->handle.i, SHUT_RDWR);*/
|
/*shutdown (client->handle.i, SHUT_RDWR);*/
|
||||||
client->bad = 1;
|
client->bad = 1;
|
||||||
}
|
}
|
||||||
else if (n == 0)
|
else if (n == 0)
|
||||||
{
|
{
|
||||||
dequeue_task_locked (httpd, client);
|
dequeue_task (httpd, client);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(HAVE_PTHREAD)
|
|
||||||
static void* response_thread (void* arg)
|
|
||||||
{
|
|
||||||
qse_httpd_t* httpd = (qse_httpd_t*)arg;
|
|
||||||
|
|
||||||
while (!httpd->stopreq)
|
|
||||||
{
|
|
||||||
int n, max, fd;
|
|
||||||
fd_set r, w;
|
|
||||||
struct timeval tv;
|
|
||||||
|
|
||||||
pthread_mutex_lock (&httpd->client.mutex);
|
|
||||||
max = make_fd_set_from_client_array (httpd, &r, &w, 0);
|
|
||||||
pthread_mutex_unlock (&httpd->client.mutex);
|
|
||||||
|
|
||||||
while (max == -1 && !httpd->stopreq)
|
|
||||||
{
|
|
||||||
struct timeval now;
|
|
||||||
struct timespec timeout;
|
|
||||||
|
|
||||||
pthread_mutex_lock (&httpd->client.mutex);
|
|
||||||
|
|
||||||
gettimeofday (&now, QSE_NULL);
|
|
||||||
timeout.tv_sec = now.tv_sec + 1;
|
|
||||||
timeout.tv_nsec = now.tv_usec * 1000;
|
|
||||||
|
|
||||||
pthread_cond_timedwait (
|
|
||||||
&httpd->client.cond, &httpd->client.mutex, &timeout);
|
|
||||||
max = make_fd_set_from_client_array (httpd, &r, &w, 0);
|
|
||||||
|
|
||||||
pthread_mutex_unlock (&httpd->client.mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (httpd->stopreq) break;
|
|
||||||
|
|
||||||
tv.tv_sec = 1;
|
|
||||||
tv.tv_usec = 0;
|
|
||||||
|
|
||||||
n = select (max + 1, &r, &w, QSE_NULL, &tv);
|
|
||||||
if (n <= -1)
|
|
||||||
{
|
|
||||||
/*if (errno == EINTR) continue; */
|
|
||||||
qse_fprintf (QSE_STDERR, QSE_T("Error: select returned failure - %hs\n"), strerror(errno));
|
|
||||||
/* break; */
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (n == 0)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FD_ISSET (httpd->client.pfd[0], &r))
|
|
||||||
{
|
|
||||||
qse_mchar_t dummy;
|
|
||||||
QSE_READ (httpd->client.pfd[0], &dummy, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (fd = 0; fd < httpd->client.array.capa; fd++)
|
|
||||||
{
|
|
||||||
qse_httpd_client_t* client = &httpd->client.array.data[fd];
|
|
||||||
|
|
||||||
if (!client->htrd) continue;
|
|
||||||
|
|
||||||
if (client->bad)
|
|
||||||
{
|
|
||||||
/*shutdown (client->handle.i, SHUT_RDWR);*/
|
|
||||||
pthread_mutex_lock (&httpd->client.mutex);
|
|
||||||
delete_from_client_array (httpd, fd);
|
|
||||||
pthread_mutex_unlock (&httpd->client.mutex);
|
|
||||||
}
|
|
||||||
else if (client->task.queue.head)
|
|
||||||
{
|
|
||||||
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;
|
|
||||||
FD_ZERO (&w);
|
|
||||||
FD_SET (client->handle.i, &w);
|
|
||||||
n = select (client->handle.i + 1, QSE_NULL, &w, QSE_NULL, &tv);
|
|
||||||
if (n > 0 && FD_ISSET(client->handle.i, &w))
|
|
||||||
{
|
|
||||||
perform_task (httpd, client);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_exit (QSE_NULL);
|
|
||||||
return QSE_NULL;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static int read_from_client (qse_httpd_t* httpd, qse_httpd_client_t* client)
|
static int read_from_client (qse_httpd_t* httpd, qse_httpd_client_t* client)
|
||||||
{
|
{
|
||||||
qse_mchar_t buf[1024];
|
qse_mchar_t buf[1024];
|
||||||
@ -938,6 +715,7 @@ qse_fprintf (QSE_STDERR, QSE_T("Debug: connection closed %d\n"), client->handle.
|
|||||||
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;
|
||||||
|
qse_printf (QSE_T("!!!!!FEEDING [%.*hs]\n"), (int)m, buf);
|
||||||
if (qse_htrd_feed (client->htrd, buf, m) <= -1)
|
if (qse_htrd_feed (client->htrd, buf, m) <= -1)
|
||||||
{
|
{
|
||||||
if (httpd->errnum == QSE_HTTPD_ENOERR)
|
if (httpd->errnum == QSE_HTTPD_ENOERR)
|
||||||
@ -955,13 +733,8 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: http error while processing \n"));
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int qse_httpd_loop (qse_httpd_t* httpd, qse_httpd_cbs_t* cbs, int threaded)
|
int qse_httpd_loop (qse_httpd_t* httpd, qse_httpd_cbs_t* cbs)
|
||||||
{
|
{
|
||||||
#if defined(HAVE_PTHREAD)
|
|
||||||
pthread_t response_thread_id;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
httpd->threaded = 0;
|
|
||||||
httpd->stopreq = 0;
|
httpd->stopreq = 0;
|
||||||
httpd->cbs = cbs;
|
httpd->cbs = cbs;
|
||||||
|
|
||||||
@ -984,43 +757,6 @@ int qse_httpd_loop (qse_httpd_t* httpd, qse_httpd_cbs_t* cbs, int threaded)
|
|||||||
|
|
||||||
init_client_array (httpd);
|
init_client_array (httpd);
|
||||||
|
|
||||||
#if defined(HAVE_PTHREAD)
|
|
||||||
/* start the response sender as a thread */
|
|
||||||
if (threaded)
|
|
||||||
{
|
|
||||||
if (QSE_PIPE(httpd->client.pfd) == 0)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
for (i = 0; i < 2; i++)
|
|
||||||
{
|
|
||||||
int flags = QSE_FCNTL (httpd->client.pfd[i], F_GETFD, 0);
|
|
||||||
if (flags >= 0)
|
|
||||||
QSE_FCNTL (httpd->client.pfd[i], F_SETFD, flags | FD_CLOEXEC);
|
|
||||||
}
|
|
||||||
|
|
||||||
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]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
while (!httpd->stopreq)
|
while (!httpd->stopreq)
|
||||||
{
|
{
|
||||||
int n, max, fd;
|
int n, max, fd;
|
||||||
@ -1031,14 +767,7 @@ int qse_httpd_loop (qse_httpd_t* httpd, qse_httpd_cbs_t* cbs, int threaded)
|
|||||||
tv.tv_sec = 1;
|
tv.tv_sec = 1;
|
||||||
tv.tv_usec = 0;
|
tv.tv_usec = 0;
|
||||||
|
|
||||||
#if defined(HAVE_PTHREAD)
|
max = make_fd_set_from_client_array (httpd, &r, &w);
|
||||||
if (httpd->threaded) pthread_mutex_lock (&httpd->client.mutex);
|
|
||||||
#endif
|
|
||||||
max = make_fd_set_from_client_array (httpd, &r, &w, 1);
|
|
||||||
#if defined(HAVE_PTHREAD)
|
|
||||||
if (httpd->threaded) pthread_mutex_unlock (&httpd->client.mutex);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
n = select (max + 1, &r, &w, QSE_NULL, &tv);
|
n = select (max + 1, &r, &w, QSE_NULL, &tv);
|
||||||
if (n <= -1)
|
if (n <= -1)
|
||||||
{
|
{
|
||||||
@ -1090,9 +819,7 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: select returned failure\n"));
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/*pthread_mutex_lock (&httpd->client.mutex);*/
|
|
||||||
delete_from_client_array (httpd, fd);
|
delete_from_client_array (httpd, fd);
|
||||||
/*pthread_mutex_unlock (&httpd->client.mutex);*/
|
|
||||||
continue; /* don't need to go to the writing part */
|
continue; /* don't need to go to the writing part */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1104,9 +831,7 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: select returned failure\n"));
|
|||||||
if (client->bad)
|
if (client->bad)
|
||||||
{
|
{
|
||||||
/*shutdown (client->handle.i, SHUT_RDWR);*/
|
/*shutdown (client->handle.i, SHUT_RDWR);*/
|
||||||
/*pthread_mutex_lock (&httpd->client.mutex);*/
|
|
||||||
delete_from_client_array (httpd, fd);
|
delete_from_client_array (httpd, fd);
|
||||||
/*pthread_mutex_unlock (&httpd->client.mutex);*/
|
|
||||||
}
|
}
|
||||||
else if (client->task.queue.head)
|
else if (client->task.queue.head)
|
||||||
{
|
{
|
||||||
@ -1116,9 +841,11 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: select returned failure\n"));
|
|||||||
task = &client->task.queue.head->task;
|
task = &client->task.queue.head->task;
|
||||||
task->trigger_mask &=
|
task->trigger_mask &=
|
||||||
~(QSE_HTTPD_TASK_TRIGGER_READABLE |
|
~(QSE_HTTPD_TASK_TRIGGER_READABLE |
|
||||||
|
QSE_HTTPD_TASK_TRIGGER_RELAYABLE |
|
||||||
QSE_HTTPD_TASK_TRIGGER_WRITABLE);
|
QSE_HTTPD_TASK_TRIGGER_WRITABLE);
|
||||||
|
|
||||||
if (!(task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_READ) &&
|
if (!(task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_READ) &&
|
||||||
|
!(task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_RELAY) &&
|
||||||
!(task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_WRITE))
|
!(task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_WRITE))
|
||||||
{
|
{
|
||||||
/* no trigger set. set the flag to
|
/* no trigger set. set the flag to
|
||||||
@ -1130,15 +857,20 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: select returned failure\n"));
|
|||||||
if ((task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_READ) &&
|
if ((task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_READ) &&
|
||||||
FD_ISSET(task->trigger[0].i, &r))
|
FD_ISSET(task->trigger[0].i, &r))
|
||||||
{
|
{
|
||||||
/* set the flag to readable */
|
|
||||||
task->trigger_mask |= QSE_HTTPD_TASK_TRIGGER_READABLE;
|
task->trigger_mask |= QSE_HTTPD_TASK_TRIGGER_READABLE;
|
||||||
perform = 1;
|
perform = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_WRITE) &&
|
if ((task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_RELAY) &&
|
||||||
FD_ISSET(task->trigger[1].i, &w))
|
FD_ISSET(task->trigger[1].i, &r))
|
||||||
|
{
|
||||||
|
task->trigger_mask |= QSE_HTTPD_TASK_TRIGGER_RELAYABLE;
|
||||||
|
perform = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_RELAY) &&
|
||||||
|
FD_ISSET(task->trigger[2].i, &w))
|
||||||
{
|
{
|
||||||
/* set the flag to writable */
|
|
||||||
task->trigger_mask |= QSE_HTTPD_TASK_TRIGGER_WRITABLE;
|
task->trigger_mask |= QSE_HTTPD_TASK_TRIGGER_WRITABLE;
|
||||||
perform = 1;
|
perform = 1;
|
||||||
}
|
}
|
||||||
@ -1146,15 +878,11 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: select returned failure\n"));
|
|||||||
|
|
||||||
if (perform)
|
if (perform)
|
||||||
{
|
{
|
||||||
tv.tv_sec = 0;
|
/* TODO: error handling -> writable() returns <= -1 */
|
||||||
tv.tv_usec = 0;
|
/* TODO: though the client side is not writable, can't i still exeucte the task?
|
||||||
FD_ZERO (&w);
|
* if the task needs to transfer anything yet.. it can do that.
|
||||||
FD_SET (client->handle.i, &w);
|
* i probably need a new trigger type??? */
|
||||||
n = select (client->handle.i + 1, QSE_NULL, &w, QSE_NULL, &tv);
|
if (httpd->cbs->mux.writable (httpd, client->handle, 0) >= 1)
|
||||||
|
|
||||||
/* TODO: logging if n == -1 */
|
|
||||||
|
|
||||||
if (n > 0 && FD_ISSET(client->handle.i, &w))
|
|
||||||
perform_task (httpd, client);
|
perform_task (httpd, client);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1162,17 +890,6 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: select returned failure\n"));
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(HAVE_PTHREAD)
|
|
||||||
if (httpd->threaded)
|
|
||||||
{
|
|
||||||
pthread_join (response_thread_id, QSE_NULL);
|
|
||||||
pthread_cond_destroy (&httpd->client.cond);
|
|
||||||
pthread_mutex_destroy (&httpd->client.mutex);
|
|
||||||
QSE_CLOSE (httpd->client.pfd[1]);
|
|
||||||
QSE_CLOSE (httpd->client.pfd[0]);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
fini_client_array (httpd);
|
fini_client_array (httpd);
|
||||||
deactivate_listeners (httpd);
|
deactivate_listeners (httpd);
|
||||||
return 0;
|
return 0;
|
||||||
@ -1370,34 +1087,20 @@ static int delete_listeners (qse_httpd_t* httpd, const qse_char_t* uri)
|
|||||||
|
|
||||||
int qse_httpd_addlistener (qse_httpd_t* httpd, const qse_char_t* uri)
|
int qse_httpd_addlistener (qse_httpd_t* httpd, const qse_char_t* uri)
|
||||||
{
|
{
|
||||||
#if defined(HAVE_PTHREAD)
|
|
||||||
int n;
|
|
||||||
pthread_mutex_lock (&httpd->listener.mutex);
|
|
||||||
n = add_listener (httpd, uri);
|
|
||||||
pthread_mutex_unlock (&httpd->listener.mutex);
|
|
||||||
return n;
|
|
||||||
#else
|
|
||||||
return add_listener (httpd, uri);
|
return add_listener (httpd, uri);
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
int qse_httpd_dellistener (qse_httpd_t* httpd, const qse_char_t* uri)
|
int qse_httpd_dellistener (qse_httpd_t* httpd, const qse_char_t* uri)
|
||||||
{
|
{
|
||||||
int n;
|
return delete_listeners (httpd, uri);
|
||||||
pthread_mutex_lock (&httpd->listener.mutex);
|
|
||||||
n = delete_listeners (httpd, uri);
|
|
||||||
pthread_mutex_unlock (&httpd->listener.mutex);
|
|
||||||
return n;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void qse_httpd_clearlisteners (qse_httpd_t* httpd)
|
void qse_httpd_clearlisteners (qse_httpd_t* httpd)
|
||||||
{
|
{
|
||||||
pthread_mutex_lock (&httpd->listener.mutex);
|
|
||||||
deactivate_listeners (httpd);
|
deactivate_listeners (httpd);
|
||||||
free_listener_list (httpd, httpd->listener.list);
|
free_listener_list (httpd, httpd->listener.list);
|
||||||
httpd->listener.list = QSE_NULL;
|
httpd->listener.list = QSE_NULL;
|
||||||
pthread_mutex_unlock (&httpd->listener.mutex);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -1407,19 +1110,8 @@ qse_httpd_task_t* qse_httpd_entask (
|
|||||||
qse_size_t xtnsize)
|
qse_size_t xtnsize)
|
||||||
{
|
{
|
||||||
qse_httpd_task_t* ret;
|
qse_httpd_task_t* ret;
|
||||||
ret = enqueue_task_locked (httpd, client, pred, task, xtnsize);
|
ret = enqueue_task (httpd, client, pred, task, xtnsize);
|
||||||
if (ret == QSE_NULL) client->bad = 1; /* mark this client bad */
|
if (ret == QSE_NULL) client->bad = 1; /* mark this client bad */
|
||||||
#if defined(HAVE_PTHREAD)
|
|
||||||
else if (httpd->threaded)
|
|
||||||
{
|
|
||||||
static qse_byte_t dummy = 0x01;
|
|
||||||
/* write to the pipe to wake up select() in
|
|
||||||
* the response thread if it was blocking. */
|
|
||||||
QSE_WRITE (httpd->client.pfd[1], &dummy, 1);
|
|
||||||
|
|
||||||
pthread_cond_signal (&httpd->client.cond);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1433,10 +1125,12 @@ 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)
|
void qse_httpd_discardcontent (qse_httpd_t* httpd, qse_htre_t* req)
|
||||||
{
|
{
|
||||||
req->flags |= QSE_HTRE_DISCARDED;
|
qse_htre_discardcontent (req);
|
||||||
/* clear the content buffer in case it has received contents
|
}
|
||||||
* partially already */
|
|
||||||
qse_mbs_clear (&req->content);
|
void qse_httpd_completecontent (qse_httpd_t* httpd, qse_htre_t* req)
|
||||||
|
{
|
||||||
|
qse_htre_completecontent (req);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -391,7 +391,7 @@ static qse_httpd_task_t* entask_error (
|
|||||||
|
|
||||||
qse_httpd_task_t* qse_httpd_entaskerror (
|
qse_httpd_task_t* qse_httpd_entaskerror (
|
||||||
qse_httpd_t* httpd, qse_httpd_client_t* client,
|
qse_httpd_t* httpd, qse_httpd_client_t* client,
|
||||||
const qse_httpd_task_t* task, int code, const qse_htre_t* req)
|
const qse_httpd_task_t* task, int code, qse_htre_t* req)
|
||||||
{
|
{
|
||||||
return entask_error (httpd, client, task, code, qse_htre_getversion(req), req->attr.keepalive);
|
return entask_error (httpd, client, task, code, qse_htre_getversion(req), req->attr.keepalive);
|
||||||
}
|
}
|
||||||
@ -399,9 +399,9 @@ qse_httpd_task_t* qse_httpd_entaskerror (
|
|||||||
/*------------------------------------------------------------------------*/
|
/*------------------------------------------------------------------------*/
|
||||||
qse_httpd_task_t* qse_httpd_entaskcontinue (
|
qse_httpd_task_t* qse_httpd_entaskcontinue (
|
||||||
qse_httpd_t* httpd, qse_httpd_client_t* client,
|
qse_httpd_t* httpd, qse_httpd_client_t* client,
|
||||||
const qse_httpd_task_t* task, const qse_htre_t* req)
|
const qse_httpd_task_t* task, qse_htre_t* req)
|
||||||
{
|
{
|
||||||
qse_http_version_t* version = qse_htre_getversion(req);
|
const qse_http_version_t* version = qse_htre_getversion(req);
|
||||||
return qse_httpd_entaskformat (
|
return qse_httpd_entaskformat (
|
||||||
httpd, client, task,
|
httpd, client, task,
|
||||||
QSE_MT("HTTP/%d.%d 100 Continue\r\n\r\n"),
|
QSE_MT("HTTP/%d.%d 100 Continue\r\n\r\n"),
|
||||||
@ -866,13 +866,17 @@ static int task_init_path (
|
|||||||
}
|
}
|
||||||
|
|
||||||
static QSE_INLINE int task_main_path_file (
|
static QSE_INLINE int task_main_path_file (
|
||||||
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task, qse_foff_t filesize)
|
qse_httpd_t* httpd, qse_httpd_client_t* client,
|
||||||
|
qse_httpd_task_t* task, qse_foff_t filesize)
|
||||||
{
|
{
|
||||||
task_path_t* data = (task_path_t*)task->ctx;
|
task_path_t* data = (task_path_t*)task->ctx;
|
||||||
qse_httpd_task_t* x = task;
|
qse_httpd_task_t* x = task;
|
||||||
qse_ubi_t handle;
|
qse_ubi_t handle;
|
||||||
int oflags;
|
int oflags;
|
||||||
|
|
||||||
|
/* TODO: if you should deal with files on a network-mounted drive,
|
||||||
|
setting a trigger or non-blocking I/O are needed. */
|
||||||
|
|
||||||
/* when it comes to the file size, using fstat after opening
|
/* when it comes to the file size, using fstat after opening
|
||||||
* can be more accurate. but this function uses information
|
* can be more accurate. but this function uses information
|
||||||
* set into the task context before the call to this function */
|
* set into the task context before the call to this function */
|
||||||
@ -886,7 +890,8 @@ qse_printf (QSE_T("opening file %hs\n"), data->name);
|
|||||||
handle.i = QSE_OPEN (data->name, oflags, 0);
|
handle.i = QSE_OPEN (data->name, oflags, 0);
|
||||||
if (handle.i <= -1)
|
if (handle.i <= -1)
|
||||||
{
|
{
|
||||||
x = entask_error (httpd, client, x, 404, &data->version, data->keepalive);
|
x = entask_error (
|
||||||
|
httpd, client, x, 404, &data->version, data->keepalive);
|
||||||
goto no_file_send;
|
goto no_file_send;
|
||||||
}
|
}
|
||||||
oflags = QSE_FCNTL (handle.i, F_GETFD, 0);
|
oflags = QSE_FCNTL (handle.i, F_GETFD, 0);
|
||||||
@ -907,7 +912,8 @@ qse_printf (QSE_T("opening file %hs\n"), data->name);
|
|||||||
|
|
||||||
if (data->range.from >= filesize)
|
if (data->range.from >= filesize)
|
||||||
{
|
{
|
||||||
x = entask_error (httpd, client, x, 416, &data->version, data->keepalive);
|
x = entask_error (
|
||||||
|
httpd, client, x, 416, &data->version, data->keepalive);
|
||||||
goto no_file_send;
|
goto no_file_send;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1104,7 +1110,7 @@ qse_httpd_task_t* qse_httpd_entaskpath (
|
|||||||
qse_httpd_client_t* client,
|
qse_httpd_client_t* client,
|
||||||
const qse_httpd_task_t* pred,
|
const qse_httpd_task_t* pred,
|
||||||
const qse_mchar_t* name,
|
const qse_mchar_t* name,
|
||||||
const qse_htre_t* req)
|
qse_htre_t* req)
|
||||||
{
|
{
|
||||||
qse_httpd_task_t task;
|
qse_httpd_task_t task;
|
||||||
task_path_t data;
|
task_path_t data;
|
||||||
@ -1143,7 +1149,7 @@ typedef struct task_cgi_arg_t task_cgi_arg_t;
|
|||||||
struct task_cgi_arg_t
|
struct task_cgi_arg_t
|
||||||
{
|
{
|
||||||
const qse_mchar_t* path;
|
const qse_mchar_t* path;
|
||||||
const qse_htre_t* req;
|
qse_htre_t* req;
|
||||||
int nph;
|
int nph;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1270,7 +1276,7 @@ static int cgi_htrd_peek_request (qse_htrd_t* htrd, qse_htre_t* req)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
qse_mchar_t* location;
|
const qse_mchar_t* location;
|
||||||
qse_mchar_t buf[128];
|
qse_mchar_t buf[128];
|
||||||
|
|
||||||
location = qse_htre_getheaderval (req, QSE_MT("Location"));
|
location = qse_htre_getheaderval (req, QSE_MT("Location"));
|
||||||
@ -1364,11 +1370,10 @@ static qse_htrd_recbs_t cgi_htrd_cbs =
|
|||||||
|
|
||||||
static qse_env_t* makecgienv (
|
static qse_env_t* makecgienv (
|
||||||
qse_httpd_t* httpd, qse_httpd_client_t* client,
|
qse_httpd_t* httpd, qse_httpd_client_t* client,
|
||||||
const qse_htre_t* req, const qse_mchar_t* path)
|
const qse_htre_t* req, const qse_mchar_t* path, qse_size_t content_length)
|
||||||
{
|
{
|
||||||
/* TODO: error check */
|
/* TODO: error check */
|
||||||
qse_env_t* env;
|
qse_env_t* env;
|
||||||
cgi_req_hdr_ctx_t ctx;
|
|
||||||
|
|
||||||
env = qse_env_open (httpd->mmgr, 0, 0);
|
env = qse_env_open (httpd->mmgr, 0, 0);
|
||||||
if (env == QSE_NULL) goto oops;
|
if (env == QSE_NULL) goto oops;
|
||||||
@ -1404,13 +1409,10 @@ qse_mbsxncpy (tmp, QSE_COUNTOF(tmp), qse_htre_getqpathptr(req), qse_htre_getqpat
|
|||||||
qse_env_insertmbs (
|
qse_env_insertmbs (
|
||||||
env, QSE_MT("REQUEST_METHOD"), qse_htre_getqmethodname(req));
|
env, QSE_MT("REQUEST_METHOD"), qse_htre_getqmethodname(req));
|
||||||
|
|
||||||
|
|
||||||
if (req->attr.content_length_set)
|
|
||||||
{
|
{
|
||||||
qse_mchar_t tmp[64];
|
qse_mchar_t tmp[64];
|
||||||
qse_fmtuintmaxtombs (
|
qse_fmtuintmaxtombs (
|
||||||
tmp, QSE_COUNTOF(tmp),
|
tmp, QSE_COUNTOF(tmp), content_length, 10,
|
||||||
req->attr.content_length, 10,
|
|
||||||
-1, QSE_MT('\0'), QSE_NULL);
|
-1, QSE_MT('\0'), QSE_NULL);
|
||||||
qse_env_insertmbs (env, QSE_MT("CONTENT_LENGTH"), tmp);
|
qse_env_insertmbs (env, QSE_MT("CONTENT_LENGTH"), tmp);
|
||||||
}
|
}
|
||||||
@ -1507,6 +1509,9 @@ static int cgi_snatch_content (
|
|||||||
{
|
{
|
||||||
task_cgi_t* cgi = (task_cgi_t*)ctx;
|
task_cgi_t* cgi = (task_cgi_t*)ctx;
|
||||||
|
|
||||||
|
if (ptr) qse_printf (QSE_T("!!!SNATCHING [%.*hs]\n"), len, ptr);
|
||||||
|
else qse_printf (QSE_T("!!!SNATCHING DONE\n"));
|
||||||
|
|
||||||
if (ptr == QSE_NULL)
|
if (ptr == QSE_NULL)
|
||||||
{
|
{
|
||||||
/* request ended. this could be a real end or
|
/* request ended. this could be a real end or
|
||||||
@ -1514,19 +1519,20 @@ static int cgi_snatch_content (
|
|||||||
QSE_ASSERT (len == 0);
|
QSE_ASSERT (len == 0);
|
||||||
cgi->req = QSE_NULL;
|
cgi->req = QSE_NULL;
|
||||||
}
|
}
|
||||||
else
|
else if (!cgi->reqfwderr)
|
||||||
{
|
{
|
||||||
/* push the contents to the own buffer */
|
/* push the contents to the own buffer */
|
||||||
if (qse_mbs_ncat (cgi->reqcon, ptr, len) == (qse_size_t)-1)
|
if (qse_mbs_ncat (cgi->reqcon, ptr, len) == (qse_size_t)-1)
|
||||||
{
|
{
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
qse_printf (QSE_T("!!!SNACHED [%.*hs]\n"), len, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int cgi_forward_content (qse_httpd_task_t* task)
|
static void cgi_forward_content (qse_httpd_t* httpd, qse_httpd_task_t* task)
|
||||||
{
|
{
|
||||||
task_cgi_t* cgi = (task_cgi_t*)task->ctx;
|
task_cgi_t* cgi = (task_cgi_t*)task->ctx;
|
||||||
|
|
||||||
@ -1534,50 +1540,71 @@ static int cgi_forward_content (qse_httpd_task_t* task)
|
|||||||
|
|
||||||
if (QSE_MBS_LEN(cgi->reqcon) > 0)
|
if (QSE_MBS_LEN(cgi->reqcon) > 0)
|
||||||
{
|
{
|
||||||
qse_ssize_t n;
|
if (cgi->reqfwderr)
|
||||||
|
|
||||||
if (!cgi->reqfwderr)
|
|
||||||
{
|
{
|
||||||
qse_printf (QSE_T("@@@@@@@WRITING %d bytes TO CGI\n"), (int)QSE_MBS_LEN(cgi->reqcon));
|
qse_mbs_clear (cgi->reqcon);
|
||||||
n = qse_pio_write (
|
}
|
||||||
cgi->pio, QSE_PIO_IN,
|
else
|
||||||
QSE_MBS_PTR(cgi->reqcon),
|
{
|
||||||
QSE_MBS_LEN(cgi->reqcon)
|
qse_ubi_t handle;
|
||||||
);
|
qse_ssize_t n;
|
||||||
|
|
||||||
|
/* TODO: handle = qse_pio_getubihandle(); */
|
||||||
|
handle.i = qse_pio_gethandle (cgi->pio, QSE_PIO_IN);
|
||||||
|
|
||||||
|
n = httpd->cbs->mux.writable (httpd, handle, 0);
|
||||||
|
if (n >= 1)
|
||||||
|
{
|
||||||
|
/* writable */
|
||||||
|
qse_printf (QSE_T("@@@@@@@@@@WRITING[%.*hs]\n"),
|
||||||
|
(int)QSE_MBS_LEN(cgi->reqcon),
|
||||||
|
QSE_MBS_PTR(cgi->reqcon));
|
||||||
|
n = qse_pio_write (
|
||||||
|
cgi->pio, QSE_PIO_IN,
|
||||||
|
QSE_MBS_PTR(cgi->reqcon),
|
||||||
|
QSE_MBS_LEN(cgi->reqcon)
|
||||||
|
);
|
||||||
|
/* TODO: improve performance.. instead of copying the remaing part to the head all the time..
|
||||||
|
grow the buffer to a certain limit. */
|
||||||
|
if (n > 0) qse_mbs_del (cgi->reqcon, 0, n);
|
||||||
|
}
|
||||||
|
|
||||||
if (n <= -1)
|
if (n <= -1)
|
||||||
{
|
{
|
||||||
qse_printf (QSE_T("@@@@@@@@WRITE TO CGI FAILED\n"));
|
qse_printf (QSE_T("@@@@@@@@WRITE TO CGI FAILED\n"));
|
||||||
/* TODO: logging ... */
|
/* TODO: logging ... */
|
||||||
cgi->reqfwderr = 1;
|
cgi->reqfwderr = 1;
|
||||||
if (cgi->req) cgi->req->flags |= QSE_HTRE_DISCARDED;
|
qse_mbs_clear (cgi->reqcon);
|
||||||
|
if (cgi->req) qse_htre_discardcontent (cgi->req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 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)
|
else if (cgi->req == QSE_NULL)
|
||||||
{
|
{
|
||||||
/* no more request content */
|
/* no more request content */
|
||||||
qse_printf (QSE_T("@@@@@@@@NOTHING MORE TO WRITE TO CGI\n"));
|
qse_printf (QSE_T("@@@@@@@@NOTHING MORE TO WRITE TO CGI\n"));
|
||||||
task->trigger_mask &=
|
task->trigger_mask &=
|
||||||
~(QSE_HTTPD_TASK_TRIGGER_WRITE |
|
~(QSE_HTTPD_TASK_TRIGGER_RELAY |
|
||||||
QSE_HTTPD_TASK_TRIGGER_WRITABLE);
|
QSE_HTTPD_TASK_TRIGGER_RELAYABLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int task_init_cgi (
|
static int task_init_cgi (
|
||||||
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
|
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
|
||||||
{
|
{
|
||||||
task_cgi_t* cgi = (task_cgi_t*)qse_httpd_gettaskxtn (httpd, task);
|
task_cgi_t* cgi;
|
||||||
task_cgi_arg_t* arg = (task_cgi_arg_t*)task->ctx;
|
task_cgi_arg_t* arg;
|
||||||
|
qse_size_t content_length;
|
||||||
|
qse_size_t len;
|
||||||
|
const qse_mchar_t* ptr;
|
||||||
|
|
||||||
|
cgi = (task_cgi_t*)qse_httpd_gettaskxtn (httpd, task);
|
||||||
|
arg = (task_cgi_arg_t*)task->ctx;
|
||||||
|
|
||||||
|
/* TODO: can content length be a different type???
|
||||||
|
* maybe qse_uintmax_t.... it thinks the data size can be larger than the max pointer size
|
||||||
|
* qse_htre_t and qse_htrd_t also needs changes to support it
|
||||||
|
*/
|
||||||
|
|
||||||
QSE_MEMSET (cgi, 0, QSE_SIZEOF(*cgi));
|
QSE_MEMSET (cgi, 0, QSE_SIZEOF(*cgi));
|
||||||
qse_mbscpy ((qse_mchar_t*)(cgi + 1), arg->path);
|
qse_mbscpy ((qse_mchar_t*)(cgi + 1), arg->path);
|
||||||
@ -1586,41 +1613,100 @@ static int task_init_cgi (
|
|||||||
cgi->keepalive = arg->req->attr.keepalive;
|
cgi->keepalive = arg->req->attr.keepalive;
|
||||||
cgi->nph = arg->nph;
|
cgi->nph = arg->nph;
|
||||||
|
|
||||||
if (!(arg->req->flags & QSE_HTRE_DISCARDED))
|
if (arg->req->state & QSE_HTRE_DISCARDED)
|
||||||
{
|
{
|
||||||
/* CGI entasking is invoked probably from the peek handler
|
qse_printf (QSE_T("XXXXXXXXXXXXXXXXX\n"));
|
||||||
* that was triggered after the request header is received
|
content_length = 0;
|
||||||
* in principle. In that case, arrange to forward content
|
goto done;
|
||||||
* 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);
|
|
||||||
|
|
||||||
|
len = qse_htre_getcontentlen(arg->req);
|
||||||
|
if ((arg->req->state & QSE_HTRE_COMPLETED) && len <= 0)
|
||||||
|
{
|
||||||
|
qse_printf (QSE_T("YYYYYYYYYYYYYYYYy\n"));
|
||||||
|
/* the content part is completed and no content
|
||||||
|
* in the content buffer. there is nothing to forward */
|
||||||
|
content_length = 0;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(arg->req->state & QSE_HTRE_COMPLETED) &&
|
||||||
|
!arg->req->attr.content_length_set)
|
||||||
|
{
|
||||||
|
qse_printf (QSE_T("ZZZZZZZZZZZZZZZ\n"));
|
||||||
|
/* if the request is not completed and doesn't have
|
||||||
|
* content-length set, it's not really possible to
|
||||||
|
* pass the content. this function, however, allows
|
||||||
|
* such a request to entask a cgi script dropping the
|
||||||
|
* content */
|
||||||
|
qse_htre_discardcontent (arg->req);
|
||||||
|
content_length = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
/* create a buffer to hold request content from the client
|
/* create a buffer to hold request content from the client
|
||||||
* and copy content received already */
|
* and copy content received already */
|
||||||
cgi->reqcon = qse_mbs_open (httpd->mmgr, 0, (len < 512? 512: len));
|
cgi->reqcon = qse_mbs_open (httpd->mmgr, 0, (len < 512? 512: len));
|
||||||
if (cgi->reqcon == QSE_NULL ||
|
if (cgi->reqcon == QSE_NULL) goto oops;
|
||||||
qse_mbs_ncpy (cgi->reqcon, ptr, len) == (qse_size_t)-1)
|
|
||||||
|
ptr = qse_htre_getcontentptr(arg->req);
|
||||||
|
if (qse_mbs_ncpy (cgi->reqcon, ptr, len) == (qse_size_t)-1)
|
||||||
{
|
{
|
||||||
cgi->init_failed = 1;
|
qse_mbs_close (cgi->reqcon);
|
||||||
|
cgi->reqcon = QSE_NULL;
|
||||||
|
goto oops;
|
||||||
}
|
}
|
||||||
else if (!(arg->req->flags & QSE_HTRE_COMPLETED))
|
|
||||||
|
if (arg->req->state & QSE_HTRE_COMPLETED)
|
||||||
{
|
{
|
||||||
|
/* no furthur forwarding is needed.
|
||||||
|
* even a chunked request entaksed when completed
|
||||||
|
* should reach here. if content-length is set
|
||||||
|
* the length should match len. */
|
||||||
|
QSE_ASSERT (len > 0);
|
||||||
|
QSE_ASSERT (!arg->req->attr.content_length_set ||
|
||||||
|
(arg->req->attr.content_length_set && arg->req->attr.content_length == len));
|
||||||
|
qse_printf (QSE_T("HHHHHHHHHHHHHHHHhh %d\n"), (int)len);
|
||||||
|
content_length = len;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* CGI entasking is invoked probably from the peek handler
|
||||||
|
* that was triggered after the request header is received.
|
||||||
|
* you can know this because the request is not completed.
|
||||||
|
* In this case, arrange to forward content
|
||||||
|
* bypassing the buffer in the request object itself. */
|
||||||
|
|
||||||
/* TODO: callback chain instead of a single pointer???
|
/* TODO: callback chain instead of a single pointer???
|
||||||
if the request is already set up with a callback, something will go wrong.
|
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
|
/* set up a callback to be called when the request content
|
||||||
* is fed to the htrd reader */
|
* is fed to the htrd reader. qse_htre_addcontent() called
|
||||||
cgi->req = (qse_htre_t*)arg->req;
|
* by htrd invokes this callback. */
|
||||||
|
cgi->req = arg->req;
|
||||||
qse_htre_setconcb (cgi->req, cgi_snatch_content, cgi);
|
qse_htre_setconcb (cgi->req, cgi_snatch_content, cgi);
|
||||||
|
|
||||||
|
QSE_ASSERT (arg->req->attr.content_length_set);
|
||||||
|
content_length = arg->req->attr.content_length;
|
||||||
|
qse_printf (QSE_T("TTTTTTTTTTTTTTTTTTTT %d\n"), (int)content_length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cgi->init_failed)
|
done:
|
||||||
{
|
cgi->env = makecgienv (httpd, client, arg->req, arg->path, content_length);
|
||||||
cgi->env = makecgienv (httpd, client, arg->req, arg->path);
|
if (cgi->env == QSE_NULL) goto oops;
|
||||||
if (cgi->env == QSE_NULL) cgi->init_failed = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
/* i don't set triggers yet. triggers will be set task_main_cgi().
|
||||||
|
* this way, task_main_cgi() is called regardless of data availability */
|
||||||
|
|
||||||
|
task->ctx = cgi;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
oops:
|
||||||
|
/* since a new task can't be added in the initializer,
|
||||||
|
* i mark that initialization failed and let task_main_cgi()
|
||||||
|
* add an error task */
|
||||||
|
cgi->init_failed = 1;
|
||||||
task->ctx = cgi;
|
task->ctx = cgi;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -1659,9 +1745,9 @@ static int task_main_cgi_5 (
|
|||||||
|
|
||||||
QSE_ASSERT (cgi->pio != QSE_NULL);
|
QSE_ASSERT (cgi->pio != QSE_NULL);
|
||||||
|
|
||||||
if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE)
|
if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_RELAYABLE)
|
||||||
{
|
{
|
||||||
if (cgi_forward_content (task) <= -1) return -1;
|
cgi_forward_content (httpd, task);
|
||||||
/* if forwarding didn't finish, something is not really right...
|
/* if forwarding didn't finish, something is not really right...
|
||||||
* so long as the output from CGI is finished, no more forwarding
|
* so long as the output from CGI is finished, no more forwarding
|
||||||
* is performed */
|
* is performed */
|
||||||
@ -1692,9 +1778,9 @@ static int task_main_cgi_4 (
|
|||||||
|
|
||||||
QSE_ASSERT (cgi->pio != QSE_NULL);
|
QSE_ASSERT (cgi->pio != QSE_NULL);
|
||||||
|
|
||||||
if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE)
|
if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_RELAYABLE)
|
||||||
{
|
{
|
||||||
if (cgi_forward_content (task) <= -1) return -1;
|
cgi_forward_content (httpd, task);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_READABLE)
|
if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_READABLE)
|
||||||
@ -1744,6 +1830,8 @@ static int task_main_cgi_4 (
|
|||||||
cgi->buf[cgi->buflen++] = QSE_MT('\n');
|
cgi->buf[cgi->buflen++] = QSE_MT('\n');
|
||||||
|
|
||||||
task->main = task_main_cgi_5;
|
task->main = task_main_cgi_5;
|
||||||
|
/* ok to chain-call since this task is called
|
||||||
|
* if the client-side is writable */
|
||||||
return task_main_cgi_5 (httpd, client, task);
|
return task_main_cgi_5 (httpd, client, task);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1781,6 +1869,8 @@ static int task_main_cgi_4 (
|
|||||||
~(QSE_HTTPD_TASK_TRIGGER_READ |
|
~(QSE_HTTPD_TASK_TRIGGER_READ |
|
||||||
QSE_HTTPD_TASK_TRIGGER_READABLE);
|
QSE_HTTPD_TASK_TRIGGER_READABLE);
|
||||||
task->main = task_main_cgi_5;
|
task->main = task_main_cgi_5;
|
||||||
|
/* ok to chain-call since this task is called
|
||||||
|
* if the client-side is writable */
|
||||||
return task_main_cgi_5 (httpd, client, task);
|
return task_main_cgi_5 (httpd, client, task);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1796,24 +1886,25 @@ static int task_main_cgi_4 (
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
qse_printf (QSE_T("CGI_4 SEND [%.*hs]\n"), (int)cgi->buflen, cgi->buf);
|
qse_printf (QSE_T("CGI_4 SEND [%.*hs]\n"), (int)cgi->buflen, cgi->buf);
|
||||||
#endif
|
#endif
|
||||||
httpd->errnum = QSE_HTTPD_ENOERR;
|
httpd->errnum = QSE_HTTPD_ENOERR;
|
||||||
n = httpd->cbs->client.send (httpd, client, cgi->buf, cgi->buflen);
|
n = httpd->cbs->client.send (httpd, client, cgi->buf, cgi->buflen);
|
||||||
if (n <= -1)
|
if (n <= -1)
|
||||||
{
|
{
|
||||||
/* can't return internal server error any more... */
|
/* can't return internal server error any more... */
|
||||||
/* TODO: logging ... */
|
/* TODO: logging ... */
|
||||||
|
qse_printf (QSE_T("CGI SEND FAILURE\n"));
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
QSE_MEMCPY (&cgi->buf[0], &cgi->buf[n], cgi->buflen - n);
|
QSE_MEMCPY (&cgi->buf[0], &cgi->buf[n], cgi->buflen - n);
|
||||||
cgi->buflen -= n;
|
cgi->buflen -= n;
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
qse_printf (QSE_T("CGI SEND DONE\n"));
|
qse_printf (QSE_T("CGI SEND DONE\n"));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
@ -1829,9 +1920,9 @@ static int task_main_cgi_3 (
|
|||||||
qse_ssize_t n;
|
qse_ssize_t n;
|
||||||
qse_size_t count;
|
qse_size_t count;
|
||||||
|
|
||||||
if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE)
|
if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_RELAYABLE)
|
||||||
{
|
{
|
||||||
if (cgi_forward_content (task) <= -1) return -1;
|
cgi_forward_content (httpd, task);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_READABLE)
|
if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_READABLE)
|
||||||
@ -1843,7 +1934,11 @@ qse_printf (QSE_T("[cgi_3 sending %d bytes]\n"), (int)count);
|
|||||||
httpd->errnum = QSE_HTTPD_ENOERR;
|
httpd->errnum = QSE_HTTPD_ENOERR;
|
||||||
n = httpd->cbs->client.send (httpd, client, cgi->res_ptr, count);
|
n = httpd->cbs->client.send (httpd, client, cgi->res_ptr, count);
|
||||||
|
|
||||||
if (n <= -1) return -1;
|
if (n <= -1)
|
||||||
|
{
|
||||||
|
qse_printf (QSE_T("[cgi-3 send failure....\n"));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
cgi->res_left -= n;
|
cgi->res_left -= n;
|
||||||
if (cgi->res_left <= 0)
|
if (cgi->res_left <= 0)
|
||||||
@ -1877,10 +1972,10 @@ static int task_main_cgi_2 (
|
|||||||
QSE_ASSERT (cgi->pio != QSE_NULL);
|
QSE_ASSERT (cgi->pio != QSE_NULL);
|
||||||
|
|
||||||
qse_printf (QSE_T("[cgi_2 ]\n"));
|
qse_printf (QSE_T("[cgi_2 ]\n"));
|
||||||
if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE)
|
if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_RELAYABLE)
|
||||||
{
|
{
|
||||||
qse_printf (QSE_T("[cgi_2 write]\n"));
|
qse_printf (QSE_T("[cgi_2 write]\n"));
|
||||||
if (cgi_forward_content (task) <= -1) return -1;
|
cgi_forward_content (httpd, task);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_READABLE)
|
if (task->trigger_mask & QSE_HTTPD_TASK_TRIGGER_READABLE)
|
||||||
@ -1896,17 +1991,15 @@ qse_printf (QSE_T("[cgi_2 read]\n"));
|
|||||||
{
|
{
|
||||||
/* can't return internal server error any more... */
|
/* can't return internal server error any more... */
|
||||||
/* TODO: logging ... */
|
/* TODO: logging ... */
|
||||||
return -1;
|
goto oops;
|
||||||
}
|
}
|
||||||
if (n == 0)
|
if (n == 0)
|
||||||
{
|
{
|
||||||
/* end of output from cgi before it has seen a header.
|
/* end of output from cgi before it has seen a header.
|
||||||
* the cgi script must be crooked. */
|
* the cgi script must be crooked. */
|
||||||
/* TODO: logging */
|
/* TODO: logging */
|
||||||
task->trigger_mask &=
|
qse_printf (QSE_T("#####PREMATURE EOF FROM CHILD\n"));
|
||||||
~(QSE_HTTPD_TASK_TRIGGER_READ |
|
goto oops;
|
||||||
QSE_HTTPD_TASK_TRIGGER_READABLE);
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cgi->buflen += n;
|
cgi->buflen += n;
|
||||||
@ -1914,7 +2007,8 @@ qse_printf (QSE_T("[cgi_2 read]\n"));
|
|||||||
if (qse_htrd_feed (cgi->htrd, cgi->buf, cgi->buflen) <= -1)
|
if (qse_htrd_feed (cgi->htrd, cgi->buf, cgi->buflen) <= -1)
|
||||||
{
|
{
|
||||||
/* TODO: logging */
|
/* TODO: logging */
|
||||||
return -1;
|
qse_printf (QSE_T("#####INVALID HEADER FROM FROM CHILD [%.*hs]\n"), (int)cgi->buflen, cgi->buf);
|
||||||
|
goto oops;
|
||||||
}
|
}
|
||||||
|
|
||||||
cgi->buflen = 0;
|
cgi->buflen = 0;
|
||||||
@ -1932,13 +2026,18 @@ qse_printf (QSE_T("[cgi_2 read]\n"));
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
if (cgi->disconnect &&
|
if (cgi->disconnect &&
|
||||||
qse_httpd_entaskdisconnect (httpd, client, task) == QSE_NULL) return -1;
|
qse_httpd_entaskdisconnect (httpd, client, task) == QSE_NULL)
|
||||||
|
{
|
||||||
|
goto oops;
|
||||||
|
}
|
||||||
|
|
||||||
cgi->res_ptr = QSE_MBS_PTR(cgi->res);
|
cgi->res_ptr = QSE_MBS_PTR(cgi->res);
|
||||||
cgi->res_left = QSE_MBS_LEN(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));
|
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;
|
task->main = task_main_cgi_3;
|
||||||
|
/* ok to chain-call since this task is called
|
||||||
|
* only if the client-side is writable */
|
||||||
return task_main_cgi_3 (httpd, client, task);
|
return task_main_cgi_3 (httpd, client, task);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1946,6 +2045,9 @@ qse_printf (QSE_T("TRAILING DATA=[%.*hs]\n"), (int)QSE_MBS_LEN(cgi->res), QSE_MB
|
|||||||
|
|
||||||
/* complete headers not seen yet. i need to be called again */
|
/* complete headers not seen yet. i need to be called again */
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
oops:
|
||||||
|
return (entask_error (httpd, client, task, 500, &cgi->version, cgi->keepalive) == QSE_NULL)? -1: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int task_main_cgi (
|
static int task_main_cgi (
|
||||||
@ -1953,23 +2055,17 @@ static int task_main_cgi (
|
|||||||
{
|
{
|
||||||
task_cgi_t* cgi = (task_cgi_t*)task->ctx;
|
task_cgi_t* cgi = (task_cgi_t*)task->ctx;
|
||||||
int pio_options;
|
int pio_options;
|
||||||
int error_code = 500;
|
|
||||||
|
|
||||||
if (cgi->init_failed) goto oops;
|
if (cgi->init_failed) goto oops;
|
||||||
|
|
||||||
if (QSE_ACCESS (cgi->path, X_OK) == -1)
|
|
||||||
{
|
|
||||||
error_code = (errno == EACCES)? 403: 404;
|
|
||||||
goto oops;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cgi->nph)
|
if (cgi->nph)
|
||||||
{
|
{
|
||||||
/* i cannot know how long the content will be
|
/* i cannot know how long the content will be
|
||||||
* since i don't parse the header. so i have to close
|
* since i don't parse the header. so i have to close
|
||||||
* the connection regardless of content-length or transfer-encoding
|
* the connection regardless of content-length or transfer-encoding
|
||||||
* in the actual header. */
|
* in the actual header. */
|
||||||
if (qse_httpd_entaskdisconnect (httpd, client, task) == QSE_NULL) return -1;
|
if (qse_httpd_entaskdisconnect (
|
||||||
|
httpd, client, task) == QSE_NULL) goto oops;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1990,8 +2086,11 @@ static int task_main_cgi (
|
|||||||
if (cgi->res == QSE_NULL) goto oops;
|
if (cgi->res == QSE_NULL) goto oops;
|
||||||
}
|
}
|
||||||
|
|
||||||
pio_options = QSE_PIO_READOUT | QSE_PIO_WRITEIN |
|
pio_options = QSE_PIO_READOUT | QSE_PIO_WRITEIN | QSE_PIO_MBSCMD;
|
||||||
QSE_PIO_ERRTONUL | QSE_PIO_MBSCMD;
|
if (httpd->option & QSE_HTTPD_CGIERRTONUL)
|
||||||
|
pio_options |= QSE_PIO_ERRTONUL;
|
||||||
|
else
|
||||||
|
pio_options |= QSE_PIO_ERRTOOUT;
|
||||||
if (httpd->option & QSE_HTTPD_CGINOCLOEXEC)
|
if (httpd->option & QSE_HTTPD_CGINOCLOEXEC)
|
||||||
pio_options |= QSE_PIO_NOCLOEXEC;
|
pio_options |= QSE_PIO_NOCLOEXEC;
|
||||||
|
|
||||||
@ -2010,8 +2109,24 @@ HANDLE for win32???
|
|||||||
task->trigger[0].i = qse_pio_gethandle (cgi->pio, QSE_PIO_OUT);
|
task->trigger[0].i = qse_pio_gethandle (cgi->pio, QSE_PIO_OUT);
|
||||||
if (cgi->reqcon)
|
if (cgi->reqcon)
|
||||||
{
|
{
|
||||||
|
/* not meaningful to check writability to the child process
|
||||||
|
* in the main loop. it is checked in cgi_forward_content().
|
||||||
|
* so the following 2 lines are commented out.
|
||||||
task->trigger_mask |= QSE_HTTPD_TASK_TRIGGER_WRITE;
|
task->trigger_mask |= QSE_HTTPD_TASK_TRIGGER_WRITE;
|
||||||
task->trigger[1].i = qse_pio_gethandle (cgi->pio, QSE_PIO_IN);
|
task->trigger[1].i = qse_pio_gethandle (cgi->pio, QSE_PIO_IN);*/
|
||||||
|
|
||||||
|
task->trigger_mask |= QSE_HTTPD_TASK_TRIGGER_RELAY;
|
||||||
|
task->trigger[1].i = client->handle.i;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cgi->reqcon)
|
||||||
|
{
|
||||||
|
/* since i didn't set triggers in the initializer (task_init_cgi()),
|
||||||
|
* it is possible that some contents has been read in already,
|
||||||
|
* forward them first. cgi_forward_content() is called after
|
||||||
|
* triggers are added above because cgi_forwrad_content()
|
||||||
|
* manipulates triggers when a forwarding error occurs. */
|
||||||
|
cgi_forward_content (httpd, task);
|
||||||
}
|
}
|
||||||
|
|
||||||
task->main = cgi->nph? task_main_cgi_4: task_main_cgi_2;
|
task->main = cgi->nph? task_main_cgi_4: task_main_cgi_2;
|
||||||
@ -2032,26 +2147,34 @@ oops:
|
|||||||
cgi->htrd = QSE_NULL;
|
cgi->htrd = QSE_NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (entask_error (httpd, client, task, error_code, &cgi->version, cgi->keepalive) == QSE_NULL)? -1: 0;
|
return (entask_error (httpd, client, task, 500, &cgi->version, cgi->keepalive) == QSE_NULL)? -1: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* TODO: global option or individual paramter for max cgi lifetime
|
/* TODO: global option or individual paramter for max cgi lifetime
|
||||||
* non-blocking pio read ...
|
* non-blocking pio read ...
|
||||||
*/
|
*/
|
||||||
|
|
||||||
qse_httpd_task_t* qse_httpd_entaskcgi (
|
static QSE_INLINE qse_httpd_task_t* entask_cgi (
|
||||||
qse_httpd_t* httpd,
|
qse_httpd_t* httpd, qse_httpd_client_t* client,
|
||||||
qse_httpd_client_t* client,
|
const qse_httpd_task_t* pred, const qse_mchar_t* path,
|
||||||
const qse_httpd_task_t* pred,
|
qse_htre_t* req, int nph)
|
||||||
const qse_mchar_t* path,
|
|
||||||
const qse_htre_t* req)
|
|
||||||
{
|
{
|
||||||
qse_httpd_task_t task;
|
qse_httpd_task_t task;
|
||||||
task_cgi_arg_t arg;
|
task_cgi_arg_t arg;
|
||||||
|
int x;
|
||||||
|
|
||||||
|
/* TODO: NEED TO CHECK IF it's a regular file and executable??
|
||||||
|
directory may be treated as executable???
|
||||||
|
*/
|
||||||
|
x = httpd->cbs->path.executable (httpd, path);
|
||||||
|
if (x == 0)
|
||||||
|
return qse_httpd_entaskerror (httpd, client, pred, 403, req);
|
||||||
|
else if (x <= -1)
|
||||||
|
return qse_httpd_entaskerror (httpd, client, pred, 404, req);
|
||||||
|
|
||||||
arg.path = path;
|
arg.path = path;
|
||||||
arg.req = req;
|
arg.req = req;
|
||||||
arg.nph = 0;
|
arg.nph = nph;
|
||||||
|
|
||||||
QSE_MEMSET (&task, 0, QSE_SIZEOF(task));
|
QSE_MEMSET (&task, 0, QSE_SIZEOF(task));
|
||||||
task.init = task_init_cgi;
|
task.init = task_init_cgi;
|
||||||
@ -2065,33 +2188,20 @@ qse_httpd_task_t* qse_httpd_entaskcgi (
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
qse_httpd_task_t* qse_httpd_entaskcgi (
|
||||||
|
qse_httpd_t* httpd, qse_httpd_client_t* client,
|
||||||
|
const qse_httpd_task_t* pred, const qse_mchar_t* path, qse_htre_t* req)
|
||||||
|
{
|
||||||
|
return entask_cgi (httpd, client, pred, path, req, 0);
|
||||||
|
}
|
||||||
|
|
||||||
qse_httpd_task_t* qse_httpd_entasknph (
|
qse_httpd_task_t* qse_httpd_entasknph (
|
||||||
qse_httpd_t* httpd,
|
qse_httpd_t* httpd, qse_httpd_client_t* client,
|
||||||
qse_httpd_client_t* client,
|
const qse_httpd_task_t* pred, const qse_mchar_t* path, qse_htre_t* req)
|
||||||
const qse_httpd_task_t* pred,
|
|
||||||
const qse_mchar_t* path,
|
|
||||||
const qse_htre_t* req)
|
|
||||||
{
|
{
|
||||||
qse_httpd_task_t task;
|
return entask_cgi (httpd, client, pred, path, req, 1);
|
||||||
task_cgi_arg_t arg;
|
|
||||||
|
|
||||||
arg.path = path;
|
|
||||||
arg.req = req;
|
|
||||||
arg.nph = 1;
|
|
||||||
|
|
||||||
QSE_MEMSET (&task, 0, QSE_SIZEOF(task));
|
|
||||||
task.init = task_init_cgi;
|
|
||||||
task.fini = task_fini_cgi;
|
|
||||||
task.main = task_main_cgi;
|
|
||||||
task.ctx = &arg;
|
|
||||||
|
|
||||||
return qse_httpd_entask (
|
|
||||||
httpd, client, pred, &task,
|
|
||||||
QSE_SIZEOF(task_cgi_t) + ((qse_mbslen(path) + 1) * QSE_SIZEOF(*path))
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*------------------------------------------------------------------------*/
|
/*------------------------------------------------------------------------*/
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
|
@ -10,6 +10,9 @@
|
|||||||
#include <locale.h>
|
#include <locale.h>
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
# include <windows.h>
|
# include <windows.h>
|
||||||
|
#else
|
||||||
|
# include <unistd.h>
|
||||||
|
# include <errno.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
@ -24,7 +27,6 @@
|
|||||||
#if defined(HAVE_SYS_SENDFILE_H)
|
#if defined(HAVE_SYS_SENDFILE_H)
|
||||||
# include <sys/sendfile.h>
|
# include <sys/sendfile.h>
|
||||||
#endif
|
#endif
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#if defined(HAVE_SENDFILE) && defined(HAVE_SENDFILE64)
|
#if defined(HAVE_SENDFILE) && defined(HAVE_SENDFILE64)
|
||||||
# if !defined(_LP64) && (QSE_SIZEOF_VOID_P<8) && defined(HAVE_SENDFILE64)
|
# if !defined(_LP64) && (QSE_SIZEOF_VOID_P<8) && defined(HAVE_SENDFILE64)
|
||||||
@ -179,6 +181,55 @@ static void fini_xtn_ssl (httpd_xtn_t* xtn)
|
|||||||
CRYPTO_cleanup_all_ex_data ();
|
CRYPTO_cleanup_all_ex_data ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
static int mux_readable (qse_httpd_t* httpd, qse_ubi_t handle, qse_ntoff_t msec)
|
||||||
|
{
|
||||||
|
fd_set r;
|
||||||
|
struct timeval tv, * tvp;
|
||||||
|
|
||||||
|
if (msec >= 0)
|
||||||
|
{
|
||||||
|
tv.tv_sec = (msec / 1000);
|
||||||
|
tv.tv_usec = ((msec % 1000) * 1000);
|
||||||
|
tvp = &tv;
|
||||||
|
}
|
||||||
|
else tvp = QSE_NULL;
|
||||||
|
|
||||||
|
FD_ZERO (&r);
|
||||||
|
FD_SET (handle.i, &r);
|
||||||
|
|
||||||
|
return select (handle.i + 1, &r, QSE_NULL, QSE_NULL, tvp);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mux_writable (qse_httpd_t* httpd, qse_ubi_t handle, qse_ntoff_t msec)
|
||||||
|
{
|
||||||
|
fd_set w;
|
||||||
|
struct timeval tv, * tvp;
|
||||||
|
|
||||||
|
if (msec >= 0)
|
||||||
|
{
|
||||||
|
tv.tv_sec = (msec / 1000);
|
||||||
|
tv.tv_usec = ((msec % 1000) * 1000);
|
||||||
|
tvp = &tv;
|
||||||
|
}
|
||||||
|
else tvp = QSE_NULL;
|
||||||
|
|
||||||
|
FD_ZERO (&w);
|
||||||
|
FD_SET (handle.i, &w);
|
||||||
|
|
||||||
|
return select (handle.i + 1, QSE_NULL, &w, QSE_NULL, tvp);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
static int path_executable (qse_httpd_t* httpd, const qse_mchar_t* path)
|
||||||
|
{
|
||||||
|
if (access (path, X_OK) == -1)
|
||||||
|
return (errno == EACCES)? 0 /*no*/: -1 /*error*/;
|
||||||
|
return 1; /* yes */
|
||||||
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------------- */
|
/* ------------------------------------------------------------------- */
|
||||||
static qse_ssize_t client_recv (
|
static qse_ssize_t client_recv (
|
||||||
qse_httpd_t* httpd, qse_httpd_client_t* client,
|
qse_httpd_t* httpd, qse_httpd_client_t* client,
|
||||||
@ -300,13 +351,13 @@ static int process_request (
|
|||||||
content_received = (qse_htre_getcontentlen(req) > 0);
|
content_received = (qse_htre_getcontentlen(req) > 0);
|
||||||
|
|
||||||
qse_printf (QSE_T("================================\n"));
|
qse_printf (QSE_T("================================\n"));
|
||||||
qse_printf (QSE_T("[%lu] %hs REQUEST ==> [%hs] version[%d.%d] method[%d]\n"),
|
qse_printf (QSE_T("[%lu] %hs REQUEST ==> [%hs] version[%d.%d] method[%hs]\n"),
|
||||||
(unsigned long)time(NULL),
|
(unsigned long)time(NULL),
|
||||||
(peek? QSE_MT("PEEK"): QSE_MT("HANDLE")),
|
(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),
|
||||||
method
|
qse_htre_getqmethodname(req)
|
||||||
);
|
);
|
||||||
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);
|
qse_htb_walk (&req->hdrtab, walk, QSE_NULL);
|
||||||
@ -364,9 +415,44 @@ if (qse_htre_getcontentlen(req) > 0)
|
|||||||
if (peek)
|
if (peek)
|
||||||
{
|
{
|
||||||
/* cgi */
|
/* cgi */
|
||||||
task = qse_httpd_entaskcgi (
|
if (req->attr.chunked)
|
||||||
httpd, client, QSE_NULL, qpath, req);
|
{
|
||||||
if (task == QSE_NULL) goto oops;
|
qse_printf (QSE_T("chunked cgi... delaying until contents are received\n"));
|
||||||
|
#if 0
|
||||||
|
req->attr.keepalive = 0;
|
||||||
|
task = qse_httpd_entaskerror (
|
||||||
|
httpd, client, QSE_NULL, 411, req);
|
||||||
|
/* 411 can't keep alive */
|
||||||
|
if (task) qse_httpd_entaskdisconnect (httpd, client, QSE_NULL);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else if (method == QSE_HTTP_POST &&
|
||||||
|
!req->attr.content_length_set)
|
||||||
|
{
|
||||||
|
req->attr.keepalive = 0;
|
||||||
|
task = qse_httpd_entaskerror (
|
||||||
|
httpd, client, QSE_NULL, 411, req);
|
||||||
|
/* 411 can't keep alive */
|
||||||
|
if (task) qse_httpd_entaskdisconnect (httpd, client, QSE_NULL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
task = qse_httpd_entaskcgi (
|
||||||
|
httpd, client, QSE_NULL, qpath, req);
|
||||||
|
if (task == QSE_NULL) goto oops;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* to support the chunked request,
|
||||||
|
* i need to wait until it's compelted and invoke cgi */
|
||||||
|
if (req->attr.chunked)
|
||||||
|
{
|
||||||
|
qse_printf (QSE_T("Entasking chunked CGI...\n"));
|
||||||
|
task = qse_httpd_entaskcgi (
|
||||||
|
httpd, client, QSE_NULL, qpath, req);
|
||||||
|
if (task == QSE_NULL) goto oops;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -446,9 +532,18 @@ int list_directory (qse_httpd_t* httpd, const qse_mchar_t* path)
|
|||||||
|
|
||||||
static qse_httpd_cbs_t httpd_cbs =
|
static qse_httpd_cbs_t httpd_cbs =
|
||||||
{
|
{
|
||||||
|
/* multiplexer */
|
||||||
|
{ mux_readable, mux_writable },
|
||||||
|
|
||||||
|
/* path operation */
|
||||||
|
{ path_executable },
|
||||||
|
|
||||||
/* client connection */
|
/* client connection */
|
||||||
{ client_recv, client_send, client_sendfile,
|
{ client_recv,
|
||||||
client_accepted, client_closed },
|
client_send,
|
||||||
|
client_sendfile,
|
||||||
|
client_accepted,
|
||||||
|
client_closed },
|
||||||
|
|
||||||
/* http request */
|
/* http request */
|
||||||
peek_request,
|
peek_request,
|
||||||
@ -508,7 +603,8 @@ int httpd_main (int argc, qse_char_t* argv[])
|
|||||||
signal (SIGINT, sigint);
|
signal (SIGINT, sigint);
|
||||||
signal (SIGPIPE, SIG_IGN);
|
signal (SIGPIPE, SIG_IGN);
|
||||||
|
|
||||||
ret = qse_httpd_loop (httpd, &httpd_cbs, 0);
|
qse_httpd_setoption (httpd, QSE_HTTPD_CGIERRTONUL);
|
||||||
|
ret = qse_httpd_loop (httpd, &httpd_cbs);
|
||||||
|
|
||||||
signal (SIGINT, SIG_DFL);
|
signal (SIGINT, SIG_DFL);
|
||||||
signal (SIGPIPE, SIG_DFL);
|
signal (SIGPIPE, SIG_DFL);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user