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