From 5c64a39482cba6ad4bc4337bd7475f96cfad9109 Mon Sep 17 00:00:00 2001 From: Lorn Potter Date: Thu, 18 Jan 2018 07:05:03 +1000 Subject: webassembly: add wasm support Change-Id: I2284f8606d512943d2abc2a96fc48effcc45a786 Reviewed-by: Eskil Abrahamsen Blomfeldt --- src/websockets/qwebsocket.cpp | 7 +- src/websockets/qwebsocket_wasm_p.cpp | 912 ++++++++++++++++++++++++++++++++++ src/websockets/qwebsocket_wasm_p.h | 228 +++++++++ src/websockets/qwebsocketserver_p.cpp | 5 + src/websockets/websockets.pro | 14 +- 5 files changed, 1163 insertions(+), 3 deletions(-) create mode 100644 src/websockets/qwebsocket_wasm_p.cpp create mode 100644 src/websockets/qwebsocket_wasm_p.h 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 #include @@ -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 . +** 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 +#include +#include +#include +#include + +#include //for more efficient string concatenation + +#include +#include + +#include + +#include +#include +#include + +using namespace emscripten; +QT_BEGIN_NAMESPACE + +QByteArray m_messageArray; + +val getBinaryMessage() +{ + return val(typed_memory_view(m_messageArray.size(), reinterpret_cast(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(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(data); + + if (handler) + handler->emitErrorReceived(evt); +} + +/*! + \internal + */ +void QWebSocketPrivate::emitErrorReceived(int errorEvt) +{ + m_lastError = static_cast(errorEvt); + Q_Q(QWebSocket); + Q_EMIT q->error(m_lastError); + QCoreApplication::processEvents(); +} + +/*! + \internal + */ +void QWebSocketPrivate::onCloseCallback(void *data, int /*evt*/) +{ + QWebSocketPrivate *handler = reinterpret_cast(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(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 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((opCode & 0x0F) | (lastFrame ? 0x80 : 0x00)); + header.append(static_cast(byte)); + + byte = 0x00; + if (maskingKey != 0) + byte |= 0x80; + if (payloadLength <= 125) { + byte |= static_cast(payloadLength); + header.append(static_cast(byte)); + } else if (payloadLength <= 0xFFFFU) { + byte |= 126; + header.append(static_cast(byte)); + header.append(static_cast(static_cast(&payloadLength)), 2); + } else if (payloadLength <= 0x7FFFFFFFFFFFFFFFULL) { + byte |= 127; + header.append(static_cast(byte)); + header.append(static_cast(static_cast(&payloadLength)), 8); + } + + if (maskingKey != 0) { + header.append(static_cast(static_cast(&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(static_cast(&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(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 . +** 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 +#include +#include + +#include +#include + +#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 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 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 -- cgit v1.2.1