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