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

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