From 3d077903706b765b528411a677285ceeb4678890 Mon Sep 17 00:00:00 2001 From: hyung-hwan Date: Tue, 27 Mar 2012 15:45:10 +0000 Subject: [PATCH] written some code for proxy handling --- qse/README.in | 25 +- qse/include/qse/net/httpd.h | 32 +++ qse/lib/cmn/pio.c | 21 +- qse/lib/net/httpd-task.c | 503 +++++++++++++++++++++++++++++++++--- qse/samples/net/http01.c | 135 ++++++++-- 5 files changed, 643 insertions(+), 73 deletions(-) diff --git a/qse/README.in b/qse/README.in index 6d69f2a9..d0df6c0d 100644 --- a/qse/README.in +++ b/qse/README.in @@ -1,19 +1,12 @@ -QSE is a code library that implements various Unix utilities in an -embeddable form and provides a set of APIs to embed them into an application. -The APIs have been designed to be flexible enough to access various aspects of -a hosting application and an embedded object from each other. By embedding -a Unix utility into an application, a developer is relieved of problems caused -by interacting with external programs and is given tighter control over it. +The QSE library implements AWK, SED, and other Unix commands in an embeddable +form and defines data types, functions, and classes that you can use when you +embed them into an application. It also provides more fundamental data types +and functions needed when you deal with memory, streams, data structures. The +interface has been designed to be flexible enough to access various aspects of +embedding application and an embedded object from each other. -Currently the library implements the following utilities: -- SED Stream Editor -- CUT Text Cutter -- AWK Interpreter - -As the library grows, more utilities will be added. - -The library is licensed under LGPLv3. - -The project webpage: @QSE_PROJECT_URL@ +* The library is licensed under the GNU Lesser General Public License + version 3: http://www.gnu.org/licenses/ +* The project webpage is at @QSE_PROJECT_URL@ For further information, contact: @QSE_PROJECT_AUTHOR@ diff --git a/qse/include/qse/net/httpd.h b/qse/include/qse/net/httpd.h index 7388e9d0..ea33abcb 100644 --- a/qse/include/qse/net/httpd.h +++ b/qse/include/qse/net/httpd.h @@ -83,6 +83,13 @@ struct qse_httpd_server_t qse_ubi_t handle; }; +typedef struct qse_httpd_peer_t qse_httpd_peer_t; +struct qse_httpd_peer_t +{ + qse_nwad_t nwad; + qse_ubi_t handle; +}; + enum qse_httpd_mux_mask_t { QSE_HTTPD_MUX_READ = (1 << 0), @@ -107,6 +114,23 @@ struct qse_httpd_cbs_t int (*accept) (qse_httpd_t* httpd, qse_httpd_server_t* server, qse_httpd_client_t* client); } server; + struct + { + int (*open) (qse_httpd_t* httpd, qse_httpd_peer_t* peer); + void (*close) (qse_httpd_t* httpd, qse_httpd_peer_t* peer); + int (*connected) (qse_httpd_t* httpd, qse_httpd_peer_t* peer); + + qse_ssize_t (*recv) ( + qse_httpd_t* httpd, + qse_httpd_peer_t* peer, + qse_mchar_t* buf, qse_size_t bufsize); + + qse_ssize_t (*send) ( + qse_httpd_t* httpd, + qse_httpd_peer_t* peer, + const qse_mchar_t* buf, qse_size_t bufsize); + } peer; + struct { void* (*open) (qse_httpd_t* httpd); @@ -454,6 +478,14 @@ qse_httpd_task_t* qse_httpd_entasknph ( qse_htre_t* req ); +qse_httpd_task_t* qse_httpd_entaskproxy ( + qse_httpd_t* httpd, + qse_httpd_client_t* client, + const qse_httpd_task_t* pred, + const qse_nwad_t* nwad, + const qse_htre_t* req +); + /* -------------------------------------------- */ void* qse_httpd_allocmem ( diff --git a/qse/lib/cmn/pio.c b/qse/lib/cmn/pio.c index 11c1dee7..6ba91fab 100644 --- a/qse/lib/cmn/pio.c +++ b/qse/lib/cmn/pio.c @@ -1484,6 +1484,9 @@ create_process: goto oops; } + /* prepare some data before vforking for vfork limitation. + * the child in vfork should not make function calls or + * change data shared with the parent. */ if (!(flags & QSE_PIO_NOCLOEXEC)) highest_fd = get_highest_fd (); envarr = env? qse_env_getarr(env): environ; @@ -1498,7 +1501,11 @@ create_process: if (pid == 0) { - /* child */ + /* the child after vfork should not make function calls. + * since the system call like close() are also normal + * functions, i have to use assembly macros to make + * system calls. */ + qse_pio_hnd_t devnull = -1; if (!(flags & QSE_PIO_NOCLOEXEC)) @@ -1522,18 +1529,15 @@ create_process: { /* child should read */ QSE_SYSCALL1 (dummy, SYS_close, handle[1]); - /*handle[1] = QSE_PIO_HND_NIL;*/ QSE_SYSCALL2 (dummy, SYS_dup2, handle[0], 0); if (dummy <= -1) goto child_oops; QSE_SYSCALL1 (dummy, SYS_close, handle[0]); - /*handle[0] = QSE_PIO_HND_NIL;*/ } if (flags & QSE_PIO_READOUT) { /* child should write */ QSE_SYSCALL1 (dummy, SYS_close, handle[2]); - /*handle[2] = QSE_PIO_HND_NIL;*/ QSE_SYSCALL2 (dummy, SYS_dup2, handle[3], 1); if (dummy <= -1) goto child_oops; @@ -1544,14 +1548,12 @@ create_process: } QSE_SYSCALL1 (dummy, SYS_close, handle[3]); - /*handle[3] = QSE_PIO_HND_NIL;*/ } if (flags & QSE_PIO_READERR) { /* child should write */ QSE_SYSCALL1 (dummy, SYS_close, handle[4]); - /*handle[4] = QSE_PIO_HND_NIL;*/ QSE_SYSCALL2 (dummy, SYS_dup2, handle[5], 2); if (dummy <= -1) goto child_oops; @@ -1562,7 +1564,6 @@ create_process: } QSE_SYSCALL1 (dummy, SYS_close, handle[5]); - /*handle[5] = QSE_PIO_HND_NIL;*/ } if ((flags & QSE_PIO_INTONUL) || @@ -1606,11 +1607,7 @@ create_process: if (flags & QSE_PIO_DROPERR) QSE_SYSCALL1 (dummy, SYS_close, 2); QSE_SYSCALL3 (dummy, SYS_execve, param.argv[0], param.argv, envarr); - if (dummy == -1) - { -printf ("hello\n"); - } - /*free_param (pio, ¶m); */ + /*free_param (pio, ¶m); don't free this in the vfork version */ child_oops: if (devnull >= 0) QSE_SYSCALL1 (dummy, SYS_close, devnull); diff --git a/qse/lib/net/httpd-task.c b/qse/lib/net/httpd-task.c index 68277d53..d772418a 100644 --- a/qse/lib/net/httpd-task.c +++ b/qse/lib/net/httpd-task.c @@ -215,7 +215,7 @@ static int task_main_format ( } qse_httpd_task_t* qse_httpd_entaskformat ( - qse_httpd_t* httpd, + qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* pred, const qse_mchar_t* fmt, ...) @@ -1719,7 +1719,6 @@ static int task_init_cgi ( if (arg->req->state & QSE_HTRE_DISCARDED) { -qse_printf (QSE_T("XXXXXXXXXXXXXXXXX\n")); content_length = 0; goto done; } @@ -1727,7 +1726,6 @@ qse_printf (QSE_T("XXXXXXXXXXXXXXXXX\n")); 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; @@ -1737,7 +1735,6 @@ qse_printf (QSE_T("YYYYYYYYYYYYYYYYy\n")); 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 @@ -1770,7 +1767,6 @@ qse_printf (QSE_T("ZZZZZZZZZZZZZZZ\n")); 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 @@ -1792,7 +1788,6 @@ qse_printf (QSE_T("HHHHHHHHHHHHHHHHhh %d\n"), (int)len); 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); } } @@ -2093,7 +2088,11 @@ static int task_main_cgi_2 ( QSE_ASSERT (cgi->pio_inited); -qse_printf (QSE_T("[cgi_2 ]\n")); +{ +qse_ntime_t now; +qse_gettime(&now); +qse_printf (QSE_T("[cgi_2 at %lld]\n"), now); +} if (task->trigger[2].mask & QSE_HTTPD_TASK_TRIGGER_READABLE) { qse_printf (QSE_T("[cgi_2 write]\n")); @@ -2354,7 +2353,7 @@ qse_httpd_task_t* qse_httpd_entasknph ( typedef struct task_proxy_arg_t task_proxy_arg_t; struct task_proxy_arg_t { - const qse_mchar_t* host; + qse_nwad_t peer_nwad; qse_htre_t* req; int nph; }; @@ -2370,6 +2369,11 @@ struct task_proxy_t qse_htrd_t* htrd; + qse_httpd_peer_t peer; +#define PEER_OPEN (1 << 0) +#define PEER_CONNECTED (1 << 1) + int peer_status; + qse_htre_t* req; /* original request associated with this */ qse_mbs_t* reqfwdbuf; /* content from the request */ int reqfwderr; @@ -2377,6 +2381,20 @@ struct task_proxy_t qse_mbs_t* res; qse_mchar_t* res_ptr; qse_size_t res_left; + + /* if true, close connection after response is sent out */ + int disconnect; + /* if true, the content of response is chunked */ + int content_chunked; + /* if true, content_length is set. */ + int content_length_set; + /* content-length that CGI returned */ + qse_size_t content_length; + /* the number of octets in the contents received */ + qse_size_t content_received; + + qse_mchar_t buf[MAX_SEND_SIZE]; + qse_size_t buflen; }; typedef struct proxy_htrd_xtn_t proxy_htrd_xtn_t; @@ -2415,22 +2433,20 @@ else qse_printf (QSE_T("!!!PROXY SNATCHING DONE\n")); * the relay trigger is not needed any more. */ task->trigger[2].mask = 0; -#if 0 - if (QSE_MBS_LEN(proxy->reqfwdbuf) > 0 && proxy->pio_inited && - !(task->trigger[1].mask & QSE_HTTPD_TASK_TRIGGER_WRITE)) + if (QSE_MBS_LEN(proxy->reqfwdbuf) > 0 && + (proxy->peer_status & PEER_CONNECTED) && + !(task->trigger[0].mask & QSE_HTTPD_TASK_TRIGGER_WRITE)) { /* there's nothing more to read from the client side. * there's something to forward in the forwarding buffer. * but no write trigger is set. add the write trigger * for task invocation. */ - task->trigger[1].mask = QSE_HTTPD_TASK_TRIGGER_WRITE; - task->trigger[1].handle = qse_pio_gethandleasubi (&proxy->pio, QSE_PIO_IN); + task->trigger[0].mask |= QSE_HTTPD_TASK_TRIGGER_WRITE; } -#endif } else if (!proxy->reqfwderr) { - /* we can write to the child process if a forwarding error + /* we can write to the peer if a forwarding error * didn't occur previously. we store data from the client side * to the forwaring buffer only if there's no such previous * error. if an error occurred, we simply drop the data. */ @@ -2446,7 +2462,16 @@ qse_printf (QSE_T("!!!PROXY SNATCHED [%.*hs]\n"), len, ptr); static int proxy_htrd_peek_request (qse_htrd_t* htrd, qse_htre_t* req) { - return -1; + proxy_htrd_xtn_t* xtn = (proxy_htrd_xtn_t*) qse_htrd_getxtn (htrd); + task_proxy_t* proxy = xtn->proxy; + + /* add initial line and headers to proxy->res */ + if (qse_mbs_cat (proxy->res, QSE_MT("\r\n")) == (qse_size_t)-1) return -1; + + /* add any contents received so far to cgi->res */ + if (qse_mbs_ncat (proxy->res, qse_htre_getcontentptr(req), qse_htre_getcontentlen(req)) == (qse_size_t)-1) return -1; + + return 0; } static qse_htrd_recbs_t proxy_htrd_cbs = @@ -2455,6 +2480,79 @@ static qse_htrd_recbs_t proxy_htrd_cbs = QSE_NULL /* not needed for proxy */ }; +static void proxy_forward_content ( + qse_httpd_t* httpd, qse_httpd_task_t* task, int writable) +{ + task_proxy_t* proxy = (task_proxy_t*)task->ctx; + + QSE_ASSERT (proxy->reqfwdbuf != QSE_NULL); + + if (QSE_MBS_LEN(proxy->reqfwdbuf) > 0) + { + /* there is something to forward in the forwarding buffer. */ + + if (proxy->reqfwderr) + { + /* a forwarding error has occurred previously. + * clear the forwarding buffer */ +qse_printf (QSE_T("FORWARD: CLEARING REQCON FOR ERROR\n")); + qse_mbs_clear (proxy->reqfwdbuf); + } + else + { + /* normal forwarding */ + qse_ssize_t n; + + if (writable) goto forward; + + n = httpd->cbs->mux.writable (httpd, proxy->peer.handle, 0); +if (n == 0) qse_printf (QSE_T("PROXY FORWARD: @@@@@@@@@NOT WRITABLE\n")); + if (n >= 1) + { + forward: + /* writable */ +qse_printf (QSE_T("PROXY FORWARD: @@@@@@@@@@WRITING[%.*hs]\n"), + (int)QSE_MBS_LEN(proxy->reqfwdbuf), + QSE_MBS_PTR(proxy->reqfwdbuf)); + n = httpd->cbs->peer.send ( + httpd, &proxy->peer, + QSE_MBS_PTR(proxy->reqfwdbuf), + QSE_MBS_LEN(proxy->reqfwdbuf) + ); + +/* 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 (proxy->reqfwdbuf, 0, n); + } + + if (n <= -1) + { +qse_printf (QSE_T("PROXY FORWARD: @@@@@@@@WRITE TO PROXY FAILED\n")); +/* TODO: logging ... */ + proxy->reqfwderr = 1; + qse_mbs_clear (proxy->reqfwdbuf); + if (proxy->req) + { + qse_htre_discardcontent (proxy->req); + /* NOTE: proxy->req may be set to QSE_NULL + * in proxy_snatch_content() triggered by + * qse_htre_discardcontent() */ + } + } + } + } + else if (proxy->req == QSE_NULL) + { + /* there is nothing to read from the client side and + * there is nothing more to forward in the forwarding buffer. + * clear the relay and write triggers. + */ +qse_printf (QSE_T("FORWARD: @@@@@@@@NOTHING MORE TO WRITE TO PROXY\n")); + task->trigger[2].mask = 0; + } +} + + static int task_init_proxy ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) { @@ -2473,14 +2571,17 @@ static int task_init_proxy ( */ QSE_MEMSET (proxy, 0, QSE_SIZEOF(*proxy)); - qse_mbscpy ((qse_mchar_t*)(proxy + 1), arg->host); - proxy->host = (qse_mchar_t*)(proxy + 1); proxy->version = *qse_htre_getversion(arg->req); proxy->keepalive = arg->req->attr.keepalive; + proxy->peer.nwad = arg->peer_nwad; + +/* -------------------------------------------------------------------- + * TODO: compose headers to send to peer and push them to fwdbuf... + * TODO: also change the content length check logic below... + * -------------------------------------------------------------------- */ if (arg->req->state & QSE_HTRE_DISCARDED) { -qse_printf (QSE_T("XXXXXXXXXXXXXXXXX\n")); content_length = 0; goto done; } @@ -2488,7 +2589,6 @@ qse_printf (QSE_T("XXXXXXXXXXXXXXXXX\n")); 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; @@ -2498,7 +2598,6 @@ qse_printf (QSE_T("YYYYYYYYYYYYYYYYy\n")); 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 @@ -2522,6 +2621,7 @@ qse_printf (QSE_T("ZZZZZZZZZZZZZZZ\n")); goto oops; } + if (arg->req->state & QSE_HTRE_COMPLETED) { /* no furthur forwarding is needed. @@ -2531,7 +2631,6 @@ qse_printf (QSE_T("ZZZZZZZZZZZZZZZ\n")); 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 @@ -2553,7 +2652,6 @@ qse_printf (QSE_T("HHHHHHHHHHHHHHHHhh %d\n"), (int)len); 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); } } @@ -2578,6 +2676,312 @@ oops: static void task_fini_proxy ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) { + task_proxy_t* proxy = (task_proxy_t*)task->ctx; + + if (proxy->peer_status & PEER_OPEN) + httpd->cbs->peer.close (httpd, &proxy->peer); + if (proxy->res) qse_mbs_close (proxy->res); + if (proxy->htrd) qse_htrd_close (proxy->htrd); + if (proxy->reqfwdbuf) qse_mbs_close (proxy->reqfwdbuf); + if (proxy->req) qse_htre_unsetconcb (proxy->req); +} + +static int task_main_proxy_5 ( + qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) +{ + task_proxy_t* proxy = (task_proxy_t*)task->ctx; + qse_ssize_t n; + + QSE_ASSERT (proxy->pio_inited); + + if (task->trigger[2].mask & QSE_HTTPD_TASK_TRIGGER_READABLE) + { + proxy_forward_content (httpd, task, 0); + } + else if (task->trigger[0].mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE) + { + proxy_forward_content (httpd, task, 1); + } + +qse_printf (QSE_T("task_main_proxy_5\n")); + if (proxy->buflen > 0) + { +/* TODO: check if proxy outputs more than content-length if it is set... */ + httpd->errnum = QSE_HTTPD_ENOERR; + n = httpd->cbs->client.send (httpd, client, proxy->buf, proxy->buflen); + if (n <= -1) + { + /* can't return internal server error any more... */ +/* TODO: logging ... */ + return -1; + } + + QSE_MEMCPY (&proxy->buf[0], &proxy->buf[n], proxy->buflen - n); + proxy->buflen -= n; + } + + /* if forwarding didn't finish, something is not really right... + * so long as the output from CGI is finished, no more forwarding + * is performed */ + return (proxy->buflen > 0 || proxy->req || + (proxy->reqfwdbuf && QSE_MBS_LEN(proxy->reqfwdbuf) > 0))? 1: 0; +} + +static int task_main_proxy_4 ( + qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) +{ + task_proxy_t* proxy = (task_proxy_t*)task->ctx; + qse_ssize_t n; + + QSE_ASSERT (proxy->pio_inited); + +qse_printf (QSE_T("task_main_proxy_4 trigger[0].mask=%d trigger[1].mask=%d trigger[2].mask=%d\n"), + task->trigger[0].mask, task->trigger[1].mask, task->trigger[2].mask); + + if (task->trigger[2].mask & QSE_HTTPD_TASK_TRIGGER_READABLE) + { + proxy_forward_content (httpd, task, 0); + } + else if (task->trigger[0].mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE) + { + proxy_forward_content (httpd, task, 1); + } + + if (task->trigger[0].mask & QSE_HTTPD_TASK_TRIGGER_READABLE) + { + /* this function assumes that the chunk length does not exceeded + * 4 hexadecimal digits. */ + QSE_ASSERT (QSE_SIZEOF(proxy->buf) <= 0xFFFF); + + qse_printf (QSE_T("task_main_proxy_4\n")); + + n = httpd->cbs->peer.recv ( + httpd, &proxy->peer, + &proxy->buf[proxy->buflen], + QSE_SIZEOF(proxy->buf) - proxy->buflen + ); + if (n <= -1) + { + /* can't return internal server error any more... */ + /* TODO: loggig ... */ + return -1; + } + if (n == 0) + { + task->trigger[0].mask = 0; + task->main = task_main_proxy_5; + /* ok to chain-call since this task is called + * if the client-side is writable */ + return task_main_proxy_5 (httpd, client, task); + } + + proxy->buflen += n; + proxy->content_received += n; + + if (proxy->content_length_set && + proxy->content_received > proxy->content_length) + { + /* TODO: proxy returning too much data... something is wrong in CGI */ + qse_printf (QSE_T("CGI FUCKED UP...RETURNING TOO MUCH DATA\n")); + return -1; + } + +#if 0 +qse_printf (QSE_T("CGI_4 SEND [%.*hs]\n"), (int)proxy->buflen, proxy->buf); +#endif + httpd->errnum = QSE_HTTPD_ENOERR; + n = httpd->cbs->client.send (httpd, client, proxy->buf, proxy->buflen); + if (n <= -1) + { + /* can't return internal server error any more... */ + /* TODO: logging ... */ + qse_printf (QSE_T("CGI SEND FAILURE\n")); + return -1; + } + + QSE_MEMCPY (&proxy->buf[0], &proxy->buf[n], proxy->buflen - n); + proxy->buflen -= n; + +#if 0 +qse_printf (QSE_T("CGI SEND DONE\n")); +#endif + } + + return 1; +} + +static int task_main_proxy_3 ( + qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) +{ + /* send the http initial line and headers built using the headers + * returned by peer. it may include some contents as well */ + + task_proxy_t* proxy = (task_proxy_t*)task->ctx; + qse_ssize_t n; + qse_size_t count; + + if (task->trigger[2].mask & QSE_HTTPD_TASK_TRIGGER_READABLE) + { + proxy_forward_content (httpd, task, 0); + } + else if (task->trigger[0].mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE) + { + proxy_forward_content (httpd, task, 1); + } + + if (task->trigger[0].mask & QSE_HTTPD_TASK_TRIGGER_READABLE) + { + count = MAX_SEND_SIZE; + if (count >= proxy->res_left) count = proxy->res_left; + +qse_printf (QSE_T("[proxy_3 sending %d bytes]\n"), (int)count); + httpd->errnum = QSE_HTTPD_ENOERR; + n = httpd->cbs->client.send (httpd, client, proxy->res_ptr, count); + + if (n <= -1) + { +qse_printf (QSE_T("[proxy-3 send failure....\n")); + return -1; + } + + proxy->res_left -= n; + if (proxy->res_left <= 0) + { +qse_printf (QSE_T("[switching to proxy-4....\n")); + task->main = task_main_proxy_4; + /* don't chain-call task_main_proxy_4 since it has another send + * and it has already been sent here. so the writability must + * be checked again in the main loop. + * => return task_main_proxy_4 (httpd, client, task);*/ + return 1; + } + + proxy->res_ptr += n; + } + + return 1; /* more work to do */ +} + + +static int task_main_proxy_2 ( + qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) +{ + task_proxy_t* proxy = (task_proxy_t*)task->ctx; + qse_ssize_t n; + qse_size_t count; + + if (task->trigger[2].mask & QSE_HTTPD_TASK_TRIGGER_READABLE) + { + proxy_forward_content (httpd, task, 0); + } + else if (task->trigger[0].mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE) + { + proxy_forward_content (httpd, task, 1); + } + + if (task->trigger[0].mask & QSE_HTTPD_TASK_TRIGGER_READABLE) + { + /* there is something to read from peer */ + n = httpd->cbs->peer.recv ( + httpd, &proxy->peer, + &proxy->buf[proxy->buflen], + QSE_SIZEOF(proxy->buf) - proxy->buflen + ); + if (n <= -1) + { + /* can't return internal server error any more... */ +/* TODO: logging ... */ + goto oops; + } + if (n == 0) + { + /* end of output from peer before it has seen a header. + * the proxy script must be crooked. */ +/* TODO: logging */ +qse_printf (QSE_T("#####PREMATURE EOF FROM PEER\n")); + goto oops; + } + + proxy->buflen += n; + + if (qse_htrd_feed (proxy->htrd, proxy->buf, proxy->buflen) <= -1) + { +/* TODO: logging */ +qse_printf (QSE_T("#####INVALID HEADER FROM FROM PEER [%.*hs]\n"), (int)proxy->buflen, proxy->buf); + goto oops; + } + + proxy->buflen = 0; + + + if (QSE_MBS_LEN(proxy->res) > 0) + { + if (proxy->disconnect && + qse_httpd_entaskdisconnect (httpd, client, task) == QSE_NULL) + { + goto oops; + } + + proxy->res_ptr = QSE_MBS_PTR(proxy->res); + proxy->res_left = QSE_MBS_LEN(proxy->res); + +qse_printf (QSE_T("TRAILING DATA=[%.*hs]\n"), (int)QSE_MBS_LEN(proxy->res), QSE_MBS_PTR(proxy->res)); + task->main = task_main_proxy_3; + /* ok to chain-call since this task is called + * only if the client-side is writable */ + return task_main_proxy_3 (httpd, client, task); + } + } + + /* complete headers not seen yet. i need to be called again */ + return 1; + +oops: + return (entask_error (httpd, client, task, 500, &proxy->version, proxy->keepalive) == QSE_NULL)? -1: 0; +} + +static int task_main_proxy_0 ( + qse_httpd_t* httpd, qse_httpd_client_t* client, qse_httpd_task_t* task) +{ + task_proxy_t* proxy = (task_proxy_t*)task->ctx; + int n; + + /* wait for peer to get connected */ + + if (task->trigger[0].mask & QSE_HTTPD_TASK_TRIGGER_READABLE || + task->trigger[0].mask & QSE_HTTPD_TASK_TRIGGER_WRITABLE) + { + n = httpd->cbs->peer.connected (httpd, &proxy->peer); + if (n <= -1) return -1; + if (n >= 1) + { + proxy->peer_status |= PEER_CONNECTED; + task->trigger[0].mask &= ~QSE_HTTPD_TASK_TRIGGER_WRITE; + if (proxy->reqfwdbuf) + { + if (proxy->req) + { + task->trigger[2].mask = QSE_HTTPD_TASK_TRIGGER_READ; + task->trigger[2].handle = client->handle; + } + else if (QSE_MBS_LEN(proxy->reqfwdbuf) > 0) + { + proxy_forward_content (httpd, task, 0); + if (QSE_MBS_LEN(proxy->reqfwdbuf) > 0) + { + task->trigger[0].mask |= QSE_HTTPD_TASK_TRIGGER_WRITE; + } + } + } +qse_printf (QSE_T("FINALLY connected to peer ...............................\n")); +qse_printf (QSE_T("FINALLY connected to peer ...............................\n")); +qse_printf (QSE_T("FINALLY connected to peer ...............................\n")); +qse_printf (QSE_T("FINALLY connected to peer ...............................\n")); + task->main = task_main_proxy_2; + } + } + + return 1; } static int task_main_proxy ( @@ -2586,6 +2990,7 @@ static int task_main_proxy ( task_proxy_t* proxy = (task_proxy_t*)task->ctx; proxy_htrd_xtn_t* xtn; int http_errnum = 500; + int n; if (proxy->init_failed) goto oops; @@ -2596,7 +3001,6 @@ static int task_main_proxy ( qse_htrd_setrecbs (proxy->htrd, &proxy_htrd_cbs); qse_htrd_setoption ( proxy->htrd, - QSE_HTRD_SKIPINITIALLINE | QSE_HTRD_PEEKONLY | QSE_HTRD_REQUEST ); @@ -2604,7 +3008,49 @@ static int task_main_proxy ( proxy->res = qse_mbs_open (httpd->mmgr, 0, 256); if (proxy->res == QSE_NULL) goto oops; -/////////////////////// + httpd->errnum = QSE_HTTPD_ENOERR; + n = httpd->cbs->peer.open (httpd, &proxy->peer); + if (n <= -1) + { +/* TODO: translate error code to http error... */ + if (httpd->errnum == QSE_HTTPD_ENOENT) http_errnum = 404; + else if (httpd->errnum == QSE_HTTPD_EACCES) http_errnum = 403; + goto oops; + } + + proxy->peer_status |= PEER_OPEN; + task->trigger[0].mask = QSE_HTTPD_TASK_TRIGGER_READ; + task->trigger[0].handle = proxy->peer.handle; + + if (n == 0) + { + /* peer not connected yet */ + task->trigger[0].mask |= QSE_HTTPD_TASK_TRIGGER_WRITE; + task->main = task_main_proxy_0; + } + else + { + /* peer connected already */ + proxy->peer_status |= PEER_CONNECTED; + if (proxy->reqfwdbuf) + { + if (proxy->req) + { + task->trigger[2].mask = QSE_HTTPD_TASK_TRIGGER_READ; + task->trigger[2].handle = client->handle; + } + else if (QSE_MBS_LEN(proxy->reqfwdbuf) > 0) + { + proxy_forward_content (httpd, task, 0); + + if (QSE_MBS_LEN(proxy->reqfwdbuf) > 0) + { + task->trigger[0].mask |= QSE_HTTPD_TASK_TRIGGER_WRITE; + } + } + } + task->main = task_main_proxy_2; + } return 1; @@ -2629,13 +3075,13 @@ qse_httpd_task_t* qse_httpd_entaskproxy ( qse_httpd_t* httpd, qse_httpd_client_t* client, const qse_httpd_task_t* pred, - const qse_mchar_t* host, + const qse_nwad_t* nwad, const qse_htre_t* req) { qse_httpd_task_t task; task_proxy_arg_t arg; - arg.host = host; + arg.peer_nwad = *nwad; arg.req = req; QSE_MEMSET (&task, 0, QSE_SIZEOF(task)); @@ -2645,8 +3091,7 @@ qse_httpd_task_t* qse_httpd_entaskproxy ( task.ctx = &arg; return qse_httpd_entask ( - httpd, client, pred, &task, - QSE_SIZEOF(task_proxy_t) + ((qse_mbslen(host) + 1) * QSE_SIZEOF(*host)) + httpd, client, pred, &task, QSE_SIZEOF(task_proxy_t) ); } diff --git a/qse/samples/net/http01.c b/qse/samples/net/http01.c index 447b7b38..55165659 100644 --- a/qse/samples/net/http01.c +++ b/qse/samples/net/http01.c @@ -320,6 +320,9 @@ static int server_open (qse_httpd_t* httpd, qse_httpd_server_t* server) fd = socket (addr.ss_family, SOCK_STREAM, IPPROTO_TCP); if (fd <= -1) goto oops; + flag = fcntl (fd, F_GETFD); + if (flag >= 0) fcntl (fd, F_SETFD, flag | FD_CLOEXEC); + flag = 1; setsockopt (fd, SOL_SOCKET, SO_REUSEADDR, &flag, QSE_SIZEOF(flag)); @@ -332,9 +335,6 @@ static int server_open (qse_httpd_t* httpd, qse_httpd_server_t* server) flag = fcntl (fd, F_GETFL); if (flag >= 0) fcntl (fd, F_SETFL, flag | O_NONBLOCK); - flag = fcntl (fd, F_GETFD); - if (flag >= 0) fcntl (fd, F_SETFD, flag | FD_CLOEXEC); - server->handle.i = fd; return 0; @@ -379,12 +379,12 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: too many client?\n")); return -1; } #endif + flag = fcntl (fd, F_GETFD); + if (flag >= 0) fcntl (fd, F_SETFD, flag | FD_CLOEXEC); flag = fcntl (fd, F_GETFL); if (flag >= 0) fcntl (fd, F_SETFL, flag | O_NONBLOCK); - flag = fcntl (fd, F_GETFD); - if (flag >= 0) fcntl (fd, F_SETFD, flag | FD_CLOEXEC); if (sockaddr_to_nwad (&addr, &client->remote_addr) <= -1) { @@ -406,6 +406,92 @@ qse_fprintf (QSE_STDERR, QSE_T("Error: too many client?\n")); /* ------------------------------------------------------------------- */ +static int peer_open (qse_httpd_t* httpd, qse_httpd_peer_t* peer) +{ + int fd = -1, flag; +/* TODO: if AF_INET6 is not defined sockaddr_storage is not available... + * create your own union or somehting similar... */ + struct sockaddr_storage addr; + int addrsize; + int connected = 1; + + addrsize = nwad_to_sockaddr (&peer->nwad, &addr); + if (addrsize <= -1) + { + qse_httpd_seterrnum (httpd, QSE_HTTPD_ENOIMPL); + return -1; + } + + fd = socket (addr.ss_family, SOCK_STREAM, IPPROTO_TCP); + if (fd <= -1) goto oops; + + flag = fcntl (fd, F_GETFD); + if (flag >= 0) fcntl (fd, F_SETFD, flag | FD_CLOEXEC); + + flag = fcntl (fd, F_GETFL); + if (flag >= 0) fcntl (fd, F_SETFL, flag | O_NONBLOCK); + + if (connect (fd, (struct sockaddr*)&addr, addrsize) <= -1) + { + if (errno != EINPROGRESS) goto oops; + connected = 0; + } + + /* restore flags */ + if (fcntl (fd, F_SETFL, flag) <= -1) goto oops; + + peer->handle.i = fd; + return connected; + +oops: + qse_httpd_seterrnum (httpd, syserr_to_errnum(errno)); + if (fd >= 0) close (fd); + return -1; +} + +static void peer_close (qse_httpd_t* httpd, qse_httpd_peer_t* peer) +{ + close (peer->handle.i); +} + +static int peer_connected (qse_httpd_t* httpd, qse_httpd_peer_t* peer) +{ +#ifdef HAVE_SOCKLEN_T + socklen_t len; +#else + int len; +#endif + int ret; + + len = QSE_SIZEOF(ret); + if (getsockopt (peer->handle.i, SOL_SOCKET, SO_ERROR, &ret, &len) <= -1) return -1; + + if (ret == EINPROGRESS) return 0; + if (ret != 0) return -1; + + return 1; /* connection completed */ +} + +static qse_ssize_t peer_recv ( + qse_httpd_t* httpd, qse_httpd_peer_t* peer, + qse_mchar_t* buf, qse_size_t bufsize) +{ + ssize_t ret = read (peer->handle.i, buf, bufsize); + if (ret <= -1) qse_httpd_seterrnum (httpd, syserr_to_errnum(errno)); + return ret; +} + +static qse_ssize_t peer_send ( + qse_httpd_t* httpd, qse_httpd_peer_t* peer, + const qse_mchar_t* buf, qse_size_t bufsize) +{ + ssize_t ret = write (peer->handle.i, buf, bufsize); + if (ret <= -1) qse_httpd_seterrnum (httpd, syserr_to_errnum(errno)); + return ret; +} + +/* ------------------------------------------------------------------- */ + struct mux_ev_t { qse_ubi_t handle; @@ -682,8 +768,8 @@ qse_printf (QSE_T("opening file [%hs] for reading\n"), path); return -1; } - flags = fcntl (fd, F_GETFD); - if (flags >= 0) fcntl (fd, F_SETFD, flags | FD_CLOEXEC); + flags = fcntl (fd, F_GETFD); + if (flags >= 0) fcntl (fd, F_SETFD, flags | FD_CLOEXEC); handle->i = fd; qse_printf (QSE_T("opened file %hs\n"), path); @@ -934,6 +1020,11 @@ if (qse_htre_getcontentlen(req) > 0) else { /* TODO: determine if to return 100-continue or other errors */ +{ +qse_ntime_t now; +qse_gettime (&now); +qse_printf (QSE_T("entasking continue at %lld\n"), (long long)now); +} if (qse_httpd_entaskcontinue ( httpd, client, QSE_NULL, req) == QSE_NULL) return -1; } @@ -1062,10 +1153,12 @@ oops: return -1; } -#if 0 static int proxy_request ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req, int peek) { + qse_httpd_task_t* task; + +#if 0 const qse_mchar_t* qpath; qpath = qse_htre_qpathptr (eq); @@ -1083,6 +1176,7 @@ qse_printf (QSE_T("Host not included....\n")); const qse_mchar_t* host; qse_parseuri (); } +#endif #if 0 @@ -1114,11 +1208,13 @@ qse_printf (QSE_T("Host not included....\n")); } #endif -if (qse_htre_getqparamlen(req) > 0) qse_printf (QSE_T("PARAMS ==> [%hs]\n"), qse_htre_getqparamptr(req)); - - - task = qse_httpd_entaskproxy (httpd, client, QSE_NULL, qpath, req); - if (task == QSE_NULL) goto oops; + if (peek) + { + qse_nwad_t nwad; + qse_strtonwad (QSE_T("192.168.1.3:80"), &nwad); + task = qse_httpd_entaskproxy (httpd, client, QSE_NULL, &nwad, req); + if (task == QSE_NULL) goto oops; + } if (!req->attr.keepalive) { @@ -1134,18 +1230,19 @@ if (qse_htre_getqparamlen(req) > 0) qse_printf (QSE_T("PARAMS ==> [%hs]\n"), qse oops: return -1; } -#endif static int peek_request ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req) { - return process_request (httpd, client, req, 1); + //return process_request (httpd, client, req, 1); + return proxy_request (httpd, client, req, 1); } static int handle_request ( qse_httpd_t* httpd, qse_httpd_client_t* client, qse_htre_t* req) { - return process_request (httpd, client, req, 0); + //return process_request (httpd, client, req, 0); + return proxy_request (httpd, client, req, 0); } int list_directory (qse_httpd_t* httpd, const qse_mchar_t* path) @@ -1158,6 +1255,12 @@ static qse_httpd_cbs_t httpd_cbs = /* server */ { server_open, server_close, server_accept }, + { peer_open, + peer_close, + peer_connected, + peer_recv, + peer_send }, + /* multiplexer */ { mux_open, mux_close,