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
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 "qdeadlinetimer.h"
12
13#if !defined(QT_NO_NETWORKPROXY)
14#include <qdebug.h>
15
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(d->socket, SIGNAL(connected()),
48 this, SLOT(slotSocketConnected()),
49 Qt::DirectConnection);
50 connect(d->socket, SIGNAL(disconnected()),
51 this, SLOT(slotSocketDisconnected()),
52 Qt::DirectConnection);
53 connect(d->socket, SIGNAL(readyRead()),
54 this, SLOT(slotSocketReadNotification()),
55 Qt::DirectConnection);
56 connect(d->socket, SIGNAL(bytesWritten(qint64)),
57 this, SLOT(slotSocketBytesWritten()),
58 Qt::DirectConnection);
59 connect(d->socket, SIGNAL(errorOccurred(QAbstractSocket::SocketError)),
60 this, SLOT(slotSocketError(QAbstractSocket::SocketError)),
61 Qt::DirectConnection);
62 connect(d->socket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
63 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("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(d->proxy.hostName(), 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("Operation is not supported");
155 setError(QAbstractSocket::UnsupportedSocketOperationError, "Unsupported socket operation"_L1);
156 return false;
157}
158
159bool QHttpSocketEngine::listen(int backlog)
160{
161 Q_UNUSED(backlog);
162 qWarning("Operation is not supported");
163 setError(QAbstractSocket::UnsupportedSocketOperationError, "Unsupported socket operation"_L1);
164 return false;
165}
166
167qintptr QHttpSocketEngine::accept()
168{
169 qWarning("Operation is not supported");
170 setError(QAbstractSocket::UnsupportedSocketOperationError, "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(QAbstractSocket::RemoteHostClosedError, "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("Operation is not supported");
224 setError(QAbstractSocket::UnsupportedSocketOperationError, "Unsupported socket operation"_L1);
225 return false;
226}
227
228bool QHttpSocketEngine::leaveMulticastGroup(const QHostAddress &,
229 const QNetworkInterface &)
230{
231 qWarning("Operation is not supported");
232 setError(QAbstractSocket::UnsupportedSocketOperationError, "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("Operation is not supported");
244 setError(QAbstractSocket::UnsupportedSocketOperationError, "Unsupported socket operation"_L1);
245 return false;
246}
247#endif // QT_NO_NETWORKINTERFACE
248
249bool QHttpSocketEngine::hasPendingDatagrams() const
250{
251 qWarning("Operation is not supported");
252 return false;
253}
254
255qint64 QHttpSocketEngine::pendingDatagramSize() const
256{
257 qWarning("Operation is not supported");
258 return -1;
259}
260#endif // QT_NO_UDPSOCKET
261
262qint64 QHttpSocketEngine::readDatagram(char *, qint64, QIpPacketHeader *, PacketHeaderOptions)
263{
264 qWarning("Operation is not supported");
265 setError(QAbstractSocket::UnsupportedSocketOperationError, "Unsupported socket operation"_L1);
266 return -1;
267}
268
269qint64 QHttpSocketEngine::writeDatagram(const char *, qint64, const QIpPacketHeader &)
270{
271 qWarning("Operation is not supported");
272 setError(QAbstractSocket::UnsupportedSocketOperationError, "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(QAbstractSocket::LowDelayOption).toInt();
293 if (option == QAbstractSocketEngine::KeepAliveOption)
294 return d->socket->socketOption(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(QAbstractSocket::LowDelayOption, value);
306 if (option == QAbstractSocketEngine::KeepAliveOption)
307 d->socket->setSocketOption(QAbstractSocket::KeepAliveOption, value);
308 return true;
309 }
310 return false;
311}
312
313bool QHttpSocketEngine::waitForRead(QDeadlineTimer deadline, bool *timedOut)
314{
315 Q_D(const QHttpSocketEngine);
316
317 if (!d->socket || d->socket->state() == QAbstractSocket::UnconnectedState)
318 return false;
319
320 // Wait for more data if nothing is available.
321 if (!d->socket->bytesAvailable()) {
322 if (!d->socket->waitForReadyRead(deadline.remainingTime())) {
323 if (d->socket->state() == QAbstractSocket::UnconnectedState)
324 return true;
325 setError(d->socket->error(), d->socket->errorString());
326 if (timedOut && d->socket->error() == QAbstractSocket::SocketTimeoutError)
327 *timedOut = true;
328 return false;
329 }
330 }
331
332 waitForProtocolHandshake(deadline);
333
334 // Report any error that may occur.
335 if (d->state != Connected) {
336 setError(d->socket->error(), d->socket->errorString());
337 if (timedOut && d->socket->error() == QAbstractSocket::SocketTimeoutError)
338 *timedOut = true;
339 return false;
340 }
341 return true;
342}
343
344bool QHttpSocketEngine::waitForWrite(QDeadlineTimer deadline, bool *timedOut)
345{
346 Q_D(const QHttpSocketEngine);
347
348 // If we're connected, just forward the call.
349 if (d->state == Connected) {
350 if (d->socket->bytesToWrite()) {
351 if (!d->socket->waitForBytesWritten(deadline.remainingTime())) {
352 if (d->socket->error() == QAbstractSocket::SocketTimeoutError && timedOut)
353 *timedOut = true;
354 return false;
355 }
356 }
357 return true;
358 }
359
360 waitForProtocolHandshake(deadline);
361
362 // Report any error that may occur.
363 if (d->state != Connected) {
364// setError(d->socket->error(), d->socket->errorString());
365 if (timedOut && d->socket->error() == QAbstractSocket::SocketTimeoutError)
366 *timedOut = true;
367 }
368
369 return true;
370}
371
372bool QHttpSocketEngine::waitForReadOrWrite(bool *readyToRead, bool *readyToWrite,
373 bool checkRead, bool checkWrite,
374 QDeadlineTimer deadline, bool *timedOut)
375{
376 Q_UNUSED(checkRead);
377
378 if (!checkWrite) {
379 // Not interested in writing? Then we wait for read notifications.
380 bool canRead = waitForRead(deadline, timedOut);
381 if (readyToRead)
382 *readyToRead = canRead;
383 return canRead;
384 }
385
386 // Interested in writing? Then we wait for write notifications.
387 bool canWrite = waitForWrite(deadline, timedOut);
388 if (readyToWrite)
389 *readyToWrite = canWrite;
390 return canWrite;
391}
392
393void QHttpSocketEngine::waitForProtocolHandshake(QDeadlineTimer deadline) const
394{
395 Q_D(const QHttpSocketEngine);
396
397 // If we're not connected yet, wait until we are (and until bytes have
398 // been received, i.e. the socket has connected, we have sent the
399 // greeting, and then received the response), or until an error occurs.
400 while (d->state != Connected && d->socket->waitForReadyRead(deadline.remainingTime())) {
401 // Loop while the protocol handshake is taking place.
402 }
403}
404
405bool QHttpSocketEngine::isReadNotificationEnabled() const
406{
407 Q_D(const QHttpSocketEngine);
408 return d->readNotificationEnabled;
409}
410
411void QHttpSocketEngine::setReadNotificationEnabled(bool enable)
412{
413 Q_D(QHttpSocketEngine);
414 if (d->readNotificationEnabled == enable)
415 return;
416
417 d->readNotificationEnabled = enable;
418 if (enable) {
419 // Enabling read notification can trigger a notification.
420 if (bytesAvailable()) {
421 slotSocketReadNotification();
422 } else if (d->socket && d->socket->state() == QAbstractSocket::UnconnectedState) {
423 emitReadNotification();
424 }
425 }
426}
427
428bool QHttpSocketEngine::isWriteNotificationEnabled() const
429{
430 Q_D(const QHttpSocketEngine);
431 return d->writeNotificationEnabled;
432}
433
434void QHttpSocketEngine::setWriteNotificationEnabled(bool enable)
435{
436 Q_D(QHttpSocketEngine);
437 d->writeNotificationEnabled = enable;
438 if (enable && d->state == Connected && d->socket->state() == QAbstractSocket::ConnectedState)
439 QMetaObject::invokeMethod(this, "writeNotification", Qt::QueuedConnection);
440}
441
442bool QHttpSocketEngine::isExceptionNotificationEnabled() const
443{
444 Q_D(const QHttpSocketEngine);
445 return d->exceptNotificationEnabled;
446}
447
448void QHttpSocketEngine::setExceptionNotificationEnabled(bool enable)
449{
450 Q_D(QHttpSocketEngine);
451 d->exceptNotificationEnabled = enable;
452}
453
454void QHttpSocketEngine::slotSocketConnected()
455{
456 Q_D(QHttpSocketEngine);
457
458 // Send the greeting.
459 const char method[] = "CONNECT";
460 QByteArray peerAddress = d->peerName.isEmpty() ?
461 d->peerAddress.toString().toLatin1() :
462 QUrl::toAce(d->peerName);
463 QByteArray path = peerAddress + ':' + QByteArray::number(d->peerPort);
464 QByteArray data = method;
465 data += ' ';
466 data += path;
467 data += " HTTP/1.1\r\n";
468 data += "Proxy-Connection: keep-alive\r\n";
469 data += "Host: " + peerAddress + "\r\n";
470 const auto headers = d->proxy.headers();
471 if (!headers.contains(QHttpHeaders::WellKnownHeader::UserAgent))
472 data += "User-Agent: Mozilla/5.0\r\n";
473 for (qsizetype i = 0; i < headers.size(); ++i) {
474 const auto name = headers.nameAt(i);
475 data += QByteArrayView(name.data(), name.size()) + ": "
476 + headers.valueAt(i) + "\r\n";
477 }
478 QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(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, 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(QAbstractSocket::ProxyProtocolError, tr("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(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(d->authenticator);
553 priv->hasFailed = false;
554 } else if (statusCode == 407) {
555 if (d->authenticator.isNull())
556 d->authenticator.detach();
557 priv = QAuthenticatorPrivate::getPrivate(d->authenticator);
558
559 const auto headers = d->reply->header();
560 priv->parseHttpResponse(headers, true, d->proxy.hostName());
561
562 if (priv->phase == QAuthenticatorPrivate::Invalid) {
563 // problem parsing the reply
564 d->socket->close();
565 setState(QAbstractSocket::UnconnectedState);
566 setError(QAbstractSocket::ProxyProtocolError, tr("Error parsing authentication request from proxy"));
567 emitConnectionNotification();
568 return;
569 }
570
571 if (priv->phase == QAuthenticatorPrivate::Done
572 || (priv->phase == QAuthenticatorPrivate::Start
573 && (priv->method == QAuthenticatorPrivate::Ntlm
574 || priv->method == QAuthenticatorPrivate::Negotiate))) {
575 if (priv->phase == QAuthenticatorPrivate::Start)
576 priv->phase = QAuthenticatorPrivate::Phase1;
577 bool credentialsWasSent = d->credentialsSent;
578 if (d->credentialsSent) {
579 // Remember that (e.g.) NTLM is two-phase, so only reset when the authentication is
580 // not currently in progress. 407 response again means the provided
581 // username/password were invalid.
582 d->authenticator.detach();
583 priv = QAuthenticatorPrivate::getPrivate(d->authenticator);
584 priv->hasFailed = true;
585 d->credentialsSent = false;
586 priv->phase = QAuthenticatorPrivate::Done;
587 }
588 if ((priv->method != QAuthenticatorPrivate::Ntlm
589 && priv->method != QAuthenticatorPrivate::Negotiate)
590 || credentialsWasSent)
591 proxyAuthenticationRequired(d->proxy, &d->authenticator);
592 }
593
594 bool willClose;
595 QByteArray proxyConnectionHeader = d->reply->headerField("Proxy-Connection");
596 // Although most proxies use the unofficial Proxy-Connection header, the Connection header
597 // from http spec is also allowed.
598 if (proxyConnectionHeader.isEmpty())
599 proxyConnectionHeader = d->reply->headerField("Connection");
600 if (proxyConnectionHeader.compare("close", Qt::CaseInsensitive) == 0) {
601 willClose = true;
602 } else if (proxyConnectionHeader.compare("keep-alive", Qt::CaseInsensitive) == 0) {
603 willClose = false;
604 } else {
605 // no Proxy-Connection header, so use the default
606 // HTTP 1.1's default behaviour is to keep persistent connections
607 // HTTP 1.0 or earlier, so we expect the server to close
608 willClose = (d->reply->majorVersion() * 0x100 + d->reply->minorVersion()) <= 0x0100;
609 }
610
611 if (willClose) {
612 // the server will disconnect, so let's avoid receiving an error
613 // especially since the signal below may trigger a new event loop
614 d->socket->disconnectFromHost();
615 d->socket->readAll();
616 //We're done with the reply and need to reset it for the next connection
617 delete d->reply;
618 d->reply = new QHttpNetworkReply(QUrl(), this);
619 }
620
621 if (priv->phase == QAuthenticatorPrivate::Done) {
622 d->authenticator = QAuthenticator();
623 setError(QAbstractSocket::ProxyAuthenticationRequiredError, tr("Authentication required"));
624 d->socket->disconnectFromHost();
625 } else {
626 // close the connection if it isn't already and reconnect using the chosen authentication method
627 d->state = SendAuthentication;
628 if (willClose) {
629 d->socket->connectToHost(d->proxy.hostName(), d->proxy.port());
630 } else {
631 // send the HTTP CONNECT again
632 slotSocketConnected();
633 }
634 return;
635 }
636 } else {
637 d->socket->close();
638 setState(QAbstractSocket::UnconnectedState);
639 if (statusCode == 403 || statusCode == 405) {
640 // 403 Forbidden
641 // 405 Method Not Allowed
642 setError(QAbstractSocket::SocketAccessError, tr("Proxy denied connection"));
643 } else if (statusCode == 404) {
644 // 404 Not Found: host lookup error
645 setError(QAbstractSocket::HostNotFoundError, QAbstractSocket::tr("Host not found"));
646 } else if (statusCode == 503) {
647 // 503 Service Unavailable: Connection Refused
648 setError(QAbstractSocket::ConnectionRefusedError, QAbstractSocket::tr("Connection refused"));
649 } else {
650 // Some other reply
651 //qWarning("UNEXPECTED RESPONSE: [%s]", responseHeader.toString().toLatin1().data());
652 setError(QAbstractSocket::ProxyProtocolError, tr("Error communicating with HTTP proxy"));
653 }
654 }
655
656 // The handshake is done; notify that we're connected (or failed to connect)
657 emitConnectionNotification();
658}
659
660bool QHttpSocketEngine::readHttpHeader()
661{
662 Q_D(QHttpSocketEngine);
663
664 if (d->state != ReadResponseHeader)
665 return false;
666
667 bool ok = true;
668 if (d->reply->d_func()->state == QHttpNetworkReplyPrivate::NothingDoneState) {
669 // do not keep old content sizes, status etc. around
670 d->reply->d_func()->clearHttpLayerInformation();
671 d->reply->d_func()->state = QHttpNetworkReplyPrivate::ReadingStatusState;
672 }
673 if (d->reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingStatusState) {
674 ok = d->reply->d_func()->readStatus(d->socket) != -1;
675 if (ok && d->reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingStatusState)
676 return true; //Not done parsing headers yet, wait for more data
677 }
678 if (ok && d->reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingHeaderState) {
679 ok = d->reply->d_func()->readHeader(d->socket) != -1;
680 if (ok && d->reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingHeaderState)
681 return true; //Not done parsing headers yet, wait for more data
682 }
683 if (ok) {
684 bool contentLengthOk;
685 int contentLength = d->reply->headerField("Content-Length").toInt(&contentLengthOk);
686 if (contentLengthOk && contentLength > 0)
687 d->pendingResponseData = contentLength;
688 d->state = ReadResponseContent; // we are done reading the header
689 }
690 return ok;
691}
692
693void QHttpSocketEngine::slotSocketBytesWritten()
694{
695 Q_D(QHttpSocketEngine);
696 if (d->state == Connected && d->writeNotificationEnabled)
697 emitWriteNotification();
698}
699
700void QHttpSocketEngine::slotSocketError(QAbstractSocket::SocketError error)
701{
702 Q_D(QHttpSocketEngine);
703
704 if (d->state != Connected) {
705 // we are in proxy handshaking stages
706 if (error == QAbstractSocket::HostNotFoundError)
707 setError(QAbstractSocket::ProxyNotFoundError, tr("Proxy server not found"));
708 else if (error == QAbstractSocket::ConnectionRefusedError)
709 setError(QAbstractSocket::ProxyConnectionRefusedError, tr("Proxy connection refused"));
710 else if (error == QAbstractSocket::SocketTimeoutError)
711 setError(QAbstractSocket::ProxyConnectionTimeoutError, tr("Proxy server connection timed out"));
712 else if (error == QAbstractSocket::RemoteHostClosedError)
713 setError(QAbstractSocket::ProxyConnectionClosedError, tr("Proxy connection closed prematurely"));
714 else
715 setError(error, d->socket->errorString());
716 emitConnectionNotification();
717 return;
718 }
719
720 // We're connected
721 if (error == QAbstractSocket::SocketTimeoutError)
722 return; // ignore this error
723
724 d->state = None;
725 setError(error, d->socket->errorString());
726 if (error != QAbstractSocket::RemoteHostClosedError)
727 qDebug() << "QHttpSocketEngine::slotSocketError: got weird error =" << error;
728 //read notification needs to always be emitted, otherwise the higher layer doesn't get the disconnected signal
729 emitReadNotification();
730}
731
732void QHttpSocketEngine::slotSocketStateChanged(QAbstractSocket::SocketState state)
733{
734 Q_UNUSED(state);
735}
736
737void QHttpSocketEngine::emitPendingReadNotification()
738{
739 Q_D(QHttpSocketEngine);
740 d->readNotificationPending = false;
741 if (d->readNotificationEnabled)
742 readNotification();
743}
744
745void QHttpSocketEngine::emitPendingWriteNotification()
746{
747 Q_D(QHttpSocketEngine);
748 d->writeNotificationPending = false;
749 if (d->writeNotificationEnabled)
750 writeNotification();
751}
752
753void QHttpSocketEngine::emitPendingConnectionNotification()
754{
755 Q_D(QHttpSocketEngine);
756 d->connectionNotificationPending = false;
757 connectionNotification();
758}
759
760void QHttpSocketEngine::emitReadNotification()
761{
762 Q_D(QHttpSocketEngine);
763 // if there is a connection notification pending we have to emit the readNotification
764 // in case there is connection error. This is only needed for Windows, but it does not
765 // hurt in other cases.
766 if ((d->readNotificationEnabled && !d->readNotificationPending) || d->connectionNotificationPending) {
767 d->readNotificationPending = true;
768 QMetaObject::invokeMethod(this, "emitPendingReadNotification", Qt::QueuedConnection);
769 }
770}
771
772void QHttpSocketEngine::emitWriteNotification()
773{
774 Q_D(QHttpSocketEngine);
775 if (d->writeNotificationEnabled && !d->writeNotificationPending) {
776 d->writeNotificationPending = true;
777 QMetaObject::invokeMethod(this, "emitPendingWriteNotification", Qt::QueuedConnection);
778 }
779}
780
781void QHttpSocketEngine::emitConnectionNotification()
782{
783 Q_D(QHttpSocketEngine);
784 if (!d->connectionNotificationPending) {
785 d->connectionNotificationPending = true;
786 QMetaObject::invokeMethod(this, "emitPendingConnectionNotification", Qt::QueuedConnection);
787 }
788}
789
790QHttpSocketEnginePrivate::QHttpSocketEnginePrivate()
797 , credentialsSent(false)
798 , pendingResponseData(0)
799{
800 socket = nullptr;
801 reply = nullptr;
802 state = QHttpSocketEngine::None;
803}
804
808
809QAbstractSocketEngine *QHttpSocketEngineHandler::createSocketEngine(QAbstractSocket::SocketType socketType,
810 const QNetworkProxy &proxy,
811 QObject *parent)
812{
813 if (socketType != QAbstractSocket::TcpSocket)
814 return nullptr;
815
816 // proxy type must have been resolved by now
817 if (proxy.type() != QNetworkProxy::HttpProxy)
818 return nullptr;
819
820 // we only accept active sockets
821 if (!qobject_cast<QAbstractSocket *>(parent))
822 return nullptr;
823
824 QHttpSocketEngine *engine = new QHttpSocketEngine(parent);
825 engine->setProxy(proxy);
826 return engine;
827}
828
829QAbstractSocketEngine *QHttpSocketEngineHandler::createSocketEngine(qintptr, QObject *)
830{
831 return nullptr;
832}
833
834QT_END_NAMESPACE
835
836#endif // !QT_NO_NETWORKPROXY
837
838#include "moc_qhttpsocketengine_p.cpp"
Combined button and popup list for selecting options.