mirror of
https://github.com/defold/extension-websocket.git
synced 2025-09-30 09:12:18 +02:00
Compare commits
22 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
bd4e5c0b35 | ||
|
ac230a6278 | ||
|
39abd7cdea | ||
|
f988413a74 | ||
|
73b236b249 | ||
|
636a11daa3 | ||
|
0f147ef180 | ||
|
3cd0328c2a | ||
|
f5aa57452f | ||
|
7e89b8a685 | ||
|
ba7454a431 | ||
|
b93a91c9b8 | ||
|
0bf63cbdf8 | ||
|
8a9dc759a4 | ||
|
70afde4cfa | ||
|
ed6d131470 | ||
|
2c9c0b74f1 | ||
|
8450a88640 | ||
|
3636ea73db | ||
|
1cc1d802b3 | ||
|
94bcc82f25 | ||
|
d62a53cbcc |
76
.github/workflows/bob.yml
vendored
Normal file
76
.github/workflows/bob.yml
vendored
Normal file
@@ -0,0 +1,76 @@
|
||||
name: Build with bob
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request_target:
|
||||
schedule:
|
||||
# nightly at 05:00 on the 1st and 15th
|
||||
- cron: 0 5 1,15 * *
|
||||
|
||||
env:
|
||||
VERSION_FILENAME: 'info.json'
|
||||
BUILD_SERVER: 'https://build.defold.com'
|
||||
|
||||
jobs:
|
||||
build_with_bob:
|
||||
strategy:
|
||||
matrix:
|
||||
platform: [armv7-android, x86_64-linux, x86_64-win32, x86-win32, js-web]
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
name: Build
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: '11.0.2'
|
||||
|
||||
- name: Get Defold version
|
||||
run: |
|
||||
TMPVAR=`curl -s http://d.defold.com/stable/${{env.VERSION_FILENAME}} | jq -r '.sha1'`
|
||||
echo "DEFOLD_VERSION=${TMPVAR}" >> $GITHUB_ENV
|
||||
echo "Found version ${TMPVAR}"
|
||||
|
||||
- name: Download bob.jar
|
||||
run: |
|
||||
wget -q http://d.defold.com/archive/stable/${{env.DEFOLD_VERSION}}/bob/bob.jar
|
||||
java -jar bob.jar --version
|
||||
|
||||
- name: Resolve libraries
|
||||
run: java -jar bob.jar resolve --email a@b.com --auth 123456
|
||||
- name: Build
|
||||
run: java -jar bob.jar --platform=${{ matrix.platform }} build --archive --build-server=${{env.BUILD_SERVER}}
|
||||
- name: Bundle
|
||||
run: java -jar bob.jar --platform=${{ matrix.platform }} bundle
|
||||
|
||||
# macOS is not technically needed for building, but we want to test bundling as well, since we're also testing the manifest merging
|
||||
build_with_bob_macos:
|
||||
strategy:
|
||||
matrix:
|
||||
platform: [armv7-darwin, x86_64-darwin]
|
||||
runs-on: macOS-latest
|
||||
|
||||
name: Build
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-java@v1
|
||||
with:
|
||||
java-version: '11.0.2'
|
||||
|
||||
- name: Get Defold version
|
||||
run: |
|
||||
TMPVAR=`curl -s http://d.defold.com/stable/${{env.VERSION_FILENAME}} | jq -r '.sha1'`
|
||||
echo "DEFOLD_VERSION=${TMPVAR}" >> $GITHUB_ENV
|
||||
echo "Found version ${TMPVAR}"
|
||||
|
||||
- name: Download bob.jar
|
||||
run: |
|
||||
wget -q http://d.defold.com/archive/stable/${{env.DEFOLD_VERSION}}/bob/bob.jar
|
||||
java -jar bob.jar --version
|
||||
|
||||
- name: Resolve libraries
|
||||
run: java -jar bob.jar resolve --email a@b.com --auth 123456
|
||||
- name: Build
|
||||
run: java -jar bob.jar --platform=${{ matrix.platform }} build --archive --build-server=${{env.BUILD_SERVER}}
|
||||
- name: Bundle
|
||||
run: java -jar bob.jar --platform=${{ matrix.platform }} bundle
|
5
.github/workflows/trigger-site-rebuild.yml
vendored
5
.github/workflows/trigger-site-rebuild.yml
vendored
@@ -1,6 +1,9 @@
|
||||
name: Trigger site rebuild
|
||||
|
||||
on: [push]
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
|
||||
jobs:
|
||||
site-rebuild:
|
||||
|
10
README.md
10
README.md
@@ -1,5 +1,9 @@
|
||||
|
||||
|
||||
# Defold websocket extension
|
||||
|
||||
[](https://github.com/defold/extension-websocket/actions)
|
||||
|
||||
## Installation
|
||||
To use this library in your Defold project, add the following URL to your `game.project` dependencies:
|
||||
|
||||
@@ -40,3 +44,9 @@ For command line debugging, there's
|
||||
* tcpdump: `sudo tcpdump -X -s0 -ilo0 port 8080 ` (example for local ws:// connection)
|
||||
|
||||
* tcpdump: `sudo tcpdump -X -s0 host echo.websocket.org` (Monitors packets to/from echo.websocket.org)
|
||||
|
||||
## Credits
|
||||
|
||||
This extension makes use of the C library WSlay by @tatsuhiro-t:
|
||||
|
||||
* https://github.com/tatsuhiro-t/wslay
|
||||
|
@@ -1,5 +1,4 @@
|
||||
local URL="://echo.websocket.org"
|
||||
|
||||
local function click_button(node, action)
|
||||
return gui.is_enabled(node) and action.pressed and gui.pick_node(node, action.x, action.y)
|
||||
end
|
||||
@@ -73,7 +72,14 @@ local function websocket_callback(self, conn, data)
|
||||
update_gui(self)
|
||||
log("Connected: " .. tostring(conn))
|
||||
elseif data.event == websocket.EVENT_ERROR then
|
||||
log("Error: '" .. data.error .. "'")
|
||||
log("Error: '" .. tostring(data.message) .. "'")
|
||||
if data.handshake_response then
|
||||
log("Handshake response status: '" .. tostring(data.handshake_response.status) .. "'")
|
||||
for key, value in pairs(data.handshake_response.headers) do
|
||||
log("Handshake response header: '" .. key .. ": " .. value .. "'")
|
||||
end
|
||||
log("Handshake response body: '" .. tostring(data.handshake_response.response) .. "'")
|
||||
end
|
||||
elseif data.event == websocket.EVENT_MESSAGE then
|
||||
log("Receiving: '" .. tostring(data.message) .. "'")
|
||||
end
|
||||
|
@@ -19,7 +19,11 @@
|
||||
members:
|
||||
- name: timeout
|
||||
type: number
|
||||
desc: Timeout for the connection sequence (milliseconds). Not used on HTML5. Default: 3000
|
||||
desc: Timeout for the connection sequence (milliseconds). Not used on HTML5. (Default is 3000)
|
||||
|
||||
- name: protocol
|
||||
type: string
|
||||
desc: the protocol to use (e.g. 'chat'). If not set, no `Sec-WebSocket-Protocol` header is sent.
|
||||
|
||||
- name: headers
|
||||
type: string
|
||||
@@ -55,11 +59,7 @@
|
||||
|
||||
- name: message
|
||||
type: string
|
||||
desc: The received data. Only valid if event is `websocket.EVENT_MESSAGE`
|
||||
|
||||
- name: error
|
||||
type: string
|
||||
desc: The error string. Only valid if event is `websocket.EVENT_ERROR`
|
||||
desc: The received data if event is `websocket.EVENT_MESSAGE`. Error message otherwise
|
||||
|
||||
|
||||
returns:
|
||||
@@ -79,7 +79,7 @@
|
||||
update_gui(self)
|
||||
log("Connected: " .. tostring(conn))
|
||||
elseif data.event == websocket.EVENT_ERROR then
|
||||
log("Error: '" .. data.error .. "'")
|
||||
log("Error: '" .. data.message .. "'")
|
||||
elseif data.event == websocket.EVENT_MESSAGE then
|
||||
log("Receiving: '" .. tostring(data.message) .. "'")
|
||||
end
|
||||
|
@@ -1,5 +1,6 @@
|
||||
#include "websocket.h"
|
||||
#include <dmsdk/dlib/socket.h>
|
||||
#include <dmsdk/dlib/http_client.h>
|
||||
#include <ctype.h> // tolower
|
||||
|
||||
namespace dmWebsocket
|
||||
@@ -40,8 +41,12 @@ static Result SendClientHandshakeHeaders(WebsocketConnection* conn)
|
||||
dmSnPrintf(port, sizeof(port), ":%d", conn->m_Url.m_Port);
|
||||
|
||||
dmSocket::Result sr;
|
||||
WS_SENDALL("GET /");
|
||||
WS_SENDALL("GET ");
|
||||
if (conn->m_Url.m_Path[0] == '\0') {
|
||||
WS_SENDALL("/"); // Default to / for empty path
|
||||
} else {
|
||||
WS_SENDALL(conn->m_Url.m_Path);
|
||||
}
|
||||
WS_SENDALL(" HTTP/1.1\r\n");
|
||||
WS_SENDALL("Host: ");
|
||||
WS_SENDALL(conn->m_Url.m_Hostname);
|
||||
@@ -66,6 +71,12 @@ static Result SendClientHandshakeHeaders(WebsocketConnection* conn)
|
||||
}
|
||||
}
|
||||
|
||||
if (conn->m_Protocol) {
|
||||
WS_SENDALL("Sec-WebSocket-Protocol: ");
|
||||
WS_SENDALL(conn->m_Protocol);
|
||||
WS_SENDALL("\r\n");
|
||||
}
|
||||
|
||||
WS_SENDALL("\r\n");
|
||||
|
||||
bail:
|
||||
@@ -109,17 +120,12 @@ Result ReceiveHeaders(WebsocketConnection* conn)
|
||||
#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, SOCKET_WAIT_TIMEOUT);
|
||||
|
||||
dmSocket::Result sr = WaitForSocket(conn, dmSocket::SELECTOR_KIND_READ, SOCKET_WAIT_TIMEOUT);
|
||||
if (dmSocket::RESULT_OK != sr)
|
||||
{
|
||||
if (dmSocket::RESULT_WOULDBLOCK)
|
||||
{
|
||||
DebugLog(1, "Waiting for socket to be available for reading");
|
||||
DebugLog(2, "Waiting for socket to be available for reading");
|
||||
return RESULT_WOULDBLOCK;
|
||||
}
|
||||
|
||||
@@ -165,69 +171,37 @@ Result ReceiveHeaders(WebsocketConnection* conn)
|
||||
}
|
||||
#endif
|
||||
|
||||
static int dmStriCmp(const char* s1, const char* s2)
|
||||
static void HandleVersion(void* user_data, int major, int minor, int status, const char* status_str)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
if (!*s1 || !*s2 || tolower((unsigned char) *s1) != tolower((unsigned char) *s2))
|
||||
{
|
||||
return (unsigned char) *s1 - (unsigned char) *s2;
|
||||
}
|
||||
s1++;
|
||||
s2++;
|
||||
}
|
||||
HandshakeResponse* response = (HandshakeResponse*)user_data;
|
||||
response->m_HttpMajor = major;
|
||||
response->m_HttpMinor = minor;
|
||||
response->m_ResponseStatusCode = status;
|
||||
}
|
||||
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
Result VerifyHeaders(WebsocketConnection* conn)
|
||||
static void HandleHeader(void* user_data, const char* key, const char* value)
|
||||
{
|
||||
return RESULT_OK;
|
||||
}
|
||||
#else
|
||||
Result VerifyHeaders(WebsocketConnection* conn)
|
||||
{
|
||||
char* r = conn->m_Buffer;
|
||||
|
||||
// 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) {
|
||||
return SetStatus(conn, RESULT_HANDSHAKE_FAILED, "Missing: '%s' in header", http_version_and_status_protocol);
|
||||
HandshakeResponse* response = (HandshakeResponse*)user_data;
|
||||
if (response->m_Headers.Remaining() == 0)
|
||||
{
|
||||
response->m_Headers.OffsetCapacity(4);
|
||||
}
|
||||
HttpHeader* new_header = new HttpHeader(key, value);
|
||||
|
||||
const char* endtag = strstr(conn->m_Buffer, "\r\n\r\n");
|
||||
response->m_Headers.Push(new_header);
|
||||
}
|
||||
|
||||
r = strstr(r, "\r\n") + 2;
|
||||
|
||||
bool connection = false;
|
||||
bool upgrade = false;
|
||||
bool valid_key = false;
|
||||
const char* protocol = "";
|
||||
static void HandleContent(void* user_data, int offset)
|
||||
{
|
||||
HandshakeResponse* response = (HandshakeResponse*)user_data;
|
||||
response->m_BodyOffset = offset;
|
||||
}
|
||||
|
||||
// TODO: Perhaps also support the Sec-WebSocket-Protocol
|
||||
|
||||
// parse the headers in place
|
||||
while (r < endtag)
|
||||
{
|
||||
// Tokenize the each header line: "Key: Value\r\n"
|
||||
const char* key = r;
|
||||
r = strchr(r, ':');
|
||||
*r = 0;
|
||||
++r;
|
||||
const char* value = r;
|
||||
while(*value == ' ')
|
||||
++value;
|
||||
r = strstr(r, "\r\n");
|
||||
*r = 0;
|
||||
r += 2;
|
||||
|
||||
// Page 18 in https://tools.ietf.org/html/rfc6455#section-11.3.3
|
||||
if (dmStriCmp(key, "Connection") == 0 && dmStriCmp(value, "Upgrade") == 0)
|
||||
connection = true;
|
||||
else if (dmStriCmp(key, "Upgrade") == 0 && dmStriCmp(value, "websocket") == 0)
|
||||
upgrade = true;
|
||||
else if (dmStriCmp(key, "Sec-WebSocket-Accept") == 0)
|
||||
{
|
||||
bool ValidateSecretKey(WebsocketConnection* conn, const char* server_key)
|
||||
{
|
||||
uint8_t client_key[32 + 40];
|
||||
uint32_t client_key_len = sizeof(client_key);
|
||||
dmCrypt::Base64Encode(conn->m_Key, sizeof(conn->m_Key), client_key, &client_key_len);
|
||||
@@ -249,24 +223,48 @@ Result VerifyHeaders(WebsocketConnection* conn)
|
||||
client_key_len = sizeof(client_key);
|
||||
dmCrypt::Base64Encode(client_key_sha1, sizeof(client_key_sha1), client_key, &client_key_len);
|
||||
client_key[client_key_len] = 0;
|
||||
|
||||
DebugLog(2, "Client key (base64): %s", client_key);
|
||||
DebugLog(2, "Server key (base64): %s", server_key);
|
||||
|
||||
DebugLog(2, "Server key (base64): %s", value);
|
||||
return strcmp(server_key, (const char*)client_key) == 0;
|
||||
}
|
||||
|
||||
if (strcmp(value, (const char*)client_key) == 0)
|
||||
valid_key = true;
|
||||
}
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
Result VerifyHeaders(WebsocketConnection* conn)
|
||||
{
|
||||
return RESULT_OK;
|
||||
}
|
||||
#else
|
||||
Result VerifyHeaders(WebsocketConnection* conn)
|
||||
{
|
||||
char* r = conn->m_Buffer;
|
||||
|
||||
// Find start of payload now because dmHttpClient::ParseHeader is destructive
|
||||
const char* start_of_payload = strstr(conn->m_Buffer, "\r\n\r\n");
|
||||
start_of_payload += 4;
|
||||
|
||||
HandshakeResponse* response = new HandshakeResponse();
|
||||
conn->m_HandshakeResponse = response;
|
||||
dmHttpClient::ParseResult parse_result = dmHttpClient::ParseHeader(r, response, true, &HandleVersion, &HandleHeader, &HandleContent);
|
||||
if (parse_result != dmHttpClient::ParseResult::PARSE_RESULT_OK)
|
||||
{
|
||||
return SetStatus(conn, RESULT_HANDSHAKE_FAILED, "Failed to parse handshake response. 'dmHttpClient::ParseResult=%i'", parse_result);
|
||||
}
|
||||
|
||||
// The response might contain both the headers, but also (if successful) the first batch of data
|
||||
endtag += 4;
|
||||
uint32_t size = conn->m_BufferSize - (endtag - conn->m_Buffer);
|
||||
conn->m_BufferSize = size;
|
||||
memmove(conn->m_Buffer, endtag, size);
|
||||
conn->m_Buffer[size] = 0;
|
||||
conn->m_HasHandshakeData = conn->m_BufferSize != 0 ? 1 : 0;
|
||||
if (response->m_ResponseStatusCode != 101) {
|
||||
return SetStatus(conn, RESULT_HANDSHAKE_FAILED, "Wrong response status: %i", response->m_ResponseStatusCode);
|
||||
}
|
||||
|
||||
HttpHeader *connection_header, *upgrade_header, *websocket_secret_header;
|
||||
connection_header = response->GetHeader("Connection");
|
||||
upgrade_header = response->GetHeader("Upgrade");
|
||||
websocket_secret_header = response->GetHeader("Sec-WebSocket-Accept");
|
||||
bool connection = connection_header && dmStriCmp(connection_header->m_Value, "Upgrade") == 0;
|
||||
bool upgrade = upgrade_header && dmStriCmp(upgrade_header->m_Value, "websocket") == 0;
|
||||
bool valid_key = websocket_secret_header && ValidateSecretKey(conn, websocket_secret_header->m_Value);
|
||||
|
||||
// Send error to lua?
|
||||
if (!connection)
|
||||
dmLogError("Failed to find the Connection keyword in the response headers");
|
||||
if (!upgrade)
|
||||
@@ -275,11 +273,21 @@ Result VerifyHeaders(WebsocketConnection* conn)
|
||||
dmLogError("Failed to find valid key in the response headers");
|
||||
|
||||
bool ok = connection && upgrade && valid_key;
|
||||
if (!ok) {
|
||||
dmLogError("Response:\n\"%s\"\n", conn->m_Buffer);
|
||||
if(!ok)
|
||||
{
|
||||
return RESULT_HANDSHAKE_FAILED;
|
||||
}
|
||||
|
||||
return ok ? RESULT_OK : RESULT_HANDSHAKE_FAILED;
|
||||
delete conn->m_HandshakeResponse;
|
||||
conn->m_HandshakeResponse = 0;
|
||||
|
||||
// The response might contain both the headers, but also (if successful) the first batch of data
|
||||
uint32_t size = conn->m_BufferSize - (start_of_payload - conn->m_Buffer);
|
||||
conn->m_BufferSize = size;
|
||||
memmove(conn->m_Buffer, start_of_payload, size);
|
||||
conn->m_Buffer[size] = 0;
|
||||
conn->m_HasHandshakeData = conn->m_BufferSize != 0 ? 1 : 0;
|
||||
return RESULT_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@@ -35,9 +35,6 @@ struct WebsocketContext
|
||||
} g_Websocket;
|
||||
|
||||
|
||||
static void HandleCallback(WebsocketConnection* conn, int event);
|
||||
|
||||
|
||||
#define STRING_CASE(_X) case _X: return #_X;
|
||||
|
||||
const char* ResultToString(Result err)
|
||||
@@ -67,6 +64,19 @@ const char* StateToString(State err)
|
||||
|
||||
#undef STRING_CASE
|
||||
|
||||
int dmStriCmp(const char* s1, const char* s2)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
if (!*s1 || !*s2 || tolower((unsigned char) *s1) != tolower((unsigned char) *s2))
|
||||
{
|
||||
return (unsigned char) *s1 - (unsigned char) *s2;
|
||||
}
|
||||
s1++;
|
||||
s2++;
|
||||
}
|
||||
}
|
||||
|
||||
void DebugLog(int level, const char* fmt, ...)
|
||||
{
|
||||
if (level > g_DebugWebSocket)
|
||||
@@ -180,7 +190,6 @@ static WebsocketConnection* CreateConnection(const char* url)
|
||||
conn->m_BufferSize = 0;
|
||||
conn->m_ConnectTimeout = 0;
|
||||
|
||||
dmURI::Parts uri;
|
||||
dmURI::Parse(url, &conn->m_Url);
|
||||
|
||||
if (strcmp(conn->m_Url.m_Scheme, "https") == 0)
|
||||
@@ -195,7 +204,7 @@ static WebsocketConnection* CreateConnection(const char* url)
|
||||
conn->m_SSLSocket = 0;
|
||||
conn->m_Status = RESULT_OK;
|
||||
conn->m_HasHandshakeData = 0;
|
||||
conn->m_WasConnected = 0;
|
||||
conn->m_HandshakeResponse = 0;
|
||||
|
||||
#if defined(HAVE_WSLAY)
|
||||
conn->m_Ctx = 0;
|
||||
@@ -212,6 +221,7 @@ static void DestroyConnection(WebsocketConnection* conn)
|
||||
#endif
|
||||
|
||||
free((void*)conn->m_CustomHeaders);
|
||||
free((void*)conn->m_Protocol);
|
||||
|
||||
if (conn->m_Callback)
|
||||
dmScript::DestroyCallback(conn->m_Callback);
|
||||
@@ -227,6 +237,9 @@ static void DestroyConnection(WebsocketConnection* conn)
|
||||
dmConnectionPool::Return(g_Websocket.m_Pool, conn->m_Connection);
|
||||
#endif
|
||||
|
||||
if (conn->m_HandshakeResponse)
|
||||
delete conn->m_HandshakeResponse;
|
||||
|
||||
|
||||
free((void*)conn->m_Buffer);
|
||||
delete conn;
|
||||
@@ -236,8 +249,6 @@ static void DestroyConnection(WebsocketConnection* conn)
|
||||
|
||||
static void CloseConnection(WebsocketConnection* conn)
|
||||
{
|
||||
State prev_state = conn->m_State;
|
||||
|
||||
// we want it to send this message in the polling
|
||||
if (conn->m_State == STATE_CONNECTED) {
|
||||
#if defined(HAVE_WSLAY)
|
||||
@@ -271,6 +282,7 @@ static int LuaConnect(lua_State* L)
|
||||
const char* url = luaL_checkstring(L, 1);
|
||||
lua_Number timeout = dmScript::CheckTableNumber(L, 2, "timeout", 3000);
|
||||
const char* custom_headers = dmScript::CheckTableString(L, 2, "headers", 0);
|
||||
const char* protocol = dmScript::CheckTableString(L, 2, "protocol", 0);
|
||||
|
||||
if (custom_headers != 0)
|
||||
{
|
||||
@@ -283,6 +295,7 @@ static int LuaConnect(lua_State* L)
|
||||
WebsocketConnection* conn = CreateConnection(url);
|
||||
conn->m_ConnectTimeout = dmTime::GetTime() + timeout * 1000;
|
||||
conn->m_CustomHeaders = custom_headers ? strdup(custom_headers) : 0;
|
||||
conn->m_Protocol = protocol ? strdup(protocol) : 0;
|
||||
|
||||
conn->m_Callback = dmScript::CreateCallback(L, 3);
|
||||
|
||||
@@ -381,12 +394,74 @@ static void HandleCallback(WebsocketConnection* conn, int event, int msg_offset,
|
||||
lua_pushlstring(L, conn->m_Buffer + msg_offset, msg_length);
|
||||
lua_setfield(L, -2, "message");
|
||||
|
||||
if(conn->m_HandshakeResponse)
|
||||
{
|
||||
HandshakeResponse* response = conn->m_HandshakeResponse;
|
||||
|
||||
lua_newtable(L);
|
||||
|
||||
lua_pushnumber(L, response->m_ResponseStatusCode);
|
||||
lua_setfield(L, -2, "status");
|
||||
|
||||
lua_pushstring(L, &conn->m_Buffer[response->m_BodyOffset]);
|
||||
lua_setfield(L, -2, "response");
|
||||
|
||||
lua_newtable(L);
|
||||
for (uint32_t i = 0; i < response->m_Headers.Size(); ++i)
|
||||
{
|
||||
lua_pushstring(L, response->m_Headers[i]->m_Value);
|
||||
lua_setfield(L, -2, response->m_Headers[i]->m_Key);
|
||||
}
|
||||
lua_setfield(L, -2, "headers");
|
||||
|
||||
lua_setfield(L, -2, "handshake_response");
|
||||
|
||||
delete conn->m_HandshakeResponse;
|
||||
conn->m_HandshakeResponse = 0;
|
||||
}
|
||||
|
||||
dmScript::PCall(L, 3, 0);
|
||||
|
||||
dmScript::TeardownCallback(conn->m_Callback);
|
||||
}
|
||||
|
||||
|
||||
HttpHeader::HttpHeader(const char* key, const char* value)
|
||||
{
|
||||
m_Key = strdup(key);
|
||||
m_Value = strdup(value);
|
||||
}
|
||||
|
||||
HttpHeader::~HttpHeader()
|
||||
{
|
||||
free((void*)m_Key);
|
||||
free((void*)m_Value);
|
||||
m_Key = 0;
|
||||
m_Value = 0;
|
||||
}
|
||||
|
||||
HttpHeader* HandshakeResponse::GetHeader(const char* header_key)
|
||||
{
|
||||
for(uint32_t i = 0; i < m_Headers.Size(); ++i)
|
||||
{
|
||||
if (dmStriCmp(m_Headers[i]->m_Key, header_key) == 0)
|
||||
{
|
||||
return m_Headers[i];
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
HandshakeResponse::~HandshakeResponse()
|
||||
{
|
||||
for(uint32_t i = 0; i < m_Headers.Size(); ++i)
|
||||
{
|
||||
delete m_Headers[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// ***************************************************************************************************
|
||||
// Life cycle functions
|
||||
|
||||
@@ -506,6 +581,12 @@ static dmExtension::Result AppFinalize(dmExtension::AppParams* params)
|
||||
|
||||
static dmExtension::Result Finalize(dmExtension::Params* params)
|
||||
{
|
||||
while (!g_Websocket.m_Connections.Empty())
|
||||
{
|
||||
WebsocketConnection* conn = g_Websocket.m_Connections.Back();
|
||||
g_Websocket.m_Connections.Pop();
|
||||
DestroyConnection(conn);
|
||||
}
|
||||
return dmExtension::RESULT_OK;
|
||||
}
|
||||
|
||||
@@ -563,10 +644,7 @@ static dmExtension::Result OnUpdate(dmExtension::Params* params)
|
||||
conn->m_BufferSize = 0;
|
||||
}
|
||||
|
||||
if (conn->m_WasConnected)
|
||||
{
|
||||
HandleCallback(conn, EVENT_DISCONNECTED, 0, conn->m_BufferSize);
|
||||
}
|
||||
|
||||
g_Websocket.m_Connections.EraseSwap(i);
|
||||
--i;
|
||||
@@ -673,7 +751,6 @@ static dmExtension::Result OnUpdate(dmExtension::Params* params)
|
||||
#endif
|
||||
dmSocket::SetBlocking(conn->m_Socket, false);
|
||||
|
||||
conn->m_WasConnected = 1;
|
||||
SetState(conn, STATE_CONNECTED);
|
||||
HandleCallback(conn, EVENT_CONNECTED, 0, 0);
|
||||
}
|
||||
@@ -709,6 +786,11 @@ static dmExtension::Result OnUpdate(dmExtension::Params* params)
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
conn->m_SSLSocket = dmSSLSocket::INVALID_SOCKET_HANDLE;
|
||||
|
||||
EM_ASM({
|
||||
// https://emscripten.org/docs/porting/networking.html#emulated-posix-tcp-sockets-over-websockets
|
||||
Module["websocket"]["subprotocol"] = $0 ? UTF8ToString($0) : null;
|
||||
}, conn->m_Protocol);
|
||||
|
||||
char uri_buffer[dmURI::MAX_URI_LEN];
|
||||
const char* uri;
|
||||
if (conn->m_Url.m_Path[0] != '\0') {
|
||||
|
@@ -78,6 +78,27 @@ namespace dmWebsocket
|
||||
uint32_t m_Type:2;
|
||||
};
|
||||
|
||||
struct HttpHeader
|
||||
{
|
||||
const char* m_Key;
|
||||
const char* m_Value;
|
||||
HttpHeader(const char* key, const char* value);
|
||||
~HttpHeader();
|
||||
};
|
||||
|
||||
struct HandshakeResponse
|
||||
{
|
||||
int m_HttpMajor;
|
||||
int m_HttpMinor;
|
||||
int m_ResponseStatusCode;
|
||||
int m_BodyOffset;
|
||||
dmArray<HttpHeader*> m_Headers;
|
||||
|
||||
~HandshakeResponse();
|
||||
HttpHeader* GetHeader(const char* header);
|
||||
};
|
||||
|
||||
|
||||
struct WebsocketConnection
|
||||
{
|
||||
dmScript::LuaCallbackInfo* m_Callback;
|
||||
@@ -91,6 +112,7 @@ namespace dmWebsocket
|
||||
dmArray<Message> m_Messages; // lengths of the messages in the data buffer
|
||||
uint64_t m_ConnectTimeout;
|
||||
uint8_t m_Key[16];
|
||||
const char* m_Protocol;
|
||||
const char* m_CustomHeaders;
|
||||
State m_State;
|
||||
char* m_Buffer;
|
||||
@@ -99,8 +121,8 @@ namespace dmWebsocket
|
||||
Result m_Status;
|
||||
uint8_t m_SSL:1;
|
||||
uint8_t m_HasHandshakeData:1;
|
||||
uint8_t m_WasConnected:1;
|
||||
uint8_t :6;
|
||||
uint8_t :7;
|
||||
HandshakeResponse* m_HandshakeResponse;
|
||||
};
|
||||
|
||||
// Set error message
|
||||
@@ -148,16 +170,6 @@ namespace dmWebsocket
|
||||
void DebugLog(int level, const char* fmt, ...);
|
||||
#endif
|
||||
|
||||
int dmStriCmp(const char* s1, const char* s2);
|
||||
void DebugPrint(int level, const char* msg, const void* _bytes, uint32_t num_bytes);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user