1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2016 Kurt Pattyn <pattyn.kurt@gmail.com>. |
4 | ** Contact: https://www.qt.io/licensing/ |
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 The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/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 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qwebsocket.h" |
41 | #include "qwebsocket_p.h" |
42 | #include "qwebsocketprotocol_p.h" |
43 | #include "qwebsockethandshakerequest_p.h" |
44 | #include "qwebsockethandshakeresponse_p.h" |
45 | #include "qdefaultmaskgenerator_p.h" |
46 | |
47 | #include <QtCore/QUrl> |
48 | #include <QtNetwork/QAuthenticator> |
49 | #include <QtNetwork/QTcpSocket> |
50 | #include <QtCore/QByteArray> |
51 | #include <QtCore/QtEndian> |
52 | #include <QtCore/QCryptographicHash> |
53 | #include <QtCore/QRegularExpression> |
54 | #include <QtCore/QStringList> |
55 | #include <QtNetwork/QHostAddress> |
56 | #include <QtCore/QStringBuilder> //for more efficient string concatenation |
57 | #ifndef QT_NONETWORKPROXY |
58 | #include <QtNetwork/QNetworkProxy> |
59 | #endif |
60 | #ifndef QT_NO_SSL |
61 | #include <QtNetwork/QSslConfiguration> |
62 | #include <QtNetwork/QSslError> |
63 | #include <QtNetwork/QSslPreSharedKeyAuthenticator> |
64 | #endif |
65 | |
66 | #include <QtCore/QDebug> |
67 | |
68 | #include <limits> |
69 | |
70 | QT_BEGIN_NAMESPACE |
71 | |
72 | const quint64 MAX_OUTGOING_FRAME_SIZE_IN_BYTES = std::numeric_limits<int>::max() - 1; |
73 | const quint64 DEFAULT_OUTGOING_FRAME_SIZE_IN_BYTES = 512 * 512 * 2; //default size of a frame when sending a message |
74 | |
75 | QWebSocketConfiguration::QWebSocketConfiguration() : |
76 | #ifndef QT_NO_SSL |
77 | m_ignoredSslErrors(), |
78 | m_ignoreSslErrors(false), |
79 | #endif |
80 | #ifndef QT_NO_NETWORKPROXY |
81 | m_proxy(QNetworkProxy::DefaultProxy), |
82 | #endif |
83 | m_pSocket(nullptr) |
84 | { |
85 | } |
86 | |
87 | /*! |
88 | \internal |
89 | */ |
90 | QWebSocketPrivate::QWebSocketPrivate(const QString &origin, QWebSocketProtocol::Version version) : |
91 | QObjectPrivate(), |
92 | m_pSocket(nullptr), |
93 | m_errorString(), |
94 | m_version(version), |
95 | m_resourceName(), |
96 | m_request(), |
97 | m_origin(origin), |
98 | m_protocol(), |
99 | m_extension(), |
100 | m_socketState(QAbstractSocket::UnconnectedState), |
101 | m_pauseMode(QAbstractSocket::PauseNever), |
102 | m_readBufferSize(0), |
103 | m_key(), |
104 | m_mustMask(true), |
105 | m_isClosingHandshakeSent(false), |
106 | m_isClosingHandshakeReceived(false), |
107 | m_closeCode(QWebSocketProtocol::CloseCodeNormal), |
108 | m_closeReason(), |
109 | m_pingTimer(), |
110 | m_configuration(), |
111 | m_pMaskGenerator(&m_defaultMaskGenerator), |
112 | m_defaultMaskGenerator(), |
113 | m_handshakeState(NothingDoneState), |
114 | m_outgoingFrameSize(DEFAULT_OUTGOING_FRAME_SIZE_IN_BYTES) |
115 | { |
116 | m_pingTimer.start(); |
117 | } |
118 | |
119 | /*! |
120 | \internal |
121 | */ |
122 | QWebSocketPrivate::QWebSocketPrivate(QTcpSocket *pTcpSocket, QWebSocketProtocol::Version version) : |
123 | QObjectPrivate(), |
124 | m_pSocket(pTcpSocket), |
125 | m_errorString(pTcpSocket->errorString()), |
126 | m_version(version), |
127 | m_resourceName(), |
128 | m_request(), |
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_configuration(), |
143 | m_pMaskGenerator(&m_defaultMaskGenerator), |
144 | m_defaultMaskGenerator(), |
145 | m_handshakeState(NothingDoneState), |
146 | m_outgoingFrameSize(DEFAULT_OUTGOING_FRAME_SIZE_IN_BYTES) |
147 | { |
148 | m_pingTimer.start(); |
149 | } |
150 | |
151 | /*! |
152 | \internal |
153 | */ |
154 | void QWebSocketPrivate::init() |
155 | { |
156 | Q_ASSERT(q_ptr); |
157 | Q_ASSERT(m_pMaskGenerator); |
158 | |
159 | m_dataProcessor->setParent(q_ptr); |
160 | m_pMaskGenerator->seed(); |
161 | |
162 | if (m_pSocket) { |
163 | makeConnections(pTcpSocket: m_pSocket); |
164 | } |
165 | } |
166 | |
167 | /*! |
168 | \internal |
169 | */ |
170 | QWebSocketPrivate::~QWebSocketPrivate() |
171 | { |
172 | } |
173 | |
174 | /*! |
175 | \internal |
176 | */ |
177 | void QWebSocketPrivate::closeGoingAway() |
178 | { |
179 | if (!m_pSocket) |
180 | return; |
181 | if (state() == QAbstractSocket::ConnectedState) |
182 | close(closeCode: QWebSocketProtocol::CloseCodeGoingAway, reason: QWebSocket::tr(s: "Connection closed" )); |
183 | releaseConnections(pTcpSocket: m_pSocket); |
184 | } |
185 | |
186 | /*! |
187 | \internal |
188 | */ |
189 | void QWebSocketPrivate::abort() |
190 | { |
191 | if (m_pSocket) |
192 | m_pSocket->abort(); |
193 | } |
194 | |
195 | /*! |
196 | \internal |
197 | */ |
198 | QAbstractSocket::SocketError QWebSocketPrivate::error() const |
199 | { |
200 | QAbstractSocket::SocketError err = QAbstractSocket::UnknownSocketError; |
201 | if (Q_LIKELY(m_pSocket)) |
202 | err = m_pSocket->error(); |
203 | return err; |
204 | } |
205 | |
206 | /*! |
207 | \internal |
208 | */ |
209 | QString QWebSocketPrivate::errorString() const |
210 | { |
211 | QString errMsg; |
212 | if (!m_errorString.isEmpty()) |
213 | errMsg = m_errorString; |
214 | else if (m_pSocket) |
215 | errMsg = m_pSocket->errorString(); |
216 | return errMsg; |
217 | } |
218 | |
219 | /*! |
220 | \internal |
221 | */ |
222 | bool QWebSocketPrivate::flush() |
223 | { |
224 | bool result = true; |
225 | if (Q_LIKELY(m_pSocket)) |
226 | result = m_pSocket->flush(); |
227 | return result; |
228 | } |
229 | |
230 | #ifndef Q_OS_WASM |
231 | |
232 | /*! |
233 | \internal |
234 | */ |
235 | qint64 QWebSocketPrivate::sendTextMessage(const QString &message) |
236 | { |
237 | return doWriteFrames(data: message.toUtf8(), isBinary: false); |
238 | } |
239 | |
240 | /*! |
241 | \internal |
242 | */ |
243 | qint64 QWebSocketPrivate::sendBinaryMessage(const QByteArray &data) |
244 | { |
245 | return doWriteFrames(data, isBinary: true); |
246 | } |
247 | |
248 | #endif |
249 | |
250 | #ifndef QT_NO_SSL |
251 | /*! |
252 | \internal |
253 | */ |
254 | void QWebSocketPrivate::setSslConfiguration(const QSslConfiguration &sslConfiguration) |
255 | { |
256 | m_configuration.m_sslConfiguration = sslConfiguration; |
257 | } |
258 | |
259 | /*! |
260 | \internal |
261 | */ |
262 | QSslConfiguration QWebSocketPrivate::sslConfiguration() const |
263 | { |
264 | return m_configuration.m_sslConfiguration; |
265 | } |
266 | |
267 | /*! |
268 | \internal |
269 | */ |
270 | void QWebSocketPrivate::ignoreSslErrors(const QList<QSslError> &errors) |
271 | { |
272 | m_configuration.m_ignoredSslErrors = errors; |
273 | if (Q_LIKELY(m_pSocket)) { |
274 | QSslSocket *pSslSocket = qobject_cast<QSslSocket *>(object: m_pSocket); |
275 | if (Q_LIKELY(pSslSocket)) |
276 | pSslSocket->ignoreSslErrors(errors); |
277 | } |
278 | } |
279 | |
280 | /*! |
281 | * \internal |
282 | */ |
283 | void QWebSocketPrivate::ignoreSslErrors() |
284 | { |
285 | m_configuration.m_ignoreSslErrors = true; |
286 | if (Q_LIKELY(m_pSocket)) { |
287 | QSslSocket *pSslSocket = qobject_cast<QSslSocket *>(object: m_pSocket); |
288 | if (Q_LIKELY(pSslSocket)) |
289 | pSslSocket->ignoreSslErrors(); |
290 | } |
291 | } |
292 | |
293 | /*! |
294 | * \internal |
295 | */ |
296 | void QWebSocketPrivate::_q_updateSslConfiguration() |
297 | { |
298 | if (QSslSocket *sslSock = qobject_cast<QSslSocket *>(object: m_pSocket)) |
299 | m_configuration.m_sslConfiguration = sslSock->sslConfiguration(); |
300 | } |
301 | |
302 | #endif |
303 | |
304 | /*! |
305 | Called from QWebSocketServer |
306 | \internal |
307 | */ |
308 | QWebSocket *QWebSocketPrivate::upgradeFrom(QTcpSocket *pTcpSocket, |
309 | const QWebSocketHandshakeRequest &request, |
310 | const QWebSocketHandshakeResponse &response, |
311 | QObject *parent) |
312 | { |
313 | QWebSocket *pWebSocket = new QWebSocket(pTcpSocket, response.acceptedVersion(), parent); |
314 | if (Q_LIKELY(pWebSocket)) { |
315 | QNetworkRequest netRequest(request.requestUrl()); |
316 | const auto = request.headers(); |
317 | for (auto it = headers.begin(), end = headers.end(); it != end; ++it) |
318 | netRequest.setRawHeader(headerName: it.key().toLatin1(), value: it.value().toLatin1()); |
319 | #ifndef QT_NO_SSL |
320 | if (QSslSocket *sslSock = qobject_cast<QSslSocket *>(object: pTcpSocket)) |
321 | pWebSocket->setSslConfiguration(sslSock->sslConfiguration()); |
322 | #endif |
323 | pWebSocket->d_func()->setExtension(response.acceptedExtension()); |
324 | pWebSocket->d_func()->setOrigin(request.origin()); |
325 | pWebSocket->d_func()->setRequest(netRequest); |
326 | pWebSocket->d_func()->setProtocol(response.acceptedProtocol()); |
327 | pWebSocket->d_func()->setResourceName(request.requestUrl().toString(options: QUrl::RemoveUserInfo)); |
328 | //a server should not send masked frames |
329 | pWebSocket->d_func()->enableMasking(enable: false); |
330 | } |
331 | |
332 | return pWebSocket; |
333 | } |
334 | |
335 | #ifndef Q_OS_WASM |
336 | |
337 | /*! |
338 | \internal |
339 | */ |
340 | void QWebSocketPrivate::close(QWebSocketProtocol::CloseCode closeCode, QString reason) |
341 | { |
342 | if (Q_UNLIKELY(!m_pSocket)) |
343 | return; |
344 | if (!m_isClosingHandshakeSent) { |
345 | Q_Q(QWebSocket); |
346 | m_closeCode = closeCode; |
347 | // 125 is the maximum length of a control frame, and 2 bytes are used for the close code: |
348 | const QByteArray reasonUtf8 = reason.toUtf8().left(len: 123); |
349 | m_closeReason = QString::fromUtf8(str: reasonUtf8); |
350 | const quint16 code = qToBigEndian<quint16>(source: closeCode); |
351 | QByteArray payload; |
352 | payload.append(s: static_cast<const char *>(static_cast<const void *>(&code)), len: 2); |
353 | if (!reasonUtf8.isEmpty()) |
354 | payload.append(a: reasonUtf8); |
355 | quint32 maskingKey = 0; |
356 | if (m_mustMask) { |
357 | maskingKey = generateMaskingKey(); |
358 | QWebSocketProtocol::mask(payload: payload.data(), size: quint64(payload.size()), maskingKey); |
359 | } |
360 | QByteArray frame = getFrameHeader(opCode: QWebSocketProtocol::OpCodeClose, |
361 | payloadLength: quint64(payload.size()), maskingKey, lastFrame: true); |
362 | |
363 | Q_ASSERT(payload.length() <= 125); |
364 | frame.append(a: payload); |
365 | m_pSocket->write(data: frame); |
366 | m_pSocket->flush(); |
367 | |
368 | m_isClosingHandshakeSent = true; |
369 | |
370 | Q_EMIT q->aboutToClose(); |
371 | } |
372 | m_pSocket->close(); |
373 | } |
374 | |
375 | /*! |
376 | \internal |
377 | */ |
378 | void QWebSocketPrivate::open(const QNetworkRequest &request, bool mask) |
379 | { |
380 | //just delete the old socket for the moment; |
381 | //later, we can add more 'intelligent' handling by looking at the URL |
382 | |
383 | Q_Q(QWebSocket); |
384 | QUrl url = request.url(); |
385 | if (!url.isValid() || url.toString().contains(QStringLiteral("\r\n" ))) { |
386 | setErrorString(QWebSocket::tr(s: "Invalid URL." )); |
387 | Q_EMIT q->error(error: QAbstractSocket::ConnectionRefusedError); |
388 | return; |
389 | } |
390 | if (m_pSocket) { |
391 | releaseConnections(pTcpSocket: m_pSocket); |
392 | m_pSocket->deleteLater(); |
393 | m_pSocket = nullptr; |
394 | } |
395 | //if (m_url != url) |
396 | if (Q_LIKELY(!m_pSocket)) { |
397 | m_dataProcessor->clear(); |
398 | m_isClosingHandshakeReceived = false; |
399 | m_isClosingHandshakeSent = false; |
400 | |
401 | setRequest(request); |
402 | QString resourceName = url.path(options: QUrl::FullyEncoded); |
403 | // Check for encoded \r\n |
404 | if (resourceName.contains(QStringLiteral("%0D%0A" ))) { |
405 | setRequest(QNetworkRequest()); //clear request |
406 | setErrorString(QWebSocket::tr(s: "Invalid resource name." )); |
407 | Q_EMIT q->error(error: QAbstractSocket::ConnectionRefusedError); |
408 | return; |
409 | } |
410 | if (!url.query().isEmpty()) { |
411 | if (!resourceName.endsWith(c: QChar::fromLatin1(c: '?'))) { |
412 | resourceName.append(c: QChar::fromLatin1(c: '?')); |
413 | } |
414 | resourceName.append(s: url.query(QUrl::FullyEncoded)); |
415 | } |
416 | if (resourceName.isEmpty()) |
417 | resourceName = QStringLiteral("/" ); |
418 | setResourceName(resourceName); |
419 | enableMasking(enable: mask); |
420 | |
421 | #ifndef QT_NO_SSL |
422 | if (url.scheme() == QStringLiteral("wss" )) { |
423 | if (!QSslSocket::supportsSsl()) { |
424 | const QString message = |
425 | QWebSocket::tr(s: "SSL Sockets are not supported on this platform." ); |
426 | setErrorString(message); |
427 | Q_EMIT q->error(error: QAbstractSocket::UnsupportedSocketOperationError); |
428 | } else { |
429 | QSslSocket *sslSocket = new QSslSocket(q); |
430 | m_pSocket = sslSocket; |
431 | if (Q_LIKELY(m_pSocket)) { |
432 | QObject::connect(sender: sslSocket, signal: &QSslSocket::connected, slot: [sslSocket](){ |
433 | sslSocket->setSocketOption(option: QAbstractSocket::LowDelayOption, value: 1); |
434 | sslSocket->setSocketOption(option: QAbstractSocket::KeepAliveOption, value: 1); |
435 | }); |
436 | m_pSocket->setReadBufferSize(m_readBufferSize); |
437 | m_pSocket->setPauseMode(m_pauseMode); |
438 | |
439 | makeConnections(pTcpSocket: m_pSocket); |
440 | setSocketState(QAbstractSocket::ConnectingState); |
441 | |
442 | sslSocket->setSslConfiguration(m_configuration.m_sslConfiguration); |
443 | if (Q_UNLIKELY(m_configuration.m_ignoreSslErrors)) |
444 | sslSocket->ignoreSslErrors(); |
445 | else |
446 | sslSocket->ignoreSslErrors(errors: m_configuration.m_ignoredSslErrors); |
447 | #ifndef QT_NO_NETWORKPROXY |
448 | sslSocket->setProxy(m_configuration.m_proxy); |
449 | m_pSocket->setProtocolTag(QStringLiteral("https" )); |
450 | #endif |
451 | sslSocket->connectToHostEncrypted(hostName: url.host(), port: quint16(url.port(defaultPort: 443))); |
452 | } else { |
453 | const QString message = QWebSocket::tr(s: "Out of memory." ); |
454 | setErrorString(message); |
455 | Q_EMIT q->error(error: QAbstractSocket::SocketResourceError); |
456 | } |
457 | } |
458 | } else |
459 | #endif |
460 | if (url.scheme() == QStringLiteral("ws" )) { |
461 | m_pSocket = new QTcpSocket(q); |
462 | if (Q_LIKELY(m_pSocket)) { |
463 | QObject::connect(sender: m_pSocket, signal: &QTcpSocket::connected, slot: [this](){ |
464 | m_pSocket->setSocketOption(option: QAbstractSocket::LowDelayOption, value: 1); |
465 | m_pSocket->setSocketOption(option: QAbstractSocket::KeepAliveOption, value: 1); |
466 | }); |
467 | m_pSocket->setReadBufferSize(m_readBufferSize); |
468 | m_pSocket->setPauseMode(m_pauseMode); |
469 | |
470 | makeConnections(pTcpSocket: m_pSocket); |
471 | setSocketState(QAbstractSocket::ConnectingState); |
472 | #ifndef QT_NO_NETWORKPROXY |
473 | m_pSocket->setProxy(m_configuration.m_proxy); |
474 | m_pSocket->setProtocolTag(QStringLiteral("http" )); |
475 | #endif |
476 | m_pSocket->connectToHost(hostName: url.host(), port: quint16(url.port(defaultPort: 80))); |
477 | } else { |
478 | const QString message = QWebSocket::tr(s: "Out of memory." ); |
479 | setErrorString(message); |
480 | Q_EMIT q->error(error: QAbstractSocket::SocketResourceError); |
481 | } |
482 | } else { |
483 | const QString message = |
484 | QWebSocket::tr(s: "Unsupported WebSocket scheme: %1" ).arg(a: url.scheme()); |
485 | setErrorString(message); |
486 | Q_EMIT q->error(error: QAbstractSocket::UnsupportedSocketOperationError); |
487 | } |
488 | } |
489 | } |
490 | |
491 | #endif |
492 | |
493 | /*! |
494 | \internal |
495 | */ |
496 | void QWebSocketPrivate::ping(const QByteArray &payload) |
497 | { |
498 | QByteArray payloadTruncated = payload.left(len: 125); |
499 | m_pingTimer.restart(); |
500 | quint32 maskingKey = 0; |
501 | if (m_mustMask) |
502 | maskingKey = generateMaskingKey(); |
503 | QByteArray pingFrame = getFrameHeader(opCode: QWebSocketProtocol::OpCodePing, |
504 | payloadLength: quint64(payloadTruncated.size()), |
505 | maskingKey, lastFrame: true); |
506 | if (m_mustMask) |
507 | QWebSocketProtocol::mask(payload: &payloadTruncated, maskingKey); |
508 | pingFrame.append(a: payloadTruncated); |
509 | qint64 ret = writeFrame(frame: pingFrame); |
510 | Q_UNUSED(ret); |
511 | } |
512 | |
513 | /*! |
514 | \internal |
515 | Sets the version to use for the WebSocket protocol; |
516 | this must be set before the socket is opened. |
517 | */ |
518 | void QWebSocketPrivate::setVersion(QWebSocketProtocol::Version version) |
519 | { |
520 | if (m_version != version) |
521 | m_version = version; |
522 | } |
523 | |
524 | /*! |
525 | \internal |
526 | Sets the resource name of the connection; must be set before the socket is openend |
527 | */ |
528 | void QWebSocketPrivate::setResourceName(const QString &resourceName) |
529 | { |
530 | if (m_resourceName != resourceName) |
531 | m_resourceName = resourceName; |
532 | } |
533 | |
534 | /*! |
535 | \internal |
536 | */ |
537 | void QWebSocketPrivate::setRequest(const QNetworkRequest &request) |
538 | { |
539 | if (m_request != request) |
540 | m_request = request; |
541 | } |
542 | |
543 | /*! |
544 | \internal |
545 | */ |
546 | void QWebSocketPrivate::setOrigin(const QString &origin) |
547 | { |
548 | if (m_origin != origin) |
549 | m_origin = origin; |
550 | } |
551 | |
552 | /*! |
553 | \internal |
554 | */ |
555 | void QWebSocketPrivate::setProtocol(const QString &protocol) |
556 | { |
557 | if (m_protocol != protocol) |
558 | m_protocol = protocol; |
559 | } |
560 | |
561 | /*! |
562 | \internal |
563 | */ |
564 | void QWebSocketPrivate::setExtension(const QString &extension) |
565 | { |
566 | if (m_extension != extension) |
567 | m_extension = extension; |
568 | } |
569 | |
570 | /*! |
571 | \internal |
572 | */ |
573 | void QWebSocketPrivate::enableMasking(bool enable) |
574 | { |
575 | if (m_mustMask != enable) |
576 | m_mustMask = enable; |
577 | } |
578 | |
579 | /*! |
580 | * \internal |
581 | */ |
582 | void QWebSocketPrivate::makeConnections(QTcpSocket *pTcpSocket) |
583 | { |
584 | Q_ASSERT(pTcpSocket); |
585 | Q_Q(QWebSocket); |
586 | |
587 | if (Q_LIKELY(pTcpSocket)) { |
588 | //pass through signals |
589 | QObject::connect(sender: pTcpSocket, signal: &QAbstractSocket::errorOccurred, |
590 | receiver: q, slot: QOverload<QAbstractSocket::SocketError>::of(ptr: &QWebSocket::error)); |
591 | #ifndef QT_NO_NETWORKPROXY |
592 | QObject::connect(sender: pTcpSocket, signal: &QAbstractSocket::proxyAuthenticationRequired, receiver: q, |
593 | slot: &QWebSocket::proxyAuthenticationRequired); |
594 | #endif // QT_NO_NETWORKPROXY |
595 | QObject::connect(sender: pTcpSocket, signal: &QAbstractSocket::readChannelFinished, receiver: q, |
596 | slot: &QWebSocket::readChannelFinished); |
597 | QObject::connect(sender: pTcpSocket, signal: &QAbstractSocket::aboutToClose, receiver: q, slot: &QWebSocket::aboutToClose); |
598 | |
599 | QObjectPrivate::connect(sender: pTcpSocket, signal: &QObject::destroyed, |
600 | receiverPrivate: this, slot: &QWebSocketPrivate::socketDestroyed); |
601 | |
602 | //catch signals |
603 | QObjectPrivate::connect(sender: pTcpSocket, signal: &QAbstractSocket::stateChanged, receiverPrivate: this, |
604 | slot: &QWebSocketPrivate::processStateChanged); |
605 | //!!!important to use a QueuedConnection here; |
606 | //with QTcpSocket there is no problem, but with QSslSocket the processing hangs |
607 | QObjectPrivate::connect(sender: pTcpSocket, signal: &QAbstractSocket::readyRead, receiverPrivate: this, |
608 | slot: &QWebSocketPrivate::processData, type: Qt::QueuedConnection); |
609 | #ifndef QT_NO_SSL |
610 | const QSslSocket * const sslSocket = qobject_cast<const QSslSocket *>(object: pTcpSocket); |
611 | if (sslSocket) { |
612 | QObject::connect(sender: sslSocket, signal: &QSslSocket::preSharedKeyAuthenticationRequired, receiver: q, |
613 | slot: &QWebSocket::preSharedKeyAuthenticationRequired); |
614 | QObject::connect(sender: sslSocket, signal: &QSslSocket::encryptedBytesWritten, receiver: q, |
615 | slot: &QWebSocket::bytesWritten); |
616 | QObjectPrivate::connect(sender: sslSocket, |
617 | signal: QOverload<const QList<QSslError>&>::of(ptr: &QSslSocket::sslErrors), |
618 | receiverPrivate: this, slot: &QWebSocketPrivate::_q_updateSslConfiguration); |
619 | QObject::connect(sender: sslSocket, |
620 | signal: QOverload<const QList<QSslError>&>::of(ptr: &QSslSocket::sslErrors), |
621 | receiver: q, slot: &QWebSocket::sslErrors); |
622 | QObjectPrivate::connect(sender: sslSocket, signal: &QSslSocket::encrypted, |
623 | receiverPrivate: this, slot: &QWebSocketPrivate::_q_updateSslConfiguration); |
624 | } else |
625 | #endif // QT_NO_SSL |
626 | { |
627 | QObject::connect(sender: pTcpSocket, signal: &QAbstractSocket::bytesWritten, receiver: q, |
628 | slot: &QWebSocket::bytesWritten); |
629 | } |
630 | } |
631 | |
632 | QObject::connect(sender: m_dataProcessor, signal: &QWebSocketDataProcessor::textFrameReceived, receiver: q, |
633 | slot: &QWebSocket::textFrameReceived); |
634 | QObject::connect(sender: m_dataProcessor, signal: &QWebSocketDataProcessor::binaryFrameReceived, receiver: q, |
635 | slot: &QWebSocket::binaryFrameReceived); |
636 | QObject::connect(sender: m_dataProcessor, signal: &QWebSocketDataProcessor::binaryMessageReceived, receiver: q, |
637 | slot: &QWebSocket::binaryMessageReceived); |
638 | QObject::connect(sender: m_dataProcessor, signal: &QWebSocketDataProcessor::textMessageReceived, receiver: q, |
639 | slot: &QWebSocket::textMessageReceived); |
640 | QObjectPrivate::connect(sender: m_dataProcessor, signal: &QWebSocketDataProcessor::errorEncountered, receiverPrivate: this, |
641 | slot: &QWebSocketPrivate::close); |
642 | QObjectPrivate::connect(sender: m_dataProcessor, signal: &QWebSocketDataProcessor::pingReceived, receiverPrivate: this, |
643 | slot: &QWebSocketPrivate::processPing); |
644 | QObjectPrivate::connect(sender: m_dataProcessor, signal: &QWebSocketDataProcessor::pongReceived, receiverPrivate: this, |
645 | slot: &QWebSocketPrivate::processPong); |
646 | QObjectPrivate::connect(sender: m_dataProcessor, signal: &QWebSocketDataProcessor::closeReceived, receiverPrivate: this, |
647 | slot: &QWebSocketPrivate::processClose); |
648 | |
649 | //fire readyread, in case we already have data inside the tcpSocket |
650 | if (pTcpSocket->bytesAvailable()) |
651 | Q_EMIT pTcpSocket->readyRead(); |
652 | } |
653 | |
654 | /*! |
655 | * \internal |
656 | */ |
657 | void QWebSocketPrivate::releaseConnections(const QTcpSocket *pTcpSocket) |
658 | { |
659 | if (Q_LIKELY(pTcpSocket)) |
660 | pTcpSocket->disconnect(); |
661 | m_dataProcessor->disconnect(); |
662 | } |
663 | |
664 | /*! |
665 | \internal |
666 | */ |
667 | QWebSocketProtocol::Version QWebSocketPrivate::version() const |
668 | { |
669 | return m_version; |
670 | } |
671 | |
672 | /*! |
673 | \internal |
674 | */ |
675 | QString QWebSocketPrivate::resourceName() const |
676 | { |
677 | return m_resourceName; |
678 | } |
679 | |
680 | /*! |
681 | \internal |
682 | */ |
683 | QNetworkRequest QWebSocketPrivate::request() const |
684 | { |
685 | return m_request; |
686 | } |
687 | |
688 | /*! |
689 | \internal |
690 | */ |
691 | QString QWebSocketPrivate::origin() const |
692 | { |
693 | return m_origin; |
694 | } |
695 | |
696 | /*! |
697 | \internal |
698 | */ |
699 | QString QWebSocketPrivate::protocol() const |
700 | { |
701 | return m_protocol; |
702 | } |
703 | |
704 | /*! |
705 | \internal |
706 | */ |
707 | QString QWebSocketPrivate::extension() const |
708 | { |
709 | return m_extension; |
710 | } |
711 | |
712 | /*! |
713 | * \internal |
714 | */ |
715 | QWebSocketProtocol::CloseCode QWebSocketPrivate::closeCode() const |
716 | { |
717 | return m_closeCode; |
718 | } |
719 | |
720 | /*! |
721 | * \internal |
722 | */ |
723 | QString QWebSocketPrivate::closeReason() const |
724 | { |
725 | return m_closeReason; |
726 | } |
727 | |
728 | /*! |
729 | * \internal |
730 | */ |
731 | QByteArray QWebSocketPrivate::(QWebSocketProtocol::OpCode opCode, |
732 | quint64 payloadLength, quint32 maskingKey, |
733 | bool lastFrame) |
734 | { |
735 | Q_Q(QWebSocket); |
736 | QByteArray ; |
737 | bool ok = payloadLength <= 0x7FFFFFFFFFFFFFFFULL; |
738 | |
739 | if (Q_LIKELY(ok)) { |
740 | //FIN, RSV1-3, opcode (RSV-1, RSV-2 and RSV-3 are zero) |
741 | quint8 byte = static_cast<quint8>((opCode & 0x0F) | (lastFrame ? 0x80 : 0x00)); |
742 | header.append(c: static_cast<char>(byte)); |
743 | |
744 | byte = 0x00; |
745 | if (maskingKey != 0) |
746 | byte |= 0x80; |
747 | if (payloadLength <= 125) { |
748 | byte |= static_cast<quint8>(payloadLength); |
749 | header.append(c: static_cast<char>(byte)); |
750 | } else if (payloadLength <= 0xFFFFU) { |
751 | byte |= 126; |
752 | header.append(c: static_cast<char>(byte)); |
753 | quint16 swapped = qToBigEndian<quint16>(source: static_cast<quint16>(payloadLength)); |
754 | header.append(s: static_cast<const char *>(static_cast<const void *>(&swapped)), len: 2); |
755 | } else if (payloadLength <= 0x7FFFFFFFFFFFFFFFULL) { |
756 | byte |= 127; |
757 | header.append(c: static_cast<char>(byte)); |
758 | quint64 swapped = qToBigEndian<quint64>(source: payloadLength); |
759 | header.append(s: static_cast<const char *>(static_cast<const void *>(&swapped)), len: 8); |
760 | } |
761 | |
762 | if (maskingKey != 0) { |
763 | const quint32 mask = qToBigEndian<quint32>(source: maskingKey); |
764 | header.append(s: static_cast<const char *>(static_cast<const void *>(&mask)), |
765 | len: sizeof(quint32)); |
766 | } |
767 | } else { |
768 | setErrorString(QStringLiteral("WebSocket::getHeader: payload too big!" )); |
769 | Q_EMIT q->error(error: QAbstractSocket::DatagramTooLargeError); |
770 | } |
771 | |
772 | return header; |
773 | } |
774 | |
775 | /*! |
776 | * \internal |
777 | */ |
778 | qint64 QWebSocketPrivate::doWriteFrames(const QByteArray &data, bool isBinary) |
779 | { |
780 | qint64 payloadWritten = 0; |
781 | if (Q_UNLIKELY(!m_pSocket) || (state() != QAbstractSocket::ConnectedState)) |
782 | return payloadWritten; |
783 | |
784 | Q_Q(QWebSocket); |
785 | const QWebSocketProtocol::OpCode firstOpCode = isBinary ? |
786 | QWebSocketProtocol::OpCodeBinary : QWebSocketProtocol::OpCodeText; |
787 | |
788 | int numFrames = data.size() / int(outgoingFrameSize()); |
789 | QByteArray tmpData(data); |
790 | tmpData.detach(); |
791 | char *payload = tmpData.data(); |
792 | quint64 sizeLeft = quint64(data.size()) % outgoingFrameSize(); |
793 | if (Q_LIKELY(sizeLeft)) |
794 | ++numFrames; |
795 | |
796 | //catch the case where the payload is zero bytes; |
797 | //in this case, we still need to send a frame |
798 | if (Q_UNLIKELY(numFrames == 0)) |
799 | numFrames = 1; |
800 | quint64 currentPosition = 0; |
801 | quint64 bytesLeft = quint64(data.size()); |
802 | |
803 | for (int i = 0; i < numFrames; ++i) { |
804 | quint32 maskingKey = 0; |
805 | if (m_mustMask) |
806 | maskingKey = generateMaskingKey(); |
807 | |
808 | const bool isLastFrame = (i == (numFrames - 1)); |
809 | const bool isFirstFrame = (i == 0); |
810 | |
811 | const quint64 size = qMin(a: bytesLeft, b: outgoingFrameSize()); |
812 | const QWebSocketProtocol::OpCode opcode = isFirstFrame ? firstOpCode |
813 | : QWebSocketProtocol::OpCodeContinue; |
814 | |
815 | //write header |
816 | m_pSocket->write(data: getFrameHeader(opCode: opcode, payloadLength: size, maskingKey, lastFrame: isLastFrame)); |
817 | |
818 | //write payload |
819 | if (Q_LIKELY(size > 0)) { |
820 | char *currentData = payload + currentPosition; |
821 | if (m_mustMask) |
822 | QWebSocketProtocol::mask(payload: currentData, size, maskingKey); |
823 | qint64 written = m_pSocket->write(data: currentData, len: static_cast<qint64>(size)); |
824 | if (Q_LIKELY(written > 0)) { |
825 | payloadWritten += written; |
826 | } else { |
827 | m_pSocket->flush(); |
828 | setErrorString(QWebSocket::tr(s: "Error writing bytes to socket: %1." ) |
829 | .arg(a: m_pSocket->errorString())); |
830 | Q_EMIT q->error(error: QAbstractSocket::NetworkError); |
831 | break; |
832 | } |
833 | } |
834 | currentPosition += size; |
835 | bytesLeft -= size; |
836 | } |
837 | if (Q_UNLIKELY(payloadWritten != data.size())) { |
838 | setErrorString(QWebSocket::tr(s: "Bytes written %1 != %2." ) |
839 | .arg(a: payloadWritten).arg(a: data.size())); |
840 | Q_EMIT q->error(error: QAbstractSocket::NetworkError); |
841 | } |
842 | return payloadWritten; |
843 | } |
844 | |
845 | /*! |
846 | \internal |
847 | */ |
848 | quint32 QWebSocketPrivate::generateMaskingKey() const |
849 | { |
850 | return m_pMaskGenerator->nextMask(); |
851 | } |
852 | |
853 | /*! |
854 | \internal |
855 | */ |
856 | QByteArray QWebSocketPrivate::generateKey() const |
857 | { |
858 | QByteArray key; |
859 | |
860 | for (int i = 0; i < 4; ++i) { |
861 | const quint32 tmp = m_pMaskGenerator->nextMask(); |
862 | key.append(s: static_cast<const char *>(static_cast<const void *>(&tmp)), len: sizeof(quint32)); |
863 | } |
864 | |
865 | return key.toBase64(); |
866 | } |
867 | |
868 | |
869 | /*! |
870 | \internal |
871 | */ |
872 | QString QWebSocketPrivate::calculateAcceptKey(const QByteArray &key) const |
873 | { |
874 | const QByteArray tmpKey = key + QByteArrayLiteral("258EAFA5-E914-47DA-95CA-C5AB0DC85B11" ); |
875 | const QByteArray hash = QCryptographicHash::hash(data: tmpKey, method: QCryptographicHash::Sha1).toBase64(); |
876 | return QString::fromLatin1(str: hash); |
877 | } |
878 | |
879 | /*! |
880 | \internal |
881 | */ |
882 | qint64 QWebSocketPrivate::writeFrames(const QList<QByteArray> &frames) |
883 | { |
884 | qint64 written = 0; |
885 | if (Q_LIKELY(m_pSocket)) { |
886 | QList<QByteArray>::const_iterator it; |
887 | for (it = frames.cbegin(); it < frames.cend(); ++it) |
888 | written += writeFrame(frame: *it); |
889 | } |
890 | return written; |
891 | } |
892 | |
893 | /*! |
894 | \internal |
895 | */ |
896 | qint64 QWebSocketPrivate::writeFrame(const QByteArray &frame) |
897 | { |
898 | qint64 written = 0; |
899 | if (Q_LIKELY(m_pSocket)) |
900 | written = m_pSocket->write(data: frame); |
901 | return written; |
902 | } |
903 | |
904 | /*! |
905 | \internal |
906 | */ |
907 | static QString readLine(QTcpSocket *pSocket) |
908 | { |
909 | Q_ASSERT(pSocket); |
910 | QString line; |
911 | char c; |
912 | while (pSocket->getChar(c: &c)) { |
913 | if (c == char('\r')) { |
914 | pSocket->getChar(c: &c); |
915 | break; |
916 | } else { |
917 | line.append(c: QChar::fromLatin1(c)); |
918 | } |
919 | } |
920 | return line; |
921 | } |
922 | |
923 | // this function is a copy of QHttpNetworkReplyPrivate::parseStatus |
924 | static bool parseStatusLine(const QByteArray &status, int *majorVersion, int *minorVersion, |
925 | int *statusCode, QString *reasonPhrase) |
926 | { |
927 | // from RFC 2616: |
928 | // Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF |
929 | // HTTP-Version = "HTTP" "/" 1*DIGIT "." 1*DIGIT |
930 | // that makes: 'HTTP/n.n xxx Message' |
931 | // byte count: 0123456789012 |
932 | |
933 | static const int minLength = 11; |
934 | static const int dotPos = 6; |
935 | static const int spacePos = 8; |
936 | static const char httpMagic[] = "HTTP/" ; |
937 | |
938 | if (status.length() < minLength |
939 | || !status.startsWith(c: httpMagic) |
940 | || status.at(i: dotPos) != '.' |
941 | || status.at(i: spacePos) != ' ') { |
942 | // I don't know how to parse this status line |
943 | return false; |
944 | } |
945 | |
946 | // optimize for the valid case: defer checking until the end |
947 | *majorVersion = status.at(i: dotPos - 1) - '0'; |
948 | *minorVersion = status.at(i: dotPos + 1) - '0'; |
949 | |
950 | int i = spacePos; |
951 | int j = status.indexOf(c: ' ', from: i + 1); // j == -1 || at(j) == ' ' so j+1 == 0 && j+1 <= length() |
952 | const QByteArray code = status.mid(index: i + 1, len: j - i - 1); |
953 | |
954 | bool ok; |
955 | *statusCode = code.toInt(ok: &ok); |
956 | *reasonPhrase = QString::fromLatin1(str: status.constData() + j + 1); |
957 | |
958 | return ok && uint(*majorVersion) <= 9 && uint(* minorVersion) <= 9; |
959 | } |
960 | |
961 | |
962 | //called on the client for a server handshake response |
963 | /*! |
964 | \internal |
965 | */ |
966 | void QWebSocketPrivate::processHandshake(QTcpSocket *pSocket) |
967 | { |
968 | Q_Q(QWebSocket); |
969 | if (Q_UNLIKELY(!pSocket)) |
970 | return; |
971 | // Reset handshake on a new connection. |
972 | if (m_handshakeState == AllDoneState) |
973 | m_handshakeState = NothingDoneState; |
974 | |
975 | QString errorDescription; |
976 | |
977 | switch (m_handshakeState) { |
978 | case NothingDoneState: |
979 | m_headers.clear(); |
980 | m_handshakeState = ReadingStatusState; |
981 | Q_FALLTHROUGH(); |
982 | case ReadingStatusState: |
983 | if (!pSocket->canReadLine()) |
984 | return; |
985 | m_statusLine = pSocket->readLine().trimmed(); |
986 | if (Q_UNLIKELY(!parseStatusLine(m_statusLine, &m_httpMajorVersion, &m_httpMinorVersion, &m_httpStatusCode, &m_httpStatusMessage))) { |
987 | errorDescription = QWebSocket::tr(s: "Invalid statusline in response: %1." ).arg(a: QString::fromLatin1(str: m_statusLine)); |
988 | break; |
989 | } |
990 | m_handshakeState = ReadingHeaderState; |
991 | Q_FALLTHROUGH(); |
992 | case ReadingHeaderState: { |
993 | // TODO: this should really use the existing code from QHttpNetworkReplyPrivate::parseHeader |
994 | auto = m_headers.end(); |
995 | while (pSocket->canReadLine()) { |
996 | QString = readLine(pSocket); |
997 | |
998 | if (headerLine.isEmpty()) { |
999 | // end of headers |
1000 | m_handshakeState = ParsingHeaderState; |
1001 | break; |
1002 | } else if (headerLine.startsWith(c: QLatin1Char(' ')) || headerLine.startsWith(c: QLatin1Char('\t'))) { |
1003 | // continuation line -- add this to the last header field |
1004 | if (Q_UNLIKELY(lastHeader == m_headers.end())) { |
1005 | errorDescription = QWebSocket::tr(s: "Malformed header in response: %1." ).arg(a: headerLine); |
1006 | break; |
1007 | } |
1008 | lastHeader.value().append(c: QLatin1Char(' ')); |
1009 | lastHeader.value().append(s: headerLine.trimmed()); |
1010 | } else { |
1011 | int colonPos = headerLine.indexOf(c: QLatin1Char(':')); |
1012 | if (Q_UNLIKELY(colonPos <= 0)) { |
1013 | errorDescription = QWebSocket::tr(s: "Malformed header in response: %1." ).arg(a: headerLine); |
1014 | break; |
1015 | } |
1016 | lastHeader = m_headers.insert(akey: headerLine.left(n: colonPos).trimmed().toLower(), |
1017 | avalue: headerLine.mid(position: colonPos + 1).trimmed()); |
1018 | } |
1019 | } |
1020 | |
1021 | if (m_handshakeState != ParsingHeaderState) { |
1022 | if (pSocket->state() != QAbstractSocket::ConnectedState) { |
1023 | errorDescription = QWebSocket::tr(s: "QWebSocketPrivate::processHandshake: Connection closed while reading header." ); |
1024 | break; |
1025 | } |
1026 | return; |
1027 | } |
1028 | Q_FALLTHROUGH(); |
1029 | } |
1030 | case ParsingHeaderState: { |
1031 | const QString acceptKey = m_headers.value(QStringLiteral("sec-websocket-accept" ), adefaultValue: QString()); |
1032 | const QString upgrade = m_headers.value(QStringLiteral("upgrade" ), adefaultValue: QString()); |
1033 | const QString connection = m_headers.value(QStringLiteral("connection" ), adefaultValue: QString()); |
1034 | // unused for the moment |
1035 | // const QString extensions = m_headers.value(QStringLiteral("sec-websocket-extensions"), |
1036 | // QString()); |
1037 | // const QString protocol = m_headers.value(QStringLiteral("sec-websocket-protocol"), |
1038 | // QString()); |
1039 | const QString version = m_headers.value(QStringLiteral("sec-websocket-version" ), adefaultValue: QString()); |
1040 | |
1041 | bool ok = false; |
1042 | if (Q_LIKELY(m_httpStatusCode == 101)) { |
1043 | //HTTP/x.y 101 Switching Protocols |
1044 | //TODO: do not check the httpStatusText right now |
1045 | ok = !(acceptKey.isEmpty() || |
1046 | (m_httpMajorVersion < 1 || m_httpMinorVersion < 1) || |
1047 | (upgrade.toLower() != QStringLiteral("websocket" )) || |
1048 | (connection.toLower() != QStringLiteral("upgrade" ))); |
1049 | if (ok) { |
1050 | const QString accept = calculateAcceptKey(key: m_key); |
1051 | ok = (accept == acceptKey); |
1052 | if (!ok) |
1053 | errorDescription = |
1054 | QWebSocket::tr(s: "Accept-Key received from server %1 does not match the client key %2." ) |
1055 | .arg(a1: acceptKey, a2: accept); |
1056 | } else { |
1057 | errorDescription = |
1058 | QWebSocket::tr(s: "QWebSocketPrivate::processHandshake: Invalid statusline in response: %1." ) |
1059 | .arg(a: QString::fromLatin1(str: m_statusLine)); |
1060 | } |
1061 | } else if (m_httpStatusCode == 400) { |
1062 | //HTTP/1.1 400 Bad Request |
1063 | if (!version.isEmpty()) { |
1064 | const QStringList versions = version.split(QStringLiteral(", " ), behavior: Qt::SkipEmptyParts); |
1065 | if (!versions.contains(str: QString::number(QWebSocketProtocol::currentVersion()))) { |
1066 | //if needed to switch protocol version, then we are finished here |
1067 | //because we cannot handle other protocols than the RFC one (v13) |
1068 | errorDescription = |
1069 | QWebSocket::tr(s: "Handshake: Server requests a version that we don't support: %1." ) |
1070 | .arg(a: versions.join(QStringLiteral(", " ))); |
1071 | } else { |
1072 | //we tried v13, but something different went wrong |
1073 | errorDescription = |
1074 | QWebSocket::tr(s: "QWebSocketPrivate::processHandshake: Unknown error condition encountered. Aborting connection." ); |
1075 | } |
1076 | } else { |
1077 | errorDescription = |
1078 | QWebSocket::tr(s: "QWebSocketPrivate::processHandshake: Unknown error condition encountered. Aborting connection." ); |
1079 | } |
1080 | } else { |
1081 | errorDescription = |
1082 | QWebSocket::tr(s: "QWebSocketPrivate::processHandshake: Unhandled http status code: %1 (%2)." ) |
1083 | .arg(a: m_httpStatusCode).arg(a: m_httpStatusMessage); |
1084 | } |
1085 | if (ok) |
1086 | m_handshakeState = AllDoneState; |
1087 | break; |
1088 | } |
1089 | case AllDoneState: |
1090 | Q_UNREACHABLE(); |
1091 | break; |
1092 | } |
1093 | |
1094 | if (m_handshakeState == AllDoneState) { |
1095 | // handshake succeeded |
1096 | setSocketState(QAbstractSocket::ConnectedState); |
1097 | Q_EMIT q->connected(); |
1098 | } else { |
1099 | // handshake failed |
1100 | m_handshakeState = AllDoneState; |
1101 | setErrorString(errorDescription); |
1102 | Q_EMIT q->error(error: QAbstractSocket::ConnectionRefusedError); |
1103 | } |
1104 | } |
1105 | |
1106 | /*! |
1107 | \internal |
1108 | */ |
1109 | void QWebSocketPrivate::processStateChanged(QAbstractSocket::SocketState socketState) |
1110 | { |
1111 | Q_ASSERT(m_pSocket); |
1112 | Q_Q(QWebSocket); |
1113 | QAbstractSocket::SocketState webSocketState = this->state(); |
1114 | |
1115 | switch (socketState) { |
1116 | case QAbstractSocket::ConnectedState: |
1117 | #ifndef QT_NO_SSL |
1118 | if (QSslSocket *sslSock = qobject_cast<QSslSocket *>(object: m_pSocket)) |
1119 | m_configuration.m_sslConfiguration = sslSock->sslConfiguration(); |
1120 | #endif |
1121 | if (webSocketState == QAbstractSocket::ConnectingState) { |
1122 | m_key = generateKey(); |
1123 | |
1124 | QList<QPair<QString, QString> > ; |
1125 | const auto = m_request.rawHeaderList(); |
1126 | for (const QByteArray &key : headerList) |
1127 | headers << qMakePair(x: QString::fromLatin1(str: key), |
1128 | y: QString::fromLatin1(str: m_request.rawHeader(headerName: key))); |
1129 | |
1130 | const auto format = QUrl::RemoveScheme | QUrl::RemoveUserInfo |
1131 | | QUrl::RemovePath | QUrl::RemoveQuery |
1132 | | QUrl::RemoveFragment; |
1133 | const QString host = m_request.url().toString(options: format).mid(position: 2); |
1134 | const QString handshake = createHandShakeRequest(resourceName: m_resourceName, |
1135 | host, |
1136 | origin: origin(), |
1137 | extensions: QString(), |
1138 | protocols: QString(), |
1139 | key: m_key, |
1140 | headers); |
1141 | if (handshake.isEmpty()) { |
1142 | m_pSocket->abort(); |
1143 | Q_EMIT q->error(error: QAbstractSocket::ConnectionRefusedError); |
1144 | return; |
1145 | } |
1146 | m_pSocket->write(data: handshake.toLatin1()); |
1147 | } |
1148 | break; |
1149 | |
1150 | case QAbstractSocket::ClosingState: |
1151 | if (webSocketState == QAbstractSocket::ConnectedState) |
1152 | setSocketState(QAbstractSocket::ClosingState); |
1153 | break; |
1154 | |
1155 | case QAbstractSocket::UnconnectedState: |
1156 | if (webSocketState != QAbstractSocket::UnconnectedState) { |
1157 | setSocketState(QAbstractSocket::UnconnectedState); |
1158 | Q_EMIT q->disconnected(); |
1159 | } |
1160 | break; |
1161 | |
1162 | case QAbstractSocket::HostLookupState: |
1163 | case QAbstractSocket::ConnectingState: |
1164 | case QAbstractSocket::BoundState: |
1165 | case QAbstractSocket::ListeningState: |
1166 | //do nothing |
1167 | //to make C++ compiler happy; |
1168 | break; |
1169 | } |
1170 | } |
1171 | |
1172 | void QWebSocketPrivate::socketDestroyed(QObject *socket) |
1173 | { |
1174 | Q_ASSERT(m_pSocket); |
1175 | if (m_pSocket == socket) |
1176 | m_pSocket = nullptr; |
1177 | } |
1178 | |
1179 | /*! |
1180 | \internal |
1181 | */ |
1182 | void QWebSocketPrivate::processData() |
1183 | { |
1184 | if (!m_pSocket) // disconnected with data still in-bound |
1185 | return; |
1186 | while (m_pSocket->bytesAvailable()) { |
1187 | if (state() == QAbstractSocket::ConnectingState) { |
1188 | if (!m_pSocket->canReadLine()) |
1189 | return; |
1190 | processHandshake(pSocket: m_pSocket); |
1191 | } else if (!m_dataProcessor->process(pIoDevice: m_pSocket)) { |
1192 | return; |
1193 | } |
1194 | } |
1195 | } |
1196 | |
1197 | /*! |
1198 | \internal |
1199 | */ |
1200 | void QWebSocketPrivate::processPing(const QByteArray &data) |
1201 | { |
1202 | Q_ASSERT(m_pSocket); |
1203 | quint32 maskingKey = 0; |
1204 | if (m_mustMask) |
1205 | maskingKey = generateMaskingKey(); |
1206 | m_pSocket->write(data: getFrameHeader(opCode: QWebSocketProtocol::OpCodePong, |
1207 | payloadLength: unsigned(data.size()), |
1208 | maskingKey, |
1209 | lastFrame: true)); |
1210 | if (data.size() > 0) { |
1211 | QByteArray maskedData = data; |
1212 | if (m_mustMask) |
1213 | QWebSocketProtocol::mask(payload: &maskedData, maskingKey); |
1214 | m_pSocket->write(data: maskedData); |
1215 | } |
1216 | } |
1217 | |
1218 | /*! |
1219 | \internal |
1220 | */ |
1221 | void QWebSocketPrivate::processPong(const QByteArray &data) |
1222 | { |
1223 | Q_Q(QWebSocket); |
1224 | Q_EMIT q->pong(elapsedTime: static_cast<quint64>(m_pingTimer.elapsed()), payload: data); |
1225 | } |
1226 | |
1227 | /*! |
1228 | \internal |
1229 | */ |
1230 | void QWebSocketPrivate::processClose(QWebSocketProtocol::CloseCode closeCode, QString closeReason) |
1231 | { |
1232 | m_isClosingHandshakeReceived = true; |
1233 | close(closeCode, reason: closeReason); |
1234 | } |
1235 | |
1236 | /*! |
1237 | \internal |
1238 | */ |
1239 | QString QWebSocketPrivate::createHandShakeRequest(QString resourceName, |
1240 | QString host, |
1241 | QString origin, |
1242 | QString extensions, |
1243 | QString protocols, |
1244 | QByteArray key, |
1245 | const QList<QPair<QString, QString> > &) |
1246 | { |
1247 | QStringList handshakeRequest; |
1248 | if (resourceName.contains(QStringLiteral("\r\n" ))) { |
1249 | setErrorString(QWebSocket::tr(s: "The resource name contains newlines. " \ |
1250 | "Possible attack detected." )); |
1251 | return QString(); |
1252 | } |
1253 | if (host.contains(QStringLiteral("\r\n" ))) { |
1254 | setErrorString(QWebSocket::tr(s: "The hostname contains newlines. " \ |
1255 | "Possible attack detected." )); |
1256 | return QString(); |
1257 | } |
1258 | if (origin.contains(QStringLiteral("\r\n" ))) { |
1259 | setErrorString(QWebSocket::tr(s: "The origin contains newlines. " \ |
1260 | "Possible attack detected." )); |
1261 | return QString(); |
1262 | } |
1263 | if (extensions.contains(QStringLiteral("\r\n" ))) { |
1264 | setErrorString(QWebSocket::tr(s: "The extensions attribute contains newlines. " \ |
1265 | "Possible attack detected." )); |
1266 | return QString(); |
1267 | } |
1268 | if (protocols.contains(QStringLiteral("\r\n" ))) { |
1269 | setErrorString(QWebSocket::tr(s: "The protocols attribute contains newlines. " \ |
1270 | "Possible attack detected." )); |
1271 | return QString(); |
1272 | } |
1273 | |
1274 | handshakeRequest << QStringLiteral("GET " ) % resourceName % QStringLiteral(" HTTP/1.1" ) << |
1275 | QStringLiteral("Host: " ) % host << |
1276 | QStringLiteral("Upgrade: websocket" ) << |
1277 | QStringLiteral("Connection: Upgrade" ) << |
1278 | QStringLiteral("Sec-WebSocket-Key: " ) % QString::fromLatin1(str: key); |
1279 | if (!origin.isEmpty()) |
1280 | handshakeRequest << QStringLiteral("Origin: " ) % origin; |
1281 | handshakeRequest << QStringLiteral("Sec-WebSocket-Version: " ) |
1282 | % QString::number(QWebSocketProtocol::currentVersion()); |
1283 | if (extensions.length() > 0) |
1284 | handshakeRequest << QStringLiteral("Sec-WebSocket-Extensions: " ) % extensions; |
1285 | if (protocols.length() > 0) |
1286 | handshakeRequest << QStringLiteral("Sec-WebSocket-Protocol: " ) % protocols; |
1287 | |
1288 | for (const auto & : headers) |
1289 | handshakeRequest << header.first % QStringLiteral(": " ) % header.second; |
1290 | |
1291 | handshakeRequest << QStringLiteral("\r\n" ); |
1292 | |
1293 | return handshakeRequest.join(QStringLiteral("\r\n" )); |
1294 | } |
1295 | |
1296 | /*! |
1297 | \internal |
1298 | */ |
1299 | QAbstractSocket::SocketState QWebSocketPrivate::state() const |
1300 | { |
1301 | return m_socketState; |
1302 | } |
1303 | |
1304 | /*! |
1305 | \internal |
1306 | */ |
1307 | void QWebSocketPrivate::setSocketState(QAbstractSocket::SocketState state) |
1308 | { |
1309 | Q_Q(QWebSocket); |
1310 | if (m_socketState != state) { |
1311 | m_socketState = state; |
1312 | Q_EMIT q->stateChanged(state: m_socketState); |
1313 | } |
1314 | } |
1315 | |
1316 | /*! |
1317 | \internal |
1318 | */ |
1319 | void QWebSocketPrivate::setMaxAllowedIncomingFrameSize(quint64 maxAllowedIncomingFrameSize) |
1320 | { |
1321 | m_dataProcessor->setMaxAllowedFrameSize(maxAllowedIncomingFrameSize); |
1322 | } |
1323 | |
1324 | /*! |
1325 | \internal |
1326 | */ |
1327 | quint64 QWebSocketPrivate::maxAllowedIncomingFrameSize() const |
1328 | { |
1329 | return m_dataProcessor->maxAllowedFrameSize(); |
1330 | } |
1331 | |
1332 | /*! |
1333 | \internal |
1334 | */ |
1335 | void QWebSocketPrivate::setMaxAllowedIncomingMessageSize(quint64 maxAllowedIncomingMessageSize) |
1336 | { |
1337 | m_dataProcessor->setMaxAllowedMessageSize(maxAllowedIncomingMessageSize); |
1338 | } |
1339 | |
1340 | /*! |
1341 | \internal |
1342 | */ |
1343 | quint64 QWebSocketPrivate::maxAllowedIncomingMessageSize() const |
1344 | { |
1345 | return m_dataProcessor->maxAllowedMessageSize(); |
1346 | } |
1347 | |
1348 | /*! |
1349 | \internal |
1350 | */ |
1351 | quint64 QWebSocketPrivate::maxIncomingMessageSize() |
1352 | { |
1353 | return QWebSocketDataProcessor::maxMessageSize(); |
1354 | } |
1355 | |
1356 | /*! |
1357 | \internal |
1358 | */ |
1359 | quint64 QWebSocketPrivate::maxIncomingFrameSize() |
1360 | { |
1361 | return QWebSocketDataProcessor::maxFrameSize(); |
1362 | } |
1363 | |
1364 | /*! |
1365 | \internal |
1366 | */ |
1367 | void QWebSocketPrivate::setOutgoingFrameSize(quint64 outgoingFrameSize) |
1368 | { |
1369 | if (outgoingFrameSize <= maxOutgoingFrameSize()) |
1370 | m_outgoingFrameSize = outgoingFrameSize; |
1371 | } |
1372 | |
1373 | /*! |
1374 | \internal |
1375 | */ |
1376 | quint64 QWebSocketPrivate::outgoingFrameSize() const |
1377 | { |
1378 | return m_outgoingFrameSize; |
1379 | } |
1380 | |
1381 | /*! |
1382 | \internal |
1383 | */ |
1384 | quint64 QWebSocketPrivate::maxOutgoingFrameSize() |
1385 | { |
1386 | return MAX_OUTGOING_FRAME_SIZE_IN_BYTES; |
1387 | } |
1388 | |
1389 | |
1390 | /*! |
1391 | \internal |
1392 | */ |
1393 | void QWebSocketPrivate::setErrorString(const QString &errorString) |
1394 | { |
1395 | if (m_errorString != errorString) |
1396 | m_errorString = errorString; |
1397 | } |
1398 | |
1399 | /*! |
1400 | \internal |
1401 | */ |
1402 | QHostAddress QWebSocketPrivate::localAddress() const |
1403 | { |
1404 | QHostAddress address; |
1405 | if (Q_LIKELY(m_pSocket)) |
1406 | address = m_pSocket->localAddress(); |
1407 | return address; |
1408 | } |
1409 | |
1410 | /*! |
1411 | \internal |
1412 | */ |
1413 | quint16 QWebSocketPrivate::localPort() const |
1414 | { |
1415 | quint16 port = 0; |
1416 | if (Q_LIKELY(m_pSocket)) |
1417 | port = m_pSocket->localPort(); |
1418 | return port; |
1419 | } |
1420 | |
1421 | /*! |
1422 | \internal |
1423 | */ |
1424 | QAbstractSocket::PauseModes QWebSocketPrivate::pauseMode() const |
1425 | { |
1426 | return m_pauseMode; |
1427 | } |
1428 | |
1429 | /*! |
1430 | \internal |
1431 | */ |
1432 | QHostAddress QWebSocketPrivate::peerAddress() const |
1433 | { |
1434 | QHostAddress address; |
1435 | if (Q_LIKELY(m_pSocket)) |
1436 | address = m_pSocket->peerAddress(); |
1437 | return address; |
1438 | } |
1439 | |
1440 | /*! |
1441 | \internal |
1442 | */ |
1443 | QString QWebSocketPrivate::peerName() const |
1444 | { |
1445 | QString name; |
1446 | if (Q_LIKELY(m_pSocket)) |
1447 | name = m_pSocket->peerName(); |
1448 | return name; |
1449 | } |
1450 | |
1451 | /*! |
1452 | \internal |
1453 | */ |
1454 | quint16 QWebSocketPrivate::peerPort() const |
1455 | { |
1456 | quint16 port = 0; |
1457 | if (Q_LIKELY(m_pSocket)) |
1458 | port = m_pSocket->peerPort(); |
1459 | return port; |
1460 | } |
1461 | |
1462 | #ifndef QT_NO_NETWORKPROXY |
1463 | /*! |
1464 | \internal |
1465 | */ |
1466 | QNetworkProxy QWebSocketPrivate::proxy() const |
1467 | { |
1468 | return m_configuration.m_proxy; |
1469 | } |
1470 | |
1471 | /*! |
1472 | \internal |
1473 | */ |
1474 | void QWebSocketPrivate::setProxy(const QNetworkProxy &networkProxy) |
1475 | { |
1476 | if (m_configuration.m_proxy != networkProxy) |
1477 | m_configuration.m_proxy = networkProxy; |
1478 | } |
1479 | #endif //QT_NO_NETWORKPROXY |
1480 | |
1481 | /*! |
1482 | \internal |
1483 | */ |
1484 | void QWebSocketPrivate::setMaskGenerator(const QMaskGenerator *maskGenerator) |
1485 | { |
1486 | if (!maskGenerator) |
1487 | m_pMaskGenerator = &m_defaultMaskGenerator; |
1488 | else if (maskGenerator != m_pMaskGenerator) |
1489 | m_pMaskGenerator = const_cast<QMaskGenerator *>(maskGenerator); |
1490 | } |
1491 | |
1492 | /*! |
1493 | \internal |
1494 | */ |
1495 | const QMaskGenerator *QWebSocketPrivate::maskGenerator() const |
1496 | { |
1497 | Q_ASSERT(m_pMaskGenerator); |
1498 | return m_pMaskGenerator; |
1499 | } |
1500 | |
1501 | /*! |
1502 | \internal |
1503 | */ |
1504 | qint64 QWebSocketPrivate::readBufferSize() const |
1505 | { |
1506 | return m_readBufferSize; |
1507 | } |
1508 | |
1509 | /*! |
1510 | \internal |
1511 | */ |
1512 | void QWebSocketPrivate::resume() |
1513 | { |
1514 | if (Q_LIKELY(m_pSocket)) |
1515 | m_pSocket->resume(); |
1516 | } |
1517 | |
1518 | /*! |
1519 | \internal |
1520 | */ |
1521 | void QWebSocketPrivate::setPauseMode(QAbstractSocket::PauseModes pauseMode) |
1522 | { |
1523 | m_pauseMode = pauseMode; |
1524 | if (Q_LIKELY(m_pSocket)) |
1525 | m_pSocket->setPauseMode(m_pauseMode); |
1526 | } |
1527 | |
1528 | /*! |
1529 | \internal |
1530 | */ |
1531 | void QWebSocketPrivate::setReadBufferSize(qint64 size) |
1532 | { |
1533 | m_readBufferSize = size; |
1534 | if (Q_LIKELY(m_pSocket)) |
1535 | m_pSocket->setReadBufferSize(m_readBufferSize); |
1536 | } |
1537 | |
1538 | #ifndef Q_OS_WASM |
1539 | /*! |
1540 | \internal |
1541 | */ |
1542 | bool QWebSocketPrivate::isValid() const |
1543 | { |
1544 | return (m_pSocket && m_pSocket->isValid() && |
1545 | (m_socketState == QAbstractSocket::ConnectedState)); |
1546 | } |
1547 | #endif |
1548 | |
1549 | QT_END_NAMESPACE |
1550 | |