1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtNetwork 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 "qhttpsocketengine_p.h"
41#include "qtcpsocket.h"
42#include "qhostaddress.h"
43#include "qurl.h"
44#include "private/qhttpnetworkreply_p.h"
45#include "private/qiodevice_p.h"
46#include "qelapsedtimer.h"
47#include "qnetworkinterface.h"
48
49#if !defined(QT_NO_NETWORKPROXY)
50#include <qdebug.h>
51
52QT_BEGIN_NAMESPACE
53
54#define DEBUG
55
56QHttpSocketEngine::QHttpSocketEngine(QObject *parent)
57 : QAbstractSocketEngine(*new QHttpSocketEnginePrivate, parent)
58{
59}
60
61QHttpSocketEngine::~QHttpSocketEngine()
62{
63}
64
65bool QHttpSocketEngine::initialize(QAbstractSocket::SocketType type, QAbstractSocket::NetworkLayerProtocol protocol)
66{
67 Q_D(QHttpSocketEngine);
68 if (type != QAbstractSocket::TcpSocket)
69 return false;
70
71 setProtocol(protocol);
72 setSocketType(type);
73 d->socket = new QTcpSocket(this);
74 d->reply = new QHttpNetworkReply(QUrl(), this);
75#ifndef QT_NO_BEARERMANAGEMENT // ### Qt6: Remove section
76 d->socket->setProperty(name: "_q_networkSession", value: property(name: "_q_networkSession"));
77#endif
78
79 // Explicitly disable proxying on the proxy socket itself to avoid
80 // unwanted recursion.
81 d->socket->setProxy(QNetworkProxy::NoProxy);
82
83 // Intercept all the signals.
84 connect(sender: d->socket, SIGNAL(connected()),
85 receiver: this, SLOT(slotSocketConnected()),
86 Qt::DirectConnection);
87 connect(sender: d->socket, SIGNAL(disconnected()),
88 receiver: this, SLOT(slotSocketDisconnected()),
89 Qt::DirectConnection);
90 connect(sender: d->socket, SIGNAL(readyRead()),
91 receiver: this, SLOT(slotSocketReadNotification()),
92 Qt::DirectConnection);
93 connect(sender: d->socket, SIGNAL(bytesWritten(qint64)),
94 receiver: this, SLOT(slotSocketBytesWritten()),
95 Qt::DirectConnection);
96 connect(sender: d->socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)),
97 receiver: this, SLOT(slotSocketError(QAbstractSocket::SocketError)),
98 Qt::DirectConnection);
99 connect(sender: d->socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
100 receiver: this, SLOT(slotSocketStateChanged(QAbstractSocket::SocketState)),
101 Qt::DirectConnection);
102
103 return true;
104}
105
106bool QHttpSocketEngine::initialize(qintptr, QAbstractSocket::SocketState)
107{
108 return false;
109}
110
111void QHttpSocketEngine::setProxy(const QNetworkProxy &proxy)
112{
113 Q_D(QHttpSocketEngine);
114 d->proxy = proxy;
115 QString user = proxy.user();
116 if (!user.isEmpty())
117 d->authenticator.setUser(user);
118 QString password = proxy.password();
119 if (!password.isEmpty())
120 d->authenticator.setPassword(password);
121}
122
123qintptr QHttpSocketEngine::socketDescriptor() const
124{
125 Q_D(const QHttpSocketEngine);
126 return d->socket ? d->socket->socketDescriptor() : -1;
127}
128
129bool QHttpSocketEngine::isValid() const
130{
131 Q_D(const QHttpSocketEngine);
132 return d->socket;
133}
134
135bool QHttpSocketEngine::connectInternal()
136{
137 Q_D(QHttpSocketEngine);
138
139 d->credentialsSent = false;
140
141 // If the handshake is done, enter ConnectedState state and return true.
142 if (d->state == Connected) {
143 qWarning(msg: "QHttpSocketEngine::connectToHost: called when already connected");
144 setState(QAbstractSocket::ConnectedState);
145 return true;
146 }
147
148 if (d->state == ConnectSent && d->socketState != QAbstractSocket::ConnectedState)
149 setState(QAbstractSocket::UnconnectedState);
150
151 // Handshake isn't done. If unconnected, start connecting.
152 if (d->state == None && d->socket->state() == QAbstractSocket::UnconnectedState) {
153 setState(QAbstractSocket::ConnectingState);
154 //limit buffer in internal socket, data is buffered in the external socket under application control
155 d->socket->setReadBufferSize(65536);
156 d->socket->connectToHost(hostName: d->proxy.hostName(), port: d->proxy.port());
157 }
158
159 // If connected (might happen right away, at least for localhost services
160 // on some BSD systems), there might already be bytes available.
161 if (bytesAvailable())
162 slotSocketReadNotification();
163
164 return d->socketState == QAbstractSocket::ConnectedState;
165}
166
167bool QHttpSocketEngine::connectToHost(const QHostAddress &address, quint16 port)
168{
169 Q_D(QHttpSocketEngine);
170
171 setPeerAddress(address);
172 setPeerPort(port);
173 d->peerName.clear();
174
175 return connectInternal();
176}
177
178bool QHttpSocketEngine::connectToHostByName(const QString &hostname, quint16 port)
179{
180 Q_D(QHttpSocketEngine);
181
182 setPeerAddress(QHostAddress());
183 setPeerPort(port);
184 d->peerName = hostname;
185
186 return connectInternal();
187}
188
189bool QHttpSocketEngine::bind(const QHostAddress &, quint16)
190{
191 qWarning(msg: "Operation is not supported");
192 setError(error: QAbstractSocket::UnsupportedSocketOperationError,
193 errorString: QLatin1String("Unsupported socket operation"));
194 return false;
195}
196
197bool QHttpSocketEngine::listen()
198{
199 qWarning(msg: "Operation is not supported");
200 setError(error: QAbstractSocket::UnsupportedSocketOperationError,
201 errorString: QLatin1String("Unsupported socket operation"));
202 return false;
203}
204
205int QHttpSocketEngine::accept()
206{
207 qWarning(msg: "Operation is not supported");
208 setError(error: QAbstractSocket::UnsupportedSocketOperationError,
209 errorString: QLatin1String("Unsupported socket operation"));
210 return -1;
211}
212
213void QHttpSocketEngine::close()
214{
215 Q_D(QHttpSocketEngine);
216 if (d->socket) {
217 d->socket->close();
218 delete d->socket;
219 d->socket = nullptr;
220 }
221}
222
223qint64 QHttpSocketEngine::bytesAvailable() const
224{
225 Q_D(const QHttpSocketEngine);
226 return d->socket ? d->socket->bytesAvailable() : 0;
227}
228
229qint64 QHttpSocketEngine::read(char *data, qint64 maxlen)
230{
231 Q_D(QHttpSocketEngine);
232 qint64 bytesRead = d->socket->read(data, maxlen);
233
234 if (d->socket->state() == QAbstractSocket::UnconnectedState
235 && d->socket->bytesAvailable() == 0) {
236 emitReadNotification();
237 }
238
239 if (bytesRead == -1) {
240 // If nothing has been read so far, and the direct socket read
241 // failed, return the socket's error. Otherwise, fall through and
242 // return as much as we read so far.
243 close();
244 setError(error: QAbstractSocket::RemoteHostClosedError,
245 errorString: QLatin1String("Remote host closed"));
246 setState(QAbstractSocket::UnconnectedState);
247 return -1;
248 }
249 return bytesRead;
250}
251
252qint64 QHttpSocketEngine::write(const char *data, qint64 len)
253{
254 Q_D(QHttpSocketEngine);
255 return d->socket->write(data, len);
256}
257
258#ifndef QT_NO_UDPSOCKET
259#ifndef QT_NO_NETWORKINTERFACE
260bool QHttpSocketEngine::joinMulticastGroup(const QHostAddress &,
261 const QNetworkInterface &)
262{
263 qWarning(msg: "Operation is not supported");
264 setError(error: QAbstractSocket::UnsupportedSocketOperationError,
265 errorString: QLatin1String("Unsupported socket operation"));
266 return false;
267}
268
269bool QHttpSocketEngine::leaveMulticastGroup(const QHostAddress &,
270 const QNetworkInterface &)
271{
272 qWarning(msg: "Operation is not supported");
273 setError(error: QAbstractSocket::UnsupportedSocketOperationError,
274 errorString: QLatin1String("Unsupported socket operation"));
275 return false;
276}
277
278QNetworkInterface QHttpSocketEngine::multicastInterface() const
279{
280 return QNetworkInterface();
281}
282
283bool QHttpSocketEngine::setMulticastInterface(const QNetworkInterface &)
284{
285 qWarning(msg: "Operation is not supported");
286 setError(error: QAbstractSocket::UnsupportedSocketOperationError,
287 errorString: QLatin1String("Unsupported socket operation"));
288 return false;
289}
290#endif // QT_NO_NETWORKINTERFACE
291
292bool QHttpSocketEngine::hasPendingDatagrams() const
293{
294 qWarning(msg: "Operation is not supported");
295 return false;
296}
297
298qint64 QHttpSocketEngine::pendingDatagramSize() const
299{
300 qWarning(msg: "Operation is not supported");
301 return -1;
302}
303#endif // QT_NO_UDPSOCKET
304
305qint64 QHttpSocketEngine::readDatagram(char *, qint64, QIpPacketHeader *, PacketHeaderOptions)
306{
307 qWarning(msg: "Operation is not supported");
308 setError(error: QAbstractSocket::UnsupportedSocketOperationError,
309 errorString: QLatin1String("Unsupported socket operation"));
310 return -1;
311}
312
313qint64 QHttpSocketEngine::writeDatagram(const char *, qint64, const QIpPacketHeader &)
314{
315 qWarning(msg: "Operation is not supported");
316 setError(error: QAbstractSocket::UnsupportedSocketOperationError,
317 errorString: QLatin1String("Unsupported socket operation"));
318 return -1;
319}
320
321qint64 QHttpSocketEngine::bytesToWrite() const
322{
323 Q_D(const QHttpSocketEngine);
324 if (d->socket) {
325 return d->socket->bytesToWrite();
326 } else {
327 return 0;
328 }
329}
330
331int QHttpSocketEngine::option(SocketOption option) const
332{
333 Q_D(const QHttpSocketEngine);
334 if (d->socket) {
335 // convert the enum and call the real socket
336 if (option == QAbstractSocketEngine::LowDelayOption)
337 return d->socket->socketOption(option: QAbstractSocket::LowDelayOption).toInt();
338 if (option == QAbstractSocketEngine::KeepAliveOption)
339 return d->socket->socketOption(option: QAbstractSocket::KeepAliveOption).toInt();
340 }
341 return -1;
342}
343
344bool QHttpSocketEngine::setOption(SocketOption option, int value)
345{
346 Q_D(QHttpSocketEngine);
347 if (d->socket) {
348 // convert the enum and call the real socket
349 if (option == QAbstractSocketEngine::LowDelayOption)
350 d->socket->setSocketOption(option: QAbstractSocket::LowDelayOption, value);
351 if (option == QAbstractSocketEngine::KeepAliveOption)
352 d->socket->setSocketOption(option: QAbstractSocket::KeepAliveOption, value);
353 return true;
354 }
355 return false;
356}
357
358bool QHttpSocketEngine::waitForRead(int msecs, bool *timedOut)
359{
360 Q_D(const QHttpSocketEngine);
361
362 if (!d->socket || d->socket->state() == QAbstractSocket::UnconnectedState)
363 return false;
364
365 QElapsedTimer stopWatch;
366 stopWatch.start();
367
368 // Wait for more data if nothing is available.
369 if (!d->socket->bytesAvailable()) {
370 if (!d->socket->waitForReadyRead(msecs: qt_subtract_from_timeout(timeout: msecs, elapsed: stopWatch.elapsed()))) {
371 if (d->socket->state() == QAbstractSocket::UnconnectedState)
372 return true;
373 setError(error: d->socket->error(), errorString: d->socket->errorString());
374 if (timedOut && d->socket->error() == QAbstractSocket::SocketTimeoutError)
375 *timedOut = true;
376 return false;
377 }
378 }
379
380 // If we're not connected yet, wait until we are, or until an error
381 // occurs.
382 while (d->state != Connected && d->socket->waitForReadyRead(msecs: qt_subtract_from_timeout(timeout: msecs, elapsed: stopWatch.elapsed()))) {
383 // Loop while the protocol handshake is taking place.
384 }
385
386 // Report any error that may occur.
387 if (d->state != Connected) {
388 setError(error: d->socket->error(), errorString: d->socket->errorString());
389 if (timedOut && d->socket->error() == QAbstractSocket::SocketTimeoutError)
390 *timedOut = true;
391 return false;
392 }
393 return true;
394}
395
396bool QHttpSocketEngine::waitForWrite(int msecs, bool *timedOut)
397{
398 Q_D(const QHttpSocketEngine);
399
400 // If we're connected, just forward the call.
401 if (d->state == Connected) {
402 if (d->socket->bytesToWrite()) {
403 if (!d->socket->waitForBytesWritten(msecs)) {
404 if (d->socket->error() == QAbstractSocket::SocketTimeoutError && timedOut)
405 *timedOut = true;
406 return false;
407 }
408 }
409 return true;
410 }
411
412 QElapsedTimer stopWatch;
413 stopWatch.start();
414
415 // If we're not connected yet, wait until we are, and until bytes have
416 // been received (i.e., the socket has connected, we have sent the
417 // greeting, and then received the response).
418 while (d->state != Connected && d->socket->waitForReadyRead(msecs: qt_subtract_from_timeout(timeout: msecs, elapsed: stopWatch.elapsed()))) {
419 // Loop while the protocol handshake is taking place.
420 }
421
422 // Report any error that may occur.
423 if (d->state != Connected) {
424// setError(d->socket->error(), d->socket->errorString());
425 if (timedOut && d->socket->error() == QAbstractSocket::SocketTimeoutError)
426 *timedOut = true;
427 }
428
429 return true;
430}
431
432bool QHttpSocketEngine::waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
433 bool checkRead, bool checkWrite,
434 int msecs, bool *timedOut)
435{
436 Q_UNUSED(checkRead);
437
438 if (!checkWrite) {
439 // Not interested in writing? Then we wait for read notifications.
440 bool canRead = waitForRead(msecs, timedOut);
441 if (readyToRead)
442 *readyToRead = canRead;
443 return canRead;
444 }
445
446 // Interested in writing? Then we wait for write notifications.
447 bool canWrite = waitForWrite(msecs, timedOut);
448 if (readyToWrite)
449 *readyToWrite = canWrite;
450 return canWrite;
451}
452
453bool QHttpSocketEngine::isReadNotificationEnabled() const
454{
455 Q_D(const QHttpSocketEngine);
456 return d->readNotificationEnabled;
457}
458
459void QHttpSocketEngine::setReadNotificationEnabled(bool enable)
460{
461 Q_D(QHttpSocketEngine);
462 if (d->readNotificationEnabled == enable)
463 return;
464
465 d->readNotificationEnabled = enable;
466 if (enable) {
467 // Enabling read notification can trigger a notification.
468 if (bytesAvailable()) {
469 slotSocketReadNotification();
470 } else if (d->socket && d->socket->state() == QAbstractSocket::UnconnectedState) {
471 emitReadNotification();
472 }
473 }
474}
475
476bool QHttpSocketEngine::isWriteNotificationEnabled() const
477{
478 Q_D(const QHttpSocketEngine);
479 return d->writeNotificationEnabled;
480}
481
482void QHttpSocketEngine::setWriteNotificationEnabled(bool enable)
483{
484 Q_D(QHttpSocketEngine);
485 d->writeNotificationEnabled = enable;
486 if (enable && d->state == Connected && d->socket->state() == QAbstractSocket::ConnectedState)
487 QMetaObject::invokeMethod(obj: this, member: "writeNotification", type: Qt::QueuedConnection);
488}
489
490bool QHttpSocketEngine::isExceptionNotificationEnabled() const
491{
492 Q_D(const QHttpSocketEngine);
493 return d->exceptNotificationEnabled;
494}
495
496void QHttpSocketEngine::setExceptionNotificationEnabled(bool enable)
497{
498 Q_D(QHttpSocketEngine);
499 d->exceptNotificationEnabled = enable;
500}
501
502void QHttpSocketEngine::slotSocketConnected()
503{
504 Q_D(QHttpSocketEngine);
505
506 // Send the greeting.
507 const char method[] = "CONNECT";
508 QByteArray peerAddress = d->peerName.isEmpty() ?
509 d->peerAddress.toString().toLatin1() :
510 QUrl::toAce(d->peerName);
511 QByteArray path = peerAddress + ':' + QByteArray::number(d->peerPort);
512 QByteArray data = method;
513 data += ' ';
514 data += path;
515 data += " HTTP/1.1\r\n";
516 data += "Proxy-Connection: keep-alive\r\n";
517 data += "Host: " + peerAddress + "\r\n";
518 if (!d->proxy.hasRawHeader(headerName: "User-Agent"))
519 data += "User-Agent: Mozilla/5.0\r\n";
520 const auto headers = d->proxy.rawHeaderList();
521 for (const QByteArray &header : headers)
522 data += header + ": " + d->proxy.rawHeader(headerName: header) + "\r\n";
523 QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(auth&: d->authenticator);
524 //qDebug() << "slotSocketConnected: priv=" << priv << (priv ? (int)priv->method : -1);
525 if (priv && priv->method != QAuthenticatorPrivate::None) {
526 d->credentialsSent = true;
527 data += "Proxy-Authorization: " + priv->calculateResponse(method, path, host: d->proxy.hostName());
528 data += "\r\n";
529 }
530 data += "\r\n";
531// qDebug() << ">>>>>>>> sending request" << this;
532// qDebug() << data;
533// qDebug(">>>>>>>");
534 d->socket->write(data);
535 d->state = ConnectSent;
536}
537
538void QHttpSocketEngine::slotSocketDisconnected()
539{
540}
541
542void QHttpSocketEngine::slotSocketReadNotification()
543{
544 Q_D(QHttpSocketEngine);
545 if (d->state != Connected && d->socket->bytesAvailable() == 0)
546 return;
547
548 if (d->state == Connected) {
549 // Forward as a read notification.
550 if (d->readNotificationEnabled)
551 emitReadNotification();
552 return;
553 }
554
555 if (d->state == ConnectSent) {
556 d->reply->d_func()->state = QHttpNetworkReplyPrivate::NothingDoneState;
557 d->state = ReadResponseHeader;
558 }
559
560 if (d->state == ReadResponseHeader) {
561 bool ok = readHttpHeader();
562 if (!ok) {
563 // protocol error, this isn't HTTP
564 d->socket->close();
565 setState(QAbstractSocket::UnconnectedState);
566 setError(error: QAbstractSocket::ProxyProtocolError, errorString: tr(s: "Did not receive HTTP response from proxy"));
567 emitConnectionNotification();
568 return;
569 }
570 if (d->state == ReadResponseHeader)
571 return; // readHttpHeader() was not done yet, need to wait for more header data
572 }
573
574 if (d->state == ReadResponseContent) {
575 qint64 skipped = d->socket->skip(maxSize: d->pendingResponseData);
576 if (skipped == -1) {
577 d->socket->disconnectFromHost();
578 emitWriteNotification();
579 return;
580 }
581 d->pendingResponseData -= uint(skipped);
582 if (d->pendingResponseData > 0)
583 return;
584 if (d->reply->d_func()->statusCode == 407)
585 d->state = SendAuthentication;
586 }
587
588 int statusCode = d->reply->statusCode();
589 QAuthenticatorPrivate *priv = nullptr;
590 if (statusCode == 200) {
591 d->state = Connected;
592 setLocalAddress(d->socket->localAddress());
593 setLocalPort(d->socket->localPort());
594 d->inboundStreamCount = d->outboundStreamCount = 1;
595 setState(QAbstractSocket::ConnectedState);
596 d->authenticator.detach();
597 priv = QAuthenticatorPrivate::getPrivate(auth&: d->authenticator);
598 priv->hasFailed = false;
599 } else if (statusCode == 407) {
600 if (d->authenticator.isNull())
601 d->authenticator.detach();
602 priv = QAuthenticatorPrivate::getPrivate(auth&: d->authenticator);
603
604 if (d->credentialsSent && priv->phase != QAuthenticatorPrivate::Phase2) {
605 // Remember that (e.g.) NTLM is two-phase, so only reset when the authentication is not currently in progress.
606 //407 response again means the provided username/password were invalid.
607 d->authenticator = QAuthenticator(); //this is needed otherwise parseHttpResponse won't set the state, and then signal isn't emitted.
608 d->authenticator.detach();
609 priv = QAuthenticatorPrivate::getPrivate(auth&: d->authenticator);
610 priv->hasFailed = true;
611 }
612
613 priv->parseHttpResponse(d->reply->header(), isProxy: true, host: d->proxy.hostName());
614
615 if (priv->phase == QAuthenticatorPrivate::Invalid) {
616 // problem parsing the reply
617 d->socket->close();
618 setState(QAbstractSocket::UnconnectedState);
619 setError(error: QAbstractSocket::ProxyProtocolError, errorString: tr(s: "Error parsing authentication request from proxy"));
620 emitConnectionNotification();
621 return;
622 }
623
624 bool willClose;
625 QByteArray proxyConnectionHeader = d->reply->headerField(name: "Proxy-Connection");
626 // Although most proxies use the unofficial Proxy-Connection header, the Connection header
627 // from http spec is also allowed.
628 if (proxyConnectionHeader.isEmpty())
629 proxyConnectionHeader = d->reply->headerField(name: "Connection");
630 if (proxyConnectionHeader.compare(c: "close", cs: Qt::CaseInsensitive) == 0) {
631 willClose = true;
632 } else if (proxyConnectionHeader.compare(c: "keep-alive", cs: Qt::CaseInsensitive) == 0) {
633 willClose = false;
634 } else {
635 // no Proxy-Connection header, so use the default
636 // HTTP 1.1's default behaviour is to keep persistent connections
637 // HTTP 1.0 or earlier, so we expect the server to close
638 willClose = (d->reply->majorVersion() * 0x100 + d->reply->minorVersion()) <= 0x0100;
639 }
640
641 if (willClose) {
642 // the server will disconnect, so let's avoid receiving an error
643 // especially since the signal below may trigger a new event loop
644 d->socket->disconnectFromHost();
645 d->socket->readAll();
646 //We're done with the reply and need to reset it for the next connection
647 delete d->reply;
648 d->reply = new QHttpNetworkReply(QUrl(), this);
649 }
650
651 if (priv->phase == QAuthenticatorPrivate::Done)
652 proxyAuthenticationRequired(proxy: d->proxy, authenticator: &d->authenticator);
653 // priv->phase will get reset to QAuthenticatorPrivate::Start if the authenticator got modified in the signal above.
654 if (priv->phase == QAuthenticatorPrivate::Done) {
655 setError(error: QAbstractSocket::ProxyAuthenticationRequiredError, errorString: tr(s: "Authentication required"));
656 d->socket->disconnectFromHost();
657 } else {
658 // close the connection if it isn't already and reconnect using the chosen authentication method
659 d->state = SendAuthentication;
660 if (willClose) {
661 d->socket->connectToHost(hostName: d->proxy.hostName(), port: d->proxy.port());
662 } else {
663 // send the HTTP CONNECT again
664 slotSocketConnected();
665 }
666 return;
667 }
668 } else {
669 d->socket->close();
670 setState(QAbstractSocket::UnconnectedState);
671 if (statusCode == 403 || statusCode == 405) {
672 // 403 Forbidden
673 // 405 Method Not Allowed
674 setError(error: QAbstractSocket::SocketAccessError, errorString: tr(s: "Proxy denied connection"));
675 } else if (statusCode == 404) {
676 // 404 Not Found: host lookup error
677 setError(error: QAbstractSocket::HostNotFoundError, errorString: QAbstractSocket::tr(s: "Host not found"));
678 } else if (statusCode == 503) {
679 // 503 Service Unavailable: Connection Refused
680 setError(error: QAbstractSocket::ConnectionRefusedError, errorString: QAbstractSocket::tr(s: "Connection refused"));
681 } else {
682 // Some other reply
683 //qWarning("UNEXPECTED RESPONSE: [%s]", responseHeader.toString().toLatin1().data());
684 setError(error: QAbstractSocket::ProxyProtocolError, errorString: tr(s: "Error communicating with HTTP proxy"));
685 }
686 }
687
688 // The handshake is done; notify that we're connected (or failed to connect)
689 emitConnectionNotification();
690}
691
692bool QHttpSocketEngine::readHttpHeader()
693{
694 Q_D(QHttpSocketEngine);
695
696 if (d->state != ReadResponseHeader)
697 return false;
698
699 bool ok = true;
700 if (d->reply->d_func()->state == QHttpNetworkReplyPrivate::NothingDoneState) {
701 // do not keep old content sizes, status etc. around
702 d->reply->d_func()->clearHttpLayerInformation();
703 d->reply->d_func()->state = QHttpNetworkReplyPrivate::ReadingStatusState;
704 }
705 if (d->reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingStatusState) {
706 ok = d->reply->d_func()->readStatus(socket: d->socket) != -1;
707 if (ok && d->reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingStatusState)
708 return true; //Not done parsing headers yet, wait for more data
709 }
710 if (ok && d->reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingHeaderState) {
711 ok = d->reply->d_func()->readHeader(socket: d->socket) != -1;
712 if (ok && d->reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingHeaderState)
713 return true; //Not done parsing headers yet, wait for more data
714 }
715 if (ok) {
716 bool contentLengthOk;
717 int contentLength = d->reply->headerField(name: "Content-Length").toInt(ok: &contentLengthOk);
718 if (contentLengthOk && contentLength > 0)
719 d->pendingResponseData = contentLength;
720 d->state = ReadResponseContent; // we are done reading the header
721 }
722 return ok;
723}
724
725void QHttpSocketEngine::slotSocketBytesWritten()
726{
727 Q_D(QHttpSocketEngine);
728 if (d->state == Connected && d->writeNotificationEnabled)
729 emitWriteNotification();
730}
731
732void QHttpSocketEngine::slotSocketError(QAbstractSocket::SocketError error)
733{
734 Q_D(QHttpSocketEngine);
735
736 if (d->state != Connected) {
737 // we are in proxy handshaking stages
738 if (error == QAbstractSocket::HostNotFoundError)
739 setError(error: QAbstractSocket::ProxyNotFoundError, errorString: tr(s: "Proxy server not found"));
740 else if (error == QAbstractSocket::ConnectionRefusedError)
741 setError(error: QAbstractSocket::ProxyConnectionRefusedError, errorString: tr(s: "Proxy connection refused"));
742 else if (error == QAbstractSocket::SocketTimeoutError)
743 setError(error: QAbstractSocket::ProxyConnectionTimeoutError, errorString: tr(s: "Proxy server connection timed out"));
744 else if (error == QAbstractSocket::RemoteHostClosedError)
745 setError(error: QAbstractSocket::ProxyConnectionClosedError, errorString: tr(s: "Proxy connection closed prematurely"));
746 else
747 setError(error, errorString: d->socket->errorString());
748 emitConnectionNotification();
749 return;
750 }
751
752 // We're connected
753 if (error == QAbstractSocket::SocketTimeoutError)
754 return; // ignore this error
755
756 d->state = None;
757 setError(error, errorString: d->socket->errorString());
758 if (error != QAbstractSocket::RemoteHostClosedError)
759 qDebug() << "QHttpSocketEngine::slotSocketError: got weird error =" << error;
760 //read notification needs to always be emitted, otherwise the higher layer doesn't get the disconnected signal
761 emitReadNotification();
762}
763
764void QHttpSocketEngine::slotSocketStateChanged(QAbstractSocket::SocketState state)
765{
766 Q_UNUSED(state);
767}
768
769void QHttpSocketEngine::emitPendingReadNotification()
770{
771 Q_D(QHttpSocketEngine);
772 d->readNotificationPending = false;
773 if (d->readNotificationEnabled)
774 readNotification();
775}
776
777void QHttpSocketEngine::emitPendingWriteNotification()
778{
779 Q_D(QHttpSocketEngine);
780 d->writeNotificationPending = false;
781 if (d->writeNotificationEnabled)
782 writeNotification();
783}
784
785void QHttpSocketEngine::emitPendingConnectionNotification()
786{
787 Q_D(QHttpSocketEngine);
788 d->connectionNotificationPending = false;
789 connectionNotification();
790}
791
792void QHttpSocketEngine::emitReadNotification()
793{
794 Q_D(QHttpSocketEngine);
795 // if there is a connection notification pending we have to emit the readNotification
796 // incase there is connection error. This is only needed for Windows, but it does not
797 // hurt in other cases.
798 if ((d->readNotificationEnabled && !d->readNotificationPending) || d->connectionNotificationPending) {
799 d->readNotificationPending = true;
800 QMetaObject::invokeMethod(obj: this, member: "emitPendingReadNotification", type: Qt::QueuedConnection);
801 }
802}
803
804void QHttpSocketEngine::emitWriteNotification()
805{
806 Q_D(QHttpSocketEngine);
807 if (d->writeNotificationEnabled && !d->writeNotificationPending) {
808 d->writeNotificationPending = true;
809 QMetaObject::invokeMethod(obj: this, member: "emitPendingWriteNotification", type: Qt::QueuedConnection);
810 }
811}
812
813void QHttpSocketEngine::emitConnectionNotification()
814{
815 Q_D(QHttpSocketEngine);
816 if (!d->connectionNotificationPending) {
817 d->connectionNotificationPending = true;
818 QMetaObject::invokeMethod(obj: this, member: "emitPendingConnectionNotification", type: Qt::QueuedConnection);
819 }
820}
821
822QHttpSocketEnginePrivate::QHttpSocketEnginePrivate()
823 : readNotificationEnabled(false)
824 , writeNotificationEnabled(false)
825 , exceptNotificationEnabled(false)
826 , readNotificationPending(false)
827 , writeNotificationPending(false)
828 , connectionNotificationPending(false)
829 , credentialsSent(false)
830 , pendingResponseData(0)
831{
832 socket = nullptr;
833 reply = nullptr;
834 state = QHttpSocketEngine::None;
835}
836
837QHttpSocketEnginePrivate::~QHttpSocketEnginePrivate()
838{
839}
840
841QAbstractSocketEngine *QHttpSocketEngineHandler::createSocketEngine(QAbstractSocket::SocketType socketType,
842 const QNetworkProxy &proxy,
843 QObject *parent)
844{
845 if (socketType != QAbstractSocket::TcpSocket)
846 return nullptr;
847
848 // proxy type must have been resolved by now
849 if (proxy.type() != QNetworkProxy::HttpProxy)
850 return nullptr;
851
852 // we only accept active sockets
853 if (!qobject_cast<QAbstractSocket *>(object: parent))
854 return nullptr;
855
856 QHttpSocketEngine *engine = new QHttpSocketEngine(parent);
857 engine->setProxy(proxy);
858 return engine;
859}
860
861QAbstractSocketEngine *QHttpSocketEngineHandler::createSocketEngine(qintptr, QObject *)
862{
863 return nullptr;
864}
865
866QT_END_NAMESPACE
867
868#endif // !QT_NO_NETWORKPROXY
869

source code of qtbase/src/network/socket/qhttpsocketengine.cpp