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