From c99a99e9b47bc21c54910d1e4519e12f5eeeeef6 Mon Sep 17 00:00:00 2001
From: Andy Green <andy@warmcat.com>
Date: Fri, 4 May 2018 12:05:56 +0800
Subject: [PATCH] LRS_DOING_TRANSACTION

---
 lib/roles/h1/ops-h1.c                         | 28 +++++++++++++++----
 lib/roles/http/server/parsers.c               |  7 ++---
 lib/roles/http/server/server.c                | 18 +++++++-----
 lib/roles/private.h                           | 21 ++++++++++----
 .../minimal-http-server-dynamic.c             |  4 +++
 .../minimal-raw-netcat/minimal-raw-netcat.c   |  8 ++++--
 6 files changed, 61 insertions(+), 25 deletions(-)

diff --git a/lib/roles/h1/ops-h1.c b/lib/roles/h1/ops-h1.c
index 8afd6268..d3b16f4d 100644
--- a/lib/roles/h1/ops-h1.c
+++ b/lib/roles/h1/ops-h1.c
@@ -308,9 +308,9 @@ lws_h1_server_socket_service(struct lws *wsi, struct lws_pollfd *pollfd)
 	     lwsi_state(wsi) == LRS_ISSUING_FILE ||
 	     lwsi_state(wsi) == LRS_HEADERS ||
 	     lwsi_state(wsi) == LRS_BODY)) {
-		if (!wsi->http.ah &&
-		    lws_header_table_attach(wsi, 0)) {
-			lwsl_info("wsi %p: ah get fail\n", wsi);
+
+		if (!wsi->http.ah && lws_header_table_attach(wsi, 0)) {
+			lwsl_info("%s: wsi %p: ah not available\n", __func__, wsi);
 			goto try_pollout;
 		}
 
@@ -319,6 +319,9 @@ lws_h1_server_socket_service(struct lws *wsi, struct lws_pollfd *pollfd)
 		 * regardless of our buflist state, we need to get it,
 		 * and either use it, or append to the buflist and use
 		 * buflist head material.
+		 *
+		 * We will not notice a connection close until the buflist is
+		 * exhausted and we tried to do a read of some kind.
 		 */
 
 		buffered = lws_buflist_aware_read(pt, wsi, &ebuf);
@@ -327,8 +330,20 @@ lws_h1_server_socket_service(struct lws *wsi, struct lws_pollfd *pollfd)
 			lwsl_info("%s: read 0 len a\n", __func__);
 			wsi->seen_zero_length_recv = 1;
 			lws_change_pollfd(wsi, LWS_POLLIN, 0);
-			goto try_pollout;
-			//goto fail;
+#if !defined(LWS_WITHOUT_EXTENSIONS)
+			/*
+			 * autobahn requires us to win the race between close
+			 * and draining the extensions
+			 */
+			if (wsi->ws &&
+			    (wsi->ws->rx_draining_ext || wsi->ws->tx_draining_ext))
+				goto try_pollout;
+#endif
+			/*
+			 * normally, we respond to close with logically closing
+			 * our side immediately
+			 */
+			goto fail;
 
 		case LWS_SSL_CAPABLE_ERROR:
 			goto fail;
@@ -469,7 +484,8 @@ try_pollout:
 
 
 fail:
-	lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "server socket svc fail");
+	lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS,
+			   "server socket svc fail");
 
 	return LWS_HPI_RET_WSI_ALREADY_DIED;
 }
diff --git a/lib/roles/http/server/parsers.c b/lib/roles/http/server/parsers.c
index 5754f909..054a839b 100644
--- a/lib/roles/http/server/parsers.c
+++ b/lib/roles/http/server/parsers.c
@@ -81,6 +81,8 @@ _lws_header_table_reset(struct allocated_headers *ah)
 	ah->nfrag = 0;
 	ah->pos = 0;
 	ah->http_response = 0;
+	ah->parser_state = WSI_TOKEN_NAME_PART;
+	ah->lextable_pos = 0;
 }
 
 // doesn't scrub the ah rxbuffer by default, parent must do if needed
@@ -99,9 +101,6 @@ __lws_header_table_reset(struct lws *wsi, int autoservice)
 
 	_lws_header_table_reset(ah);
 
-	ah->parser_state = WSI_TOKEN_NAME_PART;
-	ah->lextable_pos = 0;
-
 	/* since we will restart the ah, our new headers are not completed */
 	wsi->hdr_parsing_completed = 0;
 
@@ -353,7 +352,7 @@ int __lws_header_table_detach(struct lws *wsi, int autoservice)
 	if (!wsi) /* everybody waiting already has too many ah... */
 		goto nobody_usable_waiting;
 
-	lwsl_info("%s: last eligible wsi in wait list %p\n", __func__, wsi);
+	lwsl_info("%s: transferring ah to last eligible wsi in wait list %p (wsistate 0x%x)\n", __func__, wsi, wsi->wsistate);
 
 	wsi->http.ah = ah;
 	ah->wsi = wsi; /* new owner */
diff --git a/lib/roles/http/server/server.c b/lib/roles/http/server/server.c
index a308683f..f55a1371 100644
--- a/lib/roles/http/server/server.c
+++ b/lib/roles/http/server/server.c
@@ -330,8 +330,8 @@ lws_select_vhost(struct lws_context *context, int port, const char *servername)
 	vhost = context->vhost_list;
 	while (vhost) {
 		if (port == vhost->listen_port) {
-			lwsl_info("vhost match to %s based on port %d\n",
-					vhost->name, port);
+			lwsl_info("%s: vhost match to %s based on port %d\n",
+					__func__, vhost->name, port);
 			return vhost;
 		}
 		vhost = vhost->vhost_next;
@@ -991,6 +991,8 @@ lws_http_action(struct lws *wsi)
 		if (lws_bind_protocol(wsi, &wsi->vhost->protocols[0]))
 			return 1;
 
+		lwsi_set_state(wsi, LRS_DOING_TRANSACTION);
+
 		n = wsi->protocol->callback(wsi, LWS_CALLBACK_HTTP,
 				    wsi->user_space, uri_ptr, uri_len);
 
@@ -1547,7 +1549,7 @@ raw_transition:
 
 		/* no upgrade ack... he remained as HTTP */
 
-		lwsl_info("No upgrade\n");
+		lwsl_info("%s: %p: No upgrade\n", __func__, wsi);
 
 		lwsi_set_state(wsi, LRS_ESTABLISHED);
 		wsi->http.fop_fd = NULL;
@@ -1742,7 +1744,8 @@ lws_http_transaction_completed(struct lws *wsi)
 	 * until we can verify POLLOUT.  The part of this that confirms POLLOUT
 	 * with no partials is in lws_server_socket_service() below.
 	 */
-	lwsl_debug("%s: setting DEF_ACT from 0x%x\n", __func__, wsi->wsistate);
+	lwsl_debug("%s: %p: setting DEF_ACT from 0x%x\n", __func__,
+		   wsi, wsi->wsistate);
 	lwsi_set_state(wsi, LRS_DEFERRING_ACTION);
 	wsi->http.tx_content_length = 0;
 	wsi->http.tx_content_remain = 0;
@@ -1770,7 +1773,8 @@ lws_http_transaction_completed(struct lws *wsi)
 	if (wsi->http.ah) {
 		// lws_buflist_describe(&wsi->buflist, wsi);
 		if (!lws_buflist_next_segment_len(&wsi->buflist, NULL)) {
-			lwsl_debug("%s: nothing in buflist so detaching ah\n", __func__);
+			lwsl_info("%s: %p: nothing in buflist so detaching ah\n",
+				  __func__, wsi);
 			lws_header_table_detach(wsi, 1);
 #ifdef LWS_WITH_TLS
 			/*
@@ -1789,8 +1793,8 @@ lws_http_transaction_completed(struct lws *wsi)
 			}
 #endif
 		} else {
-			lwsl_debug("%s: resetting and keeping ah as pipeline\n",
-				   __func__);
+			lwsl_info("%s: %p: resetting and keeping ah as pipeline\n",
+				  __func__, wsi);
 			lws_header_table_reset(wsi, 0);
 			/*
 			 * If we kept the ah, we should restrict the amount
diff --git a/lib/roles/private.h b/lib/roles/private.h
index 4e0b3126..ae4278b5 100644
--- a/lib/roles/private.h
+++ b/lib/roles/private.h
@@ -122,18 +122,27 @@ enum lwsi_state {
 	LRS_HEADERS				= 21,
 	LRS_BODY				= 22,
 	LRS_ESTABLISHED				= LWSIFS_POCB | 23,
+	/* we are established, but we have embarked on serving a single
+	 * transaction.  Other transaction input may be pending, but we will
+	 * not service it while we are busy dealing with the current
+	 * transaction.
+	 *
+	 * When we complete the current transaction, we would reset our state
+	 * back to ESTABLISHED and start to process the next transaction.
+	 */
+	LRS_DOING_TRANSACTION			= LWSIFS_POCB | 24,
 
 	/* Phase 6: finishing */
 
-	LRS_WAITING_TO_SEND_CLOSE		= LWSIFS_POCB | 24,
-	LRS_RETURNED_CLOSE			= LWSIFS_POCB | 25,
-	LRS_AWAITING_CLOSE_ACK			= LWSIFS_POCB | 26,
-	LRS_FLUSHING_BEFORE_CLOSE		= LWSIFS_POCB | 27,
-	LRS_SHUTDOWN				= 28,
+	LRS_WAITING_TO_SEND_CLOSE		= LWSIFS_POCB | 25,
+	LRS_RETURNED_CLOSE			= LWSIFS_POCB | 26,
+	LRS_AWAITING_CLOSE_ACK			= LWSIFS_POCB | 27,
+	LRS_FLUSHING_BEFORE_CLOSE		= LWSIFS_POCB | 28,
+	LRS_SHUTDOWN				= 29,
 
 	/* Phase 7: dead */
 
-	LRS_DEAD_SOCKET				= 29,
+	LRS_DEAD_SOCKET				= 30,
 
 	LRS_MASK				= 0xffff
 };
diff --git a/minimal-examples/http-server/minimal-http-server-dynamic/minimal-http-server-dynamic.c b/minimal-examples/http-server/minimal-http-server-dynamic/minimal-http-server-dynamic.c
index 2b7fa4cc..b7bdaac1 100644
--- a/minimal-examples/http-server/minimal-http-server-dynamic/minimal-http-server-dynamic.c
+++ b/minimal-examples/http-server/minimal-http-server-dynamic/minimal-http-server-dynamic.c
@@ -177,6 +177,10 @@ int main(int argc, const char **argv)
 	info.protocols = protocols;
 	info.mounts = &mount;
 
+	/* for testing ah queue, not useful in real world */
+	if (lws_cmdline_option(argc, argv, "--ah1"))
+		info.max_http_header_pool = 1;
+
 	context = lws_create_context(&info);
 	if (!context) {
 		lwsl_err("lws init failed\n");
diff --git a/minimal-examples/raw/minimal-raw-netcat/minimal-raw-netcat.c b/minimal-examples/raw/minimal-raw-netcat/minimal-raw-netcat.c
index faffaa56..5bdf1be6 100644
--- a/minimal-examples/raw/minimal-raw-netcat/minimal-raw-netcat.c
+++ b/minimal-examples/raw/minimal-raw-netcat/minimal-raw-netcat.c
@@ -31,6 +31,7 @@ static struct lws *raw_wsi, *stdin_wsi;
 static uint8_t buf[LWS_PRE + 4096];
 static int waiting, interrupted;
 static struct lws_context *context;
+static int us_wait_after_input_close = LWS_USEC_PER_SEC / 10;
 
 static int
 callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason,
@@ -50,7 +51,7 @@ callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason,
 		/* stdin close, wait 1s then close the raw skt */
 		stdin_wsi = NULL; /* invalid now we close */
 		if (raw_wsi)
-			lws_set_timer_usecs(raw_wsi, LWS_USEC_PER_SEC / 10);
+			lws_set_timer_usecs(raw_wsi, us_wait_after_input_close);
 		else {
 			interrupted = 1;
 			lws_cancel_service(context);
@@ -153,7 +154,7 @@ int main(int argc, const char **argv)
 		logs = atoi(p);
 
 	lws_set_log_level(logs, NULL);
-	lwsl_user("LWS minimal raw netcat [--server ip] [--port port]\n");
+	lwsl_user("LWS minimal raw netcat [--server ip] [--port port] [-w ms]\n");
 
 	memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
 	info.options = LWS_SERVER_OPTION_EXPLICIT_VHOSTS;
@@ -193,6 +194,9 @@ int main(int argc, const char **argv)
 	if ((p = lws_cmdline_option(argc, argv, "--server")))
 		server = p;
 
+	if ((p = lws_cmdline_option(argc, argv, "-w")))
+		us_wait_after_input_close = 1000 * atoi(p);
+
 	n = getaddrinfo(server, port, &h, &r);
 	if (n) {
 		lwsl_err("%s: problem resolving %s: %s\n", __func__, 
-- 
GitLab