summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--COPYRIGHT.txt5
-rw-r--r--core/io/stream_peer_tcp.cpp5
-rw-r--r--modules/websocket/SCsub23
-rw-r--r--modules/websocket/doc_classes/WebSocketClient.xml2
-rw-r--r--modules/websocket/doc_classes/WebSocketServer.xml2
-rw-r--r--modules/websocket/register_types.cpp4
-rw-r--r--modules/websocket/wsl_client.cpp356
-rw-r--r--modules/websocket/wsl_client.h85
-rw-r--r--modules/websocket/wsl_peer.cpp339
-rw-r--r--modules/websocket/wsl_peer.h120
-rw-r--r--modules/websocket/wsl_server.cpp272
-rw-r--r--modules/websocket/wsl_server.h100
-rw-r--r--thirdparty/README.md7
-rw-r--r--thirdparty/wslay/COPYING22
-rw-r--r--thirdparty/wslay/includes/config.h8
-rw-r--r--thirdparty/wslay/includes/wslay/wslay.h772
-rw-r--r--thirdparty/wslay/includes/wslay/wslayver.h31
-rw-r--r--thirdparty/wslay/wslay_event.c1027
-rw-r--r--thirdparty/wslay/wslay_event.h142
-rw-r--r--thirdparty/wslay/wslay_frame.c340
-rw-r--r--thirdparty/wslay/wslay_frame.h76
-rw-r--r--thirdparty/wslay/wslay_net.c36
-rw-r--r--thirdparty/wslay/wslay_net.h54
-rw-r--r--thirdparty/wslay/wslay_queue.c117
-rw-r--r--thirdparty/wslay/wslay_queue.h53
-rw-r--r--thirdparty/wslay/wslay_stack.c86
-rw-r--r--thirdparty/wslay/wslay_stack.h50
27 files changed, 4130 insertions, 4 deletions
diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
index 1759c935b8..27eaf2b43d 100644
--- a/COPYRIGHT.txt
+++ b/COPYRIGHT.txt
@@ -366,6 +366,11 @@ Copyright: 2011, Khaled Mamou
2003-2009, Erwin Coumans
License: BSD-3-clause
+Files: ./thirdparty/wslay/
+Comment: Wslay
+Copyright: 2011-2015, Tatsuhiro Tsujikawa
+License: Expat
+
Files: ./thirdparty/xatlas/
Comment: xatlas
Copyright: 2018, Jonathan Young
diff --git a/core/io/stream_peer_tcp.cpp b/core/io/stream_peer_tcp.cpp
index bcdae343b8..a8dd263484 100644
--- a/core/io/stream_peer_tcp.cpp
+++ b/core/io/stream_peer_tcp.cpp
@@ -217,6 +217,11 @@ Error StreamPeerTCP::read(uint8_t *p_buffer, int p_bytes, int &r_received, bool
to_read -= read;
total_read += read;
+
+ if (!p_block) {
+ r_received = total_read;
+ return OK;
+ }
}
}
diff --git a/modules/websocket/SCsub b/modules/websocket/SCsub
index be26b08a60..e8d094fd7f 100644
--- a/modules/websocket/SCsub
+++ b/modules/websocket/SCsub
@@ -5,7 +5,7 @@ Import('env_modules')
# Thirdparty source files
-env_lws = env_modules.Clone()
+env_ws = env_modules.Clone()
if env['builtin_libwebsockets'] and not env["platform"] == "javascript": # already builtin for javascript
thirdparty_dir = "#thirdparty/libwebsockets/"
@@ -90,4 +90,23 @@ if env['builtin_libwebsockets'] and not env["platform"] == "javascript": # alrea
env_thirdparty.disable_warnings()
env_thirdparty.add_source_files(env.modules_sources, thirdparty_sources)
-env_lws.add_source_files(env.modules_sources, "*.cpp")
+ wslay_dir = "#thirdparty/wslay/"
+ wslay_sources = [
+ "wslay_net.c",
+ "wslay_event.c",
+ "wslay_queue.c",
+ "wslay_stack.c",
+ "wslay_frame.c",
+ ]
+ wslay_sources = [wslay_dir + s for s in wslay_sources]
+ env_ws.Prepend(CPPPATH=[wslay_dir + "includes/"])
+ env_ws.Append(CPPFLAGS=["-DHAVE_CONFIG_H"])
+ if env["platform"] == "windows" or env["platform"] == "uwp":
+ env_ws.Append(CPPFLAGS=["-DHAVE_WINSOCK2_H"])
+ else:
+ env_ws.Append(CPPFLAGS=["-DHAVE_NETINET_IN_H"])
+ env_wslay = env_ws.Clone()
+ env_wslay.disable_warnings()
+ env_wslay.add_source_files(env.modules_sources, wslay_sources)
+
+env_ws.add_source_files(env.modules_sources, "*.cpp")
diff --git a/modules/websocket/doc_classes/WebSocketClient.xml b/modules/websocket/doc_classes/WebSocketClient.xml
index 0d425ad1dd..c3baf9de83 100644
--- a/modules/websocket/doc_classes/WebSocketClient.xml
+++ b/modules/websocket/doc_classes/WebSocketClient.xml
@@ -22,7 +22,7 @@
<argument index="2" name="gd_mp_api" type="bool" default="false">
</argument>
<description>
- Connects to the given URL requesting one of the given [code]protocols[/code] as sub-protocol.
+ Connects to the given URL requesting one of the given [code]protocols[/code] as sub-protocol. If the list empty (default), no sub-protocol will be requested.
If [code]true[/code] is passed as [code]gd_mp_api[/code], the client will behave like a network peer for the [MultiplayerAPI], connections to non-Godot servers will not work, and [signal data_received] will not be emitted.
If [code]false[/code] is passed instead (default), you must call [PacketPeer] functions ([code]put_packet[/code], [code]get_packet[/code], etc.) on the [WebSocketPeer] returned via [code]get_peer(1)[/code] and not on this object directly (e.g. [code]get_peer(1).put_packet(data)[/code]).
</description>
diff --git a/modules/websocket/doc_classes/WebSocketServer.xml b/modules/websocket/doc_classes/WebSocketServer.xml
index 1af5e403e6..63318e5874 100644
--- a/modules/websocket/doc_classes/WebSocketServer.xml
+++ b/modules/websocket/doc_classes/WebSocketServer.xml
@@ -69,7 +69,7 @@
</argument>
<description>
Starts listening on the given port.
- You can specify the desired subprotocols via the "protocols" array. If the list empty (default), "binary" will be used.
+ You can specify the desired subprotocols via the "protocols" array. If the list empty (default), no sub-protocol will be requested.
If [code]true[/code] is passed as [code]gd_mp_api[/code], the server will behave like a network peer for the [MultiplayerAPI], connections from non-Godot clients will not work, and [signal data_received] will not be emitted.
If [code]false[/code] is passed instead (default), you must call [PacketPeer] functions ([code]put_packet[/code], [code]get_packet[/code], etc.), on the [WebSocketPeer] returned via [code]get_peer(id)[/code] to communicate with the peer with given [code]id[/code] (e.g. [code]get_peer(id).get_available_packet_count[/code]).
</description>
diff --git a/modules/websocket/register_types.cpp b/modules/websocket/register_types.cpp
index 39bf3de982..3071c5bc9e 100644
--- a/modules/websocket/register_types.cpp
+++ b/modules/websocket/register_types.cpp
@@ -40,6 +40,8 @@
#include "lws_client.h"
#include "lws_peer.h"
#include "lws_server.h"
+#include "wsl_client.h"
+#include "wsl_server.h"
#endif
void register_websocket_types() {
@@ -67,6 +69,8 @@ void register_websocket_types() {
LWSPeer::make_default();
LWSClient::make_default();
LWSServer::make_default();
+ WSLClient::make_default();
+ WSLServer::make_default();
#endif
ClassDB::register_virtual_class<WebSocketMultiplayerPeer>();
diff --git a/modules/websocket/wsl_client.cpp b/modules/websocket/wsl_client.cpp
new file mode 100644
index 0000000000..d695395635
--- /dev/null
+++ b/modules/websocket/wsl_client.cpp
@@ -0,0 +1,356 @@
+/*************************************************************************/
+/* wsl_client.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef JAVASCRIPT_ENABLED
+
+#include "wsl_client.h"
+#include "core/io/ip.h"
+#include "core/project_settings.h"
+
+void WSLClient::_do_handshake() {
+ if (_requested < _request.size() - 1) {
+ int sent = 0;
+ Error err = _connection->put_partial_data(((const uint8_t *)_request.get_data() + _requested), _request.size() - _requested - 1, sent);
+ // Sending handshake failed
+ if (err != OK) {
+ disconnect_from_host();
+ _on_error();
+ return;
+ }
+ _requested += sent;
+
+ } else {
+ uint8_t byte = 0;
+ int read = 0;
+
+ while (true) {
+ Error err = _connection->get_partial_data(&byte, 1, read);
+ if (err == ERR_FILE_EOF) {
+ // We got a disconnect.
+ disconnect_from_host();
+ _on_error();
+ return;
+ } else if (err != OK) {
+ // Got some error.
+ disconnect_from_host();
+ _on_error();
+ return;
+ } else if (read != 1) {
+ // Busy, wait next poll.
+ break;
+ }
+ // TODO lots of allocs. Use a buffer.
+ _response += byte;
+ if (_response.size() > WSL_MAX_HEADER_SIZE) {
+ // Header is too big
+ disconnect_from_host();
+ _on_error();
+ ERR_EXPLAIN("Response headers too big");
+ ERR_FAIL();
+ }
+ if (_response.ends_with("\r\n\r\n")) {
+ String protocol;
+ // Response is over, verify headers and create peer.
+ if (!_verify_headers(protocol)) {
+ disconnect_from_host();
+ _on_error();
+ ERR_EXPLAIN("Invalid response headers");
+ ERR_FAIL();
+ }
+ // Create peer.
+ WSLPeer::PeerData *data = memnew(struct WSLPeer::PeerData);
+ data->obj = this;
+ data->conn = _connection;
+ data->is_server = false;
+ data->id = 1;
+ _peer->make_context(data, _in_buf_size, _in_pkt_size, _out_buf_size, _out_pkt_size);
+ _on_connect(protocol);
+ }
+ }
+ }
+}
+
+bool WSLClient::_verify_headers(String &r_protocol) {
+ Vector<String> psa = _response.trim_suffix("\r\n\r\n").split("\r\n");
+ int len = psa.size();
+ if (len < 4) {
+ ERR_EXPLAIN("Not enough response headers.");
+ ERR_FAIL_V(false);
+ }
+
+ Vector<String> req = psa[0].split(" ", false);
+ if (req.size() < 2) {
+ ERR_EXPLAIN("Invalid protocol or status code.");
+ ERR_FAIL_V(false);
+ }
+ // Wrong protocol
+ if (req[0] != "HTTP/1.1" || req[1] != "101") {
+ ERR_EXPLAIN("Invalid protocol or status code.");
+ ERR_FAIL_V(false);
+ }
+
+ Map<String, String> headers;
+ for (int i = 1; i < len; i++) {
+ Vector<String> header = psa[i].split(":", false, 1);
+ if (header.size() != 2) {
+ ERR_EXPLAIN("Invalid header -> " + psa[i]);
+ ERR_FAIL_V(false);
+ }
+ String name = header[0].to_lower();
+ String value = header[1].strip_edges();
+ if (headers.has(name))
+ headers[name] += "," + value;
+ else
+ headers[name] = value;
+ }
+
+#define _WLS_EXPLAIN(NAME, VALUE) \
+ ERR_EXPLAIN("Missing or invalid header '" + String(NAME) + "'. Expected value '" + VALUE + "'");
+#define _WLS_CHECK(NAME, VALUE) \
+ _WLS_EXPLAIN(NAME, VALUE); \
+ ERR_FAIL_COND_V(!headers.has(NAME) || headers[NAME].to_lower() != VALUE, false);
+#define _WLS_CHECK_NC(NAME, VALUE) \
+ _WLS_EXPLAIN(NAME, VALUE); \
+ ERR_FAIL_COND_V(!headers.has(NAME) || headers[NAME] != VALUE, false);
+ _WLS_CHECK("connection", "upgrade");
+ _WLS_CHECK("upgrade", "websocket");
+ _WLS_CHECK_NC("sec-websocket-accept", WSLPeer::compute_key_response(_key));
+ if (_protocols.size() == 0) {
+ // We didn't request a custom protocol
+ ERR_FAIL_COND_V(headers.has("sec-websocket-protocol"), false);
+ } else {
+ ERR_FAIL_COND_V(!headers.has("sec-websocket-protocol"), false);
+ r_protocol = headers["sec-websocket-protocol"];
+ bool valid = false;
+ for (int i = 0; i < _protocols.size(); i++) {
+ if (_protocols[i] != r_protocol)
+ continue;
+ valid = true;
+ break;
+ }
+ if (!valid)
+ return false;
+ }
+#undef _WLS_CHECK_NC
+#undef _WLS_CHECK
+#undef _WLS_EXPLAIN
+
+ return true;
+}
+
+Error WSLClient::connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocols) {
+
+ ERR_FAIL_COND_V(_connection.is_valid(), ERR_ALREADY_IN_USE);
+
+ _peer = Ref<WSLPeer>(memnew(WSLPeer));
+ IP_Address addr;
+
+ if (!p_host.is_valid_ip_address()) {
+ addr = IP::get_singleton()->resolve_hostname(p_host);
+ } else {
+ addr = p_host;
+ }
+
+ ERR_FAIL_COND_V(!addr.is_valid(), ERR_INVALID_PARAMETER);
+
+ String port = "";
+ if ((p_port != 80 && !p_ssl) || (p_port != 443 && p_ssl)) {
+ port = ":" + itos(p_port);
+ }
+
+ Error err = _tcp->connect_to_host(addr, p_port);
+ if (err != OK) {
+ _on_error();
+ _tcp->disconnect_from_host();
+ return err;
+ }
+ _connection = _tcp;
+ _use_ssl = p_ssl;
+ _host = p_host;
+ _protocols = p_protocols;
+
+ _key = WSLPeer::generate_key();
+ // TODO custom extra headers (allow overriding this too?)
+ String request = "GET " + p_path + " HTTP/1.1\r\n";
+ request += "Host: " + p_host + port + "\r\n";
+ request += "Upgrade: websocket\r\n";
+ request += "Connection: Upgrade\r\n";
+ request += "Sec-WebSocket-Key: " + _key + "\r\n";
+ request += "Sec-WebSocket-Version: 13\r\n";
+ if (p_protocols.size() > 0) {
+ request += "Sec-WebSocket-Protocol: ";
+ for (int i = 0; i < p_protocols.size(); i++) {
+ if (i != 0)
+ request += ",";
+ request += p_protocols[i];
+ }
+ request += "\r\n";
+ }
+ request += "\r\n";
+ _request = request.utf8();
+
+ return OK;
+}
+
+int WSLClient::get_max_packet_size() const {
+ return (1 << _out_buf_size) - PROTO_SIZE;
+}
+
+void WSLClient::poll() {
+ if (_peer->is_connected_to_host()) {
+ _peer->poll();
+ if (!_peer->is_connected_to_host()) {
+ _on_disconnect(_peer->close_code != -1);
+ disconnect_from_host();
+ }
+ return;
+ }
+
+ if (_connection.is_null())
+ return; // Not connected.
+
+ switch (_tcp->get_status()) {
+ case StreamPeerTCP::STATUS_NONE:
+ // Clean close
+ _on_error();
+ disconnect_from_host();
+ break;
+ case StreamPeerTCP::STATUS_CONNECTED: {
+ Ref<StreamPeerSSL> ssl;
+ if (_use_ssl) {
+ if (_connection == _tcp) {
+ // Start SSL handshake
+ ssl = Ref<StreamPeerSSL>(StreamPeerSSL::create());
+ ERR_EXPLAIN("SSL is not available in this build");
+ ERR_FAIL_COND(ssl.is_null());
+ ssl->set_blocking_handshake_enabled(false);
+ if (ssl->connect_to_stream(_tcp, verify_ssl, _host) != OK) {
+ _on_error();
+ disconnect_from_host();
+ return;
+ }
+ _connection = ssl;
+ } else {
+ ssl = static_cast<Ref<StreamPeerSSL> >(_connection);
+ ERR_FAIL_COND(ssl.is_null()); // Bug?
+ ssl->poll();
+ }
+ if (ssl->get_status() == StreamPeerSSL::STATUS_HANDSHAKING)
+ return; // Need more polling.
+ else if (ssl->get_status() != StreamPeerSSL::STATUS_CONNECTED) {
+ _on_error();
+ disconnect_from_host();
+ return; // Error.
+ }
+ }
+ // Do websocket handshake.
+ _do_handshake();
+ } break;
+ case StreamPeerTCP::STATUS_ERROR:
+ _on_error();
+ disconnect_from_host();
+ break;
+ case StreamPeerTCP::STATUS_CONNECTING:
+ break; // Wait for connection
+ }
+}
+
+Ref<WebSocketPeer> WSLClient::get_peer(int p_peer_id) const {
+
+ ERR_FAIL_COND_V(p_peer_id != 1, NULL);
+
+ return _peer;
+}
+
+NetworkedMultiplayerPeer::ConnectionStatus WSLClient::get_connection_status() const {
+
+ if (_peer->is_connected_to_host())
+ return CONNECTION_CONNECTED;
+
+ if (_tcp->is_connected_to_host())
+ return CONNECTION_CONNECTING;
+
+ return CONNECTION_DISCONNECTED;
+}
+
+void WSLClient::disconnect_from_host(int p_code, String p_reason) {
+
+ _peer->close(p_code, p_reason);
+ _connection = Ref<StreamPeer>(NULL);
+ _tcp = Ref<StreamPeerTCP>(memnew(StreamPeerTCP));
+ _request = "";
+ _response = "";
+ _key = "";
+ _host = "";
+ _use_ssl = false;
+ _requested = 0;
+}
+
+IP_Address WSLClient::get_connected_host() const {
+
+ return IP_Address();
+}
+
+uint16_t WSLClient::get_connected_port() const {
+
+ return 1025;
+}
+
+Error WSLClient::set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) {
+ ERR_EXPLAIN("Buffers sizes can only be set before listening or connecting");
+ ERR_FAIL_COND_V(_ctx != NULL, FAILED);
+
+ _in_buf_size = nearest_shift(p_in_buffer - 1) + 10;
+ _in_pkt_size = nearest_shift(p_in_packets - 1);
+ _out_buf_size = nearest_shift(p_out_buffer - 1) + 10;
+ _out_pkt_size = nearest_shift(p_out_packets - 1);
+ return OK;
+}
+
+WSLClient::WSLClient() {
+ _in_buf_size = nearest_shift((int)GLOBAL_GET(WSC_IN_BUF) - 1) + 10;
+ _in_pkt_size = nearest_shift((int)GLOBAL_GET(WSC_IN_PKT) - 1);
+ _out_buf_size = nearest_shift((int)GLOBAL_GET(WSC_OUT_BUF) - 1) + 10;
+ _out_pkt_size = nearest_shift((int)GLOBAL_GET(WSC_OUT_PKT) - 1);
+
+ _ctx = NULL;
+ _peer.instance();
+ _tcp.instance();
+ _requested = 0;
+}
+
+WSLClient::~WSLClient() {
+
+ _peer->close_now();
+ _peer->invalidate();
+ disconnect_from_host();
+}
+
+#endif // JAVASCRIPT_ENABLED
diff --git a/modules/websocket/wsl_client.h b/modules/websocket/wsl_client.h
new file mode 100644
index 0000000000..1ead88f60b
--- /dev/null
+++ b/modules/websocket/wsl_client.h
@@ -0,0 +1,85 @@
+/*************************************************************************/
+/* wsl_client.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef WSLCLIENT_H
+#define WSLCLIENT_H
+
+#ifndef JAVASCRIPT_ENABLED
+
+#include "core/error_list.h"
+#include "core/io/stream_peer_ssl.h"
+#include "core/io/stream_peer_tcp.h"
+#include "websocket_client.h"
+#include "wsl_peer.h"
+#include "wslay/wslay.h"
+
+class WSLClient : public WebSocketClient {
+
+ GDCIIMPL(WSLClient, WebSocketClient);
+
+private:
+ int _in_buf_size;
+ int _in_pkt_size;
+ int _out_buf_size;
+ int _out_pkt_size;
+ wslay_event_context_ptr _ctx;
+ Ref<WSLPeer> _peer;
+ // XXX we could use HTTPClient with some hacking instead...
+ Ref<StreamPeerTCP> _tcp;
+ CharString _request;
+ String _response;
+ String _key;
+ String _host;
+ PoolVector<String> _protocols;
+ Ref<StreamPeer> _connection;
+ int _requested;
+ bool _use_ssl;
+
+ void _do_handshake();
+ bool _verify_headers(String &r_protocol);
+
+public:
+ Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets);
+ Error connect_to_host(String p_host, String p_path, uint16_t p_port, bool p_ssl, PoolVector<String> p_protocol = PoolVector<String>());
+ int get_max_packet_size() const;
+ Ref<WebSocketPeer> get_peer(int p_peer_id) const;
+ void disconnect_from_host(int p_code = 1000, String p_reason = "");
+ IP_Address get_connected_host() const;
+ uint16_t get_connected_port() const;
+ virtual ConnectionStatus get_connection_status() const;
+ virtual void poll();
+
+ WSLClient();
+ ~WSLClient();
+};
+
+#endif // JAVASCRIPT_ENABLED
+
+#endif // WSLCLIENT_H
diff --git a/modules/websocket/wsl_peer.cpp b/modules/websocket/wsl_peer.cpp
new file mode 100644
index 0000000000..b11bd2b70f
--- /dev/null
+++ b/modules/websocket/wsl_peer.cpp
@@ -0,0 +1,339 @@
+/*************************************************************************/
+/* lws_peer.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef JAVASCRIPT_ENABLED
+
+#include "wsl_peer.h"
+
+#include "wsl_client.h"
+#include "wsl_server.h"
+
+#include "core/math/crypto_core.h"
+#include "core/math/random_number_generator.h"
+#include "core/os/os.h"
+
+String WSLPeer::generate_key() {
+ // Random key
+ RandomNumberGenerator rng;
+ rng.set_seed(OS::get_singleton()->get_unix_time());
+ PoolVector<uint8_t> bkey;
+ int len = 16; // 16 bytes, as per RFC
+ bkey.resize(len);
+ PoolVector<uint8_t>::Write w = bkey.write();
+ for (int i = 0; i < len; i++) {
+ w[i] = (uint8_t)rng.randi_range(0, 255);
+ }
+ return CryptoCore::b64_encode_str(&w[0], len);
+}
+
+String WSLPeer::compute_key_response(String p_key) {
+ String key = p_key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; // Magic UUID as per RFC
+ Vector<uint8_t> sha = key.sha1_buffer();
+ return CryptoCore::b64_encode_str(sha.ptr(), sha.size());
+}
+
+void WSLPeer::_wsl_destroy(struct PeerData **p_data) {
+ if (!p_data || !(*p_data))
+ return;
+ struct PeerData *data = *p_data;
+ if (data->polling) {
+ data->destroy = true;
+ return;
+ }
+ wslay_event_context_free(data->ctx);
+ memdelete(data);
+ *p_data = NULL;
+}
+
+bool WSLPeer::_wsl_poll(struct PeerData *p_data) {
+ p_data->polling = true;
+ int err = 0;
+ if ((err = wslay_event_recv(p_data->ctx)) != 0 || (err = wslay_event_send(p_data->ctx)) != 0) {
+ print_verbose("Websocket (wslay) poll error: " + itos(err));
+ p_data->destroy = true;
+ }
+ p_data->polling = false;
+
+ if (p_data->destroy || (wslay_event_get_close_sent(p_data->ctx) && wslay_event_get_close_received(p_data->ctx))) {
+ bool valid = p_data->valid;
+ _wsl_destroy(&p_data);
+ return valid;
+ }
+ return false;
+}
+
+ssize_t wsl_recv_callback(wslay_event_context_ptr ctx, uint8_t *data, size_t len, int flags, void *user_data) {
+ struct WSLPeer::PeerData *peer_data = (struct WSLPeer::PeerData *)user_data;
+ if (!peer_data->valid) {
+ wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE);
+ return -1;
+ }
+ Ref<StreamPeer> conn = peer_data->conn;
+ int read = 0;
+ Error err = conn->get_partial_data(data, len, read);
+ if (err != OK) {
+ print_verbose("Websocket get data error: " + itos(err) + ", read (should be 0!): " + itos(read));
+ wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE);
+ return -1;
+ }
+ if (read == 0) {
+ wslay_event_set_error(ctx, WSLAY_ERR_WOULDBLOCK);
+ return -1;
+ }
+ return read;
+}
+
+ssize_t wsl_send_callback(wslay_event_context_ptr ctx, const uint8_t *data, size_t len, int flags, void *user_data) {
+ struct WSLPeer::PeerData *peer_data = (struct WSLPeer::PeerData *)user_data;
+ if (!peer_data->valid) {
+ wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE);
+ return -1;
+ }
+ Ref<StreamPeer> conn = peer_data->conn;
+ int sent = 0;
+ Error err = conn->put_partial_data(data, len, sent);
+ if (err != OK) {
+ wslay_event_set_error(ctx, WSLAY_ERR_CALLBACK_FAILURE);
+ return -1;
+ }
+ if (sent == 0) {
+ wslay_event_set_error(ctx, WSLAY_ERR_WOULDBLOCK);
+ return -1;
+ }
+ return sent;
+}
+
+int wsl_genmask_callback(wslay_event_context_ptr ctx, uint8_t *buf, size_t len, void *user_data) {
+ RandomNumberGenerator rng;
+ // TODO maybe use crypto in the future?
+ rng.set_seed(OS::get_singleton()->get_unix_time());
+ for (unsigned int i = 0; i < len; i++) {
+ buf[i] = (uint8_t)rng.randi_range(0, 255);
+ }
+ return 0;
+}
+
+void wsl_msg_recv_callback(wslay_event_context_ptr ctx, const struct wslay_event_on_msg_recv_arg *arg, void *user_data) {
+ struct WSLPeer::PeerData *peer_data = (struct WSLPeer::PeerData *)user_data;
+ if (!peer_data->valid) {
+ return;
+ }
+ WSLPeer *peer = (WSLPeer *)peer_data->peer;
+
+ if (peer->parse_message(arg) != OK)
+ return;
+
+ if (peer_data->is_server) {
+ WSLServer *helper = (WSLServer *)peer_data->obj;
+ helper->_on_peer_packet(peer_data->id);
+ } else {
+ WSLClient *helper = (WSLClient *)peer_data->obj;
+ helper->_on_peer_packet();
+ }
+}
+
+wslay_event_callbacks wsl_callbacks = {
+ wsl_recv_callback,
+ wsl_send_callback,
+ wsl_genmask_callback,
+ NULL, /* on_frame_recv_start_callback */
+ NULL, /* on_frame_recv_callback */
+ NULL, /* on_frame_recv_end_callback */
+ wsl_msg_recv_callback
+};
+
+Error WSLPeer::parse_message(const wslay_event_on_msg_recv_arg *arg) {
+ uint8_t is_string = 0;
+ if (arg->opcode == WSLAY_TEXT_FRAME) {
+ is_string = 1;
+ } else if (arg->opcode == WSLAY_CONNECTION_CLOSE) {
+ close_code = arg->status_code;
+ size_t len = arg->msg_length;
+ close_reason = "";
+ if (len > 2 /* first 2 bytes = close code */) {
+ close_reason.parse_utf8((char *)arg->msg + 2, len - 2);
+ }
+ if (!wslay_event_get_close_sent(_data->ctx)) {
+ if (_data->is_server) {
+ WSLServer *helper = (WSLServer *)_data->obj;
+ helper->_on_close_request(_data->id, close_code, close_reason);
+ } else {
+ WSLClient *helper = (WSLClient *)_data->obj;
+ helper->_on_close_request(close_code, close_reason);
+ }
+ }
+ return ERR_FILE_EOF;
+ } else if (arg->opcode != WSLAY_BINARY_FRAME) {
+ // Ping or pong
+ return ERR_SKIP;
+ }
+ _in_buffer.write_packet(arg->msg, arg->msg_length, &is_string);
+ return OK;
+}
+
+void WSLPeer::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) {
+ ERR_FAIL_COND(_data != NULL);
+ ERR_FAIL_COND(p_data == NULL);
+
+ _in_buffer.resize(p_in_pkt_size, p_in_buf_size);
+ _packet_buffer.resize((1 << MAX(p_in_buf_size, p_out_buf_size)));
+
+ _data = p_data;
+ _data->peer = this;
+ _data->valid = true;
+ _connection = Ref<StreamPeer>(_data->conn);
+
+ if (_data->is_server)
+ wslay_event_context_server_init(&(_data->ctx), &wsl_callbacks, _data);
+ else
+ wslay_event_context_client_init(&(_data->ctx), &wsl_callbacks, _data);
+ wslay_event_config_set_max_recv_msg_length(_data->ctx, (1 << p_in_buf_size));
+}
+
+void WSLPeer::set_write_mode(WriteMode p_mode) {
+ write_mode = p_mode;
+}
+
+WSLPeer::WriteMode WSLPeer::get_write_mode() const {
+ return write_mode;
+}
+
+void WSLPeer::poll() {
+ if (!_data)
+ return;
+
+ if (_wsl_poll(_data)) {
+ _data = NULL;
+ }
+}
+
+Error WSLPeer::put_packet(const uint8_t *p_buffer, int p_buffer_size) {
+
+ ERR_FAIL_COND_V(!is_connected_to_host(), FAILED);
+
+ struct wslay_event_msg msg; // Should I use fragmented?
+ msg.opcode = write_mode == WRITE_MODE_TEXT ? WSLAY_TEXT_FRAME : WSLAY_BINARY_FRAME;
+ msg.msg = p_buffer;
+ msg.msg_length = p_buffer_size;
+
+ wslay_event_queue_msg(_data->ctx, &msg);
+ return OK;
+}
+
+Error WSLPeer::get_packet(const uint8_t **r_buffer, int &r_buffer_size) {
+
+ r_buffer_size = 0;
+
+ ERR_FAIL_COND_V(!is_connected_to_host(), FAILED);
+
+ if (_in_buffer.packets_left() == 0)
+ return ERR_UNAVAILABLE;
+
+ int read = 0;
+ PoolVector<uint8_t>::Write rw = _packet_buffer.write();
+ _in_buffer.read_packet(rw.ptr(), _packet_buffer.size(), &_is_string, read);
+
+ *r_buffer = rw.ptr();
+ r_buffer_size = read;
+
+ return OK;
+}
+
+int WSLPeer::get_available_packet_count() const {
+
+ if (!is_connected_to_host())
+ return 0;
+
+ return _in_buffer.packets_left();
+}
+
+bool WSLPeer::was_string_packet() const {
+
+ return _is_string;
+}
+
+bool WSLPeer::is_connected_to_host() const {
+
+ return _data != NULL;
+}
+
+void WSLPeer::close_now() {
+ close(1000, "");
+ _wsl_destroy(&_data);
+}
+
+void WSLPeer::close(int p_code, String p_reason) {
+ if (_data && !wslay_event_get_close_sent(_data->ctx)) {
+ CharString cs = p_reason.utf8();
+ wslay_event_queue_close(_data->ctx, p_code, (uint8_t *)cs.ptr(), cs.size());
+ wslay_event_send(_data->ctx);
+ }
+
+ _in_buffer.clear();
+ _packet_buffer.resize(0);
+}
+
+IP_Address WSLPeer::get_connected_host() const {
+
+ ERR_FAIL_COND_V(!is_connected_to_host(), IP_Address());
+
+ IP_Address ip;
+ return ip;
+}
+
+uint16_t WSLPeer::get_connected_port() const {
+
+ ERR_FAIL_COND_V(!is_connected_to_host(), 0);
+
+ uint16_t port = 0;
+ return port;
+}
+
+void WSLPeer::invalidate() {
+ if (_data)
+ _data->valid = false;
+}
+
+WSLPeer::WSLPeer() {
+ _data = NULL;
+ _is_string = 0;
+ close_code = -1;
+ write_mode = WRITE_MODE_BINARY;
+}
+
+WSLPeer::~WSLPeer() {
+
+ close();
+ invalidate();
+ _wsl_destroy(&_data);
+ _data = NULL;
+}
+
+#endif // JAVASCRIPT_ENABLED
diff --git a/modules/websocket/wsl_peer.h b/modules/websocket/wsl_peer.h
new file mode 100644
index 0000000000..d51b304fe1
--- /dev/null
+++ b/modules/websocket/wsl_peer.h
@@ -0,0 +1,120 @@
+/*************************************************************************/
+/* wsl_peer.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef WSLPEER_H
+#define WSLPEER_H
+
+#ifndef JAVASCRIPT_ENABLED
+
+#include "core/error_list.h"
+#include "core/io/packet_peer.h"
+#include "core/ring_buffer.h"
+#include "packet_buffer.h"
+#include "websocket_peer.h"
+#include "wslay/wslay.h"
+
+#define WSL_MAX_HEADER_SIZE 4096
+
+class WSLPeer : public WebSocketPeer {
+
+ GDCIIMPL(WSLPeer, WebSocketPeer);
+
+public:
+ struct PeerData {
+ bool polling;
+ bool destroy;
+ bool valid;
+ bool is_server;
+ void *obj;
+ void *peer;
+ Ref<StreamPeer> conn;
+ int id;
+ wslay_event_context_ptr ctx;
+
+ PeerData() {
+ polling = false;
+ destroy = false;
+ valid = false;
+ is_server = false;
+ id = 1;
+ ctx = NULL;
+ obj = NULL;
+ peer = NULL;
+ }
+ };
+
+ static String compute_key_response(String p_key);
+ static String generate_key();
+
+private:
+ static bool _wsl_poll(struct PeerData *p_data);
+ static void _wsl_destroy(struct PeerData **p_data);
+
+ Ref<StreamPeer> _connection;
+ struct PeerData *_data;
+ uint8_t _is_string;
+ // Our packet info is just a boolean (is_string), using uint8_t for it.
+ PacketBuffer<uint8_t> _in_buffer;
+
+ PoolVector<uint8_t> _packet_buffer;
+
+ WriteMode write_mode;
+
+public:
+ int close_code;
+ String close_reason;
+ void poll(); // Used by client and server.
+
+ virtual int get_available_packet_count() const;
+ virtual Error get_packet(const uint8_t **r_buffer, int &r_buffer_size);
+ virtual Error put_packet(const uint8_t *p_buffer, int p_buffer_size);
+ virtual int get_max_packet_size() const { return _packet_buffer.size(); };
+
+ virtual void close_now();
+ virtual void close(int p_code = 1000, String p_reason = "");
+ virtual bool is_connected_to_host() const;
+ virtual IP_Address get_connected_host() const;
+ virtual uint16_t get_connected_port() const;
+
+ virtual WriteMode get_write_mode() const;
+ virtual void set_write_mode(WriteMode p_mode);
+ virtual bool was_string_packet() const;
+
+ 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);
+ void invalidate();
+
+ WSLPeer();
+ ~WSLPeer();
+};
+
+#endif // JAVASCRIPT_ENABLED
+
+#endif // LSWPEER_H
diff --git a/modules/websocket/wsl_server.cpp b/modules/websocket/wsl_server.cpp
new file mode 100644
index 0000000000..66f6a7e86e
--- /dev/null
+++ b/modules/websocket/wsl_server.cpp
@@ -0,0 +1,272 @@
+/*************************************************************************/
+/* lws_server.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef JAVASCRIPT_ENABLED
+
+#include "wsl_server.h"
+#include "core/os/os.h"
+#include "core/project_settings.h"
+
+bool WSLServer::PendingPeer::_parse_request(String &r_key) {
+ Vector<String> psa = request.trim_suffix("\r\n\r\n").split("\r\n");
+ int len = psa.size();
+ if (len < 4) {
+ ERR_EXPLAIN("Not enough response headers.");
+ ERR_FAIL_V(false);
+ }
+
+ Vector<String> req = psa[0].split(" ", false);
+ if (req.size() < 2) {
+ ERR_EXPLAIN("Invalid protocol or status code.");
+ ERR_FAIL_V(false);
+ }
+ // Wrong protocol
+ if (req[0] != "GET" || req[2] != "HTTP/1.1") {
+ ERR_EXPLAIN("Invalid method or HTTP version.");
+ ERR_FAIL_V(false);
+ }
+
+ Map<String, String> headers;
+ for (int i = 1; i < len; i++) {
+ Vector<String> header = psa[i].split(":", false, 1);
+ if (header.size() != 2) {
+ ERR_EXPLAIN("Invalid header -> " + psa[i]);
+ ERR_FAIL_V(false);
+ }
+ String name = header[0].to_lower();
+ String value = header[1].strip_edges();
+ if (headers.has(name))
+ headers[name] += "," + value;
+ else
+ headers[name] = value;
+ }
+#define _WLS_CHECK(NAME, VALUE) \
+ ERR_EXPLAIN("Missing or invalid header '" + String(NAME) + "'. Expected value '" + VALUE + "'"); \
+ ERR_FAIL_COND_V(!headers.has(NAME) || headers[NAME].to_lower() != VALUE, false);
+#define _WLS_CHECK_EX(NAME) \
+ ERR_EXPLAIN("Missing header '" + String(NAME) + "'."); \
+ ERR_FAIL_COND_V(!headers.has(NAME), false);
+ _WLS_CHECK("upgrade", "websocket");
+ _WLS_CHECK("sec-websocket-version", "13");
+ _WLS_CHECK_EX("sec-websocket-key");
+ _WLS_CHECK_EX("connection");
+#undef _WLS_CHECK_EX
+#undef _WLS_CHECK
+ r_key = headers["sec-websocket-key"];
+ return true;
+}
+
+Error WSLServer::PendingPeer::do_handshake() {
+ if (OS::get_singleton()->get_ticks_msec() - time > WSL_SERVER_TIMEOUT)
+ return ERR_TIMEOUT;
+ if (!has_request) {
+ uint8_t byte = 0;
+ int read = 0;
+ while (true) {
+ Error err = connection->get_partial_data(&byte, 1, read);
+ if (err != OK) // Got an error
+ return FAILED;
+ else if (read != 1) // Busy, wait next poll
+ return ERR_BUSY;
+ request += byte;
+
+ if (request.size() > WSL_MAX_HEADER_SIZE) {
+ ERR_EXPLAIN("Response headers too big");
+ ERR_FAIL_V(ERR_OUT_OF_MEMORY);
+ }
+ if (request.ends_with("\r\n\r\n")) {
+ if (!_parse_request(key)) {
+ return FAILED;
+ }
+ String r = "HTTP/1.1 101 Switching Protocols\r\n";
+ r += "Upgrade: websocket\r\n";
+ r += "Connection: Upgrade\r\n";
+ r += "Sec-WebSocket-Accept: " + WSLPeer::compute_key_response(key) + "\r\n";
+ r += "\r\n";
+ response = r.utf8();
+ has_request = true;
+ WARN_PRINTS("Parsed, " + key);
+ break;
+ }
+ }
+ }
+ if (has_request && response_sent < response.size() - 1) {
+ int sent = 0;
+ Error err = connection->put_partial_data((const uint8_t *)response.get_data() + response_sent, response.size() - response_sent - 1, sent);
+ if (err != OK) {
+ return err;
+ }
+ response_sent += sent;
+ }
+ if (response_sent < response.size() - 1)
+ return ERR_BUSY;
+ return OK;
+}
+
+Error WSLServer::listen(int p_port, PoolVector<String> p_protocols, bool gd_mp_api) {
+ ERR_FAIL_COND_V(is_listening(), ERR_ALREADY_IN_USE);
+
+ _is_multiplayer = gd_mp_api;
+ _server->listen(p_port);
+
+ return OK;
+}
+
+void WSLServer::poll() {
+
+ List<int> remove_ids;
+ for (Map<int, Ref<WebSocketPeer> >::Element *E = _peer_map.front(); E; E = E->next()) {
+ Ref<WSLPeer> peer = (WSLPeer *)E->get().ptr();
+ peer->poll();
+ if (!peer->is_connected_to_host()) {
+ _on_disconnect(E->key(), peer->close_code != -1);
+ remove_ids.push_back(E->key());
+ }
+ }
+ for (List<int>::Element *E = remove_ids.front(); E; E = E->next()) {
+ _peer_map.erase(E->get());
+ }
+ remove_ids.clear();
+
+ List<Ref<PendingPeer> > remove_peers;
+ for (List<Ref<PendingPeer> >::Element *E = _pending.front(); E; E = E->next()) {
+ Ref<PendingPeer> ppeer = E->get();
+ Error err = ppeer->do_handshake();
+ if (err == ERR_BUSY) {
+ continue;
+ } else if (err != OK) {
+ remove_peers.push_back(ppeer);
+ continue;
+ }
+ // Creating new peer
+ int32_t id = _gen_unique_id();
+
+ WSLPeer::PeerData *data = memnew(struct WSLPeer::PeerData);
+ data->obj = this;
+ data->conn = ppeer->connection;
+ data->is_server = true;
+ data->id = id;
+
+ Ref<WSLPeer> ws_peer = memnew(WSLPeer);
+ ws_peer->make_context(data, _in_buf_size, _in_pkt_size, _out_buf_size, _out_pkt_size);
+
+ _peer_map[id] = ws_peer;
+ remove_peers.push_back(ppeer);
+ _on_connect(id, "");
+ }
+ for (List<Ref<PendingPeer> >::Element *E = remove_peers.front(); E; E = E->next()) {
+ _pending.erase(E->get());
+ }
+ remove_peers.clear();
+
+ if (!_server->is_listening())
+ return;
+
+ while (_server->is_connection_available()) {
+ Ref<StreamPeer> conn = _server->take_connection();
+ if (is_refusing_new_connections())
+ continue; // Conn will go out-of-scope and be closed.
+
+ Ref<PendingPeer> peer = memnew(PendingPeer);
+ peer->connection = conn;
+ peer->time = OS::get_singleton()->get_ticks_msec();
+ _pending.push_back(peer);
+ }
+}
+
+bool WSLServer::is_listening() const {
+ return _server->is_listening();
+}
+
+int WSLServer::get_max_packet_size() const {
+ return (1 << _out_buf_size) - PROTO_SIZE;
+}
+
+void WSLServer::stop() {
+ _server->stop();
+ for (Map<int, Ref<WebSocketPeer> >::Element *E = _peer_map.front(); E; E = E->next()) {
+ Ref<WSLPeer> peer = (WSLPeer *)E->get().ptr();
+ peer->close_now();
+ }
+ _pending.clear();
+ _peer_map.clear();
+}
+
+bool WSLServer::has_peer(int p_id) const {
+ return _peer_map.has(p_id);
+}
+
+Ref<WebSocketPeer> WSLServer::get_peer(int p_id) const {
+ ERR_FAIL_COND_V(!has_peer(p_id), NULL);
+ return _peer_map[p_id];
+}
+
+IP_Address WSLServer::get_peer_address(int p_peer_id) const {
+ ERR_FAIL_COND_V(!has_peer(p_peer_id), IP_Address());
+
+ return _peer_map[p_peer_id]->get_connected_host();
+}
+
+int WSLServer::get_peer_port(int p_peer_id) const {
+ ERR_FAIL_COND_V(!has_peer(p_peer_id), 0);
+
+ return _peer_map[p_peer_id]->get_connected_port();
+}
+
+void WSLServer::disconnect_peer(int p_peer_id, int p_code, String p_reason) {
+ ERR_FAIL_COND(!has_peer(p_peer_id));
+
+ get_peer(p_peer_id)->close(p_code, p_reason);
+}
+
+Error WSLServer::set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets) {
+ ERR_EXPLAIN("Buffers sizes can only be set before listening or connecting");
+ ERR_FAIL_COND_V(_server->is_listening(), FAILED);
+
+ _in_buf_size = nearest_shift(p_in_buffer - 1) + 10;
+ _in_pkt_size = nearest_shift(p_in_packets - 1);
+ _out_buf_size = nearest_shift(p_out_buffer - 1) + 10;
+ _out_pkt_size = nearest_shift(p_out_packets - 1);
+ return OK;
+}
+
+WSLServer::WSLServer() {
+ _in_buf_size = nearest_shift((int)GLOBAL_GET(WSS_IN_BUF) - 1) + 10;
+ _in_pkt_size = nearest_shift((int)GLOBAL_GET(WSS_IN_PKT) - 1);
+ _out_buf_size = nearest_shift((int)GLOBAL_GET(WSS_OUT_BUF) - 1) + 10;
+ _out_pkt_size = nearest_shift((int)GLOBAL_GET(WSS_OUT_PKT) - 1);
+ _server.instance();
+}
+
+WSLServer::~WSLServer() {
+ stop();
+}
+
+#endif // JAVASCRIPT_ENABLED
diff --git a/modules/websocket/wsl_server.h b/modules/websocket/wsl_server.h
new file mode 100644
index 0000000000..ca627c1fc3
--- /dev/null
+++ b/modules/websocket/wsl_server.h
@@ -0,0 +1,100 @@
+/*************************************************************************/
+/* wsl_server.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2019 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2019 Godot Engine contributors (cf. AUTHORS.md) */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef WSLSERVER_H
+#define WSLSERVER_H
+
+#ifndef JAVASCRIPT_ENABLED
+
+#include "websocket_server.h"
+#include "wsl_peer.h"
+
+#include "core/io/stream_peer_tcp.h"
+#include "core/io/tcp_server.h"
+
+#define WSL_SERVER_TIMEOUT 1000
+
+class WSLServer : public WebSocketServer {
+
+ GDCIIMPL(WSLServer, WebSocketServer);
+
+private:
+ class PendingPeer : public Reference {
+
+ private:
+ bool _parse_request(String &r_key);
+
+ public:
+ Ref<StreamPeer> connection;
+
+ int time;
+ String request;
+ String key;
+ bool has_request;
+ CharString response;
+ int response_sent;
+
+ PendingPeer() {
+ time = 0;
+ has_request = false;
+ response_sent = 0;
+ }
+
+ Error do_handshake();
+ };
+
+ int _in_buf_size;
+ int _in_pkt_size;
+ int _out_buf_size;
+ int _out_pkt_size;
+
+ List<Ref<PendingPeer> > _pending;
+ Ref<TCP_Server> _server;
+
+public:
+ Error set_buffers(int p_in_buffer, int p_in_packets, int p_out_buffer, int p_out_packets);
+ Error listen(int p_port, PoolVector<String> p_protocols = PoolVector<String>(), bool gd_mp_api = false);
+ void stop();
+ bool is_listening() const;
+ int get_max_packet_size() const;
+ bool has_peer(int p_id) const;
+ Ref<WebSocketPeer> get_peer(int p_id) const;
+ IP_Address get_peer_address(int p_peer_id) const;
+ int get_peer_port(int p_peer_id) const;
+ void disconnect_peer(int p_peer_id, int p_code = 1000, String p_reason = "");
+ virtual void poll();
+
+ WSLServer();
+ ~WSLServer();
+};
+
+#endif // JAVASCRIPT_ENABLED
+
+#endif // WSLSERVER_H
diff --git a/thirdparty/README.md b/thirdparty/README.md
index 9738716221..c307840dba 100644
--- a/thirdparty/README.md
+++ b/thirdparty/README.md
@@ -262,6 +262,13 @@ changes to ensure they build for Javascript/HTML5. Those
changes are marked with `// -- GODOT --` comments.
+## wslay
+
+- Upstream: https://github.com/tatsuhiro-t/wslay
+- Version: 1.1.0
+- License: MIT
+
+
## libwebsockets
- Upstream: https://github.com/warmcat/libwebsockets
diff --git a/thirdparty/wslay/COPYING b/thirdparty/wslay/COPYING
new file mode 100644
index 0000000000..6080330303
--- /dev/null
+++ b/thirdparty/wslay/COPYING
@@ -0,0 +1,22 @@
+The MIT License
+
+Copyright (c) 2011, 2012, 2015 Tatsuhiro Tsujikawa
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/thirdparty/wslay/includes/config.h b/thirdparty/wslay/includes/config.h
new file mode 100644
index 0000000000..771ad12528
--- /dev/null
+++ b/thirdparty/wslay/includes/config.h
@@ -0,0 +1,8 @@
+#ifndef CONFIG_H
+#define CONFIG_H
+
+#ifdef BIG_ENDIAN_ENABLED
+#define WORDS_BIGENDIAN
+#endif
+
+#endif /* CONFIG_H */
diff --git a/thirdparty/wslay/includes/wslay/wslay.h b/thirdparty/wslay/includes/wslay/wslay.h
new file mode 100644
index 0000000000..2fde81a4e9
--- /dev/null
+++ b/thirdparty/wslay/includes/wslay/wslay.h
@@ -0,0 +1,772 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef WSLAY_H
+#define WSLAY_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <sys/types.h>
+
+
+/*
+ * wslay/wslayver.h is generated from wslay/wslayver.h.in by
+ * configure. The projects which do not use autotools can set
+ * WSLAY_VERSION macro from outside to avoid to generating wslayver.h
+ */
+#ifndef WSLAY_VERSION
+# include <wslay/wslayver.h>
+#endif /* WSLAY_VERSION */
+
+enum wslay_error {
+ WSLAY_ERR_WANT_READ = -100,
+ WSLAY_ERR_WANT_WRITE = -101,
+ WSLAY_ERR_PROTO = -200,
+ WSLAY_ERR_INVALID_ARGUMENT = -300,
+ WSLAY_ERR_INVALID_CALLBACK = -301,
+ WSLAY_ERR_NO_MORE_MSG = -302,
+ WSLAY_ERR_CALLBACK_FAILURE = -400,
+ WSLAY_ERR_WOULDBLOCK = -401,
+ WSLAY_ERR_NOMEM = -500
+};
+
+/*
+ * Status codes defined in RFC6455
+ */
+enum wslay_status_code {
+ WSLAY_CODE_NORMAL_CLOSURE = 1000,
+ WSLAY_CODE_GOING_AWAY = 1001,
+ WSLAY_CODE_PROTOCOL_ERROR = 1002,
+ WSLAY_CODE_UNSUPPORTED_DATA = 1003,
+ WSLAY_CODE_NO_STATUS_RCVD = 1005,
+ WSLAY_CODE_ABNORMAL_CLOSURE = 1006,
+ WSLAY_CODE_INVALID_FRAME_PAYLOAD_DATA = 1007,
+ WSLAY_CODE_POLICY_VIOLATION = 1008,
+ WSLAY_CODE_MESSAGE_TOO_BIG = 1009,
+ WSLAY_CODE_MANDATORY_EXT = 1010,
+ WSLAY_CODE_INTERNAL_SERVER_ERROR = 1011,
+ WSLAY_CODE_TLS_HANDSHAKE = 1015
+};
+
+enum wslay_io_flags {
+ /*
+ * There is more data to send.
+ */
+ WSLAY_MSG_MORE = 1
+};
+
+/*
+ * Callback function used by wslay_frame_send() function when it needs
+ * to send data. The implementation of this function must send at most
+ * len bytes of data in data. flags is the bitwise OR of zero or more
+ * of the following flag:
+ *
+ * WSLAY_MSG_MORE
+ * There is more data to send
+ *
+ * It provides some hints to tune performance and behaviour. user_data
+ * is one given in wslay_frame_context_init() function. The
+ * implementation of this function must return the number of bytes
+ * sent. If there is an error, return -1. The return value 0 is also
+ * treated an error by the library.
+ */
+typedef ssize_t (*wslay_frame_send_callback)(const uint8_t *data, size_t len,
+ int flags, void *user_data);
+/*
+ * Callback function used by wslay_frame_recv() function when it needs
+ * more data. The implementation of this function must fill at most
+ * len bytes of data into buf. The memory area of buf is allocated by
+ * library and not be freed by the application code. flags is always 0
+ * in this version. user_data is one given in
+ * wslay_frame_context_init() function. The implementation of this
+ * function must return the number of bytes filled. If there is an
+ * error, return -1. The return value 0 is also treated an error by
+ * the library.
+ */
+typedef ssize_t (*wslay_frame_recv_callback)(uint8_t *buf, size_t len,
+ int flags, void *user_data);
+/*
+ * Callback function used by wslay_frame_send() function when it needs
+ * new mask key. The implementation of this function must write
+ * exactly len bytes of mask key to buf. user_data is one given in
+ * wslay_frame_context_init() function. The implementation of this
+ * function return 0 on success. If there is an error, return -1.
+ */
+typedef int (*wslay_frame_genmask_callback)(uint8_t *buf, size_t len,
+ void *user_data);
+
+struct wslay_frame_callbacks {
+ wslay_frame_send_callback send_callback;
+ wslay_frame_recv_callback recv_callback;
+ wslay_frame_genmask_callback genmask_callback;
+};
+
+/*
+ * The opcode defined in RFC6455.
+ */
+enum wslay_opcode {
+ WSLAY_CONTINUATION_FRAME = 0x0u,
+ WSLAY_TEXT_FRAME = 0x1u,
+ WSLAY_BINARY_FRAME = 0x2u,
+ WSLAY_CONNECTION_CLOSE = 0x8u,
+ WSLAY_PING = 0x9u,
+ WSLAY_PONG = 0xau
+};
+
+/*
+ * Macro that returns 1 if opcode is control frame opcode, otherwise
+ * returns 0.
+ */
+#define wslay_is_ctrl_frame(opcode) ((opcode >> 3) & 1)
+
+/*
+ * Macros that represent and return reserved bits: RSV1, RSV2, RSV3.
+ * These macros assume that rsv is constructed by ((RSV1 << 2) |
+ * (RSV2 << 1) | RSV3)
+ */
+#define WSLAY_RSV_NONE ((uint8_t) 0)
+#define WSLAY_RSV1_BIT (((uint8_t) 1) << 2)
+#define WSLAY_RSV2_BIT (((uint8_t) 1) << 1)
+#define WSLAY_RSV3_BIT (((uint8_t) 1) << 0)
+
+#define wslay_get_rsv1(rsv) ((rsv >> 2) & 1)
+#define wslay_get_rsv2(rsv) ((rsv >> 1) & 1)
+#define wslay_get_rsv3(rsv) (rsv & 1)
+
+struct wslay_frame_iocb {
+ /* 1 for fragmented final frame, 0 for otherwise */
+ uint8_t fin;
+ /*
+ * reserved 3 bits. rsv = ((RSV1 << 2) | (RSV << 1) | RSV3).
+ * RFC6455 requires 0 unless extensions are negotiated.
+ */
+ uint8_t rsv;
+ /* 4 bit opcode */
+ uint8_t opcode;
+ /* payload length [0, 2**63-1] */
+ uint64_t payload_length;
+ /* 1 for masked frame, 0 for unmasked */
+ uint8_t mask;
+ /* part of payload data */
+ const uint8_t *data;
+ /* bytes of data defined above */
+ size_t data_length;
+};
+
+struct wslay_frame_context;
+typedef struct wslay_frame_context *wslay_frame_context_ptr;
+
+/*
+ * Initializes ctx using given callbacks and user_data. This function
+ * allocates memory for struct wslay_frame_context and stores the
+ * result to *ctx. The callback functions specified in callbacks are
+ * copied to ctx. user_data is stored in ctx and it will be passed to
+ * callback functions. When the user code finished using ctx, it must
+ * call wslay_frame_context_free to deallocate memory.
+ */
+int wslay_frame_context_init(wslay_frame_context_ptr *ctx,
+ const struct wslay_frame_callbacks *callbacks,
+ void *user_data);
+
+/*
+ * Deallocates memory pointed by ctx.
+ */
+void wslay_frame_context_free(wslay_frame_context_ptr ctx);
+
+/*
+ * Send WebSocket frame specified in iocb. ctx must be initialized
+ * using wslay_frame_context_init() function. iocb->fin must be 1 if
+ * this is a fin frame, otherwise 0. iocb->rsv is reserved bits.
+ * iocb->opcode must be the opcode of this frame. iocb->mask must be
+ * 1 if this is masked frame, otherwise 0. iocb->payload_length is
+ * the payload_length of this frame. iocb->data must point to the
+ * payload data to be sent. iocb->data_length must be the length of
+ * the data. This function calls send_callback function if it needs
+ * to send bytes. This function calls gen_mask_callback function if
+ * it needs new mask key. This function returns the number of payload
+ * bytes sent. Please note that it does not include any number of
+ * header bytes. If it cannot send any single bytes of payload, it
+ * returns WSLAY_ERR_WANT_WRITE. If the library detects error in iocb,
+ * this function returns WSLAY_ERR_INVALID_ARGUMENT. If callback
+ * functions report a failure, this function returns
+ * WSLAY_ERR_INVALID_CALLBACK. This function does not always send all
+ * given data in iocb. If there are remaining data to be sent, adjust
+ * data and data_length in iocb accordingly and call this function
+ * again.
+ */
+ssize_t wslay_frame_send(wslay_frame_context_ptr ctx,
+ struct wslay_frame_iocb *iocb);
+
+/*
+ * Receives WebSocket frame and stores it in iocb. This function
+ * returns the number of payload bytes received. This does not
+ * include header bytes. In this case, iocb will be populated as
+ * follows: iocb->fin is 1 if received frame is fin frame, otherwise
+ * 0. iocb->rsv is reserved bits of received frame. iocb->opcode is
+ * opcode of received frame. iocb->mask is 1 if received frame is
+ * masked, otherwise 0. iocb->payload_length is the payload length of
+ * received frame. iocb->data is pointed to the buffer containing
+ * received payload data. This buffer is allocated by the library and
+ * must be read-only. iocb->data_length is the number of payload
+ * bytes recieved. This function calls recv_callback if it needs to
+ * receive additional bytes. If it cannot receive any single bytes of
+ * payload, it returns WSLAY_ERR_WANT_READ. If the library detects
+ * protocol violation in a received frame, this function returns
+ * WSLAY_ERR_PROTO. If callback functions report a failure, this
+ * function returns WSLAY_ERR_INVALID_CALLBACK. This function does
+ * not always receive whole frame in a single call. If there are
+ * remaining data to be received, call this function again. This
+ * function ensures frame alignment.
+ */
+ssize_t wslay_frame_recv(wslay_frame_context_ptr ctx,
+ struct wslay_frame_iocb *iocb);
+
+struct wslay_event_context;
+/* Pointer to the event-based API context */
+typedef struct wslay_event_context *wslay_event_context_ptr;
+
+struct wslay_event_on_msg_recv_arg {
+ /* reserved bits: rsv = (RSV1 << 2) | (RSV2 << 1) | RSV3 */
+ uint8_t rsv;
+ /* opcode */
+ uint8_t opcode;
+ /* received message */
+ const uint8_t *msg;
+ /* message length */
+ size_t msg_length;
+ /*
+ * Status code iff opcode == WSLAY_CONNECTION_CLOSE. If no status
+ * code is included in the close control frame, it is set to 0.
+ */
+ uint16_t status_code;
+};
+
+/*
+ * Callback function invoked by wslay_event_recv() when a message is
+ * completely received.
+ */
+typedef void (*wslay_event_on_msg_recv_callback)
+(wslay_event_context_ptr ctx,
+ const struct wslay_event_on_msg_recv_arg *arg, void *user_data);
+
+struct wslay_event_on_frame_recv_start_arg {
+ /* fin bit; 1 for final frame, or 0. */
+ uint8_t fin;
+ /* reserved bits: rsv = (RSV1 << 2) | (RSV2 << 1) | RSV3 */
+ uint8_t rsv;
+ /* opcode of the frame */
+ uint8_t opcode;
+ /* payload length of ths frame */
+ uint64_t payload_length;
+};
+
+/*
+ * Callback function invoked by wslay_event_recv() when a new frame
+ * starts to be received. This callback function is only invoked once
+ * for each frame.
+ */
+typedef void (*wslay_event_on_frame_recv_start_callback)
+(wslay_event_context_ptr ctx,
+ const struct wslay_event_on_frame_recv_start_arg *arg, void *user_data);
+
+struct wslay_event_on_frame_recv_chunk_arg {
+ /* chunk of payload data */
+ const uint8_t *data;
+ /* length of data */
+ size_t data_length;
+};
+
+/*
+ * Callback function invoked by wslay_event_recv() when a chunk of
+ * frame payload is received.
+ */
+typedef void (*wslay_event_on_frame_recv_chunk_callback)
+(wslay_event_context_ptr ctx,
+ const struct wslay_event_on_frame_recv_chunk_arg *arg, void *user_data);
+
+/*
+ * Callback function invoked by wslay_event_recv() when a frame is
+ * completely received.
+ */
+typedef void (*wslay_event_on_frame_recv_end_callback)
+(wslay_event_context_ptr ctx, void *user_data);
+
+/*
+ * Callback function invoked by wslay_event_recv() when it wants to
+ * receive more data from peer. The implementation of this callback
+ * function must read data at most len bytes from peer and store them
+ * in buf and return the number of bytes read. flags is always 0 in
+ * this version.
+ *
+ * If there is an error, return -1 and set error code
+ * WSLAY_ERR_CALLBACK_FAILURE using wslay_event_set_error(). Wslay
+ * event-based API on the whole assumes non-blocking I/O. If the cause
+ * of error is EAGAIN or EWOULDBLOCK, set WSLAY_ERR_WOULDBLOCK
+ * instead. This is important because it tells wslay_event_recv() to
+ * stop receiving further data and return.
+ */
+typedef ssize_t (*wslay_event_recv_callback)(wslay_event_context_ptr ctx,
+ uint8_t *buf, size_t len,
+ int flags, void *user_data);
+
+/*
+ * Callback function invoked by wslay_event_send() when it wants to
+ * send more data to peer. The implementation of this callback
+ * function must send data at most len bytes to peer and return the
+ * number of bytes sent. flags is the bitwise OR of zero or more of
+ * the following flag:
+ *
+ * WSLAY_MSG_MORE
+ * There is more data to send
+ *
+ * It provides some hints to tune performance and behaviour.
+ *
+ * If there is an error, return -1 and set error code
+ * WSLAY_ERR_CALLBACK_FAILURE using wslay_event_set_error(). Wslay
+ * event-based API on the whole assumes non-blocking I/O. If the cause
+ * of error is EAGAIN or EWOULDBLOCK, set WSLAY_ERR_WOULDBLOCK
+ * instead. This is important because it tells wslay_event_send() to
+ * stop sending data and return.
+ */
+typedef ssize_t (*wslay_event_send_callback)(wslay_event_context_ptr ctx,
+ const uint8_t *data, size_t len,
+ int flags, void *user_data);
+
+/*
+ * Callback function invoked by wslay_event_send() when it wants new
+ * mask key. As described in RFC6455, only the traffic from WebSocket
+ * client is masked, so this callback function is only needed if an
+ * event-based API is initialized for WebSocket client use.
+ */
+typedef int (*wslay_event_genmask_callback)(wslay_event_context_ptr ctx,
+ uint8_t *buf, size_t len,
+ void *user_data);
+
+struct wslay_event_callbacks {
+ wslay_event_recv_callback recv_callback;
+ wslay_event_send_callback send_callback;
+ wslay_event_genmask_callback genmask_callback;
+ wslay_event_on_frame_recv_start_callback on_frame_recv_start_callback;
+ wslay_event_on_frame_recv_chunk_callback on_frame_recv_chunk_callback;
+ wslay_event_on_frame_recv_end_callback on_frame_recv_end_callback;
+ wslay_event_on_msg_recv_callback on_msg_recv_callback;
+};
+
+/*
+ * Initializes ctx as WebSocket Server. user_data is an arbitrary
+ * pointer, which is directly passed to each callback functions as
+ * user_data argument.
+ *
+ * On success, returns 0. On error, returns one of following negative
+ * values:
+ *
+ * WSLAY_ERR_NOMEM
+ * Out of memory.
+ */
+int wslay_event_context_server_init
+(wslay_event_context_ptr *ctx,
+ const struct wslay_event_callbacks *callbacks, void *user_data);
+
+/*
+ * Initializes ctx as WebSocket client. user_data is an arbitrary
+ * pointer, which is directly passed to each callback functions as
+ * user_data argument.
+ *
+ * On success, returns 0. On error, returns one of following negative
+ * values:
+ *
+ * WSLAY_ERR_NOMEM
+ * Out of memory.
+ */
+int wslay_event_context_client_init
+(wslay_event_context_ptr *ctx,
+ const struct wslay_event_callbacks *callbacks, void *user_data);
+
+/*
+ * Releases allocated resources for ctx.
+ */
+void wslay_event_context_free(wslay_event_context_ptr ctx);
+
+/*
+ * Sets a bit mask of allowed reserved bits.
+ * Currently only permitted values are WSLAY_RSV1_BIT to allow PMCE
+ * extension (see RFC-7692) or WSLAY_RSV_NONE to disable.
+ *
+ * Default: WSLAY_RSV_NONE
+ */
+void wslay_event_config_set_allowed_rsv_bits(wslay_event_context_ptr ctx,
+ uint8_t rsv);
+
+/*
+ * Enables or disables buffering of an entire message for non-control
+ * frames. If val is 0, buffering is enabled. Otherwise, buffering is
+ * disabled. If wslay_event_on_msg_recv_callback is invoked when
+ * buffering is disabled, the msg_length member of struct
+ * wslay_event_on_msg_recv_arg is set to 0.
+ *
+ * The control frames are always buffered regardless of this function call.
+ *
+ * This function must not be used after the first invocation of
+ * wslay_event_recv() function.
+ */
+void wslay_event_config_set_no_buffering(wslay_event_context_ptr ctx, int val);
+
+/*
+ * Sets maximum length of a message that can be received. The length
+ * of message is checked by wslay_event_recv() function. If the length
+ * of a message is larger than this value, reading operation is
+ * disabled (same effect with wslay_event_shutdown_read() call) and
+ * close control frame with WSLAY_CODE_MESSAGE_TOO_BIG is queued. If
+ * buffering for non-control frames is disabled, the library checks
+ * each frame payload length and does not check length of entire
+ * message.
+ *
+ * The default value is (1u << 31)-1.
+ */
+void wslay_event_config_set_max_recv_msg_length(wslay_event_context_ptr ctx,
+ uint64_t val);
+
+/*
+ * Sets callbacks to ctx. The callbacks previouly set by this function
+ * or wslay_event_context_server_init() or
+ * wslay_event_context_client_init() are replaced with callbacks.
+ */
+void wslay_event_config_set_callbacks
+(wslay_event_context_ptr ctx, const struct wslay_event_callbacks *callbacks);
+
+/*
+ * Receives messages from peer. When receiving
+ * messages, it uses wslay_event_recv_callback function. Single call
+ * of this function receives multiple messages until
+ * wslay_event_recv_callback function sets error code
+ * WSLAY_ERR_WOULDBLOCK.
+ *
+ * When close control frame is received, this function automatically
+ * queues close control frame. Also this function calls
+ * wslay_event_set_read_enabled() with second argument 0 to disable
+ * further read from peer.
+ *
+ * When ping control frame is received, this function automatically
+ * queues pong control frame.
+ *
+ * In case of a fatal errror which leads to negative return code, this
+ * function calls wslay_event_set_read_enabled() with second argument
+ * 0 to disable further read from peer.
+ *
+ * wslay_event_recv() returns 0 if it succeeds, or one of the
+ * following negative error codes:
+ *
+ * WSLAY_ERR_CALLBACK_FAILURE
+ * User defined callback function is failed.
+ *
+ * WSLAY_ERR_NOMEM
+ * Out of memory.
+ *
+ * When negative error code is returned, application must not make any
+ * further call of wslay_event_recv() and must close WebSocket
+ * connection.
+ */
+int wslay_event_recv(wslay_event_context_ptr ctx);
+
+/*
+ * Sends queued messages to peer. When sending a
+ * message, it uses wslay_event_send_callback function. Single call of
+ * wslay_event_send() sends multiple messages until
+ * wslay_event_send_callback sets error code WSLAY_ERR_WOULDBLOCK.
+ *
+ * If ctx is initialized for WebSocket client use, wslay_event_send()
+ * uses wslay_event_genmask_callback to get new mask key.
+ *
+ * When a message queued using wslay_event_queue_fragmented_msg() is
+ * sent, wslay_event_send() invokes
+ * wslay_event_fragmented_msg_callback for that message.
+ *
+ * After close control frame is sent, this function calls
+ * wslay_event_set_write_enabled() with second argument 0 to disable
+ * further transmission to peer.
+ *
+ * If there are any pending messages, wslay_event_want_write() returns
+ * 1, otherwise returns 0.
+ *
+ * In case of a fatal errror which leads to negative return code, this
+ * function calls wslay_event_set_write_enabled() with second argument
+ * 0 to disable further transmission to peer.
+ *
+ * wslay_event_send() returns 0 if it succeeds, or one of the
+ * following negative error codes:
+ *
+ * WSLAY_ERR_CALLBACK_FAILURE
+ * User defined callback function is failed.
+ *
+ * WSLAY_ERR_NOMEM
+ * Out of memory.
+ *
+ * When negative error code is returned, application must not make any
+ * further call of wslay_event_send() and must close WebSocket
+ * connection.
+ */
+int wslay_event_send(wslay_event_context_ptr ctx);
+
+struct wslay_event_msg {
+ uint8_t opcode;
+ const uint8_t *msg;
+ size_t msg_length;
+};
+
+/*
+ * Queues message specified in arg.
+ *
+ * This function supports both control and non-control messages and
+ * the given message is sent without fragmentation. If fragmentation
+ * is needed, use wslay_event_queue_fragmented_msg() function instead.
+ *
+ * This function just queues a message and does not send
+ * it. wslay_event_send() function call sends these queued messages.
+ *
+ * wslay_event_queue_msg() returns 0 if it succeeds, or returns the
+ * following negative error codes:
+ *
+ * WSLAY_ERR_NO_MORE_MSG
+ * Could not queue given message. The one of possible reason is that
+ * close control frame has been queued/sent and no further queueing
+ * message is not allowed.
+ *
+ * WSLAY_ERR_INVALID_ARGUMENT
+ * The given message is invalid.
+ *
+ * WSLAY_ERR_NOMEM
+ * Out of memory.
+ */
+int wslay_event_queue_msg(wslay_event_context_ptr ctx,
+ const struct wslay_event_msg *arg);
+
+/*
+ * Extended version of wslay_event_queue_msg which allows to set reserved bits.
+ */
+int wslay_event_queue_msg_ex(wslay_event_context_ptr ctx,
+ const struct wslay_event_msg *arg, uint8_t rsv);
+
+/*
+ * Specify "source" to generate message.
+ */
+union wslay_event_msg_source {
+ int fd;
+ void *data;
+};
+
+/*
+ * Callback function called by wslay_event_send() to read message data
+ * from source. The implementation of
+ * wslay_event_fragmented_msg_callback must store at most len bytes of
+ * data to buf and return the number of stored bytes. If all data is
+ * read (i.e., EOF), set *eof to 1. If no data can be generated at the
+ * moment, return 0. If there is an error, return -1 and set error
+ * code WSLAY_ERR_CALLBACK_FAILURE using wslay_event_set_error().
+ */
+typedef ssize_t (*wslay_event_fragmented_msg_callback)
+(wslay_event_context_ptr ctx,
+ uint8_t *buf, size_t len, const union wslay_event_msg_source *source,
+ int *eof, void *user_data);
+
+struct wslay_event_fragmented_msg {
+ /* opcode */
+ uint8_t opcode;
+ /* "source" to generate message data */
+ union wslay_event_msg_source source;
+ /* Callback function to read message data from source. */
+ wslay_event_fragmented_msg_callback read_callback;
+};
+
+/*
+ * Queues a fragmented message specified in arg.
+ *
+ * This function supports non-control messages only. For control frames,
+ * use wslay_event_queue_msg() or wslay_event_queue_close().
+ *
+ * This function just queues a message and does not send
+ * it. wslay_event_send() function call sends these queued messages.
+ *
+ * wslay_event_queue_fragmented_msg() returns 0 if it succeeds, or
+ * returns the following negative error codes:
+ *
+ * WSLAY_ERR_NO_MORE_MSG
+ * Could not queue given message. The one of possible reason is that
+ * close control frame has been queued/sent and no further queueing
+ * message is not allowed.
+ *
+ * WSLAY_ERR_INVALID_ARGUMENT
+ * The given message is invalid.
+ *
+ * WSLAY_ERR_NOMEM
+ * Out of memory.
+ */
+int wslay_event_queue_fragmented_msg
+(wslay_event_context_ptr ctx, const struct wslay_event_fragmented_msg *arg);
+
+/*
+ * Extended version of wslay_event_queue_fragmented_msg which allows to set
+ * reserved bits.
+ */
+int wslay_event_queue_fragmented_msg_ex(wslay_event_context_ptr ctx,
+ const struct wslay_event_fragmented_msg *arg, uint8_t rsv);
+
+/*
+ * Queues close control frame. This function is provided just for
+ * convenience. wslay_event_queue_msg() can queue a close control
+ * frame as well. status_code is the status code of close control
+ * frame. reason is the close reason encoded in UTF-8. reason_length
+ * is the length of reason in bytes. reason_length must be less than
+ * 123 bytes.
+ *
+ * If status_code is 0, reason and reason_length is not used and close
+ * control frame with zero-length payload will be queued.
+ *
+ * This function just queues a message and does not send
+ * it. wslay_event_send() function call sends these queued messages.
+ *
+ * wslay_event_queue_close() returns 0 if it succeeds, or returns the
+ * following negative error codes:
+ *
+ * WSLAY_ERR_NO_MORE_MSG
+ * Could not queue given message. The one of possible reason is that
+ * close control frame has been queued/sent and no further queueing
+ * message is not allowed.
+ *
+ * WSLAY_ERR_INVALID_ARGUMENT
+ * The given message is invalid.
+ *
+ * WSLAY_ERR_NOMEM
+ * Out of memory.
+ */
+int wslay_event_queue_close(wslay_event_context_ptr ctx,
+ uint16_t status_code,
+ const uint8_t *reason, size_t reason_length);
+
+/*
+ * Sets error code to tell the library there is an error. This
+ * function is typically used in user defined callback functions. See
+ * the description of callback function to know which error code
+ * should be used.
+ */
+void wslay_event_set_error(wslay_event_context_ptr ctx, int val);
+
+/*
+ * Query whehter the library want to read more data from peer.
+ *
+ * wslay_event_want_read() returns 1 if the library want to read more
+ * data from peer, or returns 0.
+ */
+int wslay_event_want_read(wslay_event_context_ptr ctx);
+
+/*
+ * Query whehter the library want to send more data to peer.
+ *
+ * wslay_event_want_write() returns 1 if the library want to send more
+ * data to peer, or returns 0.
+ */
+int wslay_event_want_write(wslay_event_context_ptr ctx);
+
+/*
+ * Prevents the event-based API context from reading any further data
+ * from peer.
+ *
+ * This function may be used with wslay_event_queue_close() if the
+ * application detects error in the data received and wants to fail
+ * WebSocket connection.
+ */
+void wslay_event_shutdown_read(wslay_event_context_ptr ctx);
+
+/*
+ * Prevents the event-based API context from sending any further data
+ * to peer.
+ */
+void wslay_event_shutdown_write(wslay_event_context_ptr ctx);
+
+/*
+ * Returns 1 if the event-based API context allows read operation, or
+ * return 0.
+ *
+ * After wslay_event_shutdown_read() is called,
+ * wslay_event_get_read_enabled() returns 0.
+ */
+int wslay_event_get_read_enabled(wslay_event_context_ptr ctx);
+
+/*
+ * Returns 1 if the event-based API context allows write operation, or
+ * return 0.
+ *
+ * After wslay_event_shutdown_write() is called,
+ * wslay_event_get_write_enabled() returns 0.
+ */
+int wslay_event_get_write_enabled(wslay_event_context_ptr ctx);
+
+/*
+ * Returns 1 if a close control frame has been received from peer, or
+ * returns 0.
+ */
+int wslay_event_get_close_received(wslay_event_context_ptr ctx);
+
+/*
+ * Returns 1 if a close control frame has been sent to peer, or
+ * returns 0.
+ */
+int wslay_event_get_close_sent(wslay_event_context_ptr ctx);
+
+/*
+ * Returns status code received in close control frame. If no close
+ * control frame has not been received, returns
+ * WSLAY_CODE_ABNORMAL_CLOSURE. If received close control frame has no
+ * status code, returns WSLAY_CODE_NO_STATUS_RCVD.
+ */
+uint16_t wslay_event_get_status_code_received(wslay_event_context_ptr ctx);
+
+/*
+ * Returns status code sent in close control frame. If no close
+ * control frame has not been sent, returns
+ * WSLAY_CODE_ABNORMAL_CLOSURE. If sent close control frame has no
+ * status code, returns WSLAY_CODE_NO_STATUS_RCVD.
+ */
+uint16_t wslay_event_get_status_code_sent(wslay_event_context_ptr ctx);
+
+/*
+ * Returns the number of queued messages.
+ */
+size_t wslay_event_get_queued_msg_count(wslay_event_context_ptr ctx);
+
+/*
+ * Returns the sum of queued message length. It only counts the
+ * message length queued using wslay_event_queue_msg() or
+ * wslay_event_queue_close().
+ */
+size_t wslay_event_get_queued_msg_length(wslay_event_context_ptr ctx);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* WSLAY_H */
diff --git a/thirdparty/wslay/includes/wslay/wslayver.h b/thirdparty/wslay/includes/wslay/wslayver.h
new file mode 100644
index 0000000000..2c3e282e9d
--- /dev/null
+++ b/thirdparty/wslay/includes/wslay/wslayver.h
@@ -0,0 +1,31 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef WSLAYVER_H
+#define WSLAYVER_H
+
+/* Version number of wslay release */
+#define WSLAY_VERSION "1.1.0"
+
+#endif /* WSLAYVER_H */
diff --git a/thirdparty/wslay/wslay_event.c b/thirdparty/wslay/wslay_event.c
new file mode 100644
index 0000000000..57415c51e0
--- /dev/null
+++ b/thirdparty/wslay/wslay_event.c
@@ -0,0 +1,1027 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "wslay_event.h"
+
+#include <string.h>
+#include <assert.h>
+#include <stdio.h>
+
+#include "wslay_queue.h"
+#include "wslay_frame.h"
+#include "wslay_net.h"
+/* Start of utf8 dfa */
+/* Copyright (c) 2008-2010 Bjoern Hoehrmann <bjoern@hoehrmann.de>
+ * See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details.
+ *
+ * Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de>
+ *
+ * Permission is hereby granted, free of charge, to any person
+ * obtaining a copy of this software and associated documentation
+ * files (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy,
+ * modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
+ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+#define UTF8_ACCEPT 0
+#define UTF8_REJECT 12
+
+static const uint8_t utf8d[] = {
+ /*
+ * The first part of the table maps bytes to character classes that
+ * to reduce the size of the transition table and create bitmasks.
+ */
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,
+ 10,3,3,3,3,3,3,3,3,3,3,3,3,4,3,3, 11,6,6,6,5,8,8,8,8,8,8,8,8,8,8,8,
+
+ /*
+ * The second part is a transition table that maps a combination
+ * of a state of the automaton and a character class to a state.
+ */
+ 0,12,24,36,60,96,84,12,12,12,48,72, 12,12,12,12,12,12,12,12,12,12,12,12,
+ 12, 0,12,12,12,12,12, 0,12, 0,12,12, 12,24,12,12,12,12,12,24,12,24,12,12,
+ 12,12,12,12,12,12,12,24,12,12,12,12, 12,24,12,12,12,12,12,12,12,24,12,12,
+ 12,12,12,12,12,12,12,36,12,36,12,12, 12,36,12,12,12,12,12,36,12,36,12,12,
+ 12,36,12,12,12,12,12,12,12,12,12,12,
+};
+
+static uint32_t
+decode(uint32_t* state, uint32_t* codep, uint32_t byte) {
+ uint32_t type = utf8d[byte];
+
+ *codep = (*state != UTF8_ACCEPT) ?
+ (byte & 0x3fu) | (*codep << 6) :
+ (0xff >> type) & (byte);
+
+ *state = utf8d[256 + *state + type];
+ return *state;
+}
+
+/* End of utf8 dfa */
+
+static ssize_t wslay_event_frame_recv_callback(uint8_t *buf, size_t len,
+ int flags, void *user_data)
+{
+ struct wslay_event_frame_user_data *e =
+ (struct wslay_event_frame_user_data*)user_data;
+ return e->ctx->callbacks.recv_callback(e->ctx, buf, len, flags, e->user_data);
+}
+
+static ssize_t wslay_event_frame_send_callback(const uint8_t *data, size_t len,
+ int flags, void *user_data)
+{
+ struct wslay_event_frame_user_data *e =
+ (struct wslay_event_frame_user_data*)user_data;
+ return e->ctx->callbacks.send_callback(e->ctx, data, len, flags,
+ e->user_data);
+}
+
+static int wslay_event_frame_genmask_callback(uint8_t *buf, size_t len,
+ void *user_data)
+{
+ struct wslay_event_frame_user_data *e =
+ (struct wslay_event_frame_user_data*)user_data;
+ return e->ctx->callbacks.genmask_callback(e->ctx, buf, len, e->user_data);
+}
+
+static int wslay_event_byte_chunk_init
+(struct wslay_event_byte_chunk **chunk, size_t len)
+{
+ *chunk = (struct wslay_event_byte_chunk*)malloc
+ (sizeof(struct wslay_event_byte_chunk));
+ if(*chunk == NULL) {
+ return WSLAY_ERR_NOMEM;
+ }
+ memset(*chunk, 0, sizeof(struct wslay_event_byte_chunk));
+ if(len) {
+ (*chunk)->data = (uint8_t*)malloc(len);
+ if((*chunk)->data == NULL) {
+ free(*chunk);
+ return WSLAY_ERR_NOMEM;
+ }
+ (*chunk)->data_length = len;
+ }
+ return 0;
+}
+
+static void wslay_event_byte_chunk_free(struct wslay_event_byte_chunk *c)
+{
+ if(!c) {
+ return;
+ }
+ free(c->data);
+ free(c);
+}
+
+static void wslay_event_byte_chunk_copy(struct wslay_event_byte_chunk *c,
+ size_t off,
+ const uint8_t *data, size_t data_length)
+{
+ memcpy(c->data+off, data, data_length);
+}
+
+static void wslay_event_imsg_set(struct wslay_event_imsg *m,
+ uint8_t fin, uint8_t rsv, uint8_t opcode)
+{
+ m->fin = fin;
+ m->rsv = rsv;
+ m->opcode = opcode;
+ m->msg_length = 0;
+}
+
+static void wslay_event_imsg_chunks_free(struct wslay_event_imsg *m)
+{
+ if(!m->chunks) {
+ return;
+ }
+ while(!wslay_queue_empty(m->chunks)) {
+ wslay_event_byte_chunk_free(wslay_queue_top(m->chunks));
+ wslay_queue_pop(m->chunks);
+ }
+}
+
+static void wslay_event_imsg_reset(struct wslay_event_imsg *m)
+{
+ m->opcode = 0xffu;
+ m->utf8state = UTF8_ACCEPT;
+ wslay_event_imsg_chunks_free(m);
+}
+
+static int wslay_event_imsg_append_chunk(struct wslay_event_imsg *m, size_t len)
+{
+ if(len == 0) {
+ return 0;
+ } else {
+ int r;
+ struct wslay_event_byte_chunk *chunk;
+ if((r = wslay_event_byte_chunk_init(&chunk, len)) != 0) {
+ return r;
+ }
+ if((r = wslay_queue_push(m->chunks, chunk)) != 0) {
+ return r;
+ }
+ m->msg_length += len;
+ return 0;
+ }
+}
+
+static int wslay_event_omsg_non_fragmented_init
+(struct wslay_event_omsg **m, uint8_t opcode, uint8_t rsv,
+ const uint8_t *msg, size_t msg_length)
+{
+ *m = (struct wslay_event_omsg*)malloc(sizeof(struct wslay_event_omsg));
+ if(!*m) {
+ return WSLAY_ERR_NOMEM;
+ }
+ memset(*m, 0, sizeof(struct wslay_event_omsg));
+ (*m)->fin = 1;
+ (*m)->opcode = opcode;
+ (*m)->rsv = rsv;
+ (*m)->type = WSLAY_NON_FRAGMENTED;
+ if(msg_length) {
+ (*m)->data = (uint8_t*)malloc(msg_length);
+ if(!(*m)->data) {
+ free(*m);
+ return WSLAY_ERR_NOMEM;
+ }
+ memcpy((*m)->data, msg, msg_length);
+ (*m)->data_length = msg_length;
+ }
+ return 0;
+}
+
+static int wslay_event_omsg_fragmented_init
+(struct wslay_event_omsg **m, uint8_t opcode, uint8_t rsv,
+ const union wslay_event_msg_source source,
+ wslay_event_fragmented_msg_callback read_callback)
+{
+ *m = (struct wslay_event_omsg*)malloc(sizeof(struct wslay_event_omsg));
+ if(!*m) {
+ return WSLAY_ERR_NOMEM;
+ }
+ memset(*m, 0, sizeof(struct wslay_event_omsg));
+ (*m)->opcode = opcode;
+ (*m)->rsv = rsv;
+ (*m)->type = WSLAY_FRAGMENTED;
+ (*m)->source = source;
+ (*m)->read_callback = read_callback;
+ return 0;
+}
+
+static void wslay_event_omsg_free(struct wslay_event_omsg *m)
+{
+ if(!m) {
+ return;
+ }
+ free(m->data);
+ free(m);
+}
+
+static uint8_t* wslay_event_flatten_queue(struct wslay_queue *queue, size_t len)
+{
+ if(len == 0) {
+ return NULL;
+ } else {
+ size_t off = 0;
+ uint8_t *buf = (uint8_t*)malloc(len);
+ if(!buf) {
+ return NULL;
+ }
+ while(!wslay_queue_empty(queue)) {
+ struct wslay_event_byte_chunk *chunk = wslay_queue_top(queue);
+ memcpy(buf+off, chunk->data, chunk->data_length);
+ off += chunk->data_length;
+ wslay_event_byte_chunk_free(chunk);
+ wslay_queue_pop(queue);
+ assert(off <= len);
+ }
+ assert(len == off);
+ return buf;
+ }
+}
+
+static int wslay_event_is_msg_queueable(wslay_event_context_ptr ctx)
+{
+ return ctx->write_enabled && (ctx->close_status & WSLAY_CLOSE_QUEUED) == 0;
+}
+
+int wslay_event_queue_close(wslay_event_context_ptr ctx, uint16_t status_code,
+ const uint8_t *reason, size_t reason_length)
+{
+ if(!wslay_event_is_msg_queueable(ctx)) {
+ return WSLAY_ERR_NO_MORE_MSG;
+ } else if(reason_length > 123) {
+ return WSLAY_ERR_INVALID_ARGUMENT;
+ } else {
+ uint8_t msg[128];
+ size_t msg_length;
+ struct wslay_event_msg arg;
+ uint16_t ncode;
+ int r;
+ if(status_code == 0) {
+ msg_length = 0;
+ } else {
+ ncode = htons(status_code);
+ memcpy(msg, &ncode, 2);
+ if(reason_length) {
+ memcpy(msg+2, reason, reason_length);
+ }
+ msg_length = reason_length+2;
+ }
+ arg.opcode = WSLAY_CONNECTION_CLOSE;
+ arg.msg = msg;
+ arg.msg_length = msg_length;
+ r = wslay_event_queue_msg(ctx, &arg);
+ if(r == 0) {
+ ctx->close_status |= WSLAY_CLOSE_QUEUED;
+ }
+ return r;
+ }
+}
+
+static int wslay_event_queue_close_wrapper
+(wslay_event_context_ptr ctx, uint16_t status_code,
+ const uint8_t *reason, size_t reason_length)
+{
+ int r;
+ ctx->read_enabled = 0;
+ if((r = wslay_event_queue_close(ctx, status_code, reason, reason_length)) &&
+ r != WSLAY_ERR_NO_MORE_MSG) {
+ return r;
+ }
+ return 0;
+}
+
+static int wslay_event_verify_rsv_bits(wslay_event_context_ptr ctx, uint8_t rsv)
+{
+ return ((rsv & ~ctx->allowed_rsv_bits) == 0);
+}
+
+int wslay_event_queue_msg(wslay_event_context_ptr ctx,
+ const struct wslay_event_msg *arg)
+{
+ return wslay_event_queue_msg_ex(ctx, arg, WSLAY_RSV_NONE);
+}
+
+int wslay_event_queue_msg_ex(wslay_event_context_ptr ctx,
+ const struct wslay_event_msg *arg, uint8_t rsv)
+{
+ int r;
+ struct wslay_event_omsg *omsg;
+ if(!wslay_event_is_msg_queueable(ctx)) {
+ return WSLAY_ERR_NO_MORE_MSG;
+ }
+ /* RSV1 is not allowed for control frames */
+ if((wslay_is_ctrl_frame(arg->opcode) &&
+ (arg->msg_length > 125 || wslay_get_rsv1(rsv)))
+ || !wslay_event_verify_rsv_bits(ctx, rsv)) {
+ return WSLAY_ERR_INVALID_ARGUMENT;
+ }
+ if((r = wslay_event_omsg_non_fragmented_init
+ (&omsg, arg->opcode, rsv, arg->msg, arg->msg_length)) != 0) {
+ return r;
+ }
+ if(wslay_is_ctrl_frame(arg->opcode)) {
+ if((r = wslay_queue_push(ctx->send_ctrl_queue, omsg)) != 0) {
+ return r;
+ }
+ } else {
+ if((r = wslay_queue_push(ctx->send_queue, omsg)) != 0) {
+ return r;
+ }
+ }
+ ++ctx->queued_msg_count;
+ ctx->queued_msg_length += arg->msg_length;
+ return 0;
+}
+
+int wslay_event_queue_fragmented_msg
+(wslay_event_context_ptr ctx, const struct wslay_event_fragmented_msg *arg)
+{
+ return wslay_event_queue_fragmented_msg_ex(ctx, arg, WSLAY_RSV_NONE);
+}
+
+int wslay_event_queue_fragmented_msg_ex(wslay_event_context_ptr ctx,
+ const struct wslay_event_fragmented_msg *arg, uint8_t rsv)
+{
+ int r;
+ struct wslay_event_omsg *omsg;
+ if(!wslay_event_is_msg_queueable(ctx)) {
+ return WSLAY_ERR_NO_MORE_MSG;
+ }
+ if(wslay_is_ctrl_frame(arg->opcode) ||
+ !wslay_event_verify_rsv_bits(ctx, rsv)) {
+ return WSLAY_ERR_INVALID_ARGUMENT;
+ }
+ if((r = wslay_event_omsg_fragmented_init
+ (&omsg, arg->opcode, rsv, arg->source, arg->read_callback)) != 0) {
+ return r;
+ }
+ if((r = wslay_queue_push(ctx->send_queue, omsg)) != 0) {
+ return r;
+ }
+ ++ctx->queued_msg_count;
+ return 0;
+}
+
+void wslay_event_config_set_callbacks
+(wslay_event_context_ptr ctx, const struct wslay_event_callbacks *callbacks)
+{
+ ctx->callbacks = *callbacks;
+}
+
+static int wslay_event_context_init
+(wslay_event_context_ptr *ctx,
+ const struct wslay_event_callbacks *callbacks,
+ void *user_data)
+{
+ int i, r;
+ struct wslay_frame_callbacks frame_callbacks = {
+ wslay_event_frame_send_callback,
+ wslay_event_frame_recv_callback,
+ wslay_event_frame_genmask_callback
+ };
+ *ctx = (wslay_event_context_ptr)malloc(sizeof(struct wslay_event_context));
+ if(!*ctx) {
+ return WSLAY_ERR_NOMEM;
+ }
+ memset(*ctx, 0, sizeof(struct wslay_event_context));
+ wslay_event_config_set_callbacks(*ctx, callbacks);
+ (*ctx)->user_data = user_data;
+ (*ctx)->frame_user_data.ctx = *ctx;
+ (*ctx)->frame_user_data.user_data = user_data;
+ if((r = wslay_frame_context_init(&(*ctx)->frame_ctx, &frame_callbacks,
+ &(*ctx)->frame_user_data)) != 0) {
+ wslay_event_context_free(*ctx);
+ return r;
+ }
+ (*ctx)->read_enabled = (*ctx)->write_enabled = 1;
+ (*ctx)->send_queue = wslay_queue_new();
+ if(!(*ctx)->send_queue) {
+ wslay_event_context_free(*ctx);
+ return WSLAY_ERR_NOMEM;
+ }
+ (*ctx)->send_ctrl_queue = wslay_queue_new();
+ if(!(*ctx)->send_ctrl_queue) {
+ wslay_event_context_free(*ctx);
+ return WSLAY_ERR_NOMEM;
+ }
+ (*ctx)->queued_msg_count = 0;
+ (*ctx)->queued_msg_length = 0;
+ for(i = 0; i < 2; ++i) {
+ wslay_event_imsg_reset(&(*ctx)->imsgs[i]);
+ (*ctx)->imsgs[i].chunks = wslay_queue_new();
+ if(!(*ctx)->imsgs[i].chunks) {
+ wslay_event_context_free(*ctx);
+ return WSLAY_ERR_NOMEM;
+ }
+ }
+ (*ctx)->imsg = &(*ctx)->imsgs[0];
+ (*ctx)->obufmark = (*ctx)->obuflimit = (*ctx)->obuf;
+ (*ctx)->status_code_sent = WSLAY_CODE_ABNORMAL_CLOSURE;
+ (*ctx)->status_code_recv = WSLAY_CODE_ABNORMAL_CLOSURE;
+ (*ctx)->max_recv_msg_length = (1u << 31)-1;
+ return 0;
+}
+
+int wslay_event_context_server_init
+(wslay_event_context_ptr *ctx,
+ const struct wslay_event_callbacks *callbacks,
+ void *user_data)
+{
+ int r;
+ if((r = wslay_event_context_init(ctx, callbacks, user_data)) != 0) {
+ return r;
+ }
+ (*ctx)->server = 1;
+ return 0;
+}
+
+int wslay_event_context_client_init
+(wslay_event_context_ptr *ctx,
+ const struct wslay_event_callbacks *callbacks,
+ void *user_data)
+{
+ int r;
+ if((r = wslay_event_context_init(ctx, callbacks, user_data)) != 0) {
+ return r;
+ }
+ (*ctx)->server = 0;
+ return 0;
+}
+
+void wslay_event_context_free(wslay_event_context_ptr ctx)
+{
+ int i;
+ if(!ctx) {
+ return;
+ }
+ for(i = 0; i < 2; ++i) {
+ wslay_event_imsg_chunks_free(&ctx->imsgs[i]);
+ wslay_queue_free(ctx->imsgs[i].chunks);
+ }
+ if(ctx->send_queue) {
+ while(!wslay_queue_empty(ctx->send_queue)) {
+ wslay_event_omsg_free(wslay_queue_top(ctx->send_queue));
+ wslay_queue_pop(ctx->send_queue);
+ }
+ wslay_queue_free(ctx->send_queue);
+ }
+ if(ctx->send_ctrl_queue) {
+ while(!wslay_queue_empty(ctx->send_ctrl_queue)) {
+ wslay_event_omsg_free(wslay_queue_top(ctx->send_ctrl_queue));
+ wslay_queue_pop(ctx->send_ctrl_queue);
+ }
+ wslay_queue_free(ctx->send_ctrl_queue);
+ }
+ wslay_frame_context_free(ctx->frame_ctx);
+ wslay_event_omsg_free(ctx->omsg);
+ free(ctx);
+}
+
+static void wslay_event_call_on_frame_recv_start_callback
+(wslay_event_context_ptr ctx, const struct wslay_frame_iocb *iocb)
+{
+ if(ctx->callbacks.on_frame_recv_start_callback) {
+ struct wslay_event_on_frame_recv_start_arg arg;
+ arg.fin = iocb->fin;
+ arg.rsv = iocb->rsv;
+ arg.opcode = iocb->opcode;
+ arg.payload_length = iocb->payload_length;
+ ctx->callbacks.on_frame_recv_start_callback(ctx, &arg, ctx->user_data);
+ }
+}
+
+static void wslay_event_call_on_frame_recv_chunk_callback
+(wslay_event_context_ptr ctx, const struct wslay_frame_iocb *iocb)
+{
+ if(ctx->callbacks.on_frame_recv_chunk_callback) {
+ struct wslay_event_on_frame_recv_chunk_arg arg;
+ arg.data = iocb->data;
+ arg.data_length = iocb->data_length;
+ ctx->callbacks.on_frame_recv_chunk_callback(ctx, &arg, ctx->user_data);
+ }
+}
+
+static void wslay_event_call_on_frame_recv_end_callback
+(wslay_event_context_ptr ctx)
+{
+ if(ctx->callbacks.on_frame_recv_end_callback) {
+ ctx->callbacks.on_frame_recv_end_callback(ctx, ctx->user_data);
+ }
+}
+
+static int wslay_event_is_valid_status_code(uint16_t status_code)
+{
+ return (1000 <= status_code && status_code <= 1011 &&
+ status_code != 1004 && status_code != 1005 && status_code != 1006) ||
+ (3000 <= status_code && status_code <= 4999);
+}
+
+static int wslay_event_config_get_no_buffering(wslay_event_context_ptr ctx)
+{
+ return (ctx->config & WSLAY_CONFIG_NO_BUFFERING) > 0;
+}
+
+int wslay_event_recv(wslay_event_context_ptr ctx)
+{
+ struct wslay_frame_iocb iocb;
+ ssize_t r;
+ while(ctx->read_enabled) {
+ memset(&iocb, 0, sizeof(iocb));
+ r = wslay_frame_recv(ctx->frame_ctx, &iocb);
+ if(r >= 0) {
+ int new_frame = 0;
+ /* RSV1 is not allowed on control and continuation frames */
+ if((!wslay_event_verify_rsv_bits(ctx, iocb.rsv)) ||
+ (wslay_get_rsv1(iocb.rsv) && (wslay_is_ctrl_frame(iocb.opcode) ||
+ iocb.opcode == WSLAY_CONTINUATION_FRAME)) ||
+ (ctx->server && !iocb.mask) || (!ctx->server && iocb.mask)) {
+ if((r = wslay_event_queue_close_wrapper
+ (ctx, WSLAY_CODE_PROTOCOL_ERROR, NULL, 0)) != 0) {
+ return r;
+ }
+ break;
+ }
+ if(ctx->imsg->opcode == 0xffu) {
+ if(iocb.opcode == WSLAY_TEXT_FRAME ||
+ iocb.opcode == WSLAY_BINARY_FRAME ||
+ iocb.opcode == WSLAY_CONNECTION_CLOSE ||
+ iocb.opcode == WSLAY_PING ||
+ iocb.opcode == WSLAY_PONG) {
+ wslay_event_imsg_set(ctx->imsg, iocb.fin, iocb.rsv, iocb.opcode);
+ new_frame = 1;
+ } else {
+ if((r = wslay_event_queue_close_wrapper
+ (ctx, WSLAY_CODE_PROTOCOL_ERROR, NULL, 0)) != 0) {
+ return r;
+ }
+ break;
+ }
+ } else if(ctx->ipayloadlen == 0 && ctx->ipayloadoff == 0) {
+ if(iocb.opcode == WSLAY_CONTINUATION_FRAME) {
+ ctx->imsg->fin = iocb.fin;
+ } else if(iocb.opcode == WSLAY_CONNECTION_CLOSE ||
+ iocb.opcode == WSLAY_PING ||
+ iocb.opcode == WSLAY_PONG) {
+ ctx->imsg = &ctx->imsgs[1];
+ wslay_event_imsg_set(ctx->imsg, iocb.fin, iocb.rsv, iocb.opcode);
+ } else {
+ if((r = wslay_event_queue_close_wrapper
+ (ctx, WSLAY_CODE_PROTOCOL_ERROR, NULL, 0)) != 0) {
+ return r;
+ }
+ break;
+ }
+ new_frame = 1;
+ }
+ if(new_frame) {
+ if(ctx->imsg->msg_length+iocb.payload_length >
+ ctx->max_recv_msg_length) {
+ if((r = wslay_event_queue_close_wrapper
+ (ctx, WSLAY_CODE_MESSAGE_TOO_BIG, NULL, 0)) != 0) {
+ return r;
+ }
+ break;
+ }
+ ctx->ipayloadlen = iocb.payload_length;
+ wslay_event_call_on_frame_recv_start_callback(ctx, &iocb);
+ if(!wslay_event_config_get_no_buffering(ctx) ||
+ wslay_is_ctrl_frame(iocb.opcode)) {
+ if((r = wslay_event_imsg_append_chunk(ctx->imsg,
+ iocb.payload_length)) != 0) {
+ ctx->read_enabled = 0;
+ return r;
+ }
+ }
+ }
+ /* If RSV1 bit is set then it is too early for utf-8 validation */
+ if((!wslay_get_rsv1(ctx->imsg->rsv) &&
+ ctx->imsg->opcode == WSLAY_TEXT_FRAME) ||
+ ctx->imsg->opcode == WSLAY_CONNECTION_CLOSE) {
+ size_t i;
+ if(ctx->imsg->opcode == WSLAY_CONNECTION_CLOSE) {
+ i = 2;
+ } else {
+ i = 0;
+ }
+ for(; i < iocb.data_length; ++i) {
+ uint32_t codep;
+ if(decode(&ctx->imsg->utf8state, &codep,
+ iocb.data[i]) == UTF8_REJECT) {
+ if((r = wslay_event_queue_close_wrapper
+ (ctx, WSLAY_CODE_INVALID_FRAME_PAYLOAD_DATA, NULL, 0)) != 0) {
+ return r;
+ }
+ break;
+ }
+ }
+ }
+ if(ctx->imsg->utf8state == UTF8_REJECT) {
+ break;
+ }
+ wslay_event_call_on_frame_recv_chunk_callback(ctx, &iocb);
+ if(iocb.data_length > 0) {
+ if(!wslay_event_config_get_no_buffering(ctx) ||
+ wslay_is_ctrl_frame(iocb.opcode)) {
+ struct wslay_event_byte_chunk *chunk;
+ chunk = wslay_queue_tail(ctx->imsg->chunks);
+ wslay_event_byte_chunk_copy(chunk, ctx->ipayloadoff,
+ iocb.data, iocb.data_length);
+ }
+ ctx->ipayloadoff += iocb.data_length;
+ }
+ if(ctx->ipayloadoff == ctx->ipayloadlen) {
+ if(ctx->imsg->fin &&
+ (ctx->imsg->opcode == WSLAY_TEXT_FRAME ||
+ ctx->imsg->opcode == WSLAY_CONNECTION_CLOSE) &&
+ ctx->imsg->utf8state != UTF8_ACCEPT) {
+ if((r = wslay_event_queue_close_wrapper
+ (ctx, WSLAY_CODE_INVALID_FRAME_PAYLOAD_DATA, NULL, 0)) != 0) {
+ return r;
+ }
+ break;
+ }
+ wslay_event_call_on_frame_recv_end_callback(ctx);
+ if(ctx->imsg->fin) {
+ if(ctx->callbacks.on_msg_recv_callback ||
+ ctx->imsg->opcode == WSLAY_CONNECTION_CLOSE ||
+ ctx->imsg->opcode == WSLAY_PING) {
+ struct wslay_event_on_msg_recv_arg arg;
+ uint16_t status_code = 0;
+ uint8_t *msg = NULL;
+ size_t msg_length = 0;
+ if(!wslay_event_config_get_no_buffering(ctx) ||
+ wslay_is_ctrl_frame(iocb.opcode)) {
+ msg = wslay_event_flatten_queue(ctx->imsg->chunks,
+ ctx->imsg->msg_length);
+ if(ctx->imsg->msg_length && !msg) {
+ ctx->read_enabled = 0;
+ return WSLAY_ERR_NOMEM;
+ }
+ msg_length = ctx->imsg->msg_length;
+ }
+ if(ctx->imsg->opcode == WSLAY_CONNECTION_CLOSE) {
+ const uint8_t *reason;
+ size_t reason_length;
+ if(ctx->imsg->msg_length >= 2) {
+ memcpy(&status_code, msg, 2);
+ status_code = ntohs(status_code);
+ if(!wslay_event_is_valid_status_code(status_code)) {
+ free(msg);
+ if((r = wslay_event_queue_close_wrapper
+ (ctx, WSLAY_CODE_PROTOCOL_ERROR, NULL, 0)) != 0) {
+ return r;
+ }
+ break;
+ }
+ reason = msg+2;
+ reason_length = ctx->imsg->msg_length-2;
+ } else {
+ reason = NULL;
+ reason_length = 0;
+ }
+ ctx->close_status |= WSLAY_CLOSE_RECEIVED;
+ ctx->status_code_recv =
+ status_code == 0 ? WSLAY_CODE_NO_STATUS_RCVD : status_code;
+ if((r = wslay_event_queue_close_wrapper
+ (ctx, status_code, reason, reason_length)) != 0) {
+ free(msg);
+ return r;
+ }
+ } else if(ctx->imsg->opcode == WSLAY_PING) {
+ struct wslay_event_msg arg;
+ arg.opcode = WSLAY_PONG;
+ arg.msg = msg;
+ arg.msg_length = ctx->imsg->msg_length;
+ if((r = wslay_event_queue_msg(ctx, &arg)) &&
+ r != WSLAY_ERR_NO_MORE_MSG) {
+ ctx->read_enabled = 0;
+ free(msg);
+ return r;
+ }
+ }
+ if(ctx->callbacks.on_msg_recv_callback) {
+ arg.rsv = ctx->imsg->rsv;
+ arg.opcode = ctx->imsg->opcode;
+ arg.msg = msg;
+ arg.msg_length = msg_length;
+ arg.status_code = status_code;
+ ctx->error = 0;
+ ctx->callbacks.on_msg_recv_callback(ctx, &arg, ctx->user_data);
+ }
+ free(msg);
+ }
+ wslay_event_imsg_reset(ctx->imsg);
+ if(ctx->imsg == &ctx->imsgs[1]) {
+ ctx->imsg = &ctx->imsgs[0];
+ }
+ }
+ ctx->ipayloadlen = ctx->ipayloadoff = 0;
+ }
+ } else {
+ if(r != WSLAY_ERR_WANT_READ ||
+ (ctx->error != WSLAY_ERR_WOULDBLOCK && ctx->error != 0)) {
+ if((r = wslay_event_queue_close_wrapper(ctx, 0, NULL, 0)) != 0) {
+ return r;
+ }
+ return WSLAY_ERR_CALLBACK_FAILURE;
+ }
+ break;
+ }
+ }
+ return 0;
+}
+
+static void wslay_event_on_non_fragmented_msg_popped
+(wslay_event_context_ptr ctx)
+{
+ ctx->omsg->fin = 1;
+ ctx->opayloadlen = ctx->omsg->data_length;
+ ctx->opayloadoff = 0;
+}
+
+static struct wslay_event_omsg* wslay_event_send_ctrl_queue_pop
+(wslay_event_context_ptr ctx)
+{
+ /*
+ * If Close control frame is queued, we don't send any control frame
+ * other than Close.
+ */
+ if(ctx->close_status & WSLAY_CLOSE_QUEUED) {
+ while(!wslay_queue_empty(ctx->send_ctrl_queue)) {
+ struct wslay_event_omsg *msg = wslay_queue_top(ctx->send_ctrl_queue);
+ wslay_queue_pop(ctx->send_ctrl_queue);
+ if(msg->opcode == WSLAY_CONNECTION_CLOSE) {
+ return msg;
+ } else {
+ wslay_event_omsg_free(msg);
+ }
+ }
+ return NULL;
+ } else {
+ struct wslay_event_omsg *msg = wslay_queue_top(ctx->send_ctrl_queue);
+ wslay_queue_pop(ctx->send_ctrl_queue);
+ return msg;
+ }
+}
+
+int wslay_event_send(wslay_event_context_ptr ctx)
+{
+ struct wslay_frame_iocb iocb;
+ ssize_t r;
+ while(ctx->write_enabled &&
+ (!wslay_queue_empty(ctx->send_queue) ||
+ !wslay_queue_empty(ctx->send_ctrl_queue) || ctx->omsg)) {
+ if(!ctx->omsg) {
+ if(wslay_queue_empty(ctx->send_ctrl_queue)) {
+ ctx->omsg = wslay_queue_top(ctx->send_queue);
+ wslay_queue_pop(ctx->send_queue);
+ } else {
+ ctx->omsg = wslay_event_send_ctrl_queue_pop(ctx);
+ if(ctx->omsg == NULL) {
+ break;
+ }
+ }
+ if(ctx->omsg->type == WSLAY_NON_FRAGMENTED) {
+ wslay_event_on_non_fragmented_msg_popped(ctx);
+ }
+ } else if(!wslay_is_ctrl_frame(ctx->omsg->opcode) &&
+ ctx->frame_ctx->ostate == PREP_HEADER &&
+ !wslay_queue_empty(ctx->send_ctrl_queue)) {
+ if((r = wslay_queue_push_front(ctx->send_queue, ctx->omsg)) != 0) {
+ ctx->write_enabled = 0;
+ return r;
+ }
+ ctx->omsg = wslay_event_send_ctrl_queue_pop(ctx);
+ if(ctx->omsg == NULL) {
+ break;
+ }
+ /* ctrl message has WSLAY_NON_FRAGMENTED */
+ wslay_event_on_non_fragmented_msg_popped(ctx);
+ }
+ if(ctx->omsg->type == WSLAY_NON_FRAGMENTED) {
+ memset(&iocb, 0, sizeof(iocb));
+ iocb.fin = 1;
+ iocb.opcode = ctx->omsg->opcode;
+ iocb.rsv = ctx->omsg->rsv;
+ iocb.mask = ctx->server^1;
+ iocb.data = ctx->omsg->data+ctx->opayloadoff;
+ iocb.data_length = ctx->opayloadlen-ctx->opayloadoff;
+ iocb.payload_length = ctx->opayloadlen;
+ r = wslay_frame_send(ctx->frame_ctx, &iocb);
+ if(r >= 0) {
+ ctx->opayloadoff += r;
+ if(ctx->opayloadoff == ctx->opayloadlen) {
+ --ctx->queued_msg_count;
+ ctx->queued_msg_length -= ctx->omsg->data_length;
+ if(ctx->omsg->opcode == WSLAY_CONNECTION_CLOSE) {
+ uint16_t status_code = 0;
+ ctx->write_enabled = 0;
+ ctx->close_status |= WSLAY_CLOSE_SENT;
+ if(ctx->omsg->data_length >= 2) {
+ memcpy(&status_code, ctx->omsg->data, 2);
+ status_code = ntohs(status_code);
+ }
+ ctx->status_code_sent =
+ status_code == 0 ? WSLAY_CODE_NO_STATUS_RCVD : status_code;
+ }
+ wslay_event_omsg_free(ctx->omsg);
+ ctx->omsg = NULL;
+ } else {
+ break;
+ }
+ } else {
+ if(r != WSLAY_ERR_WANT_WRITE ||
+ (ctx->error != WSLAY_ERR_WOULDBLOCK && ctx->error != 0)) {
+ ctx->write_enabled = 0;
+ return WSLAY_ERR_CALLBACK_FAILURE;
+ }
+ break;
+ }
+ } else {
+ if(ctx->omsg->fin == 0 && ctx->obuflimit == ctx->obufmark) {
+ int eof = 0;
+ r = ctx->omsg->read_callback(ctx, ctx->obuf, sizeof(ctx->obuf),
+ &ctx->omsg->source,
+ &eof, ctx->user_data);
+ if(r == 0) {
+ break;
+ } else if(r < 0) {
+ ctx->write_enabled = 0;
+ return WSLAY_ERR_CALLBACK_FAILURE;
+ }
+ ctx->obuflimit = ctx->obuf+r;
+ if(eof) {
+ ctx->omsg->fin = 1;
+ }
+ ctx->opayloadlen = r;
+ ctx->opayloadoff = 0;
+ }
+ memset(&iocb, 0, sizeof(iocb));
+ iocb.fin = ctx->omsg->fin;
+ iocb.opcode = ctx->omsg->opcode;
+ iocb.rsv = ctx->omsg->rsv;
+ iocb.mask = ctx->server ? 0 : 1;
+ iocb.data = ctx->obufmark;
+ iocb.data_length = ctx->obuflimit-ctx->obufmark;
+ iocb.payload_length = ctx->opayloadlen;
+ r = wslay_frame_send(ctx->frame_ctx, &iocb);
+ if(r >= 0) {
+ ctx->obufmark += r;
+ if(ctx->obufmark == ctx->obuflimit) {
+ ctx->obufmark = ctx->obuflimit = ctx->obuf;
+ if(ctx->omsg->fin) {
+ --ctx->queued_msg_count;
+ wslay_event_omsg_free(ctx->omsg);
+ ctx->omsg = NULL;
+ } else {
+ ctx->omsg->opcode = WSLAY_CONTINUATION_FRAME;
+ /* RSV1 is not set on continuation frames */
+ ctx->omsg->rsv = ctx->omsg->rsv & ~WSLAY_RSV1_BIT;
+ }
+ } else {
+ break;
+ }
+ } else {
+ if(r != WSLAY_ERR_WANT_WRITE ||
+ (ctx->error != WSLAY_ERR_WOULDBLOCK &&
+ ctx->error != 0)) {
+ ctx->write_enabled = 0;
+ return WSLAY_ERR_CALLBACK_FAILURE;
+ }
+ break;
+ }
+ }
+ }
+ return 0;
+}
+
+void wslay_event_set_error(wslay_event_context_ptr ctx, int val)
+{
+ ctx->error = val;
+}
+
+int wslay_event_want_read(wslay_event_context_ptr ctx)
+{
+ return ctx->read_enabled;
+}
+
+int wslay_event_want_write(wslay_event_context_ptr ctx)
+{
+ return ctx->write_enabled &&
+ (!wslay_queue_empty(ctx->send_queue) ||
+ !wslay_queue_empty(ctx->send_ctrl_queue) || ctx->omsg);
+}
+
+void wslay_event_shutdown_read(wslay_event_context_ptr ctx)
+{
+ ctx->read_enabled = 0;
+}
+
+void wslay_event_shutdown_write(wslay_event_context_ptr ctx)
+{
+ ctx->write_enabled = 0;
+}
+
+int wslay_event_get_read_enabled(wslay_event_context_ptr ctx)
+{
+ return ctx->read_enabled;
+}
+
+int wslay_event_get_write_enabled(wslay_event_context_ptr ctx)
+{
+ return ctx->write_enabled;
+}
+
+int wslay_event_get_close_received(wslay_event_context_ptr ctx)
+{
+ return (ctx->close_status & WSLAY_CLOSE_RECEIVED) > 0;
+}
+
+int wslay_event_get_close_sent(wslay_event_context_ptr ctx)
+{
+ return (ctx->close_status & WSLAY_CLOSE_SENT) > 0;
+}
+
+void wslay_event_config_set_allowed_rsv_bits(wslay_event_context_ptr ctx,
+ uint8_t rsv)
+{
+ /* We currently only allow WSLAY_RSV1_BIT or WSLAY_RSV_NONE */
+ ctx->allowed_rsv_bits = rsv & WSLAY_RSV1_BIT;
+}
+
+void wslay_event_config_set_no_buffering(wslay_event_context_ptr ctx, int val)
+{
+ if(val) {
+ ctx->config |= WSLAY_CONFIG_NO_BUFFERING;
+ } else {
+ ctx->config &= ~WSLAY_CONFIG_NO_BUFFERING;
+ }
+}
+
+void wslay_event_config_set_max_recv_msg_length(wslay_event_context_ptr ctx,
+ uint64_t val)
+{
+ ctx->max_recv_msg_length = val;
+}
+
+uint16_t wslay_event_get_status_code_received(wslay_event_context_ptr ctx)
+{
+ return ctx->status_code_recv;
+}
+
+uint16_t wslay_event_get_status_code_sent(wslay_event_context_ptr ctx)
+{
+ return ctx->status_code_sent;
+}
+
+size_t wslay_event_get_queued_msg_count(wslay_event_context_ptr ctx)
+{
+ return ctx->queued_msg_count;
+}
+
+size_t wslay_event_get_queued_msg_length(wslay_event_context_ptr ctx)
+{
+ return ctx->queued_msg_length;
+}
diff --git a/thirdparty/wslay/wslay_event.h b/thirdparty/wslay/wslay_event.h
new file mode 100644
index 0000000000..36feb9036d
--- /dev/null
+++ b/thirdparty/wslay/wslay_event.h
@@ -0,0 +1,142 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef WSLAY_EVENT_H
+#define WSLAY_EVENT_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <wslay/wslay.h>
+
+struct wslay_stack;
+struct wslay_queue;
+
+struct wslay_event_byte_chunk {
+ uint8_t *data;
+ size_t data_length;
+};
+
+struct wslay_event_imsg {
+ uint8_t fin;
+ uint8_t rsv;
+ uint8_t opcode;
+ uint32_t utf8state;
+ struct wslay_queue *chunks;
+ size_t msg_length;
+};
+
+enum wslay_event_msg_type {
+ WSLAY_NON_FRAGMENTED,
+ WSLAY_FRAGMENTED
+};
+
+struct wslay_event_omsg {
+ uint8_t fin;
+ uint8_t opcode;
+ uint8_t rsv;
+ enum wslay_event_msg_type type;
+
+ uint8_t *data;
+ size_t data_length;
+
+ union wslay_event_msg_source source;
+ wslay_event_fragmented_msg_callback read_callback;
+};
+
+struct wslay_event_frame_user_data {
+ wslay_event_context_ptr ctx;
+ void *user_data;
+};
+
+enum wslay_event_close_status {
+ WSLAY_CLOSE_RECEIVED = 1 << 0,
+ WSLAY_CLOSE_QUEUED = 1 << 1,
+ WSLAY_CLOSE_SENT = 1 << 2
+};
+
+enum wslay_event_config {
+ WSLAY_CONFIG_NO_BUFFERING = 1 << 0
+};
+
+struct wslay_event_context {
+ /* config status, bitwise OR of enum wslay_event_config values*/
+ uint32_t config;
+ /* maximum message length that can be received */
+ uint64_t max_recv_msg_length;
+ /* 1 if initialized for server, otherwise 0 */
+ uint8_t server;
+ /* bitwise OR of enum wslay_event_close_status values */
+ uint8_t close_status;
+ /* status code in received close control frame */
+ uint16_t status_code_recv;
+ /* status code in sent close control frame */
+ uint16_t status_code_sent;
+ wslay_frame_context_ptr frame_ctx;
+ /* 1 if reading is enabled, otherwise 0. Upon receiving close
+ control frame this value set to 0. If any errors in read
+ operation will also set this value to 0. */
+ uint8_t read_enabled;
+ /* 1 if writing is enabled, otherwise 0 Upon completing sending
+ close control frame, this value set to 0. If any errors in write
+ opration will also set this value to 0. */
+ uint8_t write_enabled;
+ /* imsg buffer to allow interleaved control frame between
+ non-control frames. */
+ struct wslay_event_imsg imsgs[2];
+ /* Pointer to imsgs to indicate current used buffer. */
+ struct wslay_event_imsg *imsg;
+ /* payload length of frame currently being received. */
+ uint64_t ipayloadlen;
+ /* next byte offset of payload currently being received. */
+ uint64_t ipayloadoff;
+ /* error value set by user callback */
+ int error;
+ /* Pointer to the message currently being sent. NULL if no message
+ is currently sent. */
+ struct wslay_event_omsg *omsg;
+ /* Queue for non-control frames */
+ struct wslay_queue/*<wslay_omsg*>*/ *send_queue;
+ /* Queue for control frames */
+ struct wslay_queue/*<wslay_omsg*>*/ *send_ctrl_queue;
+ /* Size of send_queue + size of send_ctrl_queue */
+ size_t queued_msg_count;
+ /* The sum of message length in send_queue */
+ size_t queued_msg_length;
+ /* Buffer used for fragmented messages */
+ uint8_t obuf[4096];
+ uint8_t *obuflimit;
+ uint8_t *obufmark;
+ /* payload length of frame currently being sent. */
+ uint64_t opayloadlen;
+ /* next byte offset of payload currently being sent. */
+ uint64_t opayloadoff;
+ struct wslay_event_callbacks callbacks;
+ struct wslay_event_frame_user_data frame_user_data;
+ void *user_data;
+ uint8_t allowed_rsv_bits;
+};
+
+#endif /* WSLAY_EVENT_H */
diff --git a/thirdparty/wslay/wslay_frame.c b/thirdparty/wslay/wslay_frame.c
new file mode 100644
index 0000000000..445e750ca5
--- /dev/null
+++ b/thirdparty/wslay/wslay_frame.c
@@ -0,0 +1,340 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "wslay_frame.h"
+
+#include <stddef.h>
+#include <string.h>
+#include <assert.h>
+
+#include "wslay_net.h"
+
+#define wslay_min(A, B) (((A) < (B)) ? (A) : (B))
+
+int wslay_frame_context_init(wslay_frame_context_ptr *ctx,
+ const struct wslay_frame_callbacks *callbacks,
+ void *user_data)
+{
+ *ctx = (wslay_frame_context_ptr)malloc(sizeof(struct wslay_frame_context));
+ if(*ctx == NULL) {
+ return -1;
+ }
+ memset(*ctx, 0, sizeof(struct wslay_frame_context));
+ (*ctx)->istate = RECV_HEADER1;
+ (*ctx)->ireqread = 2;
+ (*ctx)->ostate = PREP_HEADER;
+ (*ctx)->user_data = user_data;
+ (*ctx)->ibufmark = (*ctx)->ibuflimit = (*ctx)->ibuf;
+ (*ctx)->callbacks = *callbacks;
+ return 0;
+}
+
+void wslay_frame_context_free(wslay_frame_context_ptr ctx)
+{
+ free(ctx);
+}
+
+ssize_t wslay_frame_send(wslay_frame_context_ptr ctx,
+ struct wslay_frame_iocb *iocb)
+{
+ if(iocb->data_length > iocb->payload_length) {
+ return WSLAY_ERR_INVALID_ARGUMENT;
+ }
+ if(ctx->ostate == PREP_HEADER) {
+ uint8_t *hdptr = ctx->oheader;
+ memset(ctx->oheader, 0, sizeof(ctx->oheader));
+ *hdptr |= (iocb->fin << 7) & 0x80u;
+ *hdptr |= (iocb->rsv << 4) & 0x70u;
+ *hdptr |= iocb->opcode & 0xfu;
+ ++hdptr;
+ *hdptr |= (iocb->mask << 7) & 0x80u;
+ if(wslay_is_ctrl_frame(iocb->opcode) && iocb->payload_length > 125) {
+ return WSLAY_ERR_INVALID_ARGUMENT;
+ }
+ if(iocb->payload_length < 126) {
+ *hdptr |= iocb->payload_length;
+ ++hdptr;
+ } else if(iocb->payload_length < (1 << 16)) {
+ uint16_t len = htons(iocb->payload_length);
+ *hdptr |= 126;
+ ++hdptr;
+ memcpy(hdptr, &len, 2);
+ hdptr += 2;
+ } else if(iocb->payload_length < (1ull << 63)) {
+ uint64_t len = hton64(iocb->payload_length);
+ *hdptr |= 127;
+ ++hdptr;
+ memcpy(hdptr, &len, 8);
+ hdptr += 8;
+ } else {
+ /* Too large payload length */
+ return WSLAY_ERR_INVALID_ARGUMENT;
+ }
+ if(iocb->mask) {
+ if(ctx->callbacks.genmask_callback(ctx->omaskkey, 4,
+ ctx->user_data) != 0) {
+ return WSLAY_ERR_INVALID_CALLBACK;
+ } else {
+ ctx->omask = 1;
+ memcpy(hdptr, ctx->omaskkey, 4);
+ hdptr += 4;
+ }
+ }
+ ctx->ostate = SEND_HEADER;
+ ctx->oheadermark = ctx->oheader;
+ ctx->oheaderlimit = hdptr;
+ ctx->opayloadlen = iocb->payload_length;
+ ctx->opayloadoff = 0;
+ }
+ if(ctx->ostate == SEND_HEADER) {
+ ptrdiff_t len = ctx->oheaderlimit-ctx->oheadermark;
+ ssize_t r;
+ int flags = 0;
+ if(iocb->data_length > 0) {
+ flags |= WSLAY_MSG_MORE;
+ };
+ r = ctx->callbacks.send_callback(ctx->oheadermark, len, flags,
+ ctx->user_data);
+ if(r > 0) {
+ if(r > len) {
+ return WSLAY_ERR_INVALID_CALLBACK;
+ } else {
+ ctx->oheadermark += r;
+ if(ctx->oheadermark == ctx->oheaderlimit) {
+ ctx->ostate = SEND_PAYLOAD;
+ } else {
+ return WSLAY_ERR_WANT_WRITE;
+ }
+ }
+ } else {
+ return WSLAY_ERR_WANT_WRITE;
+ }
+ }
+ if(ctx->ostate == SEND_PAYLOAD) {
+ size_t totallen = 0;
+ if(iocb->data_length > 0) {
+ if(ctx->omask) {
+ uint8_t temp[4096];
+ const uint8_t *datamark = iocb->data,
+ *datalimit = iocb->data+iocb->data_length;
+ while(datamark < datalimit) {
+ size_t datalen = datalimit - datamark;
+ const uint8_t *writelimit = datamark+
+ wslay_min(sizeof(temp), datalen);
+ size_t writelen = writelimit-datamark;
+ ssize_t r;
+ size_t i;
+ for(i = 0; i < writelen; ++i) {
+ temp[i] = datamark[i]^ctx->omaskkey[(ctx->opayloadoff+i)%4];
+ }
+ r = ctx->callbacks.send_callback(temp, writelen, 0, ctx->user_data);
+ if(r > 0) {
+ if((size_t)r > writelen) {
+ return WSLAY_ERR_INVALID_CALLBACK;
+ } else {
+ datamark += r;
+ ctx->opayloadoff += r;
+ totallen += r;
+ }
+ } else {
+ if(totallen > 0) {
+ break;
+ } else {
+ return WSLAY_ERR_WANT_WRITE;
+ }
+ }
+ }
+ } else {
+ ssize_t r;
+ r = ctx->callbacks.send_callback(iocb->data, iocb->data_length, 0,
+ ctx->user_data);
+ if(r > 0) {
+ if((size_t)r > iocb->data_length) {
+ return WSLAY_ERR_INVALID_CALLBACK;
+ } else {
+ ctx->opayloadoff += r;
+ totallen = r;
+ }
+ } else {
+ return WSLAY_ERR_WANT_WRITE;
+ }
+ }
+ }
+ if(ctx->opayloadoff == ctx->opayloadlen) {
+ ctx->ostate = PREP_HEADER;
+ }
+ return totallen;
+ }
+ return WSLAY_ERR_INVALID_ARGUMENT;
+}
+
+static void wslay_shift_ibuf(wslay_frame_context_ptr ctx)
+{
+ ptrdiff_t len = ctx->ibuflimit-ctx->ibufmark;
+ memmove(ctx->ibuf, ctx->ibufmark, len);
+ ctx->ibuflimit = ctx->ibuf+len;
+ ctx->ibufmark = ctx->ibuf;
+}
+
+static ssize_t wslay_recv(wslay_frame_context_ptr ctx)
+{
+ ssize_t r;
+ if(ctx->ibufmark != ctx->ibuf) {
+ wslay_shift_ibuf(ctx);
+ }
+ r = ctx->callbacks.recv_callback
+ (ctx->ibuflimit, ctx->ibuf+sizeof(ctx->ibuf)-ctx->ibuflimit,
+ 0, ctx->user_data);
+ if(r > 0) {
+ ctx->ibuflimit += r;
+ } else {
+ r = WSLAY_ERR_WANT_READ;
+ }
+ return r;
+}
+
+#define WSLAY_AVAIL_IBUF(ctx) ((size_t)(ctx->ibuflimit - ctx->ibufmark))
+
+ssize_t wslay_frame_recv(wslay_frame_context_ptr ctx,
+ struct wslay_frame_iocb *iocb)
+{
+ ssize_t r;
+ if(ctx->istate == RECV_HEADER1) {
+ uint8_t fin, opcode, rsv, payloadlen;
+ if(WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) {
+ if((r = wslay_recv(ctx)) <= 0) {
+ return r;
+ }
+ }
+ if(WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) {
+ return WSLAY_ERR_WANT_READ;
+ }
+ fin = (ctx->ibufmark[0] >> 7) & 1;
+ rsv = (ctx->ibufmark[0] >> 4) & 7;
+ opcode = ctx->ibufmark[0] & 0xfu;
+ ctx->iom.opcode = opcode;
+ ctx->iom.fin = fin;
+ ctx->iom.rsv = rsv;
+ ++ctx->ibufmark;
+ ctx->imask = (ctx->ibufmark[0] >> 7) & 1;
+ payloadlen = ctx->ibufmark[0] & 0x7fu;
+ ++ctx->ibufmark;
+ if(wslay_is_ctrl_frame(opcode) && (payloadlen > 125 || !fin)) {
+ return WSLAY_ERR_PROTO;
+ }
+ if(payloadlen == 126) {
+ ctx->istate = RECV_EXT_PAYLOADLEN;
+ ctx->ireqread = 2;
+ } else if(payloadlen == 127) {
+ ctx->istate = RECV_EXT_PAYLOADLEN;
+ ctx->ireqread = 8;
+ } else {
+ ctx->ipayloadlen = payloadlen;
+ ctx->ipayloadoff = 0;
+ if(ctx->imask) {
+ ctx->istate = RECV_MASKKEY;
+ ctx->ireqread = 4;
+ } else {
+ ctx->istate = RECV_PAYLOAD;
+ }
+ }
+ }
+ if(ctx->istate == RECV_EXT_PAYLOADLEN) {
+ if(WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) {
+ if((r = wslay_recv(ctx)) <= 0) {
+ return r;
+ }
+ if(WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) {
+ return WSLAY_ERR_WANT_READ;
+ }
+ }
+ ctx->ipayloadlen = 0;
+ ctx->ipayloadoff = 0;
+ memcpy((uint8_t*)&ctx->ipayloadlen+(8-ctx->ireqread),
+ ctx->ibufmark, ctx->ireqread);
+ ctx->ipayloadlen = ntoh64(ctx->ipayloadlen);
+ ctx->ibufmark += ctx->ireqread;
+ if(ctx->ireqread == 8) {
+ if(ctx->ipayloadlen < (1 << 16) ||
+ ctx->ipayloadlen & (1ull << 63)) {
+ return WSLAY_ERR_PROTO;
+ }
+ } else if(ctx->ipayloadlen < 126) {
+ return WSLAY_ERR_PROTO;
+ }
+ if(ctx->imask) {
+ ctx->istate = RECV_MASKKEY;
+ ctx->ireqread = 4;
+ } else {
+ ctx->istate = RECV_PAYLOAD;
+ }
+ }
+ if(ctx->istate == RECV_MASKKEY) {
+ if(WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) {
+ if((r = wslay_recv(ctx)) <= 0) {
+ return r;
+ }
+ if(WSLAY_AVAIL_IBUF(ctx) < ctx->ireqread) {
+ return WSLAY_ERR_WANT_READ;
+ }
+ }
+ memcpy(ctx->imaskkey, ctx->ibufmark, 4);
+ ctx->ibufmark += 4;
+ ctx->istate = RECV_PAYLOAD;
+ }
+ if(ctx->istate == RECV_PAYLOAD) {
+ uint8_t *readlimit, *readmark;
+ uint64_t rempayloadlen = ctx->ipayloadlen-ctx->ipayloadoff;
+ if(WSLAY_AVAIL_IBUF(ctx) == 0 && rempayloadlen > 0) {
+ if((r = wslay_recv(ctx)) <= 0) {
+ return r;
+ }
+ }
+ readmark = ctx->ibufmark;
+ readlimit = WSLAY_AVAIL_IBUF(ctx) < rempayloadlen ?
+ ctx->ibuflimit : ctx->ibufmark+rempayloadlen;
+ if(ctx->imask) {
+ for(; ctx->ibufmark != readlimit;
+ ++ctx->ibufmark, ++ctx->ipayloadoff) {
+ ctx->ibufmark[0] ^= ctx->imaskkey[ctx->ipayloadoff % 4];
+ }
+ } else {
+ ctx->ibufmark = readlimit;
+ ctx->ipayloadoff += readlimit-readmark;
+ }
+ iocb->fin = ctx->iom.fin;
+ iocb->rsv = ctx->iom.rsv;
+ iocb->opcode = ctx->iom.opcode;
+ iocb->payload_length = ctx->ipayloadlen;
+ iocb->mask = ctx->imask;
+ iocb->data = readmark;
+ iocb->data_length = ctx->ibufmark-readmark;
+ if(ctx->ipayloadlen == ctx->ipayloadoff) {
+ ctx->istate = RECV_HEADER1;
+ ctx->ireqread = 2;
+ }
+ return iocb->data_length;
+ }
+ return WSLAY_ERR_INVALID_ARGUMENT;
+}
diff --git a/thirdparty/wslay/wslay_frame.h b/thirdparty/wslay/wslay_frame.h
new file mode 100644
index 0000000000..6a75858cc7
--- /dev/null
+++ b/thirdparty/wslay/wslay_frame.h
@@ -0,0 +1,76 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef WSLAY_FRAME_H
+#define WSLAY_FRAME_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <wslay/wslay.h>
+
+enum wslay_frame_state {
+ PREP_HEADER,
+ SEND_HEADER,
+ SEND_PAYLOAD,
+ RECV_HEADER1,
+ RECV_PAYLOADLEN,
+ RECV_EXT_PAYLOADLEN,
+ RECV_MASKKEY,
+ RECV_PAYLOAD
+};
+
+struct wslay_frame_opcode_memo {
+ uint8_t fin;
+ uint8_t opcode;
+ uint8_t rsv;
+};
+
+struct wslay_frame_context {
+ uint8_t ibuf[4096];
+ uint8_t *ibufmark;
+ uint8_t *ibuflimit;
+ struct wslay_frame_opcode_memo iom;
+ uint64_t ipayloadlen;
+ uint64_t ipayloadoff;
+ uint8_t imask;
+ uint8_t imaskkey[4];
+ enum wslay_frame_state istate;
+ size_t ireqread;
+
+ uint8_t oheader[14];
+ uint8_t *oheadermark;
+ uint8_t *oheaderlimit;
+ uint64_t opayloadlen;
+ uint64_t opayloadoff;
+ uint8_t omask;
+ uint8_t omaskkey[4];
+ enum wslay_frame_state ostate;
+
+ struct wslay_frame_callbacks callbacks;
+ void *user_data;
+};
+
+#endif /* WSLAY_FRAME_H */
diff --git a/thirdparty/wslay/wslay_net.c b/thirdparty/wslay/wslay_net.c
new file mode 100644
index 0000000000..d3867c21fb
--- /dev/null
+++ b/thirdparty/wslay/wslay_net.c
@@ -0,0 +1,36 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "wslay_net.h"
+
+#ifndef WORDS_BIGENDIAN
+
+uint64_t wslay_byteswap64(uint64_t x)
+{
+ uint64_t u = ntohl(x & 0xffffffffllu);
+ uint64_t l = ntohl(x >> 32);
+ return (u << 32) | l;
+}
+
+#endif /* !WORDS_BIGENDIAN */
diff --git a/thirdparty/wslay/wslay_net.h b/thirdparty/wslay/wslay_net.h
new file mode 100644
index 0000000000..2310870156
--- /dev/null
+++ b/thirdparty/wslay/wslay_net.h
@@ -0,0 +1,54 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef WSLAY_NET_H
+#define WSLAY_NET_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif /* HAVE_CONFIG_H */
+
+#include <wslay/wslay.h>
+
+#ifdef HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif /* HAVE_ARPA_INET_H */
+#ifdef HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#endif /* HAVE_NETINET_IN_H */
+/* For Mingw build */
+#ifdef HAVE_WINSOCK2_H
+# include <winsock2.h>
+#endif /* HAVE_WINSOCK2_H */
+
+#ifdef WORDS_BIGENDIAN
+# define ntoh64(x) (x)
+# define hton64(x) (x)
+#else /* !WORDS_BIGENDIAN */
+uint64_t wslay_byteswap64(uint64_t x);
+# define ntoh64(x) wslay_byteswap64(x)
+# define hton64(x) wslay_byteswap64(x)
+#endif /* !WORDS_BIGENDIAN */
+
+#endif /* WSLAY_NET_H */
diff --git a/thirdparty/wslay/wslay_queue.c b/thirdparty/wslay/wslay_queue.c
new file mode 100644
index 0000000000..8d2669687d
--- /dev/null
+++ b/thirdparty/wslay/wslay_queue.c
@@ -0,0 +1,117 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "wslay_queue.h"
+
+#include <string.h>
+#include <assert.h>
+
+struct wslay_queue* wslay_queue_new(void)
+{
+ struct wslay_queue *queue = (struct wslay_queue*)malloc
+ (sizeof(struct wslay_queue));
+ if(!queue) {
+ return NULL;
+ }
+ queue->top = queue->tail = NULL;
+ return queue;
+}
+
+void wslay_queue_free(struct wslay_queue *queue)
+{
+ if(!queue) {
+ return;
+ } else {
+ struct wslay_queue_cell *p = queue->top;
+ while(p) {
+ struct wslay_queue_cell *next = p->next;
+ free(p);
+ p = next;
+ }
+ free(queue);
+ }
+}
+
+int wslay_queue_push(struct wslay_queue *queue, void *data)
+{
+ struct wslay_queue_cell *new_cell = (struct wslay_queue_cell*)malloc
+ (sizeof(struct wslay_queue_cell));
+ if(!new_cell) {
+ return WSLAY_ERR_NOMEM;
+ }
+ new_cell->data = data;
+ new_cell->next = NULL;
+ if(queue->tail) {
+ queue->tail->next = new_cell;
+ queue->tail = new_cell;
+
+ } else {
+ queue->top = queue->tail = new_cell;
+ }
+ return 0;
+}
+
+int wslay_queue_push_front(struct wslay_queue *queue, void *data)
+{
+ struct wslay_queue_cell *new_cell = (struct wslay_queue_cell*)malloc
+ (sizeof(struct wslay_queue_cell));
+ if(!new_cell) {
+ return WSLAY_ERR_NOMEM;
+ }
+ new_cell->data = data;
+ new_cell->next = queue->top;
+ queue->top = new_cell;
+ if(!queue->tail) {
+ queue->tail = queue->top;
+ }
+ return 0;
+}
+
+void wslay_queue_pop(struct wslay_queue *queue)
+{
+ struct wslay_queue_cell *top = queue->top;
+ assert(top);
+ queue->top = top->next;
+ if(top == queue->tail) {
+ queue->tail = NULL;
+ }
+ free(top);
+}
+
+void* wslay_queue_top(struct wslay_queue *queue)
+{
+ assert(queue->top);
+ return queue->top->data;
+}
+
+void* wslay_queue_tail(struct wslay_queue *queue)
+{
+ assert(queue->tail);
+ return queue->tail->data;
+}
+
+int wslay_queue_empty(struct wslay_queue *queue)
+{
+ return queue->top == NULL;
+}
diff --git a/thirdparty/wslay/wslay_queue.h b/thirdparty/wslay/wslay_queue.h
new file mode 100644
index 0000000000..55e78a042e
--- /dev/null
+++ b/thirdparty/wslay/wslay_queue.h
@@ -0,0 +1,53 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef WSLAY_QUEUE_H
+#define WSLAY_QUEUE_H
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <wslay/wslay.h>
+
+struct wslay_queue_cell {
+ void *data;
+ struct wslay_queue_cell *next;
+};
+
+struct wslay_queue {
+ struct wslay_queue_cell *top;
+ struct wslay_queue_cell *tail;
+};
+
+struct wslay_queue* wslay_queue_new(void);
+void wslay_queue_free(struct wslay_queue *queue);
+int wslay_queue_push(struct wslay_queue *queue, void *data);
+int wslay_queue_push_front(struct wslay_queue *queue, void *data);
+void wslay_queue_pop(struct wslay_queue *queue);
+void* wslay_queue_top(struct wslay_queue *queue);
+void* wslay_queue_tail(struct wslay_queue *queue);
+int wslay_queue_empty(struct wslay_queue *queue);
+
+#endif /* WSLAY_QUEUE_H */
diff --git a/thirdparty/wslay/wslay_stack.c b/thirdparty/wslay/wslay_stack.c
new file mode 100644
index 0000000000..0e05d74031
--- /dev/null
+++ b/thirdparty/wslay/wslay_stack.c
@@ -0,0 +1,86 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#include "wslay_stack.h"
+
+#include <string.h>
+#include <assert.h>
+
+struct wslay_stack* wslay_stack_new()
+{
+ struct wslay_stack *stack = (struct wslay_stack*)malloc
+ (sizeof(struct wslay_stack));
+ if(!stack) {
+ return NULL;
+ }
+ stack->top = NULL;
+ return stack;
+}
+
+void wslay_stack_free(struct wslay_stack *stack)
+{
+ struct wslay_stack_cell *p;
+ if(!stack) {
+ return;
+ }
+ p = stack->top;
+ while(p) {
+ struct wslay_stack_cell *next = p->next;
+ free(p);
+ p = next;
+ }
+ free(stack);
+}
+
+int wslay_stack_push(struct wslay_stack *stack, void *data)
+{
+ struct wslay_stack_cell *new_cell = (struct wslay_stack_cell*)malloc
+ (sizeof(struct wslay_stack_cell));
+ if(!new_cell) {
+ return WSLAY_ERR_NOMEM;
+ }
+ new_cell->data = data;
+ new_cell->next = stack->top;
+ stack->top = new_cell;
+ return 0;
+}
+
+void wslay_stack_pop(struct wslay_stack *stack)
+{
+ struct wslay_stack_cell *top = stack->top;
+ assert(top);
+ stack->top = top->next;
+ free(top);
+}
+
+void* wslay_stack_top(struct wslay_stack *stack)
+{
+ assert(stack->top);
+ return stack->top->data;
+}
+
+int wslay_stack_empty(struct wslay_stack *stack)
+{
+ return stack->top == NULL;
+}
diff --git a/thirdparty/wslay/wslay_stack.h b/thirdparty/wslay/wslay_stack.h
new file mode 100644
index 0000000000..16e4e968eb
--- /dev/null
+++ b/thirdparty/wslay/wslay_stack.h
@@ -0,0 +1,50 @@
+/*
+ * Wslay - The WebSocket Library
+ *
+ * Copyright (c) 2011, 2012 Tatsuhiro Tsujikawa
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+#ifndef WSLAY_STACK_H
+#define WSLAY_STACK_H
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <wslay/wslay.h>
+
+struct wslay_stack_cell {
+ void *data;
+ struct wslay_stack_cell *next;
+};
+
+struct wslay_stack {
+ struct wslay_stack_cell *top;
+};
+
+struct wslay_stack* wslay_stack_new();
+void wslay_stack_free(struct wslay_stack *stack);
+int wslay_stack_push(struct wslay_stack *stack, void *data);
+void wslay_stack_pop(struct wslay_stack *stack);
+void* wslay_stack_top(struct wslay_stack *stack);
+int wslay_stack_empty(struct wslay_stack *stack);
+
+#endif /* WSLAY_STACK_H */