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