diff --git a/qse/include/qse/net/htrd.h b/qse/include/qse/net/htrd.h index 26e3a2f5..52bb62e0 100644 --- a/qse/include/qse/net/htrd.h +++ b/qse/include/qse/net/htrd.h @@ -37,11 +37,16 @@ enum qse_htrd_errnum_t typedef enum qse_htrd_errnum_t qse_htrd_errnum_t; +/** + * The qse_htrd_option_t type defines various options to + * change the behavior of the qse_htrd_t reader. + */ enum qse_htrd_option_t { - QSE_HTRD_LEADINGEMPTYLINES = (1 << 0), - QSE_HTRD_REQUEST = (1 << 1), - QSE_HTRD_RESPONSE = (1 << 2) + 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 */ }; typedef enum qse_htrd_option_t qse_htrd_option_t; diff --git a/qse/include/qse/net/htre.h b/qse/include/qse/net/htre.h index 16b11de3..99f0ba66 100644 --- a/qse/include/qse/net/htre.h +++ b/qse/include/qse/net/htre.h @@ -114,6 +114,13 @@ struct qse_htre_t #define qse_htre_setdiscard(re,v) QSE_BLOCK((re)->discard = (v);) +typedef int (*qse_htre_header_walker_t) ( + qse_htre_t* re, + const qse_mchar_t* key, + const qse_mchar_t* val, + void* ctx +); + #ifdef __cplusplus extern "C" { #endif @@ -143,8 +150,15 @@ int qse_htre_setstrfromxstr ( const qse_mxstr_t* xstr ); -const qse_mchar_t* qse_htre_gethdrval ( - qse_htre_t* re, const qse_mchar_t* key +const qse_mchar_t* qse_htre_getheaderval ( + qse_htre_t* re, + const qse_mchar_t* key +); + +int qse_htre_walkheaders ( + qse_htre_t* re, + qse_htre_header_walker_t walker, + void* ctx ); #ifdef __cplusplus diff --git a/qse/lib/net/htrd.c b/qse/lib/net/htrd.c index db8b84f0..82f34729 100644 --- a/qse/lib/net/htrd.c +++ b/qse/lib/net/htrd.c @@ -836,7 +836,7 @@ static QSE_INLINE int parse_initial_line_and_headers ( p = QSE_MBS_PTR(&htrd->fed.b.raw); - if (htrd->option & QSE_HTRD_LEADINGEMPTYLINES) + if (htrd->option & QSE_HTRD_SKIPEMPTYLINES) while (is_whspace_octet(*p)) p++; else while (is_space_octet(*p)) p++; @@ -844,8 +844,11 @@ static QSE_INLINE int parse_initial_line_and_headers ( QSE_ASSERT (*p != '\0'); /* parse the initial line */ - p = parse_initial_line (htrd, p); - if (p == QSE_NULL) return -1; + if (!(htrd->option & QSE_HTRD_SKIPINITIALLINE)) + { + p = parse_initial_line (htrd, p); + if (p == QSE_NULL) return -1; + } /* parse header fields */ do @@ -1050,7 +1053,7 @@ int qse_htrd_feed (qse_htrd_t* htrd, const qse_mchar_t* req, qse_size_t len) { register qse_mchar_t b = *ptr++; - if (htrd->option & QSE_HTRD_LEADINGEMPTYLINES && + if (htrd->option & QSE_HTRD_SKIPEMPTYLINES && htrd->fed.s.plen <= 0 && is_whspace_octet(b)) { /* let's drop leading whitespaces across multiple diff --git a/qse/lib/net/htre.c b/qse/lib/net/htre.c index 318cb13e..7f46bf9f 100644 --- a/qse/lib/net/htre.c +++ b/qse/lib/net/htre.c @@ -72,7 +72,7 @@ int qse_htre_setstrfromxstr ( return (qse_mbs_ncpy (str, xstr->ptr, xstr->len) == (qse_size_t)-1)? -1: 0; } -const qse_mchar_t* qse_htre_gethdrval ( +const qse_mchar_t* qse_htre_getheaderval ( qse_htre_t* re, const qse_mchar_t* name) { qse_htb_pair_t* pair; @@ -81,3 +81,34 @@ const qse_mchar_t* qse_htre_gethdrval ( return QSE_HTB_VPTR(pair); } +struct header_walker_ctx_t +{ + qse_htre_t* re; + qse_htre_header_walker_t walker; + void* ctx; + int ret; +}; + +static qse_htb_walk_t walk_headers (qse_htb_t* htb, qse_htb_pair_t* pair, void* ctx) +{ + struct header_walker_ctx_t* hwctx = (struct header_walker_ctx_t*)ctx; + if (hwctx->walker (hwctx->re, QSE_HTB_KPTR(pair), QSE_HTB_VPTR(pair), hwctx->ctx) <= -1) + { + hwctx->ret = -1; + return QSE_HTB_WALK_STOP; + } + return QSE_HTB_WALK_FORWARD; +} + +int qse_htre_walkheaders ( + qse_htre_t* re, qse_htre_header_walker_t walker, void* ctx) +{ + struct header_walker_ctx_t hwctx; + hwctx.re = re; + hwctx.walker = walker; + hwctx.ctx = ctx; + hwctx.ret = 0; + qse_htb_walk (&re->hdrtab, walk_headers, &hwctx); + return hwctx.ret; +} + diff --git a/qse/lib/net/httpd_task.c b/qse/lib/net/httpd_task.c index c8644bb1..1c1bc518 100644 --- a/qse/lib/net/httpd_task.c +++ b/qse/lib/net/httpd_task.c @@ -665,11 +665,78 @@ typedef struct task_cgi_t task_cgi_t; struct task_cgi_t { const qse_char_t* path; + + qse_htrd_t* htrd; + qse_mbs_t* res; qse_pio_t* pio; + qse_mchar_t buf[MAX_SEND_SIZE]; qse_size_t buflen; }; +typedef struct cgi_htrd_xtn_t cgi_htrd_xtn_t; +struct cgi_htrd_xtn_t +{ + task_cgi_t* cgi; +}; + +int walk_cgi_headers (qse_htre_t* req, const qse_mchar_t* key, const qse_mchar_t* val, void* ctx) +{ + task_cgi_t* cgi = (task_cgi_t*)ctx; + + if (qse_mbscmp (key, "Status") != 0) + { + if (qse_mbs_cat (cgi->res, key) == (qse_size_t)-1) return -1; + if (qse_mbs_cat (cgi->res, QSE_MT(": ")) == (qse_size_t)-1) return -1; + if (qse_mbs_cat (cgi->res, val) == (qse_size_t)-1) return -1; + if (qse_mbs_cat (cgi->res, QSE_MT("\r\n\r\n")) == (qse_size_t)-1) return -1; + } + + return 0; +} + +static int cgi_htrd_handle_request (qse_htrd_t* htrd, qse_htre_t* req) +{ + cgi_htrd_xtn_t* xtn = (cgi_htrd_xtn_t*) qse_htrd_getxtn (htrd); + task_cgi_t* cgi = xtn->cgi; + const qse_mchar_t* status; + + status = qse_htre_getheaderval (req, QSE_MT("Status")); + if (status) + { + qse_mchar_t buf[128]; + snprintf (buf, QSE_COUNTOF(buf), + QSE_MT("HTTP/%d.%d "), + qse_htre_getmajorversion(req), + qse_htre_getminorversion(req) + ); + if (qse_mbs_cat (cgi->res, buf) == (qse_size_t)-1) return -1; +/* TODO: check the syntax of status value??? */ + if (qse_mbs_cat (cgi->res, status) == (qse_size_t)-1) return -1; + if (qse_mbs_cat (cgi->res, QSE_MT("\r\n")) == (qse_size_t)-1) return -1; + } + else + { + qse_mchar_t buf[128]; + snprintf (buf, QSE_COUNTOF(buf), + QSE_MT("HTTP/%d.%d 200 OK\r\n"), + qse_htre_getmajorversion(req), + qse_htre_getminorversion(req) + ); + if (qse_mbs_cat (cgi->res, buf) == (qse_size_t)-1) return -1; + } + + if (qse_htre_walkheaders (req, walk_cgi_headers, cgi) <= -1) return -1; + return 0; +} + +static qse_htrd_recbs_t cgi_htrd_cbs = +{ + cgi_htrd_handle_request, + QSE_NULL, /* not needed for CGI */ + QSE_NULL /* not needed for CGI */ +}; + static int task_init_cgi ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) { @@ -686,7 +753,10 @@ static void task_fini_cgi ( { task_cgi_t* cgi = (task_cgi_t*)task->ctx; if (cgi->pio) qse_pio_close (cgi->pio); + if (cgi->res) qse_mbs_close (cgi->res); + if (cgi->htrd) qse_htrd_close (cgi->htrd); } + static int task_main_cgi_3 ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) { @@ -742,6 +812,14 @@ static int task_main_cgi_2 ( cgi->buflen += n; + if (qse_htrd_feed (cgi->htrd, cgi->buf, cgi->buflen) <= -1) + { +/* TODO: logging */ + return -1; + } + + +#if 0 n = send (client->handle.i, cgi->buf, cgi->buflen, 0); if (n <= -1) { @@ -752,6 +830,7 @@ static int task_main_cgi_2 ( QSE_MEMCPY (&cgi->buf[0], &cgi->buf[n], cgi->buflen - n); cgi->buflen -= n; +#endif return 1; } @@ -760,15 +839,41 @@ static int task_main_cgi ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) { task_cgi_t* cgi = (task_cgi_t*)task->ctx; + cgi_htrd_xtn_t* xtn; -qse_printf (QSE_T("[pip open for %s]\n"), cgi->path); + cgi->htrd = qse_htrd_open (httpd->mmgr, QSE_SIZEOF(cgi_htrd_xtn_t)); + if (cgi->htrd == QSE_NULL) + { +qse_printf (QSE_T("internal server error....\n")); +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); + + cgi->res = qse_mbs_open (httpd->mmgr, 0, 256); + if (cgi->res == QSE_NULL) + { + /* TODO: entask internal server errror */ + qse_htrd_close (cgi->htrd); +qse_printf (QSE_T("internal server error....\n")); + return 0; + } + +qse_printf (QSE_T("[pio open for %s]\n"), cgi->path); cgi->pio = qse_pio_open (httpd->mmgr, 0, cgi->path, QSE_PIO_READOUT | QSE_PIO_WRITEIN); if (cgi->pio == QSE_NULL) { /* TODO: entask internal server errror */ + qse_mbs_close (cgi->res); + qse_htrd_close (cgi->htrd); qse_printf (QSE_T("internal server error....\n")); return 0; } + else + { + } task->main = task_main_cgi_2; /* cause this function to be called subsequently */ @@ -794,3 +899,20 @@ qse_httpd_task_t* qse_httpd_entaskcgi ( QSE_SIZEOF(task_cgi_t) + ((qse_strlen(path) + 1) * QSE_SIZEOF(*path)) ); } + + +/*------------------------------------------------------------------------*/ + +/* +typedef struct task_proxy_t task_proxy_t; +struct task_proxy_t +{ +} + +qse_httpd_task_t* qse_httpd_entaskproxy (...) +{ +} +*/ + +/*------------------------------------------------------------------------*/ + diff --git a/qse/samples/net/http01.c b/qse/samples/net/http01.c index bce3c49e..2c6e73b6 100644 --- a/qse/samples/net/http01.c +++ b/qse/samples/net/http01.c @@ -2,12 +2,8 @@ #include #include #include +#include #include -#include -#include -#include -#include -#include #define MAX_SENDFILE_SIZE 4096 typedef struct httpd_xtn_t httpd_xtn_t; @@ -60,24 +56,31 @@ qse_printf (QSE_T("content = [%.*S]\n"), const qse_mchar_t* rangestr; qse_http_range_t range; + const qse_mchar_t* qpath = qse_htre_getqpathptr(req); + const qse_mchar_t* dot = qse_mbsrchr (qpath, QSE_MT('.')); + + if (dot && qse_mbscmp (dot, QSE_MT(".cgi")) == 0) + { +qse_httpd_entaskcgi (httpd, client, QSE_NULL, QSE_T("/bin/ls -l /etc")); + goto done; + } + rangestr = qse_htre_gethdrval (req, "Range"); if (rangestr && qse_parsehttprange (rangestr, &range) <= -1) { #if 0 -qse_httpd_entaskstatictext (httpd, client, QSE_MT("HTTP/1.1 416 Requested range not satisfiable\r\nContent-Length: 5\r\n\r\nA\r\n\r\n")); +qse_httpd_entaskstatictext (httpd, client, QSE_NULL, QSE_MT("HTTP/1.1 416 Requested range not satisfiable\r\nContent-Length: 5\r\n\r\nA\r\n\r\n")); #endif -qse_httpd_entaskcgi (httpd, client, QSE_NULL, QSE_T("/bin/ls -l /etc")); -#if 0 const qse_mchar_t* msg; msg = QSE_MT("Requested range not satisfiableREQUESTED RANGE NOT SATISFIABLE"); - x = qse_httpd_entaskformat (httpd, client, + x = qse_httpd_entaskformat ( + httpd, client, QSE_NULL, QSE_MT("HTTP/%d.%d 416 Requested range not satisfiable\r\nContent-Length: %d\r\n\r\n%s\r\n\r\n"), req->version.major, req->version.minor, (int)qse_mbslen(msg) + 4, msg ); if (x == QSE_NULL) goto oops; -#endif } else { @@ -102,6 +105,7 @@ qse_httpd_entaskcgi (httpd, client, QSE_NULL, QSE_T("/bin/ls -l /etc")); if (x == QSE_NULL) goto oops; } +done: if (req->attr.connection_close) { x = qse_httpd_entaskdisconnect (httpd, client, QSE_NULL);