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 // When already enabled, we still need to check for
417 // buffered data. The inner socket may have data in its user-space
418 // buffer that won't trigger a new OS-level read notification.
419 // Without this check, partial reads leave data stranded forever.
420 if (enable && bytesAvailable() > 0)
421 emitReadNotification();
422 return;
423 }
424
425 d->readNotificationEnabled = enable;
426 if (enable) {
427 // Enabling read notification can trigger a notification.
428 if (bytesAvailable()) {
429 slotSocketReadNotification();
430 } else if (d->socket && d->socket->state() == QAbstractSocket::UnconnectedState) {
431 emitReadNotification();
432 }
433 }
434}
435
436bool QHttpSocketEngine::isWriteNotificationEnabled() const
437{
438 Q_D(const QHttpSocketEngine);
439 return d->writeNotificationEnabled;
440}
441
442void QHttpSocketEngine::setWriteNotificationEnabled(bool enable)
443{
444 Q_D(QHttpSocketEngine);
445 d->writeNotificationEnabled = enable;
446 if (enable && d->state == Connected && d->socket->state() == QAbstractSocket::ConnectedState)
447 QMetaObject::invokeMethod(this, "writeNotification", Qt::QueuedConnection);
448}
449
450bool QHttpSocketEngine::isExceptionNotificationEnabled() const
451{
452 Q_D(const QHttpSocketEngine);
453 return d->exceptNotificationEnabled;
454}
455
456void QHttpSocketEngine::setExceptionNotificationEnabled(bool enable)
457{
458 Q_D(QHttpSocketEngine);
459 d->exceptNotificationEnabled = enable;
460}
461
462void QHttpSocketEngine::slotSocketConnected()
463{
464 Q_D(QHttpSocketEngine);
465
466 // Send the greeting.
467 const char method[] = "CONNECT";
468 QByteArray peerAddress = d->peerName.isEmpty() ?
469 d->peerAddress.toString().toLatin1() :
470 QUrl::toAce(d->peerName);
471 QByteArray path = peerAddress + ':' + QByteArray::number(d->peerPort);
472 QByteArray data = method;
473 data += ' ';
474 data += path;
475 data += " HTTP/1.1\r\n";
476 data += "Proxy-Connection: keep-alive\r\n";
477 data += "Host: " + peerAddress + "\r\n";
478 const auto headers = d->proxy.headers();
479 if (!headers.contains(QHttpHeaders::WellKnownHeader::UserAgent))
480 data += "User-Agent: Mozilla/5.0\r\n";
481 for (qsizetype i = 0; i < headers.size(); ++i) {
482 const auto name = headers.nameAt(i);
483 data += QByteArrayView(name.data(), name.size()) + ": "
484 + headers.valueAt(i) + "\r\n";
485 }
486 QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(d->authenticator);
487 //qDebug() << "slotSocketConnected: priv=" << priv << (priv ? (int)priv->method : -1);
488 if (priv && priv->method != QAuthenticatorPrivate::None) {
489 d->credentialsSent = true;
490 data += "Proxy-Authorization: " + priv->calculateResponse(method, path, d->proxy.hostName());
491 data += "\r\n";
492 }
493 data += "\r\n";
494// qDebug() << ">>>>>>>> sending request" << this;
495// qDebug() << data;
496// qDebug(">>>>>>>");
497 d->socket->write(data);
498 d->state = ConnectSent;
499}
500
501void QHttpSocketEngine::slotSocketDisconnected()
502{
503}
504
505void QHttpSocketEngine::slotSocketReadNotification()
506{
507 Q_D(QHttpSocketEngine);
508 if (d->state != Connected && d->socket->bytesAvailable() == 0)
509 return;
510
511 if (d->state == Connected) {
512 // Forward as a read notification.
513 if (d->readNotificationEnabled)
514 emitReadNotification();
515 return;
516 }
517
518 if (d->state == ConnectSent) {
519 d->reply->d_func()->state = QHttpNetworkReplyPrivate::NothingDoneState;
520 d->state = ReadResponseHeader;
521 }
522
523 if (d->state == ReadResponseHeader) {
524 bool ok = readHttpHeader();
525 if (!ok) {
526 // protocol error, this isn't HTTP
527 d->socket->close();
528 setState(QAbstractSocket::UnconnectedState);
529 setError(QAbstractSocket::ProxyProtocolError, tr("Did not receive HTTP response from proxy"));
530 emitConnectionNotification();
531 return;
532 }
533 if (d->state == ReadResponseHeader)
534 return; // readHttpHeader() was not done yet, need to wait for more header data
535 }
536
537 if (d->state == ReadResponseContent) {
538 qint64 skipped = d->socket->skip(d->pendingResponseData);
539 if (skipped == -1) {
540 d->socket->disconnectFromHost();
541 emitWriteNotification();
542 return;
543 }
544 d->pendingResponseData -= uint(skipped);
545 if (d->pendingResponseData > 0)
546 return;
547 if (d->reply->statusCode() == 407)
548 d->state = SendAuthentication;
549 }
550
551 int statusCode = d->reply->statusCode();
552 QAuthenticatorPrivate *priv = nullptr;
553 if (statusCode == 200) {
554 d->state = Connected;
555 setLocalAddress(d->socket->localAddress());
556 setLocalPort(d->socket->localPort());
557 d->inboundStreamCount = d->outboundStreamCount = 1;
558 setState(QAbstractSocket::ConnectedState);
559 d->authenticator.detach();
560 priv = QAuthenticatorPrivate::getPrivate(d->authenticator);
561 priv->hasFailed = false;
562 } else if (statusCode == 407) {
563 if (d->authenticator.isNull())
564 d->authenticator.detach();
565 priv = QAuthenticatorPrivate::getPrivate(d->authenticator);
566
567 const auto headers = d->reply->header();
568 priv->parseHttpResponse(headers, true, 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(QAbstractSocket::ProxyProtocolError, tr("Error parsing authentication request from proxy"));
575 emitConnectionNotification();
576 return;
577 }
578
579 if (priv->phase == QAuthenticatorPrivate::Done
580 || (priv->phase == QAuthenticatorPrivate::Start
581 && (priv->method == QAuthenticatorPrivate::Ntlm
582 || priv->method == QAuthenticatorPrivate::Negotiate))) {
583 if (priv->phase == QAuthenticatorPrivate::Start)
584 priv->phase = QAuthenticatorPrivate::Phase1;
585 bool credentialsWasSent = d->credentialsSent;
586 if (d->credentialsSent) {
587 // Remember that (e.g.) NTLM is two-phase, so only reset when the authentication is
588 // not currently in progress. 407 response again means the provided
589 // username/password were invalid.
590 d->authenticator.detach();
591 priv = QAuthenticatorPrivate::getPrivate(d->authenticator);
592 priv->hasFailed = true;
593 d->credentialsSent = false;
594 priv->phase = QAuthenticatorPrivate::Done;
595 }
596 if ((priv->method != QAuthenticatorPrivate::Ntlm
597 && priv->method != QAuthenticatorPrivate::Negotiate)
598 || credentialsWasSent)
599 proxyAuthenticationRequired(d->proxy, &d->authenticator);
600 }
601
602 bool willClose;
603 QByteArray proxyConnectionHeader = d->reply->headerField("Proxy-Connection");
604 // Although most proxies use the unofficial Proxy-Connection header, the Connection header
605 // from http spec is also allowed.
606 if (proxyConnectionHeader.isEmpty())
607 proxyConnectionHeader = d->reply->headerField("Connection");
608 if (proxyConnectionHeader.compare("close", Qt::CaseInsensitive) == 0) {
609 willClose = true;
610 } else if (proxyConnectionHeader.compare("keep-alive", Qt::CaseInsensitive) == 0) {
611 willClose = false;
612 } else {
613 // no Proxy-Connection header, so use the default
614 // HTTP 1.1's default behaviour is to keep persistent connections
615 // HTTP 1.0 or earlier, so we expect the server to close
616 willClose = (d->reply->majorVersion() * 0x100 + d->reply->minorVersion()) <= 0x0100;
617 }
618
619 if (willClose) {
620 // the server will disconnect, so let's avoid receiving an error
621 // especially since the signal below may trigger a new event loop
622 d->socket->disconnectFromHost();
623 d->socket->readAll();
624 //We're done with the reply and need to reset it for the next connection
625 delete d->reply;
626 d->reply = new QHttpNetworkReply(QUrl(), this);
627 }
628
629 if (priv->phase == QAuthenticatorPrivate::Done) {
630 d->authenticator = QAuthenticator();
631 setError(QAbstractSocket::ProxyAuthenticationRequiredError, tr("Authentication required"));
632 d->socket->disconnectFromHost();
633 } else {
634 // close the connection if it isn't already and reconnect using the chosen authentication method
635 d->state = SendAuthentication;
636 if (willClose) {
637 d->socket->connectToHost(d->proxy.hostName(), d->proxy.port());
638 } else {
639 // send the HTTP CONNECT again
640 slotSocketConnected();
641 }
642 return;
643 }
644 } else {
645 d->socket->close();
646 setState(QAbstractSocket::UnconnectedState);
647 if (statusCode == 403 || statusCode == 405) {
648 // 403 Forbidden
649 // 405 Method Not Allowed
650 setError(QAbstractSocket::SocketAccessError, tr("Proxy denied connection"));
651 } else if (statusCode == 404) {
652 // 404 Not Found: host lookup error
653 setError(QAbstractSocket::HostNotFoundError, QAbstractSocket::tr("Host not found"));
654 } else if (statusCode == 503) {
655 // 503 Service Unavailable: Connection Refused
656 setError(QAbstractSocket::ConnectionRefusedError, QAbstractSocket::tr("Connection refused"));
657 } else {
658 // Some other reply
659 //qWarning("UNEXPECTED RESPONSE: [%s]", responseHeader.toString().toLatin1().data());
660 setError(QAbstractSocket::ProxyProtocolError, tr("Error communicating with HTTP proxy"));
661 }
662 }
663
664 // The handshake is done; notify that we're connected (or failed to connect)
665 emitConnectionNotification();
666}
667
668bool QHttpSocketEngine::readHttpHeader()
669{
670 Q_D(QHttpSocketEngine);
671
672 if (d->state != ReadResponseHeader)
673 return false;
674
675 bool ok = true;
676 if (d->reply->d_func()->state == QHttpNetworkReplyPrivate::NothingDoneState) {
677 // do not keep old content sizes, status etc. around
678 d->reply->d_func()->clearHttpLayerInformation();
679 d->reply->d_func()->state = QHttpNetworkReplyPrivate::ReadingStatusState;
680 }
681 if (d->reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingStatusState) {
682 ok = d->reply->d_func()->readStatus(d->socket) != -1;
683 if (ok && d->reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingStatusState)
684 return true; //Not done parsing headers yet, wait for more data
685 }
686 if (ok && d->reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingHeaderState) {
687 ok = d->reply->d_func()->readHeader(d->socket) != -1;
688 if (ok && d->reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingHeaderState)
689 return true; //Not done parsing headers yet, wait for more data
690 }
691 if (ok) {
692 bool contentLengthOk;
693 int contentLength = d->reply->headerField("Content-Length").toInt(&contentLengthOk);
694 if (contentLengthOk && contentLength > 0)
695 d->pendingResponseData = contentLength;
696 d->state = ReadResponseContent; // we are done reading the header
697 }
698 return ok;
699}
700
701void QHttpSocketEngine::slotSocketBytesWritten()
702{
703 Q_D(QHttpSocketEngine);
704 if (d->state == Connected && d->writeNotificationEnabled)
705 emitWriteNotification();
706}
707
708void QHttpSocketEngine::slotSocketError(QAbstractSocket::SocketError error)
709{
710 Q_D(QHttpSocketEngine);
711
712 if (d->state != Connected) {
713 // we are in proxy handshaking stages
714 if (error == QAbstractSocket::HostNotFoundError)
715 setError(QAbstractSocket::ProxyNotFoundError, tr("Proxy server not found"));
716 else if (error == QAbstractSocket::ConnectionRefusedError)
717 setError(QAbstractSocket::ProxyConnectionRefusedError, tr("Proxy connection refused"));
718 else if (error == QAbstractSocket::SocketTimeoutError)
719 setError(QAbstractSocket::ProxyConnectionTimeoutError, tr("Proxy server connection timed out"));
720 else if (error == QAbstractSocket::RemoteHostClosedError)
721 setError(QAbstractSocket::ProxyConnectionClosedError, tr("Proxy connection closed prematurely"));
722 else
723 setError(error, d->socket->errorString());
724 emitConnectionNotification();
725 return;
726 }
727
728 // We're connected
729 if (error == QAbstractSocket::SocketTimeoutError)
730 return; // ignore this error
731
732 d->state = None;
733 setError(error, d->socket->errorString());
734 if (error != QAbstractSocket::RemoteHostClosedError)
735 qDebug() << "QHttpSocketEngine::slotSocketError: got weird error =" << error;
736 //read notification needs to always be emitted, otherwise the higher layer doesn't get the disconnected signal
737 emitReadNotification();
738}
739
740void QHttpSocketEngine::slotSocketStateChanged(QAbstractSocket::SocketState state)
741{
742 Q_UNUSED(state);
743}
744
745void QHttpSocketEngine::emitPendingReadNotification()
746{
747 Q_D(QHttpSocketEngine);
748 d->readNotificationPending = false;
749 if (d->readNotificationEnabled)
750 readNotification();
751}
752
753void QHttpSocketEngine::emitPendingWriteNotification()
754{
755 Q_D(QHttpSocketEngine);
756 d->writeNotificationPending = false;
757 if (d->writeNotificationEnabled)
758 writeNotification();
759}
760
761void QHttpSocketEngine::emitPendingConnectionNotification()
762{
763 Q_D(QHttpSocketEngine);
764 d->connectionNotificationPending = false;
765 connectionNotification();
766}
767
768void QHttpSocketEngine::emitReadNotification()
769{
770 Q_D(QHttpSocketEngine);
771 // if there is a connection notification pending we have to emit the readNotification
772 // in case there is connection error. This is only needed for Windows, but it does not
773 // hurt in other cases.
774 if ((d->readNotificationEnabled && !d->readNotificationPending) || d->connectionNotificationPending) {
775 d->readNotificationPending = true;
776 QMetaObject::invokeMethod(this, "emitPendingReadNotification", Qt::QueuedConnection);
777 }
778}
779
780void QHttpSocketEngine::emitWriteNotification()
781{
782 Q_D(QHttpSocketEngine);
783 if (d->writeNotificationEnabled && !d->writeNotificationPending) {
784 d->writeNotificationPending = true;
785 QMetaObject::invokeMethod(this, "emitPendingWriteNotification", Qt::QueuedConnection);
786 }
787}
788
789void QHttpSocketEngine::emitConnectionNotification()
790{
791 Q_D(QHttpSocketEngine);
792 if (!d->connectionNotificationPending) {
793 d->connectionNotificationPending = true;
794 QMetaObject::invokeMethod(this, "emitPendingConnectionNotification", Qt::QueuedConnection);
795 }
796}
797
798QHttpSocketEnginePrivate::QHttpSocketEnginePrivate()
805 , credentialsSent(false)
806 , pendingResponseData(0)
807{
808 socket = nullptr;
809 reply = nullptr;
810 state = QHttpSocketEngine::None;
811}
812
816
817QAbstractSocketEngine *QHttpSocketEngineHandler::createSocketEngine(QAbstractSocket::SocketType socketType,
818 const QNetworkProxy &proxy,
819 QObject *parent)
820{
821 if (socketType != QAbstractSocket::TcpSocket)
822 return nullptr;
823
824 // proxy type must have been resolved by now
825 if (proxy.type() != QNetworkProxy::HttpProxy)
826 return nullptr;
827
828 // we only accept active sockets
829 if (!qobject_cast<QAbstractSocket *>(parent))
830 return nullptr;
831
832 QHttpSocketEngine *engine = new QHttpSocketEngine(parent);
833 engine->setProxy(proxy);
834 return engine;
835}
836
837QAbstractSocketEngine *QHttpSocketEngineHandler::createSocketEngine(qintptr, QObject *)
838{
839 return nullptr;
840}
841
842QT_END_NAMESPACE
843
844#endif // !QT_NO_NETWORKPROXY
845
846#include "moc_qhttpsocketengine_p.cpp"
Combined button and popup list for selecting options.