1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtNetwork module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
42 #include "qhttpnetworkconnection_p.h"
43 #include "qhttpnetworkconnectionchannel_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()
72 , resendCurrent(false)
74 , pendingEncrypt(false)
75 , reconnectAttempts(2)
76 , authMethod(QAuthenticatorPrivate::None)
77 , proxyAuthMethod(QAuthenticatorPrivate::None)
78 , authenticationCredentialsSent(false)
79 , proxyCredentialsSent(false)
81 , ignoreAllSslErrors(false)
83 , pipeliningSupported(PipeliningSupportUnknown)
84 , networkLayerPreference(QAbstractSocket::AnyIPProtocol)
87 // Inlining this function in the header leads to compiler error on
88 // release-armv5, on at least timebox 9.2 and 10.1.
91 void QHttpNetworkConnectionChannel::init()
94 if (connection->d_func()->encrypt)
95 socket = new QSslSocket;
97 socket = new QTcpSocket;
99 socket = new QTcpSocket;
101 #ifndef QT_NO_BEARERMANAGEMENT
102 //push session down to socket
104 socket->setProperty("_q_networksession", QVariant::fromValue(networkSession));
106 #ifndef QT_NO_NETWORKPROXY
107 // Set by QNAM anyway, but let's be safe here
108 socket->setProxy(QNetworkProxy::NoProxy);
111 QObject::connect(socket, SIGNAL(bytesWritten(qint64)),
112 this, SLOT(_q_bytesWritten(qint64)),
113 Qt::DirectConnection);
114 QObject::connect(socket, SIGNAL(connected()),
115 this, SLOT(_q_connected()),
116 Qt::DirectConnection);
117 QObject::connect(socket, SIGNAL(readyRead()),
118 this, SLOT(_q_readyRead()),
119 Qt::DirectConnection);
121 // The disconnected() and error() signals may already come
122 // while calling connectToHost().
123 // In case of a cached hostname or an IP this
124 // will then emit a signal to the user of QNetworkReply
125 // but cannot be caught because the user did not have a chance yet
126 // to connect to QNetworkReply's signals.
127 qRegisterMetaType<QAbstractSocket::SocketError>("QAbstractSocket::SocketError");
128 QObject::connect(socket, SIGNAL(disconnected()),
129 this, SLOT(_q_disconnected()),
130 Qt::QueuedConnection);
131 QObject::connect(socket, SIGNAL(error(QAbstractSocket::SocketError)),
132 this, SLOT(_q_error(QAbstractSocket::SocketError)),
133 Qt::QueuedConnection);
136 #ifndef QT_NO_NETWORKPROXY
137 QObject::connect(socket, SIGNAL(proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
138 this, SLOT(_q_proxyAuthenticationRequired(QNetworkProxy,QAuthenticator*)),
139 Qt::DirectConnection);
143 QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
145 // won't be a sslSocket if encrypt is false
146 QObject::connect(sslSocket, SIGNAL(encrypted()),
147 this, SLOT(_q_encrypted()),
148 Qt::DirectConnection);
149 QObject::connect(sslSocket, SIGNAL(sslErrors(QList<QSslError>)),
150 this, SLOT(_q_sslErrors(QList<QSslError>)),
151 Qt::DirectConnection);
152 QObject::connect(sslSocket, SIGNAL(encryptedBytesWritten(qint64)),
153 this, SLOT(_q_encryptedBytesWritten(qint64)),
154 Qt::DirectConnection);
160 void QHttpNetworkConnectionChannel::close()
162 if (socket->state() == QAbstractSocket::UnconnectedState)
163 state = QHttpNetworkConnectionChannel::IdleState;
165 state = QHttpNetworkConnectionChannel::ClosingState;
171 bool QHttpNetworkConnectionChannel::sendRequest()
174 // heh, how should that happen!
175 qWarning() << "QHttpNetworkConnectionChannel::sendRequest() called without QHttpNetworkReply";
176 state = QHttpNetworkConnectionChannel::IdleState;
181 case QHttpNetworkConnectionChannel::IdleState: { // write the header
182 if (!ensureConnection()) {
183 // wait for the connection (and encryption) to be done
184 // sendRequest will be called again from either
185 // _q_connected or _q_encrypted
188 written = 0; // excluding the header
191 QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
192 replyPrivate->clear();
193 replyPrivate->connection = connection;
194 replyPrivate->connectionChannel = this;
195 replyPrivate->autoDecompress = request.d->autoDecompress;
196 replyPrivate->pipeliningUsed = false;
198 // if the url contains authentication parameters, use the new ones
199 // both channels will use the new authentication parameters
200 if (!request.url().userInfo().isEmpty() && request.withCredentials()) {
201 QUrl url = request.url();
202 QAuthenticator &auth = authenticator;
203 if (url.userName() != auth.user()
204 || (!url.password().isEmpty() && url.password() != auth.password())) {
205 auth.setUser(url.userName());
206 auth.setPassword(url.password());
207 connection->d_func()->copyCredentials(connection->d_func()->indexOf(socket), &auth, false);
209 // clear the userinfo, since we use the same request for resending
210 // userinfo in url can conflict with the one in the authenticator
211 url.setUserInfo(QString());
214 // Will only be false if QtWebKit is performing a cross-origin XMLHttpRequest
215 // and withCredentials has not been set to true.
216 if (request.withCredentials())
217 connection->d_func()->createAuthorization(socket, request);
218 #ifndef QT_NO_NETWORKPROXY
219 QByteArray header = QHttpNetworkRequestPrivate::header(request,
220 (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy));
222 QByteArray header = QHttpNetworkRequestPrivate::header(request, false);
224 socket->write(header);
225 // flushing is dangerous (QSslSocket calls transmit which might read or error)
227 QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
228 if (uploadByteDevice) {
229 // connect the signals so this function gets called again
230 QObject::connect(uploadByteDevice, SIGNAL(readyRead()),this, SLOT(_q_uploadDataReadyRead()));
232 bytesTotal = request.contentLength();
234 state = QHttpNetworkConnectionChannel::WritingState; // start writing data
235 sendRequest(); //recurse
237 state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response
238 sendRequest(); //recurse
243 case QHttpNetworkConnectionChannel::WritingState:
246 QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
247 if (!uploadByteDevice || bytesTotal == written) {
248 if (uploadByteDevice)
249 emit reply->dataSendProgress(written, bytesTotal);
250 state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response
251 sendRequest(); // recurse
255 // only feed the QTcpSocket buffer when there is less than 32 kB in it
256 const qint64 socketBufferFill = 32*1024;
257 const qint64 socketWriteMaxSize = 16*1024;
261 QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
262 // if it is really an ssl socket, check more than just bytesToWrite()
263 while ((socket->bytesToWrite() + (sslSocket ? sslSocket->encryptedBytesToWrite() : 0))
264 <= socketBufferFill && bytesTotal != written)
266 while (socket->bytesToWrite() <= socketBufferFill
267 && bytesTotal != written)
270 // get pointer to upload data
271 qint64 currentReadSize = 0;
272 qint64 desiredReadSize = qMin(socketWriteMaxSize, bytesTotal - written);
273 const char *readPointer = uploadByteDevice->readPointer(desiredReadSize, currentReadSize);
275 if (currentReadSize == -1) {
276 // premature eof happened
277 connection->d_func()->emitReplyError(socket, reply, QNetworkReply::UnknownNetworkError);
280 } else if (readPointer == 0 || currentReadSize == 0) {
281 // nothing to read currently, break the loop
284 qint64 currentWriteSize = socket->write(readPointer, currentReadSize);
285 if (currentWriteSize == -1 || currentWriteSize != currentReadSize) {
287 connection->d_func()->emitReplyError(socket, reply, QNetworkReply::UnknownNetworkError);
290 written += currentWriteSize;
291 uploadByteDevice->advanceReadPointer(currentWriteSize);
293 emit reply->dataSendProgress(written, bytesTotal);
295 if (written == bytesTotal) {
296 // make sure this function is called once again
297 state = QHttpNetworkConnectionChannel::WaitingState;
307 case QHttpNetworkConnectionChannel::WaitingState:
309 QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
310 if (uploadByteDevice) {
311 QObject::disconnect(uploadByteDevice, SIGNAL(readyRead()), this, SLOT(_q_uploadDataReadyRead()));
315 //connection->d_func()->fillPipeline(socket);
318 // ensure we try to receive a reply in all cases, even if _q_readyRead_ hat not been called
319 // this is needed if the sends an reply before we have finished sending the request. In that
320 // case receiveReply had been called before but ignored the server reply
321 if (socket->bytesAvailable())
322 QMetaObject::invokeMethod(this, "_q_receiveReply", Qt::QueuedConnection);
325 case QHttpNetworkConnectionChannel::ReadingState:
326 // ignore _q_bytesWritten in these states
335 void QHttpNetworkConnectionChannel::_q_receiveReply()
340 // heh, how should that happen!
341 qWarning() << "QHttpNetworkConnectionChannel::_q_receiveReply() called without QHttpNetworkReply,"
342 << socket->bytesAvailable() << "bytes on socket.";
347 // only run when the QHttpNetworkConnection is not currently being destructed, e.g.
348 // this function is called from _q_disconnected which is called because
349 // of ~QHttpNetworkConnectionPrivate
350 if (!qobject_cast<QHttpNetworkConnection*>(connection)) {
354 QAbstractSocket::SocketState socketState = socket->state();
356 // connection might be closed to signal the end of data
357 if (socketState == QAbstractSocket::UnconnectedState) {
358 if (socket->bytesAvailable() <= 0) {
359 if (reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) {
360 // finish this reply. this case happens when the server did not send a content length
361 reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState;
365 handleUnexpectedEOF();
369 // socket not connected but still bytes for reading.. just continue in this function
373 // read loop for the response
375 qint64 lastBytes = bytes;
379 QHttpNetworkReplyPrivate::ReplyState state = reply->d_func()->state;
381 case QHttpNetworkReplyPrivate::NothingDoneState: {
382 state = reply->d_func()->state = QHttpNetworkReplyPrivate::ReadingStatusState;
385 case QHttpNetworkReplyPrivate::ReadingStatusState: {
386 qint64 statusBytes = reply->d_func()->readStatus(socket);
387 if (statusBytes == -1) {
388 // connection broke while reading status. also handled if later _q_disconnected is called
389 handleUnexpectedEOF();
392 bytes += statusBytes;
393 lastStatus = reply->d_func()->statusCode;
396 case QHttpNetworkReplyPrivate::ReadingHeaderState: {
397 QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
398 qint64 headerBytes = replyPrivate->readHeader(socket);
399 if (headerBytes == -1) {
400 // connection broke while reading headers. also handled if later _q_disconnected is called
401 handleUnexpectedEOF();
404 bytes += headerBytes;
405 // If headers were parsed successfully now it is the ReadingDataState
406 if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState) {
407 if (replyPrivate->isCompressed() && replyPrivate->autoDecompress) {
408 // remove the Content-Length from header
409 replyPrivate->removeAutoDecompressHeader();
411 replyPrivate->autoDecompress = false;
413 if (replyPrivate->statusCode == 100) {
414 replyPrivate->clearHttpLayerInformation();
415 replyPrivate->state = QHttpNetworkReplyPrivate::ReadingStatusState;
418 if (replyPrivate->shouldEmitSignals())
419 emit reply->headerChanged();
420 // After headerChanged had been emitted
421 // we can suddenly have a replyPrivate->userProvidedDownloadBuffer
422 // this is handled in the ReadingDataState however
424 if (!replyPrivate->expectContent()) {
425 replyPrivate->state = QHttpNetworkReplyPrivate::AllDoneState;
432 case QHttpNetworkReplyPrivate::ReadingDataState: {
433 QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
434 if (socket->state() == QAbstractSocket::ConnectedState &&
435 replyPrivate->downstreamLimited && !replyPrivate->responseData.isEmpty() && replyPrivate->shouldEmitSignals()) {
436 // (only do the following when still connected, not when we have already been disconnected and there is still data)
437 // We already have some HTTP body data. We don't read more from the socket until
438 // this is fetched by QHttpNetworkAccessHttpBackend. If we would read more,
439 // we could not limit our read buffer usage.
440 // We only do this when shouldEmitSignals==true because our HTTP parsing
441 // always needs to parse the 401/407 replies. Therefore they don't really obey
442 // to the read buffer maximum size, but we don't care since they should be small.
446 if (replyPrivate->userProvidedDownloadBuffer) {
447 // the user provided a direct buffer where we should put all our data in.
448 // this only works when we can tell the user the content length and he/she can allocate
449 // the buffer in that size.
450 // note that this call will read only from the still buffered data
451 qint64 haveRead = replyPrivate->readBodyVeryFast(socket, replyPrivate->userProvidedDownloadBuffer + replyPrivate->totalProgress);
454 replyPrivate->totalProgress += haveRead;
455 // the user will get notified of it via progress signal
456 emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
457 } else if (haveRead == 0) {
458 // Happens since this called in a loop. Currently no bytes available.
459 } else if (haveRead < 0) {
460 connection->d_func()->emitReplyError(socket, reply, QNetworkReply::RemoteHostClosedError);
463 } else if (!replyPrivate->isChunked() && !replyPrivate->autoDecompress
464 && replyPrivate->bodyLength > 0) {
465 // bulk files like images should fulfill these properties and
466 // we can therefore save on memory copying
467 qint64 haveRead = replyPrivate->readBodyFast(socket, &replyPrivate->responseData);
469 replyPrivate->totalProgress += haveRead;
470 if (replyPrivate->shouldEmitSignals()) {
471 emit reply->readyRead();
472 emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
477 // use the traditional slower reading (for compressed encoding, chunked encoding,
478 // no content-length etc)
479 qint64 haveRead = replyPrivate->readBody(socket, &replyPrivate->responseData);
482 replyPrivate->totalProgress += haveRead;
483 if (replyPrivate->shouldEmitSignals()) {
484 emit reply->readyRead();
485 emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
487 } else if (haveRead == -1) {
488 // Some error occured
489 connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ProtocolFailure);
493 // still in ReadingDataState? This function will be called again by the socket's readyRead
494 if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState)
497 // everything done, fall through
499 case QHttpNetworkReplyPrivate::AllDoneState:
505 } while (bytes != lastBytes && reply);
508 // called when unexpectedly reading a -1 or when data is expected but socket is closed
509 void QHttpNetworkConnectionChannel::handleUnexpectedEOF()
512 if (reconnectAttempts <= 0) {
513 // too many errors reading/receiving/parsing the status, close the socket and emit error
514 requeueCurrentlyPipelinedRequests();
516 reply->d_func()->errorString = connection->d_func()->errorDetail(QNetworkReply::RemoteHostClosedError, socket);
517 emit reply->finishedWithError(QNetworkReply::RemoteHostClosedError, reply->d_func()->errorString);
518 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
521 reply->d_func()->clear();
522 reply->d_func()->connection = connection;
523 reply->d_func()->connectionChannel = this;
524 closeAndResendCurrentRequest();
528 bool QHttpNetworkConnectionChannel::ensureConnection()
530 QAbstractSocket::SocketState socketState = socket->state();
532 // resend this request after we receive the disconnected signal
533 if (socketState == QAbstractSocket::ClosingState) {
535 resendCurrent = true;
539 // already trying to connect?
540 if (socketState == QAbstractSocket::HostLookupState ||
541 socketState == QAbstractSocket::ConnectingState) {
545 // make sure that this socket is in a connected state, if not initiate
546 // connection to the host.
547 if (socketState != QAbstractSocket::ConnectedState) {
548 // connect to the host if not already connected.
549 state = QHttpNetworkConnectionChannel::ConnectingState;
550 pendingEncrypt = ssl;
553 pipeliningSupported = PipeliningSupportUnknown;
554 authenticationCredentialsSent = false;
555 proxyCredentialsSent = false;
556 authenticator.detach();
557 QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(authenticator);
558 priv->hasFailed = false;
559 proxyAuthenticator.detach();
560 priv = QAuthenticatorPrivate::getPrivate(proxyAuthenticator);
561 priv->hasFailed = false;
563 // This workaround is needed since we use QAuthenticator for NTLM authentication. The "phase == Done"
564 // is the usual criteria for emitting authentication signals. The "phase" is set to "Done" when the
565 // last header for Authorization is generated by the QAuthenticator. Basic & Digest logic does not
566 // check the "phase" for generating the Authorization header. NTLM authentication is a two stage
567 // process & needs the "phase". To make sure the QAuthenticator uses the current username/password
568 // the phase is reset to Start.
569 priv = QAuthenticatorPrivate::getPrivate(authenticator);
570 if (priv && priv->phase == QAuthenticatorPrivate::Done)
571 priv->phase = QAuthenticatorPrivate::Start;
572 priv = QAuthenticatorPrivate::getPrivate(proxyAuthenticator);
573 if (priv && priv->phase == QAuthenticatorPrivate::Done)
574 priv->phase = QAuthenticatorPrivate::Start;
576 QString connectHost = connection->d_func()->hostName;
577 qint16 connectPort = connection->d_func()->port;
579 #ifndef QT_NO_NETWORKPROXY
580 // HTTPS always use transparent proxy.
581 if (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy && !ssl) {
582 connectHost = connection->d_func()->networkProxy.hostName();
583 connectPort = connection->d_func()->networkProxy.port();
585 if (socket->proxy().type() == QNetworkProxy::HttpProxy) {
586 // Make user-agent field available to HTTP proxy socket engine (QTBUG-17223)
588 // ensureConnection is called before any request has been assigned, but can also be called again if reconnecting
589 if (request.url().isEmpty())
590 value = connection->d_func()->predictNextRequest().headerField("user-agent");
592 value = request.headerField("user-agent");
593 if (!value.isEmpty()) {
594 QNetworkProxy proxy(socket->proxy());
595 proxy.setRawHeader("User-Agent", value); //detaches
596 socket->setProxy(proxy);
602 QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
603 sslSocket->connectToHostEncrypted(connectHost, connectPort, QIODevice::ReadWrite, networkLayerPreference);
604 if (ignoreAllSslErrors)
605 sslSocket->ignoreSslErrors();
606 sslSocket->ignoreSslErrors(ignoreSslErrorsList);
608 // limit the socket read buffer size. we will read everything into
609 // the QHttpNetworkReply anyway, so let's grow only that and not
611 socket->setReadBufferSize(64*1024);
613 // Need to dequeue the request so that we can emit the error.
615 connection->d_func()->dequeueRequest(socket);
616 connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ProtocolUnknownError);
619 // In case of no proxy we can use the Unbuffered QTcpSocket
620 #ifndef QT_NO_NETWORKPROXY
621 if (connection->d_func()->networkProxy.type() == QNetworkProxy::NoProxy
622 && connection->cacheProxy().type() == QNetworkProxy::NoProxy
623 && connection->transparentProxy().type() == QNetworkProxy::NoProxy) {
625 socket->connectToHost(connectHost, connectPort, QIODevice::ReadWrite | QIODevice::Unbuffered, networkLayerPreference);
626 // For an Unbuffered QTcpSocket, the read buffer size has a special meaning.
627 socket->setReadBufferSize(1*1024);
628 #ifndef QT_NO_NETWORKPROXY
630 socket->connectToHost(connectHost, connectPort, QIODevice::ReadWrite, networkLayerPreference);
632 // limit the socket read buffer size. we will read everything into
633 // the QHttpNetworkReply anyway, so let's grow only that and not
635 socket->setReadBufferSize(64*1024);
644 void QHttpNetworkConnectionChannel::allDone()
649 qWarning() << "QHttpNetworkConnectionChannel::allDone() called without reply. Please report at http://bugreports.qt-project.org/";
653 // while handling 401 & 407, we might reset the status code, so save this.
654 bool emitFinished = reply->d_func()->shouldEmitSignals();
655 bool connectionCloseEnabled = reply->d_func()->isConnectionCloseEnabled();
656 detectPipeliningSupport();
659 // handleStatus() might have removed the reply because it already called connection->emitReplyError()
661 // queue the finished signal, this is required since we might send new requests from
662 // slot connected to it. The socket will not fire readyRead signal, if we are already
663 // in the slot connected to readyRead
664 if (reply && emitFinished)
665 QMetaObject::invokeMethod(reply, "finished", Qt::QueuedConnection);
668 // reset the reconnection attempts after we receive a complete reply.
669 // in case of failures, each channel will attempt two reconnects before emitting error.
670 reconnectAttempts = 2;
672 // now the channel can be seen as free/idle again, all signal emissions for the reply have been done
673 if (state != QHttpNetworkConnectionChannel::ClosingState)
674 state = QHttpNetworkConnectionChannel::IdleState;
676 // if it does not need to be sent again we can set it to 0
677 // the previous code did not do that and we had problems with accidental re-sending of a
679 // Note that this may trigger a segfault at some other point. But then we can fix the underlying
681 if (!resendCurrent) {
682 request = QHttpNetworkRequest();
686 // move next from pipeline to current request
687 if (!alreadyPipelinedRequests.isEmpty()) {
688 if (resendCurrent || connectionCloseEnabled || socket->state() != QAbstractSocket::ConnectedState) {
689 // move the pipelined ones back to the main queue
690 requeueCurrentlyPipelinedRequests();
693 // there were requests pipelined in and we can continue
694 HttpMessagePair messagePair = alreadyPipelinedRequests.takeFirst();
696 request = messagePair.first;
697 reply = messagePair.second;
698 state = QHttpNetworkConnectionChannel::ReadingState;
699 resendCurrent = false;
701 written = 0; // message body, excluding the header, irrelevant here
702 bytesTotal = 0; // message body total, excluding the header, irrelevant here
704 // pipeline even more
705 connection->d_func()->fillPipeline(socket);
709 // this was wrong, allDone gets called from that function anyway.
711 } else if (alreadyPipelinedRequests.isEmpty() && socket->bytesAvailable() > 0) {
712 // this is weird. we had nothing pipelined but still bytes available. better close it.
713 //if (socket->bytesAvailable() > 0)
717 // We do not close it anymore now, but should introduce this again after having fixed
718 // the chunked decoder in QHttpNetworkReply to read the whitespace after the last chunk.
719 // (Currently this is worked around by readStatus in the QHttpNetworkReply ignoring
720 // leading whitespace.
721 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
722 } else if (alreadyPipelinedRequests.isEmpty()) {
723 if (connectionCloseEnabled)
724 if (socket->state() != QAbstractSocket::UnconnectedState)
726 if (qobject_cast<QHttpNetworkConnection*>(connection))
727 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
731 void QHttpNetworkConnectionChannel::detectPipeliningSupport()
734 // detect HTTP Pipelining support
735 QByteArray serverHeaderField;
737 // check for HTTP/1.1
738 (reply->d_func()->majorVersion == 1 && reply->d_func()->minorVersion == 1)
739 // check for not having connection close
740 && (!reply->d_func()->isConnectionCloseEnabled())
741 // check if it is still connected
742 && (socket->state() == QAbstractSocket::ConnectedState)
743 // check for broken servers in server reply header
744 // this is adapted from http://mxr.mozilla.org/firefox/ident?i=SupportsPipelining
745 && (serverHeaderField = reply->headerField("Server"), !serverHeaderField.contains("Microsoft-IIS/4."))
746 && (!serverHeaderField.contains("Microsoft-IIS/5."))
747 && (!serverHeaderField.contains("Netscape-Enterprise/3."))
748 // this is adpoted from the knowledge of the Nokia 7.x browser team (DEF143319)
749 && (!serverHeaderField.contains("WebLogic"))
750 && (!serverHeaderField.startsWith("Rocket")) // a Python Web Server, see Web2py.com
752 pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningProbablySupported;
754 pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningSupportUnknown;
758 // called when the connection broke and we need to queue some pipelined requests again
759 void QHttpNetworkConnectionChannel::requeueCurrentlyPipelinedRequests()
761 for (int i = 0; i < alreadyPipelinedRequests.length(); i++)
762 connection->d_func()->requeueRequest(alreadyPipelinedRequests.at(i));
763 alreadyPipelinedRequests.clear();
765 // only run when the QHttpNetworkConnection is not currently being destructed, e.g.
766 // this function is called from _q_disconnected which is called because
767 // of ~QHttpNetworkConnectionPrivate
768 if (qobject_cast<QHttpNetworkConnection*>(connection))
769 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
772 void QHttpNetworkConnectionChannel::handleStatus()
777 int statusCode = reply->statusCode();
780 switch (statusCode) {
781 case 401: // auth required
782 case 407: // proxy auth required
783 if (connection->d_func()->handleAuthenticateChallenge(socket, reply, (statusCode == 407), resend)) {
785 if (!resetUploadData())
788 reply->d_func()->eraseData();
790 if (alreadyPipelinedRequests.isEmpty()) {
791 // this does a re-send without closing the connection
792 resendCurrent = true;
793 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
795 // we had requests pipelined.. better close the connection in closeAndResendCurrentRequest
796 closeAndResendCurrentRequest();
797 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
800 //authentication cancelled, close the channel.
804 emit reply->headerChanged();
805 emit reply->readyRead();
806 QNetworkReply::NetworkError errorCode = (statusCode == 407)
807 ? QNetworkReply::ProxyAuthenticationRequiredError
808 : QNetworkReply::AuthenticationRequiredError;
809 reply->d_func()->errorString = connection->d_func()->errorDetail(errorCode, socket);
810 emit reply->finishedWithError(errorCode, reply->d_func()->errorString);
814 if (qobject_cast<QHttpNetworkConnection*>(connection))
815 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
819 bool QHttpNetworkConnectionChannel::resetUploadData()
822 //this happens if server closes connection while QHttpNetworkConnectionPrivate::_q_startNextRequest is pending
825 QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
826 if (!uploadByteDevice)
829 if (uploadByteDevice->reset()) {
833 connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ContentReSendError);
839 void QHttpNetworkConnectionChannel::pipelineInto(HttpMessagePair &pair)
841 // this is only called for simple GET
843 QHttpNetworkRequest &request = pair.first;
844 QHttpNetworkReply *reply = pair.second;
845 reply->d_func()->clear();
846 reply->d_func()->connection = connection;
847 reply->d_func()->connectionChannel = this;
848 reply->d_func()->autoDecompress = request.d->autoDecompress;
849 reply->d_func()->pipeliningUsed = true;
851 #ifndef QT_NO_NETWORKPROXY
852 pipeline.append(QHttpNetworkRequestPrivate::header(request,
853 (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy)));
855 pipeline.append(QHttpNetworkRequestPrivate::header(request, false));
858 alreadyPipelinedRequests.append(pair);
860 // pipelineFlush() needs to be called at some point afterwards
863 void QHttpNetworkConnectionChannel::pipelineFlush()
865 if (pipeline.isEmpty())
868 // The goal of this is so that we have everything in one TCP packet.
869 // For the Unbuffered QTcpSocket this is manually needed, the buffered
870 // QTcpSocket does it automatically.
871 // Also, sometimes the OS does it for us (Nagle's algorithm) but that
872 // happens only sometimes.
873 socket->write(pipeline);
878 void QHttpNetworkConnectionChannel::closeAndResendCurrentRequest()
880 requeueCurrentlyPipelinedRequests();
883 resendCurrent = true;
884 if (qobject_cast<QHttpNetworkConnection*>(connection))
885 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
888 bool QHttpNetworkConnectionChannel::isSocketBusy() const
890 return (state & QHttpNetworkConnectionChannel::BusyState);
893 bool QHttpNetworkConnectionChannel::isSocketWriting() const
895 return (state & QHttpNetworkConnectionChannel::WritingState);
898 bool QHttpNetworkConnectionChannel::isSocketWaiting() const
900 return (state & QHttpNetworkConnectionChannel::WaitingState);
903 bool QHttpNetworkConnectionChannel::isSocketReading() const
905 return (state & QHttpNetworkConnectionChannel::ReadingState);
909 void QHttpNetworkConnectionChannel::_q_readyRead()
911 if (socket->state() == QAbstractSocket::ConnectedState && socket->bytesAvailable() == 0) {
912 // We got a readyRead but no bytes are available..
913 // This happens for the Unbuffered QTcpSocket
914 // Also check if socket is in ConnectedState since
915 // this function may also be invoked via the event loop.
917 qint64 ret = socket->peek(&c, 1);
919 _q_error(socket->error());
920 // We still need to handle the reply so it emits its signals etc.
927 if (isSocketWaiting() || isSocketReading()) {
928 state = QHttpNetworkConnectionChannel::ReadingState;
934 void QHttpNetworkConnectionChannel::_q_bytesWritten(qint64 bytes)
937 // bytes have been written to the socket. write even more of them :)
938 if (isSocketWriting())
940 // otherwise we do nothing
943 void QHttpNetworkConnectionChannel::_q_disconnected()
945 if (state == QHttpNetworkConnectionChannel::ClosingState) {
946 state = QHttpNetworkConnectionChannel::IdleState;
947 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
951 // read the available data before closing
952 if (isSocketWaiting() || isSocketReading()) {
954 state = QHttpNetworkConnectionChannel::ReadingState;
957 } else if (state == QHttpNetworkConnectionChannel::IdleState && resendCurrent) {
958 // re-sending request because the socket was in ClosingState
959 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
961 state = QHttpNetworkConnectionChannel::IdleState;
963 requeueCurrentlyPipelinedRequests();
968 void QHttpNetworkConnectionChannel::_q_connected()
970 // For the Happy Eyeballs we need to check if this is the first channel to connect.
971 if (!pendingEncrypt) {
972 if (connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::InProgress) {
973 if (connection->d_func()->delayedConnectionTimer.isActive())
974 connection->d_func()->delayedConnectionTimer.stop();
975 if (networkLayerPreference == QAbstractSocket::IPv4Protocol)
976 connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv4;
977 else if (networkLayerPreference == QAbstractSocket::IPv6Protocol)
978 connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv6;
980 if (socket->peerAddress().protocol() == QAbstractSocket::IPv4Protocol)
981 connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv4;
983 connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv6;
986 if (((connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::IPv4) && (networkLayerPreference != QAbstractSocket::IPv4Protocol))
987 || ((connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::IPv6) && (networkLayerPreference != QAbstractSocket::IPv6Protocol))) {
989 // This is the second connection so it has to be closed and we can schedule it for another request.
990 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
993 //The connections networkLayerState had already been decided.
997 // improve performance since we get the request sent by the kernel ASAP
998 //socket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
999 // We have this commented out now. It did not have the effect we wanted. If we want to
1000 // do this properly, Qt has to combine multiple HTTP requests into one buffer
1001 // and send this to the kernel in one syscall and then the kernel immediately sends
1002 // it as one TCP packet because of TCP_NODELAY.
1003 // However, this code is currently not in Qt, so we rely on the kernel combining
1004 // the requests into one TCP packet.
1006 // not sure yet if it helps, but it makes sense
1007 socket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
1009 pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningSupportUnknown;
1011 // ### FIXME: if the server closes the connection unexpectedly, we shouldn't send the same broken request again!
1012 //channels[i].reconnectAttempts = 2;
1013 if (!pendingEncrypt) {
1014 state = QHttpNetworkConnectionChannel::IdleState;
1016 connection->d_func()->dequeueRequest(socket);
1023 void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socketError)
1027 QNetworkReply::NetworkError errorCode = QNetworkReply::UnknownNetworkError;
1029 switch (socketError) {
1030 case QAbstractSocket::HostNotFoundError:
1031 errorCode = QNetworkReply::HostNotFoundError;
1033 case QAbstractSocket::ConnectionRefusedError:
1034 errorCode = QNetworkReply::ConnectionRefusedError;
1036 case QAbstractSocket::RemoteHostClosedError:
1037 // try to reconnect/resend before sending an error.
1038 // while "Reading" the _q_disconnected() will handle this.
1039 if (state != QHttpNetworkConnectionChannel::IdleState && state != QHttpNetworkConnectionChannel::ReadingState) {
1040 if (reconnectAttempts-- > 0) {
1041 closeAndResendCurrentRequest();
1044 errorCode = QNetworkReply::RemoteHostClosedError;
1046 } else if (state == QHttpNetworkConnectionChannel::ReadingState) {
1047 if (!reply->d_func()->expectContent()) {
1048 // No content expected, this is a valid way to have the connection closed by the server
1051 if (reply->contentLength() == -1 && !reply->d_func()->isChunked()) {
1052 // There was no content-length header and it's not chunked encoding,
1053 // so this is a valid way to have the connection closed by the server
1056 // ok, we got a disconnect even though we did not expect it
1057 errorCode = QNetworkReply::RemoteHostClosedError;
1059 errorCode = QNetworkReply::RemoteHostClosedError;
1062 case QAbstractSocket::SocketTimeoutError:
1063 // try to reconnect/resend before sending an error.
1064 if (state == QHttpNetworkConnectionChannel::WritingState && (reconnectAttempts-- > 0)) {
1065 closeAndResendCurrentRequest();
1068 errorCode = QNetworkReply::TimeoutError;
1070 case QAbstractSocket::ProxyAuthenticationRequiredError:
1071 errorCode = QNetworkReply::ProxyAuthenticationRequiredError;
1073 case QAbstractSocket::SslHandshakeFailedError:
1074 errorCode = QNetworkReply::SslHandshakeFailedError;
1077 // all other errors are treated as NetworkError
1078 errorCode = QNetworkReply::UnknownNetworkError;
1081 QPointer<QHttpNetworkConnection> that = connection;
1082 QString errorString = connection->d_func()->errorDetail(errorCode, socket, socket->errorString());
1084 // In the InProgress state the channel should not emit the error.
1085 // This will instead be handled by the connection.
1086 if (!connection->d_func()->shouldEmitChannelError(socket))
1089 // Need to dequeu the request so that we can emit the error.
1091 connection->d_func()->dequeueRequest(socket);
1093 reply->d_func()->errorString = errorString;
1094 emit reply->finishedWithError(errorCode, errorString);
1097 // send the next request
1098 QMetaObject::invokeMethod(that, "_q_startNextRequest", Qt::QueuedConnection);
1100 if (that) //signal emission triggered event loop
1104 #ifndef QT_NO_NETWORKPROXY
1105 void QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator* auth)
1107 // Need to dequeue the request before we can emit the error.
1109 connection->d_func()->dequeueRequest(socket);
1111 connection->d_func()->emitProxyAuthenticationRequired(this, proxy, auth);
1115 void QHttpNetworkConnectionChannel::_q_uploadDataReadyRead()
1121 void QHttpNetworkConnectionChannel::_q_encrypted()
1124 return; // ### error
1125 state = QHttpNetworkConnectionChannel::IdleState;
1126 pendingEncrypt = false;
1128 connection->d_func()->dequeueRequest(socket);
1133 void QHttpNetworkConnectionChannel::_q_sslErrors(const QList<QSslError> &errors)
1137 //QNetworkReply::NetworkError errorCode = QNetworkReply::ProtocolFailure;
1138 // Also pause the connection because socket notifiers may fire while an user
1139 // dialog is displaying
1140 connection->d_func()->pauseConnection();
1141 if (pendingEncrypt && !reply)
1142 connection->d_func()->dequeueRequest(socket);
1144 emit reply->sslErrors(errors);
1145 connection->d_func()->resumeConnection();
1148 void QHttpNetworkConnectionChannel::_q_encryptedBytesWritten(qint64 bytes)
1151 // bytes have been written to the socket. write even more of them :)
1152 if (isSocketWriting())
1154 // otherwise we do nothing
1159 void QHttpNetworkConnectionChannel::setConnection(QHttpNetworkConnection *c)
1161 // Inlining this function in the header leads to compiler error on
1162 // release-armv5, on at least timebox 9.2 and 10.1.
1168 #include "moc_qhttpnetworkconnectionchannel_p.cpp"
1170 #endif // QT_NO_HTTP