From a48d0b5eefb1d830e0dbb41fcc0a903501178296 Mon Sep 17 00:00:00 2001
From: Fabio Alessandrelli <fabio.alessandrelli@gmail.com>
Date: Tue, 28 Jan 2020 14:06:28 +0100
Subject: [PATCH] Disable Nagle's algorithm for WebSocket TCP.

This should greatly decrease latency for the most common use cases.
A new function WebSocketPeer::set_no_delay will allow to configure it if
so desired.
---
 modules/websocket/doc_classes/WebSocketPeer.xml | 10 ++++++++++
 modules/websocket/emws_peer.cpp                 |  5 +++++
 modules/websocket/emws_peer.h                   |  1 +
 modules/websocket/websocket_peer.cpp            |  1 +
 modules/websocket/websocket_peer.h              |  1 +
 modules/websocket/wsl_client.cpp                |  1 +
 modules/websocket/wsl_peer.cpp                  |  6 ++++++
 modules/websocket/wsl_peer.h                    |  1 +
 modules/websocket/wsl_server.cpp                |  1 +
 9 files changed, 27 insertions(+)

diff --git a/modules/websocket/doc_classes/WebSocketPeer.xml b/modules/websocket/doc_classes/WebSocketPeer.xml
index d084e1c6e65..6293b35fbf3 100644
--- a/modules/websocket/doc_classes/WebSocketPeer.xml
+++ b/modules/websocket/doc_classes/WebSocketPeer.xml
@@ -53,6 +53,16 @@
 				Returns [code]true[/code] if this peer is currently connected.
 			</description>
 		</method>
+		<method name="set_no_delay">
+			<return type="void">
+			</return>
+			<argument index="0" name="enabled" type="bool">
+			</argument>
+			<description>
+				Disable Nagle's algorithm on the underling TCP socket (default). See [method StreamPeerTCP.set_no_delay] for more information.
+				[b]Note:[/b] Not available in the HTML5 export.
+			</description>
+		</method>
 		<method name="set_write_mode">
 			<return type="void">
 			</return>
diff --git a/modules/websocket/emws_peer.cpp b/modules/websocket/emws_peer.cpp
index d07360c5255..effed8e4d9f 100644
--- a/modules/websocket/emws_peer.cpp
+++ b/modules/websocket/emws_peer.cpp
@@ -139,6 +139,11 @@ uint16_t EMWSPeer::get_connected_port() const {
 	ERR_FAIL_V_MSG(0, "Not supported in HTML5 export.");
 };
 
+void EMWSPeer::set_no_delay(bool p_enabled) {
+
+	ERR_FAIL_MSG("'set_no_delay' is not supported in HTML5 export.");
+}
+
 EMWSPeer::EMWSPeer() {
 	peer_sock = -1;
 	write_mode = WRITE_MODE_BINARY;
diff --git a/modules/websocket/emws_peer.h b/modules/websocket/emws_peer.h
index 9fe7fb8edc3..43b42f9be65 100644
--- a/modules/websocket/emws_peer.h
+++ b/modules/websocket/emws_peer.h
@@ -68,6 +68,7 @@ public:
 	virtual WriteMode get_write_mode() const;
 	virtual void set_write_mode(WriteMode p_mode);
 	virtual bool was_string_packet() const;
+	virtual void set_no_delay(bool p_enabled);
 
 	EMWSPeer();
 	~EMWSPeer();
diff --git a/modules/websocket/websocket_peer.cpp b/modules/websocket/websocket_peer.cpp
index 474b11f0121..30a59723304 100644
--- a/modules/websocket/websocket_peer.cpp
+++ b/modules/websocket/websocket_peer.cpp
@@ -46,6 +46,7 @@ void WebSocketPeer::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("close", "code", "reason"), &WebSocketPeer::close, DEFVAL(1000), DEFVAL(""));
 	ClassDB::bind_method(D_METHOD("get_connected_host"), &WebSocketPeer::get_connected_host);
 	ClassDB::bind_method(D_METHOD("get_connected_port"), &WebSocketPeer::get_connected_port);
+	ClassDB::bind_method(D_METHOD("set_no_delay", "enabled"), &WebSocketPeer::set_no_delay);
 
 	BIND_ENUM_CONSTANT(WRITE_MODE_TEXT);
 	BIND_ENUM_CONSTANT(WRITE_MODE_BINARY);
diff --git a/modules/websocket/websocket_peer.h b/modules/websocket/websocket_peer.h
index c4e1984aa06..d4173600ec8 100644
--- a/modules/websocket/websocket_peer.h
+++ b/modules/websocket/websocket_peer.h
@@ -64,6 +64,7 @@ public:
 	virtual IP_Address get_connected_host() const = 0;
 	virtual uint16_t get_connected_port() const = 0;
 	virtual bool was_string_packet() const = 0;
+	virtual void set_no_delay(bool p_enabled) = 0;
 
 	WebSocketPeer();
 	~WebSocketPeer();
diff --git a/modules/websocket/wsl_client.cpp b/modules/websocket/wsl_client.cpp
index 88a306c7f52..088f266f186 100644
--- a/modules/websocket/wsl_client.cpp
+++ b/modules/websocket/wsl_client.cpp
@@ -90,6 +90,7 @@ void WSLClient::_do_handshake() {
 				data->is_server = false;
 				data->id = 1;
 				_peer->make_context(data, _in_buf_size, _in_pkt_size, _out_buf_size, _out_pkt_size);
+				_peer->set_no_delay(true);
 				_on_connect(protocol);
 				break;
 			}
diff --git a/modules/websocket/wsl_peer.cpp b/modules/websocket/wsl_peer.cpp
index d7914295e91..08079145e45 100644
--- a/modules/websocket/wsl_peer.cpp
+++ b/modules/websocket/wsl_peer.cpp
@@ -318,6 +318,12 @@ uint16_t WSLPeer::get_connected_port() const {
 	return _data->tcp->get_connected_port();
 }
 
+void WSLPeer::set_no_delay(bool p_enabled) {
+
+	ERR_FAIL_COND(!is_connected_to_host() || _data->tcp.is_null());
+	_data->tcp->set_no_delay(p_enabled);
+}
+
 void WSLPeer::invalidate() {
 	if (_data)
 		_data->valid = false;
diff --git a/modules/websocket/wsl_peer.h b/modules/websocket/wsl_peer.h
index 2fbb7aeec3f..f1c45ee859a 100644
--- a/modules/websocket/wsl_peer.h
+++ b/modules/websocket/wsl_peer.h
@@ -109,6 +109,7 @@ public:
 	virtual WriteMode get_write_mode() const;
 	virtual void set_write_mode(WriteMode p_mode);
 	virtual bool was_string_packet() const;
+	virtual void set_no_delay(bool p_enabled);
 
 	void make_context(PeerData *p_data, unsigned int p_in_buf_size, unsigned int p_in_pkt_size, unsigned int p_out_buf_size, unsigned int p_out_pkt_size);
 	Error parse_message(const wslay_event_on_msg_recv_arg *arg);
diff --git a/modules/websocket/wsl_server.cpp b/modules/websocket/wsl_server.cpp
index 44bfb4441d7..4db650a0c16 100644
--- a/modules/websocket/wsl_server.cpp
+++ b/modules/websocket/wsl_server.cpp
@@ -206,6 +206,7 @@ void WSLServer::poll() {
 
 		Ref<WSLPeer> ws_peer = memnew(WSLPeer);
 		ws_peer->make_context(data, _in_buf_size, _in_pkt_size, _out_buf_size, _out_pkt_size);
+		ws_peer->set_no_delay(true);
 
 		_peer_map[id] = ws_peer;
 		remove_peers.push_back(ppeer);