summaryrefslogtreecommitdiffstats
path: root/src/network/access/qhttpprotocolhandler.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/access/qhttpprotocolhandler.cpp')
-rw-r--r--src/network/access/qhttpprotocolhandler.cpp119
1 files changed, 55 insertions, 64 deletions
diff --git a/src/network/access/qhttpprotocolhandler.cpp b/src/network/access/qhttpprotocolhandler.cpp
index d39589fb96..fb584eb9cc 100644
--- a/src/network/access/qhttpprotocolhandler.cpp
+++ b/src/network/access/qhttpprotocolhandler.cpp
@@ -1,49 +1,16 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2014 BlackBerry Limited. All rights reserved.
-** Contact: https://www.qt.io/licensing/
-**
-** 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 The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2014 BlackBerry Limited. All rights reserved.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <private/qhttpprotocolhandler_p.h>
#include <private/qnoncontiguousbytedevice_p.h>
#include <private/qhttpnetworkconnectionchannel_p.h>
+#include <private/qsocketabstraction_p.h>
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
QHttpProtocolHandler::QHttpProtocolHandler(QHttpNetworkConnectionChannel *channel)
: QAbstractProtocolHandler(channel)
{
@@ -68,10 +35,8 @@ void QHttpProtocolHandler::_q_receiveReply()
return;
}
- QAbstractSocket::SocketState socketState = m_socket->state();
-
// connection might be closed to signal the end of data
- if (socketState == QAbstractSocket::UnconnectedState) {
+ if (QSocketAbstraction::socketState(m_socket) == 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
@@ -107,7 +72,7 @@ void QHttpProtocolHandler::_q_receiveReply()
return;
}
bytes += statusBytes;
- m_channel->lastStatus = m_reply->d_func()->statusCode;
+ m_channel->lastStatus = m_reply->statusCode();
break;
}
case QHttpNetworkReplyPrivate::ReadingHeaderState: {
@@ -127,7 +92,8 @@ void QHttpProtocolHandler::_q_receiveReply()
} else {
replyPrivate->autoDecompress = false;
}
- if (replyPrivate->statusCode == 100) {
+ const int statusCode = m_reply->statusCode();
+ if (statusCode == 100 || (102 <= statusCode && statusCode <= 199)) {
replyPrivate->clearHttpLayerInformation();
replyPrivate->state = QHttpNetworkReplyPrivate::ReadingStatusState;
break; // ignore
@@ -148,7 +114,7 @@ void QHttpProtocolHandler::_q_receiveReply()
}
case QHttpNetworkReplyPrivate::ReadingDataState: {
QHttpNetworkReplyPrivate *replyPrivate = m_reply->d_func();
- if (m_socket->state() == QAbstractSocket::ConnectedState &&
+ if (QSocketAbstraction::socketState(m_socket) == 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
@@ -177,8 +143,7 @@ void QHttpProtocolHandler::_q_receiveReply()
m_connection->d_func()->emitReplyError(m_socket, m_reply, QNetworkReply::RemoteHostClosedError);
break;
}
- } else if (!replyPrivate->isChunked() && !replyPrivate->autoDecompress
- && replyPrivate->bodyLength > 0) {
+ } else if (!replyPrivate->isChunked() && 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);
@@ -216,6 +181,8 @@ void QHttpProtocolHandler::_q_receiveReply()
}
case QHttpNetworkReplyPrivate::AllDoneState:
m_channel->allDone();
+ if (state == QHttpNetworkReplyPrivate::AllDoneState)
+ lastBytes = bytes; // No need to loop more just to call m_channel->allDone again.
break;
default:
break;
@@ -225,7 +192,8 @@ void QHttpProtocolHandler::_q_receiveReply()
void QHttpProtocolHandler::_q_readyRead()
{
- if (m_socket->state() == QAbstractSocket::ConnectedState && m_socket->bytesAvailable() == 0) {
+ if (QSocketAbstraction::socketState(m_socket) == 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
@@ -233,7 +201,7 @@ void QHttpProtocolHandler::_q_readyRead()
char c;
qint64 ret = m_socket->peek(&c, 1);
if (ret < 0) {
- m_channel->_q_error(m_socket->error());
+ m_channel->_q_error(QSocketAbstraction::socketError(m_socket));
// We still need to handle the reply so it emits its signals etc.
if (m_reply)
_q_receiveReply();
@@ -271,9 +239,7 @@ bool QHttpProtocolHandler::sendRequest()
// _q_connected or _q_encrypted
return false;
}
- QString scheme = m_channel->request.url().scheme();
- if (scheme == QLatin1String("preconnect-http")
- || scheme == QLatin1String("preconnect-https")) {
+ if (m_channel->request.isPreConnect()) {
m_channel->state = QHttpNetworkConnectionChannel::IdleState;
m_reply->d_func()->state = QHttpNetworkReplyPrivate::AllDoneState;
m_channel->allDone();
@@ -313,12 +279,12 @@ bool QHttpProtocolHandler::sendRequest()
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_header = QHttpNetworkRequestPrivate::header(m_channel->request,
(m_connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy));
#else
- QByteArray header = QHttpNetworkRequestPrivate::header(m_channel->request, false);
+ m_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();
@@ -331,6 +297,9 @@ bool QHttpProtocolHandler::sendRequest()
m_channel->state = QHttpNetworkConnectionChannel::WritingState; // start writing data
sendRequest(); //recurse
} else {
+ // no data to send: just send the HTTP headers
+ m_socket->write(std::exchange(m_header, {}));
+ QMetaObject::invokeMethod(m_reply, "requestSent", Qt::QueuedConnection);
m_channel->state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response
sendRequest(); //recurse
}
@@ -342,6 +311,10 @@ bool QHttpProtocolHandler::sendRequest()
// write the data
QNonContiguousByteDevice* uploadByteDevice = m_channel->request.uploadByteDevice();
if (!uploadByteDevice || m_channel->bytesTotal == m_channel->written) {
+ // the upload device might have no data to send, but we still have to send the headers,
+ // do it now.
+ if (!m_header.isEmpty())
+ m_socket->write(std::exchange(m_header, {}));
if (uploadByteDevice)
emit m_reply->dataSendProgress(m_channel->written, m_channel->bytesTotal);
m_channel->state = QHttpNetworkConnectionChannel::WaitingState; // now wait for response
@@ -349,24 +322,31 @@ bool QHttpProtocolHandler::sendRequest()
break;
}
- // only feed the QTcpSocket buffer when there is less than 32 kB in it
+ // only feed the QTcpSocket buffer when there is less than 32 kB in it;
+ // note that the headers do not count towards these limits.
const qint64 socketBufferFill = 32*1024;
const qint64 socketWriteMaxSize = 16*1024;
-
+ // if it is really an ssl socket, check more than just bytesToWrite()
#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)
+ const auto encryptedBytesToWrite = [sslSocket]() -> qint64
+ {
+ return sslSocket ? sslSocket->encryptedBytesToWrite() : 0;
+ };
#else
- while (m_socket->bytesToWrite() <= socketBufferFill
- && m_channel->bytesTotal != m_channel->written)
+ const auto encryptedBytesToWrite = [](){ return qint64(0); };
#endif
+
+ // throughout this loop, we want to send the data coming from uploadByteDevice.
+ // we also need to send the headers, as we try to coalesce their write with the data.
+ // we won't send more than the limits above, excluding the headers
+ while ((m_socket->bytesToWrite() + encryptedBytesToWrite()) <= socketBufferFill
+ && m_channel->bytesTotal != m_channel->written)
{
// get pointer to upload data
qint64 currentReadSize = 0;
- qint64 desiredReadSize = qMin(socketWriteMaxSize, m_channel->bytesTotal - m_channel->written);
+ const qint64 desiredReadSize = qMin(socketWriteMaxSize, m_channel->bytesTotal - m_channel->written);
const char *readPointer = uploadByteDevice->readPointer(desiredReadSize, currentReadSize);
if (currentReadSize == -1) {
@@ -384,7 +364,18 @@ bool QHttpProtocolHandler::sendRequest()
m_connection->d_func()->emitReplyError(m_socket, m_reply, QNetworkReply::ProtocolFailure);
return false;
}
- qint64 currentWriteSize = m_socket->write(readPointer, currentReadSize);
+ qint64 currentWriteSize;
+ if (m_header.isEmpty()) {
+ currentWriteSize = m_socket->write(readPointer, currentReadSize);
+ } else {
+ // assemble header and data and send them together
+ const qint64 headerSize = m_header.size();
+ m_header.append(readPointer, currentReadSize);
+ currentWriteSize = m_socket->write(std::exchange(m_header, {}));
+ if (currentWriteSize != -1)
+ currentWriteSize -= headerSize;
+ QMetaObject::invokeMethod(m_reply, "requestSent", Qt::QueuedConnection);
+ }
if (currentWriteSize == -1 || currentWriteSize != currentReadSize) {
// socket broke down
m_connection->d_func()->emitReplyError(m_socket, m_reply, QNetworkReply::UnknownNetworkError);