diff --git a/mio/lib/http-svr.c b/mio/lib/http-svr.c index 0f3997f..e048ce2 100644 --- a/mio/lib/http-svr.c +++ b/mio/lib/http-svr.c @@ -10,6 +10,7 @@ #include #include +#define CGI_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH typedef struct mio_svc_htts_cli_t mio_svc_htts_cli_t; struct mio_svc_htts_cli_t @@ -120,7 +121,7 @@ static int test_func_handler (int rfd, int wfd) return -1; } -static int process_request (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_t* req, int peek) +static int process_request (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_t* req) { //server_xtn_t* server_xtn = GET_SERVER_XTN(htts, client->server); //mio_htts_task_t* task; @@ -133,18 +134,13 @@ static int process_request (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_ * any more. once it's decoded in the peek mode, * the decoded query path is made available in the * non-peek mode as well */ - if (peek) - { - mio_htre_perdecqpath(req); - /* TODO: proper request logging */ + mio_htre_perdecqpath(req); + /* TODO: proper request logging */ + + MIO_DEBUG2 (htts->mio, "[REQ] %s %s\n", mio_htre_getqmethodname(req), mio_htre_getqpath(req)); + - MIO_DEBUG2 (htts->mio, "[PEEK] %s %s\n", mio_htre_getqmethodname(req), mio_htre_getqpath(req)); - } -else -{ - MIO_DEBUG2 (htts->mio, "[NOPEEK] %s %s\n", mio_htre_getqmethodname(req), mio_htre_getqpath(req)); -} #if 0 mio_printf (MIO_T("================================\n")); @@ -168,93 +164,71 @@ if (mio_htre_getcontentlen(req) > 0) #endif mth = mio_htre_getqmethodtype(req); - if (peek) - { - /* 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. */ + /* 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 0 - /* TODO support X-HTTP-Method-Override */ - if (data.method == MIO_HTTP_POST) + /* TODO support X-HTTP-Method-Override */ + if (data.method == MIO_HTTP_POST) + { + tmp = mio_htre_getheaderval(req, MIO_MT("X-HTTP-Method-Override")); + if (tmp) { - tmp = mio_htre_getheaderval(req, MIO_MT("X-HTTP-Method-Override")); - if (tmp) - { - /*while (tmp->next) tmp = tmp->next;*/ /* get the last value */ - data.method = mio_mbstohttpmethod (tmp->ptr); - } + /*while (tmp->next) tmp = tmp->next;*/ /* get the last value */ + data.method = mio_mbstohttpmethod (tmp->ptr); } + } #endif - if (mth == MIO_HTTP_CONNECT) +#if 0 + if (mth == MIO_HTTP_CONNECT) + { + /* CONNECT method must not have content set. + * however, arrange to discard it if so. + * + * NOTE: CONNECT is implemented to ignore many headers like + * 'Expect: 100-continue' and 'Connection: keep-alive'. */ + mio_htre_discardcontent (req); + } + else + { +/* this part can be check in actual mio_svc_htts_doXXX() functions. + * some doXXX handlers may not require length for POST. + * it may be able to simply accept till EOF? or treat as if CONTENT_LENGTH is 0*/ + if (mth == MIO_HTTP_POST && !(req->flags & (MIO_HTRE_ATTR_LENGTH | MIO_HTRE_ATTR_CHUNKED))) { - /* CONNECT method must not have content set. - * however, arrange to discard it if so. - * - * NOTE: CONNECT is implemented to ignore many headers like - * 'Expect: 100-continue' and 'Connection: keep-alive'. */ - mio_htre_discardcontent (req); + /* POST without Content-Length nor not chunked */ + mio_htre_discardcontent (req); + /* 411 Length Required - can't keep alive. Force disconnect */ + 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 + else + { - if (mth == MIO_HTTP_POST && !(req->flags & (MIO_HTRE_ATTR_LENGTH | MIO_HTRE_ATTR_CHUNKED))) +#endif + const mio_bch_t* qpath = mio_htre_getqpath(req); + if (mio_svc_htts_docgi(htts, csck, req, "") <= -1) { - /* POST without Content-Length nor not chunked */ - mio_htre_discardcontent (req); - /* 411 Length Required - can't keep alive. Force disconnect */ - 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 */ + mio_dev_sck_halt (csck); } - 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) - { - if (mio_svc_htts_sendcgi(htts, csck, test_func_handler, req) <= -1) - { - mio_htre_discardcontent (req); - mio_dev_sck_halt (csck); - } - } - else */if (mio_svc_htts_sendfile(htts, csck, qpath, 200, mth, mio_htre_getversion(req), (req->flags & MIO_HTRE_ATTR_KEEPALIVE)) <= -1) + if (mio_comp_bcstr(qpath, "/testfunc", 0) == 0) + { + if (mio_svc_htts_sendcgi(htts, csck, test_func_handler, req) <= -1) { mio_htre_discardcontent (req); mio_dev_sck_halt (csck); } -#endif } - } - } - else - { - /* contents are all received */ - if (mth == MIO_HTTP_CONNECT) - { - MIO_DEBUG1 (htts->mio, "Switching HTRD to DUMMY for [%hs]\n", mio_htre_getqpath(req)); + else */if (mio_svc_htts_sendfile(htts, csck, qpath, 200, mth, mio_htre_getversion(req), (req->flags & MIO_HTRE_ATTR_KEEPALIVE)) <= -1) + { + mio_htre_discardcontent (req); + mio_dev_sck_halt (csck); + } - /* 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 */ - /* TODO: arrange to forward all raw bytes */ - } - else if (req->flags & MIO_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 */ } } @@ -266,6 +240,7 @@ if (mio_htre_getcontentlen(req) > 0) //// if (MIO_UNLIKELY(!task)) goto oops; } } +#endif return 0; @@ -278,20 +253,15 @@ static int client_htrd_peek_request (mio_htrd_t* htrd, mio_htre_t* req) { htrd_xtn_t* htrdxtn = (htrd_xtn_t*)mio_htrd_getxtn(htrd); mio_svc_htts_cli_t* sckxtn = (mio_svc_htts_cli_t*)mio_dev_sck_getxtn(htrdxtn->sck); - return process_request(sckxtn->htts, htrdxtn->sck, req, 1); + return process_request(sckxtn->htts, htrdxtn->sck, req); } -static int client_htrd_poke_request (mio_htrd_t* htrd, mio_htre_t* req) -{ - htrd_xtn_t* htrdxtn = (htrd_xtn_t*)mio_htrd_getxtn(htrd); - mio_svc_htts_cli_t* sckxtn = (mio_svc_htts_cli_t*)mio_dev_sck_getxtn(htrdxtn->sck); - return process_request(sckxtn->htts, htrdxtn->sck, req, 0); -} static mio_htrd_recbs_t client_htrd_recbs = { client_htrd_peek_request, - client_htrd_poke_request + MIO_NULL, + MIO_NULL }; static int init_client (mio_svc_htts_cli_t* cli, mio_dev_sck_t* sck) @@ -728,10 +698,11 @@ struct cgi_state_t mio_htrd_t* peer_htrd; mio_svc_htts_cli_t* cli; mio_http_version_t req_version; /* client request */ - mio_oow_t req_content_length; /* client request content length */ - cgi_state_res_mode_t res_mode_to_cli; + unsigned int req_content_length_unlimited: 1; unsigned int peer_eof: 1; + mio_oow_t req_content_length; /* client request content length */ + cgi_state_res_mode_t res_mode_to_cli; mio_dev_sck_on_read_t cli_org_on_read; mio_dev_sck_on_write_t cli_org_on_write; @@ -759,6 +730,71 @@ static void cgi_state_halt (cgi_state_t* cgi_state) } +static int cgi_state_write_to_client (cgi_state_t* cgi_state, const void* data, mio_iolen_t dlen) +{ + cgi_state->num_pending_writes_to_client++; + if (mio_dev_sck_write(cgi_state->cli->sck, data, dlen, MIO_NULL, MIO_NULL) <= -1) + { + cgi_state->num_pending_writes_to_client--; + return -1; + } + + if (cgi_state->num_pending_writes_to_client > CGI_STATE_PENDING_IO_THRESHOLD) + { + if (mio_dev_pro_read(cgi_state->peer, MIO_DEV_PRO_OUT, 0) <= -1) return -1; + } + return 0; +} + +static int cgi_state_writev_to_client (cgi_state_t* cgi_state, mio_iovec_t* iov, mio_iolen_t iovcnt) +{ + cgi_state->num_pending_writes_to_client++; + if (mio_dev_sck_writev(cgi_state->cli->sck, iov, iovcnt, MIO_NULL, MIO_NULL) <= -1) + { + cgi_state->num_pending_writes_to_client--; + return -1; + } + + if (cgi_state->num_pending_writes_to_client > CGI_STATE_PENDING_IO_THRESHOLD) + { + if (mio_dev_pro_read(cgi_state->peer, MIO_DEV_PRO_OUT, 0) <= -1) return -1; + } + return 0; +} + +static int cgi_state_write_to_peer (cgi_state_t* cgi_state, const void* data, mio_iolen_t dlen) +{ + cgi_state->num_pending_writes_to_peer++; + if (mio_dev_pro_write(cgi_state->peer, data, dlen, MIO_NULL) <= -1) + { + cgi_state->num_pending_writes_to_peer--; + return -1; + } + +/* TODO: check if it's already finished or something.. */ + if (cgi_state->num_pending_writes_to_peer > CGI_STATE_PENDING_IO_THRESHOLD) + { + if (mio_dev_sck_read(cgi_state->cli->sck, 0) <= -1) return -1; + } + return 0; +} + +static int cgi_state_send_final_status_to_client (cgi_state_t* cgi_state, int status_code) +{ + mio_svc_htts_cli_t* cli = cgi_state->cli; + mio_bch_t dtbuf[64]; + + mio_svc_htts_fmtgmtime (cli->htts, MIO_NULL, dtbuf, MIO_COUNTOF(dtbuf)); + + if (mio_becs_fmt(cli->sbuf, "HTTP/%d.%d %d %hs\r\nServer: %hs\r\nDate: %s\r\nConnection: close\r\nContent-Length: 0\r\n\r\n", + cgi_state->req_version.major, cgi_state->req_version.minor, + status_code, mio_http_status_to_bcstr(status_code), + cli->htts->server_name, dtbuf) == (mio_oow_t)-1) return -1; + + return cgi_state_write_to_client(cgi_state, MIO_BECS_PTR(cli->sbuf), MIO_BECS_LEN(cli->sbuf)); +} + + static void cgi_state_on_kill (cgi_state_t* cgi_state) { printf ("**** CGI_STATE_ON_KILL \n"); @@ -792,7 +828,7 @@ printf ("**** CGI_STATE_ON_KILL \n"); cgi_state->cli_org_on_write = MIO_NULL; } - cgi_state->cli->htrd->recbs.push_content = MIO_NULL; + mio_htrd_setrecbs (cgi_state->cli->htrd, &client_htrd_recbs); /* restore the callbacks */ if (MIO_BECS_LEN(&cgi_state->cli->rbuf) > 0) { @@ -806,21 +842,6 @@ printf ("**** CGI_STATE_ON_KILL \n"); } } -static int cgi_send_simple_status_to_client (cgi_state_t* cgi_state, int status_code) -{ - mio_svc_htts_cli_t* cli = cgi_state->cli; - mio_bch_t dtbuf[64]; - - mio_svc_htts_fmtgmtime (cli->htts, MIO_NULL, dtbuf, MIO_COUNTOF(dtbuf)); - - if (mio_becs_fmt(cli->sbuf, "HTTP/%d.%d %d %hs\r\nServer: %hs\r\nDate: %s\r\nConnection: close\r\nContent-Length: 0\r\n\r\n", - cgi_state->req_version.major, cgi_state->req_version.minor, - status_code, mio_http_status_to_bcstr(status_code), - cli->htts->server_name, dtbuf) == (mio_oow_t)-1) return -1; - - return mio_dev_sck_write(cli->sck, MIO_BECS_PTR(cli->sbuf), MIO_BECS_LEN(cli->sbuf), MIO_NULL, MIO_NULL); -} - static void cgi_peer_on_close (mio_dev_pro_t* pro, mio_dev_pro_sid_t sid) { mio_t* mio = pro->mio; @@ -829,6 +850,7 @@ static void cgi_peer_on_close (mio_dev_pro_t* pro, mio_dev_pro_sid_t sid) if (!cgi_state) return; /* cgi state already gone */ +printf (">>>>>>>>> PEER ON CLOSE sid=%d\n", (int)sid); printf (">> cgi_state %p\n", cgi_state); printf (">> cgi_state->peer %p\n", cgi_state->peer); printf (">> cgi_state->cli %p\n", cgi_state->cli); @@ -844,9 +866,15 @@ printf (">> cgi_state->cli->htts %p\n", cgi_state->cli->htts); MIO_ASSERT (mio, cgi_state->peer == pro); MIO_DEBUG4 (mio, "HTTS(%p) - peer %p(pid=%d) closing slave[%d]\n", cgi_state->cli->htts, pro, (int)pro->child_pid, sid); - if (sid == MIO_DEV_PRO_OUT) + if (sid == MIO_DEV_PRO_OUT && !cgi_state->peer_eof) { +/* check if it's sending... */ cgi_state->peer_eof = 1; + if (cgi_state->res_mode_to_cli == CGI_STATE_RES_MODE_CHUNKED && + cgi_state_write_to_client(cgi_state, "0\r\n\r\n", 5) <= -1) + { + cgi_state_halt (cgi_state); + } } } } @@ -859,6 +887,7 @@ static int cgi_peer_on_read (mio_dev_pro_t* pro, mio_dev_pro_sid_t sid, const vo MIO_ASSERT (mio, sid == MIO_DEV_PRO_OUT); /* since MIO_DEV_PRO_ERRTONUL is used, there should be no input from MIO_DEV_PRO_ERR */ +printf (">>>>>>>>> PEER ON READ %d\n", dlen); if (dlen <= -1) { MIO_DEBUG3 (mio, "HTTPS(%p) - read error from peer %p(pid=%u)\n", cgi_state->cli->htts, pro, (unsigned int)pro->child_pid); @@ -872,9 +901,8 @@ static int cgi_peer_on_read (mio_dev_pro_t* pro, mio_dev_pro_sid_t sid, const vo MIO_DEBUG3 (mio, "HTTPS(%p) - EOF from peer %p(pid=%u)\n", cgi_state->cli->htts, pro, (unsigned int)pro->child_pid); cgi_state->peer_eof = 1; - /* don't care about cgi_state->num_pending_writes_to_client since this is the last read */ - if (cgi_state->res_mode_to_cli == CGI_STATE_RES_MODE_CHUNKED && - mio_dev_sck_write(cgi_state->cli->sck, "0\r\n\r\n", 5, MIO_NULL, MIO_NULL) <= -1) goto oops; + if (cgi_state->res_mode_to_cli == CGI_STATE_RES_MODE_CHUNKED && + cgi_state_write_to_client(cgi_state, "0\r\n\r\n", 5) <= -1) goto oops; } else { @@ -883,7 +911,7 @@ printf ( "QQQQQQQQQQQQQQQQQQ>>>>>>>>>>>>>>>>>> feeding to peer htrd.... dlen => if (mio_htrd_feed(cgi_state->peer_htrd, data, dlen, &rem) <= -1) { printf ( "FEEDING FAILURE TO PEER HTDDRD.. dlen => %d\n", (int)dlen); - cgi_send_simple_status_to_client (cgi_state, 500); /* don't care about error */ + cgi_state_send_final_status_to_client (cgi_state, 500); /* don't care about error */ goto oops; } if (rem > 0) @@ -980,7 +1008,17 @@ printf ("CGI PEER HTRD PEEK...\n"); } if (mio_becs_cat(cli->sbuf, "\r\n") == (mio_oow_t)-1) return -1; - return mio_dev_sck_write(cli->sck, MIO_BECS_PTR(cli->sbuf), MIO_BECS_LEN(cli->sbuf), MIO_NULL, MIO_NULL); + return cgi_state_write_to_client(cgi_state, MIO_BECS_PTR(cli->sbuf), MIO_BECS_LEN(cli->sbuf)); +} + +static int cgi_peer_htrd_poke (mio_htrd_t* htrd, mio_htre_t* req) +{ + /* client request got completed */ + cgi_peer_xtn_t* cgi_peer = mio_htrd_getxtn(htrd); + cgi_state_t* cgi_state = cgi_peer->state; + + /* indicate EOF to the client */ + return cgi_state_write_to_client(cgi_state, MIO_NULL, 0); } static int cgi_peer_htrd_push_content (mio_htrd_t* htrd, mio_htre_t* req, const mio_bch_t* data, mio_oow_t dlen) @@ -1009,23 +1047,14 @@ static int cgi_peer_htrd_push_content (mio_htrd_t* htrd, mio_htre_t* req, const iov[2].iov_ptr = "\r\n"; iov[2].iov_len = 2; - cgi_state->num_pending_writes_to_client++; - if (mio_dev_sck_writev(cgi_state->cli->sck, iov, MIO_COUNTOF(iov), MIO_NULL, MIO_NULL) <= -1) - { - cgi_state->num_pending_writes_to_client--; - goto oops; - } + if (cgi_state_writev_to_client(cgi_state, iov, MIO_COUNTOF(iov)) <= -1) goto oops; break; } case CGI_STATE_RES_MODE_CLOSE: case CGI_STATE_RES_MODE_LENGTH: - cgi_state->num_pending_writes_to_client++; - if (mio_dev_sck_write(cgi_state->cli->sck, data, dlen, MIO_NULL, MIO_NULL) <= -1) - { - cgi_state->num_pending_writes_to_client--; - goto oops; - } + if (cgi_state_write_to_client(cgi_state, data, dlen) <= -1) goto oops; + break; } if (cgi_state->num_pending_writes_to_client > CGI_STATE_PENDING_IO_THRESHOLD) @@ -1042,10 +1071,22 @@ oops: static mio_htrd_recbs_t cgi_peer_htrd_recbs = { cgi_peer_htrd_peek, - MIO_NULL, + cgi_peer_htrd_poke, cgi_peer_htrd_push_content }; +static int cgi_client_htrd_poke (mio_htrd_t* htrd, mio_htre_t* req) +{ + /* client request got completed */ + htrd_xtn_t* htrdxtn = mio_htrd_getxtn(htrd); + mio_dev_sck_t* sck = htrdxtn->sck; + mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(sck); + cgi_state_t* cgi_state = RSRCL_FIRST_RSRC(&cli->rsrc); + + /* indicate EOF to the client peer */ + return cgi_state_write_to_peer(cgi_state, MIO_NULL, 0); +} + static int cgi_client_htrd_push_content (mio_htrd_t* htrd, mio_htre_t* req, const mio_bch_t* data, mio_oow_t dlen) { htrd_xtn_t* htrdxtn = mio_htrd_getxtn(htrd); @@ -1054,25 +1095,7 @@ static int cgi_client_htrd_push_content (mio_htrd_t* htrd, mio_htre_t* req, cons cgi_state_t* cgi_state = RSRCL_FIRST_RSRC(&cli->rsrc); MIO_ASSERT (sck->mio, cli->sck == sck); - - cgi_state->num_pending_writes_to_peer++; - if (mio_dev_pro_write(cgi_state->peer, data, dlen, MIO_NULL) <= -1) - { - cgi_state->num_pending_writes_to_peer--; - goto oops; - } - -#if 0 - if (cgi_state->num_pending_writes_to_peer > CGI_STATE_PENDING_IO_THRESHOLD) - { - if (mio_dev_sck_read(cli->sck, 0) <= -1) goto oops; /* disable input watching */ - } -#endif - - return 0; - -oops: - return -1; + return cgi_state_write_to_peer(cgi_state, data, dlen); } static int cgi_peer_on_write (mio_dev_pro_t* pro, mio_iolen_t wrlen, void* wrctx) @@ -1088,13 +1111,28 @@ static int cgi_peer_on_write (mio_dev_pro_t* pro, mio_iolen_t wrlen, void* wrctx MIO_DEBUG3 (mio, "HTTS(%p) - unable to write to peer %p(pid=%u)\n", cgi_state->cli->htts, pro, (int)pro->child_pid); goto oops; } - - cgi_state->num_pending_writes_to_peer--; - if (cgi_state->num_pending_writes_to_peer == CGI_STATE_PENDING_IO_THRESHOLD) + else if (wrlen == 0) { - // TODO: check if (cli has anything to read...) if so ... - if (mio_dev_sck_read(cgi_state->cli->sck, 1) <= -1) goto oops; + /* indicated EOF */ + /* do nothing here as i didn't incremented num_pending_writes_to_peer when making the write request */ + cgi_state->num_pending_writes_to_peer--; + MIO_ASSERT (mio, cgi_state->num_pending_writes_to_peer == 0); + MIO_DEBUG3 (mio, "HTTS(%p) - indicated EOF to peer %p(pid=%u)\n", cgi_state->cli->htts, pro, (int)pro->child_pid); + /* indicated EOF to the peer side. i need no more data from the client side. + * i don't need to enable input watching in the client side either */ } + else + { + MIO_ASSERT (mio, cgi_state->num_pending_writes_to_peer > 0); + + cgi_state->num_pending_writes_to_peer--; + if (cgi_state->num_pending_writes_to_peer == CGI_STATE_PENDING_IO_THRESHOLD) + { + // TODO: check if (cli has anything to read...) if so ... + if (mio_dev_sck_read(cgi_state->cli->sck, 1) <= -1) goto oops; + } + } + return 0; oops: @@ -1130,7 +1168,8 @@ printf ("** HTTS - cgi client read %p %d -> htts:%p\n", sck, (int)len, cli->h { /* EOF on the client side. arrange to close */ printf ("asking pro to close...\n"); - if (mio_dev_pro_write(cgi_state->peer, MIO_NULL, 0, MIO_NULL) <= -1) goto oops; +/* HAVE I ALREADY CLOSED IT??? Check conflict is the poke handler */ + return cgi_state_write_to_peer(cgi_state, MIO_NULL, 0); } else { @@ -1164,24 +1203,39 @@ static int cgi_client_on_write (mio_dev_sck_t* sck, mio_iolen_t wrlen, void* wrc if (wrlen <= -1) { - MIO_DEBUG3 (mio, "HTTPS(%p) - unable to write to client socket %p(%d)\n", sck->mio, sck, (int)sck->hnd); + MIO_DEBUG3 (mio, "HTTPS(%p) - unable to write to client %p(%d)\n", sck->mio, sck, (int)sck->hnd); goto oops; } -printf ("MANAGED TO WRITE TO CLIENT>... %d\n", (int)wrlen); - cgi_state->num_pending_writes_to_client--; - if (cgi_state->peer && cgi_state->num_pending_writes_to_client == CGI_STATE_PENDING_IO_THRESHOLD) + if (wrlen == 0) { - // TODO: check if there is anything to read... */ - if (mio_dev_pro_read(cgi_state->peer, MIO_DEV_PRO_OUT, 1) <= -1) goto oops; + /* if the connect is keep-alive, this part may not be called */ + cgi_state->num_pending_writes_to_client--; + MIO_ASSERT (mio, cgi_state->num_pending_writes_to_client == 0); + MIO_DEBUG3 (mio, "HTTS(%p) - indicated EOF to client %p(%d)\n", cgi_state->cli->htts, sck, (int)sck->hnd); + /* since EOF has been indicated to the client, it must not write to the client any further. + * this also means that i don't need any data from the peer side either. + * i don't need to enable input watching on the peer side */ } - -/* in case the peer died before peer read handler reads EOF */ - if (cgi_state->peer_eof && cgi_state->num_pending_writes_to_client <= 0) + else { - /*if (no_more_input is to be read from the client)*/ - cgi_state_halt (cgi_state); // must not use this... must kill the state ... - /* finished writing all */ + MIO_ASSERT (mio, cgi_state->num_pending_writes_to_client > 0); + + printf ("MANAGED TO WRITE TO CLIENT>... %d\n", (int)wrlen); + cgi_state->num_pending_writes_to_client--; + if (cgi_state->peer && cgi_state->num_pending_writes_to_client == CGI_STATE_PENDING_IO_THRESHOLD) + { + // TODO: check if there is anything to read... */ + if (mio_dev_pro_read(cgi_state->peer, MIO_DEV_PRO_OUT, 1) <= -1) goto oops; + } + + /* in case the peer died before peer read handler reads EOF */ + if (cgi_state->peer_eof && cgi_state->num_pending_writes_to_client <= 0) + { + /*if (no_more_input is to be read from the client)*/ + cgi_state_halt (cgi_state); // must not use this... must kill the state ... + /* finished writing all */ + } } return 0; @@ -1193,12 +1247,7 @@ oops: static int get_request_content_length (mio_htre_t* req, mio_oow_t* len) { - 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) + if (req->flags & MIO_HTRE_ATTR_CHUNKED) { /* "Transfer-Encoding: chunked" take precedence over "Content-Length: XXX". * @@ -1206,7 +1255,6 @@ static int get_request_content_length (mio_htre_t* req, mio_oow_t* len) * If a message is received with both a Transfer-Encoding and a * Content-Length header field, the Transfer-Encoding overrides the * Content-Length. */ - return -1; /* unable to determine content-length in advance */ } else if (req->flags & MIO_HTRE_ATTR_LENGTH) @@ -1215,7 +1263,7 @@ static int get_request_content_length (mio_htre_t* req, mio_oow_t* len) } else { - /* no content */ + /* If no Content-Length is specified in a request, it's Content-Length: 0 */ *len = 0; } return 0; @@ -1227,19 +1275,11 @@ int mio_svc_htts_docgi (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_t* r 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; + int enable_input; - 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 ? */ - } + /* ensure that you call this function before any contents is received */ + MIO_ASSERT (mio, mio_htre_getcontentlen(req) == 0); MIO_MEMSET (&mi, 0, MIO_SIZEOF(mi)); mi.flags = MIO_DEV_PRO_READOUT | MIO_DEV_PRO_ERRTONUL | MIO_DEV_PRO_WRITEIN /*| MIO_DEV_PRO_FORGET_CHILD*/; @@ -1258,7 +1298,9 @@ int mio_svc_htts_docgi (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_t* r /*cgi_state->num_pending_writes_to_client = 0; cgi_state->num_pending_writes_to_peer = 0;*/ cgi_state->req_version = *mio_htre_getversion(req); - cgi_state->req_content_length = req_content_length; + + cgi_state->req_content_length_unlimited = 0; + if (get_request_content_length(req, &cgi_state->req_content_length) <= -1) cgi_state->req_content_length_unlimited = 1; cgi_state->cli_org_on_read = csck->on_read; cgi_state->cli_org_on_write = csck->on_write; @@ -1283,14 +1325,27 @@ int mio_svc_htts_docgi (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_t* r 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 !defined(CGI_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH) + if (cgi_state->req_content_length_unlimited) + { + /* Transfer-Encoding is chunked. no content-length is known in advance. */ + + /* option 1. buffer contents. if it gets too large, send 413 Request Entity Too Large. + * option 2. send 411 Length Required immediately + * option 3. set Content-Length to -1 and use EOF to indicate the end of content [Non-Standard] */ + + if (cgi_state_send_final_status_to_client(cgi_state, 411) <= -1) goto oops; + } +#endif if (req->flags & MIO_HTRE_ATTR_EXPECT100) { /* 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) + /* CAN I LET the cgi SCRIPT handle this? */ + if (mio_comp_http_version_numbers(&req->version, 1, 1) >= 0 && + (cgi_state->req_content_length_unlimited || cgi_state->req_content_length > 0)) { /* * Don't send 100 Continue if http verions is lower than 1.1 @@ -1309,47 +1364,55 @@ int mio_svc_htts_docgi (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_t* r mio_oow_t msglen; msglen = mio_fmttobcstr(mio, msgbuf, MIO_COUNTOF(msgbuf), "HTTP/%d.%d 100 Continue\r\n\r\n", cgi_state->req_version.major, cgi_state->req_version.minor); - cgi_state->num_pending_writes_to_client++; - if (mio_dev_sck_write(csck, msgbuf, msglen, MIO_NULL, MIO_NULL) <= -1) - { - cgi_state->num_pending_writes_to_client--; - goto oops; - } + if (cgi_state_write_to_client(cgi_state, msgbuf, msglen) <= -1) 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; + /* 417 Expectation Failed */ + cgi_state_send_final_status_to_client(cgi_state, 417); + goto oops; } - if (req_content_length > 0) + enable_input = 1; +#if defined(CGI_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH) + if (cgi_state->req_content_length_unlimited) { - if (mio_htre_getcontentlen(req) > 0) - { - /* this means that it's called from the peek context */ - 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; - } - } - } + /* Transfer-Encoding is chunked. no content-length is known in advance. */ + + /* option 1. buffer contents. if it gets too large, send 413 Request Entity Too Large. + * option 2. send 411 Length Required immediately + * option 3. set Content-Length to -1 and use EOF to indicate the end of content [Non-Standard] */ - 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; +/* TODO: must set CONTENT-LENGTH to -1 before start the process */ + /* change the callbacks to subscribe to contents to be uploaded */ + cgi_state->cli->htrd->recbs.poke = cgi_client_htrd_poke; + cgi_state->cli->htrd->recbs.push_content = cgi_client_htrd_push_content; } else { - if (req_content_length > 0) +#endif + if (cgi_state->req_content_length > 0) { + + /* change the callbacks to subscribe to contents to be uploaded */ + cgi_state->cli->htrd->recbs.poke = cgi_client_htrd_poke; cgi_state->cli->htrd->recbs.push_content = cgi_client_htrd_push_content; } + else + { + /* no content to be uploaded from the client */ + /* indicate EOF to the peer and disable input wathching from the client */ + if (cgi_state_write_to_peer(cgi_state, MIO_NULL, 0) <= -1) goto oops; + enable_input = 0; + } +#if defined(CGI_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH) } +#endif + + /* TODO: store current input watching state and use it when destroying the cgi_state data */ + if (mio_dev_sck_read(csck, enable_input) <= -1) goto oops; + /* this may change later if Content-Length is included in the cgi output */ cgi_state->res_mode_to_cli = (req->flags & MIO_HTRE_ATTR_KEEPALIVE)? CGI_STATE_RES_MODE_CHUNKED: CGI_STATE_RES_MODE_CLOSE; @@ -1357,6 +1420,8 @@ int mio_svc_htts_docgi (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_t* r oops: MIO_DEBUG2 (mio, "HTTS(%p) - FAILURE in docgi - socket(%p)\n", htts, csck); + +/* TODO: can i call cgi_state_halt??? */ cgi_state->on_kill (cgi_state); /* TODO: call rsrc_free... */ mio_dev_sck_halt (csck); return -1; @@ -1534,100 +1599,6 @@ oops: return -1; } -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 = ""; - const mio_bch_t* extraval = ""; - - text[0] = '\0'; - - switch (status_code) - { -#if 0 - case 301: /* Moved Permanently */ - case 302: /* Found */ - case 303: /* See Other (since HTTP/1.1) */ - case 307: /* Temporary Redirect (since HTTP/1.1) */ - case 308: /* Permanent Redirect (Experimental RFC; RFC 7238) */ - { - mio_svc_htts_rsrc_reloc_t* reloc; - reloc = (mio_htts_rsrc_reloc_t*)extra; - extrapre = "Location: "; - extrapst = (reloc->flags & MIO_SVC_HTTS_RSRC_RELOC_APPENDSLASH)? "/\r\n": "\r\n"; - extraval = reloc->target; - break; - } -#endif - - case 304: - case 200: - case 201: - case 202: - case 203: - case 204: - case 205: - case 206: - /* nothing to do */ - break; - - - default: -#if 0 - if (method != MIO_HTTP_HEAD && - htts->opt.rcb.fmterr(htts, client, code, text, MIO_COUNTOF(text)) <= -1) return QSE_NULL; -#endif - - if (status_code == 401) - { - extrapre = "WWW-Authenticate: Basic realm=\""; - extrapst = "\"\r\n"; - extraval = (const mio_bch_t*)extra; - } - - break; - } - - mio_svc_htts_fmtgmtime(htts, MIO_NULL, dtbuf, MIO_COUNTOF(dtbuf)); - - x = mio_becs_fmt(cli->sbuf, "HTTP/%d.%d %d %s\r\nServer: %s\r\nDate: %s\r\nConnection: %s\r\nContent-Type: text/html\r\nContent-Length: %u\r\n%s%s%s\r\n%s", - version->major, version->minor, status_code, mio_http_status_to_bcstr(status_code), - htts->server_name, - dtbuf, /* DATE */ - ((req->flags & MIO_HTRE_ATTR_KEEPALIVE)? "keep-alive": "close"), /* connection */ - mio_count_bcstr(text), /* content length */ - extrapre, extraval, extraval, text - ); - if (MIO_UNLIKELY(x == (mio_oow_t)-1)) return -1; - - -/* TODO: use timedwrite? */ - if (mio_dev_sck_write(csck, MIO_BECS_PTR(cli->sbuf), MIO_BECS_LEN(cli->sbuf), MIO_NULL, MIO_NULL) <= -1) - { - mio_dev_sck_halt (csck); - return -1; - } - - 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) - { - mio_dev_sck_halt (csck); - return -1; - } - } - - return 0; -} - void mio_svc_htts_fmtgmtime (mio_svc_htts_t* htts, const mio_ntime_t* nt, mio_bch_t* buf, mio_oow_t len) { mio_ntime_t now; diff --git a/mio/lib/mio-http.h b/mio/lib/mio-http.h index c2946f7..fe77b5c 100644 --- a/mio/lib/mio-http.h +++ b/mio/lib/mio-http.h @@ -335,14 +335,6 @@ MIO_EXPORT int mio_svc_htts_sendfile ( int keepalive ); -MIO_EXPORT int mio_svc_htts_sendstatus ( - mio_svc_htts_t* htts, - mio_dev_sck_t* csck, - mio_htre_t* req, - int status_code, - void* extra -); - MIO_EXPORT void mio_svc_htts_fmtgmtime ( mio_svc_htts_t* htts, const mio_ntime_t* nt, diff --git a/mio/lib/pro.c b/mio/lib/pro.c index a5ddbd4..bbb8226 100644 --- a/mio/lib/pro.c +++ b/mio/lib/pro.c @@ -564,6 +564,13 @@ static int dev_pro_write_slave (mio_dev_t* dev, const void* data, mio_iolen_t* l mio_dev_pro_slave_t* pro = (mio_dev_pro_slave_t*)dev; ssize_t x; + if (MIO_UNLIKELY(*len <= 0)) + { + /* this is an EOF indicator */ + mio_dev_halt (dev); /* halt this slave device */ + return 1; + } + x = write(pro->pfd, data, *len); if (x <= -1) { @@ -582,6 +589,13 @@ static int dev_pro_writev_slave (mio_dev_t* dev, const mio_iovec_t* iov, mio_iol mio_dev_pro_slave_t* pro = (mio_dev_pro_slave_t*)dev; ssize_t x; + if (MIO_UNLIKELY(*iovcnt <= 0)) + { + /* this is an EOF indicator */ + mio_dev_halt (dev); /* halt this slave device */ + return 1; + } + x = writev(pro->pfd, iov, *iovcnt); if (x <= -1) { diff --git a/mio/t/d.sh b/mio/t/d.sh index 5e11886..baaf970 100755 --- a/mio/t/d.sh +++ b/mio/t/d.sh @@ -9,3 +9,4 @@ while read x do echo $x done +##echo "<>"