axmol/core/network/WebSocket-wasm.cpp

145 lines
4.6 KiB
C++

#include "network/WebSocket-wasm.h"
#include "yasio/errc.hpp"
#include "base/Logging.h"
NS_AX_BEGIN
namespace network
{
EM_BOOL WebSocket::em_ws_onopen(int eventType, const EmscriptenWebSocketOpenEvent* websocketEvent, void* userData)
{
auto ws = static_cast<WebSocket*>(userData);
if (!ws || !ws->_delegate)
return EM_TRUE;
ws->_state = WebSocket::State::OPEN;
ws->_delegate->onOpen(ws);
return EM_TRUE;
}
EM_BOOL WebSocket::em_ws_onerror(int eventType, const EmscriptenWebSocketErrorEvent* websocketEvent, void* userData)
{
auto ws = static_cast<WebSocket*>(userData);
if (!ws || !ws->_delegate)
return EM_TRUE;
ws->_state = WebSocket::State::CLOSED;
ws->_delegate->onError(ws, ErrorCode::CONNECTION_FAILURE);
return EM_TRUE;
}
EM_BOOL WebSocket::em_ws_onclose(int eventType, const EmscriptenWebSocketCloseEvent* websocketEvent, void* userData)
{
auto ws = static_cast<WebSocket*>(userData);
if (!ws || !ws->_delegate)
return EM_TRUE;
ws->_state = WebSocket::State::CLOSED;
ws->_delegate->onClose(ws);
return EM_TRUE;
}
EM_BOOL WebSocket::em_ws_onmessage(int eventType, const EmscriptenWebSocketMessageEvent* websocketEvent, void* userData)
{
auto ws = static_cast<WebSocket*>(userData);
if (!ws || !ws->_delegate)
return EM_TRUE;
WebSocket::Data dataView;
dataView.bytes = reinterpret_cast<const char*>(websocketEvent->data);
dataView.isBinary = !websocketEvent->isText;
dataView.len = websocketEvent->numBytes;
ws->_delegate->onMessage(ws, dataView);
return EM_TRUE;
}
WebSocket::WebSocket() {}
WebSocket::~WebSocket() {}
bool WebSocket::open(Delegate* delegate, std::string_view url, std::string_view caFilePath, std::string_view protocols)
{
if (url.empty())
{
AXLOG("ws open fail, url is empty!");
return false;
}
_delegate = delegate;
_url = url;
_subProtocols = protocols;
EmscriptenWebSocketCreateAttributes ws_attrs = {_url.c_str(),
_subProtocols.empty() ? nullptr : _subProtocols.c_str(), EM_TRUE};
AXLOG("ws open url: %s, protocols: %s", ws_attrs.url, ws_attrs.protocols);
_state = WebSocket::State::CONNECTING;
_wsfd = emscripten_websocket_new(&ws_attrs);
// chrome/edge can't connect
// firefox works with "Sec-Fetch-Site: cross-site" in request header
// refer: https://github.com/emscripten-core/emscripten/issues/19100
// wasm websocket callback thread same with axmol render thread
emscripten_websocket_set_onopen_callback(_wsfd, this, em_ws_onopen);
emscripten_websocket_set_onerror_callback(_wsfd, this, em_ws_onerror);
emscripten_websocket_set_onclose_callback(_wsfd, this, em_ws_onclose);
emscripten_websocket_set_onmessage_callback(_wsfd, this, em_ws_onmessage);
return true;
}
/**
* @brief Sends string data to websocket server.
*
* @param message string data.
* @lua sendstring
*/
void WebSocket::send(std::string_view message)
{
auto error = emscripten_websocket_send_utf8_text(_wsfd, message.data());
if (error)
AXLOG("Failed to emscripten_websocket_send_binary(): %d", error);
}
/**
* @brief Sends binary data to websocket server.
*
* @param binaryMsg binary string data.
* @param len the size of binary string data.
* @lua sendstring
*/
void WebSocket::send(const void* data, unsigned int len)
{
auto error = emscripten_websocket_send_binary(_wsfd, const_cast<void*>(data), len);
if (error)
AXLOG("Failed to emscripten_websocket_send_binary(): %d", error);
}
/**
* @brief Closes the connection to server synchronously.
* @note It's a synchronous method, it will not return until websocket thread exits.
*/
void WebSocket::close()
{
closeAsync(); // TODO
}
/**
* @brief Closes the connection to server asynchronously.
* @note It's an asynchronous method, it just notifies websocket thread to exit and returns directly,
* If using 'closeAsync' to close websocket connection,
* be careful of not using destructed variables in the callback of 'onClose'.
*/
void WebSocket::closeAsync()
{
// close code: Uncaught DOMException: Failed to execute 'close' on 'WebSocket':
// The code must be either 1000, or between 3000 and 4999. 1024 is neither.
EMSCRIPTEN_RESULT error =
emscripten_websocket_close(_wsfd, 3000 - yasio::errc::shutdown_by_localhost, "shutdown by localhost");
if (!error)
_state = WebSocket::State::CLOSING;
else
AXLOG("Failed to emscripten_websocket_close(): %d", error);
}
} // namespace network
NS_AX_END