added more cgi related code

This commit is contained in:
hyung-hwan 2011-08-03 10:27:30 +00:00
parent 3634d16aaf
commit 0998ae3b25
6 changed files with 234 additions and 58 deletions

View File

@ -45,8 +45,9 @@ enum qse_htrd_option_t
{
QSE_HTRD_SKIPEMPTYLINES = (1 << 0), /**< skip leading empty lines before the initial line */
QSE_HTRD_SKIPINITIALLINE = (1 << 1), /**< skip processing an initial line */
QSE_HTRD_REQUEST = (1 << 2), /**< parse input as a request */
QSE_HTRD_RESPONSE = (1 << 3) /**< parse input as a response */
QSE_HTRD_HURRIED = (1 << 2), /**< trigger a callback also after headers without processing contents */
QSE_HTRD_REQUEST = (1 << 3), /**< parse input as a request */
QSE_HTRD_RESPONSE = (1 << 4) /**< parse input as a response */
};
typedef enum qse_htrd_option_t qse_htrd_option_t;
@ -96,15 +97,6 @@ struct qse_htrd_t
void* chl;
} fed;
#if 0
struct
{
/* temporary space to store a key and value pair
* during the call to qse_http_scanqparamstr() */
qse_htob_t qparam;
} tmp;
#endif
enum
{
QSE_HTRD_RETYPE_Q,

View File

@ -35,23 +35,27 @@ struct qse_htre_t
qse_http_version_t version;
int qmethod_or_sstatus;
qse_htob_t qpath_or_smesg;
qse_htob_t qparam;
qse_mbs_t qpath_or_smesg;
qse_mbs_t qparam;
/* special attributes derived from the header */
struct
{
int chunked;
int content_length;
int content_length_set;
qse_size_t content_length;
int connection_close;
int expect_continue;
/* indicates if the content has been filled */
int hurried;
} attr;
/* header table */
qse_htb_t hdrtab;
/* content octets */
qse_htob_t content;
qse_mbs_t content;
/* if set, the rest of the contents are discarded */
int discard;
@ -140,13 +144,13 @@ void qse_htre_clear (
int qse_htre_setstrfromcstr (
qse_htre_t* re,
qse_htob_t* str,
qse_mbs_t* str,
const qse_mcstr_t* cstr
);
int qse_htre_setstrfromxstr (
qse_htre_t* re,
qse_htob_t* str,
qse_mbs_t* str,
const qse_mxstr_t* xstr
);

View File

@ -38,35 +38,35 @@ static QSE_INLINE int is_space_octet (qse_mchar_t c)
static QSE_INLINE int is_purespace_octet (qse_mchar_t c)
{
return c == ' ' || c == '\t';
return c == QSE_MT(' ') || c == QSE_MT('\t');
}
static QSE_INLINE int is_upalpha_octet (qse_mchar_t c)
{
return c >= 'A' && c <= 'Z';
return c >= QSE_MT('A') && c <= QSE_MT('Z');
}
static QSE_INLINE int is_loalpha_octet (qse_mchar_t c)
{
return c >= 'a' && c <= 'z';
return c >= QSE_MT('a') && c <= QSE_MT('z');
}
static QSE_INLINE int is_alpha_octet (qse_mchar_t c)
{
return (c >= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z');
return (c >= QSE_MT('A') && c <= QSE_MT('Z')) ||
(c >= QSE_MT('a') && c <= QSE_MT('z'));
}
static QSE_INLINE int is_digit_octet (qse_mchar_t c)
{
return c >= '0' && c <= '9';
return c >= QSE_MT('0') && c <= QSE_MT('9');
}
static QSE_INLINE int is_xdigit_octet (qse_mchar_t c)
{
return (c >= '0' && c <= '9') ||
(c >= 'A' && c <= 'F') ||
(c >= 'a' && c <= 'f');
return (c >= QSE_MT('0') && c <= QSE_MT('9')) ||
(c >= QSE_MT('A') && c <= QSE_MT('F')) ||
(c >= QSE_MT('a') && c <= QSE_MT('f'));
}
static QSE_INLINE int digit_to_num (qse_mchar_t c)
@ -534,6 +534,7 @@ static QSE_INLINE int capture_content_length (
}
htrd->re.attr.content_length = len;
htrd->re.attr.content_length_set = 1;
return 0;
}
@ -562,10 +563,10 @@ static QSE_INLINE int capture_transfer_encoding (
n = compare_octets (QSE_HTB_VPTR(pair), QSE_HTB_VLEN(pair), "chunked", 7);
if (n == 0)
{
if (htrd->re.attr.content_length > 0)
/* if (htrd->re.attr.content_length > 0) */
if (htrd->re.attr.content_length_set)
{
/* content-length is greater than 0
* while transfer-encoding: chunked is specified. */
/* both content-length and 'transfer-encoding: chunked' are specified. */
goto badre;
}
@ -881,7 +882,7 @@ static const qse_mchar_t* getchunklen (qse_htrd_t* htrd, const qse_mchar_t* ptr,
/* this function must be called in the GET_CHUNK_LEN context */
QSE_ASSERT (htrd->fed.s.chunk.phase == GET_CHUNK_LEN);
//qse_printf (QSE_T("CALLING getchunklen [%d]\n"), *ptr);
/*qse_printf (QSE_T("CALLING getchunklen [%d]\n"), *ptr);*/
if (htrd->fed.s.chunk.count <= 0)
{
/* skip leading spaces if the first character of
@ -911,7 +912,7 @@ static const qse_mchar_t* getchunklen (qse_htrd_t* htrd, const qse_mchar_t* ptr,
if (htrd->fed.s.chunk.count <= 0)
{
/* empty line - no more chunk */
//qse_printf (QSE_T("empty line chunk done....\n"));
/*qse_printf (QSE_T("empty line chunk done....\n"));*/
htrd->fed.s.chunk.phase = GET_CHUNK_DONE;
}
else if (htrd->fed.s.chunk.len <= 0)
@ -919,13 +920,13 @@ static const qse_mchar_t* getchunklen (qse_htrd_t* htrd, const qse_mchar_t* ptr,
/* length explicity specified to 0
get trailing headers .... */
htrd->fed.s.chunk.phase = GET_CHUNK_TRAILERS;
//qse_printf (QSE_T("SWITCH TO GET_CHUNK_TRAILERS....\n"));
/*qse_printf (QSE_T("SWITCH TO GET_CHUNK_TRAILERS....\n"));*/
}
else
{
/* ready to read the chunk data... */
htrd->fed.s.chunk.phase = GET_CHUNK_DATA;
//qse_printf (QSE_T("SWITCH TO GET_CHUNK_DATA....\n"));
/*qse_printf (QSE_T("SWITCH TO GET_CHUNK_DATA....\n"));*/
}
htrd->fed.s.need = htrd->fed.s.chunk.len;
@ -933,7 +934,7 @@ static const qse_mchar_t* getchunklen (qse_htrd_t* htrd, const qse_mchar_t* ptr,
}
else
{
//qse_printf (QSE_T("XXXXXXXXXXXXXXXXXxxx [%c]\n"), *ptr);
/*qse_printf (QSE_T("XXXXXXXXXXXXXXXXXxxx [%c]\n"), *ptr);*/
htrd->errnum = QSE_HTRD_EBADRE;
return QSE_NULL;
}
@ -1102,6 +1103,53 @@ int qse_htrd_feed (qse_htrd_t* htrd, const qse_mchar_t* req, qse_size_t len)
if (parse_initial_line_and_headers (htrd, req, ptr - req) <= -1)
return -1;
if (htrd->option & QSE_HTRD_HURRIED)
{
int n;
/* it pushes any trailing data into the content in this mode.
* so the handler knows if there is contents fed to this reader. */
if (push_to_buffer (htrd, &htrd->re.content, ptr, end - ptr) <= -1)
return -1;
htrd->re.attr.hurried = 1;
htrd->errnum = QSE_HTRD_ENOERR;
if (htrd->retype == QSE_HTRD_RETYPE_S)
{
QSE_ASSERTX (
htrd->recbs->response != QSE_NULL,
"set response callback before feeding"
);
n = htrd->recbs->response (htrd, &htrd->re);
}
else
{
QSE_ASSERTX (
htrd->recbs->request != QSE_NULL,
"set request callback before feeding"
);
n = htrd->recbs->request (htrd, &htrd->re);
}
/* qse_mbs_clear (&htrd->re.content); */
if (n <= -1)
{
if (htrd->errnum == QSE_HTRD_ENOERR)
htrd->errnum = QSE_HTRD_ERECBS;
/* need to clear request on error?
clear_feed (htrd); */
return -1;
}
/* if QSE_HTRD_HURRIED is set, we do not handle expect_continue */
/* if QSE_HTRD_HURRIED is set, we handle a single request only */
return 0;
}
if (htrd->retype == QSE_HTRD_RETYPE_Q &&
htrd->re.attr.expect_continue &&
htrd->recbs->expect_continue && ptr >= end)
@ -1115,6 +1163,8 @@ int qse_htrd_feed (qse_htrd_t* htrd, const qse_mchar_t* req, qse_size_t len)
* not fed here?
*/
htrd->re.attr.hurried = 0;
htrd->errnum = QSE_HTRD_ENOERR;
n = htrd->recbs->expect_continue (htrd, &htrd->re);
if (n <= -1)
@ -1136,7 +1186,7 @@ int qse_htrd_feed (qse_htrd_t* htrd, const qse_mchar_t* req, qse_size_t len)
if (htrd->re.attr.chunked)
{
/* transfer-encoding: chunked */
QSE_ASSERT (htrd->re.attr.content_length <= 0);
QSE_ASSERT (!htrd->re.attr.content_length_set);
dechunk_start:
htrd->fed.s.chunk.phase = GET_CHUNK_LEN;
@ -1251,6 +1301,7 @@ int qse_htrd_feed (qse_htrd_t* htrd, const qse_mchar_t* req, qse_size_t len)
{
int n;
htrd->re.attr.hurried = 0;
htrd->errnum = QSE_HTRD_ENOERR;
if (htrd->retype == QSE_HTRD_RETYPE_S)

View File

@ -61,13 +61,13 @@ void qse_htre_clear (qse_htre_t* re)
}
int qse_htre_setstrfromcstr (
qse_htre_t* re, qse_htob_t* str, const qse_mcstr_t* cstr)
qse_htre_t* re, qse_mbs_t* str, const qse_mcstr_t* cstr)
{
return (qse_mbs_ncpy (str, cstr->ptr, cstr->len) == (qse_size_t)-1)? -1: 0;
}
int qse_htre_setstrfromxstr (
qse_htre_t* re, qse_htob_t* str, const qse_mxstr_t* xstr)
qse_htre_t* re, qse_mbs_t* str, const qse_mxstr_t* xstr)
{
return (qse_mbs_ncpy (str, xstr->ptr, xstr->len) == (qse_size_t)-1)? -1: 0;
}

View File

@ -667,7 +667,11 @@ struct task_cgi_t
const qse_char_t* path;
qse_htrd_t* htrd;
qse_mbs_t* res;
qse_mchar_t* res_ptr;
qse_size_t res_left;
qse_pio_t* pio;
qse_mchar_t buf[MAX_SEND_SIZE];
@ -701,6 +705,8 @@ static int cgi_htrd_handle_request (qse_htrd_t* htrd, qse_htre_t* req)
task_cgi_t* cgi = xtn->cgi;
const qse_mchar_t* status;
QSE_ASSERT (req->attr.hurried);
status = qse_htre_getheaderval (req, QSE_MT("Status"));
if (status)
{
@ -726,7 +732,24 @@ static int cgi_htrd_handle_request (qse_htrd_t* htrd, qse_htre_t* req)
if (qse_mbs_cat (cgi->res, buf) == (qse_size_t)-1) return -1;
}
if (!req->attr.content_length_set)
{
if (qse_mbs_cat (cgi->res, QSE_MT("Transfer-Encoding: chunked\r\n")) == (qse_size_t)-1) return -1;
}
if (qse_htre_walkheaders (req, walk_cgi_headers, cgi) <= -1) return -1;
if (qse_htre_getcontentlen(req) > 0)
{
if (!req->attr.content_length_set)
{
qse_mchar_t buf[64];
snprintf (buf, QSE_COUNTOF(buf), QSE_MT("%lX\r\n"), (unsigned long)qse_htre_getcontentlen(req));
if (qse_mbs_cat (cgi->res, buf) == (qse_size_t)-1) return -1;
}
if (qse_mbs_ncat (cgi->res, qse_htre_getcontentptr(req), qse_htre_getcontentlen(req)) == (qse_size_t)-1) return -1;
}
return 0;
}
@ -755,9 +778,10 @@ static void task_fini_cgi (
if (cgi->pio) qse_pio_close (cgi->pio);
if (cgi->res) qse_mbs_close (cgi->res);
if (cgi->htrd) qse_htrd_close (cgi->htrd);
qse_printf (QSE_T("task_fini_cgi\n"));
}
static int task_main_cgi_3 (
static int task_main_cgi_5 (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_cgi_t* cgi = (task_cgi_t*)task->ctx;
@ -765,6 +789,76 @@ static int task_main_cgi_3 (
QSE_ASSERT (cgi->pio != QSE_NULL);
qse_printf (QSE_T("task_main_cgi_5\n"));
{
char buf[64];
snprintf (buf, sizeof(buf), "%lX\r\n", cgi->buflen);
send (client->handle.i, buf, strlen(buf), 0);
}
/* TODO: check if cgi outputs more than content-length if it is set... */
n = send (client->handle.i, cgi->buf, cgi->buflen, 0);
if (n <= -1)
{
/* can't return internal server error any more... */
/* TODO: logging ... */
return -1;
}
send (client->handle.i, "\r\n", 2, 0);
QSE_MEMCPY (&cgi->buf[0], &cgi->buf[n], cgi->buflen - n);
cgi->buflen -= n;
if (cgi->buflen > 0) return 1;
send (client->handle.i, "0\r\n\r\n", 5, 0);
return 0;
}
static int task_main_cgi_4 (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_cgi_t* cgi = (task_cgi_t*)task->ctx;
qse_ssize_t n;
QSE_ASSERT (cgi->pio != QSE_NULL);
qse_printf (QSE_T("task_main_cgi_4\n"));
/* TODO: check if cgi outputs more than content-length if it is set... */
/* <- can i make it non-block?? or use select??? pio_tryread()? */
n = qse_pio_read (
cgi->pio,
&cgi->buf[cgi->buflen],
QSE_SIZEOF(cgi->buf) - cgi->buflen,
QSE_PIO_OUT
);
if (n <= -1)
{
/* can't return internal server error any more... */
/* TODO: logging ... */
return -1;
}
if (n == 0)
{
if (cgi->buflen > 0)
{
task->main = task_main_cgi_4;
return task_main_cgi_5 (httpd, client, task);
}
else
{
send (client->handle.i, "0\r\n\r\n", 5, 0);
return 0;
}
}
cgi->buflen += n;
{
char buf[64];
snprintf (buf, sizeof(buf), "%lX\r\n", cgi->buflen);
send (client->handle.i, buf, strlen(buf), 0);
}
n = send (client->handle.i, cgi->buf, cgi->buflen, 0);
if (n <= -1)
{
@ -772,11 +866,43 @@ static int task_main_cgi_3 (
/* TODO: logging ... */
return -1;
}
send (client->handle.i, "\r\n", 2, 0);
QSE_MEMCPY (&cgi->buf[0], &cgi->buf[n], cgi->buflen - n);
cgi->buflen -= n;
return (cgi->buflen > 0)? 1: 0;
return 1;
}
static int task_main_cgi_3 (
qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task)
{
task_cgi_t* cgi = (task_cgi_t*)task->ctx;
qse_ssize_t n;
qse_size_t count;
qse_printf (QSE_T("task_main_cgi_3\n"));
count = MAX_SEND_SIZE;
if (count >= cgi->res_left) count = cgi->res_left;
n = send (
client->handle.i,
cgi->res_ptr,
count,
0
);
if (n <= -1) return -1;
cgi->res_left -= n;
if (cgi->res_left <= 0)
{
task->main = task_main_cgi_4;
return task_main_cgi_4 (httpd, client, task);
}
cgi->res_ptr += n;
return 1; /* more work to do */
}
static int task_main_cgi_2 (
@ -802,12 +928,11 @@ static int task_main_cgi_2 (
}
if (n == 0)
{
if (cgi->buflen > 0)
{
task->main = task_main_cgi_3;
return task_main_cgi_3 (httpd, client, task);
}
else return 0;
/* end of output from cgi before it has seen a header.
* the cgi script must be crooked. */
/* TODO: logging */
qse_pio_kill (cgi->pio);
return -1;
}
cgi->buflen += n;
@ -818,20 +943,19 @@ static int task_main_cgi_2 (
return -1;
}
cgi->buflen = 0;
#if 0
n = send (client->handle.i, cgi->buf, cgi->buflen, 0);
if (n <= -1)
if (QSE_MBS_LEN(cgi->res) > 0)
{
/* can't return internal server error any more... */
/* TODO: logging ... */
return -1;
/* the headers and probably some contents are ready */
cgi->res_ptr = QSE_MBS_PTR(cgi->res);
cgi->res_left = QSE_MBS_LEN(cgi->res);
task->main = task_main_cgi_3;
return task_main_cgi_3 (httpd, client, task);
}
QSE_MEMCPY (&cgi->buf[0], &cgi->buf[n], cgi->buflen - n);
cgi->buflen -= n;
#endif
/* complete headers not seen yet. i need to be called again */
return 1;
}
@ -850,7 +974,12 @@ return 0;
xtn = (cgi_htrd_xtn_t*) qse_htrd_getxtn (cgi->htrd);
xtn->cgi = cgi;
qse_htrd_setrecbs (cgi->htrd, &cgi_htrd_cbs);
qse_htrd_setoption (cgi->htrd, QSE_HTRD_SKIPINITIALLINE | QSE_HTRD_REQUEST);
qse_htrd_setoption (
cgi->htrd,
QSE_HTRD_SKIPINITIALLINE |
QSE_HTRD_HURRIED |
QSE_HTRD_REQUEST
);
cgi->res = qse_mbs_open (httpd->mmgr, 0, 256);
if (cgi->res == QSE_NULL)

View File

@ -61,11 +61,11 @@ qse_printf (QSE_T("content = [%.*S]\n"),
if (dot && qse_mbscmp (dot, QSE_MT(".cgi")) == 0)
{
qse_httpd_entaskcgi (httpd, client, QSE_NULL, QSE_T("/bin/ls -l /etc"));
qse_httpd_entaskcgi (httpd, client, QSE_NULL, QSE_T("/tmp/test.cgi"));
goto done;
}
rangestr = qse_htre_gethdrval (req, "Range");
rangestr = qse_htre_getheaderval (req, "Range");
if (rangestr && qse_parsehttprange (rangestr, &range) <= -1)
{
#if 0