1
/****************************************************************************
3
** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4
** Contact: http://www.qt-project.org/legal
6
** This file is part of the QtNetwork module of the Qt Toolkit.
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 Digia. For licensing terms and
14
** conditions see http://qt.digia.com/licensing. For further information
15
** use the contact form at http://qt.digia.com/contact-us.
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 2.1 as published by the Free Software
20
** Foundation and appearing in the file LICENSE.LGPL included in the
21
** packaging of this file. Please review the following information to
22
** ensure the GNU Lesser General Public License version 2.1 requirements
23
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25
** In addition, as a special exception, Digia gives you certain additional
26
** rights. These rights are described in the Digia Qt LGPL Exception
27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29
** GNU General Public License Usage
30
** Alternatively, this file may be used under the terms of the GNU
31
** General Public License version 3.0 as published by the Free Software
32
** Foundation and appearing in the file LICENSE.GPL included in the
33
** packaging of this file. Please review the following information to
34
** ensure the GNU General Public License version 3.0 requirements will be
35
** met: http://www.gnu.org/copyleft/gpl.html.
40
****************************************************************************/
42
#include "qhttpnetworkconnectionchannel_p.h"
43
#include "qhttpnetworkconnection_p.h"
44
#include "private/qnoncontiguousbytedevice_p.h"
52
# include <QtNetwork/qsslkey.h>
53
# include <QtNetwork/qsslcipher.h>
54
# include <QtNetwork/qsslconfiguration.h>
57
#ifndef QT_NO_BEARERMANAGEMENT
58
#include "private/qnetworksession_p.h"
63
// TODO: Put channel specific stuff here so it does not polute qhttpnetworkconnection.cpp
65
QHttpNetworkConnectionChannel::QHttpNetworkConnectionChannel()
68
, isInitialized(false)
73
, resendCurrent(false)
75
, pendingEncrypt(false)
76
, reconnectAttempts(2)
77
, authMethod(QAuthenticatorPrivate::None)
78
, proxyAuthMethod(QAuthenticatorPrivate::None)
79
, authenticationCredentialsSent(false)
80
, proxyCredentialsSent(false)
82
, ignoreAllSslErrors(false)
84
, pipeliningSupported(PipeliningSupportUnknown)
85
, networkLayerPreference(QAbstractSocket::AnyIPProtocol)
88
// Inlining this function in the header leads to compiler error on
89
// release-armv5, on at least timebox 9.2 and 10.1.
92
void QHttpNetworkConnectionChannel::init()
95
if (connection->d_func()->encrypt)
96
socket = new QSslSocket;
98
socket = new QTcpSocket;
100
socket = new QTcpSocket;
102
#ifndef QT_NO_BEARERMANAGEMENT
103
//push session down to socket
105
socket->setProperty("_q_networksession", QVariant::fromValue(networkSession));
107
#ifndef QT_NO_NETWORKPROXY
108
// Set by QNAM anyway, but let's be safe here
109
socket->setProxy(QNetworkProxy::NoProxy);
112
QObject::connect(socket, SIGNAL(bytesWritten(qint64)),
113
this, SLOT(_q_bytesWritten(qint64)),
114
Qt::DirectConnection);
115
QObject::connect(socket, SIGNAL(connected()),
116
this, SLOT(_q_connected()),
117
Qt::DirectConnection);
118
QObject::connect(socket, SIGNAL(readyRead()),
119
this, SLOT(_q_readyRead()),
120
Qt::DirectConnection);
122
// The disconnected() and error() signals may already come
123
// while calling connectToHost().
124
// In case of a cached hostname or an IP this
125
// will then emit a signal to the user of QNetworkReply
126
// but cannot be caught because the user did not have a chance yet
127
// to connect to QNetworkReply's signals.
128
qRegisterMetaType<QAbstractSocket::SocketError>();
129
QObject::connect(socket, SIGNAL(disconnected()),
130
this, SLOT(_q_disconnected()),
131
Qt::QueuedConnection);
132
QObject::connect(socket, SIGNAL(error(QAbstractSocket::SocketError)),
133
this, SLOT(_q_error(QAbstractSocket::SocketError)),
134
Qt::QueuedConnection);
137
#ifndef QT_NO_NETWORKPROXY
138
QObject::connect(socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
139
this, SLOT(_q_proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
140
Qt::DirectConnection);
144
QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
146
// won't be a sslSocket if encrypt is false
147
QObject::connect(sslSocket, SIGNAL(encrypted()),
148
this, SLOT(_q_encrypted()),
149
Qt::DirectConnection);
150
QObject::connect(sslSocket, SIGNAL(sslErrors(QList<QSslError>)),
151
this, SLOT(_q_sslErrors(QList<QSslError>)),
152
Qt::DirectConnection);
153
QObject::connect(sslSocket, SIGNAL(encryptedBytesWritten(qint64)),
154
this, SLOT(_q_encryptedBytesWritten(qint64)),
155
Qt::DirectConnection);
157
if (ignoreAllSslErrors)
158
sslSocket->ignoreSslErrors();
160
if (!ignoreSslErrorsList.isEmpty())
161
sslSocket->ignoreSslErrors(ignoreSslErrorsList);
163
if (!sslConfiguration.isNull())
164
sslSocket->setSslConfiguration(sslConfiguration);
169
#ifndef QT_NO_NETWORKPROXY
170
if (proxy.type() != QNetworkProxy::NoProxy)
171
socket->setProxy(proxy);
173
isInitialized = true;
177
void QHttpNetworkConnectionChannel::close()
180
state = QHttpNetworkConnectionChannel::IdleState;
181
else if (socket->state() == QAbstractSocket::UnconnectedState)
182
state = QHttpNetworkConnectionChannel::IdleState;
184
state = QHttpNetworkConnectionChannel::ClosingState;
191
bool QHttpNetworkConnectionChannel::sendRequest()
194
// heh, how should that happen!
195
qWarning() << "QHttpNetworkConnectionChannel::sendRequest() called without QHttpNetworkReply";
196
state = QHttpNetworkConnectionChannel::IdleState;
201
case QHttpNetworkConnectionChannel::IdleState: { // write the header
202
if (!ensureConnection()) {
203
// wait for the connection (and encryption) to be done
204
// sendRequest will be called again from either
205
// _q_connected or _q_encrypted
208
written = 0; // excluding the header
211
QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
212
replyPrivate->clear();
213
replyPrivate->connection = connection;
214
replyPrivate->connectionChannel = this;
215
replyPrivate->autoDecompress = request.d->autoDecompress;
216
replyPrivate->pipeliningUsed = false;
218
// if the url contains authentication parameters, use the new ones
219
// both channels will use the new authentication parameters
220
if (!request.url().userInfo().isEmpty() && request.withCredentials()) {
221
QUrl url = request.url();
222
QAuthenticator &auth = authenticator;
223
if (url.userName() != auth.user()
224
|| (!url.password().isEmpty() && url.password() != auth.password())) {
225
auth.setUser(url.userName());
226
auth.setPassword(url.password());
227
connection->d_func()->copyCredentials(connection->d_func()->indexOf(socket), &auth, false);
229
// clear the userinfo, since we use the same request for resending
230
// userinfo in url can conflict with the one in the authenticator
231
url.setUserInfo(QString());
234
// Will only be false if QtWebKit is performing a cross-origin XMLHttpRequest
235
// and withCredentials has not been set to true.
236
if (request.withCredentials())
237
connection->d_func()->createAuthorization(socket, request);
238
#ifndef QT_NO_NETWORKPROXY
239
QByteArray header = QHttpNetworkRequestPrivate::header(request,
240
(connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy));
242
QByteArray header = QHttpNetworkRequestPrivate::header(request, false);
244
socket->write(header);
245
// flushing is dangerous (QSslSocket calls transmit which might read or error)
247
QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
248
if (uploadByteDevice) {
249
// connect the signals so this function gets called again
250
QObject::connect(uploadByteDevice, SIGNAL(readyRead()),this, SLOT(_q_uploadDataReadyRead()));
252
bytesTotal = request.contentLength();
254
state = QHttpNetworkConnectionChannel::WritingState; // start writing data
255
sendRequest(); //recurse
257
state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response
258
sendRequest(); //recurse
263
case QHttpNetworkConnectionChannel::WritingState:
266
QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
267
if (!uploadByteDevice || bytesTotal == written) {
268
if (uploadByteDevice)
269
emit reply->dataSendProgress(written, bytesTotal);
270
state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response
271
sendRequest(); // recurse
275
// only feed the QTcpSocket buffer when there is less than 32 kB in it
276
const qint64 socketBufferFill = 32*1024;
277
const qint64 socketWriteMaxSize = 16*1024;
281
QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
282
// if it is really an ssl socket, check more than just bytesToWrite()
283
while ((socket->bytesToWrite() + (sslSocket ? sslSocket->encryptedBytesToWrite() : 0))
284
<= socketBufferFill && bytesTotal != written)
286
while (socket->bytesToWrite() <= socketBufferFill
287
&& bytesTotal != written)
290
// get pointer to upload data
291
qint64 currentReadSize = 0;
292
qint64 desiredReadSize = qMin(socketWriteMaxSize, bytesTotal - written);
293
const char *readPointer = uploadByteDevice->readPointer(desiredReadSize, currentReadSize);
295
if (currentReadSize == -1) {
296
// premature eof happened
297
connection->d_func()->emitReplyError(socket, reply, QNetworkReply::UnknownNetworkError);
299
} else if (readPointer == 0 || currentReadSize == 0) {
300
// nothing to read currently, break the loop
303
qint64 currentWriteSize = socket->write(readPointer, currentReadSize);
304
if (currentWriteSize == -1 || currentWriteSize != currentReadSize) {
306
connection->d_func()->emitReplyError(socket, reply, QNetworkReply::UnknownNetworkError);
309
written += currentWriteSize;
310
uploadByteDevice->advanceReadPointer(currentWriteSize);
312
emit reply->dataSendProgress(written, bytesTotal);
314
if (written == bytesTotal) {
315
// make sure this function is called once again
316
state = QHttpNetworkConnectionChannel::WaitingState;
326
case QHttpNetworkConnectionChannel::WaitingState:
328
QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
329
if (uploadByteDevice) {
330
QObject::disconnect(uploadByteDevice, SIGNAL(readyRead()), this, SLOT(_q_uploadDataReadyRead()));
334
//connection->d_func()->fillPipeline(socket);
337
// ensure we try to receive a reply in all cases, even if _q_readyRead_ hat not been called
338
// this is needed if the sends an reply before we have finished sending the request. In that
339
// case receiveReply had been called before but ignored the server reply
340
if (socket->bytesAvailable())
341
QMetaObject::invokeMethod(this, "_q_receiveReply", Qt::QueuedConnection);
344
case QHttpNetworkConnectionChannel::ReadingState:
345
// ignore _q_bytesWritten in these states
354
void QHttpNetworkConnectionChannel::_q_receiveReply()
359
if (socket->bytesAvailable() > 0)
360
qWarning() << "QHttpNetworkConnectionChannel::_q_receiveReply() called without QHttpNetworkReply,"
361
<< socket->bytesAvailable() << "bytes on socket.";
366
// only run when the QHttpNetworkConnection is not currently being destructed, e.g.
367
// this function is called from _q_disconnected which is called because
368
// of ~QHttpNetworkConnectionPrivate
369
if (!qobject_cast<QHttpNetworkConnection*>(connection)) {
373
QAbstractSocket::SocketState socketState = socket->state();
375
// connection might be closed to signal the end of data
376
if (socketState == QAbstractSocket::UnconnectedState) {
377
if (socket->bytesAvailable() <= 0) {
378
if (reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) {
379
// finish this reply. this case happens when the server did not send a content length
380
reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState;
384
handleUnexpectedEOF();
388
// socket not connected but still bytes for reading.. just continue in this function
392
// read loop for the response
394
qint64 lastBytes = bytes;
398
QHttpNetworkReplyPrivate::ReplyState state = reply->d_func()->state;
400
case QHttpNetworkReplyPrivate::NothingDoneState: {
401
state = reply->d_func()->state = QHttpNetworkReplyPrivate::ReadingStatusState;
404
case QHttpNetworkReplyPrivate::ReadingStatusState: {
405
qint64 statusBytes = reply->d_func()->readStatus(socket);
406
if (statusBytes == -1) {
407
// connection broke while reading status. also handled if later _q_disconnected is called
408
handleUnexpectedEOF();
411
bytes += statusBytes;
412
lastStatus = reply->d_func()->statusCode;
415
case QHttpNetworkReplyPrivate::ReadingHeaderState: {
416
QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
417
qint64 headerBytes = replyPrivate->readHeader(socket);
418
if (headerBytes == -1) {
419
// connection broke while reading headers. also handled if later _q_disconnected is called
420
handleUnexpectedEOF();
423
bytes += headerBytes;
424
// If headers were parsed successfully now it is the ReadingDataState
425
if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState) {
426
if (replyPrivate->isCompressed() && replyPrivate->autoDecompress) {
427
// remove the Content-Length from header
428
replyPrivate->removeAutoDecompressHeader();
430
replyPrivate->autoDecompress = false;
432
if (replyPrivate->statusCode == 100) {
433
replyPrivate->clearHttpLayerInformation();
434
replyPrivate->state = QHttpNetworkReplyPrivate::ReadingStatusState;
437
if (replyPrivate->shouldEmitSignals())
438
emit reply->headerChanged();
439
// After headerChanged had been emitted
440
// we can suddenly have a replyPrivate->userProvidedDownloadBuffer
441
// this is handled in the ReadingDataState however
443
if (!replyPrivate->expectContent()) {
444
replyPrivate->state = QHttpNetworkReplyPrivate::AllDoneState;
451
case QHttpNetworkReplyPrivate::ReadingDataState: {
452
QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
453
if (socket->state() == QAbstractSocket::ConnectedState &&
454
replyPrivate->downstreamLimited && !replyPrivate->responseData.isEmpty() && replyPrivate->shouldEmitSignals()) {
455
// (only do the following when still connected, not when we have already been disconnected and there is still data)
456
// We already have some HTTP body data. We don't read more from the socket until
457
// this is fetched by QHttpNetworkAccessHttpBackend. If we would read more,
458
// we could not limit our read buffer usage.
459
// We only do this when shouldEmitSignals==true because our HTTP parsing
460
// always needs to parse the 401/407 replies. Therefore they don't really obey
461
// to the read buffer maximum size, but we don't care since they should be small.
465
if (replyPrivate->userProvidedDownloadBuffer) {
466
// the user provided a direct buffer where we should put all our data in.
467
// this only works when we can tell the user the content length and he/she can allocate
468
// the buffer in that size.
469
// note that this call will read only from the still buffered data
470
qint64 haveRead = replyPrivate->readBodyVeryFast(socket, replyPrivate->userProvidedDownloadBuffer + replyPrivate->totalProgress);
473
replyPrivate->totalProgress += haveRead;
474
// the user will get notified of it via progress signal
475
emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
476
} else if (haveRead == 0) {
477
// Happens since this called in a loop. Currently no bytes available.
478
} else if (haveRead < 0) {
479
connection->d_func()->emitReplyError(socket, reply, QNetworkReply::RemoteHostClosedError);
482
} else if (!replyPrivate->isChunked() && !replyPrivate->autoDecompress
483
&& replyPrivate->bodyLength > 0) {
484
// bulk files like images should fulfill these properties and
485
// we can therefore save on memory copying
486
qint64 haveRead = replyPrivate->readBodyFast(socket, &replyPrivate->responseData);
488
replyPrivate->totalProgress += haveRead;
489
if (replyPrivate->shouldEmitSignals()) {
490
emit reply->readyRead();
491
emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
496
// use the traditional slower reading (for compressed encoding, chunked encoding,
497
// no content-length etc)
498
qint64 haveRead = replyPrivate->readBody(socket, &replyPrivate->responseData);
501
replyPrivate->totalProgress += haveRead;
502
if (replyPrivate->shouldEmitSignals()) {
503
emit reply->readyRead();
504
emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
506
} else if (haveRead == -1) {
507
// Some error occurred
508
connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ProtocolFailure);
512
// still in ReadingDataState? This function will be called again by the socket's readyRead
513
if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState)
516
// everything done, fall through
518
case QHttpNetworkReplyPrivate::AllDoneState:
524
} while (bytes != lastBytes && reply);
527
// called when unexpectedly reading a -1 or when data is expected but socket is closed
528
void QHttpNetworkConnectionChannel::handleUnexpectedEOF()
531
if (reconnectAttempts <= 0) {
532
// too many errors reading/receiving/parsing the status, close the socket and emit error
533
requeueCurrentlyPipelinedRequests();
535
reply->d_func()->errorString = connection->d_func()->errorDetail(QNetworkReply::RemoteHostClosedError, socket);
536
emit reply->finishedWithError(QNetworkReply::RemoteHostClosedError, reply->d_func()->errorString);
537
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
540
reply->d_func()->clear();
541
reply->d_func()->connection = connection;
542
reply->d_func()->connectionChannel = this;
543
closeAndResendCurrentRequest();
547
bool QHttpNetworkConnectionChannel::ensureConnection()
552
QAbstractSocket::SocketState socketState = socket->state();
554
// resend this request after we receive the disconnected signal
555
if (socketState == QAbstractSocket::ClosingState) {
557
resendCurrent = true;
561
// already trying to connect?
562
if (socketState == QAbstractSocket::HostLookupState ||
563
socketState == QAbstractSocket::ConnectingState) {
567
// make sure that this socket is in a connected state, if not initiate
568
// connection to the host.
569
if (socketState != QAbstractSocket::ConnectedState) {
570
// connect to the host if not already connected.
571
state = QHttpNetworkConnectionChannel::ConnectingState;
572
pendingEncrypt = ssl;
575
pipeliningSupported = PipeliningSupportUnknown;
576
authenticationCredentialsSent = false;
577
proxyCredentialsSent = false;
578
authenticator.detach();
579
QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(authenticator);
580
priv->hasFailed = false;
581
proxyAuthenticator.detach();
582
priv = QAuthenticatorPrivate::getPrivate(proxyAuthenticator);
583
priv->hasFailed = false;
585
// This workaround is needed since we use QAuthenticator for NTLM authentication. The "phase == Done"
586
// is the usual criteria for emitting authentication signals. The "phase" is set to "Done" when the
587
// last header for Authorization is generated by the QAuthenticator. Basic & Digest logic does not
588
// check the "phase" for generating the Authorization header. NTLM authentication is a two stage
589
// process & needs the "phase". To make sure the QAuthenticator uses the current username/password
590
// the phase is reset to Start.
591
priv = QAuthenticatorPrivate::getPrivate(authenticator);
592
if (priv && priv->phase == QAuthenticatorPrivate::Done)
593
priv->phase = QAuthenticatorPrivate::Start;
594
priv = QAuthenticatorPrivate::getPrivate(proxyAuthenticator);
595
if (priv && priv->phase == QAuthenticatorPrivate::Done)
596
priv->phase = QAuthenticatorPrivate::Start;
598
QString connectHost = connection->d_func()->hostName;
599
qint16 connectPort = connection->d_func()->port;
601
#ifndef QT_NO_NETWORKPROXY
602
// HTTPS always use transparent proxy.
603
if (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy && !ssl) {
604
connectHost = connection->d_func()->networkProxy.hostName();
605
connectPort = connection->d_func()->networkProxy.port();
607
if (socket->proxy().type() == QNetworkProxy::HttpProxy) {
608
// Make user-agent field available to HTTP proxy socket engine (QTBUG-17223)
610
// ensureConnection is called before any request has been assigned, but can also be called again if reconnecting
611
if (request.url().isEmpty())
612
value = connection->d_func()->predictNextRequest().headerField("user-agent");
614
value = request.headerField("user-agent");
615
if (!value.isEmpty()) {
616
QNetworkProxy proxy(socket->proxy());
617
proxy.setRawHeader("User-Agent", value); //detaches
618
socket->setProxy(proxy);
624
QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
625
sslSocket->connectToHostEncrypted(connectHost, connectPort, QIODevice::ReadWrite, networkLayerPreference);
626
if (ignoreAllSslErrors)
627
sslSocket->ignoreSslErrors();
628
sslSocket->ignoreSslErrors(ignoreSslErrorsList);
630
// limit the socket read buffer size. we will read everything into
631
// the QHttpNetworkReply anyway, so let's grow only that and not
633
socket->setReadBufferSize(64*1024);
635
// Need to dequeue the request so that we can emit the error.
637
connection->d_func()->dequeueRequest(socket);
638
connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ProtocolUnknownError);
641
// In case of no proxy we can use the Unbuffered QTcpSocket
642
#ifndef QT_NO_NETWORKPROXY
643
if (connection->d_func()->networkProxy.type() == QNetworkProxy::NoProxy
644
&& connection->cacheProxy().type() == QNetworkProxy::NoProxy
645
&& connection->transparentProxy().type() == QNetworkProxy::NoProxy) {
647
socket->connectToHost(connectHost, connectPort, QIODevice::ReadWrite | QIODevice::Unbuffered, networkLayerPreference);
648
// For an Unbuffered QTcpSocket, the read buffer size has a special meaning.
649
socket->setReadBufferSize(1*1024);
650
#ifndef QT_NO_NETWORKPROXY
652
socket->connectToHost(connectHost, connectPort, QIODevice::ReadWrite, networkLayerPreference);
654
// limit the socket read buffer size. we will read everything into
655
// the QHttpNetworkReply anyway, so let's grow only that and not
657
socket->setReadBufferSize(64*1024);
666
void QHttpNetworkConnectionChannel::allDone()
671
qWarning() << "QHttpNetworkConnectionChannel::allDone() called without reply. Please report at http://bugreports.qt-project.org/";
675
// while handling 401 & 407, we might reset the status code, so save this.
676
bool emitFinished = reply->d_func()->shouldEmitSignals();
677
bool connectionCloseEnabled = reply->d_func()->isConnectionCloseEnabled();
678
detectPipeliningSupport();
681
// handleStatus() might have removed the reply because it already called connection->emitReplyError()
683
// queue the finished signal, this is required since we might send new requests from
684
// slot connected to it. The socket will not fire readyRead signal, if we are already
685
// in the slot connected to readyRead
686
if (reply && emitFinished)
687
QMetaObject::invokeMethod(reply, "finished", Qt::QueuedConnection);
690
// reset the reconnection attempts after we receive a complete reply.
691
// in case of failures, each channel will attempt two reconnects before emitting error.
692
reconnectAttempts = 2;
694
// now the channel can be seen as free/idle again, all signal emissions for the reply have been done
695
if (state != QHttpNetworkConnectionChannel::ClosingState)
696
state = QHttpNetworkConnectionChannel::IdleState;
698
// if it does not need to be sent again we can set it to 0
699
// the previous code did not do that and we had problems with accidental re-sending of a
701
// Note that this may trigger a segfault at some other point. But then we can fix the underlying
703
if (!resendCurrent) {
704
request = QHttpNetworkRequest();
708
// move next from pipeline to current request
709
if (!alreadyPipelinedRequests.isEmpty()) {
710
if (resendCurrent || connectionCloseEnabled || socket->state() != QAbstractSocket::ConnectedState) {
711
// move the pipelined ones back to the main queue
712
requeueCurrentlyPipelinedRequests();
715
// there were requests pipelined in and we can continue
716
HttpMessagePair messagePair = alreadyPipelinedRequests.takeFirst();
718
request = messagePair.first;
719
reply = messagePair.second;
720
state = QHttpNetworkConnectionChannel::ReadingState;
721
resendCurrent = false;
723
written = 0; // message body, excluding the header, irrelevant here
724
bytesTotal = 0; // message body total, excluding the header, irrelevant here
726
// pipeline even more
727
connection->d_func()->fillPipeline(socket);
731
// this was wrong, allDone gets called from that function anyway.
733
} else if (alreadyPipelinedRequests.isEmpty() && socket->bytesAvailable() > 0) {
734
// this is weird. we had nothing pipelined but still bytes available. better close it.
737
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
738
} else if (alreadyPipelinedRequests.isEmpty()) {
739
if (connectionCloseEnabled)
740
if (socket->state() != QAbstractSocket::UnconnectedState)
742
if (qobject_cast<QHttpNetworkConnection*>(connection))
743
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
747
void QHttpNetworkConnectionChannel::detectPipeliningSupport()
750
// detect HTTP Pipelining support
751
QByteArray serverHeaderField;
753
// check for HTTP/1.1
754
(reply->d_func()->majorVersion == 1 && reply->d_func()->minorVersion == 1)
755
// check for not having connection close
756
&& (!reply->d_func()->isConnectionCloseEnabled())
757
// check if it is still connected
758
&& (socket->state() == QAbstractSocket::ConnectedState)
759
// check for broken servers in server reply header
760
// this is adapted from http://mxr.mozilla.org/firefox/ident?i=SupportsPipelining
761
&& (serverHeaderField = reply->headerField("Server"), !serverHeaderField.contains("Microsoft-IIS/4."))
762
&& (!serverHeaderField.contains("Microsoft-IIS/5."))
763
&& (!serverHeaderField.contains("Netscape-Enterprise/3."))
764
// this is adpoted from the knowledge of the Nokia 7.x browser team (DEF143319)
765
&& (!serverHeaderField.contains("WebLogic"))
766
&& (!serverHeaderField.startsWith("Rocket")) // a Python Web Server, see Web2py.com
768
pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningProbablySupported;
770
pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningSupportUnknown;
774
// called when the connection broke and we need to queue some pipelined requests again
775
void QHttpNetworkConnectionChannel::requeueCurrentlyPipelinedRequests()
777
for (int i = 0; i < alreadyPipelinedRequests.length(); i++)
778
connection->d_func()->requeueRequest(alreadyPipelinedRequests.at(i));
779
alreadyPipelinedRequests.clear();
781
// only run when the QHttpNetworkConnection is not currently being destructed, e.g.
782
// this function is called from _q_disconnected which is called because
783
// of ~QHttpNetworkConnectionPrivate
784
if (qobject_cast<QHttpNetworkConnection*>(connection))
785
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
788
void QHttpNetworkConnectionChannel::handleStatus()
793
int statusCode = reply->statusCode();
796
switch (statusCode) {
797
case 401: // auth required
798
case 407: // proxy auth required
799
if (connection->d_func()->handleAuthenticateChallenge(socket, reply, (statusCode == 407), resend)) {
801
if (!resetUploadData())
804
reply->d_func()->eraseData();
806
if (alreadyPipelinedRequests.isEmpty()) {
807
// this does a re-send without closing the connection
808
resendCurrent = true;
809
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
811
// we had requests pipelined.. better close the connection in closeAndResendCurrentRequest
812
closeAndResendCurrentRequest();
813
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
816
//authentication cancelled, close the channel.
820
emit reply->headerChanged();
821
emit reply->readyRead();
822
QNetworkReply::NetworkError errorCode = (statusCode == 407)
823
? QNetworkReply::ProxyAuthenticationRequiredError
824
: QNetworkReply::AuthenticationRequiredError;
825
reply->d_func()->errorString = connection->d_func()->errorDetail(errorCode, socket);
826
emit reply->finishedWithError(errorCode, reply->d_func()->errorString);
830
if (qobject_cast<QHttpNetworkConnection*>(connection))
831
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
835
bool QHttpNetworkConnectionChannel::resetUploadData()
838
//this happens if server closes connection while QHttpNetworkConnectionPrivate::_q_startNextRequest is pending
841
QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
842
if (!uploadByteDevice)
845
if (uploadByteDevice->reset()) {
849
connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ContentReSendError);
854
#ifndef QT_NO_NETWORKPROXY
856
void QHttpNetworkConnectionChannel::setProxy(const QNetworkProxy &networkProxy)
859
socket->setProxy(networkProxy);
861
proxy = networkProxy;
868
void QHttpNetworkConnectionChannel::ignoreSslErrors()
871
static_cast<QSslSocket *>(socket)->ignoreSslErrors();
873
ignoreAllSslErrors = true;
877
void QHttpNetworkConnectionChannel::ignoreSslErrors(const QList<QSslError> &errors)
880
static_cast<QSslSocket *>(socket)->ignoreSslErrors(errors);
882
ignoreSslErrorsList = errors;
885
void QHttpNetworkConnectionChannel::setSslConfiguration(const QSslConfiguration &config)
888
static_cast<QSslSocket *>(socket)->setSslConfiguration(config);
890
sslConfiguration = config;
895
void QHttpNetworkConnectionChannel::pipelineInto(HttpMessagePair &pair)
897
// this is only called for simple GET
899
QHttpNetworkRequest &request = pair.first;
900
QHttpNetworkReply *reply = pair.second;
901
reply->d_func()->clear();
902
reply->d_func()->connection = connection;
903
reply->d_func()->connectionChannel = this;
904
reply->d_func()->autoDecompress = request.d->autoDecompress;
905
reply->d_func()->pipeliningUsed = true;
907
#ifndef QT_NO_NETWORKPROXY
908
pipeline.append(QHttpNetworkRequestPrivate::header(request,
909
(connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy)));
911
pipeline.append(QHttpNetworkRequestPrivate::header(request, false));
914
alreadyPipelinedRequests.append(pair);
916
// pipelineFlush() needs to be called at some point afterwards
919
void QHttpNetworkConnectionChannel::pipelineFlush()
921
if (pipeline.isEmpty())
924
// The goal of this is so that we have everything in one TCP packet.
925
// For the Unbuffered QTcpSocket this is manually needed, the buffered
926
// QTcpSocket does it automatically.
927
// Also, sometimes the OS does it for us (Nagle's algorithm) but that
928
// happens only sometimes.
929
socket->write(pipeline);
934
void QHttpNetworkConnectionChannel::closeAndResendCurrentRequest()
936
requeueCurrentlyPipelinedRequests();
939
resendCurrent = true;
940
if (qobject_cast<QHttpNetworkConnection*>(connection))
941
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
944
bool QHttpNetworkConnectionChannel::isSocketBusy() const
946
return (state & QHttpNetworkConnectionChannel::BusyState);
949
bool QHttpNetworkConnectionChannel::isSocketWriting() const
951
return (state & QHttpNetworkConnectionChannel::WritingState);
954
bool QHttpNetworkConnectionChannel::isSocketWaiting() const
956
return (state & QHttpNetworkConnectionChannel::WaitingState);
959
bool QHttpNetworkConnectionChannel::isSocketReading() const
961
return (state & QHttpNetworkConnectionChannel::ReadingState);
965
void QHttpNetworkConnectionChannel::_q_readyRead()
967
if (socket->state() == QAbstractSocket::ConnectedState && socket->bytesAvailable() == 0) {
968
// We got a readyRead but no bytes are available..
969
// This happens for the Unbuffered QTcpSocket
970
// Also check if socket is in ConnectedState since
971
// this function may also be invoked via the event loop.
973
qint64 ret = socket->peek(&c, 1);
975
_q_error(socket->error());
976
// We still need to handle the reply so it emits its signals etc.
983
if (isSocketWaiting() || isSocketReading()) {
984
state = QHttpNetworkConnectionChannel::ReadingState;
990
void QHttpNetworkConnectionChannel::_q_bytesWritten(qint64 bytes)
993
// bytes have been written to the socket. write even more of them :)
994
if (isSocketWriting())
996
// otherwise we do nothing
999
void QHttpNetworkConnectionChannel::_q_disconnected()
1001
if (state == QHttpNetworkConnectionChannel::ClosingState) {
1002
state = QHttpNetworkConnectionChannel::IdleState;
1003
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
1007
// read the available data before closing
1008
if (isSocketWaiting() || isSocketReading()) {
1010
state = QHttpNetworkConnectionChannel::ReadingState;
1013
} else if (state == QHttpNetworkConnectionChannel::IdleState && resendCurrent) {
1014
// re-sending request because the socket was in ClosingState
1015
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
1017
state = QHttpNetworkConnectionChannel::IdleState;
1019
requeueCurrentlyPipelinedRequests();
1024
void QHttpNetworkConnectionChannel::_q_connected()
1026
// For the Happy Eyeballs we need to check if this is the first channel to connect.
1027
if (connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::InProgress) {
1028
if (connection->d_func()->delayedConnectionTimer.isActive())
1029
connection->d_func()->delayedConnectionTimer.stop();
1030
if (networkLayerPreference == QAbstractSocket::IPv4Protocol)
1031
connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv4;
1032
else if (networkLayerPreference == QAbstractSocket::IPv6Protocol)
1033
connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv6;
1035
if (socket->peerAddress().protocol() == QAbstractSocket::IPv4Protocol)
1036
connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv4;
1038
connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv6;
1040
connection->d_func()->networkLayerDetected(networkLayerPreference);
1042
if (((connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::IPv4) && (networkLayerPreference != QAbstractSocket::IPv4Protocol))
1043
|| ((connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::IPv6) && (networkLayerPreference != QAbstractSocket::IPv6Protocol))) {
1045
// This is the second connection so it has to be closed and we can schedule it for another request.
1046
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
1049
//The connections networkLayerState had already been decided.
1052
// improve performance since we get the request sent by the kernel ASAP
1053
//socket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
1054
// We have this commented out now. It did not have the effect we wanted. If we want to
1055
// do this properly, Qt has to combine multiple HTTP requests into one buffer
1056
// and send this to the kernel in one syscall and then the kernel immediately sends
1057
// it as one TCP packet because of TCP_NODELAY.
1058
// However, this code is currently not in Qt, so we rely on the kernel combining
1059
// the requests into one TCP packet.
1061
// not sure yet if it helps, but it makes sense
1062
socket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
1064
pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningSupportUnknown;
1066
// ### FIXME: if the server closes the connection unexpectedly, we shouldn't send the same broken request again!
1067
//channels[i].reconnectAttempts = 2;
1068
if (!pendingEncrypt) {
1069
state = QHttpNetworkConnectionChannel::IdleState;
1071
connection->d_func()->dequeueRequest(socket);
1078
void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socketError)
1082
QNetworkReply::NetworkError errorCode = QNetworkReply::UnknownNetworkError;
1084
switch (socketError) {
1085
case QAbstractSocket::HostNotFoundError:
1086
errorCode = QNetworkReply::HostNotFoundError;
1088
case QAbstractSocket::ConnectionRefusedError:
1089
errorCode = QNetworkReply::ConnectionRefusedError;
1091
case QAbstractSocket::RemoteHostClosedError:
1092
// try to reconnect/resend before sending an error.
1093
// while "Reading" the _q_disconnected() will handle this.
1094
if (state != QHttpNetworkConnectionChannel::IdleState && state != QHttpNetworkConnectionChannel::ReadingState) {
1095
if (reconnectAttempts-- > 0) {
1096
closeAndResendCurrentRequest();
1099
errorCode = QNetworkReply::RemoteHostClosedError;
1101
} else if (state == QHttpNetworkConnectionChannel::ReadingState) {
1102
if (!reply->d_func()->expectContent()) {
1103
// No content expected, this is a valid way to have the connection closed by the server
1106
if (reply->contentLength() == -1 && !reply->d_func()->isChunked()) {
1107
// There was no content-length header and it's not chunked encoding,
1108
// so this is a valid way to have the connection closed by the server
1111
// ok, we got a disconnect even though we did not expect it
1112
// Try to read everything from the socket before we emit the error.
1113
if (socket->bytesAvailable()) {
1114
// Read everything from the socket into the reply buffer.
1115
// we can ignore the readbuffersize as the data is already
1116
// in memory and we will not receive more data on the socket.
1117
reply->setReadBufferSize(0);
1121
// QT_NO_OPENSSL. The QSslSocket can still have encrypted bytes in the plainsocket.
1122
// So we need to check this if the socket is a QSslSocket. When the socket is flushed
1123
// it will force a decrypt of the encrypted data in the plainsocket.
1124
QSslSocket *sslSocket = static_cast<QSslSocket*>(socket);
1125
qint64 beforeFlush = sslSocket->encryptedBytesAvailable();
1126
while (sslSocket->encryptedBytesAvailable()) {
1129
qint64 afterFlush = sslSocket->encryptedBytesAvailable();
1130
if (afterFlush == beforeFlush)
1132
beforeFlush = afterFlush;
1138
errorCode = QNetworkReply::RemoteHostClosedError;
1140
errorCode = QNetworkReply::RemoteHostClosedError;
1143
case QAbstractSocket::SocketTimeoutError:
1144
// try to reconnect/resend before sending an error.
1145
if (state == QHttpNetworkConnectionChannel::WritingState && (reconnectAttempts-- > 0)) {
1146
closeAndResendCurrentRequest();
1149
errorCode = QNetworkReply::TimeoutError;
1151
case QAbstractSocket::ProxyAuthenticationRequiredError:
1152
errorCode = QNetworkReply::ProxyAuthenticationRequiredError;
1154
case QAbstractSocket::SslHandshakeFailedError:
1155
errorCode = QNetworkReply::SslHandshakeFailedError;
1158
// all other errors are treated as NetworkError
1159
errorCode = QNetworkReply::UnknownNetworkError;
1162
QPointer<QHttpNetworkConnection> that = connection;
1163
QString errorString = connection->d_func()->errorDetail(errorCode, socket, socket->errorString());
1165
// In the InProgress state the channel should not emit the error.
1166
// This will instead be handled by the connection.
1167
if (!connection->d_func()->shouldEmitChannelError(socket))
1170
// Need to dequeu the request so that we can emit the error.
1172
connection->d_func()->dequeueRequest(socket);
1174
reply->d_func()->errorString = errorString;
1175
emit reply->finishedWithError(errorCode, errorString);
1178
// send the next request
1179
QMetaObject::invokeMethod(that, "_q_startNextRequest", Qt::QueuedConnection);
1181
if (that) //signal emission triggered event loop
1185
#ifndef QT_NO_NETWORKPROXY
1186
void QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator* auth)
1188
// Need to dequeue the request before we can emit the error.
1190
connection->d_func()->dequeueRequest(socket);
1192
connection->d_func()->emitProxyAuthenticationRequired(this, proxy, auth);
1196
void QHttpNetworkConnectionChannel::_q_uploadDataReadyRead()
1202
void QHttpNetworkConnectionChannel::_q_encrypted()
1205
return; // ### error
1206
state = QHttpNetworkConnectionChannel::IdleState;
1207
pendingEncrypt = false;
1209
connection->d_func()->dequeueRequest(socket);
1214
void QHttpNetworkConnectionChannel::_q_sslErrors(const QList<QSslError> &errors)
1218
//QNetworkReply::NetworkError errorCode = QNetworkReply::ProtocolFailure;
1219
// Also pause the connection because socket notifiers may fire while an user
1220
// dialog is displaying
1221
connection->d_func()->pauseConnection();
1222
if (pendingEncrypt && !reply)
1223
connection->d_func()->dequeueRequest(socket);
1225
emit reply->sslErrors(errors);
1226
connection->d_func()->resumeConnection();
1229
void QHttpNetworkConnectionChannel::_q_encryptedBytesWritten(qint64 bytes)
1232
// bytes have been written to the socket. write even more of them :)
1233
if (isSocketWriting())
1235
// otherwise we do nothing
1240
void QHttpNetworkConnectionChannel::setConnection(QHttpNetworkConnection *c)
1242
// Inlining this function in the header leads to compiler error on
1243
// release-armv5, on at least timebox 9.2 and 10.1.
1249
#include "moc_qhttpnetworkconnectionchannel_p.cpp"
1251
#endif // QT_NO_HTTP