1 /****************************************************************************
3 ** Copyright (C) 2014 Kurt Pattyn <pattyn.kurt@gmail.com>.
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the QtWebSockets module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL21$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
26 ** In addition, as a special exception, Digia gives you certain additional
27 ** rights. These rights are described in the Digia Qt LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
32 ****************************************************************************/
34 #include "qwebsocketserver.h"
35 #include "qwebsocketserver_p.h"
37 #include "qsslserver_p.h"
39 #include "qwebsocketprotocol.h"
40 #include "qwebsockethandshakerequest_p.h"
41 #include "qwebsockethandshakeresponse_p.h"
42 #include "qwebsocket.h"
43 #include "qwebsocket_p.h"
44 #include "qwebsocketcorsauthenticator.h"
46 #include <QtNetwork/QTcpServer>
47 #include <QtNetwork/QTcpSocket>
48 #include <QtNetwork/QNetworkProxy>
55 QWebSocketServerPrivate::QWebSocketServerPrivate(const QString &serverName,
56 QWebSocketServerPrivate::SslMode secureMode,
57 QWebSocketServer * const pWebSocketServer) :
59 q_ptr(pWebSocketServer),
60 m_pTcpServer(Q_NULLPTR),
61 m_serverName(serverName),
62 m_secureMode(secureMode),
63 m_pendingConnections(),
64 m_error(QWebSocketProtocol::CloseCodeNormal),
66 m_maxPendingConnections(30)
68 Q_ASSERT(pWebSocketServer);
74 void QWebSocketServerPrivate::init()
76 if (m_secureMode == NonSecureMode) {
77 m_pTcpServer = new QTcpServer();
78 if (Q_LIKELY(m_pTcpServer))
79 QObjectPrivate::connect(m_pTcpServer, &QTcpServer::newConnection,
80 this, &QWebSocketServerPrivate::onNewConnection);
82 qFatal("Could not allocate memory for tcp server.");
85 QSslServer *pSslServer = new QSslServer();
86 m_pTcpServer = pSslServer;
87 if (Q_LIKELY(m_pTcpServer)) {
88 QObjectPrivate::connect(pSslServer, &QSslServer::newEncryptedConnection,
89 this, &QWebSocketServerPrivate::onNewConnection,
90 Qt::QueuedConnection);
91 QObject::connect(pSslServer, &QSslServer::peerVerifyError,
92 q_ptr, &QWebSocketServer::peerVerifyError);
93 QObject::connect(pSslServer, &QSslServer::sslErrors,
94 q_ptr, &QWebSocketServer::sslErrors);
97 qFatal("SSL not supported on this platform.");
100 QObject::connect(m_pTcpServer, &QTcpServer::acceptError, q_ptr, &QWebSocketServer::acceptError);
106 QWebSocketServerPrivate::~QWebSocketServerPrivate()
109 m_pTcpServer->deleteLater();
115 void QWebSocketServerPrivate::close(bool aboutToDestroy)
117 Q_Q(QWebSocketServer);
118 m_pTcpServer->close();
119 while (!m_pendingConnections.isEmpty()) {
120 QWebSocket *pWebSocket = m_pendingConnections.dequeue();
121 pWebSocket->close(QWebSocketProtocol::CloseCodeGoingAway,
122 QWebSocketServer::tr("Server closed."));
123 pWebSocket->deleteLater();
125 if (!aboutToDestroy) {
126 //emit signal via the event queue, so the server gets time
127 //to process any hanging events, like flushing buffers aso
128 QMetaObject::invokeMethod(q, "closed", Qt::QueuedConnection);
135 QString QWebSocketServerPrivate::errorString() const
137 if (m_errorString.isEmpty())
138 return m_pTcpServer->errorString();
140 return m_errorString;
146 bool QWebSocketServerPrivate::hasPendingConnections() const
148 return !m_pendingConnections.isEmpty();
154 bool QWebSocketServerPrivate::isListening() const
156 return m_pTcpServer->isListening();
162 bool QWebSocketServerPrivate::listen(const QHostAddress &address, quint16 port)
164 bool success = m_pTcpServer->listen(address, port);
166 setErrorFromSocketError(m_pTcpServer->serverError(), m_pTcpServer->errorString());
173 int QWebSocketServerPrivate::maxPendingConnections() const
175 return m_maxPendingConnections;
181 void QWebSocketServerPrivate::addPendingConnection(QWebSocket *pWebSocket)
183 if (m_pendingConnections.size() < maxPendingConnections())
184 m_pendingConnections.enqueue(pWebSocket);
190 void QWebSocketServerPrivate::setErrorFromSocketError(QAbstractSocket::SocketError error,
191 const QString &errorDescription)
194 setError(QWebSocketProtocol::CloseCodeAbnormalDisconnection, errorDescription);
200 QWebSocket *QWebSocketServerPrivate::nextPendingConnection()
202 QWebSocket *pWebSocket = Q_NULLPTR;
203 if (Q_LIKELY(!m_pendingConnections.isEmpty()))
204 pWebSocket = m_pendingConnections.dequeue();
211 void QWebSocketServerPrivate::pauseAccepting()
213 m_pTcpServer->pauseAccepting();
216 #ifndef QT_NO_NETWORKPROXY
220 QNetworkProxy QWebSocketServerPrivate::proxy() const
222 return m_pTcpServer->proxy();
228 void QWebSocketServerPrivate::setProxy(const QNetworkProxy &networkProxy)
230 m_pTcpServer->setProxy(networkProxy);
236 void QWebSocketServerPrivate::resumeAccepting()
238 m_pTcpServer->resumeAccepting();
244 QHostAddress QWebSocketServerPrivate::serverAddress() const
246 return m_pTcpServer->serverAddress();
252 QWebSocketProtocol::CloseCode QWebSocketServerPrivate::serverError() const
260 quint16 QWebSocketServerPrivate::serverPort() const
262 return m_pTcpServer->serverPort();
268 void QWebSocketServerPrivate::setMaxPendingConnections(int numConnections)
270 if (m_pTcpServer->maxPendingConnections() <= numConnections)
271 m_pTcpServer->setMaxPendingConnections(numConnections + 1);
272 m_maxPendingConnections = numConnections;
278 bool QWebSocketServerPrivate::setSocketDescriptor(qintptr socketDescriptor)
280 return m_pTcpServer->setSocketDescriptor(socketDescriptor);
286 qintptr QWebSocketServerPrivate::socketDescriptor() const
288 return m_pTcpServer->socketDescriptor();
294 QList<QWebSocketProtocol::Version> QWebSocketServerPrivate::supportedVersions() const
296 QList<QWebSocketProtocol::Version> supportedVersions;
297 supportedVersions << QWebSocketProtocol::currentVersion(); //we only support V13
298 return supportedVersions;
304 QStringList QWebSocketServerPrivate::supportedProtocols() const
306 QStringList supportedProtocols;
307 return supportedProtocols; //no protocols are currently supported
313 QStringList QWebSocketServerPrivate::supportedExtensions() const
315 QStringList supportedExtensions;
316 return supportedExtensions; //no extensions are currently supported
322 void QWebSocketServerPrivate::setServerName(const QString &serverName)
324 if (m_serverName != serverName)
325 m_serverName = serverName;
331 QString QWebSocketServerPrivate::serverName() const
339 QWebSocketServerPrivate::SslMode QWebSocketServerPrivate::secureMode() const
345 void QWebSocketServerPrivate::setSslConfiguration(const QSslConfiguration &sslConfiguration)
347 if (m_secureMode == SecureMode)
348 qobject_cast<QSslServer *>(m_pTcpServer)->setSslConfiguration(sslConfiguration);
351 QSslConfiguration QWebSocketServerPrivate::sslConfiguration() const
353 if (m_secureMode == SecureMode)
354 return qobject_cast<QSslServer *>(m_pTcpServer)->sslConfiguration();
356 return QSslConfiguration::defaultConfiguration();
360 void QWebSocketServerPrivate::setError(QWebSocketProtocol::CloseCode code, const QString &errorString)
362 if ((m_error != code) || (m_errorString != errorString)) {
363 Q_Q(QWebSocketServer);
365 m_errorString = errorString;
366 Q_EMIT q->serverError(code);
373 void QWebSocketServerPrivate::onNewConnection()
375 QTcpSocket *pTcpSocket = m_pTcpServer->nextPendingConnection();
376 //use a queued connection because a QSslSocket
377 //needs the event loop to process incoming data
378 //if not queued, data is incomplete when handshakeReceived is called
379 QObjectPrivate::connect(pTcpSocket, &QTcpSocket::readyRead,
380 this, &QWebSocketServerPrivate::handshakeReceived,
381 Qt::QueuedConnection);
387 void QWebSocketServerPrivate::onCloseConnection()
389 if (Q_LIKELY(currentSender)) {
390 QTcpSocket *pTcpSocket = qobject_cast<QTcpSocket*>(currentSender->sender);
391 if (Q_LIKELY(pTcpSocket))
399 void QWebSocketServerPrivate::handshakeReceived()
401 if (Q_UNLIKELY(!currentSender)) {
404 QTcpSocket *pTcpSocket = qobject_cast<QTcpSocket*>(currentSender->sender);
405 if (Q_UNLIKELY(!pTcpSocket)) {
408 //When using Google Chrome the handshake in received in two parts.
409 //Therefore, the readyRead signal is emitted twice.
410 //This is a guard against the BEAST attack.
411 //See: https://www.imperialviolet.org/2012/01/15/beastfollowup.html
412 //For Safari, the handshake is delivered at once
413 //FIXME: For FireFox, the readyRead signal is never emitted
414 //This is a bug in FireFox (see https://bugzilla.mozilla.org/show_bug.cgi?id=594502)
415 if (!pTcpSocket->canReadLine()) {
418 disconnect(pTcpSocket, &QTcpSocket::readyRead,
419 this, &QWebSocketServerPrivate::handshakeReceived);
420 Q_Q(QWebSocketServer);
421 bool success = false;
422 bool isSecure = false;
424 if (m_pendingConnections.length() >= maxPendingConnections()) {
426 pTcpSocket->deleteLater();
427 setError(QWebSocketProtocol::CloseCodeAbnormalDisconnection,
428 QWebSocketServer::tr("Too many pending connections."));
432 QWebSocketHandshakeRequest request(pTcpSocket->peerPort(), isSecure);
433 QTextStream textStream(pTcpSocket);
434 request.readHandshake(textStream);
436 if (request.isValid()) {
437 QWebSocketCorsAuthenticator corsAuthenticator(request.origin());
438 Q_EMIT q->originAuthenticationRequired(&corsAuthenticator);
440 QWebSocketHandshakeResponse response(request,
442 corsAuthenticator.allowed(),
444 supportedProtocols(),
445 supportedExtensions());
447 if (response.isValid()) {
448 QTextStream httpStream(pTcpSocket);
449 httpStream << response;
452 if (response.canUpgrade()) {
453 QWebSocket *pWebSocket = QWebSocketPrivate::upgradeFrom(pTcpSocket,
457 addPendingConnection(pWebSocket);
458 Q_EMIT q->newConnection();
461 setError(QWebSocketProtocol::CloseCodeAbnormalDisconnection,
462 QWebSocketServer::tr("Upgrade to WebSocket failed."));
466 setError(response.error(), response.errorString());
469 setError(QWebSocketProtocol::CloseCodeProtocolError,
470 QWebSocketServer::tr("Invalid response received."));