summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLorn Potter <lorn.potter@gmail.com>2018-01-18 07:05:03 +1000
committerFrederik Gladhorn <frederik.gladhorn@qt.io>2018-05-16 09:18:02 +0000
commit5c64a39482cba6ad4bc4337bd7475f96cfad9109 (patch)
tree54d049fd491f87de094c2929e21c6c142d4c13d6
parent91bb60bba33a8c7df49835bfacfe9a8dc7381a1e (diff)
downloadqtwebsockets-5c64a39482cba6ad4bc4337bd7475f96cfad9109.tar.gz
webassembly: add wasm support
Change-Id: I2284f8606d512943d2abc2a96fc48effcc45a786 Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
-rw-r--r--src/websockets/qwebsocket.cpp7
-rw-r--r--src/websockets/qwebsocket_wasm_p.cpp912
-rw-r--r--src/websockets/qwebsocket_wasm_p.h228
-rw-r--r--src/websockets/qwebsocketserver_p.cpp5
-rw-r--r--src/websockets/websockets.pro14
5 files changed, 1163 insertions, 3 deletions
diff --git a/src/websockets/qwebsocket.cpp b/src/websockets/qwebsocket.cpp
index 1b0fc35..becd58b 100644
--- a/src/websockets/qwebsocket.cpp
+++ b/src/websockets/qwebsocket.cpp
@@ -270,7 +270,11 @@ not been filled in with new information when the signal returns.
\sa ping()
*/
#include "qwebsocket.h"
+#ifndef Q_OS_HTML5
#include "qwebsocket_p.h"
+#else
+#include "qwebsocket_wasm_p.h"
+#endif
#include <QtCore/QUrl>
#include <QtNetwork/QTcpSocket>
@@ -662,6 +666,7 @@ quint16 QWebSocket::peerPort() const
return d->peerPort();
}
+#ifndef Q_OS_HTML5
#ifndef QT_NO_NETWORKPROXY
/*!
Returns the currently configured proxy
@@ -681,7 +686,7 @@ void QWebSocket::setProxy(const QNetworkProxy &networkProxy)
d->setProxy(networkProxy);
}
#endif
-
+#endif
/*!
Sets the generator to use for creating masks to \a maskGenerator.
The default QWebSocket generator can be reset by supplying a \e nullptr.
diff --git a/src/websockets/qwebsocket_wasm_p.cpp b/src/websockets/qwebsocket_wasm_p.cpp
new file mode 100644
index 0000000..db71cba
--- /dev/null
+++ b/src/websockets/qwebsocket_wasm_p.cpp
@@ -0,0 +1,912 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Kurt Pattyn <pattyn.kurt@gmail.com>.
+** 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.h"
+#include "qwebsocket_wasm_p.h"
+#include "qwebsocketprotocol_p.h"
+#include "qwebsockethandshakerequest_p.h"
+#include "qwebsockethandshakeresponse_p.h"
+#include "qdefaultmaskgenerator_p.h"
+
+#include <QtCore/QUrl>
+#include <QtCore/QByteArray>
+#include <QtCore/QCryptographicHash>
+#include <QtCore/QRegularExpression>
+#include <QtCore/QStringList>
+
+#include <QtCore/QStringBuilder> //for more efficient string concatenation
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QDebug>
+
+#include <limits>
+
+#include <emscripten.h>
+#include <emscripten/html5.h>
+#include <emscripten/bind.h>
+
+using namespace emscripten;
+QT_BEGIN_NAMESPACE
+
+QByteArray m_messageArray;
+
+val getBinaryMessage()
+{
+ return val(typed_memory_view(m_messageArray.size(), reinterpret_cast<const unsigned char *>(m_messageArray.constData())));
+}
+
+EMSCRIPTEN_BINDINGS(wasm_module) {
+ function("getBinaryMessage", &getBinaryMessage);
+}
+
+QWebSocketConfiguration::QWebSocketConfiguration()
+{
+}
+
+/*!
+ \internal
+*/
+QWebSocketPrivate::QWebSocketPrivate(const QString &origin, QWebSocketProtocol::Version version,
+ QWebSocket *pWebSocket) :
+ QObjectPrivate(),
+ q_ptr(pWebSocket),
+ m_errorString(),
+ m_version(version),
+ m_resourceName(),
+ m_origin(origin),
+ m_request(),
+ m_protocol(),
+ m_extension(),
+ m_socketState(QAbstractSocket::UnconnectedState),
+ m_pauseMode(QAbstractSocket::PauseNever),
+ m_readBufferSize(0),
+ m_key(),
+ m_mustMask(true),
+ m_isClosingHandshakeSent(false),
+ m_isClosingHandshakeReceived(false),
+ m_closeCode(QWebSocketProtocol::CloseCodeNormal),
+ m_closeReason(),
+ m_pingTimer(),
+ m_dataProcessor(),
+ m_configuration(),
+ m_pMaskGenerator(&m_defaultMaskGenerator),
+ m_defaultMaskGenerator(),
+ m_handshakeState(NothingDoneState)
+{
+}
+
+/*!
+ \internal
+*/
+QWebSocketPrivate::QWebSocketPrivate(QTcpSocket *pTcpSocket, QWebSocketProtocol::Version version,
+ QWebSocket *pWebSocket) :
+ QObjectPrivate(),
+ q_ptr(pWebSocket),
+ m_errorString(),
+ m_version(version),
+ m_resourceName(),
+ m_origin(),
+ m_request(),
+ m_protocol(),
+ m_extension(),
+ m_socketState(QAbstractSocket::UnconnectedState),
+ m_pauseMode(QAbstractSocket::PauseNever),
+ m_readBufferSize(0),
+ m_key(),
+ m_mustMask(true),
+ m_isClosingHandshakeSent(false),
+ m_isClosingHandshakeReceived(false),
+ m_closeCode(QWebSocketProtocol::CloseCodeNormal),
+ m_closeReason(),
+ m_pingTimer(),
+ m_dataProcessor(),
+ m_configuration(),
+ m_pMaskGenerator(&m_defaultMaskGenerator),
+ m_defaultMaskGenerator(),
+ m_handshakeState(NothingDoneState)
+{
+ Q_UNUSED(pTcpSocket)
+}
+/*!
+ \internal
+*/
+void QWebSocketPrivate::init()
+{
+ Q_ASSERT(q_ptr);
+ Q_ASSERT(m_pMaskGenerator);
+
+ m_pMaskGenerator->seed();
+ makeConnections();
+}
+
+/*!
+ \internal
+*/
+QWebSocketPrivate::~QWebSocketPrivate()
+{
+}
+
+/*!
+ \internal
+*/
+void QWebSocketPrivate::closeGoingAway()
+{
+ if (state() == QAbstractSocket::ConnectedState)
+ close(QWebSocketProtocol::CloseCodeGoingAway, QWebSocket::tr("Connection closed"));
+ releaseConnections();
+}
+
+/*!
+ \internal
+ */
+void QWebSocketPrivate::abort()
+{
+
+}
+
+/*!
+ \internal
+ */
+QAbstractSocket::SocketError QWebSocketPrivate::error() const
+{
+ QAbstractSocket::SocketError err = m_lastError;
+ return err;
+}
+
+/*!
+ \internal
+ */
+QString QWebSocketPrivate::errorString() const
+{
+ QString errMsg;
+ if (!m_errorString.isEmpty())
+ errMsg = m_errorString;
+ return errMsg;
+}
+
+/*!
+ \internal
+ */
+qint64 QWebSocketPrivate::sendTextMessage(const QString &message)
+{
+ EM_ASM_ARGS({
+ var wsMessage = Pointer_stringify($0);
+ if (window.webSocker == undefined) {
+ console.log("cannot find websocket object");
+ } else {
+ window.webSocker.send(wsMessage);
+ }
+ }, message.toLocal8Bit().constData()
+ );
+
+ return message.length();
+}
+
+/*!
+ \internal
+ */
+qint64 QWebSocketPrivate::sendBinaryMessage(const QByteArray &data)
+{
+ m_messageArray = data;
+
+ EM_ASM_ARGS({
+ var array = Module.getBinaryMessage();
+ if (window.webSocker == undefined) {
+ console.log("cannot find websocket object");
+ } else {
+ window.webSocker.binaryType = "arraybuffer";
+ window.webSocker.send(array);
+ }
+ }, data.constData()
+ );
+
+ return data.length();
+}
+
+/*!
+ Called from QWebSocketServer
+ \internal
+ */
+QWebSocket *QWebSocketPrivate::upgradeFrom(QTcpSocket *pTcpSocket,
+ const QWebSocketHandshakeRequest &request,
+ const QWebSocketHandshakeResponse &response,
+ QObject *parent)
+{
+ QWebSocket *pWebSocket = new QWebSocket(pTcpSocket, response.acceptedVersion(), parent);
+ if (Q_LIKELY(pWebSocket)) {
+ QNetworkRequest netRequest(request.requestUrl());
+ const auto headers = request.headers();
+ for (auto it = headers.begin(), end = headers.end(); it != end; ++it)
+ netRequest.setRawHeader(it.key().toLatin1(), it.value().toLatin1());
+
+ pWebSocket->d_func()->setExtension(response.acceptedExtension());
+ pWebSocket->d_func()->setOrigin(request.origin());
+ pWebSocket->d_func()->setRequest(netRequest);
+ pWebSocket->d_func()->setProtocol(response.acceptedProtocol());
+ pWebSocket->d_func()->setResourceName(request.requestUrl().toString(QUrl::RemoveUserInfo));
+ //a server should not send masked frames
+ pWebSocket->d_func()->enableMasking(false);
+ }
+
+ return pWebSocket;
+}
+
+/*!
+ \internal
+ */
+void QWebSocketPrivate::close(QWebSocketProtocol::CloseCode closeCode, QString reason)
+{
+ Q_Q(QWebSocket);
+ m_closeCode = closeCode;
+ m_closeReason = reason;
+ const quint16 closeReason = (quint16)closeCode;
+ Q_EMIT q->aboutToClose();
+ QCoreApplication::processEvents();
+
+ EM_ASM_ARGS({
+ var reasonMessage = Pointer_stringify($0);
+ var closingCode = $1;
+
+ if (window.webSocker == undefined) {
+ console.log("cannot find websocket object");
+ } else {
+ window.webSocker.close(closingCode,reasonMessage);
+ }
+ }, reason.toLatin1().data(),
+ closeReason
+ );
+}
+
+/*!
+ \internal
+ */
+void QWebSocketPrivate::onOpenCallback(void *data)
+{
+ QWebSocketPrivate *handler = reinterpret_cast<QWebSocketPrivate*>(data);
+ if (handler)
+ handler->emitConnected();
+ QCoreApplication::processEvents();
+}
+
+/*!
+ \internal
+ */
+void QWebSocketPrivate::emitConnected()
+{
+ Q_Q(QWebSocket);
+ Q_EMIT q->connected();
+ QCoreApplication::processEvents();
+}
+
+/*!
+ \internal
+ */
+void QWebSocketPrivate::onErrorCallback(void *data, int evt)
+{
+ QWebSocketPrivate *handler = reinterpret_cast<QWebSocketPrivate*>(data);
+
+ if (handler)
+ handler->emitErrorReceived(evt);
+}
+
+/*!
+ \internal
+ */
+void QWebSocketPrivate::emitErrorReceived(int errorEvt)
+{
+ m_lastError = static_cast<QAbstractSocket::SocketError>(errorEvt);
+ Q_Q(QWebSocket);
+ Q_EMIT q->error(m_lastError);
+ QCoreApplication::processEvents();
+}
+
+/*!
+ \internal
+ */
+void QWebSocketPrivate::onCloseCallback(void *data, int /*evt*/)
+{
+ QWebSocketPrivate *handler = reinterpret_cast<QWebSocketPrivate*>(data);
+
+ if (handler)
+ handler->emitDisconnected();
+}
+
+/*!
+ \internal
+ */
+void QWebSocketPrivate::emitDisconnected()
+{
+ Q_Q(QWebSocket);
+ Q_EMIT q->disconnected();
+ QCoreApplication::processEvents();
+}
+
+/*!
+ \internal
+ */
+void QWebSocketPrivate::onIncomingMessageCallback(void *data, int evt, int length, int dataType)
+{
+ QWebSocketPrivate *handler = reinterpret_cast<QWebSocketPrivate*>(data);
+ if (handler) {
+ if (dataType == 0) { //string
+ QLatin1String message((char *)evt);
+ handler->emitTextMessageReceived(message);
+ } else if (dataType == 1) {//blob ??
+ handler->emitBinaryMessageReceived(QByteArray::fromRawData((char *)evt, length));
+ } else if (dataType == 2) {//arraybuffer
+ handler->emitBinaryMessageReceived(QByteArray::fromRawData((char *)evt, length));
+ }
+ } else {
+ //error
+ }
+}
+
+/*!
+ \internal
+ */
+void QWebSocketPrivate::emitTextMessageReceived(const QString &message)
+{
+ Q_Q(QWebSocket);
+ Q_EMIT q->textMessageReceived(message);
+ QCoreApplication::processEvents();
+}
+
+/*!
+ \internal
+ */
+void QWebSocketPrivate::emitBinaryMessageReceived(const QByteArray &message)
+{
+ Q_Q(QWebSocket);
+ Q_EMIT q->binaryMessageReceived(message);
+ QCoreApplication::processEvents();
+}
+
+/*!
+ \internal
+ */
+void QWebSocketPrivate::open(const QNetworkRequest &request, bool mask)
+{
+ Q_UNUSED(mask)
+ Q_Q(QWebSocket);
+ 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;
+ }
+ QList <QByteArray> headerList = request.rawHeaderList();
+ if (headerList.count() > 0) {
+
+ }
+ jsRequest(url.toString(),
+ (void *)&onOpenCallback,
+ (void *)&onCloseCallback,
+ (void *)&onErrorCallback,
+ (void *)&onIncomingMessageCallback);
+}
+
+void QWebSocketPrivate::jsRequest(const QString &url, void *openCallback, void *closeCallback, void *errorCallback, void *incomingMessageCallback)
+{
+ EM_ASM_ARGS({
+ var wsUri = Pointer_stringify($0);
+ var handler = $1;
+ var onOpenCallbackPointer = $2;
+ var onCloseCallback = $3;
+ var onErrorCallback = $4;
+ var onIncomingMessageCallback = $5;
+
+ function connectWebSocket() {
+ if (window.webSocker == undefined) {
+ window.webSocker = {};
+
+ window.webSocker = new WebSocket(wsUri);
+
+ window.webSocker.onopen = onOpen;
+ window.webSocker.onclose = onClose;
+ window.webSocker.onmessage = onMessage;
+ window.webSocker.onerror = onError;
+ }
+ }
+
+ function onOpen(evt) {
+ Runtime.dynCall('vi', onOpenCallbackPointer, [handler]);
+ }
+
+ function onClose(evt) {
+ window.webSocker = {};
+ Runtime.dynCall('vii', onCloseCallback, [handler, evt.code]);
+ }
+
+ function onError(evt) {
+ Runtime.dynCall('vii', onErrorCallback, [handler, evt.error]);
+ }
+
+ function onMessage(evt) {
+
+ var ptr;
+ var bufferLength = 0;
+ var dataType;
+ if (window.webSocker.binaryType == "arraybuffer" && typeof evt.data == "object") {
+
+ var byteArray = new Uint8Array(evt.data);
+ bufferLength = byteArray.length;
+
+ ptr = _malloc(byteArray.length);
+ HEAP8.set(byteArray, ptr);
+
+ dataType = 2;
+ }
+ else if (window.webSocker.binaryType == "blob") {
+ var byteArray = new Int8Array($0);
+ console.log("type blob");
+ ptr = new Blob(byteArray.buffer);
+ dataType = 1;
+ }
+ else if (typeof evt.data == "string") {
+ dataType = 0;
+ ptr = allocate(intArrayFromString(evt.data), 'i8', ALLOC_NORMAL);
+ }
+
+ Runtime.dynCall('viiii', onIncomingMessageCallback, [handler, ptr, bufferLength, dataType]);
+ _free(ptr);
+ }
+
+ connectWebSocket();
+
+ }, url.toLatin1().data(),
+ this,
+ (void *)openCallback,
+ (void *)closeCallback,
+ (void *)errorCallback,
+ (void *)incomingMessageCallback
+ );
+ QCoreApplication::processEvents();
+}
+
+/*!
+ \internal
+ */
+void QWebSocketPrivate::ping(const QByteArray &payload)
+{
+ QByteArray payloadTruncated = payload.left(125);
+ m_pingTimer.restart();
+ quint32 maskingKey = 0;
+ if (m_mustMask)
+ maskingKey = generateMaskingKey();
+ QByteArray pingFrame = getFrameHeader(QWebSocketProtocol::OpCodePing, payloadTruncated.size(),
+ maskingKey, true);
+
+ if (m_mustMask)
+ QWebSocketProtocol::mask(&payloadTruncated, maskingKey);
+ pingFrame.append(payloadTruncated);
+ qint64 ret = sendBinaryMessage(pingFrame);
+ Q_UNUSED(ret);
+}
+
+/*!
+ \internal
+ Sets the version to use for the WebSocket protocol;
+ this must be set before the socket is opened.
+*/
+void QWebSocketPrivate::setVersion(QWebSocketProtocol::Version version)
+{
+ if (m_version != version)
+ m_version = version;
+}
+
+/*!
+ \internal
+ Sets the resource name of the connection; must be set before the socket is openend
+*/
+void QWebSocketPrivate::setResourceName(const QString &resourceName)
+{
+ if (m_resourceName != resourceName)
+ m_resourceName = resourceName;
+}
+
+/*!
+ \internal
+ */
+void QWebSocketPrivate::setRequest(const QNetworkRequest &request)
+{
+ if (m_request != request)
+ m_request = request;
+}
+
+/*!
+ \internal
+ */
+void QWebSocketPrivate::setOrigin(const QString &origin)
+{
+ if (m_origin != origin)
+ m_origin = origin;
+}
+
+/*!
+ \internal
+ */
+void QWebSocketPrivate::setProtocol(const QString &protocol)
+{
+ if (m_protocol != protocol)
+ m_protocol = protocol;
+}
+
+/*!
+ \internal
+ */
+void QWebSocketPrivate::setExtension(const QString &extension)
+{
+ if (m_extension != extension)
+ m_extension = extension;
+}
+
+/*!
+ \internal
+ */
+void QWebSocketPrivate::enableMasking(bool enable)
+{
+ if (m_mustMask != enable)
+ m_mustMask = enable;
+}
+
+/*!
+ * \internal
+ */
+void QWebSocketPrivate::makeConnections()
+{
+ Q_Q(QWebSocket);
+
+ QObject::connect(&m_dataProcessor, &QWebSocketDataProcessor::textFrameReceived, q,
+ &QWebSocket::textFrameReceived);
+ QObject::connect(&m_dataProcessor, &QWebSocketDataProcessor::binaryFrameReceived, q,
+ &QWebSocket::binaryFrameReceived);
+ QObject::connect(&m_dataProcessor, &QWebSocketDataProcessor::binaryMessageReceived, q,
+ &QWebSocket::binaryMessageReceived);
+ QObject::connect(&m_dataProcessor, &QWebSocketDataProcessor::textMessageReceived, q,
+ &QWebSocket::textMessageReceived);
+ QObjectPrivate::connect(&m_dataProcessor, &QWebSocketDataProcessor::errorEncountered, this,
+ &QWebSocketPrivate::close);
+ QObjectPrivate::connect(&m_dataProcessor, &QWebSocketDataProcessor::closeReceived, this,
+ &QWebSocketPrivate::processClose);
+}
+
+/*!
+ * \internal
+ */
+void QWebSocketPrivate::releaseConnections()
+{
+
+ m_dataProcessor.disconnect();
+}
+
+/*!
+ \internal
+ */
+QWebSocketProtocol::Version QWebSocketPrivate::version() const
+{
+ return m_version;
+}
+
+/*!
+ \internal
+ */
+QString QWebSocketPrivate::resourceName() const
+{
+ return m_resourceName;
+}
+
+/*!
+ \internal
+ */
+QNetworkRequest QWebSocketPrivate::request() const
+{
+ return m_request;
+}
+
+/*!
+ \internal
+ */
+QString QWebSocketPrivate::origin() const
+{
+ return m_origin;
+}
+
+/*!
+ \internal
+ */
+QString QWebSocketPrivate::protocol() const
+{
+ return m_protocol;
+}
+
+/*!
+ \internal
+ */
+QString QWebSocketPrivate::extension() const
+{
+ return m_extension;
+}
+
+/*!
+ * \internal
+ */
+QWebSocketProtocol::CloseCode QWebSocketPrivate::closeCode() const
+{
+ return m_closeCode;
+}
+
+/*!
+ * \internal
+ */
+QString QWebSocketPrivate::closeReason() const
+{
+ return m_closeReason;
+}
+
+///*!
+// * \internal
+// */
+QByteArray QWebSocketPrivate::getFrameHeader(QWebSocketProtocol::OpCode opCode,
+ quint64 payloadLength, quint32 maskingKey,
+ bool lastFrame)
+{
+ QByteArray header;
+ bool ok = payloadLength <= 0x7FFFFFFFFFFFFFFFULL;
+
+ if (Q_LIKELY(ok)) {
+ //FIN, RSV1-3, opcode (RSV-1, RSV-2 and RSV-3 are zero)
+ quint8 byte = static_cast<quint8>((opCode & 0x0F) | (lastFrame ? 0x80 : 0x00));
+ header.append(static_cast<char>(byte));
+
+ byte = 0x00;
+ if (maskingKey != 0)
+ byte |= 0x80;
+ if (payloadLength <= 125) {
+ byte |= static_cast<quint8>(payloadLength);
+ header.append(static_cast<char>(byte));
+ } else if (payloadLength <= 0xFFFFU) {
+ byte |= 126;
+ header.append(static_cast<char>(byte));
+ header.append(static_cast<const char *>(static_cast<const void *>(&payloadLength)), 2);
+ } else if (payloadLength <= 0x7FFFFFFFFFFFFFFFULL) {
+ byte |= 127;
+ header.append(static_cast<char>(byte));
+ header.append(static_cast<const char *>(static_cast<const void *>(&payloadLength)), 8);
+ }
+
+ if (maskingKey != 0) {
+ header.append(static_cast<const char *>(static_cast<const void *>(&maskingKey)),
+ sizeof(quint32));
+ }
+ } else {
+ setErrorString(QStringLiteral("WebSocket::getHeader: payload too big!"));
+ Q_EMIT q_ptr->error(QAbstractSocket::DatagramTooLargeError);
+ }
+
+ return header;
+}
+
+
+/*!
+ \internal
+ */
+quint32 QWebSocketPrivate::generateMaskingKey() const
+{
+ return m_pMaskGenerator->nextMask();
+}
+
+/*!
+ \internal
+ */
+QByteArray QWebSocketPrivate::generateKey() const
+{
+ QByteArray key;
+
+ for (int i = 0; i < 4; ++i) {
+ const quint32 tmp = m_pMaskGenerator->nextMask();
+ key.append(static_cast<const char *>(static_cast<const void *>(&tmp)), sizeof(quint32));
+ }
+
+ return key.toBase64();
+}
+
+
+/*!
+ \internal
+ */
+QString QWebSocketPrivate::calculateAcceptKey(const QByteArray &key) const
+{
+ const QByteArray tmpKey = key + QByteArrayLiteral("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
+ const QByteArray hash = QCryptographicHash::hash(tmpKey, QCryptographicHash::Sha1).toBase64();
+ return QString::fromLatin1(hash);
+}
+
+/*!
+ \internal
+ */
+void QWebSocketPrivate::processClose(QWebSocketProtocol::CloseCode closeCode, QString closeReason)
+{
+ m_isClosingHandshakeReceived = true;
+ close(closeCode, closeReason);
+}
+
+/*!
+ \internal
+ */
+QAbstractSocket::SocketState QWebSocketPrivate::state() const
+{
+ return m_socketState;
+}
+
+/*!
+ \internal
+ */
+void QWebSocketPrivate::setSocketState(QAbstractSocket::SocketState state)
+{
+ Q_Q(QWebSocket);
+ if (m_socketState != state) {
+ m_socketState = state;
+ Q_EMIT q->stateChanged(m_socketState);
+ QCoreApplication::processEvents();
+ }
+}
+
+/*!
+ \internal
+ */
+void QWebSocketPrivate::setErrorString(const QString &errorString)
+{
+ if (m_errorString != errorString)
+ m_errorString = errorString;
+}
+
+/*!
+ \internal
+ */
+QHostAddress QWebSocketPrivate::localAddress() const
+{
+ QHostAddress address;
+ return address;
+}
+
+/*!
+ \internal
+ */
+quint16 QWebSocketPrivate::localPort() const
+{
+ quint16 port = 0;
+ return port;
+}
+
+/*!
+ \internal
+ */
+QAbstractSocket::PauseModes QWebSocketPrivate::pauseMode() const
+{
+ return m_pauseMode;
+}
+
+/*!
+ \internal
+ */
+QHostAddress QWebSocketPrivate::peerAddress() const
+{
+ QHostAddress address;
+ return address;
+}
+
+/*!
+ \internal
+ */
+QString QWebSocketPrivate::peerName() const
+{
+ QString name;
+ return name;
+}
+
+/*!
+ \internal
+ */
+quint16 QWebSocketPrivate::peerPort() const
+{
+ quint16 port = 0;
+ return port;
+}
+
+
+/*!
+ \internal
+ */
+void QWebSocketPrivate::setMaskGenerator(const QMaskGenerator *maskGenerator)
+{
+ if (!maskGenerator)
+ m_pMaskGenerator = &m_defaultMaskGenerator;
+ else if (maskGenerator != m_pMaskGenerator)
+ m_pMaskGenerator = const_cast<QMaskGenerator *>(maskGenerator);
+}
+
+/*!
+ \internal
+ */
+const QMaskGenerator *QWebSocketPrivate::maskGenerator() const
+{
+ Q_ASSERT(m_pMaskGenerator);
+ return m_pMaskGenerator;
+}
+
+/*!
+ \internal
+ */
+qint64 QWebSocketPrivate::readBufferSize() const
+{
+ return m_readBufferSize;
+}
+
+/*!
+ \internal
+ */
+void QWebSocketPrivate::resume()
+{
+}
+
+/*!
+ \internal
+ */
+void QWebSocketPrivate::setPauseMode(QAbstractSocket::PauseModes pauseMode)
+{
+ m_pauseMode = pauseMode;
+}
+
+/*!
+ \internal
+ */
+void QWebSocketPrivate::setReadBufferSize(qint64 size)
+{
+ m_readBufferSize = size;
+}
+
+/*!
+ \internal
+ */
+bool QWebSocketPrivate::isValid() const
+{
+ return true;
+}
+
+QT_END_NAMESPACE
diff --git a/src/websockets/qwebsocket_wasm_p.h b/src/websockets/qwebsocket_wasm_p.h
new file mode 100644
index 0000000..b293f6c
--- /dev/null
+++ b/src/websockets/qwebsocket_wasm_p.h
@@ -0,0 +1,228 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 Kurt Pattyn <pattyn.kurt@gmail.com>.
+** 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$
+**
+****************************************************************************/
+
+#ifndef QWEBSOCKET_WASM_P_H
+#define QWEBSOCKET_WASM_P_H
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtCore/QUrl>
+#include <QtNetwork/QHostAddress>
+#include <QtNetwork/QNetworkRequest>
+
+#include <QtCore/QTime>
+#include <private/qobject_p.h>
+
+#include "qwebsocketprotocol.h"
+#include "qwebsocketdataprocessor_p.h"
+#include "qdefaultmaskgenerator_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QWebSocketHandshakeRequest;
+class QWebSocketHandshakeResponse;
+class QWebSocket;
+class QMaskGenerator;
+
+struct QWebSocketConfiguration
+{
+ Q_DISABLE_COPY(QWebSocketConfiguration)
+
+public:
+ QWebSocketConfiguration();
+
+public:
+
+};
+
+class QWebSocketPrivate : public QObjectPrivate
+{
+ Q_DISABLE_COPY(QWebSocketPrivate)
+
+public:
+ Q_DECLARE_PUBLIC(QWebSocket)
+ explicit QWebSocketPrivate(const QString &origin,
+ QWebSocketProtocol::Version version,
+ QWebSocket * const pWebSocket);
+ virtual ~QWebSocketPrivate();
+
+ void init();
+ void abort();
+ QAbstractSocket::SocketError error() const;
+ QString errorString() const;
+ bool flush() {return true;}
+ bool isValid() const;
+ QHostAddress localAddress() const;
+ quint16 localPort() const;
+ QAbstractSocket::PauseModes pauseMode() const;
+ QHostAddress peerAddress() const;
+ QString peerName() const;
+ quint16 peerPort() const;
+
+ void setMaskGenerator(const QMaskGenerator *maskGenerator);
+ const QMaskGenerator *maskGenerator() const;
+ qint64 readBufferSize() const;
+ void resume();
+ void setPauseMode(QAbstractSocket::PauseModes pauseMode);
+ void setReadBufferSize(qint64 size);
+ QAbstractSocket::SocketState state() const;
+
+ QWebSocketProtocol::Version version() const;
+ QString resourceName() const;
+ QNetworkRequest request() const;
+ QString origin() const;
+ QString protocol() const;
+ QString extension() const;
+ QWebSocketProtocol::CloseCode closeCode() const;
+ QString closeReason() const;
+
+ qint64 sendTextMessage(const QString &message);
+ qint64 sendBinaryMessage(const QByteArray &data);
+
+ void closeGoingAway();
+ void close(QWebSocketProtocol::CloseCode closeCode, QString reason);
+ void open(const QNetworkRequest &request, bool mask);
+ void ping(const QByteArray &payload);
+
+ QWebSocket * const q_ptr;
+
+ static void onOpenCallback(void *data);
+ static void onCloseCallback(void *data, int);
+ static void onErrorCallback(void *data, int);
+ static void onIncomingMessageCallback(void *data, int, int, int);
+
+ void emitConnected();
+ void emitDisconnected();
+ void emitTextMessageReceived(const QString &message);
+ void emitBinaryMessageReceived(const QByteArray &message);
+ void emitErrorReceived(int);
+
+private:
+
+ QWebSocketPrivate(QTcpSocket *pTcpSocket, QWebSocketProtocol::Version version,
+ QWebSocket *pWebSocket);
+
+ void setVersion(QWebSocketProtocol::Version version);
+ void setResourceName(const QString &resourceName);
+ void setRequest(const QNetworkRequest &request);
+ void setOrigin(const QString &origin);
+ void setProtocol(const QString &protocol);
+ void setExtension(const QString &extension);
+ void enableMasking(bool enable);
+ void setSocketState(QAbstractSocket::SocketState state);
+ void setErrorString(const QString &errorString);
+
+ void socketDestroyed(QObject *socket);
+
+ void processClose(QWebSocketProtocol::CloseCode closeCode, QString closeReason);
+ void makeConnections();
+ void releaseConnections();
+
+ QByteArray getFrameHeader(QWebSocketProtocol::OpCode opCode, quint64 payloadLength,
+ quint32 maskingKey, bool lastFrame);
+ QString calculateAcceptKey(const QByteArray &key) const;
+
+ Q_REQUIRED_RESULT static QWebSocket *
+ upgradeFrom(QTcpSocket *tcpSocket,
+ const QWebSocketHandshakeRequest &request,
+ const QWebSocketHandshakeResponse &response,
+ QObject *parent = nullptr);
+
+ quint32 generateMaskingKey() const;
+ QByteArray generateKey() const;
+
+ QString m_errorString;
+ QWebSocketProtocol::Version m_version;
+ QUrl m_resource;
+ QString m_resourceName;
+ QString m_origin;
+ QNetworkRequest m_request;
+ QString m_protocol;
+ QString m_extension;
+ QAbstractSocket::SocketState m_socketState;
+ QAbstractSocket::PauseModes m_pauseMode;
+ qint64 m_readBufferSize;
+
+ QByteArray m_key; //identification key used in handshake requests
+
+ bool m_mustMask; //a server must not mask the frames it sends
+
+ bool m_isClosingHandshakeSent;
+ bool m_isClosingHandshakeReceived;
+ QWebSocketProtocol::CloseCode m_closeCode;
+ QString m_closeReason;
+
+ QTime m_pingTimer;
+
+ QWebSocketDataProcessor m_dataProcessor;
+ QWebSocketConfiguration m_configuration;
+
+ QMaskGenerator *m_pMaskGenerator;
+ QDefaultMaskGenerator m_defaultMaskGenerator;
+
+ enum HandshakeState {
+ NothingDoneState,
+ ReadingStatusState,
+ ReadingHeaderState,
+ ParsingHeaderState,
+ AllDoneState
+ } m_handshakeState;
+ QByteArray m_statusLine;
+ int m_httpStatusCode;
+ int m_httpMajorVersion, m_httpMinorVersion;
+ QString m_httpStatusMessage;
+ QMap<QString, QString> m_headers;
+
+ friend class QWebSocketServerPrivate;
+
+ void jsRequest(const QString &url, void *openCallback, void *closeCallback, void *errorCallback, void *incomingMessageCallback);
+ QAbstractSocket::SocketError m_lastError;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWEBSOCKET_WASM_H
diff --git a/src/websockets/qwebsocketserver_p.cpp b/src/websockets/qwebsocketserver_p.cpp
index 59b50e7..c23599f 100644
--- a/src/websockets/qwebsocketserver_p.cpp
+++ b/src/websockets/qwebsocketserver_p.cpp
@@ -46,7 +46,12 @@
#include "qwebsockethandshakerequest_p.h"
#include "qwebsockethandshakeresponse_p.h"
#include "qwebsocket.h"
+#ifndef Q_OS_HTML5
#include "qwebsocket_p.h"
+#else
+#include "qwebsocket_wasm_p.h"
+#endif
+
#include "qwebsocketcorsauthenticator.h"
#include <QtNetwork/QTcpServer>
diff --git a/src/websockets/websockets.pro b/src/websockets/websockets.pro
index e4366ba..47a6691 100644
--- a/src/websockets/websockets.pro
+++ b/src/websockets/websockets.pro
@@ -16,7 +16,6 @@ PUBLIC_HEADERS += \
$$PWD/qmaskgenerator.h
PRIVATE_HEADERS += \
- $$PWD/qwebsocket_p.h \
$$PWD/qwebsocketserver_p.h \
$$PWD/qwebsocketprotocol_p.h \
$$PWD/qwebsockethandshakerequest_p.h \
@@ -28,7 +27,6 @@ PRIVATE_HEADERS += \
SOURCES += \
$$PWD/qwebsocket.cpp \
- $$PWD/qwebsocket_p.cpp \
$$PWD/qwebsocketserver.cpp \
$$PWD/qwebsocketserver_p.cpp \
$$PWD/qwebsocketprotocol.cpp \
@@ -40,6 +38,18 @@ SOURCES += \
$$PWD/qmaskgenerator.cpp \
$$PWD/qdefaultmaskgenerator_p.cpp
+emscripten: {
+ SOURCES += \
+ $$PWD/qwebsocket_wasm_p.cpp
+ PRIVATE_HEADERS += \
+ $$PWD/qwebsocket_wasm_p.h
+ } else {
+ SOURCES += \
+ $$PWD/qwebsocket_p.cpp
+ PRIVATE_HEADERS += \
+ $$PWD/qwebsocket_p.h
+}
+
qtConfig(ssl) {
SOURCES += $$PWD/qsslserver.cpp
PRIVATE_HEADERS += $$PWD/qsslserver_p.h