From 57ca185651daf1bd2e0405939aeee2974a9158cd Mon Sep 17 00:00:00 2001 From: hyung-hwan Date: Fri, 15 May 2020 06:18:49 +0000 Subject: [PATCH] changed to clear the on_write callbacks for completed write requestrs after having triggered timer callbacks collectively and also before triggering on_read() for each device cleaned up debugging messages in dns-cli.c experimenting with http server implementation --- mio/bin/t01.c | 3 +- mio/lib/dns-cli.c | 55 ++--- mio/lib/htrd.c | 64 +++--- mio/lib/http-svr.c | 489 +++++++++++++++++++++++++++++---------------- mio/lib/mio-htrd.h | 5 +- mio/lib/mio-htre.h | 2 +- mio/lib/mio-http.h | 26 ++- mio/lib/mio.c | 147 ++++++++++---- mio/lib/pro.c | 8 +- 9 files changed, 522 insertions(+), 277 deletions(-) diff --git a/mio/bin/t01.c b/mio/bin/t01.c index 188cf81..d67ffef 100644 --- a/mio/bin/t01.c +++ b/mio/bin/t01.c @@ -924,7 +924,7 @@ int main (int argc, char* argv[]) setup_arp_tester(mio); setup_ping4_tester(mio); -#if 1 +#if 0 for (i = 0; i < 5; i++) { mio_dev_pro_t* pro; @@ -978,6 +978,7 @@ for (i = 0; i < 5; i++) dnc = mio_svc_dnc_start(mio, &servaddr, MIO_NULL, &send_tmout, &reply_tmout, 2); /* option - send to all, send one by one */ htts = mio_svc_htts_start(mio, &htts_bind_addr); mio_svc_htts_setservernamewithbcstr (htts, "MIO-HTTP"); + #if 1 { mio_dns_bqr_t qrs[] = diff --git a/mio/lib/dns-cli.c b/mio/lib/dns-cli.c index 294ba5e..e996b7b 100644 --- a/mio/lib/dns-cli.c +++ b/mio/lib/dns-cli.c @@ -150,7 +150,7 @@ static void release_dns_msg (mio_svc_dnc_t* dnc, mio_dns_msg_t* msg) mio_t* mio = dnc->mio; dnc_dns_msg_xtn_t* msgxtn = dnc_dns_msg_getxtn(msg); -MIO_DEBUG1 (mio, "releasing dns msg %d\n", (int)mio_ntoh16(mio_dns_msg_to_pkt(msg)->id)); +MIO_DEBUG1 (mio, "DNC - releasing dns message - msgid:%d\n", (int)mio_ntoh16(mio_dns_msg_to_pkt(msg)->id)); if (msg == dnc->pending_req || msgxtn->next || msgxtn->prev) { @@ -179,13 +179,13 @@ static int handle_tcp_packet (mio_dev_sck_t* dev, mio_dns_pkt_t* pkt, mio_uint16 if (!pkt->qr) { - MIO_DEBUG0 (mio, "dropping dns request received over tcp ...\n"); /* TODO: add source info */ + MIO_DEBUG0 (mio, "DNC - dropping dns request received over tcp ...\n"); /* TODO: add source info */ return 0; /* drop request. nothing to do */ } id = mio_ntoh16(pkt->id); -MIO_DEBUG1 (mio, "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>id [%d] >>>>>>>>>>>>>>>>>>\n", id); +MIO_DEBUG1 (mio, "DNC - got dns response over tcp - msgid:%d\n", id); reqmsg = dnc->pending_req; while (reqmsg) @@ -203,7 +203,7 @@ MIO_DEBUG1 (mio, "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>id [%d] >>>> reqmsg = reqmsgxtn->next; } -MIO_DEBUG1 (mio, "unknown dns response over tcp... %d\n", pkt->id); /* TODO: add source info */ +MIO_DEBUG1 (mio, "DNC - unknown dns response over tcp... msgid:%d\n", id); /* TODO: add source info */ return 0; } @@ -240,12 +240,12 @@ static int on_tcp_read (mio_dev_sck_t* dev, const void* data, mio_iolen_t dlen, if (MIO_UNLIKELY(dlen <= -1)) { - MIO_DEBUG1 (mio, "dns tcp read error ....%js\n", mio_geterrmsg(mio)); /* TODO: add source packet */ + MIO_DEBUG1 (mio, "DNC - dns tcp read error ....%js\n", mio_geterrmsg(mio)); /* TODO: add source packet */ goto oops; } else if (MIO_UNLIKELY(dlen == 0)) { - MIO_DEBUG0 (mio, "dns tcp read error ...premature socket hangul\n"); /* TODO: add source packet */ + MIO_DEBUG0 (mio, "DNC - dns tcp read error ...premature tcp socket end\n"); /* TODO: add source packet */ goto oops; } @@ -311,7 +311,7 @@ static void on_tcp_reply_timeout (mio_t* mio, const mio_ntime_t* now, mio_tmrjob MIO_ASSERT (mio, reqmsgxtn->rtmridx == MIO_TMRIDX_INVALID); MIO_ASSERT (mio, dev == dnc->tcp_sck); -MIO_DEBUG0 (mio, "*** TIMEOUT ==> unable to receive dns response in time over TCP...\n"); +MIO_DEBUG1 (mio, "DNC - unable to receive dns response in time over TCP - msgid:%d\n", (int)mio_ntoh16(mio_dns_msg_to_pkt(reqmsg)->id)); if (MIO_LIKELY(reqmsgxtn->on_done)) reqmsgxtn->on_done (dnc, reqmsg, MIO_ETMOUT, MIO_NULL, 0); release_dns_msg (dnc, reqmsg); @@ -336,6 +336,8 @@ static int on_tcp_write (mio_dev_sck_t* dev, mio_iolen_t wrlen, void* wrctx, con /* question. schedule to wait for response */ mio_tmrjob_t tmrjob; + MIO_DEBUG1 (mio, "DNC - sent dns question over tcp - msgid:%d\n", (int)mio_ntoh16(mio_dns_msg_to_pkt(msg)->id)); + MIO_MEMSET (&tmrjob, 0, MIO_SIZEOF(tmrjob)); tmrjob.ctx = msg; mio_gettime (mio, &tmrjob.when); @@ -347,7 +349,7 @@ static int on_tcp_write (mio_dev_sck_t* dev, mio_iolen_t wrlen, void* wrctx, con { /* call the callback to indicate this operation failure in the middle of transaction */ status = mio_geterrnum(mio); - MIO_DEBUG0 (mio, "unable to schedule tcp timeout...\n"); + MIO_DEBUG1 (mio, "DNC - unable to schedule tcp timeout - msgid: %d\n", (int)mio_ntoh16(mio_dns_msg_to_pkt(msg)->id)); goto finalize; } @@ -356,6 +358,7 @@ static int on_tcp_write (mio_dev_sck_t* dev, mio_iolen_t wrlen, void* wrctx, con else { /* no error. successfuly sent a message. no reply is expected */ + MIO_DEBUG1 (mio, "DNC - sent dns message over tcp - msgid:%d\n", (int)mio_ntoh16(mio_dns_msg_to_pkt(msg)->id)); status = MIO_ENOERR; goto finalize; } @@ -375,6 +378,8 @@ static int write_dns_msg_over_tcp (mio_dev_sck_t* dev, mio_dns_msg_t* msg) mio_uint16_t pktlen; mio_iovec_t iov[2]; + MIO_DEBUG1 (mio, "DNC - sending dns message over tcp - msgid:%d\n", (int)mio_ntoh16(mio_dns_msg_to_pkt(msg)->id)); + pktlen = mio_hton16(msg->pktlen); MIO_ASSERT (mio, msgxtn->rtries == 0); @@ -426,11 +431,11 @@ static void on_tcp_disconnect (mio_dev_sck_t* dev) if (status == MIO_ENOERR) { - MIO_DEBUG0 (mio, "TCP DISCONNECTED\n"); + MIO_DEBUG0 (mio, "DNC - TCP DISCONNECTED\n"); } else { - MIO_DEBUG2 (mio, "TCP UNABLED TO CONNECT %d -> %js\n", status, mio_errnum_to_errstr(status)); + MIO_DEBUG2 (mio, "DNC - TCP UNABLE TO CONNECT %d -> %js\n", status, mio_errnum_to_errstr(status)); } reqmsg = dnc->pending_req; @@ -509,7 +514,7 @@ static int switch_reqmsg_transport_to_tcp (mio_svc_dnc_t* dnc, mio_dns_msg_t* re reqmsgxtn->rtries = 0; if (!reqmsgxtn->pending && mio_dns_msg_to_pkt(reqmsg)->qr == 0) chain_pending_dns_reqmsg (dnc, reqmsg); -printf ("SWITCHED >>>>>>>>>>>>>>>>> %p %p %p %p %p\n", reqmsg, reqmsgxtn, reqmsgxtn->dev, dnc->udp_sck, dnc->tcp_sck); +MIO_DEBUG6 (mio, "DNC - switched transport to tcp - msgid:%d %p %p %p %p %p\n", (int)mio_ntoh16(mio_dns_msg_to_pkt(reqmsg)->id), reqmsg, reqmsgxtn, reqmsgxtn->dev, dnc->udp_sck, dnc->tcp_sck); if (MIO_DEV_SCK_GET_PROGRESS(dnc->tcp_sck) & MIO_DEV_SCK_CONNECTED) { @@ -536,19 +541,19 @@ static int on_udp_read (mio_dev_sck_t* dev, const void* data, mio_iolen_t dlen, if (MIO_UNLIKELY(dlen <= -1)) { - MIO_DEBUG1 (mio, "dns read error ....%js\n", mio_geterrmsg(mio)); /* TODO: add source packet */ + MIO_DEBUG1 (mio, "DNC - dns read error ....%js\n", mio_geterrmsg(mio)); /* TODO: add source packet */ return 0; } if (MIO_UNLIKELY(dlen < MIO_SIZEOF(*pkt))) { - MIO_DEBUG0 (mio, "dns packet too small from ....\n"); /* TODO: add source packet */ + MIO_DEBUG0 (mio, "DNC - dns packet too small from ....\n"); /* TODO: add source packet */ return 0; /* drop */ } pkt = (mio_dns_pkt_t*)data; if (!pkt->qr) { - MIO_DEBUG0 (mio, "dropping dns request received ...\n"); /* TODO: add source info */ + MIO_DEBUG0 (mio, "DNC - dropping dns request received ...\n"); /* TODO: add source info */ return 0; /* drop request */ } @@ -583,7 +588,7 @@ static int on_udp_read (mio_dev_sck_t* dev, const void* data, mio_iolen_t dlen, /* TODO: add an option to call an error callback with TRUNCATION error code instead of fallback to received UDP truncated message */ } -MIO_DEBUG1 (mio, "received dns response over udp..id %d\n", id); +MIO_DEBUG1 (mio, "DNC - received dns response over udp for msgid:%d\n", id); if (MIO_LIKELY(reqmsgxtn->on_done)) reqmsgxtn->on_done (dnc, reqmsg, MIO_ENOERR, data, dlen); release_dns_msg (dnc, reqmsg); return 0; @@ -593,7 +598,7 @@ MIO_DEBUG1 (mio, "received dns response over udp..id %d\n", id); } /* the response id didn't match the ID of pending requests - need to wait longer? */ -MIO_DEBUG1 (mio, "unknown dns response... %d\n", pkt->id); /* TODO: add source info */ +MIO_DEBUG1 (mio, "DNC - unknown dns response over udp... msgid:%d\n", id); /* TODO: add source info */ return 0; } @@ -608,12 +613,13 @@ static void on_udp_reply_timeout (mio_t* mio, const mio_ntime_t* now, mio_tmrjob MIO_ASSERT (mio, msgxtn->rtmridx == MIO_TMRIDX_INVALID); MIO_ASSERT (mio, dev == dnc->udp_sck); -MIO_DEBUG0 (mio, "*** TIMEOUT ==> unable to receive dns response in time...\n"); +MIO_DEBUG1 (mio, "DNC - unable to receive dns response in time over udp - msgid:%d\n", (int)mio_ntoh16(mio_dns_msg_to_pkt(reqmsg)->id)); if (msgxtn->rtries < msgxtn->rmaxtries) { mio_ntime_t* tmout; tmout = MIO_IS_POS_NTIME(&msgxtn->wtmout)? &msgxtn->wtmout: MIO_NULL; +MIO_DEBUG1 (mio, "DNC - sending dns question again over udp - msgid:%d\n", (int)mio_ntoh16(mio_dns_msg_to_pkt(reqmsg)->id)); if (mio_dev_sck_timedwrite(dev, mio_dns_msg_to_pkt(reqmsg), reqmsg->pktlen, tmout, reqmsg, &msgxtn->servaddr) >= 0) return; /* resent */ /* retry failed */ @@ -624,7 +630,6 @@ MIO_DEBUG0 (mio, "*** TIMEOUT ==> unable to receive dns response in time...\n"); release_dns_msg (dnc, reqmsg); } - static int on_udp_write (mio_dev_sck_t* dev, mio_iolen_t wrlen, void* wrctx, const mio_skad_t* dstaddr) { mio_t* mio = dev->mio; @@ -646,7 +651,7 @@ static int on_udp_write (mio_dev_sck_t* dev, mio_iolen_t wrlen, void* wrctx, con /* question. schedule to wait for response */ mio_tmrjob_t tmrjob; -MIO_DEBUG1 (mio, "sent dns question %d\n", (int)mio_ntoh16(mio_dns_msg_to_pkt(msg)->id)); + MIO_DEBUG1 (mio, "DNC - sent dns question over udp - msgid:%d\n", (int)mio_ntoh16(mio_dns_msg_to_pkt(msg)->id)); MIO_MEMSET (&tmrjob, 0, MIO_SIZEOF(tmrjob)); tmrjob.ctx = msg; mio_gettime (mio, &tmrjob.when); @@ -658,10 +663,10 @@ MIO_DEBUG1 (mio, "sent dns question %d\n", (int)mio_ntoh16(mio_dns_msg_to_pkt(ms { /* call the callback to indicate this operation failure in the middle of transaction */ status = mio_geterrnum(mio); - MIO_DEBUG0 (mio, "unable to schedule timeout...\n"); + MIO_DEBUG1 (mio, "DNC - unable to schedule udp timeout - msgid:%d\n", (int)mio_ntoh16(mio_dns_msg_to_pkt(msg)->id)); goto finalize; } - + if (msgxtn->rtries == 0) { /* this is the first wait */ @@ -673,7 +678,7 @@ MIO_DEBUG1 (mio, "sent dns question %d\n", (int)mio_ntoh16(mio_dns_msg_to_pkt(ms } else { -MIO_DEBUG1 (mio, "sent dns message %d\n", (int)mio_ntoh16(mio_dns_msg_to_pkt(msg)->id)); + MIO_DEBUG1 (mio, "DNC - sent dns message over udp - msgid:%d\n", (int)mio_ntoh16(mio_dns_msg_to_pkt(msg)->id)); /* sent an answer. however this may be a question if msgxtn->rmaxtries is 0. */ status = MIO_ENOERR; goto finalize; @@ -693,7 +698,7 @@ static void on_udp_connect (mio_dev_sck_t* dev) static void on_udp_disconnect (mio_dev_sck_t* dev) { - mio_t* mio = dev->mio; + /*mio_t* mio = dev->mio;*/ mio_svc_dnc_t* dnc = ((dnc_sck_xtn_t*)mio_dev_sck_getxtn(dev))->dnc; mio_dns_msg_t* reqmsg; @@ -763,6 +768,7 @@ mio_svc_dnc_t* mio_svc_dnc_start (mio_t* mio, const mio_skad_t* serv_addr, const } MIO_SVCL_APPEND_SVC (&mio->actsvc, (mio_svc_t*)dnc); + MIO_DEBUG1 (mio, "DNC - STARTED SERVICE %p\n", dnc); return dnc; oops: @@ -778,6 +784,7 @@ void mio_svc_dnc_stop (mio_svc_dnc_t* dnc) { mio_t* mio = dnc->mio; + MIO_DEBUG1 (mio, "DNC - STOPPING SERVICE %p\n", dnc); if (dnc->udp_sck) mio_dev_sck_kill (dnc->udp_sck); if (dnc->tcp_sck) mio_dev_sck_kill (dnc->tcp_sck); while (dnc->pending_req) release_dns_msg (dnc, dnc->pending_req); @@ -793,6 +800,8 @@ static MIO_INLINE int send_dns_msg (mio_svc_dnc_t* dnc, mio_dns_msg_t* msg, int if ((send_flags & MIO_SVC_DNC_SEND_FLAG_PREFER_TCP) && switch_reqmsg_transport_to_tcp(dnc, msg) >= 0) return 0; + MIO_DEBUG1 (dnc->mio, "DNC - sending dns message over udp - msgid:%d\n", (int)mio_ntoh16(mio_dns_msg_to_pkt(msg)->id)); + tmout = MIO_IS_POS_NTIME(&msgxtn->wtmout)? &msgxtn->wtmout: MIO_NULL; /* TODO: optionally, override dnc->serv_addr and use the target address passed as a parameter */ return mio_dev_sck_timedwrite(dnc->udp_sck, mio_dns_msg_to_pkt(msg), msg->pktlen, tmout, msg, &msgxtn->servaddr); diff --git a/mio/lib/htrd.c b/mio/lib/htrd.c index 55e60d2..e073909 100644 --- a/mio/lib/htrd.c +++ b/mio/lib/htrd.c @@ -728,7 +728,7 @@ static mio_htb_pair_t* hdr_cbserter ( } else { - if (capture_key_header (tx->htrd, p) <= -1) + if (capture_key_header(tx->htrd, p) <= -1) { /* Destroy the pair created here * as it is not added to the hash table yet */ @@ -793,7 +793,7 @@ static mio_htb_pair_t* hdr_cbserter ( /* append it to the list*/ tmp->next = val; - if (capture_key_header (tx->htrd, pair) <= -1) return MIO_NULL; + if (capture_key_header(tx->htrd, pair) <= -1) return MIO_NULL; return pair; } } @@ -1102,7 +1102,7 @@ done: } /* feed the percent encoded string */ -int mio_htrd_feed (mio_htrd_t* htrd, const mio_bch_t* req, mio_oow_t len) +int mio_htrd_feed (mio_htrd_t* htrd, const mio_bch_t* req, mio_oow_t len, mio_oow_t* rem) { const mio_bch_t* end = req + len; const mio_bch_t* ptr = req; @@ -1122,7 +1122,7 @@ int mio_htrd_feed (mio_htrd_t* htrd, const mio_bch_t* req, mio_oow_t len) { /* treat everything as contents. * i don't care about headers or whatsoever. */ - return push_content (htrd, req, len); + return push_content(htrd, req, len); } /* does this goto drop code maintainability? */ @@ -1199,7 +1199,7 @@ int mio_htrd_feed (mio_htrd_t* htrd, const mio_bch_t* req, mio_oow_t len) /* we got a complete request header. */ MIO_ASSERT (htrd->mio, htrd->fed.s.crlf <= 3); - + /* reset the crlf state */ htrd->fed.s.crlf = 0; /* reset the raw request length */ @@ -1228,7 +1228,7 @@ int mio_htrd_feed (mio_htrd_t* htrd, const mio_bch_t* req, mio_oow_t len) * reading CGI outputs. So it comes with * awkwardity described above. */ - if (ptr < end && push_content (htrd, ptr, end - ptr) <= -1) return -1; + if (ptr < end && push_content(htrd, ptr, end - ptr) <= -1) return -1; /* i don't really know if it is really completed * with content. MIO_HTRD_PEEKONLY is not compatible @@ -1246,7 +1246,7 @@ int mio_htrd_feed (mio_htrd_t* htrd, const mio_bch_t* req, mio_oow_t len) if (htrd->re.flags & MIO_HTRE_ATTR_CHUNKED) { /* transfer-encoding: chunked */ - MIO_ASSERT (htrd->mio, !(htrd->re.flags & MIO_HTRE_ATTR_LENGTH)); + /*MIO_ASSERT (htrd->mio, !(htrd->re.flags & MIO_HTRE_ATTR_LENGTH)); <- this assertion is wrong. non-conforming client may include content-length while transfer-encoding is chunked*/ dechunk_start: htrd->fed.s.chunk.phase = GET_CHUNK_LEN; @@ -1254,8 +1254,8 @@ int mio_htrd_feed (mio_htrd_t* htrd, const mio_bch_t* req, mio_oow_t len) htrd->fed.s.chunk.count = 0; dechunk_resume: - ptr = getchunklen (htrd, ptr, end - ptr); - if (ptr == MIO_NULL) return -1; + ptr = getchunklen(htrd, ptr, end - ptr); + if (MIO_UNLIKELY(!ptr)) return -1; if (htrd->fed.s.chunk.phase == GET_CHUNK_LEN) { @@ -1274,8 +1274,8 @@ int mio_htrd_feed (mio_htrd_t* htrd, const mio_bch_t* req, mio_oow_t len) htrd->fed.s.crlf = 2; dechunk_get_trailers: - ptr = get_trailing_headers (htrd, ptr, end); - if (ptr == MIO_NULL) return -1; + ptr = get_trailing_headers(htrd, ptr, end); + if (!MIO_UNLIKELY(ptr)) return -1; if (htrd->fed.s.chunk.phase == GET_CHUNK_TRAILERS) { @@ -1450,12 +1450,12 @@ XXXXXXXX * plus complete content body and the header * of the next request. */ int n; - htrd->errnum = MIO_HTRD_ENOERR; + htrd->errnum = MIO_HTRD_ENOERR; n = htrd->recbs->peek(htrd, &htrd->re); if (n <= -1) { if (htrd->errnum == MIO_HTRD_ENOERR) - htrd->errnum = MIO_HTRD_ERECBS; + htrd->errnum = MIO_HTRD_ERECBS; /* need to clear request on error? clear_feed (htrd); */ return -1; @@ -1472,7 +1472,7 @@ XXXXXXXX if (n <= -1) { if (htrd->errnum == MIO_HTRD_ENOERR) - htrd->errnum = MIO_HTRD_ERECBS; + htrd->errnum = MIO_HTRD_ERECBS; /* need to clear request on error? clear_feed (htrd); */ return -1; @@ -1484,28 +1484,37 @@ mio_printf (MIO_T("CONTENT_LENGTH %d, RAW HEADER LENGTH %d\n"), (int)MIO_BECS_LEN(&htrd->re.content), (int)MIO_BECS_LEN(&htrd->fed.b.raw)); #endif - clear_feed (htrd); - if (ptr >= end) return 0; /* no more feeds to handle */ - if (htrd->flags & FEEDING_SUSPENDED) + if (rem) + { + /* stop even if there are fed data left */ + *rem = end - ptr; /* remaining feeds */ + return 0; /* to indicate completed when the 'rem' is not NULL */ + } + else if (ptr >= end) + { + /* no more feeds to handle */ + return 0; + } + + if (htrd->flags & FEEDING_SUSPENDED) /* in case the callback called mio_htrd_suspend() */ { htrd->errnum = MIO_HTRD_ESUSPENDED; return -1; } /*if (htrd->option & MIO_HTRD_DUMMY)*/ - if (htrd->flags & FEEDING_DUMMIFIED) + if (htrd->flags & FEEDING_DUMMIFIED) /* in case the callback called mio_htrd_dummify() */ { /* once the mode changes to RAW in a callback, * left-over is pushed as contents */ if (ptr < end) - return push_content (htrd, ptr, end - ptr); + return push_content(htrd, ptr, end - ptr); else return 0; } - /* let ptr point to the next character to LF or * the optional contents */ req = ptr; @@ -1537,31 +1546,32 @@ mio_printf (MIO_T("CONTENT_LENGTH %d, RAW HEADER LENGTH %d\n"), if (ptr > req) { /* enbuffer the incomplete request */ - if (push_to_buffer (htrd, &htrd->fed.b.raw, req, ptr - req) <= -1) return -1; + if (push_to_buffer(htrd, &htrd->fed.b.raw, req, ptr - req) <= -1) return -1; } feedme_more: if (header_completed_during_this_feed && htrd->recbs->peek) { int n; - htrd->errnum = MIO_HTRD_ENOERR; - n = htrd->recbs->peek (htrd, &htrd->re); + htrd->errnum = MIO_HTRD_ENOERR; + n = htrd->recbs->peek(htrd, &htrd->re); if (n <= -1) { if (htrd->errnum == MIO_HTRD_ENOERR) - htrd->errnum = MIO_HTRD_ERECBS; + htrd->errnum = MIO_HTRD_ERECBS; /* need to clear request on error? clear_feed (htrd); */ return -1; } } + if (rem) *rem = 0; return 0; } int mio_htrd_halt (mio_htrd_t* htrd) { - if (htrd->fed.s.flags & CONSUME_UNTIL_CLOSE || !htrd->clean) + if ((htrd->fed.s.flags & CONSUME_UNTIL_CLOSE) || !htrd->clean) { mio_htre_completecontent (&htrd->re); @@ -1573,7 +1583,7 @@ int mio_htrd_halt (mio_htrd_t* htrd) if (n <= -1) { if (htrd->errnum == MIO_HTRD_ENOERR) - htrd->errnum = MIO_HTRD_ERECBS; + htrd->errnum = MIO_HTRD_ERECBS; /* need to clear request on error? clear_feed (htrd); */ return -1; @@ -1657,7 +1667,7 @@ int mio_htrd_scanqparam (mio_htrd_t* htrd, const mio_bcs_t* cstr) MIO_ASSERT (htrd->mio, htrd->recbs->qparamstr != MIO_NULL); htrd->errnum = MIO_HTRD_ENOERR; - if (htrd->recbs->qparamstr (htrd, &key, &val) <= -1) + if (htrd->recbs->qparamstr(htrd, &key, &val) <= -1) { if (htrd->errnum == MIO_HTRD_ENOERR) htrd->errnum = MIO_HTRD_ERECBS; diff --git a/mio/lib/http-svr.c b/mio/lib/http-svr.c index ea5e75b..d3a0321 100644 --- a/mio/lib/http-svr.c +++ b/mio/lib/http-svr.c @@ -172,7 +172,6 @@ if (mio_htre_getcontentlen(req) > 0) * 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 0 /* TODO support X-HTTP-Method-Override */ if (data.method == MIO_HTTP_POST) @@ -197,36 +196,22 @@ if (mio_htre_getcontentlen(req) > 0) } else { -//mio_svc_htts_sendstatus (htts, csck, 500, mth, mio_htre_getversion(req), (req->flags & MIO_HTRE_ATTR_KEEPALIVE), MIO_NULL); -//return 0; - if (mth == MIO_HTTP_POST && - !(req->flags & MIO_HTRE_ATTR_LENGTH) && - !(req->flags & MIO_HTRE_ATTR_CHUNKED)) + if (mth == MIO_HTTP_POST && !(req->flags & (MIO_HTRE_ATTR_LENGTH | MIO_HTRE_ATTR_CHUNKED))) { /* POST without Content-Length nor not chunked */ - req->flags &= ~MIO_HTRE_ATTR_KEEPALIVE; - mio_htre_discardcontent (req); - + mio_htre_discardcontent (req); /* 411 Length Required - can't keep alive. Force disconnect */ - mio_svc_htts_sendstatus (htts, csck, 411, mth, mio_htre_getversion(req), 0, MIO_NULL); /* TODO: error check... */ + req->flags &= ~MIO_HTRE_ATTR_KEEPALIVE; /* to cause sendstatus() to close */ + if (mio_svc_htts_sendstatus(htts, csck, req, 411, MIO_NULL) <= -1) mio_dev_sck_halt (csck); /*TODO: redo this sendstatus */ } else { -/* TODO: handle 100 continue??? */ - if ((req->flags & MIO_HTRE_ATTR_EXPECT) && - mio_comp_http_version_numbers(&req->version, 1, 1) >= 0 && - mio_htre_getcontentlen(req) <= 0) - { - if (req->flags & MIO_HTRE_ATTR_EXPECT100) - { - mio_dev_sck_write(csck, "HTTP/1.1 100 Continue\r\n", 23, MIO_NULL, MIO_NULL); - } - else - { - } - } - const mio_bch_t* qpath = mio_htre_getqpath(req); + if (mio_svc_htts_docgi(htts, csck, req, "") <= -1) + { + mio_dev_sck_halt (csck); + } +#if 0 /* if (mio_comp_bcstr(qpath, "/testfunc", 0) == 0) { @@ -241,54 +226,8 @@ if (mio_htre_getcontentlen(req) > 0) mio_htre_discardcontent (req); mio_dev_sck_halt (csck); } - - - /* - if (mio_comp_bcstr(qpath, "/mio.c", 0) == 0) - { - mio_svc_htts_sendfile (htts, csck, "/home/hyung-hwan/projects/mio/lib/mio.c", 200, mth, mio_htre_getversion(req), (req->flags & MIO_HTRE_ATTR_KEEPALIVE)); - } - else - { - mio_svc_htts_sendstatus (htts, csck, 500, mth, mio_htre_getversion(req), (req->flags & MIO_HTRE_ATTR_KEEPALIVE), MIO_NULL); - }*/ - } -#if 0 - else if (server_xtn->makersrc(htts, 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 MIO_HTTPD_RSRC_ERROR - * resource. */ - mio_htts_discardcontent (req); - task = mio_htts_entaskerror(htts, client, MIO_NULL, 500, req); - } - else - { - task = MIO_NULL; - - if ((rsrc.flags & MIO_HTTPD_RSRC_100_CONTINUE) && - (task = mio_htts_entaskcontinue(htts, client, task, req)) == MIO_NULL) - { - /* inject '100 continue' first if it is needed */ - goto oops; - } - - /* arrange the actual resource to be returned */ - task = mio_htts_entaskrsrc(htts, client, task, &rsrc, req); - server_xtn->freersrc (htts, client, req, &rsrc); - - /* if the resource is indicating to return an error, - * discard the contents since i won't return them */ - if (rsrc.type == MIO_HTTPD_RSRC_ERROR) - { - mio_htre_discardcontent (req); - } - } - - if (task == MIO_NULL) goto oops; #endif + } } } else @@ -296,38 +235,14 @@ if (mio_htre_getcontentlen(req) > 0) /* contents are all received */ if (mth == MIO_HTTP_CONNECT) { - MIO_DEBUG1 (htts->mio, "Switching HTRD to DUMMY for [%hs]\n", mio_htre_getqpath(req)); /* Switch the http reader to a dummy mode so that the subsqeuent * input(request) is just treated as data to the request just * completed */ mio_htrd_dummify (cli->htrd); - /* connect is not handled in the peek mode. create a proxy resource here */ -//////// -#if 0 - if (server_xtn->makersrc(htts, client, req, &rsrc) <= -1) - { - MIO_DEBUG1 (htts->mio, "Cannot make resource for [%hs]\n", mio_htre_getqpath(req)); - - /* 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 MIO_HTTPD_RSRC_ERROR - * resource. */ - task = mio_htts_entaskerror(htts, client, MIO_NULL, 500, req); - } - else - { - /* arrange the actual resource to be returned */ - task = mio_htts_entaskrsrc (htts, client, MIO_NULL, &rsrc, req); - server_xtn->freersrc (htts, client, req, &rsrc); - } - - if (task == MIO_NULL) goto oops; -#endif -//////// + /* TODO: arrange to forward all raw bytes */ } else if (req->flags & MIO_HTRE_ATTR_PROXIED) { @@ -446,25 +361,52 @@ static void fini_client (mio_svc_htts_cli_t* cli) /* ------------------------------------------------------------------------ */ -static int client_on_read (mio_dev_sck_t* sck, const void* buf, mio_iolen_t len, const mio_skad_t* srcaddr) +static int listener_on_read (mio_dev_sck_t* sck, const void* buf, mio_iolen_t len, const mio_skad_t* srcaddr) { + /* unlike the function name, this callback is set on both the listener and the client. + * however, it must never be triggered for the listener */ + mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(sck); -printf ("** HTTS - client read %p %d -> htts:%p\n", sck, (int)len, cli->htts); + mio_oow_t rem; + int x; + + MIO_ASSERT (sck->mio, sck != cli->htts->lsck); + +printf ("** HTTS - client read socket(%p) %d -> htts:%p\n", sck, (int)len, cli->htts); +{ +mio_iolen_t i; +for (i = 0; i < len; i++) +{ +printf ("%c", ((const mio_uint8_t*)buf)[i]); +} +} if (len <= 0) { mio_dev_sck_halt (sck); } - else if (mio_htrd_feed(cli->htrd, buf, len) <= -1) + else if ((x = mio_htrd_feed(cli->htrd, buf, len, &rem)) <= -1) { +/* in some cases, we may have to send some http response depending on the failure type */ +/* BADRE -> bad request? */ +printf ("** HTTS - client htrd feed failure socket(%p) - %d\n", sck, x); + /* TODO: either send bad request or server failure ... */ mio_dev_sck_halt (sck); } + else if (rem > 0) + { + /* TODO: store the remaining data in the client's buffer */ + } return 0; } -static int client_on_write (mio_dev_sck_t* sck, mio_iolen_t wrlen, void* wrctx, const mio_skad_t* dstaddr) +static int listener_on_write (mio_dev_sck_t* sck, mio_iolen_t wrlen, void* wrctx, const mio_skad_t* dstaddr) { + mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(sck); mio_svc_htts_rsrc_t* rsrc = (mio_svc_htts_rsrc_t*)wrctx; + + MIO_ASSERT (sck->mio, sck != cli->htts->lsck); + if (rsrc) { int n; @@ -479,18 +421,7 @@ static int client_on_write (mio_dev_sck_t* sck, mio_iolen_t wrlen, void* wrctx, } return 0; -} -/* ------------------------------------------------------------------------ */ - -static int listener_on_read (mio_dev_sck_t* sck, const void* buf, mio_iolen_t len, const mio_skad_t* srcaddr) -{ - return 0; -} - -static int listener_on_write (mio_dev_sck_t* sck, mio_iolen_t wrlen, void* wrctx, const mio_skad_t* dstaddr) -{ - return 0; } static void listener_on_connect (mio_dev_sck_t* sck) @@ -502,13 +433,9 @@ static void listener_on_connect (mio_dev_sck_t* sck) /* accepted a new client */ MIO_DEBUG3 (sck->mio, "HTTS(%p) - accepted... %p %d \n", cli->htts, sck, sck->hnd); - /* the accepted socket inherits various event callbacks. switch some of them to avoid sharing */ - sck->on_read = client_on_read; - sck->on_write = client_on_write; - if (init_client(cli, sck) <= -1) { - MIO_INFO2 (sck->mio, "UNABLE TO INITIALIZE NEW CLIENT %p %d\n", sck, (int)sck->hnd); + MIO_DEBUG2 (sck->mio, "UNABLE TO INITIALIZE NEW CLIENT %p %d\n", sck, (int)sck->hnd); mio_dev_sck_halt (sck); } } @@ -529,21 +456,21 @@ static void listener_on_disconnect (mio_dev_sck_t* sck) { case MIO_DEV_SCK_CONNECTING: /* only for connecting sockets */ - MIO_INFO1 (sck->mio, "OUTGOING SESSION DISCONNECTED - FAILED TO CONNECT (%d) TO REMOTE SERVER\n", (int)sck->hnd); + MIO_DEBUG1 (sck->mio, "OUTGOING SESSION DISCONNECTED - FAILED TO CONNECT (%d) TO REMOTE SERVER\n", (int)sck->hnd); break; case MIO_DEV_SCK_CONNECTING_SSL: /* only for connecting sockets */ - MIO_INFO1 (sck->mio, "OUTGOING SESSION DISCONNECTED - FAILED TO SSL-CONNECT (%d) TO REMOTE SERVER\n", (int)sck->hnd); + MIO_DEBUG1 (sck->mio, "OUTGOING SESSION DISCONNECTED - FAILED TO SSL-CONNECT (%d) TO REMOTE SERVER\n", (int)sck->hnd); break; case MIO_DEV_SCK_CONNECTED: /* only for connecting sockets */ - MIO_INFO1 (sck->mio, "OUTGOING CLIENT CONNECTION GOT TORN DOWN %p(%d).......\n", (int)sck->hnd); + MIO_DEBUG1 (sck->mio, "OUTGOING CLIENT CONNECTION GOT TORN DOWN %p(%d).......\n", (int)sck->hnd); break; case MIO_DEV_SCK_LISTENING: - MIO_INFO2 (sck->mio, "LISTNER SOCKET %p(%d) - SHUTTUING DOWN\n", sck, (int)sck->hnd); + MIO_DEBUG2 (sck->mio, "LISTNER SOCKET %p(%d) - SHUTTUING DOWN\n", sck, (int)sck->hnd); break; case MIO_DEV_SCK_ACCEPTING_SSL: /* special case. */ @@ -552,17 +479,17 @@ static void listener_on_disconnect (mio_dev_sck_t* sck) * the cli extension are is not initialized yet */ MIO_ASSERT (sck->mio, sck != cli->sck); MIO_ASSERT (sck->mio, cli->sck == cli->htts->lsck); /* the field is a copy of the extension are of the listener socket. so it should point to the listner socket */ - MIO_INFO2 (sck->mio, "LISTENER UNABLE TO SSL-ACCEPT CLIENT %p(%d) ....%p\n", sck, (int)sck->hnd); + MIO_DEBUG2 (sck->mio, "LISTENER UNABLE TO SSL-ACCEPT CLIENT %p(%d) ....%p\n", sck, (int)sck->hnd); return; case MIO_DEV_SCK_ACCEPTED: /* only for sockets accepted by the listeners. will never come here because * the disconnect call for such sockets have been changed in listener_on_connect() */ - MIO_INFO2 (sck->mio, "ACCEPTED CLIENT SOCKET %p(%d) GOT DISCONNECTED.......\n", sck, (int)sck->hnd); + MIO_DEBUG2 (sck->mio, "ACCEPTED CLIENT SOCKET %p(%d) GOT DISCONNECTED.......\n", sck, (int)sck->hnd); break; default: - MIO_INFO2 (sck->mio, "SOCKET %p(%d) DISCONNECTED AFTER ALL.......\n", sck, (int)sck->hnd); + MIO_DEBUG2 (sck->mio, "SOCKET %p(%d) DISCONNECTED AFTER ALL.......\n", sck, (int)sck->hnd); break; } @@ -639,7 +566,7 @@ mio_svc_htts_t* mio_svc_htts_start (mio_t* mio, const mio_skad_t* bind_addr) MIO_MEMSET (&info, 0, MIO_SIZEOF(info)); info.b.localaddr = *bind_addr; info.b.options = MIO_DEV_SCK_BIND_REUSEADDR | MIO_DEV_SCK_BIND_REUSEPORT; - info.b.options |= MIO_DEV_SCK_BIND_SSL; + /*info.b.options |= MIO_DEV_SCK_BIND_SSL; */ info.b.ssl_certfile = "localhost.crt"; info.b.ssl_keyfile = "localhost.key"; if (mio_dev_sck_bind(htts->lsck, &info.b) <= -1) goto oops; @@ -657,7 +584,7 @@ mio_svc_htts_t* mio_svc_htts_start (mio_t* mio, const mio_skad_t* bind_addr) MIO_SVCL_APPEND_SVC (&mio->actsvc, (mio_svc_t*)htts); CLIL_INIT (&htts->cli); - MIO_DEBUG3 (mio, "STARTED SVC(HTTS) LISTENER %p LISTENER SOCKET %p(%d)\n", htts, htts->lsck, (int)htts->lsck->hnd); + MIO_DEBUG3 (mio, "HTTS - STARTED SERVICE %p - LISTENER SOCKET %p(%d)\n", htts, htts->lsck, (int)htts->lsck->hnd); return htts; oops: @@ -673,7 +600,7 @@ void mio_svc_htts_stop (mio_svc_htts_t* htts) { mio_t* mio = htts->mio; - MIO_DEBUG3 (mio, "STOPPING SVC(HTTS) %p LISTENER SOCKET %p(%d)\n", htts, htts->lsck, (int)(htts->lsck? htts->lsck->hnd: -1)); + MIO_DEBUG3 (mio, "HTTS - STOPPING SERVICE %p - LISTENER SOCKET %p(%d)\n", htts, htts->lsck, (int)(htts->lsck? htts->lsck->hnd: -1)); /* htts->lsck may be null if the socket has been destroyed for operational error and * forgotten in the disconnect callback thereafter */ @@ -732,7 +659,7 @@ mio_svc_htts_rsrc_t* mio_svc_htts_rsrc_make (mio_svc_htts_t* htts, mio_dev_sck_t void mio_svc_htts_rsrc_kill (mio_svc_htts_rsrc_t* rsrc) { -printf ("RSRC KILL >>>>> %p\n", rsrc->htts); +printf ("RSRC KILL >>>>> htts=> %p\n", rsrc->htts); mio_t* mio = rsrc->htts->mio; if (rsrc->on_kill) rsrc->on_kill (rsrc); @@ -936,34 +863,99 @@ int mio_svc_htts_dofile (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_t* /* ----------------------------------------------------------------- */ + +struct cgi_state_t +{ + /**** CHANGE THESE FIELDS AFTER RSRC CLEANUP */ + mio_svc_htts_t* htts; + mio_svc_htts_rsrc_t* rsrc_prev; + mio_svc_htts_rsrc_t* rsrc_next; + mio_svc_htts_rsrc_on_kill_t on_kill; + /**** CHANGE THESE FIELDS AFTER RSRC CLEANUP */ + + mio_oow_t num_pending_writes_to_client; + mio_oow_t num_pending_writes_to_peer; + mio_dev_pro_t* peer; + mio_htrd_t* peer_htrd; + mio_svc_htts_cli_t* cli; + mio_htre_t* req; + mio_oow_t req_content_length; + + mio_dev_sck_on_read_t cli_org_on_read; + mio_dev_sck_on_write_t cli_org_on_write; +}; +typedef struct cgi_state_t cgi_state_t; + +struct cgi_peer_xtn_t +{ + cgi_state_t* state; +}; +typedef struct cgi_peer_xtn_t cgi_peer_xtn_t; + +static void cgi_state_on_kill (cgi_state_t* cgi_state) +{ +printf ("**** CGI_STATE_ON_KILL \n"); + if (cgi_state->peer) + { + mio_dev_pro_kill (cgi_state->peer); + cgi_state->peer = MIO_NULL; + } + + if (cgi_state->peer_htrd) + { + mio_htrd_close (cgi_state->peer_htrd); + cgi_state->peer_htrd = MIO_NULL; + } + + if (cgi_state->cli_org_on_read) + { + cgi_state->cli->sck->on_read = cgi_state->cli_org_on_read; + cgi_state->cli_org_on_read = MIO_NULL; + } + + if (cgi_state->cli_org_on_write) + { + cgi_state->cli->sck->on_write = cgi_state->cli_org_on_write; + cgi_state->cli_org_on_write = MIO_NULL; + } +} + static void cgi_peer_on_close (mio_dev_pro_t* pro, mio_dev_pro_sid_t sid) { mio_t* mio = pro->mio; + cgi_peer_xtn_t* cgi_peer = mio_dev_pro_getxtn(pro); + if (sid == MIO_DEV_PRO_MASTER) - MIO_INFO1 (mio, "PROCESS(%d) CLOSE MASTER\n", (int)pro->child_pid); + { + MIO_DEBUG1 (mio, "PROCESS(%d) CLOSE MASTER\n", (int)pro->child_pid); + cgi_peer->state->peer = MIO_NULL; /* clear this peer from the state */ + } else - MIO_INFO2 (mio, "PROCESS(%d) CLOSE SLAVE[%d]\n", (int)pro->child_pid, sid); + { + MIO_DEBUG2 (mio, "PROCESS(%d) CLOSE SLAVE[%d]\n", (int)pro->child_pid, sid); + } } static int cgi_peer_on_read (mio_dev_pro_t* pro, mio_dev_pro_sid_t sid, const void* data, mio_iolen_t dlen) { mio_t* mio = pro->mio; + cgi_peer_xtn_t* cgi_peer = mio_dev_pro_getxtn(pro); if (dlen <= -1) { - MIO_INFO1 (mio, "PROCESS(%d): READ TIMED OUT...\n", (int)pro->child_pid); + MIO_DEBUG1 (mio, "PROCESS(%d): READ TIMED OUT...\n", (int)pro->child_pid); mio_dev_pro_halt (pro); return 0; } else if (dlen <= 0) { - MIO_INFO1 (mio, "PROCESS(%d): EOF RECEIVED...\n", (int)pro->child_pid); + MIO_DEBUG1 (mio, "PROCESS(%d): EOF RECEIVED...\n", (int)pro->child_pid); /* no outstanding request. but EOF */ mio_dev_pro_halt (pro); return 0; } - MIO_INFO5 (mio, "PROCESS(%d) READ DATA ON SLAVE[%d] len=%d [%.*hs]\n", (int)pro->child_pid, (int)sid, (int)dlen, dlen, (char*)data); + MIO_DEBUG5 (mio, "PROCESS(%d) READ DATA ON SLAVE[%d] len=%d [%.*hs]\n", (int)pro->child_pid, (int)sid, (int)dlen, dlen, (char*)data); if (sid == MIO_DEV_PRO_OUT) { mio_dev_pro_read (pro, sid, 0); @@ -975,18 +967,34 @@ static int cgi_peer_on_read (mio_dev_pro_t* pro, mio_dev_pro_sid_t sid, const vo static int cgi_peer_on_write (mio_dev_pro_t* pro, mio_iolen_t wrlen, void* wrctx) { mio_t* mio = pro->mio; + cgi_peer_xtn_t* cgi_peer = mio_dev_pro_getxtn(pro); + cgi_state_t* cgi_state = cgi_peer->state; mio_ntime_t tmout; + if (wrlen <= -1) { - MIO_INFO1 (mio, "PROCESS(%d): WRITE TIMED OUT...\n", (int)pro->child_pid); + MIO_DEBUG1 (mio, "PROCESS(%d): WRITE TIMED OUT...\n", (int)pro->child_pid); mio_dev_pro_halt (pro); return 0; } MIO_DEBUG2 (mio, "PROCESS(%d) wrote data of %d bytes\n", (int)pro->child_pid, (int)wrlen); + +#if 0 /*mio_dev_pro_read (pro, MIO_DEV_PRO_OUT, 1);*/ MIO_INIT_NTIME (&tmout, 5, 0); mio_dev_pro_timedread (pro, MIO_DEV_PRO_OUT, 1, &tmout); +#endif + + +#if 0 + if (mio_dev_pro_write(pro, cgi_peer->cli->req, length, MIO_NULL) <= -1) + { + /* halt both peer and cli */ + } +#endif + + cgi_state->num_pending_writes_to_peer--; return 0; } @@ -994,84 +1002,211 @@ static int cgi_peer_on_write (mio_dev_pro_t* pro, mio_iolen_t wrlen, void* wrctx static int cgi_client_on_read (mio_dev_sck_t* sck, const void* buf, mio_iolen_t len, const mio_skad_t* srcaddr) { mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(sck); + cgi_state_t* cgi_state = RSRCL_FIRST_RSRC(&cli->rsrc); + printf ("** HTTS - cgi client read %p %d -> htts:%p\n", sck, (int)len, cli->htts); - if (len <= 0) + if (len <= -1) { /* read error */ - mio_dev_sck_halt (sck); + goto oops; } - else if (mio_htrd_feed(cli->htrd, buf, len) <= -1) + else if (len == 0) { - mio_dev_sck_halt (sck); + /* EOF */ + } + else + { +#if 0 + mio_oow_t rem; + if (mio_htrd_feed(cli->htrd, buf, len, &rem) <= -1) goto oops; + if (rem > 0) + { + /* TODO: there is left-over data */ + } +#endif + + cgi_state->num_pending_writes_to_peer++; + if (mio_dev_pro_write(cgi_state->peer, buf, len, MIO_NULL) <= -1) + { + cgi_state->num_pending_writes_to_peer--; + goto oops; + } + + if (cgi_state->num_pending_writes_to_peer > 1) + { + if (mio_dev_sck_read(sck, 0) <= -1) goto oops; /* disable input watching */ + } } return 0; + +oops: +/* TODO: arrange to kill the entire cgi_state??? */ + mio_dev_sck_halt (sck); + return 0; } static int cgi_client_on_write (mio_dev_sck_t* sck, mio_iolen_t wrlen, void* wrctx, const mio_skad_t* dstaddr) { + mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(sck); + cgi_state_t* cgi_state = RSRCL_FIRST_RSRC(&cli->rsrc); + + + return 0; } -static void cgi_client_on_disconnect (mio_dev_sck_t* sck) +static int get_request_content_length (mio_htre_t* req, mio_oow_t* len) { - client_on_disconnect (sck); -} + if (req->state & (MIO_HTRE_COMPLETED | MIO_HTRE_DISCARDED)) + { + /* no more content to receive */ + *len = mio_htre_getcontentlen(req); + } + else if (req->flags & MIO_HTRE_ATTR_CHUNKED) + { + /* "Transfer-Encoding: chunked" take precedence over "Content-Length: XXX". + * + * [RFC7230] + * If a message is received with both a Transfer-Encoding and a + * Content-Length header field, the Transfer-Encoding overrides the + * Content-Length. */ -typedef int (*mio_htre_concb_t) ( - mio_htre_t* re, - const mio_bch_t* ptr, - mio_oow_t len, - void* ctx -); - -int cgi_forward_client_to_peer (mio_htre_t* re, const mio_bch_t* ptr, mio_oow_t len, void* ctx) -{ - mio_dev_pro_t* pro = (mio_dev_pro_t*)ctx; - return mio_dev_pro_write(pro, ptr, len, MIO_NULL); - /* TODO: if there are too many pending write requests, stop read event on the client */ + return -1; /* unable to determine content-length in advance */ + } + else if (req->flags & MIO_HTRE_ATTR_LENGTH) + { + *len = req->attr.content_length; + } + else + { + /* no content */ + *len = 0; + } + return 0; } int mio_svc_htts_docgi (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_t* req, const mio_bch_t* docroot) { - mio_dev_pro_t* pro = MIO_NULL; + mio_t* mio = htts->mio; + mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(csck); + cgi_state_t* cgi_state = MIO_NULL; + cgi_peer_xtn_t* cgi_peer; + mio_oow_t req_content_length; mio_dev_pro_make_t mi; + if (get_request_content_length(req, &req_content_length) <= -1) + { + /* chunked - the length is not know in advance */ + /* don't start the process yet. */ + + /* option 1. send 411 Length Required */ + /* option 2. support chunked message box in the request */ + /* option 3[non standard]. set CONTENT_LENGTH to -1 and indicate the end of INPUT by sending EOF */ + req_content_length = MIO_TYPE_MAX(mio_oow_t); /* TODO: change type or add another variable ? */ + } + MIO_MEMSET (&mi, 0, MIO_SIZEOF(mi)); - mi.flags = MIO_DEV_PRO_READOUT /*| MIO_DEV_PRO_READERR*/ | MIO_DEV_PRO_WRITEIN /*| MIO_DEV_PRO_FORGET_CHILD*/; + mi.flags = MIO_DEV_PRO_READOUT | MIO_DEV_PRO_ERRTONUL | MIO_DEV_PRO_WRITEIN /*| MIO_DEV_PRO_FORGET_CHILD*/; mi.cmd = mio_htre_getqpath(req); /* TODO: combine it with docroot */ mi.on_read = cgi_peer_on_read; mi.on_write = cgi_peer_on_write; mi.on_close = cgi_peer_on_close; -/* TODO: create cgi environment variables... */ - pro = mio_dev_pro_make(htts->mio, 0, &mi); - if (MIO_UNLIKELY(!pro)) goto oops; + cgi_state = mio_callocmem(mio, MIO_SIZEOF(*cgi_state)); + if (MIO_UNLIKELY(!cgi_state)) goto oops; -/* THE process device must be ready for I/O */ - if (mio_htre_getcontentlen(req) > 0) + cgi_state->htts = htts; /*TODO: delete this field after rsrd renewal? */ + cgi_state->on_kill = cgi_state_on_kill; + + cgi_state->cli = cli; + /*cgi_state->num_pending_writes_to_client = 0; + cgi_state->num_pending_writes_to_peer = 0;*/ + cgi_state->req = req; + cgi_state->req_content_length = req_content_length; + + cgi_state->cli_org_on_read = csck->on_read; + cgi_state->cli_org_on_write = csck->on_write; + csck->on_read = cgi_client_on_read; + csck->on_write = cgi_client_on_write; + + RSRCL_APPEND_RSRC (&cli->rsrc, cgi_state); /* attach it to the client information */ + +/* TODO: create cgi environment variables... */ +/* TODO: + * never put Expect: 100-continue to environment variable + */ + cgi_state->peer = mio_dev_pro_make(mio, MIO_SIZEOF(*cgi_peer), &mi); + if (MIO_UNLIKELY(!cgi_state->peer)) goto oops; + cgi_peer = mio_dev_pro_getxtn(cgi_state->peer); + cgi_peer->state = cgi_state; + + cgi_state->peer_htrd = mio_htrd_open(mio, MIO_SIZEOF(*cgi_peer)); + if (MIO_UNLIKELY(!cgi_state->peer_htrd)) goto oops; + cgi_peer = mio_htrd_getxtn(cgi_state->peer_htrd); + cgi_peer->state = cgi_state; +/* TODO: any other handling before this? */ +/* I CAN't SIMPLY WRITE LIKE THIS, the cgi must require this. + * send contents depending how cgi want Expect: continue handling to be handled */ + + if (req->flags & MIO_HTRE_ATTR_EXPECT100) { - if (mio_dev_pro_write(pro, mio_htre_getcontentptr(req), mio_htre_getcontentlen(req), MIO_NULL) <= -1) goto oops; + /* TODO: Expect: 100-continue? who should handle this? cgi? or the http server? */ + if (mio_comp_http_version_numbers(&req->version, 1, 1) >= 0 && req_content_length > 0) + { + /* + * Don't send 100 Continue if http verions is lower than 1.1 + * [RFC7231] + * A server that receives a 100-continue expectation in an HTTP/1.0 + * request MUST ignore that expectation. + * + * Don't send 100 Continue if expected content lenth is 0. + * [RFC7231] + * A server MAY omit sending a 100 (Continue) response if it has + * already received some or all of the message body for the + * corresponding request, or if the framing indicates that there is + * no message body. + */ + cgi_state->num_pending_writes_to_client++; + if (mio_dev_sck_write(csck, "HTTP/1.1 100 Continue\r\n", 23, MIO_NULL, MIO_NULL) <= -1) + { + cgi_state->num_pending_writes_to_client--; + goto oops; + } + } + } + else if (req->flags & MIO_HTRE_ATTR_EXPECT) + { + mio_htre_discardcontent (req); + req->flags &= ~MIO_HTRE_ATTR_KEEPALIVE; /* to cause sendstatus() to close */ + if (mio_svc_htts_sendstatus(htts, csck, req, 417, MIO_NULL) <= -1) goto oops; } - mio_htre_setconcb (req, cgi_forward_client_to_peer, pro); + if (req_content_length > 0) + { + if (mio_htre_getcontentlen(req) > 0) + { + cgi_state->num_pending_writes_to_peer++; + if (mio_dev_pro_write(cgi_state->peer, mio_htre_getcontentptr(req), mio_htre_getcontentlen(req), MIO_NULL) <= -1) + { + cgi_state->num_pending_writes_to_peer--; + goto oops; + } + } + } -#if 0 - csck->on_read = cgi_client_on_read; - cssk->on_write = cgi_client_on_write; - csck->on_disconnect = cgi_client_on_disconnect; -#endif - -#if 0 - rsrc = mio_svc_htts_rsrc_make(htts, csck, rsrc_cgi_on_read, rsrc_cgi_on_write, rsrc_cgi_on_kill, MIO_SIZEOF(*rsrc_cgi)); - if (MIO_UNLIKELY(!rsrc)) goto oops; -#endif + if (req->state & (MIO_HTRE_COMPLETED | MIO_HTRE_DISCARDED)) + { + /* disable input watching from the client */ + if (mio_dev_sck_read(csck, 0) <= -1) goto oops; + } return 0; - oops: - if (pro) mio_dev_pro_kill (pro); + MIO_DEBUG2 (mio, "HTTS(%p) - FAILURE in docgi - socket(%p)\n", htts, csck); + cgi_state->on_kill (cgi_state); /* TODO: call rsrc_free... */ + mio_dev_sck_halt (csck); return -1; } @@ -1213,13 +1348,14 @@ oops: return -1; } -int mio_svc_htts_sendstatus (mio_svc_htts_t* htts, mio_dev_sck_t* csck, int status_code, mio_http_method_t method, const mio_http_version_t* version, int keepalive, void* extra) +int mio_svc_htts_sendstatus (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_t* req, int status_code, void* extra) { /* TODO: change this to use send status */ mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(csck); mio_bch_t text[1024]; /* TODO: make this buffer dynamic or scalable */ mio_bch_t dtbuf[64]; mio_oow_t x; + mio_http_version_t* version = mio_htre_getversion(req); const mio_bch_t* extrapre = ""; const mio_bch_t* extrapst = ""; @@ -1279,7 +1415,7 @@ int mio_svc_htts_sendstatus (mio_svc_htts_t* htts, mio_dev_sck_t* csck, int stat version->major, version->minor, status_code, mio_http_status_to_bcstr(status_code), htts->server_name, dtbuf, /* DATE */ - (keepalive? "keep-alive": "close"), /* connection */ + ((req->flags & MIO_HTRE_ATTR_KEEPALIVE)? "keep-alive": "close"), /* connection */ mio_count_bcstr(text), /* content length */ extrapre, extraval, extraval, text ); @@ -1292,7 +1428,8 @@ int mio_svc_htts_sendstatus (mio_svc_htts_t* htts, mio_dev_sck_t* csck, int stat mio_dev_sck_halt (csck); return -1; } - else if (!keepalive) + + if (!(req->flags & MIO_HTRE_ATTR_KEEPALIVE)) { /* arrange to close the writing end */ if (mio_dev_sck_write(csck, MIO_NULL, 0, MIO_NULL, MIO_NULL) <= -1) diff --git a/mio/lib/mio-htrd.h b/mio/lib/mio-htrd.h index da185ec..37b545e 100644 --- a/mio/lib/mio-htrd.h +++ b/mio/lib/mio-htrd.h @@ -179,8 +179,9 @@ MIO_EXPORT void mio_htrd_setrecbs ( */ MIO_EXPORT int mio_htrd_feed ( mio_htrd_t* htrd, /**< htrd */ - const mio_bch_t* req, /**< request octets */ - mio_oow_t len /**< number of octets */ + const mio_bch_t* req, /**< request octets */ + mio_oow_t len, /**< number of octets */ + mio_oow_t* rem ); /** diff --git a/mio/lib/mio-htre.h b/mio/lib/mio-htre.h index 39714b6..c1e5f48 100644 --- a/mio/lib/mio-htre.h +++ b/mio/lib/mio-htre.h @@ -37,7 +37,7 @@ */ /* header and contents of request/response */ -typedef struct mio_htre_t mio_htre_t; +/*typedef struct mio_htre_t mio_htre_t; <--- defined in mio-http.h TODO: remove recursive definition */ typedef struct mio_htre_hdrval_t mio_htre_hdrval_t; enum mio_htre_state_t diff --git a/mio/lib/mio-http.h b/mio/lib/mio-http.h index 074aea3..317e04e 100644 --- a/mio/lib/mio-http.h +++ b/mio/lib/mio-http.h @@ -156,6 +156,7 @@ typedef enum mio_perenc_http_opt_t mio_perenc_bcstr_opt_t; /* -------------------------------------------------------------- */ +typedef struct mio_htre_t mio_htre_t; typedef struct mio_svc_htts_t mio_svc_htts_t; typedef struct mio_svc_httc_t mio_svc_httc_t; @@ -164,6 +165,13 @@ typedef struct mio_svc_httc_t mio_svc_httc_t; typedef struct mio_svc_htts_rsrc_t mio_svc_htts_rsrc_t; typedef mio_uint64_t mio_foff_t ; /* TODO: define this via the main configure.ac ... */ + +typedef int (*mio_svc_htts_rsrc_on_read_t) ( + mio_svc_htts_rsrc_t* rsrc, + mio_dev_sck_t* sck +); + + typedef int (*mio_svc_htts_rsrc_on_write_t) ( mio_svc_htts_rsrc_t* rsrc, mio_dev_sck_t* sck @@ -185,12 +193,14 @@ struct mio_svc_htts_rsrc_t mio_svc_htts_rsrc_t* rsrc_prev; mio_svc_htts_rsrc_t* rsrc_next; + mio_svc_htts_rsrc_on_kill_t on_kill; + mio_svc_htts_rsrc_on_write_t on_write; + int flags; mio_bch_t* content_type; mio_foff_t content_length; - mio_svc_htts_rsrc_on_write_t on_write; - mio_svc_htts_rsrc_on_kill_t on_kill; + }; /* -------------------------------------------------------------- */ @@ -307,6 +317,14 @@ MIO_EXPORT int mio_svc_htts_setservernamewithbcstr ( ); +MIO_EXPORT int mio_svc_htts_docgi ( + mio_svc_htts_t* htts, + mio_dev_sck_t* csck, + mio_htre_t* req, + const mio_bch_t* docroot +); + + MIO_EXPORT int mio_svc_htts_sendfile ( mio_svc_htts_t* htts, mio_dev_sck_t* csck, @@ -320,10 +338,8 @@ MIO_EXPORT int mio_svc_htts_sendfile ( MIO_EXPORT int mio_svc_htts_sendstatus ( mio_svc_htts_t* htts, mio_dev_sck_t* csck, + mio_htre_t* req, int status_code, - mio_http_method_t method, - const mio_http_version_t* version, - int keepalive, void* extra ); diff --git a/mio/lib/mio.c b/mio/lib/mio.c index 9e4fff1..69bda96 100644 --- a/mio/lib/mio.c +++ b/mio/lib/mio.c @@ -298,6 +298,93 @@ static MIO_INLINE void unlink_wq (mio_t* mio, mio_wq_t* q) MIO_WQ_UNLINK (q); } +static void fire_cwq_handlers (mio_t* mio) +{ + /* execute callbacks for completed write operations */ + while (!MIO_CWQ_IS_EMPTY(&mio->cwq)) + { + mio_cwq_t* cwq; + mio_oow_t cwqfl_index; + mio_dev_t* dev_to_halt; + + cwq = MIO_CWQ_HEAD(&mio->cwq); + if (cwq->dev->dev_evcb->on_write(cwq->dev, cwq->olen, cwq->ctx, &cwq->dstaddr) <= -1) + { + MIO_DEBUG1 (mio, "MIO - Error returned by on_write() of device %p in cwq\n", cwq->dev); + dev_to_halt = cwq->dev; + } + else + { + dev_to_halt = MIO_NULL; + } + cwq->dev->cw_count--; + MIO_CWQ_UNLINK (cwq); + + cwqfl_index = MIO_ALIGN_POW2(cwq->dstaddr.len, MIO_CWQFL_ALIGN) / MIO_CWQFL_SIZE; + if (cwqfl_index < MIO_COUNTOF(mio->cwqfl)) + { + /* reuse the cwq object if dstaddr is 0 in size. chain it to the free list */ + cwq->q_next = mio->cwqfl[cwqfl_index]; + mio->cwqfl[cwqfl_index] = cwq; + } + else + { + /* TODO: more reuse of objects of different size? */ + mio_freemem (mio, cwq); + } + + if (dev_to_halt) mio_dev_halt (dev_to_halt); + } +} + +static void fire_cwq_handlers_for_dev (mio_t* mio, mio_dev_t* dev) +{ + mio_cwq_t* cwq, * next; + + MIO_ASSERT (mio, dev->cw_count > 0); /* Ensure to check dev->cw_count before calling this function */ + + cwq = MIO_CWQ_HEAD(&mio->cwq); + while (cwq != &mio->cwq) + { + next = MIO_CWQ_NEXT(cwq); + if (cwq->dev == dev) /* TODO: THIS LOOP TOO INEFFICIENT??? MAINTAIN PER-DEVICE LIST OF CWQ? */ + { + mio_dev_t* dev_to_halt; + mio_oow_t cwqfl_index; + + if (cwq->dev->dev_evcb->on_write(cwq->dev, cwq->olen, cwq->ctx, &cwq->dstaddr) <= -1) + { + MIO_DEBUG1 (mio, "MIO - Error returned by on_write() of device %p in cwq\n", cwq->dev); + dev_to_halt = cwq->dev; + } + else + { + dev_to_halt = MIO_NULL; + } + + cwq->dev->cw_count--; + MIO_CWQ_UNLINK (cwq); + + cwqfl_index = MIO_ALIGN_POW2(cwq->dstaddr.len, MIO_CWQFL_ALIGN) / MIO_CWQFL_SIZE; + if (cwqfl_index < MIO_COUNTOF(mio->cwqfl)) + { + /* reuse the cwq object if dstaddr is 0 in size. chain it to the free list */ + cwq->q_next = mio->cwqfl[cwqfl_index]; + mio->cwqfl[cwqfl_index] = cwq; + } + else + { + /* TODO: more reuse of objects of different size? */ + mio_freemem (mio, cwq); + } + + if (dev_to_halt) mio_dev_halt (dev_to_halt); + } + cwq = next; + } + +} + static MIO_INLINE void handle_event (mio_t* mio, mio_dev_t* dev, int events, int rdhup) { MIO_ASSERT (mio, mio == dev->mio); @@ -389,7 +476,7 @@ static MIO_INLINE void handle_event (mio_t* mio, mio_dev_t* dev, int events, int if (y <= -1) { - MIO_DEBUG1 (mio, "Error returned by on_write() of device %p\n", dev); + MIO_DEBUG1 (mio, "MIO - Error returned by on_write() of device %p\n", dev); mio_dev_halt (dev); dev = MIO_NULL; break; @@ -474,6 +561,19 @@ static MIO_INLINE void handle_event (mio_t* mio, mio_dev_t* dev, int events, int } else /*if (x >= 1) */ { + /* call on_write() callbacks enqueued fro the device before calling on_read(). + * if on_write() callback is delayed, there can be out-of-order execution + * between on_read() and on_write() callbacks. for instance, if a write request + * is started from within on_read() callback, and the input data is available + * in the next iteration of this loop, the on_read() callback is triggered + * before the on_write() callbacks scheduled before that on_read() callback. */ + if (dev->cw_count > 0) + { + fire_cwq_handlers_for_dev (mio, dev); + /* it will still invoke the on_read() callbak below even if + * the device gets halted inside fire_cwq_handlers_for_dev() */ + } + if (len <= 0 && (dev->dev_cap & MIO_DEV_CAP_STREAM)) { /* EOF received. for a stream device, a zero-length @@ -565,45 +665,15 @@ int mio_exec (mio_t* mio) int ret = 0; /* execute callbacks for completed write operations */ - while (!MIO_CWQ_IS_EMPTY(&mio->cwq)) - { - mio_cwq_t* cwq; - mio_oow_t cwqfl_index; - mio_dev_t* dev_to_halt; - - cwq = MIO_CWQ_HEAD(&mio->cwq); - if (cwq->dev->dev_evcb->on_write(cwq->dev, cwq->olen, cwq->ctx, &cwq->dstaddr) <= -1) - { - MIO_DEBUG1 (mio, "Error returned by on_write() of device %p in cwq\n", cwq->dev); - dev_to_halt = cwq->dev; - } - else - { - dev_to_halt = MIO_NULL; - } - cwq->dev->cw_count--; - MIO_CWQ_UNLINK (cwq); - - cwqfl_index = MIO_ALIGN_POW2(cwq->dstaddr.len, MIO_CWQFL_ALIGN) / MIO_CWQFL_SIZE; - if (cwqfl_index < MIO_COUNTOF(mio->cwqfl)) - { - /* reuse the cwq object if dstaddr is 0 in size. chain it to the free list */ - cwq->q_next = mio->cwqfl[cwqfl_index]; - mio->cwqfl[cwqfl_index] = cwq; - } - else - { - /* TODO: more reuse of objects of different size? */ - mio_freemem (mio, cwq); - } - - if (dev_to_halt) mio_dev_halt (dev_to_halt); - } + fire_cwq_handlers (mio); /* execute the scheduled jobs before checking devices with the * multiplexer. the scheduled jobs can safely destroy the devices */ mio_firetmrjobs (mio, MIO_NULL, MIO_NULL); + /* execute callbacks for completed write operations again in case there were some jobs initiaated in the timer jobs */ + //fire_cwq_handlers (mio); + if (!MIO_DEVL_IS_EMPTY(&mio->actdev)) { /* wait on the multiplexer only if there is at least 1 active device */ @@ -618,7 +688,7 @@ int mio_exec (mio_t* mio) if (mio_sys_waitmux(mio, &tmout, handle_event) <= -1) { - MIO_DEBUG0 (mio, "WARNING - Failed to wait on mutiplexer\n"); + MIO_DEBUG0 (mio, "MIO - WARNING - Failed to wait on mutiplexer\n"); ret = -1; } } @@ -627,7 +697,7 @@ int mio_exec (mio_t* mio) while (!MIO_DEVL_IS_EMPTY(&mio->hltdev)) { mio_dev_t* dev = MIO_DEVL_FIRST_DEV(&mio->hltdev); - MIO_DEBUG1 (mio, "Killing HALTED device %p\n", dev); + MIO_DEBUG1 (mio, "MIO - Killing HALTED device %p\n", dev); mio_dev_kill (dev); } @@ -846,6 +916,7 @@ void mio_dev_kill (mio_dev_t* dev) next = MIO_CWQ_NEXT(cwq); if (cwq->dev == dev) { + cwq->dev->cw_count--; MIO_CWQ_UNLINK (cwq); mio_freemem (mio, cwq); } @@ -906,7 +977,7 @@ void mio_dev_halt (mio_dev_t* dev) if (dev->dev_cap & MIO_DEV_CAP_ACTIVE) { - MIO_DEBUG1 (mio, "HALTING DEVICE %p\n", dev); + MIO_DEBUG1 (mio, "MIO - HALTING DEVICE %p\n", dev); /* delink the device object from the active device list */ MIO_DEVL_UNLINK_DEV (dev); diff --git a/mio/lib/pro.c b/mio/lib/pro.c index df64152..c0adc31 100644 --- a/mio/lib/pro.c +++ b/mio/lib/pro.c @@ -198,9 +198,9 @@ static pid_t standard_fork_and_exec (mio_t* mio, int pfds[], int flags, param_t* (flags & MIO_DEV_PRO_ERRTONUL)) { #if defined(O_LARGEFILE) - devnull = open ("/dev/null", O_RDWR | O_LARGEFILE, 0); + devnull = open("/dev/null", O_RDWR | O_LARGEFILE, 0); #else - devnull = open ("/dev/null", O_RDWR, 0); + devnull = open("/dev/null", O_RDWR, 0); #endif if (devnull == MIO_SYSHND_INVALID) goto slave_oops; } @@ -224,7 +224,7 @@ static int dev_pro_make_master (mio_dev_t* dev, void* ctx) mio_t* mio = dev->mio; mio_dev_pro_t* rdev = (mio_dev_pro_t*)dev; mio_dev_pro_make_t* info = (mio_dev_pro_make_t*)ctx; - mio_syshnd_t pfds[6]; + mio_syshnd_t pfds[6] = { MIO_SYSHND_INVALID, MIO_SYSHND_INVALID, MIO_SYSHND_INVALID, MIO_SYSHND_INVALID, MIO_SYSHND_INVALID, MIO_SYSHND_INVALID }; int i, minidx = -1, maxidx = -1; param_t param; pid_t pid; @@ -459,7 +459,7 @@ static int dev_pro_kill_master (mio_dev_t* dev, int force) int killed = 0; await_child: - wpid = waitpid (rdev->child_pid, &status, WNOHANG); + wpid = waitpid(rdev->child_pid, &status, WNOHANG); if (wpid == 0) { if (force && !killed)