diff options
author | Milian Wolff <milian.wolff@kdab.com> | 2014-01-31 17:39:19 +0100 |
---|---|---|
committer | The Qt Project <gerrit-noreply@qt-project.org> | 2014-03-06 23:55:11 +0100 |
commit | 6547d3305a872163158510cae5b8fc61efb8bca0 (patch) | |
tree | 8a51312a8c598207c53e1027169c52f0d214d06b | |
parent | 6b3569c2cb761afe4eba626067b7c686eda866e6 (diff) | |
download | qtwebchannel-6547d3305a872163158510cae5b8fc61efb8bca0.tar.gz |
Port code to QtWebSockets.
This removes the custom WebSocket server implementation and replaces
it by a dependency on the QtWebSockets module.
Sadly, the QtWebSocket module does not yet support custom protocols.
Also, there is quite some boiler plate code required, something which
I want to simplify upstream in the QtWebSockets module later.
Change-Id: I8066418fb1857d23b8593c443bc9a98ded917a99
Reviewed-by: Kurt Pattyn <pattyn.kurt@gmail.com>
Reviewed-by: Frederik Gladhorn <frederik.gladhorn@digia.com>
-rw-r--r-- | src/webchannel/qwebchannel.js | 3 | ||||
-rw-r--r-- | src/webchannel/qwebchannelsocket.cpp | 61 | ||||
-rw-r--r-- | src/webchannel/qwebchannelsocket_p.h | 16 | ||||
-rw-r--r-- | src/webchannel/qwebsocketserver.cpp | 430 | ||||
-rw-r--r-- | src/webchannel/qwebsocketserver_p.h | 163 | ||||
-rw-r--r-- | src/webchannel/qwebsockettransport.cpp | 4 | ||||
-rw-r--r-- | src/webchannel/webchannel.pro | 4 | ||||
-rw-r--r-- | sync.profile | 5 |
8 files changed, 68 insertions, 618 deletions
diff --git a/src/webchannel/qwebchannel.js b/src/webchannel/qwebchannel.js index 887c5a0..9aa174b 100644 --- a/src/webchannel/qwebchannel.js +++ b/src/webchannel/qwebchannel.js @@ -101,7 +101,8 @@ var QWebChannel = function(baseUrlOrSocket, initCallback, rawChannel) } else { ///TODO: use ssl? var socketUrl = "ws://" + baseUrlOrSocket; - this.socket = new WebSocket(socketUrl, "QWebChannel"); + ///TODO: use QWebChannel protocol, once custom protcols are supported by QtWebSocket + this.socket = new WebSocket(socketUrl /*, "QWebChannel" */); this.socket.onopen = this.initialized this.socket.onclose = function() diff --git a/src/webchannel/qwebchannelsocket.cpp b/src/webchannel/qwebchannelsocket.cpp index b3daf8f..e6a58c8 100644 --- a/src/webchannel/qwebchannelsocket.cpp +++ b/src/webchannel/qwebchannelsocket.cpp @@ -44,18 +44,26 @@ #include <QUuid> #include <QDebug> +#include <QtWebSockets/QWebSocket> + QT_BEGIN_NAMESPACE QWebChannelSocket::QWebChannelSocket(QObject *parent) - : QWebSocketServer(parent) + : QWebSocketServer(QStringLiteral("QWebChannel Server"), NonSecureMode, parent) , m_messageHandler(Q_NULLPTR) , m_useSecret(true) , m_starting(false) { - connect(this, SIGNAL(error(QAbstractSocket::SocketError)), + connect(this, SIGNAL(acceptError(QAbstractSocket::SocketError)), SLOT(socketError())); - connect(this, SIGNAL(textDataReceived(QString)), - SLOT(messageReceived(QString))); + connect(this, SIGNAL(newConnection()), + SLOT(validateNewConnection())); +} + +QWebChannelSocket::~QWebChannelSocket() +{ + close(); + qDeleteAll(m_clients); } void QWebChannelSocket::initLater() @@ -66,13 +74,29 @@ void QWebChannelSocket::initLater() m_starting = true; } -bool QWebChannelSocket::isValid(const HeaderData &connection) +void QWebChannelSocket::sendMessage(const QString &message) +{ + foreach (QWebSocket *client, m_clients) { + client->sendTextMessage(message); + } +} + +void QWebChannelSocket::validateNewConnection() { - if (!QWebSocketServer::isValid(connection)) { - return false; + QWebSocket *client = nextPendingConnection(); + // FIXME: client->protocol() != QStringLiteral("QWebChannel") + // protocols are not supported in QtWebSockets yet... + if (m_useSecret && client->requestUrl().path() != m_secret) + { + client->close(QWebSocketProtocol::CloseCodeBadOperation); + client->deleteLater(); + } else { + connect(client, SIGNAL(textMessageReceived(QString)), + SLOT(messageReceived(QString))); + connect(client, SIGNAL(disconnected()), + SLOT(clientDisconnected())); + m_clients << client; } - return connection.protocol == QByteArrayLiteral("QWebChannel") - && connection.path == m_secret; } void QWebChannelSocket::init() @@ -81,9 +105,9 @@ void QWebChannelSocket::init() m_starting = false; if (m_useSecret) { - m_secret = QUuid::createUuid().toByteArray(); + m_secret = QUuid::createUuid().toString(); // replace { by / - m_secret[0] = '/'; + m_secret[0] = QLatin1Char('/'); // chop of trailing } m_secret.chop(1); } @@ -93,7 +117,7 @@ void QWebChannelSocket::init() return; } - m_baseUrl = QStringLiteral("127.0.0.1:%1%2").arg(port()).arg(QString::fromLatin1(m_secret)); + m_baseUrl = QStringLiteral("127.0.0.1:%1%2").arg(serverPort()).arg(m_secret); emit initialized(); emit baseUrlChanged(m_baseUrl); } @@ -108,6 +132,19 @@ void QWebChannelSocket::messageReceived(const QString &message) if (m_messageHandler) { m_messageHandler->handleMessage(message); } + emit textDataReceived(message); +} + +void QWebChannelSocket::clientDisconnected() +{ + QWebSocket *client = qobject_cast<QWebSocket*>(sender()); + if (!client) { + return; + } + const int idx = m_clients.indexOf(client); + Q_ASSERT(idx != -1); + m_clients.remove(idx); + client->deleteLater(); } QT_END_NAMESPACE diff --git a/src/webchannel/qwebchannelsocket_p.h b/src/webchannel/qwebchannelsocket_p.h index 9d6cde5..74f5c48 100644 --- a/src/webchannel/qwebchannelsocket_p.h +++ b/src/webchannel/qwebchannelsocket_p.h @@ -42,7 +42,8 @@ #ifndef QWEBCHANNELSOCKET_P_H #define QWEBCHANNELSOCKET_P_H -#include "qwebsocketserver_p.h" +#include <QtWebSockets/QWebSocketServer> + #include "qwebchanneltransportinterface.h" QT_BEGIN_NAMESPACE @@ -51,7 +52,7 @@ class QWebChannelSocket : public QWebSocketServer { Q_OBJECT public: - QByteArray m_secret; + QString m_secret; QString m_baseUrl; QWebChannelMessageHandlerInterface *m_messageHandler; @@ -59,21 +60,26 @@ public: bool m_starting; explicit QWebChannelSocket(QObject *parent = 0); + virtual ~QWebChannelSocket(); void initLater(); + void sendMessage(const QString &message); signals: void failed(const QString &reason); void initialized(); void baseUrlChanged(const QString &baseUrl); - -protected: - bool isValid(const HeaderData &connection) Q_DECL_OVERRIDE; + void textDataReceived(const QString &message); private slots: + void validateNewConnection(); void init(); void socketError(); void messageReceived(const QString &message); + void clientDisconnected(); + +private: + QVector<QWebSocket*> m_clients; }; QT_END_NAMESPACE diff --git a/src/webchannel/qwebsocketserver.cpp b/src/webchannel/qwebsocketserver.cpp deleted file mode 100644 index 5a5b5a7..0000000 --- a/src/webchannel/qwebsocketserver.cpp +++ /dev/null @@ -1,430 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com> -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtWebChannel 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 Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "qwebsocketserver_p.h" - -#include <QTcpServer> -#include <QTcpSocket> -#include <QCryptographicHash> -#include <QtEndian> - -#include <limits> - -QT_BEGIN_NAMESPACE - -namespace { -template<typename T> -inline static void appendBytes(QByteArray& data, T value) -{ - data.append(reinterpret_cast<const char*>(&value), sizeof(value)); -} - -inline static void unmask(QByteArray& data, char mask[4]) -{ - for (int i = 0; i < data.size(); ++i) { - int j = i % 4; - data[i] = data[i] ^ mask[j]; - } -} - -inline static char bitMask(int bit) -{ - return 1 << bit; -} - -// see: http://tools.ietf.org/html/rfc6455#page-28 -static const char FIN_BIT = bitMask(7); -static const char MASKED_BIT = bitMask(7); -static const char OPCODE_RANGE = bitMask(4) - 1; -static const char PAYLOAD_RANGE = bitMask(7) - 1; -static const char EXTENDED_PAYLOAD = 126; -static const char EXTENDED_LONG_PAYLOAD = 127; -} - -QWebSocketServer::QWebSocketServer(QObject* parent) -: QObject(parent) -, m_server(new QTcpServer(this)) -{ - connect(m_server, SIGNAL(newConnection()), - SLOT(newConnection())); - connect(m_server, SIGNAL(acceptError(QAbstractSocket::SocketError)), - SIGNAL(error(QAbstractSocket::SocketError))); -} - -QWebSocketServer::~QWebSocketServer() -{ - close(); -} - -bool QWebSocketServer::listen(const QHostAddress& address, quint16 port) -{ - return m_server->listen(address, port); -} - -void QWebSocketServer::close() -{ - sendFrame(Frame::ConnectionClose, QByteArray()); - m_server->close(); -} - -quint16 QWebSocketServer::port() const -{ - return m_server->serverPort(); -} - -QHostAddress QWebSocketServer::address() const -{ - return m_server->serverAddress(); -} - -QString QWebSocketServer::errorString() const -{ - return m_server->errorString(); -} - -void QWebSocketServer::newConnection() -{ - if (!m_server->hasPendingConnections()) - return; - - QTcpSocket* connection = m_server->nextPendingConnection(); - m_connections.insert(connection, Connection()); - connect(connection, SIGNAL(readyRead()), - SLOT(readSocketData())); - connect(connection, SIGNAL(error(QAbstractSocket::SocketError)), - SIGNAL(error(QAbstractSocket::SocketError))); - connect(connection, SIGNAL(disconnected()), - SLOT(disconnected())); -} - -void QWebSocketServer::disconnected() -{ - QTcpSocket* socket = qobject_cast<QTcpSocket*>(sender()); - Q_ASSERT(socket); - - m_connections.remove(socket); -} - -static const QByteArray headerSwitchProtocols = QByteArrayLiteral("HTTP/1.1 101 Switching Protocols"); -static const QByteArray headerGet = QByteArrayLiteral("GET "); -static const QByteArray headerHTTP = QByteArrayLiteral("HTTP/1.1"); -static const QByteArray headerHost = QByteArrayLiteral("Host: "); -static const QByteArray headerUpgrade = QByteArrayLiteral("Upgrade: websocket"); -static const QByteArray headerConnection = QByteArrayLiteral("Connection: Upgrade"); -static const QByteArray headerSecKey = QByteArrayLiteral("Sec-WebSocket-Key: "); -static const QByteArray headerSecProtocol = QByteArrayLiteral("Sec-WebSocket-Protocol: "); -static const QByteArray headerSecVersion = QByteArrayLiteral("Sec-WebSocket-Version: 13"); -static const QByteArray headerSecAccept = QByteArrayLiteral("Sec-WebSocket-Accept: "); -static const QByteArray headerOrigin = QByteArrayLiteral("Origin: "); -static const QByteArray headerMagicKey = QByteArrayLiteral("258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); -static const QByteArray headerEOL = QByteArrayLiteral("\r\n"); -static const QByteArray httpBadRequest = QByteArrayLiteral("HTTP/1.1 400 Bad Request\r\n"); - -void QWebSocketServer::readSocketData() -{ - QTcpSocket* socket = qobject_cast<QTcpSocket*>(sender()); - Q_ASSERT(socket); - - Connection& connection = m_connections[socket]; - - if (!connection.header.wasUpgraded) { - readHeaderData(socket, connection.header); - } - - if (connection.header.wasUpgraded) { - while (socket->bytesAvailable()) { - if (!readFrameData(socket, connection.currentFrame)) { - close(socket, connection.header); - } - } - } -} - -void QWebSocketServer::readHeaderData(QTcpSocket* socket, HeaderData& header) -{ - while (socket->canReadLine()) { - QByteArray line = socket->readLine().trimmed(); - if (line.isEmpty()) { - // finalize - if (isValid(header)) { - upgrade(socket, header); - } else { - close(socket, header); - } - break; - } else if (line.startsWith(headerGet) && line.endsWith(headerHTTP)) { - header.path = line.mid(headerGet.size(), line.size() - headerGet.size() - headerHTTP.size()).trimmed(); - } else if (line.startsWith(headerHost)) { - header.host = line.mid(headerHost.size()).trimmed(); - } else if (line.startsWith(headerSecKey)) { - header.key = line.mid(headerSecKey.size()).trimmed(); - } else if (line.startsWith(headerOrigin)) { - header.origin = line.mid(headerOrigin.size()).trimmed(); - } else if (line.startsWith(headerSecProtocol)) { - header.protocol = line.mid(headerSecProtocol.size()).trimmed(); - } else if (line == headerUpgrade) { - header.hasUpgrade = true; - } else if (line == headerConnection) { - header.hasConnection = true; - } else if (line == headerSecVersion) { - header.hasVersion = true; - } else { - header.otherHeaders << line; - } - } -} - -// see: http://tools.ietf.org/html/rfc6455#page-28 -bool QWebSocketServer::readFrameData(QTcpSocket* socket, Frame& frame) -{ - int bytesAvailable = socket->bytesAvailable(); - if (frame.state == Frame::ReadStart) { - if (bytesAvailable < 2) { - return true; - } - uchar buffer[2]; - socket->read(reinterpret_cast<char*>(buffer), 2); - bytesAvailable -= 2; - frame.fin = buffer[0] & FIN_BIT; - // skip rsv1, rsv2, rsv3 - // last four bits are the opcode - quint8 opcode = buffer[0] & OPCODE_RANGE; - if (opcode != Frame::ContinuationFrame && opcode != Frame::BinaryFrame && - opcode != Frame::ConnectionClose && opcode != Frame::TextFrame && - opcode != Frame::Ping && opcode != Frame::Pong) - { - qWarning() << "invalid opcode: " << opcode; - return false; - } - frame.opcode = static_cast<Frame::Opcode>(opcode); - // test first, i.e. highest bit for mask - frame.masked = buffer[1] & MASKED_BIT; - if (!frame.masked) { - qWarning() << "unmasked frame received"; - return false; - } - // final seven bits are the payload length - frame.length = static_cast<quint8>(buffer[1] & PAYLOAD_RANGE); - if (frame.length == EXTENDED_PAYLOAD) { - frame.state = Frame::ReadExtendedPayload; - } else if (frame.length == EXTENDED_LONG_PAYLOAD) { - frame.state = Frame::ReadExtendedLongPayload; - } else { - frame.state = Frame::ReadMask; - } - } - if (frame.state == Frame::ReadExtendedPayload) { - if (bytesAvailable < 2) { - return true; - } - uchar buffer[2]; - socket->read(reinterpret_cast<char*>(buffer), 2); - bytesAvailable -= 2; - frame.length = qFromBigEndian<quint16>(buffer); - frame.state = Frame::ReadMask; - } - if (frame.state == Frame::ReadExtendedLongPayload) { - if (bytesAvailable < 8) { - return true; - } - uchar buffer[8]; - socket->read(reinterpret_cast<char*>(buffer), 8); - bytesAvailable -= 8; - quint64 longSize = qFromBigEndian<quint64>(buffer); - // QByteArray uses int for size type so limit ourselves to that size as well - if (longSize > static_cast<quint64>(std::numeric_limits<int>::max())) { - return false; - } - frame.length = static_cast<int>(longSize); - frame.state = Frame::ReadMask; - } - if (frame.state == Frame::ReadMask) { - if (bytesAvailable < 4) { - return true; - } - socket->read(frame.mask, 4); - bytesAvailable -= 4; - frame.state = Frame::ReadData; - frame.data.reserve(frame.length); - } - if (frame.state == Frame::ReadData && (bytesAvailable || !frame.length)) { - frame.data.append(socket->read(qMin(frame.length - frame.data.size(), bytesAvailable))); - if (frame.data.size() == frame.length) { - frame.state = Frame::ReadStart; - handleFrame(socket, frame); - } - } - return true; -} - -void QWebSocketServer::handleFrame(QTcpSocket* socket, Frame& frame) -{ - unmask(frame.data, frame.mask); - - // fragmentation support - see http://tools.ietf.org/html/rfc6455#page-33 - if (!frame.fin) { - if (frame.opcode != Frame::ContinuationFrame) { - frame.initialOpcode = frame.opcode; - } - frame.fragments += frame.data; - } else if (frame.fin && frame.opcode == Frame::ContinuationFrame) { - frame.opcode = frame.initialOpcode; - frame.data = frame.fragments + frame.data; - } // otherwise if it's fin and a non-continuation frame its a single-frame message - - switch (frame.opcode) { - case Frame::ContinuationFrame: - // do nothing - break; - case Frame::Ping: - socket->write(frameHeader(Frame::Pong, 0)); - break; - case Frame::Pong: - emit pongReceived(); - break; - case Frame::ConnectionClose: - ///TODO: handle? - qWarning("Unhandled connection close frame"); - break; - case Frame::BinaryFrame: - emit binaryDataReceived(frame.data); - break; - case Frame::TextFrame: - emit textDataReceived(QString::fromUtf8(frame.data)); - break; - } - - if (frame.fin) { - frame = Frame(); - } -} - -bool QWebSocketServer::isValid(const HeaderData& header) -{ - return !header.path.isEmpty() && !header.host.isEmpty() && !header.key.isEmpty() - && header.hasUpgrade && header.hasConnection && header.hasVersion; -} - -void QWebSocketServer::close(QTcpSocket* socket, const HeaderData& header) -{ - if (header.wasUpgraded) { - //TODO: implement this properly - see http://tools.ietf.org/html/rfc6455#page-36 - socket->write(frameHeader(Frame::Frame::ConnectionClose, 0)); - } else { - socket->write(httpBadRequest); - } - socket->close(); -} - -void QWebSocketServer::upgrade(QTcpSocket* socket, HeaderData& header) -{ - socket->write(headerSwitchProtocols); - socket->write(headerEOL); - - socket->write(headerUpgrade); - socket->write(headerEOL); - - socket->write(headerConnection); - socket->write(headerEOL); - - socket->write(headerSecAccept); - socket->write(QCryptographicHash::hash( header.key + headerMagicKey, QCryptographicHash::Sha1 ).toBase64()); - socket->write(headerEOL); - - if (!header.protocol.isEmpty()) { - socket->write(headerSecProtocol); - socket->write(header.protocol); - socket->write(headerEOL); - } - - socket->write(headerEOL); - - header.wasUpgraded = true; -} - -void QWebSocketServer::sendMessage(const QByteArray& message) const -{ - sendFrame(Frame::TextFrame, message); -} - -void QWebSocketServer::sendFrame(Frame::Opcode opcode, const QByteArray& data) const -{ - if (m_connections.isEmpty()) { - return; - } - const QByteArray& header = frameHeader(opcode, data.size()); - QHash< QTcpSocket*, Connection >::const_iterator it = m_connections.constBegin(); - while (it != m_connections.constEnd()) { - if (it.value().header.wasUpgraded) { - it.key()->write(header); - it.key()->write(data); - } - ++it; - } -} - -// see: http://tools.ietf.org/html/rfc6455#page-28 -QByteArray QWebSocketServer::frameHeader(QWebSocketServer::Frame::Opcode opcode, const int dataSize) const -{ - // we only support single frames for now - Q_ASSERT(opcode != Frame::ContinuationFrame); - - QByteArray header; - header.reserve(4); - header.append(FIN_BIT | opcode); - if (dataSize < EXTENDED_PAYLOAD) { - header.append(static_cast<char>(dataSize)); - } else if (dataSize < std::numeric_limits<quint16>::max()) { - header.append(EXTENDED_PAYLOAD); - appendBytes(header, qToBigEndian<quint16>(dataSize)); - } else { - header.append(EXTENDED_LONG_PAYLOAD); - appendBytes(header, qToBigEndian<quint64>(dataSize)); - } - return header; -} - -void QWebSocketServer::ping() const -{ - sendFrame(Frame::Ping, QByteArray()); -} - -QT_END_NAMESPACE diff --git a/src/webchannel/qwebsocketserver_p.h b/src/webchannel/qwebsocketserver_p.h deleted file mode 100644 index 34c9ec2..0000000 --- a/src/webchannel/qwebsocketserver_p.h +++ /dev/null @@ -1,163 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2014 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Milian Wolff <milian.wolff@kdab.com> -** Contact: http://www.qt-project.org/legal -** -** This file is part of the QtWebChannel 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 Digia. For licensing terms and -** conditions see http://qt.digia.com/licensing. For further information -** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Digia gives you certain additional -** rights. These rights are described in the Digia Qt LGPL Exception -** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#ifndef QWEBSOCKET_H -#define QWEBSOCKET_H - -#include <QObject> -#include <QHostAddress> - -QT_BEGIN_NAMESPACE - -class QTcpServer; -class QTcpSocket; - -class QWebSocketServer : public QObject -{ - Q_OBJECT - -public: - explicit QWebSocketServer(QObject* parent = 0); - virtual ~QWebSocketServer(); - - bool listen(const QHostAddress& address = QHostAddress::LocalHost, quint16 port = 0); - void close(); - - QHostAddress address() const; - quint16 port() const; - - QString errorString() const; - -signals: - void opened(); - void error(QAbstractSocket::SocketError); - void textDataReceived(const QString& data); - void binaryDataReceived(const QByteArray& data); - void pongReceived(); - -public slots: - void sendMessage(const QByteArray& message) const; - void ping() const; - -private slots: - void newConnection(); - void readSocketData(); - void disconnected(); - -protected: - struct HeaderData - { - HeaderData() - : hasVersion(false) - , hasUpgrade(false) - , hasConnection(false) - , wasUpgraded(false) - { - } - QByteArray path; - QByteArray host; - QByteArray origin; - QByteArray key; - QByteArray protocol; - QVector<QByteArray> otherHeaders; - // no bitmap here - we only have few of these objects - bool hasVersion; - bool hasUpgrade; - bool hasConnection; - bool wasUpgraded; - }; - virtual bool isValid(const HeaderData& connection); - -private: - struct Frame - { - enum State { - ReadStart, - ReadExtendedPayload, - ReadExtendedLongPayload, - ReadMask, - ReadData, - Finished - }; - enum Opcode { - ContinuationFrame = 0x0, - TextFrame = 0x1, - BinaryFrame = 0x2, - ConnectionClose = 0x8, - Ping = 0x9, - Pong = 0xA - }; - // no bitmap here - we only have a few of these objects - State state; - Opcode opcode; - bool fin; - bool masked; - ///NOTE: standard says unsigned 64bit integer but QByteArray only supports 'int' size - int length; - char mask[4]; - QByteArray data; - // fragmentation support - Opcode initialOpcode; - QByteArray fragments; - }; - struct Connection - { - HeaderData header; - Frame currentFrame; - }; - - void readHeaderData(QTcpSocket* socket, HeaderData& header); - void close(QTcpSocket* socket, const HeaderData& header); - void upgrade(QTcpSocket* socket, HeaderData& header); - bool readFrameData(QTcpSocket* socket, Frame& frame); - void handleFrame(QTcpSocket* socket, Frame& frame); - - void sendFrame(Frame::Opcode opcode, const QByteArray& data) const; - void sendFrame(QTcpSocket* socket, Frame::Opcode opcode, const QByteArray& data) const; - QByteArray frameHeader(Frame::Opcode opcode, const int dataSize) const; - - QTcpServer* m_server; - QHash<QTcpSocket*, Connection> m_connections; -}; - -QT_END_NAMESPACE - -#endif // QWEBSOCKET_H diff --git a/src/webchannel/qwebsockettransport.cpp b/src/webchannel/qwebsockettransport.cpp index be991a8..9c9ef97 100644 --- a/src/webchannel/qwebsockettransport.cpp +++ b/src/webchannel/qwebsockettransport.cpp @@ -67,12 +67,12 @@ QWebSocketTransport::~QWebSocketTransport() void QWebSocketTransport::sendMessage(const QByteArray &message) const { - d->sendMessage(message); + d->sendMessage(QString::fromUtf8(message)); } void QWebSocketTransport::sendMessage(const QString &message) const { - d->sendMessage(message.toUtf8()); + d->sendMessage(message); } void QWebSocketTransport::setMessageHandler(QWebChannelMessageHandlerInterface *handler) diff --git a/src/webchannel/webchannel.pro b/src/webchannel/webchannel.pro index 279eb37..f04e3eb 100644 --- a/src/webchannel/webchannel.pro +++ b/src/webchannel/webchannel.pro @@ -1,5 +1,5 @@ TARGET = QtWebChannel -QT = core network +QT = core network websockets CONFIG += warn_on strict_flags load(qt_module) @@ -18,7 +18,6 @@ PUBLIC_HEADERS += \ PRIVATE_HEADERS += \ qwebchannel_p.h \ qmetaobjectpublisher_p.h \ - qwebsocketserver_p.h \ qwebchannelsocket_p.h \ variantargument_p.h \ signalhandler_p.h @@ -26,7 +25,6 @@ PRIVATE_HEADERS += \ SOURCES += \ qwebchannel.cpp \ qmetaobjectpublisher.cpp \ - qwebsocketserver.cpp \ qwebchannelsocket.cpp \ qwebsockettransport.cpp diff --git a/sync.profile b/sync.profile index 36c410b..da8a563 100644 --- a/sync.profile +++ b/sync.profile @@ -11,9 +11,10 @@ # - an empty string to use the same branch under test (dependencies will become "refs/heads/master" if we are in the master branch) # %dependencies = ( - "qtbase" => "", + "qtbase" => "stable", + "qtwebsockets" => "dev", # optional dependencies: - "qtdeclarative" => "", + "qtdeclarative" => "stable", # TODO: disabled for now as it breaks CI builds on OSX # requires changes to qtqa scripts as discussed with sifalt, sahumada, tronical # "qtwebkit" => "", |