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