f1c616dbd95b238bdf71efd5762232774d3e5318
[contrib/qtwebsockets.git] / src / websockets / qwebsocketserver_p.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtWebSockets module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
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.
16 **
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 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qwebsocketserver.h"
43 #include "qwebsocketserver_p.h"
44 #ifndef QT_NO_SSL
45 #include "qsslserver_p.h"
46 #endif
47 #include "qwebsocketprotocol.h"
48 #include "qwebsockethandshakerequest_p.h"
49 #include "qwebsockethandshakeresponse_p.h"
50 #include "qwebsocket.h"
51 #include "qwebsocket_p.h"
52 #include "qwebsocketcorsauthenticator.h"
53
54 #include <QtNetwork/QTcpServer>
55 #include <QtNetwork/QTcpSocket>
56 #include <QtNetwork/QNetworkProxy>
57
58 QT_BEGIN_NAMESPACE
59
60 /*!
61     \internal
62  */
63 QWebSocketServerPrivate::QWebSocketServerPrivate(const QString &serverName,
64                                                  QWebSocketServerPrivate::SslMode secureMode,
65                                                  QWebSocketServer * const pWebSocketServer) :
66     QObjectPrivate(),
67     q_ptr(pWebSocketServer),
68     m_pTcpServer(Q_NULLPTR),
69     m_serverName(serverName),
70     m_secureMode(secureMode),
71     m_pendingConnections(),
72     m_error(QWebSocketProtocol::CloseCodeNormal),
73     m_errorString()
74 {
75     Q_ASSERT(pWebSocketServer);
76 }
77
78 /*!
79     \internal
80  */
81 void QWebSocketServerPrivate::init()
82 {
83     if (m_secureMode == NonSecureMode) {
84         m_pTcpServer = new QTcpServer();
85         if (Q_LIKELY(m_pTcpServer))
86             QObjectPrivate::connect(m_pTcpServer, &QTcpServer::newConnection,
87                                     this, &QWebSocketServerPrivate::onNewConnection);
88         else
89             qFatal("Could not allocate memory for tcp server.");
90     } else {
91 #ifndef QT_NO_SSL
92         QSslServer *pSslServer = new QSslServer();
93         m_pTcpServer = pSslServer;
94         if (Q_LIKELY(m_pTcpServer)) {
95             QObjectPrivate::connect(pSslServer, &QSslServer::newEncryptedConnection,
96                                     this, &QWebSocketServerPrivate::onNewConnection);
97             QObject::connect(pSslServer, &QSslServer::peerVerifyError,
98                              q_ptr, &QWebSocketServer::peerVerifyError);
99             QObject::connect(pSslServer, &QSslServer::sslErrors,
100                              q_ptr, &QWebSocketServer::sslErrors);
101         }
102 #else
103         qFatal("SSL not supported on this platform.");
104 #endif
105     }
106     QObject::connect(m_pTcpServer, &QTcpServer::acceptError, q_ptr, &QWebSocketServer::acceptError);
107 }
108
109 /*!
110     \internal
111  */
112 QWebSocketServerPrivate::~QWebSocketServerPrivate()
113 {
114     close(true);
115     m_pTcpServer->deleteLater();
116 }
117
118 /*!
119     \internal
120  */
121 void QWebSocketServerPrivate::close(bool aboutToDestroy)
122 {
123     Q_Q(QWebSocketServer);
124     m_pTcpServer->close();
125     while (!m_pendingConnections.isEmpty()) {
126         QWebSocket *pWebSocket = m_pendingConnections.dequeue();
127         pWebSocket->close(QWebSocketProtocol::CloseCodeGoingAway,
128                           QWebSocketServer::tr("Server closed."));
129         pWebSocket->deleteLater();
130     }
131     if (!aboutToDestroy) {
132         //emit signal via the event queue, so the server gets time
133         //to process any hanging events, like flushing buffers aso
134         QMetaObject::invokeMethod(q, "closed", Qt::QueuedConnection);
135     }
136 }
137
138 /*!
139     \internal
140  */
141 QString QWebSocketServerPrivate::errorString() const
142 {
143     if (m_errorString.isEmpty())
144         return m_pTcpServer->errorString();
145     else
146         return m_errorString;
147 }
148
149 /*!
150     \internal
151  */
152 bool QWebSocketServerPrivate::hasPendingConnections() const
153 {
154     return !m_pendingConnections.isEmpty();
155 }
156
157 /*!
158     \internal
159  */
160 bool QWebSocketServerPrivate::isListening() const
161 {
162     return m_pTcpServer->isListening();
163 }
164
165 /*!
166     \internal
167  */
168 bool QWebSocketServerPrivate::listen(const QHostAddress &address, quint16 port)
169 {
170     bool success = m_pTcpServer->listen(address, port);
171     if (!success)
172         setErrorFromSocketError(m_pTcpServer->serverError(), m_pTcpServer->errorString());
173     return success;
174 }
175
176 /*!
177     \internal
178  */
179 int QWebSocketServerPrivate::maxPendingConnections() const
180 {
181     return m_pTcpServer->maxPendingConnections();
182 }
183
184 /*!
185     \internal
186  */
187 void QWebSocketServerPrivate::addPendingConnection(QWebSocket *pWebSocket)
188 {
189     if (m_pendingConnections.size() < maxPendingConnections())
190         m_pendingConnections.enqueue(pWebSocket);
191 }
192
193 /*!
194     \internal
195  */
196 void QWebSocketServerPrivate::setErrorFromSocketError(QAbstractSocket::SocketError error,
197                                                       const QString &errorDescription)
198 {
199     Q_UNUSED(error);
200     setError(QWebSocketProtocol::CloseCodeAbnormalDisconnection, errorDescription);
201 }
202
203 /*!
204     \internal
205  */
206 QWebSocket *QWebSocketServerPrivate::nextPendingConnection()
207 {
208     QWebSocket *pWebSocket = Q_NULLPTR;
209     if (Q_LIKELY(!m_pendingConnections.isEmpty()))
210         pWebSocket = m_pendingConnections.dequeue();
211     return pWebSocket;
212 }
213
214 /*!
215     \internal
216  */
217 void QWebSocketServerPrivate::pauseAccepting()
218 {
219     m_pTcpServer->pauseAccepting();
220 }
221
222 #ifndef QT_NO_NETWORKPROXY
223 /*!
224     \internal
225  */
226 QNetworkProxy QWebSocketServerPrivate::proxy() const
227 {
228     return m_pTcpServer->proxy();
229 }
230
231 /*!
232     \internal
233  */
234 void QWebSocketServerPrivate::setProxy(const QNetworkProxy &networkProxy)
235 {
236     m_pTcpServer->setProxy(networkProxy);
237 }
238 #endif
239 /*!
240     \internal
241  */
242 void QWebSocketServerPrivate::resumeAccepting()
243 {
244     m_pTcpServer->resumeAccepting();
245 }
246
247 /*!
248     \internal
249  */
250 QHostAddress QWebSocketServerPrivate::serverAddress() const
251 {
252     return m_pTcpServer->serverAddress();
253 }
254
255 /*!
256     \internal
257  */
258 QWebSocketProtocol::CloseCode QWebSocketServerPrivate::serverError() const
259 {
260     return m_error;
261 }
262
263 /*!
264     \internal
265  */
266 quint16 QWebSocketServerPrivate::serverPort() const
267 {
268     return m_pTcpServer->serverPort();
269 }
270
271 /*!
272     \internal
273  */
274 void QWebSocketServerPrivate::setMaxPendingConnections(int numConnections)
275 {
276     m_pTcpServer->setMaxPendingConnections(numConnections);
277 }
278
279 /*!
280     \internal
281  */
282 bool QWebSocketServerPrivate::setSocketDescriptor(qintptr socketDescriptor)
283 {
284     return m_pTcpServer->setSocketDescriptor(socketDescriptor);
285 }
286
287 /*!
288     \internal
289  */
290 qintptr QWebSocketServerPrivate::socketDescriptor() const
291 {
292     return m_pTcpServer->socketDescriptor();
293 }
294
295 /*!
296     \internal
297  */
298 QList<QWebSocketProtocol::Version> QWebSocketServerPrivate::supportedVersions() const
299 {
300     QList<QWebSocketProtocol::Version> supportedVersions;
301     supportedVersions << QWebSocketProtocol::currentVersion();  //we only support V13
302     return supportedVersions;
303 }
304
305 /*!
306     \internal
307  */
308 QStringList QWebSocketServerPrivate::supportedProtocols() const
309 {
310     QStringList supportedProtocols;
311     return supportedProtocols;  //no protocols are currently supported
312 }
313
314 /*!
315     \internal
316  */
317 QStringList QWebSocketServerPrivate::supportedExtensions() const
318 {
319     QStringList supportedExtensions;
320     return supportedExtensions; //no extensions are currently supported
321 }
322
323 /*!
324   \internal
325  */
326 void QWebSocketServerPrivate::setServerName(const QString &serverName)
327 {
328     if (m_serverName != serverName)
329         m_serverName = serverName;
330 }
331
332 /*!
333   \internal
334  */
335 QString QWebSocketServerPrivate::serverName() const
336 {
337     return m_serverName;
338 }
339
340 /*!
341   \internal
342  */
343 QWebSocketServerPrivate::SslMode QWebSocketServerPrivate::secureMode() const
344 {
345     return m_secureMode;
346 }
347
348 #ifndef QT_NO_SSL
349 void QWebSocketServerPrivate::setSslConfiguration(const QSslConfiguration &sslConfiguration)
350 {
351     if (m_secureMode == SecureMode)
352         qobject_cast<QSslServer *>(m_pTcpServer)->setSslConfiguration(sslConfiguration);
353 }
354
355 QSslConfiguration QWebSocketServerPrivate::sslConfiguration() const
356 {
357     if (m_secureMode == SecureMode)
358         return qobject_cast<QSslServer *>(m_pTcpServer)->sslConfiguration();
359     else
360         return QSslConfiguration::defaultConfiguration();
361 }
362 #endif
363
364 void QWebSocketServerPrivate::setError(QWebSocketProtocol::CloseCode code, const QString &errorString)
365 {
366     if ((m_error != code) || (m_errorString != errorString)) {
367         Q_Q(QWebSocketServer);
368         m_error = code;
369         m_errorString = errorString;
370         Q_EMIT q->serverError(code);
371     }
372 }
373
374 /*!
375     \internal
376  */
377 void QWebSocketServerPrivate::onNewConnection()
378 {
379     QTcpSocket *pTcpSocket = m_pTcpServer->nextPendingConnection();
380     QObjectPrivate::connect(pTcpSocket, &QTcpSocket::readyRead,
381                             this, &QWebSocketServerPrivate::handshakeReceived);
382 }
383
384 /*!
385     \internal
386  */
387 void QWebSocketServerPrivate::onCloseConnection()
388 {
389     if (Q_LIKELY(currentSender)) {
390         QTcpSocket *pTcpSocket = qobject_cast<QTcpSocket*>(currentSender->sender);
391         if (Q_LIKELY(pTcpSocket))
392             pTcpSocket->close();
393     }
394 }
395
396 /*!
397     \internal
398  */
399 void QWebSocketServerPrivate::handshakeReceived()
400 {
401     if (Q_UNLIKELY(!currentSender)) {
402         qWarning() << QWebSocketServer::tr("Sender is NULL. This is a Qt bug.");
403         return;
404     }
405     QTcpSocket *pTcpSocket = qobject_cast<QTcpSocket*>(currentSender->sender);
406     if (Q_UNLIKELY(!pTcpSocket)) {
407         qWarning() << QWebSocketServer::tr("Sender is not a QTcpSocket. This is a Qt bug!!!");
408         return;
409     }
410     Q_Q(QWebSocketServer);
411     bool success = false;
412     bool isSecure = false;
413
414     disconnect(pTcpSocket, &QTcpSocket::readyRead,
415                this, &QWebSocketServerPrivate::handshakeReceived);
416
417     if (m_pendingConnections.length() >= maxPendingConnections()) {
418         pTcpSocket->close();
419         pTcpSocket->deleteLater();
420         qWarning() << QWebSocketServer::tr("Too many pending connections: " \
421                                            "New websocket connection not accepted.");
422         setError(QWebSocketProtocol::CloseCodeAbnormalDisconnection,
423                  QWebSocketServer::tr("Too many pending connections."));
424         return;
425     }
426
427     QWebSocketHandshakeRequest request(pTcpSocket->peerPort(), isSecure);
428     QTextStream textStream(pTcpSocket);
429     request.readHandshake(textStream);
430
431     if (request.isValid()) {
432         QWebSocketCorsAuthenticator corsAuthenticator(request.origin());
433         Q_EMIT q->originAuthenticationRequired(&corsAuthenticator);
434
435         QWebSocketHandshakeResponse response(request,
436                                              m_serverName,
437                                              corsAuthenticator.allowed(),
438                                              supportedVersions(),
439                                              supportedProtocols(),
440                                              supportedExtensions());
441
442         if (response.isValid()) {
443             QTextStream httpStream(pTcpSocket);
444             httpStream << response;
445             httpStream.flush();
446
447             if (response.canUpgrade()) {
448                 QWebSocket *pWebSocket = QWebSocketPrivate::upgradeFrom(pTcpSocket,
449                                                                         request,
450                                                                         response);
451                 if (pWebSocket) {
452                     addPendingConnection(pWebSocket);
453                     Q_EMIT q->newConnection();
454                     success = true;
455                 } else {
456                     setError(QWebSocketProtocol::CloseCodeAbnormalDisconnection,
457                              QWebSocketServer::tr("Upgrading to websocket failed."));
458                 }
459             }
460             else {
461                 setError(response.error(), response.errorString());
462             }
463         } else {
464             setError(QWebSocketProtocol::CloseCodeProtocolError,
465                      QWebSocketServer::tr("Invalid response received."));
466         }
467     }
468     if (!success) {
469         qWarning() << QWebSocketServer::tr("Closing socket because of invalid or unsupported request.");
470         pTcpSocket->close();
471     }
472 }
473
474 QT_END_NAMESPACE