m_url(url)
{
connect(&m_webSocket, SIGNAL(connected()), this, SLOT(onConnected()));
+ connect(&m_webSocket, SIGNAL(disconnected()), this, SIGNAL(closed()));
m_webSocket.open(QUrl(url));
}
//! [constructor]
explicit EchoClient(const QUrl &url, QObject *parent = Q_NULLPTR);
Q_SIGNALS:
+ void closed();
public Q_SLOTS:
{
QCoreApplication a(argc, argv);
EchoClient client(QUrl("ws://localhost:1234"));
+ QObject::connect(&client, SIGNAL(closed()), &a, SLOT(quit()));
Q_UNUSED(client);
//! [constructor]
EchoServer::EchoServer(quint16 port, QObject *parent) :
QObject(parent),
- m_pWebSocketServer(Q_NULLPTR),
+ m_pWebSocketServer(new QWebSocketServer(QStringLiteral("Echo Server"),
+ QWebSocketServer::NON_SECURE_MODE, this)),
m_clients()
{
- m_pWebSocketServer = new QWebSocketServer("Echo Server", QWebSocketServer::NON_SECURE_MODE, this);
- if (m_pWebSocketServer->listen(QHostAddress::Any, port))
- {
+ if (m_pWebSocketServer->listen(QHostAddress::Any, port)) {
qDebug() << "Echoserver listening on port" << port;
connect(m_pWebSocketServer, SIGNAL(newConnection()), this, SLOT(onNewConnection()));
+ connect(m_pWebSocketServer, SIGNAL(closed()), this, SIGNAL(closed()));
}
}
//! [constructor]
+EchoServer::~EchoServer()
+{
+ m_pWebSocketServer->close();
+ while (!m_clients.isEmpty()) {
+ QWebSocket *pWebSocket = m_clients.takeFirst();
+ delete pWebSocket;
+ }
+}
+
//! [onNewConnection]
void EchoServer::onNewConnection()
{
connect(pSocket, SIGNAL(textMessageReceived(QString)), this, SLOT(processMessage(QString)));
connect(pSocket, SIGNAL(binaryMessageReceived(QByteArray)), this, SLOT(processBinaryMessage(QByteArray)));
connect(pSocket, SIGNAL(disconnected()), this, SLOT(socketDisconnected()));
- //connect(pSocket, SIGNAL(pong(quint64)), this, SLOT(processPong(quint64)));
m_clients << pSocket;
}
void EchoServer::processMessage(QString message)
{
QWebSocket *pClient = qobject_cast<QWebSocket *>(sender());
- if (pClient)
- {
+ if (pClient) {
pClient->write(message);
}
+ m_pWebSocketServer->close();
}
//! [processMessage]
void EchoServer::processBinaryMessage(QByteArray message)
{
QWebSocket *pClient = qobject_cast<QWebSocket *>(sender());
- if (pClient)
- {
+ if (pClient) {
pClient->write(message);
}
}
void EchoServer::socketDisconnected()
{
QWebSocket *pClient = qobject_cast<QWebSocket *>(sender());
- if (pClient)
- {
+ if (pClient) {
m_clients.removeAll(pClient);
pClient->deleteLater();
}
Q_OBJECT
public:
explicit EchoServer(quint16 port, QObject *parent = Q_NULLPTR);
+ ~EchoServer();
Q_SIGNALS:
+ void closed();
private Q_SLOTS:
void onNewConnection();
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
- EchoServer server(1234);
-
- Q_UNUSED(server);
+ EchoServer *server = new EchoServer(1234);
+ QObject::connect(server, SIGNAL(closed()), &a, SLOT(quit()));
return a.exec();
}
#include <QtNetwork/QSslSocket>
#include <QtNetwork/QSslCipher>
+QT_BEGIN_NAMESPACE
+
QSslServer::QSslServer(QObject *parent) :
QTcpServer(parent),
m_sslConfiguration(QSslConfiguration::defaultConfiguration())
pSslSocket->setSslConfiguration(m_sslConfiguration);
- if (pSslSocket->setSocketDescriptor(socket))
- {
+ if (pSslSocket->setSocketDescriptor(socket)) {
connect(pSslSocket, SIGNAL(peerVerifyError(QSslError)), this, SIGNAL(peerVerifyError(QSslError)));
connect(pSslSocket, SIGNAL(sslErrors(QList<QSslError>)), this, SIGNAL(sslErrors(QList<QSslError>)));
connect(pSslSocket, SIGNAL(encrypted()), this, SIGNAL(newEncryptedConnection()));
addPendingConnection(pSslSocket);
pSslSocket->startServerEncryption();
- }
- else
- {
+ } else {
delete pSslSocket;
}
}
+
+QT_END_NAMESPACE
void QWebSocket::ping(const QByteArray &payload)
{
Q_D(QWebSocket);
- if (payload.length() > 125)
- {
- payload.left(125);
- }
d->ping(payload);
}
*/
QWebSocketPrivate::~QWebSocketPrivate()
{
- if (m_pSocket)
- {
- if (state() == QAbstractSocket::ConnectedState)
- {
- close(QWebSocketProtocol::CC_GOING_AWAY, tr("Connection closed"));
- }
- releaseConnections(m_pSocket.data());
-// m_pSocket->deleteLater();
-// m_pSocket = Q_NULLPTR;
+ if (!m_pSocket) {
+ return;
+ }
+ if (state() == QAbstractSocket::ConnectedState) {
+ close(QWebSocketProtocol::CC_GOING_AWAY, tr("Connection closed"));
}
+ releaseConnections(m_pSocket.data());
}
/*!
*/
void QWebSocketPrivate::abort()
{
- if (m_pSocket)
- {
+ if (m_pSocket) {
m_pSocket->abort();
}
}
QAbstractSocket::SocketError QWebSocketPrivate::error() const
{
QAbstractSocket::SocketError err = QAbstractSocket::OperationError;
- if (m_pSocket)
- {
+ if (m_pSocket) {
err = m_pSocket->error();
}
return err;
QString QWebSocketPrivate::errorString() const
{
QString errMsg;
- if (!m_errorString.isEmpty())
- {
+ if (!m_errorString.isEmpty()) {
errMsg = m_errorString;
- }
- else if (m_pSocket)
- {
+ } else if (m_pSocket) {
errMsg = m_pSocket->errorString();
}
return errMsg;
bool QWebSocketPrivate::flush()
{
bool result = true;
- if (m_pSocket)
- {
+ if (m_pSocket) {
result = m_pSocket->flush();
}
return result;
*/
qint64 QWebSocketPrivate::write(const char *message)
{
- //TODO: create a QByteArray from message, and directly call doWriteData
+ //TODO: create a QByteArray from message, and directly call doWriteFrames
//now the data is converted to a string, and then converted back to a bytearray
return write(QString::fromUtf8(message));
}
*/
qint64 QWebSocketPrivate::write(const char *message, qint64 maxSize)
{
- //TODO: create a QByteArray from message, and directly call doWriteData
+ //TODO: create a QByteArray from message, and directly call doWriteFrames
//now the data is converted to a string, and then converted back to a bytearray
return write(QString::fromUtf8(message, static_cast<int>(maxSize)));
}
*/
qint64 QWebSocketPrivate::write(const QString &message)
{
- return doWriteData(message.toUtf8(), false);
+ return doWriteFrames(message.toUtf8(), false);
}
/*!
*/
qint64 QWebSocketPrivate::write(const QByteArray &data)
{
- return doWriteData(data, true);
+ return doWriteFrames(data, true);
}
#ifndef QT_NO_SSL
void QWebSocketPrivate::ignoreSslErrors()
{
m_configuration.m_ignoreSslErrors = true;
- if (m_pSocket)
- {
+ if (m_pSocket) {
QSslSocket *pSslSocket = qobject_cast<QSslSocket *>(m_pSocket.data());
- if (pSslSocket)
- {
+ if (pSslSocket) {
pSslSocket->ignoreSslErrors();
}
}
*/
void QWebSocketPrivate::close(QWebSocketProtocol::CloseCode closeCode, QString reason)
{
- if (m_pSocket)
- {
- if (!m_isClosingHandshakeSent)
- {
- Q_Q(QWebSocket);
- quint32 maskingKey = 0;
- if (m_mustMask)
- {
- maskingKey = generateMaskingKey();
- }
- const quint16 code = qToBigEndian<quint16>(closeCode);
- QByteArray payload;
- payload.append(static_cast<const char *>(static_cast<const void *>(&code)), 2);
- if (!reason.isEmpty())
- {
- payload.append(reason.toUtf8());
- }
- if (m_mustMask)
- {
- QWebSocketProtocol::mask(payload.data(), payload.size(), maskingKey);
- }
- QByteArray frame = getFrameHeader(QWebSocketProtocol::OC_CLOSE, payload.size(), maskingKey, true);
- frame.append(payload);
- m_pSocket->write(frame);
- m_pSocket->flush();
+ if (!m_pSocket) {
+ return;
+ }
+ if (!m_isClosingHandshakeSent) {
+ Q_Q(QWebSocket);
+ quint32 maskingKey = 0;
+ if (m_mustMask) {
+ maskingKey = generateMaskingKey();
+ }
+ const quint16 code = qToBigEndian<quint16>(closeCode);
+ QByteArray payload;
+ payload.append(static_cast<const char *>(static_cast<const void *>(&code)), 2);
+ if (!reason.isEmpty()) {
+ payload.append(reason.toUtf8());
+ }
+ if (m_mustMask) {
+ QWebSocketProtocol::mask(payload.data(), payload.size(), maskingKey);
+ }
+ QByteArray frame = getFrameHeader(QWebSocketProtocol::OC_CLOSE, payload.size(), maskingKey, true);
+ frame.append(payload);
+ m_pSocket->write(frame);
+ m_pSocket->flush();
- m_isClosingHandshakeSent = true;
+ m_isClosingHandshakeSent = true;
- Q_EMIT q->aboutToClose();
- }
- m_pSocket->close();
+ Q_EMIT q->aboutToClose();
}
+ m_pSocket->close();
}
/*!
{
//m_pSocket.reset(); //just delete the old socket for the moment; later, we can add more 'intelligent' handling by looking at the url
QTcpSocket *pTcpSocket = m_pSocket.take();
- if (pTcpSocket)
- {
+ if (pTcpSocket) {
releaseConnections(pTcpSocket);
pTcpSocket->deleteLater();
}
//if (m_url != url)
- if (!m_pSocket)
- {
+ if (!m_pSocket) {
Q_Q(QWebSocket);
m_dataProcessor.clear();
setRequestUrl(url);
QString resourceName = url.path();
- if (!url.query().isEmpty())
- {
- if (!resourceName.endsWith(QChar::fromLatin1('?')))
- {
+ if (!url.query().isEmpty()) {
+ if (!resourceName.endsWith(QChar::fromLatin1('?'))) {
resourceName.append(QChar::fromLatin1('?'));
}
resourceName.append(url.query());
}
- if (resourceName.isEmpty())
- {
+ if (resourceName.isEmpty()) {
resourceName = QStringLiteral("/");
}
setResourceName(resourceName);
enableMasking(mask);
#ifndef QT_NO_SSL
- if (url.scheme() == QStringLiteral("wss"))
- {
- if (!QSslSocket::supportsSsl())
- {
+ if (url.scheme() == QStringLiteral("wss")) {
+ if (!QSslSocket::supportsSsl()) {
const QString message = tr("SSL Sockets are not supported on this platform.");
- qWarning() << message;
setErrorString(message);
emit q->error(QAbstractSocket::UnsupportedSocketOperationError);
- }
- else
- {
+ } else {
QSslSocket *sslSocket = new QSslSocket(this);
m_pSocket.reset(sslSocket);
setSocketState(QAbstractSocket::ConnectingState);
sslSocket->setSslConfiguration(m_configuration.m_sslConfiguration);
- if (m_configuration.m_ignoreSslErrors)
- {
+ if (m_configuration.m_ignoreSslErrors) {
sslSocket->ignoreSslErrors();
- }
- else
- {
+ } else {
sslSocket->ignoreSslErrors(m_configuration.m_ignoredSslErrors);
}
#ifndef QT_NO_NETWORKPROXY
#endif
sslSocket->connectToHostEncrypted(url.host(), url.port(443));
}
- }
- else
+ } else
#endif
- if (url.scheme() == QStringLiteral("ws"))
- {
+ if (url.scheme() == QStringLiteral("ws")) {
m_pSocket.reset(new QTcpSocket(this));
makeConnections(m_pSocket.data());
m_pSocket->setProxy(m_configuration.m_proxy);
#endif
m_pSocket->connectToHost(url.host(), url.port(80));
- }
- else
- {
+ } else {
const QString message = tr("Unsupported websockets scheme: %1").arg(url.scheme());
- qWarning() << message;
setErrorString(message);
emit q->error(QAbstractSocket::UnsupportedSocketOperationError);
}
/*!
\internal
*/
-void QWebSocketPrivate::ping(const QByteArray &payload)
+void QWebSocketPrivate::ping(QByteArray payload)
{
- Q_ASSERT(payload.length() < 126);
+ if (payload.length() > 125) {
+ payload.truncate(125);
+ }
m_pingTimer.restart();
QByteArray pingFrame = getFrameHeader(QWebSocketProtocol::OC_PING, payload.size(), 0 /*do not mask*/, true);
pingFrame.append(payload);
*/
void QWebSocketPrivate::setVersion(QWebSocketProtocol::Version version)
{
- m_version = version;
+ if (m_version != version) {
+ m_version = version;
+ }
}
/*!
*/
void QWebSocketPrivate::setResourceName(const QString &resourceName)
{
- m_resourceName = resourceName;
+ if (m_resourceName != resourceName) {
+ m_resourceName = resourceName;
+ }
}
/*!
*/
void QWebSocketPrivate::setRequestUrl(const QUrl &requestUrl)
{
- m_requestUrl = requestUrl;
+ if (m_requestUrl != requestUrl) {
+ m_requestUrl = requestUrl;
+ }
}
/*!
*/
void QWebSocketPrivate::setOrigin(const QString &origin)
{
- m_origin = origin;
+ if (m_origin != origin) {
+ m_origin = origin;
+ }
}
/*!
*/
void QWebSocketPrivate::setProtocol(const QString &protocol)
{
- m_protocol = protocol;
+ if (m_protocol != protocol) {
+ m_protocol = protocol;
+ }
}
/*!
*/
void QWebSocketPrivate::setExtension(const QString &extension)
{
- m_extension = extension;
+ if (m_extension != extension) {
+ m_extension = extension;
+ }
}
/*!
*/
void QWebSocketPrivate::enableMasking(bool enable)
{
- m_mustMask = enable;
-}
-
-/*!
- * \internal
- */
-qint64 QWebSocketPrivate::doWriteData(const QByteArray &data, bool isBinary)
-{
- return doWriteFrames(data, isBinary);
+ if (m_mustMask != enable) {
+ m_mustMask = enable;
+ }
}
/*!
Q_ASSERT(pTcpSocket);
Q_Q(QWebSocket);
- //pass through signals
- connect(pTcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), q, SIGNAL(error(QAbstractSocket::SocketError)));
- connect(pTcpSocket, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)), q, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)));
- connect(pTcpSocket, SIGNAL(readChannelFinished()), q, SIGNAL(readChannelFinished()));
- connect(pTcpSocket, SIGNAL(aboutToClose()), q, SIGNAL(aboutToClose()));
- //connect(pTcpSocket, SIGNAL(bytesWritten(qint64)), q, SIGNAL(bytesWritten(qint64)));
-
- //catch signals
- connect(pTcpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(processStateChanged(QAbstractSocket::SocketState)));
- //!!!important to use a QueuedConnection here; with QTcpSocket there is no problem, but with QSslSocket the processing hangs
- connect(pTcpSocket, SIGNAL(readyRead()), this, SLOT(processData()), Qt::QueuedConnection);
+ if (pTcpSocket) {
+ //pass through signals
+ connect(pTcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), q, SIGNAL(error(QAbstractSocket::SocketError)));
+ connect(pTcpSocket, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)), q, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)));
+ connect(pTcpSocket, SIGNAL(readChannelFinished()), q, SIGNAL(readChannelFinished()));
+ connect(pTcpSocket, SIGNAL(aboutToClose()), q, SIGNAL(aboutToClose()));
+
+ //catch signals
+ connect(pTcpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(processStateChanged(QAbstractSocket::SocketState)));
+ //!!!important to use a QueuedConnection here; with QTcpSocket there is no problem, but with QSslSocket the processing hangs
+ connect(pTcpSocket, SIGNAL(readyRead()), this, SLOT(processData()), Qt::QueuedConnection);
+ }
connect(&m_dataProcessor, SIGNAL(textFrameReceived(QString,bool)), q, SIGNAL(textFrameReceived(QString,bool)));
connect(&m_dataProcessor, SIGNAL(binaryFrameReceived(QByteArray,bool)), q, SIGNAL(binaryFrameReceived(QByteArray,bool)));
*/
void QWebSocketPrivate::releaseConnections(const QTcpSocket *pTcpSocket)
{
- Q_Q(QWebSocket);
- if (pTcpSocket)
- {
- //pass through signals
- disconnect(pTcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), q, SIGNAL(error(QAbstractSocket::SocketError)));
- disconnect(pTcpSocket, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)), q, SIGNAL(proxyAuthenticationRequired(const QNetworkProxy &, QAuthenticator *)));
- disconnect(pTcpSocket, SIGNAL(readChannelFinished()), q, SIGNAL(readChannelFinished()));
- disconnect(pTcpSocket, SIGNAL(aboutToClose()), q, SIGNAL(aboutToClose()));
- //disconnect(pTcpSocket, SIGNAL(bytesWritten(qint64)), q, SIGNAL(bytesWritten(qint64)));
-
- //catched signals
- disconnect(pTcpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), this, SLOT(processStateChanged(QAbstractSocket::SocketState)));
- disconnect(pTcpSocket, SIGNAL(readyRead()), this, SLOT(processData()));
+ if (pTcpSocket) {
+ disconnect(pTcpSocket);
}
- disconnect(&m_dataProcessor, SIGNAL(pingReceived(QByteArray)), this, SLOT(processPing(QByteArray)));
- disconnect(&m_dataProcessor, SIGNAL(pongReceived(QByteArray)), this, SLOT(processPong(QByteArray)));
- disconnect(&m_dataProcessor, SIGNAL(closeReceived(QWebSocketProtocol::CloseCode,QString)), this, SLOT(processClose(QWebSocketProtocol::CloseCode,QString)));
- disconnect(&m_dataProcessor, SIGNAL(textFrameReceived(QString,bool)), q, SIGNAL(textFrameReceived(QString,bool)));
- disconnect(&m_dataProcessor, SIGNAL(binaryFrameReceived(QByteArray,bool)), q, SIGNAL(binaryFrameReceived(QByteArray,bool)));
- disconnect(&m_dataProcessor, SIGNAL(binaryMessageReceived(QByteArray)), q, SIGNAL(binaryMessageReceived(QByteArray)));
- disconnect(&m_dataProcessor, SIGNAL(textMessageReceived(QString)), q, SIGNAL(textMessageReceived(QString)));
- disconnect(&m_dataProcessor, SIGNAL(errorEncountered(QWebSocketProtocol::CloseCode,QString)), this, SLOT(close(QWebSocketProtocol::CloseCode,QString)));
+ disconnect(&m_dataProcessor);
}
/*!
/*!
* \internal
*/
-QByteArray QWebSocketPrivate::getFrameHeader(QWebSocketProtocol::OpCode opCode, quint64 payloadLength, quint32 maskingKey, bool lastFrame) const
+QByteArray QWebSocketPrivate::getFrameHeader(QWebSocketProtocol::OpCode opCode, quint64 payloadLength, quint32 maskingKey, bool lastFrame)
{
QByteArray header;
quint8 byte = 0x00;
bool ok = payloadLength <= 0x7FFFFFFFFFFFFFFFULL;
- if (ok)
- {
+ if (ok) {
//FIN, RSV1-3, opcode
byte = static_cast<quint8>((opCode & 0x0F) | (lastFrame ? 0x80 : 0x00)); //FIN, opcode
//RSV-1, RSV-2 and RSV-3 are zero
header.append(static_cast<char>(byte));
- //Now write the masking bit and the payload length byte
byte = 0x00;
- if (maskingKey != 0)
- {
+ if (maskingKey != 0) {
byte |= 0x80;
}
- if (payloadLength <= 125)
- {
+ if (payloadLength <= 125) {
byte |= static_cast<quint8>(payloadLength);
header.append(static_cast<char>(byte));
- }
- else if (payloadLength <= 0xFFFFU)
- {
+ } else if (payloadLength <= 0xFFFFU) {
byte |= 126;
header.append(static_cast<char>(byte));
quint16 swapped = qToBigEndian<quint16>(static_cast<quint16>(payloadLength));
header.append(static_cast<const char *>(static_cast<const void *>(&swapped)), 2);
- }
- else if (payloadLength <= 0x7FFFFFFFFFFFFFFFULL)
- {
+ } else if (payloadLength <= 0x7FFFFFFFFFFFFFFFULL) {
byte |= 127;
header.append(static_cast<char>(byte));
quint64 swapped = qToBigEndian<quint64>(payloadLength);
header.append(static_cast<const char *>(static_cast<const void *>(&swapped)), 8);
}
- //Write mask
- if (maskingKey != 0)
- {
- //TODO: to big endian?
+ if (maskingKey != 0) {
const quint32 mask = qToBigEndian<quint32>(maskingKey);
header.append(static_cast<const char *>(static_cast<const void *>(&mask)), sizeof(quint32));
}
- }
- else
- {
- //setErrorString("WebSocket::getHeader: payload too big!");
- //Q_EMIT q_ptr->error(QAbstractSocket::DatagramTooLargeError);
- qDebug() << "WebSocket::getHeader: payload too big!";
+ } else {
+ setErrorString(QStringLiteral("WebSocket::getHeader: payload too big!"));
+ Q_EMIT q_ptr->error(QAbstractSocket::DatagramTooLargeError);
}
return header;
qint64 QWebSocketPrivate::doWriteFrames(const QByteArray &data, bool isBinary)
{
qint64 payloadWritten = 0;
- if (m_pSocket)
- {
- Q_Q(QWebSocket);
- const QWebSocketProtocol::OpCode firstOpCode = isBinary ? QWebSocketProtocol::OC_BINARY : QWebSocketProtocol::OC_TEXT;
-
- int numFrames = data.size() / FRAME_SIZE_IN_BYTES;
- QByteArray tmpData(data);
- tmpData.detach();
- char *payload = tmpData.data();
- quint64 sizeLeft = static_cast<quint64>(data.size()) % FRAME_SIZE_IN_BYTES;
- if (sizeLeft)
- {
- ++numFrames;
- }
- if (numFrames == 0) //catch the case where the payload is zero bytes; in that case, we still need to send a frame
- {
- numFrames = 1;
+ if (!m_pSocket) {
+ return payloadWritten;
+ }
+ Q_Q(QWebSocket);
+ const QWebSocketProtocol::OpCode firstOpCode = isBinary ?
+ QWebSocketProtocol::OC_BINARY : QWebSocketProtocol::OC_TEXT;
+
+ int numFrames = data.size() / FRAME_SIZE_IN_BYTES;
+ QByteArray tmpData(data);
+ //TODO: really necessary?
+ tmpData.detach();
+ char *payload = tmpData.data();
+ quint64 sizeLeft = quint64(data.size()) % FRAME_SIZE_IN_BYTES;
+ if (sizeLeft) {
+ ++numFrames;
+ }
+ //catch the case where the payload is zero bytes;
+ //in this case, we still need to send a frame
+ if (numFrames == 0) {
+ numFrames = 1;
+ }
+ quint64 currentPosition = 0;
+ qint64 bytesWritten = 0;
+ quint64 bytesLeft = data.size();
+
+ for (int i = 0; i < numFrames; ++i) {
+ quint32 maskingKey = 0;
+ if (m_mustMask) {
+ maskingKey = generateMaskingKey();
}
- quint64 currentPosition = 0;
- qint64 bytesWritten = 0;
- quint64 bytesLeft = data.size();
-
- for (int i = 0; i < numFrames; ++i)
- {
- quint32 maskingKey = 0;
- if (m_mustMask)
- {
- maskingKey = generateMaskingKey();
- }
- const bool isLastFrame = (i == (numFrames - 1));
- const bool isFirstFrame = (i == 0);
+ const bool isLastFrame = (i == (numFrames - 1));
+ const bool isFirstFrame = (i == 0);
- const quint64 size = qMin(bytesLeft, FRAME_SIZE_IN_BYTES);
- const QWebSocketProtocol::OpCode opcode = isFirstFrame ? firstOpCode : QWebSocketProtocol::OC_CONTINUE;
+ const quint64 size = qMin(bytesLeft, FRAME_SIZE_IN_BYTES);
+ const QWebSocketProtocol::OpCode opcode = isFirstFrame ? firstOpCode : QWebSocketProtocol::OC_CONTINUE;
- //write header
- bytesWritten += m_pSocket->write(getFrameHeader(opcode, size, maskingKey, isLastFrame));
+ //write header
+ bytesWritten += m_pSocket->write(getFrameHeader(opcode, size, maskingKey, isLastFrame));
- //write payload
- if (size > 0)
- {
- char *currentData = payload + currentPosition;
- if (m_mustMask)
- {
- QWebSocketProtocol::mask(currentData, size, maskingKey);
- }
- qint64 written = m_pSocket->write(currentData, static_cast<qint64>(size));
- if (written > 0)
- {
- bytesWritten += written;
- payloadWritten += written;
- }
- else
- {
- setErrorString(tr("Error writing bytes to socket: %1.").arg(m_pSocket->errorString()));
- qDebug() << errorString();
- m_pSocket->flush();
- Q_EMIT q->error(QAbstractSocket::NetworkError);
- break;
- }
+ //write payload
+ if (size > 0) {
+ char *currentData = payload + currentPosition;
+ if (m_mustMask) {
+ QWebSocketProtocol::mask(currentData, size, maskingKey);
+ }
+ qint64 written = m_pSocket->write(currentData, static_cast<qint64>(size));
+ if (written > 0) {
+ bytesWritten += written;
+ payloadWritten += written;
+ } else {
+ m_pSocket->flush();
+ setErrorString(tr("Error writing bytes to socket: %1.").arg(m_pSocket->errorString()));
+ Q_EMIT q->error(QAbstractSocket::NetworkError);
+ break;
}
- currentPosition += size;
- bytesLeft -= size;
- }
- if (payloadWritten != data.size())
- {
- setErrorString(tr("Bytes written %1 != %2.").arg(payloadWritten).arg(data.size()));
- qDebug() << errorString();
- Q_EMIT q->error(QAbstractSocket::NetworkError);
}
+ currentPosition += size;
+ bytesLeft -= size;
+ }
+ if (payloadWritten != data.size()) {
+ setErrorString(tr("Bytes written %1 != %2.").arg(payloadWritten).arg(data.size()));
+ Q_EMIT q->error(QAbstractSocket::NetworkError);
}
return payloadWritten;
}
*/
quint32 QWebSocketPrivate::generateRandomNumber() const
{
- return static_cast<quint32>((static_cast<double>(qrand()) / RAND_MAX) * std::numeric_limits<quint32>::max());
+ return quint32((double(qrand()) / RAND_MAX) * std::numeric_limits<quint32>::max());
}
/*!
{
QByteArray key;
- for (int i = 0; i < 4; ++i)
- {
- quint32 tmp = generateRandomNumber();
+ for (int i = 0; i < 4; ++i) {
+ const quint32 tmp = generateRandomNumber();
key.append(static_cast<const char *>(static_cast<const void *>(&tmp)), sizeof(quint32));
}
qint64 QWebSocketPrivate::writeFrames(const QList<QByteArray> &frames)
{
qint64 written = 0;
- if (m_pSocket)
- {
- for (int i = 0; i < frames.size(); ++i)
- {
- written += writeFrame(frames[i]);
+ if (m_pSocket) {
+ QList<QByteArray>::const_iterator it;
+ for (it = frames.cbegin(); it < frames.cend(); ++it) {
+ written += writeFrame(*it);
}
}
return written;
qint64 QWebSocketPrivate::writeFrame(const QByteArray &frame)
{
qint64 written = 0;
- if (m_pSocket)
- {
+ if (m_pSocket) {
written = m_pSocket->write(frame);
}
return written;
{
Q_ASSERT(pSocket);
QString line;
- if (pSocket)
- {
+ if (pSocket) {
char c;
- while (pSocket->getChar(&c))
- {
- if (c == '\r')
- {
+ while (pSocket->getChar(&c)) {
+ if (c == char('\r')) {
pSocket->getChar(&c);
break;
- }
- else
- {
+ } else {
line.append(QChar::fromLatin1(c));
}
}
void QWebSocketPrivate::processHandshake(QTcpSocket *pSocket)
{
Q_Q(QWebSocket);
- if (!pSocket)
- {
+ if (!pSocket) {
return;
}
int httpStatusCode;
QString httpStatusMessage;
const QRegularExpressionMatch match = regExp.match(statusLine);
- if (match.hasMatch())
- {
+ if (match.hasMatch()) {
QStringList tokens = match.capturedTexts();
tokens.removeFirst(); //remove the search string
- if (tokens.length() == 3)
- {
+ if (tokens.length() == 3) {
httpProtocol = tokens[0];
httpStatusCode = tokens[1].toInt();
httpStatusMessage = tokens[2].trimmed();
ok = true;
}
}
- if (!ok)
- {
+ if (!ok) {
errorDescription = tr("Invalid statusline in response: %1.").arg(statusLine);
- }
- else
- {
+ } else {
QString headerLine = readLine(pSocket);
QMap<QString, QString> headers;
- while (!headerLine.isEmpty())
- {
+ while (!headerLine.isEmpty()) {
const QStringList headerField = headerLine.split(QStringLiteral(": "), QString::SkipEmptyParts);
headers.insertMulti(headerField[0], headerField[1]);
headerLine = readLine(pSocket);
//const QString protocol = headers.value(QStringLiteral("Sec-WebSocket-Protocol"), QStringLiteral(""));
const QString version = headers.value(QStringLiteral("Sec-WebSocket-Version"), QStringLiteral(""));
- if (httpStatusCode == 101) //HTTP/x.y 101 Switching Protocols
- {
+ if (httpStatusCode == 101) {
+ //HTTP/x.y 101 Switching Protocols
bool conversionOk = false;
const float version = httpProtocol.midRef(5).toFloat(&conversionOk);
//TODO: do not check the httpStatusText right now
(!conversionOk || (version < 1.1f)) ||
(upgrade.toLower() != QStringLiteral("websocket")) ||
(connection.toLower() != QStringLiteral("upgrade")));
- if (ok)
- {
+ if (ok) {
const QString accept = calculateAcceptKey(QString::fromLatin1(m_key));
ok = (accept == acceptKey);
- if (!ok)
- {
+ if (!ok) {
errorDescription = tr("Accept-Key received from server %1 does not match the client key %2.").arg(acceptKey).arg(accept);
}
- }
- else
- {
+ } else {
errorDescription = tr("QWebSocketPrivate::processHandshake: Invalid statusline in response: %1.").arg(statusLine);
}
- }
- else if (httpStatusCode == 400) //HTTP/1.1 400 Bad Request
- {
- if (!version.isEmpty())
- {
+ } else if (httpStatusCode == 400) {
+ //HTTP/1.1 400 Bad Request
+ if (!version.isEmpty()) {
const QStringList versions = version.split(QStringLiteral(", "), QString::SkipEmptyParts);
- if (!versions.contains(QString::number(QWebSocketProtocol::currentVersion())))
- {
+ if (!versions.contains(QString::number(QWebSocketProtocol::currentVersion()))) {
//if needed to switch protocol version, then we are finished here
//because we cannot handle other protocols than the RFC one (v13)
errorDescription = tr("Handshake: Server requests a version that we don't support: %1.").arg(versions.join(QStringLiteral(", ")));
ok = false;
- }
- else
- {
+ } else {
//we tried v13, but something different went wrong
errorDescription = tr("QWebSocketPrivate::processHandshake: Unknown error condition encountered. Aborting connection.");
ok = false;
}
}
- }
- else
- {
+ } else {
errorDescription = tr("QWebSocketPrivate::processHandshake: Unhandled http status code: %1 (%2).").arg(httpStatusCode).arg(httpStatusMessage);
ok = false;
}
- if (!ok)
- {
- qDebug() << errorDescription;
+ if (!ok) {
setErrorString(errorDescription);
Q_EMIT q->error(QAbstractSocket::ConnectionRefusedError);
- }
- else
- {
+ } else {
//handshake succeeded
setSocketState(QAbstractSocket::ConnectedState);
Q_EMIT q->connected();
{
case QAbstractSocket::ConnectedState:
{
- if (webSocketState == QAbstractSocket::ConnectingState)
- {
+ if (webSocketState == QAbstractSocket::ConnectingState) {
m_key = generateKey();
- QString handshake = createHandShakeRequest(m_resourceName, m_requestUrl.host() % QStringLiteral(":") % QString::number(m_requestUrl.port(80)), origin(), QStringLiteral(""), QStringLiteral(""), m_key);
+ const QString handshake = createHandShakeRequest(m_resourceName, m_requestUrl.host() % QStringLiteral(":") % QString::number(m_requestUrl.port(80)), origin(), QStringLiteral(""), QStringLiteral(""), m_key);
m_pSocket->write(handshake.toLatin1());
}
break;
}
case QAbstractSocket::ClosingState:
{
- if (webSocketState == QAbstractSocket::ConnectedState)
- {
+ if (webSocketState == QAbstractSocket::ConnectedState) {
setSocketState(QAbstractSocket::ClosingState);
}
break;
}
case QAbstractSocket::UnconnectedState:
{
- if (webSocketState != QAbstractSocket::UnconnectedState)
- {
+ if (webSocketState != QAbstractSocket::UnconnectedState) {
setSocketState(QAbstractSocket::UnconnectedState);
Q_EMIT q->disconnected();
}
void QWebSocketPrivate::processData()
{
Q_ASSERT(m_pSocket);
- while (m_pSocket->bytesAvailable())
- {
- if (state() == QAbstractSocket::ConnectingState)
- {
+ while (m_pSocket->bytesAvailable()) {
+ if (state() == QAbstractSocket::ConnectingState) {
processHandshake(m_pSocket.data());
- }
- else
- {
+ } else {
m_dataProcessor.process(m_pSocket.data());
}
}
{
Q_ASSERT(m_pSocket);
quint32 maskingKey = 0;
- if (m_mustMask)
- {
+ if (m_mustMask) {
maskingKey = generateMaskingKey();
}
m_pSocket->write(getFrameHeader(QWebSocketProtocol::OC_PONG, data.size(), maskingKey, true));
- if (data.size() > 0)
- {
- if (m_mustMask)
- {
+ if (data.size() > 0) {
+ if (m_mustMask) {
QWebSocketProtocol::mask(&data, maskingKey);
}
m_pSocket->write(data);
QStringLiteral("Upgrade: websocket") <<
QStringLiteral("Connection: Upgrade") <<
QStringLiteral("Sec-WebSocket-Key: ") % QString::fromLatin1(key);
- if (!origin.isEmpty())
- {
+ if (!origin.isEmpty()) {
handshakeRequest << QStringLiteral("Origin: ") % origin;
}
handshakeRequest << QStringLiteral("Sec-WebSocket-Version: ") % QString::number(QWebSocketProtocol::currentVersion());
- if (extensions.length() > 0)
- {
+ if (extensions.length() > 0) {
handshakeRequest << QStringLiteral("Sec-WebSocket-Extensions: ") % extensions;
}
- if (protocols.length() > 0)
- {
+ if (protocols.length() > 0) {
handshakeRequest << QStringLiteral("Sec-WebSocket-Protocol: ") % protocols;
}
handshakeRequest << QStringLiteral("\r\n");
bool QWebSocketPrivate::waitForConnected(int msecs)
{
bool result = false;
- if (m_pSocket)
- {
+ if (m_pSocket) {
result = m_pSocket->waitForConnected(msecs);
}
return result;
bool QWebSocketPrivate::waitForDisconnected(int msecs)
{
bool result = false;
- if (m_pSocket)
- {
+ if (m_pSocket) {
result = m_pSocket->waitForDisconnected(msecs);
}
return result;
void QWebSocketPrivate::setSocketState(QAbstractSocket::SocketState state)
{
Q_Q(QWebSocket);
- if (m_socketState != state)
- {
+ if (m_socketState != state) {
m_socketState = state;
Q_EMIT q->stateChanged(m_socketState);
}
*/
void QWebSocketPrivate::setErrorString(const QString &errorString)
{
- m_errorString = errorString;
+ if (m_errorString != errorString) {
+ m_errorString = errorString;
+ }
}
/*!
QHostAddress QWebSocketPrivate::localAddress() const
{
QHostAddress address;
- if (m_pSocket)
- {
+ if (m_pSocket) {
address = m_pSocket->localAddress();
}
return address;
quint16 QWebSocketPrivate::localPort() const
{
quint16 port = 0;
- if (m_pSocket)
- {
+ if (m_pSocket) {
port = m_pSocket->localPort();
}
return port;
QAbstractSocket::PauseModes QWebSocketPrivate::pauseMode() const
{
QAbstractSocket::PauseModes mode = QAbstractSocket::PauseNever;
- if (m_pSocket)
- {
+ if (m_pSocket) {
mode = m_pSocket->pauseMode();
}
return mode;
QHostAddress QWebSocketPrivate::peerAddress() const
{
QHostAddress address;
- if (m_pSocket)
- {
+ if (m_pSocket) {
address = m_pSocket->peerAddress();
}
return address;
QString QWebSocketPrivate::peerName() const
{
QString name;
- if (m_pSocket)
- {
+ if (m_pSocket) {
name = m_pSocket->peerName();
}
return name;
quint16 QWebSocketPrivate::peerPort() const
{
quint16 port = 0;
- if (m_pSocket)
- {
+ if (m_pSocket) {
port = m_pSocket->peerPort();
}
return port;
*/
void QWebSocketPrivate::setProxy(const QNetworkProxy &networkProxy)
{
- m_configuration.m_proxy = networkProxy;
+ if (networkProxy != networkProxy) {
+ m_configuration.m_proxy = networkProxy;
+ }
}
#endif //QT_NO_NETWORKPROXY
qint64 QWebSocketPrivate::readBufferSize() const
{
qint64 size = 0;
- if (m_pSocket)
- {
+ if (m_pSocket) {
size = m_pSocket->readBufferSize();
}
return size;
*/
void QWebSocketPrivate::resume()
{
- if (m_pSocket)
- {
+ if (m_pSocket) {
m_pSocket->resume();
}
}
*/
void QWebSocketPrivate::setPauseMode(QAbstractSocket::PauseModes pauseMode)
{
- if (m_pSocket)
- {
+ if (m_pSocket) {
m_pSocket->setPauseMode(pauseMode);
}
}
*/
void QWebSocketPrivate::setReadBufferSize(qint64 size)
{
- if (m_pSocket)
- {
+ if (m_pSocket) {
m_pSocket->setReadBufferSize(size);
}
}
*/
void QWebSocketPrivate::setSocketOption(QAbstractSocket::SocketOption option, const QVariant &value)
{
- if (m_pSocket)
- {
+ if (m_pSocket) {
m_pSocket->setSocketOption(option, value);
}
}
QVariant QWebSocketPrivate::socketOption(QAbstractSocket::SocketOption option)
{
QVariant val;
- if (m_pSocket)
- {
+ if (m_pSocket) {
val = m_pSocket->socketOption(option);
}
return val;
struct QWebSocketConfiguration
{
+ Q_DISABLE_COPY(QWebSocketConfiguration)
+
public:
QWebSocketConfiguration();
public Q_SLOTS:
void close(QWebSocketProtocol::CloseCode closeCode, QString reason);
void open(const QUrl &url, bool mask);
- void ping(const QByteArray &payload);
+ void ping(QByteArray payload);
#ifndef QT_NO_SSL
void ignoreSslErrors();
void setSocketState(QAbstractSocket::SocketState state);
void setErrorString(const QString &errorString);
- qint64 doWriteData(const QByteArray &data, bool isBinary);
qint64 doWriteFrames(const QByteArray &data, bool isBinary);
void makeConnections(const QTcpSocket *pTcpSocket);
void releaseConnections(const QTcpSocket *pTcpSocket);
- QByteArray getFrameHeader(QWebSocketProtocol::OpCode opCode, quint64 payloadLength, quint32 maskingKey, bool lastFrame) const;
+ QByteArray getFrameHeader(QWebSocketProtocol::OpCode opCode, quint64 payloadLength, quint32 maskingKey, bool lastFrame);
QString calculateAcceptKey(const QString &key) const;
QString createHandShakeRequest(QString resourceName,
QString host,
*/
QWebSocketCorsAuthenticator::~QWebSocketCorsAuthenticator()
{
- if (d_ptr)
- {
- delete d_ptr;
- }
}
/*!
return *this;
}
+#ifdef Q_COMPILER_RVALUE_REFS
+QWebSocketCorsAuthenticator::QWebSocketCorsAuthenticator(QWebSocketCorsAuthenticator &&other) :
+ d_ptr(other.d_ptr.take())
+{}
+
+QWebSocketCorsAuthenticator &QWebSocketCorsAuthenticator::operator =(QWebSocketCorsAuthenticator &&other)
+{
+ qSwap(d_ptr, other.d_ptr);
+ return *this;
+}
+
+#endif
+
+void QWebSocketCorsAuthenticator::swap(QWebSocketCorsAuthenticator &other)
+{
+ if (&other != this) {
+ qSwap(d_ptr, other.d_ptr);
+ }
+}
+
/*!
Returns the origin this autenticator is handling about.
*/
#define QWEBSOCKETCORSAUTHENTICATOR_H
#include "QtWebSockets/qwebsockets_global.h"
+#include <QtCore/QScopedPointer>
QT_BEGIN_NAMESPACE
~QWebSocketCorsAuthenticator();
QWebSocketCorsAuthenticator(const QWebSocketCorsAuthenticator &other);
+#ifdef Q_COMPILER_RVALUE_REFS
+ QWebSocketCorsAuthenticator(QWebSocketCorsAuthenticator &&other);
+ QWebSocketCorsAuthenticator &operator =(QWebSocketCorsAuthenticator &&other);
+#endif
+
+ void swap(QWebSocketCorsAuthenticator &other);
+
QWebSocketCorsAuthenticator &operator =(const QWebSocketCorsAuthenticator &other);
QString origin() const;
bool allowed() const;
private:
- QWebSocketCorsAuthenticatorPrivate * const d_ptr;
+ QScopedPointer<QWebSocketCorsAuthenticatorPrivate> d_ptr;
};
QT_END_NAMESPACE
m_binaryMessage(),
m_textMessage(),
m_payloadLength(0),
- m_pConverterState(0),
+ m_pConverterState(Q_NULLPTR),
m_pTextCodec(QTextCodec::codecForName("UTF-8"))
{
clear();
QWebSocketDataProcessor::~QWebSocketDataProcessor()
{
clear();
- if (m_pConverterState)
- {
+ if (m_pConverterState) {
delete m_pConverterState;
- m_pConverterState = 0;
+ m_pConverterState = Q_NULLPTR;
}
}
{
bool isDone = false;
- while (!isDone)
- {
+ while (!isDone) {
QWebSocketFrame frame = QWebSocketFrame::readFrame(pIoDevice);
- if (frame.isValid())
- {
- if (frame.isControlFrame())
- {
+ if (frame.isValid()) {
+ if (frame.isControlFrame()) {
isDone = processControlFrame(frame);
- }
- else //we have a dataframe; opcode can be OC_CONTINUE, OC_TEXT or OC_BINARY
- {
- if (!m_isFragmented && frame.isContinuationFrame())
- {
+ } else {
+ //we have a dataframe; opcode can be OC_CONTINUE, OC_TEXT or OC_BINARY
+ if (!m_isFragmented && frame.isContinuationFrame()) {
clear();
Q_EMIT errorEncountered(QWebSocketProtocol::CC_PROTOCOL_ERROR, tr("Received Continuation frame, while there is nothing to continue."));
return;
}
- if (m_isFragmented && frame.isDataFrame() && !frame.isContinuationFrame())
- {
+ if (m_isFragmented && frame.isDataFrame() && !frame.isContinuationFrame()) {
clear();
Q_EMIT errorEncountered(QWebSocketProtocol::CC_PROTOCOL_ERROR, tr("All data frames after the initial data frame must have opcode 0 (continuation)."));
return;
}
- if (!frame.isContinuationFrame())
- {
+ if (!frame.isContinuationFrame()) {
m_opCode = frame.opCode();
m_isFragmented = !frame.isFinalFrame();
}
quint64 messageLength = (quint64)(m_opCode == QWebSocketProtocol::OC_TEXT) ? m_textMessage.length() : m_binaryMessage.length();
- if ((messageLength + quint64(frame.payload().length())) > MAX_MESSAGE_SIZE_IN_BYTES)
- {
+ if ((messageLength + quint64(frame.payload().length())) > MAX_MESSAGE_SIZE_IN_BYTES) {
clear();
Q_EMIT errorEncountered(QWebSocketProtocol::CC_TOO_MUCH_DATA, tr("Received message is too big."));
return;
}
- if (m_opCode == QWebSocketProtocol::OC_TEXT)
- {
+ if (m_opCode == QWebSocketProtocol::OC_TEXT) {
QString frameTxt = m_pTextCodec->toUnicode(frame.payload().constData(), frame.payload().size(), m_pConverterState);
bool failed = (m_pConverterState->invalidChars != 0) || (frame.isFinalFrame() && (m_pConverterState->remainingChars != 0));
- if (failed)
- {
+ if (failed) {
clear();
Q_EMIT errorEncountered(QWebSocketProtocol::CC_WRONG_DATATYPE, tr("Invalid UTF-8 code encountered."));
return;
- }
- else
- {
+ } else {
m_textMessage.append(frameTxt);
Q_EMIT textFrameReceived(frameTxt, frame.isFinalFrame());
}
- }
- else
- {
+ } else {
m_binaryMessage.append(frame.payload());
Q_EMIT binaryFrameReceived(frame.payload(), frame.isFinalFrame());
}
- if (frame.isFinalFrame())
- {
- if (m_opCode == QWebSocketProtocol::OC_TEXT)
- {
+ if (frame.isFinalFrame()) {
+ if (m_opCode == QWebSocketProtocol::OC_TEXT) {
Q_EMIT textMessageReceived(m_textMessage);
- }
- else
- {
+ } else {
Q_EMIT binaryMessageReceived(m_binaryMessage);
}
clear();
isDone = true;
}
}
- }
- else
- {
+ } else {
Q_EMIT errorEncountered(frame.closeCode(), frame.closeReason());
clear();
isDone = true;
m_binaryMessage.clear();
m_textMessage.clear();
m_payloadLength = 0;
- if (m_pConverterState)
- {
- if ((m_pConverterState->remainingChars != 0) || (m_pConverterState->invalidChars != 0))
- {
+ if (m_pConverterState) {
+ if ((m_pConverterState->remainingChars != 0) || (m_pConverterState->invalidChars != 0)) {
delete m_pConverterState;
- m_pConverterState = 0;
+ m_pConverterState = Q_NULLPTR;
}
}
if (!m_pConverterState)
quint16 closeCode = QWebSocketProtocol::CC_NORMAL;
QString closeReason;
QByteArray payload = frame.payload();
- if (payload.size() == 1) //size is either 0 (no close code and no reason) or >= 2 (at least a close code of 2 bytes)
- {
+ if (payload.size() == 1) {
+ //size is either 0 (no close code and no reason) or >= 2 (at least a close code of 2 bytes)
closeCode = QWebSocketProtocol::CC_PROTOCOL_ERROR;
closeReason = tr("Payload of close frame is too small.");
- }
- else if (payload.size() > 1) //close frame can have a close code and reason
- {
+ } else if (payload.size() > 1) {
+ //close frame can have a close code and reason
closeCode = qFromBigEndian<quint16>(reinterpret_cast<const uchar *>(payload.constData()));
- if (!QWebSocketProtocol::isCloseCodeValid(closeCode))
- {
+ if (!QWebSocketProtocol::isCloseCodeValid(closeCode)) {
closeCode = QWebSocketProtocol::CC_PROTOCOL_ERROR;
closeReason = tr("Invalid close code %1 detected.").arg(closeCode);
- }
- else
- {
- if (payload.size() > 2)
- {
+ } else {
+ if (payload.size() > 2) {
QTextCodec *tc = QTextCodec::codecForName("UTF-8");
QTextCodec::ConverterState state(QTextCodec::ConvertInvalidToNull);
closeReason = tc->toUnicode(payload.constData() + 2, payload.size() - 2, &state);
const bool failed = (state.invalidChars != 0) || (state.remainingChars != 0);
- if (failed)
- {
+ if (failed) {
closeCode = QWebSocketProtocol::CC_WRONG_DATATYPE;
closeReason = tr("Invalid UTF-8 code encountered.");
}
}
default:
{
- qDebug() << "DataProcessor::processControlFrame: Invalid opcode detected:" << static_cast<int>(frame.opCode());
+ Q_EMIT errorEncountered(QWebSocketProtocol::CC_PROTOCOL_ERROR, tr("Invalid opcode detected: %1").arg(int(frame.opCode())));
//Do nothing
break;
}
#include <QtCore/QtEndian>
#include <QtCore/QDebug>
+QT_BEGIN_NAMESPACE
+
/*!
\internal
*/
return *this;
}
+#ifdef Q_COMPILER_RVALUE_REFS
+/*!
+ \internal
+ */
+QWebSocketFrame::QWebSocketFrame(QWebSocketFrame &&other) :
+ m_closeCode(qMove(other.m_closeCode)),
+ m_closeReason(qMove(other.m_closeReason)),
+ m_isFinalFrame(qMove(other.m_isFinalFrame)),
+ m_mask(qMove(other.m_mask)),
+ m_rsv1(qMove(other.m_rsv1)),
+ m_rsv2(qMove(other.m_rsv2)),
+ m_rsv3(qMove(other.m_rsv3)),
+ m_opCode(qMove(other.m_opCode)),
+ m_length(qMove(other.m_length)),
+ m_payload(qMove(other.m_payload)),
+ m_isValid(qMove(other.m_isValid))
+{}
+
+
+/*!
+ \internal
+ */
+QWebSocketFrame &QWebSocketFrame::operator =(QWebSocketFrame &&other)
+{
+ qSwap(m_closeCode, other.m_closeCode);
+ qSwap(m_closeReason, other.m_closeReason);
+ qSwap(m_isFinalFrame, other.m_isFinalFrame);
+ qSwap(m_mask, other.m_mask);
+ qSwap(m_rsv1, other.m_rsv1);
+ qSwap(m_rsv2, other.m_rsv2);
+ qSwap(m_rsv3, other.m_rsv3);
+ qSwap(m_opCode, other.m_opCode);
+ qSwap(m_length, other.m_length);
+ qSwap(m_payload, other.m_payload);
+ qSwap(m_isValid, other.m_isValid);
+
+ return *this;
+}
+
+#endif
+
+/*!
+ \internal
+ */
+void QWebSocketFrame::swap(QWebSocketFrame &other)
+{
+ if (&other != this) {
+ qSwap(m_closeCode, other.m_closeCode);
+ qSwap(m_closeReason, other.m_closeReason);
+ qSwap(m_isFinalFrame, other.m_isFinalFrame);
+ qSwap(m_mask, other.m_mask);
+ qSwap(m_rsv1, other.m_rsv1);
+ qSwap(m_rsv2, other.m_rsv2);
+ qSwap(m_rsv3, other.m_rsv3);
+ qSwap(m_opCode, other.m_opCode);
+ qSwap(m_length, other.m_length);
+ qSwap(m_payload, other.m_payload);
+ qSwap(m_isValid, other.m_isValid);
+ }
+}
+
/*!
\internal
*/
//the GUI will hang for at most 5 seconds
//maybe, a QStateMachine should be used
bool ok = pIoDevice->waitForReadyRead(5000);
- if (!ok)
- {
+ if (!ok) {
frame.setError(QWebSocketProtocol::CC_GOING_AWAY, QObject::tr("Timeout when reading data from socket."));
processingState = PS_DISPATCH_RESULT;
- }
- else
- {
+ } else {
processingState = returnState;
}
break;
}
case PS_READ_HEADER:
{
- if (pIoDevice->bytesAvailable() >= 2)
- {
+ if (pIoDevice->bytesAvailable() >= 2) {
//FIN, RSV1-3, Opcode
char header[2] = {0};
bytesRead = pIoDevice->read(header, 2);
break;
}
}
- if (!frame.checkValidity())
- {
+ if (!frame.checkValidity()) {
processingState = PS_DISPATCH_RESULT;
}
- }
- else
- {
+ } else {
WAIT_FOR_MORE_DATA(2);
}
break;
case PS_READ_PAYLOAD_LENGTH:
{
- if (pIoDevice->bytesAvailable() >= 2)
- {
+ if (pIoDevice->bytesAvailable() >= 2) {
uchar length[2] = {0};
bytesRead = pIoDevice->read(reinterpret_cast<char *>(length), 2);
- if (bytesRead == -1)
- {
+ if (bytesRead == -1) {
frame.setError(QWebSocketProtocol::CC_GOING_AWAY, QObject::tr("Error occurred while reading from the network: %1").arg(pIoDevice->errorString()));
processingState = PS_DISPATCH_RESULT;
- }
- else
- {
+ } else {
payloadLength = qFromBigEndian<quint16>(reinterpret_cast<const uchar *>(length));
- if (payloadLength < 126)
- {
+ if (payloadLength < 126) {
//see http://tools.ietf.org/html/rfc6455#page-28 paragraph 5.2
//"in all cases, the minimal number of bytes MUST be used to encode
//the length, for example, the length of a 124-byte-long string
//can't be encoded as the sequence 126, 0, 124"
frame.setError(QWebSocketProtocol::CC_PROTOCOL_ERROR, QObject::tr("Lengths smaller than 126 must be expressed as one byte."));
processingState = PS_DISPATCH_RESULT;
- }
- else
- {
+ } else {
processingState = hasMask ? PS_READ_MASK : PS_READ_PAYLOAD;
}
}
- }
- else
- {
+ } else {
WAIT_FOR_MORE_DATA(2);
}
break;
case PS_READ_BIG_PAYLOAD_LENGTH:
{
- if (pIoDevice->bytesAvailable() >= 8)
- {
+ if (pIoDevice->bytesAvailable() >= 8) {
uchar length[8] = {0};
bytesRead = pIoDevice->read(reinterpret_cast<char *>(length), 8);
- if (bytesRead < 8)
- {
+ if (bytesRead < 8) {
frame.setError(QWebSocketProtocol::CC_ABNORMAL_DISCONNECTION, QObject::tr("Something went wrong during reading from the network."));
processingState = PS_DISPATCH_RESULT;
- }
- else
- {
+ } else {
//Most significant bit must be set to 0 as per http://tools.ietf.org/html/rfc6455#section-5.2
- //TODO: Do we check for that? Now we just strip off the highest bit
+ //We don't check for that. We just strip off the highest bit
payloadLength = qFromBigEndian<quint64>(length) & ~(1ULL << 63);
- if (payloadLength <= 0xFFFFu)
- {
+ if (payloadLength <= 0xFFFFu) {
//see http://tools.ietf.org/html/rfc6455#page-28 paragraph 5.2
//"in all cases, the minimal number of bytes MUST be used to encode
//the length, for example, the length of a 124-byte-long string
//can't be encoded as the sequence 126, 0, 124"
frame.setError(QWebSocketProtocol::CC_PROTOCOL_ERROR, QObject::tr("Lengths smaller than 65536 (2^16) must be expressed as 2 bytes."));
processingState = PS_DISPATCH_RESULT;
- }
- else
- {
+ } else {
processingState = hasMask ? PS_READ_MASK : PS_READ_PAYLOAD;
}
}
- }
- else
- {
+ } else {
WAIT_FOR_MORE_DATA(8);
}
case PS_READ_MASK:
{
- if (pIoDevice->bytesAvailable() >= 4)
- {
+ if (pIoDevice->bytesAvailable() >= 4) {
bytesRead = pIoDevice->read(reinterpret_cast<char *>(&frame.m_mask), sizeof(frame.m_mask));
- if (bytesRead == -1)
- {
+ if (bytesRead == -1) {
frame.setError(QWebSocketProtocol::CC_GOING_AWAY, QObject::tr("Error while reading from the network: %1.").arg(pIoDevice->errorString()));
processingState = PS_DISPATCH_RESULT;
- }
- else
- {
+ } else {
frame.m_mask = qFromBigEndian(frame.m_mask);
processingState = PS_READ_PAYLOAD;
}
- }
- else
- {
+ } else {
WAIT_FOR_MORE_DATA(4);
}
break;
case PS_READ_PAYLOAD:
{
- if (!payloadLength)
- {
+ if (!payloadLength) {
processingState = PS_DISPATCH_RESULT;
- }
- else if (payloadLength > MAX_FRAME_SIZE_IN_BYTES)
- {
+ } else if (payloadLength > MAX_FRAME_SIZE_IN_BYTES) {
frame.setError(QWebSocketProtocol::CC_TOO_MUCH_DATA, QObject::tr("Maximum framesize exceeded."));
processingState = PS_DISPATCH_RESULT;
- }
- else
- {
- quint64 bytesAvailable = static_cast<quint64>(pIoDevice->bytesAvailable());
- if (bytesAvailable >= payloadLength)
- {
+ } else {
+ quint64 bytesAvailable = quint64(pIoDevice->bytesAvailable());
+ if (bytesAvailable >= payloadLength) {
frame.m_payload = pIoDevice->read(payloadLength);
//payloadLength can be safely cast to an integer, as the MAX_FRAME_SIZE_IN_BYTES = MAX_INT
- if (frame.m_payload.length() != static_cast<int>(payloadLength)) //some error occurred; refer to the Qt documentation
- {
+ if (frame.m_payload.length() != int(payloadLength)) {
+ //some error occurred; refer to the Qt documentation of QIODevice::read()
frame.setError(QWebSocketProtocol::CC_ABNORMAL_DISCONNECTION, QObject::tr("Some serious error occurred while reading from the network."));
processingState = PS_DISPATCH_RESULT;
- }
- else
- {
- if (hasMask)
- {
+ } else {
+ if (hasMask) {
QWebSocketProtocol::mask(&frame.m_payload, frame.m_mask);
}
processingState = PS_DISPATCH_RESULT;
}
- }
- else
- {
+ } else {
WAIT_FOR_MORE_DATA(payloadLength); //if payload is too big, then this will timeout
}
}
*/
bool QWebSocketFrame::checkValidity()
{
- if (!isValid())
- {
- if (m_rsv1 || m_rsv2 || m_rsv3)
- {
- setError(QWebSocketProtocol::CC_PROTOCOL_ERROR, QObject::tr("Rsv field is non-zero"));
- }
- else if (QWebSocketProtocol::isOpCodeReserved(m_opCode))
- {
- setError(QWebSocketProtocol::CC_PROTOCOL_ERROR, QObject::tr("Used reserved opcode"));
- }
- else if (isControlFrame())
- {
- if (m_length > 125)
- {
- setError(QWebSocketProtocol::CC_PROTOCOL_ERROR, QObject::tr("Controle frame is larger than 125 bytes"));
- }
- else if (!m_isFinalFrame)
- {
- setError(QWebSocketProtocol::CC_PROTOCOL_ERROR, QObject::tr("Controle frames cannot be fragmented"));
- }
- else
- {
- m_isValid = true;
- }
- }
- else
- {
+ if (isValid()) {
+ return true;
+ }
+ if (m_rsv1 || m_rsv2 || m_rsv3) {
+ setError(QWebSocketProtocol::CC_PROTOCOL_ERROR, QObject::tr("Rsv field is non-zero"));
+ } else if (QWebSocketProtocol::isOpCodeReserved(m_opCode)) {
+ setError(QWebSocketProtocol::CC_PROTOCOL_ERROR, QObject::tr("Used reserved opcode"));
+ } else if (isControlFrame()) {
+ if (m_length > 125) {
+ setError(QWebSocketProtocol::CC_PROTOCOL_ERROR, QObject::tr("Controle frame is larger than 125 bytes"));
+ } else if (!m_isFinalFrame) {
+ setError(QWebSocketProtocol::CC_PROTOCOL_ERROR, QObject::tr("Controle frames cannot be fragmented"));
+ } else {
m_isValid = true;
}
+ } else {
+ m_isValid = true;
}
return m_isValid;
}
+
+QT_END_NAMESPACE
QWebSocketFrame &operator =(const QWebSocketFrame &other);
+#ifdef Q_COMPILER_RVALUE_REFS
+ QWebSocketFrame(QWebSocketFrame &&other);
+ QWebSocketFrame &operator =(QWebSocketFrame &&other);
+#endif
+
+ void swap(QWebSocketFrame &other);
+
QWebSocketProtocol::CloseCode closeCode() const;
QString closeReason() const;
bool isFinalFrame() const;
{
m_isValid = false;
clear();
- if (textStream.status() == QTextStream::Ok)
- {
- const QString requestLine = textStream.readLine();
- const QStringList tokens = requestLine.split(' ', QString::SkipEmptyParts);
- if (tokens.length() < 3)
- {
- m_isValid = false;
+ if (textStream.status() != QTextStream::Ok) {
+ return textStream;
+ }
+ const QString requestLine = textStream.readLine();
+ const QStringList tokens = requestLine.split(' ', QString::SkipEmptyParts);
+ if (tokens.length() < 3) {
+ m_isValid = false;
+ clear();
+ return textStream;
+ }
+ const QString verb(tokens.at(0));
+ const QString resourceName(tokens.at(1));
+ const QString httpProtocol(tokens.at(2));
+ bool conversionOk = false;
+ const float httpVersion = httpProtocol.midRef(5).toFloat(&conversionOk);
+
+ if (!conversionOk) {
+ clear();
+ m_isValid = false;
+ return textStream;
+ }
+ QString headerLine = textStream.readLine();
+ m_headers.clear();
+ while (!headerLine.isEmpty()) {
+ const QStringList headerField = headerLine.split(QStringLiteral(": "), QString::SkipEmptyParts);
+ if (headerField.length() < 2) {
clear();
+ return textStream;
}
- else
- {
- const QString verb(tokens.at(0));
- const QString resourceName(tokens.at(1));
- const QString httpProtocol(tokens.at(2));
- bool conversionOk = false;
- const float httpVersion = httpProtocol.midRef(5).toFloat(&conversionOk);
-
- if (conversionOk)
- {
- QString headerLine = textStream.readLine();
- m_headers.clear();
- while (!headerLine.isEmpty())
- {
- const QStringList headerField = headerLine.split(QStringLiteral(": "), QString::SkipEmptyParts);
- if (headerField.length() < 2)
- {
- clear();
- return textStream;
- }
- m_headers.insertMulti(headerField.at(0), headerField.at(1));
- headerLine = textStream.readLine();
- }
-
- const QString host = m_headers.value(QStringLiteral("Host"), QStringLiteral(""));
- m_requestUrl = QUrl::fromEncoded(resourceName.toLatin1());
- if (m_requestUrl.isRelative())
- {
- m_requestUrl.setHost(host);
- }
- if (m_requestUrl.scheme().isEmpty())
- {
- const QString scheme = isSecure() ? QStringLiteral("wss") : QStringLiteral("ws");
- m_requestUrl.setScheme(scheme);
- }
-
- const QStringList versionLines = m_headers.values(QStringLiteral("Sec-WebSocket-Version"));
- for (QStringList::const_iterator v = versionLines.begin(); v != versionLines.end(); ++v)
- {
- const QStringList versions = (*v).split(QStringLiteral(","), QString::SkipEmptyParts);
- for (QStringList::const_iterator i = versions.begin(); i != versions.end(); ++i)
- {
- bool ok = false;
- (void)(*i).toUInt(&ok);
- if (!ok)
- {
- clear();
- return textStream;
- }
- const QWebSocketProtocol::Version ver = QWebSocketProtocol::versionFromString((*i).trimmed());
- m_versions << ver;
- }
- }
- std::sort(m_versions.begin(), m_versions.end(), std::greater<QWebSocketProtocol::Version>()); //sort in descending order
- m_key = m_headers.value(QStringLiteral("Sec-WebSocket-Key"), QStringLiteral(""));
- const QString upgrade = m_headers.value(QStringLiteral("Upgrade"), QStringLiteral("")); //must be equal to "websocket", case-insensitive
- const QString connection = m_headers.value(QStringLiteral("Connection"), QStringLiteral("")); //must contain "Upgrade", case-insensitive
- const QStringList connectionLine = connection.split(QStringLiteral(","), QString::SkipEmptyParts);
- QStringList connectionValues;
- for (QStringList::const_iterator c = connectionLine.begin(); c != connectionLine.end(); ++c)
- {
- connectionValues << (*c).trimmed();
- }
-
- //optional headers
- m_origin = m_headers.value(QStringLiteral("Sec-WebSocket-Origin"), QStringLiteral(""));
- const QStringList protocolLines = m_headers.values(QStringLiteral("Sec-WebSocket-Protocol"));
- for (QStringList::const_iterator pl = protocolLines.begin(); pl != protocolLines.end(); ++pl)
- {
- QStringList protocols = (*pl).split(QStringLiteral(","), QString::SkipEmptyParts);
- for (QStringList::const_iterator p = protocols.begin(); p != protocols.end(); ++p)
- {
- m_protocols << (*p).trimmed();
- }
- }
- const QStringList extensionLines = m_headers.values(QStringLiteral("Sec-WebSocket-Extensions"));
- for (QStringList::const_iterator el = extensionLines.begin(); el != extensionLines.end(); ++el)
- {
- QStringList extensions = (*el).split(QStringLiteral(","), QString::SkipEmptyParts);
- for (QStringList::const_iterator e = extensions.begin(); e != extensions.end(); ++e)
- {
- m_extensions << (*e).trimmed();
- }
- }
+ m_headers.insertMulti(headerField.at(0), headerField.at(1));
+ headerLine = textStream.readLine();
+ }
- //TODO: authentication field
+ const QString host = m_headers.value(QStringLiteral("Host"), QStringLiteral(""));
+ m_requestUrl = QUrl::fromEncoded(resourceName.toLatin1());
+ if (m_requestUrl.isRelative()) {
+ m_requestUrl.setHost(host);
+ }
+ if (m_requestUrl.scheme().isEmpty()) {
+ const QString scheme = isSecure() ? QStringLiteral("wss") : QStringLiteral("ws");
+ m_requestUrl.setScheme(scheme);
+ }
- m_isValid = !(host.isEmpty() ||
- resourceName.isEmpty() ||
- m_versions.isEmpty() ||
- m_key.isEmpty() ||
- (verb != QStringLiteral("GET")) ||
- (!conversionOk || (httpVersion < 1.1f)) ||
- (upgrade.toLower() != QStringLiteral("websocket")) ||
- (!connectionValues.contains(QStringLiteral("upgrade"), Qt::CaseInsensitive)));
- }
- if (!m_isValid)
- {
+ const QStringList versionLines = m_headers.values(QStringLiteral("Sec-WebSocket-Version"));
+ for (QStringList::const_iterator v = versionLines.begin(); v != versionLines.end(); ++v) {
+ const QStringList versions = (*v).split(QStringLiteral(","), QString::SkipEmptyParts);
+ for (QStringList::const_iterator i = versions.begin(); i != versions.end(); ++i) {
+ bool ok = false;
+ (void)(*i).toUInt(&ok);
+ if (!ok) {
clear();
+ return textStream;
}
+ const QWebSocketProtocol::Version ver = QWebSocketProtocol::versionFromString((*i).trimmed());
+ m_versions << ver;
+ }
+ }
+ //sort in descending order
+ std::sort(m_versions.begin(), m_versions.end(), std::greater<QWebSocketProtocol::Version>());
+ m_key = m_headers.value(QStringLiteral("Sec-WebSocket-Key"), QStringLiteral(""));
+ //must contain "Upgrade", case-insensitive
+ const QString upgrade = m_headers.value(QStringLiteral("Upgrade"), QStringLiteral(""));
+ //must be equal to "websocket", case-insensitive
+ const QString connection = m_headers.value(QStringLiteral("Connection"), QStringLiteral(""));
+ const QStringList connectionLine = connection.split(QStringLiteral(","), QString::SkipEmptyParts);
+ QStringList connectionValues;
+ for (QStringList::const_iterator c = connectionLine.begin(); c != connectionLine.end(); ++c) {
+ connectionValues << (*c).trimmed();
+ }
+
+ //optional headers
+ m_origin = m_headers.value(QStringLiteral("Sec-WebSocket-Origin"), QStringLiteral(""));
+ const QStringList protocolLines = m_headers.values(QStringLiteral("Sec-WebSocket-Protocol"));
+ for (QStringList::const_iterator pl = protocolLines.begin(); pl != protocolLines.end(); ++pl) {
+ QStringList protocols = (*pl).split(QStringLiteral(","), QString::SkipEmptyParts);
+ for (QStringList::const_iterator p = protocols.begin(); p != protocols.end(); ++p) {
+ m_protocols << (*p).trimmed();
}
}
+ const QStringList extensionLines = m_headers.values(QStringLiteral("Sec-WebSocket-Extensions"));
+ for (QStringList::const_iterator el = extensionLines.begin(); el != extensionLines.end(); ++el) {
+ QStringList extensions = (*el).split(QStringLiteral(","), QString::SkipEmptyParts);
+ for (QStringList::const_iterator e = extensions.begin(); e != extensions.end(); ++e) {
+ m_extensions << (*e).trimmed();
+ }
+ }
+
+ //TODO: authentication field
+
+ m_isValid = !(host.isEmpty() ||
+ resourceName.isEmpty() ||
+ m_versions.isEmpty() ||
+ m_key.isEmpty() ||
+ (verb != QStringLiteral("GET")) ||
+ (!conversionOk || (httpVersion < 1.1f)) ||
+ (upgrade.toLower() != QStringLiteral("websocket")) ||
+ (!connectionValues.contains(QStringLiteral("upgrade"), Qt::CaseInsensitive)));
+ if (!m_isValid) {
+ clear();
+ }
return textStream;
}
#include "qwebsockethandshakeresponse_p.h"
#include "qwebsockethandshakerequest_p.h"
+#include "qwebsocketprotocol.h"
#include <QtCore/QString>
#include <QtCore/QTextStream>
m_response(),
m_acceptedProtocol(),
m_acceptedExtension(),
- m_acceptedVersion(QWebSocketProtocol::V_Unknow)
+ m_acceptedVersion(QWebSocketProtocol::V_Unknow),
+ m_error(QWebSocketProtocol::CC_NORMAL),
+ m_errorString()
{
- m_response = getHandshakeResponse(request, serverName, isOriginAllowed, supportedVersions, supportedProtocols, supportedExtensions);
+ m_response = getHandshakeResponse(request, serverName,
+ isOriginAllowed, supportedVersions,
+ supportedProtocols, supportedExtensions);
m_isValid = true;
}
*/
QString QWebSocketHandshakeResponse::calculateAcceptKey(const QString &key) const
{
- const QString tmpKey = key % QStringLiteral("258EAFA5-E914-47DA-95CA-C5AB0DC85B11"); //the UID comes from RFC6455
+ //the UID comes from RFC6455
+ const QString tmpKey = key % QStringLiteral("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
const QByteArray hash = QCryptographicHash::hash(tmpKey.toLatin1(), QCryptographicHash::Sha1);
return QString::fromLatin1(hash.toBase64());
}
QStringList response;
m_canUpgrade = false;
- if (!isOriginAllowed)
- {
- if (!m_canUpgrade)
- {
+ if (!isOriginAllowed) {
+ if (!m_canUpgrade) {
+ m_error = QWebSocketProtocol::CC_POLICY_VIOLATED;
+ m_errorString = tr("Access forbidden.");
response << QStringLiteral("HTTP/1.1 403 Access Forbidden");
}
- }
- else
- {
- if (request.isValid())
- {
+ } else {
+ if (request.isValid()) {
const QString acceptKey = calculateAcceptKey(request.key());
const QList<QString> matchingProtocols = supportedProtocols.toSet().intersect(request.protocols().toSet()).toList();
const QList<QString> matchingExtensions = supportedExtensions.toSet().intersect(request.extensions().toSet()).toList();
QList<QWebSocketProtocol::Version> matchingVersions = request.versions().toSet().intersect(supportedVersions.toSet()).toList();
std::sort(matchingVersions.begin(), matchingVersions.end(), std::greater<QWebSocketProtocol::Version>()); //sort in descending order
- if (matchingVersions.isEmpty())
- {
+ if (matchingVersions.isEmpty()) {
+ m_error = QWebSocketProtocol::CC_PROTOCOL_ERROR;
+ m_errorString = tr("Unsupported version requested.");
m_canUpgrade = false;
- }
- else
- {
+ } else {
response << QStringLiteral("HTTP/1.1 101 Switching Protocols") <<
QStringLiteral("Upgrade: websocket") <<
QStringLiteral("Connection: Upgrade") <<
QStringLiteral("Sec-WebSocket-Accept: ") % acceptKey;
- if (!matchingProtocols.isEmpty())
- {
+ if (!matchingProtocols.isEmpty()) {
m_acceptedProtocol = matchingProtocols.first();
response << QStringLiteral("Sec-WebSocket-Protocol: ") % m_acceptedProtocol;
}
- if (!matchingExtensions.isEmpty())
- {
+ if (!matchingExtensions.isEmpty()) {
m_acceptedExtension = matchingExtensions.first();
response << QStringLiteral("Sec-WebSocket-Extensions: ") % m_acceptedExtension;
}
QString origin = request.origin().trimmed();
- if (origin.isEmpty())
- {
+ if (origin.isEmpty()) {
origin = QStringLiteral("*");
}
response << QStringLiteral("Server: ") % serverName <<
m_acceptedVersion = QWebSocketProtocol::currentVersion();
m_canUpgrade = true;
}
- }
- else
- {
+ } else {
+ m_error = QWebSocketProtocol::CC_PROTOCOL_ERROR;
+ m_errorString = tr("Bad handshake request received.");
m_canUpgrade = false;
}
- if (!m_canUpgrade)
- {
+ if (!m_canUpgrade) {
response << QStringLiteral("HTTP/1.1 400 Bad Request");
QStringList versions;
- Q_FOREACH(QWebSocketProtocol::Version version, supportedVersions)
- {
+ Q_FOREACH (QWebSocketProtocol::Version version, supportedVersions) {
versions << QString::number(static_cast<int>(version));
}
response << QStringLiteral("Sec-WebSocket-Version: ") % versions.join(QStringLiteral(", "));
*/
QTextStream &QWebSocketHandshakeResponse::writeToStream(QTextStream &textStream) const
{
- if (!m_response.isEmpty())
- {
+ if (!m_response.isEmpty()) {
textStream << m_response.toLatin1().constData();
- }
- else
- {
+ } else {
textStream.setStatus(QTextStream::WriteFailed);
}
return textStream;
/*!
\internal
*/
+QWebSocketProtocol::CloseCode QWebSocketHandshakeResponse::error() const
+{
+ return m_error;
+}
+
+/*!
+ \internal
+ */
+QString QWebSocketHandshakeResponse::errorString() const
+{
+ return m_errorString;
+}
+
+/*!
+ \internal
+ */
QString QWebSocketHandshakeResponse::acceptedExtension() const
{
return m_acceptedExtension;
QString acceptedExtension() const;
QWebSocketProtocol::Version acceptedVersion() const;
+ QWebSocketProtocol::CloseCode error() const;
+ QString errorString() const;
+
public Q_SLOTS:
Q_SIGNALS:
QString m_acceptedProtocol;
QString m_acceptedExtension;
QWebSocketProtocol::Version m_acceptedVersion;
+ QWebSocketProtocol::CloseCode m_error;
+ QString m_errorString;
QString calculateAcceptKey(const QString &key) const;
QString getHandshakeResponse(const QWebSocketHandshakeRequest &request,
const int ver = versionString.toInt(&ok);
QSet<Version> supportedVersions;
supportedVersions << V_0 << V_4 << V_5 << V_6 << V_7 << V_8 << V_13;
- if (ok)
- {
- if (supportedVersions.contains(static_cast<Version>(ver)))
- {
+ if (ok) {
+ if (supportedVersions.contains(static_cast<Version>(ver))) {
version = static_cast<Version>(ver);
}
}
*/
void mask(QByteArray *payload, quint32 maskingKey)
{
+ Q_ASSERT(payload);
mask(payload->data(), payload->size(), maskingKey);
}
*/
void mask(char *payload, quint64 size, quint32 maskingKey)
{
- const quint8 mask[] = { static_cast<quint8>((maskingKey & 0xFF000000u) >> 24),
- static_cast<quint8>((maskingKey & 0x00FF0000u) >> 16),
- static_cast<quint8>((maskingKey & 0x0000FF00u) >> 8),
- static_cast<quint8>((maskingKey & 0x000000FFu))
+ Q_ASSERT(payload);
+ const quint8 mask[] = { quint8((maskingKey & 0xFF000000u) >> 24),
+ quint8((maskingKey & 0x00FF0000u) >> 16),
+ quint8((maskingKey & 0x0000FF00u) >> 8),
+ quint8((maskingKey & 0x000000FFu))
};
int i = 0;
- while (size-- > 0)
- {
+ while (size-- > 0) {
*payload++ ^= mask[i++ % 4];
}
}
*/
/*!
+ \fn void QWebSocketServer::serverError(QNetworkProtocol::CloseCode closeCode)
+ This signal is emitted when an error occurs during the setup of a web socket connection.
+ The \a closeCode parameter describes the type of error that occurred
+
+ \sa errorString()
+*/
+
+/*!
\fn void QWebSocketServer::newConnection()
This signal is emitted every time a new connection is available.
*/
/*!
+ \fn void QWebSocketServer::closed()
+ This signal is emitted when the server closed it's connection.
+
+ \sa close()
+*/
+
+/*!
\fn void QWebSocketServer::originAuthenticationRequired(QWebSocketCorsAuthenticator *authenticator)
This signal is emitted when a new connection is requested.
The slot connected to this signal should indicate whether the origin (which can be determined by the origin() call)
Returns an error code for the last error that occurred.
\sa errorString()
*/
-QAbstractSocket::SocketError QWebSocketServer::serverError() const
+QWebSocketProtocol::CloseCode QWebSocketServer::error() const
{
Q_D(const QWebSocketServer);
return d->serverError();
/*!
Returns a list of websocket subprotocols that this server supports.
*/
-QList<QString> QWebSocketServer::supportedProtocols() const
+QStringList QWebSocketServer::supportedProtocols() const
{
Q_D(const QWebSocketServer);
return d->supportedProtocols();
/*!
Returns a list of websocket extensions that this server supports.
*/
-QList<QString> QWebSocketServer::supportedExtensions() const
+QStringList QWebSocketServer::supportedExtensions() const
{
Q_D(const QWebSocketServer);
return d->supportedExtensions();
bool hasPendingConnections() const;
virtual QWebSocket *nextPendingConnection();
- QAbstractSocket::SocketError serverError() const;
+ QWebSocketProtocol::CloseCode error() const;
QString errorString() const;
void pauseAccepting();
#endif
QList<QWebSocketProtocol::Version> supportedVersions() const;
- QList<QString> supportedProtocols() const;
- QList<QString> supportedExtensions() const;
+ QStringList supportedProtocols() const;
+ QStringList supportedExtensions() const;
Q_SIGNALS:
void acceptError(QAbstractSocket::SocketError socketError);
+ void serverError(QWebSocketProtocol::CloseCode closeCode);
//TODO: should use a delegate iso of a synchronous signal
void originAuthenticationRequired(QWebSocketCorsAuthenticator *pAuthenticator);
void newConnection();
void peerVerifyError(const QSslError &error);
void sslErrors(const QList<QSslError> &errors);
#endif
+ void closed();
private:
QWebSocketServerPrivate * const d_ptr;
m_pTcpServer(Q_NULLPTR),
m_serverName(serverName),
m_secureMode(secureMode),
- m_pendingConnections()
+ m_pendingConnections(),
+ m_error(QWebSocketProtocol::CC_NORMAL),
+ m_errorString()
{
Q_ASSERT(pWebSocketServer);
- if (m_secureMode == NON_SECURE_MODE)
- {
+ if (m_secureMode == NON_SECURE_MODE) {
m_pTcpServer = new QTcpServer(this);
connect(m_pTcpServer, SIGNAL(newConnection()), this, SLOT(onNewConnection()));
- }
- else
+ } else
{
#ifndef QT_NO_SSL
QSslServer *pSslServer = new QSslServer(this);
connect(pSslServer, SIGNAL(newEncryptedConnection()), this, SLOT(onNewConnection()));
connect(pSslServer, SIGNAL(peerVerifyError(QSslError)), q_ptr, SIGNAL(peerVerifyError(QSslError)));
connect(pSslServer, SIGNAL(sslErrors(QList<QSslError>)), q_ptr, SIGNAL(sslErrors(QList<QSslError>)));
+#else
+ qFatal("SSL not supported on this platform.");
#endif
}
connect(m_pTcpServer, SIGNAL(acceptError(QAbstractSocket::SocketError)), q_ptr, SIGNAL(acceptError(QAbstractSocket::SocketError)));
*/
QWebSocketServerPrivate::~QWebSocketServerPrivate()
{
- while (!m_pendingConnections.isEmpty())
- {
- QWebSocket *pWebSocket = m_pendingConnections.dequeue();
- pWebSocket->close(QWebSocketProtocol::CC_GOING_AWAY, tr("Server closed."));
- pWebSocket->deleteLater();
- }
+ close();
m_pTcpServer->deleteLater();
}
*/
void QWebSocketServerPrivate::close()
{
+ Q_Q(QWebSocketServer);
m_pTcpServer->close();
+ while (!m_pendingConnections.isEmpty()) {
+ QWebSocket *pWebSocket = m_pendingConnections.dequeue();
+ pWebSocket->close(QWebSocketProtocol::CC_GOING_AWAY, tr("Server closed."));
+ pWebSocket->deleteLater();
+ }
+ //emit signal via the event queue, so the server gets time
+ //to process any hanging events, like flushing buffers aso
+ QMetaObject::invokeMethod(q, "closed", Qt::QueuedConnection);
}
/*!
*/
QString QWebSocketServerPrivate::errorString() const
{
- return m_pTcpServer->errorString();
+ if (m_errorString.isEmpty()) {
+ return m_pTcpServer->errorString();
+ } else {
+ return m_errorString;
+ }
}
/*!
*/
void QWebSocketServerPrivate::addPendingConnection(QWebSocket *pWebSocket)
{
- if (m_pendingConnections.size() < maxPendingConnections())
- {
+ if (m_pendingConnections.size() < maxPendingConnections()) {
m_pendingConnections.enqueue(pWebSocket);
}
}
QWebSocket *QWebSocketServerPrivate::nextPendingConnection()
{
QWebSocket *pWebSocket = Q_NULLPTR;
- if (!m_pendingConnections.isEmpty())
- {
+ if (!m_pendingConnections.isEmpty()) {
pWebSocket = m_pendingConnections.dequeue();
}
return pWebSocket;
/*!
\internal
*/
-QAbstractSocket::SocketError QWebSocketServerPrivate::serverError() const
+QWebSocketProtocol::CloseCode QWebSocketServerPrivate::serverError() const
{
- return m_pTcpServer->serverError();
+ return m_error;
}
/*!
/*!
\internal
*/
-QList<QString> QWebSocketServerPrivate::supportedProtocols() const
+QStringList QWebSocketServerPrivate::supportedProtocols() const
{
QList<QString> supportedProtocols;
return supportedProtocols; //no protocols are currently supported
/*!
\internal
*/
-QList<QString> QWebSocketServerPrivate::supportedExtensions() const
+QStringList QWebSocketServerPrivate::supportedExtensions() const
{
QList<QString> supportedExtensions;
return supportedExtensions; //no extensions are currently supported
*/
void QWebSocketServerPrivate::setServerName(const QString &serverName)
{
- m_serverName = serverName;
+ if (m_serverName != serverName) {
+ m_serverName = serverName;
+ }
}
/*!
#ifndef QT_NO_SSL
void QWebSocketServerPrivate::setSslConfiguration(const QSslConfiguration &sslConfiguration)
{
- if (m_secureMode == SECURE_MODE)
- {
+ if (m_secureMode == SECURE_MODE) {
qobject_cast<QSslServer *>(m_pTcpServer)->setSslConfiguration(sslConfiguration);
+ } else {
+ qWarning() << tr("Cannot set SSL configuration for non-secure server.");
}
}
QSslConfiguration QWebSocketServerPrivate::sslConfiguration() const
{
- if (m_secureMode == SECURE_MODE)
- {
+ if (m_secureMode == SECURE_MODE) {
return qobject_cast<QSslServer *>(m_pTcpServer)->sslConfiguration();
- }
- else
- {
+ } else {
return QSslConfiguration::defaultConfiguration();
}
}
#endif
+void QWebSocketServerPrivate::setError(QWebSocketProtocol::CloseCode code, QString errorString)
+{
+ if ((m_error != code) || (m_errorString != errorString)) {
+ Q_Q(QWebSocketServer);
+ m_error = code;
+ m_errorString = errorString;
+ Q_EMIT q->serverError(code);
+ }
+}
+
/*!
\internal
*/
void QWebSocketServerPrivate::onCloseConnection()
{
QTcpSocket *pTcpSocket = qobject_cast<QTcpSocket*>(sender());
- if (pTcpSocket)
- {
+ if (pTcpSocket) {
pTcpSocket->close();
}
}
{
Q_Q(QWebSocketServer);
QTcpSocket *pTcpSocket = qobject_cast<QTcpSocket*>(sender());
- if (pTcpSocket)
- {
+ if (pTcpSocket) {
bool success = false;
bool isSecure = false;
QTextStream textStream(pTcpSocket);
textStream >> request;
- if (request.isValid())
- {
+ if (request.isValid()) {
QWebSocketCorsAuthenticator corsAuthenticator(request.origin());
Q_EMIT q->originAuthenticationRequired(&corsAuthenticator);
supportedProtocols(),
supportedExtensions());
- if (response.isValid())
- {
+ if (response.isValid()) {
QTextStream httpStream(pTcpSocket);
httpStream << response;
httpStream.flush();
- if (response.canUpgrade())
- {
+ if (response.canUpgrade()) {
QWebSocket *pWebSocket = QWebSocketPrivate::upgradeFrom(pTcpSocket, request, response);
- if (pWebSocket)
- {
+ if (pWebSocket) {
pWebSocket->setParent(this);
addPendingConnection(pWebSocket);
Q_EMIT q->newConnection();
success = true;
- }
- else
- {
- //TODO: should set or emit error
- qDebug() << tr("Upgrading to websocket failed.");
+ } else {
+ setError(QWebSocketProtocol::CC_ABNORMAL_DISCONNECTION, tr("Upgrading to websocket failed."));
}
}
- else
- {
- //TODO: should set or emit error
- qDebug() << tr("Cannot upgrade to websocket.");
+ else {
+ setError(response.error(), response.errorString());
}
- }
- else
- {
- //TODO: should set or emit error
- qDebug() << tr("Invalid response received.");
+ } else {
+ setError(QWebSocketProtocol::CC_PROTOCOL_ERROR, tr("Invalid response received."));
}
}
- if (!success)
- {
- //TODO: should set or emit error
- qDebug() << tr("Closing socket because of invalid or unsupported request.");
+ if (!success) {
+ qWarning() << tr("Closing socket because of invalid or unsupported request.");
pTcpSocket->close();
}
- }
- else
- {
- qWarning() << "Sender socket is NULL. This should not happen, otherwise it is a Qt bug!!!";
+ } else {
+ qWarning() << tr("Sender socket is NULL. This should not happen, otherwise it is a Qt bug!!!");
}
}
#endif
void resumeAccepting();
QHostAddress serverAddress() const;
- QAbstractSocket::SocketError serverError() const;
+ QWebSocketProtocol::CloseCode serverError() const;
quint16 serverPort() const;
void setMaxPendingConnections(int numConnections);
bool setSocketDescriptor(qintptr socketDescriptor);
bool waitForNewConnection(int msec = 0, bool *timedOut = Q_NULLPTR);
QList<QWebSocketProtocol::Version> supportedVersions() const;
- QList<QString> supportedProtocols() const;
- QList<QString> supportedExtensions() const;
+ QStringList supportedProtocols() const;
+ QStringList supportedExtensions() const;
void setServerName(const QString &serverName);
QString serverName() const;
QSslConfiguration sslConfiguration() const;
#endif
+ void setError(QWebSocketProtocol::CloseCode code, QString errorString);
+
private Q_SLOTS:
void onNewConnection();
void onCloseConnection();
QString m_serverName;
SecureMode m_secureMode;
QQueue<QWebSocket *> m_pendingConnections;
+ QWebSocketProtocol::CloseCode m_error;
+ QString m_errorString;
void addPendingConnection(QWebSocket *pWebSocket);
};
//otherwise, the intermediate object is deleted and the data pointer becomes invalid
QByteArray latin1 = inputdata.toLatin1();
char *data = latin1.data();
- //char *data = inputdata.toLatin1().data();
-
- qDebug() << hex << mask;
- qDebug() << QByteArray(data, inputdata.size()).toHex();
QWebSocketProtocol::mask(data, inputdata.size(), mask);
QCOMPARE(QByteArray::fromRawData(data, inputdata.size()), result);
url.setPath("/runCase?");
QUrlQuery query;
query.addQueryItem("case", QString::number(nbr + 1));
- query.addQueryItem("agent", "QWebSockets/0.9");
+ query.addQueryItem("agent", "QtWebSockets/1.0");
url.setQuery(query);
pWebSocket->open(url);
spy.wait(60000);
pWebSocket->close();
delete pWebSocket;
- pWebSocket = 0;
+ pWebSocket = Q_NULLPTR;
runTestCase(nbr + 1, total);
}
QVERIFY(numberOfTestCases > 0);
QObject::disconnect(pWebSocket, &QWebSocket::textMessageReceived, 0, 0);
-
runTestCases(0, numberOfTestCases);
url.setPath("/updateReports?");
pWebSocket->open(url);
spy.wait(60000);
delete pWebSocket;
- pWebSocket = 0;
}
QTEST_MAIN(tst_ComplianceTest)