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