From 855e0fb88ed66420f85d3b41e96d9b38dd53054c Mon Sep 17 00:00:00 2001 From: hyung-hwan Date: Mon, 10 Oct 2022 01:41:07 +0900 Subject: [PATCH] enabled the http file handler to accept POST, PUT, DELETE --- Makefile.am | 2 +- Makefile.in | 4 +- bin/t01.c | 10 +- bin/t06.c | 20 ++-- bin/webs.c | 11 +- lib/hio-htre.h | 30 +++++- lib/hio-http.h | 39 ++++++- lib/htrd.c | 2 +- lib/http-cgi.c | 23 ++-- lib/http-fcgi.c | 11 +- lib/http-file.c | 274 +++++++++++++++++++++++++----------------------- lib/http-thr.c | 27 +++-- lib/http-txt.c | 12 ++- lib/http.c | 46 ++++---- 14 files changed, 294 insertions(+), 217 deletions(-) diff --git a/Makefile.am b/Makefile.am index 36049a6..8fd6191 100644 --- a/Makefile.am +++ b/Makefile.am @@ -19,7 +19,7 @@ rpm: dist-gzip cp @PACKAGE_NAME@-@PACKAGE_VERSION@.tar.gz "@abs_builddir@/pkgs/RPM/SOURCES" rpmbuild --define "_topdir @abs_builddir@/pkgs/RPM" -ba @abs_builddir@/pkgs/hio.spec --target=@build_cpu@ -docker: +docker: all mkdir -p data rm -rf data/* tar -cvf hio-webs.tar bin/hio-webs data diff --git a/Makefile.in b/Makefile.in index d3a3a10..3fb84fc 100644 --- a/Makefile.in +++ b/Makefile.in @@ -165,7 +165,7 @@ am__DIST_COMMON = $(srcdir)/Dockerfile.in $(srcdir)/Makefile.in \ $(top_srcdir)/ac/config.guess $(top_srcdir)/ac/config.sub \ $(top_srcdir)/ac/install-sh $(top_srcdir)/ac/ltmain.sh \ $(top_srcdir)/ac/missing $(top_srcdir)/pkgs/hio.spec.in \ - ac/ar-lib ac/compile ac/config.guess ac/config.sub \ + ac/ar-lib ac/compile ac/config.guess ac/config.sub ac/depcomp \ ac/install-sh ac/ltmain.sh ac/missing DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) distdir = $(PACKAGE)-$(VERSION) @@ -846,7 +846,7 @@ rpm: dist-gzip cp @PACKAGE_NAME@-@PACKAGE_VERSION@.tar.gz "@abs_builddir@/pkgs/RPM/SOURCES" rpmbuild --define "_topdir @abs_builddir@/pkgs/RPM" -ba @abs_builddir@/pkgs/hio.spec --target=@build_cpu@ -docker: +docker: all mkdir -p data rm -rf data/* tar -cvf hio-webs.tar bin/hio-webs data diff --git a/bin/t01.c b/bin/t01.c index 48142f6..4c406ea 100644 --- a/bin/t01.c +++ b/bin/t01.c @@ -961,7 +961,7 @@ if (hio_htre_getcontentlen(req) > 0) hio_htre_discardcontent (req); /* 411 Length Required - can't keep alive. Force disconnect */ req->flags &= ~HIO_HTRE_ATTR_KEEPALIVE; /* to cause sendstatus() to close */ - if (hio_svc_htts_sendstatus(htts, csck, req, 411, HIO_NULL) <= -1) goto oops; + if (hio_svc_htts_sendstatus(htts, csck, req, HIO_HTTP_STATUS_LENGTH_REQUIRED, HIO_NULL) <= -1) goto oops; } else @@ -970,13 +970,13 @@ 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); + x = hio_svc_htts_dothr(htts, csck, req, on_htts_thr_request, HIO_NULL, 0); else if (hio_comp_bcstr_limited(qpath, "/txt/", 5, 1) == 0) - x = hio_svc_htts_dotxt(htts, csck, req, 200, "text/plain", qpath); + x = hio_svc_htts_dotxt(htts, csck, req, HIO_HTTP_STATUS_OK, "text/plain", qpath, 0); else if (hio_comp_bcstr_limited(qpath, "/cgi/", 5, 1) == 0) - x = hio_svc_htts_docgi(htts, csck, req, "", qpath + 4); + x = hio_svc_htts_docgi(htts, csck, req, "", qpath + 4, 0); else - x = hio_svc_htts_dofile(htts, csck, req, "", qpath, "text/plain"); + x = hio_svc_htts_dofile(htts, csck, req, "", qpath, "text/plain", 0); if (x <= -1) goto oops; } #if 0 diff --git a/bin/t06.c b/bin/t06.c index e630163..05de469 100644 --- a/bin/t06.c +++ b/bin/t06.c @@ -45,7 +45,7 @@ static void on_htts_thr_request (hio_t* hio, hio_dev_thr_iopair_t* iop, hio_svc_ return; } - fprintf (fp, "Status: 200\r\n"); + fprintf (fp, "Status: %d\r\n", HIO_HTTP_STATUS_OK); fprintf (fp, "Content-Type: text/html\r\n\r\n"); fprintf (fp, "request path = %s\n", tfi->req_path); @@ -82,13 +82,13 @@ static void on_htts_thr2_request (hio_t* hio, hio_dev_thr_iopair_t* iop, hio_svc sf = fopen(&tfi->req_path[5], "r"); if (!sf) { - fprintf (fp, "Status: 404\r\n\r\n"); + fprintf (fp, "Status: %d\r\n\r\n", HIO_HTTP_STATUS_NOT_FOUND); } else { char buf[4096]; - fprintf (fp, "Status: 200\r\n"); + fprintf (fp, "Status: %d\r\n", HIO_HTTP_STATUS_OK); fprintf (fp, "Content-Type: text/html\r\n\r\n"); while (!feof(sf) && !ferror(sf)) @@ -188,7 +188,7 @@ if (hio_htre_getcontentlen(req) > 0) hio_htre_discardcontent (req); /* 411 Length Required - can't keep alive. Force disconnect */ req->flags &= ~HIO_HTRE_ATTR_KEEPALIVE; /* to cause sendstatus() to close */ - if (hio_svc_htts_sendstatus(htts, csck, req, 411, HIO_NULL) <= -1) goto oops; + if (hio_svc_htts_sendstatus(htts, csck, req, HIO_HTTP_STATUS_LENGTH_REQUIRED, HIO_NULL) <= -1) goto oops; } else @@ -197,21 +197,21 @@ 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); + x = hio_svc_htts_dothr(htts, csck, req, on_htts_thr_request, HIO_NULL, HIO_SVC_HTTS_THR_NO_100_CONTINUE); 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); + x = hio_svc_htts_dothr(htts, csck, req, on_htts_thr2_request, HIO_NULL, HIO_SVC_HTTS_THR_NO_100_CONTINUE); else if (hio_comp_bcstr_limited(qpath, "/txt/", 5, 1) == 0) - x = hio_svc_htts_dotxt(htts, csck, req, 200, "text/plain", qpath); + x = hio_svc_htts_dotxt(htts, csck, req, HIO_HTTP_STATUS_OK, "text/plain", qpath, 0); else if (hio_comp_bcstr_limited(qpath, "/cgi/", 5, 1) == 0) - x = hio_svc_htts_docgi(htts, csck, req, "", qpath + 4); + x = hio_svc_htts_docgi(htts, csck, req, "", qpath + 4, 0); else if (hio_comp_bcstr_limited(qpath, "/fcgi/", 5, 1) == 0) { hio_skad_t fcgis_addr; hio_bcstrtoskad(hio, "127.0.0.1:9000", &fcgis_addr); - x = hio_svc_htts_dofcgi(htts, csck, req, &fcgis_addr); + x = hio_svc_htts_dofcgi(htts, csck, req, &fcgis_addr, 0); } else - x = hio_svc_htts_dofile(htts, csck, req, "", qpath, "text/plain"); + x = hio_svc_htts_dofile(htts, csck, req, "", qpath, "text/plain", 0); if (x <= -1) goto oops; } #if 0 diff --git a/bin/webs.c b/bin/webs.c index dd8bc75..4c46fd2 100644 --- a/bin/webs.c +++ b/bin/webs.c @@ -21,21 +21,22 @@ static int process_http_request (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_ mth = hio_htre_getqmethodtype(req); qpath = hio_htre_getqpath(req); - if (mth == HIO_HTTP_GET || mth == HIO_HTTP_POST) + +// if (mth == HIO_HTTP_GET || mth == HIO_HTTP_POST) { /* TODO: proper mime-type */ const hio_bch_t* dot; hio_bch_t mt[128]; - dot = hio_rfind_bchar_in_bcstr(qpath, '.'); hio_fmttobcstr (hio, mt, HIO_COUNTOF(mt), "text/%hs", ((dot && dot[1] != '\0')? &dot[1]: "plain")); /* TODO: error check */ - if (hio_svc_htts_dofile(htts, csck, req, ext->docroot, qpath, mt) <= -1) goto oops; + if (hio_svc_htts_dofile(htts, csck, req, ext->docroot, qpath, mt, 0) <= -1) goto oops; } +#if 0 else { - if (hio_svc_htts_dotxt(htts, csck, req, 403, "text/plain", hio_http_status_to_bcstr(403)) <= -1) goto oops; + if (hio_svc_htts_dotxt(htts, csck, req, HIO_HTTP_STATUS_FORBIDDEN, "text/plain", hio_http_status_to_bcstr(403), 0) <= -1) goto oops; } - +#endif return 0; oops: diff --git a/lib/hio-htre.h b/lib/hio-htre.h index 7aeb04a..c97b32d 100644 --- a/lib/hio-htre.h +++ b/lib/hio-htre.h @@ -92,9 +92,37 @@ enum hio_http_method_t HIO_HTTP_UNSUBSCRIBE, #endif }; - typedef enum hio_http_method_t hio_http_method_t; +enum hio_http_status_t +{ + HIO_HTTP_STATUS_CONTINUE = 100, + HIO_HTTP_STATUS_SWITCH_PROTOCOL = 101, + + HIO_HTTP_STATUS_OK = 200, + HIO_HTTP_STATUS_CREATED = 201, + HIO_HTTP_STATUS_ACCEPTED = 202, + HIO_HTTP_STATUS_NON_AUTHORITATIVE = 203, + HIO_HTTP_STATUS_NO_CONTENT = 204, + HIO_HTTP_STATUS_RESET_CONTENT = 205, + HIO_HTTP_STATUS_PARTIAL_CONTENT = 206, + + HIO_HTTP_STATUS_MOVED_PERMANENTLY = 301, + HIO_HTTP_STATUS_NOT_MODIFIED = 304, + + HIO_HTTP_STATUS_BAD_REQUEST = 400, + HIO_HTTP_STATUS_FORBIDDEN = 403, + HIO_HTTP_STATUS_NOT_FOUND = 404, + HIO_HTTP_STATUS_METHOD_NOT_ALLOWED = 405, + HIO_HTTP_STATUS_LENGTH_REQUIRED = 411, + HIO_HTTP_STATUS_RANGE_NOT_SATISFIABLE = 416, + HIO_HTTP_STATUS_EXPECTATION_FAILED = 417, + + HIO_HTTP_STATUS_INTERNAL_SERVER_ERROR = 500, + +}; +typedef enum hio_http_status_t hio_http_status_t; + /* * You should not manipulate an object of the #hio_htre_t * type directly since it's complex. Use #hio_htrd_t to diff --git a/lib/hio-http.h b/lib/hio-http.h index e7abb93..3187817 100644 --- a/lib/hio-http.h +++ b/lib/hio-http.h @@ -137,6 +137,30 @@ typedef void (*hio_svc_htts_thr_func_t) ( /* -------------------------------------------------------------- */ + +enum hio_svc_htts_cgi_option_t +{ + HIO_SVC_HTTS_CGI_NO_100_CONTINUE = (1 << 0) +}; + +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) +}; + +#if 0 +enum hio_svc_htts_txt_option_t +{ + /* no option yet */ +}; +#endif + #if defined(__cplusplus) extern "C" { #endif @@ -307,14 +331,16 @@ HIO_EXPORT int hio_svc_htts_docgi ( hio_dev_sck_t* csck, hio_htre_t* req, const hio_bch_t* docroot, - const hio_bch_t* script + const hio_bch_t* script, + int options ); HIO_EXPORT int hio_svc_htts_dofcgi ( hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* req, - const hio_skad_t* fcgis_addr + const hio_skad_t* fcgis_addr, + int options ); HIO_EXPORT int hio_svc_htts_dofile ( @@ -323,7 +349,8 @@ HIO_EXPORT int hio_svc_htts_dofile ( hio_htre_t* req, const hio_bch_t* docroot, const hio_bch_t* filepath, - const hio_bch_t* mime_type + const hio_bch_t* mime_type, + int options ); HIO_EXPORT int hio_svc_htts_dothr ( @@ -331,7 +358,8 @@ HIO_EXPORT int hio_svc_htts_dothr ( hio_dev_sck_t* csck, hio_htre_t* req, hio_svc_htts_thr_func_t func, - void* ctx + void* ctx, + int options ); HIO_EXPORT int hio_svc_htts_dotxt ( @@ -340,7 +368,8 @@ HIO_EXPORT int hio_svc_htts_dotxt ( hio_htre_t* req, int status_code, const hio_bch_t* content_type, - const hio_bch_t* content_text + const hio_bch_t* content_text, + int options ); HIO_EXPORT hio_svc_htts_rsrc_t* hio_svc_htts_rsrc_make ( diff --git a/lib/htrd.c b/lib/htrd.c index a47fb39..67ce6e7 100644 --- a/lib/htrd.c +++ b/lib/htrd.c @@ -554,7 +554,7 @@ static int capture_expect (hio_htrd_t* htrd, hio_htb_pair_t* pair) val = HIO_HTB_VPTR(pair); while (val) - { + { /* Expect: 100-continue is included */ if (hio_comp_bcstr(val->ptr, "100-continue", 1) == 0) htrd->re.flags |= HIO_HTRE_ATTR_EXPECT100; val = val->next; diff --git a/lib/http-cgi.c b/lib/http-cgi.c index 22cc2da..e0f08df 100644 --- a/lib/http-cgi.c +++ b/lib/http-cgi.c @@ -58,6 +58,7 @@ struct cgi_t { HIO_SVC_HTTS_RSRC_HEADER; + int options; hio_oow_t num_pending_writes_to_client; hio_oow_t num_pending_writes_to_peer; hio_dev_pro_t* peer; @@ -69,6 +70,7 @@ struct cgi_t unsigned int keep_alive: 1; 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 */ @@ -160,7 +162,7 @@ static int cgi_write_last_chunk_to_client (cgi_t* cgi) { if (!cgi->ever_attempted_to_write_to_client) { - if (cgi_send_final_status_to_client(cgi, 500, 0) <= -1) return -1; + if (cgi_send_final_status_to_client(cgi, HIO_HTTP_STATUS_INTERNAL_SERVER_ERROR, 0) <= -1) return -1; } else { @@ -226,7 +228,7 @@ static HIO_INLINE void cgi_mark_over (cgi_t* cgi, int over_bits) hio_dev_pro_halt (cgi->peer); } - if (cgi->keep_alive) + if (cgi->keep_alive && !cgi->client_eof_detected) { /* how to arrange to delete this cgi object and put the socket back to the normal waiting state??? */ HIO_ASSERT (cgi->htts->hio, cgi->client->rsrc == (hio_svc_htts_rsrc_t*)cgi); @@ -379,6 +381,7 @@ static int peer_on_read (hio_dev_pro_t* pro, hio_dev_pro_sid_t sid, const void* if (dlen == 0) { HIO_DEBUG3 (hio, "HTTS(%p) - EOF from peer %p(pid=%u)\n", cgi->client->htts, pro, (unsigned int)pro->child_pid); + cgi->client_eof_detected = 1; if (!(cgi->over & CGI_OVER_READ_FROM_PEER)) { @@ -404,7 +407,7 @@ static int peer_on_read (hio_dev_pro_t* pro, hio_dev_pro_sid_t sid, const void* if (!cgi->ever_attempted_to_write_to_client && !(cgi->over & CGI_OVER_WRITE_TO_CLIENT)) { - cgi_send_final_status_to_client (cgi, 500, 1); /* don't care about error because it jumps to oops below anyway */ + cgi_send_final_status_to_client (cgi, HIO_HTTP_STATUS_INTERNAL_SERVER_ERROR, 1); /* don't care about error because it jumps to oops below anyway */ } goto oops; @@ -921,7 +924,7 @@ static int peer_on_fork (hio_dev_pro_t* pro, void* fork_ctx) return 0; } -int hio_svc_htts_docgi (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* req, const hio_bch_t* docroot, const hio_bch_t* script) +int hio_svc_htts_docgi (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* req, const hio_bch_t* docroot, const hio_bch_t* script, int options) { hio_t* hio = htts->hio; hio_svc_htts_cli_t* cli = hio_dev_sck_getxtn(csck); @@ -953,6 +956,7 @@ int hio_svc_htts_docgi (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* r cgi = (cgi_t*)hio_svc_htts_rsrc_make(htts, HIO_SIZEOF(*cgi), cgi_on_kill); if (HIO_UNLIKELY(!cgi)) goto oops; + cgi->options = options; cgi->client = cli; /*cgi->num_pending_writes_to_client = 0; cgi->num_pending_writes_to_peer = 0;*/ @@ -973,7 +977,7 @@ int hio_svc_htts_docgi (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* r if (access(mi.cmd, X_OK) == -1) { - cgi_send_final_status_to_client (cgi, 403, 1); /* 403 Forbidden */ + cgi_send_final_status_to_client (cgi, HIO_HTTP_STATUS_FORBIDDEN, 1); /* 403 Forbidden */ goto oops; /* TODO: must not go to oops. just destroy the cgi and finalize the request .. */ } @@ -999,7 +1003,7 @@ int hio_svc_htts_docgi (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* r * 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_send_final_status_to_client(cgi, 411, 1) <= -1) goto oops; + if (cgi_send_final_status_to_client(cgi, HIO_HTTP_STATUS_LENGTH_REQUIRED, 1) <= -1) goto oops; } #endif @@ -1007,7 +1011,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 (hio_comp_http_version_numbers(&req->version, 1, 1) >= 0 && + 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)) { /* @@ -1026,7 +1031,7 @@ int hio_svc_htts_docgi (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* r hio_bch_t msgbuf[64]; hio_oow_t msglen; - msglen = hio_fmttobcstr(hio, msgbuf, HIO_COUNTOF(msgbuf), "HTTP/%d.%d 100 Continue\r\n\r\n", cgi->req_version.major, cgi->req_version.minor); + msglen = hio_fmttobcstr(hio, msgbuf, HIO_COUNTOF(msgbuf), "HTTP/%d.%d %d %hs\r\n\r\n", cgi->req_version.major, cgi->req_version.minor, HIO_HTTP_STATUS_CONTINUE, hio_http_status_to_bcstr(HIO_HTTP_STATUS_CONTINUE)); if (cgi_write_to_client(cgi, msgbuf, msglen) <= -1) goto oops; cgi->ever_attempted_to_write_to_client = 0; /* reset this as it's polluted for 100 continue */ } @@ -1034,7 +1039,7 @@ int hio_svc_htts_docgi (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* r else if (req->flags & HIO_HTRE_ATTR_EXPECT) { /* 417 Expectation Failed */ - cgi_send_final_status_to_client(cgi, 417, 1); + cgi_send_final_status_to_client(cgi, HIO_HTTP_STATUS_EXPECTATION_FAILED, 1); goto oops; } diff --git a/lib/http-fcgi.c b/lib/http-fcgi.c index a0f0f21..5a691c6 100644 --- a/lib/http-fcgi.c +++ b/lib/http-fcgi.c @@ -497,7 +497,7 @@ static void fcgi_on_kill (fcgi_t* fcgi) #endif } -int hio_svc_htts_dofcgi (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* req, const hio_skad_t* fcgis_addr) +int hio_svc_htts_dofcgi (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* req, const hio_skad_t* fcgis_addr, int options) { hio_t* hio = htts->hio; hio_svc_htts_cli_t* cli = hio_dev_sck_getxtn(csck); @@ -565,7 +565,7 @@ int hio_svc_htts_dofcgi (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* * 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_send_final_status_to_client(cgi, 411, 1) <= -1) goto oops; + if (cgi_send_final_status_to_client(cgi, HIO_HTTP_STATUS_LENGTH_REQUIRED, 1) <= -1) goto oops; } #endif @@ -573,7 +573,8 @@ int hio_svc_htts_dofcgi (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* { /* TODO: Expect: 100-continue? who should handle this? cgi? or the http server? */ /* CAN I LET the cgi SCRIPT handle this? */ - if (hio_comp_http_version_numbers(&req->version, 1, 1) >= 0 && + if (!(options & HIO_SVC_HTTS_CGI_NO_100_CONTINUE) && + hio_comp_http_version_numbers(&req->version, 1, 1) >= 0 && (fcgi->req_content_length_unlimited || fcgi->req_content_length > 0)) { /* @@ -592,7 +593,7 @@ int hio_svc_htts_dofcgi (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* hio_bch_t msgbuf[64]; hio_oow_t msglen; - msglen = hio_fmttobcstr(hio, msgbuf, HIO_COUNTOF(msgbuf), "HTTP/%d.%d 100 Continue\r\n\r\n", fcgi->req_version.major, fcgi->req_version.minor); + msglen = hio_fmttobcstr(hio, msgbuf, HIO_COUNTOF(msgbuf), "HTTP/%d.%d %d %hs\r\n\r\n", fcgi->req_version.major, fcgi->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) goto oops; fcgi->ever_attempted_to_write_to_client = 0; /* reset this as it's polluted for 100 continue */ } @@ -600,7 +601,7 @@ int hio_svc_htts_dofcgi (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* else if (req->flags & HIO_HTRE_ATTR_EXPECT) { /* 417 Expectation Failed */ - fcgi_send_final_status_to_client(fcgi, 417, 1); + fcgi_send_final_status_to_client(fcgi, HIO_HTTP_STATUS_EXPECTATION_FAILED, 1); goto oops; } diff --git a/lib/http-file.c b/lib/http-file.c index 4841176..3ca2b0e 100644 --- a/lib/http-file.c +++ b/lib/http-file.c @@ -43,8 +43,6 @@ enum file_res_mode_t }; typedef enum file_res_mode_t file_res_mode_t; -#define FILE_PENDING_IO_THRESHOLD 5 - #define FILE_OVER_READ_FROM_CLIENT (1 << 0) #define FILE_OVER_READ_FROM_PEER (1 << 1) #define FILE_OVER_WRITE_TO_CLIENT (1 << 2) @@ -55,6 +53,7 @@ struct file_t { HIO_SVC_HTTS_RSRC_HEADER; + int options; hio_oow_t num_pending_writes_to_client; hio_oow_t num_pending_writes_to_peer; int sendfile_ok; @@ -98,7 +97,7 @@ static void file_halt_participating_devices (file_t* file) HIO_ASSERT (file->client->htts->hio, file->client != HIO_NULL); HIO_ASSERT (file->client->htts->hio, file->client->sck != HIO_NULL); - HIO_DEBUG3 (file->client->htts->hio, "HTTS(%p) - file(c=%d,p=%d) Halting participating devices in file state\n", file->client->htts, (int)file->client->sck->hnd, (int)file->peer); + HIO_DEBUG3 (file->client->htts->hio, "HTTS(%p) - file(c=%d,p=%d) Halting participating devices\n", file->client->htts, (int)file->client->sck->hnd, (int)file->peer); /* only the client socket device. * the peer side is just a file descriptor - no hio-managed device */ @@ -116,11 +115,6 @@ static int file_write_to_client (file_t* file, const void* data, hio_iolen_t dle return -1; } - if (file->num_pending_writes_to_client > FILE_PENDING_IO_THRESHOLD) - { - /* STOP READING */ - /*if (hio_dev_pro_read(file->peer, HIO_DEV_PRO_OUT, 0) <= -1) return -1;*/ - } return 0; } @@ -135,11 +129,6 @@ static int file_sendfile_to_client (file_t* file, hio_foff_t foff, hio_iolen_t l return -1; } - if (file->num_pending_writes_to_client > FILE_PENDING_IO_THRESHOLD) - { - /* STOP READING */ - /*if (hio_dev_pro_read(file->peer, HIO_DEV_PRO_OUT, 0) <= -1) return -1;*/ - } return 0; } @@ -248,10 +237,20 @@ static int file_write_to_peer (file_t* file, const void* data, hio_iolen_t dlen) } else { + hio_iolen_t pos, rem, n; if (file->req_method == HIO_HTTP_GET) return 0; + if (file->peer <= -1) return 0; /* peer open proabably failed */ - HIO_ASSERT (hio, file->peer >= 0); - return write(file->peer, data, dlen) <= -1? -1: 0; + /* TODO: async file io -> create a file device?? */ + pos = 0; + rem = dlen; + while (rem > 0) + { + n = write(file->peer, &((const hio_uint8_t*)data)[pos], rem); + if (n <= -1) return -1; + rem -= n; + pos += n; + } } return 0; @@ -334,9 +333,10 @@ static int file_client_on_read (hio_dev_sck_t* sck, const void* buf, hio_iolen_t goto oops; } - if (!file->peer) + if (file->peer <= -1) { - /* the peer is gone */ + /* 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->client->htts, (int)sck->hnd, file->peer); goto oops; /* do what? just return 0? */ } @@ -344,12 +344,14 @@ static int file_client_on_read (hio_dev_sck_t* sck, const void* buf, hio_iolen_t { /* 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->client->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() */ { - if (file_write_to_peer(file, HIO_NULL, 0) <= -1) goto oops; - file->client_eof_detected = 1; + int n; + n = file_write_to_peer(file, HIO_NULL, 0); file_mark_over (file, FILE_OVER_READ_FROM_CLIENT); + if (n <= -1) goto oops; } } else @@ -422,7 +424,6 @@ oops: return 0; } - /* --------------------------------------------------------------------- */ static int file_client_htrd_poke (hio_htrd_t* htrd, hio_htre_t* req) @@ -438,7 +439,7 @@ static int file_client_htrd_poke (hio_htrd_t* htrd, hio_htre_t* req) if (file->req_method != HIO_HTTP_GET) { - if (file_send_final_status_to_client(file, 200, 0) <= -1) return -1; + if (file_send_final_status_to_client(file, HIO_HTTP_STATUS_OK, 0) <= -1) return -1; } file_mark_over (file, FILE_OVER_READ_FROM_CLIENT); @@ -476,7 +477,7 @@ static int file_send_header_to_client (file_t* file, int status_code, int force_ if (!force_close) force_close = !file->keep_alive; content_length = file->end_offset - file->start_offset + 1; - if (status_code == 200 && file->total_size != content_length) status_code = 206; + 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\nContent-Type: %hs\r\n", file->req_version.major, file->req_version.minor, @@ -485,7 +486,7 @@ static int file_send_header_to_client (file_t* file, int status_code, int force_ (force_close? "close": "keep-alive"), mime_type) == (hio_oow_t)-1) return -1; if (file->req_method == HIO_HTTP_GET && hio_becs_fcat(cli->sbuf, "ETag: %hs\r\n", file->peer_etag) == (hio_oow_t)-1) return -1; - if (status_code == 206 && 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; + 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 @@ -566,7 +567,12 @@ static int file_send_contents_to_client (file_t* file) return 0; } -static HIO_INLINE int process_range_header (file_t* file, hio_htre_t* req) +#define ERRNO_TO_STATUS_CODE(x) ( \ + ((x) == ENOENT)? HIO_HTTP_STATUS_NOT_FOUND: \ + ((x) == EPERM || (x) == EACCES)? HIO_HTTP_STATUS_FORBIDDEN: HIO_HTTP_STATUS_INTERNAL_SERVER_ERROR \ +) + +static HIO_INLINE int process_range_header (file_t* file, hio_htre_t* req, int* error_status) { struct stat st; const hio_htre_hdrval_t* tmp; @@ -574,14 +580,14 @@ static HIO_INLINE int process_range_header (file_t* file, hio_htre_t* req) if (fstat(file->peer, &st) <= -1) { - file_send_final_status_to_client (file, 500, 1); + *error_status = ERRNO_TO_STATUS_CODE(errno); return -1; } if ((st.st_mode & S_IFMT) != S_IFREG) { /* TODO: support directory listing if S_IFDIR? still disallow special files. */ - file_send_final_status_to_client (file, 403, 1); /* forbidden */ + *error_status = HIO_HTTP_STATUS_FORBIDDEN; return -1; } @@ -617,7 +623,7 @@ static HIO_INLINE int process_range_header (file_t* file, hio_htre_t* req) if (hio_parse_http_range_bcstr(tmp->ptr, &range) <= -1) { range_not_satisifiable: - file_send_final_status_to_client (file, 416, 1); /* 406 Requested Range Not Satisfiable */ + *error_status = HIO_HTTP_STATUS_RANGE_NOT_SATISFIABLE; return -1; } @@ -646,7 +652,13 @@ static HIO_INLINE int process_range_header (file_t* file, hio_htre_t* req) } if (file->start_offset > 0) - lseek(file->peer, file->start_offset, SEEK_SET); + { + if (lseek(file->peer, file->start_offset, SEEK_SET) <= -1) + { + *error_status = ERRNO_TO_STATUS_CODE(errno); + return -1; + } + } } else @@ -660,86 +672,39 @@ static HIO_INLINE int process_range_header (file_t* file, hio_htre_t* req) return 0; } -#define ERRNO_TO_STATUS_CODE(x) ( \ - ((x) == ENOENT)? 404: \ - ((x) == EPERM || (x) == EACCES)? 403: 500 \ -) - -static int open_peer (file_t* file, const hio_bch_t* actual_file) +static int open_peer_with_mode (file_t* file, const hio_bch_t* actual_file, int flags, int* error_status) { - switch (file->req_method) + flags |= O_NONBLOCK; +#if defined(O_CLOEXEC) + flags |= O_CLOEXEC; +#endif +#if defined(O_LARGEFILE) + flags |= O_LARGEFILE; +#endif + + file->peer = open(actual_file, flags, 0644); + if (HIO_UNLIKELY(file->peer <= -1)) { - case HIO_HTTP_GET: - case HIO_HTTP_HEAD: - { - int flags; - - if (access(actual_file, R_OK) == -1) - { - file_send_final_status_to_client (file, ERRNO_TO_STATUS_CODE(errno), 1); /* 404 not found 403 Forbidden */ - return -1; - } - - flags = O_RDONLY | O_NONBLOCK; - #if defined(O_CLOEXEC) - flags |= O_CLOEXEC; - #endif - #if defined(O_LARGEFILE) - flags |= O_LARGEFILE; - #endif - file->peer = open(actual_file, flags); - - if (HIO_UNLIKELY(file->peer <= -1)) - { - file_send_final_status_to_client (file, ERRNO_TO_STATUS_CODE(errno), 1); - return -1; - } - - return 0; - } - -#if 0 - case HIO_HTTP_PUT: - case HIO_HTTP_POST: -/* TOOD: this is destructive. jump to default if not allowed by flags... */ - file->peer = open(actual_file, O_WRONLY | O_TRUNC | O_CREAT | O_NONBLOCK | O_CLOEXEC, 0644); - if (HIO_UNLIKELY(file->peer <= -1)) - { - file_send_final_status_to_client (file, ERRNO_TO_STATUS_CODE(errno), 1); - return -1; - } - return 0; - - case HIO_HTTP_PATCH: -/* TOOD: this is destructive. jump to default if not allowed by flags... */ - file->peer = open(actual_file, O_WRONLY | O_NONBLOCK | O_CLOEXEC, 0644); - if (HIO_UNLIKELY(file->peer <= -1)) - { - file_send_final_status_to_client (file, ERRNO_TO_STATUS_CODE(errno), 1); - return -1; - } - return 0; -#endif - -#if 0 - case HIO_HTTP_DELETE: - /* TODO: */ -#endif + *error_status = ERRNO_TO_STATUS_CODE(errno); + return -1; } - file_send_final_status_to_client (file, 405, 1); /* 405: method not allowed */ - return -1; + return 0; } -static HIO_INLINE void fadvise_on_peer (file_t* file) +static HIO_INLINE void set_tcp_cork (hio_dev_sck_t* sck) { -#if defined(HAVE_POSIX_FADVISE) - if (file->req_method == HIO_HTTP_GET) - posix_fadvise (file->peer, file->start_offset, file->end_offset - file->start_offset + 1, POSIX_FADV_SEQUENTIAL); +#if defined(TCP_CORK) + int tcp_cork = 1; + #if defined(SOL_TCP) + hio_dev_sck_setsockopt (sck, SOL_TCP, TCP_CORK, &tcp_cork, HIO_SIZEOF(tcp_cork)); + #elif defined(IPPROTO_TCP) + hio_dev_sck_setsockopt (sck, IPPROTO_TCP, TCP_CORK, &tcp_cork, HIO_SIZEOF(tcp_cork)); + #endif #endif } -int hio_svc_htts_dofile (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* req, const hio_bch_t* docroot, const hio_bch_t* filepath, const hio_bch_t* mime_type) +int hio_svc_htts_dofile (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* req, const hio_bch_t* docroot, const hio_bch_t* filepath, const hio_bch_t* mime_type, int options) { /* TODO: ETag, Last-Modified... */ @@ -747,6 +712,7 @@ int hio_svc_htts_dofile (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* hio_svc_htts_cli_t* cli = hio_dev_sck_getxtn(csck); file_t* file = HIO_NULL; hio_bch_t* actual_file = HIO_NULL; + int status_code; /* ensure that you call this function before any contents is received */ HIO_ASSERT (hio, hio_htre_getcontentlen(req) == 0); @@ -760,6 +726,7 @@ int hio_svc_htts_dofile (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* file = (file_t*)hio_svc_htts_rsrc_make(htts, HIO_SIZEOF(*file), file_on_kill); if (HIO_UNLIKELY(!file)) goto oops; + file->options = options; file->client = cli; file->sendfile_ok = hio_dev_sck_sendfileok(cli->sck); /*file->num_pending_writes_to_client = 0; @@ -781,11 +748,6 @@ int hio_svc_htts_dofile (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* file->peer_tmridx = HIO_TMRIDX_INVALID; file->peer = -1; - if (open_peer(file, actual_file) <= -1 || - process_range_header(file, req) <= -1) goto oops; - - fadvise_on_peer (file); - #if !defined(FILE_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH) if (file->req_content_length_unlimited) { @@ -794,21 +756,21 @@ int hio_svc_htts_dofile (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* /* 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 (file_send_final_status_to_client(file, 411, 1) <= -1) goto oops; + if (file_send_final_status_to_client(file, HIO_HTTP_STATUS_LENGTH_REQUIRED, 1) <= -1) goto oops; } #endif if (req->flags & HIO_HTRE_ATTR_EXPECT100) { - if (hio_comp_http_version_numbers(&req->version, 1, 1) >= 0 && + 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) && (file->req_method != HIO_HTTP_GET && file->req_method != HIO_HTTP_HEAD)) { hio_bch_t msgbuf[64]; hio_oow_t msglen; - msglen = hio_fmttobcstr(hio, msgbuf, HIO_COUNTOF(msgbuf), "HTTP/%d.%d 100 Continue\r\n\r\n", file->req_version.major, file->req_version.minor); + msglen = hio_fmttobcstr(hio, msgbuf, HIO_COUNTOF(msgbuf), "HTTP/%d.%d %d %hs\r\n\r\n", file->req_version.major, file->req_version.minor, HIO_HTTP_STATUS_CONTINUE, hio_http_status_to_bcstr(HIO_HTTP_STATUS_CONTINUE)); if (file_write_to_client(file, msgbuf, msglen) <= -1) goto oops; file->ever_attempted_to_write_to_client = 0; /* reset this as it's polluted for 100 continue */ } @@ -816,7 +778,7 @@ int hio_svc_htts_dofile (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* else if (req->flags & HIO_HTRE_ATTR_EXPECT) { /* 417 Expectation Failed */ - file_send_final_status_to_client (file, 417, 1); + file_send_final_status_to_client (file, HIO_HTTP_STATUS_EXPECTATION_FAILED, 1); goto oops; } @@ -843,9 +805,15 @@ int hio_svc_htts_dofile (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* else { /* no content to be uploaded from the client */ + #if 0 /* indicate EOF to the peer and disable input wathching from the client */ if (file_write_to_peer(file, HIO_NULL, 0) <= -1) goto oops; + HIO_ASSERT (hio, file->over | FILE_OVER_WRITE_TO_PEER); /* must be set by the call to file_write_to_peer() above */ + file_mark_over (file, FILE_OVER_READ_FROM_CLIENT); + #else + /* no peer is open yet. so simply set the mars forcibly instead of calling file_write_to_peer() with null data */ file_mark_over (file, FILE_OVER_READ_FROM_CLIENT | FILE_OVER_WRITE_TO_PEER); + #endif } #if defined(FILE_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH) } @@ -863,34 +831,74 @@ int hio_svc_htts_dofile (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* file->res_mode_to_cli = FILE_RES_MODE_CLOSE; } - if (file->req_method == HIO_HTTP_GET) + switch (file->req_method) { - if (file->etag_match) - { - /* 304 not modified */ - if (file_send_final_status_to_client(file, 304, 0) <= -1) goto oops; - file_mark_over (file, FILE_OVER_READ_FROM_PEER | FILE_OVER_WRITE_TO_PEER); - } - else - { - /* normal full transfer */ - #if defined(TCP_CORK) - int tcp_cork = 1; - #if defined(SOL_TCP) - hio_dev_sck_setsockopt(file->client->sck, SOL_TCP, TCP_CORK, &tcp_cork, HIO_SIZEOF(tcp_cork)); - #elif defined(IPPROTO_TCP) - hio_dev_sck_setsockopt(file->client->sck, IPPROTO_TCP, TCP_CORK, &tcp_cork, HIO_SIZEOF(tcp_cork)); - #endif - #endif + case HIO_HTTP_GET: + if (open_peer_with_mode(file, actual_file, O_RDONLY, &status_code) <= -1) goto done_with_status_code; + if (process_range_header(file, req, &status_code) <= -1) goto done_with_status_code; - if (file_send_header_to_client(file, 200, 0, mime_type) <= -1 || + if (file->etag_match) + { + status_code = HIO_HTTP_STATUS_NOT_MODIFIED; + goto done_with_status_code; + } + + /* normal full transfer */ + #if defined(HAVE_POSIX_FADVISE) + posix_fadvise (file->peer, file->start_offset, file->end_offset - file->start_offset + 1, POSIX_FADV_SEQUENTIAL); + #endif + set_tcp_cork (file->client->sck); + + if (file_send_header_to_client(file, HIO_HTTP_STATUS_OK, 0, mime_type) <= -1 || file_send_contents_to_client(file) <= -1) goto oops; - } - } - else if (file->req_method == HIO_HTTP_HEAD) - { - if (file_send_header_to_client(file, 200, 0, mime_type) <= -1) goto oops; - file_mark_over (file, FILE_OVER_READ_FROM_PEER | FILE_OVER_WRITE_TO_PEER); + + break; + + case HIO_HTTP_HEAD: + if (open_peer_with_mode(file, actual_file, O_RDONLY, &status_code) <= -1) goto done_with_status_code; + if (process_range_header(file, req, &status_code) <= -1) goto done_with_status_code; + status_code = HIO_HTTP_STATUS_OK; + goto done_with_status_code; + + case HIO_HTTP_POST: + case HIO_HTTP_PUT: + if (file->options & HIO_SVC_HTTS_FILE_READ_ONLY) + { + status_code = HIO_HTTP_STATUS_METHOD_NOT_ALLOWED; + goto done_with_status_code; + } + + if (open_peer_with_mode(file, actual_file, O_WRONLY | O_TRUNC | O_CREAT, &status_code) <= -1) goto done_with_status_code; + + /* the client input must be written to the peer side */ + file_mark_over (file, FILE_OVER_READ_FROM_PEER); + break; + + case HIO_HTTP_DELETE: + if (file->options & HIO_SVC_HTTS_FILE_READ_ONLY) + { + status_code = HIO_HTTP_STATUS_METHOD_NOT_ALLOWED; + goto done_with_status_code; + } + + if (unlink(actual_file) <= -1) + { + if (errno != EISDIR || (errno == EISDIR && rmdir(actual_file) <= -1)) + { + status_code = ERRNO_TO_STATUS_CODE(errno); + goto done_with_status_code; + } + } + + status_code = HIO_HTTP_STATUS_OK; + goto done_with_status_code; + + default: + status_code = HIO_HTTP_STATUS_METHOD_NOT_ALLOWED; + done_with_status_code: + if (file_send_final_status_to_client(file, status_code, 0) <= -1) goto oops; + file_mark_over (file, FILE_OVER_READ_FROM_PEER | FILE_OVER_WRITE_TO_PEER); + break; } /* TODO: store current input watching state and use it when destroying the file data */ diff --git a/lib/http-thr.c b/lib/http-thr.c index 17c68cf..ffa1377 100644 --- a/lib/http-thr.c +++ b/lib/http-thr.c @@ -59,6 +59,7 @@ struct thr_state_t { HIO_SVC_HTTS_RSRC_HEADER; + int options; hio_oow_t num_pending_writes_to_client; hio_oow_t num_pending_writes_to_peer; hio_dev_thr_t* peer; @@ -70,6 +71,7 @@ struct thr_state_t unsigned int keep_alive: 1; 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 */ @@ -160,7 +162,7 @@ static int thr_state_write_last_chunk_to_client (thr_state_t* thr_state) { if (!thr_state->ever_attempted_to_write_to_client) { - if (thr_state_send_final_status_to_client(thr_state, 500, 0) <= -1) return -1; + if (thr_state_send_final_status_to_client(thr_state, HIO_HTTP_STATUS_INTERNAL_SERVER_ERROR, 0) <= -1) return -1; } else { @@ -225,7 +227,7 @@ static HIO_INLINE void thr_state_mark_over (thr_state_t* thr_state, int over_bit hio_dev_thr_halt (thr_state->peer); } - if (thr_state->keep_alive) + if (thr_state->keep_alive && !thr_state->client_eof_detected) { /* how to arrange to delete this thr_state object and put the socket back to the normal waiting state??? */ HIO_ASSERT (thr_state->htts->hio, thr_state->client->rsrc == (hio_svc_htts_rsrc_t*)thr_state); @@ -396,7 +398,7 @@ static int thr_peer_on_read (hio_dev_thr_t* thr, const void* data, hio_iolen_t d if (!thr_state->ever_attempted_to_write_to_client && !(thr_state->over & THR_STATE_OVER_WRITE_TO_CLIENT)) { - thr_state_send_final_status_to_client (thr_state, 500, 1); /* don't care about error because it jumps to oops below anyway */ + thr_state_send_final_status_to_client (thr_state, HIO_HTTP_STATUS_INTERNAL_SERVER_ERROR, 1); /* don't care about error because it jumps to oops below anyway */ } goto oops; @@ -452,7 +454,7 @@ static int thr_peer_htrd_peek (hio_htrd_t* htrd, hio_htre_t* req) thr_state_t* thr_state = thr_peer->state; hio_svc_htts_cli_t* cli = thr_state->client; hio_bch_t dtbuf[64]; - int status_code = 200; + int status_code = HIO_HTTP_STATUS_OK; if (req->attr.content_length) { @@ -693,11 +695,14 @@ static int thr_client_on_read (hio_dev_sck_t* sck, const void* buf, hio_iolen_t { /* EOF on the client side. arrange to close */ HIO_DEBUG3 (hio, "HTTPS(%p) - EOF from client %p(hnd=%d)\n", thr_state->client->htts, sck, (int)sck->hnd); + thr_state->client_eof_detected = 1; if (!(thr_state->over & THR_STATE_OVER_READ_FROM_CLIENT)) /* if this is true, EOF is received without thr_client_htrd_poke() */ { - if (thr_state_write_to_peer(thr_state, HIO_NULL, 0) <= -1) goto oops; + int n; + n = thr_state_write_to_peer(thr_state, HIO_NULL, 0); thr_state_mark_over (thr_state, THR_STATE_OVER_READ_FROM_CLIENT); + if (n <= -1) goto oops; } } else @@ -812,7 +817,7 @@ static int thr_capture_request_header (hio_htre_t* req, const hio_bch_t* key, co return 0; } -int hio_svc_htts_dothr (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* req, hio_svc_htts_thr_func_t func, void* ctx) +int hio_svc_htts_dothr (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* req, hio_svc_htts_thr_func_t func, void* ctx, int options) { hio_t* hio = htts->hio; hio_svc_htts_cli_t* cli = hio_dev_sck_getxtn(csck); @@ -857,6 +862,7 @@ int hio_svc_htts_dothr (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* r thr_state = (thr_state_t*)hio_svc_htts_rsrc_make(htts, HIO_SIZEOF(*thr_state), thr_state_on_kill); if (HIO_UNLIKELY(!thr_state)) goto oops; + thr_state->options = options; thr_state->client = cli; /*thr_state->num_pending_writes_to_client = 0; thr_state->num_pending_writes_to_peer = 0;*/ @@ -902,7 +908,7 @@ int hio_svc_htts_dothr (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* r * 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 (thr_state_send_final_status_to_client(thr_state, 411, 1) <= -1) goto oops; + if (thr_state_send_final_status_to_client(thr_state, HIO_HTTP_STATUS_LENGTH_REQUIRED, 1) <= -1) goto oops; } #endif @@ -910,7 +916,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 (hio_comp_http_version_numbers(&req->version, 1, 1) >= 0 && + if (!(options & HIO_SVC_HTTS_THR_NO_100_CONTINUE) && + hio_comp_http_version_numbers(&req->version, 1, 1) >= 0 && (thr_state->req_content_length_unlimited || thr_state->req_content_length > 0)) { /* @@ -929,7 +936,7 @@ int hio_svc_htts_dothr (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* r hio_bch_t msgbuf[64]; hio_oow_t msglen; - msglen = hio_fmttobcstr(hio, msgbuf, HIO_COUNTOF(msgbuf), "HTTP/%d.%d 100 Continue\r\n\r\n", thr_state->req_version.major, thr_state->req_version.minor); + msglen = hio_fmttobcstr(hio, msgbuf, HIO_COUNTOF(msgbuf), "HTTP/%d.%d %d %hs\r\n\r\n", thr_state->req_version.major, thr_state->req_version.minor, HIO_HTTP_STATUS_CONTINUE, hio_http_status_to_bcstr(HIO_HTTP_STATUS_CONTINUE)); if (thr_state_write_to_client(thr_state, msgbuf, msglen) <= -1) goto oops; thr_state->ever_attempted_to_write_to_client = 0; /* reset this as it's polluted for 100 continue */ } @@ -937,7 +944,7 @@ int hio_svc_htts_dothr (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* r else if (req->flags & HIO_HTRE_ATTR_EXPECT) { /* 417 Expectation Failed */ - thr_state_send_final_status_to_client(thr_state, 417, 1); + thr_state_send_final_status_to_client(thr_state, HIO_HTTP_STATUS_EXPECTATION_FAILED, 1); goto oops; } diff --git a/lib/http-txt.c b/lib/http-txt.c index d4781cb..c316830 100644 --- a/lib/http-txt.c +++ b/lib/http-txt.c @@ -33,6 +33,7 @@ struct txt_t { HIO_SVC_HTTS_RSRC_HEADER; + int options; hio_oow_t num_pending_writes_to_client; hio_svc_htts_cli_t* client; hio_http_version_t req_version; /* client request */ @@ -40,6 +41,7 @@ struct txt_t unsigned int over: 2; /* must be large enough to accomodate TXT_OVER_ALL */ unsigned int keep_alive: 1; 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 */ @@ -133,7 +135,7 @@ static HIO_INLINE void txt_mark_over (txt_t* txt, int over_bits) if (old_over != TXT_OVER_ALL && txt->over == TXT_OVER_ALL) { /* ready to stop */ - if (txt->keep_alive) + if (txt->keep_alive && !txt->client_eof_detected) { /* how to arrange to delete this txt object and put the socket back to the normal waiting state??? */ HIO_ASSERT (txt->htts->hio, txt->client->rsrc == (hio_svc_htts_rsrc_t*)txt); @@ -248,7 +250,8 @@ static int txt_client_on_read (hio_dev_sck_t* sck, const void* buf, hio_iolen_t { /* EOF on the client side. arrange to close */ HIO_DEBUG3 (hio, "HTTPS(%p) - EOF from client %p(hnd=%d)\n", txt->client->htts, sck, (int)sck->hnd); - + txt->client_eof_detected = 1; + if (!(txt->over & TXT_OVER_READ_FROM_CLIENT)) /* if this is true, EOF is received without txt_client_htrd_poke() */ { txt_mark_over (txt, TXT_OVER_READ_FROM_CLIENT); @@ -316,7 +319,7 @@ oops: return 0; } -int hio_svc_htts_dotxt (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* req, int status_code, const hio_bch_t* content_type, const hio_bch_t* content_text) +int hio_svc_htts_dotxt (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* req, int status_code, const hio_bch_t* content_type, const hio_bch_t* content_text, int options) { hio_t* hio = htts->hio; hio_svc_htts_cli_t* cli = hio_dev_sck_getxtn(csck); @@ -328,6 +331,7 @@ int hio_svc_htts_dotxt (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* r txt = (txt_t*)hio_svc_htts_rsrc_make(htts, HIO_SIZEOF(*txt), txt_on_kill); if (HIO_UNLIKELY(!txt)) goto oops; + txt->options = options; txt->client = cli; /*txt->num_pending_writes_to_client = 0;*/ txt->req_version = *hio_htre_getversion(req); @@ -350,7 +354,7 @@ int hio_svc_htts_dotxt (hio_svc_htts_t* htts, hio_dev_sck_t* csck, hio_htre_t* r else if (req->flags & HIO_HTRE_ATTR_EXPECT) { /* 417 Expectation Failed */ - txt_send_final_status_to_client(txt, 417, HIO_NULL, HIO_NULL, 1); + txt_send_final_status_to_client(txt, HIO_HTTP_STATUS_EXPECTATION_FAILED, HIO_NULL, HIO_NULL, 1); goto oops; } diff --git a/lib/http.c b/lib/http.c index 2b2d83d..d0a969a 100644 --- a/lib/http.c +++ b/lib/http.c @@ -46,50 +46,50 @@ const hio_bch_t* hio_http_status_to_bcstr (int code) switch (code) { - case 100: msg = "Continue"; break; - case 101: msg = "Switching Protocols"; break; + case HIO_HTTP_STATUS_CONTINUE: msg = "Continue"; break; + case HIO_HTTP_STATUS_SWITCH_PROTOCOL: msg = "Switching Protocols"; break; - case 200: msg = "OK"; break; - case 201: msg = "Created"; break; - case 202: msg = "Accepted"; break; - case 203: msg = "Non-Authoritative Information"; break; - case 204: msg = "No Content"; break; - case 205: msg = "Reset Content"; break; - case 206: msg = "Partial Content"; break; + case HIO_HTTP_STATUS_OK: msg = "OK"; break; + case HIO_HTTP_STATUS_CREATED: msg = "Created"; break; + case HIO_HTTP_STATUS_ACCEPTED: msg = "Accepted"; break; + case HIO_HTTP_STATUS_NON_AUTHORITATIVE: msg = "Non-Authoritative Information"; break; + case HIO_HTTP_STATUS_NO_CONTENT: msg = "No Content"; break; + case HIO_HTTP_STATUS_RESET_CONTENT: msg = "Reset Content"; break; + case HIO_HTTP_STATUS_PARTIAL_CONTENT: msg = "Partial Content"; break; case 300: msg = "Multiple Choices"; break; - case 301: msg = "Moved Permanently"; break; + case HIO_HTTP_STATUS_MOVED_PERMANENTLY: msg = "Moved Permanently"; break; case 302: msg = "Found"; break; case 303: msg = "See Other"; break; - case 304: msg = "Not Modified"; break; + case HIO_HTTP_STATUS_NOT_MODIFIED: msg = "Not Modified"; break; case 305: msg = "Use Proxy"; break; case 307: msg = "Temporary Redirect"; break; case 308: msg = "Permanent Redirect"; break; - case 400: msg = "Bad Request"; break; + case HIO_HTTP_STATUS_BAD_REQUEST: msg = "Bad Request"; break; case 401: msg = "Unauthorized"; break; case 402: msg = "Payment Required"; break; - case 403: msg = "Forbidden"; break; - case 404: msg = "Not Found"; break; - case 405: msg = "Method Not Allowed"; break; + case HIO_HTTP_STATUS_FORBIDDEN: msg = "Forbidden"; break; + case HIO_HTTP_STATUS_NOT_FOUND: msg = "Not Found"; break; + case HIO_HTTP_STATUS_METHOD_NOT_ALLOWED: msg = "Method Not Allowed"; break; case 406: msg = "Not Acceptable"; break; case 407: msg = "Proxy Authentication Required"; break; case 408: msg = "Request Timeout"; break; case 409: msg = "Conflict"; break; case 410: msg = "Gone"; break; - case 411: msg = "Length Required"; break; + case HIO_HTTP_STATUS_LENGTH_REQUIRED: msg = "Length Required"; break; case 412: msg = "Precondition Failed"; break; case 413: msg = "Request Entity Too Large"; break; case 414: msg = "Request-URI Too Long"; break; case 415: msg = "Unsupported Media Type"; break; - case 416: msg = "Requested Range Not Satisfiable"; break; - case 417: msg = "Expectation Failed"; break; + case HIO_HTTP_STATUS_RANGE_NOT_SATISFIABLE: msg = "Requested Range Not Satisfiable"; break; + case HIO_HTTP_STATUS_EXPECTATION_FAILED: msg = "Expectation Failed"; break; case 426: msg = "Upgrade Required"; break; case 428: msg = "Precondition Required"; break; case 429: msg = "Too Many Requests"; break; case 431: msg = "Request Header Fields Too Large"; break; - case 500: msg = "Internal Server Error"; 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; @@ -217,13 +217,7 @@ int hio_parse_http_range_bcstr (const hio_bch_t* str, hio_http_range_t* range) hio_foff_t from, to; int type = HIO_HTTP_RANGE_PROPER; - if (str[0] != 'b' || - str[1] != 'y' || - str[2] != 't' || - str[3] != 'e' || - str[4] != 's' || - str[5] != '=') return -1; - + if (str[0] != 'b' || str[1] != 'y' || str[2] != 't' || str[3] != 'e' || str[4] != 's' || str[5] != '=') return -1; str += 6; from = to = 0;