blob: 85fcab2e889e87e32490275e2acdc586dcdbc1b0 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtWebSockets module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qwebsocket_p.h"
#include <QtCore/qcoreapplication.h>
#include <emscripten.h>
#include <emscripten/bind.h>
using namespace emscripten;
static void q_onErrorCallback(val event)
{
val target = event["target"];
QWebSocketPrivate *wsp = reinterpret_cast<QWebSocketPrivate*>(target["data-context"].as<quintptr>());
Q_ASSERT (wsp);
emit wsp->q_func()->error(wsp->error());
}
static void q_onCloseCallback(val event)
{
val target = event["target"];
QWebSocketPrivate *wsp = reinterpret_cast<QWebSocketPrivate*>(target["data-context"].as<quintptr>());
Q_ASSERT (wsp);
wsp->setSocketState(QAbstractSocket::UnconnectedState);
emit wsp->q_func()->disconnected();
}
static void q_onOpenCallback(val event)
{
val target = event["target"];
QWebSocketPrivate *wsp = reinterpret_cast<QWebSocketPrivate*>(target["data-context"].as<quintptr>());
Q_ASSERT (wsp);
wsp->setSocketState(QAbstractSocket::ConnectedState);
emit wsp->q_func()->connected();
}
static void q_onIncomingMessageCallback(val event)
{
val target = event["target"];
if (event["data"].typeOf().as<std::string>() == "string") {
QWebSocketPrivate *wsp = reinterpret_cast<QWebSocketPrivate*>(target["data-context"].as<quintptr>());
Q_ASSERT (wsp);
const QString message = QString::fromStdString(event["data"].as<std::string>());
if (!message.isEmpty())
wsp->q_func()->textMessageReceived(message);
} else {
val reader = val::global("FileReader").new_();
reader.set("onload", val::module_property("QWebSocketPrivate_readBlob"));
reader.set("data-context", target["data-context"]);
reader.call<void>("readAsArrayBuffer", event["data"]);
}
}
static void q_readBlob(val event)
{
val fileReader = event["target"];
QWebSocketPrivate *wsp = reinterpret_cast<QWebSocketPrivate*>(fileReader["data-context"].as<quintptr>());
Q_ASSERT (wsp);
// Set up source typed array
val result = fileReader["result"]; // ArrayBuffer
val Uint8Array = val::global("Uint8Array");
val sourceTypedArray = Uint8Array.new_(result);
// Allocate and set up destination typed array
const size_t size = result["byteLength"].as<size_t>();
QByteArray buffer(size, Qt::Uninitialized);
val destinationTypedArray = Uint8Array.new_(val::module_property("HEAPU8")["buffer"],
reinterpret_cast<quintptr>(buffer.data()), size);
destinationTypedArray.call<void>("set", sourceTypedArray);
wsp->q_func()->binaryMessageReceived(buffer);
}
EMSCRIPTEN_BINDINGS(wasm_module) {
function("QWebSocketPrivate_onErrorCallback", q_onErrorCallback);
function("QWebSocketPrivate_onCloseCallback", q_onCloseCallback);
function("QWebSocketPrivate_onOpenCallback", q_onOpenCallback);
function("QWebSocketPrivate_onIncomingMessageCallback", q_onIncomingMessageCallback);
function("QWebSocketPrivate_readBlob", q_readBlob);
}
qint64 QWebSocketPrivate::sendTextMessage(const QString &message)
{
socketContext.call<void>("send", message.toStdString());
return message.length();
}
qint64 QWebSocketPrivate::sendBinaryMessage(const QByteArray &data)
{
// Make a copy of the payload data; we don't know how long WebSocket.send() will
// retain the memory view, while the QByteArray passed to this function may be
// destroyed as soon as this function returns. In addition, the WebSocket.send()
// API does not accept data from a view backet by a SharedArrayBuffer, which will
// be the case for the view produced by typed_memory_view() when threads are enabled.
val Uint8Array = val::global("Uint8Array");
val dataCopy = Uint8Array.new_(data.size());
val dataView = val(typed_memory_view(data.size(),
reinterpret_cast<const unsigned char *>
(data.constData())));
dataCopy.call<void>("set", dataView);
socketContext.call<void>("send", dataCopy);
return data.length();
}
void QWebSocketPrivate::close(QWebSocketProtocol::CloseCode closeCode, QString reason)
{
Q_Q(QWebSocket);
m_closeCode = closeCode;
m_closeReason = reason;
Q_EMIT q->aboutToClose();
setSocketState(QAbstractSocket::ClosingState);
socketContext.call<void>("close", static_cast<quint16>(closeCode),
reason.toLatin1().toStdString());
}
void QWebSocketPrivate::open(const QNetworkRequest &request, bool mask)
{
Q_UNUSED(mask)
Q_Q(QWebSocket);
const QUrl url = request.url();
if (!url.isValid() || url.toString().contains(QStringLiteral("\r\n"))) {
setErrorString(QWebSocket::tr("Invalid URL."));
Q_EMIT q->error(QAbstractSocket::ConnectionRefusedError);
return;
}
setSocketState(QAbstractSocket::ConnectingState);
const std::string urlbytes = url.toString().toStdString();
// HTML WebSockets do not support arbitrary request headers, but
// do support the WebSocket protocol header. This header is
// required for some use cases like MQTT.
const std::string protocolHeaderValue = request.rawHeader("Sec-WebSocket-Protocol").toStdString();
val webSocket = val::global("WebSocket");
socketContext = !protocolHeaderValue.empty()
? webSocket.new_(urlbytes, protocolHeaderValue)
: webSocket.new_(urlbytes);
socketContext.set("onerror", val::module_property("QWebSocketPrivate_onErrorCallback"));
socketContext.set("onclose", val::module_property("QWebSocketPrivate_onCloseCallback"));
socketContext.set("onopen", val::module_property("QWebSocketPrivate_onOpenCallback"));
socketContext.set("onmessage", val::module_property("QWebSocketPrivate_onIncomingMessageCallback"));
socketContext.set("data-context", val(quintptr(reinterpret_cast<void *>(this))));
}
bool QWebSocketPrivate::isValid() const
{
return (!socketContext.isUndefined() &&
(m_socketState == QAbstractSocket::ConnectedState));
}