From 0998ae3b253765e1f2b8047ec1cf63d545af1746 Mon Sep 17 00:00:00 2001 From: hyung-hwan Date: Wed, 3 Aug 2011 10:27:30 +0000 Subject: [PATCH] added more cgi related code --- qse/include/qse/net/htrd.h | 14 +--- qse/include/qse/net/htre.h | 16 ++-- qse/lib/net/htrd.c | 87 +++++++++++++++---- qse/lib/net/htre.c | 4 +- qse/lib/net/httpd_task.c | 167 ++++++++++++++++++++++++++++++++----- qse/samples/net/http01.c | 4 +- 6 files changed, 234 insertions(+), 58 deletions(-) diff --git a/qse/include/qse/net/htrd.h b/qse/include/qse/net/htrd.h index 52bb62e0..bbd75a93 100644 --- a/qse/include/qse/net/htrd.h +++ b/qse/include/qse/net/htrd.h @@ -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, diff --git a/qse/include/qse/net/htre.h b/qse/include/qse/net/htre.h index 99f0ba66..68ab2f14 100644 --- a/qse/include/qse/net/htre.h +++ b/qse/include/qse/net/htre.h @@ -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 ); diff --git a/qse/lib/net/htrd.c b/qse/lib/net/htrd.c index 82f34729..06c62e28 100644 --- a/qse/lib/net/htrd.c +++ b/qse/lib/net/htrd.c @@ -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) diff --git a/qse/lib/net/htre.c b/qse/lib/net/htre.c index 7f46bf9f..3410325a 100644 --- a/qse/lib/net/htre.c +++ b/qse/lib/net/htre.c @@ -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; } diff --git a/qse/lib/net/httpd_task.c b/qse/lib/net/httpd_task.c index 1c1bc518..cc129060 100644 --- a/qse/lib/net/httpd_task.c +++ b/qse/lib/net/httpd_task.c @@ -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) diff --git a/qse/samples/net/http01.c b/qse/samples/net/http01.c index 2c6e73b6..008b3750 100644 --- a/qse/samples/net/http01.c +++ b/qse/samples/net/http01.c @@ -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