From cbcc33554ad6e90de2a571f5048c63e6b3b634d7 Mon Sep 17 00:00:00 2001 From: hyung-hwan Date: Sun, 19 Mar 2023 05:04:32 +0900 Subject: [PATCH] refactoring htts with common code pushed to http-svr.c --- bin/t06.c | 4 +- lib/hio-htre.h | 5 + lib/hio-http.h | 35 ++- lib/hio.c | 2 +- lib/http-cgi.c | 42 ++-- lib/http-fcgi.c | 346 ++++++---------------------- lib/http-file.c | 77 ++----- lib/http-svr.c | 597 +++++++++++++++++++++++++++++++----------------- lib/http-thr.c | 74 +----- lib/http-txt.c | 28 +-- lib/http.c | 9 +- 11 files changed, 528 insertions(+), 691 deletions(-) diff --git a/bin/t06.c b/bin/t06.c index 2accc2c..55c49bb 100644 --- a/bin/t06.c +++ b/bin/t06.c @@ -197,9 +197,9 @@ if (hio_htre_getcontentlen(req) > 0) const hio_bch_t* qpath = hio_htre_getqpath(req); int x; if (hio_comp_bcstr_limited(qpath, "/thr/", 5, 1) == 0) - x = hio_svc_htts_dothr(htts, csck, req, on_htts_thr_request, HIO_NULL, HIO_SVC_HTTS_THR_NO_100_CONTINUE, HIO_NULL); + x = hio_svc_htts_dothr(htts, csck, req, on_htts_thr_request, HIO_NULL, 0, HIO_NULL); else if (hio_comp_bcstr_limited(qpath, "/thr2/", 6, 1) == 0) - x = hio_svc_htts_dothr(htts, csck, req, on_htts_thr2_request, HIO_NULL, HIO_SVC_HTTS_THR_NO_100_CONTINUE, HIO_NULL); + x = hio_svc_htts_dothr(htts, csck, req, on_htts_thr2_request, HIO_NULL, 0, HIO_NULL); else if (hio_comp_bcstr_limited(qpath, "/txt/", 5, 1) == 0) x = hio_svc_htts_dotxt(htts, csck, req, HIO_HTTP_STATUS_OK, "text/plain", qpath, 0, HIO_NULL); else if (hio_comp_bcstr_limited(qpath, "/cgi/", 5, 1) == 0) diff --git a/lib/hio-htre.h b/lib/hio-htre.h index 769498b..704cd90 100644 --- a/lib/hio-htre.h +++ b/lib/hio-htre.h @@ -127,6 +127,11 @@ enum hio_http_status_t HIO_HTTP_STATUS_EXPECTATION_FAILED = 417, HIO_HTTP_STATUS_INTERNAL_SERVER_ERROR = 500, + HIO_HTTP_STATUS_NOT_IMPLEMENTED = 501, + HIO_HTTP_STATUS_BAD_GATEWAY = 502, + HIO_HTTP_STATUS_SERVICE_UNAVAILABLE = 503, + HIO_HTTP_STATUS_GATEWAY_TIMEOUT = 504, + HIO_HTTP_STATUS_VERSION_NOT_SUPPORTED = 505 }; typedef enum hio_http_status_t hio_http_status_t; diff --git a/lib/hio-http.h b/lib/hio-http.h index bf69de2..853e71d 100644 --- a/lib/hio-http.h +++ b/lib/hio-http.h @@ -100,13 +100,19 @@ typedef void (*hio_svc_htts_task_on_kill_t) ( unsigned int task_keep_client_alive: 1; \ unsigned int task_req_qpath_ending_with_slash: 1; \ unsigned int task_req_qpath_is_root: 1; \ + unsigned int task_req_conlen_unlimited: 1; \ unsigned int task_res_chunked: 1; \ + unsigned int task_res_started: 1; \ unsigned int task_res_ended: 1; \ + unsigned int task_res_ever_sent: 1; \ + int task_req_flags; \ hio_http_version_t task_req_version; \ hio_http_method_t task_req_method; \ hio_bch_t* task_req_qmth; \ hio_bch_t* task_req_qpath; \ - hio_http_status_t task_status_code; + hio_oow_t task_req_conlen; \ + hio_http_status_t task_status_code; \ + hio_ooi_t task_res_pending_writes; struct hio_svc_htts_task_t { @@ -191,28 +197,30 @@ typedef void (*hio_svc_htts_fun_func_t) ( /* -------------------------------------------------------------- */ +#if 0 enum hio_svc_htts_cgi_option_t { - HIO_SVC_HTTS_CGI_NO_100_CONTINUE = (1 << 0) + /* no option yet */ }; enum hio_svc_htts_fcgi_option_t { - HIO_SVC_HTTS_FCGI_NO_100_CONTINUE = (1 << 0) + /* no option yet */ }; +#endif enum hio_svc_htts_file_option_t { - HIO_SVC_HTTS_FILE_NO_100_CONTINUE = (1 << 0), - HIO_SVC_HTTS_FILE_READ_ONLY = (1 << 1) -}; - -enum hio_svc_htts_thr_option_t -{ - HIO_SVC_HTTS_THR_NO_100_CONTINUE = (1 << 0) + HIO_SVC_HTTS_FILE_READ_ONLY = (1 << 0) }; #if 0 +enum hio_svc_htts_thr_option_t +{ + /* no option yet */ +}; + + enum hio_svc_htts_txt_option_t { /* no option yet */ @@ -492,7 +500,7 @@ HIO_EXPORT void hio_svc_htts_task_kill ( hio_svc_htts_task_t* task ); -HIO_EXPORT int hio_svc_htts_task_buildfinalres ( +HIO_EXPORT int hio_svc_htts_task_sendfinalres ( hio_svc_htts_task_t* task, int status_code, const hio_bch_t* content_type, @@ -530,6 +538,11 @@ HIO_EXPORT int hio_svc_htts_task_endreshdr ( hio_svc_htts_task_t* task ); +HIO_EXPORT int hio_svc_htts_task_handleexpect100 ( + hio_svc_htts_task_t* task, + int options +); + HIO_EXPORT void hio_svc_htts_fmtgmtime ( hio_svc_htts_t* htts, const hio_ntime_t* nt, diff --git a/lib/hio.c b/lib/hio.c index c002ce1..0e1ea17 100644 --- a/lib/hio.c +++ b/lib/hio.c @@ -1110,7 +1110,7 @@ free_device: HIO_DEBUG1 (hio, "MIO - Unset ZOMBIE on device %p\n", dev); } - HIO_DEBUG1 (hio, "MIO - Freeing device %p\n", dev); + HIO_DEBUG1 (hio, "MIO - Freeed device %p\n", dev); hio_freemem (hio, dev); return 0; } diff --git a/lib/http-cgi.c b/lib/http-cgi.c index 09b8645..2cf0b3f 100644 --- a/lib/http-cgi.c +++ b/lib/http-cgi.c @@ -67,12 +67,10 @@ struct cgi_t hio_htrd_t* peer_htrd; unsigned int over: 4; /* must be large enough to accomodate CGI_OVER_ALL */ - unsigned int req_content_length_unlimited: 1; unsigned int ever_attempted_to_write_to_client: 1; unsigned int client_eof_detected: 1; unsigned int client_disconnected: 1; unsigned int client_htrd_recbs_changed: 1; - hio_oow_t req_content_length; /* client request content length */ cgi_res_mode_t res_mode_to_cli; hio_dev_sck_on_read_t client_org_on_read; @@ -105,6 +103,7 @@ static int cgi_write_to_client (cgi_t* cgi, const void* data, hio_iolen_t dlen) cgi->ever_attempted_to_write_to_client = 1; cgi->num_pending_writes_to_client++; +printf ("cgi->num %d\n", cgi->num_pending_writes_to_client); if (hio_dev_sck_write(cgi->task_csck, data, dlen, HIO_NULL, HIO_NULL) <= -1) { cgi->num_pending_writes_to_client--; @@ -127,6 +126,7 @@ static int cgi_writev_to_client (cgi_t* cgi, hio_iovec_t* iov, hio_iolen_t iovcn cgi->ever_attempted_to_write_to_client = 1; cgi->num_pending_writes_to_client++; + if (hio_dev_sck_writev(cgi->task_csck, iov, iovcnt, HIO_NULL, HIO_NULL) <= -1) { cgi->num_pending_writes_to_client--; @@ -141,13 +141,9 @@ static int cgi_writev_to_client (cgi_t* cgi, hio_iovec_t* iov, hio_iolen_t iovcn return 0; } -static int cgi_send_final_status_to_client (cgi_t* cgi, int status_code, int force_close) +static HIO_INLINE int cgi_send_final_status_to_client (cgi_t* cgi, int status_code, int force_close) { - hio_svc_htts_cli_t* cli = cgi->task_client; - if (!cli) return 0; /* client disconnected probably */ - if (hio_svc_htts_task_buildfinalres(cgi, status_code, HIO_NULL, HIO_NULL, force_close) <= -1) return -1; - return (cgi_write_to_client(cgi, HIO_BECS_PTR(cli->sbuf), HIO_BECS_LEN(cli->sbuf)) <= -1 || - (force_close && cgi_write_to_client(cgi, HIO_NULL, 0) <= -1))? -1: 0; + return hio_svc_htts_task_sendfinalres(cgi, status_code, HIO_NULL, HIO_NULL, force_close); } static int cgi_write_last_chunk_to_client (cgi_t* cgi) @@ -501,17 +497,20 @@ static int peer_htrd_peek (hio_htrd_t* htrd, hio_htre_t* req) hio_htre_walkheaders(req, peer_capture_response_header, cgi) <= -1 || hio_svc_htts_task_endreshdr(cgi) <= -1) return -1; - cgi->task_status_code = status_code; - return cgi_write_to_client(cgi, HIO_BECS_PTR(cli->sbuf), HIO_BECS_LEN(cli->sbuf)); + return 0; } static int peer_htrd_poke (hio_htrd_t* htrd, hio_htre_t* req) { - /* client request got completed */ + /* peer response got completed */ cgi_peer_xtn_t* peer = hio_htrd_getxtn(htrd); cgi_t* cgi = peer->cgi; +#if 1 if (cgi_write_last_chunk_to_client(cgi) <= -1) return -1; +#else + if (hio_svc_htts_task_endbody(cgi) <= -1) return -1; ???? +#endif cgi_mark_over (cgi, CGI_OVER_READ_FROM_PEER); return 0; @@ -641,7 +640,7 @@ static int cgi_client_on_read (hio_dev_sck_t* sck, const void* buf, hio_iolen_t if (len <= -1) { /* read error */ - HIO_DEBUG2 (cli->htts->hio, "HTTS(%p) - read error on client %p(%d)\n", sck, (int)sck->hnd); + HIO_DEBUG3 (cli->htts->hio, "HTTS(%p) - read error on client %p(%d)\n", cgi->htts, sck, (int)sck->hnd); goto oops; } @@ -693,9 +692,11 @@ static int cgi_client_on_write (hio_dev_sck_t* sck, hio_iolen_t wrlen, void* wrc hio_svc_htts_cli_t* cli = hio_dev_sck_getxtn(sck); cgi_t* cgi = (cgi_t*)cli->task; +printf ("on_write cgi->num %d wrlen=%d\n", cgi->num_pending_writes_to_client, (int)wrlen); + if (wrlen <= -1) { - HIO_DEBUG3 (hio, "HTTS(%p) - unable to write to client %p(%d)\n", sck->hio, sck, (int)sck->hnd); + HIO_DEBUG3 (hio, "HTTS(%p) - unable to write to client %p(%d)\n", cgi->htts, sck, (int)sck->hnd); goto oops; } @@ -715,6 +716,7 @@ static int cgi_client_on_write (hio_dev_sck_t* sck, hio_iolen_t wrlen, void* wrc HIO_ASSERT (hio, cgi->num_pending_writes_to_client > 0); cgi->num_pending_writes_to_client--; + if (cgi->peer && cgi->num_pending_writes_to_client == CGI_PENDING_IO_THRESHOLD) { if (!(cgi->over & CGI_OVER_READ_FROM_PEER) && @@ -921,9 +923,6 @@ int hio_svc_htts_docgi (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* r cgi->on_kill = on_kill; cgi->options = options; - /*cgi->num_pending_writes_to_client = 0; - cgi->num_pending_writes_to_peer = 0;*/ - cgi->req_content_length_unlimited = hio_htre_getreqcontentlen(req, &cgi->req_content_length); /* remember the client socket's io event handlers */ cgi->client_org_on_read = csck->on_read; @@ -957,7 +956,7 @@ int hio_svc_htts_docgi (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* r HIO_SVC_HTTS_TASK_REF (cgi, peer_xtn->cgi); /* peer->cgi in htrd = cgi */ #if !defined(CGI_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH) - if (cgi->req_content_length_unlimited) + if (cgi->task_req_conlen_unlimited) { /* Transfer-Encoding is chunked. no content-length is known in advance. */ @@ -973,9 +972,8 @@ int hio_svc_htts_docgi (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* r { /* TODO: Expect: 100-continue? who should handle this? cgi? or the http server? */ /* CAN I LET the cgi SCRIPT handle this? */ - if (!(options & HIO_SVC_HTTS_CGI_NO_100_CONTINUE) && - hio_comp_http_version_numbers(&req->version, 1, 1) >= 0 && - (cgi->req_content_length_unlimited || cgi->req_content_length > 0)) + if (hio_comp_http_version_numbers(&req->version, 1, 1) >= 0 && + (cgi->task_req_conlen_unlimited || cgi->task_req_conlen > 0)) { /* * Don't send 100 Continue if http verions is lower than 1.1 @@ -1006,7 +1004,7 @@ int hio_svc_htts_docgi (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* r } #if defined(CGI_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH) - if (cgi->req_content_length_unlimited) + if (cgi->task_req_conlen_unlimited) { /* change the callbacks to subscribe to contents to be uploaded */ cgi->client_htrd_org_recbs = *hio_htrd_getrecbs(cgi->task_client->htrd); @@ -1017,7 +1015,7 @@ int hio_svc_htts_docgi (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* r else { #endif - if (cgi->req_content_length > 0) + if (cgi->task_req_conlen > 0) { /* change the callbacks to subscribe to contents to be uploaded */ cgi->client_htrd_org_recbs = *hio_htrd_getrecbs(cgi->task_client->htrd); diff --git a/lib/http-fcgi.c b/lib/http-fcgi.c index e32faa8..ed3b83f 100644 --- a/lib/http-fcgi.c +++ b/lib/http-fcgi.c @@ -35,13 +35,7 @@ struct fcgi_t hio_htrd_t* peer_htrd; unsigned int over: 4; /* must be large enough to accomodate FCGI_OVER_ALL */ - unsigned int req_content_length_unlimited: 1; - unsigned int ever_attempted_to_write_to_client: 1; - unsigned int last_chunk_sent: 1; - unsigned int client_disconnected: 1; - unsigned int client_eof_detected: 1; unsigned int client_htrd_recbs_changed: 1; - hio_oow_t req_content_length; /* client request content length */ fcgi_res_mode_t res_mode_to_cli; hio_dev_sck_on_read_t client_org_on_read; @@ -69,91 +63,9 @@ static void fcgi_halt_participating_devices (fcgi_t* fcgi) unbind_task_from_peer (fcgi, 1); } -static int fcgi_write_to_client (fcgi_t* fcgi, const void* data, hio_iolen_t dlen) +static HIO_INLINE int fcgi_send_final_status_to_client (fcgi_t* fcgi, int status_code, int force_close) { - if (fcgi->task_csck) - { - fcgi->ever_attempted_to_write_to_client = 1; - -HIO_DEBUG2 (fcgi->htts->hio, "WR TO C[%.*hs]\n", dlen, data); - fcgi->num_pending_writes_to_client++; - if (hio_dev_sck_write(fcgi->task_csck, data, dlen, HIO_NULL, HIO_NULL) <= -1) - { - fcgi->num_pending_writes_to_client--; - return -1; - } - - #if 0 - if (fcgi->num_pending_writes_to_client > FCGI_PENDING_IO_THRESHOLD_TO_CLIENT) - { - - /* the fcgic service is shared. whent the client side is stuck, - * it's not natural to stop reading from the whole service. */ - /* if (hio_svc_fcgic_read(fcgi->peer, 0) <= -1) return -1; */ - - /* do nothing for now. TODO: but should the slow client connection be aborted??? */ - } - #endif - } - return 0; -} - -static int fcgi_writev_to_client (fcgi_t* fcgi, hio_iovec_t* iov, hio_iolen_t iovcnt) -{ - if (fcgi->task_csck) - { - fcgi->ever_attempted_to_write_to_client = 1; - - fcgi->num_pending_writes_to_client++; - if (hio_dev_sck_writev(fcgi->task_csck, iov, iovcnt, HIO_NULL, HIO_NULL) <= -1) - { - fcgi->num_pending_writes_to_client--; - return -1; - } - - #if 0 - if (fcgi->num_pending_writes_to_client > FCGI_PENDING_IO_THRESHOLD_TO_CLIENT) - { - - /* the fcgic service is shared. whent the client side is stuck, - * it's not natural to stop reading from the whole service. */ - /* if (hio_svc_fcgic_read(fcgi->peer, 0) <= -1) return -1; */ - - /* do nothing for now. TODO: but should the slow client connection be aborted??? */ - } - #endif - } - return 0; -} - -static int fcgi_send_final_status_to_client (fcgi_t* fcgi, int status_code, int force_close) -{ - hio_svc_htts_cli_t* cli = fcgi->task_client; - if (!cli) return 0; /* client disconnected probably */ - if (hio_svc_htts_task_buildfinalres(fcgi, status_code, HIO_NULL, HIO_NULL, force_close) <= -1) return -1; - return (fcgi_write_to_client(fcgi, HIO_BECS_PTR(cli->sbuf), HIO_BECS_LEN(cli->sbuf)) <= -1 || - (force_close && fcgi_write_to_client(fcgi, HIO_NULL, 0) <= -1))? -1: 0; -} - -static int fcgi_write_last_chunk_to_client (fcgi_t* fcgi) -{ - if (!fcgi->last_chunk_sent) - { - fcgi->last_chunk_sent = 1; - - if (!fcgi->ever_attempted_to_write_to_client) - { - if (fcgi_send_final_status_to_client(fcgi, HIO_HTTP_STATUS_INTERNAL_SERVER_ERROR, 0) <= -1) return -1; - } - else - { - if (fcgi->res_mode_to_cli == FCGI_RES_MODE_CHUNKED && - fcgi_write_to_client(fcgi, "0\r\n\r\n", 5) <= -1) return -1; - } - - if (!fcgi->task_keep_client_alive && fcgi_write_to_client(fcgi, HIO_NULL, 0) <= -1) return -1; - } - return 0; + return hio_svc_htts_task_sendfinalres(fcgi, status_code, HIO_NULL, HIO_NULL, force_close); } static int fcgi_write_stdin_to_peer (fcgi_t* fcgi, const void* data, hio_iolen_t dlen) @@ -212,6 +124,8 @@ static HIO_INLINE void fcgi_mark_over (fcgi_t* fcgi, int over_bits) if (old_over != FCGI_OVER_ALL && fcgi->over == FCGI_OVER_ALL) { /* ready to stop */ + HIO_SVC_HTTS_TASK_RCUP ((hio_svc_htts_task_t*)fcgi); + if (fcgi->peer) { hio_svc_fcgic_untie (fcgi->peer); @@ -220,13 +134,11 @@ static HIO_INLINE void fcgi_mark_over (fcgi_t* fcgi, int over_bits) if (fcgi->task_csck) { -HIO_DEBUG2 (hio, "HTTS(%p) - ALL OVER keeping client(%p) alive\n", fcgi->htts, fcgi->task_csck); - if (fcgi->task_keep_client_alive && !fcgi->client_eof_detected) + if (fcgi->task_keep_client_alive) { HIO_DEBUG2 (hio, "HTTS(%p) - keeping client(%p) alive\n", fcgi->htts, fcgi->task_csck); HIO_ASSERT (fcgi->htts->hio, fcgi->task_client->task == (hio_svc_htts_task_t*)fcgi); unbind_task_from_client (fcgi, 1); - /* fcgi must not be accessed from here down as it could have been destroyed in unbind_task_from_client() */ } else { @@ -235,6 +147,8 @@ HIO_DEBUG2 (hio, "HTTS(%p) - ALL OVER keeping client(%p) alive\n", fcgi->htts, f hio_dev_sck_halt (fcgi->task_csck); } } + + HIO_SVC_HTTS_TASK_RCDOWN ((hio_svc_htts_task_t*)fcgi); /* it may destroy fcgi here */ } } @@ -286,11 +200,13 @@ static void fcgi_peer_on_untie (hio_svc_fcgic_sess_t* peer, void* ctx) * fcgi_halt_participating_devices() calls hio_svc_fcgi_untie() again * to cause an infinite loop if we don't reset fcgi->peer to HIO_NULL here */ - HIO_DEBUG5 (hio, "HTTS(%p) - fcgi(t=%p,c=%p[%d],p=%p) - peer untied\n", fcgi->htts, fcgi, fcgi->task_client, (fcgi->task_csck? fcgi->task_csck->hnd: -1), fcgi->peer); + HIO_DEBUG5 (hio, "HTTS(%p) - fcgi(t=%p,c=%p[%d],p=%p) - untieing peer\n", fcgi->htts, fcgi, fcgi->task_client, (fcgi->task_csck? fcgi->task_csck->hnd: -1), fcgi->peer); fcgi->peer = HIO_NULL; - fcgi_write_last_chunk_to_client (fcgi); + hio_svc_htts_task_endbody (fcgi); unbind_task_from_peer (fcgi, 1); + + HIO_DEBUG5 (hio, "HTTS(%p) - fcgi(t=%p,c=%p[%d],p=%p) - untied peer\n", fcgi->htts, fcgi, fcgi->task_client, (fcgi->task_csck? fcgi->task_csck->hnd: -1), fcgi->peer); } static int fcgi_peer_on_read (hio_svc_fcgic_sess_t* peer, const void* data, hio_iolen_t dlen, void* ctx) @@ -315,7 +231,7 @@ static int fcgi_peer_on_read (hio_svc_fcgic_sess_t* peer, const void* data, hio_ /* the fcgi script could be misbehaving. * it still has to read more but EOF is read. * otherwise peer_htrd_poke() should have been called */ - n = fcgi_write_last_chunk_to_client(fcgi); + n = hio_svc_htts_task_endbody(fcgi); fcgi_mark_over (fcgi, FCGI_OVER_READ_FROM_PEER); if (n <= -1) goto oops; } @@ -330,10 +246,9 @@ static int fcgi_peer_on_read (hio_svc_fcgic_sess_t* peer, const void* data, hio_ { HIO_DEBUG2 (hio, "HTTS(%p) - unable to feed peer htrd - peer %p\n", htts, peer); - if (!fcgi->ever_attempted_to_write_to_client && - !(fcgi->over & FCGI_OVER_WRITE_TO_CLIENT)) + if (!fcgi->task_res_started && !(fcgi->over & FCGI_OVER_WRITE_TO_CLIENT)) { - fcgi_send_final_status_to_client (fcgi, HIO_HTTP_STATUS_INTERNAL_SERVER_ERROR, 1); /* don't care about error because it jumps to oops below anyway */ + fcgi_send_final_status_to_client (fcgi, HIO_HTTP_STATUS_BAD_GATEWAY, 1); /* don't care about error because it jumps to oops below anyway */ } goto oops; @@ -379,6 +294,7 @@ static int peer_capture_response_header (hio_htre_t* req, const hio_bch_t* key, static int peer_htrd_peek (hio_htrd_t* htrd, hio_htre_t* req) { + /* response header received from the peer */ fcgi_peer_xtn_t* peer = hio_htrd_getxtn(htrd); fcgi_t* fcgi = peer->fcgi; hio_svc_htts_cli_t* cli = fcgi->task_client; @@ -386,86 +302,40 @@ static int peer_htrd_peek (hio_htrd_t* htrd, hio_htre_t* req) const hio_bch_t* status_desc = HIO_NULL; int chunked; - if (HIO_UNLIKELY(!cli)) + if (HIO_LIKELY(cli)) /* only if the client is still connected */ { - /* client disconnected or not connectd */ - return 0; + // TOOD: remove content_length if content_length is negative or not numeric. + if (req->attr.content_length) fcgi->res_mode_to_cli = FCGI_RES_MODE_LENGTH; + if (req->attr.status) hio_parse_http_status_header_value(req->attr.status, &status_code, &status_desc); + + chunked = fcgi->task_keep_client_alive && !req->attr.content_length; + + if (hio_svc_htts_task_startreshdr(fcgi, status_code, status_desc, chunked) <= -1 || + hio_htre_walkheaders(req, peer_capture_response_header, fcgi) <= -1 || + hio_svc_htts_task_endreshdr(fcgi) <= -1) return -1; } - // TOOD: remove content_length if content_length is negative or not numeric. - if (req->attr.content_length) fcgi->res_mode_to_cli = FCGI_RES_MODE_LENGTH; - if (req->attr.status) hio_parse_http_status_header_value(req->attr.status, &status_code, &status_desc); - - chunked = fcgi->task_keep_client_alive && !req->attr.content_length; - - if (hio_svc_htts_task_startreshdr(fcgi, status_code, status_desc, chunked) <= -1 || - hio_htre_walkheaders(req, peer_capture_response_header, fcgi) <= -1 || - hio_svc_htts_task_endreshdr(fcgi) <= -1) return -1; - - fcgi->task_status_code = status_code; - return fcgi_write_to_client(fcgi, HIO_BECS_PTR(cli->sbuf), HIO_BECS_LEN(cli->sbuf)); + return 0; } static int peer_htrd_poke (hio_htrd_t* htrd, hio_htre_t* req) { + /* complete response received from the peer */ fcgi_peer_xtn_t* peer = hio_htrd_getxtn(htrd); fcgi_t* fcgi = peer->fcgi; + int n; - if (fcgi_write_last_chunk_to_client(fcgi) <= -1) return -1; - + n = hio_svc_htts_task_endbody(fcgi); fcgi_mark_over (fcgi, FCGI_OVER_READ_FROM_PEER); - return 0; + return n; } static int peer_htrd_push_content (hio_htrd_t* htrd, hio_htre_t* req, const hio_bch_t* data, hio_oow_t dlen) { fcgi_peer_xtn_t* peer = hio_htrd_getxtn(htrd); fcgi_t* fcgi = peer->fcgi; - HIO_ASSERT (fcgi->htts->hio, htrd == fcgi->peer_htrd); - - switch (fcgi->res_mode_to_cli) - { - case FCGI_RES_MODE_CHUNKED: - { - hio_iovec_t iov[3]; - hio_bch_t lbuf[16]; - hio_oow_t llen; - - /* hio_fmt_uintmax_to_bcstr() null-terminates the output. only HIO_COUNTOF(lbuf) - 1 - * is enough to hold '\r' and '\n' at the back without '\0'. */ - llen = hio_fmt_uintmax_to_bcstr(lbuf, HIO_COUNTOF(lbuf) - 1, dlen, 16 | HIO_FMT_UINTMAX_UPPERCASE, 0, '\0', HIO_NULL); - lbuf[llen++] = '\r'; - lbuf[llen++] = '\n'; - - iov[0].iov_ptr = lbuf; - iov[0].iov_len = llen; - iov[1].iov_ptr = (void*)data; - iov[1].iov_len = dlen; - iov[2].iov_ptr = "\r\n"; - iov[2].iov_len = 2; - - if (fcgi_writev_to_client(fcgi, iov, HIO_COUNTOF(iov)) <= -1) goto oops; - break; - } - - case FCGI_RES_MODE_CLOSE: - case FCGI_RES_MODE_LENGTH: - if (fcgi_write_to_client(fcgi, data, dlen) <= -1) goto oops; - break; - } - -#if 0 - if (fcgi->num_pending_writes_to_client > FCGI_PENDING_IO_THRESHOLD_TO_CLIENT) - { - if (hio_svc_fcgic_read(fcgi->peer, 0) <= -1) goto oops; - } -#endif - - return 0; - -oops: - return -1; + return hio_svc_htts_task_addresbody(fcgi, data, dlen); } static hio_htrd_recbs_t peer_htrd_recbs = @@ -518,7 +388,7 @@ static void fcgi_client_on_disconnect (hio_dev_sck_t* sck) hio_svc_htts_t* htts = cli->htts; fcgi_t* fcgi = (fcgi_t*)cli->task; - HIO_DEBUG4 (hio, "HTTS(%p) - fcgi(t=%p,c=%p,csck=%p) - client socket disconnect handled\n", htts, fcgi, cli, sck); + HIO_DEBUG4 (hio, "HTTS(%p) - fcgi(t=%p,c=%p,csck=%p) - handling client socket disconnect\n", htts, fcgi, cli, sck); /* fcgi may be null if there is no associated task or * the previously associated one is already gone */ @@ -526,16 +396,19 @@ static void fcgi_client_on_disconnect (hio_dev_sck_t* sck) { HIO_ASSERT (hio, sck == fcgi->task_csck); - /* set fcgi->client_disconnect to 1 before unbind_task_from_client() - * because fcgi can be destroyed if its reference count hits 0 */ - fcgi->client_disconnected = 1; + HIO_SVC_HTTS_TASK_RCUP (fcgi); + + /* detach the task from the client and the client socket */ unbind_task_from_client (fcgi, 1); - /* this is the original callback restored in unbind_task_from_client() */ - if (sck->on_disconnect) sck->on_disconnect (sck); + /* call the parent handler*/ + /*if (fcgi->client_org_on_disconnect) fcgi->client_org_on_disconnect (sck);*/ + if (sck->on_disconnect) sck->on_disconnect (sck); /* restored to the orginal parent handelr in unbind_task_from_client() */ + + HIO_SVC_HTTS_TASK_RCDOWN (fcgi); } - HIO_DEBUG4 (hio, "HTTS(%p) - fcgi(t=%p,c=%p,csck=%p) - client socket disconnect handled\n", htts, fcgi, cli, sck); + HIO_DEBUG4 (hio, "HTTS(%p) - fcgi(t=%p,c=%p,csck=%p) - handled client socket disconnect\n", htts, fcgi, cli, sck); /* Note: after this callback, the actual device pointed to by 'sck' will be freed in the main loop. */ } @@ -544,52 +417,35 @@ static int fcgi_client_on_read (hio_dev_sck_t* sck, const void* buf, hio_iolen_t hio_t* hio = sck->hio; hio_svc_htts_cli_t* cli = hio_dev_sck_getxtn(sck); fcgi_t* fcgi = (fcgi_t*)cli->task; + int n; HIO_ASSERT (hio, sck == cli->sck); + n = fcgi->client_org_on_read? fcgi->client_org_on_read(sck, buf, len, srcaddr): 0; + if (len <= -1) { /* read error */ - HIO_DEBUG2 (cli->htts->hio, "HTTS(%p) - read error on client %p(%d)\n", sck, (int)sck->hnd); + HIO_DEBUG3 (cli->htts->hio, "HTTS(%p) - read error on client %p(%d)\n", fcgi->htts, sck, (int)sck->hnd); goto oops; } - if (!fcgi->peer) - { - /* the peer is gone */ - goto oops; /* do what? just return 0? */ - } - if (len == 0) { /* EOF on the client side. arrange to close */ HIO_DEBUG3 (hio, "HTTS(%p) - EOF from client %p(hnd=%d)\n", fcgi->htts, sck, (int)sck->hnd); - fcgi->client_eof_detected = 1; if (!(fcgi->over & FCGI_OVER_READ_FROM_CLIENT)) /* if this is true, EOF is received without fcgi_client_htrd_poke() */ { /* indicate eof to the write side */ - int n; - n = fcgi_write_stdin_to_peer(fcgi, HIO_NULL, 0); + int x; + x = fcgi_write_stdin_to_peer(fcgi, HIO_NULL, 0); fcgi_mark_over (fcgi, FCGI_OVER_READ_FROM_CLIENT); - if (n <= -1) goto oops; - } - } - else - { - hio_oow_t rem; - - HIO_ASSERT (hio, !(fcgi->over & FCGI_OVER_READ_FROM_CLIENT)); - - if (hio_htrd_feed(cli->htrd, buf, len, &rem) <= -1) goto oops; - - if (rem > 0) - { - /* TODO store this to client buffer. once the current resource is completed, arrange to call on_read() with it */ - HIO_DEBUG3 (hio, "HTTS(%p) - excessive data after contents by fcgi client %p(%d)\n", sck->hio, sck, (int)sck->hnd); + if (x <= -1) goto oops; } } + if (n <= -1) goto oops; return 0; oops: @@ -602,46 +458,31 @@ static int fcgi_client_on_write (hio_dev_sck_t* sck, hio_iolen_t wrlen, void* wr hio_t* hio = sck->hio; hio_svc_htts_cli_t* cli = hio_dev_sck_getxtn(sck); fcgi_t* fcgi = (fcgi_t*)cli->task; + int n; - if (wrlen <= -1) - { - HIO_DEBUG3 (hio, "HTTS(%p) - unable to write to client %p(%d)\n", sck->hio, sck, (int)sck->hnd); - goto oops; - } + n = fcgi->client_org_on_write? fcgi->client_org_on_write(sck, wrlen, wrctx, dstaddr): 0; if (wrlen == 0) { - /* if the connect is keep-alive, this part may not be called */ - fcgi->num_pending_writes_to_client--; - HIO_ASSERT (hio, fcgi->num_pending_writes_to_client == 0); - HIO_DEBUG3 (hio, "HTTS(%p) - indicated EOF to client %p(%d)\n", fcgi->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 */ fcgi_mark_over (fcgi, FCGI_OVER_WRITE_TO_CLIENT); } - else + else if (wrlen > 0) { - HIO_ASSERT (hio, fcgi->num_pending_writes_to_client > 0); - - fcgi->num_pending_writes_to_client--; - if (fcgi->peer && fcgi->num_pending_writes_to_client == FCGI_PENDING_IO_THRESHOLD_TO_CLIENT) + #if 0 + if (fcgi->peer && fcgi->task_res_pending_writes == FCGI_PENDING_IO_THRESHOLD_TO_CLIENT) { - #if 0 // TODO + /* enable reading from fcgi */ if (!(fcgi->over & FCGI_OVER_READ_FROM_PEER) && hio_svc_fcgic_read(fcgi->peer, 1) <= -1) goto oops; - #endif } + #endif - if ((fcgi->over & FCGI_OVER_READ_FROM_PEER) && fcgi->num_pending_writes_to_client <= 0) + if ((fcgi->over & FCGI_OVER_READ_FROM_PEER) && fcgi->task_res_pending_writes <= 0) { fcgi_mark_over (fcgi, FCGI_OVER_WRITE_TO_CLIENT); } } - return 0; - -oops: - fcgi_halt_participating_devices (fcgi); + if (n <= -1 || wrlen <= -1) fcgi_halt_participating_devices (fcgi); return 0; } @@ -818,16 +659,18 @@ static void unbind_task_from_client (fcgi_t* fcgi, int rcdown) fcgi->client_org_on_disconnect = HIO_NULL; } -HIO_DEBUG2 (csck->hio, "UNBINDING CLEINT FROM TASK... client=%p csck=%p\n", fcgi->task_client, csck); - /* there is some ordering issue in using HIO_SVC_HTTS_TASK_UNREF() * because it can destroy the fcgi itself. so reset fcgi->task_client->task * to null and call RCDOWN() later */ fcgi->task_client->task = HIO_NULL; + + /* these two lines are also done in csck_on_disconnect() in http-svr.c because the socket is destroyed. + * the same lines here are because the task is unbound while the socket is still alive */ fcgi->task_client = HIO_NULL; fcgi->task_csck = HIO_NULL; - if (!fcgi->client_disconnected && (!fcgi->task_keep_client_alive || hio_dev_sck_read(csck, 1) <= -1)) + /* enable input watching on the socket being unbound */ + if (fcgi->task_keep_client_alive && hio_dev_sck_read(csck, 1) <= -1) { HIO_DEBUG2 (fcgi->htts->hio, "HTTS(%p) - halting client(%p) for failure to enable input watching\n", fcgi->htts, csck); hio_dev_sck_halt (csck); @@ -894,62 +737,10 @@ static void unbind_task_from_peer (fcgi_t* fcgi, int rcdown) } } -static int setup_for_expect100 (fcgi_t* fcgi, hio_htre_t* req, int options) -{ -#if !defined(FCGI_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH) - if (fcgi->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 (fcgi_send_final_status_to_client(fcgi, HIO_HTTP_STATUS_LENGTH_REQUIRED, 1) <= -1) return -1; - } -#endif - - if (req->flags & HIO_HTRE_ATTR_EXPECT100) - { - /* TODO: Expect: 100-continue? who should handle this? fcgi? or the http server? */ - /* CAN I LET the fcgi SCRIPT handle this? */ - if (!(options & HIO_SVC_HTTS_FCGI_NO_100_CONTINUE) && - hio_comp_http_version_numbers(&req->version, 1, 1) >= 0 && - (fcgi->req_content_length_unlimited || fcgi->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. - */ - hio_bch_t msgbuf[64]; - hio_oow_t msglen; - - msglen = hio_fmttobcstr(fcgi->htts->hio, msgbuf, HIO_COUNTOF(msgbuf), "HTTP/%d.%d %d %hs\r\n\r\n", fcgi->task_req_version.major, fcgi->task_req_version.minor, HIO_HTTP_STATUS_CONTINUE, hio_http_status_to_bcstr(HIO_HTTP_STATUS_CONTINUE)); - if (fcgi_write_to_client(fcgi, msgbuf, msglen) <= -1) return -1; - fcgi->ever_attempted_to_write_to_client = 0; /* reset this as it's polluted for 100 continue */ - } - } - else if (req->flags & HIO_HTRE_ATTR_EXPECT) - { - /* 417 Expectation Failed */ - fcgi_send_final_status_to_client(fcgi, HIO_HTTP_STATUS_EXPECTATION_FAILED, 1); - return -1; - } - - return 0; -} - static int setup_for_content_length(fcgi_t* fcgi, hio_htre_t* req) { #if defined(FCGI_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH) - if (fcgi->req_content_length_unlimited) + if (fcgi->task_req_conlen_unlimited) { /* change the callbacks to subscribe to contents to be uploaded */ fcgi->client_htrd_org_recbs = *hio_htrd_getrecbs(fcgi->task_client->htrd); @@ -960,7 +751,7 @@ static int setup_for_content_length(fcgi_t* fcgi, hio_htre_t* req) else { #endif - if (fcgi->req_content_length > 0) + if (fcgi->task_req_conlen > 0) { /* change the callbacks to subscribe to contents to be uploaded */ fcgi->client_htrd_org_recbs = *hio_htrd_getrecbs(fcgi->task_client->htrd); @@ -1003,15 +794,12 @@ int hio_svc_htts_dofcgi (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* fcgi = (fcgi_t*)hio_svc_htts_task_make(htts, HIO_SIZEOF(*fcgi), fcgi_on_kill, req, csck); if (HIO_UNLIKELY(!fcgi)) goto oops; - fcgi->on_kill = on_kill; - /*fcgi->num_pending_writes_to_client = 0; - fcgi->num_pending_writes_to_peer = 0;*/ - fcgi->req_content_length_unlimited = hio_htre_getreqcontentlen(req, &fcgi->req_content_length); + fcgi->on_kill = on_kill; /* custom on_kill handler by the caller */ bind_task_to_client (fcgi, csck); if (bind_task_to_peer(fcgi, fcgis_addr) <= -1) goto oops; - if (setup_for_expect100(fcgi, req, options) <= -1) goto oops; + if (hio_svc_htts_task_handleexpect100(fcgi, options) <= -1) goto oops; if (setup_for_content_length(fcgi, req) <= -1) goto oops; /* TODO: store current input watching state and use it when destroying the fcgi data */ diff --git a/lib/http-file.c b/lib/http-file.c index 026a8db..9b0588c 100644 --- a/lib/http-file.c +++ b/lib/http-file.c @@ -70,13 +70,11 @@ struct file_t hio_bch_t peer_etag[128]; unsigned int over: 4; /* must be large enough to accomodate FILE_OVER_ALL */ - unsigned int req_content_length_unlimited: 1; unsigned int ever_attempted_to_write_to_client: 1; unsigned int client_eof_detected: 1; unsigned int client_disconnected: 1; unsigned int client_htrd_recbs_changed: 1; unsigned int etag_match: 1; - hio_oow_t req_content_length; /* client request content length */ file_res_mode_t res_mode_to_cli; hio_dev_sck_on_read_t client_org_on_read; @@ -133,11 +131,7 @@ static int file_sendfile_to_client (file_t* file, hio_foff_t foff, hio_iolen_t l static int file_send_final_status_to_client (file_t* file, int status_code, int force_close) { - hio_svc_htts_cli_t* cli = file->task_client; - if (!cli) return 0; /* client disconnected probably */ - if (hio_svc_htts_task_buildfinalres(file, status_code, HIO_NULL, HIO_NULL, force_close) <= -1) return -1; - return (file_write_to_client(file, HIO_BECS_PTR(cli->sbuf), HIO_BECS_LEN(cli->sbuf)) <= -1 || - (force_close && file_write_to_client(file, HIO_NULL, 0) <= -1))? -1: 0; + return hio_svc_htts_task_sendfinalres(file, status_code, HIO_NULL, HIO_NULL, force_close); } static void file_close_peer (file_t* file) @@ -318,21 +312,21 @@ static int file_client_on_read (hio_dev_sck_t* sck, const void* buf, hio_iolen_t if (len <= -1) { /* read error */ - HIO_DEBUG3 (cli->htts->hio, "HTTS(%p) - file(c=%d,p=%d) read error on client\n", file->task_client->htts, (int)sck->hnd, file->peer); + HIO_DEBUG3 (cli->htts->hio, "HTTS(%p) - file(c=%d,p=%d) read error on client\n", file->htts, (int)sck->hnd, file->peer); goto oops; } if (file->peer <= -1) { /* the peer is gone or not even opened */ - HIO_DEBUG3 (cli->htts->hio, "HTTS(%p) - file(c=%d,p=%d) read on client, no peer to write\n", file->task_client->htts, (int)sck->hnd, file->peer); + HIO_DEBUG3 (cli->htts->hio, "HTTS(%p) - file(c=%d,p=%d) read on client, no peer to write\n", file->htts, (int)sck->hnd, file->peer); goto oops; /* do what? just return 0? */ } if (len == 0) { /* EOF on the client side. arrange to close */ - HIO_DEBUG3 (cli->htts->hio, "HTTS(%p) - file(c=%d,p=%d) EOF detected on client\n", file->task_client->htts, (int)sck->hnd, file->peer); + HIO_DEBUG3 (cli->htts->hio, "HTTS(%p) - file(c=%d,p=%d) EOF detected on client\n", file->htts, (int)sck->hnd, file->peer); file->client_eof_detected = 1; if (!(file->over & FILE_OVER_READ_FROM_CLIENT)) /* if this is true, EOF is received without file_client_htrd_poke() */ @@ -354,7 +348,7 @@ static int file_client_on_read (hio_dev_sck_t* sck, const void* buf, hio_iolen_t if (rem > 0) { /* TODO store this to client buffer. once the current resource is completed, arrange to call on_read() with it */ - HIO_DEBUG3 (cli->htts->hio, "HTTS(%p) - file(c=%d,p=%d) excessive data after contents on client\n", file->task_client->htts, (int)sck->hnd, file->peer); + HIO_DEBUG3 (cli->htts->hio, "HTTS(%p) - file(c=%d,p=%d) excessive data after contents on client\n", file->htts, (int)sck->hnd, file->peer); } } @@ -376,7 +370,7 @@ static int file_client_on_write (hio_dev_sck_t* sck, hio_iolen_t wrlen, void* wr if (wrlen <= -1) { - HIO_DEBUG3 (hio, "HTTS(%p) - file(c=%d,p=%d) unable to write to client\n", file->task_client->htts, (int)sck->hnd, file->peer); + HIO_DEBUG3 (hio, "HTTS(%p) - file(c=%d,p=%d) unable to write to client\n", file->htts, (int)sck->hnd, file->peer); goto oops; } @@ -385,7 +379,7 @@ static int file_client_on_write (hio_dev_sck_t* sck, hio_iolen_t wrlen, void* wr /* if the connect is keep-alive, this part may not be called */ file->num_pending_writes_to_client--; HIO_ASSERT (hio, file->num_pending_writes_to_client == 0); - HIO_DEBUG3 (hio, "HTTS(%p) - file(c=%d,p=%d) indicated EOF to client\n", file->task_client->htts, (int)sck->hnd, file->peer); + HIO_DEBUG3 (hio, "HTTS(%p) - file(c=%d,p=%d) indicated EOF to client\n", file->htts, (int)sck->hnd, file->peer); /* 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 */ @@ -457,45 +451,6 @@ static hio_htrd_recbs_t file_client_htrd_recbs = static int file_send_header_to_client (file_t* file, int status_code, int force_close, const hio_bch_t* mime_type) { -#if 0 - hio_svc_htts_cli_t* cli = file->task_client; - hio_bch_t dtbuf[64]; - hio_foff_t content_length; - - hio_svc_htts_fmtgmtime (cli->htts, HIO_NULL, dtbuf, HIO_COUNTOF(dtbuf)); - - if (!force_close) force_close = !file->task_keep_client_alive; - - content_length = file->end_offset - file->start_offset + 1; - if (status_code == HIO_HTTP_STATUS_OK && file->total_size != content_length) status_code = HIO_HTTP_STATUS_PARTIAL_CONTENT; - - if (hio_becs_fmt(cli->sbuf, "HTTP/%d.%d %d %hs\r\nServer: %hs\r\nDate: %s\r\nConnection: %hs\r\nAccept-Ranges: bytes\r\n", - file->task_req_version.major, file->task_req_version.minor, - status_code, hio_http_status_to_bcstr(status_code), - cli->htts->server_name, dtbuf, - (force_close? "close": "keep-alive")) == (hio_oow_t)-1) return -1; - - /* Content-Type is not set if mime_type is null or blank */ - if (mime_type && mime_type[0] != '\0' && - hio_becs_fcat(cli->sbuf, "Content-Type: %hs\r\n", mime_type) == (hio_oow_t)-1) return -1; - - if ((file->task_req_method == HIO_HTTP_GET || file->task_req_method == HIO_HTTP_HEAD) && - hio_becs_fcat(cli->sbuf, "ETag: %hs\r\n", file->peer_etag) == (hio_oow_t)-1) return -1; - - if (status_code == HIO_HTTP_STATUS_PARTIAL_CONTENT && - hio_becs_fcat(cli->sbuf, "Content-Ranges: bytes %ju-%ju/%ju\r\n", (hio_uintmax_t)file->start_offset, (hio_uintmax_t)file->end_offset, (hio_uintmax_t)file->total_size) == (hio_oow_t)-1) return -1; - -/* ----- */ -// TODO: Allow-Contents -// Allow-Headers... support custom headers... - if (hio_becs_fcat(cli->sbuf, "Access-Control-Allow-Origin: *\r\n", (hio_uintmax_t)content_length) == (hio_oow_t)-1) return -1; -/* ----- */ - - if (hio_becs_fcat(cli->sbuf, "Content-Length: %ju\r\n\r\n", (hio_uintmax_t)content_length) == (hio_oow_t)-1) return -1; - - file->task_status_code = status_code; - return file_write_to_client(file, HIO_BECS_PTR(cli->sbuf), HIO_BECS_LEN(cli->sbuf)); -#else hio_svc_htts_cli_t* cli = file->task_client; hio_foff_t content_length; @@ -529,9 +484,7 @@ static int file_send_header_to_client (file_t* file, int status_code, int force_ if (hio_svc_htts_task_endreshdr(file) <= -1) return -1; - file->task_status_code = status_code; - return file_write_to_client(file, HIO_BECS_PTR(cli->sbuf), HIO_BECS_LEN(cli->sbuf)); -#endif + return 0; } static void send_contents_to_client_later (hio_t* hio, const hio_ntime_t* now, hio_tmrjob_t* tmrjob) @@ -792,9 +745,6 @@ int hio_svc_htts_dofile (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* file->options = options; file->cbs = cbs; /* the given pointer must outlive the lifespan of the while file handling cycle. */ file->sendfile_ok = hio_dev_sck_sendfileok(csck); - /*file->num_pending_writes_to_client = 0; - file->num_pending_writes_to_peer = 0;*/ - file->req_content_length_unlimited = hio_htre_getreqcontentlen(req, &file->req_content_length); file->client_org_on_read = csck->on_read; file->client_org_on_write = csck->on_write; @@ -810,7 +760,7 @@ int hio_svc_htts_dofile (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* HIO_SVC_HTTS_TASK_REF ((hio_svc_htts_task_t*)file, cli->task); /* cli->task = file with ref-count up */ #if !defined(FILE_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH) - if (file->req_content_length_unlimited) + if (file->task_req_conlen_unlimited) { /* Transfer-Encoding is chunked. no content-length is known in advance. */ @@ -823,9 +773,8 @@ int hio_svc_htts_dofile (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* if (req->flags & HIO_HTRE_ATTR_EXPECT100) { - if (!(options & HIO_SVC_HTTS_FILE_NO_100_CONTINUE) && - hio_comp_http_version_numbers(&req->version, 1, 1) >= 0 && - (file->req_content_length_unlimited || file->req_content_length > 0) && + if (hio_comp_http_version_numbers(&req->version, 1, 1) >= 0 && + (file->task_req_conlen_unlimited || file->task_req_conlen > 0) && (file->task_req_method != HIO_HTTP_GET && file->task_req_method != HIO_HTTP_HEAD)) { hio_bch_t msgbuf[64]; @@ -844,7 +793,7 @@ int hio_svc_htts_dofile (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* } #if defined(FILE_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH) - if (file->req_content_length_unlimited) + if (file->task_req_conlen_unlimited) { /* change the callbacks to subscribe to contents to be uploaded */ file->client_htrd_org_recbs = *hio_htrd_getrecbs(file->task_client->htrd); @@ -855,7 +804,7 @@ int hio_svc_htts_dofile (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* else { #endif - if (file->req_content_length > 0) + if (file->task_req_conlen > 0) { /* change the callbacks to subscribe to contents to be uploaded */ file->client_htrd_org_recbs = *hio_htrd_getrecbs(file->task_client->htrd); diff --git a/lib/http-svr.c b/lib/http-svr.c index 9070ea6..fa781ed 100644 --- a/lib/http-svr.c +++ b/lib/http-svr.c @@ -30,6 +30,14 @@ #define INVALID_LIDX HIO_TYPE_MAX(hio_oow_t) +static int htts_svr_wrctx; + +/* ------------------------------------------------------------------------ */ + +static int client_on_read (hio_dev_sck_t* sck, const void* buf, hio_iolen_t len, const hio_skad_t* srcaddr); +static int client_on_write (hio_dev_sck_t* sck, hio_iolen_t wrlen, void* wrctx, const hio_skad_t* dstaddr); +static void client_on_disconnect (hio_dev_sck_t* sck); + /* ------------------------------------------------------------------------ */ static int client_htrd_peek_request (hio_htrd_t* htrd, hio_htre_t* req) { @@ -81,7 +89,13 @@ static int init_client (hio_svc_htts_cli_t* cli, hio_dev_sck_t* sck) hio_htrd_setrecbs (cli->htrd, &client_htrd_recbs); hio_gettime (sck->hio, &cli->last_active); - HIO_DEBUG4 (sck->hio, "HTTS(%p) - initialized client(%p,%p,%d)\n", cli->htts, cli, sck, (int)sck->hnd); + + HIO_DEBUG4 (sck->hio, "HTTS(%p) - client(c=%p,csck=%d[%d]) - initialized\n", cli->htts, cli, sck, (int)sck->hnd); + + sck->on_read = client_on_read; + sck->on_write = client_on_write; + sck->on_disconnect = client_on_disconnect; + return 0; oops: @@ -102,10 +116,16 @@ oops: static void fini_client (hio_svc_htts_cli_t* cli) { - HIO_DEBUG5 (cli->sck->hio, "HTTS(%p) - finalizing client(c=%p,sck=%p[%d],task=%p)\n", cli->htts, cli, cli->sck, (int)cli->sck->hnd, cli->task); + HIO_DEBUG4(cli->sck->hio, "HTTS(%p) - client(c=%p,sck=%p[%d]) - finalizing\n", cli->htts, cli, cli->sck, (int)cli->sck->hnd); if (cli->task) { + cli->task->task_keep_client_alive = 0; + + /* the client socket is not valid any longer. let's forget it */ + cli->task->task_client = HIO_NULL; + cli->task->task_csck = HIO_NULL; + HIO_SVC_HTTS_TASK_UNREF (cli->task); } @@ -129,63 +149,18 @@ static void fini_client (hio_svc_htts_cli_t* cli) cli->sck = HIO_NULL; cli->htts = HIO_NULL; */ + + HIO_DEBUG4(cli->sck->hio, "HTTS(%p) - client(c=%p,sck=%p[%d]) - finalized\n", cli->htts, cli, cli->sck, (int)cli->sck->hnd); } /* ------------------------------------------------------------------------ */ static int listener_on_read (hio_dev_sck_t* sck, const void* buf, hio_iolen_t len, const hio_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 */ - - hio_t* hio = sck->hio; - hio_svc_htts_cli_t* cli = hio_dev_sck_getxtn(sck); - hio_oow_t rem; - int x; - - HIO_ASSERT (hio, cli->l_idx == INVALID_LIDX); - - /* if a task has been set(cli->task not NULL) on the client, the task must take over - * this handler. this handler is never called unless the the overriding handler - * call this. */ - HIO_ASSERT (hio, cli->task == HIO_NULL); - - if (len <= -1) - { - HIO_DEBUG3 (hio, "HTTS(%p) - unable to read client %p(%d)\n", cli->htts, sck, (int)sck->hnd); - goto oops; - } - - if (len == 0) - { - HIO_DEBUG3 (hio, "HTTS(%p) - EOF on client %p(%d)\n", cli->htts, sck, (int)sck->hnd); - goto oops; - } - - hio_gettime (hio, &cli->last_active); - if ((x = hio_htrd_feed(cli->htrd, buf, len, &rem)) <= -1) - { - HIO_DEBUG3 (hio, "HTTS(%p) - feed error onto client htrd %p(%d)\n", cli->htts, sck, (int)sck->hnd); - goto oops; - } - - if (rem > 0) - { - if (cli->task) - { - /* TODO store this to client buffer. once the current resource is completed, arrange to call on_read() with it */ - } - else - { - /* TODO: no resource in action. so feed one more time */ - } - } - + /* unlike the function name, this callback is set on both the listener and the client initially. + * init_client() changes the socket's on_read to client_on_read. so this hander must never be called */ + if (len <= -1) hio_dev_sck_halt (sck); return 0; - -oops: - hio_dev_sck_halt (sck); - return 0; /* still return success here. instead call halt() */ } static int listener_on_write (hio_dev_sck_t* sck, hio_iolen_t wrlen, void* wrctx, const hio_skad_t* dstaddr) @@ -195,13 +170,14 @@ static int listener_on_write (hio_dev_sck_t* sck, hio_iolen_t wrlen, void* wrctx hio_svc_htts_cli_t* cli = hio_dev_sck_getxtn(sck); HIO_ASSERT (sck->hio, cli->l_idx == INVALID_LIDX); +#if 0 /* if a resource has been set(cli->task not NULL), the resource must take over * this handler. this handler is never called unless the the overriding handler * call this. */ HIO_ASSERT (sck->hio, cli->task == HIO_NULL); +#endif /* anyways, nothing to do upon write completion */ - return 0; } @@ -261,7 +237,7 @@ static void listener_on_disconnect (hio_dev_sck_t* sck) * on_disconnected() with this code is called without corresponding on_connect(). * the cli extension are is not initialized yet */ HIO_ASSERT (hio, sck != xtn->sck); - //HIO_ASSERT (hio, 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 */ + /*HIO_ASSERT (hio, 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 */ HIO_DEBUG3 (hio, "HTTS(%p) - LISTENER UNABLE TO SSL-ACCEPT CLIENT %p[%d]\n", htts, sck, (int)sck->hnd); return; @@ -288,13 +264,124 @@ static void listener_on_disconnect (hio_dev_sck_t* sck) else { /* client socket */ - HIO_DEBUG3 (hio, "HTTS(%p) - client socket disconnect %p[%d]\n", xtn->htts, sck, (int)sck->hnd); - HIO_ASSERT (hio, xtn->sck == sck); - fini_client (xtn); + client_on_disconnect (sck); } } +/* --------------------------------------------------------------- */ + + +static int client_on_read (hio_dev_sck_t* sck, const void* buf, hio_iolen_t len, const hio_skad_t* srcaddr) +{ + hio_svc_htts_cli_t* cli = hio_dev_sck_getxtn(sck); + hio_t* hio = sck->hio; + hio_svc_htts_t* htts = cli->htts; + hio_svc_htts_task_t* task = cli->task; + hio_oow_t rem; + int x; + + HIO_ASSERT (hio, cli->l_idx == INVALID_LIDX); + + if (len <= -1) + { + HIO_DEBUG3 (hio, "HTTS(%p) - unable to read client %p(%d)\n", htts, sck, (int)sck->hnd); + if (task) task->task_keep_client_alive = 0; + goto oops; + } + + if (len == 0) + { + HIO_DEBUG3 (hio, "HTTS(%p) - EOF on client %p(%d)\n", htts, sck, (int)sck->hnd); + if (task) task->task_keep_client_alive = 0; + goto oops; + } + + hio_gettime (hio, &cli->last_active); + if ((x = hio_htrd_feed(cli->htrd, buf, len, &rem)) <= -1) + { + HIO_DEBUG3 (hio, "HTTS(%p) - feed error onto client htrd %p(%d)\n", htts, sck, (int)sck->hnd); + goto oops; + } + + if (rem > 0) + { + HIO_DEBUG3 (hio, "HTTS(%p) - excessive data after contents by client %p(%d)\n", htts, sck, (int)sck->hnd); + if (cli->task) + { + /* TODO store this to client buffer. once the current resource is completed, arrange to call on_read() with it */ + } + else + { + /* TODO: no resource in action. so feed one more time */ + } + } + + return 0; + +oops: + if (sck->on_read == client_on_read) + { + /* halt only if clinet_on_read() is the current handler. + * client_on_read() can be chain-called by the overriding handler */ + hio_dev_sck_halt (sck); + } + return 0; /* still return success here. instead call halt() */ +} + +static int client_on_write (hio_dev_sck_t* sck, hio_iolen_t wrlen, void* wrctx, const hio_skad_t* dstaddr) +{ + hio_svc_htts_cli_t* cli = hio_dev_sck_getxtn(sck); + hio_t* hio = sck->hio; + hio_svc_htts_t* htts = cli->htts; + hio_svc_htts_task_t* task = cli->task; + + HIO_ASSERT (hio, cli->l_idx == INVALID_LIDX); + + /* handle event if it's write by self */ + if (wrctx == &htts_svr_wrctx) + { + if (wrlen <= -1) + { + HIO_DEBUG3 (hio, "HTTS(%p) - unable to write to client %p(%d)\n", htts, sck, (int)sck->hnd); + hio_dev_sck_halt (sck); + } + else if (wrlen == 0) + { + /* if connect: is keep-alive, this part may not be called */ + task->task_res_pending_writes--; + } + else + { + HIO_ASSERT (hio, task->task_res_pending_writes > 0); + task->task_res_pending_writes--; + } + } + + return 0; +} + +static void client_on_disconnect (hio_dev_sck_t* sck) +{ + hio_t* hio = sck->hio; + hio_svc_htts_cli_t* cli = hio_dev_sck_getxtn(sck); + hio_svc_htts_t* htts = cli->htts; + hio_svc_htts_task_t* task = cli->task; + + /* called if init_client() has swapped the on_disconnect handler successfully */ + + HIO_ASSERT (hio, cli->sck == sck); + HIO_ASSERT (hio, cli->l_idx == INVALID_LIDX); + + HIO_DEBUG4 (hio, "HTTS(%p) - task(t=%p,c=%p,csck=%p) - handling client socket disconnect\n", htts, task, cli, sck); + + fini_client (cli); + + HIO_DEBUG4 (hio, "HTTS(%p) - task(t=%p,c=%p,csck=%p) - handled client socket disconnect\n", htts, task, cli, sck); + /* Note: after this callback, the actual device pointed to by 'sck' will be freed in the main loop. */ +} + /* ------------------------------------------------------------------------ */ + #define MAX_CLIENT_IDLE 10 static void halt_idle_clients (hio_t* hio, const hio_ntime_t* now, hio_tmrjob_t* job) @@ -547,18 +634,23 @@ void hio_svc_htts_stop (hio_svc_htts_t* htts) if (htts->l.sck[i]) hio_dev_sck_kill (htts->l.sck[i]); } +HIO_DEBUG0 (hio, "11111111111111111\n"); while (!HIO_SVC_HTTS_CLIL_IS_EMPTY(&htts->cli)) { hio_svc_htts_cli_t* cli = HIO_SVC_HTTS_CLIL_FIRST_CLI(&htts->cli); + HIO_ASSERT (hio, cli->sck != HIO_NULL); +HIO_DEBUG1 (hio, "cli-> %p\n", cli->sck); hio_dev_sck_kill (cli->sck); } +HIO_DEBUG0 (hio, "222222222222222222\n"); while (!HIO_SVC_HTTS_TASKL_IS_EMPTY(&htts->task)) { hio_svc_htts_task_t* task = HIO_SVC_HTTS_TASKL_FIRST_TASK(&htts->task); hio_svc_htts_task_kill (task); } +HIO_DEBUG0 (hio, "3333333333333333\n"); HIO_SVCL_UNLINK_SVC (htts); if (htts->server_name && htts->server_name != htts->server_name_buf) hio_freemem (hio, htts->server_name); @@ -684,12 +776,20 @@ hio_svc_htts_task_t* hio_svc_htts_task_make (hio_svc_htts_t* htts, hio_oow_t tas task->task_req_qpath_is_root = (hio_htre_getqpathlen(req) == 1 && hio_htre_getqpath(req)[0] == '/'); task->task_req_method = hio_htre_getqmethodtype(req); task->task_req_version = *hio_htre_getversion(req); + task->task_req_conlen_unlimited = hio_htre_getreqcontentlen(req, &task->task_req_conlen); + task->task_req_flags = req->flags; task->task_req_qmth = (hio_bch_t*)((hio_uint8_t*)task + task_size); task->task_req_qpath = task->task_req_qmth + qmth_len + 1; HIO_MEMCPY (task->task_req_qmth, hio_htre_getqmethodname(req),qmth_len + 1); HIO_MEMCPY (task->task_req_qpath, hio_htre_getqpath(req), qpath_len + 1); + /* originally set to listener_on_write/listener_on_disconnect, but set to + * client_on_write/client_on_disconnect in init_client() when the client socket is accepted */ + HIO_ASSERT (hio, csck->on_read == client_on_read); + HIO_ASSERT (hio, csck->on_write == client_on_write); + HIO_ASSERT (hio, csck->on_disconnect == client_on_disconnect); + HIO_DEBUG2 (hio, "HTTS(%p) - allocated task %p\n", htts, task); return task; } @@ -707,7 +807,188 @@ void hio_svc_htts_task_kill (hio_svc_htts_task_t* task) HIO_DEBUG2 (hio, "HTTS(%p) - destroyed task %p\n", htts, task); } -int hio_svc_htts_task_buildfinalres (hio_svc_htts_task_t* task, int status_code, const hio_bch_t* content_type, const hio_bch_t* content_text, int force_close) +int hio_svc_htts_task_startreshdr (hio_svc_htts_task_t* task, int status_code, const hio_bch_t* status_desc, int chunked) +{ + hio_svc_htts_cli_t* cli = task->task_client; + hio_bch_t dtbuf[64]; + + HIO_ASSERT (task->htts->hio, cli != HIO_NULL); + HIO_ASSERT (task->htts->hio, !task->task_res_started); + HIO_ASSERT (task->htts->hio, !task->task_res_ended); + + hio_svc_htts_fmtgmtime (cli->htts, HIO_NULL, dtbuf, HIO_COUNTOF(dtbuf)); + + if (hio_becs_fmt(cli->sbuf, "HTTP/%d.%d ", task->task_req_version.major, task->task_req_version.minor) == (hio_oow_t)-1) return -1; + if (hio_becs_fcat(cli->sbuf, "%d %hs\r\n", status_code, (status_desc? status_desc: hio_http_status_to_bcstr(status_code))) == (hio_oow_t)-1) return -1; + if (hio_becs_fcat(cli->sbuf, "Server: %hs\r\nDate: %hs\r\n", cli->htts->server_name, dtbuf) == (hio_oow_t)-1) return -1; + + if (chunked && hio_becs_cat(cli->sbuf, "Transfer-Encoding: chunked\r\n") == (hio_oow_t)-1) return -1; + if (hio_becs_cat(cli->sbuf, (task->task_keep_client_alive? "Connection: keep-alive\r\n": "Connection: close\r\n")) == (hio_oow_t)-1) return -1; + + task->task_res_chunked = chunked; + task->task_res_started = 1; + task->task_status_code = status_code; + return 0; +} + +static int is_res_header_acceptable (const hio_bch_t* key) +{ + return hio_comp_bcstr(key, "Status", 1) != 0 && + hio_comp_bcstr(key, "Connection", 1) != 0 && + hio_comp_bcstr(key, "Transfer-Encoding", 1) != 0 && + hio_comp_bcstr(key, "Server", 1) != 0 && + hio_comp_bcstr(key, "Date", 1) != 0; +} + +int hio_svc_htts_task_addreshdrs (hio_svc_htts_task_t* task, const hio_bch_t* key, const hio_htre_hdrval_t* value) +{ + hio_svc_htts_cli_t* cli = task->task_client; + + HIO_ASSERT (task->htts->hio, cli != HIO_NULL); + HIO_ASSERT (task->htts->hio, task->task_res_started); + HIO_ASSERT (task->htts->hio, !task->task_res_ended); + + if (!is_res_header_acceptable(key)) return 0; /* ignore it*/ + while (value) + { + if (hio_becs_fcat(cli->sbuf, "%hs: %hs\r\n", key, value->ptr) == (hio_oow_t)-1) return -1; + value = value->next; + } + + return 0; +} + +int hio_svc_htts_task_addreshdr (hio_svc_htts_task_t* task, const hio_bch_t* key, const hio_bch_t* value) +{ + hio_svc_htts_cli_t* cli = task->task_client; + HIO_ASSERT (task->htts->hio, cli != HIO_NULL); + HIO_ASSERT (task->htts->hio, task->task_res_started); + HIO_ASSERT (task->htts->hio, !task->task_res_ended); + + if (!is_res_header_acceptable(key)) return 0; /* just ignore it*/ + if (hio_becs_fcat(cli->sbuf, "%hs: %hs\r\n", key, value) == (hio_oow_t)-1) return -1; + return 0; +} + +int hio_svc_htts_task_addreshdrfmt (hio_svc_htts_task_t* task, const hio_bch_t* key, const hio_bch_t* vfmt, ...) +{ + hio_svc_htts_cli_t* cli = task->task_client; + va_list ap; + + HIO_ASSERT (task->htts->hio, cli != HIO_NULL); + HIO_ASSERT (task->htts->hio, task->task_res_started); + HIO_ASSERT (task->htts->hio, !task->task_res_ended); + + if (!is_res_header_acceptable(key)) return 0; /* just ignore it*/ + if (hio_becs_fcat(cli->sbuf, "%hs: ", key) == (hio_oow_t)-1) return -1; + va_start (ap, vfmt); + if (hio_becs_vfcat(cli->sbuf, vfmt, ap) == (hio_oow_t)-1) + { + va_end (ap); + return -1; + } + va_end (ap); + if (hio_becs_cat(cli->sbuf, "\r\n") == (hio_oow_t)-1) return -1; + return 0; +} + +static int write_raw_to_client (hio_svc_htts_task_t* task, const void* data, hio_iolen_t dlen) +{ + HIO_ASSERT (task->htts->hio, task->task_client != HIO_NULL); + HIO_ASSERT (task->htts->hio, task->task_csck != HIO_NULL); + +//HIO_DEBUG2 (task->htts->hio, "WR TO C[%.*hs]\n", dlen, data); + + task->task_res_ever_sent = 1; + task->task_res_pending_writes++; + if (hio_dev_sck_write(task->task_csck, data, dlen, &htts_svr_wrctx, HIO_NULL) <= -1) + { + task->task_res_pending_writes--; + return -1; + } + + return 0; +} + +static int write_chunk_to_client (hio_svc_htts_task_t* task, const void* data, hio_iolen_t dlen) +{ + hio_iovec_t iov[3]; + hio_bch_t lbuf[16]; + hio_oow_t llen; + + HIO_ASSERT (task->htts->hio, task->task_client != HIO_NULL); + HIO_ASSERT (task->htts->hio, task->task_csck != HIO_NULL); + + /* hio_fmt_uintmax_to_bcstr() null-terminates the output. only HIO_COUNTOF(lbuf) - 1 + * is enough to hold '\r' and '\n' at the back without '\0'. */ + llen = hio_fmt_uintmax_to_bcstr(lbuf, HIO_COUNTOF(lbuf) - 1, dlen, 16 | HIO_FMT_UINTMAX_UPPERCASE, 0, '\0', HIO_NULL); + lbuf[llen++] = '\r'; + lbuf[llen++] = '\n'; + + iov[0].iov_ptr = lbuf; + iov[0].iov_len = llen; + iov[1].iov_ptr = (void*)data; + iov[1].iov_len = dlen; + iov[2].iov_ptr = "\r\n"; + iov[2].iov_len = 2; + +//HIO_DEBUG2 (task->htts->hio, "WR(CHNK) TO C[%.*hs]\n", dlen, data); + + task->task_res_ever_sent = 1; + task->task_res_pending_writes++; + if (hio_dev_sck_writev(task->task_csck, iov, HIO_COUNTOF(iov), &htts_svr_wrctx, HIO_NULL) <= -1) + { + task->task_res_pending_writes--; + return -1; + } + + return 0; +} + +int hio_svc_htts_task_endreshdr (hio_svc_htts_task_t* task) +{ + hio_svc_htts_cli_t* cli = task->task_client; + HIO_ASSERT (task->htts->hio, cli != HIO_NULL); + HIO_ASSERT (task->htts->hio, task->task_res_started); + HIO_ASSERT (task->htts->hio, !task->task_res_ended); + + if (hio_becs_cat(cli->sbuf, "\r\n") == (hio_oow_t)-1) return -1; + if (task->task_csck && write_raw_to_client(task, HIO_BECS_PTR(cli->sbuf), HIO_BECS_LEN(cli->sbuf)) <= -1) return -1; + +/*(force_close && fcgi_write_to_client(fcgi, HIO_NULL, 0) <= -1))? -1: 0;*/ + return 0; +} + +int hio_svc_htts_task_addresbody (hio_svc_htts_task_t* task, const void* data, hio_iolen_t dlen) +{ + if (!task->task_csck) return 0; + return task->task_res_chunked? write_chunk_to_client(task, data, dlen): write_raw_to_client(task, data, dlen); +} + +int hio_svc_htts_task_endbody (hio_svc_htts_task_t* task) +{ + /* send the last chunk */ + + if (!task->task_res_ended) + { + task->task_res_ended = 1; + + if (!task->task_res_ever_sent) + { + if (hio_svc_htts_task_sendfinalres(task, HIO_HTTP_STATUS_INTERNAL_SERVER_ERROR, HIO_NULL, HIO_NULL, 0) <= -1) return -1; + } + else + { + if (task->task_csck && task->task_res_chunked && write_raw_to_client(task, "0\r\n\r\n", 5) <= -1) return -1; + } + + if (task->task_csck && !task->task_keep_client_alive && write_raw_to_client(task, HIO_NULL, 0) <= -1) return -1; + } + + return 0; +} + +int hio_svc_htts_task_sendfinalres (hio_svc_htts_task_t* task, int status_code, const hio_bch_t* content_type, const hio_bch_t* content_text, int force_close) { hio_svc_htts_t* htts = task->htts; hio_t* hio = htts->hio; @@ -757,179 +1038,65 @@ int hio_svc_htts_task_buildfinalres (hio_svc_htts_task_t* task, int status_code, { if (hio_becs_fcat(cli->sbuf, "Content-Length: %zu\r\n\r\n%hs", content_len, content_text) == (hio_oow_t)-1) return -1; } - + task->task_status_code = status_code; /* remember the status code sent to the client. doesn't matter if it fails to write or not */ -// if (hio_dev_sck_write(task->task_csck, HIO_BECS_PTR(cli->sbuf), HIO_BECS_LEN(cli->sbuf), HIO_NULL, HIO_NULL) <= -1) return -1; -// if (force_close && hio_dev_sck_write(task->task_csck, HIO_NULL, 0, HIO_NULL, HIO_NULL) <= -1) return -1; + + if (write_raw_to_client(task, HIO_BECS_PTR(cli->sbuf), HIO_BECS_LEN(cli->sbuf)) <= -1) return -1; + if (force_close && write_raw_to_client(task, HIO_NULL, 0) <= -1) return -1; return 1; } -int hio_svc_htts_task_startreshdr (hio_svc_htts_task_t* task, int status_code, const hio_bch_t* status_desc, int chunked) +int hio_svc_htts_task_handleexpect100 (hio_svc_htts_task_t* task, int options) { - hio_svc_htts_cli_t* cli = task->task_client; - hio_bch_t dtbuf[64]; - - HIO_ASSERT (task->htts->hio, cli != HIO_NULL); - - hio_svc_htts_fmtgmtime (cli->htts, HIO_NULL, dtbuf, HIO_COUNTOF(dtbuf)); - - if (hio_becs_fmt(cli->sbuf, "HTTP/%d.%d ", task->task_req_version.major, task->task_req_version.minor) == (hio_oow_t)-1) return -1; - if (hio_becs_fcat(cli->sbuf, "%d %hs\r\n", status_code, (status_desc? status_desc: hio_http_status_to_bcstr(status_code))) == (hio_oow_t)-1) return -1; - if (hio_becs_fcat(cli->sbuf, "Server: %hs\r\nDate: %hs\r\n", cli->htts->server_name, dtbuf) == (hio_oow_t)-1) return -1; - - if (chunked && hio_becs_cat(cli->sbuf, "Transfer-Encoding: chunked\r\n") == (hio_oow_t)-1) return -1; - if (hio_becs_cat(cli->sbuf, (task->task_keep_client_alive? "Connection: keep-alive\r\n": "Connection: close\r\n")) == (hio_oow_t)-1) return -1; - - task->task_res_chunked = 1; - return 0; -} - -static int is_res_header_acceptable (const hio_bch_t* key) -{ - return hio_comp_bcstr(key, "Status", 1) != 0 && - hio_comp_bcstr(key, "Connection", 1) != 0 && - hio_comp_bcstr(key, "Transfer-Encoding", 1) != 0 && - hio_comp_bcstr(key, "Server", 1) != 0 && - hio_comp_bcstr(key, "Date", 1) != 0; -} - -int hio_svc_htts_task_addreshdrs (hio_svc_htts_task_t* task, const hio_bch_t* key, const hio_htre_hdrval_t* value) -{ - hio_svc_htts_cli_t* cli = task->task_client; - HIO_ASSERT (task->htts->hio, cli != HIO_NULL); - - if (!is_res_header_acceptable(key)) return 0; /* ignore it*/ - while (value) +#if !defined(TASK_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH) + if (task->task_req_conlen_unlimited) { - if (hio_becs_fcat(cli->sbuf, "%hs: %hs\r\n", key, value->ptr) == (hio_oow_t)-1) return -1; - value = value->next; + /* 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 (hio_svc_htts_task_sendfinalres(task, HIO_HTTP_STATUS_LENGTH_REQUIRED, HIO_NULL, HIO_NULL, 1) <= -1) return -1; } +#endif - return 0; -} - -int hio_svc_htts_task_addreshdr (hio_svc_htts_task_t* task, const hio_bch_t* key, const hio_bch_t* value) -{ - hio_svc_htts_cli_t* cli = task->task_client; - HIO_ASSERT (task->htts->hio, cli != HIO_NULL); - - if (!is_res_header_acceptable(key)) return 0; /* just ignore it*/ - if (hio_becs_fcat(cli->sbuf, "%hs: %hs\r\n", key, value) == (hio_oow_t)-1) return -1; - return 0; -} - -int hio_svc_htts_task_addreshdrfmt (hio_svc_htts_task_t* task, const hio_bch_t* key, const hio_bch_t* vfmt, ...) -{ - hio_svc_htts_cli_t* cli = task->task_client; - va_list ap; - HIO_ASSERT (task->htts->hio, cli != HIO_NULL); - - if (!is_res_header_acceptable(key)) return 0; /* just ignore it*/ - if (hio_becs_fcat(cli->sbuf, "%hs: ", key) == (hio_oow_t)-1) return -1; - va_start (ap, vfmt); - if (hio_becs_vfcat(cli->sbuf, vfmt, ap) == (hio_oow_t)-1) + if (task->task_req_flags & HIO_HTRE_ATTR_EXPECT100) { - va_end (ap); - return -1; - } - va_end (ap); - if (hio_becs_cat(cli->sbuf, "\r\n") == (hio_oow_t)-1) return -1; - return 0; -} - -int hio_svc_htts_task_endreshdr (hio_svc_htts_task_t* task) -{ - hio_svc_htts_cli_t* cli = task->task_client; - HIO_ASSERT (task->htts->hio, cli != HIO_NULL); - - if (hio_becs_cat(cli->sbuf, "\r\n") == (hio_oow_t)-1) return -1; -/* TODO: can i send here??? */ - return 0; -} - -static int write_raw_to_client (hio_svc_htts_task_t* task, const void* data, hio_iolen_t dlen) -{ - HIO_ASSERT (task->htts->hio, task->task_client != HIO_NULL); - HIO_ASSERT (task->htts->hio, task->task_csck != HIO_NULL); - - //task->ever_attempted_to_write_to_client = 1; - -HIO_DEBUG2 (task->htts->hio, "WR TO C[%.*hs]\n", dlen, data); - - //task->num_pending_writes_to_client++; - if (hio_dev_sck_write(task->task_csck, data, dlen, HIO_NULL, HIO_NULL) <= -1) - { - //task->num_pending_writes_to_client--; - return -1; - } - - return 0; -} - -static int write_chunk_to_client (hio_svc_htts_task_t* task, const void* data, hio_iolen_t dlen) -{ - hio_iovec_t iov[3]; - hio_bch_t lbuf[16]; - hio_oow_t llen; - - HIO_ASSERT (task->htts->hio, task->task_client != HIO_NULL); - HIO_ASSERT (task->htts->hio, task->task_csck != HIO_NULL); - - /* hio_fmt_uintmax_to_bcstr() null-terminates the output. only HIO_COUNTOF(lbuf) - 1 - * is enough to hold '\r' and '\n' at the back without '\0'. */ - llen = hio_fmt_uintmax_to_bcstr(lbuf, HIO_COUNTOF(lbuf) - 1, dlen, 16 | HIO_FMT_UINTMAX_UPPERCASE, 0, '\0', HIO_NULL); - lbuf[llen++] = '\r'; - lbuf[llen++] = '\n'; - - iov[0].iov_ptr = lbuf; - iov[0].iov_len = llen; - iov[1].iov_ptr = (void*)data; - iov[1].iov_len = dlen; - iov[2].iov_ptr = "\r\n"; - iov[2].iov_len = 2; - -HIO_DEBUG2 (task->htts->hio, "WR(CHNK) TO C[%.*hs]\n", dlen, data); - - //task->ever_attempted_to_write_to_client = 1; - //task->num_pending_writes_to_client++; - if (hio_dev_sck_writev(task->task_csck, iov, HIO_COUNTOF(iov), HIO_NULL, HIO_NULL) <= -1) - { - //task->num_pending_writes_to_client--; - return -1; - } - - return 0; -} - -int hio_svc_htts_task_addresbody (hio_svc_htts_task_t* task, const void* data, hio_iolen_t dlen) -{ - return task->task_res_chunked? write_chunk_to_client(task, data, dlen): write_raw_to_client(task, data, dlen); -} - -int hio_svc_htts_task_endbody (hio_svc_htts_task_t* task) -{ - /* send the last chunk */ - - if (!task->task_res_ended) - { - task->task_res_ended = 1; - -// if (!task->ever_attempted_to_write_to_client) -// { -// if (fcgi_send_final_status_to_client(fcgi, HIO_HTTP_STATUS_INTERNAL_SERVER_ERROR, 0) <= -1) return -1; -// } -// else + /* TODO: Expect: 100-continue? who should handle this? fcgi? or the http server? */ + /* CAN I LET the fcgi SCRIPT handle this? */ + if (hio_comp_http_version_numbers(&task->task_req_version, 1, 1) >= 0 && + (task->task_req_conlen_unlimited || task->task_req_conlen > 0) && + (task->task_req_method != HIO_HTTP_GET && task->task_req_method != HIO_HTTP_HEAD)) { - if (task->task_res_chunked && write_raw_to_client(task, "0\r\n\r\n", 5) <= -1) return -1; + /* + * 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 length 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. + */ + hio_bch_t msgbuf[64]; + hio_oow_t msglen; + msglen = hio_fmttobcstr(task->htts->hio, msgbuf, HIO_COUNTOF(msgbuf), "HTTP/%d.%d %d %hs\r\n\r\n", task->task_req_version.major, task->task_req_version.minor, HIO_HTTP_STATUS_CONTINUE, hio_http_status_to_bcstr(HIO_HTTP_STATUS_CONTINUE)); + if (task->task_csck && write_raw_to_client(task, msgbuf, msglen) <= -1) return -1; + task->task_res_ever_sent = 0; /* reset this as it's polluted for 100 continue */ } - - if (!task->task_keep_client_alive && write_raw_to_client(task, HIO_NULL, 0) <= -1) return -1; + } + else if (task->task_req_flags & HIO_HTRE_ATTR_EXPECT) + { + /* 417 Expectation Failed */ + hio_svc_htts_task_sendfinalres(task, HIO_HTTP_STATUS_EXPECTATION_FAILED, HIO_NULL, HIO_NULL, 1); + return -1; } return 0; } - /* ----------------------------------------------------------------- */ int hio_svc_htts_doproxy (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* req, const hio_bch_t* upstream) diff --git a/lib/http-thr.c b/lib/http-thr.c index 0804739..39c68d7 100644 --- a/lib/http-thr.c +++ b/lib/http-thr.c @@ -70,12 +70,10 @@ struct thr_task_t hio_htrd_t* peer_htrd; unsigned int over: 4; /* must be large enough to accomodate THR_OVER_ALL */ - unsigned int req_content_length_unlimited: 1; unsigned int ever_attempted_to_write_to_client: 1; unsigned int client_eof_detected: 1; unsigned int client_disconnected: 1; unsigned int client_htrd_recbs_changed: 1; - hio_oow_t req_content_length; /* client request content length */ thr_res_mode_t res_mode_to_cli; hio_dev_sck_on_read_t client_org_on_read; @@ -143,13 +141,9 @@ static int thr_writev_to_client (thr_task_t* thr, hio_iovec_t* iov, hio_iolen_t return 0; } -static int thr_send_final_status_to_client (thr_task_t* thr, int status_code, int force_close) +static HIO_INLINE int thr_send_final_status_to_client (thr_task_t* thr, int status_code, int force_close) { - hio_svc_htts_cli_t* cli = thr->task_client; - if (!cli) return 0; /* client disconnected probably */ - if (hio_svc_htts_task_buildfinalres(thr, status_code, HIO_NULL, HIO_NULL, force_close) <= -1) return -1; - return (thr_write_to_client(thr, HIO_BECS_PTR(cli->sbuf), HIO_BECS_LEN(cli->sbuf)) <= -1 || - (force_close && thr_write_to_client(thr, HIO_NULL, 0) <= -1))? -1: 0; + return hio_svc_htts_task_sendfinalres(thr, status_code, HIO_NULL, HIO_NULL, force_close); } static int thr_write_last_chunk_to_client (thr_task_t* thr) @@ -428,51 +422,6 @@ static int peer_capture_response_header (hio_htre_t* req, const hio_bch_t* key, static int thr_peer_htrd_peek (hio_htrd_t* htrd, hio_htre_t* req) { -#if 0 - thr_peer_xtn_t* peer_xtn = hio_htrd_getxtn(htrd); - thr_task_t* thr = peer_xtn->task; - hio_svc_htts_cli_t* cli = thr->task_client; - hio_bch_t dtbuf[64]; - int status_code = HIO_HTTP_STATUS_OK; - const hio_bch_t* status_desc = HIO_NULL; - - if (req->attr.content_length) - { -// TOOD: remove content_length if content_length is negative or not numeric. - thr->res_mode_to_cli = THR_RES_MODE_LENGTH; - } - - if (req->attr.status) hio_parse_http_status_header_value(req->attr.status, &status_code, &status_desc); - - hio_svc_htts_fmtgmtime (cli->htts, HIO_NULL, dtbuf, HIO_COUNTOF(dtbuf)); - - if (hio_becs_fmt(cli->sbuf, "HTTP/%d.%d %d %hs\r\nServer: %hs\r\nDate: %hs\r\n", - thr->task_req_version.major, thr->task_req_version.minor, - status_code, (status_desc? status_desc: hio_http_status_to_bcstr(status_code)), - cli->htts->server_name, dtbuf) == (hio_oow_t)-1) return -1; - - if (hio_htre_walkheaders(req, thr_peer_capture_response_header, cli) <= -1) return -1; - - switch (thr->res_mode_to_cli) - { - case THR_RES_MODE_CHUNKED: - if (hio_becs_cat(cli->sbuf, "Transfer-Encoding: chunked\r\n") == (hio_oow_t)-1) return -1; - /*if (hio_becs_cat(cli->sbuf, "Connection: keep-alive\r\n") == (hio_oow_t)-1) return -1;*/ - break; - - case THR_RES_MODE_CLOSE: - if (hio_becs_cat(cli->sbuf, "Connection: close\r\n") == (hio_oow_t)-1) return -1; - break; - - case THR_RES_MODE_LENGTH: - if (hio_becs_cat(cli->sbuf, (thr->task_keep_client_alive? "Connection: keep-alive\r\n": "Connection: close\r\n")) == (hio_oow_t)-1) return -1; - } - - if (hio_becs_cat(cli->sbuf, "\r\n") == (hio_oow_t)-1) return -1; - - thr->task_status_code = status_code; - return thr_write_to_client(thr, HIO_BECS_PTR(cli->sbuf), HIO_BECS_LEN(cli->sbuf)); -#else thr_peer_xtn_t* peer = hio_htrd_getxtn(htrd); thr_task_t* thr = peer->task; hio_svc_htts_cli_t* cli = thr->task_client; @@ -496,9 +445,7 @@ static int thr_peer_htrd_peek (hio_htrd_t* htrd, hio_htre_t* req) hio_htre_walkheaders(req, peer_capture_response_header, thr) <= -1 || hio_svc_htts_task_endreshdr(thr) <= -1) return -1; - thr->task_status_code = status_code; - return thr_write_to_client(thr, HIO_BECS_PTR(cli->sbuf), HIO_BECS_LEN(cli->sbuf)); -#endif + return 0; } static int thr_peer_htrd_poke (hio_htrd_t* htrd, hio_htre_t* req) @@ -899,10 +846,6 @@ int hio_svc_htts_dothr (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* r thr->on_kill = on_kill; thr->options = options; - /*thr->num_pending_writes_to_client = 0; - thr->num_pending_writes_to_peer = 0;*/ - thr->req_content_length_unlimited = hio_htre_getreqcontentlen(req, &thr->req_content_length); - thr->client_org_on_read = csck->on_read; thr->client_org_on_write = csck->on_write; thr->client_org_on_disconnect = csck->on_disconnect; @@ -939,7 +882,7 @@ int hio_svc_htts_dothr (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* r HIO_SVC_HTTS_TASK_REF (thr, peer_xtn->task); #if !defined(THR_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH) - if (thr->req_content_length_unlimited) + if (thr->task_req_conlen_unlimited) { /* Transfer-Encoding is chunked. no content-length is known in advance. */ @@ -955,9 +898,8 @@ int hio_svc_htts_dothr (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* r { /* TODO: Expect: 100-continue? who should handle this? thr? or the http server? */ /* CAN I LET the thr SCRIPT handle this? */ - if (!(options & HIO_SVC_HTTS_THR_NO_100_CONTINUE) && - hio_comp_http_version_numbers(&req->version, 1, 1) >= 0 && - (thr->req_content_length_unlimited || thr->req_content_length > 0)) + if (hio_comp_http_version_numbers(&req->version, 1, 1) >= 0 && + (thr->task_req_conlen_unlimited || thr->task_req_conlen > 0)) { /* * Don't send 100 Continue if http verions is lower than 1.1 @@ -988,9 +930,9 @@ int hio_svc_htts_dothr (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* r } #if defined(THR_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH) - have_content = thr->req_content_length > 0 || thr->req_content_length_unlimited; + have_content = thr->task_req_conlen > 0 || thr->task_req_conlen_unlimited; #else - have_content = thr->req_content_length > 0; + have_content = thr->task_req_conlen > 0; #endif if (have_content) { diff --git a/lib/http-txt.c b/lib/http-txt.c index 42a0e38..d99b67c 100644 --- a/lib/http-txt.c +++ b/lib/http-txt.c @@ -39,11 +39,9 @@ struct txt_t hio_oow_t num_pending_writes_to_client; unsigned int over: 2; /* must be large enough to accomodate TXT_OVER_ALL */ - unsigned int req_content_length_unlimited: 1; unsigned int client_eof_detected: 1; unsigned int client_disconnected: 1; unsigned int client_htrd_recbs_changed: 1; - hio_oow_t req_content_length; /* client request content length */ hio_dev_sck_on_read_t client_org_on_read; hio_dev_sck_on_write_t client_org_on_write; @@ -72,29 +70,9 @@ static int txt_write_to_client (txt_t* txt, const void* data, hio_iolen_t dlen) return 0; } -#if 0 -static int txt_writev_to_client (txt_t* txt, hio_iovec_t* iov, hio_iolen_t iovcnt) -{ - if (txt->task_csck) - { - txt->num_pending_writes_to_client++; - if (hio_dev_sck_writev(txt->task_csck, iov, iovcnt, HIO_NULL, HIO_NULL) <= -1) - { - txt->num_pending_writes_to_client--; - return -1; - } - } - return 0; -} -#endif - static int txt_send_final_status_to_client (txt_t* txt, int status_code, const hio_bch_t* content_type, const hio_bch_t* content_text, int force_close) { - hio_svc_htts_cli_t* cli = txt->task_client; - if (!cli) return 0; /* client disconnected probably */ - if (hio_svc_htts_task_buildfinalres(txt, status_code, content_type, content_text, force_close) <= -1) return -1; - return (txt_write_to_client(txt, HIO_BECS_PTR(cli->sbuf), HIO_BECS_LEN(cli->sbuf)) <= -1 || - (force_close && txt_write_to_client(txt, HIO_NULL, 0) <= -1))? -1: 0; + return hio_svc_htts_task_sendfinalres(txt, status_code, content_type, content_text, force_close); } static HIO_INLINE void txt_mark_over (txt_t* txt, int over_bits) @@ -311,8 +289,6 @@ int hio_svc_htts_dotxt (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* r txt->on_kill = on_kill; txt->options = options; - /*txt->num_pending_writes_to_client = 0;*/ - txt->req_content_length_unlimited = hio_htre_getreqcontentlen(req, &txt->req_content_length); txt->client_org_on_read = csck->on_read; txt->client_org_on_write = csck->on_write; @@ -335,7 +311,7 @@ int hio_svc_htts_dotxt (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* r goto oops; } - if (txt->req_content_length_unlimited || txt->req_content_length > 0) + if (txt->task_req_conlen_unlimited || txt->task_req_conlen > 0) { /* change the callbacks to subscribe to contents to be uploaded */ txt->client_htrd_org_recbs = *hio_htrd_getrecbs(txt->task_client->htrd); diff --git a/lib/http.c b/lib/http.c index f4bf6f2..7af8c84 100644 --- a/lib/http.c +++ b/lib/http.c @@ -90,11 +90,10 @@ const hio_bch_t* hio_http_status_to_bcstr (int code) case 431: msg = "Request Header Fields Too Large"; break; case HIO_HTTP_STATUS_INTERNAL_SERVER_ERROR: msg = "Internal Server Error"; break; - case 501: msg = "Not Implemented"; break; - case 502: msg = "Bad Gateway"; break; - case 503: msg = "Service Unavailable"; break; - case 504: msg = "Gateway Timeout"; break; - case 505: msg = "HTTP Version Not Supported"; break; + case HIO_HTTP_STATUS_NOT_IMPLEMENTED: msg = "Not Implemented"; break; + case HIO_HTTP_STATUS_BAD_GATEWAY: msg = "Bad Gateway"; break; + case HIO_HTTP_STATUS_SERVICE_UNAVAILABLE: msg = "Service Unavailable"; break; + case HIO_HTTP_STATUS_VERSION_NOT_SUPPORTED: msg = "HTTP Version Not Supported"; break; default: msg = "Unknown"; break; }