diff --git a/CMakeLists.txt b/CMakeLists.txt
index cc50eb3c84deedf95ba4abe352e15e2259e23f9c..de16b5d81a6faf070687e9a46f03d164007f5f14 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -951,7 +951,7 @@ if (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR (CMAKE_C_COMPILER_ID
 	    set (GCOV_FLAGS "-fprofile-arcs -ftest-coverage -O0")
     endif()
     if (UNIX AND NOT LWS_WITH_ESP32)
-	    set(CMAKE_C_FLAGS "-O3 -Wall -Wsign-compare -Wignored-qualifiers -Wtype-limits -Wuninitialized -Werror ${VISIBILITY_FLAG} -Wundef ${GCOV_FLAGS} ${CMAKE_C_FLAGS}" )
+	    set(CMAKE_C_FLAGS "-O0 -Wall -Wsign-compare -Wignored-qualifiers -Wtype-limits -Wuninitialized -Werror ${VISIBILITY_FLAG} -Wundef ${GCOV_FLAGS} ${CMAKE_C_FLAGS}" )
     else()
 	    set(CMAKE_C_FLAGS "-Wall -Wsign-compare -Wignored-qualifiers -Wtype-limits -Wuninitialized -Werror ${VISIBILITY_FLAG} ${GCOV_FLAGS} ${CMAKE_C_FLAGS}" )
     endif()
diff --git a/lib/client/client-handshake.c b/lib/client/client-handshake.c
index c1bfb54ac48768703defecc4edfa252c827bd0cf..35e8075f0360ec20960ea298a59af5918e47c467 100644
--- a/lib/client/client-handshake.c
+++ b/lib/client/client-handshake.c
@@ -100,8 +100,8 @@ lws_client_connect_2(struct lws *wsi)
 			 *     going through the queue
 			 */
 			if (w->client_h2_alpn &&
-			    (w->state == LWSS_HTTP2_CLIENT_WAITING_TO_SEND_HEADERS ||
-			     w->state == LWSS_HTTP2_CLIENT_ESTABLISHED)) {
+			    (lwsi_state(w) == LRS_H2_WAITING_TO_SEND_HEADERS ||
+			     lwsi_state(w) == LRS_ESTABLISHED)) {
 
 				lwsl_info("%s: just join h2 directly\n",
 						__func__);
@@ -114,7 +114,7 @@ lws_client_connect_2(struct lws *wsi)
 #endif
 
 			lwsl_info("applying %p to txn queue on %p (%d)\n", wsi, w,
-					w->state);
+					lwsi_state(w));
 			/*
 			 * ...let's add ourselves to his transaction queue...
 			 */
@@ -334,7 +334,7 @@ create_new_conn:
 			goto oom4;
 		}
 
-		wsi->mode = LWSCM_WSCL_WAITING_CONNECT;
+		lwsi_set_state(wsi, LRS_WAITING_CONNECT);
 
 		lws_libev_accept(wsi, wsi->desc);
 		lws_libuv_accept(wsi, wsi->desc);
@@ -449,7 +449,7 @@ create_new_conn:
 		lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_PROXY_RESPONSE,
 				AWAITING_TIMEOUT);
 
-		wsi->mode = LWSCM_WSCL_WAITING_PROXY_REPLY;
+		lwsi_set_state(wsi, LRS_WAITING_PROXY_REPLY);
 
 		return wsi;
 	}
@@ -467,7 +467,7 @@ create_new_conn:
 		lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SOCKS_GREETING_REPLY,
 				AWAITING_TIMEOUT);
 
-		wsi->mode = LWSCM_WSCL_WAITING_SOCKS_GREETING_REPLY;
+		lwsi_set_state(wsi, LRS_WAITING_SOCKS_GREETING_REPLY);
 
 		return wsi;
 	}
@@ -480,13 +480,14 @@ send_hs:
 		 * We are pipelining on an already-established connection...
 		 * we can skip tls establishment.
 		 */
-		wsi->mode = LWSCM_WSCL_ISSUE_HANDSHAKE2;
+
+		lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE2);
 
 		/*
 		 * we can't send our headers directly, because they have to
 		 * be sent when the parent is writeable.  The parent will check
 		 * for anybody on his client transaction queue that is in
-		 * LWSCM_WSCL_ISSUE_HANDSHAKE2, and let them write.
+		 * LRS_H1C_ISSUE_HANDSHAKE2, and let them write.
 		 *
 		 * If we are trying to do this too early, before the master
 		 * connection has written his own headers,
@@ -495,7 +496,7 @@ send_hs:
 		lwsl_debug("wsi %p: waiting to send headers\n", wsi);
 	} else {
 		/* we are making our own connection */
-		wsi->mode = LWSCM_WSCL_ISSUE_HANDSHAKE;
+		lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE);
 
 		/*
 		 * provoke service to issue the handshake directly.
@@ -546,11 +547,7 @@ oom4:
 	/* we're closing, losing some rx is OK */
 	lws_header_table_force_to_detachable_state(wsi);
 
-	if (wsi->mode == LWSCM_HTTP_CLIENT ||
-	    wsi->mode == LWSCM_HTTP2_CLIENT ||
-	    wsi->mode == LWSCM_HTTP_CLIENT_ACCEPTED ||
-	    wsi->mode == LWSCM_HTTP2_CLIENT_ACCEPTED ||
-	    wsi->mode == LWSCM_WSCL_WAITING_CONNECT) {
+	if (lwsi_role_client(wsi) && !(lwsi_state(wsi) & LWSIFS_NOTEST)) {
 		wsi->protocol->callback(wsi,
 			LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
 			wsi->user_space, (void *)cce, strlen(cce));
@@ -654,7 +651,7 @@ lws_client_reset(struct lws **pwsi, int ssl, const char *address, int port,
 #endif
 
 	wsi->desc.sockfd = LWS_SOCK_INVALID;
-	wsi->state = LWSS_CLIENT_UNCONNECTED;
+	lwsi_set_state(wsi, LRS_UNCONNECTED);
 	wsi->protocol = NULL;
 	wsi->pending_timeout = NO_PENDING_TIMEOUT;
 	wsi->c_port = port;
@@ -878,7 +875,7 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
 
 	wsi->context = i->context;
 	/* assert the mode and union status (hdr) clearly */
-	lws_union_transition(wsi, LWSCM_HTTP_CLIENT);
+	lws_role_transition(wsi, LWSI_ROLE_H1_CLIENT, LRS_UNCONNECTED);
 	wsi->desc.sockfd = LWS_SOCK_INVALID;
 
 	/* 1) fill up the wsi with stuff from the connect_info as far as it
@@ -903,7 +900,6 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
 	}
 
 	wsi->user_space = NULL;
-	wsi->state = LWSS_CLIENT_UNCONNECTED;
 	wsi->pending_timeout = NO_PENDING_TIMEOUT;
 	wsi->position_in_fds_table = -1;
 	wsi->c_port = i->port;
@@ -920,6 +916,9 @@ lws_client_connect_via_info(struct lws_client_connect_info *i)
 	wsi->protocol = &wsi->vhost->protocols[0];
 	wsi->client_pipeline = !!(i->ssl_connection & LCCSCF_PIPELINE);
 
+	/* reasonable place to start */
+	lwsi_set_role(wsi, LWSI_ROLE_H1_CLIENT);
+
 	/*
 	 * 1) for http[s] connection, allow protocol selection by name
 	 * 2) for ws[s], if local_protocol_name given also use it for
@@ -1093,27 +1092,6 @@ lws_client_connect_via_info2(struct lws *wsi)
 		lws_client_stash_destroy(wsi);
 #endif
 
-	/*
-	 * Check with each extension if it is able to route and proxy this
-	 * connection for us.  For example, an extension like x-google-mux
-	 * can handle this and then we don't need an actual socket for this
-	 * connection.
-	 */
-
-	if (lws_ext_cb_all_exts(wsi->context, wsi,
-				LWS_EXT_CB_CAN_PROXY_CLIENT_CONNECTION,
-				(void *)stash->address,
-				wsi->c_port) > 0) {
-		lwsl_client("lws_client_connect: ext handling conn\n");
-
-		lws_set_timeout(wsi,
-			PENDING_TIMEOUT_AWAITING_EXTENSION_CONNECT_RESPONSE,
-			        AWAITING_TIMEOUT);
-
-		wsi->mode = LWSCM_WSCL_WAITING_EXTENSION_CONNECT;
-		return wsi;
-	}
-	lwsl_client("lws_client_connect: direct conn\n");
 	wsi->context->count_wsi_allocated++;
 
 	return lws_client_connect_2(wsi);
diff --git a/lib/client/client-parser.c b/lib/client/client-parser.c
index 9174a52805a07286fbdcf1845958adc689827d02..8c754eb7a7573036d8a0719e030a57087718260c 100644
--- a/lib/client/client-parser.c
+++ b/lib/client/client-parser.c
@@ -352,8 +352,8 @@ spill:
 					   wsi->ws->rx_ubuf_head - 2))
 				goto utf8_fail;
 
-			/* is this an acknowledgement of our close? */
-			if (wsi->state == LWSS_AWAITING_CLOSE_ACK) {
+			/* is this an acknowledgment of our close? */
+			if (lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK) {
 				/*
 				 * fine he has told us he is closing too, let's
 				 * finish our close
@@ -404,7 +404,7 @@ spill:
 					  &wsi->ws->rx_ubuf[LWS_PRE],
 					  wsi->ws->rx_ubuf_head,
 					  LWS_WRITE_CLOSE);
-			wsi->state = LWSS_RETURNED_CLOSE_ALREADY;
+			lwsi_set_state(wsi, LRS_RETURNED_CLOSE);
 			/* close the connection */
 			return -1;
 
@@ -573,9 +573,9 @@ utf8_fail:
 		else
 			lws_remove_wsi_from_draining_ext_list(wsi);
 
-		if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY ||
-		    wsi->state == LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION ||
-		    wsi->state == LWSS_AWAITING_CLOSE_ACK)
+		if (lwsi_state(wsi) == LRS_RETURNED_CLOSE ||
+		    lwsi_state(wsi) == LRS_WAITING_TO_SEND_CLOSE ||
+		    lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK)
 			goto already_done;
 
 		m = wsi->protocol->callback(wsi,
diff --git a/lib/client/client.c b/lib/client/client.c
index 0799d83ec126bf78158e0d65b58a18d9878bf142..46964bfdf0402b8ec538a5bc18f9f64e844e7460 100644
--- a/lib/client/client.c
+++ b/lib/client/client.c
@@ -26,47 +26,43 @@ lws_handshake_client(struct lws *wsi, unsigned char **buf, size_t len)
 {
 	int m;
 
-	switch (wsi->mode) {
-	case LWSCM_WSCL_WAITING_PROXY_REPLY:
-	case LWSCM_WSCL_ISSUE_HANDSHAKE:
-	case LWSCM_WSCL_WAITING_SERVER_REPLY:
-	case LWSCM_WSCL_WAITING_EXTENSION_CONNECT:
-	case LWSCM_WS_CLIENT:
-		while (len) {
-			/*
-			 * we were accepting input but now we stopped doing so
-			 */
-			if (lws_is_flowcontrolled(wsi)) {
-				lwsl_debug("%s: caching %ld\n", __func__, (long)len);
-				lws_rxflow_cache(wsi, *buf, 0, (int)len);
-				return 0;
-			}
-			if (wsi->ws->rx_draining_ext) {
+	if ((lwsi_state(wsi) != LRS_WAITING_PROXY_REPLY) &&
+	    (lwsi_state(wsi) != LRS_H1C_ISSUE_HANDSHAKE) &&
+	    (lwsi_state(wsi) != LRS_WAITING_SERVER_REPLY) &&
+	    !lwsi_role_client(wsi))
+		return 0;
+
+	while (len) {
+		/*
+		 * we were accepting input but now we stopped doing so
+		 */
+		if (lws_is_flowcontrolled(wsi)) {
+			lwsl_debug("%s: caching %ld\n", __func__, (long)len);
+			lws_rxflow_cache(wsi, *buf, 0, (int)len);
+			return 0;
+		}
+		if (wsi->ws->rx_draining_ext) {
 #if !defined(LWS_NO_CLIENT)
-				if (wsi->mode == LWSCM_WS_CLIENT)
-					m = lws_client_rx_sm(wsi, 0);
-				else
+			if (lwsi_role_client(wsi))
+				m = lws_client_rx_sm(wsi, 0);
+			else
 #endif
-					m = lws_rx_sm(wsi, 0);
-				if (m < 0)
-					return -1;
-				continue;
-			}
-			/* account for what we're using in rxflow buffer */
-			if (wsi->rxflow_buffer)
-				wsi->rxflow_pos++;
-
-			if (lws_client_rx_sm(wsi, *(*buf)++)) {
-				lwsl_debug("client_rx_sm exited\n");
+				m = lws_rx_sm(wsi, 0);
+			if (m < 0)
 				return -1;
-			}
-			len--;
+			continue;
 		}
-		lwsl_debug("%s: finished with %ld\n", __func__, (long)len);
-		return 0;
-	default:
-		break;
+		/* account for what we're using in rxflow buffer */
+		if (wsi->rxflow_buffer)
+			wsi->rxflow_pos++;
+
+		if (lws_client_rx_sm(wsi, *(*buf)++)) {
+			lwsl_debug("client_rx_sm exited\n");
+			return -1;
+		}
+		len--;
 	}
+	lwsl_debug("%s: finished with %ld\n", __func__, (long)len);
 
 	return 0;
 }
@@ -163,7 +159,7 @@ lws_client_socket_service(struct lws *wsi, struct lws_pollfd *pollfd,
 			struct lws *w = lws_container_of(d, struct lws,
 						  dll_client_transaction_queue);
 
-			if (w->mode == LWSCM_WSCL_ISSUE_HANDSHAKE2) {
+			if (lwsi_state(w) == LRS_H1C_ISSUE_HANDSHAKE2) {
 				/*
 				 * pollfd has the master sockfd in it... we
 				 * need to use that in HANDSHAKE2 to understand
@@ -179,9 +175,9 @@ lws_client_socket_service(struct lws *wsi, struct lws_pollfd *pollfd,
 		return 0;
 	}
 
-	switch (wsi->mode) {
+	switch (lwsi_state(wsi)) {
 
-	case LWSCM_WSCL_WAITING_CONNECT:
+	case LRS_WAITING_CONNECT:
 
 		/*
 		 * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE
@@ -199,9 +195,9 @@ lws_client_socket_service(struct lws *wsi, struct lws_pollfd *pollfd,
 
 #if defined(LWS_WITH_SOCKS5)
 	/* SOCKS Greeting Reply */
-	case LWSCM_WSCL_WAITING_SOCKS_GREETING_REPLY:
-	case LWSCM_WSCL_WAITING_SOCKS_AUTH_REPLY:
-	case LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY:
+	case LRS_WAITING_SOCKS_GREETING_REPLY:
+	case LRS_WAITING_SOCKS_AUTH_REPLY:
+	case LRS_WAITING_SOCKS_CONNECT_REPLY:
 
 		/* handle proxy hung up on us */
 
@@ -221,16 +217,16 @@ lws_client_socket_service(struct lws *wsi, struct lws_pollfd *pollfd,
 			goto bail3;
 		}
 
-		switch (wsi->mode) {
+		switch (lwsi_state(wsi)) {
 
-		case LWSCM_WSCL_WAITING_SOCKS_GREETING_REPLY:
+		case LRS_WAITING_SOCKS_GREETING_REPLY:
 			if (pt->serv_buf[0] != SOCKS_VERSION_5)
 				goto socks_reply_fail;
 
 			if (pt->serv_buf[1] == SOCKS_AUTH_NO_AUTH) {
 				lwsl_client("SOCKS GR: No Auth Method\n");
 				socks_generate_msg(wsi, SOCKS_MSG_CONNECT, &len);
-				conn_mode = LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY;
+				conn_mode = LRS_WAITING_SOCKS_CONNECT_REPLY;
 				pending_timeout =
 				   PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY;
 				goto socks_send;
@@ -241,21 +237,21 @@ lws_client_socket_service(struct lws *wsi, struct lws_pollfd *pollfd,
 				socks_generate_msg(wsi,
 						   SOCKS_MSG_USERNAME_PASSWORD,
 						   &len);
-				conn_mode = LWSCM_WSCL_WAITING_SOCKS_AUTH_REPLY;
+				conn_mode = LRS_WAITING_SOCKS_AUTH_REPLY;
 				pending_timeout =
 				      PENDING_TIMEOUT_AWAITING_SOCKS_AUTH_REPLY;
 				goto socks_send;
 			}
 			goto socks_reply_fail;
 
-		case LWSCM_WSCL_WAITING_SOCKS_AUTH_REPLY:
+		case LRS_WAITING_SOCKS_AUTH_REPLY:
 			if (pt->serv_buf[0] != SOCKS_SUBNEGOTIATION_VERSION_1 ||
 			    pt->serv_buf[1] != SOCKS_SUBNEGOTIATION_STATUS_SUCCESS)
 				goto socks_reply_fail;
 
 			lwsl_client("SOCKS password OK, sending connect\n");
 			socks_generate_msg(wsi, SOCKS_MSG_CONNECT, &len);
-			conn_mode = LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY;
+			conn_mode = LRS_WAITING_SOCKS_CONNECT_REPLY;
 			pending_timeout =
 				   PENDING_TIMEOUT_AWAITING_SOCKS_CONNECT_REPLY;
 socks_send:
@@ -267,7 +263,7 @@ socks_send:
 			}
 
 			lws_set_timeout(wsi, pending_timeout, AWAITING_TIMEOUT);
-			wsi->mode = conn_mode;
+			lwsi_set_state(wsi, conn_mode);
 			break;
 
 socks_reply_fail:
@@ -275,7 +271,7 @@ socks_reply_fail:
 				    pt->serv_buf[0], pt->serv_buf[1]);
 			goto bail3;
 
-		case LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY:
+		case LRS_WAITING_SOCKS_CONNECT_REPLY:
 			if (pt->serv_buf[0] != SOCKS_VERSION_5 ||
 			    pt->serv_buf[1] != SOCKS_REQUEST_REPLY_SUCCESS)
 				goto socks_reply_fail;
@@ -298,7 +294,7 @@ socks_reply_fail:
 		break;
 #endif
 
-	case LWSCM_WSCL_WAITING_PROXY_REPLY:
+	case LRS_WAITING_PROXY_REPLY:
 
 		/* handle proxy hung up on us */
 
@@ -333,7 +329,7 @@ socks_reply_fail:
 
 		/* fallthru */
 
-	case LWSCM_WSCL_ISSUE_HANDSHAKE:
+	case LRS_H1C_ISSUE_HANDSHAKE:
 
 		/*
 		 * we are under PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE
@@ -370,7 +366,7 @@ start_ws_handshake:
 
 		/* fallthru */
 
-	case LWSCM_WSCL_WAITING_SSL:
+	case LRS_WAITING_SSL:
 
 		if (wsi->use_ssl & LCCSCF_USE_SSL) {
 			n = lws_ssl_client_connect2(wsi, ebuf, sizeof(ebuf));
@@ -395,8 +391,8 @@ start_ws_handshake:
 			lwsl_info("client connection upgraded to h2\n");
 			lws_h2_configure_if_upgraded(wsi);
 
-			lws_union_transition(wsi, LWSCM_HTTP2_CLIENT);
-			wsi->state = LWSS_HTTP2_CLIENT_SEND_SETTINGS;
+			lws_role_transition(wsi, LWSI_ROLE_H2_CLIENT,
+					    LRS_H2_CLIENT_SEND_SETTINGS);
 
 			/* send the H2 preface to legitimize the connection */
 			if (lws_h2_issue_preface(wsi)) {
@@ -407,16 +403,16 @@ start_ws_handshake:
 			break;
 		}
 #endif
-		wsi->mode = LWSCM_WSCL_ISSUE_HANDSHAKE2;
+		lwsi_set_state(wsi, LRS_H1C_ISSUE_HANDSHAKE2);
 		lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_CLIENT_HS_SEND,
 				context->timeout_secs);
 
 		/* fallthru */
 
-	case LWSCM_WSCL_ISSUE_HANDSHAKE2:
+	case LRS_H1C_ISSUE_HANDSHAKE2:
 		p = lws_generate_client_handshake(wsi, p);
 		if (p == NULL) {
-			if (wsi->mode == LWSCM_RAW)
+			if (lwsi_role_raw(wsi))
 				return 0;
 
 			lwsl_err("Failed to generate handshake for client\n");
@@ -445,7 +441,7 @@ start_ws_handshake:
 		}
 
 		if (wsi->client_http_body_pending) {
-			wsi->mode = LWSCM_WSCL_ISSUE_HTTP_BODY;
+			lwsi_set_state(wsi, LRS_ISSUE_HTTP_BODY);
 			lws_set_timeout(wsi,
 					PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD,
 					context->timeout_secs);
@@ -457,7 +453,7 @@ start_ws_handshake:
 
 		goto client_http_body_sent;
 
-	case LWSCM_WSCL_ISSUE_HTTP_BODY:
+	case LRS_ISSUE_HTTP_BODY:
 		if (wsi->client_http_body_pending) {
 			lws_set_timeout(wsi,
 					PENDING_TIMEOUT_CLIENT_ISSUE_PAYLOAD,
@@ -469,12 +465,12 @@ client_http_body_sent:
 		/* prepare ourselves to do the parsing */
 		wsi->ah->parser_state = WSI_TOKEN_NAME_PART;
 		wsi->ah->lextable_pos = 0;
-		wsi->mode = LWSCM_WSCL_WAITING_SERVER_REPLY;
+		lwsi_set_state(wsi, LRS_WAITING_SERVER_REPLY);
 		lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE,
 				context->timeout_secs);
 		break;
 
-	case LWSCM_WSCL_WAITING_SERVER_REPLY:
+	case LRS_WAITING_SERVER_REPLY:
 		/*
 		 * handle server hanging up on us...
 		 * but if there is POLLIN waiting, handle that first
@@ -556,13 +552,6 @@ bail3:
 		lws_close_free_wsi(wsi, LWS_CLOSE_STATUS_NOSTATUS, "cbail3");
 		return -1;
 
-	case LWSCM_WSCL_WAITING_EXTENSION_CONNECT:
-		lwsl_ext("LWSCM_WSCL_WAITING_EXTENSION_CONNECT\n");
-		break;
-
-	case LWSCM_WSCL_PENDING_CANDIDATE_CHILD:
-		lwsl_ext("LWSCM_WSCL_PENDING_CANDIDATE_CHILD\n");
-		break;
 	default:
 		break;
 	}
@@ -598,8 +587,8 @@ lws_http_transaction_completed_client(struct lws *wsi)
 	if (user_callback_handle_rxflow(wsi_eff->protocol->callback,
 			wsi_eff, LWS_CALLBACK_COMPLETED_CLIENT_HTTP,
 			wsi_eff->user_space, NULL, 0)) {
-		lwsl_debug("%s: Completed call returned nonzero (mode %d)\n",
-						__func__, wsi_eff->mode);
+		lwsl_debug("%s: Completed call returned nonzero (role 0x%x)\n",
+						__func__, lwsi_role(wsi_eff));
 		return -1;
 	}
 
@@ -652,17 +641,16 @@ lws_http_transaction_completed_client(struct lws *wsi)
 
 	/*
 	 * H1: we can serialize the queued guys into into the same ah
-	 * (H2: everybody needs their own ah until STREAM_END)
+	 * H2: everybody needs their own ah until their own STREAM_END
 	 */
 
 	/* otherwise set ourselves up ready to go again */
-	wsi->state = LWSS_CLIENT_HTTP_ESTABLISHED;
+	lwsi_set_state(wsi, LRS_WAITING_SERVER_REPLY);
 	wsi->http.rx_content_length = 0;
 	wsi->hdr_parsing_completed = 0;
 
 	wsi->ah->parser_state = WSI_TOKEN_NAME_PART;
 	wsi->ah->lextable_pos = 0;
-	wsi->mode = LWSCM_WSCL_WAITING_SERVER_REPLY;
 
 	lws_set_timeout(wsi, PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE,
 			wsi->context->timeout_secs);
@@ -729,10 +717,12 @@ lws_client_interpret_server_handshake(struct lws *wsi)
 		/* we are being an http client...
 		 */
 		if (wsi->client_h2_alpn)
-			lws_union_transition(wsi, LWSCM_HTTP2_CLIENT_ACCEPTED);
+			lws_role_transition(wsi, LWSI_ROLE_H2_CLIENT,
+						LRS_ESTABLISHED);
 		else
-			lws_union_transition(wsi, LWSCM_HTTP_CLIENT_ACCEPTED);
-		wsi->state = LWSS_CLIENT_HTTP_ESTABLISHED;
+			lws_role_transition(wsi, LWSI_ROLE_H1_CLIENT,
+					    LRS_ESTABLISHED);
+
 		wsi->ah = ah;
 		ah->http_response = 0;
 	}
@@ -889,9 +879,10 @@ lws_client_interpret_server_handshake(struct lws *wsi)
 					ww->client_pipeline = 0;
 
 					/* go back to "trying to connect" state */
-					lws_union_transition(ww, LWSCM_HTTP_CLIENT);
+					lws_role_transition(ww,
+							LWSI_ROLE_H1_CLIENT,
+							LRS_UNCONNECTED);
 					ww->user_space = NULL;
-					ww->state = LWSS_CLIENT_UNCONNECTED;
 				} lws_end_foreach_dll_safe(d, d1);
 				lws_vhost_unlock(wsi->vhost);
 			}
@@ -1084,7 +1075,7 @@ lws_client_interpret_server_handshake(struct lws *wsi)
 	 */
 	n = 0;
 	/* keep client connection pre-bound protocol */
-	if (!(wsi->mode & LWSCM_FLAG_IMPLIES_CALLBACK_CLOSED_CLIENT_HTTP))
+	if (!lwsi_role_client(wsi))
 		wsi->protocol = NULL;
 
 	while (wsi->vhost->protocols[n].callback) {
@@ -1098,7 +1089,7 @@ lws_client_interpret_server_handshake(struct lws *wsi)
 
 	if (!wsi->vhost->protocols[n].callback) { /* no match */
 		/* if server, that's already fatal */
-		if (!(wsi->mode & LWSCM_FLAG_IMPLIES_CALLBACK_CLOSED_CLIENT_HTTP)) {
+		if (!lwsi_role_client(wsi)) {
 			lwsl_info("%s: fail protocol %s\n", __func__, p);
 			cce = "HS: Cannot match protocol";
 			goto bail2;
@@ -1336,8 +1327,7 @@ check_accept:
 	/* free up his parsing allocations */
 	lws_header_table_detach(wsi, 0);
 
-	lws_union_transition(wsi, LWSCM_WS_CLIENT);
-	wsi->state = LWSS_ESTABLISHED;
+	lws_role_transition(wsi, LWSI_ROLE_H1_CLIENT, LRS_ESTABLISHED);
 	lws_restart_ws_ping_pong_timer(wsi);
 
 	wsi->rxflow_change_to = LWS_RXFLOW_ALLOW;
@@ -1370,6 +1360,8 @@ check_accept:
 	}
 #endif
 
+	lwsi_set_role(wsi, LWSI_ROLE_WS1_CLIENT);
+
 	lwsl_debug("handshake OK for protocol %s\n", wsi->protocol->name);
 
 	/* call him back to inform him he is up */
@@ -1470,7 +1462,7 @@ lws_generate_client_handshake(struct lws *wsi, char *pkt)
 			return NULL;
 
 		lws_header_table_force_to_detachable_state(wsi);
-		lws_union_transition(wsi, LWSCM_RAW);
+		lws_role_transition(wsi, LWSI_ROLE_RAW_SOCKET, LRS_ESTABLISHED);
 		lws_header_table_detach(wsi, 1);
 
 		return NULL;
diff --git a/lib/client/ssl-client.c b/lib/client/ssl-client.c
index fbe7a9b70cea47d0b1a04eeb8bf1197af6b3b280..82bdb89617079dfbc2f33dd46fbfb313cf0a8983 100644
--- a/lib/client/ssl-client.c
+++ b/lib/client/ssl-client.c
@@ -29,8 +29,7 @@ lws_ssl_client_connect1(struct lws *wsi)
 
 	lws_latency_pre(context, wsi);
 	n = lws_tls_client_connect(wsi);
-	lws_latency(context, wsi,
-	  "SSL_connect LWSCM_WSCL_ISSUE_HANDSHAKE", n, n > 0);
+	lws_latency(context, wsi, "SSL_connect hs", n, n > 0);
 
 	switch (n) {
 	case LWS_SSL_CAPABLE_ERROR:
@@ -41,7 +40,7 @@ lws_ssl_client_connect1(struct lws *wsi)
 		lws_callback_on_writable(wsi);
 		/* fallthru */
 	case LWS_SSL_CAPABLE_MORE_SERVICE_READ:
-		wsi->mode = LWSCM_WSCL_WAITING_SSL;
+		lwsi_set_state(wsi, LRS_WAITING_SSL);
 		break;
 	case LWS_SSL_CAPABLE_MORE_SERVICE:
 		break;
@@ -55,13 +54,13 @@ lws_ssl_client_connect2(struct lws *wsi, char *errbuf, int len)
 {
 	int n = 0;
 
-	if (wsi->mode == LWSCM_WSCL_WAITING_SSL) {
+	if (lwsi_state(wsi) == LRS_WAITING_SSL) {
 		lws_latency_pre(wsi->context, wsi);
 
 		n = lws_tls_client_connect(wsi);
 		lwsl_debug("%s: SSL_connect says %d\n", __func__, n);
 		lws_latency(wsi->context, wsi,
-			    "SSL_connect LWSCM_WSCL_WAITING_SSL", n, n > 0);
+			    "SSL_connect LRS_WAITING_SSL", n, n > 0);
 
 		switch (n) {
 		case LWS_SSL_CAPABLE_ERROR:
@@ -73,7 +72,7 @@ lws_ssl_client_connect2(struct lws *wsi, char *errbuf, int len)
 			lws_callback_on_writable(wsi);
 			/* fallthru */
 		case LWS_SSL_CAPABLE_MORE_SERVICE_READ:
-			wsi->mode = LWSCM_WSCL_WAITING_SSL;
+			lwsi_set_state(wsi, LRS_WAITING_SSL);
 			/* fallthru */
 		case LWS_SSL_CAPABLE_MORE_SERVICE:
 			return 0;
diff --git a/lib/context.c b/lib/context.c
index 33dbbe075a2e4206d2cab90f00ca3e24896e0885..dca7e2949bffccb37b117b8dcec74348732083a1 100644
--- a/lib/context.c
+++ b/lib/context.c
@@ -966,7 +966,7 @@ lws_create_event_pipes(struct lws_context *context)
 			return 1;
 		}
 		wsi->context = context;
-		wsi->mode = LWSCM_EVENT_PIPE;
+		lwsi_set_role(wsi, LWSI_ROLE_EVENT_PIPE);
 		wsi->protocol = NULL;
 		wsi->tsi = n;
 		wsi->vhost = NULL;
diff --git a/lib/event-libs/libev.c b/lib/event-libs/libev.c
index 8f12fb7af927ab360c7d110a01fbe2653cefbb37..ca8992ccd6bd489aac807d34297917f3eac7cc75 100644
--- a/lib/event-libs/libev.c
+++ b/lib/event-libs/libev.c
@@ -185,7 +185,7 @@ lws_libev_accept(struct lws *new_wsi, lws_sock_file_fd_type desc)
 	if (!LWS_LIBEV_ENABLED(context))
 		return;
 
-	if (new_wsi->mode == LWSCM_RAW_FILEDESC)
+	if (lwsi_role(new_wsi) == LWSI_ROLE_RAW_FILE)
 		fd = desc.filefd;
 	else
 		fd = desc.sockfd;
diff --git a/lib/event-libs/libevent.c b/lib/event-libs/libevent.c
index 61af5d3f93376bdac8ba7d6411a031e406c6a3d5..ddd9dde5859a3700c8408413b3b930f9074fda06 100644
--- a/lib/event-libs/libevent.c
+++ b/lib/event-libs/libevent.c
@@ -174,7 +174,7 @@ lws_libevent_accept(struct lws *new_wsi, lws_sock_file_fd_type desc)
 	// Initialize the event
 	pt = &context->pt[(int)new_wsi->tsi];
 
-	if (new_wsi->mode == LWSCM_RAW_FILEDESC)
+	if (lwsi_role(new_wsi) == LWSI_ROLE_RAW_FILE)
 		fd = desc.filefd;
 	else
 		fd = desc.sockfd;
diff --git a/lib/event-libs/libuv.c b/lib/event-libs/libuv.c
index f84ed458788f68041bee5e7197a035807f94a942..15f93cc39ea76e939691ef77b584cc1e11880bf5 100644
--- a/lib/event-libs/libuv.c
+++ b/lib/event-libs/libuv.c
@@ -429,7 +429,7 @@ lws_libuv_accept(struct lws *wsi, lws_sock_file_fd_type desc)
 		return;
 
 	wsi->w_read.context = context;
-	if (wsi->mode == LWSCM_RAW_FILEDESC || wsi->event_pipe)
+	if (lwsi_role(wsi) == LWSI_ROLE_RAW_FILE || wsi->event_pipe)
 		uv_poll_init(pt->io_loop_uv, &wsi->w_read.uv_watcher,
 			     (int)(long long)desc.filefd);
 	else
@@ -543,7 +543,7 @@ lws_libuv_closewsi(uv_handle_t* handle)
 	 * We get called back here for every wsi that closes
 	 */
 
-	if (wsi->mode == LWSCM_SERVER_LISTENER &&
+	if (lwsi_role(wsi) == LWSI_ROLE_LISTEN_SOCKET &&
 	    wsi->context->deprecated) {
 		lspd = 1;
 		context->deprecation_pending_listen_close_count--;
diff --git a/lib/handshake.c b/lib/handshake.c
index 77d0cc1f4286c1bab8fb3821b4fbb61743df1a32..2a89fec0256689a40026b3097ba276b32d5df4ee 100644
--- a/lib/handshake.c
+++ b/lib/handshake.c
@@ -65,20 +65,17 @@ lws_read(struct lws *wsi, unsigned char *buf, lws_filepos_t len)
 	unsigned char *last_char, *oldbuf = buf;
 	lws_filepos_t body_chunk_len;
 	size_t n;
+
 #if defined(LWS_WITH_HTTP2)
-	int m;
-#endif
 
-	// lwsl_notice("%s: state %d\n", __func__, wsi->state);
+	if (lwsi_role_h2(wsi) &&
+	    !lwsi_role_ws(wsi) &&
+	    lwsi_state(wsi) != LRS_BODY) {
+		int m;
+
+		// lwsl_notice("%s: h2 path: wsistate 0x%x len %d\n", __func__,
+		//		wsi->wsistate, (int)len);
 
-	switch (wsi->state) {
-#if defined(LWS_WITH_HTTP2)
-	case LWSS_HTTP2_CLIENT_ESTABLISHED:
-	case LWSS_HTTP2_CLIENT_SEND_SETTINGS:
-	case LWSS_HTTP2_CLIENT_WAITING_TO_SEND_HEADERS:
-	case LWSS_HTTP2_AWAIT_CLIENT_PREFACE:
-	case LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS:
-	case LWSS_HTTP2_ESTABLISHED:
 		/*
 		 * wsi here is always the network connection wsi, not a stream
 		 * wsi.  Once we unpicked the framing we will find the right
@@ -143,23 +140,32 @@ lws_read(struct lws *wsi, unsigned char *buf, lws_filepos_t len)
 			len -= body_chunk_len;
 		}
 //		lwsl_debug("%s: used up block\n", __func__);
-		break;
+		goto read_ok;
+	}
 #endif
 
-	case LWSS_HTTP_ISSUING_FILE:
+	// lwsl_notice("%s: h1 path: wsi state 0x%x\n", __func__, lwsi_state(wsi));
+
+	switch (lwsi_state(wsi)) {
+
+	case LRS_ISSUING_FILE:
 		return 0;
 
-	case LWSS_CLIENT_HTTP_ESTABLISHED:
-		break;
+	case LRS_ESTABLISHED:
+
+		if (lwsi_role_non_ws_client(wsi))
+			break;
+
+		if (lwsi_role_ws(wsi))
+			goto ws_mode;
 
-	case LWSS_HTTP:
 		wsi->hdr_parsing_completed = 0;
 
 		/* fallthru */
 
-	case LWSS_HTTP_HEADERS:
+	case LRS_HEADERS:
 		if (!wsi->ah) {
-			lwsl_err("%s: LWSS_HTTP_HEADERS: NULL ah\n", __func__);
+			lwsl_err("%s: LRS_HEADERS: NULL ah\n", __func__);
 			assert(0);
 		}
 		lwsl_parser("issuing %d bytes to parser\n", (int)len);
@@ -173,7 +179,7 @@ lws_read(struct lws *wsi, unsigned char *buf, lws_filepos_t len)
 			goto bail;
 
 		/* we might have transitioned to RAW */
-		if (wsi->mode == LWSCM_RAW)
+		if (lwsi_role_raw(wsi))
 			 /* we gave the read buffer to RAW handler already */
 			goto read_ok;
 
@@ -191,13 +197,13 @@ lws_read(struct lws *wsi, unsigned char *buf, lws_filepos_t len)
 			/* More header content on the way */
 			goto read_ok;
 
-		switch (wsi->state) {
-			case LWSS_HTTP:
-			case LWSS_HTTP_HEADERS:
+		switch (lwsi_state(wsi)) {
+			case LRS_ESTABLISHED:
+			case LRS_HEADERS:
 				goto read_ok;
-			case LWSS_HTTP_ISSUING_FILE:
+			case LRS_ISSUING_FILE:
 				goto read_ok;
-			case LWSS_HTTP_BODY:
+			case LRS_BODY:
 				wsi->http.rx_content_remain =
 						wsi->http.rx_content_length;
 				if (wsi->http.rx_content_remain)
@@ -210,7 +216,7 @@ lws_read(struct lws *wsi, unsigned char *buf, lws_filepos_t len)
 		}
 		break;
 
-	case LWSS_HTTP_BODY:
+	case LRS_BODY:
 http_postbody:
 		//lwsl_notice("http post body\n");
 		while (len && wsi->http.rx_content_remain) {
@@ -281,24 +287,25 @@ postbody_completion:
 					goto bail;
 
 				if (wsi->http2_substream)
-					wsi->state = LWSS_HTTP2_ESTABLISHED;
+					lwsi_set_state(wsi, LRS_ESTABLISHED);
 			}
 
 			break;
 		}
 		break;
 
-	case LWSS_ESTABLISHED:
-	case LWSS_AWAITING_CLOSE_ACK:
-	case LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION:
-	case LWSS_SHUTDOWN:
-	case LWSS_SHUTDOWN | _LSF_POLLOUT | _LSF_CCB:
+	case LRS_AWAITING_CLOSE_ACK:
+	case LRS_WAITING_TO_SEND_CLOSE:
+	case LRS_SHUTDOWN:
+
+ws_mode:
+
 		if (lws_handshake_client(wsi, &buf, (size_t)len))
 			goto bail;
 
-		switch (wsi->mode) {
-		case LWSCM_WS_SERVING:
-		case LWSCM_HTTP2_WS_SERVING:
+		switch (lwsi_role(wsi)) {
+		case LWSI_ROLE_WS1_SERVER:
+		case LWSI_ROLE_WS2_SERVER:
 			/*
 			 * for h2 we are on the swsi
 			 */
@@ -311,17 +318,20 @@ postbody_completion:
 		}
 		break;
 
-	case LWSS_HTTP_DEFERRING_ACTION:
-		lwsl_debug("%s: LWSS_HTTP_DEFERRING_ACTION\n", __func__);
+	case LRS_DEFERRING_ACTION:
+		lwsl_debug("%s: LRS_DEFERRING_ACTION\n", __func__);
+		break;
+
+	case LRS_SSL_ACK_PENDING:
 		break;
 
-	case LWSS_DEAD_SOCKET:
-		lwsl_err("%s: Unhandled state LWSS_DEAD_SOCKET\n", __func__);
+	case LRS_DEAD_SOCKET:
+		lwsl_err("%s: Unhandled state LRS_DEAD_SOCKET\n", __func__);
 		assert(0);
 		/* fallthru */
 
 	default:
-		lwsl_err("%s: Unhandled state %d\n", __func__, wsi->state);
+		lwsl_err("%s: Unhandled state %d\n", __func__, lwsi_state(wsi));
 		goto bail;
 	}
 
diff --git a/lib/header.c b/lib/header.c
index 7c2ef9b71b0d9d2494bc6f0cce3e2ebc93602a0e..1be0f7b99dd8cf0299e49d3330ae49ddb50fef8d 100644
--- a/lib/header.c
+++ b/lib/header.c
@@ -38,7 +38,7 @@ lws_add_http_header_by_name(struct lws *wsi, const unsigned char *name,
 			    unsigned char **p, unsigned char *end)
 {
 #ifdef LWS_WITH_HTTP2
-	if (wsi->mode == LWSCM_HTTP2_SERVING || wsi->mode == LWSCM_HTTP2_CLIENT_ACCEPTED)
+	if (lwsi_role_h2(wsi))
 		return lws_add_http2_header_by_name(wsi, name,
 						    value, length, p, end);
 #else
@@ -66,7 +66,7 @@ int lws_finalize_http_header(struct lws *wsi, unsigned char **p,
 			     unsigned char *end)
 {
 #ifdef LWS_WITH_HTTP2
-	if (wsi->mode == LWSCM_HTTP2_SERVING || wsi->mode == LWSCM_HTTP2_CLIENT_ACCEPTED)
+	if (lwsi_role_h2(wsi))
 		return 0;
 #else
 	(void)wsi;
@@ -105,7 +105,7 @@ lws_add_http_header_by_token(struct lws *wsi, enum lws_token_indexes token,
 {
 	const unsigned char *name;
 #ifdef LWS_WITH_HTTP2
-	if (wsi->mode == LWSCM_HTTP2_SERVING || wsi->mode == LWSCM_HTTP2_CLIENT_ACCEPTED)
+	if (lwsi_role_h2(wsi))
 		return lws_add_http2_header_by_token(wsi, token, value,
 						     length, p, end);
 #endif
@@ -201,7 +201,7 @@ lws_add_http_header_status(struct lws *wsi, unsigned int _code,
 #endif
 
 #ifdef LWS_WITH_HTTP2
-	if (wsi->mode == LWSCM_HTTP2_SERVING)
+	if (lwsi_role_h2(wsi))
 		return lws_add_http2_header_status(wsi, code, p, end);
 #endif
 	if (code >= 400 && code < (400 + ARRAY_SIZE(err400)))
diff --git a/lib/http2/http2.c b/lib/http2/http2.c
index 0cbf996eb21375775c4b8ae90efae17602e25afe..0ea20ff3d3e3ac1555559a8b1d3b9d7f749a5650 100644
--- a/lib/http2/http2.c
+++ b/lib/http2/http2.c
@@ -197,8 +197,8 @@ lws_wsi_server_new(struct lws_vhost *vh, struct lws *parent_wsi,
 	wsi->h2.tx_cr = nwsi->h2.h2n->set.s[H2SET_INITIAL_WINDOW_SIZE];
 	wsi->h2.peer_tx_cr_est = nwsi->vhost->set.s[H2SET_INITIAL_WINDOW_SIZE];
 
-	wsi->state = LWSS_HTTP2_ESTABLISHED;
-	wsi->mode = parent_wsi->mode;
+	lwsi_set_state(wsi, LRS_ESTABLISHED);
+	lwsi_set_role(wsi, lwsi_role(parent_wsi));
 
 	wsi->protocol = &vh->protocols[0];
 	if (lws_ensure_user_space(wsi))
@@ -258,8 +258,9 @@ lws_wsi_h2_adopt(struct lws *parent_wsi, struct lws *wsi)
 	if (lws_ensure_user_space(wsi))
 		goto bail1;
 
-	wsi->state = LWSS_HTTP2_CLIENT_WAITING_TO_SEND_HEADERS;
-	wsi->mode = LWSCM_HTTP2_CLIENT_ACCEPTED;
+	lwsi_set_role(wsi, LWSI_ROLE_H2_CLIENT);
+	lwsi_set_state(wsi, LRS_H2_WAITING_TO_SEND_HEADERS);
+
 	lws_callback_on_writable(wsi);
 
 	wsi->vhost->conn_stats.h2_subs++;
@@ -289,8 +290,9 @@ int lws_h2_issue_preface(struct lws *wsi)
 		(int)strlen(preface))
 		return 1;
 
-	wsi->state = LWSS_HTTP2_CLIENT_ESTABLISHED;
-	wsi->mode = LWSCM_HTTP2_CLIENT_ACCEPTED;
+	lwsi_set_role(wsi, LWSI_ROLE_H2_CLIENT);
+	lwsi_set_state(wsi, LRS_H2_WAITING_TO_SEND_HEADERS);
+
 	h2n->count = 0;
 	wsi->h2.tx_cr = 65535;
 
@@ -653,8 +655,8 @@ int lws_h2_do_pps_send(struct lws *wsi)
 			goto bail;
 		}
 		/* this is the end of the preface dance then? */
-		if (wsi->state == LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS) {
-			wsi->state = LWSS_HTTP2_ESTABLISHED;
+		if (lwsi_state(wsi) == LRS_H2_AWAIT_SETTINGS) {
+			lwsi_set_state(wsi, LRS_ESTABLISHED);
 			wsi->http.fop_fd = NULL;
 			if (lws_is_ssl(lws_get_network_wsi(wsi)))
 				break;
@@ -825,7 +827,7 @@ lws_h2_parse_frame_header(struct lws *wsi)
 		lwsl_notice("received oversize frame %d\n", h2n->length);
 		lws_h2_goaway(wsi, H2_ERR_FRAME_SIZE_ERROR,
 			      "Peer ignored our frame size setting");
-		return 0;
+		return 1;
 	}
 
 	if (h2n->swsi)
@@ -1198,11 +1200,11 @@ lws_h2_parse_end_of_frame(struct lws *wsi)
 
 			assert(lws_h2_wsi_from_id(wsi, 1) == h2n->swsi);
 
-			wsi->state = LWSS_HTTP2_CLIENT_WAITING_TO_SEND_HEADERS;
-			wsi->mode = LWSCM_HTTP2_CLIENT_ACCEPTED;
+			lwsi_set_role(wsi, LWSI_ROLE_H2_CLIENT);
+			lwsi_set_state(wsi, LRS_H2_WAITING_TO_SEND_HEADERS);
 
-			h2n->swsi->state = LWSS_HTTP2_CLIENT_WAITING_TO_SEND_HEADERS;
-			h2n->swsi->mode = LWSCM_HTTP2_CLIENT_ACCEPTED;
+			lwsi_set_role(h2n->swsi, LWSI_ROLE_H2_CLIENT);
+			lwsi_set_state(h2n->swsi, LRS_H2_WAITING_TO_SEND_HEADERS);
 
 			/* pass on the initial headers to SID 1 */
 			h2n->swsi->ah = wsi->ah;
@@ -1244,7 +1246,7 @@ lws_h2_parse_end_of_frame(struct lws *wsi)
 				struct lws *w = lws_container_of(d, struct lws,
 							  dll_client_transaction_queue);
 
-				if (w->mode == LWSCM_WSCL_ISSUE_HANDSHAKE2) {
+				if (lwsi_state(w) == LRS_H1C_ISSUE_HANDSHAKE2) {
 					lwsl_info("%s: client pipeq %p to be h2\n",
 							__func__, w);
 					/* remove ourselves from the client queue */
@@ -1400,7 +1402,7 @@ lws_h2_parse_end_of_frame(struct lws *wsi)
 
 		wsi->vhost->conn_stats.h2_trans++;
 
-		h2n->swsi->state = LWSS_HTTP2_DEFERRING_ACTION;
+		lwsi_set_state(h2n->swsi, LRS_DEFERRING_ACTION);
 		lws_callback_on_writable(h2n->swsi);
 		break;
 
@@ -1434,8 +1436,14 @@ lws_h2_parse_end_of_frame(struct lws *wsi)
 		    h2n->flags & LWS_H2_FLAG_END_STREAM) {
 			lwsl_info("%s: %p: DATA: end stream\n", __func__, h2n->swsi);
 
-			if (h2n->swsi->h2.h2_state == LWS_H2_STATE_OPEN)
+			if (h2n->swsi->h2.h2_state == LWS_H2_STATE_OPEN) {
 				lws_h2_state(h2n->swsi, LWS_H2_STATE_HALF_CLOSED_REMOTE);
+		//		lws_h2_rst_stream(h2n->swsi, H2_ERR_NO_ERROR,
+		//				  "client done");
+
+		//		if (lws_http_transaction_completed_client(h2n->swsi))
+		//			lwsl_debug("tx completed returned close\n");
+			}
 
 			//if (h2n->swsi->h2.h2_state == LWS_H2_STATE_HALF_CLOSED_LOCAL)
 			{
@@ -1571,8 +1579,8 @@ lws_h2_parser(struct lws *wsi, unsigned char *in, lws_filepos_t inlen,
 
 		// lwsl_notice("%s: 0x%x\n", __func__, c);
 
-		switch (wsi->state) {
-		case LWSS_HTTP2_AWAIT_CLIENT_PREFACE:
+		switch (lwsi_state(wsi)) {
+		case LRS_H2_AWAIT_PREFACE:
 			if (preface[h2n->count++] != c)
 				goto fail;
 
@@ -1580,7 +1588,7 @@ lws_h2_parser(struct lws *wsi, unsigned char *in, lws_filepos_t inlen,
 				break;
 
 			lwsl_info("http2: %p: established\n", wsi);
-			wsi->state = LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS;
+			lwsi_set_state(wsi, LRS_H2_AWAIT_SETTINGS);
 			h2n->count = 0;
 			wsi->h2.tx_cr = 65535;
 
@@ -1595,10 +1603,9 @@ lws_h2_parser(struct lws *wsi, unsigned char *in, lws_filepos_t inlen,
 			lws_pps_schedule(wsi, pps);
 			break;
 
-		case LWSS_HTTP2_CLIENT_WAITING_TO_SEND_HEADERS:
-		case LWSS_HTTP2_CLIENT_ESTABLISHED:
-		case LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS:
-		case LWSS_HTTP2_ESTABLISHED:
+		case LRS_H2_WAITING_TO_SEND_HEADERS:
+		case LRS_ESTABLISHED:
+		case LRS_H2_AWAIT_SETTINGS:
 			if (h2n->frame_state != LWS_H2_FRAME_HEADER_LENGTH)
 				goto try_frame_start;
 
@@ -1700,6 +1707,8 @@ lws_h2_parser(struct lws *wsi, unsigned char *in, lws_filepos_t inlen,
 
 			case LWS_H2_FRAME_TYPE_DATA:
 
+				// lwsl_notice("%s: LWS_H2_FRAME_TYPE_DATA\n", __func__);
+
 				/* let the network wsi live a bit longer if subs are active...
 				 * our frame may take a long time to chew through */
 				if (!wsi->ws_over_h2_count)
@@ -1708,9 +1717,11 @@ lws_h2_parser(struct lws *wsi, unsigned char *in, lws_filepos_t inlen,
 				if (!h2n->swsi)
 					break;
 
-				if (h2n->swsi->state == LWSS_HTTP2_ESTABLISHED) {
-					h2n->swsi->state = LWSS_HTTP_BODY;
-					lwsl_notice("%s: setting swsi %p to LWSS_HTTP_BODY\n", __func__, h2n->swsi);
+				if (lwsi_role_http(h2n->swsi) &&
+				    lwsi_state(h2n->swsi) == LRS_ESTABLISHED) {
+					lwsi_set_state(h2n->swsi, LRS_BODY);
+					lwsl_info("%s: setting swsi %p to LRS_BODY\n",
+							__func__, h2n->swsi);
 				}
 
 				if (lws_hdr_total_length(h2n->swsi,
@@ -1862,9 +1873,11 @@ lws_h2_parser(struct lws *wsi, unsigned char *in, lws_filepos_t inlen,
 			}
 
 frame_end:
-			if (h2n->count > h2n->length)
+			if (h2n->count > h2n->length) {
 				lwsl_notice("%s: count > length %d %d\n",
 					    __func__, h2n->count, h2n->length);
+				goto fail;
+			}
 			if (h2n->count != h2n->length)
 				break;
 
@@ -1914,8 +1927,10 @@ try_frame_start:
 				if (lws_h2_parse_frame_header(wsi))
 					goto fail;
 			break;
-		}
 
+		default:
+			break;
+		}
 	}
 
 	*inused = in - oldin;
@@ -2031,7 +2046,7 @@ lws_h2_client_handshake(struct lws *wsi)
 	}
 
 	lws_h2_state(wsi, LWS_H2_STATE_OPEN);
-	wsi->state = LWSS_HTTP2_CLIENT_ESTABLISHED;
+	lwsi_set_state(wsi, LRS_ESTABLISHED);
 
 	return 0;
 }
@@ -2079,7 +2094,7 @@ lws_h2_ws_handshake(struct lws *wsi)
 	 * mode / state of the nwsi will get the h2 processing done.
 	 */
 
-	wsi->state = LWSS_ESTABLISHED;
+	lwsi_set_state(wsi, LRS_ESTABLISHED);
 	wsi->lws_rx_parse_state = LWS_RXPS_NEW;
 
 	uri_ptr = lws_hdr_simple_ptr(wsi, WSI_TOKEN_HTTP_COLON_PATH);
diff --git a/lib/http2/ssl-http2.c b/lib/http2/ssl-http2.c
index 035708fc431fdedac1f3516c9e08fcd404850cb0..41dc1127b7ead6b65ddc7634a283ff83a6060006 100644
--- a/lib/http2/ssl-http2.c
+++ b/lib/http2/ssl-http2.c
@@ -132,8 +132,7 @@ int lws_h2_configure_if_upgraded(struct lws *wsi)
 
 	ah = wsi->ah;
 
-	lws_union_transition(wsi, LWSCM_HTTP2_SERVING);
-	wsi->state = LWSS_HTTP2_AWAIT_CLIENT_PREFACE;
+	lws_role_transition(wsi, LWSI_ROLE_H2_SERVER, LRS_H2_AWAIT_PREFACE);
 
 	/* http2 union member has http union struct at start */
 	wsi->ah = ah;
diff --git a/lib/libwebsockets.c b/lib/libwebsockets.c
index 83e8e74511409594cf737ae6d20c97e5d2639130..563b594af7a9dde1fe24f9a67f06640ed46c310e 100644
--- a/lib/libwebsockets.c
+++ b/lib/libwebsockets.c
@@ -58,6 +58,22 @@ static const char * const log_level_names[] = {
 };
 #endif
 
+#if defined (_DEBUG)
+void lwsi_set_role(struct lws *wsi, lws_wsi_state_t role)
+{
+	wsi->wsistate = (wsi->wsistate & (~LWSI_ROLE_MASK)) | role;
+
+	lwsl_debug("lwsi_set_role(%p, 0x%x)\n", wsi, wsi->wsistate);
+}
+
+void lwsi_set_state(struct lws *wsi, lws_wsi_state_t lrs)
+{
+	wsi->wsistate = (wsi->wsistate & (~LRS_MASK)) | lrs;
+
+	lwsl_debug("lwsi_set_state(%p, 0x%x)\n", wsi, wsi->wsistate);
+}
+#endif
+
 void
 __lws_free_wsi(struct lws *wsi)
 {
@@ -588,7 +604,7 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, const char *
 		wsi->child_list = NULL;
 	}
 
-	if (wsi->mode == LWSCM_RAW_FILEDESC) {
+	if (lwsi_role(wsi) == LWSI_ROLE_RAW_FILE) {
 		lws_remove_child_from_any_parent(wsi);
 		__remove_wsi_socket_from_fds(wsi);
 		wsi->protocol->callback(wsi, LWS_CALLBACK_RAW_CLOSE_FILE,
@@ -596,10 +612,10 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, const char *
 		goto async_close;
 	}
 
-	wsi->state_pre_close = wsi->state;
+	wsi->wsistate_pre_close = wsi->wsistate;
 
 #ifdef LWS_WITH_CGI
-	if (wsi->mode == LWSCM_CGI) {
+	if (lwsi_role(wsi) == LWSI_ROLE_CGI) {
 		/* we are not a network connection, but a handler for CGI io */
 		if (wsi->parent && wsi->parent->cgi) {
 
@@ -622,16 +638,14 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, const char *
 	lws_client_stash_destroy(wsi);
 #endif
 
-	if (wsi->mode == LWSCM_RAW) {
+	if (lwsi_role(wsi) == LWSI_ROLE_RAW_SOCKET) {
 		wsi->protocol->callback(wsi,
 			LWS_CALLBACK_RAW_CLOSE, wsi->user_space, NULL, 0);
 		wsi->socket_is_permanently_unusable = 1;
 		goto just_kill_connection;
 	}
 
-	if ((wsi->mode == LWSCM_HTTP_SERVING_ACCEPTED ||
-	     wsi->mode == LWSCM_HTTP2_SERVING) &&
-	    wsi->http.fop_fd != NULL) {
+	if (lwsi_role_http_server(wsi) && wsi->http.fop_fd != NULL) {
 		lws_vfs_file_close(&wsi->http.fop_fd);
 		wsi->vhost->protocols->callback(wsi,
 			LWS_CALLBACK_CLOSED_HTTP, wsi->user_space, NULL, 0);
@@ -639,29 +653,29 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, const char *
 	}
 	if (wsi->socket_is_permanently_unusable ||
 	    reason == LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY ||
-	    wsi->state == LWSS_SHUTDOWN)
+	    lwsi_state(wsi) == LRS_SHUTDOWN)
 		goto just_kill_connection;
 
-	switch (wsi->state_pre_close) {
-	case LWSS_DEAD_SOCKET:
+	switch (wsi->wsistate_pre_close & LRS_MASK) {
+	case LRS_DEAD_SOCKET:
 		return;
 
 	/* we tried the polite way... */
-	case LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION:
-	case LWSS_AWAITING_CLOSE_ACK:
+	case LRS_WAITING_TO_SEND_CLOSE:
+	case LRS_AWAITING_CLOSE_ACK:
 		goto just_kill_connection;
 
-	case LWSS_FLUSHING_SEND_BEFORE_CLOSE:
+	case LRS_FLUSHING_BEFORE_CLOSE:
 		if (wsi->trunc_len) {
 			lws_callback_on_writable(wsi);
 			return;
 		}
-		lwsl_info("%p: end FLUSHING_STORED_SEND_BEFORE_CLOSE\n", wsi);
+		lwsl_info("%p: end LRS_FLUSHING_BEFORE_CLOSE\n", wsi);
 		goto just_kill_connection;
 	default:
 		if (wsi->trunc_len) {
-			lwsl_info("%p: FLUSHING_STORED_SEND_BEFORE_CLOSE\n", wsi);
-			wsi->state = LWSS_FLUSHING_SEND_BEFORE_CLOSE;
+			lwsl_info("%p: LRS_FLUSHING_BEFORE_CLOSE\n", wsi);
+			lwsi_set_state(wsi, LRS_FLUSHING_BEFORE_CLOSE);
 			__lws_set_timeout(wsi,
 				PENDING_FLUSH_STORED_SEND_BEFORE_CLOSE, 5);
 			return;
@@ -669,13 +683,11 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, const char *
 		break;
 	}
 
-	if (wsi->mode == LWSCM_WSCL_WAITING_CONNECT ||
-	    wsi->mode == LWSCM_WSCL_ISSUE_HANDSHAKE)
+	if (lwsi_state(wsi) == LRS_WAITING_CONNECT ||
+	    lwsi_state(wsi) == LRS_H1C_ISSUE_HANDSHAKE)
 		goto just_kill_connection;
 
-	if (!wsi->told_user_closed &&
-	    (wsi->mode == LWSCM_HTTP_SERVING ||
-	     wsi->mode == LWSCM_HTTP2_SERVING)) {
+	if (!wsi->told_user_closed && lwsi_role_http_server(wsi)) {
 		if (wsi->user_space && wsi->protocol_bind_balance) {
 			wsi->vhost->protocols->callback(wsi,
 						LWS_CALLBACK_HTTP_DROP_PROTOCOL,
@@ -731,14 +743,14 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, const char *
 	 * add any necessary version-specific stuff.  If the write fails,
 	 * no worries we are closing anyway.  If we didn't initiate this
 	 * close, then our state has been changed to
-	 * LWSS_RETURNED_CLOSE_ALREADY and we will skip this.
+	 * LRS_RETURNED_CLOSE and we will skip this.
 	 *
 	 * Likewise if it's a second call to close this connection after we
 	 * sent the close indication to the peer already, we are in state
-	 * LWSS_AWAITING_CLOSE_ACK and will skip doing this a second time.
+	 * LRS_AWAITING_CLOSE_ACK and will skip doing this a second time.
 	 */
 
-	if (lws_state_is_ws(wsi->state_pre_close) &&
+	if ((wsi->wsistate_pre_close & LWSIFR_WS) &&
 	    (wsi->ws->close_in_ping_buffer_len || /* already a reason */
 	     (reason != LWS_CLOSE_STATUS_NOSTATUS &&
 	     (reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY)))) {
@@ -755,7 +767,7 @@ __lws_close_free_wsi(struct lws *wsi, enum lws_close_status reason, const char *
 
 		lwsl_debug("waiting for chance to send close\n");
 		wsi->waiting_to_send_close_frame = 1;
-		wsi->state = LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION;
+		lwsi_set_state(wsi, LRS_WAITING_TO_SEND_CLOSE);
 		__lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_SEND, 5);
 		lws_callback_on_writable(wsi);
 
@@ -862,21 +874,19 @@ just_kill_connection:
 		wsi->protocol_bind_balance = 0;
 	}
 
-	if ((wsi->mode == LWSCM_WSCL_WAITING_SERVER_REPLY ||
-	     wsi->mode == LWSCM_WSCL_WAITING_CONNECT) &&
-	    !wsi->already_did_cce)
+	if ((lwsi_state(wsi) == LRS_WAITING_SERVER_REPLY ||
+	     lwsi_state(wsi) == LRS_WAITING_CONNECT) && !wsi->already_did_cce)
 		wsi->protocol->callback(wsi,
-				LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
+				        LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
 						wsi->user_space, NULL, 0);
 
-	if (wsi->mode & LWSCM_FLAG_IMPLIES_CALLBACK_CLOSED_CLIENT_HTTP) {
+	if (lwsi_role_client(wsi) && !(lwsi_state(wsi) & LWSIFS_NOTEST)) {
 		const struct lws_protocols *pro = wsi->protocol;
 
 		if (!wsi->protocol)
 			pro = &wsi->vhost->protocols[0];
-		pro->callback(wsi,
-					LWS_CALLBACK_CLOSED_CLIENT_HTTP,
-						  wsi->user_space, NULL, 0);
+		pro->callback(wsi, LWS_CALLBACK_CLOSED_CLIENT_HTTP,
+			      wsi->user_space, NULL, 0);
 		wsi->told_user_closed = 1;
 	}
 
@@ -889,10 +899,10 @@ just_kill_connection:
 	 * for the POLLIN to show a zero-size rx before coming back and doing
 	 * the actual close.
 	 */
-	if (wsi->mode != LWSCM_RAW &&
-	    !(wsi->mode & LWSCM_FLAG_IMPLIES_CALLBACK_CLOSED_CLIENT_HTTP) &&
-	    wsi->state != LWSS_SHUTDOWN &&
-	    wsi->state != LWSS_CLIENT_UNCONNECTED &&
+	if (lwsi_role(wsi) != LWSI_ROLE_RAW_SOCKET &&
+	    !lwsi_role_client(wsi) &&
+	    lwsi_state(wsi) != LRS_SHUTDOWN &&
+	    lwsi_state(wsi) != LRS_UNCONNECTED &&
 	    reason != LWS_CLOSE_STATUS_NOSTATUS_CONTEXT_DESTROY &&
 	    !wsi->socket_is_permanently_unusable) {
 
@@ -910,9 +920,9 @@ just_kill_connection:
 	} else
 #endif
 		{
-			lwsl_info("%s: shutdown conn: %p (sock %d, state %d)\n",
+			lwsl_info("%s: shutdown conn: %p (sock %d, state 0x%x)\n",
 				  __func__, wsi, (int)(long)wsi->desc.sockfd,
-				  wsi->state);
+				  lwsi_state(wsi));
 			if (!wsi->socket_is_permanently_unusable &&
 			    lws_sockfd_valid(wsi->desc.sockfd)) {
 				wsi->socket_is_permanently_unusable = 1;
@@ -920,8 +930,8 @@ just_kill_connection:
 			}
 		}
 		if (n)
-			lwsl_debug("closing: shutdown (state %d) ret %d\n",
-				   wsi->state, LWS_ERRNO);
+			lwsl_debug("closing: shutdown (state 0x%x) ret %d\n",
+				   lwsi_state(wsi), LWS_ERRNO);
 
 		/*
 		 * This causes problems on WINCE / ESP32 with disconnection
@@ -931,10 +941,10 @@ just_kill_connection:
 		/* libuv: no event available to guarantee completion */
 		if (!wsi->socket_is_permanently_unusable &&
 		    lws_sockfd_valid(wsi->desc.sockfd) &&
-		    wsi->state != ((wsi->state & ~0x1f) | LWSS_SHUTDOWN) &&
+		    lwsi_state(wsi) != LRS_SHUTDOWN &&
 		    !LWS_LIBUV_ENABLED(context)) {
 			__lws_change_pollfd(wsi, LWS_POLLOUT, LWS_POLLIN);
-			wsi->state = (wsi->state & ~0x1f) | LWSS_SHUTDOWN;
+			lwsi_set_state(wsi, LRS_SHUTDOWN);
 			__lws_set_timeout(wsi, PENDING_TIMEOUT_SHUTDOWN_FLUSH,
 					context->timeout_secs);
 
@@ -967,13 +977,10 @@ just_kill_connection:
 	else
 		lws_same_vh_protocol_remove(wsi);
 
-	wsi->state = LWSS_DEAD_SOCKET;
+	lwsi_set_state(wsi, LRS_DEAD_SOCKET);
 	lws_free_set_NULL(wsi->rxflow_buffer);
 
-	if (lws_state_is_ws(wsi->state_pre_close) ||
-	    wsi->mode == LWSCM_WS_SERVING ||
-	    wsi->mode == LWSCM_HTTP2_WS_SERVING ||
-	    wsi->mode == LWSCM_WS_CLIENT) {
+	if ((wsi->wsistate_pre_close & LWSIFR_WS) || lwsi_role_ws(wsi)) {
 
 		if (wsi->ws->rx_draining_ext) {
 			struct lws **w = &pt->rx_draining_ext_list;
@@ -1016,24 +1023,23 @@ just_kill_connection:
 
 	/* tell the user it's all over for this guy */
 
-	if (wsi->protocol &&
-	    !wsi->told_user_closed &&
+	if (wsi->protocol && !wsi->told_user_closed &&
 	    wsi->protocol->callback &&
-	    wsi->mode != LWSCM_RAW &&
-	    (wsi->state_pre_close & _LSF_CCB)) {
-		if (wsi->mode == LWSCM_WS_CLIENT)
+	    lwsi_role(wsi) != LWSI_ROLE_RAW_SOCKET &&
+	    !(wsi->wsistate_pre_close & LWSIFS_NOTEST)) {
+		if (lwsi_role_client(wsi))
 			wsi->protocol->callback(wsi, LWS_CALLBACK_CLIENT_CLOSED,
 						wsi->user_space, NULL, 0);
 		else
 			wsi->protocol->callback(wsi, LWS_CALLBACK_CLOSED,
 						wsi->user_space, NULL, 0);
-	} else if (wsi->mode == LWSCM_HTTP_SERVING_ACCEPTED) {
+	} else if (lwsi_role_http_server(wsi)) {
 		lwsl_debug("calling back CLOSED_HTTP\n");
 		wsi->vhost->protocols->callback(wsi, LWS_CALLBACK_CLOSED_HTTP,
 					       wsi->user_space, NULL, 0 );
 	} else
-		lwsl_debug("not calling back closed mode=%d state=%d\n",
-			   wsi->mode, wsi->state_pre_close);
+		lwsl_debug("not calling back closed role=0x%x state=0x%x\n",
+			   lwsi_role(wsi), wsi->wsistate_pre_close);
 
 	/* deallocate any active extension contexts */
 
@@ -1051,8 +1057,7 @@ async_close:
 	wsi->socket_is_permanently_unusable = 1;
 
 #ifdef LWS_WITH_LIBUV
-	if (!wsi->parent_carries_io &&
-	    lws_sockfd_valid(wsi->desc.sockfd))
+	if (!wsi->parent_carries_io && lws_sockfd_valid(wsi->desc.sockfd))
 		if (LWS_LIBUV_ENABLED(context)) {
 			if (wsi->listener) {
 				lwsl_debug("%s: stop listener poll\n", __func__);
@@ -1712,7 +1717,7 @@ lws_rx_flow_control(struct lws *wsi, int _enable)
 
 	wsi->rxflow_change_to = LWS_RXFLOW_PENDING_CHANGE | !wsi->rxflow_bitmap;
 
-	lwsl_info("%s: 0x%p: bitmap 0x%x: en 0x%x, ch 0x%x\n", __func__, wsi,
+	lwsl_info("%s: %p: bitmap 0x%x: en 0x%x, ch 0x%x\n", __func__, wsi,
 		  wsi->rxflow_bitmap, en, wsi->rxflow_change_to);
 
 	if (_enable & LWS_RXFLOW_REASON_FLAG_PROCESS_NOW ||
@@ -2220,7 +2225,7 @@ lws_get_peer_write_allowance(struct lws *wsi)
 {
 #ifdef LWS_WITH_HTTP2
 	/* only if we are using HTTP2 on this connection */
-	if (wsi->mode != LWSCM_HTTP2_SERVING)
+	if (!lwsi_role_h2(wsi))
 		return -1;
 
 	return lws_h2_tx_cr_get(wsi);
@@ -2231,10 +2236,11 @@ lws_get_peer_write_allowance(struct lws *wsi)
 }
 
 LWS_VISIBLE void
-lws_union_transition(struct lws *wsi, enum connection_mode mode)
+lws_role_transition(struct lws *wsi, enum lwsi_role role, enum lwsi_state state)
 {
-	lwsl_debug("%s: %p: mode %d\n", __func__, wsi, mode);
-	wsi->mode = mode;
+	lwsl_debug("%s: %p: role 0x%x, state 0x%x\n", __func__, wsi, role, state);
+	lwsi_set_role(wsi, role);
+	lwsi_set_state(wsi, state);
 }
 
 LWS_VISIBLE struct lws_plat_file_ops *
@@ -2332,9 +2338,7 @@ lws_close_reason(struct lws *wsi, enum lws_close_status status,
 	unsigned char *p, *start;
 	int budget = sizeof(wsi->ws->ping_payload_buf) - LWS_PRE;
 
-	assert(wsi->mode == LWSCM_WS_SERVING ||
-	       wsi->mode == LWSCM_HTTP2_WS_SERVING ||
-	       wsi->mode == LWSCM_WS_CLIENT);
+	assert(lwsi_role_ws(wsi));
 
 	start = p = &wsi->ws->ping_payload_buf[LWS_PRE];
 
@@ -2823,7 +2827,7 @@ LWS_EXTERN void
 lws_restart_ws_ping_pong_timer(struct lws *wsi)
 {
 	if (!wsi->context->ws_ping_pong_interval ||
-	    !lws_state_is_ws(wsi->state))
+	    !lwsi_role_ws(wsi))
 		return;
 
 	wsi->ws->time_next_ping_check = (time_t)lws_now_secs();
diff --git a/lib/libwebsockets.h b/lib/libwebsockets.h
index 55115cc9ae0507fd220d0de481741524456b2af6..fd3056cf17d78a5713d3f1fadf379ae995df9594 100644
--- a/lib/libwebsockets.h
+++ b/lib/libwebsockets.h
@@ -2162,7 +2162,7 @@ enum lws_extension_callback_reasons {
 	LWS_EXT_CB_HANDSHAKE_REPLY_TX			= 14,
 	LWS_EXT_CB_FLUSH_PENDING_TX			= 15,
 	LWS_EXT_CB_EXTENDED_PAYLOAD_RX			= 16,
-	LWS_EXT_CB_CAN_PROXY_CLIENT_CONNECTION		= 17,
+	LWS_EXT_CB_UNUSED1				= 17,
 	LWS_EXT_CB_1HZ					= 18,
 	LWS_EXT_CB_REQUEST_ON_WRITEABLE			= 19,
 	LWS_EXT_CB_IS_WRITEABLE				= 20,
@@ -4719,7 +4719,7 @@ enum pending_timeout {
 	PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE		=  4,
 	PENDING_TIMEOUT_AWAITING_PING				=  5,
 	PENDING_TIMEOUT_CLOSE_ACK				=  6,
-	PENDING_TIMEOUT_AWAITING_EXTENSION_CONNECT_RESPONSE	=  7,
+	PENDING_TIMEOUT_UNUSED1					=  7,
 	PENDING_TIMEOUT_SENT_CLIENT_HANDSHAKE			=  8,
 	PENDING_TIMEOUT_SSL_ACCEPT				=  9,
 	PENDING_TIMEOUT_HTTP_CONTENT				= 10,
diff --git a/lib/output.c b/lib/output.c
index 92799758357412202895b9e6b4b9540ebd838c0c..a7a3353ea9fd09b077277d9c73bbf75a4b0b5622 100644
--- a/lib/output.c
+++ b/lib/output.c
@@ -77,8 +77,7 @@ int lws_issue_raw(struct lws *wsi, unsigned char *buf, size_t len)
 	if (!len)
 		return 0;
 	/* just ignore sends after we cleared the truncation buffer */
-	if (wsi->state == LWSS_FLUSHING_SEND_BEFORE_CLOSE &&
-	    !wsi->trunc_len)
+	if (lwsi_state(wsi) == LRS_FLUSHING_BEFORE_CLOSE && !wsi->trunc_len)
 		return (int)len;
 
 	if (wsi->trunc_len && (buf < wsi->trunc_alloc ||
@@ -154,7 +153,7 @@ handle_truncated_send:
 			lwsl_info("** %p partial send completed\n", wsi);
 			/* done with it, but don't free it */
 			n = (int)real_len;
-			if (wsi->state == LWSS_FLUSHING_SEND_BEFORE_CLOSE) {
+			if (lwsi_state(wsi) == LRS_FLUSHING_BEFORE_CLOSE) {
 				lwsl_info("** %p signalling to close now\n", wsi);
 				return -1; /* retry closing now */
 			}
@@ -216,7 +215,7 @@ LWS_VISIBLE int lws_write(struct lws *wsi, unsigned char *buf, size_t len,
 			  enum lws_write_protocol wp)
 {
 	struct lws_context_per_thread *pt = &wsi->context->pt[(int)wsi->tsi];
-	int masked7 = (wsi->mode == LWSCM_WS_CLIENT);
+	int masked7 = lwsi_role_client(wsi);
 	unsigned char is_masked_bit = 0;
 	unsigned char *dropmask = NULL;
 	struct lws_tokens eff_buf;
@@ -256,7 +255,7 @@ LWS_VISIBLE int lws_write(struct lws *wsi, unsigned char *buf, size_t len,
 	if (wsi->vhost)
 		wsi->vhost->conn_stats.tx += len;
 
-	if (wsi->ws && wsi->ws->tx_draining_ext && lws_state_is_ws(wsi->state)) {
+	if (wsi->ws && wsi->ws->tx_draining_ext && lwsi_role_ws(wsi)) {
 		/* remove us from the list */
 		struct lws **w = &pt->tx_draining_ext_list;
 
@@ -287,13 +286,13 @@ LWS_VISIBLE int lws_write(struct lws *wsi, unsigned char *buf, size_t len,
 
 	/* if not in a state to send ws stuff, then just send nothing */
 
-	if (!lws_state_is_ws(wsi->state) &&
-	    ((wsi->state != LWSS_RETURNED_CLOSE_ALREADY &&
-	      wsi->state != LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION &&
-	      wsi->state != LWSS_AWAITING_CLOSE_ACK) ||
+	if (!lwsi_role_ws(wsi) &&
+	    ((lwsi_state(wsi) != LRS_RETURNED_CLOSE &&
+	      lwsi_state(wsi) != LRS_WAITING_TO_SEND_CLOSE &&
+	      lwsi_state(wsi) != LRS_AWAITING_CLOSE_ACK) ||
 			    wp1f != LWS_WRITE_CLOSE)) {
 		//assert(0);
-		lwsl_debug("binning %d %d\n", wsi->state, wp1f);
+		lwsl_debug("binning %d %d\n", lwsi_state(wsi), wp1f);
 		return 0;
 	}
 
@@ -514,9 +513,7 @@ send_raw:
 		/*
 		 * ws-over-h2 also ends up here after the ws framing applied
 		 */
-		if (wsi->mode == LWSCM_HTTP2_SERVING ||
-		    wsi->mode == LWSCM_HTTP2_WS_SERVING ||
-		    wsi->mode == LWSCM_HTTP2_CLIENT_ACCEPTED) {
+		if (lwsi_role_h2(wsi)) {
 			unsigned char flags = 0;
 
 			n = LWS_H2_FRAME_TYPE_DATA;
@@ -812,7 +809,7 @@ all_sent:
 		)
 #endif
 		     {
-			wsi->state = LWSS_HTTP;
+			lwsi_set_state(wsi, LRS_ESTABLISHED);
 			/* we might be in keepalive, so close it off here */
 			lws_vfs_file_close(&wsi->http.fop_fd);
 			
diff --git a/lib/pollfd.c b/lib/pollfd.c
index d3b26679560830c760d63adf7f3050cf21961d23..d8836e29e6c5780078e9e1a3a136c563cdc82a14 100644
--- a/lib/pollfd.c
+++ b/lib/pollfd.c
@@ -428,7 +428,7 @@ lws_callback_on_writable(struct lws *wsi)
 #endif
 	int n;
 
-	if (wsi->state == LWSS_SHUTDOWN)
+	if (lwsi_state(wsi) == LRS_SHUTDOWN)
 		return 0;
 
 	if (wsi->socket_is_permanently_unusable)
@@ -462,12 +462,9 @@ lws_callback_on_writable(struct lws *wsi)
 #endif
 
 #ifdef LWS_WITH_HTTP2
-	lwsl_info("%s: %p (mode %d)\n", __func__, wsi, wsi->mode);
+	lwsl_info("%s: %p (role/state 0x%x)\n", __func__, wsi, wsi->wsistate);
 
-	if (wsi->mode != LWSCM_HTTP2_SERVING &&
-	    wsi->mode != LWSCM_HTTP2_CLIENT &&
-	    wsi->mode != LWSCM_HTTP2_CLIENT_ACCEPTED &&
-	    wsi->mode != LWSCM_HTTP2_WS_SERVING)
+	if (!lwsi_role_h2(wsi))
 		goto network_sock;
 
 	if (wsi->h2.requested_POLLOUT
diff --git a/lib/private-libwebsockets.h b/lib/private-libwebsockets.h
index 3061acbc8cc3c5b8435ab0769d0fd291be46f185..5dfdb3fb45d8349f966130155bea892270fbfbe7 100644
--- a/lib/private-libwebsockets.h
+++ b/lib/private-libwebsockets.h
@@ -479,55 +479,148 @@ enum lws_websocket_opcodes_07 {
 };
 
 
-enum lws_connection_states {
-	/* FLAG: one or another kind of ws link */
-	_LSF_WEBSOCKET					= (1 << 5),
-	/* FLAG: close callback */
-	_LSF_CCB					= (1 << 6),
-	/* FLAG: pollout capable */
-	_LSF_POLLOUT					= (1 << 7),
-
-	LWSS_HTTP					= _LSF_CCB | 0,
-	LWSS_HTTP_ISSUING_FILE				=  1,
-	LWSS_HTTP_HEADERS				=  2,
-	LWSS_HTTP_BODY					= _LSF_CCB | 3,
-	LWSS_DEAD_SOCKET				=  4,
-	LWSS_ESTABLISHED				= _LSF_CCB | 5 |
-							  _LSF_WEBSOCKET |
-							  _LSF_POLLOUT,
-	LWSS_CLIENT_HTTP_ESTABLISHED			=  6,
-	LWSS_CLIENT_UNCONNECTED				=  7,
-	LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION		= _LSF_CCB |  8 |
-							  _LSF_POLLOUT,
-	LWSS_RETURNED_CLOSE_ALREADY			= _LSF_CCB |  9 |
-							  _LSF_POLLOUT,
-	LWSS_AWAITING_CLOSE_ACK				= _LSF_CCB | 10,
-	LWSS_FLUSHING_SEND_BEFORE_CLOSE			= _LSF_CCB | 11 |
-							  _LSF_POLLOUT,
-	LWSS_SHUTDOWN					= 12,
-
-	LWSS_HTTP2_AWAIT_CLIENT_PREFACE			= 13,
-	LWSS_HTTP2_ESTABLISHED_PRE_SETTINGS		= 14 | _LSF_POLLOUT,
-	LWSS_HTTP2_ESTABLISHED				= _LSF_CCB | 15 |
-							  _LSF_POLLOUT,
-	LWSS_HTTP2_ESTABLISHED_WS			= _LSF_CCB | 16 |
-							  _LSF_WEBSOCKET |
-							  _LSF_POLLOUT,
-
-	LWSS_CGI					= 17,
-
-	LWSS_HTTP2_DEFERRING_ACTION			= _LSF_CCB | 18 |
-							  _LSF_POLLOUT,
-
-	LWSS_HTTP_DEFERRING_ACTION			= _LSF_CCB | 19 |
-							  _LSF_POLLOUT,
-
-	LWSS_HTTP2_CLIENT_SEND_SETTINGS			= 20 | _LSF_POLLOUT,
-	LWSS_HTTP2_CLIENT_WAITING_TO_SEND_HEADERS	= 21 | _LSF_POLLOUT,
-	LWSS_HTTP2_CLIENT_ESTABLISHED			= 22 | _LSF_POLLOUT,
-};
-
-#define lws_state_is_ws(s) (!!((s) & _LSF_WEBSOCKET))
+typedef uint32_t lws_wsi_state_t;
+
+/*
+ *  31     16 15      0
+ *  [  role ] [ state ]
+ *
+ * The role part is generally invariant for the lifetime of the wsi, although
+ * it can change if the connection role itself does, eg, if the connection
+ * upgrades from H1 -> WS1 the role is changed at that point.
+ *
+ * The state part reflects the dynamic connection state, and the states are
+ * reused between roles.
+ *
+ * None of the internal role or state representations are made available outside
+ * of lws internals.
+ */
+
+#define _RS 16
+
+#define LWSIFR_HTTP	(0x008 << _RS)
+#define LWSIFR_H2	(0x010 << _RS)
+#define LWSIFR_CLIENT	(0x020 << _RS)
+#define LWSIFR_SERVER	(0x040 << _RS)
+#define LWSIFR_WS	(0x080 << _RS)
+#define LWSIFR_RAW	(0x100 << _RS)
+
+enum lwsi_role {
+	LWSI_ROLE_UNSET		= (0 << _RS),
+
+	LWSI_ROLE_H1_SERVER	= (LWSIFR_SERVER | LWSIFR_HTTP            ),
+	LWSI_ROLE_H2_SERVER	= (LWSIFR_SERVER | LWSIFR_HTTP | LWSIFR_H2),
+	LWSI_ROLE_H1_CLIENT	= (LWSIFR_CLIENT | LWSIFR_HTTP            ),
+	LWSI_ROLE_H2_CLIENT	= (LWSIFR_CLIENT | LWSIFR_HTTP | LWSIFR_H2),
+	LWSI_ROLE_WS1_SERVER	= (LWSIFR_SERVER | LWSIFR_WS              ),
+	LWSI_ROLE_WS2_SERVER	= (LWSIFR_SERVER | LWSIFR_WS   | LWSIFR_H2),
+	LWSI_ROLE_WS1_CLIENT	= (LWSIFR_CLIENT | LWSIFR_WS              ),
+	LWSI_ROLE_WS2_CLIENT	= (LWSIFR_CLIENT | LWSIFR_WS   | LWSIFR_H2),
+
+	LWSI_ROLE_CGI		= (1 << _RS),
+	LWSI_ROLE_LISTEN_SOCKET	= (2 << _RS),
+	LWSI_ROLE_EVENT_PIPE	= (3 << _RS),
+	LWSI_ROLE_RAW_FILE	= (LWSIFR_RAW | (4 << _RS)),
+	LWSI_ROLE_RAW_SOCKET	= (LWSIFR_RAW | (5 << _RS)),
+
+	LWSI_ROLE_MASK					= (0xffff << _RS),
+};
+
+#define lwsi_role(wsi) \
+			(wsi->wsistate & LWSI_ROLE_MASK)
+#if !defined (_DEBUG)
+#define lwsi_set_role(wsi, role) wsi->wsistate = \
+			(wsi->wsistate & (~LWSI_ROLE_MASK)) | role
+#else
+void lwsi_set_role(struct lws *wsi, lws_wsi_state_t role);
+#endif
+#define lwsi_role_ws(wsi) (!!(wsi->wsistate & LWSIFR_WS))
+#define lwsi_role_ws_client(wsi) \
+			((wsi->wsistate & (LWSIFR_CLIENT | LWSIFR_WS))\
+				       == (LWSIFR_CLIENT | LWSIFR_WS))
+#define lwsi_role_non_ws_client(wsi) \
+			((wsi->wsistate & (LWSIFR_CLIENT | LWSIFR_WS))\
+				       == (LWSIFR_CLIENT))
+#define lwsi_role_client(wsi) (!!(wsi->wsistate & LWSIFR_CLIENT))
+#define lwsi_role_raw(wsi) (!!(wsi->wsistate & LWSIFR_RAW))
+#define lwsi_role_http_server(wsi) \
+			((wsi->wsistate & (LWSIFR_SERVER | LWSIFR_HTTP))\
+					 == (LWSIFR_SERVER | LWSIFR_HTTP))
+#define lwsi_role_http_client(wsi) \
+			((wsi->wsistate & (LWSIFR_CLIENT | LWSIFR_HTTP))\
+					 == (LWSIFR_CLIENT | LWSIFR_HTTP))
+#define lwsi_role_http(wsi) (!!(wsi->wsistate & LWSIFR_HTTP))
+#define lwsi_role_h2(wsi) (!!(wsi->wsistate & LWSIFR_H2))
+
+/* Pollout wants a callback in this state */
+#define LWSIFS_POCB		(0x100)
+/* Before any protocol connection was established */
+#define LWSIFS_NOTEST		(0x200)
+
+enum lwsi_state {
+
+	/* Phase 1: pre-transport */
+
+	LRS_UNCONNECTED				= LWSIFS_NOTEST | 0,
+	LRS_WAITING_CONNECT			= LWSIFS_NOTEST | 1,
+
+	/* Phase 2: establishing intermediaries on top of transport */
+
+	LRS_WAITING_PROXY_REPLY			= LWSIFS_NOTEST | 2,
+	LRS_WAITING_SSL				= LWSIFS_NOTEST | 3,
+	LRS_WAITING_SOCKS_GREETING_REPLY	= LWSIFS_NOTEST | 4,
+	LRS_WAITING_SOCKS_CONNECT_REPLY		= LWSIFS_NOTEST | 5,
+	LRS_WAITING_SOCKS_AUTH_REPLY		= LWSIFS_NOTEST | 6,
+
+	/* Phase 3: establishing tls tunnel */
+
+	LRS_SSL_INIT				= LWSIFS_NOTEST | 7,
+	LRS_SSL_ACK_PENDING			= LWSIFS_NOTEST | 8,
+	LRS_PRE_WS_SERVING_ACCEPT		= LWSIFS_NOTEST | 9,
+
+	/* Phase 4: connected */
+
+	LRS_WAITING_SERVER_REPLY		= LWSIFS_NOTEST | 10,
+	LRS_H2_AWAIT_PREFACE			= LWSIFS_NOTEST | 11,
+	LRS_H2_AWAIT_SETTINGS			= LWSIFS_NOTEST |
+						  LWSIFS_POCB | 12,
+
+	/* Phase 5: protocol logically established */
+
+	LRS_H2_CLIENT_SEND_SETTINGS		= LWSIFS_POCB | 13,
+	LRS_H2_WAITING_TO_SEND_HEADERS		= LWSIFS_POCB | 14,
+	LRS_DEFERRING_ACTION			= LWSIFS_POCB | 15,
+	LRS_H1C_ISSUE_HANDSHAKE			= 16,
+	LRS_H1C_ISSUE_HANDSHAKE2		= 17,
+	LRS_ISSUE_HTTP_BODY			= 18,
+	LRS_ISSUING_FILE			= 19,
+	LRS_HEADERS				= 20,
+	LRS_BODY				= 21,
+	LRS_ESTABLISHED				= LWSIFS_POCB | 22,
+
+	/* Phase 6: finishing */
+
+	LRS_WAITING_TO_SEND_CLOSE		= LWSIFS_POCB | 23,
+	LRS_RETURNED_CLOSE			= LWSIFS_POCB | 24,
+	LRS_AWAITING_CLOSE_ACK			= 25,
+	LRS_FLUSHING_BEFORE_CLOSE		= LWSIFS_POCB | 26,
+	LRS_SHUTDOWN				= 27,
+
+	/* Phase 7: dead */
+
+	LRS_DEAD_SOCKET				= 28,
+
+	LRS_MASK				= 0xffff
+};
+
+#define lwsi_state(wsi) ((enum lwsi_state)(wsi->wsistate & LRS_MASK))
+#define lwsi_state_est(wsi) (!(wsi->wsistate & LWSIFS_NOTEST))
+#if !defined (_DEBUG)
+#define lwsi_set_state(wsi, lrs) wsi->wsistate = \
+			  (wsi->wsistate & (~LRS_MASK)) | lrs
+#else
+void lwsi_set_state(struct lws *wsi, lws_wsi_state_t lrs);
+#endif
 
 enum http_version {
 	HTTP_VERSION_1_0,
@@ -568,54 +661,6 @@ enum lws_rx_parse_state {
 	LWS_RXPS_PAYLOAD_UNTIL_LENGTH_EXHAUSTED
 };
 
-#define LWSCM_FLAG_IMPLIES_CALLBACK_CLOSED_CLIENT_HTTP 32
-
-enum connection_mode {
-	LWSCM_HTTP_SERVING,
-	/* actual HTTP service going on */
-	LWSCM_HTTP_SERVING_ACCEPTED,
-	LWSCM_PRE_WS_SERVING_ACCEPT,
-
-	LWSCM_WS_SERVING,
-	LWSCM_WS_CLIENT,
-
-	LWSCM_HTTP2_SERVING,
-	LWSCM_HTTP2_WS_SERVING,
-
-	/* transient, ssl delay hiding */
-	LWSCM_SSL_ACK_PENDING,
-	LWSCM_SSL_INIT,
-	/* as above, but complete into LWSCM_RAW */
-	LWSCM_SSL_ACK_PENDING_RAW,
-	LWSCM_SSL_INIT_RAW,
-
-	/* special internal types */
-	LWSCM_SERVER_LISTENER,
-	LWSCM_CGI, /* stdin, stdout, stderr for another cgi master wsi */
-	LWSCM_RAW, /* raw with bulk handling */
-	LWSCM_RAW_FILEDESC, /* raw without bulk handling */
-	LWSCM_EVENT_PIPE, /* event pipe with no vhost or protocol binding */
-
-	/* HTTP Client related */
-	LWSCM_HTTP_CLIENT = LWSCM_FLAG_IMPLIES_CALLBACK_CLOSED_CLIENT_HTTP,
-	LWSCM_HTTP_CLIENT_ACCEPTED, /* actual HTTP service going on */
-	LWSCM_HTTP2_CLIENT,
-	LWSCM_HTTP2_CLIENT_ACCEPTED,
-	LWSCM_WSCL_WAITING_CONNECT,
-	LWSCM_WSCL_WAITING_PROXY_REPLY,
-	LWSCM_WSCL_ISSUE_HANDSHAKE,
-	LWSCM_WSCL_ISSUE_HANDSHAKE2,
-	LWSCM_WSCL_ISSUE_HTTP_BODY,
-	LWSCM_WSCL_WAITING_SSL,
-	LWSCM_WSCL_WAITING_SERVER_REPLY,
-	LWSCM_WSCL_WAITING_EXTENSION_CONNECT,
-	LWSCM_WSCL_PENDING_CANDIDATE_CHILD,
-	LWSCM_WSCL_WAITING_SOCKS_GREETING_REPLY,
-	LWSCM_WSCL_WAITING_SOCKS_CONNECT_REPLY,
-	LWSCM_WSCL_WAITING_SOCKS_AUTH_REPLY,
-
-	/****** add new things just above ---^ ******/
-};
 
 /* enums of socks version */
 enum socks_version {
@@ -1966,6 +2011,9 @@ struct lws {
 
 	time_t pending_timeout_set;
 
+	lws_wsi_state_t	wsistate;
+	lws_wsi_state_t wsistate_pre_close;
+
 	/* ints */
 	int position_in_fds_table;
 	uint32_t rxflow_len;
@@ -2048,14 +2096,11 @@ struct lws {
 #endif
 	unsigned short pending_timeout_limit;
 
-	uint8_t state; /* enum lws_connection_states */
-	uint8_t mode; /* enum connection_mode */
-
 	/* chars */
 #if !defined(LWS_WITHOUT_EXTENSIONS)
 	uint8_t count_act_ext;
 #endif
-	uint8_t state_pre_close;
+
 	char lws_rx_parse_state; /* enum lws_rx_parse_state */
 	char rx_frame_type; /* enum lws_write_protocol */
 	char pending_timeout; /* enum pending_timeout */
@@ -2237,7 +2282,7 @@ LWS_EXTERN int LWS_WARN_UNUSED_RESULT
 lws_issue_raw_ext_access(struct lws *wsi, unsigned char *buf, size_t len);
 
 LWS_EXTERN void
-lws_union_transition(struct lws *wsi, enum connection_mode mode);
+lws_role_transition(struct lws *wsi, enum lwsi_role role, enum lwsi_state state);
 
 LWS_EXTERN int LWS_WARN_UNUSED_RESULT
 user_callback_handle_rxflow(lws_callback_function, struct lws *wsi,
diff --git a/lib/server/cgi.c b/lib/server/cgi.c
index ee69584773eb5777fc7cb937639f4a4d76a1ee6e..e1cbc12beeeadb5f2c1d15bae8900e06e56c9cd5 100644
--- a/lib/server/cgi.c
+++ b/lib/server/cgi.c
@@ -88,8 +88,8 @@ lws_create_basic_wsi(struct lws_context *context, int tsi)
 
 	/* initialize the instance struct */
 
-	new_wsi->state = LWSS_CGI;
-	new_wsi->mode = LWSCM_CGI;
+	lws_role_transition(new_wsi, LWSI_ROLE_CGI, LRS_ESTABLISHED);
+
 	new_wsi->hdr_parsing_completed = 0;
 	new_wsi->position_in_fds_table = -1;
 
diff --git a/lib/server/parsers.c b/lib/server/parsers.c
index dffd75ea86a6e1221792de7a8e524962b0b8fc10..7577d75308cbcfbcbb9a0063e3ab0760149ebcc4 100644
--- a/lib/server/parsers.c
+++ b/lib/server/parsers.c
@@ -272,7 +272,7 @@ reset:
 	lws_pt_unlock(pt);
 
 #ifndef LWS_NO_CLIENT
-	if (wsi->state == LWSS_CLIENT_UNCONNECTED)
+	if (lwsi_role_client(wsi) && lwsi_state(wsi) == LRS_UNCONNECTED)
 		if (!lws_client_connect_via_info2(wsi))
 			/* our client connect has failed, the wsi
 			 * has been closed
@@ -345,10 +345,10 @@ int __lws_header_table_detach(struct lws *wsi, int autoservice)
 		 * unreasonably long time
 		 */
 		lwsl_debug("%s: wsi %p: ah held %ds, "
-			    "ah.rxpos %d, ah.rxlen %d, mode/state %d %d,"
+			    "ah.rxpos %d, ah.rxlen %d, role/state 0x%x 0x%x,"
 			    "\n", __func__, wsi,
 			    (int)(now - ah->assigned),
-			    ah->rxpos, ah->rxlen, wsi->mode, wsi->state);
+			    ah->rxpos, ah->rxlen, lwsi_role(wsi), lwsi_state(wsi));
 	}
 
 	ah->assigned = 0;
@@ -429,7 +429,7 @@ int __lws_header_table_detach(struct lws *wsi, int autoservice)
 	pt->ah_wait_list_length--;
 
 #ifndef LWS_NO_CLIENT
-	if (wsi->state == LWSS_CLIENT_UNCONNECTED) {
+	if (lwsi_role_client(wsi) && lwsi_state(wsi) == LRS_UNCONNECTED) {
 		lws_pt_unlock(pt);
 
 		if (!lws_client_connect_via_info2(wsi)) {
@@ -991,8 +991,8 @@ swallow:
 
 			/* collecting and checking a name part */
 		case WSI_TOKEN_NAME_PART:
-			lwsl_parser("WSI_TOKEN_NAME_PART '%c' 0x%02X (mode=%d) "
-				    "wsi->lextable_pos=%d\n", c, c, wsi->mode,
+			lwsl_parser("WSI_TOKEN_NAME_PART '%c' 0x%02X (role=0x%x) "
+				    "wsi->lextable_pos=%d\n", c, c, lwsi_role(wsi),
 				    ah->lextable_pos);
 
 			if (c >= 'A' && c <= 'Z')
@@ -1037,10 +1037,11 @@ nope:
 			}
 
 			/*
-			 * Server needs to look out for unknown methods...
+			 * If it's h1, server needs to look out for unknown
+			 * methods...
 			 */
 			if (ah->lextable_pos < 0 &&
-			    (wsi->mode == LWSCM_HTTP_SERVING)) {
+			    lwsi_role(wsi) == LWSI_ROLE_H1_SERVER) {
 				/* this is not a header we know about */
 				for (m = 0; m < ARRAY_SIZE(methods); m++)
 					if (ah->frag_index[methods[m]]) {
@@ -1571,7 +1572,7 @@ spill:
 		case LWSWSOPC_CLOSE:
 
 			/* is this an acknowledgment of our close? */
-			if (wsi->state == LWSS_AWAITING_CLOSE_ACK) {
+			if (lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK) {
 				/*
 				 * fine he has told us he is closing too, let's
 				 * finish our close
@@ -1579,7 +1580,7 @@ spill:
 				lwsl_parser("seen client close ack\n");
 				return -1;
 			}
-			if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY)
+			if (lwsi_state(wsi) == LRS_RETURNED_CLOSE)
 				/* if he sends us 2 CLOSE, kill him */
 				return -1;
 
@@ -1603,7 +1604,7 @@ spill:
 				return -1;
 
 			lwsl_parser("server sees client close packet\n");
-			wsi->state = LWSS_RETURNED_CLOSE_ALREADY;
+			lwsi_set_state(wsi, LRS_RETURNED_CLOSE);
 			/* deal with the close packet contents as a PONG */
 			wsi->ws->payload_is_close = 1;
 			goto process_as_ping;
@@ -1700,8 +1701,8 @@ ping_drop:
 drain_extension:
 		lwsl_ext("%s: passing %d to ext\n", __func__, eff_buf.token_len);
 
-		if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY ||
-		    wsi->state == LWSS_AWAITING_CLOSE_ACK)
+		if (lwsi_state(wsi) == LRS_RETURNED_CLOSE ||
+		    lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK)
 			goto already_done;
 #if !defined(LWS_WITHOUT_EXTENSIONS)
 		n = lws_ext_cb_active(wsi, LWS_EXT_CB_PAYLOAD_RX, &eff_buf, 0);
diff --git a/lib/server/server-handshake.c b/lib/server/server-handshake.c
index 0cf54919ad3cde28a65a2b4da06f1f68bcab840f..c3590cb3e23d5b360fe41d6a26005b1fc48aeca2 100644
--- a/lib/server/server-handshake.c
+++ b/lib/server/server-handshake.c
@@ -339,7 +339,7 @@ handshake_0405(struct lws_context *context, struct lws *wsi)
 
 	/* alright clean up and set ourselves into established state */
 
-	wsi->state = LWSS_ESTABLISHED;
+	lwsi_set_state(wsi, LRS_ESTABLISHED);
 	wsi->lws_rx_parse_state = LWS_RXPS_NEW;
 
 	{
diff --git a/lib/server/server.c b/lib/server/server.c
index 62944a5e49cbaa6d5ef76b8e28f13ab16c24bd0e..a99b114962ab9d80b7861234d8af4bc0df2971d9 100644
--- a/lib/server/server.c
+++ b/lib/server/server.c
@@ -243,7 +243,7 @@ done_list:
 		}
 		wsi->context = vhost->context;
 		wsi->desc.sockfd = sockfd;
-		wsi->mode = LWSCM_SERVER_LISTENER;
+		lwsi_set_role(wsi, LWSI_ROLE_LISTEN_SOCKET);
 		wsi->protocol = vhost->protocols;
 		wsi->tsi = m;
 		wsi->vhost = vhost;
@@ -488,7 +488,7 @@ lws_http_serve(struct lws *wsi, char *uri, const char *origin,
 		wsi->http.fop_fd = fops->LWS_FOP_OPEN(wsi->context->fops,
 							path, vpath, &fflags);
 		if (!wsi->http.fop_fd) {
-			lwsl_err("Unable to open '%s': errno %d\n", path, errno);
+			lwsl_info("Unable to open '%s': errno %d\n", path, errno);
 
 			return -1;
 		}
@@ -806,7 +806,7 @@ lws_server_init_wsi_for_ws(struct lws *wsi)
 {
 	int n;
 
-	wsi->state = LWSS_ESTABLISHED;
+	lwsi_set_state(wsi, LRS_ESTABLISHED);
 	lws_restart_ws_ping_pong_timer(wsi);
 
 	/*
@@ -1016,9 +1016,9 @@ lws_process_ws_upgrade(struct lws *wsi)
 	lws_pt_lock(pt, __func__);
 
 	if (wsi->h2_stream_carries_ws)
-		lws_union_transition(wsi, LWSCM_HTTP2_WS_SERVING);
+		lws_role_transition(wsi, LWSI_ROLE_WS2_SERVER, LRS_ESTABLISHED);
 	else
-		lws_union_transition(wsi, LWSCM_WS_SERVING);
+		lws_role_transition(wsi, LWSI_ROLE_WS1_SERVER, LRS_ESTABLISHED);
 	/*
 	 * Because rxpos/rxlen shows something in the ah, we will get
 	 * service guaranteed next time around the event loop
@@ -1592,15 +1592,15 @@ deal_body:
 	 * In any case, return 0 and let lws_read decide how to
 	 * proceed based on state
 	 */
-	if (wsi->state != LWSS_HTTP_ISSUING_FILE) {
+	if (lwsi_state(wsi) != LRS_ISSUING_FILE) {
 		/* Prepare to read body if we have a content length: */
 		lwsl_debug("wsi->http.rx_content_length %lld %d %d\n",
 			   (long long)wsi->http.rx_content_length,
 			   wsi->upgraded_to_http2, wsi->http2_substream);
 		if (wsi->http.rx_content_length > 0) {
-			lwsl_info("%s: %p: LWSS_HTTP_BODY state set\n",
-				    __func__, wsi);
-			wsi->state = LWSS_HTTP_BODY;
+			lwsi_set_state(wsi, LRS_BODY);
+			lwsl_info("%s: %p: LRS_BODY state set (0x%x)\n",
+				    __func__, wsi, wsi->wsistate);
 			wsi->http.rx_content_remain =
 					wsi->http.rx_content_length;
 		}
@@ -1645,10 +1645,9 @@ lws_handshake_server(struct lws *wsi, unsigned char **buf, size_t len)
 	}
 
 	while (len) {
-		if (wsi->mode != LWSCM_HTTP_SERVING &&
-		    wsi->mode != LWSCM_HTTP2_SERVING &&
-		    wsi->mode != LWSCM_HTTP_SERVING_ACCEPTED) {
-			lwsl_err("%s: bad wsi mode %d\n", __func__, wsi->mode);
+		if (!lwsi_role_http_server(wsi)) {
+			lwsl_err("%s: bad wsi role 0x%x\n", __func__,
+					lwsi_role(wsi));
 			goto bail_nuke_ah;
 		}
 
@@ -1677,7 +1676,8 @@ raw_transition:
 					goto bail_nuke_ah;
 
 				lws_header_table_force_to_detachable_state(wsi);
-				lws_union_transition(wsi, LWSCM_RAW);
+				lws_role_transition(wsi, LWSI_ROLE_RAW_SOCKET,
+						LRS_ESTABLISHED);
 				lws_header_table_detach(wsi, 1);
 
 				if (m == 2 && (wsi->protocol->callback)(wsi,
@@ -1708,7 +1708,7 @@ raw_transition:
 		} else
 			lwsl_info("no host\n");
 
-		if (wsi->mode != LWSCM_HTTP2_SERVING) {
+		if (lwsi_role(wsi) != LWSI_ROLE_H2_SERVER) {
 			wsi->vhost->conn_stats.h1_trans++;
 			if (!wsi->conn_stat_done) {
 				wsi->vhost->conn_stats.h1_conn++;
@@ -1769,7 +1769,7 @@ raw_transition:
 			goto raw_transition;
 		}
 
-		wsi->mode = LWSCM_PRE_WS_SERVING_ACCEPT;
+		lwsi_set_state(wsi, LRS_PRE_WS_SERVING_ACCEPT);
 		lws_set_timeout(wsi, NO_PENDING_TIMEOUT, 0);
 
 		/* is this websocket protocol or normal http 1.0? */
@@ -1800,8 +1800,7 @@ raw_transition:
 
 		lwsl_info("No upgrade\n");
 
-		lws_union_transition(wsi, LWSCM_HTTP_SERVING_ACCEPTED);
-		wsi->state = LWSS_HTTP;
+		lwsi_set_state(wsi, LRS_ESTABLISHED);
 		wsi->http.fop_fd = NULL;
 
 		lwsl_debug("%s: wsi %p: ah %p\n", __func__, (void *)wsi,
@@ -1830,8 +1829,6 @@ upgrade_h2c:
 
 		/* adopt the header info */
 
-		lws_union_transition(wsi, LWSCM_HTTP2_SERVING);
-
 		if (!wsi->h2.h2n) {
 			wsi->h2.h2n = lws_zalloc(sizeof(*wsi->h2.h2n),
 						   "h2n");
@@ -1858,7 +1855,7 @@ upgrade_h2c:
 			return 1;
 		}
 
-		wsi->state = LWSS_HTTP2_AWAIT_CLIENT_PREFACE;
+		lwsi_set_state(wsi, LRS_H2_AWAIT_PREFACE);
 		wsi->upgraded_to_http2 = 1;
 
 		return 0;
@@ -1930,8 +1927,7 @@ lws_create_new_server_wsi(struct lws_vhost *vhost)
 
 	/* initialize the instance struct */
 
-	new_wsi->state = LWSS_HTTP;
-	new_wsi->mode = LWSCM_HTTP_SERVING;
+	lwsi_set_state(new_wsi, LRS_UNCONNECTED);
 	new_wsi->hdr_parsing_completed = 0;
 
 #ifdef LWS_OPENSSL_SUPPORT
@@ -1997,7 +1993,7 @@ 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.
 	 */
-	wsi->state = LWSS_HTTP_DEFERRING_ACTION;
+	lwsi_set_state(wsi, LRS_DEFERRING_ACTION);
 	wsi->http.tx_content_length = 0;
 	wsi->http.tx_content_remain = 0;
 	wsi->hdr_parsing_completed = 0;
@@ -2056,8 +2052,7 @@ lws_http_transaction_completed(struct lws *wsi)
 		if (wsi->ah)
 			wsi->ah->ues = URIES_IDLE;
 
-		if (wsi->mode == LWSCM_HTTP_SERVING_ACCEPTED)
-			wsi->mode = LWSCM_HTTP_SERVING;
+		lwsi_set_state(wsi, LRS_ESTABLISHED);
 	} else
 		if (wsi->preamble_rx)
 			if (lws_header_table_attach(wsi, 0))
@@ -2139,7 +2134,7 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,
 			new_wsi->desc.sockfd = LWS_SOCK_INVALID;
 			lwsl_debug("binding to %s\n", new_wsi->protocol->name);
 			lws_bind_protocol(new_wsi, new_wsi->protocol);
-			lws_union_transition(new_wsi, LWSCM_WS_SERVING);
+			lws_role_transition(new_wsi, LWSI_ROLE_WS1_SERVER, LRS_ESTABLISHED);
 			/* allocate the ws struct for the wsi */
 			new_wsi->ws = lws_zalloc(sizeof(*new_wsi->ws), "ws struct");
 			if (!new_wsi->ws) {
@@ -2157,7 +2152,8 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,
 		else { /* this is the only time he will transition */
 			lws_bind_protocol(new_wsi,
 				&vh->protocols[vh->raw_protocol_index]);
-			lws_union_transition(new_wsi, LWSCM_RAW);
+			lws_role_transition(new_wsi, LWSI_ROLE_RAW_SOCKET,
+					LRS_ESTABLISHED);
 		}
 
 	if (type & LWS_ADOPT_SOCKET) { /* socket desc */
@@ -2201,20 +2197,26 @@ lws_adopt_descriptor_vhost(struct lws_vhost *vh, lws_adoption_type type,
 		/* non-SSL */
 		if (!(type & LWS_ADOPT_HTTP)) {
 			if (!(type & LWS_ADOPT_SOCKET))
-				new_wsi->mode = LWSCM_RAW_FILEDESC;
+				lwsi_set_role(new_wsi, LWSI_ROLE_RAW_FILE);
 			else
-				new_wsi->mode = LWSCM_RAW;
+				lwsi_set_role(new_wsi, LWSI_ROLE_RAW_SOCKET);
+		} else {
+			lwsi_set_role(new_wsi, LWSI_ROLE_H1_SERVER);
+			lwsi_set_state(new_wsi, LRS_HEADERS);
 		}
 	} else {
 		/* SSL */
 		if (!(type & LWS_ADOPT_HTTP))
-			new_wsi->mode = LWSCM_SSL_INIT_RAW;
+			lwsi_set_role(new_wsi, LWSI_ROLE_RAW_SOCKET);
 		else
-			new_wsi->mode = LWSCM_SSL_INIT;
+			lwsi_set_role(new_wsi, LWSI_ROLE_H1_SERVER);
 
+		lwsi_set_state(new_wsi, LRS_SSL_INIT);
 		ssl = 1;
 	}
 
+	lwsl_debug("new wsi wsistate 0x%x\n", new_wsi->wsistate);
+
 	lws_libev_accept(new_wsi, new_wsi->desc);
 	lws_libuv_accept(new_wsi, new_wsi->desc);
 	lws_libevent_accept(new_wsi, new_wsi->desc);
@@ -2394,12 +2396,12 @@ lws_server_socket_service(struct lws_context *context, struct lws *wsi,
 #endif
 	int n, len;
 
-	switch (wsi->mode) {
+	switch (lwsi_role(wsi)) {
 
-	case LWSCM_HTTP_SERVING:
-	case LWSCM_HTTP_SERVING_ACCEPTED:
-	case LWSCM_HTTP2_SERVING:
-	case LWSCM_RAW:
+	case LWSI_ROLE_H1_SERVER:
+	case LWSI_ROLE_H2_SERVER:
+	case LWSI_ROLE_RAW_SOCKET:
+	case LWSI_ROLE_RAW_FILE:
 
 		/* handle http headers coming in */
 
@@ -2421,7 +2423,7 @@ lws_server_socket_service(struct lws_context *context, struct lws *wsi,
 			break;
 		}
 
-		if (wsi->state == LWSS_HTTP_DEFERRING_ACTION)
+		if (lwsi_state(wsi) == LRS_DEFERRING_ACTION)
 			goto try_pollout;
 
 		/* any incoming data ready? */
@@ -2442,11 +2444,19 @@ lws_server_socket_service(struct lws_context *context, struct lws *wsi,
 			goto try_pollout;
 		}
 
+		/*
+		 * We haven't processed that the tunnel is set up yet, so
+		 * defer reading
+		 */
+		if (lwsi_state(wsi) == LRS_SSL_ACK_PENDING)
+			break;
+
 		/* these states imply we MUST have an ah attached */
 
-		if (wsi->mode != LWSCM_RAW && (wsi->state == LWSS_HTTP ||
-		    wsi->state == LWSS_HTTP_ISSUING_FILE ||
-		    wsi->state == LWSS_HTTP_HEADERS)) {
+		if (!lwsi_role_raw(wsi) &&
+		    (lwsi_state(wsi) == LRS_ESTABLISHED ||
+		     lwsi_state(wsi) == LRS_ISSUING_FILE ||
+		     lwsi_state(wsi) == LRS_HEADERS)) {
 			if (!wsi->ah) {
 				/* no autoservice beacuse we will do it next */
 				if (lws_header_table_attach(wsi, 0)) {
@@ -2488,9 +2498,6 @@ lws_server_socket_service(struct lws_context *context, struct lws *wsi,
 				}
 			}
 
-			// lwsl_notice("new read, rxpos %d / rxlen %d\n", ah->rxpos, ah->rxlen);
-			// lwsl_hexdump_level(LLL_NOTICE, ah->rx, ah->rxlen);
-
 			if (!(ah->rxpos != ah->rxlen && ah->rxlen)) {
 				lwsl_err("%s: assert: rxpos %d, rxlen %d\n",
 					 __func__, ah->rxpos, ah->rxlen);
@@ -2499,8 +2506,8 @@ lws_server_socket_service(struct lws_context *context, struct lws *wsi,
 			}
 			
 			/* just ignore incoming if waiting for close */
-			if (wsi->state == LWSS_FLUSHING_SEND_BEFORE_CLOSE ||
-			    wsi->state == LWSS_HTTP_ISSUING_FILE)
+			if (lwsi_state(wsi) == LRS_FLUSHING_BEFORE_CLOSE ||
+			    lwsi_state(wsi) == LRS_ISSUING_FILE)
 				goto try_pollout;
 
 			/*
@@ -2523,9 +2530,7 @@ lws_server_socket_service(struct lws_context *context, struct lws *wsi,
 				   wsi->ah->rxlen);
 
 			if (lws_header_table_is_in_detachable_state(wsi) &&
-			    (wsi->mode != LWSCM_HTTP_SERVING &&
-			     wsi->mode != LWSCM_HTTP_SERVING_ACCEPTED &&
-			     wsi->mode != LWSCM_HTTP2_SERVING))
+				lwsi_role_raw(wsi)) // ???
 				lws_header_table_detach(wsi, 1);
 
 			break;
@@ -2546,7 +2551,8 @@ lws_server_socket_service(struct lws_context *context, struct lws *wsi,
 
 			len = lws_ssl_capable_read(wsi, pt->serv_buf,
 						   context->pt_serv_buf_size);
-			lwsl_debug("%s: wsi %p read %d\r\n", __func__, wsi, len);
+			lwsl_debug("%s: wsi %p read %d (wsistate 0x%x)\n",
+					__func__, wsi, len, wsi->wsistate);
 			switch (len) {
 			case 0:
 				lwsl_info("%s: read 0 len b\n", __func__);
@@ -2561,7 +2567,7 @@ lws_server_socket_service(struct lws_context *context, struct lws *wsi,
 			if (len < 0) /* coverity */
 				goto fail;
 		}
-		if (wsi->mode == LWSCM_RAW) {
+		if (lwsi_role_raw(wsi)) {
 			n = user_callback_handle_rxflow(wsi->protocol->callback,
 							wsi, LWS_CALLBACK_RAW_RX,
 							wsi->user_space,
@@ -2574,8 +2580,8 @@ lws_server_socket_service(struct lws_context *context, struct lws *wsi,
 		}
 
 		/* just ignore incoming if waiting for close */
-		if (wsi->state != LWSS_FLUSHING_SEND_BEFORE_CLOSE &&
-		    wsi->state != LWSS_HTTP_ISSUING_FILE) {
+		if (lwsi_state(wsi) != LRS_FLUSHING_BEFORE_CLOSE &&
+		    lwsi_state(wsi) != LRS_ISSUING_FILE) {
 			/*
 			 * this may want to send
 			 * (via HTTP callback for example)
@@ -2637,21 +2643,21 @@ try_pollout:
 		/* clear back-to-back write detection */
 		wsi->could_have_pending = 0;
 
-		if (wsi->state == LWSS_HTTP_DEFERRING_ACTION) {
-			lwsl_debug("%s: LWSS_HTTP_DEFERRING_ACTION now writable\n",
+		if (lwsi_state(wsi) == LRS_DEFERRING_ACTION) {
+			lwsl_debug("%s: LRS_DEFERRING_ACTION now writable\n",
 				   __func__);
 
 			if (wsi->ah)
 				lwsl_debug("     existing ah rxpos %d / rxlen %d\n",
 				   wsi->ah->rxpos, wsi->ah->rxlen);
-			wsi->state = LWSS_HTTP;
+			lwsi_set_state(wsi, LRS_ESTABLISHED);
 			if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
 				lwsl_info("failed at set pollfd\n");
 				goto fail;
 			}
 		}
 
-		if (wsi->mode == LWSCM_RAW) {
+		if (lwsi_role_raw(wsi)) {
 			lws_stats_atomic_bump(wsi->context, pt,
 						LWSSTATS_C_WRITEABLE_CB, 1);
 #if defined(LWS_WITH_STATS)
@@ -2679,7 +2685,7 @@ try_pollout:
 		if (!wsi->hdr_parsing_completed)
 			break;
 
-		if (wsi->state != LWSS_HTTP_ISSUING_FILE) {
+		if (lwsi_state(wsi) != LRS_ISSUING_FILE) {
 
 			lws_stats_atomic_bump(wsi->context, pt,
 						LWSSTATS_C_WRITEABLE_CB, 1);
@@ -2718,7 +2724,7 @@ try_pollout:
 
 		break;
 
-	case LWSCM_SERVER_LISTENER:
+	case LWSI_ROLE_LISTEN_SOCKET:
 
 #if LWS_POSIX
 		/* pollin means a client has connected to us then
@@ -2876,7 +2882,7 @@ lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
 		wsi->http.fop_fd = fops->LWS_FOP_OPEN(wsi->context->fops,
 							file, vpath, &fflags);
 		if (!wsi->http.fop_fd) {
-			lwsl_err("Unable to open: '%s': errno %d\n", file, errno);
+			lwsl_info("Unable to open: '%s': errno %d\n", file, errno);
 
 			return -1;
 		}
@@ -3056,7 +3062,7 @@ lws_serve_http_file(struct lws *wsi, const char *file, const char *content_type,
 	}
 
 	wsi->http.filepos = 0;
-	wsi->state = LWSS_HTTP_ISSUING_FILE;
+	lwsi_set_state(wsi, LRS_ISSUING_FILE);
 
 	lws_callback_on_writable(wsi);
 
diff --git a/lib/server/ssl-server.c b/lib/server/ssl-server.c
index 5a133814a8a7859f01ae6165c9d4a4039736ecc1..c2a16f7dfa1ea212b2642fbb5a00941b07b48147 100644
--- a/lib/server/ssl-server.c
+++ b/lib/server/ssl-server.c
@@ -1,7 +1,7 @@
 /*
  * libwebsockets - small server side websockets and web server implementation
  *
- * Copyright (C) 2010-2017 Andy Green <andy@warmcat.com>
+ * Copyright (C) 2010-2018 Andy Green <andy@warmcat.com>
  *
  *  This library is free software; you can redistribute it and/or
  *  modify it under the terms of the GNU Lesser General Public
@@ -118,9 +118,9 @@ lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd)
 	if (!LWS_SSL_ENABLED(wsi->vhost))
 		return 0;
 
-	switch (wsi->mode) {
-	case LWSCM_SSL_INIT:
-	case LWSCM_SSL_INIT_RAW:
+	switch (lwsi_state(wsi)) {
+	case LRS_SSL_INIT:
+
 		if (wsi->ssl)
 			lwsl_err("%s: leaking ssl\n", __func__);
 		if (accept_fd == LWS_SOCK_INVALID)
@@ -144,10 +144,6 @@ lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd)
 			/* that was the last allowed SSL connection */
 			lws_gate_accepts(context, 0);
 
-		//lwsl_notice("%s: ssl restr %d, simul %d\n", __func__,
-		//		context->simultaneous_ssl_restriction,
-		//		context->simultaneous_ssl);
-
 #if defined(LWS_WITH_STATS)
 	context->updated = 1;
 #endif
@@ -156,10 +152,7 @@ lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd)
 		 * as a live connection.  That way we can retry when more
 		 * pieces come if we're not sorted yet
 		 */
-		if (wsi->mode == LWSCM_SSL_INIT)
-			wsi->mode = LWSCM_SSL_ACK_PENDING;
-		else
-			wsi->mode = LWSCM_SSL_ACK_PENDING_RAW;
+		lwsi_set_state(wsi, LRS_SSL_ACK_PENDING);
 
 		lws_pt_lock(pt, __func__);
 		if (__insert_wsi_socket_into_fds(context, wsi)) {
@@ -175,8 +168,8 @@ lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd)
 
 		/* fallthru */
 
-	case LWSCM_SSL_ACK_PENDING:
-	case LWSCM_SSL_ACK_PENDING_RAW:
+	case LRS_SSL_ACK_PENDING:
+
 		if (lws_change_pollfd(wsi, LWS_POLLOUT, 0)) {
 			lwsl_err("%s: lws_change_pollfd failed\n", __func__);
 			goto fail;
@@ -258,7 +251,7 @@ lws_server_socket_service_ssl(struct lws *wsi, lws_sockfd_type accept_fd)
 				      LWSSTATS_C_SSL_CONNECTIONS_ACCEPT_SPIN, 1);
 		n = lws_tls_server_accept(wsi);
 		lws_latency(context, wsi,
-			"SSL_accept LWSCM_SSL_ACK_PENDING\n", n, n == 1);
+			"SSL_accept LRS_SSL_ACK_PENDING\n", n, n == 1);
 		lwsl_info("SSL_accept says %d\n", n);
 		switch (n) {
 		case LWS_SSL_CAPABLE_DONE:
@@ -302,16 +295,17 @@ accepted:
 		lws_set_timeout(wsi, PENDING_TIMEOUT_ESTABLISH_WITH_SERVER,
 				context->timeout_secs);
 
-		if (wsi->mode == LWSCM_SSL_ACK_PENDING_RAW)
-			wsi->mode = LWSCM_RAW;
-		else
-			wsi->mode = LWSCM_HTTP_SERVING;
+		lwsi_set_state(wsi, LRS_ESTABLISHED);
+
 #if defined(LWS_WITH_HTTP2)
 		if (lws_h2_configure_if_upgraded(wsi))
 			goto fail;
 #endif
 		lwsl_debug("accepted new SSL conn\n");
 		break;
+
+	default:
+		break;
 	}
 
 	return 0;
diff --git a/lib/service.c b/lib/service.c
index a82c1cb82c251c59b1836701443528491b85ae0a..c9210a2ecfc565692910031942efbe94affb79a9 100644
--- a/lib/service.c
+++ b/lib/service.c
@@ -41,23 +41,23 @@ lws_calllback_as_writeable(struct lws *wsi)
 	}
 #endif
 
-	switch (wsi->mode) {
-	case LWSCM_RAW:
+	switch (lwsi_role(wsi)) {
+	case LWSI_ROLE_RAW_SOCKET:
 		n = LWS_CALLBACK_RAW_WRITEABLE;
 		break;
-	case LWSCM_RAW_FILEDESC:
+	case LWSI_ROLE_RAW_FILE:
 		n = LWS_CALLBACK_RAW_WRITEABLE_FILE;
 		break;
-	case LWSCM_WS_CLIENT:
+	case LWSI_ROLE_WS1_CLIENT:
+	case LWSI_ROLE_WS2_CLIENT:
 		n = LWS_CALLBACK_CLIENT_WRITEABLE;
 		break;
-	case LWSCM_WSCL_ISSUE_HTTP_BODY:
-	case LWSCM_HTTP2_CLIENT:
-	case LWSCM_HTTP2_CLIENT_ACCEPTED:
+	case LWSI_ROLE_H1_CLIENT:
+	case LWSI_ROLE_H2_CLIENT:
 		n = LWS_CALLBACK_CLIENT_HTTP_WRITEABLE;
 		break;
-	case LWSCM_HTTP2_WS_SERVING:
-	case LWSCM_WS_SERVING:
+	case LWSI_ROLE_WS1_SERVER:
+	case LWSI_ROLE_WS2_SERVER:
 		n = LWS_CALLBACK_SERVER_WRITEABLE;
 		break;
 	default:
@@ -117,12 +117,12 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
 		/* leave POLLOUT active either way */
 		goto bail_ok;
 	} else
-		if (wsi->state == LWSS_FLUSHING_SEND_BEFORE_CLOSE) {
+		if (lwsi_state(wsi) == LRS_FLUSHING_BEFORE_CLOSE) {
 			wsi->socket_is_permanently_unusable = 1;
 			goto bail_die; /* retry closing now */
 		}
 
-	if (wsi->mode == LWSCM_WSCL_ISSUE_HTTP_BODY)
+	if (lwsi_state(wsi) == LRS_ISSUE_HTTP_BODY)
 		goto user_service;
 
 #ifdef LWS_WITH_HTTP2
@@ -167,14 +167,14 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
 	 * 3a: close notification packet requested from close api
 	 */
 
-	if (wsi->state == LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION) {
+	if (lwsi_state(wsi) == LRS_WAITING_TO_SEND_CLOSE) {
 		lwsl_debug("sending close packet\n");
 		wsi->waiting_to_send_close_frame = 0;
 		n = lws_write(wsi, &wsi->ws->ping_payload_buf[LWS_PRE],
 			      wsi->ws->close_in_ping_buffer_len,
 			      LWS_WRITE_CLOSE);
 		if (n >= 0) {
-			wsi->state = LWSS_AWAITING_CLOSE_ACK;
+			lwsi_set_state(wsi, LRS_AWAITING_CLOSE_ACK);
 			lws_set_timeout(wsi, PENDING_TIMEOUT_CLOSE_ACK, 5);
 			lwsl_debug("sent close indication, awaiting ack\n");
 
@@ -186,8 +186,8 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
 
 	/* else, the send failed and we should just hang up */
 
-	if ((lws_state_is_ws(wsi->state) && wsi->ws->ping_pending_flag) ||
-	    (wsi->state == LWSS_RETURNED_CLOSE_ALREADY &&
+	if ((lwsi_role_ws(wsi) && wsi->ws->ping_pending_flag) ||
+	    (lwsi_state(wsi) == LRS_RETURNED_CLOSE &&
 	     wsi->ws->payload_is_close)) {
 
 		if (wsi->ws->payload_is_close)
@@ -210,7 +210,7 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
 		goto bail_ok;
 	}
 
-	if (lws_state_is_ws(wsi->state) && !wsi->socket_is_permanently_unusable &&
+	if (lwsi_role_ws_client(wsi) && !wsi->socket_is_permanently_unusable &&
 	    wsi->ws->send_check_ping) {
 
 		lwsl_info("issuing ping on wsi %p\n", wsi);
@@ -235,7 +235,7 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
 	/* Priority 4: if we are closing, not allowed to send more data frags
 	 *	       which means user callback or tx ext flush banned now
 	 */
-	if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY)
+	if (lwsi_state(wsi) == LRS_RETURNED_CLOSE)
 		goto user_service;
 
 	/* Priority 5: Tx path extension with more to send
@@ -245,7 +245,7 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
 	 *	       payload ordering, but since they are always complete
 	 *	       fragments control packets can interleave OK.
 	 */
-	if (lws_state_is_ws(wsi->state) && wsi->ws->tx_draining_ext) {
+	if (lwsi_role_ws_client(wsi) && wsi->ws->tx_draining_ext) {
 		lwsl_ext("SERVICING TX EXT DRAINING\n");
 		if (lws_write(wsi, NULL, 0, LWS_WRITE_CONTINUATION) < 0)
 			goto bail_die;
@@ -272,7 +272,7 @@ lws_handle_POLLOUT_event(struct lws *wsi, struct lws_pollfd *pollfd)
 	 */
 
 	ret = 1;
-	if (wsi->mode == LWSCM_RAW || wsi->mode == LWSCM_RAW_FILEDESC)
+	if (lwsi_role_raw(wsi))
 		ret = 0;
 
 	while (ret == 1) {
@@ -384,9 +384,8 @@ user_service:
 		vwsi->leave_pollout_active = 0;
 	}
 
-	if (wsi->mode != LWSCM_WSCL_ISSUE_HTTP_BODY &&
-	    wsi->mode != LWSCM_HTTP2_CLIENT_ACCEPTED &&
-	    !wsi->hdr_parsing_completed)
+	if (lwsi_role_client(wsi) && !wsi->hdr_parsing_completed &&
+			lwsi_state(wsi) != LRS_H2_WAITING_TO_SEND_HEADERS)
 		goto bail_ok;
 
 
@@ -408,10 +407,7 @@ user_service_go_again:
 	 * notifications, so we can't hold pointers
 	 */
 
-	if (wsi->mode != LWSCM_HTTP2_SERVING &&
-	    wsi->mode != LWSCM_HTTP2_WS_SERVING &&
-	    wsi->mode != LWSCM_HTTP2_CLIENT &&
-	    wsi->mode != LWSCM_HTTP2_CLIENT_ACCEPTED) {
+	if (!lwsi_role_h2(wsi)) {
 		lwsl_info("%s: non http2\n", __func__);
 		goto notify;
 	}
@@ -479,7 +475,7 @@ user_service_go_again:
 		}
 
 		w->h2.requested_POLLOUT = 0;
-		lwsl_info("%s: child %p (state %d)\n", __func__, w, w->state);
+		lwsl_info("%s: child %p (state %d)\n", __func__, w, lwsi_state(w));
 
 		/* if we arrived here, even by looping, we checked choked */
 		w->could_have_pending = 0;
@@ -497,14 +493,14 @@ user_service_go_again:
 			goto next_child;
 		}
 
-		if (w->state == LWSS_HTTP2_CLIENT_WAITING_TO_SEND_HEADERS) {
+		if (lwsi_state(w) == LRS_H2_WAITING_TO_SEND_HEADERS) {
 			if (lws_h2_client_handshake(w))
 				return -1;
 
 			goto next_child;
 		}
 
-		if (w->state == LWSS_HTTP2_DEFERRING_ACTION) {
+		if (lwsi_state(w) == LRS_DEFERRING_ACTION) {
 
 			/*
 			 * we had to defer the http_action to the POLLOUT
@@ -513,7 +509,7 @@ user_service_go_again:
 			 * that there is no partial pending on the network wsi.
 			 */
 
-			w->state = LWSS_HTTP2_ESTABLISHED;
+			lwsi_set_state(w, LRS_ESTABLISHED);
 
 			lwsl_info("  h2 action start...\n");
 			n = lws_http_action(w);
@@ -536,7 +532,7 @@ user_service_go_again:
 			goto next_child;
 		}
 
-		if (w->state == LWSS_HTTP_ISSUING_FILE) {
+		if (lwsi_state(w) == LRS_ISSUING_FILE) {
 
 			((volatile struct lws *)w)->leave_pollout_active = 0;
 
@@ -572,14 +568,14 @@ user_service_go_again:
 
 		/* Notify peer that we decided to close */
 
-		if (w->state == LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION) {
+		if (lwsi_state(w) == LRS_WAITING_TO_SEND_CLOSE) {
 			lwsl_debug("sending close packet\n");
 			w->waiting_to_send_close_frame = 0;
 			n = lws_write(w, &w->ws->ping_payload_buf[LWS_PRE],
 				      w->ws->close_in_ping_buffer_len,
 				      LWS_WRITE_CLOSE);
 			if (n >= 0) {
-				w->state = LWSS_AWAITING_CLOSE_ACK;
+				lwsi_set_state(w, LRS_AWAITING_CLOSE_ACK);
 				lws_set_timeout(w, PENDING_TIMEOUT_CLOSE_ACK, 5);
 				lwsl_debug("sent close indication, awaiting ack\n");
 			}
@@ -590,12 +586,13 @@ user_service_go_again:
 		/* Acknowledge receipt of peer's notification he closed,
 		 * then logically close ourself */
 
-		if ((lws_state_is_ws(w->state) && w->ws->ping_pending_flag) ||
-		    (w->state == LWSS_RETURNED_CLOSE_ALREADY &&
+		if ((lwsi_role_ws(w) && w->ws->ping_pending_flag) ||
+		    (lwsi_state(w) == LRS_RETURNED_CLOSE &&
 		     w->ws->payload_is_close)) {
 
 			if (w->ws->payload_is_close)
-				write_type = LWS_WRITE_CLOSE | LWS_WRITE_H2_STREAM_END;
+				write_type = LWS_WRITE_CLOSE |
+					     LWS_WRITE_H2_STREAM_END;
 
 			n = lws_write(w, &w->ws->ping_payload_buf[LWS_PRE],
 				      w->ws->ping_payload_len, write_type);
@@ -608,7 +605,7 @@ user_service_go_again:
 				/* oh... a close frame was it... then we are done */
 				lwsl_debug("Acknowledged peer's close packet\n");
 				w->ws->payload_is_close = 0;
-				w->state = LWSS_RETURNED_CLOSE_ALREADY;
+				lwsi_set_state(w, LRS_RETURNED_CLOSE);
 				lws_close_free_wsi(w, LWS_CLOSE_STATUS_NOSTATUS, "returned close packet");
 				wa = &wsi->h2.child_list;
 				goto next_child;
@@ -741,7 +738,7 @@ __lws_service_timeout_check(struct lws *wsi, time_t sec)
 		 * cleanup like flush partials.
 		 */
 		wsi->socket_is_permanently_unusable = 1;
-		if (wsi->mode == LWSCM_WSCL_WAITING_SSL && wsi->protocol)
+		if (lwsi_state(wsi) == LRS_WAITING_SSL && wsi->protocol)
 			wsi->protocol->callback(wsi,
 				LWS_CALLBACK_CLIENT_CONNECTION_ERROR,
 				wsi->user_space,
@@ -1092,7 +1089,7 @@ lws_is_ws_with_ext(struct lws *wsi)
 #if defined(LWS_WITHOUT_EXTENSIONS)
 	return 0;
 #else
-	return lws_state_is_ws(wsi->state) && !!wsi->count_act_ext;
+	return lwsi_role_ws(wsi) && !!wsi->count_act_ext;
 #endif
 }
 
@@ -1368,7 +1365,7 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd,
 				wsi = vh->same_vh_protocol_list[n];
 
 				while (wsi) {
-					if (lws_state_is_ws(wsi->state) &&
+					if (lwsi_role_ws(wsi) &&
 					    !wsi->socket_is_permanently_unusable &&
 					    !wsi->ws->send_check_ping &&
 					    wsi->ws->time_next_ping_check &&
@@ -1452,7 +1449,7 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd,
 	}
 
 #ifdef LWS_OPENSSL_SUPPORT
-	if (wsi->state == LWSS_SHUTDOWN && lws_is_ssl(wsi) && wsi->ssl) {
+	if (lwsi_state(wsi) == LRS_SHUTDOWN && lws_is_ssl(wsi) && wsi->ssl) {
 		n = 0;
 		switch (__lws_tls_shutdown(wsi)) {
 		case LWS_SSL_CAPABLE_DONE:
@@ -1470,8 +1467,10 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd,
 
 	/* okay, what we came here to do... */
 
-	switch (wsi->mode) {
-	case LWSCM_EVENT_PIPE:
+	// lwsl_err("x 0x%x\n", wsi->wsistate);
+
+	switch (lwsi_role(wsi)) {
+	case LWSI_ROLE_EVENT_PIPE:
 	{
 #if !defined(WIN32) && !defined(_WIN32)
 		char s[10];
@@ -1500,16 +1499,17 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd,
 
 		goto handled;
 	}
-	case LWSCM_HTTP_SERVING:
-	case LWSCM_HTTP_CLIENT:
-	case LWSCM_HTTP_SERVING_ACCEPTED:
-	case LWSCM_SERVER_LISTENER:
-	case LWSCM_SSL_ACK_PENDING:
-	case LWSCM_SSL_ACK_PENDING_RAW:
-
-		if (wsi->state == LWSS_CLIENT_HTTP_ESTABLISHED)
+
+	case LWSI_ROLE_H1_CLIENT:
+
+		if (lwsi_state(wsi) == LRS_ESTABLISHED)
 			goto handled;
 
+		goto do_client;
+
+	case LWSI_ROLE_H1_SERVER:
+	case LWSI_ROLE_LISTEN_SOCKET:
+
 #ifdef LWS_WITH_CGI
 		if (wsi->cgi && (pollfd->revents & LWS_POLLOUT)) {
 			n = lws_handle_POLLOUT_event(wsi, pollfd);
@@ -1519,13 +1519,13 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd,
 		}
 #endif
 		/* fallthru */
-	case LWSCM_RAW:
+	case LWSI_ROLE_RAW_SOCKET:
 		n = lws_server_socket_service(context, wsi, pollfd);
 		if (n) /* closed by above */
 			return 1;
 		goto handled;
 
-	case LWSCM_RAW_FILEDESC:
+	case LWSI_ROLE_RAW_FILE:
 
 		if (pollfd->revents & LWS_POLLOUT) {
 			n = lws_calllback_as_writeable(wsi);
@@ -1537,14 +1537,13 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd,
 				goto close_and_handled;
 		}
 		n = LWS_CALLBACK_RAW_RX;
-		if (wsi->mode == LWSCM_RAW_FILEDESC)
+		if (lwsi_role(wsi) == LWSI_ROLE_RAW_FILE)
 			n = LWS_CALLBACK_RAW_RX_FILE;
 
 		if (pollfd->revents & LWS_POLLIN) {
 			if (user_callback_handle_rxflow(
 					wsi->protocol->callback,
-					wsi, n,
-					wsi->user_space, NULL, 0)) {
+					wsi, n, wsi->user_space, NULL, 0)) {
 				lwsl_debug("raw rx callback closed it\n");
 				goto close_and_handled;
 			}
@@ -1555,32 +1554,44 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd,
 		n = 0;
 		goto handled;
 
-	case LWSCM_WS_SERVING:
-	case LWSCM_WS_CLIENT:
-	case LWSCM_HTTP2_SERVING:
-	case LWSCM_HTTP2_WS_SERVING:
-	case LWSCM_HTTP_CLIENT_ACCEPTED:
-	case LWSCM_HTTP2_CLIENT_ACCEPTED:
-	case LWSCM_HTTP2_CLIENT:
+	case LWSI_ROLE_WS1_SERVER:
+	case LWSI_ROLE_WS1_CLIENT:
+	case LWSI_ROLE_H2_SERVER:
+	case LWSI_ROLE_WS2_SERVER:
+	case LWSI_ROLE_H2_CLIENT:
+	case LWSI_ROLE_WS2_CLIENT:
+
+		 lwsl_info("%s: wsistate 0x%x, pollout %d\n", __func__,
+			   wsi->wsistate, pollfd->revents & LWS_POLLOUT);
+
+		/*
+		 * something went wrong with parsing the handshake, and
+		 * we ended up back in the event loop without completing it
+		 */
+		if (lwsi_state(wsi) == LRS_PRE_WS_SERVING_ACCEPT) {
+			wsi->socket_is_permanently_unusable = 1;
+			goto close_and_handled;
+		}
 
-		// lwsl_notice("%s: mode %d, state %d\n", __func__, wsi->mode, wsi->state);
+		if (lwsi_state(wsi) == LRS_WAITING_CONNECT)
+			goto do_client;
 
 		/* 1: something requested a callback when it was OK to write */
 
 		if ((pollfd->revents & LWS_POLLOUT) &&
-		    (wsi->state & _LSF_POLLOUT) /* ...our state cares ... */ &&
+		    (lwsi_state(wsi) & LWSIFS_POCB) /* ...our state cares ... */ &&
 		    lws_handle_POLLOUT_event(wsi, pollfd)) {
-			if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY)
-				wsi->state = LWSS_FLUSHING_SEND_BEFORE_CLOSE;
+			if (lwsi_state(wsi) == LRS_RETURNED_CLOSE)
+				lwsi_set_state(wsi, LRS_FLUSHING_BEFORE_CLOSE);
 			// lwsl_notice("lws_service_fd: closing\n");
 			/* the write failed... it's had it */
 			wsi->socket_is_permanently_unusable = 1;
 			goto close_and_handled;
 		}
 
-		if (wsi->state == LWSS_RETURNED_CLOSE_ALREADY ||
-		    wsi->state == LWSS_WAITING_TO_SEND_CLOSE_NOTIFICATION ||
-		    wsi->state == LWSS_AWAITING_CLOSE_ACK) {
+		if (lwsi_state(wsi) == LRS_RETURNED_CLOSE ||
+		    lwsi_state(wsi) == LRS_WAITING_TO_SEND_CLOSE ||
+		    lwsi_state(wsi) == LRS_AWAITING_CLOSE_ACK) {
 			/*
 			 * we stopped caring about anything except control
 			 * packets.  Force flow control off, defeat tx
@@ -1624,11 +1635,11 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd,
 		/* 2: RX Extension needs to be drained
 		 */
 
-		if (lws_state_is_ws(wsi->state) && wsi->ws->rx_draining_ext) {
+		if (lwsi_role_ws(wsi) && wsi->ws && wsi->ws->rx_draining_ext) {
 
 			lwsl_ext("%s: RX EXT DRAINING: Service\n", __func__);
 #ifndef LWS_NO_CLIENT
-			if (wsi->mode == LWSCM_WS_CLIENT) {
+			if (lwsi_role_ws_client(wsi)) {
 				n = lws_client_rx_sm(wsi, 0);
 				if (n < 0)
 					/* we closed wsi */
@@ -1687,6 +1698,7 @@ lws_service_fd_tsi(struct lws_context *context, struct lws_pollfd *pollfd,
 
 		if (!(pollfd->revents & pollfd->events & LWS_POLLIN))
 			break;
+
 read:
 		if (lws_is_flowcontrolled(wsi)) {
 			lwsl_info("%s: %p should be rxflow (bm 0x%x)..\n",
@@ -1695,13 +1707,16 @@ read:
 		}
 
 		if (wsi->ah && wsi->ah->rxlen - wsi->ah->rxpos) {
-			lwsl_info("%s: %p: inherited ah rx %d\n", __func__, wsi, wsi->ah->rxlen - wsi->ah->rxpos);
+			lwsl_info("%s: %p: inherited ah rx %d\n", __func__,
+					wsi, wsi->ah->rxlen - wsi->ah->rxpos);
 			eff_buf.token_len = wsi->ah->rxlen -
 					    wsi->ah->rxpos;
 			eff_buf.token = (char *)wsi->ah->rx +
 					wsi->ah->rxpos;
 		} else {
-			if (wsi->mode != LWSCM_HTTP_CLIENT_ACCEPTED) {
+			if (!(lwsi_role_client(wsi) &&
+			      (lwsi_state(wsi) != LRS_ESTABLISHED &&
+			       lwsi_state(wsi) != LRS_H2_WAITING_TO_SEND_HEADERS))) {
 				/*
 				 * extension may not consume everything
 				 * (eg, pmd may be constrained
@@ -1767,8 +1782,8 @@ read:
 
 drain:
 #ifndef LWS_NO_CLIENT
-		if ((wsi->mode == LWSCM_HTTP_CLIENT_ACCEPTED ||
-		     wsi->mode == LWSS_HTTP2_CLIENT_ESTABLISHED) &&
+		if (lwsi_role_http_client(wsi) &&
+				wsi->hdr_parsing_completed &&
 		    !wsi->told_user_closed) {
 
 			/*
@@ -1848,7 +1863,7 @@ drain:
 				&& !wsi->client_h2_alpn
 #endif
 				) {
-			lwsl_notice("%s: %p: detaching ah\n", __func__, wsi);
+			lwsl_info("%s: %p: detaching ah\n", __func__, wsi);
 			lws_header_table_force_to_detachable_state(wsi);
 			lws_header_table_detach(wsi, 0);
 		}
@@ -1878,7 +1893,7 @@ drain:
 
 		break;
 #ifdef LWS_WITH_CGI
-	case LWSCM_CGI: /* we exist to handle a cgi's stdin/out/err data...
+	case LWSI_ROLE_CGI: /* we exist to handle a cgi's stdin/out/err data...
 			 * do the callback on our master wsi
 			 */
 		{
@@ -1901,9 +1916,9 @@ drain:
 			args.stdwsi = &wsi->parent->cgi->stdwsi[0];
 			args.hdr_state = wsi->hdr_state;
 
-			lwsl_debug("CGI LWS_STDOUT %p mode %d state %d\n",
-				   wsi->parent, wsi->parent->mode,
-				   wsi->parent->state);
+			lwsl_debug("CGI LWS_STDOUT %p role 0x%x state 0x%x\n",
+				   wsi->parent, lwsi_role(wsi->parent),
+				   lwsi_state(wsi->parent));
 
 			if (user_callback_handle_rxflow(
 					wsi->parent->protocol->callback,
@@ -1915,32 +1930,23 @@ drain:
 			break;
 		}
 #endif
-	/*
-	 * something went wrong with parsing the handshake, and
-	 * we ended up back in the event loop without completing it
-	 */
-	case LWSCM_PRE_WS_SERVING_ACCEPT:
-		wsi->socket_is_permanently_unusable = 1;
-		goto close_and_handled;
+	}
 
-	default:
-#ifdef LWS_NO_CLIENT
-		break;
-#else
-		if ((pollfd->revents & LWS_POLLOUT) &&
-		    lws_handle_POLLOUT_event(wsi, pollfd)) {
-			lwsl_debug("POLLOUT event closed it\n");
-			goto close_and_handled;
-		}
+	n = 0;
+	goto handled;
 
-		n = lws_client_socket_service(wsi, pollfd, NULL);
-		if (n)
-			return 1;
-		goto handled;
-#endif
+do_client:
+#if !defined(LWS_NO_CLIENT)
+	if ((pollfd->revents & LWS_POLLOUT) &&
+	    lws_handle_POLLOUT_event(wsi, pollfd)) {
+		lwsl_debug("POLLOUT event closed it\n");
+		goto close_and_handled;
 	}
 
-	n = 0;
+	n = lws_client_socket_service(wsi, pollfd, NULL);
+	if (n)
+		return 1;
+#endif
 	goto handled;
 
 close_and_handled:
diff --git a/lib/tls/mbedtls/ssl.c b/lib/tls/mbedtls/ssl.c
index 3ecfa8db64aa5637fa78d0c12f145f0381a48ec8..7bf33a01381f304343c9d1d61cb9ed538668d474 100644
--- a/lib/tls/mbedtls/ssl.c
+++ b/lib/tls/mbedtls/ssl.c
@@ -257,16 +257,13 @@ lws_ssl_close(struct lws *wsi)
 	SSL_free(wsi->ssl);
 	wsi->ssl = NULL;
 
-	if (!(wsi->mode & LWSCM_FLAG_IMPLIES_CALLBACK_CLOSED_CLIENT_HTTP) &&
+	if (!lwsi_role_client(wsi) &&
 	    wsi->context->simultaneous_ssl_restriction &&
 	    wsi->context->simultaneous_ssl-- ==
 			    wsi->context->simultaneous_ssl_restriction)
 		/* we made space and can do an accept */
 		lws_gate_accepts(wsi->context, 1);
 
-	//lwsl_notice("%s: ssl restr %d, simul %d\n", __func__,
-	//		wsi->context->simultaneous_ssl_restriction,
-	//		wsi->context->simultaneous_ssl);
 #if defined(LWS_WITH_STATS)
 	wsi->context->updated = 1;
 #endif
diff --git a/lib/tls/openssl/ssl.c b/lib/tls/openssl/ssl.c
index 62ce488647bf0b0d916b2414012a5f5251cba255..3f37a9bf711e314acb7ab204d38f115d36e62646 100644
--- a/lib/tls/openssl/ssl.c
+++ b/lib/tls/openssl/ssl.c
@@ -659,7 +659,7 @@ lws_tls_peer_cert_info(struct lws *wsi, enum lws_tls_cert_info type,
 	x509 = SSL_get_peer_certificate(wsi->ssl);
 
 	if (!x509) {
-		lwsl_notice("no peer cert\n");
+		lwsl_debug("no peer cert\n");
 
 		return -1;
 	}
diff --git a/minimal-examples/http-client/minimal-http-client/minimal-http-client.c b/minimal-examples/http-client/minimal-http-client/minimal-http-client.c
index 6796346fa3d3d4b74bf35882b23b5be1cd658e7b..3056283339d366c07c90017fb9034b8865c9e024 100644
--- a/minimal-examples/http-client/minimal-http-client/minimal-http-client.c
+++ b/minimal-examples/http-client/minimal-http-client/minimal-http-client.c
@@ -88,22 +88,39 @@ sigint_handler(int sig)
 	interrupted = 1;
 }
 
+static int findswitch(int argc, char **argv, const char *val)
+{
+	while (--argc > 0) {
+		if (!strcmp(argv[argc], val))
+			return argc;
+	}
+
+	return 0;
+}
+
 int main(int argc, char **argv)
 {
 	struct lws_context_creation_info info;
 	struct lws_client_connect_info i;
 	struct lws_context *context;
-	int n = 0;
+	int logs = LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE
+		   /*
+		    * For LLL_ verbosity above NOTICE to be built into lws,
+		    * lws must have been configured and built with
+		    * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE
+		    *
+		    * | LLL_INFO   | LLL_PARSER  | LLL_HEADER | LLL_EXT |
+		    *   LLL_CLIENT | LLL_LATENCY | LLL_DEBUG
+		    */ ;
+	int n = 0, m;
 
 	signal(SIGINT, sigint_handler);
-	lws_set_log_level(LLL_USER | LLL_ERR | LLL_WARN | LLL_NOTICE
-			/* for LLL_ verbosity above NOTICE to be built into lws,
-			 * lws must have been configured and built with
-			 * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */
-			/* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */
-			/* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */
-			/* | LLL_DEBUG */, NULL);
+	/* you can set the log level on commandline with, eg, -d 15 */
+	m = findswitch(argc, argv, "-d");
+	if (m && m + 1 < argc)
+		logs = atoi(argv[m + 1]);
 
+	lws_set_log_level(logs, NULL);
 	lwsl_user("LWS minimal http client\n");
 
 	memset(&info, 0, sizeof info); /* otherwise uninitialized garbage */
diff --git a/minimal-examples/raw/minimal-raw-file/minimal-raw-file.c b/minimal-examples/raw/minimal-raw-file/minimal-raw-file.c
index be3e74d0a0dae20dcb554098a1d315308a0fe2e6..98199572b4c7a8c7d6cc8a8970c62facaa34b3f3 100644
--- a/minimal-examples/raw/minimal-raw-file/minimal-raw-file.c
+++ b/minimal-examples/raw/minimal-raw-file/minimal-raw-file.c
@@ -54,7 +54,7 @@ callback_raw_test(struct lws *wsi, enum lws_callback_reasons reason,
 		break;
 
 	case LWS_CALLBACK_PROTOCOL_DESTROY:
-		if (vhd->u.filefd != -1)
+		if (vhd && vhd->u.filefd != -1)
 			close(vhd->u.filefd);
 		break;
 
diff --git a/minimal-examples/raw/minimal-raw-vhost/minimal-raw-vhost.c b/minimal-examples/raw/minimal-raw-vhost/minimal-raw-vhost.c
index 8c1b2ff74c98baf3315668b2f9eaffadb7648b4d..7989c2c4c140a6055c73f20ec378a6134829cf04 100644
--- a/minimal-examples/raw/minimal-raw-vhost/minimal-raw-vhost.c
+++ b/minimal-examples/raw/minimal-raw-vhost/minimal-raw-vhost.c
@@ -131,7 +131,7 @@ int main(int argc, char **argv)
 			/* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */
 			/* | LLL_DEBUG */, NULL);
 
-	lwsl_user("LWS minimal raw vhost\n");
+	lwsl_user("LWS minimal raw vhost | nc localhost 7681\n");
 
 	context = lws_create_context(&info);
 	if (!context) {
diff --git a/minimal-examples/ws-client/minimal-ws-client-pmd-bulk/minimal-ws-client-pmd-bulk.c b/minimal-examples/ws-client/minimal-ws-client-pmd-bulk/minimal-ws-client-pmd-bulk.c
index b5825e3cd6728ef6f0397f6809bfab3da9ba4c51..2f3c331c94d5714dbac106dad7b033c97c9d3945 100644
--- a/minimal-examples/ws-client/minimal-ws-client-pmd-bulk/minimal-ws-client-pmd-bulk.c
+++ b/minimal-examples/ws-client/minimal-ws-client-pmd-bulk/minimal-ws-client-pmd-bulk.c
@@ -76,7 +76,7 @@ static int findswitch(int argc, char **argv, const char *val)
 {
 	while (--argc > 0) {
 		if (!strcmp(argv[argc], val))
-			return 1;
+			return argc;
 	}
 
 	return 0;
@@ -110,6 +110,7 @@ int main(int argc, char **argv)
 			/* | LLL_DEBUG */, NULL);
 
 	lwsl_user("LWS minimal ws client + permessage-deflate + multifragment bulk message\n");
+	lwsl_user("   needs minimal-ws-server-pmd-bulk running to communicate with\n");
 	lwsl_user("   %s [-n (no exts)] [-c (compressible)]\n", argv[0]);
 	context = lws_create_context(&info);
 	if (!context) {
diff --git a/minimal-examples/ws-client/minimal-ws-client-rx/minimal-ws-client.c b/minimal-examples/ws-client/minimal-ws-client-rx/minimal-ws-client.c
index 5c82597adeb5e96a08b7d2c9bef608fdbc05438c..a7ff86ae2902d81836b1766a18dcd0ea9b86d507 100644
--- a/minimal-examples/ws-client/minimal-ws-client-rx/minimal-ws-client.c
+++ b/minimal-examples/ws-client/minimal-ws-client-rx/minimal-ws-client.c
@@ -82,7 +82,7 @@ int main(int argc, char **argv)
 			/* for LLL_ verbosity above NOTICE to be built into lws,
 			 * lws must have been configured and built with
 			 * -DCMAKE_BUILD_TYPE=DEBUG instead of =RELEASE */
-			| LLL_INFO /* | LLL_PARSER */ /* | LLL_HEADER */
+			/* | LLL_INFO */ /* | LLL_PARSER */ /* | LLL_HEADER */
 			/* | LLL_EXT */ /* | LLL_CLIENT */ /* | LLL_LATENCY */
 			/* | LLL_DEBUG */, NULL);
 
diff --git a/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/mount-origin/index.html b/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/mount-origin/index.html
index 29954f44ce58cc95f366bf2b58d1d90468bd760d..f6a22f8d9c9fe1bc812f8768855fc2e5a0a1479e 100644
--- a/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/mount-origin/index.html
+++ b/minimal-examples/ws-server/minimal-ws-server-pmd-bulk/mount-origin/index.html
@@ -58,10 +58,10 @@ try {
 		document.getElementById("status").textContent = "ws open "+ ws.extensions;
 	} 
 
-	ws.onmessage =function got_packet(msg) {
-		console.log("Received ws message len " + msg.data.length);
+	ws.onmessage = function got_packet(msg) {
+		console.log("Received ws message len " + msg.data.size);
 		document.getElementById("r").value =
-			document.getElementById("r").value + "\nReceived: " + msg.data.length + " bytes\n";
+			document.getElementById("r").value + "\nReceived: " + msg.data.size + " bytes\n";
 		document.getElementById("r").scrollTop =
 			document.getElementById("r").scrollHeight;
 
diff --git a/minimal-examples/ws-server/minimal-ws-server-threads/CMakeLists.txt b/minimal-examples/ws-server/minimal-ws-server-threads/CMakeLists.txt
index 4f2320536be7bf0824684c3e98af7ba0d67c7a92..cbfee936e96e21aee317dcbb2e408d6e3220a2fe 100644
--- a/minimal-examples/ws-server/minimal-ws-server-threads/CMakeLists.txt
+++ b/minimal-examples/ws-server/minimal-ws-server-threads/CMakeLists.txt
@@ -82,9 +82,9 @@ if (requirements)
 	add_executable(${SAMP} ${SRCS})
 
 	if (websockets_shared)
-		target_link_libraries(${SAMP} websockets_shared)
+		target_link_libraries(${SAMP} websockets_shared pthread)
 		add_dependencies(${SAMP} websockets_shared)
 	else()
-		target_link_libraries(${SAMP} websockets)
+		target_link_libraries(${SAMP} websockets pthread)
 	endif()
-endif()
\ No newline at end of file
+endif()
diff --git a/minimal-examples/ws-server/minimal-ws-server-threads/mount-origin/index.html b/minimal-examples/ws-server/minimal-ws-server-threads/mount-origin/index.html
index 5f6b28e4708b43225fde276be40aeac164709c2b..928eaf9062fb5c13046120db6669967dabe80d6a 100644
--- a/minimal-examples/ws-server/minimal-ws-server-threads/mount-origin/index.html
+++ b/minimal-examples/ws-server/minimal-ws-server-threads/mount-origin/index.html
@@ -54,8 +54,7 @@ function new_ws(urlpath, protocol)
 ws = new_ws(get_appropriate_ws_url(""), "lws-minimal");
 try {
 	ws.onopen = function() {
-		document.getElementById("m").disabled = 0;
-		document.getElementById("b").disabled = 0;
+		document.getElementById("r").disabled = 0;
 	} 
 
 	ws.onmessage =function got_packet(msg) {
@@ -78,8 +77,7 @@ try {
 	} 
 
 	ws.onclose = function(){
-		document.getElementById("m").disabled = 1;
-		document.getElementById("b").disabled = 1;
+		document.getElementById("r").disabled = 1;
 	}
 } catch(exception) {
 	alert('<p>Error' + exception);  
diff --git a/plugins/ssh-base/sshd.c b/plugins/ssh-base/sshd.c
index 3c53f55f09407720ba07946cfafaf099e00ff978..bee87fb6df5576819a0bb8af0ccc73db64ceeb64 100644
--- a/plugins/ssh-base/sshd.c
+++ b/plugins/ssh-base/sshd.c
@@ -1724,7 +1724,7 @@ again:
 			if (ch->peer_window_est < 32768) {
 				write_task(pss, ch, SSH_WT_WINDOW_ADJUST);
 				ch->peer_window_est += 32768;
-				lwsl_notice("extra peer WINDOW_ADJUST (~ %d)\n",
+				lwsl_info("extra peer WINDOW_ADJUST (~ %d)\n",
 					    ch->peer_window_est);
 			}
 
diff --git a/test-apps/attack.sh b/test-apps/attack.sh
index 554ac4dbe2f65493d4adb5d1440c0c8a3b60b5eb..672ff646eb6a96d62e45fd1130a89fa1fc43865d 100755
--- a/test-apps/attack.sh
+++ b/test-apps/attack.sh
@@ -779,6 +779,8 @@ echo
 echo "--- survived OK ---"
 kill -2 $CPID
 
+exit 0
+
 # coverage...
 # run the test client against mirror for one period and exit
 killall libwebsockets-test-server 2>/dev/null
diff --git a/test-apps/test-client.c b/test-apps/test-client.c
index 784d4e0d113eb4e96a2e853ab3ba20f58b9fac2e..88bc8de6599f51cca150f8a87be56061e2fb7495 100644
--- a/test-apps/test-client.c
+++ b/test-apps/test-client.c
@@ -365,6 +365,7 @@ callback_lws_mirror(struct lws *wsi, enum lws_callback_reasons reason,
 		break;
 
 	case LWS_CALLBACK_CLIENT_WRITEABLE:
+		lwsl_user("LWS_CALLBACK_CLIENT_WRITEABLE\n");
 		if (flag_no_mirror_traffic)
 			return 0;
 
diff --git a/test-apps/test-server.c b/test-apps/test-server.c
index f3a5fce6ffb5780a947a6f241b813de743328678..9e75a0fa7c676529f80823b7e2ab1673c293c993 100644
--- a/test-apps/test-server.c
+++ b/test-apps/test-server.c
@@ -253,6 +253,7 @@ int main(int argc, char **argv)
 #ifndef LWS_NO_DAEMONIZE
 	int daemonize = 0;
 #endif
+	char no_dumb_send = 0;
 
 	/*
 	 * take care to zero down the info struct, he contains random garbaage
@@ -262,7 +263,7 @@ int main(int argc, char **argv)
 	info.port = 7681;
 
 	while (n >= 0) {
-		n = getopt_long(argc, argv, "eci:hsap:d:Dr:C:K:A:R:vu:g:P:kU:", options, NULL);
+		n = getopt_long(argc, argv, "eci:hsap:d:Dr:C:K:A:R:vu:g:P:kU:n", options, NULL);
 		if (n < 0)
 			continue;
 		switch (n) {
@@ -286,6 +287,9 @@ int main(int argc, char **argv)
 		case 'd':
 			debug_level = atoi(optarg);
 			break;
+		case 'n':
+			no_dumb_send = 1;
+			break;
 		case 's':
 			use_ssl = 1;
 			opts |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT;
@@ -501,8 +505,9 @@ int main(int argc, char **argv)
 
 		ms = (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
 		if ((ms - oldms) > 50) {
-			lws_callback_on_writable_all_protocol(context,
-				&protocols[PROTOCOL_DUMB_INCREMENT]);
+			if (!no_dumb_send)
+				lws_callback_on_writable_all_protocol(context,
+					&protocols[PROTOCOL_DUMB_INCREMENT]);
 			oldms = ms;
 		}