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()
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>("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);
300 } else if (readPointer == 0 || currentReadSize == 0) {
301 // nothing to read currently, break the loop
304 qint64 currentWriteSize = socket->write(readPointer, currentReadSize);
305 if (currentWriteSize == -1 || currentWriteSize != currentReadSize) {
307 connection->d_func()->emitReplyError(socket, reply, QNetworkReply::UnknownNetworkError);
310 written += currentWriteSize;
311 uploadByteDevice->advanceReadPointer(currentWriteSize);
313 emit reply->dataSendProgress(written, bytesTotal);
315 if (written == bytesTotal) {
316 // make sure this function is called once again
317 state = QHttpNetworkConnectionChannel::WaitingState;
327 case QHttpNetworkConnectionChannel::WaitingState:
329 QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
330 if (uploadByteDevice) {
331 QObject::disconnect(uploadByteDevice, SIGNAL(readyRead()), this, SLOT(_q_uploadDataReadyRead()));
335 //connection->d_func()->fillPipeline(socket);
338 // ensure we try to receive a reply in all cases, even if _q_readyRead_ hat not been called
339 // this is needed if the sends an reply before we have finished sending the request. In that
340 // case receiveReply had been called before but ignored the server reply
341 if (socket->bytesAvailable())
342 QMetaObject::invokeMethod(this, "_q_receiveReply", Qt::QueuedConnection);
345 case QHttpNetworkConnectionChannel::ReadingState:
346 // ignore _q_bytesWritten in these states
355 void QHttpNetworkConnectionChannel::_q_receiveReply()
360 if (socket->bytesAvailable() > 0)
361 qWarning() << "QHttpNetworkConnectionChannel::_q_receiveReply() called without QHttpNetworkReply,"
362 << socket->bytesAvailable() << "bytes on socket.";
367 // only run when the QHttpNetworkConnection is not currently being destructed, e.g.
368 // this function is called from _q_disconnected which is called because
369 // of ~QHttpNetworkConnectionPrivate
370 if (!qobject_cast<QHttpNetworkConnection*>(connection)) {
374 QAbstractSocket::SocketState socketState = socket->state();
376 // connection might be closed to signal the end of data
377 if (socketState == QAbstractSocket::UnconnectedState) {
378 if (socket->bytesAvailable() <= 0) {
379 if (reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) {
380 // finish this reply. this case happens when the server did not send a content length
381 reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState;
385 handleUnexpectedEOF();
389 // socket not connected but still bytes for reading.. just continue in this function
393 // read loop for the response
395 qint64 lastBytes = bytes;
399 QHttpNetworkReplyPrivate::ReplyState state = reply->d_func()->state;
401 case QHttpNetworkReplyPrivate::NothingDoneState: {
402 state = reply->d_func()->state = QHttpNetworkReplyPrivate::ReadingStatusState;
405 case QHttpNetworkReplyPrivate::ReadingStatusState: {
406 qint64 statusBytes = reply->d_func()->readStatus(socket);
407 if (statusBytes == -1) {
408 // connection broke while reading status. also handled if later _q_disconnected is called
409 handleUnexpectedEOF();
412 bytes += statusBytes;
413 lastStatus = reply->d_func()->statusCode;
416 case QHttpNetworkReplyPrivate::ReadingHeaderState: {
417 QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
418 qint64 headerBytes = replyPrivate->readHeader(socket);
419 if (headerBytes == -1) {
420 // connection broke while reading headers. also handled if later _q_disconnected is called
421 handleUnexpectedEOF();
424 bytes += headerBytes;
425 // If headers were parsed successfully now it is the ReadingDataState
426 if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState) {
427 if (replyPrivate->isCompressed() && replyPrivate->autoDecompress) {
428 // remove the Content-Length from header
429 replyPrivate->removeAutoDecompressHeader();
431 replyPrivate->autoDecompress = false;
433 if (replyPrivate->statusCode == 100) {
434 replyPrivate->clearHttpLayerInformation();
435 replyPrivate->state = QHttpNetworkReplyPrivate::ReadingStatusState;
438 if (replyPrivate->shouldEmitSignals())
439 emit reply->headerChanged();
440 // After headerChanged had been emitted
441 // we can suddenly have a replyPrivate->userProvidedDownloadBuffer
442 // this is handled in the ReadingDataState however
444 if (!replyPrivate->expectContent()) {
445 replyPrivate->state = QHttpNetworkReplyPrivate::AllDoneState;
452 case QHttpNetworkReplyPrivate::ReadingDataState: {
453 QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
454 if (socket->state() == QAbstractSocket::ConnectedState &&
455 replyPrivate->downstreamLimited && !replyPrivate->responseData.isEmpty() && replyPrivate->shouldEmitSignals()) {
456 // (only do the following when still connected, not when we have already been disconnected and there is still data)
457 // We already have some HTTP body data. We don't read more from the socket until
458 // this is fetched by QHttpNetworkAccessHttpBackend. If we would read more,
459 // we could not limit our read buffer usage.
460 // We only do this when shouldEmitSignals==true because our HTTP parsing
461 // always needs to parse the 401/407 replies. Therefore they don't really obey
462 // to the read buffer maximum size, but we don't care since they should be small.
466 if (replyPrivate->userProvidedDownloadBuffer) {
467 // the user provided a direct buffer where we should put all our data in.
468 // this only works when we can tell the user the content length and he/she can allocate
469 // the buffer in that size.
470 // note that this call will read only from the still buffered data
471 qint64 haveRead = replyPrivate->readBodyVeryFast(socket, replyPrivate->userProvidedDownloadBuffer + replyPrivate->totalProgress);
474 replyPrivate->totalProgress += haveRead;
475 // the user will get notified of it via progress signal
476 emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
477 } else if (haveRead == 0) {
478 // Happens since this called in a loop. Currently no bytes available.
479 } else if (haveRead < 0) {
480 connection->d_func()->emitReplyError(socket, reply, QNetworkReply::RemoteHostClosedError);
483 } else if (!replyPrivate->isChunked() && !replyPrivate->autoDecompress
484 && replyPrivate->bodyLength > 0) {
485 // bulk files like images should fulfill these properties and
486 // we can therefore save on memory copying
487 qint64 haveRead = replyPrivate->readBodyFast(socket, &replyPrivate->responseData);
489 replyPrivate->totalProgress += haveRead;
490 if (replyPrivate->shouldEmitSignals()) {
491 emit reply->readyRead();
492 emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
497 // use the traditional slower reading (for compressed encoding, chunked encoding,
498 // no content-length etc)
499 qint64 haveRead = replyPrivate->readBody(socket, &replyPrivate->responseData);
502 replyPrivate->totalProgress += haveRead;
503 if (replyPrivate->shouldEmitSignals()) {
504 emit reply->readyRead();
505 emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
507 } else if (haveRead == -1) {
508 // Some error occured
509 connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ProtocolFailure);
513 // still in ReadingDataState? This function will be called again by the socket's readyRead
514 if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState)
517 // everything done, fall through
519 case QHttpNetworkReplyPrivate::AllDoneState:
525 } while (bytes != lastBytes && reply);
528 // called when unexpectedly reading a -1 or when data is expected but socket is closed
529 void QHttpNetworkConnectionChannel::handleUnexpectedEOF()
532 if (reconnectAttempts <= 0) {
533 // too many errors reading/receiving/parsing the status, close the socket and emit error
534 requeueCurrentlyPipelinedRequests();
536 reply->d_func()->errorString = connection->d_func()->errorDetail(QNetworkReply::RemoteHostClosedError, socket);
537 emit reply->finishedWithError(QNetworkReply::RemoteHostClosedError, reply->d_func()->errorString);
538 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
541 reply->d_func()->clear();
542 reply->d_func()->connection = connection;
543 reply->d_func()->connectionChannel = this;
544 closeAndResendCurrentRequest();
548 bool QHttpNetworkConnectionChannel::ensureConnection()
553 QAbstractSocket::SocketState socketState = socket->state();
555 // resend this request after we receive the disconnected signal
556 if (socketState == QAbstractSocket::ClosingState) {
558 resendCurrent = true;
562 // already trying to connect?
563 if (socketState == QAbstractSocket::HostLookupState ||
564 socketState == QAbstractSocket::ConnectingState) {
568 // make sure that this socket is in a connected state, if not initiate
569 // connection to the host.
570 if (socketState != QAbstractSocket::ConnectedState) {
571 // connect to the host if not already connected.
572 state = QHttpNetworkConnectionChannel::ConnectingState;
573 pendingEncrypt = ssl;
576 pipeliningSupported = PipeliningSupportUnknown;
577 authenticationCredentialsSent = false;
578 proxyCredentialsSent = false;
579 authenticator.detach();
580 QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(authenticator);
581 priv->hasFailed = false;
582 proxyAuthenticator.detach();
583 priv = QAuthenticatorPrivate::getPrivate(proxyAuthenticator);
584 priv->hasFailed = false;
586 // This workaround is needed since we use QAuthenticator for NTLM authentication. The "phase == Done"
587 // is the usual criteria for emitting authentication signals. The "phase" is set to "Done" when the
588 // last header for Authorization is generated by the QAuthenticator. Basic & Digest logic does not
589 // check the "phase" for generating the Authorization header. NTLM authentication is a two stage
590 // process & needs the "phase". To make sure the QAuthenticator uses the current username/password
591 // the phase is reset to Start.
592 priv = QAuthenticatorPrivate::getPrivate(authenticator);
593 if (priv && priv->phase == QAuthenticatorPrivate::Done)
594 priv->phase = QAuthenticatorPrivate::Start;
595 priv = QAuthenticatorPrivate::getPrivate(proxyAuthenticator);
596 if (priv && priv->phase == QAuthenticatorPrivate::Done)
597 priv->phase = QAuthenticatorPrivate::Start;
599 QString connectHost = connection->d_func()->hostName;
600 qint16 connectPort = connection->d_func()->port;
602 #ifndef QT_NO_NETWORKPROXY
603 // HTTPS always use transparent proxy.
604 if (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy && !ssl) {
605 connectHost = connection->d_func()->networkProxy.hostName();
606 connectPort = connection->d_func()->networkProxy.port();
608 if (socket->proxy().type() == QNetworkProxy::HttpProxy) {
609 // Make user-agent field available to HTTP proxy socket engine (QTBUG-17223)
611 // ensureConnection is called before any request has been assigned, but can also be called again if reconnecting
612 if (request.url().isEmpty())
613 value = connection->d_func()->predictNextRequest().headerField("user-agent");
615 value = request.headerField("user-agent");
616 if (!value.isEmpty()) {
617 QNetworkProxy proxy(socket->proxy());
618 proxy.setRawHeader("User-Agent", value); //detaches
619 socket->setProxy(proxy);
625 QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
626 sslSocket->connectToHostEncrypted(connectHost, connectPort, QIODevice::ReadWrite, networkLayerPreference);
627 if (ignoreAllSslErrors)
628 sslSocket->ignoreSslErrors();
629 sslSocket->ignoreSslErrors(ignoreSslErrorsList);
631 // limit the socket read buffer size. we will read everything into
632 // the QHttpNetworkReply anyway, so let's grow only that and not
634 socket->setReadBufferSize(64*1024);
636 // Need to dequeue the request so that we can emit the error.
638 connection->d_func()->dequeueRequest(socket);
639 connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ProtocolUnknownError);
642 // In case of no proxy we can use the Unbuffered QTcpSocket
643 #ifndef QT_NO_NETWORKPROXY
644 if (connection->d_func()->networkProxy.type() == QNetworkProxy::NoProxy
645 && connection->cacheProxy().type() == QNetworkProxy::NoProxy
646 && connection->transparentProxy().type() == QNetworkProxy::NoProxy) {
648 socket->connectToHost(connectHost, connectPort, QIODevice::ReadWrite | QIODevice::Unbuffered, networkLayerPreference);
649 // For an Unbuffered QTcpSocket, the read buffer size has a special meaning.
650 socket->setReadBufferSize(1*1024);
651 #ifndef QT_NO_NETWORKPROXY
653 socket->connectToHost(connectHost, connectPort, QIODevice::ReadWrite, networkLayerPreference);
655 // limit the socket read buffer size. we will read everything into
656 // the QHttpNetworkReply anyway, so let's grow only that and not
658 socket->setReadBufferSize(64*1024);
667 void QHttpNetworkConnectionChannel::allDone()
672 qWarning() << "QHttpNetworkConnectionChannel::allDone() called without reply. Please report at http://bugreports.qt-project.org/";
676 // while handling 401 & 407, we might reset the status code, so save this.
677 bool emitFinished = reply->d_func()->shouldEmitSignals();
678 bool connectionCloseEnabled = reply->d_func()->isConnectionCloseEnabled();
679 detectPipeliningSupport();
682 // handleStatus() might have removed the reply because it already called connection->emitReplyError()
684 // queue the finished signal, this is required since we might send new requests from
685 // slot connected to it. The socket will not fire readyRead signal, if we are already
686 // in the slot connected to readyRead
687 if (reply && emitFinished)
688 QMetaObject::invokeMethod(reply, "finished", Qt::QueuedConnection);
691 // reset the reconnection attempts after we receive a complete reply.
692 // in case of failures, each channel will attempt two reconnects before emitting error.
693 reconnectAttempts = 2;
695 // now the channel can be seen as free/idle again, all signal emissions for the reply have been done
696 if (state != QHttpNetworkConnectionChannel::ClosingState)
697 state = QHttpNetworkConnectionChannel::IdleState;
699 // if it does not need to be sent again we can set it to 0
700 // the previous code did not do that and we had problems with accidental re-sending of a
702 // Note that this may trigger a segfault at some other point. But then we can fix the underlying
704 if (!resendCurrent) {
705 request = QHttpNetworkRequest();
709 // move next from pipeline to current request
710 if (!alreadyPipelinedRequests.isEmpty()) {
711 if (resendCurrent || connectionCloseEnabled || socket->state() != QAbstractSocket::ConnectedState) {
712 // move the pipelined ones back to the main queue
713 requeueCurrentlyPipelinedRequests();
716 // there were requests pipelined in and we can continue
717 HttpMessagePair messagePair = alreadyPipelinedRequests.takeFirst();
719 request = messagePair.first;
720 reply = messagePair.second;
721 state = QHttpNetworkConnectionChannel::ReadingState;
722 resendCurrent = false;
724 written = 0; // message body, excluding the header, irrelevant here
725 bytesTotal = 0; // message body total, excluding the header, irrelevant here
727 // pipeline even more
728 connection->d_func()->fillPipeline(socket);
732 // this was wrong, allDone gets called from that function anyway.
734 } else if (alreadyPipelinedRequests.isEmpty() && socket->bytesAvailable() > 0) {
735 // this is weird. we had nothing pipelined but still bytes available. better close it.
738 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
739 } else if (alreadyPipelinedRequests.isEmpty()) {
740 if (connectionCloseEnabled)
741 if (socket->state() != QAbstractSocket::UnconnectedState)
743 if (qobject_cast<QHttpNetworkConnection*>(connection))
744 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
748 void QHttpNetworkConnectionChannel::detectPipeliningSupport()
751 // detect HTTP Pipelining support
752 QByteArray serverHeaderField;
754 // check for HTTP/1.1
755 (reply->d_func()->majorVersion == 1 && reply->d_func()->minorVersion == 1)
756 // check for not having connection close
757 && (!reply->d_func()->isConnectionCloseEnabled())
758 // check if it is still connected
759 && (socket->state() == QAbstractSocket::ConnectedState)
760 // check for broken servers in server reply header
761 // this is adapted from http://mxr.mozilla.org/firefox/ident?i=SupportsPipelining
762 && (serverHeaderField = reply->headerField("Server"), !serverHeaderField.contains("Microsoft-IIS/4."))
763 && (!serverHeaderField.contains("Microsoft-IIS/5."))
764 && (!serverHeaderField.contains("Netscape-Enterprise/3."))
765 // this is adpoted from the knowledge of the Nokia 7.x browser team (DEF143319)
766 && (!serverHeaderField.contains("WebLogic"))
767 && (!serverHeaderField.startsWith("Rocket")) // a Python Web Server, see Web2py.com
769 pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningProbablySupported;
771 pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningSupportUnknown;
775 // called when the connection broke and we need to queue some pipelined requests again
776 void QHttpNetworkConnectionChannel::requeueCurrentlyPipelinedRequests()
778 for (int i = 0; i < alreadyPipelinedRequests.length(); i++)
779 connection->d_func()->requeueRequest(alreadyPipelinedRequests.at(i));
780 alreadyPipelinedRequests.clear();
782 // only run when the QHttpNetworkConnection is not currently being destructed, e.g.
783 // this function is called from _q_disconnected which is called because
784 // of ~QHttpNetworkConnectionPrivate
785 if (qobject_cast<QHttpNetworkConnection*>(connection))
786 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
789 void QHttpNetworkConnectionChannel::handleStatus()
794 int statusCode = reply->statusCode();
797 switch (statusCode) {
798 case 401: // auth required
799 case 407: // proxy auth required
800 if (connection->d_func()->handleAuthenticateChallenge(socket, reply, (statusCode == 407), resend)) {
802 if (!resetUploadData())
805 reply->d_func()->eraseData();
807 if (alreadyPipelinedRequests.isEmpty()) {
808 // this does a re-send without closing the connection
809 resendCurrent = true;
810 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
812 // we had requests pipelined.. better close the connection in closeAndResendCurrentRequest
813 closeAndResendCurrentRequest();
814 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
817 //authentication cancelled, close the channel.
821 emit reply->headerChanged();
822 emit reply->readyRead();
823 QNetworkReply::NetworkError errorCode = (statusCode == 407)
824 ? QNetworkReply::ProxyAuthenticationRequiredError
825 : QNetworkReply::AuthenticationRequiredError;
826 reply->d_func()->errorString = connection->d_func()->errorDetail(errorCode, socket);
827 emit reply->finishedWithError(errorCode, reply->d_func()->errorString);
831 if (qobject_cast<QHttpNetworkConnection*>(connection))
832 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
836 bool QHttpNetworkConnectionChannel::resetUploadData()
839 //this happens if server closes connection while QHttpNetworkConnectionPrivate::_q_startNextRequest is pending
842 QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
843 if (!uploadByteDevice)
846 if (uploadByteDevice->reset()) {
850 connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ContentReSendError);
855 #ifndef QT_NO_NETWORKPROXY
857 void QHttpNetworkConnectionChannel::setProxy(const QNetworkProxy &networkProxy)
860 socket->setProxy(networkProxy);
862 proxy = networkProxy;
869 void QHttpNetworkConnectionChannel::ignoreSslErrors()
872 static_cast<QSslSocket *>(socket)->ignoreSslErrors();
874 ignoreAllSslErrors = true;
878 void QHttpNetworkConnectionChannel::ignoreSslErrors(const QList<QSslError> &errors)
881 static_cast<QSslSocket *>(socket)->ignoreSslErrors(errors);
883 ignoreSslErrorsList = errors;
886 void QHttpNetworkConnectionChannel::setSslConfiguration(const QSslConfiguration &config)
889 static_cast<QSslSocket *>(socket)->setSslConfiguration(config);
891 sslConfiguration = config;
896 void QHttpNetworkConnectionChannel::pipelineInto(HttpMessagePair &pair)
898 // this is only called for simple GET
900 QHttpNetworkRequest &request = pair.first;
901 QHttpNetworkReply *reply = pair.second;
902 reply->d_func()->clear();
903 reply->d_func()->connection = connection;
904 reply->d_func()->connectionChannel = this;
905 reply->d_func()->autoDecompress = request.d->autoDecompress;
906 reply->d_func()->pipeliningUsed = true;
908 #ifndef QT_NO_NETWORKPROXY
909 pipeline.append(QHttpNetworkRequestPrivate::header(request,
910 (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy)));
912 pipeline.append(QHttpNetworkRequestPrivate::header(request, false));
915 alreadyPipelinedRequests.append(pair);
917 // pipelineFlush() needs to be called at some point afterwards
920 void QHttpNetworkConnectionChannel::pipelineFlush()
922 if (pipeline.isEmpty())
925 // The goal of this is so that we have everything in one TCP packet.
926 // For the Unbuffered QTcpSocket this is manually needed, the buffered
927 // QTcpSocket does it automatically.
928 // Also, sometimes the OS does it for us (Nagle's algorithm) but that
929 // happens only sometimes.
930 socket->write(pipeline);
935 void QHttpNetworkConnectionChannel::closeAndResendCurrentRequest()
937 requeueCurrentlyPipelinedRequests();
940 resendCurrent = true;
941 if (qobject_cast<QHttpNetworkConnection*>(connection))
942 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
945 bool QHttpNetworkConnectionChannel::isSocketBusy() const
947 return (state & QHttpNetworkConnectionChannel::BusyState);
950 bool QHttpNetworkConnectionChannel::isSocketWriting() const
952 return (state & QHttpNetworkConnectionChannel::WritingState);
955 bool QHttpNetworkConnectionChannel::isSocketWaiting() const
957 return (state & QHttpNetworkConnectionChannel::WaitingState);
960 bool QHttpNetworkConnectionChannel::isSocketReading() const
962 return (state & QHttpNetworkConnectionChannel::ReadingState);
966 void QHttpNetworkConnectionChannel::_q_readyRead()
968 if (socket->state() == QAbstractSocket::ConnectedState && socket->bytesAvailable() == 0) {
969 // We got a readyRead but no bytes are available..
970 // This happens for the Unbuffered QTcpSocket
971 // Also check if socket is in ConnectedState since
972 // this function may also be invoked via the event loop.
974 qint64 ret = socket->peek(&c, 1);
976 _q_error(socket->error());
977 // We still need to handle the reply so it emits its signals etc.
984 if (isSocketWaiting() || isSocketReading()) {
985 state = QHttpNetworkConnectionChannel::ReadingState;
991 void QHttpNetworkConnectionChannel::_q_bytesWritten(qint64 bytes)
994 // bytes have been written to the socket. write even more of them :)
995 if (isSocketWriting())
997 // otherwise we do nothing
1000 void QHttpNetworkConnectionChannel::_q_disconnected()
1002 if (state == QHttpNetworkConnectionChannel::ClosingState) {
1003 state = QHttpNetworkConnectionChannel::IdleState;
1004 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
1008 // read the available data before closing
1009 if (isSocketWaiting() || isSocketReading()) {
1011 state = QHttpNetworkConnectionChannel::ReadingState;
1014 } else if (state == QHttpNetworkConnectionChannel::IdleState && resendCurrent) {
1015 // re-sending request because the socket was in ClosingState
1016 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
1018 state = QHttpNetworkConnectionChannel::IdleState;
1020 requeueCurrentlyPipelinedRequests();
1025 void QHttpNetworkConnectionChannel::_q_connected()
1027 // For the Happy Eyeballs we need to check if this is the first channel to connect.
1028 if (!pendingEncrypt) {
1029 if (connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::InProgress) {
1030 if (connection->d_func()->delayedConnectionTimer.isActive())
1031 connection->d_func()->delayedConnectionTimer.stop();
1032 if (networkLayerPreference == QAbstractSocket::IPv4Protocol)
1033 connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv4;
1034 else if (networkLayerPreference == QAbstractSocket::IPv6Protocol)
1035 connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv6;
1037 if (socket->peerAddress().protocol() == QAbstractSocket::IPv4Protocol)
1038 connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv4;
1040 connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv6;
1042 connection->d_func()->networkLayerDetected(networkLayerPreference);
1044 if (((connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::IPv4) && (networkLayerPreference != QAbstractSocket::IPv4Protocol))
1045 || ((connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::IPv6) && (networkLayerPreference != QAbstractSocket::IPv6Protocol))) {
1047 // This is the second connection so it has to be closed and we can schedule it for another request.
1048 QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
1051 //The connections networkLayerState had already been decided.
1055 // improve performance since we get the request sent by the kernel ASAP
1056 //socket->setSocketOption(QAbstractSocket::LowDelayOption, 1);
1057 // We have this commented out now. It did not have the effect we wanted. If we want to
1058 // do this properly, Qt has to combine multiple HTTP requests into one buffer
1059 // and send this to the kernel in one syscall and then the kernel immediately sends
1060 // it as one TCP packet because of TCP_NODELAY.
1061 // However, this code is currently not in Qt, so we rely on the kernel combining
1062 // the requests into one TCP packet.
1064 // not sure yet if it helps, but it makes sense
1065 socket->setSocketOption(QAbstractSocket::KeepAliveOption, 1);
1067 pipeliningSupported = QHttpNetworkConnectionChannel::PipeliningSupportUnknown;
1069 // ### FIXME: if the server closes the connection unexpectedly, we shouldn't send the same broken request again!
1070 //channels[i].reconnectAttempts = 2;
1071 if (!pendingEncrypt) {
1072 state = QHttpNetworkConnectionChannel::IdleState;
1074 connection->d_func()->dequeueRequest(socket);
1081 void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socketError)
1085 QNetworkReply::NetworkError errorCode = QNetworkReply::UnknownNetworkError;
1087 switch (socketError) {
1088 case QAbstractSocket::HostNotFoundError:
1089 errorCode = QNetworkReply::HostNotFoundError;
1091 case QAbstractSocket::ConnectionRefusedError:
1092 errorCode = QNetworkReply::ConnectionRefusedError;
1094 case QAbstractSocket::RemoteHostClosedError:
1095 // try to reconnect/resend before sending an error.
1096 // while "Reading" the _q_disconnected() will handle this.
1097 if (state != QHttpNetworkConnectionChannel::IdleState && state != QHttpNetworkConnectionChannel::ReadingState) {
1098 if (reconnectAttempts-- > 0) {
1099 closeAndResendCurrentRequest();
1102 errorCode = QNetworkReply::RemoteHostClosedError;
1104 } else if (state == QHttpNetworkConnectionChannel::ReadingState) {
1105 if (!reply->d_func()->expectContent()) {
1106 // No content expected, this is a valid way to have the connection closed by the server
1109 if (reply->contentLength() == -1 && !reply->d_func()->isChunked()) {
1110 // There was no content-length header and it's not chunked encoding,
1111 // so this is a valid way to have the connection closed by the server
1114 // ok, we got a disconnect even though we did not expect it
1115 // Try to read everything from the socket before we emit the error.
1116 if (socket->bytesAvailable()) {
1117 // Read everything from the socket into the reply buffer.
1118 // we can ignore the readbuffersize as the data is already
1119 // in memory and we will not recieve more data on the socket.
1120 reply->setReadBufferSize(0);
1124 // QT_NO_OPENSSL. The QSslSocket can still have encrypted bytes in the plainsocket.
1125 // So we need to check this if the socket is a QSslSocket. When the socket is flushed
1126 // it will force a decrypt of the encrypted data in the plainsocket.
1127 QSslSocket *sslSocket = static_cast<QSslSocket*>(socket);
1128 qint64 beforeFlush = sslSocket->encryptedBytesAvailable();
1129 while (sslSocket->encryptedBytesAvailable()) {
1132 qint64 afterFlush = sslSocket->encryptedBytesAvailable();
1133 if (afterFlush == beforeFlush)
1135 beforeFlush = afterFlush;
1141 errorCode = QNetworkReply::RemoteHostClosedError;
1143 errorCode = QNetworkReply::RemoteHostClosedError;
1146 case QAbstractSocket::SocketTimeoutError:
1147 // try to reconnect/resend before sending an error.
1148 if (state == QHttpNetworkConnectionChannel::WritingState && (reconnectAttempts-- > 0)) {
1149 closeAndResendCurrentRequest();
1152 errorCode = QNetworkReply::TimeoutError;
1154 case QAbstractSocket::ProxyAuthenticationRequiredError:
1155 errorCode = QNetworkReply::ProxyAuthenticationRequiredError;
1157 case QAbstractSocket::SslHandshakeFailedError:
1158 errorCode = QNetworkReply::SslHandshakeFailedError;
1161 // all other errors are treated as NetworkError
1162 errorCode = QNetworkReply::UnknownNetworkError;
1165 QPointer<QHttpNetworkConnection> that = connection;
1166 QString errorString = connection->d_func()->errorDetail(errorCode, socket, socket->errorString());
1168 // In the InProgress state the channel should not emit the error.
1169 // This will instead be handled by the connection.
1170 if (!connection->d_func()->shouldEmitChannelError(socket))
1173 // Need to dequeu the request so that we can emit the error.
1175 connection->d_func()->dequeueRequest(socket);
1177 reply->d_func()->errorString = errorString;
1178 emit reply->finishedWithError(errorCode, errorString);
1181 // send the next request
1182 QMetaObject::invokeMethod(that, "_q_startNextRequest", Qt::QueuedConnection);
1184 if (that) //signal emission triggered event loop
1188 #ifndef QT_NO_NETWORKPROXY
1189 void QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator* auth)
1191 // Need to dequeue the request before we can emit the error.
1193 connection->d_func()->dequeueRequest(socket);
1195 connection->d_func()->emitProxyAuthenticationRequired(this, proxy, auth);
1199 void QHttpNetworkConnectionChannel::_q_uploadDataReadyRead()
1205 void QHttpNetworkConnectionChannel::_q_encrypted()
1208 return; // ### error
1209 state = QHttpNetworkConnectionChannel::IdleState;
1210 pendingEncrypt = false;
1212 connection->d_func()->dequeueRequest(socket);
1217 void QHttpNetworkConnectionChannel::_q_sslErrors(const QList<QSslError> &errors)
1221 //QNetworkReply::NetworkError errorCode = QNetworkReply::ProtocolFailure;
1222 // Also pause the connection because socket notifiers may fire while an user
1223 // dialog is displaying
1224 connection->d_func()->pauseConnection();
1225 if (pendingEncrypt && !reply)
1226 connection->d_func()->dequeueRequest(socket);
1228 emit reply->sslErrors(errors);
1229 connection->d_func()->resumeConnection();
1232 void QHttpNetworkConnectionChannel::_q_encryptedBytesWritten(qint64 bytes)
1235 // bytes have been written to the socket. write even more of them :)
1236 if (isSocketWriting())
1238 // otherwise we do nothing
1243 void QHttpNetworkConnectionChannel::setConnection(QHttpNetworkConnection *c)
1245 // Inlining this function in the header leads to compiler error on
1246 // release-armv5, on at least timebox 9.2 and 10.1.
1252 #include "moc_qhttpnetworkconnectionchannel_p.cpp"
1254 #endif // QT_NO_HTTP