From 597824fc89b69bc9ca9815809fdf8125dedfc0e0 Mon Sep 17 00:00:00 2001 From: hyung-hwan Date: Sun, 24 Mar 2013 16:35:44 +0000 Subject: [PATCH] changed the request inspection order for resource making --- qse/include/qse/http/htre.h | 1 + qse/include/qse/http/httpd.h | 6 ++ qse/lib/http/httpd-dir.c | 23 +++++ qse/lib/http/httpd-file.c | 30 +++++- qse/lib/http/httpd-std.c | 192 ++++++++++++++++++++--------------- 5 files changed, 167 insertions(+), 85 deletions(-) diff --git a/qse/include/qse/http/htre.h b/qse/include/qse/http/htre.h index 77635dce..631cae35 100644 --- a/qse/include/qse/http/htre.h +++ b/qse/include/qse/http/htre.h @@ -100,6 +100,7 @@ struct qse_htre_t #define QSE_HTRE_ATTR_KEEPALIVE (1 << 2) #define QSE_HTRE_ATTR_EXPECT (1 << 3) #define QSE_HTRE_ATTR_EXPECT100 (1 << 4) +#define QSE_HTRE_ATTR_PROXIED (1 << 5) int flags; qse_size_t content_length; const qse_mchar_t* status; /* for cgi */ diff --git a/qse/include/qse/http/httpd.h b/qse/include/qse/http/httpd.h index b64595fc..e25089ff 100644 --- a/qse/include/qse/http/httpd.h +++ b/qse/include/qse/http/httpd.h @@ -469,6 +469,11 @@ enum qse_httpd_rsrc_type_t }; typedef enum qse_httpd_rsrc_type_t qse_httpd_rsrc_type_t; +enum qse_httpd_rsrc_flag_t +{ + QSE_HTTPD_RSRC_100_CONTINUE = (1 << 0) +}; + typedef struct qse_httpd_rsrc_cgi_t qse_httpd_rsrc_cgi_t; struct qse_httpd_rsrc_cgi_t { @@ -484,6 +489,7 @@ typedef struct qse_httpd_rsrc_t qse_httpd_rsrc_t; struct qse_httpd_rsrc_t { qse_httpd_rsrc_type_t type; + int flags; /**< bitwised-ORed of #qse_httpd_rsrc_flag_t */ union { struct diff --git a/qse/lib/http/httpd-dir.c b/qse/lib/http/httpd-dir.c index 57517707..540df7b3 100644 --- a/qse/lib/http/httpd-dir.c +++ b/qse/lib/http/httpd-dir.c @@ -30,6 +30,7 @@ struct task_dir_t qse_mcstr_t qpath; qse_http_version_t version; int keepalive; + int headonly; }; typedef struct task_dseg_t task_dseg_t; @@ -457,6 +458,9 @@ qse_httpd_task_t* qse_httpd_entaskdir ( { qse_httpd_task_t task; task_dir_t data; + int meth; + + meth = qse_htre_getqmethodtype(req); QSE_MEMSET (&data, 0, QSE_SIZEOF(data)); data.path.ptr = path; @@ -466,6 +470,25 @@ qse_httpd_task_t* qse_httpd_entaskdir ( data.version = *qse_htre_getversion(req); data.keepalive = (req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE); + switch (meth) + { + case QSE_HTTP_HEAD: + data.headonly = 1; + break; + + case QSE_HTTP_OPTIONS: + break; + + case QSE_HTTP_GET: + case QSE_HTTP_POST: + case QSE_HTTP_PUT: + break; + + default: + /* Method not allowed */ + return qse_httpd_entaskerr (httpd, client, pred, 405, req); + } + QSE_MEMSET (&task, 0, QSE_SIZEOF(task)); task.init = task_init_dir; task.main = task_main_dir; diff --git a/qse/lib/http/httpd-file.c b/qse/lib/http/httpd-file.c index 129874cb..08573b38 100644 --- a/qse/lib/http/httpd-file.c +++ b/qse/lib/http/httpd-file.c @@ -36,6 +36,7 @@ struct task_file_t qse_ntime_t if_modified_since; qse_http_version_t version; int keepalive; + int headonly; }; typedef struct task_fseg_t task_fseg_t; @@ -234,6 +235,7 @@ static QSE_INLINE int task_main_file ( ); if (x) { + if (file->headonly) goto no_file_send; x = entask_file_segment ( httpd, client, x, handle, @@ -288,7 +290,11 @@ static QSE_INLINE int task_main_file ( qse_httpd_fmtgmtimetobb (httpd, &st.mtime, 1), etag ); - if (x) x = entask_file_segment (httpd, client, x, handle, 0, st.size); + if (x) + { + if (file->headonly) goto no_file_send; + x = entask_file_segment (httpd, client, x, handle, 0, st.size); + } } if (x) return 0; @@ -311,6 +317,9 @@ qse_httpd_task_t* qse_httpd_entaskfile ( qse_httpd_task_t task; task_file_t data; const qse_htre_hdrval_t* tmp; + int meth; + + meth = qse_htre_getqmethodtype(req);; QSE_MEMSET (&data, 0, QSE_SIZEOF(data)); data.path.ptr = path; @@ -323,6 +332,25 @@ qse_httpd_task_t* qse_httpd_entaskfile ( data.version = *qse_htre_getversion(req); data.keepalive = (req->attr.flags & QSE_HTRE_ATTR_KEEPALIVE); + switch (meth) + { + case QSE_HTTP_HEAD: + data.headonly = 1; + break; + + case QSE_HTTP_OPTIONS: + break; + + case QSE_HTTP_GET: + case QSE_HTTP_POST: + case QSE_HTTP_PUT: + break; + + default: + /* Method not allowed */ + return qse_httpd_entaskerr (httpd, client, pred, 405, req); + } + tmp = qse_htre_getheaderval(req, QSE_MT("Range")); if (tmp) { diff --git a/qse/lib/http/httpd-std.c b/qse/lib/http/httpd-std.c index 070b241f..53361df9 100644 --- a/qse/lib/http/httpd-std.c +++ b/qse/lib/http/httpd-std.c @@ -1826,18 +1826,13 @@ qse_printf (QSE_T("HEADER OK %d[%hs] %d[%hs]\n"), (int)QSE_HTB_KLEN(pair), QSE_ static int process_request ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req, int peek) { - int method; qse_httpd_task_t* task; - int content_received; server_xtn_t* server_xtn; server_xtn = (server_xtn_t*)qse_httpd_getserverxtn (httpd, client->server); - method = qse_htre_getqmethodtype(req); - content_received = (qse_htre_getcontentlen(req) > 0); - /* percent-decode the query path to the original buffer - * since i'm not gonna need it in the original form + * since i'm not going to need it in the original form * any more. once it's decoded in the peek mode, * the decoded query path is made available in the * non-peek mode as well */ @@ -1866,84 +1861,68 @@ if (qse_htre_getcontentlen(req) > 0) if (peek) { -#if 0 - if (method == QSE_HTTP_HEAD || method == QSE_HTTP_GET) + qse_httpd_rsrc_t rsrc; + + /* determine what to do once the header fields are all received. + * i don't want to delay this until the contents are received. + * if you don't like this behavior, you must implement your own + * callback function for request handling. */ + + if (qse_htre_getqmethodtype(req) == QSE_HTTP_POST && + !(req->attr.flags & QSE_HTRE_ATTR_LENGTH) && + !(req->attr.flags & QSE_HTRE_ATTR_CHUNKED)) { - /* i'll discard request contents if the method is HEAD or GET */ + /* POST without Content-Length nor not chunked */ + req->attr.flags &= ~QSE_HTRE_ATTR_KEEPALIVE; qse_httpd_discardcontent (httpd, req); - } -#endif - if ((req->attr.flags & QSE_HTRE_ATTR_EXPECT) && - (req->version.major > 1 || - (req->version.major == 1 && req->version.minor >= 1)) && - !content_received) - { - int code; - - /* "Expect" in the header, version 1.1 or higher, - * and no content received yet. - * if the partial or complete content is already received, - * we don't need to send '100 continue'. */ - - if (req->attr.flags & QSE_HTRE_ATTR_EXPECT100) + task = qse_httpd_entaskerr (httpd, client, QSE_NULL, 411, req); + if (task) { - /* "Expect: 100-continue" in the header */ - if (qse_httpd_entaskcontinue (httpd, client, QSE_NULL, req) == QSE_NULL) return -1; - } - else - { - /* if expectation fails, the client must not send the contents. - * however, some erroneous clients may do that. - * calling qse_httpd_discardcontent() won't do any harms */ - qse_httpd_discardcontent (httpd, req); - task = qse_httpd_entaskerr (httpd, client, QSE_NULL, 417, req); - if (task == QSE_NULL) goto oops; + /* 411 Length Required - can't keep alive. Force disconnect */ + task = qse_httpd_entaskdisconnect (httpd, client, QSE_NULL); } } - } - - if (method == QSE_HTTP_GET || method == QSE_HTTP_POST) - { - if (peek) - { - qse_httpd_rsrc_t rsrc; - - if (method == QSE_HTTP_POST && - !(req->attr.flags & QSE_HTRE_ATTR_LENGTH) && - !(req->attr.flags & QSE_HTRE_ATTR_CHUNKED)) - { - req->attr.flags &= ~QSE_HTRE_ATTR_KEEPALIVE; - qse_httpd_discardcontent (httpd, req); - task = qse_httpd_entaskerr (httpd, client, QSE_NULL, 411, req); - if (task) - { - /* 411 Length Required - can't keep alive. Force disconnect */ - task = qse_httpd_entaskdisconnect (httpd, client, QSE_NULL); - } - } - else if (server_xtn->makersrc (httpd, client, req, &rsrc) <= -1) - { - qse_httpd_discardcontent (httpd, req); - task = qse_httpd_entaskerr (httpd, client, QSE_NULL, 500, req); - } - else - { - task = qse_httpd_entaskrsrc (httpd, client, QSE_NULL, &rsrc, req); - server_xtn->freersrc (httpd, client, req, &rsrc); - } - if (task == QSE_NULL) goto oops; - } - } - else - { - if (peek) + else if (server_xtn->makersrc (httpd, client, req, &rsrc) <= -1) { + /* failed to make a resource. just send the internal server error. + * the makersrc handler can return a negative number to return + * '500 Internal Server Error'. If it wants to return a specific + * error code, it should return 0 with the QSE_HTTPD_RSRC_ERR + * resource. */ qse_httpd_discardcontent (httpd, req); + task = qse_httpd_entaskerr (httpd, client, QSE_NULL, 500, req); } else { - task = qse_httpd_entaskerr (httpd, client, QSE_NULL, 405, req); - if (task == QSE_NULL) goto oops; + task = QSE_NULL; + + /* inject '100 continue' first if it is needed */ + if ((rsrc.flags & QSE_HTTPD_RSRC_100_CONTINUE) && + (task = qse_httpd_entaskcontinue (httpd, client, task, req)) == QSE_NULL) goto oops; + + /* arrange the actual resource to be returned */ + task = qse_httpd_entaskrsrc (httpd, client, task, &rsrc, req); + server_xtn->freersrc (httpd, client, req, &rsrc); + + /* if the resource is indicating to return an error, + * discard the contents since i won't return them */ + if (rsrc.type == QSE_HTTPD_RSRC_ERR) qse_httpd_discardcontent (httpd, req); + } + if (task == QSE_NULL) goto oops; + } + else + { + /* contents are all received */ + + if (req->attr.flags & QSE_HTRE_ATTR_PROXIED) + { + /* the contents should be proxied. + * do nothing locally */ + } + else + { + /* when the request is handled locally, + * there's nothing special to do here */ } } @@ -2435,7 +2414,7 @@ static int make_resource ( struct rsrc_tmp_t tmp; qse_httpd_stat_t st; - int n, stx; + int n, stx, meth; QSE_MEMSET (&tmp, 0, QSE_SIZEOF(tmp)); tmp.qpath = qse_htre_getqpath(req); @@ -2445,7 +2424,7 @@ static int make_resource ( server_xtn = qse_httpd_getserverxtn (httpd, client->server); - if (server_xtn->query (httpd, client->server, req, tmp.xpath, QSE_HTTPD_SERVERSTD_ROOT, &tmp.root) <= -1) return -1; + if (server_xtn->query (httpd, client->server, req, QSE_NULL, QSE_HTTPD_SERVERSTD_ROOT, &tmp.root) <= -1) return -1; if (tmp.root.type == QSE_HTTPD_SERVERSTD_ROOT_NWAD) { /* proxy the request */ @@ -2453,13 +2432,30 @@ static int make_resource ( /*target->u.proxy.dst = client->orgdst_addr;*/ target->u.proxy.dst = tmp.root.u.nwad; target->u.proxy.src = client->remote_addr; + + /* mark that this request is going to be proxied. */ + req->attr.flags |= QSE_HTRE_ATTR_PROXIED; return 0; } + /* handle the request locally */ + +#if 0 + meth = qse_htre_getqmethodtype(req); + if (meth != QSE_HTTP_GET && meth != QSE_HTTP_POST && meth != QSE_HTTP_PUT && method != QSE_HTTP_) + { +/* TODO: handle more method types */ + /* method not allowed */ + target->type = QSE_HTTPD_RSRC_ERR; + target->u.err.code = 405; + return 0; + } +#endif + QSE_ASSERT (tmp.root.type == QSE_HTTPD_SERVERSTD_ROOT_PATH); - if (server_xtn->query (httpd, client->server, req, tmp.xpath, QSE_HTTPD_SERVERSTD_REALM, &tmp.realm) <= -1 || - server_xtn->query (httpd, client->server, req, tmp.xpath, QSE_HTTPD_SERVERSTD_INDEX, &tmp.index) <= -1) + if (server_xtn->query (httpd, client->server, req, QSE_NULL, QSE_HTTPD_SERVERSTD_REALM, &tmp.realm) <= -1 || + server_xtn->query (httpd, client->server, req, QSE_NULL, QSE_HTTPD_SERVERSTD_INDEX, &tmp.index) <= -1) { return -1; } @@ -2477,7 +2473,7 @@ static int make_resource ( authv = qse_htre_getheaderval (req, QSE_MT("Authorization")); if (authv) { - while (authv->next) authv = authv->next; + /*while (authv->next) authv = authv->next;*/ if (qse_mbszcasecmp(authv->ptr, QSE_MT("Basic "), 6) == 0) { @@ -2488,11 +2484,11 @@ static int make_resource ( authl = qse_mbslen(&authv->ptr[6]); if (authl > server_xtn->auth.len) { - qse_mchar_t* tmp; - tmp = qse_httpd_reallocmem (httpd, server_xtn->auth.ptr, authl * QSE_SIZEOF(qse_mchar_t)); - if (!tmp) return -1; + qse_mchar_t* tptr; + tptr = qse_httpd_reallocmem (httpd, server_xtn->auth.ptr, authl * QSE_SIZEOF(qse_mchar_t)); + if (!tptr) return -1; - server_xtn->auth.ptr = tmp; + server_xtn->auth.ptr = tptr; /* the maximum capacity that can hold the largest authorization value */ server_xtn->auth.len = authl; } @@ -2503,7 +2499,7 @@ static int make_resource ( tmp.auth.key.ptr = server_xtn->auth.ptr; tmp.auth.key.len = authl2; - if (server_xtn->query (httpd, client->server, req, tmp.xpath, QSE_HTTPD_SERVERSTD_AUTH, &tmp.auth) >= 0 && tmp.auth.authok) goto auth_ok; + if (server_xtn->query (httpd, client->server, req, QSE_NULL, QSE_HTTPD_SERVERSTD_AUTH, &tmp.auth) >= 0 && tmp.auth.authok) goto auth_ok; } } @@ -2513,6 +2509,34 @@ static int make_resource ( } auth_ok: + + /* if authentication is ok or no authentication is required, + * handle 'Expect: 100-continue' if it is contained in the header */ + if ((req->attr.flags & QSE_HTRE_ATTR_EXPECT) && + (req->version.major > 1 || (req->version.major == 1 && req->version.minor >= 1)) && + qse_htre_getcontentlen(req) <= 0) + { + /* "Expect" in the header, version 1.1 or higher, + * and no content received yet. don't care about the method type. + * + * if the partial or complete content is already received, + * we don't need to send '100 continue'. */ + + if (req->attr.flags & QSE_HTRE_ATTR_EXPECT100) + { + /* "Expect: 100-continue" in the header. + * mark to return "100 continue" */ + target->flags |= QSE_HTTPD_RSRC_100_CONTINUE; + } + else + { + /* Expectation Failed */ + target->type = QSE_HTTPD_RSRC_ERR; + target->u.err.code = 417; + return 0; + } + } + tmp.xpath = merge_paths (httpd, tmp.root.u.path.val, tmp.qpath_rp); if (tmp.xpath == QSE_NULL) return -1;