added http-txt.c
This commit is contained in:
		| @ -320,3 +320,31 @@ int mio_htre_perdecqpath (mio_htre_t* re) | |||||||
|  |  | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | int mio_htre_getreqcontentlen (mio_htre_t* req, mio_oow_t* len) | ||||||
|  | { | ||||||
|  | 	/* return the potential content length to expect to receive if used as a request */ | ||||||
|  |  | ||||||
|  | 	if (req->flags & MIO_HTRE_ATTR_CHUNKED) | ||||||
|  | 	{ | ||||||
|  | 		/* "Transfer-Encoding: chunked" take precedence over "Content-Length: XXX".  | ||||||
|  | 		 * | ||||||
|  | 		 * [RFC7230] | ||||||
|  | 		 *  If a message is received with both a Transfer-Encoding and a | ||||||
|  | 		 *  Content-Length header field, the Transfer-Encoding overrides the | ||||||
|  | 		 *  Content-Length. */ | ||||||
|  | 		return 1; /* unable to determine content-length in advance. unlimited */ | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (req->flags & MIO_HTRE_ATTR_LENGTH) | ||||||
|  | 	{ | ||||||
|  | 		*len = req->attr.content_length; | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 	{ | ||||||
|  | 		/* If no Content-Length is specified in a request, it's Content-Length: 0 */ | ||||||
|  | 		*len = 0; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return 0; /* limited to the length set in *len */ | ||||||
|  | } | ||||||
|  | |||||||
| @ -759,31 +759,6 @@ oops: | |||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static MIO_INLINE int get_request_content_length (mio_htre_t* req, mio_oow_t* len) |  | ||||||
| { |  | ||||||
| 	if (req->flags & MIO_HTRE_ATTR_CHUNKED) |  | ||||||
| 	{ |  | ||||||
| 		/* "Transfer-Encoding: chunked" take precedence over "Content-Length: XXX".  |  | ||||||
| 		 * |  | ||||||
| 		 * [RFC7230] |  | ||||||
| 		 *  If a message is received with both a Transfer-Encoding and a |  | ||||||
| 		 *  Content-Length header field, the Transfer-Encoding overrides the |  | ||||||
| 		 *  Content-Length. */ |  | ||||||
| 		return 1; /* unable to determine content-length in advance. unlimited */ |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (req->flags & MIO_HTRE_ATTR_LENGTH) |  | ||||||
| 	{ |  | ||||||
| 		*len = req->attr.content_length; |  | ||||||
| 	} |  | ||||||
| 	else |  | ||||||
| 	{ |  | ||||||
| 		/* If no Content-Length is specified in a request, it's Content-Length: 0 */ |  | ||||||
| 		*len = 0; |  | ||||||
| 	} |  | ||||||
| 	return 0; /* limited to the length set in *len */ |  | ||||||
| } |  | ||||||
|  |  | ||||||
| struct cgi_peer_fork_ctx_t | struct cgi_peer_fork_ctx_t | ||||||
| { | { | ||||||
| 	mio_svc_htts_cli_t* cli; | 	mio_svc_htts_cli_t* cli; | ||||||
| @ -882,7 +857,7 @@ static int cgi_peer_on_fork (mio_dev_pro_t* pro, void* fork_ctx) | |||||||
| 	setenv ("REQUEST_URI", mio_htre_getqpath(fc->req), 1); | 	setenv ("REQUEST_URI", mio_htre_getqpath(fc->req), 1); | ||||||
| 	if (qparam) setenv ("QUERY_STRING", qparam, 1); | 	if (qparam) setenv ("QUERY_STRING", qparam, 1); | ||||||
|  |  | ||||||
| 	if (get_request_content_length(fc->req, &content_length) == 0) | 	if (mio_htre_getreqcontentlen(fc->req, &content_length) == 0) | ||||||
| 	{ | 	{ | ||||||
| 		mio_fmt_uintmax_to_bcstr(tmp, MIO_COUNTOF(tmp), content_length, 10, 0, '\0', MIO_NULL); | 		mio_fmt_uintmax_to_bcstr(tmp, MIO_COUNTOF(tmp), content_length, 10, 0, '\0', MIO_NULL); | ||||||
| 		setenv ("CONTENT_LENGTH", tmp, 1); | 		setenv ("CONTENT_LENGTH", tmp, 1); | ||||||
| @ -955,7 +930,7 @@ int mio_svc_htts_docgi (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_t* r | |||||||
| 	/*cgi_state->num_pending_writes_to_client = 0; | 	/*cgi_state->num_pending_writes_to_client = 0; | ||||||
| 	cgi_state->num_pending_writes_to_peer = 0;*/ | 	cgi_state->num_pending_writes_to_peer = 0;*/ | ||||||
| 	cgi_state->req_version = *mio_htre_getversion(req); | 	cgi_state->req_version = *mio_htre_getversion(req); | ||||||
| 	cgi_state->req_content_length_unlimited = get_request_content_length(req, &cgi_state->req_content_length); | 	cgi_state->req_content_length_unlimited = mio_htre_getreqcontentlen(req, &cgi_state->req_content_length); | ||||||
|  |  | ||||||
| 	cgi_state->client_org_on_read = csck->on_read; | 	cgi_state->client_org_on_read = csck->on_read; | ||||||
| 	cgi_state->client_org_on_write = csck->on_write; | 	cgi_state->client_org_on_write = csck->on_write; | ||||||
|  | |||||||
| @ -765,31 +765,6 @@ oops: | |||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|  |  | ||||||
| static MIO_INLINE int get_request_content_length (mio_htre_t* req, mio_oow_t* len) |  | ||||||
| { |  | ||||||
| 	if (req->flags & MIO_HTRE_ATTR_CHUNKED) |  | ||||||
| 	{ |  | ||||||
| 		/* "Transfer-Encoding: chunked" take precedence over "Content-Length: XXX".  |  | ||||||
| 		 * |  | ||||||
| 		 * [RFC7230] |  | ||||||
| 		 *  If a message is received with both a Transfer-Encoding and a |  | ||||||
| 		 *  Content-Length header field, the Transfer-Encoding overrides the |  | ||||||
| 		 *  Content-Length. */ |  | ||||||
| 		return 1; /* unable to determine content-length in advance. unlimited */ |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if (req->flags & MIO_HTRE_ATTR_LENGTH) |  | ||||||
| 	{ |  | ||||||
| 		*len = req->attr.content_length; |  | ||||||
| 	} |  | ||||||
| 	else |  | ||||||
| 	{ |  | ||||||
| 		/* If no Content-Length is specified in a request, it's Content-Length: 0 */ |  | ||||||
| 		*len = 0; |  | ||||||
| 	} |  | ||||||
| 	return 0; /* limited to the length set in *len */ |  | ||||||
| } |  | ||||||
|  |  | ||||||
| static void free_thr_start_info (void* ctx) | static void free_thr_start_info (void* ctx) | ||||||
| { | { | ||||||
| 	thr_func_start_t* tfs = (thr_func_start_t*)ctx; | 	thr_func_start_t* tfs = (thr_func_start_t*)ctx; | ||||||
| @ -852,7 +827,7 @@ int mio_svc_htts_dothr (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_t* r | |||||||
| 	/*thr_state->num_pending_writes_to_client = 0; | 	/*thr_state->num_pending_writes_to_client = 0; | ||||||
| 	thr_state->num_pending_writes_to_peer = 0;*/ | 	thr_state->num_pending_writes_to_peer = 0;*/ | ||||||
| 	thr_state->req_version = *mio_htre_getversion(req); | 	thr_state->req_version = *mio_htre_getversion(req); | ||||||
| 	thr_state->req_content_length_unlimited = get_request_content_length(req, &thr_state->req_content_length); | 	thr_state->req_content_length_unlimited = mio_htre_getreqcontentlen(req, &thr_state->req_content_length); | ||||||
|  |  | ||||||
| 	thr_state->client_org_on_read = csck->on_read; | 	thr_state->client_org_on_read = csck->on_read; | ||||||
| 	thr_state->client_org_on_write = csck->on_write; | 	thr_state->client_org_on_write = csck->on_write; | ||||||
|  | |||||||
							
								
								
									
										518
									
								
								mio/lib/http-txt.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										518
									
								
								mio/lib/http-txt.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,518 @@ | |||||||
|  | /* | ||||||
|  |  * $Id$ | ||||||
|  |  * | ||||||
|  |     Copyright (c) 2016-2020 Chung, Hyung-Hwan. All rights reserved. | ||||||
|  |  | ||||||
|  |     Redistribution and use in source and binary forms, with or without | ||||||
|  |     modification, are permitted provided that the following conditions | ||||||
|  |     are met: | ||||||
|  |     1. Redistributions of source code must retain the above copyright | ||||||
|  |        notice, this list of conditions and the following disclaimer. | ||||||
|  |     2. Redistributions in binary form must reproduce the above copyright | ||||||
|  |        notice, this list of conditions and the following disclaimer in the | ||||||
|  |        documentation and/or other materials provided with the distribution. | ||||||
|  |  | ||||||
|  |     THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR | ||||||
|  |     IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WAfRRANTIES | ||||||
|  |     OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | ||||||
|  |     IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | ||||||
|  |     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | ||||||
|  |     NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||||||
|  |     DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||||||
|  |     THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||||||
|  |     (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | ||||||
|  |     THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||||||
|  |  */ | ||||||
|  | #include "http-prv.h" | ||||||
|  | #include <mio-txt.h> | ||||||
|  | #include <mio-fmt.h> | ||||||
|  | #include <mio-chr.h> | ||||||
|  |  | ||||||
|  | #define TXT_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH | ||||||
|  |  | ||||||
|  | enum txt_state_res_mode_t | ||||||
|  | { | ||||||
|  | 	TXT_STATE_RES_MODE_CHUNKED, | ||||||
|  | 	TXT_STATE_RES_MODE_CLOSE, | ||||||
|  | 	TXT_STATE_RES_MODE_LENGTH | ||||||
|  | }; | ||||||
|  | typedef enum txt_state_res_mode_t txt_state_res_mode_t; | ||||||
|  |  | ||||||
|  | #define TXT_STATE_PENDING_IO_TXTESHOLD 5 | ||||||
|  |  | ||||||
|  | #define TXT_STATE_OVER_READ_FROM_CLIENT (1 << 0) | ||||||
|  | #define TXT_STATE_OVER_READ_FROM_PEER   (1 << 1) | ||||||
|  | #define TXT_STATE_OVER_WRITE_TO_CLIENT  (1 << 2) | ||||||
|  | #define TXT_STATE_OVER_WRITE_TO_PEER    (1 << 3) | ||||||
|  | #define TXT_STATE_OVER_ALL (TXT_STATE_OVER_READ_FROM_CLIENT | TXT_STATE_OVER_READ_FROM_PEER | TXT_STATE_OVER_WRITE_TO_CLIENT | TXT_STATE_OVER_WRITE_TO_PEER) | ||||||
|  |  | ||||||
|  | struct txt_func_start_t | ||||||
|  | { | ||||||
|  | 	mio_t* mio; | ||||||
|  | 	mio_svc_htts_txt_func_t txt_func; | ||||||
|  | 	void* txt_ctx; | ||||||
|  | 	mio_svc_htts_txt_func_info_t tfi; | ||||||
|  | }; | ||||||
|  | typedef struct txt_func_start_t txt_func_start_t; | ||||||
|  |  | ||||||
|  | struct txt_state_t | ||||||
|  | { | ||||||
|  | 	MIO_SVC_HTTS_RSRC_HEADER; | ||||||
|  |  | ||||||
|  | 	mio_oow_t num_pending_writes_to_client; | ||||||
|  | 	mio_svc_htts_cli_t* client; | ||||||
|  | 	mio_http_version_t req_version; /* client request */ | ||||||
|  |  | ||||||
|  | 	unsigned int over: 4; /* must be large enough to accomodate TXT_STATE_OVER_ALL */ | ||||||
|  | 	unsigned int keep_alive: 1; | ||||||
|  | 	unsigned int req_content_length_unlimited: 1; | ||||||
|  | 	unsigned int ever_attempted_to_write_to_client: 1; | ||||||
|  | 	unsigned int client_disconnected: 1; | ||||||
|  | 	mio_oow_t req_content_length; /* client request content length */ | ||||||
|  | 	txt_state_res_mode_t res_mode_to_cli; | ||||||
|  |  | ||||||
|  | 	mio_dev_sck_on_read_t client_org_on_read; | ||||||
|  | 	mio_dev_sck_on_write_t client_org_on_write; | ||||||
|  | 	mio_dev_sck_on_disconnect_t client_org_on_disconnect; | ||||||
|  | 	mio_htrd_recbs_t client_htrd_org_recbs; | ||||||
|  | 	 | ||||||
|  |  | ||||||
|  |  | ||||||
|  | }; | ||||||
|  | typedef struct txt_state_t txt_state_t; | ||||||
|  |  | ||||||
|  | static void txt_state_halt_participating_devices (txt_state_t* txt_state) | ||||||
|  | { | ||||||
|  | 	MIO_ASSERT (txt_state->client->htts->mio, txt_state->client != MIO_NULL); | ||||||
|  | 	MIO_ASSERT (txt_state->client->htts->mio, txt_state->client->sck != MIO_NULL); | ||||||
|  |  | ||||||
|  | 	MIO_DEBUG3 (txt_state->client->htts->mio, "HTTS(%p) - Halting participating devices in txt state %p(client=%p)\n", txt_state->client->htts, txt_state, txt_state->client->sck); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	mio_dev_sck_halt (txt_state->client->sck); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int txt_state_write_to_client (txt_state_t* txt_state, const void* data, mio_iolen_t dlen) | ||||||
|  | { | ||||||
|  | 	txt_state->ever_attempted_to_write_to_client = 1; | ||||||
|  |  | ||||||
|  | 	txt_state->num_pending_writes_to_client++; | ||||||
|  | 	if (mio_dev_sck_write(txt_state->client->sck, data, dlen, MIO_NULL, MIO_NULL) <= -1)  | ||||||
|  | 	{ | ||||||
|  | 		txt_state->num_pending_writes_to_client--; | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (txt_state->num_pending_writes_to_client > TXT_STATE_PENDING_IO_TXTESHOLD) | ||||||
|  | 	{ | ||||||
|  | 	} | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int txt_state_writev_to_client (txt_state_t* txt_state, mio_iovec_t* iov, mio_iolen_t iovcnt) | ||||||
|  | { | ||||||
|  | 	txt_state->ever_attempted_to_write_to_client = 1; | ||||||
|  |  | ||||||
|  | 	txt_state->num_pending_writes_to_client++; | ||||||
|  | 	if (mio_dev_sck_writev(txt_state->client->sck, iov, iovcnt, MIO_NULL, MIO_NULL) <= -1)  | ||||||
|  | 	{ | ||||||
|  | 		txt_state->num_pending_writes_to_client--; | ||||||
|  | 		return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (txt_state->num_pending_writes_to_client > TXT_STATE_PENDING_IO_TXTESHOLD) | ||||||
|  | 	{ | ||||||
|  | 	} | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int txt_state_send_final_status_to_client (txt_state_t* txt_state, int status_code, int force_close) | ||||||
|  | { | ||||||
|  | 	mio_svc_htts_cli_t* cli = txt_state->client; | ||||||
|  | 	mio_bch_t dtbuf[64]; | ||||||
|  |  | ||||||
|  | 	mio_svc_htts_fmtgmtime (cli->htts, MIO_NULL, dtbuf, MIO_COUNTOF(dtbuf)); | ||||||
|  |  | ||||||
|  | 	if (!force_close) force_close = !txt_state->keep_alive; | ||||||
|  | 	if (mio_becs_fmt(cli->sbuf, "HTTP/%d.%d %d %hs\r\nServer: %hs\r\nDate: %s\r\nConnection: %hs\r\nContent-Length: 0\r\n\r\n", | ||||||
|  | 		txt_state->req_version.major, txt_state->req_version.minor, | ||||||
|  | 		status_code, mio_http_status_to_bcstr(status_code), | ||||||
|  | 		cli->htts->server_name, dtbuf, | ||||||
|  | 		(force_close? "close": "keep-alive")) == (mio_oow_t)-1) return -1; | ||||||
|  |  | ||||||
|  | 	return (txt_state_write_to_client(txt_state, MIO_BECS_PTR(cli->sbuf), MIO_BECS_LEN(cli->sbuf)) <= -1 || | ||||||
|  | 	        (force_close && txt_state_write_to_client(txt_state, MIO_NULL, 0) <= -1))? -1: 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  | static int txt_state_write_last_chunk_to_client (txt_state_t* txt_state) | ||||||
|  | { | ||||||
|  | 	if (!txt_state->ever_attempted_to_write_to_client) | ||||||
|  | 	{ | ||||||
|  | 		if (txt_state_send_final_status_to_client(txt_state, 500, 0) <= -1) return -1; | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 	{ | ||||||
|  | 		if (txt_state->res_mode_to_cli == TXT_STATE_RES_MODE_CHUNKED && | ||||||
|  | 		    txt_state_write_to_client(txt_state, "0\r\n\r\n", 5) <= -1) return -1; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (!txt_state->keep_alive && txt_state_write_to_client(txt_state, MIO_NULL, 0) <= -1) return -1; | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static MIO_INLINE void txt_state_mark_over (txt_state_t* txt_state, int over_bits) | ||||||
|  | { | ||||||
|  | 	unsigned int old_over; | ||||||
|  |  | ||||||
|  | 	old_over = txt_state->over; | ||||||
|  | 	txt_state->over |= over_bits; | ||||||
|  |  | ||||||
|  | 	MIO_DEBUG4 (txt_state->htts->mio, "HTTS(%p) - client=%p new-bits=%x over=%x\n", txt_state->htts, txt_state->client->sck, (int)over_bits, (int)txt_state->over); | ||||||
|  |  | ||||||
|  | 	if (!(old_over & TXT_STATE_OVER_READ_FROM_CLIENT) && (txt_state->over & TXT_STATE_OVER_READ_FROM_CLIENT)) | ||||||
|  | 	{ | ||||||
|  | 		if (mio_dev_sck_read(txt_state->client->sck, 0) <= -1)  | ||||||
|  | 		{ | ||||||
|  | 			MIO_DEBUG2 (txt_state->htts->mio, "HTTS(%p) - halting client(%p) for failure to disable input watching\n", txt_state->htts, txt_state->client->sck); | ||||||
|  | 			mio_dev_sck_halt (txt_state->client->sck); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (old_over != TXT_STATE_OVER_ALL && txt_state->over == TXT_STATE_OVER_ALL) | ||||||
|  | 	{ | ||||||
|  | 		/* ready to stop */ | ||||||
|  | 		if (txt_state->keep_alive)  | ||||||
|  | 		{ | ||||||
|  | 			/* how to arrange to delete this txt_state object and put the socket back to the normal waiting state??? */ | ||||||
|  | 			MIO_ASSERT (txt_state->htts->mio, txt_state->client->rsrc == (mio_svc_htts_rsrc_t*)txt_state); | ||||||
|  |  | ||||||
|  | printf ("DETACHING FROM THE MAIN CLIENT RSRC... state -> %p\n", txt_state->client->rsrc); | ||||||
|  | 			MIO_SVC_HTTS_RSRC_DETACH (txt_state->client->rsrc); | ||||||
|  | 			/* txt_state must not be access from here down as it could have been destroyed */ | ||||||
|  | 		} | ||||||
|  | 		else | ||||||
|  | 		{ | ||||||
|  | 			MIO_DEBUG2 (txt_state->htts->mio, "HTTS(%p) - halting client(%p) for no keep-alive\n", txt_state->htts, txt_state->client->sck); | ||||||
|  | 			mio_dev_sck_shutdown (txt_state->client->sck, MIO_DEV_SCK_SHUTDOWN_WRITE); | ||||||
|  | 			mio_dev_sck_halt (txt_state->client->sck); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static void txt_state_on_kill (txt_state_t* txt_state) | ||||||
|  | { | ||||||
|  | printf ("**** TXT_STATE_ON_KILL \n"); | ||||||
|  | 	if (txt_state->client_org_on_read) | ||||||
|  | 	{ | ||||||
|  | 		txt_state->client->sck->on_read = txt_state->client_org_on_read; | ||||||
|  | 		txt_state->client_org_on_read = MIO_NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (txt_state->client_org_on_write) | ||||||
|  | 	{ | ||||||
|  | 		txt_state->client->sck->on_write = txt_state->client_org_on_write; | ||||||
|  | 		txt_state->client_org_on_write = MIO_NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  |  | ||||||
|  | 	if (txt_state->client_org_on_disconnect) | ||||||
|  | 	{ | ||||||
|  | 		txt_state->client->sck->on_disconnect = txt_state->client_org_on_disconnect; | ||||||
|  | 		txt_state->client_org_on_disconnect = MIO_NULL; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	mio_htrd_setrecbs (txt_state->client->htrd, &txt_state->client_htrd_org_recbs); /* restore the callbacks */ | ||||||
|  |  | ||||||
|  | 	if (!txt_state->client_disconnected) | ||||||
|  | 	{ | ||||||
|  | printf ("ENABLING INPUT WATCHING on CLIENT %p. \n", txt_state->client->sck); | ||||||
|  | 		if (!txt_state->keep_alive || mio_dev_sck_read(txt_state->client->sck, 1) <= -1) | ||||||
|  | 		{ | ||||||
|  | 			MIO_DEBUG2 (txt_state->htts->mio, "HTTS(%p) - halting client(%p) for failure to enable input watching\n", txt_state->htts, txt_state->client->sck); | ||||||
|  | 			mio_dev_sck_halt (txt_state->client->sck); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | printf ("**** TXT_STATE_ON_KILL DONE\n"); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int txt_client_htrd_poke (mio_htrd_t* htrd, mio_htre_t* req) | ||||||
|  | { | ||||||
|  | 	/* client request got completed */ | ||||||
|  | 	mio_svc_htts_cli_htrd_xtn_t* htrdxtn = (mio_svc_htts_cli_htrd_xtn_t*)mio_htrd_getxtn(htrd); | ||||||
|  | 	mio_dev_sck_t* sck = htrdxtn->sck; | ||||||
|  | 	mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(sck); | ||||||
|  | 	txt_state_t* txt_state = (txt_state_t*)cli->rsrc; | ||||||
|  |  | ||||||
|  | printf (">> CLIENT REQUEST COMPLETED\n"); | ||||||
|  |  | ||||||
|  | 	txt_state_mark_over (txt_state, TXT_STATE_OVER_READ_FROM_CLIENT); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int txt_client_htrd_push_content (mio_htrd_t* htrd, mio_htre_t* req, const mio_bch_t* data, mio_oow_t dlen) | ||||||
|  | { | ||||||
|  | 	/* | ||||||
|  | 	mio_svc_htts_cli_htrd_xtn_t* htrdxtn = (mio_svc_htts_cli_htrd_xtn_t*)mio_htrd_getxtn(htrd); | ||||||
|  | 	mio_dev_sck_t* sck = htrdxtn->sck; | ||||||
|  | 	mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(sck); | ||||||
|  | 	txt_state_t* txt_state = (txt_state_t*)cli->rsrc; | ||||||
|  | 	MIO_ASSERT (sck->mio, cli->sck == sck); | ||||||
|  | 	*/ | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static mio_htrd_recbs_t txt_client_htrd_recbs = | ||||||
|  | { | ||||||
|  | 	MIO_NULL, | ||||||
|  | 	txt_client_htrd_poke, | ||||||
|  | 	txt_client_htrd_push_content | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | static void txt_client_on_disconnect (mio_dev_sck_t* sck) | ||||||
|  | { | ||||||
|  | 	mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(sck); | ||||||
|  | 	txt_state_t* txt_state = (txt_state_t*)cli->rsrc; | ||||||
|  | 	txt_state->client_disconnected = 1; | ||||||
|  | 	txt_state->client_org_on_disconnect (sck); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int txt_client_on_read (mio_dev_sck_t* sck, const void* buf, mio_iolen_t len, const mio_skad_t* srcaddr) | ||||||
|  | { | ||||||
|  | 	mio_t* mio = sck->mio; | ||||||
|  | 	mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(sck); | ||||||
|  | 	txt_state_t* txt_state = (txt_state_t*)cli->rsrc; | ||||||
|  |  | ||||||
|  | 	MIO_ASSERT (mio, sck == cli->sck); | ||||||
|  |  | ||||||
|  | 	if (len <= -1) | ||||||
|  | 	{ | ||||||
|  | 		/* read error */ | ||||||
|  | 		MIO_DEBUG2 (cli->htts->mio, "HTTPS(%p) - read error on client %p(%d)\n", sck, (int)sck->hnd); | ||||||
|  | 		goto oops; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (len == 0) | ||||||
|  | 	{ | ||||||
|  | 		/* EOF on the client side. arrange to close */ | ||||||
|  | 		MIO_DEBUG3 (mio, "HTTPS(%p) - EOF from client %p(hnd=%d)\n", txt_state->client->htts, sck, (int)sck->hnd); | ||||||
|  |  | ||||||
|  | 		if (!(txt_state->over & TXT_STATE_OVER_READ_FROM_CLIENT)) /* if this is true, EOF is received without txt_client_htrd_poke() */ | ||||||
|  | 		{ | ||||||
|  | 			txt_state_mark_over (txt_state, TXT_STATE_OVER_READ_FROM_CLIENT); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 	{ | ||||||
|  | 		mio_oow_t rem; | ||||||
|  |  | ||||||
|  | 		MIO_ASSERT (mio, !(txt_state->over & TXT_STATE_OVER_READ_FROM_CLIENT)); | ||||||
|  |  | ||||||
|  | 		if (mio_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 */ | ||||||
|  | printf ("UUUUUUUUUUUUUUUUUUUUUUUUUUGGGGGHHHHHHHHHHHH .......... TXT CLIENT GIVING EXCESSIVE DATA AFTER CONTENTS...\n"); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  |  | ||||||
|  | oops: | ||||||
|  | 	txt_state_halt_participating_devices (txt_state); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | static int txt_client_on_write (mio_dev_sck_t* sck, mio_iolen_t wrlen, void* wrctx, const mio_skad_t* dstaddr) | ||||||
|  | { | ||||||
|  | 	mio_t* mio = sck->mio; | ||||||
|  | 	mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(sck); | ||||||
|  | 	txt_state_t* txt_state = (txt_state_t*)cli->rsrc; | ||||||
|  |  | ||||||
|  | 	if (wrlen <= -1) | ||||||
|  | 	{ | ||||||
|  | 		MIO_DEBUG3 (mio, "HTTPS(%p) - unable to write to client %p(%d)\n", sck->mio, sck, (int)sck->hnd); | ||||||
|  | 		goto oops; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	if (wrlen == 0) | ||||||
|  | 	{ | ||||||
|  | 		/* if the connect is keep-alive, this part may not be called */ | ||||||
|  | 		txt_state->num_pending_writes_to_client--; | ||||||
|  | 		MIO_ASSERT (mio, txt_state->num_pending_writes_to_client == 0); | ||||||
|  | 		MIO_DEBUG3 (mio, "HTTS(%p) - indicated EOF to client %p(%d)\n", txt_state->client->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 */ | ||||||
|  | 		txt_state_mark_over (txt_state, TXT_STATE_OVER_WRITE_TO_CLIENT); | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 	{ | ||||||
|  | 		MIO_ASSERT (mio, txt_state->num_pending_writes_to_client > 0); | ||||||
|  |  | ||||||
|  | 		txt_state->num_pending_writes_to_client--; | ||||||
|  |  | ||||||
|  | 		if ((txt_state->over & TXT_STATE_OVER_READ_FROM_PEER) && txt_state->num_pending_writes_to_client <= 0) | ||||||
|  | 		{ | ||||||
|  | 			txt_state_mark_over (txt_state, TXT_STATE_OVER_WRITE_TO_CLIENT); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	return 0; | ||||||
|  |  | ||||||
|  | oops: | ||||||
|  | 	txt_state_halt_participating_devices (txt_state); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | int mio_svc_htts_dotxt (mio_svc_htts_t* htts, mio_dev_sck_t* csck, mio_htre_t* req, mio_svc_htts_txt_func_t func, void* ctx) | ||||||
|  | { | ||||||
|  | 	mio_t* mio = htts->mio; | ||||||
|  | 	mio_svc_htts_cli_t* cli = mio_dev_sck_getxtn(csck); | ||||||
|  | 	txt_state_t* txt_state = MIO_NULL; | ||||||
|  | 	mio_dev_txt_make_t mi; | ||||||
|  | 	txt_func_start_t* tfs; | ||||||
|  |  | ||||||
|  | 	/* ensure that you call this function before any contents is received */ | ||||||
|  | 	MIO_ASSERT (mio, mio_htre_getcontentlen(req) == 0); | ||||||
|  |  | ||||||
|  | 	tfs = mio_callocmem(mio, MIO_SIZEOF(*tfs)); | ||||||
|  | 	if (!tfs) goto oops; | ||||||
|  |  | ||||||
|  | 	tfs->mio = mio; | ||||||
|  | 	tfs->txt_func = func; | ||||||
|  | 	tfs->txt_ctx = ctx; | ||||||
|  |  | ||||||
|  | 	tfs->tfi.req_method = mio_htre_getqmethodtype(req); | ||||||
|  | 	tfs->tfi.req_version = *mio_htre_getversion(req); | ||||||
|  | 	tfs->tfi.req_path = mio_dupbcstr(mio, mio_htre_getqpath(req), MIO_NULL); | ||||||
|  | 	if (!tfs->tfi.req_path) goto oops; | ||||||
|  | 	if (mio_htre_getqparam(req)) | ||||||
|  | 	{ | ||||||
|  | 		tfs->tfi.req_param = mio_dupbcstr(mio, mio_htre_getqparam(req), MIO_NULL); | ||||||
|  | 		if (!tfs->tfi.req_param) goto oops; | ||||||
|  | 	} | ||||||
|  | 	/* TODO: copy headers.. */ | ||||||
|  | 	tfs->tfi.server_addr = cli->sck->localaddr; | ||||||
|  | 	tfs->tfi.client_addr = cli->sck->remoteaddr; | ||||||
|  |  | ||||||
|  | 	txt_state = (txt_state_t*)mio_svc_htts_rsrc_make(htts, MIO_SIZEOF(*txt_state), txt_state_on_kill); | ||||||
|  | 	if (MIO_UNLIKELY(!txt_state)) goto oops; | ||||||
|  |  | ||||||
|  | 	txt_state->client = cli; | ||||||
|  | 	/*txt_state->num_pending_writes_to_client = 0; | ||||||
|  | 	txt_state->num_pending_writes_to_peer = 0;*/ | ||||||
|  | 	txt_state->req_version = *mio_htre_getversion(req); | ||||||
|  | 	txt_state->req_content_length_unlimited = mio_htre_getreqcontentlen(req, &txt_state->req_content_length); | ||||||
|  |  | ||||||
|  | 	txt_state->client_org_on_read = csck->on_read; | ||||||
|  | 	txt_state->client_org_on_write = csck->on_write; | ||||||
|  | 	txt_state->client_org_on_disconnect = csck->on_disconnect; | ||||||
|  | 	csck->on_read = txt_client_on_read; | ||||||
|  | 	csck->on_write = txt_client_on_write; | ||||||
|  | 	csck->on_disconnect = txt_client_on_disconnect; | ||||||
|  |  | ||||||
|  | 	MIO_ASSERT (mio, cli->rsrc == MIO_NULL); | ||||||
|  | 	MIO_SVC_HTTS_RSRC_ATTACH (txt_state, cli->rsrc); | ||||||
|  |  | ||||||
|  | #if !defined(TXT_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH) | ||||||
|  | 	if (txt_state->req_content_length_unlimited) | ||||||
|  | 	{ | ||||||
|  | 		/* Transfer-Encoding is chunked. no content-length is known in advance. */ | ||||||
|  | 		 | ||||||
|  | 		/* option 1. buffer contents. if it gets too large, send 413 Request Entity Too Large. | ||||||
|  | 		 * option 2. send 411 Length Required immediately | ||||||
|  | 		 * option 3. set Content-Length to -1 and use EOF to indicate the end of content [Non-Standard] */ | ||||||
|  |  | ||||||
|  | 		if (txt_state_send_final_status_to_client(txt_state, 411, 1) <= -1) goto oops; | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | 	if (req->flags & MIO_HTRE_ATTR_EXPECT100) | ||||||
|  | 	{ | ||||||
|  | 		/* TODO: Expect: 100-continue? who should handle this? txt? or the http server? */ | ||||||
|  | 		/* CAN I LET the txt SCRIPT handle this? */ | ||||||
|  | 		if (mio_comp_http_version_numbers(&req->version, 1, 1) >= 0 &&  | ||||||
|  | 		   (txt_state->req_content_length_unlimited || txt_state->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. | ||||||
|  | 			 */ | ||||||
|  | 			mio_bch_t msgbuf[64]; | ||||||
|  | 			mio_oow_t msglen; | ||||||
|  |  | ||||||
|  | 			msglen = mio_fmttobcstr(mio, msgbuf, MIO_COUNTOF(msgbuf), "HTTP/%d.%d 100 Continue\r\n\r\n", txt_state->req_version.major, txt_state->req_version.minor); | ||||||
|  | 			if (txt_state_write_to_client(txt_state, msgbuf, msglen) <= -1) goto oops; | ||||||
|  | 			txt_state->ever_attempted_to_write_to_client = 0; /* reset this as it's polluted for 100 continue */ | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	else if (req->flags & MIO_HTRE_ATTR_EXPECT) | ||||||
|  | 	{ | ||||||
|  | 		/* 417 Expectation Failed */ | ||||||
|  | 		txt_state_send_final_status_to_client(txt_state, 417, 1); | ||||||
|  | 		goto oops; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | #if defined(TXT_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH) | ||||||
|  | 	if (txt_state->req_content_length_unlimited) | ||||||
|  | 	{ | ||||||
|  | 		/* change the callbacks to subscribe to contents to be uploaded */ | ||||||
|  | 		txt_state->client_htrd_org_recbs = *mio_htrd_getrecbs(txt_state->client->htrd); | ||||||
|  | 		txt_client_htrd_recbs.peek = txt_state->client_htrd_org_recbs.peek; | ||||||
|  | 		mio_htrd_setrecbs (txt_state->client->htrd, &txt_client_htrd_recbs); | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 	{ | ||||||
|  | #endif | ||||||
|  | 		if (txt_state->req_content_length > 0) | ||||||
|  | 		{ | ||||||
|  | 			/* change the callbacks to subscribe to contents to be uploaded */ | ||||||
|  | 			txt_state->client_htrd_org_recbs = *mio_htrd_getrecbs(txt_state->client->htrd); | ||||||
|  | 			txt_client_htrd_recbs.peek = txt_state->client_htrd_org_recbs.peek; | ||||||
|  | 			mio_htrd_setrecbs (txt_state->client->htrd, &txt_client_htrd_recbs); | ||||||
|  | 		} | ||||||
|  | 		else | ||||||
|  | 		{ | ||||||
|  | 			/* no content to be uploaded from the client */ | ||||||
|  | 			/* indicate EOF to the peer and disable input wathching from the client */ | ||||||
|  | 			txt_state_mark_over (txt_state, TXT_STATE_OVER_READ_FROM_CLIENT | TXT_STATE_OVER_WRITE_TO_PEER); | ||||||
|  | 		} | ||||||
|  | #if defined(TXT_ALLOW_UNLIMITED_REQ_CONTENT_LENGTH) | ||||||
|  | 	} | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | 	/* this may change later if Content-Length is included in the txt output */ | ||||||
|  | 	if (req->flags & MIO_HTRE_ATTR_KEEPALIVE) | ||||||
|  | 	{ | ||||||
|  | 		txt_state->keep_alive = 1; | ||||||
|  | 		txt_state->res_mode_to_cli = TXT_STATE_RES_MODE_CHUNKED;  | ||||||
|  | 		/* the mode still can get switched to TXT_STATE_RES_MODE_LENGTH if the txt script emits Content-Length */ | ||||||
|  | 	} | ||||||
|  | 	else | ||||||
|  | 	{ | ||||||
|  | 		txt_state->keep_alive = 0; | ||||||
|  | 		txt_state->res_mode_to_cli = TXT_STATE_RES_MODE_CLOSE; | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	/* TODO: store current input watching state and use it when destroying the txt_state data */ | ||||||
|  | 	if (mio_dev_sck_read(csck, !(txt_state->over & TXT_STATE_OVER_READ_FROM_CLIENT)) <= -1) goto oops; | ||||||
|  | 	return 0; | ||||||
|  |  | ||||||
|  | oops: | ||||||
|  | 	MIO_DEBUG2 (mio, "HTTS(%p) - FAILURE in dotxt - socket(%p)\n", htts, csck); | ||||||
|  | 	if (tfs) free_txt_start_info (tfs); | ||||||
|  | 	if (txt_state) txt_state_halt_participating_devices (txt_state); | ||||||
|  | 	return -1; | ||||||
|  | } | ||||||
| @ -308,6 +308,12 @@ MIO_EXPORT int mio_htre_perdecqpath ( | |||||||
| 	mio_htre_t*      req | 	mio_htre_t*      req | ||||||
| ); | ); | ||||||
|  |  | ||||||
|  |  | ||||||
|  | MIO_EXPORT int mio_htre_getreqcontentlen ( | ||||||
|  | 	mio_htre_t*      req, | ||||||
|  | 	mio_oow_t*       len | ||||||
|  | ); | ||||||
|  |  | ||||||
| #if defined(__cplusplus) | #if defined(__cplusplus) | ||||||
| } | } | ||||||
| #endif | #endif | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user