summaryrefslogtreecommitdiffstats
path: root/src/network/access
diff options
context:
space:
mode:
authorPeter Hartmann <phartmann@blackberry.com>2014-01-21 16:28:01 +0100
committerThe Qt Project <gerrit-noreply@qt-project.org>2014-02-13 03:27:43 +0100
commit5b14bf342f43bd6cb02ad751db8da851850814bb (patch)
tree03f0ae2ca6aa5aa8a59f9e0b3fb1ef805ff43c26 /src/network/access
parent57f209497c3799c28838cbca314fa93140302aba (diff)
HTTP internals: introduce protocol handlers
... to defer the decision which protocol will be used on a specific channel. This is to allow using the SPDY protocol instead of HTTP (to be implemented in a later commit); which protocol will be used can only be decided after the SSL handshake. Change-Id: I6b538320668fe4994438f0095ecdc445677cf0a6 Reviewed-by: Peter Hartmann <phartmann@blackberry.com>
Diffstat (limited to 'src/network/access')
-rw-r--r--src/network/access/access.pri4
-rw-r--r--src/network/access/qabstractprotocolhandler.cpp68
-rw-r--r--src/network/access/qabstractprotocolhandler_p.h88
-rw-r--r--src/network/access/qhttpnetworkconnection_p.h1
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel.cpp382
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel_p.h4
-rw-r--r--src/network/access/qhttpnetworkreply_p.h1
-rw-r--r--src/network/access/qhttpprotocolhandler.cpp431
-rw-r--r--src/network/access/qhttpprotocolhandler_p.h77
9 files changed, 692 insertions, 364 deletions
diff --git a/src/network/access/access.pri b/src/network/access/access.pri
index aaaf05b551..3017417da8 100644
--- a/src/network/access/access.pri
+++ b/src/network/access/access.pri
@@ -7,6 +7,8 @@ HEADERS += \
access/qhttpnetworkreply_p.h \
access/qhttpnetworkconnection_p.h \
access/qhttpnetworkconnectionchannel_p.h \
+ access/qabstractprotocolhandler_p.h \
+ access/qhttpprotocolhandler_p.h \
access/qnetworkaccessauthenticationmanager_p.h \
access/qnetworkaccessmanager.h \
access/qnetworkaccessmanager_p.h \
@@ -43,6 +45,8 @@ SOURCES += \
access/qhttpnetworkreply.cpp \
access/qhttpnetworkconnection.cpp \
access/qhttpnetworkconnectionchannel.cpp \
+ access/qabstractprotocolhandler.cpp \
+ access/qhttpprotocolhandler.cpp \
access/qnetworkaccessauthenticationmanager.cpp \
access/qnetworkaccessmanager.cpp \
access/qnetworkaccesscache.cpp \
diff --git a/src/network/access/qabstractprotocolhandler.cpp b/src/network/access/qabstractprotocolhandler.cpp
new file mode 100644
index 0000000000..e72bb63236
--- /dev/null
+++ b/src/network/access/qabstractprotocolhandler.cpp
@@ -0,0 +1,68 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qabstractprotocolhandler_p.h>
+#include <private/qhttpnetworkconnectionchannel_p.h>
+
+#ifndef QT_NO_HTTP
+
+QT_BEGIN_NAMESPACE
+
+QAbstractProtocolHandler::QAbstractProtocolHandler(QHttpNetworkConnectionChannel *channel)
+ : m_channel(channel), m_reply(0), m_socket(m_channel->socket), m_connection(m_channel->connection)
+{
+ Q_ASSERT(m_channel);
+ Q_ASSERT(m_socket);
+ Q_ASSERT(m_connection);
+}
+
+QAbstractProtocolHandler::~QAbstractProtocolHandler()
+{
+}
+
+void QAbstractProtocolHandler::setReply(QHttpNetworkReply *reply)
+{
+ m_reply = reply;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_HTTP
diff --git a/src/network/access/qabstractprotocolhandler_p.h b/src/network/access/qabstractprotocolhandler_p.h
new file mode 100644
index 0000000000..387d08ccac
--- /dev/null
+++ b/src/network/access/qabstractprotocolhandler_p.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QABSTRACTPROTOCOLHANDLER_H
+#define QABSTRACTPROTOCOLHANDLER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the Network Access API. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifndef QT_NO_HTTP
+
+#include <QtCore/qglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+class QHttpNetworkConnectionChannel;
+class QHttpNetworkReply;
+class QAbstractSocket;
+class QHttpNetworkConnection;
+
+class QAbstractProtocolHandler {
+public:
+ QAbstractProtocolHandler(QHttpNetworkConnectionChannel *channel);
+ virtual ~QAbstractProtocolHandler();
+
+ virtual void _q_receiveReply() = 0;
+ virtual void _q_readyRead() = 0;
+ virtual bool sendRequest() = 0;
+ void setReply(QHttpNetworkReply *reply);
+
+protected:
+ QHttpNetworkConnectionChannel *m_channel;
+ QHttpNetworkReply *m_reply;
+ QAbstractSocket *m_socket;
+ QHttpNetworkConnection *m_connection;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_HTTP
+
+#endif // QABSTRACTPROTOCOLHANDLER_H
diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h
index 2aaaad24ac..526326c3fd 100644
--- a/src/network/access/qhttpnetworkconnection_p.h
+++ b/src/network/access/qhttpnetworkconnection_p.h
@@ -139,6 +139,7 @@ private:
friend class QHttpNetworkReply;
friend class QHttpNetworkReplyPrivate;
friend class QHttpNetworkConnectionChannel;
+ friend class QHttpProtocolHandler;
Q_PRIVATE_SLOT(d_func(), void _q_startNextRequest())
Q_PRIVATE_SLOT(d_func(), void _q_hostLookupFinished(QHostInfo))
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
index 6e786893ed..6f06c18732 100644
--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
@@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
** Contact: http://www.qt-project.org/legal
**
** This file is part of the QtNetwork module of the Qt Toolkit.
@@ -48,6 +49,8 @@
#ifndef QT_NO_HTTP
+#include <private/qhttpprotocolhandler_p.h>
+
#ifndef QT_NO_SSL
# include <QtNetwork/qsslkey.h>
# include <QtNetwork/qsslcipher.h>
@@ -78,6 +81,7 @@ QHttpNetworkConnectionChannel::QHttpNetworkConnectionChannel()
, proxyAuthMethod(QAuthenticatorPrivate::None)
, authenticationCredentialsSent(false)
, proxyCredentialsSent(false)
+ , protocolHandler(0)
#ifndef QT_NO_SSL
, ignoreAllSslErrors(false)
#endif
@@ -163,8 +167,8 @@ void QHttpNetworkConnectionChannel::init()
if (!sslConfiguration.isNull())
sslSocket->setSslConfiguration(sslConfiguration);
}
-
#endif
+ protocolHandler.reset(new QHttpProtocolHandler(this));
#ifndef QT_NO_NETWORKPROXY
if (proxy.type() != QNetworkProxy::NoProxy)
@@ -193,349 +197,21 @@ void QHttpNetworkConnectionChannel::close()
bool QHttpNetworkConnectionChannel::sendRequest()
{
- if (!reply) {
- // heh, how should that happen!
- qWarning() << "QHttpNetworkConnectionChannel::sendRequest() called without QHttpNetworkReply";
- state = QHttpNetworkConnectionChannel::IdleState;
- return false;
- }
-
- switch (state) {
- case QHttpNetworkConnectionChannel::IdleState: { // write the header
- if (!ensureConnection()) {
- // wait for the connection (and encryption) to be done
- // sendRequest will be called again from either
- // _q_connected or _q_encrypted
- return false;
- }
- QString scheme = request.url().scheme();
- if (scheme == QLatin1String("preconnect-http")
- || scheme == QLatin1String("preconnect-https")) {
- state = QHttpNetworkConnectionChannel::IdleState;
- reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState;
- allDone();
- connection->preConnectFinished(); // will only decrease the counter
- reply = 0; // so we can reuse this channel
- return true; // we have a working connection and are done
- }
-
- written = 0; // excluding the header
- bytesTotal = 0;
-
- QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
- replyPrivate->clear();
- replyPrivate->connection = connection;
- replyPrivate->connectionChannel = this;
- replyPrivate->autoDecompress = request.d->autoDecompress;
- replyPrivate->pipeliningUsed = false;
-
- // if the url contains authentication parameters, use the new ones
- // both channels will use the new authentication parameters
- if (!request.url().userInfo().isEmpty() && request.withCredentials()) {
- QUrl url = request.url();
- QAuthenticator &auth = authenticator;
- if (url.userName() != auth.user()
- || (!url.password().isEmpty() && url.password() != auth.password())) {
- auth.setUser(url.userName(QUrl::FullyDecoded));
- auth.setPassword(url.password(QUrl::FullyDecoded));
- connection->d_func()->copyCredentials(connection->d_func()->indexOf(socket), &auth, false);
- }
- // clear the userinfo, since we use the same request for resending
- // userinfo in url can conflict with the one in the authenticator
- url.setUserInfo(QString());
- request.setUrl(url);
- }
- // Will only be false if Qt WebKit is performing a cross-origin XMLHttpRequest
- // and withCredentials has not been set to true.
- if (request.withCredentials())
- connection->d_func()->createAuthorization(socket, request);
-#ifndef QT_NO_NETWORKPROXY
- QByteArray header = QHttpNetworkRequestPrivate::header(request,
- (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy));
-#else
- QByteArray header = QHttpNetworkRequestPrivate::header(request, false);
-#endif
- socket->write(header);
- // flushing is dangerous (QSslSocket calls transmit which might read or error)
-// socket->flush();
- QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
- if (uploadByteDevice) {
- // connect the signals so this function gets called again
- QObject::connect(uploadByteDevice, SIGNAL(readyRead()),this, SLOT(_q_uploadDataReadyRead()));
-
- bytesTotal = request.contentLength();
-
- state = QHttpNetworkConnectionChannel::WritingState; // start writing data
- sendRequest(); //recurse
- } else {
- state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response
- sendRequest(); //recurse
- }
-
- break;
- }
- case QHttpNetworkConnectionChannel::WritingState:
- {
- // write the data
- QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
- if (!uploadByteDevice || bytesTotal == written) {
- if (uploadByteDevice)
- emit reply->dataSendProgress(written, bytesTotal);
- state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response
- sendRequest(); // recurse
- break;
- }
-
- // only feed the QTcpSocket buffer when there is less than 32 kB in it
- const qint64 socketBufferFill = 32*1024;
- const qint64 socketWriteMaxSize = 16*1024;
-
-
-#ifndef QT_NO_SSL
- QSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);
- // if it is really an ssl socket, check more than just bytesToWrite()
- while ((socket->bytesToWrite() + (sslSocket ? sslSocket->encryptedBytesToWrite() : 0))
- <= socketBufferFill && bytesTotal != written)
-#else
- while (socket->bytesToWrite() <= socketBufferFill
- && bytesTotal != written)
-#endif
- {
- // get pointer to upload data
- qint64 currentReadSize = 0;
- qint64 desiredReadSize = qMin(socketWriteMaxSize, bytesTotal - written);
- const char *readPointer = uploadByteDevice->readPointer(desiredReadSize, currentReadSize);
-
- if (currentReadSize == -1) {
- // premature eof happened
- connection->d_func()->emitReplyError(socket, reply, QNetworkReply::UnknownNetworkError);
- return false;
- } else if (readPointer == 0 || currentReadSize == 0) {
- // nothing to read currently, break the loop
- break;
- } else {
- qint64 currentWriteSize = socket->write(readPointer, currentReadSize);
- if (currentWriteSize == -1 || currentWriteSize != currentReadSize) {
- // socket broke down
- connection->d_func()->emitReplyError(socket, reply, QNetworkReply::UnknownNetworkError);
- return false;
- } else {
- written += currentWriteSize;
- uploadByteDevice->advanceReadPointer(currentWriteSize);
-
- emit reply->dataSendProgress(written, bytesTotal);
-
- if (written == bytesTotal) {
- // make sure this function is called once again
- state = QHttpNetworkConnectionChannel::WaitingState;
- sendRequest();
- break;
- }
- }
- }
- }
- break;
- }
-
- case QHttpNetworkConnectionChannel::WaitingState:
- {
- QNonContiguousByteDevice* uploadByteDevice = request.uploadByteDevice();
- if (uploadByteDevice) {
- QObject::disconnect(uploadByteDevice, SIGNAL(readyRead()), this, SLOT(_q_uploadDataReadyRead()));
- }
-
- // HTTP pipelining
- //connection->d_func()->fillPipeline(socket);
- //socket->flush();
-
- // ensure we try to receive a reply in all cases, even if _q_readyRead_ hat not been called
- // this is needed if the sends an reply before we have finished sending the request. In that
- // case receiveReply had been called before but ignored the server reply
- if (socket->bytesAvailable())
- QMetaObject::invokeMethod(this, "_q_receiveReply", Qt::QueuedConnection);
- break;
- }
- case QHttpNetworkConnectionChannel::ReadingState:
- // ignore _q_bytesWritten in these states
- // fall through
- default:
- break;
- }
- return true;
+ Q_ASSERT(!protocolHandler.isNull());
+ return protocolHandler->sendRequest();
}
void QHttpNetworkConnectionChannel::_q_receiveReply()
{
- Q_ASSERT(socket);
-
- if (!reply) {
- if (socket->bytesAvailable() > 0)
- qWarning() << "QHttpNetworkConnectionChannel::_q_receiveReply() called without QHttpNetworkReply,"
- << socket->bytesAvailable() << "bytes on socket.";
- close();
- return;
- }
-
- // only run when the QHttpNetworkConnection is not currently being destructed, e.g.
- // this function is called from _q_disconnected which is called because
- // of ~QHttpNetworkConnectionPrivate
- if (!qobject_cast<QHttpNetworkConnection*>(connection)) {
- return;
- }
-
- QAbstractSocket::SocketState socketState = socket->state();
-
- // connection might be closed to signal the end of data
- if (socketState == QAbstractSocket::UnconnectedState) {
- if (socket->bytesAvailable() <= 0) {
- if (reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) {
- // finish this reply. this case happens when the server did not send a content length
- reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState;
- allDone();
- return;
- } else {
- handleUnexpectedEOF();
- return;
- }
- } else {
- // socket not connected but still bytes for reading.. just continue in this function
- }
- }
-
- // read loop for the response
- qint64 bytes = 0;
- qint64 lastBytes = bytes;
- do {
- lastBytes = bytes;
-
- QHttpNetworkReplyPrivate::ReplyState state = reply->d_func()->state;
- switch (state) {
- case QHttpNetworkReplyPrivate::NothingDoneState: {
- state = reply->d_func()->state = QHttpNetworkReplyPrivate::ReadingStatusState;
- // fallthrough
- }
- case QHttpNetworkReplyPrivate::ReadingStatusState: {
- qint64 statusBytes = reply->d_func()->readStatus(socket);
- if (statusBytes == -1) {
- // connection broke while reading status. also handled if later _q_disconnected is called
- handleUnexpectedEOF();
- return;
- }
- bytes += statusBytes;
- lastStatus = reply->d_func()->statusCode;
- break;
- }
- case QHttpNetworkReplyPrivate::ReadingHeaderState: {
- QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
- qint64 headerBytes = replyPrivate->readHeader(socket);
- if (headerBytes == -1) {
- // connection broke while reading headers. also handled if later _q_disconnected is called
- handleUnexpectedEOF();
- return;
- }
- bytes += headerBytes;
- // If headers were parsed successfully now it is the ReadingDataState
- if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState) {
- if (replyPrivate->isCompressed() && replyPrivate->autoDecompress) {
- // remove the Content-Length from header
- replyPrivate->removeAutoDecompressHeader();
- } else {
- replyPrivate->autoDecompress = false;
- }
- if (replyPrivate->statusCode == 100) {
- replyPrivate->clearHttpLayerInformation();
- replyPrivate->state = QHttpNetworkReplyPrivate::ReadingStatusState;
- break; // ignore
- }
- if (replyPrivate->shouldEmitSignals())
- emit reply->headerChanged();
- // After headerChanged had been emitted
- // we can suddenly have a replyPrivate->userProvidedDownloadBuffer
- // this is handled in the ReadingDataState however
-
- if (!replyPrivate->expectContent()) {
- replyPrivate->state = QHttpNetworkReplyPrivate::AllDoneState;
- allDone();
- break;
- }
- }
- break;
- }
- case QHttpNetworkReplyPrivate::ReadingDataState: {
- QHttpNetworkReplyPrivate *replyPrivate = reply->d_func();
- if (socket->state() == QAbstractSocket::ConnectedState &&
- replyPrivate->downstreamLimited && !replyPrivate->responseData.isEmpty() && replyPrivate->shouldEmitSignals()) {
- // (only do the following when still connected, not when we have already been disconnected and there is still data)
- // We already have some HTTP body data. We don't read more from the socket until
- // this is fetched by QHttpNetworkAccessHttpBackend. If we would read more,
- // we could not limit our read buffer usage.
- // We only do this when shouldEmitSignals==true because our HTTP parsing
- // always needs to parse the 401/407 replies. Therefore they don't really obey
- // to the read buffer maximum size, but we don't care since they should be small.
- return;
- }
-
- if (replyPrivate->userProvidedDownloadBuffer) {
- // the user provided a direct buffer where we should put all our data in.
- // this only works when we can tell the user the content length and he/she can allocate
- // the buffer in that size.
- // note that this call will read only from the still buffered data
- qint64 haveRead = replyPrivate->readBodyVeryFast(socket, replyPrivate->userProvidedDownloadBuffer + replyPrivate->totalProgress);
- if (haveRead > 0) {
- bytes += haveRead;
- replyPrivate->totalProgress += haveRead;
- // the user will get notified of it via progress signal
- emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
- } else if (haveRead == 0) {
- // Happens since this called in a loop. Currently no bytes available.
- } else if (haveRead < 0) {
- connection->d_func()->emitReplyError(socket, reply, QNetworkReply::RemoteHostClosedError);
- break;
- }
- } else if (!replyPrivate->isChunked() && !replyPrivate->autoDecompress
- && replyPrivate->bodyLength > 0) {
- // bulk files like images should fulfill these properties and
- // we can therefore save on memory copying
- qint64 haveRead = replyPrivate->readBodyFast(socket, &replyPrivate->responseData);
- bytes += haveRead;
- replyPrivate->totalProgress += haveRead;
- if (replyPrivate->shouldEmitSignals()) {
- emit reply->readyRead();
- emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
- }
- }
- else
- {
- // use the traditional slower reading (for compressed encoding, chunked encoding,
- // no content-length etc)
- qint64 haveRead = replyPrivate->readBody(socket, &replyPrivate->responseData);
- if (haveRead > 0) {
- bytes += haveRead;
- replyPrivate->totalProgress += haveRead;
- if (replyPrivate->shouldEmitSignals()) {
- emit reply->readyRead();
- emit reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
- }
- } else if (haveRead == -1) {
- // Some error occurred
- connection->d_func()->emitReplyError(socket, reply, QNetworkReply::ProtocolFailure);
- break;
- }
- }
- // still in ReadingDataState? This function will be called again by the socket's readyRead
- if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState)
- break;
+ Q_ASSERT(!protocolHandler.isNull());
+ protocolHandler->_q_receiveReply();
+}
- // everything done, fall through
- }
- case QHttpNetworkReplyPrivate::AllDoneState:
- allDone();
- break;
- default:
- break;
- }
- } while (bytes != lastBytes && reply);
+void QHttpNetworkConnectionChannel::_q_readyRead()
+{
+ Q_ASSERT(!protocolHandler.isNull());
+ protocolHandler->_q_readyRead();
}
// called when unexpectedly reading a -1 or when data is expected but socket is closed
@@ -724,6 +400,7 @@ void QHttpNetworkConnectionChannel::allDone()
if (!resendCurrent) {
request = QHttpNetworkRequest();
reply = 0;
+ protocolHandler->setReply(0);
}
// move next from pipeline to current request
@@ -738,6 +415,7 @@ void QHttpNetworkConnectionChannel::allDone()
request = messagePair.first;
reply = messagePair.second;
+ protocolHandler->setReply(messagePair.second);
state = QHttpNetworkConnectionChannel::ReadingState;
resendCurrent = false;
@@ -982,32 +660,6 @@ bool QHttpNetworkConnectionChannel::isSocketReading() const
return (state & QHttpNetworkConnectionChannel::ReadingState);
}
-//private slots
-void QHttpNetworkConnectionChannel::_q_readyRead()
-{
- if (socket->state() == QAbstractSocket::ConnectedState && socket->bytesAvailable() == 0) {
- // We got a readyRead but no bytes are available..
- // This happens for the Unbuffered QTcpSocket
- // Also check if socket is in ConnectedState since
- // this function may also be invoked via the event loop.
- char c;
- qint64 ret = socket->peek(&c, 1);
- if (ret < 0) {
- _q_error(socket->error());
- // We still need to handle the reply so it emits its signals etc.
- if (reply)
- _q_receiveReply();
- return;
- }
- }
-
- if (isSocketWaiting() || isSocketReading()) {
- state = QHttpNetworkConnectionChannel::ReadingState;
- if (reply)
- _q_receiveReply();
- }
-}
-
void QHttpNetworkConnectionChannel::_q_bytesWritten(qint64 bytes)
{
Q_UNUSED(bytes);
@@ -1224,6 +876,8 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
reply->d_func()->errorString = errorString;
emit reply->finishedWithError(errorCode, errorString);
reply = 0;
+ if (protocolHandler)
+ protocolHandler->setReply(0);
}
// send the next request
QMetaObject::invokeMethod(that, "_q_startNextRequest", Qt::QueuedConnection);
diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h
index c8138b5453..7230eb2543 100644
--- a/src/network/access/qhttpnetworkconnectionchannel_p.h
+++ b/src/network/access/qhttpnetworkconnectionchannel_p.h
@@ -66,6 +66,7 @@
#include <private/qhttpnetworkreply_p.h>
#include <private/qhttpnetworkconnection_p.h>
+#include <private/qabstractprotocolhandler_p.h>
#ifndef QT_NO_HTTP
@@ -117,6 +118,7 @@ public:
QAuthenticator proxyAuthenticator;
bool authenticationCredentialsSent;
bool proxyCredentialsSent;
+ QScopedPointer<QAbstractProtocolHandler> protocolHandler;
#ifndef QT_NO_SSL
bool ignoreAllSslErrors;
QList<QSslError> ignoreSslErrorsList;
@@ -193,6 +195,8 @@ public:
void _q_sslErrors(const QList<QSslError> &errors); // ssl errors from the socket
void _q_encryptedBytesWritten(qint64 bytes); // proceed sending
#endif
+
+ friend class QHttpProtocolHandler;
};
QT_END_NAMESPACE
diff --git a/src/network/access/qhttpnetworkreply_p.h b/src/network/access/qhttpnetworkreply_p.h
index 7aea9f14ec..583c3e426f 100644
--- a/src/network/access/qhttpnetworkreply_p.h
+++ b/src/network/access/qhttpnetworkreply_p.h
@@ -164,6 +164,7 @@ private:
friend class QHttpNetworkConnection;
friend class QHttpNetworkConnectionPrivate;
friend class QHttpNetworkConnectionChannel;
+ friend class QHttpProtocolHandler;
};
diff --git a/src/network/access/qhttpprotocolhandler.cpp b/src/network/access/qhttpprotocolhandler.cpp
new file mode 100644
index 0000000000..15cba48285
--- /dev/null
+++ b/src/network/access/qhttpprotocolhandler.cpp
@@ -0,0 +1,431 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qhttpprotocolhandler_p.h>
+#include <private/qnoncontiguousbytedevice_p.h>
+#include <private/qhttpnetworkconnectionchannel_p.h>
+
+#ifndef QT_NO_HTTP
+
+QT_BEGIN_NAMESPACE
+
+QHttpProtocolHandler::QHttpProtocolHandler(QHttpNetworkConnectionChannel *channel)
+ : QAbstractProtocolHandler(channel)
+{
+}
+
+void QHttpProtocolHandler::_q_receiveReply()
+{
+ Q_ASSERT(m_socket);
+
+ if (!m_reply) {
+ if (m_socket->bytesAvailable() > 0)
+ qWarning() << "QAbstractProtocolHandler::_q_receiveReply() called without QHttpNetworkReply,"
+ << m_socket->bytesAvailable() << "bytes on socket.";
+ m_channel->close();
+ return;
+ }
+
+ // only run when the QHttpNetworkConnection is not currently being destructed, e.g.
+ // this function is called from _q_disconnected which is called because
+ // of ~QHttpNetworkConnectionPrivate
+ if (!qobject_cast<QHttpNetworkConnection*>(m_connection)) {
+ return;
+ }
+
+ QAbstractSocket::SocketState socketState = m_socket->state();
+
+ // connection might be closed to signal the end of data
+ if (socketState == QAbstractSocket::UnconnectedState) {
+ if (m_socket->bytesAvailable() <= 0) {
+ if (m_reply->d_func()->state == QHttpNetworkReplyPrivate::ReadingDataState) {
+ // finish this reply. this case happens when the server did not send a content length
+ m_reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState;
+ m_channel->allDone();
+ return;
+ } else {
+ m_channel->handleUnexpectedEOF();
+ return;
+ }
+ } else {
+ // socket not connected but still bytes for reading.. just continue in this function
+ }
+ }
+
+ // read loop for the response
+ qint64 bytes = 0;
+ qint64 lastBytes = bytes;
+ do {
+ lastBytes = bytes;
+
+ QHttpNetworkReplyPrivate::ReplyState state = m_reply->d_func()->state;
+ switch (state) {
+ case QHttpNetworkReplyPrivate::NothingDoneState: {
+ m_reply->d_func()->state = QHttpNetworkReplyPrivate::ReadingStatusState;
+ // fallthrough
+ }
+ case QHttpNetworkReplyPrivate::ReadingStatusState: {
+ qint64 statusBytes = m_reply->d_func()->readStatus(m_socket);
+ if (statusBytes == -1) {
+ // connection broke while reading status. also handled if later _q_disconnected is called
+ m_channel->handleUnexpectedEOF();
+ return;
+ }
+ bytes += statusBytes;
+ m_channel->lastStatus = m_reply->d_func()->statusCode;
+ break;
+ }
+ case QHttpNetworkReplyPrivate::ReadingHeaderState: {
+ QHttpNetworkReplyPrivate *replyPrivate = m_reply->d_func();
+ qint64 headerBytes = replyPrivate->readHeader(m_socket);
+ if (headerBytes == -1) {
+ // connection broke while reading headers. also handled if later _q_disconnected is called
+ m_channel->handleUnexpectedEOF();
+ return;
+ }
+ bytes += headerBytes;
+ // If headers were parsed successfully now it is the ReadingDataState
+ if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState) {
+ if (replyPrivate->isCompressed() && replyPrivate->autoDecompress) {
+ // remove the Content-Length from header
+ replyPrivate->removeAutoDecompressHeader();
+ } else {
+ replyPrivate->autoDecompress = false;
+ }
+ if (replyPrivate->statusCode == 100) {
+ replyPrivate->clearHttpLayerInformation();
+ replyPrivate->state = QHttpNetworkReplyPrivate::ReadingStatusState;
+ break; // ignore
+ }
+ if (replyPrivate->shouldEmitSignals())
+ emit m_reply->headerChanged();
+ // After headerChanged had been emitted
+ // we can suddenly have a replyPrivate->userProvidedDownloadBuffer
+ // this is handled in the ReadingDataState however
+
+ if (!replyPrivate->expectContent()) {
+ replyPrivate->state = QHttpNetworkReplyPrivate::AllDoneState;
+ m_channel->allDone();
+ break;
+ }
+ }
+ break;
+ }
+ case QHttpNetworkReplyPrivate::ReadingDataState: {
+ QHttpNetworkReplyPrivate *replyPrivate = m_reply->d_func();
+ if (m_socket->state() == QAbstractSocket::ConnectedState &&
+ replyPrivate->downstreamLimited && !replyPrivate->responseData.isEmpty() && replyPrivate->shouldEmitSignals()) {
+ // (only do the following when still connected, not when we have already been disconnected and there is still data)
+ // We already have some HTTP body data. We don't read more from the socket until
+ // this is fetched by QHttpNetworkAccessHttpBackend. If we would read more,
+ // we could not limit our read buffer usage.
+ // We only do this when shouldEmitSignals==true because our HTTP parsing
+ // always needs to parse the 401/407 replies. Therefore they don't really obey
+ // to the read buffer maximum size, but we don't care since they should be small.
+ return;
+ }
+
+ if (replyPrivate->userProvidedDownloadBuffer) {
+ // the user provided a direct buffer where we should put all our data in.
+ // this only works when we can tell the user the content length and he/she can allocate
+ // the buffer in that size.
+ // note that this call will read only from the still buffered data
+ qint64 haveRead = replyPrivate->readBodyVeryFast(m_socket, replyPrivate->userProvidedDownloadBuffer + replyPrivate->totalProgress);
+ if (haveRead > 0) {
+ bytes += haveRead;
+ replyPrivate->totalProgress += haveRead;
+ // the user will get notified of it via progress signal
+ emit m_reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
+ } else if (haveRead == 0) {
+ // Happens since this called in a loop. Currently no bytes available.
+ } else if (haveRead < 0) {
+ m_connection->d_func()->emitReplyError(m_socket, m_reply, QNetworkReply::RemoteHostClosedError);
+ break;
+ }
+ } else if (!replyPrivate->isChunked() && !replyPrivate->autoDecompress
+ && replyPrivate->bodyLength > 0) {
+ // bulk files like images should fulfill these properties and
+ // we can therefore save on memory copying
+ qint64 haveRead = replyPrivate->readBodyFast(m_socket, &replyPrivate->responseData);
+ bytes += haveRead;
+ replyPrivate->totalProgress += haveRead;
+ if (replyPrivate->shouldEmitSignals()) {
+ emit m_reply->readyRead();
+ emit m_reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
+ }
+ }
+ else
+ {
+ // use the traditional slower reading (for compressed encoding, chunked encoding,
+ // no content-length etc)
+ qint64 haveRead = replyPrivate->readBody(m_socket, &replyPrivate->responseData);
+ if (haveRead > 0) {
+ bytes += haveRead;
+ replyPrivate->totalProgress += haveRead;
+ if (replyPrivate->shouldEmitSignals()) {
+ emit m_reply->readyRead();
+ emit m_reply->dataReadProgress(replyPrivate->totalProgress, replyPrivate->bodyLength);
+ }
+ } else if (haveRead == -1) {
+ // Some error occurred
+ m_connection->d_func()->emitReplyError(m_socket, m_reply, QNetworkReply::ProtocolFailure);
+ break;
+ }
+ }
+ // still in ReadingDataState? This function will be called again by the socket's readyRead
+ if (replyPrivate->state == QHttpNetworkReplyPrivate::ReadingDataState)
+ break;
+
+ // everything done, fall through
+ }
+ case QHttpNetworkReplyPrivate::AllDoneState:
+ m_channel->allDone();
+ break;
+ default:
+ break;
+ }
+ } while (bytes != lastBytes && m_reply);
+}
+
+void QHttpProtocolHandler::_q_readyRead()
+{
+ if (m_socket->state() == QAbstractSocket::ConnectedState && m_socket->bytesAvailable() == 0) {
+ // We got a readyRead but no bytes are available..
+ // This happens for the Unbuffered QTcpSocket
+ // Also check if socket is in ConnectedState since
+ // this function may also be invoked via the event loop.
+ char c;
+ qint64 ret = m_socket->peek(&c, 1);
+ if (ret < 0) {
+ m_channel->_q_error(m_socket->error());
+ // We still need to handle the reply so it emits its signals etc.
+ if (m_reply)
+ _q_receiveReply();
+ return;
+ }
+ }
+
+ if (m_channel->isSocketWaiting() || m_channel->isSocketReading()) {
+ m_channel->state = QHttpNetworkConnectionChannel::ReadingState;
+ if (m_reply)
+ _q_receiveReply();
+ }
+}
+
+bool QHttpProtocolHandler::sendRequest()
+{
+ m_reply = m_channel->reply;
+
+ if (!m_reply) {
+ // heh, how should that happen!
+ qWarning() << "QAbstractProtocolHandler::sendRequest() called without QHttpNetworkReply";
+ m_channel->state = QHttpNetworkConnectionChannel::IdleState;
+ return false;
+ }
+
+ switch (m_channel->state) {
+ case QHttpNetworkConnectionChannel::IdleState: { // write the header
+ if (!m_channel->ensureConnection()) {
+ // wait for the connection (and encryption) to be done
+ // sendRequest will be called again from either
+ // _q_connected or _q_encrypted
+ return false;
+ }
+ QString scheme = m_channel->request.url().scheme();
+ if (scheme == QLatin1String("preconnect-http")
+ || scheme == QLatin1String("preconnect-https")) {
+ m_channel->state = QHttpNetworkConnectionChannel::IdleState;
+ m_reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState;
+ m_channel->allDone();
+ m_connection->preConnectFinished(); // will only decrease the counter
+ m_reply = 0; // so we can reuse this channel
+ return true; // we have a working connection and are done
+ }
+
+ m_channel->written = 0; // excluding the header
+ m_channel->bytesTotal = 0;
+
+ QHttpNetworkReplyPrivate *replyPrivate = m_reply->d_func();
+ replyPrivate->clear();
+ replyPrivate->connection = m_connection;
+ replyPrivate->connectionChannel = m_channel;
+ replyPrivate->autoDecompress = m_channel->request.d->autoDecompress;
+ replyPrivate->pipeliningUsed = false;
+
+ // if the url contains authentication parameters, use the new ones
+ // both channels will use the new authentication parameters
+ if (!m_channel->request.url().userInfo().isEmpty() && m_channel->request.withCredentials()) {
+ QUrl url = m_channel->request.url();
+ QAuthenticator &auth = m_channel->authenticator;
+ if (url.userName() != auth.user()
+ || (!url.password().isEmpty() && url.password() != auth.password())) {
+ auth.setUser(url.userName());
+ auth.setPassword(url.password());
+ m_connection->d_func()->copyCredentials(m_connection->d_func()->indexOf(m_socket), &auth, false);
+ }
+ // clear the userinfo, since we use the same request for resending
+ // userinfo in url can conflict with the one in the authenticator
+ url.setUserInfo(QString());
+ m_channel->request.setUrl(url);
+ }
+ // Will only be false if Qt WebKit is performing a cross-origin XMLHttpRequest
+ // and withCredentials has not been set to true.
+ if (m_channel->request.withCredentials())
+ m_connection->d_func()->createAuthorization(m_socket, m_channel->request);
+#ifndef QT_NO_NETWORKPROXY
+ QByteArray header = QHttpNetworkRequestPrivate::header(m_channel->request,
+ (m_connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy));
+#else
+ QByteArray header = QHttpNetworkRequestPrivate::header(m_channel->request, false);
+#endif
+ m_socket->write(header);
+ // flushing is dangerous (QSslSocket calls transmit which might read or error)
+// m_socket->flush();
+ QNonContiguousByteDevice* uploadByteDevice = m_channel->request.uploadByteDevice();
+ if (uploadByteDevice) {
+ // connect the signals so this function gets called again
+ QObject::connect(uploadByteDevice, SIGNAL(readyRead()), m_channel, SLOT(_q_uploadDataReadyRead()));
+
+ m_channel->bytesTotal = m_channel->request.contentLength();
+
+ m_channel->state = QHttpNetworkConnectionChannel::WritingState; // start writing data
+ sendRequest(); //recurse
+ } else {
+ m_channel->state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response
+ sendRequest(); //recurse
+ }
+
+ break;
+ }
+ case QHttpNetworkConnectionChannel::WritingState:
+ {
+ // write the data
+ QNonContiguousByteDevice* uploadByteDevice = m_channel->request.uploadByteDevice();
+ if (!uploadByteDevice || m_channel->bytesTotal == m_channel->written) {
+ if (uploadByteDevice)
+ emit m_reply->dataSendProgress(m_channel->written, m_channel->bytesTotal);
+ m_channel->state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response
+ sendRequest(); // recurse
+ break;
+ }
+
+ // only feed the QTcpSocket buffer when there is less than 32 kB in it
+ const qint64 socketBufferFill = 32*1024;
+ const qint64 socketWriteMaxSize = 16*1024;
+
+
+#ifndef QT_NO_SSL
+ QSslSocket *sslSocket = qobject_cast<QSslSocket*>(m_socket);
+ // if it is really an ssl socket, check more than just bytesToWrite()
+ while ((m_socket->bytesToWrite() + (sslSocket ? sslSocket->encryptedBytesToWrite() : 0))
+ <= socketBufferFill && m_channel->bytesTotal != m_channel->written)
+#else
+ while (m_socket->bytesToWrite() <= socketBufferFill
+ && m_channel->bytesTotal != m_channel->written)
+#endif
+ {
+ // get pointer to upload data
+ qint64 currentReadSize = 0;
+ qint64 desiredReadSize = qMin(socketWriteMaxSize, m_channel->bytesTotal - m_channel->written);
+ const char *readPointer = uploadByteDevice->readPointer(desiredReadSize, currentReadSize);
+
+ if (currentReadSize == -1) {
+ // premature eof happened
+ m_connection->d_func()->emitReplyError(m_socket, m_reply, QNetworkReply::UnknownNetworkError);
+ return false;
+ } else if (readPointer == 0 || currentReadSize == 0) {
+ // nothing to read currently, break the loop
+ break;
+ } else {
+ qint64 currentWriteSize = m_socket->write(readPointer, currentReadSize);
+ if (currentWriteSize == -1 || currentWriteSize != currentReadSize) {
+ // socket broke down
+ m_connection->d_func()->emitReplyError(m_socket, m_reply, QNetworkReply::UnknownNetworkError);
+ return false;
+ } else {
+ m_channel->written += currentWriteSize;
+ uploadByteDevice->advanceReadPointer(currentWriteSize);
+
+ emit m_reply->dataSendProgress(m_channel->written, m_channel->bytesTotal);
+
+ if (m_channel->written == m_channel->bytesTotal) {
+ // make sure this function is called once again
+ m_channel->state = QHttpNetworkConnectionChannel::WaitingState;
+ sendRequest();
+ break;
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ case QHttpNetworkConnectionChannel::WaitingState:
+ {
+ QNonContiguousByteDevice* uploadByteDevice = m_channel->request.uploadByteDevice();
+ if (uploadByteDevice) {
+ QObject::disconnect(uploadByteDevice, SIGNAL(readyRead()), m_channel, SLOT(_q_uploadDataReadyRead()));
+ }
+
+ // HTTP pipelining
+ //m_connection->d_func()->fillPipeline(m_socket);
+ //m_socket->flush();
+
+ // ensure we try to receive a reply in all cases, even if _q_readyRead_ hat not been called
+ // this is needed if the sends an reply before we have finished sending the request. In that
+ // case receiveReply had been called before but ignored the server reply
+ if (m_socket->bytesAvailable())
+ QMetaObject::invokeMethod(m_channel, "_q_receiveReply", Qt::QueuedConnection);
+ break;
+ }
+ case QHttpNetworkConnectionChannel::ReadingState:
+ // ignore _q_bytesWritten in these states
+ // fall through
+ default:
+ break;
+ }
+ return true;
+}
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_HTTP
diff --git a/src/network/access/qhttpprotocolhandler_p.h b/src/network/access/qhttpprotocolhandler_p.h
new file mode 100644
index 0000000000..2bbc044b6c
--- /dev/null
+++ b/src/network/access/qhttpprotocolhandler_p.h
@@ -0,0 +1,77 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtNetwork module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QHTTPPROTOCOLHANDLER_H
+#define QHTTPPROTOCOLHANDLER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of the Network Access API. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <private/qabstractprotocolhandler_p.h>
+
+#ifndef QT_NO_HTTP
+
+QT_BEGIN_NAMESPACE
+
+class QHttpProtocolHandler : public QAbstractProtocolHandler {
+public:
+ QHttpProtocolHandler(QHttpNetworkConnectionChannel *channel);
+
+private:
+ virtual void _q_receiveReply() Q_DECL_OVERRIDE;
+ virtual void _q_readyRead() Q_DECL_OVERRIDE;
+ virtual bool sendRequest() Q_DECL_OVERRIDE;
+};
+
+QT_END_NAMESPACE
+
+#endif // QT_NO_HTTP
+
+#endif