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