Connect sslErrors signal when a secure connection is established
[contrib/qtwebsockets.git] / src / websockets / qwebsocket_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 "qwebsocket.h"
43 #include "qwebsocket_p.h"
44 #include "qwebsocketprotocol_p.h"
45 #include "qwebsockethandshakerequest_p.h"
46 #include "qwebsockethandshakeresponse_p.h"
47
48 #include <QtCore/QUrl>
49 #include <QtNetwork/QAuthenticator>
50 #include <QtNetwork/QTcpSocket>
51 #include <QtCore/QByteArray>
52 #include <QtCore/QtEndian>
53 #include <QtCore/QCryptographicHash>
54 #include <QtCore/QRegularExpression>
55 #include <QtCore/QStringList>
56 #include <QtNetwork/QHostAddress>
57 #include <QtCore/QStringBuilder>   //for more efficient string concatenation
58 #ifndef QT_NONETWORKPROXY
59 #include <QtNetwork/QNetworkProxy>
60 #endif
61 #ifndef QT_NO_SSL
62 #include <QtNetwork/QSslConfiguration>
63 #include <QtNetwork/QSslError>
64 #endif
65
66 #include <QtCore/QDebug>
67
68 #include <limits>
69
70 QT_BEGIN_NAMESPACE
71
72 const quint64 FRAME_SIZE_IN_BYTES = 512 * 512 * 2;      //maximum size of a frame when sending a message
73
74 QWebSocketConfiguration::QWebSocketConfiguration() :
75 #ifndef QT_NO_SSL
76     m_sslConfiguration(QSslConfiguration::defaultConfiguration()),
77     m_ignoredSslErrors(),
78     m_ignoreSslErrors(false),
79 #endif
80 #ifndef QT_NONETWORKPROXY
81     m_proxy(QNetworkProxy::DefaultProxy)
82 #endif
83 {
84 }
85
86 /*!
87     \internal
88 */
89 QWebSocketPrivate::QWebSocketPrivate(const QString &origin, QWebSocketProtocol::Version version,
90                                      QWebSocket *pWebSocket) :
91     QObjectPrivate(),
92     q_ptr(pWebSocket),
93     m_pSocket(),
94     m_errorString(QWebSocket::tr("Unknown error")),
95     m_version(version),
96     m_resourceName(),
97     m_requestUrl(),
98     m_origin(origin),
99     m_protocol(),
100     m_extension(),
101     m_socketState(QAbstractSocket::UnconnectedState),
102     m_pauseMode(QAbstractSocket::PauseNever),
103     m_readBufferSize(0),
104     m_key(),
105     m_mustMask(true),
106     m_isClosingHandshakeSent(false),
107     m_isClosingHandshakeReceived(false),
108     m_closeCode(QWebSocketProtocol::CloseCodeNormal),
109     m_closeReason(),
110     m_pingTimer(),
111     m_dataProcessor(),
112     m_configuration()
113 {
114 }
115
116 /*!
117     \internal
118 */
119 QWebSocketPrivate::QWebSocketPrivate(QTcpSocket *pTcpSocket, QWebSocketProtocol::Version version,
120                                      QWebSocket *pWebSocket) :
121     QObjectPrivate(),
122     q_ptr(pWebSocket),
123     m_pSocket(pTcpSocket),
124     m_errorString(pTcpSocket->errorString()),
125     m_version(version),
126     m_resourceName(),
127     m_requestUrl(),
128     m_origin(),
129     m_protocol(),
130     m_extension(),
131     m_socketState(pTcpSocket->state()),
132     m_pauseMode(pTcpSocket->pauseMode()),
133     m_readBufferSize(pTcpSocket->readBufferSize()),
134     m_key(),
135     m_mustMask(true),
136     m_isClosingHandshakeSent(false),
137     m_isClosingHandshakeReceived(false),
138     m_closeCode(QWebSocketProtocol::CloseCodeNormal),
139     m_closeReason(),
140     m_pingTimer(),
141     m_dataProcessor(),
142     m_configuration()
143 {
144 }
145
146 /*!
147     \internal
148 */
149 void QWebSocketPrivate::init()
150 {
151     Q_ASSERT(q_ptr);
152     //TODO: need a better randomizer
153     qsrand(static_cast<uint>(QDateTime::currentMSecsSinceEpoch()));
154
155     if (m_pSocket) {
156         makeConnections(m_pSocket.data());
157     }
158 }
159
160 /*!
161     \internal
162 */
163 QWebSocketPrivate::~QWebSocketPrivate()
164 {
165     if (!m_pSocket)
166         return;
167     if (state() == QAbstractSocket::ConnectedState)
168         close(QWebSocketProtocol::CloseCodeGoingAway, QWebSocket::tr("Connection closed"));
169     releaseConnections(m_pSocket.data());
170 }
171
172 /*!
173     \internal
174  */
175 void QWebSocketPrivate::abort()
176 {
177     if (m_pSocket)
178         m_pSocket->abort();
179 }
180
181 /*!
182     \internal
183  */
184 QAbstractSocket::SocketError QWebSocketPrivate::error() const
185 {
186     QAbstractSocket::SocketError err = QAbstractSocket::UnknownSocketError;
187     if (Q_LIKELY(m_pSocket))
188         err = m_pSocket->error();
189     return err;
190 }
191
192 /*!
193     \internal
194  */
195 QString QWebSocketPrivate::errorString() const
196 {
197     QString errMsg;
198     if (!m_errorString.isEmpty())
199         errMsg = m_errorString;
200     else if (m_pSocket)
201         errMsg = m_pSocket->errorString();
202     return errMsg;
203 }
204
205 /*!
206     \internal
207  */
208 bool QWebSocketPrivate::flush()
209 {
210     bool result = true;
211     if (Q_LIKELY(m_pSocket))
212         result = m_pSocket->flush();
213     return result;
214 }
215
216 /*!
217     \internal
218  */
219 qint64 QWebSocketPrivate::sendTextMessage(const QString &message)
220 {
221     return doWriteFrames(message.toUtf8(), false);
222 }
223
224 /*!
225     \internal
226  */
227 qint64 QWebSocketPrivate::sendBinaryMessage(const QByteArray &data)
228 {
229     return doWriteFrames(data, true);
230 }
231
232 #ifndef QT_NO_SSL
233 /*!
234     \internal
235  */
236 void QWebSocketPrivate::setSslConfiguration(const QSslConfiguration &sslConfiguration)
237 {
238     m_configuration.m_sslConfiguration = sslConfiguration;
239 }
240
241 /*!
242     \internal
243  */
244 QSslConfiguration QWebSocketPrivate::sslConfiguration() const
245 {
246     return m_configuration.m_sslConfiguration;
247 }
248
249 /*!
250     \internal
251  */
252 void QWebSocketPrivate::ignoreSslErrors(const QList<QSslError> &errors)
253 {
254     m_configuration.m_ignoredSslErrors = errors;
255 }
256
257 /*!
258  * \internal
259  */
260 void QWebSocketPrivate::ignoreSslErrors()
261 {
262     m_configuration.m_ignoreSslErrors = true;
263     if (Q_LIKELY(m_pSocket)) {
264         QSslSocket *pSslSocket = qobject_cast<QSslSocket *>(m_pSocket.data());
265         if (Q_LIKELY(pSslSocket))
266             pSslSocket->ignoreSslErrors();
267     }
268 }
269
270 #endif
271
272 /*!
273   Called from QWebSocketServer
274   \internal
275  */
276 QWebSocket *QWebSocketPrivate::upgradeFrom(QTcpSocket *pTcpSocket,
277                                            const QWebSocketHandshakeRequest &request,
278                                            const QWebSocketHandshakeResponse &response,
279                                            QObject *parent)
280 {
281     QWebSocket *pWebSocket = new QWebSocket(pTcpSocket, response.acceptedVersion(), parent);
282     if (Q_LIKELY(pWebSocket)) {
283         pWebSocket->d_func()->setExtension(response.acceptedExtension());
284         pWebSocket->d_func()->setOrigin(request.origin());
285         pWebSocket->d_func()->setRequestUrl(request.requestUrl());
286         pWebSocket->d_func()->setProtocol(response.acceptedProtocol());
287         pWebSocket->d_func()->setResourceName(request.requestUrl().toString(QUrl::RemoveUserInfo));
288         //a server should not send masked frames
289         pWebSocket->d_func()->enableMasking(false);
290     }
291
292     return pWebSocket;
293 }
294
295 /*!
296     \internal
297  */
298 void QWebSocketPrivate::close(QWebSocketProtocol::CloseCode closeCode, QString reason)
299 {
300     if (Q_UNLIKELY(!m_pSocket))
301         return;
302     if (!m_isClosingHandshakeSent) {
303         Q_Q(QWebSocket);
304         const quint16 code = qToBigEndian<quint16>(closeCode);
305         QByteArray payload;
306         payload.append(static_cast<const char *>(static_cast<const void *>(&code)), 2);
307         if (!reason.isEmpty())
308             payload.append(reason.toUtf8());
309         quint32 maskingKey = 0;
310         if (m_mustMask) {
311             maskingKey = generateMaskingKey();
312             QWebSocketProtocol::mask(payload.data(), payload.size(), maskingKey);
313         }
314         QByteArray frame = getFrameHeader(QWebSocketProtocol::OpCodeClose,
315                                           payload.size(), maskingKey, true);
316         frame.append(payload);
317         m_pSocket->write(frame);
318         m_pSocket->flush();
319
320         m_isClosingHandshakeSent = true;
321
322         Q_EMIT q->aboutToClose();
323     }
324     m_pSocket->close();
325 }
326
327 /*!
328     \internal
329  */
330 void QWebSocketPrivate::open(const QUrl &url, bool mask)
331 {
332     //just delete the old socket for the moment;
333     //later, we can add more 'intelligent' handling by looking at the url
334     //m_pSocket.reset();
335     QTcpSocket *pTcpSocket = m_pSocket.take();
336     if (pTcpSocket) {
337         releaseConnections(pTcpSocket);
338         pTcpSocket->deleteLater();
339     }
340     //if (m_url != url)
341     if (Q_LIKELY(!m_pSocket)) {
342         Q_Q(QWebSocket);
343
344         m_dataProcessor.clear();
345         m_isClosingHandshakeReceived = false;
346         m_isClosingHandshakeSent = false;
347
348         setRequestUrl(url);
349         QString resourceName = url.path();
350         if (!url.query().isEmpty()) {
351             if (!resourceName.endsWith(QChar::fromLatin1('?'))) {
352                 resourceName.append(QChar::fromLatin1('?'));
353             }
354             resourceName.append(url.query());
355         }
356         if (resourceName.isEmpty())
357             resourceName = QStringLiteral("/");
358         setResourceName(resourceName);
359         enableMasking(mask);
360
361     #ifndef QT_NO_SSL
362         if (url.scheme() == QStringLiteral("wss")) {
363             if (!QSslSocket::supportsSsl()) {
364                 const QString message =
365                         QWebSocket::tr("SSL Sockets are not supported on this platform.");
366                 setErrorString(message);
367                 Q_EMIT q->error(QAbstractSocket::UnsupportedSocketOperationError);
368             } else {
369                 QSslSocket *sslSocket = new QSslSocket;
370                 m_pSocket.reset(sslSocket);
371                 if (Q_LIKELY(m_pSocket)) {
372                     m_pSocket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
373                     m_pSocket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
374                     m_pSocket->setReadBufferSize(m_readBufferSize);
375                     m_pSocket->setPauseMode(m_pauseMode);
376
377                     makeConnections(m_pSocket.data());
378                     QObject::connect(sslSocket, &QSslSocket::encryptedBytesWritten, q,
379                                      &QWebSocket::bytesWritten);
380                     typedef void (QSslSocket:: *sslErrorSignalType)(const QList<QSslError> &);
381                     QObject::connect(sslSocket,
382                                      static_cast<sslErrorSignalType>(&QSslSocket::sslErrors),
383                                      q, &QWebSocket::sslErrors);
384                     setSocketState(QAbstractSocket::ConnectingState);
385
386                     sslSocket->setSslConfiguration(m_configuration.m_sslConfiguration);
387                     if (Q_UNLIKELY(m_configuration.m_ignoreSslErrors))
388                         sslSocket->ignoreSslErrors();
389                     else
390                         sslSocket->ignoreSslErrors(m_configuration.m_ignoredSslErrors);
391     #ifndef QT_NO_NETWORKPROXY
392                     sslSocket->setProxy(m_configuration.m_proxy);
393     #endif
394                     sslSocket->connectToHostEncrypted(url.host(), url.port(443));
395                 } else {
396                     const QString message = QWebSocket::tr("Out of memory.");
397                     setErrorString(message);
398                     Q_EMIT q->error(QAbstractSocket::SocketResourceError);
399                 }
400             }
401         } else
402     #endif
403         if (url.scheme() == QStringLiteral("ws")) {
404             m_pSocket.reset(new QTcpSocket);
405             if (Q_LIKELY(m_pSocket)) {
406                 m_pSocket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
407                 m_pSocket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
408                 m_pSocket->setReadBufferSize(m_readBufferSize);
409                 m_pSocket->setPauseMode(m_pauseMode);
410
411                 makeConnections(m_pSocket.data());
412                 QObject::connect(m_pSocket.data(), &QAbstractSocket::bytesWritten, q,
413                                  &QWebSocket::bytesWritten);
414                 setSocketState(QAbstractSocket::ConnectingState);
415     #ifndef QT_NO_NETWORKPROXY
416                 m_pSocket->setProxy(m_configuration.m_proxy);
417     #endif
418                 m_pSocket->connectToHost(url.host(), url.port(80));
419             } else {
420                 const QString message = QWebSocket::tr("Out of memory.");
421                 setErrorString(message);
422                 Q_EMIT q->error(QAbstractSocket::SocketResourceError);
423             }
424         } else {
425             const QString message =
426                     QWebSocket::tr("Unsupported websockets scheme: %1").arg(url.scheme());
427             setErrorString(message);
428             Q_EMIT q->error(QAbstractSocket::UnsupportedSocketOperationError);
429         }
430     }
431 }
432
433 /*!
434     \internal
435  */
436 void QWebSocketPrivate::ping(const QByteArray &payload)
437 {
438     QByteArray payloadTruncated = payload.left(125);
439     m_pingTimer.restart();
440     QByteArray pingFrame = getFrameHeader(QWebSocketProtocol::OpCodePing, payloadTruncated.size(),
441                                           0 /*do not mask*/, true);
442     pingFrame.append(payloadTruncated);
443     qint64 ret = writeFrame(pingFrame);
444     Q_UNUSED(ret);
445 }
446
447 /*!
448   \internal
449     Sets the version to use for the websocket protocol;
450     this must be set before the socket is opened.
451 */
452 void QWebSocketPrivate::setVersion(QWebSocketProtocol::Version version)
453 {
454     if (m_version != version)
455         m_version = version;
456 }
457
458 /*!
459     \internal
460     Sets the resource name of the connection; must be set before the socket is openend
461 */
462 void QWebSocketPrivate::setResourceName(const QString &resourceName)
463 {
464     if (m_resourceName != resourceName)
465         m_resourceName = resourceName;
466 }
467
468 /*!
469   \internal
470  */
471 void QWebSocketPrivate::setRequestUrl(const QUrl &requestUrl)
472 {
473     if (m_requestUrl != requestUrl)
474         m_requestUrl = requestUrl;
475 }
476
477 /*!
478   \internal
479  */
480 void QWebSocketPrivate::setOrigin(const QString &origin)
481 {
482     if (m_origin != origin)
483         m_origin = origin;
484 }
485
486 /*!
487   \internal
488  */
489 void QWebSocketPrivate::setProtocol(const QString &protocol)
490 {
491     if (m_protocol != protocol)
492         m_protocol = protocol;
493 }
494
495 /*!
496   \internal
497  */
498 void QWebSocketPrivate::setExtension(const QString &extension)
499 {
500     if (m_extension != extension)
501         m_extension = extension;
502 }
503
504 /*!
505   \internal
506  */
507 void QWebSocketPrivate::enableMasking(bool enable)
508 {
509     if (m_mustMask != enable)
510         m_mustMask = enable;
511 }
512
513 /*!
514  * \internal
515  */
516 void QWebSocketPrivate::makeConnections(const QTcpSocket *pTcpSocket)
517 {
518     Q_ASSERT(pTcpSocket);
519     Q_Q(QWebSocket);
520
521     if (Q_LIKELY(pTcpSocket)) {
522         //pass through signals
523         typedef void (QAbstractSocket:: *ASErrorSignal)(QAbstractSocket::SocketError);
524         typedef void (QWebSocket:: *WSErrorSignal)(QAbstractSocket::SocketError);
525         QObject::connect(pTcpSocket,
526                          static_cast<ASErrorSignal>(&QAbstractSocket::error),
527                          q, static_cast<WSErrorSignal>(&QWebSocket::error));
528         QObject::connect(pTcpSocket, &QAbstractSocket::proxyAuthenticationRequired, q,
529                          &QWebSocket::proxyAuthenticationRequired);
530         QObject::connect(pTcpSocket, &QAbstractSocket::readChannelFinished, q,
531                          &QWebSocket::readChannelFinished);
532         QObject::connect(pTcpSocket, &QAbstractSocket::aboutToClose, q, &QWebSocket::aboutToClose);
533
534         //catch signals
535         QObjectPrivate::connect(pTcpSocket, &QAbstractSocket::stateChanged, this,
536                                 &QWebSocketPrivate::processStateChanged);
537         //!!!important to use a QueuedConnection here;
538         //with QTcpSocket there is no problem, but with QSslSocket the processing hangs
539         QObjectPrivate::connect(pTcpSocket, &QAbstractSocket::readyRead, this,
540                                 &QWebSocketPrivate::processData, Qt::QueuedConnection);
541     }
542
543     QObject::connect(&m_dataProcessor, &QWebSocketDataProcessor::textFrameReceived, q,
544                      &QWebSocket::textFrameReceived);
545     QObject::connect(&m_dataProcessor, &QWebSocketDataProcessor::binaryFrameReceived, q,
546                      &QWebSocket::binaryFrameReceived);
547     QObject::connect(&m_dataProcessor, &QWebSocketDataProcessor::binaryMessageReceived, q,
548                      &QWebSocket::binaryMessageReceived);
549     QObject::connect(&m_dataProcessor, &QWebSocketDataProcessor::textMessageReceived, q,
550                      &QWebSocket::textMessageReceived);
551     QObjectPrivate::connect(&m_dataProcessor, &QWebSocketDataProcessor::errorEncountered, this,
552                             &QWebSocketPrivate::close);
553     QObjectPrivate::connect(&m_dataProcessor, &QWebSocketDataProcessor::pingReceived, this,
554                             &QWebSocketPrivate::processPing);
555     QObjectPrivate::connect(&m_dataProcessor, &QWebSocketDataProcessor::pongReceived, this,
556                             &QWebSocketPrivate::processPong);
557     QObjectPrivate::connect(&m_dataProcessor, &QWebSocketDataProcessor::closeReceived, this,
558                             &QWebSocketPrivate::processClose);
559 }
560
561 /*!
562  * \internal
563  */
564 void QWebSocketPrivate::releaseConnections(const QTcpSocket *pTcpSocket)
565 {
566     if (Q_LIKELY(pTcpSocket))
567         pTcpSocket->disconnect(pTcpSocket);
568     m_dataProcessor.disconnect(&m_dataProcessor);
569 }
570
571 /*!
572     \internal
573  */
574 QWebSocketProtocol::Version QWebSocketPrivate::version() const
575 {
576     return m_version;
577 }
578
579 /*!
580     \internal
581  */
582 QString QWebSocketPrivate::resourceName() const
583 {
584     return m_resourceName;
585 }
586
587 /*!
588     \internal
589  */
590 QUrl QWebSocketPrivate::requestUrl() const
591 {
592     return m_requestUrl;
593 }
594
595 /*!
596     \internal
597  */
598 QString QWebSocketPrivate::origin() const
599 {
600     return m_origin;
601 }
602
603 /*!
604     \internal
605  */
606 QString QWebSocketPrivate::protocol() const
607 {
608     return m_protocol;
609 }
610
611 /*!
612     \internal
613  */
614 QString QWebSocketPrivate::extension() const
615 {
616     return m_extension;
617 }
618
619 /*!
620  * \internal
621  */
622 QWebSocketProtocol::CloseCode QWebSocketPrivate::closeCode() const
623 {
624     return m_closeCode;
625 }
626
627 /*!
628  * \internal
629  */
630 QString QWebSocketPrivate::closeReason() const
631 {
632     return m_closeReason;
633 }
634
635 /*!
636  * \internal
637  */
638 QByteArray QWebSocketPrivate::getFrameHeader(QWebSocketProtocol::OpCode opCode,
639                                              quint64 payloadLength, quint32 maskingKey,
640                                              bool lastFrame)
641 {
642     QByteArray header;
643     quint8 byte = 0x00;
644     bool ok = payloadLength <= 0x7FFFFFFFFFFFFFFFULL;
645
646     if (Q_LIKELY(ok)) {
647         //FIN, RSV1-3, opcode (RSV-1, RSV-2 and RSV-3 are zero)
648         byte = static_cast<quint8>((opCode & 0x0F) | (lastFrame ? 0x80 : 0x00));
649         header.append(static_cast<char>(byte));
650
651         byte = 0x00;
652         if (maskingKey != 0)
653             byte |= 0x80;
654         if (payloadLength <= 125) {
655             byte |= static_cast<quint8>(payloadLength);
656             header.append(static_cast<char>(byte));
657         } else if (payloadLength <= 0xFFFFU) {
658             byte |= 126;
659             header.append(static_cast<char>(byte));
660             quint16 swapped = qToBigEndian<quint16>(static_cast<quint16>(payloadLength));
661             header.append(static_cast<const char *>(static_cast<const void *>(&swapped)), 2);
662         } else if (payloadLength <= 0x7FFFFFFFFFFFFFFFULL) {
663             byte |= 127;
664             header.append(static_cast<char>(byte));
665             quint64 swapped = qToBigEndian<quint64>(payloadLength);
666             header.append(static_cast<const char *>(static_cast<const void *>(&swapped)), 8);
667         }
668
669         if (maskingKey != 0) {
670             const quint32 mask = qToBigEndian<quint32>(maskingKey);
671             header.append(static_cast<const char *>(static_cast<const void *>(&mask)),
672                           sizeof(quint32));
673         }
674     } else {
675         setErrorString(QStringLiteral("WebSocket::getHeader: payload too big!"));
676         Q_EMIT q_ptr->error(QAbstractSocket::DatagramTooLargeError);
677     }
678
679     return header;
680 }
681
682 /*!
683  * \internal
684  */
685 qint64 QWebSocketPrivate::doWriteFrames(const QByteArray &data, bool isBinary)
686 {
687     qint64 payloadWritten = 0;
688     if (Q_UNLIKELY(!m_pSocket) || (state() != QAbstractSocket::ConnectedState))
689         return payloadWritten;
690
691     Q_Q(QWebSocket);
692     const QWebSocketProtocol::OpCode firstOpCode = isBinary ?
693                 QWebSocketProtocol::OpCodeBinary : QWebSocketProtocol::OpCodeText;
694
695     int numFrames = data.size() / FRAME_SIZE_IN_BYTES;
696     QByteArray tmpData(data);
697     tmpData.detach();
698     char *payload = tmpData.data();
699     quint64 sizeLeft = quint64(data.size()) % FRAME_SIZE_IN_BYTES;
700     if (Q_LIKELY(sizeLeft))
701         ++numFrames;
702
703     //catch the case where the payload is zero bytes;
704     //in this case, we still need to send a frame
705     if (Q_UNLIKELY(numFrames == 0))
706         numFrames = 1;
707     quint64 currentPosition = 0;
708     qint64 bytesWritten = 0;
709     quint64 bytesLeft = data.size();
710
711     for (int i = 0; i < numFrames; ++i) {
712         quint32 maskingKey = 0;
713         if (m_mustMask)
714             maskingKey = generateMaskingKey();
715
716         const bool isLastFrame = (i == (numFrames - 1));
717         const bool isFirstFrame = (i == 0);
718
719         const quint64 size = qMin(bytesLeft, FRAME_SIZE_IN_BYTES);
720         const QWebSocketProtocol::OpCode opcode = isFirstFrame ? firstOpCode
721                                                                : QWebSocketProtocol::OpCodeContinue;
722
723         //write header
724         bytesWritten += m_pSocket->write(getFrameHeader(opcode, size, maskingKey, isLastFrame));
725
726         //write payload
727         if (Q_LIKELY(size > 0)) {
728             char *currentData = payload + currentPosition;
729             if (m_mustMask)
730                 QWebSocketProtocol::mask(currentData, size, maskingKey);
731             qint64 written = m_pSocket->write(currentData, static_cast<qint64>(size));
732             if (Q_LIKELY(written > 0)) {
733                 bytesWritten += written;
734                 payloadWritten += written;
735             } else {
736                 m_pSocket->flush();
737                 setErrorString(QWebSocket::tr("Error writing bytes to socket: %1.")
738                                .arg(m_pSocket->errorString()));
739                 Q_EMIT q->error(QAbstractSocket::NetworkError);
740                 break;
741             }
742         }
743         currentPosition += size;
744         bytesLeft -= size;
745     }
746     if (Q_UNLIKELY(payloadWritten != data.size())) {
747         setErrorString(QWebSocket::tr("Bytes written %1 != %2.")
748                        .arg(payloadWritten).arg(data.size()));
749         Q_EMIT q->error(QAbstractSocket::NetworkError);
750     }
751     return payloadWritten;
752 }
753
754 /*!
755  * \internal
756  */
757 quint32 QWebSocketPrivate::generateRandomNumber() const
758 {
759     //TODO: need a better randomizer
760     return quint32((double(qrand()) / RAND_MAX) * std::numeric_limits<quint32>::max());
761 }
762
763 /*!
764     \internal
765  */
766 quint32 QWebSocketPrivate::generateMaskingKey() const
767 {
768     return generateRandomNumber();
769 }
770
771 /*!
772     \internal
773  */
774 QByteArray QWebSocketPrivate::generateKey() const
775 {
776     QByteArray key;
777
778     for (int i = 0; i < 4; ++i) {
779         const quint32 tmp = generateRandomNumber();
780         key.append(static_cast<const char *>(static_cast<const void *>(&tmp)), sizeof(quint32));
781     }
782
783     return key.toBase64();
784 }
785
786
787 /*!
788     \internal
789  */
790 QString QWebSocketPrivate::calculateAcceptKey(const QByteArray &key) const
791 {
792     const QByteArray tmpKey = key + QByteArrayLiteral("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");
793     const QByteArray hash = QCryptographicHash::hash(tmpKey, QCryptographicHash::Sha1).toBase64();
794     return QString::fromLatin1(hash);
795 }
796
797 /*!
798     \internal
799  */
800 qint64 QWebSocketPrivate::writeFrames(const QList<QByteArray> &frames)
801 {
802     qint64 written = 0;
803     if (Q_LIKELY(m_pSocket)) {
804         QList<QByteArray>::const_iterator it;
805         for (it = frames.cbegin(); it < frames.cend(); ++it)
806             written += writeFrame(*it);
807     }
808     return written;
809 }
810
811 /*!
812     \internal
813  */
814 qint64 QWebSocketPrivate::writeFrame(const QByteArray &frame)
815 {
816     qint64 written = 0;
817     if (Q_LIKELY(m_pSocket))
818         written = m_pSocket->write(frame);
819     return written;
820 }
821
822 /*!
823     \internal
824  */
825 QString readLine(QTcpSocket *pSocket)
826 {
827     Q_ASSERT(pSocket);
828     QString line;
829     char c;
830     while (pSocket->getChar(&c)) {
831         if (c == char('\r')) {
832             pSocket->getChar(&c);
833             break;
834         } else {
835             line.append(QChar::fromLatin1(c));
836         }
837     }
838     return line;
839 }
840
841 //called on the client for a server handshake response
842 /*!
843     \internal
844  */
845 void QWebSocketPrivate::processHandshake(QTcpSocket *pSocket)
846 {
847     Q_Q(QWebSocket);
848     if (Q_UNLIKELY(!pSocket))
849         return;
850
851     bool ok = false;
852     QString errorDescription;
853
854     const QString regExpStatusLine(QStringLiteral("^(HTTP/[0-9]+\\.[0-9]+)\\s([0-9]+)\\s(.*)"));
855     const QRegularExpression regExp(regExpStatusLine);
856     const QString statusLine = readLine(pSocket);
857     QString httpProtocol;
858     int httpStatusCode;
859     QString httpStatusMessage;
860     const QRegularExpressionMatch match = regExp.match(statusLine);
861     if (Q_LIKELY(match.hasMatch())) {
862         QStringList tokens = match.capturedTexts();
863         tokens.removeFirst();   //remove the search string
864         if (tokens.length() == 3) {
865             httpProtocol = tokens[0];
866             httpStatusCode = tokens[1].toInt();
867             httpStatusMessage = tokens[2].trimmed();
868             ok = true;
869         }
870     }
871     if (Q_UNLIKELY(!ok)) {
872         errorDescription = QWebSocket::tr("Invalid statusline in response: %1.").arg(statusLine);
873     } else {
874         QString headerLine = readLine(pSocket);
875         QMap<QString, QString> headers;
876         while (!headerLine.isEmpty()) {
877             const QStringList headerField = headerLine.split(QStringLiteral(": "),
878                                                              QString::SkipEmptyParts);
879             if (headerField.size() == 2) {
880                 headers.insertMulti(headerField[0], headerField[1]);
881             }
882             headerLine = readLine(pSocket);
883         }
884
885         const QString acceptKey = headers.value(QStringLiteral("Sec-WebSocket-Accept"),
886                                                 QString());
887         const QString upgrade = headers.value(QStringLiteral("Upgrade"), QString());
888         const QString connection = headers.value(QStringLiteral("Connection"), QString());
889 //        unused for the moment
890 //        const QString extensions = headers.value(QStringLiteral("Sec-WebSocket-Extensions"),
891 //                                                 QString());
892 //        const QString protocol = headers.value(QStringLiteral("Sec-WebSocket-Protocol"),
893 //                                               QString());
894         const QString version = headers.value(QStringLiteral("Sec-WebSocket-Version"),
895                                               QString());
896
897         if (Q_LIKELY(httpStatusCode == 101)) {
898             //HTTP/x.y 101 Switching Protocols
899             bool conversionOk = false;
900             const float version = httpProtocol.midRef(5).toFloat(&conversionOk);
901             //TODO: do not check the httpStatusText right now
902             ok = !(acceptKey.isEmpty() ||
903                    (!conversionOk || (version < 1.1f)) ||
904                    (upgrade.toLower() != QStringLiteral("websocket")) ||
905                    (connection.toLower() != QStringLiteral("upgrade")));
906             if (ok) {
907                 const QString accept = calculateAcceptKey(m_key);
908                 ok = (accept == acceptKey);
909                 if (!ok)
910                     errorDescription =
911                       QWebSocket::tr("Accept-Key received from server %1 does not match the client key %2.")
912                             .arg(acceptKey).arg(accept);
913             } else {
914                 errorDescription =
915                     QWebSocket::tr("QWebSocketPrivate::processHandshake: Invalid statusline in response: %1.")
916                         .arg(statusLine);
917             }
918         } else if (httpStatusCode == 400) {
919             //HTTP/1.1 400 Bad Request
920             if (!version.isEmpty()) {
921                 const QStringList versions = version.split(QStringLiteral(", "),
922                                                            QString::SkipEmptyParts);
923                 if (!versions.contains(QString::number(QWebSocketProtocol::currentVersion()))) {
924                     //if needed to switch protocol version, then we are finished here
925                     //because we cannot handle other protocols than the RFC one (v13)
926                     errorDescription =
927                             QWebSocket::tr("Handshake: Server requests a version that we don't support: %1.")
928                             .arg(versions.join(QStringLiteral(", ")));
929                     ok = false;
930                 } else {
931                     //we tried v13, but something different went wrong
932                     errorDescription =
933                         QWebSocket::tr("QWebSocketPrivate::processHandshake: Unknown error condition encountered. Aborting connection.");
934                     ok = false;
935                 }
936             }
937         } else {
938             errorDescription =
939                     QWebSocket::tr("QWebSocketPrivate::processHandshake: Unhandled http status code: %1 (%2).")
940                         .arg(httpStatusCode).arg(httpStatusMessage);
941             ok = false;
942         }
943
944         if (!ok) {
945             setErrorString(errorDescription);
946             Q_EMIT q->error(QAbstractSocket::ConnectionRefusedError);
947         } else {
948             //handshake succeeded
949             setSocketState(QAbstractSocket::ConnectedState);
950             Q_EMIT q->connected();
951         }
952     }
953 }
954
955 /*!
956     \internal
957  */
958 void QWebSocketPrivate::processStateChanged(QAbstractSocket::SocketState socketState)
959 {
960     Q_ASSERT(m_pSocket);
961     Q_Q(QWebSocket);
962     QAbstractSocket::SocketState webSocketState = this->state();
963     switch (socketState) {
964     case QAbstractSocket::ConnectedState:
965         if (webSocketState == QAbstractSocket::ConnectingState) {
966             m_key = generateKey();
967             const QString handshake =
968                     createHandShakeRequest(m_resourceName,
969                                            m_requestUrl.host()
970                                                 % QStringLiteral(":")
971                                                 % QString::number(m_requestUrl.port(80)),
972                                            origin(),
973                                            QString(),
974                                            QString(),
975                                            m_key);
976             m_pSocket->write(handshake.toLatin1());
977         }
978         break;
979
980     case QAbstractSocket::ClosingState:
981         if (webSocketState == QAbstractSocket::ConnectedState)
982             setSocketState(QAbstractSocket::ClosingState);
983         break;
984
985     case QAbstractSocket::UnconnectedState:
986         if (webSocketState != QAbstractSocket::UnconnectedState) {
987             setSocketState(QAbstractSocket::UnconnectedState);
988             Q_EMIT q->disconnected();
989         }
990         break;
991
992     case QAbstractSocket::HostLookupState:
993     case QAbstractSocket::ConnectingState:
994     case QAbstractSocket::BoundState:
995     case QAbstractSocket::ListeningState:
996         //do nothing
997         //to make C++ compiler happy;
998         break;
999     default:
1000         break;
1001     }
1002 }
1003
1004 /*!
1005  \internal
1006  */
1007 void QWebSocketPrivate::processData()
1008 {
1009     Q_ASSERT(m_pSocket);
1010     while (m_pSocket->bytesAvailable()) {
1011         if (state() == QAbstractSocket::ConnectingState)
1012             processHandshake(m_pSocket.data());
1013         else
1014             m_dataProcessor.process(m_pSocket.data());
1015     }
1016 }
1017
1018 /*!
1019  \internal
1020  */
1021 void QWebSocketPrivate::processPing(const QByteArray &data)
1022 {
1023     Q_ASSERT(m_pSocket);
1024     quint32 maskingKey = 0;
1025     if (m_mustMask)
1026         maskingKey = generateMaskingKey();
1027     m_pSocket->write(getFrameHeader(QWebSocketProtocol::OpCodePong, data.size(), maskingKey, true));
1028     if (data.size() > 0) {
1029         QByteArray maskedData = data;
1030         if (m_mustMask)
1031             QWebSocketProtocol::mask(&maskedData, maskingKey);
1032         m_pSocket->write(maskedData);
1033     }
1034 }
1035
1036 /*!
1037  \internal
1038  */
1039 void QWebSocketPrivate::processPong(const QByteArray &data)
1040 {
1041     Q_Q(QWebSocket);
1042     Q_EMIT q->pong(static_cast<quint64>(m_pingTimer.elapsed()), data);
1043 }
1044
1045 /*!
1046  \internal
1047  */
1048 void QWebSocketPrivate::processClose(QWebSocketProtocol::CloseCode closeCode, QString closeReason)
1049 {
1050     m_isClosingHandshakeReceived = true;
1051     close(closeCode, closeReason);
1052 }
1053
1054 /*!
1055     \internal
1056  */
1057 QString QWebSocketPrivate::createHandShakeRequest(QString resourceName,
1058                                                   QString host,
1059                                                   QString origin,
1060                                                   QString extensions,
1061                                                   QString protocols,
1062                                                   QByteArray key)
1063 {
1064     QStringList handshakeRequest;
1065
1066     handshakeRequest << QStringLiteral("GET ") % resourceName % QStringLiteral(" HTTP/1.1") <<
1067                         QStringLiteral("Host: ") % host <<
1068                         QStringLiteral("Upgrade: websocket") <<
1069                         QStringLiteral("Connection: Upgrade") <<
1070                         QStringLiteral("Sec-WebSocket-Key: ") % QString::fromLatin1(key);
1071     if (!origin.isEmpty())
1072         handshakeRequest << QStringLiteral("Origin: ") % origin;
1073     handshakeRequest << QStringLiteral("Sec-WebSocket-Version: ")
1074                             % QString::number(QWebSocketProtocol::currentVersion());
1075     if (extensions.length() > 0)
1076         handshakeRequest << QStringLiteral("Sec-WebSocket-Extensions: ") % extensions;
1077     if (protocols.length() > 0)
1078         handshakeRequest << QStringLiteral("Sec-WebSocket-Protocol: ") % protocols;
1079     handshakeRequest << QStringLiteral("\r\n");
1080
1081     return handshakeRequest.join(QStringLiteral("\r\n"));
1082 }
1083
1084 /*!
1085     \internal
1086  */
1087 QAbstractSocket::SocketState QWebSocketPrivate::state() const
1088 {
1089     return m_socketState;
1090 }
1091
1092 /*!
1093     \internal
1094  */
1095 void QWebSocketPrivate::setSocketState(QAbstractSocket::SocketState state)
1096 {
1097     Q_Q(QWebSocket);
1098     if (m_socketState != state) {
1099         m_socketState = state;
1100         Q_EMIT q->stateChanged(m_socketState);
1101     }
1102 }
1103
1104 /*!
1105     \internal
1106  */
1107 void QWebSocketPrivate::setErrorString(const QString &errorString)
1108 {
1109     if (m_errorString != errorString)
1110         m_errorString = errorString;
1111 }
1112
1113 /*!
1114     \internal
1115  */
1116 QHostAddress QWebSocketPrivate::localAddress() const
1117 {
1118     QHostAddress address;
1119     if (Q_LIKELY(m_pSocket))
1120         address = m_pSocket->localAddress();
1121     return address;
1122 }
1123
1124 /*!
1125     \internal
1126  */
1127 quint16 QWebSocketPrivate::localPort() const
1128 {
1129     quint16 port = 0;
1130     if (Q_LIKELY(m_pSocket))
1131         port = m_pSocket->localPort();
1132     return port;
1133 }
1134
1135 /*!
1136     \internal
1137  */
1138 QAbstractSocket::PauseModes QWebSocketPrivate::pauseMode() const
1139 {
1140     return m_pauseMode;
1141 }
1142
1143 /*!
1144     \internal
1145  */
1146 QHostAddress QWebSocketPrivate::peerAddress() const
1147 {
1148     QHostAddress address;
1149     if (Q_LIKELY(m_pSocket))
1150         address = m_pSocket->peerAddress();
1151     return address;
1152 }
1153
1154 /*!
1155     \internal
1156  */
1157 QString QWebSocketPrivate::peerName() const
1158 {
1159     QString name;
1160     if (Q_LIKELY(m_pSocket))
1161         name = m_pSocket->peerName();
1162     return name;
1163 }
1164
1165 /*!
1166     \internal
1167  */
1168 quint16 QWebSocketPrivate::peerPort() const
1169 {
1170     quint16 port = 0;
1171     if (Q_LIKELY(m_pSocket))
1172         port = m_pSocket->peerPort();
1173     return port;
1174 }
1175
1176 #ifndef QT_NO_NETWORKPROXY
1177 /*!
1178     \internal
1179  */
1180 QNetworkProxy QWebSocketPrivate::proxy() const
1181 {
1182     return m_configuration.m_proxy;
1183 }
1184
1185 /*!
1186     \internal
1187  */
1188 void QWebSocketPrivate::setProxy(const QNetworkProxy &networkProxy)
1189 {
1190     if (networkProxy != networkProxy)
1191         m_configuration.m_proxy = networkProxy;
1192 }
1193 #endif  //QT_NO_NETWORKPROXY
1194
1195 /*!
1196     \internal
1197  */
1198 qint64 QWebSocketPrivate::readBufferSize() const
1199 {
1200     return m_readBufferSize;
1201 }
1202
1203 /*!
1204     \internal
1205  */
1206 void QWebSocketPrivate::resume()
1207 {
1208     if (Q_LIKELY(m_pSocket))
1209         m_pSocket->resume();
1210 }
1211
1212 /*!
1213   \internal
1214  */
1215 void QWebSocketPrivate::setPauseMode(QAbstractSocket::PauseModes pauseMode)
1216 {
1217     m_pauseMode = pauseMode;
1218     if (Q_LIKELY(m_pSocket))
1219         m_pSocket->setPauseMode(m_pauseMode);
1220 }
1221
1222 /*!
1223     \internal
1224  */
1225 void QWebSocketPrivate::setReadBufferSize(qint64 size)
1226 {
1227     m_readBufferSize = size;
1228     if (Q_LIKELY(m_pSocket))
1229         m_pSocket->setReadBufferSize(m_readBufferSize);
1230 }
1231
1232 /*!
1233     \internal
1234  */
1235 bool QWebSocketPrivate::isValid() const
1236 {
1237     return (m_pSocket && m_pSocket->isValid() &&
1238             (m_socketState == QAbstractSocket::ConnectedState));
1239 }
1240
1241 QT_END_NAMESPACE