mirror of
https://github.com/defold/extension-websocket.git
synced 2025-06-27 09:47:44 +02:00
Websocket now supports html5 as well
This commit is contained in:
parent
1e6c534609
commit
5de32250c3
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,5 +1,6 @@
|
|||||||
/.internal
|
/.internal
|
||||||
/build
|
/build
|
||||||
|
/bundle*
|
||||||
.externalToolBuilders
|
.externalToolBuilders
|
||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
@ -1,15 +1,9 @@
|
|||||||
local URL="://echo.websocket.org"
|
local URL="://echo.websocket.org"
|
||||||
|
|
||||||
local function click_button(node, action)
|
local function click_button(node, action)
|
||||||
--print("mouse", action.x, action.y)
|
|
||||||
|
|
||||||
return gui.is_enabled(node) and action.pressed and gui.pick_node(node, action.x, action.y)
|
return gui.is_enabled(node) and action.pressed and gui.pick_node(node, action.x, action.y)
|
||||||
end
|
end
|
||||||
|
|
||||||
local function http_handle_response(self, id, response)
|
|
||||||
print(response.status, response.response)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function update_gui(self)
|
local function update_gui(self)
|
||||||
if self.connection then
|
if self.connection then
|
||||||
gui.set_enabled(self.connect_ws_node, false)
|
gui.set_enabled(self.connect_ws_node, false)
|
||||||
@ -47,11 +41,8 @@ function init(self)
|
|||||||
self.send_node = gui.get_node("send/button")
|
self.send_node = gui.get_node("send/button")
|
||||||
self.close_node = gui.get_node("close/button")
|
self.close_node = gui.get_node("close/button")
|
||||||
self.connection_text = gui.get_node("connection_text")
|
self.connection_text = gui.get_node("connection_text")
|
||||||
update_gui(self)
|
|
||||||
|
|
||||||
--http.request("https://www.google.com", "GET", http_handle_response)
|
|
||||||
|
|
||||||
self.connection = nil
|
self.connection = nil
|
||||||
|
update_gui(self)
|
||||||
end
|
end
|
||||||
|
|
||||||
function final(self)
|
function final(self)
|
||||||
@ -78,19 +69,17 @@ local function websocket_callback(self, conn, data)
|
|||||||
self.connection = nil
|
self.connection = nil
|
||||||
end
|
end
|
||||||
update_gui(self)
|
update_gui(self)
|
||||||
|
elseif data.event == websocket.EVENT_ERROR then
|
||||||
|
if data.error then
|
||||||
|
log("Error:", data.error)
|
||||||
|
end
|
||||||
elseif data.event == websocket.EVENT_MESSAGE then
|
elseif data.event == websocket.EVENT_MESSAGE then
|
||||||
log("Receiving: '" .. tostring(data.message) .. "'")
|
log("Receiving: '" .. tostring(data.message) .. "'")
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function connect(self, scheme)
|
local function connect(self, scheme)
|
||||||
local params = {
|
local params = {}
|
||||||
mode = "client" --only supported mode currently
|
|
||||||
--mode = "client",
|
|
||||||
--protocol = "tlsv1_2",
|
|
||||||
--verify = "none",
|
|
||||||
--options = "all",
|
|
||||||
}
|
|
||||||
|
|
||||||
self.url = scheme .. URL
|
self.url = scheme .. URL
|
||||||
log("Connecting to " .. self.url)
|
log("Connecting to " .. self.url)
|
||||||
@ -109,7 +98,6 @@ end
|
|||||||
|
|
||||||
|
|
||||||
function on_input(self, action_id, action)
|
function on_input(self, action_id, action)
|
||||||
--print("MAWE", action_id)
|
|
||||||
if click_button(self.connect_ws_node, action) then
|
if click_button(self.connect_ws_node, action) then
|
||||||
connect(self, "ws")
|
connect(self, "ws")
|
||||||
elseif click_button(self.connect_wss_node, action) then
|
elseif click_button(self.connect_wss_node, action) then
|
||||||
|
@ -16,13 +16,13 @@ static void CreateKey(uint8_t* key, size_t len)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#define WS_SENDALL(s) \
|
#define WS_SENDALL(s) \
|
||||||
sock_res = Send(conn, s, strlen(s), 0);\
|
sr = Send(conn, s, strlen(s), 0);\
|
||||||
if (sock_res != dmSocket::RESULT_OK)\
|
if (sr != dmSocket::RESULT_OK)\
|
||||||
{\
|
{\
|
||||||
goto bail;\
|
goto bail;\
|
||||||
}\
|
}\
|
||||||
|
|
||||||
Result SendClientHandshake(WebsocketConnection* conn)
|
static Result SendClientHandshakeHeaders(WebsocketConnection* conn)
|
||||||
{
|
{
|
||||||
CreateKey(conn->m_Key, sizeof(conn->m_Key));
|
CreateKey(conn->m_Key, sizeof(conn->m_Key));
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ Result SendClientHandshake(WebsocketConnection* conn)
|
|||||||
if (!(conn->m_Url.m_Port == 80 || conn->m_Url.m_Port == 443))
|
if (!(conn->m_Url.m_Port == 80 || conn->m_Url.m_Port == 443))
|
||||||
dmSnPrintf(port, sizeof(port), ":%d", conn->m_Url.m_Port);
|
dmSnPrintf(port, sizeof(port), ":%d", conn->m_Url.m_Port);
|
||||||
|
|
||||||
dmSocket::Result sock_res = dmSocket::RESULT_OK;
|
dmSocket::Result sr;
|
||||||
WS_SENDALL("GET /");
|
WS_SENDALL("GET /");
|
||||||
WS_SENDALL(conn->m_Url.m_Path);
|
WS_SENDALL(conn->m_Url.m_Path);
|
||||||
WS_SENDALL(" HTTP/1.1\r\n");
|
WS_SENDALL(" HTTP/1.1\r\n");
|
||||||
@ -60,9 +60,9 @@ Result SendClientHandshake(WebsocketConnection* conn)
|
|||||||
WS_SENDALL("\r\n");
|
WS_SENDALL("\r\n");
|
||||||
|
|
||||||
bail:
|
bail:
|
||||||
if (sock_res != dmSocket::RESULT_OK)
|
if (sr != dmSocket::RESULT_OK)
|
||||||
{
|
{
|
||||||
return SetStatus(conn, RESULT_HANDSHAKE_FAILED, "SendClientHandshake failed: %s", dmSocket::ResultToString(sock_res));
|
return SetStatus(conn, RESULT_HANDSHAKE_FAILED, "SendClientHandshake failed: %s", dmSocket::ResultToString(sr));
|
||||||
}
|
}
|
||||||
|
|
||||||
return RESULT_OK;
|
return RESULT_OK;
|
||||||
@ -70,68 +70,114 @@ bail:
|
|||||||
|
|
||||||
#undef WS_SENDALL
|
#undef WS_SENDALL
|
||||||
|
|
||||||
|
Result SendClientHandshake(WebsocketConnection* conn)
|
||||||
// Currently blocking!
|
|
||||||
Result ReceiveHeaders(WebsocketConnection* conn)
|
|
||||||
{
|
{
|
||||||
while (1)
|
dmSocket::Result sr = WaitForSocket(conn, dmSocket::SELECTOR_KIND_WRITE, SOCKET_WAIT_TIMEOUT);
|
||||||
|
if (dmSocket::RESULT_WOULDBLOCK == sr)
|
||||||
{
|
{
|
||||||
int max_to_recv = (int)(conn->m_BufferCapacity - 1) - conn->m_BufferSize; // allow for a terminating null character
|
return RESULT_WOULDBLOCK;
|
||||||
|
|
||||||
if (max_to_recv <= 0)
|
|
||||||
{
|
|
||||||
return SetStatus(conn, RESULT_HANDSHAKE_FAILED, "Receive buffer full: %u bytes", conn->m_BufferCapacity);
|
|
||||||
}
|
|
||||||
|
|
||||||
int recv_bytes = 0;
|
|
||||||
dmSocket::Result r = Receive(conn, conn->m_Buffer + conn->m_BufferSize, max_to_recv, &recv_bytes);
|
|
||||||
|
|
||||||
if( r == dmSocket::RESULT_WOULDBLOCK )
|
|
||||||
{
|
|
||||||
r = dmSocket::RESULT_TRY_AGAIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (r == dmSocket::RESULT_TRY_AGAIN)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (r != dmSocket::RESULT_OK)
|
|
||||||
{
|
|
||||||
return SetStatus(conn, RESULT_HANDSHAKE_FAILED, "Receive error: %s", dmSocket::ResultToString(r));
|
|
||||||
}
|
|
||||||
|
|
||||||
conn->m_BufferSize += recv_bytes;
|
|
||||||
|
|
||||||
// NOTE: We have an extra byte for null-termination so no buffer overrun here.
|
|
||||||
conn->m_Buffer[conn->m_BufferSize] = '\0';
|
|
||||||
|
|
||||||
// Check if the end of the response has arrived
|
|
||||||
if (conn->m_BufferSize >= 4 && strcmp(conn->m_Buffer + conn->m_BufferSize - 4, "\r\n\r\n") == 0)
|
|
||||||
{
|
|
||||||
return RESULT_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (r == 0)
|
|
||||||
{
|
|
||||||
return SetStatus(conn, RESULT_HANDSHAKE_FAILED, "Failed to parse headers:\n%s", conn->m_Buffer);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (dmSocket::RESULT_OK != sr)
|
||||||
|
{
|
||||||
|
return SetStatus(conn, RESULT_HANDSHAKE_FAILED, "Connection not ready for sending data: %s", dmSocket::ResultToString(sr));
|
||||||
|
}
|
||||||
|
|
||||||
|
// In emscripten, the sockets are actually already websockets, so no handshake necessary
|
||||||
|
#if defined(__EMSCRIPTEN__)
|
||||||
|
return RESULT_OK;
|
||||||
|
#else
|
||||||
|
return SendClientHandshakeHeaders(conn);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#if defined(__EMSCRIPTEN__)
|
||||||
|
Result ReceiveHeaders(WebsocketConnection* conn)
|
||||||
|
{
|
||||||
|
return RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
Result ReceiveHeaders(WebsocketConnection* conn)
|
||||||
|
{
|
||||||
|
dmSocket::Selector selector;
|
||||||
|
dmSocket::SelectorZero(&selector);
|
||||||
|
dmSocket::SelectorSet(&selector, dmSocket::SELECTOR_KIND_READ, conn->m_Socket);
|
||||||
|
|
||||||
|
dmSocket::Result sr = dmSocket::Select(&selector, 200*1000);
|
||||||
|
|
||||||
|
if (dmSocket::RESULT_OK != sr)
|
||||||
|
{
|
||||||
|
if (dmSocket::RESULT_WOULDBLOCK)
|
||||||
|
{
|
||||||
|
dmLogWarning("Waiting for socket to be available for reading");
|
||||||
|
return RESULT_WOULDBLOCK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SetStatus(conn, RESULT_HANDSHAKE_FAILED, "Failed waiting for more handshake headers: %s", dmSocket::ResultToString(sr));
|
||||||
|
}
|
||||||
|
|
||||||
|
int max_to_recv = (int)(conn->m_BufferCapacity - 1) - conn->m_BufferSize; // allow for a terminating null character
|
||||||
|
|
||||||
|
if (max_to_recv <= 0)
|
||||||
|
{
|
||||||
|
return SetStatus(conn, RESULT_HANDSHAKE_FAILED, "Receive buffer full: %u bytes", conn->m_BufferCapacity);
|
||||||
|
}
|
||||||
|
|
||||||
|
int recv_bytes = 0;
|
||||||
|
sr = Receive(conn, conn->m_Buffer + conn->m_BufferSize, max_to_recv, &recv_bytes);
|
||||||
|
|
||||||
|
if( sr == dmSocket::RESULT_WOULDBLOCK )
|
||||||
|
{
|
||||||
|
sr = dmSocket::RESULT_TRY_AGAIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sr == dmSocket::RESULT_TRY_AGAIN)
|
||||||
|
return RESULT_WOULDBLOCK;
|
||||||
|
|
||||||
|
if (sr != dmSocket::RESULT_OK)
|
||||||
|
{
|
||||||
|
return SetStatus(conn, RESULT_HANDSHAKE_FAILED, "Receive error: %s", dmSocket::ResultToString(sr));
|
||||||
|
}
|
||||||
|
|
||||||
|
conn->m_BufferSize += recv_bytes;
|
||||||
|
|
||||||
|
// NOTE: We have an extra byte for null-termination so no buffer overrun here.
|
||||||
|
conn->m_Buffer[conn->m_BufferSize] = '\0';
|
||||||
|
|
||||||
|
// Check if the end of the response has arrived
|
||||||
|
if (conn->m_BufferSize >= 4 && strcmp(conn->m_Buffer + conn->m_BufferSize - 4, "\r\n\r\n") == 0)
|
||||||
|
{
|
||||||
|
return RESULT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return RESULT_WOULDBLOCK;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(__EMSCRIPTEN__)
|
||||||
|
Result VerifyHeaders(WebsocketConnection* conn)
|
||||||
|
{
|
||||||
|
return RESULT_OK;
|
||||||
|
}
|
||||||
|
#else
|
||||||
Result VerifyHeaders(WebsocketConnection* conn)
|
Result VerifyHeaders(WebsocketConnection* conn)
|
||||||
{
|
{
|
||||||
char* r = conn->m_Buffer;
|
char* r = conn->m_Buffer;
|
||||||
|
|
||||||
const char* http_version_and_status_protocol = "HTTP/1.1 101"; // optionally "Web Socket Protocol Handshake"
|
// According to protocol, the response should start with "HTTP/1.1 <statuscode> <message>"
|
||||||
|
const char* http_version_and_status_protocol = "HTTP/1.1 101";
|
||||||
if (strstr(r, http_version_and_status_protocol) != r) {
|
if (strstr(r, http_version_and_status_protocol) != r) {
|
||||||
return SetStatus(conn, RESULT_HANDSHAKE_FAILED, "Missing: '%s' in header", http_version_and_status_protocol);
|
return SetStatus(conn, RESULT_HANDSHAKE_FAILED, "Missing: '%s' in header", http_version_and_status_protocol);
|
||||||
}
|
}
|
||||||
|
|
||||||
r = strstr(r, "\r\n") + 2;
|
r = strstr(r, "\r\n") + 2;
|
||||||
|
|
||||||
bool upgraded = false;
|
bool upgraded = false;
|
||||||
bool valid_key = false;
|
bool valid_key = false;
|
||||||
const char* protocol = "";
|
const char* protocol = "";
|
||||||
|
|
||||||
// Sec-WebSocket-Protocol
|
// TODO: Perhaps also support the Sec-WebSocket-Protocol
|
||||||
|
|
||||||
// parse the headers in place
|
// parse the headers in place
|
||||||
while (r)
|
while (r)
|
||||||
@ -155,7 +201,6 @@ Result VerifyHeaders(WebsocketConnection* conn)
|
|||||||
|
|
||||||
uint8_t client_key[32 + 40];
|
uint8_t client_key[32 + 40];
|
||||||
uint32_t client_key_len = sizeof(client_key);
|
uint32_t client_key_len = sizeof(client_key);
|
||||||
//mbedtls_base64_encode((unsigned char*)client_key, sizeof(client_key), &client_key_len, (const unsigned char*)conn->m_Key, sizeof(conn->m_Key));
|
|
||||||
dmCrypt::Base64Encode(conn->m_Key, sizeof(conn->m_Key), client_key, &client_key_len);
|
dmCrypt::Base64Encode(conn->m_Key, sizeof(conn->m_Key), client_key, &client_key_len);
|
||||||
client_key[client_key_len] = 0;
|
client_key[client_key_len] = 0;
|
||||||
|
|
||||||
@ -166,7 +211,6 @@ Result VerifyHeaders(WebsocketConnection* conn)
|
|||||||
uint8_t client_key_sha1[20];
|
uint8_t client_key_sha1[20];
|
||||||
dmCrypt::HashSha1(client_key, client_key_len, client_key_sha1);
|
dmCrypt::HashSha1(client_key, client_key_len, client_key_sha1);
|
||||||
|
|
||||||
//mbedtls_base64_encode((unsigned char*)client_key, sizeof(client_key), &client_key_len, client_key_sha1, sizeof(client_key_sha1));
|
|
||||||
client_key_len = sizeof(client_key);
|
client_key_len = sizeof(client_key);
|
||||||
dmCrypt::Base64Encode(client_key_sha1, sizeof(client_key_sha1), client_key, &client_key_len);
|
dmCrypt::Base64Encode(client_key_sha1, sizeof(client_key_sha1), client_key, &client_key_len);
|
||||||
client_key[client_key_len] = 0;
|
client_key[client_key_len] = 0;
|
||||||
@ -179,7 +223,17 @@ Result VerifyHeaders(WebsocketConnection* conn)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!upgraded)
|
||||||
|
dmLogError("Failed to find the Upgrade keyword in the response headers");
|
||||||
|
if (!valid_key)
|
||||||
|
dmLogError("Failed to find valid key in the response headers");
|
||||||
|
|
||||||
|
if (!(upgraded && valid_key)) {
|
||||||
|
dmLogError("Response:\n\"%s\"\n", conn->m_Buffer);
|
||||||
|
}
|
||||||
|
|
||||||
return (upgraded && valid_key) ? RESULT_OK : RESULT_HANDSHAKE_FAILED;
|
return (upgraded && valid_key) ? RESULT_OK : RESULT_HANDSHAKE_FAILED;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
@ -5,6 +5,14 @@
|
|||||||
namespace dmWebsocket
|
namespace dmWebsocket
|
||||||
{
|
{
|
||||||
|
|
||||||
|
dmSocket::Result WaitForSocket(WebsocketConnection* conn, dmSocket::SelectorKind kind, int timeout)
|
||||||
|
{
|
||||||
|
dmSocket::Selector selector;
|
||||||
|
dmSocket::SelectorZero(&selector);
|
||||||
|
dmSocket::SelectorSet(&selector, kind, conn->m_Socket);
|
||||||
|
return dmSocket::Select(&selector, timeout);
|
||||||
|
}
|
||||||
|
|
||||||
dmSocket::Result Send(WebsocketConnection* conn, const char* buffer, int length, int* out_sent_bytes)
|
dmSocket::Result Send(WebsocketConnection* conn, const char* buffer, int length, int* out_sent_bytes)
|
||||||
{
|
{
|
||||||
int total_sent_bytes = 0;
|
int total_sent_bytes = 0;
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include "script_util.h"
|
#include "script_util.h"
|
||||||
#include <dmsdk/dlib/connection_pool.h>
|
#include <dmsdk/dlib/connection_pool.h>
|
||||||
#include <dmsdk/dlib/dns.h>
|
#include <dmsdk/dlib/dns.h>
|
||||||
|
#include <dmsdk/dlib/sslsocket.h>
|
||||||
|
|
||||||
namespace dmWebsocket {
|
namespace dmWebsocket {
|
||||||
|
|
||||||
@ -37,18 +38,44 @@ Result SetStatus(WebsocketConnection* conn, Result status, const char* format, .
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void HandleCallback(WebsocketConnection* conn, int event, const uint8_t* msg, size_t msg_len);
|
||||||
|
|
||||||
|
|
||||||
|
#define STRING_CASE(_X) case _X: return #_X;
|
||||||
|
|
||||||
|
const char* ResultToString(Result err)
|
||||||
|
{
|
||||||
|
switch(err) {
|
||||||
|
STRING_CASE(RESULT_OK);
|
||||||
|
STRING_CASE(RESULT_ERROR);
|
||||||
|
STRING_CASE(RESULT_FAIL_WSLAY_INIT);
|
||||||
|
STRING_CASE(RESULT_NOT_CONNECTED);
|
||||||
|
STRING_CASE(RESULT_HANDSHAKE_FAILED);
|
||||||
|
STRING_CASE(RESULT_WOULDBLOCK);
|
||||||
|
default: return "Unknown result";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* StateToString(State err)
|
||||||
|
{
|
||||||
|
switch(err) {
|
||||||
|
STRING_CASE(STATE_CONNECTING);
|
||||||
|
STRING_CASE(STATE_HANDSHAKE_WRITE);
|
||||||
|
STRING_CASE(STATE_HANDSHAKE_READ);
|
||||||
|
STRING_CASE(STATE_CONNECTED);
|
||||||
|
STRING_CASE(STATE_DISCONNECTED);
|
||||||
|
default: return "Unknown error";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef STRING_CASE
|
||||||
|
|
||||||
|
#define WS_DEBUG(...)
|
||||||
|
//#define WS_DEBUG dmLogWarning
|
||||||
|
|
||||||
// ***************************************************************************************************
|
// ***************************************************************************************************
|
||||||
// LUA functions
|
// LUA functions
|
||||||
|
|
||||||
const struct wslay_event_callbacks g_WslCallbacks = {
|
|
||||||
WSL_RecvCallback,
|
|
||||||
WSL_SendCallback,
|
|
||||||
WSL_GenmaskCallback,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
NULL,
|
|
||||||
WSL_OnMsgRecvCallback
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
static WebsocketConnection* CreateConnection(const char* url)
|
static WebsocketConnection* CreateConnection(const char* url)
|
||||||
@ -72,28 +99,36 @@ static WebsocketConnection* CreateConnection(const char* url)
|
|||||||
|
|
||||||
static void DestroyConnection(WebsocketConnection* conn)
|
static void DestroyConnection(WebsocketConnection* conn)
|
||||||
{
|
{
|
||||||
|
#if defined(HAVE_WSLAY)
|
||||||
if (conn->m_State == STATE_CONNECTED)
|
if (conn->m_State == STATE_CONNECTED)
|
||||||
wslay_event_context_free(conn->m_Ctx);
|
WSL_Exit(conn->m_Ctx);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (conn->m_Callback)
|
if (conn->m_Callback)
|
||||||
dmScript::DestroyCallback(conn->m_Callback);
|
dmScript::DestroyCallback(conn->m_Callback);
|
||||||
|
|
||||||
if (conn->m_Connection)
|
if (conn->m_Connection)
|
||||||
dmConnectionPool::Close(g_Websocket.m_Pool, conn->m_Connection);
|
dmConnectionPool::Return(g_Websocket.m_Pool, conn->m_Connection);
|
||||||
|
|
||||||
free((void*)conn->m_Buffer);
|
free((void*)conn->m_Buffer);
|
||||||
free((void*)conn);
|
free((void*)conn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void CloseConnection(WebsocketConnection* conn)
|
static void CloseConnection(WebsocketConnection* conn)
|
||||||
{
|
{
|
||||||
|
State prev_state = conn->m_State;
|
||||||
|
|
||||||
// we want it to send this message in the polling
|
// we want it to send this message in the polling
|
||||||
if (conn->m_State == STATE_CONNECTED) {
|
if (conn->m_State == STATE_CONNECTED) {
|
||||||
const char* reason = "Client wants to close";
|
#if defined(HAVE_WSLAY)
|
||||||
wslay_event_queue_close(conn->m_Ctx, 0, (const uint8_t*)reason, strlen(reason));
|
WSL_Close(conn->m_Ctx);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
else
|
|
||||||
conn->m_State = STATE_DISCONNECTED;
|
conn->m_State = STATE_DISCONNECTED;
|
||||||
|
|
||||||
|
WS_DEBUG("%s -> %s", StateToString(prev_state), StateToString(conn->m_State));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int FindConnection(WebsocketConnection* conn)
|
static int FindConnection(WebsocketConnection* conn)
|
||||||
@ -177,6 +212,7 @@ static int LuaSend(lua_State* L)
|
|||||||
size_t string_length = 0;
|
size_t string_length = 0;
|
||||||
const char* string = luaL_checklstring(L, 2, &string_length);
|
const char* string = luaL_checklstring(L, 2, &string_length);
|
||||||
|
|
||||||
|
#if defined(HAVE_WSLAY)
|
||||||
int write_mode = WSLAY_BINARY_FRAME; // WSLAY_TEXT_FRAME
|
int write_mode = WSLAY_BINARY_FRAME; // WSLAY_TEXT_FRAME
|
||||||
|
|
||||||
struct wslay_event_msg msg;
|
struct wslay_event_msg msg;
|
||||||
@ -185,6 +221,17 @@ static int LuaSend(lua_State* L)
|
|||||||
msg.msg_length = string_length;
|
msg.msg_length = string_length;
|
||||||
|
|
||||||
wslay_event_queue_msg(conn->m_Ctx, &msg); // it makes a copy of the data
|
wslay_event_queue_msg(conn->m_Ctx, &msg); // it makes a copy of the data
|
||||||
|
#else
|
||||||
|
|
||||||
|
dmSocket::Result sr = Send(conn, string, string_length, 0);
|
||||||
|
if (dmSocket::RESULT_OK != sr)
|
||||||
|
{
|
||||||
|
conn->m_Status = RESULT_ERROR;
|
||||||
|
dmSnPrintf(conn->m_Buffer, conn->m_BufferCapacity, "Failed to send on websocket");
|
||||||
|
HandleCallback(conn, EVENT_ERROR, 0, 0);
|
||||||
|
CloseConnection(conn);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -229,25 +276,6 @@ static void HandleCallback(WebsocketConnection* conn, int event, const uint8_t*
|
|||||||
dmScript::TeardownCallback(conn->m_Callback);
|
dmScript::TeardownCallback(conn->m_Callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define WSLAY_CASE(_X) case _X: return #_X;
|
|
||||||
|
|
||||||
static const char* WSL_ResultToString(int err)
|
|
||||||
{
|
|
||||||
switch(err) {
|
|
||||||
WSLAY_CASE(WSLAY_ERR_WANT_READ);
|
|
||||||
WSLAY_CASE(WSLAY_ERR_WANT_WRITE);
|
|
||||||
WSLAY_CASE(WSLAY_ERR_PROTO);
|
|
||||||
WSLAY_CASE(WSLAY_ERR_INVALID_ARGUMENT);
|
|
||||||
WSLAY_CASE(WSLAY_ERR_INVALID_CALLBACK);
|
|
||||||
WSLAY_CASE(WSLAY_ERR_NO_MORE_MSG);
|
|
||||||
WSLAY_CASE(WSLAY_ERR_CALLBACK_FAILURE);
|
|
||||||
WSLAY_CASE(WSLAY_ERR_WOULDBLOCK);
|
|
||||||
WSLAY_CASE(WSLAY_ERR_NOMEM);
|
|
||||||
default: return "Unknown error";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#undef WSLAY_CASE
|
|
||||||
|
|
||||||
// ***************************************************************************************************
|
// ***************************************************************************************************
|
||||||
// Life cycle functions
|
// Life cycle functions
|
||||||
@ -268,13 +296,14 @@ static void LuaInit(lua_State* L)
|
|||||||
// Register lua names
|
// Register lua names
|
||||||
luaL_register(L, MODULE_NAME, Websocket_module_methods);
|
luaL_register(L, MODULE_NAME, Websocket_module_methods);
|
||||||
|
|
||||||
#define SETCONSTANT(name, val) \
|
#define SETCONSTANT(_X) \
|
||||||
lua_pushnumber(L, (lua_Number) val); \
|
lua_pushnumber(L, (lua_Number) _X); \
|
||||||
lua_setfield(L, -2, #name);
|
lua_setfield(L, -2, #_X);
|
||||||
|
|
||||||
SETCONSTANT(EVENT_CONNECTED, EVENT_CONNECTED);
|
SETCONSTANT(EVENT_CONNECTED);
|
||||||
SETCONSTANT(EVENT_DISCONNECTED, EVENT_DISCONNECTED);
|
SETCONSTANT(EVENT_DISCONNECTED);
|
||||||
SETCONSTANT(EVENT_MESSAGE, EVENT_MESSAGE);
|
SETCONSTANT(EVENT_MESSAGE);
|
||||||
|
SETCONSTANT(EVENT_ERROR);
|
||||||
|
|
||||||
#undef SETCONSTANT
|
#undef SETCONSTANT
|
||||||
|
|
||||||
@ -299,21 +328,26 @@ static dmExtension::Result WebsocketAppInitialize(dmExtension::AppParams* params
|
|||||||
dmLogError("Failed to create connection pool: %d", result);
|
dmLogError("Failed to create connection pool: %d", result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We can do without the channel, it will then fallback to the dmSocket::GetHostname (as opposed to dmDNS::GetHostname)
|
||||||
|
#if defined(HAVE_WSLAY)
|
||||||
dmDNS::Result dns_result = dmDNS::NewChannel(&g_Websocket.m_Channel);
|
dmDNS::Result dns_result = dmDNS::NewChannel(&g_Websocket.m_Channel);
|
||||||
|
|
||||||
if (dmDNS::RESULT_OK != dns_result)
|
if (dmDNS::RESULT_OK != dns_result)
|
||||||
{
|
{
|
||||||
dmLogError("Failed to create connection pool: %d", dns_result);
|
dmLogError("Failed to create connection pool: %d", dns_result);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
g_Websocket.m_Initialized = 1;
|
g_Websocket.m_Initialized = 1;
|
||||||
if (g_Websocket.m_Channel == 0 || g_Websocket.m_Pool == 0)
|
if (!g_Websocket.m_Pool)
|
||||||
{
|
{
|
||||||
if (g_Websocket.m_Channel)
|
if (!g_Websocket.m_Pool)
|
||||||
dmDNS::DeleteChannel(g_Websocket.m_Channel);
|
{
|
||||||
if (g_Websocket.m_Pool)
|
dmLogInfo("pool is null!");
|
||||||
dmConnectionPool::Delete(g_Websocket.m_Pool);
|
dmConnectionPool::Delete(g_Websocket.m_Pool);
|
||||||
|
}
|
||||||
|
|
||||||
|
dmLogInfo("%s extension not initialized", MODULE_NAME);
|
||||||
g_Websocket.m_Initialized = 0;
|
g_Websocket.m_Initialized = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -326,7 +360,7 @@ static dmExtension::Result WebsocketInitialize(dmExtension::Params* params)
|
|||||||
return dmExtension::RESULT_OK;
|
return dmExtension::RESULT_OK;
|
||||||
|
|
||||||
LuaInit(params->m_L);
|
LuaInit(params->m_L);
|
||||||
dmLogInfo("Registered %s extension\n", MODULE_NAME);
|
dmLogInfo("Registered %s extension", MODULE_NAME);
|
||||||
|
|
||||||
return dmExtension::RESULT_OK;
|
return dmExtension::RESULT_OK;
|
||||||
}
|
}
|
||||||
@ -366,37 +400,55 @@ static dmExtension::Result WebsocketOnUpdate(dmExtension::Params* params)
|
|||||||
}
|
}
|
||||||
else if (STATE_CONNECTED == conn->m_State)
|
else if (STATE_CONNECTED == conn->m_State)
|
||||||
{
|
{
|
||||||
// Do we need to loop here?
|
#if defined(HAVE_WSLAY)
|
||||||
int err = 0;
|
int r = WSL_Poll(conn->m_Ctx);
|
||||||
if ((err = wslay_event_recv(conn->m_Ctx)) != 0 || (err = wslay_event_send(conn->m_Ctx)) != 0) {
|
if (0 != r)
|
||||||
dmLogError("Websocket poll error: %s from %s", WSL_ResultToString(err), conn->m_Url.m_Hostname);
|
{
|
||||||
|
CLOSE_CONN("Websocket closing for %s (%s)", conn->m_Url.m_Hostname, WSL_ResultToString(r));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
r = WSL_WantsExit(conn->m_Ctx);
|
||||||
|
if (0 != r)
|
||||||
|
{
|
||||||
|
CLOSE_CONN("Websocket received close event for %s", conn->m_Url.m_Hostname);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
int recv_bytes = 0;
|
||||||
|
dmSocket::Result sr = Receive(conn, conn->m_Buffer, conn->m_BufferCapacity, &recv_bytes);
|
||||||
|
if( sr == dmSocket::RESULT_WOULDBLOCK )
|
||||||
|
{
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((wslay_event_get_close_sent(conn->m_Ctx) && wslay_event_get_close_received(conn->m_Ctx))) {
|
if (dmSocket::RESULT_OK == sr)
|
||||||
CLOSE_CONN("Websocket received close event for %s", conn->m_Url.m_Hostname);
|
{
|
||||||
|
conn->m_BufferSize += recv_bytes;
|
||||||
|
conn->m_HasMessage = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CLOSE_CONN("Websocket failed to receive data %s", dmSocket::ResultToString(sr));
|
||||||
conn->m_State = STATE_DISCONNECTED;
|
conn->m_State = STATE_DISCONNECTED;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
if (conn->m_HasMessage)
|
if (conn->m_HasMessage)
|
||||||
{
|
{
|
||||||
HandleCallback(conn, EVENT_MESSAGE, (uint8_t*)conn->m_Buffer, conn->m_BufferSize);
|
HandleCallback(conn, EVENT_MESSAGE, (uint8_t*)conn->m_Buffer, conn->m_BufferSize);
|
||||||
conn->m_HasMessage = 0;
|
conn->m_HasMessage = 0;
|
||||||
|
conn->m_BufferSize = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (STATE_HANDSHAKE == conn->m_State)
|
else if (STATE_HANDSHAKE_READ == conn->m_State)
|
||||||
{
|
{
|
||||||
// TODO: Split up this state into three?
|
Result result = ReceiveHeaders(conn);
|
||||||
// e.g. STATE_HANDSHAKE_SEND, STATE_HANDSHAKE_RECEIVE, STATE_HANDSHAKE_VERIFY
|
if (RESULT_WOULDBLOCK == result)
|
||||||
|
|
||||||
Result result = SendClientHandshake(conn);
|
|
||||||
if (RESULT_OK != result)
|
|
||||||
{
|
{
|
||||||
CLOSE_CONN("Failed sending handshake: %d", result);
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = ReceiveHeaders(conn);
|
|
||||||
if (RESULT_OK != result)
|
if (RESULT_OK != result)
|
||||||
{
|
{
|
||||||
CLOSE_CONN("Failed receiving handshake headers. %d", result);
|
CLOSE_CONN("Failed receiving handshake headers. %d", result);
|
||||||
@ -410,35 +462,55 @@ static dmExtension::Result WebsocketOnUpdate(dmExtension::Params* params)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Currently only supports client implementation
|
#if defined(HAVE_WSLAY)
|
||||||
int ret = -1;
|
int r = WSL_Init(&conn->m_Ctx, g_Websocket.m_BufferSize, (void*)conn);
|
||||||
ret = wslay_event_context_client_init(&conn->m_Ctx, &g_WslCallbacks, conn);
|
if (0 != r)
|
||||||
if (ret == 0)
|
|
||||||
wslay_event_config_set_max_recv_msg_length(conn->m_Ctx, g_Websocket.m_BufferSize);
|
|
||||||
if (ret != 0)
|
|
||||||
{
|
{
|
||||||
CLOSE_CONN("Failed initializing wslay: %s", WSL_ResultToString(ret));
|
SetStatus(conn, RESULT_FAIL_WSLAY_INIT, "Failed initializing wslay: %s", WSL_ResultToString(r));
|
||||||
SetStatus(conn, RESULT_HANDSHAKE_FAILED, "Failed initializing wslay: %s", WSL_ResultToString(ret));
|
CLOSE_CONN("Failed initializing wslay: %s", WSL_ResultToString(r));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (conn->m_Socket) {
|
dmSocket::SetNoDelay(conn->m_Socket, true);
|
||||||
dmSocket::SetNoDelay(conn->m_Socket, true);
|
// Don't go lower than 1000 since some platforms might not have that good precision
|
||||||
dmSocket::SetBlocking(conn->m_Socket, false);
|
dmSocket::SetReceiveTimeout(conn->m_Socket, 1000);
|
||||||
dmSocket::SetReceiveTimeout(conn->m_Socket, 500);
|
if (conn->m_SSLSocket)
|
||||||
}
|
dmSSLSocket::SetReceiveTimeout(conn->m_SSLSocket, 1000);
|
||||||
|
#endif
|
||||||
|
dmSocket::SetBlocking(conn->m_Socket, false);
|
||||||
|
|
||||||
conn->m_Buffer[0] = 0;
|
conn->m_Buffer[0] = 0;
|
||||||
conn->m_BufferSize = 0;
|
conn->m_BufferSize = 0;
|
||||||
conn->m_State = STATE_CONNECTED;
|
conn->m_State = STATE_CONNECTED;
|
||||||
|
|
||||||
|
WS_DEBUG("STATE_HANDSHAKE -> STATE_CONNECTED");
|
||||||
|
|
||||||
HandleCallback(conn, EVENT_CONNECTED, 0, 0);
|
HandleCallback(conn, EVENT_CONNECTED, 0, 0);
|
||||||
}
|
}
|
||||||
|
else if (STATE_HANDSHAKE_WRITE == conn->m_State)
|
||||||
|
{
|
||||||
|
Result result = SendClientHandshake(conn);
|
||||||
|
if (RESULT_WOULDBLOCK == result)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (RESULT_OK != result)
|
||||||
|
{
|
||||||
|
CLOSE_CONN("Failed sending handshake: %d", result);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
conn->m_State = STATE_HANDSHAKE_READ;
|
||||||
|
WS_DEBUG("STATE_HANDSHAKE_WRITE -> STATE_HANDSHAKE_READ");
|
||||||
|
}
|
||||||
else if (STATE_CONNECTING == conn->m_State)
|
else if (STATE_CONNECTING == conn->m_State)
|
||||||
{
|
{
|
||||||
// wait for it to finish
|
|
||||||
dmSocket::Result socket_result;
|
dmSocket::Result socket_result;
|
||||||
dmConnectionPool::Result pool_result = dmConnectionPool::Dial(g_Websocket.m_Pool, conn->m_Url.m_Hostname, conn->m_Url.m_Port, g_Websocket.m_Channel, conn->m_SSL, g_Websocket.m_Timeout, &conn->m_Connection, &socket_result);
|
int timeout = g_Websocket.m_Timeout;
|
||||||
|
#if defined(__EMSCRIPTEN__)
|
||||||
|
timeout = 0;
|
||||||
|
#endif
|
||||||
|
dmConnectionPool::Result pool_result = dmConnectionPool::Dial(g_Websocket.m_Pool, conn->m_Url.m_Hostname, conn->m_Url.m_Port, g_Websocket.m_Channel, conn->m_SSL, timeout, &conn->m_Connection, &socket_result);
|
||||||
if (dmConnectionPool::RESULT_OK != pool_result)
|
if (dmConnectionPool::RESULT_OK != pool_result)
|
||||||
{
|
{
|
||||||
CLOSE_CONN("Failed to open connection: %s", dmSocket::ResultToString(socket_result));
|
CLOSE_CONN("Failed to open connection: %s", dmSocket::ResultToString(socket_result));
|
||||||
@ -447,7 +519,9 @@ static dmExtension::Result WebsocketOnUpdate(dmExtension::Params* params)
|
|||||||
|
|
||||||
conn->m_Socket = dmConnectionPool::GetSocket(g_Websocket.m_Pool, conn->m_Connection);
|
conn->m_Socket = dmConnectionPool::GetSocket(g_Websocket.m_Pool, conn->m_Connection);
|
||||||
conn->m_SSLSocket = dmConnectionPool::GetSSLSocket(g_Websocket.m_Pool, conn->m_Connection);
|
conn->m_SSLSocket = dmConnectionPool::GetSSLSocket(g_Websocket.m_Pool, conn->m_Connection);
|
||||||
conn->m_State = STATE_HANDSHAKE;
|
conn->m_State = STATE_HANDSHAKE_WRITE;
|
||||||
|
|
||||||
|
WS_DEBUG("STATE_CONNECTING -> STATE_HANDSHAKE");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,13 @@
|
|||||||
// include the Defold SDK
|
// include the Defold SDK
|
||||||
#include <dmsdk/sdk.h>
|
#include <dmsdk/sdk.h>
|
||||||
|
|
||||||
#include <wslay/wslay.h>
|
#if !defined(__EMSCRIPTEN__)
|
||||||
|
#define HAVE_WSLAY 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(HAVE_WSLAY)
|
||||||
|
#include <wslay/wslay.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <dmsdk/dlib/connection_pool.h>
|
#include <dmsdk/dlib/connection_pool.h>
|
||||||
#include <dmsdk/dlib/socket.h>
|
#include <dmsdk/dlib/socket.h>
|
||||||
@ -19,10 +25,14 @@ namespace dmCrypt
|
|||||||
|
|
||||||
namespace dmWebsocket
|
namespace dmWebsocket
|
||||||
{
|
{
|
||||||
|
// Maximum time to wait for a socket
|
||||||
|
static const int SOCKET_WAIT_TIMEOUT = 4*1000;
|
||||||
|
|
||||||
enum State
|
enum State
|
||||||
{
|
{
|
||||||
STATE_CONNECTING,
|
STATE_CONNECTING,
|
||||||
STATE_HANDSHAKE,
|
STATE_HANDSHAKE_WRITE,
|
||||||
|
STATE_HANDSHAKE_READ,
|
||||||
STATE_CONNECTED,
|
STATE_CONNECTED,
|
||||||
STATE_DISCONNECTED,
|
STATE_DISCONNECTED,
|
||||||
};
|
};
|
||||||
@ -30,9 +40,11 @@ namespace dmWebsocket
|
|||||||
enum Result
|
enum Result
|
||||||
{
|
{
|
||||||
RESULT_OK,
|
RESULT_OK,
|
||||||
|
RESULT_ERROR,
|
||||||
RESULT_FAIL_WSLAY_INIT,
|
RESULT_FAIL_WSLAY_INIT,
|
||||||
RESULT_NOT_CONNECTED,
|
RESULT_NOT_CONNECTED,
|
||||||
RESULT_HANDSHAKE_FAILED,
|
RESULT_HANDSHAKE_FAILED,
|
||||||
|
RESULT_WOULDBLOCK,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum Event
|
enum Event
|
||||||
@ -40,12 +52,15 @@ namespace dmWebsocket
|
|||||||
EVENT_CONNECTED,
|
EVENT_CONNECTED,
|
||||||
EVENT_DISCONNECTED,
|
EVENT_DISCONNECTED,
|
||||||
EVENT_MESSAGE,
|
EVENT_MESSAGE,
|
||||||
|
EVENT_ERROR,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WebsocketConnection
|
struct WebsocketConnection
|
||||||
{
|
{
|
||||||
dmScript::LuaCallbackInfo* m_Callback;
|
dmScript::LuaCallbackInfo* m_Callback;
|
||||||
|
#if defined(HAVE_WSLAY)
|
||||||
wslay_event_context_ptr m_Ctx;
|
wslay_event_context_ptr m_Ctx;
|
||||||
|
#endif
|
||||||
dmURI::Parts m_Url;
|
dmURI::Parts m_Url;
|
||||||
dmConnectionPool::HConnection m_Connection;
|
dmConnectionPool::HConnection m_Connection;
|
||||||
dmSocket::Socket m_Socket;
|
dmSocket::Socket m_Socket;
|
||||||
@ -70,17 +85,26 @@ namespace dmWebsocket
|
|||||||
// Communication
|
// Communication
|
||||||
dmSocket::Result Send(WebsocketConnection* conn, const char* buffer, int length, int* out_sent_bytes);
|
dmSocket::Result Send(WebsocketConnection* conn, const char* buffer, int length, int* out_sent_bytes);
|
||||||
dmSocket::Result Receive(WebsocketConnection* conn, void* buffer, int length, int* received_bytes);
|
dmSocket::Result Receive(WebsocketConnection* conn, void* buffer, int length, int* received_bytes);
|
||||||
|
dmSocket::Result WaitForSocket(WebsocketConnection* conn, dmSocket::SelectorKind kind, int timeout);
|
||||||
|
|
||||||
// Handshake
|
// Handshake
|
||||||
Result SendClientHandshake(WebsocketConnection* conn);
|
Result SendClientHandshake(WebsocketConnection* conn);
|
||||||
Result ReceiveHeaders(WebsocketConnection* conn);
|
Result ReceiveHeaders(WebsocketConnection* conn);
|
||||||
Result VerifyHeaders(WebsocketConnection* conn);
|
Result VerifyHeaders(WebsocketConnection* conn);
|
||||||
|
|
||||||
|
#if defined(HAVE_WSLAY)
|
||||||
// Wslay callbacks
|
// Wslay callbacks
|
||||||
|
int WSL_Init(wslay_event_context_ptr* ctx, ssize_t buffer_size, void* userctx);
|
||||||
|
void WSL_Exit(wslay_event_context_ptr ctx);
|
||||||
|
int WSL_Close(wslay_event_context_ptr ctx);
|
||||||
|
int WSL_Poll(wslay_event_context_ptr ctx);
|
||||||
|
int WSL_WantsExit(wslay_event_context_ptr ctx);
|
||||||
ssize_t WSL_RecvCallback(wslay_event_context_ptr ctx, uint8_t *buf, size_t len, int flags, void *user_data);
|
ssize_t WSL_RecvCallback(wslay_event_context_ptr ctx, uint8_t *buf, size_t len, int flags, void *user_data);
|
||||||
ssize_t WSL_SendCallback(wslay_event_context_ptr ctx, const uint8_t *data, size_t len, int flags, void *user_data);
|
ssize_t WSL_SendCallback(wslay_event_context_ptr ctx, const uint8_t *data, size_t len, int flags, void *user_data);
|
||||||
void WSL_OnMsgRecvCallback(wslay_event_context_ptr ctx, const struct wslay_event_on_msg_recv_arg *arg, void *user_data);
|
void WSL_OnMsgRecvCallback(wslay_event_context_ptr ctx, const struct wslay_event_on_msg_recv_arg *arg, void *user_data);
|
||||||
int WSL_GenmaskCallback(wslay_event_context_ptr ctx, uint8_t *buf, size_t len, void *user_data);
|
int WSL_GenmaskCallback(wslay_event_context_ptr ctx, uint8_t *buf, size_t len, void *user_data);
|
||||||
|
const char* WSL_ResultToString(int err);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Random numbers (PCG)
|
// Random numbers (PCG)
|
||||||
typedef struct { uint64_t state; uint64_t inc; } pcg32_random_t;
|
typedef struct { uint64_t state; uint64_t inc; } pcg32_random_t;
|
||||||
|
@ -1,8 +1,81 @@
|
|||||||
#include "websocket.h"
|
#include "websocket.h"
|
||||||
|
|
||||||
|
#if defined(HAVE_WSLAY)
|
||||||
|
|
||||||
namespace dmWebsocket
|
namespace dmWebsocket
|
||||||
{
|
{
|
||||||
|
|
||||||
|
const struct wslay_event_callbacks g_WslCallbacks = {
|
||||||
|
WSL_RecvCallback,
|
||||||
|
WSL_SendCallback,
|
||||||
|
WSL_GenmaskCallback,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
NULL,
|
||||||
|
WSL_OnMsgRecvCallback
|
||||||
|
};
|
||||||
|
|
||||||
|
#define WSLAY_CASE(_X) case _X: return #_X;
|
||||||
|
|
||||||
|
const char* WSL_ResultToString(int err)
|
||||||
|
{
|
||||||
|
switch(err) {
|
||||||
|
WSLAY_CASE(WSLAY_ERR_WANT_READ);
|
||||||
|
WSLAY_CASE(WSLAY_ERR_WANT_WRITE);
|
||||||
|
WSLAY_CASE(WSLAY_ERR_PROTO);
|
||||||
|
WSLAY_CASE(WSLAY_ERR_INVALID_ARGUMENT);
|
||||||
|
WSLAY_CASE(WSLAY_ERR_INVALID_CALLBACK);
|
||||||
|
WSLAY_CASE(WSLAY_ERR_NO_MORE_MSG);
|
||||||
|
WSLAY_CASE(WSLAY_ERR_CALLBACK_FAILURE);
|
||||||
|
WSLAY_CASE(WSLAY_ERR_WOULDBLOCK);
|
||||||
|
WSLAY_CASE(WSLAY_ERR_NOMEM);
|
||||||
|
default: return "Unknown error";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef WSLAY_CASE
|
||||||
|
|
||||||
|
|
||||||
|
int WSL_Init(wslay_event_context_ptr* ctx, ssize_t buffer_size, void* userctx)
|
||||||
|
{
|
||||||
|
// Currently only supports client implementation
|
||||||
|
int ret = -1;
|
||||||
|
ret = wslay_event_context_client_init(ctx, &g_WslCallbacks, userctx);
|
||||||
|
if (ret == 0)
|
||||||
|
wslay_event_config_set_max_recv_msg_length(*ctx, buffer_size);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void WSL_Exit(wslay_event_context_ptr ctx)
|
||||||
|
{
|
||||||
|
wslay_event_context_free(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
int WSL_Close(wslay_event_context_ptr ctx)
|
||||||
|
{
|
||||||
|
const char* reason = "Client wants to close";
|
||||||
|
wslay_event_queue_close(ctx, 0, (const uint8_t*)reason, strlen(reason));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WSL_Poll(wslay_event_context_ptr ctx)
|
||||||
|
{
|
||||||
|
int r = 0;
|
||||||
|
if ((r = wslay_event_recv(ctx)) != 0 || (r = wslay_event_send(ctx)) != 0) {
|
||||||
|
dmLogError("Websocket poll error: %s", WSL_ResultToString(r));
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WSL_WantsExit(wslay_event_context_ptr ctx)
|
||||||
|
{
|
||||||
|
if ((wslay_event_get_close_sent(ctx) && wslay_event_get_close_received(ctx))) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
ssize_t WSL_RecvCallback(wslay_event_context_ptr ctx, uint8_t *buf, size_t len, int flags, void *user_data)
|
ssize_t WSL_RecvCallback(wslay_event_context_ptr ctx, uint8_t *buf, size_t len, int flags, void *user_data)
|
||||||
{
|
{
|
||||||
WebsocketConnection* conn = (WebsocketConnection*)user_data;
|
WebsocketConnection* conn = (WebsocketConnection*)user_data;
|
||||||
@ -135,3 +208,5 @@ int WSL_GenmaskCallback(wslay_event_context_ptr ctx, uint8_t *buf, size_t len, v
|
|||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
#endif // HAVE_WSLAY
|
Loading…
x
Reference in New Issue
Block a user