summaryrefslogtreecommitdiffstats
path: root/src/network/access
diff options
context:
space:
mode:
Diffstat (limited to 'src/network/access')
-rw-r--r--src/network/access/http2/bitstreams.cpp42
-rw-r--r--src/network/access/http2/bitstreams_p.h44
-rw-r--r--src/network/access/http2/hpack.cpp89
-rw-r--r--src/network/access/http2/hpack_p.h43
-rw-r--r--src/network/access/http2/hpacktable.cpp51
-rw-r--r--src/network/access/http2/hpacktable_p.h44
-rw-r--r--src/network/access/http2/http2frames.cpp53
-rw-r--r--src/network/access/http2/http2frames_p.h59
-rw-r--r--src/network/access/http2/http2protocol.cpp132
-rw-r--r--src/network/access/http2/http2protocol_p.h50
-rw-r--r--src/network/access/http2/http2streams.cpp40
-rw-r--r--src/network/access/http2/http2streams_p.h40
-rw-r--r--src/network/access/http2/huffman.cpp45
-rw-r--r--src/network/access/http2/huffman_p.h46
-rw-r--r--src/network/access/qabstractnetworkcache.cpp43
-rw-r--r--src/network/access/qabstractnetworkcache.h42
-rw-r--r--src/network/access/qabstractnetworkcache_p.h40
-rw-r--r--src/network/access/qabstractprotocolhandler.cpp40
-rw-r--r--src/network/access/qabstractprotocolhandler_p.h40
-rw-r--r--src/network/access/qdecompresshelper.cpp110
-rw-r--r--src/network/access/qdecompresshelper_p.h48
-rw-r--r--src/network/access/qhsts.cpp104
-rw-r--r--src/network/access/qhsts_p.h46
-rw-r--r--src/network/access/qhstspolicy.cpp44
-rw-r--r--src/network/access/qhstspolicy.h42
-rw-r--r--src/network/access/qhstsstore.cpp50
-rw-r--r--src/network/access/qhstsstore_p.h40
-rw-r--r--src/network/access/qhttp1configuration.cpp154
-rw-r--r--src/network/access/qhttp1configuration.h62
-rw-r--r--src/network/access/qhttp2configuration.cpp42
-rw-r--r--src/network/access/qhttp2configuration.h42
-rw-r--r--src/network/access/qhttp2connection.cpp1752
-rw-r--r--src/network/access/qhttp2connection_p.h372
-rw-r--r--src/network/access/qhttp2protocolhandler.cpp449
-rw-r--r--src/network/access/qhttp2protocolhandler_p.h45
-rw-r--r--src/network/access/qhttpheaderparser.cpp118
-rw-r--r--src/network/access/qhttpheaderparser_p.h87
-rw-r--r--src/network/access/qhttpheaders.cpp1551
-rw-r--r--src/network/access/qhttpheaders.h281
-rw-r--r--src/network/access/qhttpmultipart.cpp85
-rw-r--r--src/network/access/qhttpmultipart.h42
-rw-r--r--src/network/access/qhttpmultipart_p.h40
-rw-r--r--src/network/access/qhttpnetworkconnection.cpp336
-rw-r--r--src/network/access/qhttpnetworkconnection_p.h83
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel.cpp136
-rw-r--r--src/network/access/qhttpnetworkconnectionchannel_p.h45
-rw-r--r--src/network/access/qhttpnetworkheader.cpp46
-rw-r--r--src/network/access/qhttpnetworkheader_p.h51
-rw-r--r--src/network/access/qhttpnetworkreply.cpp103
-rw-r--r--src/network/access/qhttpnetworkreply_p.h74
-rw-r--r--src/network/access/qhttpnetworkrequest.cpp70
-rw-r--r--src/network/access/qhttpnetworkrequest_p.h50
-rw-r--r--src/network/access/qhttpprotocolhandler.cpp60
-rw-r--r--src/network/access/qhttpprotocolhandler_p.h42
-rw-r--r--src/network/access/qhttpthreaddelegate.cpp102
-rw-r--r--src/network/access/qhttpthreaddelegate_p.h68
-rw-r--r--src/network/access/qnetworkaccessauthenticationmanager.cpp48
-rw-r--r--src/network/access/qnetworkaccessauthenticationmanager_p.h40
-rw-r--r--src/network/access/qnetworkaccessbackend.cpp50
-rw-r--r--src/network/access/qnetworkaccessbackend_p.h41
-rw-r--r--src/network/access/qnetworkaccesscache.cpp42
-rw-r--r--src/network/access/qnetworkaccesscache_p.h42
-rw-r--r--src/network/access/qnetworkaccesscachebackend.cpp51
-rw-r--r--src/network/access/qnetworkaccesscachebackend_p.h40
-rw-r--r--src/network/access/qnetworkaccessdebugpipebackend.cpp50
-rw-r--r--src/network/access/qnetworkaccessdebugpipebackend_p.h40
-rw-r--r--src/network/access/qnetworkaccessfilebackend.cpp62
-rw-r--r--src/network/access/qnetworkaccessfilebackend_p.h40
-rw-r--r--src/network/access/qnetworkaccessmanager.cpp321
-rw-r--r--src/network/access/qnetworkaccessmanager.h64
-rw-r--r--src/network/access/qnetworkaccessmanager_p.h42
-rw-r--r--src/network/access/qnetworkcookie.cpp167
-rw-r--r--src/network/access/qnetworkcookie.h47
-rw-r--r--src/network/access/qnetworkcookie_p.h48
-rw-r--r--src/network/access/qnetworkcookiejar.cpp123
-rw-r--r--src/network/access/qnetworkcookiejar.h40
-rw-r--r--src/network/access/qnetworkcookiejar_p.h40
-rw-r--r--src/network/access/qnetworkdiskcache.cpp178
-rw-r--r--src/network/access/qnetworkdiskcache.h40
-rw-r--r--src/network/access/qnetworkdiskcache_p.h57
-rw-r--r--src/network/access/qnetworkfile.cpp42
-rw-r--r--src/network/access/qnetworkfile_p.h40
-rw-r--r--src/network/access/qnetworkreply.cpp64
-rw-r--r--src/network/access/qnetworkreply.h51
-rw-r--r--src/network/access/qnetworkreply_p.h40
-rw-r--r--src/network/access/qnetworkreplydataimpl.cpp40
-rw-r--r--src/network/access/qnetworkreplydataimpl_p.h40
-rw-r--r--src/network/access/qnetworkreplyfileimpl.cpp61
-rw-r--r--src/network/access/qnetworkreplyfileimpl_p.h54
-rw-r--r--src/network/access/qnetworkreplyhttpimpl.cpp350
-rw-r--r--src/network/access/qnetworkreplyhttpimpl_p.h46
-rw-r--r--src/network/access/qnetworkreplyimpl.cpp53
-rw-r--r--src/network/access/qnetworkreplyimpl_p.h42
-rw-r--r--src/network/access/qnetworkreplywasmimpl.cpp180
-rw-r--r--src/network/access/qnetworkreplywasmimpl_p.h45
-rw-r--r--src/network/access/qnetworkrequest.cpp416
-rw-r--r--src/network/access/qnetworkrequest.h94
-rw-r--r--src/network/access/qnetworkrequest_p.h42
-rw-r--r--src/network/access/qnetworkrequestfactory.cpp727
-rw-r--r--src/network/access/qnetworkrequestfactory.h100
-rw-r--r--src/network/access/qnetworkrequestfactory_p.h56
-rw-r--r--src/network/access/qrestaccessmanager.cpp828
-rw-r--r--src/network/access/qrestaccessmanager.h127
-rw-r--r--src/network/access/qrestaccessmanager_p.h89
-rw-r--r--src/network/access/qrestreply.cpp560
-rw-r--r--src/network/access/qrestreply.h71
-rw-r--r--src/network/access/qrestreply_p.h40
-rw-r--r--src/network/access/qsocketabstraction_p.h91
108 files changed, 9059 insertions, 4898 deletions
diff --git a/src/network/access/http2/bitstreams.cpp b/src/network/access/http2/bitstreams.cpp
index d22c7cd4ec..c35f0e3aaa 100644
--- a/src/network/access/http2/bitstreams.cpp
+++ b/src/network/access/http2/bitstreams.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "bitstreams_p.h"
#include "huffman_p.h"
@@ -99,7 +63,7 @@ void BitOStream::write(quint32 src)
}
}
-void BitOStream::write(const QByteArray &src, bool compressed)
+void BitOStream::write(QByteArrayView src, bool compressed)
{
quint32 byteLen = src.size();
if (compressed && byteLen) {
diff --git a/src/network/access/http2/bitstreams_p.h b/src/network/access/http2/bitstreams_p.h
index ca272062a6..c96adb5390 100644
--- a/src/network/access/http2/bitstreams_p.h
+++ b/src/network/access/http2/bitstreams_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef BITSTREAMS_P_H
#define BITSTREAMS_P_H
@@ -51,7 +15,7 @@
// We mean it.
//
-#include <QtCore/qglobal.h>
+#include <QtCore/private/qglobal_p.h>
#include <QtCore/qdebug.h>
#include <type_traits>
@@ -79,7 +43,7 @@ public:
// * 32-bit integers
// * strings
void write(quint32 src);
- void write(const QByteArray &src, bool compressed);
+ void write(QByteArrayView src, bool compressed);
quint64 bitLength() const;
quint64 byteLength() const;
diff --git a/src/network/access/http2/hpack.cpp b/src/network/access/http2/hpack.cpp
index 7461095f0a..9e970dda53 100644
--- a/src/network/access/http2/hpack.cpp
+++ b/src/network/access/http2/hpack.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "bitstreams_p.h"
#include "hpack_p.h"
@@ -127,7 +91,7 @@ bool read_bit_pattern(const BitPattern &pattern, BitIStream &inputStream)
return true;
}
-bool is_request_pseudo_header(const QByteArray &name)
+bool is_request_pseudo_header(QByteArrayView name)
{
return name == ":method" || name == ":scheme" ||
name == ":authority" || name == ":path";
@@ -230,8 +194,8 @@ bool Encoder::encodeRequestPseudoHeaders(BitOStream &outputStream,
using size_type = decltype(header.size());
bool methodFound = false;
- const char *headerName[] = {":authority", ":scheme", ":path"};
- const size_type nHeaders = sizeof headerName / sizeof headerName[0];
+ constexpr QByteArrayView headerName[] = {":authority", ":scheme", ":path"};
+ constexpr size_type nHeaders = std::size(headerName);
bool headerFound[nHeaders] = {};
for (const auto &field : header) {
@@ -540,6 +504,49 @@ void Decoder::handleStreamError(BitIStream &inputStream)
// HTTP2 layer will end with session error/COMPRESSION_ERROR.
}
+std::optional<QUrl> makePromiseKeyUrl(const HttpHeader &requestHeader)
+{
+ constexpr QByteArrayView names[] = { ":authority", ":method", ":path", ":scheme" };
+ enum PseudoHeaderEnum
+ {
+ Authority,
+ Method,
+ Path,
+ Scheme
+ };
+ std::array<std::optional<QByteArrayView>, std::size(names)> pseudoHeaders{};
+ for (const auto &field : requestHeader) {
+ const auto *it = std::find(std::begin(names), std::end(names), QByteArrayView(field.name));
+ if (it != std::end(names)) {
+ const auto index = std::distance(std::begin(names), it);
+ if (field.value.isEmpty() || pseudoHeaders.at(index).has_value())
+ return {};
+ pseudoHeaders[index] = field.value;
+ }
+ }
+
+ auto optionalIsSet = [](const auto &x) { return x.has_value(); };
+ if (!std::all_of(pseudoHeaders.begin(), pseudoHeaders.end(), optionalIsSet)) {
+ // All four required, HTTP/2 8.1.2.3.
+ return {};
+ }
+
+ const QByteArrayView method = pseudoHeaders[Method].value();
+ if (method.compare("get", Qt::CaseInsensitive) != 0 &&
+ method.compare("head", Qt::CaseInsensitive) != 0) {
+ return {};
+ }
+
+ QUrl url;
+ url.setScheme(QLatin1StringView(pseudoHeaders[Scheme].value()));
+ url.setAuthority(QLatin1StringView(pseudoHeaders[Authority].value()));
+ url.setPath(QLatin1StringView(pseudoHeaders[Path].value()));
+
+ if (!url.isValid())
+ return {};
+ return url;
+}
+
}
QT_END_NAMESPACE
diff --git a/src/network/access/http2/hpack_p.h b/src/network/access/http2/hpack_p.h
index 8c2701e7af..b407b81941 100644
--- a/src/network/access/http2/hpack_p.h
+++ b/src/network/access/http2/hpack_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef HPACK_P_H
#define HPACK_P_H
@@ -54,8 +18,10 @@
#include "hpacktable_p.h"
#include <QtCore/qglobal.h>
+#include <QtCore/qurl.h>
#include <vector>
+#include <optional>
QT_BEGIN_NAMESPACE
@@ -148,6 +114,7 @@ private:
FieldLookupTable lookupTable;
};
+std::optional<QUrl> makePromiseKeyUrl(const HttpHeader &requestHeader);
}
QT_END_NAMESPACE
diff --git a/src/network/access/http2/hpacktable.cpp b/src/network/access/http2/hpacktable.cpp
index d963cf261b..2c728b37e3 100644
--- a/src/network/access/http2/hpacktable.cpp
+++ b/src/network/access/http2/hpacktable.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "hpacktable_p.h"
@@ -52,7 +16,7 @@ QT_BEGIN_NAMESPACE
namespace HPack
{
-HeaderSize entry_size(const QByteArray &name, const QByteArray &value)
+HeaderSize entry_size(QByteArrayView name, QByteArrayView value)
{
// 32 comes from HPACK:
// "4.1 Calculating Table Size
@@ -62,8 +26,10 @@ HeaderSize entry_size(const QByteArray &name, const QByteArray &value)
// for counting the number of references to the name and value would have
// 32 octets of overhead."
- const unsigned sum = unsigned(name.size() + value.size());
- if (std::numeric_limits<unsigned>::max() - 32 < sum)
+ size_t sum;
+ if (qAddOverflow(size_t(name.size()), size_t(value.size()), &sum))
+ return HeaderSize();
+ if (sum > (std::numeric_limits<unsigned>::max() - 32))
return HeaderSize();
return HeaderSize(true, quint32(sum + 32));
}
@@ -382,8 +348,7 @@ quint32 FieldLookupTable::indexOfChunk(const Chunk *chunk) const
return quint32(i);
}
- Q_UNREACHABLE();
- return 0;
+ Q_UNREACHABLE_RETURN(0);
}
quint32 FieldLookupTable::keyToIndex(const SearchEntry &key) const
diff --git a/src/network/access/http2/hpacktable_p.h b/src/network/access/http2/hpacktable_p.h
index 587d86f09c..d57013150b 100644
--- a/src/network/access/http2/hpacktable_p.h
+++ b/src/network/access/http2/hpacktable_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef HPACKTABLE_P_H
#define HPACKTABLE_P_H
@@ -52,7 +16,7 @@
//
#include <QtCore/qbytearray.h>
-#include <QtCore/qglobal.h>
+#include <QtCore/private/qglobal_p.h>
#include <QtCore/qpair.h>
#include <vector>
@@ -88,7 +52,7 @@ struct Q_AUTOTEST_EXPORT HeaderField
using HeaderSize = QPair<bool, quint32>;
-HeaderSize entry_size(const QByteArray &name, const QByteArray &value);
+HeaderSize entry_size(QByteArrayView name, QByteArrayView value);
inline HeaderSize entry_size(const HeaderField &entry)
{
diff --git a/src/network/access/http2/http2frames.cpp b/src/network/access/http2/http2frames.cpp
index f1f2cdf8f4..e07c96b803 100644
--- a/src/network/access/http2/http2frames.cpp
+++ b/src/network/access/http2/http2frames.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "http2frames_p.h"
@@ -171,6 +135,7 @@ FrameStatus Frame::validateHeader() const
// 6.6 PUSH_PROMISE
if (framePayloadSize < 4)
return FrameStatus::sizeError;
+ break;
default:
// DATA/HEADERS/CONTINUATION will be verified
// when we have payload.
@@ -294,7 +259,7 @@ const uchar *Frame::hpackBlockBegin() const
return begin;
}
-FrameStatus FrameReader::read(QAbstractSocket &socket)
+FrameStatus FrameReader::read(QIODevice &socket)
{
if (offset < frameHeaderSize) {
if (!readHeader(socket))
@@ -322,7 +287,7 @@ FrameStatus FrameReader::read(QAbstractSocket &socket)
return frame.validatePayload();
}
-bool FrameReader::readHeader(QAbstractSocket &socket)
+bool FrameReader::readHeader(QIODevice &socket)
{
Q_ASSERT(offset < frameHeaderSize);
@@ -338,7 +303,7 @@ bool FrameReader::readHeader(QAbstractSocket &socket)
return offset == frameHeaderSize;
}
-bool FrameReader::readPayload(QAbstractSocket &socket)
+bool FrameReader::readPayload(QIODevice &socket)
{
Q_ASSERT(offset < frame.buffer.size());
Q_ASSERT(frame.buffer.size() > frameHeaderSize);
@@ -429,7 +394,7 @@ void FrameWriter::updatePayloadSize()
setPayloadSize(size);
}
-bool FrameWriter::write(QAbstractSocket &socket) const
+bool FrameWriter::write(QIODevice &socket) const
{
auto &buffer = frame.buffer;
Q_ASSERT(buffer.size() >= frameHeaderSize);
@@ -443,7 +408,7 @@ bool FrameWriter::write(QAbstractSocket &socket) const
return nWritten != -1 && size_type(nWritten) == buffer.size();
}
-bool FrameWriter::writeHEADERS(QAbstractSocket &socket, quint32 sizeLimit)
+bool FrameWriter::writeHEADERS(QIODevice &socket, quint32 sizeLimit)
{
auto &buffer = frame.buffer;
Q_ASSERT(buffer.size() >= frameHeaderSize);
@@ -493,7 +458,7 @@ bool FrameWriter::writeHEADERS(QAbstractSocket &socket, quint32 sizeLimit)
return true;
}
-bool FrameWriter::writeDATA(QAbstractSocket &socket, quint32 sizeLimit,
+bool FrameWriter::writeDATA(QIODevice &socket, quint32 sizeLimit,
const uchar *src, quint32 size)
{
// With DATA frame(s) we always have:
diff --git a/src/network/access/http2/http2frames_p.h b/src/network/access/http2/http2frames_p.h
index 4bdc775806..48e3f751b7 100644
--- a/src/network/access/http2/http2frames_p.h
+++ b/src/network/access/http2/http2frames_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef HTTP2FRAMES_P_H
#define HTTP2FRAMES_P_H
@@ -63,7 +27,7 @@
QT_BEGIN_NAMESPACE
class QHttp2ProtocolHandler;
-class QAbstractSocket;
+class QIODevice;
namespace Http2
{
@@ -101,15 +65,15 @@ struct Q_AUTOTEST_EXPORT Frame
class Q_AUTOTEST_EXPORT FrameReader
{
public:
- FrameStatus read(QAbstractSocket &socket);
+ FrameStatus read(QIODevice &socket);
Frame &inboundFrame()
{
return frame;
}
private:
- bool readHeader(QAbstractSocket &socket);
- bool readPayload(QAbstractSocket &socket);
+ bool readHeader(QIODevice &socket);
+ bool readPayload(QIODevice &socket);
quint32 offset = 0;
Frame frame;
@@ -159,20 +123,25 @@ public:
{
append(&payload[0], &payload[0] + payload.size());
}
+ void append(QByteArrayView payload)
+ {
+ append(reinterpret_cast<const uchar *>(payload.begin()),
+ reinterpret_cast<const uchar *>(payload.end()));
+ }
void append(const uchar *begin, const uchar *end);
// Write as a single frame:
- bool write(QAbstractSocket &socket) const;
+ bool write(QIODevice &socket) const;
// Two types of frames we are sending are affected by frame size limits:
// HEADERS and DATA. HEADERS' payload (hpacked HTTP headers, following a
// frame header) is always in our 'buffer', we send the initial HEADERS
// frame first and then CONTINUTATION frame(s) if needed:
- bool writeHEADERS(QAbstractSocket &socket, quint32 sizeLimit);
+ bool writeHEADERS(QIODevice &socket, quint32 sizeLimit);
// With DATA frames the actual payload is never in our 'buffer', it's a
// 'readPointer' from QNonContiguousData. We split this payload as needed
// into DATA frames with correct payload size fitting into frame size limit:
- bool writeDATA(QAbstractSocket &socket, quint32 sizeLimit,
+ bool writeDATA(QIODevice &socket, quint32 sizeLimit,
const uchar *src, quint32 size);
private:
void updatePayloadSize();
diff --git a/src/network/access/http2/http2protocol.cpp b/src/network/access/http2/http2protocol.cpp
index baae68bc30..8e7e176c41 100644
--- a/src/network/access/http2/http2protocol.cpp
+++ b/src/network/access/http2/http2protocol.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "http2protocol_p.h"
#include "http2frames_p.h"
@@ -50,6 +14,10 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
+QT_IMPL_METATYPE_EXTERN_TAGGED(Http2::Settings, Http2__Settings)
+
Q_LOGGING_CATEGORY(QT_HTTP2, "qt.network.http2")
namespace Http2
@@ -108,12 +76,10 @@ void appendProtocolUpgradeHeaders(const QHttp2Configuration &config, QHttpNetwor
Q_ASSERT(request);
// RFC 2616, 14.10
// RFC 7540, 3.2
- QByteArray value(request->headerField("Connection"));
+ const QByteArray connectionHeader = request->headerField("Connection");
+ const auto separator = connectionHeader.isEmpty() ? QByteArrayView() : QByteArrayView(", ");
// We _append_ 'Upgrade':
- if (value.size())
- value += ", ";
-
- value += "Upgrade, HTTP2-Settings";
+ QByteArray value = connectionHeader + separator + "Upgrade, HTTP2-Settings";
request->setHeaderField("Connection", value);
// This we just (re)write.
request->setHeaderField("Upgrade", "h2c");
@@ -128,7 +94,7 @@ void qt_error(quint32 errorCode, QNetworkReply::NetworkError &error,
{
if (errorCode > quint32(HTTP_1_1_REQUIRED)) {
error = QNetworkReply::ProtocolFailure;
- errorMessage = QLatin1String("RST_STREAM with unknown error code (%1)");
+ errorMessage = "RST_STREAM with unknown error code (%1)"_L1;
errorMessage = errorMessage.arg(errorCode);
return;
}
@@ -142,61 +108,61 @@ void qt_error(quint32 errorCode, QNetworkReply::NetworkError &error,
break;
case PROTOCOL_ERROR:
error = QNetworkReply::ProtocolFailure;
- errorMessage = QLatin1String("HTTP/2 protocol error");
+ errorMessage = "HTTP/2 protocol error"_L1;
break;
case INTERNAL_ERROR:
error = QNetworkReply::InternalServerError;
- errorMessage = QLatin1String("Internal server error");
+ errorMessage = "Internal server error"_L1;
break;
case FLOW_CONTROL_ERROR:
error = QNetworkReply::ProtocolFailure;
- errorMessage = QLatin1String("Flow control error");
+ errorMessage = "Flow control error"_L1;
break;
case SETTINGS_TIMEOUT:
error = QNetworkReply::TimeoutError;
- errorMessage = QLatin1String("SETTINGS ACK timeout error");
+ errorMessage = "SETTINGS ACK timeout error"_L1;
break;
case STREAM_CLOSED:
error = QNetworkReply::ProtocolFailure;
- errorMessage = QLatin1String("Server received frame(s) on a half-closed stream");
+ errorMessage = "Server received frame(s) on a half-closed stream"_L1;
break;
case FRAME_SIZE_ERROR:
error = QNetworkReply::ProtocolFailure;
- errorMessage = QLatin1String("Server received a frame with an invalid size");
+ errorMessage = "Server received a frame with an invalid size"_L1;
break;
case REFUSE_STREAM:
error = QNetworkReply::ProtocolFailure;
- errorMessage = QLatin1String("Server refused a stream");
+ errorMessage = "Server refused a stream"_L1;
break;
case CANCEL:
error = QNetworkReply::ProtocolFailure;
- errorMessage = QLatin1String("Stream is no longer needed");
+ errorMessage = "Stream is no longer needed"_L1;
break;
case COMPRESSION_ERROR:
error = QNetworkReply::ProtocolFailure;
- errorMessage = QLatin1String("Server is unable to maintain the "
- "header compression context for the connection");
+ errorMessage = "Server is unable to maintain the "
+ "header compression context for the connection"_L1;
break;
case CONNECT_ERROR:
// TODO: in Qt6 we'll have to add more error codes in QNetworkReply.
error = QNetworkReply::UnknownNetworkError;
- errorMessage = QLatin1String("The connection established in response "
- "to a CONNECT request was reset or abnormally closed");
+ errorMessage = "The connection established in response "
+ "to a CONNECT request was reset or abnormally closed"_L1;
break;
case ENHANCE_YOUR_CALM:
error = QNetworkReply::UnknownServerError;
- errorMessage = QLatin1String("Server dislikes our behavior, excessive load detected.");
+ errorMessage = "Server dislikes our behavior, excessive load detected."_L1;
break;
case INADEQUATE_SECURITY:
error = QNetworkReply::ContentAccessDenied;
- errorMessage = QLatin1String("The underlying transport has properties "
- "that do not meet minimum security "
- "requirements");
+ errorMessage = "The underlying transport has properties "
+ "that do not meet minimum security "
+ "requirements"_L1;
break;
case HTTP_1_1_REQUIRED:
error = QNetworkReply::ProtocolFailure;
- errorMessage = QLatin1String("Server requires that HTTP/1.1 "
- "be used instead of HTTP/2.");
+ errorMessage = "Server requires that HTTP/1.1 "
+ "be used instead of HTTP/2."_L1;
}
}
@@ -218,19 +184,45 @@ QNetworkReply::NetworkError qt_error(quint32 errorCode)
bool is_protocol_upgraded(const QHttpNetworkReply &reply)
{
- if (reply.statusCode() == 101) {
- // Do some minimal checks here - we expect 'Upgrade: h2c' to be found.
- const auto &header = reply.header();
- for (const QPair<QByteArray, QByteArray> &field : header) {
- if (field.first.compare("upgrade", Qt::CaseInsensitive) == 0 &&
- field.second.compare("h2c", Qt::CaseInsensitive) == 0)
- return true;
- }
+ if (reply.statusCode() != 101)
+ return false;
+
+ // Do some minimal checks here - we expect 'Upgrade: h2c' to be found.
+ for (const auto &v : reply.header().values(QHttpHeaders::WellKnownHeader::Upgrade)) {
+ if (v.compare("h2c", Qt::CaseInsensitive) == 0)
+ return true;
}
return false;
}
+std::vector<uchar> assemble_hpack_block(const std::vector<Frame> &frames)
+{
+ std::vector<uchar> hpackBlock;
+
+ size_t total = 0;
+ for (const auto &frame : frames) {
+ if (qAddOverflow(total, size_t{frame.hpackBlockSize()}, &total))
+ return hpackBlock;
+ }
+
+ if (!total)
+ return hpackBlock;
+
+ hpackBlock.resize(total);
+ auto dst = hpackBlock.begin();
+ for (const auto &frame : frames) {
+ if (const auto hpackBlockSize = frame.hpackBlockSize()) {
+ const uchar *src = frame.hpackBlockBegin();
+ std::copy(src, src + hpackBlockSize, dst);
+ dst += hpackBlockSize;
+ }
+ }
+
+ return hpackBlock;
+}
+
+
} // namespace Http2
QT_END_NAMESPACE
diff --git a/src/network/access/http2/http2protocol_p.h b/src/network/access/http2/http2protocol_p.h
index ed5f2bf561..fb5ff199c5 100644
--- a/src/network/access/http2/http2protocol_p.h
+++ b/src/network/access/http2/http2protocol_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef HTTP2PROTOCOL_P_H
#define HTTP2PROTOCOL_P_H
@@ -54,9 +18,11 @@
#include <QtNetwork/qnetworkreply.h>
#include <QtCore/qloggingcategory.h>
#include <QtCore/qmetatype.h>
-#include <QtCore/qglobal.h>
+#include <QtCore/private/qglobal_p.h>
#include <QtCore/qmap.h>
+#include <vector>
+
// Different HTTP/2 constants/values as defined by RFC 7540.
QT_BEGIN_NAMESPACE
@@ -148,11 +114,13 @@ const quint32 lastValidStreamID((quint32(1) << 31) - 1); // HTTP/2, 5.1.1
// HTTP/2 servers are not afraid to immediately set it to the possible max,
// we do the same and split this window size between our concurrent streams.
const qint32 maxSessionReceiveWindowSize((quint32(1) << 31) - 1);
-const qint32 qtDefaultStreamReceiveWindowSize = maxSessionReceiveWindowSize / maxConcurrentStreams;
+// Presumably, we never use up to 100 streams so let it be 10 simultaneous:
+const qint32 qtDefaultStreamReceiveWindowSize = maxSessionReceiveWindowSize / 10;
struct Frame configurationToSettingsFrame(const QHttp2Configuration &configuration);
QByteArray settingsFrameToBase64(const Frame &settingsFrame);
void appendProtocolUpgradeHeaders(const QHttp2Configuration &configuration, QHttpNetworkRequest *request);
+std::vector<uchar> assemble_hpack_block(const std::vector<Frame> &frames);
extern const Q_AUTOTEST_EXPORT char Http2clientPreface[clientPrefaceLength];
@@ -200,6 +168,6 @@ Q_DECLARE_LOGGING_CATEGORY(QT_HTTP2)
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(Http2::Settings)
+QT_DECL_METATYPE_EXTERN_TAGGED(Http2::Settings, Http2__Settings, Q_NETWORK_EXPORT)
#endif
diff --git a/src/network/access/http2/http2streams.cpp b/src/network/access/http2/http2streams.cpp
index fa39c1d57b..3de8b946fe 100644
--- a/src/network/access/http2/http2streams.cpp
+++ b/src/network/access/http2/http2streams.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "http2streams_p.h"
diff --git a/src/network/access/http2/http2streams_p.h b/src/network/access/http2/http2streams_p.h
index 0be6b3b253..e3745cd2d4 100644
--- a/src/network/access/http2/http2streams_p.h
+++ b/src/network/access/http2/http2streams_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef HTTP2STREAMS_P_H
#define HTTP2STREAMS_P_H
diff --git a/src/network/access/http2/huffman.cpp b/src/network/access/http2/huffman.cpp
index 1c6d2e3352..e957c3311a 100644
--- a/src/network/access/http2/huffman.cpp
+++ b/src/network/access/http2/huffman.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "bitstreams_p.h"
#include "huffman_p.h"
@@ -381,7 +345,7 @@ void write_huffman_code(BitOStream &outputStream, const CodeEntry &code)
// That's from HPACK's specs - we deal with octets.
static_assert(std::numeric_limits<uchar>::digits == 8, "octets expected");
-quint64 huffman_encoded_bit_length(const QByteArray &inputData)
+quint64 huffman_encoded_bit_length(QByteArrayView inputData)
{
quint64 bitLength = 0;
for (int i = 0, e = inputData.size(); i < e; ++i)
@@ -390,7 +354,7 @@ quint64 huffman_encoded_bit_length(const QByteArray &inputData)
return bitLength;
}
-void huffman_encode_string(const QByteArray &inputData, BitOStream &outputStream)
+void huffman_encode_string(QByteArrayView inputData, BitOStream &outputStream)
{
for (int i = 0, e = inputData.size(); i < e; ++i) {
const auto value = uchar(inputData[i]);
@@ -402,6 +366,7 @@ void huffman_encode_string(const QByteArray &inputData, BitOStream &outputStream
outputStream.writeBits(0xff, 8 - outputStream.bitLength() % 8);
}
+static constexpr
bool padding_is_valid(quint32 chunk, quint32 nBits)
{
Q_ASSERT(nBits);
diff --git a/src/network/access/http2/huffman_p.h b/src/network/access/http2/huffman_p.h
index c5324d42b1..daa2b31bb3 100644
--- a/src/network/access/http2/huffman_p.h
+++ b/src/network/access/http2/huffman_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef HUFFMAN_P_H
#define HUFFMAN_P_H
@@ -51,7 +15,7 @@
// We mean it.
//
-#include <QtCore/qglobal.h>
+#include <QtCore/private/qglobal_p.h>
QT_BEGIN_NAMESPACE
@@ -69,8 +33,8 @@ struct CodeEntry
class BitOStream;
-quint64 huffman_encoded_bit_length(const QByteArray &inputData);
-void huffman_encode_string(const QByteArray &inputData, BitOStream &outputStream);
+quint64 huffman_encoded_bit_length(QByteArrayView inputData);
+void huffman_encode_string(QByteArrayView inputData, BitOStream &outputStream);
// PrefixTable:
// Huffman codes with a small bit length
diff --git a/src/network/access/qabstractnetworkcache.cpp b/src/network/access/qabstractnetworkcache.cpp
index 06e21d1b88..c8b940d801 100644
--- a/src/network/access/qabstractnetworkcache.cpp
+++ b/src/network/access/qabstractnetworkcache.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qabstractnetworkcache.h"
#include "qabstractnetworkcache_p.h"
@@ -43,6 +7,7 @@
#include <qdatastream.h>
#include <qdatetime.h>
#include <qurl.h>
+#include <qhash.h>
#include <qdebug.h>
@@ -544,3 +509,5 @@ QAbstractNetworkCache::~QAbstractNetworkCache()
*/
QT_END_NAMESPACE
+
+#include "moc_qabstractnetworkcache.cpp"
diff --git a/src/network/access/qabstractnetworkcache.h b/src/network/access/qabstractnetworkcache.h
index a4048c5b8f..c70dcf737c 100644
--- a/src/network/access/qabstractnetworkcache.h
+++ b/src/network/access/qabstractnetworkcache.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QABSTRACTNETWORKCACHE_H
#define QABSTRACTNETWORKCACHE_H
@@ -70,7 +34,7 @@ public:
QNetworkCacheMetaData &operator=(const QNetworkCacheMetaData &other);
void swap(QNetworkCacheMetaData &other) noexcept
- { qSwap(d, other.d); }
+ { d.swap(other.d); }
bool operator==(const QNetworkCacheMetaData &other) const;
inline bool operator!=(const QNetworkCacheMetaData &other) const
diff --git a/src/network/access/qabstractnetworkcache_p.h b/src/network/access/qabstractnetworkcache_p.h
index fee723e315..625cfcb1c3 100644
--- a/src/network/access/qabstractnetworkcache_p.h
+++ b/src/network/access/qabstractnetworkcache_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QABSTRACTNETWORKCACHE_P_H
#define QABSTRACTNETWORKCACHE_P_H
diff --git a/src/network/access/qabstractprotocolhandler.cpp b/src/network/access/qabstractprotocolhandler.cpp
index 6847816ba7..5e5019901c 100644
--- a/src/network/access/qabstractprotocolhandler.cpp
+++ b/src/network/access/qabstractprotocolhandler.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** 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) 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/qabstractprotocolhandler_p.h>
#include <private/qhttpnetworkconnectionchannel_p.h>
diff --git a/src/network/access/qabstractprotocolhandler_p.h b/src/network/access/qabstractprotocolhandler_p.h
index 04a07734dd..ad82aae66e 100644
--- a/src/network/access/qabstractprotocolhandler_p.h
+++ b/src/network/access/qabstractprotocolhandler_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** 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) 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
#ifndef QABSTRACTPROTOCOLHANDLER_H
#define QABSTRACTPROTOCOLHANDLER_H
diff --git a/src/network/access/qdecompresshelper.cpp b/src/network/access/qdecompresshelper.cpp
index f3227efe8a..52a0d9fc06 100644
--- a/src/network/access/qdecompresshelper.cpp
+++ b/src/network/access/qdecompresshelper.cpp
@@ -1,46 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** 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) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qdecompresshelper_p.h"
-#include <QtCore/private/qbytearray_p.h>
#include <QtCore/qiodevice.h>
+#include <QtCore/qcoreapplication.h>
#include <limits>
#include <zlib.h>
@@ -59,7 +23,7 @@ QT_BEGIN_NAMESPACE
namespace {
struct ContentEncodingMapping
{
- char name[8];
+ QByteArrayView name;
QDecompressHelper::ContentEncoding encoding;
};
@@ -74,10 +38,10 @@ constexpr ContentEncodingMapping contentEncodingMapping[] {
{ "deflate", QDecompressHelper::Deflate },
};
-QDecompressHelper::ContentEncoding encodingFromByteArray(const QByteArray &ce) noexcept
+QDecompressHelper::ContentEncoding encodingFromByteArray(QByteArrayView ce) noexcept
{
for (const auto &mapping : contentEncodingMapping) {
- if (ce.compare(QByteArrayView(mapping.name, strlen(mapping.name)), Qt::CaseInsensitive) == 0)
+ if (ce.compare(mapping.name, Qt::CaseInsensitive) == 0)
return mapping.encoding;
}
return QDecompressHelper::None;
@@ -103,22 +67,19 @@ ZSTD_DStream *toZstandardPointer(void *ptr)
#endif
}
-bool QDecompressHelper::isSupportedEncoding(const QByteArray &encoding)
+bool QDecompressHelper::isSupportedEncoding(QByteArrayView encoding)
{
return encodingFromByteArray(encoding) != QDecompressHelper::None;
}
QByteArrayList QDecompressHelper::acceptedEncoding()
{
- static QByteArrayList accepted = []() {
- QByteArrayList list;
- list.reserve(sizeof(contentEncodingMapping) / sizeof(contentEncodingMapping[0]));
- for (const auto &mapping : contentEncodingMapping) {
- list << QByteArray(mapping.name);
- }
- return list;
- }();
- return accepted;
+ QByteArrayList list;
+ list.reserve(std::size(contentEncodingMapping));
+ for (const auto &mapping : contentEncodingMapping) {
+ list << mapping.name.toByteArray();
+ }
+ return list;
}
QDecompressHelper::~QDecompressHelper()
@@ -126,18 +87,21 @@ QDecompressHelper::~QDecompressHelper()
clear();
}
-bool QDecompressHelper::setEncoding(const QByteArray &encoding)
+bool QDecompressHelper::setEncoding(QByteArrayView encoding)
{
Q_ASSERT(contentEncoding == QDecompressHelper::None);
if (contentEncoding != QDecompressHelper::None) {
qWarning("Encoding is already set.");
+ // This isn't an error, so it doesn't set errorStr, it's just wrong usage.
return false;
}
ContentEncoding ce = encodingFromByteArray(encoding);
if (ce == None) {
- qWarning("An unsupported content encoding was selected: %s", encoding.data());
+ errorStr = QCoreApplication::translate("QHttp", "Unsupported content encoding: %1")
+ .arg(QLatin1String(encoding));
return false;
}
+ errorStr = QString(); // clear error
return setEncoding(ce);
}
@@ -179,7 +143,8 @@ bool QDecompressHelper::setEncoding(ContentEncoding ce)
break;
}
if (!decoderPointer) {
- qWarning("Failed to initialize the decoder.");
+ errorStr = QCoreApplication::translate("QHttp",
+ "Failed to initialize the compression decoder.");
contentEncoding = QDecompressHelper::None;
return false;
}
@@ -435,8 +400,13 @@ qsizetype QDecompressHelper::readInternal(char *data, qsizetype maxSize)
clear();
totalUncompressedBytes += bytesRead;
- if (isPotentialArchiveBomb())
+ if (isPotentialArchiveBomb()) {
+ errorStr = QCoreApplication::translate(
+ "QHttp",
+ "The decompressed output exceeds the limits specified by "
+ "QNetworkRequest::decompressedSafetyCheckThreshold()");
return -1;
+ }
return bytesRead;
}
@@ -517,11 +487,29 @@ qint64 QDecompressHelper::encodedBytesAvailable() const
return compressedDataBuffer.byteAmount();
}
+/*!
+ \internal
+ Returns whether or not the object is valid.
+ If it becomes invalid after an operation has been performed
+ then an error has occurred.
+ \sa errorString()
+*/
bool QDecompressHelper::isValid() const
{
return contentEncoding != None;
}
+/*!
+ \internal
+ Returns a string describing the error that occurred or an empty
+ string if no error occurred.
+ \sa isValid()
+*/
+QString QDecompressHelper::errorString() const
+{
+ return errorStr;
+}
+
void QDecompressHelper::clear()
{
switch (contentEncoding) {
@@ -564,6 +552,8 @@ void QDecompressHelper::clear()
totalBytesRead = 0;
totalUncompressedBytes = 0;
totalCompressedBytes = 0;
+
+ errorStr.clear();
}
qsizetype QDecompressHelper::readZLib(char *data, const qsizetype maxSize)
@@ -719,8 +709,9 @@ qsizetype QDecompressHelper::readBrotli(char *data, const qsizetype maxSize)
switch (result) {
case BROTLI_DECODER_RESULT_ERROR:
- qWarning("Brotli error: %s",
- BrotliDecoderErrorString(BrotliDecoderGetErrorCode(brotliDecoderState)));
+ errorStr = QLatin1String("Brotli error: %1")
+ .arg(QString::fromUtf8(BrotliDecoderErrorString(
+ BrotliDecoderGetErrorCode(brotliDecoderState))));
return -1;
case BROTLI_DECODER_RESULT_SUCCESS:
BrotliDecoderDestroyInstance(brotliDecoderState);
@@ -766,7 +757,8 @@ qsizetype QDecompressHelper::readZstandard(char *data, const qsizetype maxSize)
while (outBuf.pos < outBuf.size && (inBuf.pos < inBuf.size || decoderHasData)) {
size_t retValue = ZSTD_decompressStream(zstdStream, &outBuf, &inBuf);
if (ZSTD_isError(retValue)) {
- qWarning("ZStandard error: %s", ZSTD_getErrorName(retValue));
+ errorStr = QLatin1String("ZStandard error: %1")
+ .arg(QString::fromUtf8(ZSTD_getErrorName(retValue)));
return -1;
} else {
decoderHasData = false;
diff --git a/src/network/access/qdecompresshelper_p.h b/src/network/access/qdecompresshelper_p.h
index b0b60b2119..c837c14521 100644
--- a/src/network/access/qdecompresshelper_p.h
+++ b/src/network/access/qdecompresshelper_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** 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) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef DECOMPRESS_HELPER_P_H
#define DECOMPRESS_HELPER_P_H
@@ -73,7 +37,7 @@ public:
QDecompressHelper() = default;
~QDecompressHelper();
- bool setEncoding(const QByteArray &contentEncoding);
+ bool setEncoding(QByteArrayView contentEncoding);
bool isCountingBytes() const;
void setCountingBytesEnabled(bool shouldCount);
@@ -93,9 +57,11 @@ public:
void setDecompressedSafetyCheckThreshold(qint64 threshold);
- static bool isSupportedEncoding(const QByteArray &encoding);
+ static bool isSupportedEncoding(QByteArrayView encoding);
static QByteArrayList acceptedEncoding();
+ QString errorString() const;
+
private:
bool isPotentialArchiveBomb() const;
bool hasDataInternal() const;
@@ -120,6 +86,8 @@ private:
bool countDecompressed = false;
std::unique_ptr<QDecompressHelper> countHelper;
+ QString errorStr;
+
// Used for calculating the ratio
qint64 archiveBombCheckThreshold = 10 * 1024 * 1024;
qint64 totalUncompressedBytes = 0;
diff --git a/src/network/access/qhsts.cpp b/src/network/access/qhsts.cpp
index 99c4c605dd..21ed08ce4a 100644
--- a/src/network/access/qhsts.cpp
+++ b/src/network/access/qhsts.cpp
@@ -1,44 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** 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) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qhsts_p.h"
+#include "qhttpheaders.h"
+
#include "QtCore/private/qipaddress_p.h"
#include "QtCore/qlist.h"
@@ -76,7 +42,7 @@ static bool is_valid_domain_name(const QString &host)
return true;
}
-void QHstsCache::updateFromHeaders(const QList<QPair<QByteArray, QByteArray>> &headers,
+void QHstsCache::updateFromHeaders(const QHttpHeaders &headers,
const QUrl &url)
{
if (!url.isValid())
@@ -210,7 +176,7 @@ bool QHstsCache::isKnownHost(const QUrl &url) const
}
}
- const int dot = nameToTest.fragment.indexOf(QLatin1Char('.'));
+ const qsizetype dot = nameToTest.fragment.indexOf(u'.');
if (dot == -1)
break;
@@ -322,7 +288,7 @@ static bool isSeparator(char c)
return isLWS(c) || std::find(separators, end, c) != end;
}
-static QByteArray unescapeMaxAge(const QByteArray &value)
+static QByteArrayView unescapeMaxAge(QByteArrayView value)
{
if (value.size() < 2 || value[0] != '"')
return value;
@@ -360,27 +326,25 @@ quoted-pair = "\" CHAR
*/
-bool QHstsHeaderParser::parse(const QList<QPair<QByteArray, QByteArray>> &headers)
+bool QHstsHeaderParser::parse(const QHttpHeaders &headers)
{
- for (const auto &h : headers) {
- // We use '==' since header name was already 'trimmed' for us:
- if (h.first == "Strict-Transport-Security") {
- header = h.second;
- // RFC6797, 8.1:
- //
- // The UA MUST ignore any STS header fields not conforming to the
- // grammar specified in Section 6.1 ("Strict-Transport-Security HTTP
- // Response Header Field").
- //
- // If a UA receives more than one STS header field in an HTTP
- // response message over secure transport, then the UA MUST process
- // only the first such header field.
- //
- // We read this as: ignore all invalid headers and take the first valid:
- if (parseSTSHeader() && maxAgeFound) {
- expiry = QDateTime::currentDateTimeUtc().addSecs(maxAge);
- return true;
- }
+ for (const auto &value : headers.values(
+ QHttpHeaders::WellKnownHeader::StrictTransportSecurity)) {
+ header = value;
+ // RFC6797, 8.1:
+ //
+ // The UA MUST ignore any STS header fields not conforming to the
+ // grammar specified in Section 6.1 ("Strict-Transport-Security HTTP
+ // Response Header Field").
+ //
+ // If a UA receives more than one STS header field in an HTTP
+ // response message over secure transport, then the UA MUST process
+ // only the first such header field.
+ //
+ // We read this as: ignore all invalid headers and take the first valid:
+ if (parseSTSHeader() && maxAgeFound) {
+ expiry = QDateTime::currentDateTimeUtc().addSecs(maxAge);
+ return true;
}
}
@@ -436,7 +400,7 @@ bool QHstsHeaderParser::parseDirective()
if (token == ";") // That's a weird grammar, but that's what it is.
return true;
- if (!isTOKEN(token[0])) // Not a valid directive-name.
+ if (!isTOKEN(token.at(0))) // Not a valid directive-name.
return false;
const QByteArray directiveName = token;
@@ -481,7 +445,7 @@ bool QHstsHeaderParser::processDirective(const QByteArray &name, const QByteArra
return false;
}
- const QByteArray unescapedValue = unescapeMaxAge(value);
+ const QByteArrayView unescapedValue = unescapeMaxAge(value);
if (!unescapedValue.size())
return false;
@@ -517,13 +481,13 @@ bool QHstsHeaderParser::nextToken()
// Fortunately enough, by this point qhttpnetworkreply already got rid of
// [CRLF] parts, but we can have 1*(SP|HT) yet.
- while (tokenPos < header.size() && isLWS(header[tokenPos]))
+ while (tokenPos < header.size() && isLWS(header.at(tokenPos)))
++tokenPos;
if (tokenPos == header.size())
return true;
- const char ch = header[tokenPos];
+ const char ch = header.at(tokenPos);
if (ch == ';' || ch == '=') {
token.append(ch);
++tokenPos;
@@ -537,17 +501,17 @@ bool QHstsHeaderParser::nextToken()
if (ch == '"') {
int last = tokenPos + 1;
while (last < header.size()) {
- if (header[last] == '"') {
+ if (header.at(last) == '"') {
// The end of a quoted-string.
break;
- } else if (header[last] == '\\') {
+ } else if (header.at(last) == '\\') {
// quoted-pair = "\" CHAR
- if (last + 1 < header.size() && isCHAR(header[last + 1]))
+ if (last + 1 < header.size() && isCHAR(header.at(last + 1)))
last += 2;
else
return false;
} else {
- if (!isTEXT(header[last]))
+ if (!isTEXT(header.at(last)))
return false;
++last;
}
@@ -568,7 +532,7 @@ bool QHstsHeaderParser::nextToken()
return false;
int last = tokenPos + 1;
- while (last < header.size() && isTOKEN(header[last]))
+ while (last < header.size() && isTOKEN(header.at(last)))
++last;
token = header.mid(tokenPos, last - tokenPos);
diff --git a/src/network/access/qhsts_p.h b/src/network/access/qhsts_p.h
index ea9769ac6c..ff9378197b 100644
--- a/src/network/access/qhsts_p.h
+++ b/src/network/access/qhsts_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** 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) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QHSTS_P_H
#define QHSTS_P_H
@@ -67,11 +31,13 @@
QT_BEGIN_NAMESPACE
+class QHttpHeaders;
+
class Q_AUTOTEST_EXPORT QHstsCache
{
public:
- void updateFromHeaders(const QList<QPair<QByteArray, QByteArray>> &headers,
+ void updateFromHeaders(const QHttpHeaders &headers,
const QUrl &url);
void updateFromPolicies(const QList<QHstsPolicy> &hosts);
void updateKnownHost(const QUrl &url, const QDateTime &expires,
@@ -126,7 +92,7 @@ class Q_AUTOTEST_EXPORT QHstsHeaderParser
{
public:
- bool parse(const QList<QPair<QByteArray, QByteArray>> &headers);
+ bool parse(const QHttpHeaders &headers);
QDateTime expirationDate() const { return expiry; }
bool includeSubDomains() const { return subDomainsFound; }
diff --git a/src/network/access/qhstspolicy.cpp b/src/network/access/qhstspolicy.cpp
index 3d15cba407..323e562c3c 100644
--- a/src/network/access/qhstspolicy.cpp
+++ b/src/network/access/qhstspolicy.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** 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) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qhstspolicy.h"
@@ -57,8 +21,8 @@ QT_BEGIN_NAMESPACE
RFC6797.
You can set expiry time and host name for this policy, and control whether it
- applies to subdomains, either in the constructor or by calling setExpiry(),
- setHost() and setIncludesSubdomains().
+ applies to subdomains, either in the constructor or by calling \l setExpiry(),
+ \l setHost() and \l setIncludesSubDomains().
\sa QNetworkAccessManager::setStrictTransportSecurityEnabled()
*/
diff --git a/src/network/access/qhstspolicy.h b/src/network/access/qhstspolicy.h
index 0cf0c73f9c..af5841906c 100644
--- a/src/network/access/qhstspolicy.h
+++ b/src/network/access/qhstspolicy.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** 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) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QHSTSPOLICY_H
#define QHSTSPOLICY_H
@@ -68,7 +32,7 @@ public:
QHstsPolicy &operator=(QHstsPolicy &&other) noexcept { swap(other); return *this; }
~QHstsPolicy();
- void swap(QHstsPolicy &other) noexcept { qSwap(d, other.d); }
+ void swap(QHstsPolicy &other) noexcept { d.swap(other.d); }
void setHost(const QString &host, QUrl::ParsingMode mode = QUrl::DecodedMode);
QString host(QUrl::ComponentFormattingOptions options = QUrl::FullyDecoded) const;
diff --git a/src/network/access/qhstsstore.cpp b/src/network/access/qhstsstore.cpp
index 67be8a2de2..a972a90ee7 100644
--- a/src/network/access/qhstsstore.cpp
+++ b/src/network/access/qhstsstore.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** 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) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qhstsstore_p.h"
#include "qhstspolicy.h"
@@ -52,6 +16,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
static QString host_name_to_settings_key(const QString &hostName)
{
const QByteArray hostNameAsHex(hostName.toUtf8().toHex());
@@ -114,7 +80,7 @@ void QHstsStore::synchronize()
if (observedPolicies.size()) {
beginHstsGroups();
- for (const QHstsPolicy &policy : qAsConst(observedPolicies)) {
+ for (const QHstsPolicy &policy : std::as_const(observedPolicies)) {
const QString key(host_name_to_settings_key(policy.host()));
// If we fail to write a new, updated policy, we also remove the old one.
if (policy.isExpired() || !serializePolicy(key, policy))
@@ -136,13 +102,13 @@ QString QHstsStore::absoluteFilePath(const QString &dirName)
{
const QDir dir(dirName.isEmpty() ? QStandardPaths::writableLocation(QStandardPaths::CacheLocation)
: dirName);
- return dir.absoluteFilePath(QLatin1String("hstsstore"));
+ return dir.absoluteFilePath("hstsstore"_L1);
}
void QHstsStore::beginHstsGroups()
{
- store.beginGroup(QLatin1String("StrictTransportSecurity"));
- store.beginGroup(QLatin1String("Policies"));
+ store.beginGroup("StrictTransportSecurity"_L1);
+ store.beginGroup("Policies"_L1);
}
void QHstsStore::endHstsGroups()
diff --git a/src/network/access/qhstsstore_p.h b/src/network/access/qhstsstore_p.h
index bbf7379b61..c1ec3af122 100644
--- a/src/network/access/qhstsstore_p.h
+++ b/src/network/access/qhstsstore_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 The Qt Company Ltd.
-** 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) 2017 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QHSTSSTORE_P_H
#define QHSTSSTORE_P_H
diff --git a/src/network/access/qhttp1configuration.cpp b/src/network/access/qhttp1configuration.cpp
new file mode 100644
index 0000000000..cfa929bca5
--- /dev/null
+++ b/src/network/access/qhttp1configuration.cpp
@@ -0,0 +1,154 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qhttp1configuration.h"
+
+#include <QtCore/private/qnumeric_p.h>
+#include <QtCore/qhashfunctions.h>
+
+QT_BEGIN_NAMESPACE
+
+// QHttp1ConfigurationPrivate is unused until we need it:
+static_assert(sizeof(QHttp1Configuration) == sizeof(void*),
+ "You have added too many members to QHttp1Configuration::ShortData. "
+ "Decrease their size or switch to using a d-pointer.");
+
+/*!
+ \class QHttp1Configuration
+ \brief The QHttp1Configuration class controls HTTP/1 parameters and settings.
+ \since 6.5
+
+ \reentrant
+ \inmodule QtNetwork
+ \ingroup network
+ \ingroup shared
+
+ QHttp1Configuration controls HTTP/1 parameters and settings that
+ QNetworkAccessManager will use to send requests and process responses.
+
+ \note The configuration must be set before the first request
+ was sent to a given host (and thus an HTTP/1 session established).
+
+ \sa QNetworkRequest::setHttp1Configuration(), QNetworkRequest::http1Configuration(), QNetworkAccessManager
+*/
+
+/*!
+ Default constructs a QHttp1Configuration object.
+*/
+QHttp1Configuration::QHttp1Configuration()
+ : u(ShortData{6, {}}) // QHttpNetworkConnectionPrivate::defaultHttpChannelCount
+{
+}
+
+/*!
+ Copy-constructs this QHttp1Configuration.
+*/
+QHttp1Configuration::QHttp1Configuration(const QHttp1Configuration &)
+ = default;
+
+/*!
+ \fn QHttp1Configuration::QHttp1Configuration(QHttp1Configuration &&other)
+
+ Move-constructs this QHttp1Configuration from \a other.
+
+ \note The moved-from object \a other is placed in a
+ partially-formed state, in which the only valid operations are
+ destruction and assignment of a new value.
+*/
+
+/*!
+ Copy-assigns \a other to this QHttp1Configuration.
+*/
+QHttp1Configuration &QHttp1Configuration::operator=(const QHttp1Configuration &)
+ = default;
+
+/*!
+ \fn QHttp1Configuration &QHttp1Configuration::operator=(QHttp1Configuration &&)
+
+ Move-assigns \a other to this QHttp1Configuration.
+
+ \note The moved-from object \a other is placed in a
+ partially-formed state, in which the only valid operations are
+ destruction and assignment of a new value.
+*/
+
+/*!
+ Destructor.
+*/
+QHttp1Configuration::~QHttp1Configuration()
+ = default;
+
+/*!
+ Sets the number of connections (minimum: 1; maximum: 255)
+ used per http(s) \e{host}:\e{port} combination to \a number.
+
+ If \a number is ≤ 0, does nothing. If \a number is > 255, 255 is used.
+
+ \sa numberOfConnectionsPerHost
+*/
+void QHttp1Configuration::setNumberOfConnectionsPerHost(qsizetype number)
+{
+ auto n = qt_saturate<std::uint8_t>(number);
+ if (n == 0)
+ return;
+ u.data.numConnectionsPerHost = n;
+}
+
+/*!
+ Returns the number of connections used per http(s) \c{host}:\e{port}
+ combination. The default is six (6).
+
+ \sa setNumberOfConnectionsPerHost
+*/
+qsizetype QHttp1Configuration::numberOfConnectionsPerHost() const
+{
+ return u.data.numConnectionsPerHost;
+}
+
+/*!
+ \fn void QHttp1Configuration::swap(QHttp1Configuration &other)
+
+ Swaps this HTTP/1 configuration with \a other. This operation is very fast
+ and never fails.
+*/
+
+/*!
+ \fn bool QHttp1Configuration::operator==(const QHttp1Configuration &lhs, const QHttp1Configuration &rhs) noexcept
+ \since 6.5
+
+ Returns \c true if \a lhs and \a rhs represent the same set of HTTP/1
+ parameters.
+*/
+
+/*!
+ \fn bool QHttp1Configuration::operator!=(const QHttp1Configuration &lhs, const QHttp1Configuration &rhs) noexcept
+ \since 6.5
+
+ Returns \c true if \a lhs and \a rhs do not represent the same set of
+ HTTP/1 parameters.
+*/
+
+/*!
+ \fn size_t QHttp1Configuration::qHash(const QHttp1Configuration &key, size_t seed)
+ \since 6.5
+
+ Returns the hash value for the \a key, using \a seed to seed the calculation.
+*/
+
+/*!
+ \internal
+*/
+bool QHttp1Configuration::equals(const QHttp1Configuration &other) const noexcept
+{
+ return u.data.numConnectionsPerHost == other.u.data.numConnectionsPerHost;
+}
+
+/*!
+ \internal
+*/
+size_t QHttp1Configuration::hash(size_t seed) const noexcept
+{
+ return qHash(u.data.numConnectionsPerHost, seed);
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qhttp1configuration.h b/src/network/access/qhttp1configuration.h
new file mode 100644
index 0000000000..128b8aa5aa
--- /dev/null
+++ b/src/network/access/qhttp1configuration.h
@@ -0,0 +1,62 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QHTTP1CONFIGURATION_H
+#define QHTTP1CONFIGURATION_H
+
+#include <QtNetwork/qtnetworkglobal.h>
+
+#include <utility>
+#include <cstdint>
+
+QT_REQUIRE_CONFIG(http);
+
+QT_BEGIN_NAMESPACE
+
+class QHttp1ConfigurationPrivate;
+class QHttp1Configuration
+{
+public:
+ Q_NETWORK_EXPORT QHttp1Configuration();
+ Q_NETWORK_EXPORT QHttp1Configuration(const QHttp1Configuration &other);
+ QHttp1Configuration(QHttp1Configuration &&other) noexcept
+ : u{other.u} { other.u.d = nullptr; }
+
+ Q_NETWORK_EXPORT QHttp1Configuration &operator=(const QHttp1Configuration &other);
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QHttp1Configuration)
+
+ Q_NETWORK_EXPORT ~QHttp1Configuration();
+
+ Q_NETWORK_EXPORT void setNumberOfConnectionsPerHost(qsizetype amount);
+ Q_NETWORK_EXPORT qsizetype numberOfConnectionsPerHost() const;
+
+ void swap(QHttp1Configuration &other) noexcept
+ { std::swap(u, other.u); }
+
+private:
+ struct ShortData {
+ std::uint8_t numConnectionsPerHost;
+ char reserved[sizeof(void*) - sizeof(numConnectionsPerHost)];
+ };
+ union U {
+ U(ShortData _data) : data(_data) {}
+ QHttp1ConfigurationPrivate *d;
+ ShortData data;
+ } u;
+
+ Q_NETWORK_EXPORT bool equals(const QHttp1Configuration &other) const noexcept;
+ Q_NETWORK_EXPORT size_t hash(size_t seed) const noexcept;
+
+ friend bool operator==(const QHttp1Configuration &lhs, const QHttp1Configuration &rhs) noexcept
+ { return lhs.equals(rhs); }
+ friend bool operator!=(const QHttp1Configuration &lhs, const QHttp1Configuration &rhs) noexcept
+ { return !lhs.equals(rhs); }
+
+ friend size_t qHash(const QHttp1Configuration &key, size_t seed = 0) noexcept { return key.hash(seed); }
+};
+
+Q_DECLARE_SHARED(QHttp1Configuration)
+
+QT_END_NAMESPACE
+
+#endif // QHTTP1CONFIGURATION_H
diff --git a/src/network/access/qhttp2configuration.cpp b/src/network/access/qhttp2configuration.cpp
index 626a564fff..b718ddc755 100644
--- a/src/network/access/qhttp2configuration.cpp
+++ b/src/network/access/qhttp2configuration.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** 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) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qhttp2configuration.h"
@@ -253,7 +217,7 @@ bool QHttp2Configuration::setStreamReceiveWindowSize(unsigned size)
/*!
Returns the window size for stream-level flow control.
The default value QNetworkAccessManager will be using is
- 65535 octets (see \l {https://httpwg.org/specs/rfc7540.html#SettingValues}{RFC 7540}).
+ 214748364 octets (see \l {https://httpwg.org/specs/rfc7540.html#SettingValues}{RFC 7540}).
*/
unsigned QHttp2Configuration::streamReceiveWindowSize() const
{
diff --git a/src/network/access/qhttp2configuration.h b/src/network/access/qhttp2configuration.h
index 3d8ba771b4..ae08b664d4 100644
--- a/src/network/access/qhttp2configuration.h
+++ b/src/network/access/qhttp2configuration.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2019 The Qt Company Ltd.
-** 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) 2019 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QHTTP2CONFIGURATION_H
#define QHTTP2CONFIGURATION_H
@@ -44,9 +8,7 @@
#include <QtCore/qshareddata.h>
-#ifndef Q_CLANG_QDOC
QT_REQUIRE_CONFIG(http);
-#endif
QT_BEGIN_NAMESPACE
diff --git a/src/network/access/qhttp2connection.cpp b/src/network/access/qhttp2connection.cpp
new file mode 100644
index 0000000000..8560e0da38
--- /dev/null
+++ b/src/network/access/qhttp2connection.cpp
@@ -0,0 +1,1752 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qhttp2connection_p.h"
+
+#include <private/bitstreams_p.h>
+
+#include <QtCore/private/qnumeric_p.h>
+#include <QtCore/private/qiodevice_p.h>
+#include <QtCore/private/qnoncontiguousbytedevice_p.h>
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/QRandomGenerator>
+#include <QtCore/qloggingcategory.h>
+
+#include <algorithm>
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(qHttp2ConnectionLog, "qt.network.http2.connection", QtCriticalMsg)
+
+using namespace Qt::StringLiterals;
+using namespace Http2;
+
+/*!
+ \class QHttp2Stream
+ \inmodule QtNetwork
+ \internal
+
+ The QHttp2Stream class represents a single HTTP/2 stream.
+ Must be created by QHttp2Connection.
+
+ \sa QHttp2Connection
+*/
+
+QHttp2Stream::QHttp2Stream(QHttp2Connection *connection, quint32 streamID) noexcept
+ : QObject(connection), m_streamID(streamID)
+{
+ Q_ASSERT(connection);
+ Q_ASSERT(streamID); // stream id 0 is reserved for connection control messages
+ qCDebug(qHttp2ConnectionLog, "[%p] new stream %u", connection, streamID);
+}
+
+QHttp2Stream::~QHttp2Stream() noexcept = default;
+
+/*!
+ \fn quint32 QHttp2Stream::streamID() const noexcept
+
+ Returns the stream ID of this stream.
+*/
+
+/*!
+ \fn void QHttp2Stream::headersReceived(const HPack::HttpHeader &headers, bool endStream)
+
+ This signal is emitted when the remote peer has sent a HEADERS frame, and
+ potentially some CONTINUATION frames, ending with the END_HEADERS flag
+ to this stream.
+
+ The headers are internally combined and decompressed, and are accessible
+ through the \a headers parameter. If the END_STREAM flag was set, the
+ \a endStream parameter will be \c true, indicating that the peer does not
+ intend to send any more frames on this stream.
+
+ \sa receivedHeaders()
+*/
+
+/*!
+ \fn void QHttp2Stream::headersUpdated()
+
+ This signal may be emitted if a new HEADERS frame was received after
+ already processing a previous HEADERS frame.
+
+ \sa headersReceived(), receivedHeaders()
+*/
+
+/*!
+ \fn void QHttp2Stream::errorOccurred(quint32 errorCode, const QString &errorString)
+
+ This signal is emitted when the stream has encountered an error. The
+ \a errorCode parameter is the HTTP/2 error code, and the \a errorString
+ parameter is a human-readable description of the error.
+
+ \sa https://www.rfc-editor.org/rfc/rfc7540#section-7
+*/
+
+/*!
+ \fn void QHttp2Stream::stateChanged(State newState)
+
+ This signal is emitted when the state of the stream changes. The \a newState
+ parameter is the new state of the stream.
+
+ Examples of this is sending or receiving a frame with the END_STREAM flag.
+ This will transition the stream to the HalfClosedLocal or HalfClosedRemote
+ state, respectively.
+
+ \sa state()
+*/
+
+
+/*!
+ \fn void QHttp2Stream::promisedStreamReceived(quint32 newStreamID)
+
+ This signal is emitted when the remote peer has promised a new stream with
+ the given \a newStreamID.
+
+ \sa QHttp2Connection::promisedStream()
+*/
+
+/*!
+ \fn void QHttp2Stream::uploadBlocked()
+
+ This signal is emitted when the stream is unable to send more data because
+ the remote peer's receive window is full.
+
+ This is mostly intended for diagnostics as there is no expectation that the
+ user can do anything to react to this.
+*/
+
+/*!
+ \fn void QHttp2Stream::dataReceived(const QByteArray &data, bool endStream)
+
+ This signal is emitted when the stream has received a DATA frame from the
+ remote peer. The \a data parameter contains the payload of the frame, and
+ the \a endStream parameter is \c true if the END_STREAM flag was set.
+
+ \sa downloadBuffer()
+*/
+
+/*!
+ \fn void QHttp2Stream::bytesWritten(qint64 bytesWritten)
+
+ This signal is emitted when the stream has written \a bytesWritten bytes to
+ the network.
+*/
+
+/*!
+ \fn void QHttp2Stream::uploadDeviceError(const QString &errorString)
+
+ This signal is emitted if the upload device encounters an error while
+ sending data. The \a errorString parameter is a human-readable description
+ of the error.
+*/
+
+/*!
+ \fn void QHttp2Stream::uploadFinished()
+
+ This signal is emitted when the stream has finished sending all the data
+ from the upload device.
+
+ If the END_STREAM flag was set for sendData() then the stream will be
+ closed for further writes before this signal is emitted.
+*/
+
+/*!
+ \fn bool QHttp2Stream::isUploadingDATA() const noexcept
+
+ Returns \c true if the stream is currently sending DATA frames.
+*/
+
+/*!
+ \fn State QHttp2Stream::state() const noexcept
+
+ Returns the current state of the stream.
+
+ \sa stateChanged()
+*/
+/*!
+ \fn bool QHttp2Stream::isActive() const noexcept
+
+ Returns \c true if the stream has been opened and is not yet closed.
+*/
+/*!
+ \fn bool QHttp2Stream::isPromisedStream() const noexcept
+
+ Returns \c true if the stream was promised by the remote peer.
+*/
+/*!
+ \fn bool QHttp2Stream::wasReset() const noexcept
+
+ Returns \c true if the stream was reset by the remote peer.
+*/
+/*!
+ \fn quint32 QHttp2Stream::RST_STREAM_code() const noexcept
+
+ Returns the HTTP/2 error code if the stream was reset by the remote peer.
+ If the stream was not reset, this function returns 0.
+*/
+/*!
+ \fn HPack::HttpHeader QHttp2Stream::receivedHeaders() const noexcept
+
+ Returns the headers received from the remote peer, if any.
+*/
+/*!
+ \fn QByteDataBuffer QHttp2Stream::downloadBuffer() const noexcept
+
+ Returns the buffer containing the data received from the remote peer.
+*/
+
+void QHttp2Stream::finishWithError(quint32 errorCode, const QString &message)
+{
+ qCDebug(qHttp2ConnectionLog, "[%p] stream %u finished with error: %ls (error code: %u)",
+ getConnection(), m_streamID, qUtf16Printable(message), errorCode);
+ transitionState(StateTransition::RST);
+ emit errorOccurred(errorCode, message);
+}
+
+void QHttp2Stream::finishWithError(quint32 errorCode)
+{
+ QNetworkReply::NetworkError error = QNetworkReply::NoError;
+ QString message;
+ qt_error(errorCode, error, message);
+ finishWithError(error, message);
+}
+
+/*!
+ Sends a RST_STREAM frame with the given \a errorCode.
+ This closes the stream for both sides, any further frames will be dropped.
+
+ Returns \c false if the stream is closed or idle, also if it fails to send
+ the RST_STREAM frame. Otherwise, returns \c true.
+*/
+bool QHttp2Stream::sendRST_STREAM(quint32 errorCode)
+{
+ if (m_state == State::Closed || m_state == State::Idle)
+ return false;
+ qCDebug(qHttp2ConnectionLog, "[%p] sending RST_STREAM on stream %u, code: %u", getConnection(),
+ m_streamID, errorCode);
+ transitionState(StateTransition::RST);
+
+ QHttp2Connection *connection = getConnection();
+ FrameWriter &frameWriter = connection->frameWriter;
+ frameWriter.start(FrameType::RST_STREAM, FrameFlag::EMPTY, m_streamID);
+ frameWriter.append(errorCode);
+ return frameWriter.write(*connection->getSocket());
+}
+
+/*!
+ Sends a DATA frame with the bytes obtained from \a device.
+
+ This function will send as many DATA frames as needed to send all the data
+ from \a device. If \a endStream is \c true, the END_STREAM flag will be set.
+
+ \a device must stay alive for the duration of the upload.
+ A way of doing this is to heap-allocate the \a device and parent it to the
+ QHttp2Stream.
+*/
+void QHttp2Stream::sendDATA(QIODevice *device, bool endStream)
+{
+ Q_ASSERT(!m_uploadDevice);
+ Q_ASSERT(!m_uploadByteDevice);
+ Q_ASSERT(device);
+ if (m_state != State::Open && m_state != State::HalfClosedRemote)
+ return;
+
+ auto *byteDevice = QNonContiguousByteDeviceFactory::create(device);
+ connect(this, &QHttp2Stream::uploadFinished, byteDevice, &QObject::deleteLater);
+ byteDevice->setParent(this);
+ m_uploadDevice = device;
+ qCDebug(qHttp2ConnectionLog, "[%p] starting sendDATA on stream %u, of IODevice: %p",
+ getConnection(), m_streamID, device);
+ sendDATA(byteDevice, endStream);
+}
+
+/*!
+ Sends a DATA frame with the bytes obtained from \a device.
+
+ This function will send as many DATA frames as needed to send all the data
+ from \a device. If \a endStream is \c true, the END_STREAM flag will be set.
+
+ \a device must stay alive for the duration of the upload.
+ A way of doing this is to heap-allocate the \a device and parent it to the
+ QHttp2Stream.
+*/
+void QHttp2Stream::sendDATA(QNonContiguousByteDevice *device, bool endStream)
+{
+ Q_ASSERT(!m_uploadByteDevice);
+ Q_ASSERT(device);
+ if (m_state != State::Open && m_state != State::HalfClosedRemote)
+ return;
+
+ qCDebug(qHttp2ConnectionLog, "[%p] starting sendDATA on stream %u, of device: %p",
+ getConnection(), m_streamID, device);
+
+ m_uploadByteDevice = device;
+ m_endStreamAfterDATA = endStream;
+ connect(m_uploadByteDevice, &QNonContiguousByteDevice::readyRead, this,
+ &QHttp2Stream::maybeResumeUpload);
+ connect(m_uploadByteDevice, &QObject::destroyed, this, &QHttp2Stream::uploadDeviceDestroyed);
+
+ internalSendDATA();
+}
+
+void QHttp2Stream::internalSendDATA()
+{
+ Q_ASSERT(m_uploadByteDevice);
+ QHttp2Connection *connection = getConnection();
+ Q_ASSERT(connection->maxFrameSize > frameHeaderSize);
+ QIODevice *socket = connection->getSocket();
+
+ qCDebug(qHttp2ConnectionLog,
+ "[%p] stream %u, about to write to socket, current session window size: %d, stream "
+ "window size: %d, bytes available: %lld",
+ connection, m_streamID, connection->sessionSendWindowSize, m_sendWindow,
+ m_uploadByteDevice->size() - m_uploadByteDevice->pos());
+
+ qint32 remainingWindowSize = std::min<qint32>(connection->sessionSendWindowSize, m_sendWindow);
+ FrameWriter &frameWriter = connection->frameWriter;
+ qint64 totalBytesWritten = 0;
+ const auto deviceCanRead = [this, connection] {
+ // We take advantage of knowing the internals of one of the devices used.
+ // It will request X bytes to move over to the http thread if there's
+ // not enough left, so we give it a large size. It will anyway return
+ // the size it can actually provide.
+ const qint64 requestSize = connection->maxFrameSize * 10ll;
+ qint64 tmp = 0;
+ return m_uploadByteDevice->readPointer(requestSize, tmp) != nullptr && tmp > 0;
+ };
+
+ bool sentEND_STREAM = false;
+ while (remainingWindowSize && deviceCanRead()) {
+ quint32 bytesWritten = 0;
+ qint32 remainingBytesInFrame = qint32(connection->maxFrameSize);
+ frameWriter.start(FrameType::DATA, FrameFlag::EMPTY, streamID());
+
+ while (remainingWindowSize && deviceCanRead() && remainingBytesInFrame) {
+ const qint32 maxToWrite = std::min(remainingWindowSize, remainingBytesInFrame);
+
+ qint64 outBytesAvail = 0;
+ const char *readPointer = m_uploadByteDevice->readPointer(maxToWrite, outBytesAvail);
+ if (!readPointer || outBytesAvail <= 0) {
+ qCDebug(qHttp2ConnectionLog,
+ "[%p] stream %u, cannot write data, device (%p) has %lld bytes available",
+ connection, m_streamID, m_uploadByteDevice, outBytesAvail);
+ break;
+ }
+ const qint32 bytesToWrite = qint32(std::min<qint64>(maxToWrite, outBytesAvail));
+ frameWriter.append(QByteArrayView(readPointer, bytesToWrite));
+ m_uploadByteDevice->advanceReadPointer(bytesToWrite);
+
+ bytesWritten += bytesToWrite;
+
+ m_sendWindow -= bytesToWrite;
+ Q_ASSERT(m_sendWindow >= 0);
+ connection->sessionSendWindowSize -= bytesToWrite;
+ Q_ASSERT(connection->sessionSendWindowSize >= 0);
+ remainingBytesInFrame -= bytesToWrite;
+ Q_ASSERT(remainingBytesInFrame >= 0);
+ remainingWindowSize -= bytesToWrite;
+ Q_ASSERT(remainingWindowSize >= 0);
+ }
+
+ qCDebug(qHttp2ConnectionLog, "[%p] stream %u, writing %u bytes to socket", connection,
+ m_streamID, bytesWritten);
+ if (!deviceCanRead() && m_uploadByteDevice->atEnd() && m_endStreamAfterDATA) {
+ sentEND_STREAM = true;
+ frameWriter.addFlag(FrameFlag::END_STREAM);
+ }
+ if (!frameWriter.write(*socket)) {
+ qCDebug(qHttp2ConnectionLog, "[%p] stream %u, failed to write to socket", connection,
+ m_streamID);
+ finishWithError(QNetworkReply::ProtocolFailure, "failed to write to socket"_L1);
+ return;
+ }
+
+ totalBytesWritten += bytesWritten;
+ }
+
+ qCDebug(qHttp2ConnectionLog,
+ "[%p] stream %u, wrote %lld bytes total, if the device is not exhausted, we'll write "
+ "more later. Remaining window size: %d",
+ connection, m_streamID, totalBytesWritten, remainingWindowSize);
+
+ emit bytesWritten(totalBytesWritten);
+ if (sentEND_STREAM || (!deviceCanRead() && m_uploadByteDevice->atEnd())) {
+ qCDebug(qHttp2ConnectionLog,
+ "[%p] stream %u, exhausted device %p, sent END_STREAM? %d, %ssending end stream "
+ "after DATA",
+ connection, m_streamID, m_uploadByteDevice, sentEND_STREAM,
+ m_endStreamAfterDATA ? "" : "not ");
+ if (!sentEND_STREAM && m_endStreamAfterDATA) {
+ // We need to send an empty DATA frame with END_STREAM since we
+ // have exhausted the device, but we haven't sent END_STREAM yet.
+ // This can happen if we got a final readyRead to signify no more
+ // data available, but we hadn't sent the END_STREAM flag yet.
+ frameWriter.start(FrameType::DATA, FrameFlag::END_STREAM, streamID());
+ frameWriter.write(*socket);
+ }
+ finishSendDATA();
+ } else if (isUploadBlocked()) {
+ qCDebug(qHttp2ConnectionLog, "[%p] stream %u, upload blocked", connection, m_streamID);
+ emit uploadBlocked();
+ }
+}
+
+void QHttp2Stream::finishSendDATA()
+{
+ if (m_endStreamAfterDATA)
+ transitionState(StateTransition::CloseLocal);
+
+ disconnect(m_uploadByteDevice, nullptr, this, nullptr);
+ m_uploadDevice = nullptr;
+ m_uploadByteDevice = nullptr;
+ emit uploadFinished();
+}
+
+void QHttp2Stream::maybeResumeUpload()
+{
+ qCDebug(qHttp2ConnectionLog,
+ "[%p] stream %u, maybeResumeUpload. Upload device: %p, bytes available: %lld, blocked? "
+ "%d",
+ getConnection(), m_streamID, m_uploadByteDevice,
+ !m_uploadByteDevice ? 0 : m_uploadByteDevice->size() - m_uploadByteDevice->pos(),
+ isUploadBlocked());
+ if (isUploadingDATA() && !isUploadBlocked())
+ internalSendDATA();
+}
+
+/*!
+ Returns \c true if the stream is currently unable to send more data because
+ the remote peer's receive window is full.
+*/
+bool QHttp2Stream::isUploadBlocked() const noexcept
+{
+ constexpr auto MinFrameSize = Http2::frameHeaderSize + 1; // 1 byte payload
+ return isUploadingDATA()
+ && (m_sendWindow <= MinFrameSize
+ || getConnection()->sessionSendWindowSize <= MinFrameSize);
+}
+
+void QHttp2Stream::uploadDeviceReadChannelFinished()
+{
+ maybeResumeUpload();
+}
+
+/*!
+ Sends a HEADERS frame with the given \a headers and \a priority.
+ If \a endStream is \c true, the END_STREAM flag will be set, and the stream
+ will be closed for future writes.
+ If the headers are too large, or the stream is not in the correct state,
+ this function will return \c false. Otherwise, it will return \c true.
+*/
+bool QHttp2Stream::sendHEADERS(const HPack::HttpHeader &headers, bool endStream, quint8 priority)
+{
+ using namespace HPack;
+ if (auto hs = header_size(headers);
+ !hs.first || hs.second > getConnection()->maxHeaderListSize()) {
+ return false;
+ }
+
+ transitionState(StateTransition::Open);
+
+ Q_ASSERT(m_state == State::Open || m_state == State::HalfClosedRemote);
+
+ QHttp2Connection *connection = getConnection();
+
+ qCDebug(qHttp2ConnectionLog, "[%p] stream %u, sending HEADERS frame with %u entries",
+ connection, streamID(), uint(headers.size()));
+
+ QIODevice *socket = connection->getSocket();
+ FrameWriter &frameWriter = connection->frameWriter;
+
+ frameWriter.start(FrameType::HEADERS, FrameFlag::PRIORITY | FrameFlag::END_HEADERS, streamID());
+ if (endStream)
+ frameWriter.addFlag(FrameFlag::END_STREAM);
+
+ frameWriter.append(quint32()); // No stream dependency in Qt.
+ frameWriter.append(priority);
+
+ // Compress in-place:
+ BitOStream outputStream(frameWriter.outboundFrame().buffer);
+ if (connection->m_connectionType == QHttp2Connection::Type::Client) {
+ if (!connection->encoder.encodeRequest(outputStream, headers))
+ return false;
+ } else {
+ if (!connection->encoder.encodeResponse(outputStream, headers))
+ return false;
+ }
+
+ bool result = frameWriter.writeHEADERS(*socket, connection->maxFrameSize);
+ if (endStream)
+ transitionState(StateTransition::CloseLocal);
+
+ return result;
+}
+
+/*!
+ Sends a WINDOW_UPDATE frame with the given \a delta.
+ This increases our receive window size for this stream, allowing the remote
+ peer to send more data.
+*/
+void QHttp2Stream::sendWINDOW_UPDATE(quint32 delta)
+{
+ QHttp2Connection *connection = getConnection();
+ m_recvWindow += qint32(delta);
+ connection->sendWINDOW_UPDATE(streamID(), delta);
+}
+
+void QHttp2Stream::uploadDeviceDestroyed()
+{
+ if (isUploadingDATA()) {
+ // We're in the middle of sending DATA frames, we need to abort
+ // the stream.
+ sendRST_STREAM(CANCEL);
+ emit uploadDeviceError("Upload device destroyed while uploading"_L1);
+ }
+ m_uploadDevice = nullptr;
+}
+
+void QHttp2Stream::setState(State newState)
+{
+ if (m_state == newState)
+ return;
+ qCDebug(qHttp2ConnectionLog, "[%p] stream %u, state changed from %d to %d", getConnection(),
+ streamID(), int(m_state), int(newState));
+ m_state = newState;
+ emit stateChanged(newState);
+}
+
+// Changes the state as appropriate given the current state and the transition.
+// Always call this before emitting any signals since the recipient might rely
+// on the new state!
+void QHttp2Stream::transitionState(StateTransition transition)
+{
+ switch (m_state) {
+ case State::Idle:
+ if (transition == StateTransition::Open)
+ setState(State::Open);
+ else
+ Q_UNREACHABLE(); // We should transition to Open before ever getting here
+ break;
+ case State::Open:
+ switch (transition) {
+ case StateTransition::CloseLocal:
+ setState(State::HalfClosedLocal);
+ break;
+ case StateTransition::CloseRemote:
+ setState(State::HalfClosedRemote);
+ break;
+ case StateTransition::RST:
+ setState(State::Closed);
+ break;
+ case StateTransition::Open: // no-op
+ break;
+ }
+ break;
+ case State::HalfClosedLocal:
+ if (transition == StateTransition::CloseRemote || transition == StateTransition::RST)
+ setState(State::Closed);
+ break;
+ case State::HalfClosedRemote:
+ if (transition == StateTransition::CloseLocal || transition == StateTransition::RST)
+ setState(State::Closed);
+ break;
+ case State::ReservedRemote:
+ if (transition == StateTransition::RST) {
+ setState(State::Closed);
+ } else if (transition == StateTransition::CloseLocal) { // Receiving HEADER closes local
+ setState(State::HalfClosedLocal);
+ }
+ break;
+ case State::Closed:
+ break;
+ }
+}
+
+void QHttp2Stream::handleDATA(const Frame &inboundFrame)
+{
+ QHttp2Connection *connection = getConnection();
+
+ qCDebug(qHttp2ConnectionLog, "[%p] stream %u, received DATA frame with payload of %u bytes",
+ connection, m_streamID, inboundFrame.payloadSize());
+
+ if (qint32(inboundFrame.payloadSize()) > m_recvWindow) {
+ qCDebug(qHttp2ConnectionLog,
+ "[%p] stream %u, received DATA frame with payload size %u, "
+ "but recvWindow is %d, sending FLOW_CONTROL_ERROR",
+ connection, m_streamID, inboundFrame.payloadSize(), m_recvWindow);
+ finishWithError(QNetworkReply::ProtocolFailure, "flow control error"_L1);
+ sendRST_STREAM(FLOW_CONTROL_ERROR);
+ return;
+ }
+ m_recvWindow -= qint32(inboundFrame.payloadSize());
+ const bool endStream = inboundFrame.flags().testFlag(FrameFlag::END_STREAM);
+ // Uncompress data if needed and append it ...
+ if (inboundFrame.dataSize() > 0 || endStream) {
+ QByteArray fragment(reinterpret_cast<const char *>(inboundFrame.dataBegin()),
+ inboundFrame.dataSize());
+ if (endStream)
+ transitionState(StateTransition::CloseRemote);
+ emit dataReceived(fragment, endStream);
+ m_downloadBuffer.append(std::move(fragment));
+ }
+
+ if (!endStream && m_recvWindow < connection->streamInitialReceiveWindowSize / 2) {
+ // @future[consider]: emit signal instead
+ sendWINDOW_UPDATE(quint32(connection->streamInitialReceiveWindowSize - m_recvWindow));
+ }
+}
+
+void QHttp2Stream::handleHEADERS(Http2::FrameFlags frameFlags, const HPack::HttpHeader &headers)
+{
+ if (m_state == State::Idle)
+ transitionState(StateTransition::Open);
+ const bool endStream = frameFlags.testFlag(FrameFlag::END_STREAM);
+ if (endStream)
+ transitionState(StateTransition::CloseRemote);
+ if (!headers.empty()) {
+ m_headers.insert(m_headers.end(), headers.begin(), headers.end());
+ emit headersUpdated();
+ }
+ emit headersReceived(headers, endStream);
+}
+
+void QHttp2Stream::handleRST_STREAM(const Frame &inboundFrame)
+{
+ transitionState(StateTransition::RST);
+ m_RST_STREAM_code = qFromBigEndian<quint32>(inboundFrame.dataBegin());
+ if (isUploadingDATA()) {
+ disconnect(m_uploadByteDevice, nullptr, this, nullptr);
+ m_uploadDevice = nullptr;
+ m_uploadByteDevice = nullptr;
+ }
+ finishWithError(*m_RST_STREAM_code, ""_L1);
+}
+
+void QHttp2Stream::handleWINDOW_UPDATE(const Frame &inboundFrame)
+{
+ const quint32 delta = qFromBigEndian<quint32>(inboundFrame.dataBegin());
+ const bool valid = delta && delta <= quint32(std::numeric_limits<qint32>::max());
+ qint32 sum = 0;
+ if (!valid || qAddOverflow(m_sendWindow, qint32(delta), &sum)) {
+ qCDebug(qHttp2ConnectionLog,
+ "[%p] stream %u, received WINDOW_UPDATE frame with invalid delta %u, sending "
+ "PROTOCOL_ERROR",
+ getConnection(), m_streamID, delta);
+ finishWithError(QNetworkReply::ProtocolFailure, "invalid WINDOW_UPDATE delta"_L1);
+ sendRST_STREAM(PROTOCOL_ERROR);
+ return;
+ }
+ m_sendWindow = sum;
+ // Stream may have been unblocked, so maybe try to write again
+ if (isUploadingDATA())
+ maybeResumeUpload();
+}
+
+/*!
+ \class QHttp2Connection
+ \inmodule QtNetwork
+ \internal
+
+ The QHttp2Connection class represents a HTTP/2 connection.
+ It can only be created through the static functions
+ createDirectConnection(), createUpgradedConnection(),
+ and createDirectServerConnection().
+
+ createDirectServerConnection() is used for server-side connections, and has
+ certain limitations that a client does not.
+
+ As a client you can create a QHttp2Stream with createStream().
+
+ \sa QHttp2Stream
+*/
+
+/*!
+ \fn void QHttp2Connection::newIncomingStream(QHttp2Stream *stream)
+
+ This signal is emitted when a new \a stream is received from the remote
+ peer.
+*/
+
+/*!
+ \fn void QHttp2Connection::newPromisedStream(QHttp2Stream *stream)
+
+ This signal is emitted when the remote peer has promised a new \a stream.
+*/
+
+/*!
+ \fn void QHttp2Connection::errorReceived()
+
+ This signal is emitted when the connection has received an error.
+*/
+
+/*!
+ \fn void QHttp2Connection::connectionClosed()
+
+ This signal is emitted when the connection has been closed.
+*/
+
+/*!
+ \fn void QHttp2Connection::settingsFrameReceived()
+
+ This signal is emitted when the connection has received a SETTINGS frame.
+*/
+
+/*!
+ \fn void QHttp2Connection::errorOccurred(Http2::Http2Error errorCode, const QString &errorString)
+
+ This signal is emitted when the connection has encountered an error. The
+ \a errorCode parameter is the HTTP/2 error code, and the \a errorString
+ parameter is a human-readable description of the error.
+*/
+
+/*!
+ \fn void QHttp2Connection::receivedGOAWAY(quint32 errorCode, quint32 lastStreamID)
+
+ This signal is emitted when the connection has received a GOAWAY frame. The
+ \a errorCode parameter is the HTTP/2 error code, and the \a lastStreamID
+ parameter is the last stream ID that the remote peer will process.
+
+ Any streams of a higher stream ID created by us will be ignored or reset.
+*/
+
+/*!
+ Create a new HTTP2 connection given a \a config and a \a socket.
+ This function assumes that the Upgrade headers etc. in http/1 have already
+ been sent and that the connection is already upgraded to http/2.
+
+ The object returned will be a child to the \a socket, or null on failure.
+*/
+QHttp2Connection *QHttp2Connection::createUpgradedConnection(QIODevice *socket,
+ const QHttp2Configuration &config)
+{
+ Q_ASSERT(socket);
+
+ auto connection = std::unique_ptr<QHttp2Connection>(new QHttp2Connection(socket));
+ connection->setH2Configuration(config);
+ connection->m_connectionType = QHttp2Connection::Type::Client;
+ // HTTP2 connection is already established and request was sent, so stream 1
+ // is already 'active' and is closed for any further outgoing data.
+ QHttp2Stream *stream = connection->createStreamInternal().unwrap();
+ Q_ASSERT(stream->streamID() == 1);
+ stream->setState(QHttp2Stream::State::HalfClosedLocal);
+ connection->m_upgradedConnection = true;
+
+ if (!connection->sendClientPreface()) {
+ qCWarning(qHttp2ConnectionLog, "[%p] Failed to send client preface", connection.get());
+ return nullptr;
+ }
+
+ return connection.release();
+}
+
+/*!
+ Create a new HTTP2 connection given a \a config and a \a socket.
+ This function will immediately send the client preface.
+
+ The object returned will be a child to the \a socket, or null on failure.
+*/
+QHttp2Connection *QHttp2Connection::createDirectConnection(QIODevice *socket,
+ const QHttp2Configuration &config)
+{
+ auto connection = std::unique_ptr<QHttp2Connection>(new QHttp2Connection(socket));
+ connection->setH2Configuration(config);
+ connection->m_connectionType = QHttp2Connection::Type::Client;
+
+ if (!connection->sendClientPreface()) {
+ qCWarning(qHttp2ConnectionLog, "[%p] Failed to send client preface", connection.get());
+ return nullptr;
+ }
+
+ return connection.release();
+}
+
+/*!
+ Create a new HTTP2 connection given a \a config and a \a socket.
+
+ The object returned will be a child to the \a socket, or null on failure.
+*/
+QHttp2Connection *QHttp2Connection::createDirectServerConnection(QIODevice *socket,
+ const QHttp2Configuration &config)
+{
+ auto connection = std::unique_ptr<QHttp2Connection>(new QHttp2Connection(socket));
+ connection->setH2Configuration(config);
+ connection->m_connectionType = QHttp2Connection::Type::Server;
+
+ connection->m_nextStreamID = 2; // server-initiated streams must be even
+
+ connection->m_waitingForClientPreface = true;
+
+ return connection.release();
+}
+
+/*!
+ Creates a stream on this connection.
+
+ Automatically picks the next available stream ID and returns a pointer to
+ the new stream, if possible. Otherwise returns an error.
+
+ \sa QHttp2Connection::CreateStreamError, QHttp2Stream
+*/
+QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError> QHttp2Connection::createStream()
+{
+ Q_ASSERT(m_connectionType == Type::Client); // This overload is just for clients
+ if (m_nextStreamID > lastValidStreamID)
+ return { QHttp2Connection::CreateStreamError::StreamIdsExhausted };
+ return createStreamInternal();
+}
+
+QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError>
+QHttp2Connection::createStreamInternal()
+{
+ if (m_goingAway)
+ return { QHttp2Connection::CreateStreamError::ReceivedGOAWAY };
+ const quint32 streamID = m_nextStreamID;
+ if (size_t(m_maxConcurrentStreams) <= size_t(numActiveLocalStreams()))
+ return { QHttp2Connection::CreateStreamError::MaxConcurrentStreamsReached };
+ m_nextStreamID += 2;
+ return { createStreamInternal_impl(streamID) };
+}
+
+QHttp2Stream *QHttp2Connection::createStreamInternal_impl(quint32 streamID)
+{
+ qsizetype numStreams = m_streams.size();
+ QPointer<QHttp2Stream> &stream = m_streams[streamID];
+ if (numStreams == m_streams.size()) // stream already existed
+ return nullptr;
+ stream = new QHttp2Stream(this, streamID);
+ stream->m_recvWindow = streamInitialReceiveWindowSize;
+ stream->m_sendWindow = streamInitialSendWindowSize;
+ return stream;
+}
+
+qsizetype QHttp2Connection::numActiveStreamsImpl(quint32 mask) const noexcept
+{
+ const auto shouldCount = [mask](const QPointer<QHttp2Stream> &stream) -> bool {
+ return stream && (stream->streamID() & 1) == mask;
+ };
+ return std::count_if(m_streams.cbegin(), m_streams.cend(), shouldCount);
+}
+
+/*!
+ \internal
+ The number of streams the remote peer has started that are still active.
+*/
+qsizetype QHttp2Connection::numActiveRemoteStreams() const noexcept
+{
+ const quint32 RemoteMask = m_connectionType == Type::Client ? 0 : 1;
+ return numActiveStreamsImpl(RemoteMask);
+}
+
+/*!
+ \internal
+ The number of streams we have started that are still active.
+*/
+qsizetype QHttp2Connection::numActiveLocalStreams() const noexcept
+{
+ const quint32 LocalMask = m_connectionType == Type::Client ? 1 : 0;
+ return numActiveStreamsImpl(LocalMask);
+}
+
+/*!
+ Return a pointer to a stream with the given \a streamID, or null if no such
+ stream exists or it was deleted.
+*/
+QHttp2Stream *QHttp2Connection::getStream(quint32 streamID) const
+{
+ return m_streams.value(streamID, nullptr).get();
+}
+
+
+/*!
+ \fn QHttp2Stream *QHttp2Connection::promisedStream(const QUrl &streamKey) const
+
+ Returns a pointer to the stream that was promised with the given
+ \a streamKey, if any. Otherwise, returns null.
+*/
+
+/*!
+ \fn void QHttp2Connection::close()
+
+ This sends a GOAWAY frame on the connection stream, gracefully closing the
+ connection.
+*/
+
+/*!
+ \fn bool QHttp2Connection::isGoingAway() const noexcept
+
+ Returns \c true if the connection is in the process of being closed, or
+ \c false otherwise.
+*/
+
+/*!
+ \fn quint32 QHttp2Connection::maxConcurrentStreams() const noexcept
+
+ Returns the maximum number of concurrent streams we are allowed to have
+ active at any given time. This is a directional setting, and the remote
+ peer may have a different value.
+*/
+
+/*!
+ \fn quint32 QHttp2Connection::maxHeaderListSize() const noexcept
+
+ Returns the maximum size of the header which the peer is willing to accept.
+*/
+
+/*!
+ \fn bool QHttp2Connection::isUpgradedConnection() const noexcept
+
+ Returns \c true if this connection was created as a result of an HTTP/1
+ upgrade to HTTP/2, or \c false otherwise.
+*/
+
+QHttp2Connection::QHttp2Connection(QIODevice *socket) : QObject(socket)
+{
+ Q_ASSERT(socket);
+ Q_ASSERT(socket->isOpen());
+ Q_ASSERT(socket->openMode() & QIODevice::ReadWrite);
+ // We don't make any connections directly because this is used in
+ // in the http2 protocol handler, which is used by
+ // QHttpNetworkConnectionChannel. Which in turn owns and deals with all the
+ // socket connections.
+}
+
+QHttp2Connection::~QHttp2Connection() noexcept
+{
+ // delete streams now so that any calls it might make back to this
+ // Connection will operate on a valid object.
+ for (QPointer<QHttp2Stream> &stream : std::exchange(m_streams, {}))
+ delete stream.get();
+}
+
+bool QHttp2Connection::serverCheckClientPreface()
+{
+ if (!m_waitingForClientPreface)
+ return true;
+ auto *socket = getSocket();
+ if (socket->bytesAvailable() < Http2::clientPrefaceLength)
+ return false;
+ if (!readClientPreface()) {
+ socket->close();
+ emit errorOccurred(Http2Error::PROTOCOL_ERROR, "invalid client preface"_L1);
+ qCDebug(qHttp2ConnectionLog, "[%p] Invalid client preface", this);
+ return false;
+ }
+ qCDebug(qHttp2ConnectionLog, "[%p] Peer sent valid client preface", this);
+ m_waitingForClientPreface = false;
+ if (!sendServerPreface()) {
+ connectionError(Http2::INTERNAL_ERROR, "Failed to send server preface");
+ return false;
+ }
+ return true;
+}
+
+bool QHttp2Connection::sendPing()
+{
+ std::array<char, 8> data;
+
+ QRandomGenerator gen;
+ gen.generate(data.begin(), data.end());
+ return sendPing(data);
+}
+
+bool QHttp2Connection::sendPing(QByteArrayView data)
+{
+ frameWriter.start(FrameType::PING, FrameFlag::EMPTY, connectionStreamID);
+
+ Q_ASSERT(data.length() == 8);
+ if (!m_lastPingSignature) {
+ m_lastPingSignature = data.toByteArray();
+ } else {
+ qCWarning(qHttp2ConnectionLog, "[%p] No PING is sent while waiting for the previous PING.", this);
+ return false;
+ }
+
+ frameWriter.append((uchar*)data.data(), (uchar*)data.end());
+ frameWriter.write(*getSocket());
+ return true;
+}
+
+/*!
+ This function must be called when you have received a readyRead signal
+ (or equivalent) from the QIODevice. It will read and process any incoming
+ HTTP/2 frames and emit signals as appropriate.
+*/
+void QHttp2Connection::handleReadyRead()
+{
+ /* event loop */
+ if (m_connectionType == Type::Server && !serverCheckClientPreface())
+ return;
+
+ const auto streamIsActive = [](const QPointer<QHttp2Stream> &stream) {
+ return stream && stream->isActive();
+ };
+ if (m_goingAway && std::none_of(m_streams.cbegin(), m_streams.cend(), streamIsActive)) {
+ close();
+ return;
+ }
+ QIODevice *socket = getSocket();
+
+ qCDebug(qHttp2ConnectionLog, "[%p] Receiving data, %lld bytes available", this,
+ socket->bytesAvailable());
+
+ using namespace Http2;
+ while (!m_goingAway || std::any_of(m_streams.cbegin(), m_streams.cend(), streamIsActive)) {
+ const auto result = frameReader.read(*socket);
+ if (result != FrameStatus::goodFrame)
+ qCDebug(qHttp2ConnectionLog, "[%p] Tried to read frame, got %d", this, int(result));
+ switch (result) {
+ case FrameStatus::incompleteFrame:
+ return;
+ case FrameStatus::protocolError:
+ return connectionError(PROTOCOL_ERROR, "invalid frame");
+ case FrameStatus::sizeError:
+ return connectionError(FRAME_SIZE_ERROR, "invalid frame size");
+ default:
+ break;
+ }
+
+ Q_ASSERT(result == FrameStatus::goodFrame);
+
+ inboundFrame = std::move(frameReader.inboundFrame());
+
+ const auto frameType = inboundFrame.type();
+ qCDebug(qHttp2ConnectionLog, "[%p] Successfully read a frame, with type: %d", this,
+ int(frameType));
+ if (continuationExpected && frameType != FrameType::CONTINUATION)
+ return connectionError(PROTOCOL_ERROR, "CONTINUATION expected");
+
+ switch (frameType) {
+ case FrameType::DATA:
+ handleDATA();
+ break;
+ case FrameType::HEADERS:
+ handleHEADERS();
+ break;
+ case FrameType::PRIORITY:
+ handlePRIORITY();
+ break;
+ case FrameType::RST_STREAM:
+ handleRST_STREAM();
+ break;
+ case FrameType::SETTINGS:
+ handleSETTINGS();
+ break;
+ case FrameType::PUSH_PROMISE:
+ handlePUSH_PROMISE();
+ break;
+ case FrameType::PING:
+ handlePING();
+ break;
+ case FrameType::GOAWAY:
+ handleGOAWAY();
+ break;
+ case FrameType::WINDOW_UPDATE:
+ handleWINDOW_UPDATE();
+ break;
+ case FrameType::CONTINUATION:
+ handleCONTINUATION();
+ break;
+ case FrameType::LAST_FRAME_TYPE:
+ // 5.1 - ignore unknown frames.
+ break;
+ }
+ }
+}
+
+bool QHttp2Connection::readClientPreface()
+{
+ auto *socket = getSocket();
+ Q_ASSERT(socket->bytesAvailable() >= Http2::clientPrefaceLength);
+ char buffer[Http2::clientPrefaceLength];
+ const qint64 read = socket->read(buffer, Http2::clientPrefaceLength);
+ if (read != Http2::clientPrefaceLength)
+ return false;
+ return memcmp(buffer, Http2::Http2clientPreface, Http2::clientPrefaceLength) == 0;
+}
+
+/*!
+ This function must be called when the socket has been disconnected, and will
+ end all remaining streams with an error.
+*/
+void QHttp2Connection::handleConnectionClosure()
+{
+ const auto errorString = QCoreApplication::translate("QHttp", "Connection closed");
+ for (auto it = m_streams.begin(), end = m_streams.end(); it != end; ++it) {
+ auto stream = it.value();
+ if (stream && stream->isActive())
+ stream->finishWithError(QNetworkReply::RemoteHostClosedError, errorString);
+ }
+}
+
+void QHttp2Connection::setH2Configuration(QHttp2Configuration config)
+{
+ m_config = std::move(config);
+
+ // These values comes from our own API so trust it to be sane.
+ maxSessionReceiveWindowSize = qint32(m_config.sessionReceiveWindowSize());
+ pushPromiseEnabled = m_config.serverPushEnabled();
+ streamInitialReceiveWindowSize = qint32(m_config.streamReceiveWindowSize());
+ encoder.setCompressStrings(m_config.huffmanCompressionEnabled());
+}
+
+void QHttp2Connection::connectionError(Http2Error errorCode, const char *message)
+{
+ Q_ASSERT(message);
+ if (m_goingAway)
+ return;
+
+ qCCritical(qHttp2ConnectionLog, "[%p] Connection error: %s (%d)", this, message,
+ int(errorCode));
+
+ m_goingAway = true;
+ sendGOAWAY(errorCode);
+ const auto error = qt_error(errorCode);
+ auto messageView = QLatin1StringView(message);
+
+ for (QHttp2Stream *stream : std::as_const(m_streams)) {
+ if (stream && stream->isActive())
+ stream->finishWithError(error, messageView);
+ }
+
+ closeSession();
+}
+
+void QHttp2Connection::closeSession()
+{
+ emit connectionClosed();
+}
+
+bool QHttp2Connection::streamWasReset(quint32 streamID) noexcept
+{
+ return m_resetStreamIDs.contains(streamID);
+}
+
+bool QHttp2Connection::isInvalidStream(quint32 streamID) noexcept
+{
+ auto stream = m_streams.value(streamID, nullptr);
+ return !stream && !streamWasReset(streamID);
+}
+
+bool QHttp2Connection::sendClientPreface()
+{
+ QIODevice *socket = getSocket();
+ // 3.5 HTTP/2 Connection Preface
+ const qint64 written = socket->write(Http2clientPreface, clientPrefaceLength);
+ if (written != clientPrefaceLength)
+ return false;
+
+ if (!sendSETTINGS()) {
+ qCWarning(qHttp2ConnectionLog, "[%p] Failed to send SETTINGS", this);
+ return false;
+ }
+ return true;
+}
+
+bool QHttp2Connection::sendServerPreface()
+{
+ // We send our SETTINGS frame and ACK the client's SETTINGS frame when it
+ // arrives.
+ if (!sendSETTINGS()) {
+ qCWarning(qHttp2ConnectionLog, "[%p] Failed to send SETTINGS", this);
+ return false;
+ }
+ return true;
+}
+
+bool QHttp2Connection::sendSETTINGS()
+{
+ QIODevice *socket = getSocket();
+ // 6.5 SETTINGS
+ frameWriter.setOutboundFrame(configurationToSettingsFrame(m_config));
+ qCDebug(qHttp2ConnectionLog, "[%p] Sending SETTINGS frame, %d bytes", this,
+ frameWriter.outboundFrame().payloadSize());
+ Q_ASSERT(frameWriter.outboundFrame().payloadSize());
+
+ if (!frameWriter.write(*socket))
+ return false;
+
+ sessionReceiveWindowSize = maxSessionReceiveWindowSize;
+ // We only send WINDOW_UPDATE for the connection if the size differs from the
+ // default 64 KB:
+ const auto delta = maxSessionReceiveWindowSize - defaultSessionWindowSize;
+ if (delta && !sendWINDOW_UPDATE(connectionStreamID, delta))
+ return false;
+
+ waitingForSettingsACK = true;
+ return true;
+}
+
+bool QHttp2Connection::sendWINDOW_UPDATE(quint32 streamID, quint32 delta)
+{
+ qCDebug(qHttp2ConnectionLog, "[%p] Sending WINDOW_UPDATE frame, stream %d, delta %u", this,
+ streamID, delta);
+ frameWriter.start(FrameType::WINDOW_UPDATE, FrameFlag::EMPTY, streamID);
+ frameWriter.append(delta);
+ return frameWriter.write(*getSocket());
+}
+
+bool QHttp2Connection::sendGOAWAY(quint32 errorCode)
+{
+ frameWriter.start(FrameType::GOAWAY, FrameFlag::EMPTY,
+ Http2PredefinedParameters::connectionStreamID);
+ frameWriter.append(quint32(m_lastIncomingStreamID));
+ frameWriter.append(errorCode);
+ return frameWriter.write(*getSocket());
+}
+
+bool QHttp2Connection::sendSETTINGS_ACK()
+{
+ frameWriter.start(FrameType::SETTINGS, FrameFlag::ACK, Http2::connectionStreamID);
+ return frameWriter.write(*getSocket());
+}
+
+void QHttp2Connection::handleDATA()
+{
+ Q_ASSERT(inboundFrame.type() == FrameType::DATA);
+
+ const auto streamID = inboundFrame.streamID();
+ if (streamID == connectionStreamID)
+ return connectionError(PROTOCOL_ERROR, "DATA on the connection stream");
+
+ if (isInvalidStream(streamID))
+ return connectionError(ENHANCE_YOUR_CALM, "DATA on invalid stream");
+
+ if (qint32(inboundFrame.payloadSize()) > sessionReceiveWindowSize) {
+ qCDebug(qHttp2ConnectionLog,
+ "[%p] Received DATA frame with payload size %u, "
+ "but recvWindow is %d, sending FLOW_CONTROL_ERROR",
+ this, inboundFrame.payloadSize(), sessionReceiveWindowSize);
+ return connectionError(FLOW_CONTROL_ERROR, "Flow control error");
+ }
+
+ sessionReceiveWindowSize -= inboundFrame.payloadSize();
+
+ auto it = m_streams.constFind(streamID);
+ if (it != m_streams.cend() && it.value())
+ it.value()->handleDATA(inboundFrame);
+
+ if (sessionReceiveWindowSize < maxSessionReceiveWindowSize / 2) {
+ // @future[consider]: emit signal instead
+ QMetaObject::invokeMethod(this, &QHttp2Connection::sendWINDOW_UPDATE, Qt::QueuedConnection,
+ quint32(connectionStreamID),
+ quint32(maxSessionReceiveWindowSize - sessionReceiveWindowSize));
+ sessionReceiveWindowSize = maxSessionReceiveWindowSize;
+ }
+}
+
+void QHttp2Connection::handleHEADERS()
+{
+ Q_ASSERT(inboundFrame.type() == FrameType::HEADERS);
+
+ const auto streamID = inboundFrame.streamID();
+ qCDebug(qHttp2ConnectionLog, "[%p] Received HEADERS frame on stream %d", this, streamID);
+
+ if (streamID == connectionStreamID)
+ return connectionError(PROTOCOL_ERROR, "HEADERS on 0x0 stream");
+
+ const bool isClient = m_connectionType == Type::Client;
+ const bool isClientInitiatedStream = !!(streamID & 1);
+ const bool isRemotelyInitiatedStream = isClient ^ isClientInitiatedStream;
+
+ if (isRemotelyInitiatedStream && streamID > m_lastIncomingStreamID) {
+ QHttp2Stream *newStream = createStreamInternal_impl(streamID);
+ Q_ASSERT(newStream);
+ m_lastIncomingStreamID = streamID;
+ qCDebug(qHttp2ConnectionLog, "[%p] Created new incoming stream %d", this, streamID);
+ emit newIncomingStream(newStream);
+ } else if (auto it = m_streams.constFind(streamID); it == m_streams.cend()) {
+ qCDebug(qHttp2ConnectionLog, "[%p] Received HEADERS on non-existent stream %d", this,
+ streamID);
+ return connectionError(PROTOCOL_ERROR, "HEADERS on invalid stream");
+ } else if (!*it || (*it)->wasReset()) {
+ qCDebug(qHttp2ConnectionLog, "[%p] Received HEADERS on reset stream %d", this, streamID);
+ return connectionError(ENHANCE_YOUR_CALM, "HEADERS on invalid stream");
+ }
+
+ const auto flags = inboundFrame.flags();
+ if (flags.testFlag(FrameFlag::PRIORITY)) {
+ qCDebug(qHttp2ConnectionLog, "[%p] HEADERS frame on stream %d has PRIORITY flag", this,
+ streamID);
+ handlePRIORITY();
+ if (m_goingAway)
+ return;
+ }
+
+ const bool endHeaders = flags.testFlag(FrameFlag::END_HEADERS);
+ continuedFrames.clear();
+ continuedFrames.push_back(std::move(inboundFrame));
+ if (!endHeaders) {
+ continuationExpected = true;
+ return;
+ }
+
+ handleContinuedHEADERS();
+}
+
+void QHttp2Connection::handlePRIORITY()
+{
+ Q_ASSERT(inboundFrame.type() == FrameType::PRIORITY
+ || inboundFrame.type() == FrameType::HEADERS);
+
+ const auto streamID = inboundFrame.streamID();
+ if (streamID == connectionStreamID)
+ return connectionError(PROTOCOL_ERROR, "PRIORITY on 0x0 stream");
+
+ if (isInvalidStream(streamID))
+ return connectionError(ENHANCE_YOUR_CALM, "PRIORITY on invalid stream");
+
+ quint32 streamDependency = 0;
+ uchar weight = 0;
+ const bool noErr = inboundFrame.priority(&streamDependency, &weight);
+ Q_UNUSED(noErr);
+ Q_ASSERT(noErr);
+
+ const bool exclusive = streamDependency & 0x80000000;
+ streamDependency &= ~0x80000000;
+
+ // Ignore this for now ...
+ // Can be used for streams (re)prioritization - 5.3
+ Q_UNUSED(exclusive);
+ Q_UNUSED(weight);
+}
+
+void QHttp2Connection::handleRST_STREAM()
+{
+ Q_ASSERT(inboundFrame.type() == FrameType::RST_STREAM);
+
+ // "RST_STREAM frames MUST be associated with a stream.
+ // If a RST_STREAM frame is received with a stream identifier of 0x0,
+ // the recipient MUST treat this as a connection error (Section 5.4.1)
+ // of type PROTOCOL_ERROR.
+ const auto streamID = inboundFrame.streamID();
+ if (streamID == connectionStreamID)
+ return connectionError(PROTOCOL_ERROR, "RST_STREAM on 0x0");
+
+ if (!(streamID & 0x1)) { // @future[server]: must be updated for server-side handling
+ // RST_STREAM on a promised stream:
+ // since we do not keep track of such streams,
+ // just ignore.
+ return;
+ }
+
+ // Anything greater than m_nextStreamID has not been started yet.
+ if (streamID >= m_nextStreamID) {
+ // "RST_STREAM frames MUST NOT be sent for a stream
+ // in the "idle" state. .. the recipient MUST treat this
+ // as a connection error (Section 5.4.1) of type PROTOCOL_ERROR."
+ return connectionError(PROTOCOL_ERROR, "RST_STREAM on idle stream");
+ }
+
+ Q_ASSERT(inboundFrame.dataSize() == 4);
+
+ if (QPointer<QHttp2Stream> stream = m_streams[streamID])
+ stream->handleRST_STREAM(inboundFrame);
+}
+
+void QHttp2Connection::handleSETTINGS()
+{
+ // 6.5 SETTINGS.
+ Q_ASSERT(inboundFrame.type() == FrameType::SETTINGS);
+
+ if (inboundFrame.streamID() != connectionStreamID)
+ return connectionError(PROTOCOL_ERROR, "SETTINGS on invalid stream");
+
+ if (inboundFrame.flags().testFlag(FrameFlag::ACK)) {
+ if (!waitingForSettingsACK)
+ return connectionError(PROTOCOL_ERROR, "unexpected SETTINGS ACK");
+ qCDebug(qHttp2ConnectionLog, "[%p] Received SETTINGS ACK", this);
+ waitingForSettingsACK = false;
+ return;
+ }
+ qCDebug(qHttp2ConnectionLog, "[%p] Received SETTINGS frame", this);
+
+ if (inboundFrame.dataSize()) {
+ auto src = inboundFrame.dataBegin();
+ for (const uchar *end = src + inboundFrame.dataSize(); src != end; src += 6) {
+ const Settings identifier = Settings(qFromBigEndian<quint16>(src));
+ const quint32 intVal = qFromBigEndian<quint32>(src + 2);
+ if (!acceptSetting(identifier, intVal)) {
+ // If not accepted - we finish with connectionError.
+ qCDebug(qHttp2ConnectionLog, "[%p] Received an unacceptable setting, %u, %u", this,
+ quint32(identifier), intVal);
+ return; // connectionError already called in acceptSetting.
+ }
+ }
+ }
+
+ qCDebug(qHttp2ConnectionLog, "[%p] Sending SETTINGS ACK", this);
+ emit settingsFrameReceived();
+ sendSETTINGS_ACK();
+}
+
+void QHttp2Connection::handlePUSH_PROMISE()
+{
+ // 6.6 PUSH_PROMISE.
+ Q_ASSERT(inboundFrame.type() == FrameType::PUSH_PROMISE);
+
+ if (!pushPromiseEnabled && !waitingForSettingsACK) {
+ // This means, server ACKed our 'NO PUSH',
+ // but sent us PUSH_PROMISE anyway.
+ return connectionError(PROTOCOL_ERROR, "unexpected PUSH_PROMISE frame");
+ }
+
+ const auto streamID = inboundFrame.streamID();
+ if (streamID == connectionStreamID)
+ return connectionError(PROTOCOL_ERROR, "PUSH_PROMISE with invalid associated stream (0x0)");
+
+ auto it = m_streams.constFind(streamID);
+#if 0 // Needs to be done after some timeout in case the stream has only just been reset
+ if (it != m_streams.constEnd()) {
+ QHttp2Stream *associatedStream = it->get();
+ if (associatedStream->state() != QHttp2Stream::State::Open
+ && associatedStream->state() != QHttp2Stream::State::HalfClosedLocal) {
+ // Cause us to error out below:
+ it = m_streams.constEnd();
+ }
+ }
+#endif
+ if (it == m_streams.constEnd())
+ return connectionError(ENHANCE_YOUR_CALM, "PUSH_PROMISE with invalid associated stream");
+
+ const auto reservedID = qFromBigEndian<quint32>(inboundFrame.dataBegin());
+ if ((reservedID & 1) || reservedID <= m_lastIncomingStreamID || reservedID > lastValidStreamID)
+ return connectionError(PROTOCOL_ERROR, "PUSH_PROMISE with invalid promised stream ID");
+
+ auto *stream = createStreamInternal_impl(reservedID);
+ if (!stream)
+ return connectionError(PROTOCOL_ERROR, "PUSH_PROMISE with already active stream ID");
+ m_lastIncomingStreamID = reservedID;
+ stream->setState(QHttp2Stream::State::ReservedRemote);
+
+ if (!pushPromiseEnabled) {
+ // "ignoring a PUSH_PROMISE frame causes the stream state to become
+ // indeterminate" - let's send RST_STREAM frame with REFUSE_STREAM code.
+ stream->sendRST_STREAM(REFUSE_STREAM);
+ }
+
+ const bool endHeaders = inboundFrame.flags().testFlag(FrameFlag::END_HEADERS);
+ continuedFrames.clear();
+ continuedFrames.push_back(std::move(inboundFrame));
+
+ if (!endHeaders) {
+ continuationExpected = true;
+ return;
+ }
+
+ handleContinuedHEADERS();
+}
+
+void QHttp2Connection::handlePING()
+{
+ Q_ASSERT(inboundFrame.type() == FrameType::PING);
+ Q_ASSERT(inboundFrame.dataSize() == 8);
+
+ if (inboundFrame.streamID() != connectionStreamID)
+ return connectionError(PROTOCOL_ERROR, "PING on invalid stream");
+
+ if (inboundFrame.flags() & FrameFlag::ACK) {
+ QByteArrayView pingSignature(reinterpret_cast<const char *>(inboundFrame.dataBegin()), 8);
+ if (!m_lastPingSignature.has_value()) {
+ emit pingFrameRecived(PingState::PongNoPingSent);
+ qCWarning(qHttp2ConnectionLog, "[%p] PING with ACK received but no PING was sent.", this);
+ } else if (pingSignature != m_lastPingSignature) {
+ emit pingFrameRecived(PingState::PongSignatureChanged);
+ qCWarning(qHttp2ConnectionLog, "[%p] PING signature does not match the last PING.", this);
+ } else {
+ emit pingFrameRecived(PingState::PongSignatureIdentical);
+ }
+ m_lastPingSignature.reset();
+ return;
+ } else {
+ emit pingFrameRecived(PingState::Ping);
+
+ }
+
+
+ frameWriter.start(FrameType::PING, FrameFlag::ACK, connectionStreamID);
+ frameWriter.append(inboundFrame.dataBegin(), inboundFrame.dataBegin() + 8);
+ frameWriter.write(*getSocket());
+}
+
+void QHttp2Connection::handleGOAWAY()
+{
+ // 6.8 GOAWAY
+
+ Q_ASSERT(inboundFrame.type() == FrameType::GOAWAY);
+ // "An endpoint MUST treat a GOAWAY frame with a stream identifier
+ // other than 0x0 as a connection error (Section 5.4.1) of type PROTOCOL_ERROR."
+ if (inboundFrame.streamID() != connectionStreamID)
+ return connectionError(PROTOCOL_ERROR, "GOAWAY on invalid stream");
+
+ const uchar *const src = inboundFrame.dataBegin();
+ quint32 lastStreamID = qFromBigEndian<quint32>(src);
+ const quint32 errorCode = qFromBigEndian<quint32>(src + 4);
+
+ if (!lastStreamID) {
+ // "The last stream identifier can be set to 0 if no
+ // streams were processed."
+ lastStreamID = 1;
+ } else if (!(lastStreamID & 0x1)) {
+ // 5.1.1 - we (client) use only odd numbers as stream identifiers.
+ return connectionError(PROTOCOL_ERROR, "GOAWAY with invalid last stream ID");
+ } else if (lastStreamID >= m_nextStreamID) {
+ // "A server that is attempting to gracefully shut down a connection SHOULD
+ // send an initial GOAWAY frame with the last stream identifier set to 2^31-1
+ // and a NO_ERROR code."
+ if (lastStreamID != lastValidStreamID || errorCode != HTTP2_NO_ERROR)
+ return connectionError(PROTOCOL_ERROR, "GOAWAY invalid stream/error code");
+ } else {
+ lastStreamID += 2;
+ }
+
+ m_goingAway = true;
+
+ emit receivedGOAWAY(errorCode, lastStreamID);
+
+ for (quint32 id = lastStreamID; id < m_nextStreamID; id += 2) {
+ QHttp2Stream *stream = m_streams.value(id, nullptr);
+ if (stream && stream->isActive())
+ stream->finishWithError(errorCode, "Received GOAWAY"_L1);
+ }
+
+ const auto isActive = [](const QHttp2Stream *stream) { return stream && stream->isActive(); };
+ if (std::none_of(m_streams.cbegin(), m_streams.cend(), isActive))
+ closeSession();
+}
+
+void QHttp2Connection::handleWINDOW_UPDATE()
+{
+ Q_ASSERT(inboundFrame.type() == FrameType::WINDOW_UPDATE);
+
+ const quint32 delta = qFromBigEndian<quint32>(inboundFrame.dataBegin());
+ const bool valid = delta && delta <= quint32(std::numeric_limits<qint32>::max());
+ const auto streamID = inboundFrame.streamID();
+
+ qCDebug(qHttp2ConnectionLog(), "[%p] Received WINDOW_UPDATE, stream %d, delta %d", this,
+ streamID, delta);
+ if (streamID == connectionStreamID) {
+ qint32 sum = 0;
+ if (!valid || qAddOverflow(sessionSendWindowSize, qint32(delta), &sum))
+ return connectionError(PROTOCOL_ERROR, "WINDOW_UPDATE invalid delta");
+ sessionSendWindowSize = sum;
+ for (auto &stream : m_streams) {
+ if (!stream || !stream->isActive())
+ continue;
+ // Stream may have been unblocked, so maybe try to write again
+ if (stream->isUploadingDATA() && !stream->isUploadBlocked())
+ QMetaObject::invokeMethod(stream, &QHttp2Stream::maybeResumeUpload,
+ Qt::QueuedConnection);
+ }
+ } else {
+ QHttp2Stream *stream = m_streams.value(streamID);
+ if (!stream || !stream->isActive()) {
+ // WINDOW_UPDATE on closed streams can be ignored.
+ qCDebug(qHttp2ConnectionLog, "[%p] Received WINDOW_UPDATE on closed stream %d", this,
+ streamID);
+ return;
+ }
+ stream->handleWINDOW_UPDATE(inboundFrame);
+ }
+}
+
+void QHttp2Connection::handleCONTINUATION()
+{
+ Q_ASSERT(inboundFrame.type() == FrameType::CONTINUATION);
+ if (continuedFrames.empty())
+ return connectionError(PROTOCOL_ERROR,
+ "CONTINUATION without a preceding HEADERS or PUSH_PROMISE");
+
+ if (inboundFrame.streamID() != continuedFrames.front().streamID())
+ return connectionError(PROTOCOL_ERROR, "CONTINUATION on invalid stream");
+
+ const bool endHeaders = inboundFrame.flags().testFlag(FrameFlag::END_HEADERS);
+ continuedFrames.push_back(std::move(inboundFrame));
+
+ if (!endHeaders)
+ return;
+
+ continuationExpected = false;
+ handleContinuedHEADERS();
+}
+
+void QHttp2Connection::handleContinuedHEADERS()
+{
+ // 'Continued' HEADERS can be: the initial HEADERS/PUSH_PROMISE frame
+ // with/without END_HEADERS flag set plus, if no END_HEADERS flag,
+ // a sequence of one or more CONTINUATION frames.
+ Q_ASSERT(!continuedFrames.empty());
+ const auto firstFrameType = continuedFrames[0].type();
+ Q_ASSERT(firstFrameType == FrameType::HEADERS || firstFrameType == FrameType::PUSH_PROMISE);
+
+ const auto streamID = continuedFrames[0].streamID();
+
+ const auto streamIt = m_streams.constFind(streamID);
+ if (firstFrameType == FrameType::HEADERS) {
+ if (streamIt != m_streams.cend()) {
+ QHttp2Stream *stream = streamIt.value();
+ if (stream->state() != QHttp2Stream::State::HalfClosedLocal
+ && stream->state() != QHttp2Stream::State::ReservedRemote
+ && stream->state() != QHttp2Stream::State::Idle
+ && stream->state() != QHttp2Stream::State::Open) {
+ // We can receive HEADERS on streams initiated by our requests
+ // (these streams are in halfClosedLocal or open state) or
+ // remote-reserved streams from a server's PUSH_PROMISE.
+ stream->finishWithError(QNetworkReply::ProtocolFailure,
+ "HEADERS on invalid stream"_L1);
+ stream->sendRST_STREAM(CANCEL);
+ return;
+ }
+ }
+ // Else: we cannot just ignore our peer's HEADERS frames - they change
+ // HPACK context - even though the stream was reset; apparently the peer
+ // has yet to see the reset.
+ }
+
+ std::vector<uchar> hpackBlock(assemble_hpack_block(continuedFrames));
+ const bool hasHeaderFields = !hpackBlock.empty();
+ if (hasHeaderFields) {
+ HPack::BitIStream inputStream{ hpackBlock.data(), hpackBlock.data() + hpackBlock.size() };
+ if (!decoder.decodeHeaderFields(inputStream))
+ return connectionError(COMPRESSION_ERROR, "HPACK decompression failed");
+ } else {
+ if (firstFrameType == FrameType::PUSH_PROMISE) {
+ // It could be a PRIORITY sent in HEADERS - already handled by this
+ // point in handleHEADERS. If it was PUSH_PROMISE (HTTP/2 8.2.1):
+ // "The header fields in PUSH_PROMISE and any subsequent CONTINUATION
+ // frames MUST be a valid and complete set of request header fields
+ // (Section 8.1.2.3) ... If a client receives a PUSH_PROMISE that does
+ // not include a complete and valid set of header fields or the :method
+ // pseudo-header field identifies a method that is not safe, it MUST
+ // respond with a stream error (Section 5.4.2) of type PROTOCOL_ERROR."
+ if (streamIt != m_streams.cend())
+ (*streamIt)->sendRST_STREAM(PROTOCOL_ERROR);
+ return;
+ }
+
+ // We got back an empty hpack block. Now let's figure out if there was an error.
+ constexpr auto hpackBlockHasContent = [](const auto &c) { return c.hpackBlockSize() > 0; };
+ const bool anyHpackBlock = std::any_of(continuedFrames.cbegin(), continuedFrames.cend(),
+ hpackBlockHasContent);
+ if (anyHpackBlock) // There was hpack block data, but returned empty => it overflowed.
+ return connectionError(FRAME_SIZE_ERROR, "HEADERS frame too large");
+ }
+
+ if (streamIt == m_streams.cend()) // No more processing without a stream from here on.
+ return;
+
+ switch (firstFrameType) {
+ case FrameType::HEADERS:
+ streamIt.value()->handleHEADERS(continuedFrames[0].flags(), decoder.decodedHeader());
+ break;
+ case FrameType::PUSH_PROMISE: {
+ std::optional<QUrl> promiseKey = HPack::makePromiseKeyUrl(decoder.decodedHeader());
+ if (!promiseKey)
+ return; // invalid URL/key !
+ if (m_promisedStreams.contains(*promiseKey))
+ return; // already promised!
+ const auto promiseID = qFromBigEndian<quint32>(continuedFrames[0].dataBegin());
+ QHttp2Stream *stream = m_streams.value(promiseID);
+ stream->transitionState(QHttp2Stream::StateTransition::CloseLocal);
+ stream->handleHEADERS(continuedFrames[0].flags(), decoder.decodedHeader());
+ emit newPromisedStream(stream); // @future[consider] add promise key as argument?
+ m_promisedStreams.emplace(*promiseKey, promiseID);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+bool QHttp2Connection::acceptSetting(Http2::Settings identifier, quint32 newValue)
+{
+ switch (identifier) {
+ case Settings::HEADER_TABLE_SIZE_ID: {
+ qCDebug(qHttp2ConnectionLog, "[%p] Received SETTINGS HEADER_TABLE_SIZE %d", this, newValue);
+ if (newValue > maxAcceptableTableSize) {
+ connectionError(PROTOCOL_ERROR, "SETTINGS invalid table size");
+ return false;
+ }
+ encoder.setMaxDynamicTableSize(newValue);
+ break;
+ }
+ case Settings::INITIAL_WINDOW_SIZE_ID: {
+ qCDebug(qHttp2ConnectionLog, "[%p] Received SETTINGS INITIAL_WINDOW_SIZE %d", this,
+ newValue);
+ // For every active stream - adjust its window
+ // (and handle possible overflows as errors).
+ if (newValue > quint32(std::numeric_limits<qint32>::max())) {
+ connectionError(FLOW_CONTROL_ERROR, "SETTINGS invalid initial window size");
+ return false;
+ }
+
+ const qint32 delta = qint32(newValue) - streamInitialSendWindowSize;
+ streamInitialSendWindowSize = qint32(newValue);
+
+ qCDebug(qHttp2ConnectionLog, "[%p] Adjusting initial window size for %zu streams by %d",
+ this, size_t(m_streams.size()), delta);
+ for (const QPointer<QHttp2Stream> &stream : std::as_const(m_streams)) {
+ if (!stream || !stream->isActive())
+ continue;
+ qint32 sum = 0;
+ if (qAddOverflow(stream->m_sendWindow, delta, &sum)) {
+ stream->sendRST_STREAM(PROTOCOL_ERROR);
+ stream->finishWithError(QNetworkReply::ProtocolFailure,
+ "SETTINGS window overflow"_L1);
+ continue;
+ }
+ stream->m_sendWindow = sum;
+ if (delta > 0 && stream->isUploadingDATA() && !stream->isUploadBlocked()) {
+ QMetaObject::invokeMethod(stream, &QHttp2Stream::maybeResumeUpload,
+ Qt::QueuedConnection);
+ }
+ }
+ break;
+ }
+ case Settings::MAX_CONCURRENT_STREAMS_ID: {
+ qCDebug(qHttp2ConnectionLog, "[%p] Received SETTINGS MAX_CONCURRENT_STREAMS %d", this,
+ newValue);
+ m_maxConcurrentStreams = newValue;
+ break;
+ }
+ case Settings::MAX_FRAME_SIZE_ID: {
+ qCDebug(qHttp2ConnectionLog, "[%p] Received SETTINGS MAX_FRAME_SIZE %d", this, newValue);
+ if (newValue < Http2::minPayloadLimit || newValue > Http2::maxPayloadSize) {
+ connectionError(PROTOCOL_ERROR, "SETTINGS max frame size is out of range");
+ return false;
+ }
+ maxFrameSize = newValue;
+ break;
+ }
+ case Settings::MAX_HEADER_LIST_SIZE_ID: {
+ qCDebug(qHttp2ConnectionLog, "[%p] Received SETTINGS MAX_HEADER_LIST_SIZE %d", this,
+ newValue);
+ // We just remember this value, it can later
+ // prevent us from sending any request (and this
+ // will end up in request/reply error).
+ m_maxHeaderListSize = newValue;
+ break;
+ }
+ case Http2::Settings::ENABLE_PUSH_ID:
+ qCDebug(qHttp2ConnectionLog, "[%p] Received SETTINGS ENABLE_PUSH %d", this, newValue);
+ if (newValue != 0 && newValue != 1) {
+ connectionError(PROTOCOL_ERROR, "SETTINGS peer sent illegal value for ENABLE_PUSH");
+ return false;
+ }
+ if (m_connectionType == Type::Client) {
+ if (newValue == 1) {
+ connectionError(PROTOCOL_ERROR, "SETTINGS server sent ENABLE_PUSH=1");
+ return false;
+ }
+ } else { // server-side
+ pushPromiseEnabled = newValue;
+ break;
+ }
+ }
+
+ return true;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qhttp2connection_p.cpp"
diff --git a/src/network/access/qhttp2connection_p.h b/src/network/access/qhttp2connection_p.h
new file mode 100644
index 0000000000..ca2cae58e0
--- /dev/null
+++ b/src/network/access/qhttp2connection_p.h
@@ -0,0 +1,372 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef HTTP2CONNECTION_P_H
+#define HTTP2CONNECTION_P_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/qtnetworkglobal_p.h>
+
+#include <QtCore/qobject.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qvarlengtharray.h>
+#include <QtCore/qxpfunctional.h>
+#include <QtNetwork/qhttp2configuration.h>
+#include <QtNetwork/qtcpsocket.h>
+
+#include <private/http2protocol_p.h>
+#include <private/http2streams_p.h>
+#include <private/http2frames_p.h>
+#include <private/hpack_p.h>
+
+#include <variant>
+#include <optional>
+#include <type_traits>
+#include <limits>
+
+QT_BEGIN_NAMESPACE
+
+template <typename T, typename Err>
+class QH2Expected
+{
+ static_assert(!std::is_same_v<T, Err>, "T and Err must be different types");
+public:
+ // Rule Of Zero applies
+ QH2Expected(T &&value) : m_data(std::move(value)) { }
+ QH2Expected(const T &value) : m_data(value) { }
+ QH2Expected(Err &&error) : m_data(std::move(error)) { }
+ QH2Expected(const Err &error) : m_data(error) { }
+
+ QH2Expected &operator=(T &&value)
+ {
+ m_data = std::move(value);
+ return *this;
+ }
+ QH2Expected &operator=(const T &value)
+ {
+ m_data = value;
+ return *this;
+ }
+ QH2Expected &operator=(Err &&error)
+ {
+ m_data = std::move(error);
+ return *this;
+ }
+ QH2Expected &operator=(const Err &error)
+ {
+ m_data = error;
+ return *this;
+ }
+ T unwrap() const
+ {
+ Q_ASSERT(ok());
+ return std::get<T>(m_data);
+ }
+ Err error() const
+ {
+ Q_ASSERT(has_error());
+ return std::get<Err>(m_data);
+ }
+ bool ok() const noexcept { return std::holds_alternative<T>(m_data); }
+ bool has_value() const noexcept { return ok(); }
+ bool has_error() const noexcept { return std::holds_alternative<Err>(m_data); }
+ void clear() noexcept { m_data.reset(); }
+
+private:
+ std::variant<T, Err> m_data;
+};
+
+class QHttp2Connection;
+class Q_NETWORK_EXPORT QHttp2Stream : public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY_MOVE(QHttp2Stream)
+
+public:
+ enum class State { Idle, ReservedRemote, Open, HalfClosedLocal, HalfClosedRemote, Closed };
+ Q_ENUM(State)
+ constexpr static quint8 DefaultPriority = 127;
+
+ ~QHttp2Stream() noexcept;
+
+ // HTTP2 things
+ quint32 streamID() const noexcept { return m_streamID; }
+
+ // Are we waiting for a larger send window before sending more data?
+ bool isUploadBlocked() const noexcept;
+ bool isUploadingDATA() const noexcept { return m_uploadByteDevice != nullptr; }
+ State state() const noexcept { return m_state; }
+ bool isActive() const noexcept { return m_state != State::Closed && m_state != State::Idle; }
+ bool isPromisedStream() const noexcept { return m_isReserved; }
+ bool wasReset() const noexcept { return m_RST_STREAM_code.has_value(); }
+ quint32 RST_STREAM_code() const noexcept { return m_RST_STREAM_code.value_or(0); }
+ // Just the list of headers, as received, may contain duplicates:
+ HPack::HttpHeader receivedHeaders() const noexcept { return m_headers; }
+
+ QByteDataBuffer downloadBuffer() const noexcept { return m_downloadBuffer; }
+
+Q_SIGNALS:
+ void headersReceived(const HPack::HttpHeader &headers, bool endStream);
+ void headersUpdated();
+ void errorOccurred(quint32 errorCode, const QString &errorString);
+ void stateChanged(QHttp2Stream::State newState);
+ void promisedStreamReceived(quint32 newStreamID);
+ void uploadBlocked();
+ void dataReceived(const QByteArray &data, bool endStream);
+
+ void bytesWritten(qint64 bytesWritten);
+ void uploadDeviceError(const QString &errorString);
+ void uploadFinished();
+
+public Q_SLOTS:
+ bool sendRST_STREAM(quint32 errorCode);
+ bool sendHEADERS(const HPack::HttpHeader &headers, bool endStream,
+ quint8 priority = DefaultPriority);
+ void sendDATA(QIODevice *device, bool endStream);
+ void sendDATA(QNonContiguousByteDevice *device, bool endStream);
+ void sendWINDOW_UPDATE(quint32 delta);
+
+private Q_SLOTS:
+ void maybeResumeUpload();
+ void uploadDeviceReadChannelFinished();
+ void uploadDeviceDestroyed();
+
+private:
+ friend class QHttp2Connection;
+ QHttp2Stream(QHttp2Connection *connection, quint32 streamID) noexcept;
+
+ [[nodiscard]] QHttp2Connection *getConnection() const
+ {
+ return qobject_cast<QHttp2Connection *>(parent());
+ }
+
+ enum class StateTransition {
+ Open,
+ CloseLocal,
+ CloseRemote,
+ RST,
+ };
+
+ void setState(State newState);
+ void transitionState(StateTransition transition);
+ void internalSendDATA();
+ void finishSendDATA();
+
+ void handleDATA(const Http2::Frame &inboundFrame);
+ void handleHEADERS(Http2::FrameFlags frameFlags, const HPack::HttpHeader &headers);
+ void handleRST_STREAM(const Http2::Frame &inboundFrame);
+ void handleWINDOW_UPDATE(const Http2::Frame &inboundFrame);
+
+ void finishWithError(quint32 errorCode, const QString &message);
+ void finishWithError(quint32 errorCode);
+
+ // Keep it const since it never changes after creation
+ const quint32 m_streamID = 0;
+ qint32 m_recvWindow = 0;
+ qint32 m_sendWindow = 0;
+ bool m_endStreamAfterDATA = false;
+ std::optional<quint32> m_RST_STREAM_code;
+
+ QIODevice *m_uploadDevice = nullptr;
+ QNonContiguousByteDevice *m_uploadByteDevice = nullptr;
+
+ QByteDataBuffer m_downloadBuffer;
+ State m_state = State::Idle;
+ HPack::HttpHeader m_headers;
+ bool m_isReserved = false;
+};
+
+class Q_NETWORK_EXPORT QHttp2Connection : public QObject
+{
+ Q_OBJECT
+ Q_DISABLE_COPY_MOVE(QHttp2Connection)
+
+public:
+ enum class CreateStreamError {
+ MaxConcurrentStreamsReached,
+ StreamIdsExhausted,
+ ReceivedGOAWAY,
+ };
+ Q_ENUM(CreateStreamError)
+
+ enum class PingState {
+ Ping,
+ PongSignatureIdentical,
+ PongSignatureChanged,
+ PongNoPingSent, // We got an ACKed ping but had not sent any
+ };
+
+ // For a pre-established connection:
+ [[nodiscard]] static QHttp2Connection *
+ createUpgradedConnection(QIODevice *socket, const QHttp2Configuration &config);
+ // For a new connection, potential TLS handshake must already be finished:
+ [[nodiscard]] static QHttp2Connection *createDirectConnection(QIODevice *socket,
+ const QHttp2Configuration &config);
+ [[nodiscard]] static QHttp2Connection *
+ createDirectServerConnection(QIODevice *socket, const QHttp2Configuration &config);
+ ~QHttp2Connection();
+
+ [[nodiscard]] QH2Expected<QHttp2Stream *, CreateStreamError> createStream();
+
+ QHttp2Stream *getStream(quint32 streamId) const;
+ QHttp2Stream *promisedStream(const QUrl &streamKey) const
+ {
+ if (quint32 id = m_promisedStreams.value(streamKey, 0); id)
+ return m_streams.value(id);
+ return nullptr;
+ }
+
+ void close() { sendGOAWAY(Http2::HTTP2_NO_ERROR); }
+
+ bool isGoingAway() const noexcept { return m_goingAway; }
+
+ quint32 maxConcurrentStreams() const noexcept { return m_maxConcurrentStreams; }
+ quint32 maxHeaderListSize() const noexcept { return m_maxHeaderListSize; }
+
+ bool isUpgradedConnection() const noexcept { return m_upgradedConnection; }
+
+Q_SIGNALS:
+ void newIncomingStream(QHttp2Stream *stream);
+ void newPromisedStream(QHttp2Stream *stream);
+ void errorReceived(/*@future: add as needed?*/); // Connection errors only, no stream-specific errors
+ void connectionClosed();
+ void settingsFrameReceived();
+ void pingFrameRecived(QHttp2Connection::PingState state);
+ void errorOccurred(Http2::Http2Error errorCode, const QString &errorString);
+ void receivedGOAWAY(quint32 errorCode, quint32 lastStreamID);
+public Q_SLOTS:
+ bool sendPing();
+ bool sendPing(QByteArrayView data);
+ void handleReadyRead();
+ void handleConnectionClosure();
+
+private:
+ friend class QHttp2Stream;
+ [[nodiscard]] QIODevice *getSocket() const { return qobject_cast<QIODevice *>(parent()); }
+
+ QH2Expected<QHttp2Stream *, QHttp2Connection::CreateStreamError> createStreamInternal();
+ QHttp2Stream *createStreamInternal_impl(quint32 streamID);
+
+ bool isInvalidStream(quint32 streamID) noexcept;
+ bool streamWasReset(quint32 streamID) noexcept;
+
+ void connectionError(Http2::Http2Error errorCode,
+ const char *message); // Connection failed to be established?
+ void setH2Configuration(QHttp2Configuration config);
+ void closeSession();
+ qsizetype numActiveStreamsImpl(quint32 mask) const noexcept;
+ qsizetype numActiveRemoteStreams() const noexcept;
+ qsizetype numActiveLocalStreams() const noexcept;
+
+ bool sendClientPreface();
+ bool sendSETTINGS();
+ bool sendServerPreface();
+ bool serverCheckClientPreface();
+ bool sendWINDOW_UPDATE(quint32 streamID, quint32 delta);
+ bool sendGOAWAY(quint32 errorCode);
+ bool sendSETTINGS_ACK();
+
+ void handleDATA();
+ void handleHEADERS();
+ void handlePRIORITY();
+ void handleRST_STREAM();
+ void handleSETTINGS();
+ void handlePUSH_PROMISE();
+ void handlePING();
+ void handleGOAWAY();
+ void handleWINDOW_UPDATE();
+ void handleCONTINUATION();
+
+ void handleContinuedHEADERS();
+
+ bool acceptSetting(Http2::Settings identifier, quint32 newValue);
+
+ bool readClientPreface();
+
+ explicit QHttp2Connection(QIODevice *socket);
+
+ enum class Type { Client, Server } m_connectionType = Type::Client;
+
+ bool waitingForSettingsACK = false;
+
+ static constexpr quint32 maxAcceptableTableSize = 16 * HPack::FieldLookupTable::DefaultSize;
+ // HTTP/2 4.3: Header compression is stateful. One compression context and
+ // one decompression context are used for the entire connection.
+ HPack::Decoder decoder = HPack::Decoder(HPack::FieldLookupTable::DefaultSize);
+ HPack::Encoder encoder = HPack::Encoder(HPack::FieldLookupTable::DefaultSize, true);
+
+ QHttp2Configuration m_config;
+ QHash<quint32, QPointer<QHttp2Stream>> m_streams;
+ QHash<QUrl, quint32> m_promisedStreams;
+ QVarLengthArray<quint32> m_resetStreamIDs;
+ std::optional<QByteArray> m_lastPingSignature = std::nullopt;
+ quint32 m_nextStreamID = 1;
+
+ // Peer's max frame size (this min is the default value
+ // we start with, that can be updated by SETTINGS frame):
+ quint32 maxFrameSize = Http2::minPayloadLimit;
+
+ Http2::FrameReader frameReader;
+ Http2::Frame inboundFrame;
+ Http2::FrameWriter frameWriter;
+
+ // Temporary storage to assemble HEADERS' block
+ // from several CONTINUATION frames ...
+ bool continuationExpected = false;
+ std::vector<Http2::Frame> continuedFrames;
+
+ // Control flow:
+
+ // This is how many concurrent streams our peer allows us, 100 is the
+ // initial value, can be updated by the server's SETTINGS frame(s):
+ quint32 m_maxConcurrentStreams = Http2::maxConcurrentStreams;
+ // While we allow sending SETTTINGS_MAX_CONCURRENT_STREAMS to limit our peer,
+ // it's just a hint and we do not actually enforce it (and we can continue
+ // sending requests and creating streams while maxConcurrentStreams allows).
+
+ // This is our (client-side) maximum possible receive window size, we set
+ // it in a ctor from QHttp2Configuration, it does not change after that.
+ // The default is 64Kb:
+ qint32 maxSessionReceiveWindowSize = Http2::defaultSessionWindowSize;
+
+ // Our session current receive window size, updated in a ctor from
+ // QHttp2Configuration. Signed integer since it can become negative
+ // (it's still a valid window size).
+ qint32 sessionReceiveWindowSize = Http2::defaultSessionWindowSize;
+ // Our per-stream receive window size, default is 64 Kb, will be updated
+ // from QHttp2Configuration. Again, signed - can become negative.
+ qint32 streamInitialReceiveWindowSize = Http2::defaultSessionWindowSize;
+
+ // These are our peer's receive window sizes, they will be updated by the
+ // peer's SETTINGS and WINDOW_UPDATE frames, defaults presumed to be 64Kb.
+ qint32 sessionSendWindowSize = Http2::defaultSessionWindowSize;
+ qint32 streamInitialSendWindowSize = Http2::defaultSessionWindowSize;
+
+ // Our peer's header size limitations. It's unlimited by default, but can
+ // be changed via peer's SETTINGS frame.
+ quint32 m_maxHeaderListSize = (std::numeric_limits<quint32>::max)();
+ // While we can send SETTINGS_MAX_HEADER_LIST_SIZE value (our limit on
+ // the headers size), we never enforce it, it's just a hint to our peer.
+
+ bool m_upgradedConnection = false;
+ bool m_goingAway = false;
+ bool pushPromiseEnabled = false;
+ quint32 m_lastIncomingStreamID = Http2::connectionStreamID;
+
+ // Server-side only:
+ bool m_waitingForClientPreface = false;
+};
+
+QT_END_NAMESPACE
+
+#endif // HTTP2CONNECTION_P_H
diff --git a/src/network/access/qhttp2protocolhandler.cpp b/src/network/access/qhttp2protocolhandler.cpp
index 26d80f5298..d9341dc643 100644
--- a/src/network/access/qhttp2protocolhandler.cpp
+++ b/src/network/access/qhttp2protocolhandler.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qhttpnetworkconnection_p.h"
#include "qhttp2protocolhandler_p.h"
@@ -46,10 +10,12 @@
#include <private/qnoncontiguousbytedevice_p.h>
#include <QtNetwork/qabstractsocket.h>
+
#include <QtCore/qloggingcategory.h>
#include <QtCore/qendian.h>
#include <QtCore/qdebug.h>
#include <QtCore/qlist.h>
+#include <QtCore/qnumeric.h>
#include <QtCore/qurl.h>
#include <qhttp2configuration.h>
@@ -62,9 +28,12 @@
#include <algorithm>
#include <vector>
+#include <optional>
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
namespace
{
@@ -79,10 +48,10 @@ HPack::HttpHeader build_headers(const QHttpNetworkRequest &request, quint32 maxH
// 1. Before anything - mandatory fields, if they do not fit into maxHeaderList -
// then stop immediately with error.
const auto auth = request.url().authority(QUrl::FullyEncoded | QUrl::RemoveUserInfo).toLatin1();
- header.push_back(HeaderField(":authority", auth));
- header.push_back(HeaderField(":method", request.methodName()));
- header.push_back(HeaderField(":path", request.uri(useProxy)));
- header.push_back(HeaderField(":scheme", request.url().scheme().toLatin1()));
+ header.emplace_back(":authority", auth);
+ header.emplace_back(":method", request.methodName());
+ header.emplace_back(":path", request.uri(useProxy));
+ header.emplace_back(":scheme", request.url().scheme().toLatin1());
HeaderSize size = header_size(header);
if (!size.first) // Ooops!
@@ -91,9 +60,11 @@ HPack::HttpHeader build_headers(const QHttpNetworkRequest &request, quint32 maxH
if (size.second > maxHeaderListSize)
return HttpHeader(); // Bad, we cannot send this request ...
- const auto requestHeader = request.header();
- for (const auto &field : requestHeader) {
- const HeaderSize delta = entry_size(field.first, field.second);
+ const QHttpHeaders requestHeader = request.header();
+ for (qsizetype i = 0; i < requestHeader.size(); ++i) {
+ const auto name = requestHeader.nameAt(i);
+ const auto value = requestHeader.valueAt(i);
+ const HeaderSize delta = entry_size(name, value);
if (!delta.first) // Overflow???
break;
if (std::numeric_limits<quint32>::max() - delta.second < size.second)
@@ -102,73 +73,38 @@ HPack::HttpHeader build_headers(const QHttpNetworkRequest &request, quint32 maxH
if (size.second > maxHeaderListSize)
break;
- if (field.first.compare("connection", Qt::CaseInsensitive) == 0 ||
- field.first.compare("host", Qt::CaseInsensitive) == 0 ||
- field.first.compare("keep-alive", Qt::CaseInsensitive) == 0 ||
- field.first.compare("proxy-connection", Qt::CaseInsensitive) == 0 ||
- field.first.compare("transfer-encoding", Qt::CaseInsensitive) == 0)
+ if (name == "connection"_L1 || name == "host"_L1 || name == "keep-alive"_L1
+ || name == "proxy-connection"_L1 || name == "transfer-encoding"_L1) {
continue; // Those headers are not valid (section 3.2.1) - from QSpdyProtocolHandler
+ }
// TODO: verify with specs, which fields are valid to send ....
- // toLower - 8.1.2 .... "header field names MUST be converted to lowercase prior
- // to their encoding in HTTP/2.
- // A request or response containing uppercase header field names
- // MUST be treated as malformed (Section 8.1.2.6)".
- header.push_back(HeaderField(field.first.toLower(), field.second));
+ //
+ // Note: RFC 7450 8.1.2 (HTTP/2) states that header field names must be lower-cased
+ // prior to their encoding in HTTP/2; header name fields in QHttpHeaders are already
+ // lower-cased
+ header.emplace_back(QByteArray{name.data(), name.size()},
+ QByteArray{value.data(), value.size()});
}
return header;
}
-std::vector<uchar> assemble_hpack_block(const std::vector<Http2::Frame> &frames)
-{
- std::vector<uchar> hpackBlock;
-
- quint32 total = 0;
- for (const auto &frame : frames)
- total += frame.hpackBlockSize();
-
- if (!total)
- return hpackBlock;
-
- hpackBlock.resize(total);
- auto dst = hpackBlock.begin();
- for (const auto &frame : frames) {
- if (const auto hpackBlockSize = frame.hpackBlockSize()) {
- const uchar *src = frame.hpackBlockBegin();
- std::copy(src, src + hpackBlockSize, dst);
- dst += hpackBlockSize;
- }
- }
-
- return hpackBlock;
-}
-
QUrl urlkey_from_request(const QHttpNetworkRequest &request)
{
QUrl url;
url.setScheme(request.url().scheme());
url.setAuthority(request.url().authority(QUrl::FullyEncoded | QUrl::RemoveUserInfo));
- url.setPath(QLatin1String(request.uri(false)));
+ url.setPath(QLatin1StringView(request.uri(false)));
return url;
}
-bool sum_will_overflow(qint32 windowSize, qint32 delta)
-{
- if (windowSize > 0)
- return std::numeric_limits<qint32>::max() - windowSize < delta;
- return std::numeric_limits<qint32>::min() - windowSize > delta;
-}
-
}// Unnamed namespace
// Since we anyway end up having this in every function definition:
using namespace Http2;
-const std::deque<quint32>::size_type QHttp2ProtocolHandler::maxRecycledStreams = 10000;
-const quint32 QHttp2ProtocolHandler::maxAcceptableTableSize;
-
QHttp2ProtocolHandler::QHttp2ProtocolHandler(QHttpNetworkConnectionChannel *channel)
: QAbstractProtocolHandler(channel),
decoder(HPack::FieldLookupTable::DefaultSize),
@@ -239,8 +175,7 @@ void QHttp2ProtocolHandler::_q_uploadDataReadyRead()
auto &stream = activeStreams[streamID];
if (!sendDATA(stream)) {
- finishStreamWithError(stream, QNetworkReply::UnknownNetworkError,
- QLatin1String("failed to send DATA"));
+ finishStreamWithError(stream, QNetworkReply::UnknownNetworkError, "failed to send DATA"_L1);
sendRST_STREAM(streamID, INTERNAL_ERROR);
markAsReset(streamID);
deleteActiveStream(streamID);
@@ -353,9 +288,7 @@ bool QHttp2ProtocolHandler::sendRequest()
auto &requests = m_channel->h2RequestsToSend;
for (auto it = requests.begin(), endIt = requests.end(); it != endIt;) {
const auto &pair = *it;
- const QString scheme(pair.first.url().scheme());
- if (scheme == QLatin1String("preconnect-http")
- || scheme == QLatin1String("preconnect-https")) {
+ if (pair.first.isPreConnect()) {
m_connection->preConnectFinished();
emit pair.second->finished();
it = requests.erase(it);
@@ -393,11 +326,13 @@ bool QHttp2ProtocolHandler::sendRequest()
initReplyFromPushPromise(message, key);
}
- const auto streamsToUse = std::min<quint32>(maxConcurrentStreams > quint32(activeStreams.size())
- ? maxConcurrentStreams - quint32(activeStreams.size()) : 0,
- requests.size());
+ const auto isClientSide = [](const auto &pair) -> bool { return (pair.first & 1) == 1; };
+ const auto activeClientSideStreams = std::count_if(
+ activeStreams.constKeyValueBegin(), activeStreams.constKeyValueEnd(), isClientSide);
+ const qint64 streamsToUse = qBound(0, qint64(maxConcurrentStreams) - activeClientSideStreams,
+ requests.size());
auto it = requests.begin();
- for (quint32 i = 0; i < streamsToUse; ++i) {
+ for (qint64 i = 0; i < streamsToUse; ++i) {
const qint32 newStreamID = createNewStream(*it);
if (!newStreamID) {
// TODO: actually we have to open a new connection.
@@ -410,14 +345,14 @@ bool QHttp2ProtocolHandler::sendRequest()
Stream &newStream = activeStreams[newStreamID];
if (!sendHEADERS(newStream)) {
finishStreamWithError(newStream, QNetworkReply::UnknownNetworkError,
- QLatin1String("failed to send HEADERS frame(s)"));
+ "failed to send HEADERS frame(s)"_L1);
deleteActiveStream(newStreamID);
continue;
}
if (newStream.data() && !sendDATA(newStream)) {
finishStreamWithError(newStream, QNetworkReply::UnknownNetworkError,
- QLatin1String("failed to send DATA frame(s)"));
+ "failed to send DATA frame(s)"_L1);
sendRST_STREAM(newStreamID, INTERNAL_ERROR);
markAsReset(newStreamID);
deleteActiveStream(newStreamID);
@@ -540,7 +475,7 @@ bool QHttp2ProtocolHandler::sendDATA(Stream &stream)
}
frameWriter.start(FrameType::DATA, FrameFlag::EMPTY, stream.streamID);
- const qint32 bytesWritten = std::min<qint32>(slot, chunkSize);
+ const qint32 bytesWritten = qint32(std::min<qint64>(slot, chunkSize));
if (!frameWriter.writeDATA(*m_socket, maxFrameSize, src, bytesWritten))
return false;
@@ -612,12 +547,12 @@ void QHttp2ProtocolHandler::handleDATA()
sessionReceiveWindowSize -= inboundFrame.payloadSize();
- if (activeStreams.contains(streamID)) {
- auto &stream = activeStreams[streamID];
+ auto it = activeStreams.find(streamID);
+ if (it != activeStreams.end()) {
+ Stream &stream = it.value();
if (qint32(inboundFrame.payloadSize()) > stream.recvWindow) {
- finishStreamWithError(stream, QNetworkReply::ProtocolFailure,
- QLatin1String("flow control error"));
+ finishStreamWithError(stream, QNetworkReply::ProtocolFailure, "flow control error"_L1);
sendRST_STREAM(streamID, FLOW_CONTROL_ERROR);
markAsReset(streamID);
deleteActiveStream(streamID);
@@ -891,7 +826,7 @@ void QHttp2ProtocolHandler::handleGOAWAY()
// successful completion.
if (errorCode == HTTP2_NO_ERROR) {
error = QNetworkReply::ContentReSendError;
- message = QLatin1String("Server stopped accepting new streams before this stream was established");
+ message = "Server stopped accepting new streams before this stream was established"_L1;
}
for (quint32 id = lastStreamID; id < nextID; id += 2) {
@@ -920,24 +855,27 @@ void QHttp2ProtocolHandler::handleWINDOW_UPDATE()
const auto streamID = inboundFrame.streamID();
if (streamID == Http2::connectionStreamID) {
- if (!valid || sum_will_overflow(sessionSendWindowSize, delta))
+ qint32 sum = 0;
+ if (!valid || qAddOverflow(sessionSendWindowSize, qint32(delta), &sum))
return connectionError(PROTOCOL_ERROR, "WINDOW_UPDATE invalid delta");
- sessionSendWindowSize += delta;
+ sessionSendWindowSize = sum;
} else {
- if (!activeStreams.contains(streamID)) {
+ auto it = activeStreams.find(streamID);
+ if (it == activeStreams.end()) {
// WINDOW_UPDATE on closed streams can be ignored.
return;
}
- auto &stream = activeStreams[streamID];
- if (!valid || sum_will_overflow(stream.sendWindow, delta)) {
+ Stream &stream = it.value();
+ qint32 sum = 0;
+ if (!valid || qAddOverflow(stream.sendWindow, qint32(delta), &sum)) {
finishStreamWithError(stream, QNetworkReply::ProtocolFailure,
- QLatin1String("invalid WINDOW_UPDATE delta"));
+ "invalid WINDOW_UPDATE delta"_L1);
sendRST_STREAM(streamID, PROTOCOL_ERROR);
markAsReset(streamID);
deleteActiveStream(streamID);
return;
}
- stream.sendWindow += delta;
+ stream.sendWindow = sum;
}
// Since we're in _q_receiveReply at the moment, let's first handle other
@@ -976,9 +914,10 @@ void QHttp2ProtocolHandler::handleContinuedHEADERS()
const auto streamID = continuedFrames[0].streamID();
+ const auto streamIt = activeStreams.find(streamID);
if (firstFrameType == FrameType::HEADERS) {
- if (activeStreams.contains(streamID)) {
- Stream &stream = activeStreams[streamID];
+ if (streamIt != activeStreams.end()) {
+ Stream &stream = streamIt.value();
if (stream.state != Stream::halfClosedLocal
&& stream.state != Stream::remoteReserved
&& stream.state != Stream::open) {
@@ -986,7 +925,7 @@ void QHttp2ProtocolHandler::handleContinuedHEADERS()
// (these streams are in halfClosedLocal or open state) or
// remote-reserved streams from a server's PUSH_PROMISE.
finishStreamWithError(stream, QNetworkReply::ProtocolFailure,
- QLatin1String("HEADERS on invalid stream"));
+ "HEADERS on invalid stream"_L1);
sendRST_STREAM(streamID, CANCEL);
markAsReset(streamID);
deleteActiveStream(streamID);
@@ -1000,8 +939,13 @@ void QHttp2ProtocolHandler::handleContinuedHEADERS()
// has yet to see the reset.
}
- std::vector<uchar> hpackBlock(assemble_hpack_block(continuedFrames));
- if (!hpackBlock.size()) {
+ std::vector<uchar> hpackBlock(Http2::assemble_hpack_block(continuedFrames));
+ const bool hasHeaderFields = !hpackBlock.empty();
+ if (hasHeaderFields) {
+ HPack::BitIStream inputStream{&hpackBlock[0], &hpackBlock[0] + hpackBlock.size()};
+ if (!decoder.decodeHeaderFields(inputStream))
+ return connectionError(COMPRESSION_ERROR, "HPACK decompression failed");
+ } else if (firstFrameType == FrameType::PUSH_PROMISE) {
// It could be a PRIORITY sent in HEADERS - already handled by this
// point in handleHEADERS. If it was PUSH_PROMISE (HTTP/2 8.2.1):
// "The header fields in PUSH_PROMISE and any subsequent CONTINUATION
@@ -1010,21 +954,16 @@ void QHttp2ProtocolHandler::handleContinuedHEADERS()
// not include a complete and valid set of header fields or the :method
// pseudo-header field identifies a method that is not safe, it MUST
// respond with a stream error (Section 5.4.2) of type PROTOCOL_ERROR."
- if (firstFrameType == FrameType::PUSH_PROMISE)
- resetPromisedStream(continuedFrames[0], Http2::PROTOCOL_ERROR);
-
+ resetPromisedStream(continuedFrames[0], Http2::PROTOCOL_ERROR);
return;
}
- HPack::BitIStream inputStream{&hpackBlock[0], &hpackBlock[0] + hpackBlock.size()};
- if (!decoder.decodeHeaderFields(inputStream))
- return connectionError(COMPRESSION_ERROR, "HPACK decompression failed");
-
switch (firstFrameType) {
case FrameType::HEADERS:
- if (activeStreams.contains(streamID)) {
- Stream &stream = activeStreams[streamID];
- updateStream(stream, decoder.decodedHeader());
+ if (streamIt != activeStreams.end()) {
+ Stream &stream = streamIt.value();
+ if (hasHeaderFields)
+ updateStream(stream, decoder.decodedHeader());
// Needs to resend the request; we should finish and delete the current stream
const bool needResend = stream.request().d->needResendWithCredentials;
// No DATA frames. Or needs to resend.
@@ -1067,17 +1006,18 @@ bool QHttp2ProtocolHandler::acceptSetting(Http2::Settings identifier, quint32 ne
std::vector<quint32> brokenStreams;
brokenStreams.reserve(activeStreams.size());
for (auto &stream : activeStreams) {
- if (sum_will_overflow(stream.sendWindow, delta)) {
+ qint32 sum = 0;
+ if (qAddOverflow(stream.sendWindow, delta, &sum)) {
brokenStreams.push_back(stream.streamID);
continue;
}
- stream.sendWindow += delta;
+ stream.sendWindow = sum;
}
for (auto id : brokenStreams) {
auto &stream = activeStreams[id];
finishStreamWithError(stream, QNetworkReply::ProtocolFailure,
- QLatin1String("SETTINGS window overflow"));
+ "SETTINGS window overflow"_L1);
sendRST_STREAM(id, PROTOCOL_ERROR);
markAsReset(id);
deleteActiveStream(id);
@@ -1136,11 +1076,9 @@ void QHttp2ProtocolHandler::updateStream(Stream &stream, const HPack::HttpHeader
// moment and we are probably not done yet. So we extract url and set it
// here, if needed.
int statusCode = 0;
- QUrl redirectUrl;
-
for (const auto &pair : headers) {
const auto &name = pair.name;
- auto value = pair.value;
+ const auto value = QByteArrayView(pair.value);
// TODO: part of this code copies what SPDY protocol handler does when
// processing headers. Binary nature of HTTP/2 and SPDY saves us a lot
@@ -1160,74 +1098,32 @@ void QHttp2ProtocolHandler::updateStream(Stream &stream, const HPack::HttpHeader
if (ok)
httpReply->setContentLength(length);
} else {
- if (name == "location")
- redirectUrl = QUrl::fromEncoded(value);
- QByteArray binder(", ");
- if (name == "set-cookie")
- binder = "\n";
- httpReply->appendHeaderField(name, value.replace('\0', binder));
+ const auto binder = name == "set-cookie" ? QByteArrayView("\n") : QByteArrayView(", ");
+ httpReply->appendHeaderField(name, QByteArray(pair.value).replace('\0', binder));
}
}
- const auto handleAuth = [&, this](const QByteArray &authField, bool isProxy) -> bool {
- Q_ASSERT(httpReply);
- const auto auth = authField.trimmed();
- if (auth.startsWith("Negotiate") || auth.startsWith("NTLM")) {
- // @todo: We're supposed to fall back to http/1.1:
- // https://docs.microsoft.com/en-us/iis/get-started/whats-new-in-iis-10/http2-on-iis#when-is-http2-not-supported
- // "Windows authentication (NTLM/Kerberos/Negotiate) is not supported with HTTP/2.
- // In this case IIS will fall back to HTTP/1.1."
- // Though it might be OK to ignore this. The server shouldn't let us connect with
- // HTTP/2 if it doesn't support us using it.
- } else if (!auth.isEmpty()) {
- // Somewhat mimics parts of QHttpNetworkConnectionChannel::handleStatus
- bool resend = false;
- const bool authenticateHandled = m_connection->d_func()->handleAuthenticateChallenge(
- m_socket, httpReply, isProxy, resend);
- if (authenticateHandled && resend) {
- httpReply->d_func()->eraseData();
- // Add the request back in queue, we'll retry later now that
- // we've gotten some username/password set on it:
- httpRequest.d->needResendWithCredentials = true;
- m_channel->h2RequestsToSend.insert(httpRequest.priority(), stream.httpPair);
- httpReply->d_func()->clearHeaders();
- // If we have data we were uploading we need to reset it:
- if (stream.data()) {
- stream.data()->reset();
- httpReplyPrivate->totallyUploadedData = 0;
- }
- return true;
- } // else: Authentication failed or was cancelled
- }
- return false;
- };
+ // Discard all informational (1xx) replies with the exception of 101.
+ // Also see RFC 9110 (Chapter 15.2)
+ if (statusCode == 100 || (102 <= statusCode && statusCode <= 199)) {
+ httpReplyPrivate->clearHttpLayerInformation();
+ return;
+ }
- if (httpReply) {
- // See Note further down. These statuses would in HTTP/1.1 be handled
- // by QHttpNetworkConnectionChannel::handleStatus. But because h2 has
- // multiple streams/requests in a single channel this structure does not
- // map properly to that function.
- if (httpReply->statusCode() == 401) {
- const auto wwwAuth = httpReply->headerField("www-authenticate");
- if (handleAuth(wwwAuth, false)) {
- sendRST_STREAM(stream.streamID, CANCEL);
- markAsReset(stream.streamID);
- // The stream is finalized and deleted after returning
- return;
- } // else: errors handled later
- } else if (httpReply->statusCode() == 407) {
- const auto proxyAuth = httpReply->headerField("proxy-authenticate");
- if (handleAuth(proxyAuth, true)) {
- sendRST_STREAM(stream.streamID, CANCEL);
- markAsReset(stream.streamID);
- // The stream is finalized and deleted after returning
- return;
- } // else: errors handled later
+ if (QHttpNetworkReply::isHttpRedirect(statusCode) && httpRequest.isFollowRedirects()) {
+ QHttpNetworkConnectionPrivate::ParseRedirectResult result =
+ m_connection->d_func()->parseRedirectResponse(httpReply);
+ if (result.errorCode != QNetworkReply::NoError) {
+ auto errorString = m_connection->d_func()->errorDetail(result.errorCode, m_socket);
+ finishStreamWithError(stream, result.errorCode, errorString);
+ sendRST_STREAM(stream.streamID, INTERNAL_ERROR);
+ markAsReset(stream.streamID);
+ return;
}
- }
- if (QHttpNetworkReply::isHttpRedirect(statusCode) && redirectUrl.isValid())
- httpReply->setRedirectUrl(redirectUrl);
+ if (result.redirectUrl.isValid())
+ httpReply->setRedirectUrl(result.redirectUrl);
+ }
if (httpReplyPrivate->isCompressed() && httpRequest.d->autoDecompress)
httpReplyPrivate->removeAutoDecompressHeader();
@@ -1271,8 +1167,7 @@ void QHttp2ProtocolHandler::updateStream(Stream &stream, const Frame &frame,
replyPrivate->totalProgress += length;
- const QByteArray wrapped(data, length);
- replyPrivate->responseData.append(wrapped);
+ replyPrivate->responseData.append(QByteArray(data, length));
if (replyPrivate->shouldEmitSignals()) {
if (connectionType == Qt::DirectConnection) {
@@ -1289,6 +1184,91 @@ void QHttp2ProtocolHandler::updateStream(Stream &stream, const Frame &frame,
}
}
+// After calling this function, either the request will be re-sent or
+// the reply will be finishedWithError! Do not emit finished() or similar on the
+// reply after this!
+void QHttp2ProtocolHandler::handleAuthorization(Stream &stream)
+{
+ auto *httpReply = stream.reply();
+ auto *httpReplyPrivate = httpReply->d_func();
+ auto &httpRequest = stream.request();
+
+ Q_ASSERT(httpReply && (httpReply->statusCode() == 401 || httpReply->statusCode() == 407));
+
+ const auto handleAuth = [&, this](QByteArrayView authField, bool isProxy) -> bool {
+ Q_ASSERT(httpReply);
+ const QByteArrayView auth = authField.trimmed();
+ if (auth.startsWith("Negotiate") || auth.startsWith("NTLM")) {
+ // @todo: We're supposed to fall back to http/1.1:
+ // https://docs.microsoft.com/en-us/iis/get-started/whats-new-in-iis-10/http2-on-iis#when-is-http2-not-supported
+ // "Windows authentication (NTLM/Kerberos/Negotiate) is not supported with HTTP/2.
+ // In this case IIS will fall back to HTTP/1.1."
+ // Though it might be OK to ignore this. The server shouldn't let us connect with
+ // HTTP/2 if it doesn't support us using it.
+ return false;
+ }
+ // Somewhat mimics parts of QHttpNetworkConnectionChannel::handleStatus
+ bool resend = false;
+ const bool authenticateHandled = m_connection->d_func()->handleAuthenticateChallenge(
+ m_socket, httpReply, isProxy, resend);
+ if (authenticateHandled) {
+ if (resend) {
+ httpReply->d_func()->eraseData();
+ // Add the request back in queue, we'll retry later now that
+ // we've gotten some username/password set on it:
+ httpRequest.d->needResendWithCredentials = true;
+ m_channel->h2RequestsToSend.insert(httpRequest.priority(), stream.httpPair);
+ httpReply->d_func()->clearHeaders();
+ // If we have data we were uploading we need to reset it:
+ if (stream.data()) {
+ stream.data()->reset();
+ httpReplyPrivate->totallyUploadedData = 0;
+ }
+ // We automatically try to send new requests when the stream is
+ // closed, so we don't need to call sendRequest ourselves.
+ return true;
+ } // else: we're just not resending the request.
+ // @note In the http/1.x case we (at time of writing) call close()
+ // for the connectionChannel (which is a bit weird, we could surely
+ // reuse the open socket outside "connection:close"?), but in http2
+ // we only have one channel, so we won't close anything.
+ } else {
+ // No authentication header or authentication isn't supported, but
+ // we got a 401/407 so we cannot succeed. We need to emit signals
+ // for headers and data, and then finishWithError.
+ emit httpReply->headerChanged();
+ emit httpReply->readyRead();
+ QNetworkReply::NetworkError error = httpReply->statusCode() == 401
+ ? QNetworkReply::AuthenticationRequiredError
+ : QNetworkReply::ProxyAuthenticationRequiredError;
+ finishStreamWithError(stream, QNetworkReply::AuthenticationRequiredError,
+ m_connection->d_func()->errorDetail(error, m_socket));
+ }
+ return false;
+ };
+
+ // These statuses would in HTTP/1.1 be handled by
+ // QHttpNetworkConnectionChannel::handleStatus. But because h2 has
+ // multiple streams/requests in a single channel this structure does not
+ // map properly to that function.
+ bool authOk = true;
+ switch (httpReply->statusCode()) {
+ case 401:
+ authOk = handleAuth(httpReply->headerField("www-authenticate"), false);
+ break;
+ case 407:
+ authOk = handleAuth(httpReply->headerField("proxy-authenticate"), true);
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+ if (authOk) {
+ markAsReset(stream.streamID);
+ deleteActiveStream(stream.streamID);
+ } // else: errors handled inside handleAuth
+}
+
+// Called when we have received a frame with the END_STREAM flag set
void QHttp2ProtocolHandler::finishStream(Stream &stream, Qt::ConnectionType connectionType)
{
Q_ASSERT(stream.state == Stream::remoteReserved || stream.reply());
@@ -1296,6 +1276,15 @@ void QHttp2ProtocolHandler::finishStream(Stream &stream, Qt::ConnectionType conn
stream.state = Stream::closed;
auto httpReply = stream.reply();
if (httpReply) {
+ int statusCode = httpReply->statusCode();
+ if (statusCode == 401 || statusCode == 407) {
+ // handleAuthorization will either re-send the request or
+ // finishWithError. In either case we don't want to emit finished
+ // here.
+ handleAuthorization(stream);
+ return;
+ }
+
httpReply->disconnect(this);
if (stream.data())
stream.data()->disconnect(this);
@@ -1419,9 +1408,10 @@ quint32 QHttp2ProtocolHandler::popStreamToResume()
auto &queue = suspendedStreams[rank];
auto it = queue.begin();
for (; it != queue.end(); ++it) {
- if (!activeStreams.contains(*it))
+ auto stream = activeStreams.constFind(*it);
+ if (stream == activeStreams.cend())
continue;
- if (activeStreams[*it].sendWindow > 0)
+ if (stream->sendWindow > 0)
break;
}
@@ -1444,8 +1434,8 @@ void QHttp2ProtocolHandler::removeFromSuspended(quint32 streamID)
void QHttp2ProtocolHandler::deleteActiveStream(quint32 streamID)
{
- if (activeStreams.contains(streamID)) {
- auto &stream = activeStreams[streamID];
+ if (const auto it = activeStreams.constFind(streamID); it != activeStreams.cend()) {
+ const Stream &stream = it.value();
if (stream.reply()) {
stream.reply()->disconnect(this);
streamIDs.remove(stream.reply());
@@ -1454,7 +1444,7 @@ void QHttp2ProtocolHandler::deleteActiveStream(quint32 streamID)
stream.data()->disconnect(this);
streamIDs.remove(stream.data());
}
- activeStreams.remove(streamID);
+ activeStreams.erase(it);
}
removeFromSuspended(streamID);
@@ -1477,13 +1467,14 @@ void QHttp2ProtocolHandler::resumeSuspendedStreams()
if (!streamID)
return;
- if (!activeStreams.contains(streamID))
+ auto it = activeStreams.find(streamID);
+ if (it == activeStreams.end())
continue;
+ Stream &stream = it.value();
- Stream &stream = activeStreams[streamID];
if (!sendDATA(stream)) {
finishStreamWithError(stream, QNetworkReply::UnknownNetworkError,
- QLatin1String("failed to send DATA"));
+ "failed to send DATA"_L1);
sendRST_STREAM(streamID, INTERNAL_ERROR);
markAsReset(streamID);
deleteActiveStream(streamID);
@@ -1509,42 +1500,18 @@ bool QHttp2ProtocolHandler::tryReserveStream(const Http2::Frame &pushPromiseFram
{
Q_ASSERT(pushPromiseFrame.type() == FrameType::PUSH_PROMISE);
- QMap<QByteArray, QByteArray> pseudoHeaders;
- for (const auto &field : requestHeader) {
- if (field.name == ":scheme" || field.name == ":path"
- || field.name == ":authority" || field.name == ":method") {
- if (field.value.isEmpty() || pseudoHeaders.contains(field.name))
- return false;
- pseudoHeaders[field.name] = field.value;
- }
- }
-
- if (pseudoHeaders.size() != 4) {
- // All four required, HTTP/2 8.1.2.3.
- return false;
- }
-
- const QByteArray method = pseudoHeaders[":method"];
- if (method.compare("get", Qt::CaseInsensitive) != 0 &&
- method.compare("head", Qt::CaseInsensitive) != 0)
- return false;
-
- QUrl url;
- url.setScheme(QLatin1String(pseudoHeaders[":scheme"]));
- url.setAuthority(QLatin1String(pseudoHeaders[":authority"]));
- url.setPath(QLatin1String(pseudoHeaders[":path"]));
-
- if (!url.isValid())
+ const auto url = HPack::makePromiseKeyUrl(requestHeader);
+ if (!url.has_value())
return false;
Q_ASSERT(activeStreams.contains(pushPromiseFrame.streamID()));
const Stream &associatedStream = activeStreams[pushPromiseFrame.streamID()];
const auto associatedUrl = urlkey_from_request(associatedStream.request());
- if (url.adjusted(QUrl::RemovePath) != associatedUrl.adjusted(QUrl::RemovePath))
+ if (url->adjusted(QUrl::RemovePath) != associatedUrl.adjusted(QUrl::RemovePath))
return false;
- const auto urlKey = url.toString();
+ const auto urlKey = url->toString();
if (promisedData.contains(urlKey)) // duplicate push promise
return false;
@@ -1583,8 +1550,8 @@ void QHttp2ProtocolHandler::initReplyFromPushPromise(const HttpMessagePair &mess
bool replyFinished = false;
Stream *promisedStream = nullptr;
- if (activeStreams.contains(promise.reservedID)) {
- promisedStream = &activeStreams[promise.reservedID];
+ if (auto it = activeStreams.find(promise.reservedID); it != activeStreams.end()) {
+ promisedStream = &it.value();
// Ok, we have an active (not closed yet) stream waiting for more frames,
// let's pretend we requested it:
promisedStream->httpPair = message;
@@ -1594,8 +1561,8 @@ void QHttp2ProtocolHandler::initReplyFromPushPromise(const HttpMessagePair &mess
streamInitialSendWindowSize,
streamInitialReceiveWindowSize);
closedStream.state = Stream::halfClosedLocal;
- activeStreams.insert(promise.reservedID, closedStream);
- promisedStream = &activeStreams[promise.reservedID];
+ it = activeStreams.insert(promise.reservedID, closedStream);
+ promisedStream = &it.value();
replyFinished = true;
}
@@ -1629,7 +1596,7 @@ void QHttp2ProtocolHandler::connectionError(Http2::Http2Error errorCode,
m_channel->emitFinishedWithError(error, message);
for (auto &stream: activeStreams)
- finishStreamWithError(stream, error, QLatin1String(message));
+ finishStreamWithError(stream, error, QLatin1StringView(message));
closeSession();
}
@@ -1645,3 +1612,5 @@ void QHttp2ProtocolHandler::closeSession()
}
QT_END_NAMESPACE
+
+#include "moc_qhttp2protocolhandler_p.cpp"
diff --git a/src/network/access/qhttp2protocolhandler_p.h b/src/network/access/qhttp2protocolhandler_p.h
index 14deabd70b..3b818771a6 100644
--- a/src/network/access/qhttp2protocolhandler_p.h
+++ b/src/network/access/qhttp2protocolhandler_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QHTTP2PROTOCOLHANDLER_P_H
#define QHTTP2PROTOCOLHANDLER_P_H
@@ -130,6 +94,7 @@ private:
bool acceptSetting(Http2::Settings identifier, quint32 newValue);
+ void handleAuthorization(Stream &stream);
void updateStream(Stream &stream, const HPack::HttpHeader &headers,
Qt::ConnectionType connectionType = Qt::DirectConnection);
void updateStream(Stream &stream, const Http2::Frame &dataFrame,
@@ -156,7 +121,7 @@ private:
// the client's preface 24-byte message.
bool waitingForSettingsACK = false;
- static const quint32 maxAcceptableTableSize = 16 * HPack::FieldLookupTable::DefaultSize;
+ inline static const quint32 maxAcceptableTableSize = 16 * HPack::FieldLookupTable::DefaultSize;
// HTTP/2 4.3: Header compression is stateful. One compression context and
// one decompression context are used for the entire connection.
HPack::Decoder decoder;
@@ -165,7 +130,7 @@ private:
QHash<QObject *, int> streamIDs;
QHash<quint32, Stream> activeStreams;
std::deque<quint32> suspendedStreams[3]; // 3 for priorities: High, Normal, Low.
- static const std::deque<quint32>::size_type maxRecycledStreams;
+ inline static const std::deque<quint32>::size_type maxRecycledStreams = 10000;
std::deque<quint32> recycledStreams;
// Peer's max frame size (this min is the default value
diff --git a/src/network/access/qhttpheaderparser.cpp b/src/network/access/qhttpheaderparser.cpp
index 5caa541cb2..0b7882c18a 100644
--- a/src/network/access/qhttpheaderparser.cpp
+++ b/src/network/access/qhttpheaderparser.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** 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) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qhttpheaderparser_p.h"
@@ -43,12 +7,6 @@
QT_BEGIN_NAMESPACE
-// both constants are taken from the default settings of Apache
-// see: http://httpd.apache.org/docs/2.2/mod/core.html#limitrequestfieldsize and
-// http://httpd.apache.org/docs/2.2/mod/core.html#limitrequestfields
-static const int MAX_HEADER_FIELD_SIZE = 8 * 1024;
-static const int MAX_HEADER_FIELDS = 100;
-
QHttpHeaderParser::QHttpHeaderParser()
: statusCode(100) // Required by tst_QHttpNetworkConnection::ignoresslerror(failure)
, majorVersion(0)
@@ -73,7 +31,7 @@ static bool fieldNameCheck(QByteArrayView name)
|| otherCharacters.contains(c);
};
- return name.size() > 0 && std::all_of(name.begin(), name.end(), fieldNameChar);
+ return !name.empty() && std::all_of(name.begin(), name.end(), fieldNameChar);
}
bool QHttpHeaderParser::parseHeaders(QByteArrayView header)
@@ -85,43 +43,46 @@ bool QHttpHeaderParser::parseHeaders(QByteArrayView header)
return h.startsWith(' ') || h.startsWith('\t');
};
// Headers, if non-empty, start with a non-space and end with a newline:
- if (hSpaceStart(header) || (header.size() && !header.endsWith('\n')))
+ if (hSpaceStart(header) || (!header.empty() && !header.endsWith('\n')))
return false;
while (int tail = header.endsWith("\n\r\n") ? 2 : header.endsWith("\n\n") ? 1 : 0)
header.chop(tail);
- QList<QPair<QByteArray, QByteArray>> result;
- while (header.size()) {
- const int colon = header.indexOf(':');
+ if (header.size() - (header.endsWith("\r\n") ? 2 : 1) > maxTotalSize)
+ return false;
+
+ QHttpHeaders result;
+ while (!header.empty()) {
+ const qsizetype colon = header.indexOf(':');
if (colon == -1) // if no colon check if empty headers
- return result.size() == 0 && (header == "\n" || header == "\r\n");
- if (result.size() >= MAX_HEADER_FIELDS)
+ return result.isEmpty() && (header == "\n" || header == "\r\n");
+ if (result.size() >= maxFieldCount)
return false;
QByteArrayView name = header.first(colon);
if (!fieldNameCheck(name))
return false;
header = header.sliced(colon + 1);
QByteArray value;
- int valueSpace = MAX_HEADER_FIELD_SIZE - name.size() - 1;
+ qsizetype valueSpace = maxFieldSize - name.size() - 1;
do {
- const int endLine = header.indexOf('\n');
+ const qsizetype endLine = header.indexOf('\n');
Q_ASSERT(endLine != -1);
auto line = header.first(endLine); // includes space
valueSpace -= line.size() - (line.endsWith('\r') ? 1 : 0);
if (valueSpace < 0)
return false;
line = line.trimmed();
- if (line.size()) {
+ if (!line.empty()) {
if (value.size())
- value += ' ' + line.toByteArray();
+ value += ' ' + line;
else
value = line.toByteArray();
}
header = header.sliced(endLine + 1);
} while (hSpaceStart(header));
- Q_ASSERT(name.size() + 1 + value.size() <= MAX_HEADER_FIELD_SIZE);
- result.append(qMakePair(name.toByteArray(), value));
+ Q_ASSERT(name.size() + 1 + value.size() <= maxFieldSize);
+ result.append(name, value);
}
fields = result;
@@ -141,7 +102,7 @@ bool QHttpHeaderParser::parseStatus(QByteArrayView status)
static const int spacePos = 8;
static const char httpMagic[] = "HTTP/";
- if (status.length() < minLength
+ if (status.size() < minLength
|| !status.startsWith(httpMagic)
|| status.at(dotPos) != '.'
|| status.at(spacePos) != ' ') {
@@ -154,11 +115,11 @@ bool QHttpHeaderParser::parseStatus(QByteArrayView status)
minorVersion = status.at(dotPos + 1) - '0';
int i = spacePos;
- int j = status.indexOf(' ', i + 1);
+ qsizetype j = status.indexOf(' ', i + 1);
const QByteArrayView code = j > i ? status.sliced(i + 1, j - i - 1)
: status.sliced(i + 1);
- bool ok;
+ bool ok = false;
statusCode = code.toInt(&ok);
reasonPhrase = j > i ? QString::fromLatin1(status.sliced(j + 1))
@@ -167,62 +128,49 @@ bool QHttpHeaderParser::parseStatus(QByteArrayView status)
return ok && uint(majorVersion) <= 9 && uint(minorVersion) <= 9;
}
-const QList<QPair<QByteArray, QByteArray> >& QHttpHeaderParser::headers() const
+const QHttpHeaders& QHttpHeaderParser::headers() const
{
return fields;
}
-QByteArray QHttpHeaderParser::firstHeaderField(const QByteArray &name,
+QByteArray QHttpHeaderParser::firstHeaderField(QByteArrayView name,
const QByteArray &defaultValue) const
{
- for (auto it = fields.constBegin(); it != fields.constEnd(); ++it) {
- if (name.compare(it->first, Qt::CaseInsensitive) == 0)
- return it->second;
- }
- return defaultValue;
+ return fields.value(name, defaultValue).toByteArray();
}
-QByteArray QHttpHeaderParser::combinedHeaderValue(const QByteArray &name, const QByteArray &defaultValue) const
+QByteArray QHttpHeaderParser::combinedHeaderValue(QByteArrayView name, const QByteArray &defaultValue) const
{
const QList<QByteArray> allValues = headerFieldValues(name);
if (allValues.isEmpty())
return defaultValue;
- else
- return allValues.join(", ");
+ return allValues.join(", ");
}
-QList<QByteArray> QHttpHeaderParser::headerFieldValues(const QByteArray &name) const
+QList<QByteArray> QHttpHeaderParser::headerFieldValues(QByteArrayView name) const
{
- QList<QByteArray> result;
- for (auto it = fields.constBegin(); it != fields.constEnd(); ++it)
- if (name.compare(it->first, Qt::CaseInsensitive) == 0)
- result += it->second;
-
- return result;
+ return fields.values(name);
}
-void QHttpHeaderParser::removeHeaderField(const QByteArray &name)
+void QHttpHeaderParser::removeHeaderField(QByteArrayView name)
{
- auto firstEqualsName = [&name](const QPair<QByteArray, QByteArray> &header) {
- return name.compare(header.first, Qt::CaseInsensitive) == 0;
- };
- fields.removeIf(firstEqualsName);
+ fields.removeAll(name);
}
void QHttpHeaderParser::setHeaderField(const QByteArray &name, const QByteArray &data)
{
removeHeaderField(name);
- fields.append(qMakePair(name, data));
+ fields.append(name, data);
}
void QHttpHeaderParser::prependHeaderField(const QByteArray &name, const QByteArray &data)
{
- fields.prepend(qMakePair(name, data));
+ fields.insert(0, name, data);
}
void QHttpHeaderParser::appendHeaderField(const QByteArray &name, const QByteArray &data)
{
- fields.append(qMakePair(name, data));
+ fields.append(name, data);
}
void QHttpHeaderParser::clearHeaders()
diff --git a/src/network/access/qhttpheaderparser_p.h b/src/network/access/qhttpheaderparser_p.h
index cc8b91c84f..5e8f3c8130 100644
--- a/src/network/access/qhttpheaderparser_p.h
+++ b/src/network/access/qhttpheaderparser_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2021 The Qt Company Ltd.
-** 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) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QHTTPHEADERPARSER_H
#define QHTTPHEADERPARSER_H
@@ -52,6 +16,7 @@
//
#include <QtNetwork/private/qtnetworkglobal_p.h>
+#include <QtNetwork/qhttpheaders.h>
#include <QByteArray>
#include <QList>
@@ -60,7 +25,26 @@
QT_BEGIN_NAMESPACE
-class Q_NETWORK_PRIVATE_EXPORT QHttpHeaderParser
+namespace HeaderConstants {
+
+// We previously used 8K, which is common on server side, but it turned out to
+// not be enough for various uses. Historically Firefox used 10K as the limit of
+// a single field, but some Location headers and Authorization challenges can
+// get even longer. Other browsers, such as Chrome, instead have a limit on the
+// total size of all the headers (as well as extra limits on some of the
+// individual fields). We'll use 100K as our default limit, which would be a ridiculously large
+// header, with the possibility to override it where we need to.
+static constexpr int MAX_HEADER_FIELD_SIZE = 100 * 1024;
+// Taken from http://httpd.apache.org/docs/2.2/mod/core.html#limitrequestfields
+static constexpr int MAX_HEADER_FIELDS = 100;
+// Chromium has a limit on the total size of the header set to 256KB,
+// which is a reasonable default for QNetworkAccessManager.
+// https://stackoverflow.com/a/3436155
+static constexpr int MAX_TOTAL_HEADER_SIZE = 256 * 1024;
+
+}
+
+class Q_NETWORK_EXPORT QHttpHeaderParser
{
public:
QHttpHeaderParser();
@@ -69,7 +53,7 @@ public:
bool parseHeaders(QByteArrayView headers);
bool parseStatus(QByteArrayView status);
- const QList<QPair<QByteArray, QByteArray> >& headers() const;
+ const QHttpHeaders& headers() const;
void setStatusCode(int code);
int getStatusCode() const;
int getMajorVersion() const;
@@ -79,23 +63,36 @@ public:
QString getReasonPhrase() const;
void setReasonPhrase(const QString &reason);
- QByteArray firstHeaderField(const QByteArray &name,
+ QByteArray firstHeaderField(QByteArrayView name,
const QByteArray &defaultValue = QByteArray()) const;
- QByteArray combinedHeaderValue(const QByteArray &name,
+ QByteArray combinedHeaderValue(QByteArrayView name,
const QByteArray &defaultValue = QByteArray()) const;
- QList<QByteArray> headerFieldValues(const QByteArray &name) const;
+ QList<QByteArray> headerFieldValues(QByteArrayView name) const;
void setHeaderField(const QByteArray &name, const QByteArray &data);
void prependHeaderField(const QByteArray &name, const QByteArray &data);
void appendHeaderField(const QByteArray &name, const QByteArray &data);
- void removeHeaderField(const QByteArray &name);
+ void removeHeaderField(QByteArrayView name);
void clearHeaders();
+ void setMaxHeaderFieldSize(qsizetype size) { maxFieldSize = size; }
+ qsizetype maxHeaderFieldSize() const { return maxFieldSize; }
+
+ void setMaxTotalHeaderSize(qsizetype size) { maxTotalSize = size; }
+ qsizetype maxTotalHeaderSize() const { return maxTotalSize; }
+
+ void setMaxHeaderFields(qsizetype count) { maxFieldCount = count; }
+ qsizetype maxHeaderFields() const { return maxFieldCount; }
+
private:
- QList<QPair<QByteArray, QByteArray> > fields;
+ QHttpHeaders fields;
QString reasonPhrase;
int statusCode;
int majorVersion;
int minorVersion;
+
+ qsizetype maxFieldSize = HeaderConstants::MAX_HEADER_FIELD_SIZE;
+ qsizetype maxTotalSize = HeaderConstants::MAX_TOTAL_HEADER_SIZE;
+ qsizetype maxFieldCount = HeaderConstants::MAX_HEADER_FIELDS;
};
diff --git a/src/network/access/qhttpheaders.cpp b/src/network/access/qhttpheaders.cpp
new file mode 100644
index 0000000000..c63da899a8
--- /dev/null
+++ b/src/network/access/qhttpheaders.cpp
@@ -0,0 +1,1551 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qhttpheaders.h"
+
+#include <private/qoffsetstringarray_p.h>
+
+#include <QtCore/qcompare.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qset.h>
+#include <QtCore/qttypetraits.h>
+
+#include <q20algorithm.h>
+#include <string_view>
+#include <variant>
+
+QT_BEGIN_NAMESPACE
+
+Q_LOGGING_CATEGORY(lcQHttpHeaders, "qt.network.http.headers");
+
+/*!
+ \class QHttpHeaders
+ \since 6.7
+ \ingroup
+ \inmodule QtNetwork
+
+ \brief QHttpHeaders is a class for holding HTTP headers.
+
+ The class is an interface type for Qt networking APIs that
+ use or consume such headers.
+
+ \section1 Allowed field name and value characters
+
+ An HTTP header consists of \e name and \e value.
+ When setting these, QHttpHeaders validates \e name and \e value
+ to only contain characters allowed by the HTTP RFCs. For detailed
+ information see
+ \l {https://datatracker.ietf.org/doc/html/rfc9110#name-field-values}
+ {RFC 9110 Chapters 5.1 and 5.5}.
+
+ In all, this means:
+ \list
+ \li \c name must consist of visible ASCII characters, and must not be
+ empty
+ \li \c value may consist of arbitrary bytes, as long as header
+ and use case specific encoding rules are adhered to. \c value
+ may be empty
+ \endlist
+
+ The setters of this class automatically remove any leading or trailing
+ whitespaces from \e value, as they must be ignored during the
+ \e value processing.
+
+ \section1 Combining values
+
+ Most HTTP header values can be combined with a single comma \c {','}
+ plus an optional whitespace, and the semantic meaning is preserved.
+ As an example, these two should be semantically similar:
+ \badcode
+ // Values as separate header entries
+ myheadername: myheadervalue1
+ myheadername: myheadervalue2
+ // Combined value
+ myheadername: myheadervalue1, myheadervalue2
+ \endcode
+
+ However, there is a notable exception to this rule:
+ \l {https://datatracker.ietf.org/doc/html/rfc9110#name-field-order}
+ {Set-Cookie}. Due to this and the possibility of custom use cases,
+ QHttpHeaders does not automatically combine the values.
+
+ \section1 Performance
+
+ Most QHttpHeaders functions provide both
+ \l QHttpHeaders::WellKnownHeader and \l QAnyStringView overloads.
+ From a memory-usage and computation point of view it is recommended
+ to use the \l QHttpHeaders::WellKnownHeader overloads.
+*/
+
+// This list is from IANA HTTP Field Name Registry
+// https://www.iana.org/assignments/http-fields
+// It contains entries that are either "permanent"
+// or "deprecated" as of October 2023.
+// Usage relies on enum values keeping in same order.
+// ### Qt7 check if some of these headers have been obsoleted,
+// and also check if the enums benefit from reordering
+static constexpr auto headerNames = qOffsetStringArray(
+ // IANA Permanent status:
+ "a-im",
+ "accept",
+ "accept-additions",
+ "accept-ch",
+ "accept-datetime",
+ "accept-encoding",
+ "accept-features",
+ "accept-language",
+ "accept-patch",
+ "accept-post",
+ "accept-ranges",
+ "accept-signature",
+ "access-control-allow-credentials",
+ "access-control-allow-headers",
+ "access-control-allow-methods",
+ "access-control-allow-origin",
+ "access-control-expose-headers",
+ "access-control-max-age",
+ "access-control-request-headers",
+ "access-control-request-method",
+ "age",
+ "allow",
+ "alpn",
+ "alt-svc",
+ "alt-used",
+ "alternates",
+ "apply-to-redirect-ref",
+ "authentication-control",
+ "authentication-info",
+ "authorization",
+ "cache-control",
+ "cache-status",
+ "cal-managed-id",
+ "caldav-timezones",
+ "capsule-protocol",
+ "cdn-cache-control",
+ "cdn-loop",
+ "cert-not-after",
+ "cert-not-before",
+ "clear-site-data",
+ "client-cert",
+ "client-cert-chain",
+ "close",
+ "connection",
+ "content-digest",
+ "content-disposition",
+ "content-encoding",
+ "content-id",
+ "content-language",
+ "content-length",
+ "content-location",
+ "content-range",
+ "content-security-policy",
+ "content-security-policy-report-only",
+ "content-type",
+ "cookie",
+ "cross-origin-embedder-policy",
+ "cross-origin-embedder-policy-report-only",
+ "cross-origin-opener-policy",
+ "cross-origin-opener-policy-report-only",
+ "cross-origin-resource-policy",
+ "dasl",
+ "date",
+ "dav",
+ "delta-base",
+ "depth",
+ "destination",
+ "differential-id",
+ "dpop",
+ "dpop-nonce",
+ "early-data",
+ "etag",
+ "expect",
+ "expect-ct",
+ "expires",
+ "forwarded",
+ "from",
+ "hobareg",
+ "host",
+ "if",
+ "if-match",
+ "if-modified-since",
+ "if-none-match",
+ "if-range",
+ "if-schedule-tag-match",
+ "if-unmodified-since",
+ "im",
+ "include-referred-token-binding-id",
+ "keep-alive",
+ "label",
+ "last-event-id",
+ "last-modified",
+ "link",
+ "location",
+ "lock-token",
+ "max-forwards",
+ "memento-datetime",
+ "meter",
+ "mime-version",
+ "negotiate",
+ "nel",
+ "odata-entityid",
+ "odata-isolation",
+ "odata-maxversion",
+ "odata-version",
+ "optional-www-authenticate",
+ "ordering-type",
+ "origin",
+ "origin-agent-cluster",
+ "oscore",
+ "oslc-core-version",
+ "overwrite",
+ "ping-from",
+ "ping-to",
+ "position",
+ "prefer",
+ "preference-applied",
+ "priority",
+ "proxy-authenticate",
+ "proxy-authentication-info",
+ "proxy-authorization",
+ "proxy-status",
+ "public-key-pins",
+ "public-key-pins-report-only",
+ "range",
+ "redirect-ref",
+ "referer",
+ "refresh",
+ "replay-nonce",
+ "repr-digest",
+ "retry-after",
+ "schedule-reply",
+ "schedule-tag",
+ "sec-purpose",
+ "sec-token-binding",
+ "sec-websocket-accept",
+ "sec-websocket-extensions",
+ "sec-websocket-key",
+ "sec-websocket-protocol",
+ "sec-websocket-version",
+ "server",
+ "server-timing",
+ "set-cookie",
+ "signature",
+ "signature-input",
+ "slug",
+ "soapaction",
+ "status-uri",
+ "strict-transport-security",
+ "sunset",
+ "surrogate-capability",
+ "surrogate-control",
+ "tcn",
+ "te",
+ "timeout",
+ "topic",
+ "traceparent",
+ "tracestate",
+ "trailer",
+ "transfer-encoding",
+ "ttl",
+ "upgrade",
+ "urgency",
+ "user-agent",
+ "variant-vary",
+ "vary",
+ "via",
+ "want-content-digest",
+ "want-repr-digest",
+ "www-authenticate",
+ "x-content-type-options",
+ "x-frame-options",
+ // IANA Deprecated status:
+ "accept-charset",
+ "c-pep-info",
+ "pragma",
+ "protocol-info",
+ "protocol-query"
+ // If you append here, regenerate the index table
+);
+
+namespace {
+struct ByIndirectHeaderName
+{
+ constexpr bool operator()(quint8 lhs, quint8 rhs) const noexcept
+ {
+ return (*this)(map(lhs), map(rhs));
+ }
+ constexpr bool operator()(quint8 lhs, QByteArrayView rhs) const noexcept
+ {
+ return (*this)(map(lhs), rhs);
+ }
+ constexpr bool operator()(QByteArrayView lhs, quint8 rhs) const noexcept
+ {
+ return (*this)(lhs, map(rhs));
+ }
+ constexpr bool operator()(QByteArrayView lhs, QByteArrayView rhs) const noexcept
+ {
+ // ### just `lhs < rhs` when QByteArrayView relational operators are constexpr
+ return std::string_view(lhs) < std::string_view(rhs);
+ }
+private:
+ static constexpr QByteArrayView map(quint8 i) noexcept
+ {
+ return headerNames.viewAt(i);
+ }
+};
+} // unnamed namespace
+
+// This index table contains the indexes of 'headerNames' entries (above) in alphabetical order.
+// This allows a more efficient binary search for the names [O(logN)]. The 'headerNames' itself
+// cannot be guaranteed to be in alphabetical order, as it must keep the same order as the
+// WellKnownHeader enum, which may get appended over time.
+//
+// Note: when appending new enums, this must be regenerated
+static constexpr quint8 orderedHeaderNameIndexes[] = {
+ 0, // a-im
+ 1, // accept
+ 2, // accept-additions
+ 3, // accept-ch
+ 172, // accept-charset
+ 4, // accept-datetime
+ 5, // accept-encoding
+ 6, // accept-features
+ 7, // accept-language
+ 8, // accept-patch
+ 9, // accept-post
+ 10, // accept-ranges
+ 11, // accept-signature
+ 12, // access-control-allow-credentials
+ 13, // access-control-allow-headers
+ 14, // access-control-allow-methods
+ 15, // access-control-allow-origin
+ 16, // access-control-expose-headers
+ 17, // access-control-max-age
+ 18, // access-control-request-headers
+ 19, // access-control-request-method
+ 20, // age
+ 21, // allow
+ 22, // alpn
+ 23, // alt-svc
+ 24, // alt-used
+ 25, // alternates
+ 26, // apply-to-redirect-ref
+ 27, // authentication-control
+ 28, // authentication-info
+ 29, // authorization
+ 173, // c-pep-info
+ 30, // cache-control
+ 31, // cache-status
+ 32, // cal-managed-id
+ 33, // caldav-timezones
+ 34, // capsule-protocol
+ 35, // cdn-cache-control
+ 36, // cdn-loop
+ 37, // cert-not-after
+ 38, // cert-not-before
+ 39, // clear-site-data
+ 40, // client-cert
+ 41, // client-cert-chain
+ 42, // close
+ 43, // connection
+ 44, // content-digest
+ 45, // content-disposition
+ 46, // content-encoding
+ 47, // content-id
+ 48, // content-language
+ 49, // content-length
+ 50, // content-location
+ 51, // content-range
+ 52, // content-security-policy
+ 53, // content-security-policy-report-only
+ 54, // content-type
+ 55, // cookie
+ 56, // cross-origin-embedder-policy
+ 57, // cross-origin-embedder-policy-report-only
+ 58, // cross-origin-opener-policy
+ 59, // cross-origin-opener-policy-report-only
+ 60, // cross-origin-resource-policy
+ 61, // dasl
+ 62, // date
+ 63, // dav
+ 64, // delta-base
+ 65, // depth
+ 66, // destination
+ 67, // differential-id
+ 68, // dpop
+ 69, // dpop-nonce
+ 70, // early-data
+ 71, // etag
+ 72, // expect
+ 73, // expect-ct
+ 74, // expires
+ 75, // forwarded
+ 76, // from
+ 77, // hobareg
+ 78, // host
+ 79, // if
+ 80, // if-match
+ 81, // if-modified-since
+ 82, // if-none-match
+ 83, // if-range
+ 84, // if-schedule-tag-match
+ 85, // if-unmodified-since
+ 86, // im
+ 87, // include-referred-token-binding-id
+ 88, // keep-alive
+ 89, // label
+ 90, // last-event-id
+ 91, // last-modified
+ 92, // link
+ 93, // location
+ 94, // lock-token
+ 95, // max-forwards
+ 96, // memento-datetime
+ 97, // meter
+ 98, // mime-version
+ 99, // negotiate
+ 100, // nel
+ 101, // odata-entityid
+ 102, // odata-isolation
+ 103, // odata-maxversion
+ 104, // odata-version
+ 105, // optional-www-authenticate
+ 106, // ordering-type
+ 107, // origin
+ 108, // origin-agent-cluster
+ 109, // oscore
+ 110, // oslc-core-version
+ 111, // overwrite
+ 112, // ping-from
+ 113, // ping-to
+ 114, // position
+ 174, // pragma
+ 115, // prefer
+ 116, // preference-applied
+ 117, // priority
+ 175, // protocol-info
+ 176, // protocol-query
+ 118, // proxy-authenticate
+ 119, // proxy-authentication-info
+ 120, // proxy-authorization
+ 121, // proxy-status
+ 122, // public-key-pins
+ 123, // public-key-pins-report-only
+ 124, // range
+ 125, // redirect-ref
+ 126, // referer
+ 127, // refresh
+ 128, // replay-nonce
+ 129, // repr-digest
+ 130, // retry-after
+ 131, // schedule-reply
+ 132, // schedule-tag
+ 133, // sec-purpose
+ 134, // sec-token-binding
+ 135, // sec-websocket-accept
+ 136, // sec-websocket-extensions
+ 137, // sec-websocket-key
+ 138, // sec-websocket-protocol
+ 139, // sec-websocket-version
+ 140, // server
+ 141, // server-timing
+ 142, // set-cookie
+ 143, // signature
+ 144, // signature-input
+ 145, // slug
+ 146, // soapaction
+ 147, // status-uri
+ 148, // strict-transport-security
+ 149, // sunset
+ 150, // surrogate-capability
+ 151, // surrogate-control
+ 152, // tcn
+ 153, // te
+ 154, // timeout
+ 155, // topic
+ 156, // traceparent
+ 157, // tracestate
+ 158, // trailer
+ 159, // transfer-encoding
+ 160, // ttl
+ 161, // upgrade
+ 162, // urgency
+ 163, // user-agent
+ 164, // variant-vary
+ 165, // vary
+ 166, // via
+ 167, // want-content-digest
+ 168, // want-repr-digest
+ 169, // www-authenticate
+ 170, // x-content-type-options
+ 171, // x-frame-options
+};
+static_assert(std::size(orderedHeaderNameIndexes) == size_t(headerNames.count()));
+static_assert(q20::is_sorted(std::begin(orderedHeaderNameIndexes),
+ std::end(orderedHeaderNameIndexes),
+ ByIndirectHeaderName{}));
+
+/*!
+ \enum QHttpHeaders::WellKnownHeader
+
+ List of well known headers as per
+ \l {https://www.iana.org/assignments/http-fields}{IANA registry}.
+
+ \value AIM
+ \value Accept
+ \value AcceptAdditions
+ \value AcceptCH
+ \value AcceptDatetime
+ \value AcceptEncoding
+ \value AcceptFeatures
+ \value AcceptLanguage
+ \value AcceptPatch
+ \value AcceptPost
+ \value AcceptRanges
+ \value AcceptSignature
+ \value AccessControlAllowCredentials
+ \value AccessControlAllowHeaders
+ \value AccessControlAllowMethods
+ \value AccessControlAllowOrigin
+ \value AccessControlExposeHeaders
+ \value AccessControlMaxAge
+ \value AccessControlRequestHeaders
+ \value AccessControlRequestMethod
+ \value Age
+ \value Allow
+ \value ALPN
+ \value AltSvc
+ \value AltUsed
+ \value Alternates
+ \value ApplyToRedirectRef
+ \value AuthenticationControl
+ \value AuthenticationInfo
+ \value Authorization
+ \value CacheControl
+ \value CacheStatus
+ \value CalManagedID
+ \value CalDAVTimezones
+ \value CapsuleProtocol
+ \value CDNCacheControl
+ \value CDNLoop
+ \value CertNotAfter
+ \value CertNotBefore
+ \value ClearSiteData
+ \value ClientCert
+ \value ClientCertChain
+ \value Close
+ \value Connection
+ \value ContentDigest
+ \value ContentDisposition
+ \value ContentEncoding
+ \value ContentID
+ \value ContentLanguage
+ \value ContentLength
+ \value ContentLocation
+ \value ContentRange
+ \value ContentSecurityPolicy
+ \value ContentSecurityPolicyReportOnly
+ \value ContentType
+ \value Cookie
+ \value CrossOriginEmbedderPolicy
+ \value CrossOriginEmbedderPolicyReportOnly
+ \value CrossOriginOpenerPolicy
+ \value CrossOriginOpenerPolicyReportOnly
+ \value CrossOriginResourcePolicy
+ \value DASL
+ \value Date
+ \value DAV
+ \value DeltaBase
+ \value Depth
+ \value Destination
+ \value DifferentialID
+ \value DPoP
+ \value DPoPNonce
+ \value EarlyData
+ \value ETag
+ \value Expect
+ \value ExpectCT
+ \value Expires
+ \value Forwarded
+ \value From
+ \value Hobareg
+ \value Host
+ \value If
+ \value IfMatch
+ \value IfModifiedSince
+ \value IfNoneMatch
+ \value IfRange
+ \value IfScheduleTagMatch
+ \value IfUnmodifiedSince
+ \value IM
+ \value IncludeReferredTokenBindingID
+ \value KeepAlive
+ \value Label
+ \value LastEventID
+ \value LastModified
+ \value Link
+ \value Location
+ \value LockToken
+ \value MaxForwards
+ \value MementoDatetime
+ \value Meter
+ \value MIMEVersion
+ \value Negotiate
+ \value NEL
+ \value ODataEntityId
+ \value ODataIsolation
+ \value ODataMaxVersion
+ \value ODataVersion
+ \value OptionalWWWAuthenticate
+ \value OrderingType
+ \value Origin
+ \value OriginAgentCluster
+ \value OSCORE
+ \value OSLCCoreVersion
+ \value Overwrite
+ \value PingFrom
+ \value PingTo
+ \value Position
+ \value Prefer
+ \value PreferenceApplied
+ \value Priority
+ \value ProxyAuthenticate
+ \value ProxyAuthenticationInfo
+ \value ProxyAuthorization
+ \value ProxyStatus
+ \value PublicKeyPins
+ \value PublicKeyPinsReportOnly
+ \value Range
+ \value RedirectRef
+ \value Referer
+ \value Refresh
+ \value ReplayNonce
+ \value ReprDigest
+ \value RetryAfter
+ \value ScheduleReply
+ \value ScheduleTag
+ \value SecPurpose
+ \value SecTokenBinding
+ \value SecWebSocketAccept
+ \value SecWebSocketExtensions
+ \value SecWebSocketKey
+ \value SecWebSocketProtocol
+ \value SecWebSocketVersion
+ \value Server
+ \value ServerTiming
+ \value SetCookie
+ \value Signature
+ \value SignatureInput
+ \value SLUG
+ \value SoapAction
+ \value StatusURI
+ \value StrictTransportSecurity
+ \value Sunset
+ \value SurrogateCapability
+ \value SurrogateControl
+ \value TCN
+ \value TE
+ \value Timeout
+ \value Topic
+ \value Traceparent
+ \value Tracestate
+ \value Trailer
+ \value TransferEncoding
+ \value TTL
+ \value Upgrade
+ \value Urgency
+ \value UserAgent
+ \value VariantVary
+ \value Vary
+ \value Via
+ \value WantContentDigest
+ \value WantReprDigest
+ \value WWWAuthenticate
+ \value XContentTypeOptions
+ \value XFrameOptions
+ \value AcceptCharset
+ \value CPEPInfo
+ \value Pragma
+ \value ProtocolInfo
+ \value ProtocolQuery
+*/
+
+static QByteArray fieldToByteArray(QLatin1StringView s) noexcept
+{
+ return QByteArray(s.data(), s.size());
+}
+
+static QByteArray fieldToByteArray(QUtf8StringView s) noexcept
+{
+ return QByteArray(s.data(), s.size());
+}
+
+static QByteArray fieldToByteArray(QStringView s)
+{
+ return s.toLatin1();
+}
+
+static QByteArray normalizedName(QAnyStringView name)
+{
+ return name.visit([](auto name){ return fieldToByteArray(name); }).toLower();
+}
+
+struct HeaderName
+{
+ explicit HeaderName(QHttpHeaders::WellKnownHeader name) : data(name)
+ {
+ }
+
+ explicit HeaderName(QAnyStringView name)
+ {
+ auto nname = normalizedName(name);
+ if (auto h = HeaderName::toWellKnownHeader(nname))
+ data = *h;
+ else
+ data = std::move(nname);
+ }
+
+ // Returns an enum corresponding with the 'name' if possible. Uses binary search (O(logN)).
+ // The function doesn't normalize the data; needs to be done by the caller if needed
+ static std::optional<QHttpHeaders::WellKnownHeader> toWellKnownHeader(QByteArrayView name) noexcept
+ {
+ auto indexesBegin = std::cbegin(orderedHeaderNameIndexes);
+ auto indexesEnd = std::cend(orderedHeaderNameIndexes);
+
+ auto result = std::lower_bound(indexesBegin, indexesEnd, name, ByIndirectHeaderName{});
+
+ if (result != indexesEnd && name == headerNames[*result])
+ return static_cast<QHttpHeaders::WellKnownHeader>(*result);
+ return std::nullopt;
+ }
+
+ QByteArrayView asView() const noexcept
+ {
+ return std::visit([](const auto &arg) -> QByteArrayView {
+ using T = decltype(arg);
+ if constexpr (std::is_same_v<T, const QByteArray &>)
+ return arg;
+ else if constexpr (std::is_same_v<T, const QHttpHeaders::WellKnownHeader &>)
+ return headerNames.viewAt(qToUnderlying(arg));
+ else
+ static_assert(QtPrivate::type_dependent_false<T>());
+ }, data);
+ }
+
+ QByteArray asByteArray() const noexcept
+ {
+ return std::visit([](const auto &arg) -> QByteArray {
+ using T = decltype(arg);
+ if constexpr (std::is_same_v<T, const QByteArray &>) {
+ return arg;
+ } else if constexpr (std::is_same_v<T, const QHttpHeaders::WellKnownHeader &>) {
+ const auto view = headerNames.viewAt(qToUnderlying(arg));
+ return QByteArray::fromRawData(view.constData(), view.size());
+ } else {
+ static_assert(QtPrivate::type_dependent_false<T>());
+ }
+ }, data);
+ }
+
+private:
+ // Store the data as 'enum' whenever possible; more performant, and comparison relies on that
+ std::variant<QHttpHeaders::WellKnownHeader, QByteArray> data;
+
+ friend bool comparesEqual(const HeaderName &lhs, const HeaderName &rhs) noexcept
+ {
+ // Here we compare two std::variants, which will return false if the types don't match.
+ // That is beneficial here because we avoid unnecessary comparisons; but it also means
+ // we must always store the data as WellKnownHeader when possible (in other words, if
+ // we get a string that is mappable to a WellKnownHeader). To guard against accidental
+ // misuse, the 'data' is private and the constructors must be used.
+ return lhs.data == rhs.data;
+ }
+ Q_DECLARE_EQUALITY_COMPARABLE(HeaderName)
+};
+
+// A clarification on case-sensitivity:
+// - Header *names* are case-insensitive; Content-Type and content-type are considered equal
+// - Header *values* are case-sensitive
+// (In addition, the HTTP/2 and HTTP/3 standards mandate that all headers must be lower-cased when
+// encoded into transmission)
+struct Header {
+ HeaderName name;
+ QByteArray value;
+};
+
+auto headerNameMatches(const HeaderName &name)
+{
+ return [&name](const Header &header) { return header.name == name; };
+}
+
+class QHttpHeadersPrivate : public QSharedData
+{
+public:
+ QHttpHeadersPrivate() = default;
+
+ // The 'Self' is supplied as parameter to static functions so that
+ // we can define common methods which 'detach()' the private itself.
+ using Self = QExplicitlySharedDataPointer<QHttpHeadersPrivate>;
+ static void removeAll(Self &d, const HeaderName &name);
+ static void replaceOrAppend(Self &d, const HeaderName &name, const QByteArray &value);
+
+ void combinedValue(const HeaderName &name, QByteArray &result) const;
+ void values(const HeaderName &name, QList<QByteArray> &result) const;
+ QByteArrayView value(const HeaderName &name, QByteArrayView defaultValue) const noexcept;
+
+ QList<Header> headers;
+};
+
+QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QHttpHeadersPrivate)
+template <> void QExplicitlySharedDataPointer<QHttpHeadersPrivate>::detach()
+{
+ if (!d) {
+ d = new QHttpHeadersPrivate();
+ d->ref.ref();
+ } else if (d->ref.loadRelaxed() != 1) {
+ detach_helper();
+ }
+}
+
+void QHttpHeadersPrivate::removeAll(Self &d, const HeaderName &name)
+{
+ const auto it = std::find_if(d->headers.cbegin(), d->headers.cend(), headerNameMatches(name));
+
+ if (it != d->headers.cend()) {
+ // Found something to remove, calculate offset so we can proceed from the match-location
+ const auto matchOffset = it - d->headers.cbegin();
+ d.detach();
+ // Rearrange all matches to the end and erase them
+ d->headers.erase(std::remove_if(d->headers.begin() + matchOffset, d->headers.end(),
+ headerNameMatches(name)),
+ d->headers.end());
+ }
+}
+
+void QHttpHeadersPrivate::combinedValue(const HeaderName &name, QByteArray &result) const
+{
+ const char* separator = "";
+ for (const auto &h : std::as_const(headers)) {
+ if (h.name == name) {
+ result.append(separator);
+ result.append(h.value);
+ separator = ", ";
+ }
+ }
+}
+
+void QHttpHeadersPrivate::values(const HeaderName &name, QList<QByteArray> &result) const
+{
+ for (const auto &h : std::as_const(headers)) {
+ if (h.name == name)
+ result.append(h.value);
+ }
+}
+
+QByteArrayView QHttpHeadersPrivate::value(const HeaderName &name, QByteArrayView defaultValue) const noexcept
+{
+ for (const auto &h : std::as_const(headers)) {
+ if (h.name == name)
+ return h.value;
+ }
+ return defaultValue;
+}
+
+void QHttpHeadersPrivate::replaceOrAppend(Self &d, const HeaderName &name, const QByteArray &value)
+{
+ d.detach();
+ auto it = std::find_if(d->headers.begin(), d->headers.end(), headerNameMatches(name));
+ if (it != d->headers.end()) {
+ // Found something to replace => replace, and then rearrange any remaining
+ // matches to the end and erase them
+ it->value = value;
+ d->headers.erase(
+ std::remove_if(it + 1, d->headers.end(), headerNameMatches(name)),
+ d->headers.end());
+ } else {
+ // Found nothing to replace => append
+ d->headers.append(Header{name, value});
+ }
+}
+
+/*!
+ Creates a new QHttpHeaders object.
+*/
+QHttpHeaders::QHttpHeaders() noexcept : d()
+{
+}
+
+/*!
+ Creates a new QHttpHeaders object that is populated with
+ \a headers.
+
+ \sa {Allowed field name and value characters}
+*/
+QHttpHeaders QHttpHeaders::fromListOfPairs(const QList<std::pair<QByteArray, QByteArray>> &headers)
+{
+ QHttpHeaders h;
+ h.reserve(headers.size());
+ for (const auto &header : headers)
+ h.append(header.first, header.second);
+ return h;
+}
+
+/*!
+ Creates a new QHttpHeaders object that is populated with
+ \a headers.
+
+ \sa {Allowed field name and value characters}
+*/
+QHttpHeaders QHttpHeaders::fromMultiMap(const QMultiMap<QByteArray, QByteArray> &headers)
+{
+ QHttpHeaders h;
+ h.reserve(headers.size());
+ for (const auto &[name,value] : headers.asKeyValueRange())
+ h.append(name, value);
+ return h;
+}
+
+/*!
+ Creates a new QHttpHeaders object that is populated with
+ \a headers.
+
+ \sa {Allowed field name and value characters}
+*/
+QHttpHeaders QHttpHeaders::fromMultiHash(const QMultiHash<QByteArray, QByteArray> &headers)
+{
+ QHttpHeaders h;
+ h.reserve(headers.size());
+ for (const auto &[name,value] : headers.asKeyValueRange())
+ h.append(name, value);
+ return h;
+}
+
+/*!
+ Disposes of the headers object.
+*/
+QHttpHeaders::~QHttpHeaders()
+ = default;
+
+/*!
+ Creates a copy of \a other.
+*/
+QHttpHeaders::QHttpHeaders(const QHttpHeaders &other)
+ = default;
+
+/*!
+ Assigns the contents of \a other and returns a reference to this object.
+*/
+QHttpHeaders &QHttpHeaders::operator=(const QHttpHeaders &other)
+ = default;
+
+/*!
+ \fn QHttpHeaders::QHttpHeaders(QHttpHeaders &&other) noexcept
+
+ Move-constructs the object from \a other, which will be left
+ \l{isEmpty()}{empty}.
+*/
+
+/*!
+ \fn QHttpHeaders &QHttpHeaders::operator=(QHttpHeaders &&other) noexcept
+
+ Move-assigns \a other and returns a reference to this object.
+
+ \a other will be left \l{isEmpty()}{empty}.
+*/
+
+/*!
+ \fn void QHttpHeaders::swap(QHttpHeaders &other)
+
+ Swaps this QHttpHeaders with \a other. This function is very fast and
+ never fails.
+*/
+
+#ifndef QT_NO_DEBUG_STREAM
+/*!
+ \fn QDebug QHttpHeaders::operator<<(QDebug debug,
+ const QHttpHeaders &headers)
+
+ Writes \a headers into \a debug stream.
+*/
+QDebug operator<<(QDebug debug, const QHttpHeaders &headers)
+{
+ const QDebugStateSaver saver(debug);
+ debug.resetFormat().nospace();
+
+ debug << "QHttpHeaders(";
+ if (headers.d) {
+ debug << "headers = ";
+ const char *separator = "";
+ for (const auto &h : headers.d->headers) {
+ debug << separator << h.name.asView() << ':' << h.value;
+ separator = " | ";
+ }
+ }
+ debug << ")";
+ return debug;
+}
+#endif
+
+// A clarification on string encoding:
+// Setters and getters only accept names and values that are Latin-1 representable:
+// Either they are directly ASCII/Latin-1, or if they are UTF-X, they only use first 256
+// of the unicode points. For example using a '€' (U+20AC) in value would yield a warning
+// and the call is ignored.
+// Furthermore the 'name' has more strict rules than the 'value'
+
+// TODO FIXME REMOVEME once this is merged:
+// https://codereview.qt-project.org/c/qt/qtbase/+/508829
+static bool isUtf8Latin1Representable(QUtf8StringView s) noexcept
+{
+ // L1 encoded in UTF8 has at most the form
+ // - 0b0XXX'XXXX - US-ASCII
+ // - 0b1100'00XX 0b10XX'XXXX - at most 8 non-zero LSB bits allowed in L1
+ bool inMultibyte = false;
+ for (unsigned char c : s) {
+ if (c < 128) { // US-ASCII
+ if (inMultibyte)
+ return false; // invalid sequence
+ } else {
+ // decode as UTF-8:
+ if ((c & 0b1110'0000) == 0b1100'0000) { // two-octet UTF-8 leader
+ if (inMultibyte)
+ return false; // invalid sequence
+ inMultibyte = true;
+ const auto bits_7_to_11 = c & 0b0001'1111;
+ if (bits_7_to_11 < 0b10)
+ return false; // invalid sequence (US-ASCII encoded in two octets)
+ if (bits_7_to_11 > 0b11) // more than the two LSB
+ return false; // outside L1
+ } else if ((c & 0b1100'0000) == 0b1000'0000) { // trailing UTF-8 octet
+ if (!inMultibyte)
+ return false; // invalid sequence
+ inMultibyte = false; // only one continuation allowed
+ } else {
+ return false; // invalid sequence or outside of L1
+ }
+ }
+ }
+ if (inMultibyte)
+ return false; // invalid sequence: premature end
+ return true;
+}
+
+static constexpr auto isValidHttpHeaderNameChar = [](uchar c) noexcept
+{
+ // RFC 9110 Chapters "5.1 Field Names" and "5.6.2 Tokens"
+ // field-name = token
+ // token = 1*tchar
+ // tchar = "!" / "#" / "$" / "%" / "&" / "'" / "*" /
+ // "+" / "-" / "." / "^" / "_" / "`" / "|" / "~"
+ // / DIGIT / ALPHA
+ // ; any VCHAR, except delimiters
+ // (for explanation on VCHAR see isValidHttpHeaderValueChar)
+ return (('A' <= c && c <= 'Z')
+ || ('a' <= c && c <= 'z')
+ || ('0' <= c && c <= '9')
+ || ('#' <= c && c <= '\'')
+ || ('^' <= c && c <= '`')
+ || c == '|' || c == '~' || c == '!' || c == '*' || c == '+' || c == '-' || c == '.');
+};
+
+static bool headerNameValidImpl(QLatin1StringView name) noexcept
+{
+ return std::all_of(name.begin(), name.end(), isValidHttpHeaderNameChar);
+}
+
+static bool headerNameValidImpl(QUtf8StringView name) noexcept
+{
+ // Traversing the UTF-8 string char-by-char is fine in this case as
+ // the isValidHttpHeaderNameChar rejects any value above 0x7E. UTF-8
+ // only has bytes <= 0x7F if they truly represent that ASCII character.
+ return headerNameValidImpl(QLatin1StringView(QByteArrayView(name)));
+}
+
+static bool headerNameValidImpl(QStringView name) noexcept
+{
+ return std::all_of(name.begin(), name.end(), [](QChar c) {
+ return isValidHttpHeaderNameChar(c.toLatin1());
+ });
+}
+
+static bool isValidHttpHeaderNameField(QAnyStringView name) noexcept
+{
+ if (name.isEmpty()) {
+ qCWarning(lcQHttpHeaders, "HTTP header name cannot be empty");
+ return false;
+ }
+ const bool valid = name.visit([](auto name){ return headerNameValidImpl(name); });
+ if (!valid)
+ qCWarning(lcQHttpHeaders, "HTTP header name contained illegal character(s)");
+ return valid;
+}
+
+static constexpr auto isValidHttpHeaderValueChar = [](uchar c) noexcept
+{
+ // RFC 9110 Chapter 5.5, Field Values
+ // field-value = *field-content
+ // field-content = field-vchar
+ // [ 1*( SP / HTAB / field-vchar ) field-vchar ]
+ // field-vchar = VCHAR / obs-text
+ // obs-text = %x80-FF
+ // VCHAR is defined as "any visible US-ASCII character", and RFC 5234 B.1.
+ // defines it as %x21-7E
+ // Note: The ABNF above states that field-content and thus field-value cannot
+ // start or end with SP/HTAB. The caller should handle this.
+ return (c >= 0x80 // obs-text (extended ASCII)
+ || (0x20 <= c && c <= 0x7E) // SP (0x20) + VCHAR
+ || (c == 0x09)); // HTAB
+};
+
+static bool headerValueValidImpl(QLatin1StringView value) noexcept
+{
+ return std::all_of(value.begin(), value.end(), isValidHttpHeaderValueChar);
+}
+
+static bool headerValueValidImpl(QUtf8StringView value) noexcept
+{
+ if (!isUtf8Latin1Representable(value)) // TODO FIXME see the function
+ return false;
+ return std::all_of(value.begin(), value.end(), isValidHttpHeaderValueChar);
+}
+
+static bool headerValueValidImpl(QStringView value) noexcept
+{
+ return std::all_of(value.begin(), value.end(), [](QChar c) {
+ return isValidHttpHeaderValueChar(c.toLatin1());
+ });
+}
+
+static bool isValidHttpHeaderValueField(QAnyStringView value) noexcept
+{
+ const bool valid = value.visit([](auto value){ return headerValueValidImpl(value); });
+ if (!valid)
+ qCWarning(lcQHttpHeaders, "HTTP header value contained illegal character(s)");
+ return valid;
+}
+
+static QByteArray normalizedValue(QAnyStringView value)
+{
+ // Note on trimming away any leading or trailing whitespace of 'value':
+ // RFC 9110 (HTTP 1.1, 2022, Chapter 5.5) does not allow leading or trailing whitespace
+ // RFC 7230 (HTTP 1.1, 2014, Chapter 3.2) allows them optionally, but also mandates that
+ // they are ignored during processing
+ // RFC 7540 (HTTP/2) does not seem explicit about it
+ // => for maximum compatibility, trim away any leading or trailing whitespace
+ return value.visit([](auto value){ return fieldToByteArray(value); }).trimmed();
+}
+
+/*!
+ Appends a header entry with \a name and \a value and returns \c true
+ if successful.
+
+ \sa append(QHttpHeaders::WellKnownHeader, QAnyStringView)
+ \sa {Allowed field name and value characters}
+*/
+bool QHttpHeaders::append(QAnyStringView name, QAnyStringView value)
+{
+ if (!isValidHttpHeaderNameField(name) || !isValidHttpHeaderValueField(value))
+ return false;
+
+ d.detach();
+ d->headers.push_back({HeaderName{name}, normalizedValue(value)});
+ return true;
+}
+
+/*!
+ \overload append(QAnyStringView, QAnyStringView)
+*/
+bool QHttpHeaders::append(WellKnownHeader name, QAnyStringView value)
+{
+ if (!isValidHttpHeaderValueField(value))
+ return false;
+
+ d.detach();
+ d->headers.push_back({HeaderName{name}, normalizedValue(value)});
+ return true;
+}
+
+/*!
+ Inserts a header entry at index \a i, with \a name and \a value. The index
+ must be valid (see \l size()). Returns whether the insert succeeded.
+
+ \sa append(),
+ insert(qsizetype, QHttpHeaders::WellKnownHeader, QAnyStringView), size()
+ \sa {Allowed field name and value characters}
+*/
+bool QHttpHeaders::insert(qsizetype i, QAnyStringView name, QAnyStringView value)
+{
+ verify(i, 0);
+ if (!isValidHttpHeaderNameField(name) || !isValidHttpHeaderValueField(value))
+ return false;
+
+ d.detach();
+ d->headers.insert(i, {HeaderName{name}, normalizedValue(value)});
+ return true;
+}
+
+/*!
+ \overload insert(qsizetype, QAnyStringView, QAnyStringView)
+*/
+bool QHttpHeaders::insert(qsizetype i, WellKnownHeader name, QAnyStringView value)
+{
+ verify(i, 0);
+ if (!isValidHttpHeaderValueField(value))
+ return false;
+
+ d.detach();
+ d->headers.insert(i, {HeaderName{name}, normalizedValue(value)});
+ return true;
+}
+
+/*!
+ Replaces the header entry at index \a i, with \a name and \a newValue.
+ The index must be valid (see \l size()). Returns whether the replace
+ succeeded.
+
+ \sa append(),
+ replace(qsizetype, QHttpHeaders::WellKnownHeader, QAnyStringView), size()
+ \sa {Allowed field name and value characters}
+*/
+bool QHttpHeaders::replace(qsizetype i, QAnyStringView name, QAnyStringView newValue)
+{
+ verify(i);
+ if (!isValidHttpHeaderNameField(name) || !isValidHttpHeaderValueField(newValue))
+ return false;
+
+ d.detach();
+ d->headers.replace(i, {HeaderName{name}, normalizedValue(newValue)});
+ return true;
+}
+
+/*!
+ \overload replace(qsizetype, QAnyStringView, QAnyStringView)
+*/
+bool QHttpHeaders::replace(qsizetype i, WellKnownHeader name, QAnyStringView newValue)
+{
+ verify(i);
+ if (!isValidHttpHeaderValueField(newValue))
+ return false;
+
+ d.detach();
+ d->headers.replace(i, {HeaderName{name}, normalizedValue(newValue)});
+ return true;
+}
+
+/*!
+ \since 6.8
+
+ If QHttpHeaders already contains \a name, replaces its value with
+ \a newValue and removes possible additional \a name entries.
+ If \a name didn't exist, appends a new entry. Returns \c true
+ if successful.
+
+ This function is a convenience method for setting a unique
+ \a name : \a newValue header. For most headers the relative order does not
+ matter, which allows reusing an existing entry if one exists.
+
+ \sa replaceOrAppend(QAnyStringView, QAnyStringView)
+*/
+bool QHttpHeaders::replaceOrAppend(WellKnownHeader name, QAnyStringView newValue)
+{
+ if (isEmpty())
+ return append(name, newValue);
+
+ if (!isValidHttpHeaderValueField(newValue))
+ return false;
+
+ QHttpHeadersPrivate::replaceOrAppend(d, HeaderName{name}, normalizedValue(newValue));
+ return true;
+}
+
+/*!
+ \overload replaceOrAppend(WellKnownHeader, QAnyStringView)
+*/
+bool QHttpHeaders::replaceOrAppend(QAnyStringView name, QAnyStringView newValue)
+{
+ if (isEmpty())
+ return append(name, newValue);
+
+ if (!isValidHttpHeaderNameField(name) || !isValidHttpHeaderValueField(newValue))
+ return false;
+
+ QHttpHeadersPrivate::replaceOrAppend(d, HeaderName{name}, normalizedValue(newValue));
+ return true;
+}
+
+/*!
+ Returns whether the headers contain header with \a name.
+
+ \sa contains(QHttpHeaders::WellKnownHeader)
+*/
+bool QHttpHeaders::contains(QAnyStringView name) const
+{
+ if (isEmpty())
+ return false;
+
+ return std::any_of(d->headers.cbegin(), d->headers.cend(), headerNameMatches(HeaderName{name}));
+}
+
+/*!
+ \overload has(QAnyStringView)
+*/
+bool QHttpHeaders::contains(WellKnownHeader name) const
+{
+ if (isEmpty())
+ return false;
+
+ return std::any_of(d->headers.cbegin(), d->headers.cend(), headerNameMatches(HeaderName{name}));
+}
+
+/*!
+ Removes the header \a name.
+
+ \sa removeAt(), removeAll(QHttpHeaders::WellKnownHeader)
+*/
+void QHttpHeaders::removeAll(QAnyStringView name)
+{
+ if (isEmpty())
+ return;
+
+ return QHttpHeadersPrivate::removeAll(d, HeaderName(name));
+}
+
+/*!
+ \overload removeAll(QAnyStringView)
+*/
+void QHttpHeaders::removeAll(WellKnownHeader name)
+{
+ if (isEmpty())
+ return;
+
+ return QHttpHeadersPrivate::removeAll(d, HeaderName(name));
+}
+
+/*!
+ Removes the header at index \a i. The index \a i must be valid
+ (see \l size()).
+
+ \sa removeAll(QHttpHeaders::WellKnownHeader),
+ removeAll(QAnyStringView), size()
+*/
+void QHttpHeaders::removeAt(qsizetype i)
+{
+ verify(i);
+ d.detach();
+ d->headers.removeAt(i);
+}
+
+/*!
+ Returns the value of the (first) header \a name, or \a defaultValue if it
+ doesn't exist.
+
+ \sa value(QHttpHeaders::WellKnownHeader, QByteArrayView)
+*/
+QByteArrayView QHttpHeaders::value(QAnyStringView name, QByteArrayView defaultValue) const noexcept
+{
+ if (isEmpty())
+ return defaultValue;
+
+ return d->value(HeaderName{name}, defaultValue);
+}
+
+/*!
+ \overload value(QAnyStringView, QByteArrayView)
+*/
+QByteArrayView QHttpHeaders::value(WellKnownHeader name, QByteArrayView defaultValue) const noexcept
+{
+ if (isEmpty())
+ return defaultValue;
+
+ return d->value(HeaderName{name}, defaultValue);
+}
+
+/*!
+ Returns the values of header \a name in a list. Returns an empty
+ list if header with \a name doesn't exist.
+
+ \sa values(QHttpHeaders::WellKnownHeader)
+*/
+QList<QByteArray> QHttpHeaders::values(QAnyStringView name) const
+{
+ QList<QByteArray> result;
+ if (isEmpty())
+ return result;
+
+ d->values(HeaderName{name}, result);
+ return result;
+}
+
+/*!
+ \overload values(QAnyStringView)
+*/
+QList<QByteArray> QHttpHeaders::values(WellKnownHeader name) const
+{
+ QList<QByteArray> result;
+ if (isEmpty())
+ return result;
+
+ d->values(HeaderName{name}, result);
+ return result;
+}
+
+/*!
+ Returns the header value at index \a i. The index \a i must be valid
+ (see \l size()).
+
+ \sa size(), value(), values(), combinedValue(), nameAt()
+*/
+QByteArrayView QHttpHeaders::valueAt(qsizetype i) const noexcept
+{
+ verify(i);
+ return d->headers.at(i).value;
+}
+
+/*!
+ Returns the header name at index \a i. The index \a i must be valid
+ (see \l size()).
+
+ Header names are case-insensitive, and the returned names are lower-cased.
+
+ \sa size(), valueAt()
+*/
+QLatin1StringView QHttpHeaders::nameAt(qsizetype i) const noexcept
+{
+ verify(i);
+ return QLatin1StringView{d->headers.at(i).name.asView()};
+}
+
+/*!
+ Returns the values of header \a name in a comma-combined string.
+ Returns a \c null QByteArray if the header with \a name doesn't
+ exist.
+
+ \note Accessing the value(s) of 'Set-Cookie' header this way may not work
+ as intended. It is a notable exception in the
+ \l {https://datatracker.ietf.org/doc/html/rfc9110#name-field-order}{HTTP RFC}
+ in that its values cannot be combined this way. Prefer \l values() instead.
+
+ \sa values(QAnyStringView)
+*/
+QByteArray QHttpHeaders::combinedValue(QAnyStringView name) const
+{
+ QByteArray result;
+ if (isEmpty())
+ return result;
+
+ d->combinedValue(HeaderName{name}, result);
+ return result;
+}
+
+/*!
+ \overload combinedValue(QAnyStringView)
+*/
+QByteArray QHttpHeaders::combinedValue(WellKnownHeader name) const
+{
+ QByteArray result;
+ if (isEmpty())
+ return result;
+
+ d->combinedValue(HeaderName{name}, result);
+ return result;
+}
+
+/*!
+ Returns the number of header entries.
+*/
+qsizetype QHttpHeaders::size() const noexcept
+{
+ if (!d)
+ return 0;
+ return d->headers.size();
+}
+
+/*!
+ Attempts to allocate memory for at least \a size header entries.
+
+ If you know in advance how how many header entries there will
+ be, you may call this function to prevent reallocations
+ and memory fragmentation.
+*/
+void QHttpHeaders::reserve(qsizetype size)
+{
+ d.detach();
+ d->headers.reserve(size);
+}
+
+/*!
+ \fn bool QHttpHeaders::isEmpty() const noexcept
+
+ Returns \c true if the headers have size 0; otherwise returns \c false.
+
+ \sa size()
+*/
+
+/*!
+ Returns a header name corresponding to the provided \a name as a view.
+*/
+QByteArrayView QHttpHeaders::wellKnownHeaderName(WellKnownHeader name) noexcept
+{
+ return headerNames[qToUnderlying(name)];
+}
+
+/*!
+ Returns the header entries as a list of (name, value) pairs.
+ Header names are case-insensitive, and the returned names are lower-cased.
+*/
+QList<std::pair<QByteArray, QByteArray>> QHttpHeaders::toListOfPairs() const
+{
+ QList<std::pair<QByteArray, QByteArray>> list;
+ if (isEmpty())
+ return list;
+ list.reserve(size());
+ for (const auto & h : std::as_const(d->headers))
+ list.append({h.name.asByteArray(), h.value});
+ return list;
+}
+
+/*!
+ Returns the header entries as a map from name to value(s).
+ Header names are case-insensitive, and the returned names are lower-cased.
+*/
+QMultiMap<QByteArray, QByteArray> QHttpHeaders::toMultiMap() const
+{
+ QMultiMap<QByteArray, QByteArray> map;
+ if (isEmpty())
+ return map;
+ for (const auto &h : std::as_const(d->headers))
+ map.insert(h.name.asByteArray(), h.value);
+ return map;
+}
+
+/*!
+ Returns the header entries as a hash from name to value(s).
+ Header names are case-insensitive, and the returned names are lower-cased.
+*/
+QMultiHash<QByteArray, QByteArray> QHttpHeaders::toMultiHash() const
+{
+ QMultiHash<QByteArray, QByteArray> hash;
+ if (isEmpty())
+ return hash;
+ hash.reserve(size());
+ for (const auto &h : std::as_const(d->headers))
+ hash.insert(h.name.asByteArray(), h.value);
+ return hash;
+}
+
+/*!
+ Clears all header entries.
+
+ \sa size()
+*/
+void QHttpHeaders::clear()
+{
+ if (isEmpty())
+ return;
+ d.detach();
+ d->headers.clear();
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qhttpheaders.h b/src/network/access/qhttpheaders.h
new file mode 100644
index 0000000000..97dc415e55
--- /dev/null
+++ b/src/network/access/qhttpheaders.h
@@ -0,0 +1,281 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QHTTPHEADERS_H
+#define QHTTPHEADERS_H
+
+#include <QtNetwork/qnetworkrequest.h>
+#include <QtCore/qshareddata.h>
+#include <QtCore/qcontainerfwd.h>
+
+QT_BEGIN_NAMESPACE
+
+class QDebug;
+
+class QHttpHeadersPrivate;
+QT_DECLARE_QESDP_SPECIALIZATION_DTOR_WITH_EXPORT(QHttpHeadersPrivate, Q_NETWORK_EXPORT)
+class QHttpHeaders
+{
+ Q_GADGET_EXPORT(Q_NETWORK_EXPORT)
+public:
+ enum class WellKnownHeader {
+ // IANA Permanent status:
+ AIM,
+ Accept,
+ AcceptAdditions,
+ AcceptCH,
+ AcceptDatetime,
+ AcceptEncoding,
+ AcceptFeatures,
+ AcceptLanguage,
+ AcceptPatch,
+ AcceptPost,
+ AcceptRanges,
+ AcceptSignature,
+ AccessControlAllowCredentials,
+ AccessControlAllowHeaders,
+ AccessControlAllowMethods,
+ AccessControlAllowOrigin,
+ AccessControlExposeHeaders,
+ AccessControlMaxAge,
+ AccessControlRequestHeaders,
+ AccessControlRequestMethod,
+ Age,
+ Allow,
+ ALPN,
+ AltSvc,
+ AltUsed,
+ Alternates,
+ ApplyToRedirectRef,
+ AuthenticationControl,
+ AuthenticationInfo,
+ Authorization,
+ CacheControl,
+ CacheStatus,
+ CalManagedID,
+ CalDAVTimezones,
+ CapsuleProtocol,
+ CDNCacheControl,
+ CDNLoop,
+ CertNotAfter,
+ CertNotBefore,
+ ClearSiteData,
+ ClientCert,
+ ClientCertChain,
+ Close,
+ Connection,
+ ContentDigest,
+ ContentDisposition,
+ ContentEncoding,
+ ContentID,
+ ContentLanguage,
+ ContentLength,
+ ContentLocation,
+ ContentRange,
+ ContentSecurityPolicy,
+ ContentSecurityPolicyReportOnly,
+ ContentType,
+ Cookie,
+ CrossOriginEmbedderPolicy,
+ CrossOriginEmbedderPolicyReportOnly,
+ CrossOriginOpenerPolicy,
+ CrossOriginOpenerPolicyReportOnly,
+ CrossOriginResourcePolicy,
+ DASL,
+ Date,
+ DAV,
+ DeltaBase,
+ Depth,
+ Destination,
+ DifferentialID,
+ DPoP,
+ DPoPNonce,
+ EarlyData,
+ ETag,
+ Expect,
+ ExpectCT,
+ Expires,
+ Forwarded,
+ From,
+ Hobareg,
+ Host,
+ If,
+ IfMatch,
+ IfModifiedSince,
+ IfNoneMatch,
+ IfRange,
+ IfScheduleTagMatch,
+ IfUnmodifiedSince,
+ IM,
+ IncludeReferredTokenBindingID,
+ KeepAlive,
+ Label,
+ LastEventID,
+ LastModified,
+ Link,
+ Location,
+ LockToken,
+ MaxForwards,
+ MementoDatetime,
+ Meter,
+ MIMEVersion,
+ Negotiate,
+ NEL,
+ ODataEntityId,
+ ODataIsolation,
+ ODataMaxVersion,
+ ODataVersion,
+ OptionalWWWAuthenticate,
+ OrderingType,
+ Origin,
+ OriginAgentCluster,
+ OSCORE,
+ OSLCCoreVersion,
+ Overwrite,
+ PingFrom,
+ PingTo,
+ Position,
+ Prefer,
+ PreferenceApplied,
+ Priority,
+ ProxyAuthenticate,
+ ProxyAuthenticationInfo,
+ ProxyAuthorization,
+ ProxyStatus,
+ PublicKeyPins,
+ PublicKeyPinsReportOnly,
+ Range,
+ RedirectRef,
+ Referer,
+ Refresh,
+ ReplayNonce,
+ ReprDigest,
+ RetryAfter,
+ ScheduleReply,
+ ScheduleTag,
+ SecPurpose,
+ SecTokenBinding,
+ SecWebSocketAccept,
+ SecWebSocketExtensions,
+ SecWebSocketKey,
+ SecWebSocketProtocol,
+ SecWebSocketVersion,
+ Server,
+ ServerTiming,
+ SetCookie,
+ Signature,
+ SignatureInput,
+ SLUG,
+ SoapAction,
+ StatusURI,
+ StrictTransportSecurity,
+ Sunset,
+ SurrogateCapability,
+ SurrogateControl,
+ TCN,
+ TE,
+ Timeout,
+ Topic,
+ Traceparent,
+ Tracestate,
+ Trailer,
+ TransferEncoding,
+ TTL,
+ Upgrade,
+ Urgency,
+ UserAgent,
+ VariantVary,
+ Vary,
+ Via,
+ WantContentDigest,
+ WantReprDigest,
+ WWWAuthenticate,
+ XContentTypeOptions,
+ XFrameOptions,
+ // IANA Deprecated status:
+ AcceptCharset,
+ CPEPInfo,
+ Pragma,
+ ProtocolInfo,
+ ProtocolQuery,
+ };
+ Q_ENUM(WellKnownHeader)
+
+ Q_NETWORK_EXPORT QHttpHeaders() noexcept;
+ Q_NETWORK_EXPORT ~QHttpHeaders();
+
+ Q_NETWORK_EXPORT QHttpHeaders(const QHttpHeaders &other);
+ QHttpHeaders(QHttpHeaders &&other) noexcept = default;
+ Q_NETWORK_EXPORT QHttpHeaders &operator=(const QHttpHeaders &other);
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QHttpHeaders)
+ void swap(QHttpHeaders &other) noexcept { d.swap(other.d); }
+
+ Q_NETWORK_EXPORT bool append(QAnyStringView name, QAnyStringView value);
+ Q_NETWORK_EXPORT bool append(WellKnownHeader name, QAnyStringView value);
+
+ Q_NETWORK_EXPORT bool insert(qsizetype i, QAnyStringView name, QAnyStringView value);
+ Q_NETWORK_EXPORT bool insert(qsizetype i, WellKnownHeader name, QAnyStringView value);
+
+ Q_NETWORK_EXPORT bool replace(qsizetype i, QAnyStringView name, QAnyStringView newValue);
+ Q_NETWORK_EXPORT bool replace(qsizetype i, WellKnownHeader name, QAnyStringView newValue);
+
+ Q_NETWORK_EXPORT bool replaceOrAppend(QAnyStringView name, QAnyStringView newValue);
+ Q_NETWORK_EXPORT bool replaceOrAppend(WellKnownHeader name, QAnyStringView newValue);
+
+ Q_NETWORK_EXPORT bool contains(QAnyStringView name) const;
+ Q_NETWORK_EXPORT bool contains(WellKnownHeader name) const;
+
+ Q_NETWORK_EXPORT void clear();
+ Q_NETWORK_EXPORT void removeAll(QAnyStringView name);
+ Q_NETWORK_EXPORT void removeAll(WellKnownHeader name);
+ Q_NETWORK_EXPORT void removeAt(qsizetype i);
+
+ Q_NETWORK_EXPORT QByteArrayView value(QAnyStringView name, QByteArrayView defaultValue = {}) const noexcept;
+ Q_NETWORK_EXPORT QByteArrayView value(WellKnownHeader name, QByteArrayView defaultValue = {}) const noexcept;
+
+ Q_NETWORK_EXPORT QList<QByteArray> values(QAnyStringView name) const;
+ Q_NETWORK_EXPORT QList<QByteArray> values(WellKnownHeader name) const;
+
+ Q_NETWORK_EXPORT QByteArrayView valueAt(qsizetype i) const noexcept;
+ Q_NETWORK_EXPORT QLatin1StringView nameAt(qsizetype i) const noexcept;
+
+ Q_NETWORK_EXPORT QByteArray combinedValue(QAnyStringView name) const;
+ Q_NETWORK_EXPORT QByteArray combinedValue(WellKnownHeader name) const;
+
+ Q_NETWORK_EXPORT qsizetype size() const noexcept;
+ Q_NETWORK_EXPORT void reserve(qsizetype size);
+ bool isEmpty() const noexcept { return size() == 0; }
+
+ Q_NETWORK_EXPORT static QByteArrayView wellKnownHeaderName(WellKnownHeader name) noexcept;
+
+ Q_NETWORK_EXPORT static QHttpHeaders
+ fromListOfPairs(const QList<std::pair<QByteArray, QByteArray>> &headers);
+ Q_NETWORK_EXPORT static QHttpHeaders
+ fromMultiMap(const QMultiMap<QByteArray, QByteArray> &headers);
+ Q_NETWORK_EXPORT static QHttpHeaders
+ fromMultiHash(const QMultiHash<QByteArray, QByteArray> &headers);
+
+ Q_NETWORK_EXPORT QList<std::pair<QByteArray, QByteArray>> toListOfPairs() const;
+ Q_NETWORK_EXPORT QMultiMap<QByteArray, QByteArray> toMultiMap() const;
+ Q_NETWORK_EXPORT QMultiHash<QByteArray, QByteArray> toMultiHash() const;
+
+private:
+#ifndef QT_NO_DEBUG_STREAM
+ friend Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QHttpHeaders &headers);
+#endif
+ Q_ALWAYS_INLINE void verify([[maybe_unused]] qsizetype pos = 0,
+ [[maybe_unused]] qsizetype n = 1) const
+ {
+ Q_ASSERT(pos >= 0);
+ Q_ASSERT(pos <= size());
+ Q_ASSERT(n >= 0);
+ Q_ASSERT(n <= size() - pos);
+ }
+ QExplicitlySharedDataPointer<QHttpHeadersPrivate> d;
+};
+
+Q_DECLARE_SHARED(QHttpHeaders)
+
+QT_END_NAMESPACE
+
+#endif // QHTTPHEADERS_H
diff --git a/src/network/access/qhttpmultipart.cpp b/src/network/access/qhttpmultipart.cpp
index d6fefc4314..6d81f1b957 100644
--- a/src/network/access/qhttpmultipart.cpp
+++ b/src/network/access/qhttpmultipart.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qhttpmultipart.h"
#include "qhttpmultipart_p.h"
@@ -353,11 +317,11 @@ void QHttpMultiPart::setBoundary(const QByteArray &boundary)
qint64 QHttpPartPrivate::bytesAvailable() const
{
checkHeaderCreated();
- qint64 bytesAvailable = header.count();
+ qint64 bytesAvailable = header.size();
if (bodyDevice) {
bytesAvailable += bodyDevice->bytesAvailable() - readPointer;
} else {
- bytesAvailable += body.count() - readPointer;
+ bytesAvailable += body.size() - readPointer;
}
// the device might have closed etc., so make sure we do not return a negative value
return qMax(bytesAvailable, (qint64) 0);
@@ -367,7 +331,7 @@ qint64 QHttpPartPrivate::readData(char *data, qint64 maxSize)
{
checkHeaderCreated();
qint64 bytesRead = 0;
- qint64 headerDataCount = header.count();
+ qint64 headerDataCount = header.size();
// read header if it has not been read yet
if (readPointer < headerDataCount) {
@@ -385,7 +349,7 @@ qint64 QHttpPartPrivate::readData(char *data, qint64 maxSize)
bytesRead += dataBytesRead;
readPointer += dataBytesRead;
} else {
- qint64 contentBytesRead = qMin(body.count() - readPointer + headerDataCount, maxSize - bytesRead);
+ qint64 contentBytesRead = qMin(body.size() - readPointer + headerDataCount, maxSize - bytesRead);
const char *contentData = body.constData();
// if this method is called several times, we need to find the
// right offset in the content ourselves:
@@ -400,11 +364,11 @@ qint64 QHttpPartPrivate::readData(char *data, qint64 maxSize)
qint64 QHttpPartPrivate::size() const
{
checkHeaderCreated();
- qint64 size = header.count();
+ qint64 size = header.size();
if (bodyDevice) {
size += bodyDevice->size();
} else {
- size += body.count();
+ size += body.size();
}
return size;
}
@@ -422,10 +386,9 @@ void QHttpPartPrivate::checkHeaderCreated() const
{
if (!headerCreated) {
// copied from QHttpNetworkRequestPrivate::header() and adapted
- QList<QPair<QByteArray, QByteArray> > fields = allRawHeaders();
- QList<QPair<QByteArray, QByteArray> >::const_iterator it = fields.constBegin();
- for (; it != fields.constEnd(); ++it)
- header += it->first + ": " + it->second + "\r\n";
+ const auto fields = allRawHeaders();
+ for (const auto &[name, value] : fields)
+ header += name + ": " + value + "\r\n";
header += "\r\n";
headerCreated = true;
}
@@ -440,7 +403,7 @@ QHttpMultiPartPrivate::QHttpMultiPartPrivate() : contentType(QHttpMultiPart::Mix
+ QByteArray::fromRawData(reinterpret_cast<char *>(random), sizeof(random)).toBase64();
// boundary must not be longer than 70 characters, see RFC 2046, section 5.1.1
- Q_ASSERT(boundary.count() <= 70);
+ Q_ASSERT(boundary.size() <= 70);
}
qint64 QHttpMultiPartIODevice::size() const
@@ -449,8 +412,8 @@ qint64 QHttpMultiPartIODevice::size() const
// including boundary (needed later in readData)
if (deviceSize == -1) {
qint64 currentSize = 0;
- qint64 boundaryCount = multiPart->boundary.count();
- for (int a = 0; a < multiPart->parts.count(); a++) {
+ qint64 boundaryCount = multiPart->boundary.size();
+ for (int a = 0; a < multiPart->parts.size(); a++) {
partOffsets.append(currentSize);
// 4 additional bytes for the "--" before and the "\r\n" after the boundary,
// and 2 bytes for the "\r\n" after the content
@@ -464,7 +427,7 @@ qint64 QHttpMultiPartIODevice::size() const
bool QHttpMultiPartIODevice::isSequential() const
{
- for (int a = 0; a < multiPart->parts.count(); a++) {
+ for (int a = 0; a < multiPart->parts.size(); a++) {
QIODevice *device = multiPart->parts.at(a).d->bodyDevice;
// we are sequential if any of the bodyDevices of our parts are sequential;
// when reading from a byte array, we are not sequential
@@ -478,7 +441,7 @@ bool QHttpMultiPartIODevice::reset()
{
// Reset QIODevice's data
QIODevice::reset();
- for (int a = 0; a < multiPart->parts.count(); a++)
+ for (int a = 0; a < multiPart->parts.size(); a++)
if (!multiPart->parts[a].d->reset())
return false;
readPointer = 0;
@@ -489,17 +452,17 @@ qint64 QHttpMultiPartIODevice::readData(char *data, qint64 maxSize)
qint64 bytesRead = 0, index = 0;
// skip the parts we have already read
- while (index < multiPart->parts.count() &&
+ while (index < multiPart->parts.size() &&
readPointer >= partOffsets.at(index) + multiPart->parts.at(index).d->size()
- + multiPart->boundary.count() + 6) // 6 == 2 boundary dashes, \r\n after boundary, \r\n after multipart
+ + multiPart->boundary.size() + 6) // 6 == 2 boundary dashes, \r\n after boundary, \r\n after multipart
index++;
// read the data
- while (bytesRead < maxSize && index < multiPart->parts.count()) {
+ while (bytesRead < maxSize && index < multiPart->parts.size()) {
// check whether we need to read the boundary of the current part
QByteArray boundaryData = "--" + multiPart->boundary + "\r\n";
- qint64 boundaryCount = boundaryData.count();
+ qint64 boundaryCount = boundaryData.size();
qint64 partIndex = readPointer - partOffsets.at(index);
if (partIndex < boundaryCount) {
qint64 boundaryBytesRead = qMin(boundaryCount - partIndex, maxSize - bytesRead);
@@ -530,10 +493,10 @@ qint64 QHttpMultiPartIODevice::readData(char *data, qint64 maxSize)
}
}
// check whether we need to return the final boundary
- if (bytesRead < maxSize && index == multiPart->parts.count()) {
+ if (bytesRead < maxSize && index == multiPart->parts.size()) {
QByteArray finalBoundary = "--" + multiPart->boundary + "--\r\n";
- qint64 boundaryIndex = readPointer + finalBoundary.count() - size();
- qint64 lastBoundaryBytesRead = qMin(finalBoundary.count() - boundaryIndex, maxSize - bytesRead);
+ qint64 boundaryIndex = readPointer + finalBoundary.size() - size();
+ qint64 lastBoundaryBytesRead = qMin(finalBoundary.size() - boundaryIndex, maxSize - bytesRead);
memcpy(data + bytesRead, finalBoundary.constData() + boundaryIndex, lastBoundaryBytesRead);
bytesRead += lastBoundaryBytesRead;
readPointer += lastBoundaryBytesRead;
@@ -550,3 +513,5 @@ qint64 QHttpMultiPartIODevice::writeData(const char *data, qint64 maxSize)
QT_END_NAMESPACE
+
+#include "moc_qhttpmultipart.cpp"
diff --git a/src/network/access/qhttpmultipart.h b/src/network/access/qhttpmultipart.h
index 7f07008c0f..26e5fafdf2 100644
--- a/src/network/access/qhttpmultipart.h
+++ b/src/network/access/qhttpmultipart.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QHTTPMULTIPART_H
#define QHTTPMULTIPART_H
@@ -65,7 +29,7 @@ public:
QHttpPart &operator=(QHttpPart &&other) noexcept { swap(other); return *this; }
QHttpPart &operator=(const QHttpPart &other);
- void swap(QHttpPart &other) noexcept { qSwap(d, other.d); }
+ void swap(QHttpPart &other) noexcept { d.swap(other.d); }
bool operator==(const QHttpPart &other) const;
inline bool operator!=(const QHttpPart &other) const
diff --git a/src/network/access/qhttpmultipart_p.h b/src/network/access/qhttpmultipart_p.h
index e7fd2d6d3d..d485fcf5cd 100644
--- a/src/network/access/qhttpmultipart_p.h
+++ b/src/network/access/qhttpmultipart_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QHTTPMULTIPART_P_H
#define QHTTPMULTIPART_P_H
diff --git a/src/network/access/qhttpnetworkconnection.cpp b/src/network/access/qhttpnetworkconnection.cpp
index 89402cb58d..1897380e0e 100644
--- a/src/network/access/qhttpnetworkconnection.cpp
+++ b/src/network/access/qhttpnetworkconnection.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qhttpnetworkconnection_p.h"
#include <private/qabstractsocket_p.h>
@@ -53,6 +17,8 @@
#include <qbuffer.h>
#include <qpair.h>
#include <qdebug.h>
+#include <qspan.h>
+#include <qvarlengtharray.h>
#ifndef QT_NO_SSL
# include <private/qsslsocket_p.h>
@@ -66,7 +32,7 @@
QT_BEGIN_NAMESPACE
-const int QHttpNetworkConnectionPrivate::defaultHttpChannelCount = 6;
+using namespace Qt::StringLiterals;
// The pipeline length. So there will be 4 requests in flight.
const int QHttpNetworkConnectionPrivate::defaultPipelineLength = 3;
@@ -74,43 +40,33 @@ const int QHttpNetworkConnectionPrivate::defaultPipelineLength = 3;
// This means that there are 2 requests in flight and 2 slots free that will be re-filled.
const int QHttpNetworkConnectionPrivate::defaultRePipelineLength = 2;
-
-QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(const QString &hostName,
- quint16 port, bool encrypt,
- QHttpNetworkConnection::ConnectionType type)
-: state(RunningState),
- networkLayerState(Unknown),
- hostName(hostName), port(port), encrypt(encrypt), delayIpv4(true)
- , activeChannelCount(type == QHttpNetworkConnection::ConnectionTypeHTTP2
- || type == QHttpNetworkConnection::ConnectionTypeHTTP2Direct
- ? 1 : defaultHttpChannelCount)
- , channelCount(defaultHttpChannelCount)
-#ifndef QT_NO_NETWORKPROXY
- , networkProxy(QNetworkProxy::NoProxy)
-#endif
- , preConnectRequests(0)
- , connectionType(type)
+static int getPreferredActiveChannelCount(QHttpNetworkConnection::ConnectionType type,
+ int defaultValue)
{
- // We allocate all 6 channels even if it's HTTP/2 enabled connection:
- // in case the protocol negotiation via NPN/ALPN fails, we will have
- // normally working HTTP/1.1.
- Q_ASSERT(channelCount >= activeChannelCount);
- channels = new QHttpNetworkConnectionChannel[channelCount];
+ return (type == QHttpNetworkConnection::ConnectionTypeHTTP2
+ || type == QHttpNetworkConnection::ConnectionTypeHTTP2Direct)
+ ? 1
+ : defaultValue;
}
-QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(quint16 connectionCount, const QString &hostName,
- quint16 port, bool encrypt,
- QHttpNetworkConnection::ConnectionType type)
-: state(RunningState), networkLayerState(Unknown),
- hostName(hostName), port(port), encrypt(encrypt), delayIpv4(true),
- activeChannelCount(connectionCount), channelCount(connectionCount)
+QHttpNetworkConnectionPrivate::QHttpNetworkConnectionPrivate(
+ quint16 connectionCount, const QString &hostName, quint16 port, bool encrypt,
+ QHttpNetworkConnection::ConnectionType type)
+ : hostName(hostName),
+ port(port),
+ encrypt(encrypt),
+ activeChannelCount(getPreferredActiveChannelCount(type, connectionCount)),
+ channelCount(connectionCount),
+ channels(new QHttpNetworkConnectionChannel[channelCount]),
#ifndef QT_NO_NETWORKPROXY
- , networkProxy(QNetworkProxy::NoProxy)
+ networkProxy(QNetworkProxy::NoProxy),
#endif
- , preConnectRequests(0)
- , connectionType(type)
+ connectionType(type)
{
- channels = new QHttpNetworkConnectionChannel[channelCount];
+ // We allocate all 6 channels even if it's an HTTP/2-enabled
+ // connection: in case the protocol negotiation via NPN/ALPN fails,
+ // we will have normally working HTTP/1.1.
+ Q_ASSERT(channelCount >= activeChannelCount);
}
@@ -179,7 +135,7 @@ void QHttpNetworkConnectionPrivate::resumeConnection()
QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
}
-int QHttpNetworkConnectionPrivate::indexOf(QAbstractSocket *socket) const
+int QHttpNetworkConnectionPrivate::indexOf(QIODevice *socket) const
{
for (int i = 0; i < activeChannelCount; ++i)
if (channels[i].socket == socket)
@@ -193,7 +149,7 @@ int QHttpNetworkConnectionPrivate::indexOf(QAbstractSocket *socket) const
// emitted. This function will check the status of the connection channels if we
// have not decided the networkLayerState and will return true if the channel error
// should be emitted by the channel.
-bool QHttpNetworkConnectionPrivate::shouldEmitChannelError(QAbstractSocket *socket)
+bool QHttpNetworkConnectionPrivate::shouldEmitChannelError(QIODevice *socket)
{
Q_Q(QHttpNetworkConnection);
@@ -251,6 +207,17 @@ qint64 QHttpNetworkConnectionPrivate::uncompressedBytesAvailableNextBlock(const
return reply.d_func()->responseData.sizeNextBlock();
}
+static QByteArray makeAcceptLanguage()
+{
+ QString systemLocale = QLocale::system().name();
+ if (systemLocale == "C"_L1)
+ return "en,*"_ba;
+ systemLocale.replace('_'_L1, '-'_L1);
+ if (systemLocale.startsWith("en-"_L1))
+ return (systemLocale + ",*"_L1).toLatin1();
+ return (systemLocale + ",en,*"_L1).toLatin1();
+}
+
void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair)
{
QHttpNetworkRequest &request = messagePair.first;
@@ -265,8 +232,9 @@ void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair)
const qint64 contentLength = request.contentLength();
const qint64 uploadDeviceSize = uploadByteDevice->size();
if (contentLength != -1 && uploadDeviceSize != -1) {
- // both values known, take the smaller one.
- request.setContentLength(qMin(uploadDeviceSize, contentLength));
+ // Both values known: use the smaller one.
+ if (uploadDeviceSize < contentLength)
+ request.setContentLength(uploadDeviceSize);
} else if (contentLength == -1 && uploadDeviceSize != -1) {
// content length not supplied by user, but the upload device knows it
request.setContentLength(uploadDeviceSize);
@@ -300,8 +268,8 @@ void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair)
value = request.headerField("accept-encoding");
if (value.isEmpty()) {
#ifndef QT_NO_COMPRESS
- const QByteArrayList &acceptedEncoding = QDecompressHelper::acceptedEncoding();
- request.setHeaderField("Accept-Encoding", acceptedEncoding.join(", "));
+ const static QByteArray acceptedEncoding = QDecompressHelper::acceptedEncoding().join(", ");
+ request.setHeaderField("Accept-Encoding", acceptedEncoding);
request.d->autoDecompress = true;
#else
// if zlib is not available set this to false always
@@ -314,17 +282,8 @@ void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair)
// not with us, but we work around this by setting
// one always.
value = request.headerField("accept-language");
- if (value.isEmpty()) {
- QString systemLocale = QLocale::system().name().replace(QChar::fromLatin1('_'),QChar::fromLatin1('-'));
- QString acceptLanguage;
- if (systemLocale == QLatin1String("C"))
- acceptLanguage = QString::fromLatin1("en,*");
- else if (systemLocale.startsWith(QLatin1String("en-")))
- acceptLanguage = systemLocale + QLatin1String(",*");
- else
- acceptLanguage = systemLocale + QLatin1String(",en,*");
- request.setHeaderField("Accept-Language", std::move(acceptLanguage).toLatin1());
- }
+ if (value.isEmpty())
+ request.setHeaderField("Accept-Language", makeAcceptLanguage());
// set the User Agent
value = request.headerField("user-agent");
@@ -337,7 +296,7 @@ void QHttpNetworkConnectionPrivate::prepareRequest(HttpMessagePair &messagePair)
QByteArray host;
if (add.setAddress(hostName)) {
if (add.protocol() == QAbstractSocket::IPv6Protocol)
- host = '[' + hostName.toLatin1() + ']'; //format the ipv6 in the standard way
+ host = (u'[' + hostName + u']').toLatin1(); //format the ipv6 in the standard way
else
host = hostName.toLatin1();
@@ -433,7 +392,7 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket
resend = false;
//create the response header to be used with QAuthenticatorPrivate.
- QList<QPair<QByteArray, QByteArray> > fields = reply->header();
+ const auto headers = reply->header();
// Check that any of the proposed authenticate methods are supported
const QByteArray header = isProxy ? "proxy-authenticate" : "www-authenticate";
@@ -449,12 +408,18 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket
if (auth->isNull())
auth->detach();
QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(*auth);
- priv->parseHttpResponse(fields, isProxy, reply->url().host());
+ priv->parseHttpResponse(headers, isProxy, reply->url().host());
// Update method in case it changed
if (priv->method == QAuthenticatorPrivate::None)
return false;
- if (priv->phase == QAuthenticatorPrivate::Done) {
+ if (priv->phase == QAuthenticatorPrivate::Done ||
+ (priv->phase == QAuthenticatorPrivate::Start
+ && (priv->method == QAuthenticatorPrivate::Ntlm
+ || priv->method == QAuthenticatorPrivate::Negotiate))) {
+ if (priv->phase == QAuthenticatorPrivate::Start)
+ priv->phase = QAuthenticatorPrivate::Phase1;
+
pauseConnection();
if (!isProxy) {
if (channels[i].authenticationCredentialsSent) {
@@ -520,31 +485,37 @@ bool QHttpNetworkConnectionPrivate::handleAuthenticateChallenge(QAbstractSocket
return false;
}
-QUrl QHttpNetworkConnectionPrivate::parseRedirectResponse(QAbstractSocket *socket, QHttpNetworkReply *reply)
+// Used by the HTTP1 code-path
+QUrl QHttpNetworkConnectionPrivate::parseRedirectResponse(QAbstractSocket *socket,
+ QHttpNetworkReply *reply)
+{
+ ParseRedirectResult result = parseRedirectResponse(reply);
+ if (result.errorCode != QNetworkReply::NoError) {
+ emitReplyError(socket, reply, result.errorCode);
+ return {};
+ }
+ return std::move(result.redirectUrl);
+}
+
+QHttpNetworkConnectionPrivate::ParseRedirectResult
+QHttpNetworkConnectionPrivate::parseRedirectResponse(QHttpNetworkReply *reply)
{
if (!reply->request().isFollowRedirects())
- return QUrl();
+ return {{}, QNetworkReply::NoError};
QUrl redirectUrl;
- const QList<QPair<QByteArray, QByteArray> > fields = reply->header();
- for (const QNetworkReply::RawHeaderPair &header : fields) {
- if (header.first.compare("location", Qt::CaseInsensitive) == 0) {
- redirectUrl = QUrl::fromEncoded(header.second);
- break;
- }
+ const QHttpHeaders fields = reply->header();
+ if (const auto h = fields.values(QHttpHeaders::WellKnownHeader::Location); !h.empty()) {
+ redirectUrl = QUrl::fromEncoded(h.first());
}
- // If the location url is invalid/empty, we emit ProtocolUnknownError
- if (!redirectUrl.isValid()) {
- emitReplyError(socket, reply, QNetworkReply::ProtocolUnknownError);
- return QUrl();
- }
+ // If the location url is invalid/empty, we return ProtocolUnknownError
+ if (!redirectUrl.isValid())
+ return {{}, QNetworkReply::ProtocolUnknownError};
// Check if we have exceeded max redirects allowed
- if (reply->request().redirectCount() <= 0) {
- emitReplyError(socket, reply, QNetworkReply::TooManyRedirectsError);
- return QUrl();
- }
+ if (reply->request().redirectCount() <= 0)
+ return {{}, QNetworkReply::TooManyRedirectsError};
// Resolve the URL if it's relative
if (redirectUrl.isRelative())
@@ -552,7 +523,7 @@ QUrl QHttpNetworkConnectionPrivate::parseRedirectResponse(QAbstractSocket *socke
// Check redirect url protocol
const QUrl priorUrl(reply->request().url());
- if (redirectUrl.scheme() == QLatin1String("http") || redirectUrl.scheme() == QLatin1String("https")) {
+ if (redirectUrl.scheme() == "http"_L1 || redirectUrl.scheme() == "https"_L1) {
switch (reply->request().redirectPolicy()) {
case QNetworkRequest::NoLessSafeRedirectPolicy:
// Here we could handle https->http redirects as InsecureProtocolError.
@@ -565,8 +536,7 @@ QUrl QHttpNetworkConnectionPrivate::parseRedirectResponse(QAbstractSocket *socke
if (priorUrl.host() != redirectUrl.host()
|| priorUrl.scheme() != redirectUrl.scheme()
|| priorUrl.port() != redirectUrl.port()) {
- emitReplyError(socket, reply, QNetworkReply::InsecureRedirectError);
- return QUrl();
+ return {{}, QNetworkReply::InsecureRedirectError};
}
break;
case QNetworkRequest::UserVerifiedRedirectPolicy:
@@ -575,13 +545,12 @@ QUrl QHttpNetworkConnectionPrivate::parseRedirectResponse(QAbstractSocket *socke
Q_ASSERT(!"Unexpected redirect policy");
}
} else {
- emitReplyError(socket, reply, QNetworkReply::ProtocolUnknownError);
- return QUrl();
+ return {{}, QNetworkReply::ProtocolUnknownError};
}
- return redirectUrl;
+ return {std::move(redirectUrl), QNetworkReply::NoError};
}
-void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request)
+void QHttpNetworkConnectionPrivate::createAuthorization(QIODevice *socket, QHttpNetworkRequest &request)
{
Q_ASSERT(socket);
@@ -591,9 +560,15 @@ void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket,
QAuthenticatorPrivate *priv = QAuthenticatorPrivate::getPrivate(*authenticator);
// Send "Authorization" header, but not if it's NTLM and the socket is already authenticated.
if (priv && priv->method != QAuthenticatorPrivate::None) {
- if ((priv->method != QAuthenticatorPrivate::Ntlm
- && request.headerField("Authorization").isEmpty())
- || channel.lastStatus == 401) {
+ const bool ntlmNego = priv->method == QAuthenticatorPrivate::Ntlm
+ || priv->method == QAuthenticatorPrivate::Negotiate;
+ const bool authNeeded = channel.lastStatus == 401;
+ const bool ntlmNegoOk = ntlmNego && authNeeded
+ && (priv->phase != QAuthenticatorPrivate::Done
+ || !channel.authenticationCredentialsSent);
+ const bool otherOk =
+ !ntlmNego && (authNeeded || request.headerField("Authorization").isEmpty());
+ if (ntlmNegoOk || otherOk) {
QByteArray response = priv->calculateResponse(request.methodName(), request.uri(false),
request.url().host());
request.setHeaderField("Authorization", response);
@@ -606,7 +581,13 @@ void QHttpNetworkConnectionPrivate::createAuthorization(QAbstractSocket *socket,
priv = QAuthenticatorPrivate::getPrivate(*authenticator);
// Send "Proxy-Authorization" header, but not if it's NTLM and the socket is already authenticated.
if (priv && priv->method != QAuthenticatorPrivate::None) {
- if (priv->method != QAuthenticatorPrivate::Ntlm || channel.lastStatus == 407) {
+ const bool ntlmNego = priv->method == QAuthenticatorPrivate::Ntlm
+ || priv->method == QAuthenticatorPrivate::Negotiate;
+ const bool proxyAuthNeeded = channel.lastStatus == 407;
+ const bool ntlmNegoOk = ntlmNego && proxyAuthNeeded
+ && (priv->phase != QAuthenticatorPrivate::Done || !channel.proxyCredentialsSent);
+ const bool otherOk = !ntlmNego;
+ if (ntlmNegoOk || otherOk) {
QByteArray response = priv->calculateResponse(request.methodName(), request.uri(false),
networkProxy.hostName());
request.setHeaderField("Proxy-Authorization", response);
@@ -705,7 +686,7 @@ void QHttpNetworkConnectionPrivate::requeueRequest(const HttpMessagePair &pair)
QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
}
-bool QHttpNetworkConnectionPrivate::dequeueRequest(QAbstractSocket *socket)
+bool QHttpNetworkConnectionPrivate::dequeueRequest(QIODevice *socket)
{
int i = 0;
if (socket)
@@ -771,7 +752,7 @@ void QHttpNetworkConnectionPrivate::fillPipeline(QAbstractSocket *socket)
if (channels[i].reply == nullptr)
return;
- if (! (defaultPipelineLength - channels[i].alreadyPipelinedRequests.length() >= defaultRePipelineLength)) {
+ if (! (defaultPipelineLength - channels[i].alreadyPipelinedRequests.size() >= defaultRePipelineLength)) {
return;
}
@@ -812,28 +793,28 @@ void QHttpNetworkConnectionPrivate::fillPipeline(QAbstractSocket *socket)
int lengthBefore;
while (!highPriorityQueue.isEmpty()) {
- lengthBefore = channels[i].alreadyPipelinedRequests.length();
+ lengthBefore = channels[i].alreadyPipelinedRequests.size();
fillPipeline(highPriorityQueue, channels[i]);
- if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength) {
+ if (channels[i].alreadyPipelinedRequests.size() >= defaultPipelineLength) {
channels[i].pipelineFlush();
return;
}
- if (lengthBefore == channels[i].alreadyPipelinedRequests.length())
+ if (lengthBefore == channels[i].alreadyPipelinedRequests.size())
break; // did not process anything, now do the low prio queue
}
while (!lowPriorityQueue.isEmpty()) {
- lengthBefore = channels[i].alreadyPipelinedRequests.length();
+ lengthBefore = channels[i].alreadyPipelinedRequests.size();
fillPipeline(lowPriorityQueue, channels[i]);
- if (channels[i].alreadyPipelinedRequests.length() >= defaultPipelineLength) {
+ if (channels[i].alreadyPipelinedRequests.size() >= defaultPipelineLength) {
channels[i].pipelineFlush();
return;
}
- if (lengthBefore == channels[i].alreadyPipelinedRequests.length())
+ if (lengthBefore == channels[i].alreadyPipelinedRequests.size())
break; // did not process anything
}
@@ -847,7 +828,7 @@ bool QHttpNetworkConnectionPrivate::fillPipeline(QList<HttpMessagePair> &queue,
if (queue.isEmpty())
return true;
- for (int i = queue.count() - 1; i >= 0; --i) {
+ for (int i = queue.size() - 1; i >= 0; --i) {
HttpMessagePair messagePair = queue.at(i);
const QHttpNetworkRequest &request = messagePair.first;
@@ -914,6 +895,8 @@ QString QHttpNetworkConnectionPrivate::errorDetail(QNetworkReply::NetworkError e
break;
case QNetworkReply::SslHandshakeFailedError:
errorString = QCoreApplication::translate("QHttp", "SSL handshake failed");
+ if (socket)
+ errorString += ": "_L1 + socket->errorString();
break;
case QNetworkReply::TooManyRedirectsError:
errorString = QCoreApplication::translate("QHttp", "Too many redirects");
@@ -967,7 +950,7 @@ void QHttpNetworkConnectionPrivate::removeReply(QHttpNetworkReply *reply)
}
// is the reply inside the pipeline of this channel already?
- for (int j = 0; j < channels[i].alreadyPipelinedRequests.length(); j++) {
+ for (int j = 0; j < channels[i].alreadyPipelinedRequests.size(); j++) {
if (channels[i].alreadyPipelinedRequests.at(j).second == reply) {
// Remove that HttpMessagePair
channels[i].alreadyPipelinedRequests.removeAt(j);
@@ -985,23 +968,22 @@ void QHttpNetworkConnectionPrivate::removeReply(QHttpNetworkReply *reply)
return;
}
}
-#ifndef QT_NO_SSL
// is the reply inside the H2 pipeline of this channel already?
- QMultiMap<int, HttpMessagePair>::iterator it = channels[i].h2RequestsToSend.begin();
- QMultiMap<int, HttpMessagePair>::iterator end = channels[i].h2RequestsToSend.end();
- for (; it != end; ++it) {
- if (it.value().second == reply) {
- channels[i].h2RequestsToSend.remove(it.key());
-
- QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
- return;
- }
+ const auto foundReply = [reply](const HttpMessagePair &pair) {
+ return pair.second == reply;
+ };
+ auto &seq = channels[i].h2RequestsToSend;
+ const auto end = seq.cend();
+ auto it = std::find_if(seq.cbegin(), end, foundReply);
+ if (it != end) {
+ seq.erase(it);
+ QMetaObject::invokeMethod(q, "_q_startNextRequest", Qt::QueuedConnection);
+ return;
}
-#endif
}
// remove from the high priority queue
if (!highPriorityQueue.isEmpty()) {
- for (int j = highPriorityQueue.count() - 1; j >= 0; --j) {
+ for (int j = highPriorityQueue.size() - 1; j >= 0; --j) {
HttpMessagePair messagePair = highPriorityQueue.at(j);
if (messagePair.second == reply) {
highPriorityQueue.removeAt(j);
@@ -1012,7 +994,7 @@ void QHttpNetworkConnectionPrivate::removeReply(QHttpNetworkReply *reply)
}
// remove from the low priority queue
if (!lowPriorityQueue.isEmpty()) {
- for (int j = lowPriorityQueue.count() - 1; j >= 0; --j) {
+ for (int j = lowPriorityQueue.size() - 1; j >= 0; --j) {
HttpMessagePair messagePair = lowPriorityQueue.at(j);
if (messagePair.second == reply) {
lowPriorityQueue.removeAt(j);
@@ -1041,6 +1023,11 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
//resend the necessary ones.
for (int i = 0; i < activeChannelCount; ++i) {
if (channels[i].resendCurrent && (channels[i].state != QHttpNetworkConnectionChannel::ClosingState)) {
+ if (!channels[i].socket
+ || channels[i].socket->state() == QAbstractSocket::UnconnectedState) {
+ if (!channels[i].ensureConnection())
+ continue;
+ }
channels[i].resendCurrent = false;
// if this is not possible, error will be emitted and connection terminated
@@ -1115,7 +1102,7 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
// If there is not already any connected channels we need to connect a new one.
// We do not pair the channel with the request until we know if it is
// connected or not. This is to reuse connected channels before we connect new once.
- int queuedRequests = highPriorityQueue.count() + lowPriorityQueue.count();
+ int queuedRequests = highPriorityQueue.size() + lowPriorityQueue.size();
// in case we have in-flight preconnect requests and normal requests,
// we only need one socket for each (preconnect, normal request) pair
@@ -1128,7 +1115,7 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
if (neededOpenChannels <= 0)
return;
- QQueue<int> channelsToConnect;
+ QVarLengthArray<int> channelsToConnect;
// use previously used channels first
for (int i = 0; i < activeChannelCount && neededOpenChannels > 0; ++i) {
@@ -1144,7 +1131,7 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
if (!channels[i].reply && !channels[i].isSocketBusy()
&& (channels[i].socket->state() == QAbstractSocket::UnconnectedState)) {
- channelsToConnect.enqueue(i);
+ channelsToConnect.push_back(i);
neededOpenChannels--;
}
}
@@ -1154,12 +1141,14 @@ void QHttpNetworkConnectionPrivate::_q_startNextRequest()
if (channels[i].socket)
continue;
- channelsToConnect.enqueue(i);
+ channelsToConnect.push_back(i);
neededOpenChannels--;
}
- while (!channelsToConnect.isEmpty()) {
- const int channel = channelsToConnect.dequeue();
+ auto channelToConnectSpan = QSpan{channelsToConnect};
+ while (!channelToConnectSpan.isEmpty()) {
+ const int channel = channelToConnectSpan.front();
+ channelToConnectSpan = channelToConnectSpan.sliced(1);
if (networkLayerState == IPv4)
channels[channel].networkLayerPreference = QAbstractSocket::IPv4Protocol;
@@ -1263,23 +1252,30 @@ void QHttpNetworkConnectionPrivate::_q_hostLookupFinished(const QHostInfo &info)
networkLayerState = QHttpNetworkConnectionPrivate::IPv6;
QMetaObject::invokeMethod(this->q_func(), "_q_startNextRequest", Qt::QueuedConnection);
} else {
+ auto lookupError = QNetworkReply::HostNotFoundError;
+#ifndef QT_NO_NETWORKPROXY
+ // if the proxy can lookup hostnames, all hostname lookups except for the lookup of the
+ // proxy hostname are delegated to the proxy.
+ auto proxyCapabilities = networkProxy.capabilities() | channels[0].proxy.capabilities();
+ if (proxyCapabilities & QNetworkProxy::HostNameLookupCapability)
+ lookupError = QNetworkReply::ProxyNotFoundError;
+#endif
if (dequeueRequest(channels[0].socket)) {
- emitReplyError(channels[0].socket, channels[0].reply, QNetworkReply::HostNotFoundError);
+ emitReplyError(channels[0].socket, channels[0].reply, lookupError);
networkLayerState = QHttpNetworkConnectionPrivate::Unknown;
} else if (connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2
|| connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
- for (const HttpMessagePair &h2Pair : qAsConst(channels[0].h2RequestsToSend)) {
+ for (const HttpMessagePair &h2Pair : std::as_const(channels[0].h2RequestsToSend)) {
// emit error for all replies
QHttpNetworkReply *currentReply = h2Pair.second;
Q_ASSERT(currentReply);
- emitReplyError(channels[0].socket, currentReply, QNetworkReply::HostNotFoundError);
+ emitReplyError(channels[0].socket, currentReply, lookupError);
}
} else {
- // Should not happen: we start a host lookup before sending a request,
- // so it's natural to have requests either in HTTP/2 queue, or in low/high
- // priority queues.
- qWarning("QHttpNetworkConnectionPrivate::_q_hostLookupFinished"
- " could not de-queue request, failed to report HostNotFoundError");
+ // We can end up here if a request has been aborted or otherwise failed (e.g. timeout)
+ // before the host lookup was finished.
+ qDebug("QHttpNetworkConnectionPrivate::_q_hostLookupFinished"
+ " could not de-queue request, failed to report HostNotFoundError");
networkLayerState = QHttpNetworkConnectionPrivate::Unknown;
}
}
@@ -1332,18 +1328,6 @@ void QHttpNetworkConnectionPrivate::_q_connectDelayedChannel()
channels[1].ensureConnection();
}
-QHttpNetworkConnection::QHttpNetworkConnection(const QString &hostName, quint16 port, bool encrypt,
- QHttpNetworkConnection::ConnectionType connectionType, QObject *parent)
- : QObject(*(new QHttpNetworkConnectionPrivate(hostName, port, encrypt , connectionType)), parent)
-{
- Q_D(QHttpNetworkConnection);
- d->init();
- if (QNetworkConnectionMonitor::isEnabled()) {
- connect(&d->connectionMonitor, &QNetworkConnectionMonitor::reachabilityChanged,
- this, &QHttpNetworkConnection::onlineStateChanged, Qt::QueuedConnection);
- }
-}
-
QHttpNetworkConnection::QHttpNetworkConnection(quint16 connectionCount, const QString &hostName,
quint16 port, bool encrypt, QObject *parent,
QHttpNetworkConnection::ConnectionType connectionType)
@@ -1404,7 +1388,7 @@ void QHttpNetworkConnection::setCacheProxy(const QNetworkProxy &networkProxy)
d->networkProxy = networkProxy;
// update the authenticator
if (!d->networkProxy.user().isEmpty()) {
- for (int i = 0; i < d->activeChannelCount; ++i) {
+ for (int i = 0; i < d->channelCount; ++i) {
d->channels[i].proxyAuthenticator.setUser(d->networkProxy.user());
d->channels[i].proxyAuthenticator.setPassword(d->networkProxy.password());
}
@@ -1420,7 +1404,7 @@ QNetworkProxy QHttpNetworkConnection::cacheProxy() const
void QHttpNetworkConnection::setTransparentProxy(const QNetworkProxy &networkProxy)
{
Q_D(QHttpNetworkConnection);
- for (int i = 0; i < d->activeChannelCount; ++i)
+ for (int i = 0; i < d->channelCount; ++i)
d->channels[i].setProxy(networkProxy);
}
@@ -1431,9 +1415,9 @@ QNetworkProxy QHttpNetworkConnection::transparentProxy() const
}
#endif
-QHttpNetworkConnection::ConnectionType QHttpNetworkConnection::connectionType()
+QHttpNetworkConnection::ConnectionType QHttpNetworkConnection::connectionType() const
{
- Q_D(QHttpNetworkConnection);
+ Q_D(const QHttpNetworkConnection);
return d->connectionType;
}
@@ -1468,9 +1452,9 @@ void QHttpNetworkConnection::setSslConfiguration(const QSslConfiguration &config
d->channels[i].setSslConfiguration(config);
}
-std::shared_ptr<QSslContext> QHttpNetworkConnection::sslContext()
+std::shared_ptr<QSslContext> QHttpNetworkConnection::sslContext() const
{
- Q_D(QHttpNetworkConnection);
+ Q_D(const QHttpNetworkConnection);
return d->sslContext;
}
@@ -1566,12 +1550,12 @@ void QHttpNetworkConnectionPrivate::emitProxyAuthenticationRequired(const QHttpN
pauseConnection();
QHttpNetworkReply *reply;
if ((connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2
- && (chan->switchedToHttp2 || chan->h2RequestsToSend.count() > 0))
+ && (chan->switchedToHttp2 || chan->h2RequestsToSend.size() > 0))
|| connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
// we choose the reply to emit the proxyAuth signal from somewhat arbitrarily,
// but that does not matter because the signal will ultimately be emitted
// by the QNetworkAccessManager.
- Q_ASSERT(chan->h2RequestsToSend.count() > 0);
+ Q_ASSERT(chan->h2RequestsToSend.size() > 0);
reply = chan->h2RequestsToSend.cbegin().value().second;
} else { // HTTP
reply = chan->reply;
diff --git a/src/network/access/qhttpnetworkconnection_p.h b/src/network/access/qhttpnetworkconnection_p.h
index f0d67d6594..36234a24ba 100644
--- a/src/network/access/qhttpnetworkconnection_p.h
+++ b/src/network/access/qhttpnetworkconnection_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QHTTPNETWORKCONNECTION_H
#define QHTTPNETWORKCONNECTION_H
@@ -88,7 +52,7 @@ class QSslContext;
#endif // !QT_NO_SSL
class QHttpNetworkConnectionPrivate;
-class Q_AUTOTEST_EXPORT QHttpNetworkConnection : public QObject
+class Q_NETWORK_EXPORT QHttpNetworkConnection : public QObject
{
Q_OBJECT
public:
@@ -99,9 +63,6 @@ public:
ConnectionTypeHTTP2Direct
};
- explicit QHttpNetworkConnection(const QString &hostName, quint16 port = 80, bool encrypt = false,
- ConnectionType connectionType = ConnectionTypeHTTP,
- QObject *parent = nullptr);
QHttpNetworkConnection(quint16 channelCount, const QString &hostName, quint16 port = 80,
bool encrypt = false, QObject *parent = nullptr,
ConnectionType connectionType = ConnectionTypeHTTP);
@@ -128,7 +89,7 @@ public:
QHttpNetworkConnectionChannel *channels() const;
- ConnectionType connectionType();
+ ConnectionType connectionType() const;
void setConnectionType(ConnectionType type);
QHttp2Configuration http2Parameters() const;
@@ -138,7 +99,7 @@ public:
void setSslConfiguration(const QSslConfiguration &config);
void ignoreSslErrors(int channel = -1);
void ignoreSslErrors(const QList<QSslError> &errors, int channel = -1);
- std::shared_ptr<QSslContext> sslContext();
+ std::shared_ptr<QSslContext> sslContext() const;
void setSslContext(std::shared_ptr<QSslContext> context);
#endif
@@ -173,8 +134,10 @@ typedef QPair<QHttpNetworkRequest, QHttpNetworkReply*> HttpMessagePair;
class QHttpNetworkConnectionPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QHttpNetworkConnection)
+ Q_DISABLE_COPY_MOVE(QHttpNetworkConnectionPrivate)
public:
- static const int defaultHttpChannelCount;
+ // Note: Only used from auto tests, normal usage is via QHttp1Configuration
+ static constexpr int defaultHttpChannelCount = 6;
static const int defaultPipelineLength;
static const int defaultRePipelineLength;
@@ -191,26 +154,24 @@ public:
IPv4or6
};
- QHttpNetworkConnectionPrivate(const QString &hostName, quint16 port, bool encrypt,
- QHttpNetworkConnection::ConnectionType type);
- QHttpNetworkConnectionPrivate(quint16 channelCount, const QString &hostName, quint16 port, bool encrypt,
- QHttpNetworkConnection::ConnectionType type);
+ QHttpNetworkConnectionPrivate(quint16 connectionCount, const QString &hostName, quint16 port,
+ bool encrypt, QHttpNetworkConnection::ConnectionType type);
~QHttpNetworkConnectionPrivate();
void init();
void pauseConnection();
void resumeConnection();
- ConnectionState state;
- NetworkLayerPreferenceState networkLayerState;
+ ConnectionState state = RunningState;
+ NetworkLayerPreferenceState networkLayerState = Unknown;
enum { ChunkSize = 4096 };
- int indexOf(QAbstractSocket *socket) const;
+ int indexOf(QIODevice *socket) const;
QHttpNetworkReply *queueRequest(const QHttpNetworkRequest &request);
void requeueRequest(const HttpMessagePair &pair); // e.g. after pipeline broke
void fillHttp2Queue();
- bool dequeueRequest(QAbstractSocket *socket);
+ bool dequeueRequest(QIODevice *socket);
void prepareRequest(HttpMessagePair &request);
void updateChannel(int i, const HttpMessagePair &messagePair);
QHttpNetworkRequest predictNextRequest() const;
@@ -234,7 +195,7 @@ public:
void _q_hostLookupFinished(const QHostInfo &info);
void _q_connectDelayedChannel();
- void createAuthorization(QAbstractSocket *socket, QHttpNetworkRequest &request);
+ void createAuthorization(QIODevice *socket, QHttpNetworkRequest &request);
QString errorDetail(QNetworkReply::NetworkError errorCode, QAbstractSocket *socket,
const QString &extraDetail = QString());
@@ -244,15 +205,15 @@ public:
QString hostName;
quint16 port;
bool encrypt;
- bool delayIpv4;
+ bool delayIpv4 = true;
// Number of channels we are trying to use at the moment:
int activeChannelCount;
// The total number of channels we reserved:
const int channelCount;
QTimer delayedConnectionTimer;
- QHttpNetworkConnectionChannel *channels; // parallel connections to the server
- bool shouldEmitChannelError(QAbstractSocket *socket);
+ QHttpNetworkConnectionChannel * const channels; // parallel connections to the server
+ bool shouldEmitChannelError(QIODevice *socket);
qint64 uncompressedBytesAvailable(const QHttpNetworkReply &reply) const;
qint64 uncompressedBytesAvailableNextBlock(const QHttpNetworkReply &reply) const;
@@ -260,6 +221,12 @@ public:
void emitReplyError(QAbstractSocket *socket, QHttpNetworkReply *reply, QNetworkReply::NetworkError errorCode);
bool handleAuthenticateChallenge(QAbstractSocket *socket, QHttpNetworkReply *reply, bool isProxy, bool &resend);
+ struct ParseRedirectResult {
+ QUrl redirectUrl;
+ QNetworkReply::NetworkError errorCode;
+ };
+ static ParseRedirectResult parseRedirectResponse(QHttpNetworkReply *reply);
+ // Used by the HTTP1 code-path
QUrl parseRedirectResponse(QAbstractSocket *socket, QHttpNetworkReply *reply);
#ifndef QT_NO_NETWORKPROXY
@@ -271,7 +238,7 @@ public:
QList<HttpMessagePair> highPriorityQueue;
QList<HttpMessagePair> lowPriorityQueue;
- int preConnectRequests;
+ int preConnectRequests = 0;
QHttpNetworkConnection::ConnectionType connectionType;
diff --git a/src/network/access/qhttpnetworkconnectionchannel.cpp b/src/network/access/qhttpnetworkconnectionchannel.cpp
index c8bea484a9..6766989690 100644
--- a/src/network/access/qhttpnetworkconnectionchannel.cpp
+++ b/src/network/access/qhttpnetworkconnectionchannel.cpp
@@ -1,49 +1,12 @@
-/****************************************************************************
-**
-** 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 "qhttpnetworkconnectionchannel_p.h"
#include "qhttpnetworkconnection_p.h"
#include "qhttp2configuration.h"
#include "private/qnoncontiguousbytedevice_p.h"
-#include <qpair.h>
#include <qdebug.h>
#include <private/qhttp2protocolhandler_p.h>
@@ -58,6 +21,9 @@
#include "private/qnetconmonitor_p.h"
+#include <memory>
+#include <utility>
+
QT_BEGIN_NAMESPACE
namespace
@@ -242,7 +208,7 @@ void QHttpNetworkConnectionChannel::abort()
bool QHttpNetworkConnectionChannel::sendRequest()
{
- Q_ASSERT(!protocolHandler.isNull());
+ Q_ASSERT(protocolHandler);
return protocolHandler->sendRequest();
}
@@ -255,7 +221,7 @@ bool QHttpNetworkConnectionChannel::sendRequest()
void QHttpNetworkConnectionChannel::sendRequestDelayed()
{
QMetaObject::invokeMethod(this, [this] {
- Q_ASSERT(!protocolHandler.isNull());
+ Q_ASSERT(protocolHandler);
if (reply)
protocolHandler->sendRequest();
}, Qt::ConnectionType::QueuedConnection);
@@ -263,13 +229,13 @@ void QHttpNetworkConnectionChannel::sendRequestDelayed()
void QHttpNetworkConnectionChannel::_q_receiveReply()
{
- Q_ASSERT(!protocolHandler.isNull());
+ Q_ASSERT(protocolHandler);
protocolHandler->_q_receiveReply();
}
void QHttpNetworkConnectionChannel::_q_readyRead()
{
- Q_ASSERT(!protocolHandler.isNull());
+ Q_ASSERT(protocolHandler);
protocolHandler->_q_readyRead();
}
@@ -358,9 +324,9 @@ bool QHttpNetworkConnectionChannel::ensureConnection()
QHttpNetworkReply *potentialReply = connection->d_func()->predictNextRequestsReply();
if (potentialReply) {
- QMetaObject::invokeMethod(potentialReply, "socketConnecting", Qt::QueuedConnection);
- } else if (h2RequestsToSend.count() > 0) {
- QMetaObject::invokeMethod(h2RequestsToSend.values().at(0).second, "socketConnecting", Qt::QueuedConnection);
+ QMetaObject::invokeMethod(potentialReply, "socketStartedConnecting", Qt::QueuedConnection);
+ } else if (!h2RequestsToSend.isEmpty()) {
+ QMetaObject::invokeMethod(std::as_const(h2RequestsToSend).first().second, "socketStartedConnecting", Qt::QueuedConnection);
}
#ifndef QT_NO_NETWORKPROXY
@@ -378,8 +344,8 @@ bool QHttpNetworkConnectionChannel::ensureConnection()
if (connection->connectionType()
== QHttpNetworkConnection::ConnectionTypeHTTP2Direct
|| (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2
- && h2RequestsToSend.count() > 0)) {
- value = h2RequestsToSend.first().first.headerField("user-agent");
+ && !h2RequestsToSend.isEmpty())) {
+ value = std::as_const(h2RequestsToSend).first().first.headerField("user-agent");
} else {
value = connection->d_func()->predictNextRequest().headerField("user-agent");
}
@@ -478,18 +444,18 @@ void QHttpNetworkConnectionChannel::allDone()
// trick with ProtocolHandlerDeleter, a QObject-derived class.
// These dances below just make it somewhat exception-safe.
// 1. Create a new owner:
- QAbstractProtocolHandler *oldHandler = protocolHandler.data();
- QScopedPointer<ProtocolHandlerDeleter> deleter(new ProtocolHandlerDeleter(oldHandler));
+ QAbstractProtocolHandler *oldHandler = protocolHandler.get();
+ auto deleter = std::make_unique<ProtocolHandlerDeleter>(oldHandler);
// 2. Retire the old one:
- protocolHandler.take();
+ Q_UNUSED(protocolHandler.release());
// 3. Call 'deleteLater':
deleter->deleteLater();
// 3. Give up the ownerthip:
- deleter.take();
+ Q_UNUSED(deleter.release());
connection->fillHttp2Queue();
protocolHandler.reset(new QHttp2ProtocolHandler(this));
- QHttp2ProtocolHandler *h2c = static_cast<QHttp2ProtocolHandler *>(protocolHandler.data());
+ QHttp2ProtocolHandler *h2c = static_cast<QHttp2ProtocolHandler *>(protocolHandler.get());
QMetaObject::invokeMethod(h2c, "_q_receiveReply", Qt::QueuedConnection);
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
// If we only had one request sent with H2 allowed, we may fail to send
@@ -607,7 +573,7 @@ void QHttpNetworkConnectionChannel::detectPipeliningSupport()
// called when the connection broke and we need to queue some pipelined requests again
void QHttpNetworkConnectionChannel::requeueCurrentlyPipelinedRequests()
{
- for (int i = 0; i < alreadyPipelinedRequests.length(); i++)
+ for (int i = 0; i < alreadyPipelinedRequests.size(); i++)
connection->d_func()->requeueRequest(alreadyPipelinedRequests.at(i));
alreadyPipelinedRequests.clear();
@@ -863,7 +829,7 @@ void QHttpNetworkConnectionChannel::_q_disconnected()
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
}
state = QHttpNetworkConnectionChannel::IdleState;
- if (alreadyPipelinedRequests.length()) {
+ if (alreadyPipelinedRequests.size()) {
// If nothing was in a pipeline, no need in calling
// _q_startNextRequest (which it does):
requeueCurrentlyPipelinedRequests();
@@ -890,6 +856,8 @@ void QHttpNetworkConnectionChannel::_q_connected()
connection->d_func()->networkLayerState = QHttpNetworkConnectionPrivate::IPv6;
}
connection->d_func()->networkLayerDetected(networkLayerPreference);
+ if (connection->d_func()->activeChannelCount > 1 && !connection->d_func()->encrypt)
+ QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
} else {
bool anyProtocol = networkLayerPreference == QAbstractSocket::AnyIPProtocol;
if (((connection->d_func()->networkLayerState == QHttpNetworkConnectionPrivate::IPv4)
@@ -942,7 +910,7 @@ void QHttpNetworkConnectionChannel::_q_connected()
} else if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
state = QHttpNetworkConnectionChannel::IdleState;
protocolHandler.reset(new QHttp2ProtocolHandler(this));
- if (h2RequestsToSend.count() > 0) {
+ if (h2RequestsToSend.size() > 0) {
// In case our peer has sent us its settings (window size, max concurrent streams etc.)
// let's give _q_receiveReply a chance to read them first ('invokeMethod', QueuedConnection).
QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
@@ -983,6 +951,10 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
break;
case QAbstractSocket::ConnectionRefusedError:
errorCode = QNetworkReply::ConnectionRefusedError;
+#ifndef QT_NO_NETWORKPROXY
+ if (connection->d_func()->networkProxy.type() != QNetworkProxy::NoProxy && !ssl)
+ errorCode = QNetworkReply::ProxyConnectionRefusedError;
+#endif
break;
case QAbstractSocket::RemoteHostClosedError:
// This error for SSL comes twice in a row, first from SSL layer ("The TLS/SSL connection has been closed") then from TCP layer.
@@ -995,11 +967,11 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
// we do not resend, but must report errors if any request is in progress (note, while
// not in its sendRequest(), protocol handler switches the channel to IdleState, thus
// this check is under this condition in 'if'):
- if (protocolHandler.data()) {
+ if (protocolHandler) {
if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct
|| (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2
&& switchedToHttp2)) {
- auto h2Handler = static_cast<QHttp2ProtocolHandler *>(protocolHandler.data());
+ auto h2Handler = static_cast<QHttp2ProtocolHandler *>(protocolHandler.get());
h2Handler->handleConnectionClosure();
}
}
@@ -1064,6 +1036,9 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
}
errorCode = QNetworkReply::TimeoutError;
break;
+ case QAbstractSocket::ProxyConnectionRefusedError:
+ errorCode = QNetworkReply::ProxyConnectionRefusedError;
+ break;
case QAbstractSocket::ProxyAuthenticationRequiredError:
errorCode = QNetworkReply::ProxyAuthenticationRequiredError;
break;
@@ -1121,16 +1096,15 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2
|| connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
- QList<HttpMessagePair> h2Pairs = h2RequestsToSend.values();
- for (int a = 0; a < h2Pairs.count(); ++a) {
+ const auto h2RequestsToSendCopy = std::exchange(h2RequestsToSend, {});
+ for (const auto &httpMessagePair : h2RequestsToSendCopy) {
// emit error for all replies
- QHttpNetworkReply *currentReply = h2Pairs.at(a).second;
+ QHttpNetworkReply *currentReply = httpMessagePair.second;
currentReply->d_func()->errorString = errorString;
currentReply->d_func()->httpErrorCode = errorCode;
Q_ASSERT(currentReply);
emit currentReply->finishedWithError(errorCode, errorString);
}
- h2RequestsToSend.clear();
}
// send the next request
@@ -1154,9 +1128,9 @@ void QHttpNetworkConnectionChannel::_q_error(QAbstractSocket::SocketError socket
void QHttpNetworkConnectionChannel::_q_proxyAuthenticationRequired(const QNetworkProxy &proxy, QAuthenticator* auth)
{
if ((connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2
- && (switchedToHttp2 || h2RequestsToSend.count() > 0))
+ && (switchedToHttp2 || h2RequestsToSend.size() > 0))
|| connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
- if (h2RequestsToSend.count() > 0)
+ if (h2RequestsToSend.size() > 0)
connection->d_func()->emitProxyAuthenticationRequired(this, proxy, auth);
} else { // HTTP
// Need to dequeue the request before we can emit the error.
@@ -1179,9 +1153,9 @@ void QHttpNetworkConnectionChannel::emitFinishedWithError(QNetworkReply::Network
{
if (reply)
emit reply->finishedWithError(error, QHttpNetworkConnectionChannel::tr(message));
- QList<HttpMessagePair> h2Pairs = h2RequestsToSend.values();
- for (int a = 0; a < h2Pairs.count(); ++a) {
- QHttpNetworkReply *currentReply = h2Pairs.at(a).second;
+ const auto h2RequestsToSendCopy = h2RequestsToSend;
+ for (const auto &httpMessagePair : h2RequestsToSendCopy) {
+ QHttpNetworkReply *currentReply = httpMessagePair.second;
Q_ASSERT(currentReply);
emit currentReply->finishedWithError(error, QHttpNetworkConnectionChannel::tr(message));
}
@@ -1262,14 +1236,12 @@ void QHttpNetworkConnectionChannel::_q_encrypted()
if (connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2 ||
connection->connectionType() == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
- if (h2RequestsToSend.count() > 0) {
+ if (!h2RequestsToSend.isEmpty()) {
// Similar to HTTP/1.1 counterpart below:
- const auto &h2Pairs = h2RequestsToSend.values(); // (request, reply)
- const auto &pair = h2Pairs.first();
+ const auto &pair = std::as_const(h2RequestsToSend).first();
emit pair.second->encrypted();
// In case our peer has sent us its settings (window size, max concurrent streams etc.)
// let's give _q_receiveReply a chance to read them first ('invokeMethod', QueuedConnection).
- QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
}
} else { // HTTP
if (!reply)
@@ -1282,14 +1254,14 @@ void QHttpNetworkConnectionChannel::_q_encrypted()
if (reply)
sendRequestDelayed();
}
+ QMetaObject::invokeMethod(connection, "_q_startNextRequest", Qt::QueuedConnection);
}
void QHttpNetworkConnectionChannel::requeueHttp2Requests()
{
- QList<HttpMessagePair> h2Pairs = h2RequestsToSend.values();
- for (int a = 0; a < h2Pairs.count(); ++a)
- connection->d_func()->requeueRequest(h2Pairs.at(a));
- h2RequestsToSend.clear();
+ const auto h2RequestsToSendCopy = std::exchange(h2RequestsToSend, {});
+ for (const auto &httpMessagePair : h2RequestsToSendCopy)
+ connection->d_func()->requeueRequest(httpMessagePair);
}
void QHttpNetworkConnectionChannel::_q_sslErrors(const QList<QSslError> &errors)
@@ -1308,10 +1280,10 @@ void QHttpNetworkConnectionChannel::_q_sslErrors(const QList<QSslError> &errors)
}
#ifndef QT_NO_SSL
else { // HTTP/2
- QList<HttpMessagePair> h2Pairs = h2RequestsToSend.values();
- for (int a = 0; a < h2Pairs.count(); ++a) {
+ const auto h2RequestsToSendCopy = h2RequestsToSend;
+ for (const auto &httpMessagePair : h2RequestsToSendCopy) {
// emit SSL errors for all replies
- QHttpNetworkReply *currentReply = h2Pairs.at(a).second;
+ QHttpNetworkReply *currentReply = httpMessagePair.second;
Q_ASSERT(currentReply);
emit currentReply->sslErrors(errors);
}
@@ -1331,10 +1303,10 @@ void QHttpNetworkConnectionChannel::_q_preSharedKeyAuthenticationRequired(QSslPr
if (reply)
emit reply->preSharedKeyAuthenticationRequired(authenticator);
} else {
- QList<HttpMessagePair> h2Pairs = h2RequestsToSend.values();
- for (int a = 0; a < h2Pairs.count(); ++a) {
+ const auto h2RequestsToSendCopy = h2RequestsToSend;
+ for (const auto &httpMessagePair : h2RequestsToSendCopy) {
// emit SSL errors for all replies
- QHttpNetworkReply *currentReply = h2Pairs.at(a).second;
+ QHttpNetworkReply *currentReply = httpMessagePair.second;
Q_ASSERT(currentReply);
emit currentReply->preSharedKeyAuthenticationRequired(authenticator);
}
diff --git a/src/network/access/qhttpnetworkconnectionchannel_p.h b/src/network/access/qhttpnetworkconnectionchannel_p.h
index ecf1e20106..c42290feca 100644
--- a/src/network/access/qhttpnetworkconnectionchannel_p.h
+++ b/src/network/access/qhttpnetworkconnectionchannel_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QHTTPNETWORKCONNECTIONCHANNEL_H
#define QHTTPNETWORKCONNECTIONCHANNEL_H
@@ -76,8 +40,11 @@
# include <QtNetwork/qtcpsocket.h>
#endif
+#include <QtCore/qpointer.h>
#include <QtCore/qscopedpointer.h>
+#include <memory>
+
QT_REQUIRE_CONFIG(http);
QT_BEGIN_NAMESPACE
@@ -120,7 +87,7 @@ public:
QAuthenticator proxyAuthenticator;
bool authenticationCredentialsSent;
bool proxyCredentialsSent;
- QScopedPointer<QAbstractProtocolHandler> protocolHandler;
+ std::unique_ptr<QAbstractProtocolHandler> protocolHandler;
QMultiMap<int, HttpMessagePair> h2RequestsToSend;
bool switchedToHttp2 = false;
#ifndef QT_NO_SSL
diff --git a/src/network/access/qhttpnetworkheader.cpp b/src/network/access/qhttpnetworkheader.cpp
index 78b79d89e5..7f9c94dc9c 100644
--- a/src/network/access/qhttpnetworkheader.cpp
+++ b/src/network/access/qhttpnetworkheader.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qhttpnetworkheader_p.h"
@@ -65,7 +29,7 @@ void QHttpNetworkHeaderPrivate::setContentLength(qint64 length)
setHeaderField("Content-Length", QByteArray::number(length));
}
-QByteArray QHttpNetworkHeaderPrivate::headerField(const QByteArray &name, const QByteArray &defaultValue) const
+QByteArray QHttpNetworkHeaderPrivate::headerField(QByteArrayView name, const QByteArray &defaultValue) const
{
QList<QByteArray> allValues = headerFieldValues(name);
if (allValues.isEmpty())
@@ -74,7 +38,7 @@ QByteArray QHttpNetworkHeaderPrivate::headerField(const QByteArray &name, const
return allValues.join(", ");
}
-QList<QByteArray> QHttpNetworkHeaderPrivate::headerFieldValues(const QByteArray &name) const
+QList<QByteArray> QHttpNetworkHeaderPrivate::headerFieldValues(QByteArrayView name) const
{
return parser.headerFieldValues(name);
}
@@ -89,7 +53,7 @@ void QHttpNetworkHeaderPrivate::prependHeaderField(const QByteArray &name, const
parser.prependHeaderField(name, data);
}
-QList<QPair<QByteArray, QByteArray> > QHttpNetworkHeaderPrivate::headers() const
+QHttpHeaders QHttpNetworkHeaderPrivate::headers() const
{
return parser.headers();
}
diff --git a/src/network/access/qhttpnetworkheader_p.h b/src/network/access/qhttpnetworkheader_p.h
index 8447474158..afbc6cb6fe 100644
--- a/src/network/access/qhttpnetworkheader_p.h
+++ b/src/network/access/qhttpnetworkheader_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QHTTPNETWORKHEADER_H
#define QHTTPNETWORKHEADER_H
@@ -53,6 +17,7 @@
#include <QtNetwork/private/qtnetworkglobal_p.h>
#include <QtNetwork/private/qhttpheaderparser_p.h>
+#include <QtNetwork/qhttpheaders.h>
#include <qshareddata.h>
#include <qurl.h>
@@ -76,8 +41,8 @@ public:
virtual qint64 contentLength() const = 0;
virtual void setContentLength(qint64 length) = 0;
- virtual QList<QPair<QByteArray, QByteArray> > header() const = 0;
- virtual QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue = QByteArray()) const = 0;
+ virtual QHttpHeaders header() const = 0;
+ virtual QByteArray headerField(QByteArrayView name, const QByteArray &defaultValue = QByteArray()) const = 0;
virtual void setHeaderField(const QByteArray &name, const QByteArray &data) = 0;
};
@@ -92,12 +57,12 @@ public:
qint64 contentLength() const;
void setContentLength(qint64 length);
- QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue = QByteArray()) const;
- QList<QByteArray> headerFieldValues(const QByteArray &name) const;
+ QByteArray headerField(QByteArrayView name, const QByteArray &defaultValue = QByteArray()) const;
+ QList<QByteArray> headerFieldValues(QByteArrayView name) const;
void setHeaderField(const QByteArray &name, const QByteArray &data);
void prependHeaderField(const QByteArray &name, const QByteArray &data);
void clearHeaders();
- QList<QPair<QByteArray, QByteArray> > headers() const;
+ QHttpHeaders headers() const;
bool operator==(const QHttpNetworkHeaderPrivate &other) const;
};
diff --git a/src/network/access/qhttpnetworkreply.cpp b/src/network/access/qhttpnetworkreply.cpp
index 7582ce179f..5711c96b18 100644
--- a/src/network/access/qhttpnetworkreply.cpp
+++ b/src/network/access/qhttpnetworkreply.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qhttpnetworkreply_p.h"
#include "qhttpnetworkconnection_p.h"
@@ -50,6 +14,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
QHttpNetworkReply::QHttpNetworkReply(const QUrl &url, QObject *parent)
: QObject(*new QHttpNetworkReplyPrivate(url), parent)
{
@@ -101,12 +67,12 @@ void QHttpNetworkReply::setContentLength(qint64 length)
d->setContentLength(length);
}
-QList<QPair<QByteArray, QByteArray> > QHttpNetworkReply::header() const
+QHttpHeaders QHttpNetworkReply::header() const
{
return d_func()->parser.headers();
}
-QByteArray QHttpNetworkReply::headerField(const QByteArray &name, const QByteArray &defaultValue) const
+QByteArray QHttpNetworkReply::headerField(QByteArrayView name, const QByteArray &defaultValue) const
{
return d_func()->headerField(name, defaultValue);
}
@@ -123,7 +89,7 @@ void QHttpNetworkReply::appendHeaderField(const QByteArray &name, const QByteArr
d->appendHeaderField(name, data);
}
-void QHttpNetworkReply::parseHeader(const QByteArray &header)
+void QHttpNetworkReply::parseHeader(QByteArrayView header)
{
Q_D(QHttpNetworkReply);
d->parseHeader(header);
@@ -352,8 +318,7 @@ QHttpNetworkReplyPrivate::QHttpNetworkReplyPrivate(const QUrl &newUrl)
{
QString scheme = newUrl.scheme();
- if (scheme == QLatin1String("preconnect-http")
- || scheme == QLatin1String("preconnect-https"))
+ if (scheme == "preconnect-http"_L1 || scheme == "preconnect-https"_L1)
// make sure we do not close the socket after preconnecting
connectionCloseEnabled = false;
}
@@ -403,7 +368,7 @@ void QHttpNetworkReplyPrivate::removeAutoDecompressHeader()
{
// The header "Content-Encoding = gzip" is retained.
// Content-Length is removed since the actual one sent by the server is for compressed data
- QByteArray name("content-length");
+ constexpr auto name = QByteArrayView("content-length");
QByteArray contentLength = parser.firstHeaderField(name);
bool parseOk = false;
qint64 value = contentLength.toLongLong(&parseOk);
@@ -413,23 +378,7 @@ void QHttpNetworkReplyPrivate::removeAutoDecompressHeader()
}
}
-bool QHttpNetworkReplyPrivate::findChallenge(bool forProxy, QByteArray &challenge) const
-{
- challenge.clear();
- // find out the type of authentication protocol requested.
- QByteArray header = forProxy ? "proxy-authenticate" : "www-authenticate";
- // pick the best protocol (has to match parsing in QAuthenticatorPrivate)
- QList<QByteArray> challenges = headerFieldValues(header);
- for (int i = 0; i<challenges.size(); i++) {
- QByteArray line = challenges.at(i);
- // todo use qstrincmp
- if (!line.toLower().startsWith("negotiate"))
- challenge = line;
- }
- return !challenge.isEmpty();
-}
-
-qint64 QHttpNetworkReplyPrivate::readStatus(QAbstractSocket *socket)
+qint64 QHttpNetworkReplyPrivate::readStatus(QIODevice *socket)
{
if (fragment.isEmpty()) {
// reserve bytes for the status line. This is better than always append() which reallocs the byte array
@@ -455,7 +404,7 @@ qint64 QHttpNetworkReplyPrivate::readStatus(QAbstractSocket *socket)
if (c == '\n') {
// remove the CR at the end
if (fragment.endsWith('\r')) {
- fragment.truncate(fragment.length()-1);
+ fragment.truncate(fragment.size()-1);
}
bool ok = parseStatus(fragment);
state = ReadingHeaderState;
@@ -469,7 +418,7 @@ qint64 QHttpNetworkReplyPrivate::readStatus(QAbstractSocket *socket)
}
// is this a valid reply?
- if (fragment.length() == 5 && !fragment.startsWith("HTTP/")) {
+ if (fragment.size() == 5 && !fragment.startsWith("HTTP/")) {
fragment.clear();
return -1;
}
@@ -478,12 +427,12 @@ qint64 QHttpNetworkReplyPrivate::readStatus(QAbstractSocket *socket)
return bytes;
}
-bool QHttpNetworkReplyPrivate::parseStatus(const QByteArray &status)
+bool QHttpNetworkReplyPrivate::parseStatus(QByteArrayView status)
{
return parser.parseStatus(status);
}
-qint64 QHttpNetworkReplyPrivate::readHeader(QAbstractSocket *socket)
+qint64 QHttpNetworkReplyPrivate::readHeader(QIODevice *socket)
{
if (fragment.isEmpty()) {
// according to http://dev.opera.com/articles/view/mama-http-headers/ the average size of the header
@@ -517,8 +466,8 @@ qint64 QHttpNetworkReplyPrivate::readHeader(QAbstractSocket *socket)
allHeaders = true;
// there is another case: We have no headers. Then the fragment equals just the line ending
- if ((fragment.length() == 2 && fragment.endsWith("\r\n"))
- || (fragment.length() == 1 && fragment.endsWith("\n")))
+ if ((fragment.size() == 2 && fragment.endsWith("\r\n"))
+ || (fragment.size() == 1 && fragment.endsWith("\n")))
allHeaders = true;
}
}
@@ -545,7 +494,7 @@ qint64 QHttpNetworkReplyPrivate::readHeader(QAbstractSocket *socket)
return bytes;
}
-void QHttpNetworkReplyPrivate::parseHeader(const QByteArray &header)
+void QHttpNetworkReplyPrivate::parseHeader(QByteArrayView header)
{
parser.parseHeaders(header);
}
@@ -567,7 +516,7 @@ bool QHttpNetworkReplyPrivate::isConnectionCloseEnabled()
// note this function can only be used for non-chunked, non-compressed with
// known content length
-qint64 QHttpNetworkReplyPrivate::readBodyVeryFast(QAbstractSocket *socket, char *b)
+qint64 QHttpNetworkReplyPrivate::readBodyVeryFast(QIODevice *socket, char *b)
{
// This first read is to flush the buffer inside the socket
qint64 haveRead = 0;
@@ -586,7 +535,7 @@ qint64 QHttpNetworkReplyPrivate::readBodyVeryFast(QAbstractSocket *socket, char
// note this function can only be used for non-chunked, non-compressed with
// known content length
-qint64 QHttpNetworkReplyPrivate::readBodyFast(QAbstractSocket *socket, QByteDataBuffer *rb)
+qint64 QHttpNetworkReplyPrivate::readBodyFast(QIODevice *socket, QByteDataBuffer *rb)
{
qint64 toBeRead = qMin(socket->bytesAvailable(), bodyLength - contentRead);
@@ -616,7 +565,7 @@ qint64 QHttpNetworkReplyPrivate::readBodyFast(QAbstractSocket *socket, QByteData
}
-qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QByteDataBuffer *out)
+qint64 QHttpNetworkReplyPrivate::readBody(QIODevice *socket, QByteDataBuffer *out)
{
qint64 bytes = 0;
@@ -636,7 +585,7 @@ qint64 QHttpNetworkReplyPrivate::readBody(QAbstractSocket *socket, QByteDataBuff
return bytes;
}
-qint64 QHttpNetworkReplyPrivate::readReplyBodyRaw(QAbstractSocket *socket, QByteDataBuffer *out, qint64 size)
+qint64 QHttpNetworkReplyPrivate::readReplyBodyRaw(QIODevice *socket, QByteDataBuffer *out, qint64 size)
{
// FIXME get rid of this function and just use readBodyFast and give it socket->bytesAvailable()
qint64 bytes = 0;
@@ -669,7 +618,7 @@ qint64 QHttpNetworkReplyPrivate::readReplyBodyRaw(QAbstractSocket *socket, QByte
}
-qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QAbstractSocket *socket, QByteDataBuffer *out)
+qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QIODevice *socket, QByteDataBuffer *out)
{
qint64 bytes = 0;
while (socket->bytesAvailable()) {
@@ -731,7 +680,7 @@ qint64 QHttpNetworkReplyPrivate::readReplyBodyChunked(QAbstractSocket *socket, Q
return bytes;
}
-qint64 QHttpNetworkReplyPrivate::getChunkSize(QAbstractSocket *socket, qint64 *chunkSize)
+qint64 QHttpNetworkReplyPrivate::getChunkSize(QIODevice *socket, qint64 *chunkSize)
{
qint64 bytes = 0;
char crlf[2];
@@ -752,8 +701,8 @@ qint64 QHttpNetworkReplyPrivate::getChunkSize(QAbstractSocket *socket, qint64 *c
bytes += socket->read(crlf, 1); // read the \n
bool ok = false;
// ignore the chunk-extension
- fragment = fragment.mid(0, fragment.indexOf(';')).trimmed();
- *chunkSize = fragment.toLong(&ok, 16);
+ const auto fragmentView = QByteArrayView(fragment).mid(0, fragment.indexOf(';')).trimmed();
+ *chunkSize = fragmentView.toLong(&ok, 16);
fragment.clear();
break; // size done
} else {
@@ -855,3 +804,5 @@ void QHttpNetworkReply::ignoreSslErrors(const QList<QSslError> &errors)
QT_END_NAMESPACE
+
+#include "moc_qhttpnetworkreply_p.cpp"
diff --git a/src/network/access/qhttpnetworkreply_p.h b/src/network/access/qhttpnetworkreply_p.h
index 39e8b24303..caec82bd7e 100644
--- a/src/network/access/qhttpnetworkreply_p.h
+++ b/src/network/access/qhttpnetworkreply_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QHTTPNETWORKREPLY_H
#define QHTTPNETWORKREPLY_H
@@ -77,6 +41,9 @@ Q_MOC_INCLUDE(<QtNetwork/QNetworkProxy>)
Q_MOC_INCLUDE(<QtNetwork/QAuthenticator>)
#include <private/qdecompresshelper_p.h>
+#include <QtNetwork/qhttpheaders.h>
+
+#include <QtCore/qpointer.h>
QT_REQUIRE_CONFIG(http);
@@ -87,7 +54,7 @@ class QHttpNetworkConnectionChannel;
class QHttpNetworkRequest;
class QHttpNetworkConnectionPrivate;
class QHttpNetworkReplyPrivate;
-class Q_AUTOTEST_EXPORT QHttpNetworkReply : public QObject, public QHttpNetworkHeader
+class Q_NETWORK_EXPORT QHttpNetworkReply : public QObject, public QHttpNetworkHeader
{
Q_OBJECT
public:
@@ -106,11 +73,11 @@ public:
qint64 contentLength() const override;
void setContentLength(qint64 length) override;
- QList<QPair<QByteArray, QByteArray> > header() const override;
- QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue = QByteArray()) const override;
+ QHttpHeaders header() const override;
+ QByteArray headerField(QByteArrayView name, const QByteArray &defaultValue = QByteArray()) const override;
void setHeaderField(const QByteArray &name, const QByteArray &data) override;
void appendHeaderField(const QByteArray &name, const QByteArray &data);
- void parseHeader(const QByteArray &header); // used for testing
+ void parseHeader(QByteArrayView header); // used for testing
QHttpNetworkRequest request() const;
void setRequest(const QHttpNetworkRequest &request);
@@ -174,7 +141,7 @@ Q_SIGNALS:
#endif
Q_SIGNALS:
- void socketConnecting();
+ void socketStartedConnecting();
void requestSent();
void readyRead();
void finished();
@@ -205,21 +172,20 @@ class Q_AUTOTEST_EXPORT QHttpNetworkReplyPrivate : public QObjectPrivate, public
public:
QHttpNetworkReplyPrivate(const QUrl &newUrl = QUrl());
~QHttpNetworkReplyPrivate();
- qint64 readStatus(QAbstractSocket *socket);
- bool parseStatus(const QByteArray &status);
- qint64 readHeader(QAbstractSocket *socket);
- void parseHeader(const QByteArray &header);
+ qint64 readStatus(QIODevice *socket);
+ bool parseStatus(QByteArrayView status);
+ qint64 readHeader(QIODevice *socket);
+ void parseHeader(QByteArrayView header);
void appendHeaderField(const QByteArray &name, const QByteArray &data);
- qint64 readBody(QAbstractSocket *socket, QByteDataBuffer *out);
- qint64 readBodyVeryFast(QAbstractSocket *socket, char *b);
- qint64 readBodyFast(QAbstractSocket *socket, QByteDataBuffer *rb);
- bool findChallenge(bool forProxy, QByteArray &challenge) const;
+ qint64 readBody(QIODevice *socket, QByteDataBuffer *out);
+ qint64 readBodyVeryFast(QIODevice *socket, char *b);
+ qint64 readBodyFast(QIODevice *socket, QByteDataBuffer *rb);
void clear();
void clearHttpLayerInformation();
- qint64 readReplyBodyRaw(QAbstractSocket *in, QByteDataBuffer *out, qint64 size);
- qint64 readReplyBodyChunked(QAbstractSocket *in, QByteDataBuffer *out);
- qint64 getChunkSize(QAbstractSocket *in, qint64 *chunkSize);
+ qint64 readReplyBodyRaw(QIODevice *in, QByteDataBuffer *out, qint64 size);
+ qint64 readReplyBodyChunked(QIODevice *in, QByteDataBuffer *out);
+ qint64 getChunkSize(QIODevice *in, qint64 *chunkSize);
bool isRedirecting() const;
bool shouldEmitSignals();
diff --git a/src/network/access/qhttpnetworkrequest.cpp b/src/network/access/qhttpnetworkrequest.cpp
index f370445059..7a4ffb1684 100644
--- a/src/network/access/qhttpnetworkrequest.cpp
+++ b/src/network/access/qhttpnetworkrequest.cpp
@@ -1,47 +1,13 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qhttpnetworkrequest_p.h"
#include "private/qnoncontiguousbytedevice_p.h"
QT_BEGIN_NAMESPACE
+QT_IMPL_METATYPE_EXTERN(QHttpNetworkRequest)
+
QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(QHttpNetworkRequest::Operation op,
QHttpNetworkRequest::Priority pri, const QUrl &newUrl)
: QHttpNetworkHeaderPrivate(newUrl), operation(op), priority(pri), uploadByteDevice(nullptr),
@@ -61,6 +27,7 @@ QHttpNetworkRequestPrivate::QHttpNetworkRequestPrivate(const QHttpNetworkRequest
pipeliningAllowed(other.pipeliningAllowed),
http2Allowed(other.http2Allowed),
http2Direct(other.http2Direct),
+ h2cAllowed(other.h2cAllowed),
withCredentials(other.withCredentials),
ssl(other.ssl),
preConnect(other.preConnect),
@@ -85,6 +52,7 @@ bool QHttpNetworkRequestPrivate::operator==(const QHttpNetworkRequestPrivate &ot
&& (pipeliningAllowed == other.pipeliningAllowed)
&& (http2Allowed == other.http2Allowed)
&& (http2Direct == other.http2Direct)
+ && (h2cAllowed == other.h2cAllowed)
// we do not clear the customVerb in setOperation
&& (operation != QHttpNetworkRequest::Custom || (customVerb == other.customVerb))
&& (withCredentials == other.withCredentials)
@@ -144,9 +112,9 @@ QByteArray QHttpNetworkRequest::uri(bool throughProxy) const
QByteArray QHttpNetworkRequestPrivate::header(const QHttpNetworkRequest &request, bool throughProxy)
{
- QList<QPair<QByteArray, QByteArray> > fields = request.header();
+ const QHttpHeaders headers = request.header();
QByteArray ba;
- ba.reserve(40 + fields.length()*25); // very rough lower bound estimation
+ ba.reserve(40 + headers.size() * 25); // very rough lower bound estimation
ba += request.methodName();
ba += ' ';
@@ -158,12 +126,10 @@ QByteArray QHttpNetworkRequestPrivate::header(const QHttpNetworkRequest &request
ba += QByteArray::number(request.minorVersion());
ba += "\r\n";
- QList<QPair<QByteArray, QByteArray> >::const_iterator it = fields.constBegin();
- QList<QPair<QByteArray, QByteArray> >::const_iterator endIt = fields.constEnd();
- for (; it != endIt; ++it) {
- ba += it->first;
+ for (qsizetype i = 0; i < headers.size(); ++i) {
+ ba += headers.nameAt(i);
ba += ": ";
- ba += it->second;
+ ba += headers.valueAt(i);
ba += "\r\n";
}
if (request.d->operation == QHttpNetworkRequest::Post) {
@@ -269,12 +235,12 @@ void QHttpNetworkRequest::setContentLength(qint64 length)
d->setContentLength(length);
}
-QList<QPair<QByteArray, QByteArray> > QHttpNetworkRequest::header() const
+QHttpHeaders QHttpNetworkRequest::header() const
{
return d->parser.headers();
}
-QByteArray QHttpNetworkRequest::headerField(const QByteArray &name, const QByteArray &defaultValue) const
+QByteArray QHttpNetworkRequest::headerField(QByteArrayView name, const QByteArray &defaultValue) const
{
return d->headerField(name, defaultValue);
}
@@ -365,6 +331,16 @@ void QHttpNetworkRequest::setHTTP2Direct(bool b)
d->http2Direct = b;
}
+bool QHttpNetworkRequest::isH2cAllowed() const
+{
+ return d->h2cAllowed;
+}
+
+void QHttpNetworkRequest::setH2cAllowed(bool b)
+{
+ d->h2cAllowed = b;
+}
+
bool QHttpNetworkRequest::withCredentials() const
{
return d->withCredentials;
diff --git a/src/network/access/qhttpnetworkrequest_p.h b/src/network/access/qhttpnetworkrequest_p.h
index 50fdd64290..131885f6d2 100644
--- a/src/network/access/qhttpnetworkrequest_p.h
+++ b/src/network/access/qhttpnetworkrequest_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QHTTPNETWORKREQUEST_H
#define QHTTPNETWORKREQUEST_H
@@ -101,8 +65,8 @@ public:
qint64 contentLength() const override;
void setContentLength(qint64 length) override;
- QList<QPair<QByteArray, QByteArray> > header() const override;
- QByteArray headerField(const QByteArray &name, const QByteArray &defaultValue = QByteArray()) const override;
+ QHttpHeaders header() const override;
+ QByteArray headerField(QByteArrayView name, const QByteArray &defaultValue = QByteArray()) const override;
void setHeaderField(const QByteArray &name, const QByteArray &data) override;
void prependHeaderField(const QByteArray &name, const QByteArray &data);
void clearHeaders();
@@ -125,6 +89,9 @@ public:
bool isHTTP2Direct() const;
void setHTTP2Direct(bool b);
+ bool isH2cAllowed() const;
+ void setH2cAllowed(bool b);
+
bool withCredentials() const;
void setWithCredentials(bool b);
@@ -179,6 +146,7 @@ public:
bool pipeliningAllowed;
bool http2Allowed;
bool http2Direct;
+ bool h2cAllowed = false;
bool withCredentials;
bool ssl;
bool preConnect;
@@ -191,6 +159,6 @@ public:
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(QHttpNetworkRequest)
+QT_DECL_METATYPE_EXTERN(QHttpNetworkRequest, Q_AUTOTEST_EXPORT)
#endif // QHTTPNETWORKREQUEST_H
diff --git a/src/network/access/qhttpprotocolhandler.cpp b/src/network/access/qhttpprotocolhandler.cpp
index 488c226459..28eab03890 100644
--- a/src/network/access/qhttpprotocolhandler.cpp
+++ b/src/network/access/qhttpprotocolhandler.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** 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>
@@ -44,6 +8,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
QHttpProtocolHandler::QHttpProtocolHandler(QHttpNetworkConnectionChannel *channel)
: QAbstractProtocolHandler(channel)
{
@@ -127,7 +93,8 @@ void QHttpProtocolHandler::_q_receiveReply()
} else {
replyPrivate->autoDecompress = false;
}
- if (m_reply->statusCode() == 100) {
+ const int statusCode = m_reply->statusCode();
+ if (statusCode == 100 || (102 <= statusCode && statusCode <= 199)) {
replyPrivate->clearHttpLayerInformation();
replyPrivate->state = QHttpNetworkReplyPrivate::ReadingStatusState;
break; // ignore
@@ -272,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();
@@ -319,7 +284,6 @@ bool QHttpProtocolHandler::sendRequest()
#else
m_header = QHttpNetworkRequestPrivate::header(m_channel->request, false);
#endif
- QMetaObject::invokeMethod(m_reply, "requestSent", Qt::QueuedConnection);
// flushing is dangerous (QSslSocket calls transmit which might read or error)
// m_socket->flush();
@@ -334,7 +298,8 @@ bool QHttpProtocolHandler::sendRequest()
sendRequest(); //recurse
} else {
// no data to send: just send the HTTP headers
- m_socket->write(qExchange(m_header, {}));
+ 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
}
@@ -349,7 +314,7 @@ bool QHttpProtocolHandler::sendRequest()
// 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(qExchange(m_header, {}));
+ 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
@@ -406,9 +371,10 @@ bool QHttpProtocolHandler::sendRequest()
// assemble header and data and send them together
const qint64 headerSize = m_header.size();
m_header.append(readPointer, currentReadSize);
- currentWriteSize = m_socket->write(qExchange(m_header, {}));
+ 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
diff --git a/src/network/access/qhttpprotocolhandler_p.h b/src/network/access/qhttpprotocolhandler_p.h
index f2da21d3b6..221e98d538 100644
--- a/src/network/access/qhttpprotocolhandler_p.h
+++ b/src/network/access/qhttpprotocolhandler_p.h
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** 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
#ifndef QHTTPPROTOCOLHANDLER_H
#define QHTTPPROTOCOLHANDLER_H
diff --git a/src/network/access/qhttpthreaddelegate.cpp b/src/network/access/qhttpthreaddelegate.cpp
index 50a14b6258..b0ae0dcf44 100644
--- a/src/network/access/qhttpthreaddelegate.cpp
+++ b/src/network/access/qhttpthreaddelegate.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//#define QHTTPTHREADDELEGATE_DEBUG
#include "qhttpthreaddelegate_p.h"
@@ -52,6 +16,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
static QNetworkReply::NetworkError statusCodeFromHttp(int httpStatusCode, const QUrl &url)
{
QNetworkReply::NetworkError code;
@@ -128,14 +94,12 @@ static QByteArray makeCacheKey(QUrl &url, QNetworkProxy *proxy, const QString &p
QString result;
QUrl copy = url;
QString scheme = copy.scheme();
- bool isEncrypted = scheme == QLatin1String("https")
- || scheme == QLatin1String("preconnect-https");
+ bool isEncrypted = scheme == "https"_L1 || scheme == "preconnect-https"_L1;
copy.setPort(copy.port(isEncrypted ? 443 : 80));
- if (scheme == QLatin1String("preconnect-http")) {
- copy.setScheme(QLatin1String("http"));
- } else if (scheme == QLatin1String("preconnect-https")) {
- copy.setScheme(QLatin1String("https"));
- }
+ if (scheme == "preconnect-http"_L1)
+ copy.setScheme("http"_L1);
+ else if (scheme == "preconnect-https"_L1)
+ copy.setScheme("https"_L1);
result = copy.toString(QUrl::RemoveUserInfo | QUrl::RemovePath |
QUrl::RemoveQuery | QUrl::RemoveFragment | QUrl::FullyEncoded);
@@ -145,12 +109,12 @@ static QByteArray makeCacheKey(QUrl &url, QNetworkProxy *proxy, const QString &p
switch (proxy->type()) {
case QNetworkProxy::Socks5Proxy:
- key.setScheme(QLatin1String("proxy-socks5"));
+ key.setScheme("proxy-socks5"_L1);
break;
case QNetworkProxy::HttpProxy:
case QNetworkProxy::HttpCachingProxy:
- key.setScheme(QLatin1String("proxy-http"));
+ key.setScheme("proxy-http"_L1);
break;
default:
@@ -172,7 +136,7 @@ static QByteArray makeCacheKey(QUrl &url, QNetworkProxy *proxy, const QString &p
Q_UNUSED(proxy);
#endif
if (!peerVerifyName.isEmpty())
- result += QLatin1Char(':') + peerVerifyName;
+ result += u':' + peerVerifyName;
return "http-connection:" + std::move(result).toLatin1();
}
@@ -181,9 +145,9 @@ class QNetworkAccessCachedHttpConnection: public QHttpNetworkConnection,
{
// Q_OBJECT
public:
- QNetworkAccessCachedHttpConnection(const QString &hostName, quint16 port, bool encrypt,
+ QNetworkAccessCachedHttpConnection(quint16 connectionCount, const QString &hostName, quint16 port, bool encrypt,
QHttpNetworkConnection::ConnectionType connectionType)
- : QHttpNetworkConnection(hostName, port, encrypt, connectionType)
+ : QHttpNetworkConnection(connectionCount, hostName, port, encrypt, /*parent=*/nullptr, connectionType)
{
setExpires(true);
setShareable(true);
@@ -290,6 +254,12 @@ void QHttpThreadDelegate::startRequest()
connectionType = QHttpNetworkConnection::ConnectionTypeHTTP2Direct;
}
+ // Use HTTP/1.1 if h2c is not allowed and we would otherwise choose to use it
+ if (!ssl && connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2
+ && !httpRequest.isH2cAllowed()) {
+ connectionType = QHttpNetworkConnection::ConnectionTypeHTTP;
+ }
+
#if QT_CONFIG(ssl)
// See qnetworkreplyhttpimpl, delegate's initialization code.
Q_ASSERT(!ssl || incomingSslConfiguration.data());
@@ -327,7 +297,7 @@ void QHttpThreadDelegate::startRequest()
if (!httpConnection) {
// no entry in cache; create an object
// the http object is actually a QHttpNetworkConnection
- httpConnection = new QNetworkAccessCachedHttpConnection(urlCopy.host(), urlCopy.port(), ssl,
+ httpConnection = new QNetworkAccessCachedHttpConnection(http1Parameters.numberOfConnectionsPerHost(), urlCopy.host(), urlCopy.port(), ssl,
connectionType);
if (connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2
|| connectionType == QHttpNetworkConnection::ConnectionTypeHTTP2Direct) {
@@ -378,7 +348,7 @@ void QHttpThreadDelegate::startRequest()
// Don't care about ignored SSL errors for now in the synchronous HTTP case.
} else if (!synchronous) {
- connect(httpReply,SIGNAL(socketConnecting()), this, SIGNAL(socketConnecting()));
+ connect(httpReply,SIGNAL(socketStartedConnecting()), this, SIGNAL(socketStartedConnecting()));
connect(httpReply,SIGNAL(requestSent()), this, SIGNAL(requestSent()));
connect(httpReply,SIGNAL(headerChanged()), this, SLOT(headerChangedSlot()));
connect(httpReply,SIGNAL(finished()), this, SLOT(finishedSlot()));
@@ -516,8 +486,8 @@ void QHttpThreadDelegate::finishedSlot()
if (httpReply->statusCode() >= 400) {
// it's an error reply
- QString msg = QLatin1String(QT_TRANSLATE_NOOP("QNetworkReply",
- "Error transferring %1 - server replied: %2"));
+ QString msg = QLatin1StringView(QT_TRANSLATE_NOOP("QNetworkReply",
+ "Error transferring %1 - server replied: %2"));
msg = msg.arg(httpRequest.url().toString(), httpReply->reasonPhrase());
emit error(statusCodeFromHttp(httpReply->statusCode(), httpRequest.url()), msg);
}
@@ -542,8 +512,8 @@ void QHttpThreadDelegate::synchronousFinishedSlot()
#endif
if (httpReply->statusCode() >= 400) {
// it's an error reply
- QString msg = QLatin1String(QT_TRANSLATE_NOOP("QNetworkReply",
- "Error transferring %1 - server replied: %2"));
+ QString msg = QLatin1StringView(QT_TRANSLATE_NOOP("QNetworkReply",
+ "Error transferring %1 - server replied: %2"));
incomingErrorDetail = msg.arg(httpRequest.url().toString(), httpReply->reasonPhrase());
incomingErrorCode = statusCodeFromHttp(httpReply->statusCode(), httpRequest.url());
}
@@ -597,11 +567,6 @@ void QHttpThreadDelegate::synchronousFinishedWithErrorSlot(QNetworkReply::Networ
httpReply = nullptr;
}
-static void downloadBufferDeleter(char *ptr)
-{
- delete[] ptr;
-}
-
void QHttpThreadDelegate::headerChangedSlot()
{
if (!httpReply)
@@ -619,14 +584,11 @@ void QHttpThreadDelegate::headerChangedSlot()
// Is using a zerocopy buffer allowed by user and possible with this reply?
if (httpReply->supportsUserProvidedDownloadBuffer()
&& (downloadBufferMaximumSize > 0) && (httpReply->contentLength() <= downloadBufferMaximumSize)) {
- QT_TRY {
- char *buf = new char[httpReply->contentLength()]; // throws if allocation fails
- if (buf) {
- downloadBuffer = QSharedPointer<char>(buf, downloadBufferDeleter);
- httpReply->setUserProvidedDownloadBuffer(buf);
- }
- } QT_CATCH(const std::bad_alloc &) {
- // in out of memory situations, don't use downloadbuffer.
+ char *buf = new (std::nothrow) char[httpReply->contentLength()];
+ // in out of memory situations, don't use downloadBuffer.
+ if (buf) {
+ downloadBuffer = QSharedPointer<char>(buf, [](auto p) { delete[] p; });
+ httpReply->setUserProvidedDownloadBuffer(buf);
}
}
@@ -769,3 +731,5 @@ void QHttpThreadDelegate::synchronousProxyAuthenticationRequiredSlot(const QNet
#endif
QT_END_NAMESPACE
+
+#include "moc_qhttpthreaddelegate_p.cpp"
diff --git a/src/network/access/qhttpthreaddelegate_p.h b/src/network/access/qhttpthreaddelegate_p.h
index bdafeb3693..38e9fb4d78 100644
--- a/src/network/access/qhttpthreaddelegate_p.h
+++ b/src/network/access/qhttpthreaddelegate_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QHTTPTHREADDELEGATE_H
#define QHTTPTHREADDELEGATE_H
@@ -62,12 +26,14 @@
#include <QNetworkReply>
#include "qhttpnetworkrequest_p.h"
#include "qhttpnetworkconnection_p.h"
+#include "qhttp1configuration.h"
#include "qhttp2configuration.h"
#include <QSharedPointer>
#include <QScopedPointer>
#include "private/qnoncontiguousbytedevice_p.h"
#include "qnetworkaccessauthenticationmanager_p.h"
#include <QtNetwork/private/http2protocol_p.h>
+#include <QtNetwork/qhttpheaders.h>
QT_REQUIRE_CONFIG(http);
@@ -109,7 +75,7 @@ public:
// outgoing, Retrieved in the synchronous HTTP case
QByteArray synchronousDownloadData;
- QList<QPair<QByteArray,QByteArray> > incomingHeaders;
+ QHttpHeaders incomingHeaders;
int incomingStatusCode;
QString incomingReasonPhrase;
bool isPipeliningUsed;
@@ -118,6 +84,7 @@ public:
qint64 removedContentLength;
QNetworkReply::NetworkError incomingErrorCode;
QString incomingErrorDetail;
+ QHttp1Configuration http1Parameters;
QHttp2Configuration http2Parameters;
bool isCompressed;
@@ -144,9 +111,9 @@ signals:
void sslConfigurationChanged(const QSslConfiguration &);
void preSharedKeyAuthenticationRequired(QSslPreSharedKeyAuthenticator *);
#endif
- void socketConnecting();
+ void socketStartedConnecting();
void requestSent();
- void downloadMetaData(const QList<QPair<QByteArray,QByteArray> > &, int, const QString &, bool,
+ void downloadMetaData(const QHttpHeaders &, int, const QString &, bool,
QSharedPointer<char>, qint64, qint64, bool, bool);
void downloadProgress(qint64, qint64);
void downloadData(const QByteArray &);
@@ -198,22 +165,18 @@ class QNonContiguousByteDeviceThreadForwardImpl : public QNonContiguousByteDevic
{
Q_OBJECT
protected:
- bool wantDataPending;
- qint64 m_amount;
- char *m_data;
+ bool wantDataPending = false;
+ qint64 m_amount = 0;
+ char *m_data = nullptr;
QByteArray m_dataArray;
- bool m_atEnd;
- qint64 m_size;
- qint64 m_pos; // to match calls of haveDataSlot with the expected position
+ bool m_atEnd = false;
+ qint64 m_size = 0;
+ qint64 m_pos = 0; // to match calls of haveDataSlot with the expected position
public:
QNonContiguousByteDeviceThreadForwardImpl(bool aE, qint64 s)
: QNonContiguousByteDevice(),
- wantDataPending(false),
- m_amount(0),
- m_data(nullptr),
m_atEnd(aE),
- m_size(s),
- m_pos(0)
+ m_size(s)
{
}
@@ -286,6 +249,7 @@ public:
if (b) {
// the reset succeeded, we're at pos 0 again
m_pos = 0;
+ m_atEnd = false;
// the HTTP code will anyway abort the request if !b.
}
return b;
diff --git a/src/network/access/qnetworkaccessauthenticationmanager.cpp b/src/network/access/qnetworkaccessauthenticationmanager.cpp
index 7b2e439fe3..ab7c27b885 100644
--- a/src/network/access/qnetworkaccessauthenticationmanager.cpp
+++ b/src/network/access/qnetworkaccessauthenticationmanager.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qnetworkaccessauthenticationmanager_p.h"
#include "qnetworkaccessmanager.h"
@@ -51,6 +15,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
class QNetworkAuthenticationCache : private QList<QNetworkAuthenticationCredential>,
public QNetworkAccessCache::CacheableObject
{
@@ -105,16 +71,16 @@ static QByteArray proxyAuthenticationKey(const QNetworkProxy &proxy, const QStri
switch (proxy.type()) {
case QNetworkProxy::Socks5Proxy:
- key.setScheme(QLatin1String("proxy-socks5"));
+ key.setScheme("proxy-socks5"_L1);
break;
case QNetworkProxy::HttpProxy:
case QNetworkProxy::HttpCachingProxy:
- key.setScheme(QLatin1String("proxy-http"));
+ key.setScheme("proxy-http"_L1);
break;
case QNetworkProxy::FtpCachingProxy:
- key.setScheme(QLatin1String("proxy-ftp"));
+ key.setScheme("proxy-ftp"_L1);
break;
case QNetworkProxy::DefaultProxy:
diff --git a/src/network/access/qnetworkaccessauthenticationmanager_p.h b/src/network/access/qnetworkaccessauthenticationmanager_p.h
index 340991c5ff..f88360f1c1 100644
--- a/src/network/access/qnetworkaccessauthenticationmanager_p.h
+++ b/src/network/access/qnetworkaccessauthenticationmanager_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKACCESSAUTHENTICATIONMANAGER_P_H
#define QNETWORKACCESSAUTHENTICATIONMANAGER_P_H
diff --git a/src/network/access/qnetworkaccessbackend.cpp b/src/network/access/qnetworkaccessbackend.cpp
index b544e8436b..5dbcef4bbe 100644
--- a/src/network/access/qnetworkaccessbackend.cpp
+++ b/src/network/access/qnetworkaccessbackend.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** 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) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qnetworkaccessbackend_p.h"
#include "qnetworkreplyimpl_p.h"
@@ -72,7 +36,7 @@ public:
static QBasicAtomicInt valid;
};
Q_GLOBAL_STATIC(QNetworkAccessBackendFactoryData, factoryData)
-QBasicAtomicInt QNetworkAccessBackendFactoryData::valid = Q_BASIC_ATOMIC_INITIALIZER(0);
+Q_CONSTINIT QBasicAtomicInt QNetworkAccessBackendFactoryData::valid = Q_BASIC_ATOMIC_INITIALIZER(0);
class QNetworkAccessBackendPrivate : public QObjectPrivate
{
@@ -849,6 +813,12 @@ QNetworkAccessBackendFactory::QNetworkAccessBackendFactory()
/*!
Destructs QNetworkAccessBackendFactory
*/
-QNetworkAccessBackendFactory::~QNetworkAccessBackendFactory() = default;
+QNetworkAccessBackendFactory::~QNetworkAccessBackendFactory()
+{
+ if (factoryData.exists())
+ factoryData->removeAll(this);
+};
QT_END_NAMESPACE
+
+#include "moc_qnetworkaccessbackend_p.cpp"
diff --git a/src/network/access/qnetworkaccessbackend_p.h b/src/network/access/qnetworkaccessbackend_p.h
index d2742c180c..799ae3faad 100644
--- a/src/network/access/qnetworkaccessbackend_p.h
+++ b/src/network/access/qnetworkaccessbackend_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** 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) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKACCESSBACKEND_P_H
#define QNETWORKACCESSBACKEND_P_H
@@ -60,6 +24,7 @@
#include <QtCore/qobject.h>
#include <QtCore/qflags.h>
#include <QtCore/qbytearrayview.h>
+#include <QtCore/private/qglobal_p.h>
#if QT_CONFIG(ssl)
#include <QtNetwork/qsslconfiguration.h>
diff --git a/src/network/access/qnetworkaccesscache.cpp b/src/network/access/qnetworkaccesscache.cpp
index 2e9c275c6b..2bc0e8fb70 100644
--- a/src/network/access/qnetworkaccesscache.cpp
+++ b/src/network/access/qnetworkaccesscache.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qnetworkaccesscache_p.h"
#include "QtCore/qpointer.h"
@@ -364,3 +328,5 @@ void QNetworkAccessCache::removeEntry(const QByteArray &key)
}
QT_END_NAMESPACE
+
+#include "moc_qnetworkaccesscache_p.cpp"
diff --git a/src/network/access/qnetworkaccesscache_p.h b/src/network/access/qnetworkaccesscache_p.h
index 3f96c0266a..3be7967ca1 100644
--- a/src/network/access/qnetworkaccesscache_p.h
+++ b/src/network/access/qnetworkaccesscache_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKACCESSCACHE_P_H
#define QNETWORKACCESSCACHE_P_H
@@ -123,6 +87,4 @@ private:
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(QNetworkAccessCache::CacheableObject*)
-
#endif
diff --git a/src/network/access/qnetworkaccesscachebackend.cpp b/src/network/access/qnetworkaccesscachebackend.cpp
index 9ed951751a..fd8174c143 100644
--- a/src/network/access/qnetworkaccesscachebackend.cpp
+++ b/src/network/access/qnetworkaccesscachebackend.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//#define QNETWORKACCESSCACHEBACKEND_DEBUG
@@ -44,9 +8,12 @@
#include "qfileinfo.h"
#include "qdir.h"
#include "qcoreapplication.h"
+#include "qhash.h"
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
QNetworkAccessCacheBackend::QNetworkAccessCacheBackend()
: QNetworkAccessBackend(QNetworkAccessBackend::TargetType::Local)
{
@@ -86,10 +53,10 @@ bool QNetworkAccessCacheBackend::sendCacheContents()
// set the raw headers
const QNetworkCacheMetaData::RawHeaderList rawHeaders = item.rawHeaders();
for (const auto &header : rawHeaders) {
- if (header.first.toLower() == "cache-control") {
- const QByteArray cacheControlValue = header.second.toLower();
- if (cacheControlValue.contains("must-revalidate")
- || cacheControlValue.contains("no-cache")) {
+ if (header.first.compare("cache-control", Qt::CaseInsensitive) == 0) {
+ const QLatin1StringView cacheControlValue(header.second);
+ if (cacheControlValue.contains("must-revalidate"_L1, Qt::CaseInsensitive)
+ || cacheControlValue.contains("no-cache"_L1, Qt::CaseInsensitive)) {
return false;
}
}
diff --git a/src/network/access/qnetworkaccesscachebackend_p.h b/src/network/access/qnetworkaccesscachebackend_p.h
index fc26dda454..80ec1535de 100644
--- a/src/network/access/qnetworkaccesscachebackend_p.h
+++ b/src/network/access/qnetworkaccesscachebackend_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKACCESSCACHEBACKEND_P_H
#define QNETWORKACCESSCACHEBACKEND_P_H
diff --git a/src/network/access/qnetworkaccessdebugpipebackend.cpp b/src/network/access/qnetworkaccessdebugpipebackend.cpp
index 51c13b7241..4b12f9fe31 100644
--- a/src/network/access/qnetworkaccessdebugpipebackend.cpp
+++ b/src/network/access/qnetworkaccessdebugpipebackend.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qnetworkaccessdebugpipebackend_p.h"
#include "QtCore/qdatastream.h"
@@ -46,6 +10,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
#ifdef QT_BUILD_INTERNAL
enum {
@@ -74,7 +40,7 @@ QNetworkAccessDebugPipeBackendFactory::create(QNetworkAccessManager::Operation o
}
QUrl url = request.url();
- if (url.scheme() == QLatin1String("debugpipe"))
+ if (url.scheme() == "debugpipe"_L1)
return new QNetworkAccessDebugPipeBackend;
return nullptr;
}
@@ -109,7 +75,7 @@ void QNetworkAccessDebugPipeBackend::open()
// socket bytes written -> we can push more from upstream to socket
connect(&socket, SIGNAL(bytesWritten(qint64)), SLOT(socketBytesWritten(qint64)));
- bareProtocol = QUrlQuery(url()).queryItemValue(QLatin1String("bare")) == QLatin1String("1");
+ bareProtocol = QUrlQuery(url()).queryItemValue("bare"_L1) == "1"_L1;
if (operation() == QNetworkAccessManager::PutOperation) {
createUploadByteDevice();
@@ -218,7 +184,7 @@ void QNetworkAccessDebugPipeBackend::possiblyFinish()
void QNetworkAccessDebugPipeBackend::close()
{
- qWarning("QNetworkAccessDebugPipeBackend::closeDownstreamChannel() %d",operation());;
+ qWarning("QNetworkAccessDebugPipeBackend::closeDownstreamChannel() %d",operation());
//if (operation() == QNetworkAccessManager::GetOperation)
// socket.disconnectFromHost();
}
@@ -270,3 +236,5 @@ void QNetworkAccessDebugPipeBackend::socketConnected()
#endif
QT_END_NAMESPACE
+
+#include "moc_qnetworkaccessdebugpipebackend_p.cpp"
diff --git a/src/network/access/qnetworkaccessdebugpipebackend_p.h b/src/network/access/qnetworkaccessdebugpipebackend_p.h
index 50ef5cea7d..b3e9e1d40b 100644
--- a/src/network/access/qnetworkaccessdebugpipebackend_p.h
+++ b/src/network/access/qnetworkaccessdebugpipebackend_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKACCESSDEBUGPIPEBACKEND_P_H
#define QNETWORKACCESSDEBUGPIPEBACKEND_P_H
diff --git a/src/network/access/qnetworkaccessfilebackend.cpp b/src/network/access/qnetworkaccessfilebackend.cpp
index c5f1826203..2100c188a5 100644
--- a/src/network/access/qnetworkaccessfilebackend.cpp
+++ b/src/network/access/qnetworkaccessfilebackend.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qnetworkaccessfilebackend_p.h"
#include "qfileinfo.h"
@@ -47,6 +11,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
QStringList QNetworkAccessFileBackendFactory::supportedSchemes() const
{
QStringList schemes;
@@ -74,13 +40,13 @@ QNetworkAccessFileBackendFactory::create(QNetworkAccessManager::Operation op,
}
QUrl url = request.url();
- if (url.scheme().compare(QLatin1String("qrc"), Qt::CaseInsensitive) == 0
+ if (url.scheme().compare("qrc"_L1, Qt::CaseInsensitive) == 0
#if defined(Q_OS_ANDROID)
- || url.scheme().compare(QLatin1String("assets"), Qt::CaseInsensitive) == 0
+ || url.scheme().compare("assets"_L1, Qt::CaseInsensitive) == 0
#endif
|| url.isLocalFile()) {
return new QNetworkAccessFileBackend;
- } else if (!url.scheme().isEmpty() && url.authority().isEmpty() && (url.scheme().length() > 1)) {
+ } else if (!url.scheme().isEmpty() && url.authority().isEmpty() && (url.scheme().size() > 1)) {
// check if QFile could, in theory, open this URL via the file engines
// it has to be in the format:
// prefix:path/to/file
@@ -112,7 +78,7 @@ void QNetworkAccessFileBackend::open()
{
QUrl url = this->url();
- if (url.host() == QLatin1String("localhost"))
+ if (url.host() == "localhost"_L1)
url.setHost(QString());
#if !defined(Q_OS_WIN)
// do not allow UNC paths on Unix
@@ -125,17 +91,17 @@ void QNetworkAccessFileBackend::open()
}
#endif // !defined(Q_OS_WIN)
if (url.path().isEmpty())
- url.setPath(QLatin1String("/"));
+ url.setPath("/"_L1);
setUrl(url);
QString fileName = url.toLocalFile();
if (fileName.isEmpty()) {
- if (url.scheme() == QLatin1String("qrc")) {
- fileName = QLatin1Char(':') + url.path();
+ if (url.scheme() == "qrc"_L1) {
+ fileName = u':' + url.path();
} else {
#if defined(Q_OS_ANDROID)
- if (url.scheme() == QLatin1String("assets"))
- fileName = QLatin1String("assets:") + url.path();
+ if (url.scheme() == "assets"_L1)
+ fileName = "assets:"_L1 + url.path();
else
#endif
fileName = url.toString(QUrl::RemoveAuthority | QUrl::RemoveFragment | QUrl::RemoveQuery);
@@ -286,3 +252,5 @@ qint64 QNetworkAccessFileBackend::read(char *data, qint64 maxlen)
}
QT_END_NAMESPACE
+
+#include "moc_qnetworkaccessfilebackend_p.cpp"
diff --git a/src/network/access/qnetworkaccessfilebackend_p.h b/src/network/access/qnetworkaccessfilebackend_p.h
index 716869e50d..2c3a106f2a 100644
--- a/src/network/access/qnetworkaccessfilebackend_p.h
+++ b/src/network/access/qnetworkaccessfilebackend_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKACCESSFILEBACKEND_P_H
#define QNETWORKACCESSFILEBACKEND_P_H
diff --git a/src/network/access/qnetworkaccessmanager.cpp b/src/network/access/qnetworkaccessmanager.cpp
index 1af2547f3e..4e13c9924b 100644
--- a/src/network/access/qnetworkaccessmanager.cpp
+++ b/src/network/access/qnetworkaccessmanager.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** 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) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtNetwork/private/qtnetworkglobal_p.h>
@@ -68,9 +32,9 @@
#include "QtCore/qurl.h"
#include "QtNetwork/private/qauthenticator_p.h"
#include "QtNetwork/qsslconfiguration.h"
-#include "QtNetwork/private/http2protocol_p.h"
#if QT_CONFIG(http)
+#include "QtNetwork/private/http2protocol_p.h"
#include "qhttpmultipart.h"
#include "qhttpmultipart_p.h"
#include "qnetworkreplyhttpimpl_p.h"
@@ -80,12 +44,16 @@
#include <QHostInfo>
+#include "QtCore/qapplicationstatic.h"
+#include "QtCore/qloggingcategory.h"
#include <QtCore/private/qfactoryloader_p.h>
#if defined(Q_OS_MACOS)
+#include <QtCore/private/qcore_mac_p.h>
+
#include <CoreServices/CoreServices.h>
#include <SystemConfiguration/SystemConfiguration.h>
-#include <Security/SecKeychain.h>
+#include <Security/Security.h>
#endif
#ifdef Q_OS_WASM
#include "qnetworkreplywasmimpl_p.h"
@@ -99,73 +67,94 @@
QT_BEGIN_NAMESPACE
-Q_GLOBAL_STATIC(QNetworkAccessFileBackendFactory, fileBackend)
+using namespace Qt::StringLiterals;
+using namespace std::chrono_literals;
-#ifdef QT_BUILD_INTERNAL
+Q_LOGGING_CATEGORY(lcQnam, "qt.network.access.manager")
+
+Q_APPLICATION_STATIC(QNetworkAccessFileBackendFactory, fileBackend)
+
+#if QT_CONFIG(private_tests)
Q_GLOBAL_STATIC(QNetworkAccessDebugPipeBackendFactory, debugpipeBackend)
#endif
-Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
- (QNetworkAccessBackendFactory_iid,
- QLatin1String("/networkaccess")))
+Q_APPLICATION_STATIC(QFactoryLoader, qnabfLoader, QNetworkAccessBackendFactory_iid, "/networkaccess"_L1)
+
#if defined(Q_OS_MACOS)
bool getProxyAuth(const QString& proxyHostname, const QString &scheme, QString& username, QString& password)
{
- OSStatus err;
- SecKeychainItemRef itemRef;
- bool retValue = false;
- SecProtocolType protocolType = kSecProtocolTypeAny;
- if (scheme.compare(QLatin1String("ftp"),Qt::CaseInsensitive)==0) {
- protocolType = kSecProtocolTypeFTPProxy;
- } else if (scheme.compare(QLatin1String("http"),Qt::CaseInsensitive)==0
- || scheme.compare(QLatin1String("preconnect-http"),Qt::CaseInsensitive)==0) {
- protocolType = kSecProtocolTypeHTTPProxy;
- } else if (scheme.compare(QLatin1String("https"),Qt::CaseInsensitive)==0
- || scheme.compare(QLatin1String("preconnect-https"),Qt::CaseInsensitive)==0) {
- protocolType = kSecProtocolTypeHTTPSProxy;
+ CFStringRef protocolType = nullptr;
+ if (scheme.compare("ftp"_L1, Qt::CaseInsensitive) == 0) {
+ protocolType = kSecAttrProtocolFTPProxy;
+ } else if (scheme.compare("http"_L1, Qt::CaseInsensitive) == 0
+ || scheme.compare("preconnect-http"_L1, Qt::CaseInsensitive) == 0) {
+ protocolType = kSecAttrProtocolHTTPProxy;
+ } else if (scheme.compare("https"_L1,Qt::CaseInsensitive)==0
+ || scheme.compare("preconnect-https"_L1, Qt::CaseInsensitive) == 0) {
+ protocolType = kSecAttrProtocolHTTPSProxy;
+ } else {
+ qCWarning(lcQnam) << "Cannot query user name and password for a proxy, unnknown protocol:"
+ << scheme;
+ return false;
}
- QByteArray proxyHostnameUtf8(proxyHostname.toUtf8());
- err = SecKeychainFindInternetPassword(NULL,
- proxyHostnameUtf8.length(), proxyHostnameUtf8.constData(),
- 0,NULL,
- 0, NULL,
- 0, NULL,
- 0,
- protocolType,
- kSecAuthenticationTypeAny,
- 0, NULL,
- &itemRef);
- if (err == noErr) {
-
- SecKeychainAttribute attr;
- SecKeychainAttributeList attrList;
- UInt32 length;
- void *outData;
-
- attr.tag = kSecAccountItemAttr;
- attr.length = 0;
- attr.data = NULL;
-
- attrList.count = 1;
- attrList.attr = &attr;
-
- if (SecKeychainItemCopyContent(itemRef, NULL, &attrList, &length, &outData) == noErr) {
- username = QString::fromUtf8((const char*)attr.data, attr.length);
- password = QString::fromUtf8((const char*)outData, length);
- SecKeychainItemFreeContent(&attrList,outData);
- retValue = true;
- }
- CFRelease(itemRef);
+
+ QCFType<CFMutableDictionaryRef> query(CFDictionaryCreateMutable(kCFAllocatorDefault,
+ 0, nullptr, nullptr));
+ Q_ASSERT(query);
+
+ CFDictionaryAddValue(query, kSecClass, kSecClassInternetPassword);
+ CFDictionaryAddValue(query, kSecAttrProtocol, protocolType);
+
+ QCFType<CFStringRef> serverName; // Note the scope.
+ if (proxyHostname.size()) {
+ serverName = proxyHostname.toCFString();
+ CFDictionaryAddValue(query, kSecAttrServer, serverName);
+ }
+
+ // This is to get the user name in the result:
+ CFDictionaryAddValue(query, kSecReturnAttributes, kCFBooleanTrue);
+ // This one to get the password:
+ CFDictionaryAddValue(query, kSecReturnData, kCFBooleanTrue);
+
+ // The default for kSecMatchLimit key is 1 (the first match only), which is fine,
+ // so don't set this value explicitly.
+
+ QCFType<CFTypeRef> replyData;
+ if (SecItemCopyMatching(query, &replyData) != errSecSuccess) {
+ qCWarning(lcQnam, "Failed to extract user name and password from the keychain.");
+ return false;
+ }
+
+ if (!replyData || CFDictionaryGetTypeID() != CFGetTypeID(replyData)) {
+ qCWarning(lcQnam, "Query returned data in unexpected format.");
+ return false;
+ }
+
+ CFDictionaryRef accountData = replyData.as<CFDictionaryRef>();
+ const void *value = CFDictionaryGetValue(accountData, kSecAttrAccount);
+ if (!value || CFGetTypeID(value) != CFStringGetTypeID()) {
+ qCWarning(lcQnam, "Cannot find user name or its format is unknown.");
+ return false;
+ }
+ username = QString::fromCFString(static_cast<CFStringRef>(value));
+
+ value = CFDictionaryGetValue(accountData, kSecValueData);
+ if (!value || CFGetTypeID(value) != CFDataGetTypeID()) {
+ qCWarning(lcQnam, "Cannot find password or its format is unknown.");
+ return false;
}
- return retValue;
+ const CFDataRef passData = static_cast<const CFDataRef>(value);
+ password = QString::fromLocal8Bit(reinterpret_cast<const char *>(CFDataGetBytePtr(passData)),
+ qsizetype(CFDataGetLength(passData)));
+ return true;
}
-#endif
+#endif // Q_OS_MACOS
static void ensureInitialized()
{
-#ifdef QT_BUILD_INTERNAL
+#if QT_CONFIG(private_tests)
(void) debugpipeBackend();
#endif
@@ -792,6 +781,46 @@ QNetworkReply *QNetworkAccessManager::get(const QNetworkRequest &request)
}
/*!
+ \since 6.7
+
+ \overload
+
+ \note A GET request with a message body is not cached.
+
+ \note If the request is redirected, the message body will be kept only if the status code is
+ 307 or 308.
+*/
+
+QNetworkReply *QNetworkAccessManager::get(const QNetworkRequest &request, QIODevice *data)
+{
+ QNetworkRequest newRequest(request);
+ return d_func()->postProcess(
+ createRequest(QNetworkAccessManager::GetOperation, newRequest, data));
+}
+
+/*!
+ \since 6.7
+
+ \overload
+
+ \note A GET request with a message body is not cached.
+
+ \note If the request is redirected, the message body will be kept only if the status code is
+ 307 or 308.
+*/
+
+QNetworkReply *QNetworkAccessManager::get(const QNetworkRequest &request, const QByteArray &data)
+{
+ QBuffer *buffer = new QBuffer;
+ buffer->setData(data);
+ buffer->open(QIODevice::ReadOnly);
+
+ QNetworkReply *reply = get(request, buffer);
+ buffer->setParent(reply);
+ return reply;
+}
+
+/*!
Sends an HTTP POST request to the destination specified by \a request
and returns a new QNetworkReply object opened for reading that will
contain the reply sent by the server. The contents of the \a data
@@ -827,6 +856,24 @@ QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, const
return reply;
}
+/*!
+ \overload
+
+ \since 6.8
+
+ Sends the POST request specified by \a request without a body and returns
+ a new QNetworkReply object.
+*/
+QNetworkReply *QNetworkAccessManager::post(const QNetworkRequest &request, std::nullptr_t nptr)
+{
+ Q_UNUSED(nptr);
+ QIODevice *dev = nullptr;
+
+ return d_func()->postProcess(createRequest(QNetworkAccessManager::PostOperation,
+ request,
+ dev));
+}
+
#if QT_CONFIG(http) || defined(Q_OS_WASM)
/*!
\since 4.8
@@ -911,6 +958,23 @@ QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, const
}
/*!
+ \overload
+
+ \since 6.8
+
+ Sends the PUT request specified by \a request without a body and returns
+ a new QNetworkReply object.
+*/
+
+QNetworkReply *QNetworkAccessManager::put(const QNetworkRequest &request, std::nullptr_t nptr)
+{
+ Q_UNUSED(nptr);
+ QIODevice *dev = nullptr;
+
+ return d_func()->postProcess(createRequest(QNetworkAccessManager::PutOperation, request, dev));
+}
+
+/*!
\since 4.6
Sends a request to delete the resource identified by the URL of \a request.
@@ -977,7 +1041,7 @@ void QNetworkAccessManager::connectToHostEncrypted(const QString &hostName, quin
QUrl url;
url.setHost(hostName);
url.setPort(port);
- url.setScheme(QLatin1String("preconnect-https"));
+ url.setScheme("preconnect-https"_L1);
QNetworkRequest request(url);
if (sslConfiguration != QSslConfiguration::defaultConfiguration())
request.setSslConfiguration(sslConfiguration);
@@ -1008,7 +1072,7 @@ void QNetworkAccessManager::connectToHost(const QString &hostName, quint16 port)
QUrl url;
url.setHost(hostName);
url.setPort(port);
- url.setScheme(QLatin1String("preconnect-http"));
+ url.setScheme("preconnect-http"_L1);
QNetworkRequest request(url);
get(request);
}
@@ -1144,8 +1208,8 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
}
#if QT_CONFIG(http) || defined (Q_OS_WASM)
- if (!req.transferTimeout())
- req.setTransferTimeout(transferTimeout());
+ if (req.transferTimeoutAsDuration() == 0ms)
+ req.setTransferTimeout(transferTimeoutAsDuration());
#endif
if (autoDeleteReplies()
@@ -1163,13 +1227,13 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
|| op == QNetworkAccessManager::HeadOperation) {
if (isLocalFile
#ifdef Q_OS_ANDROID
- || scheme == QLatin1String("assets")
+ || scheme == "assets"_L1
#endif
- || scheme == QLatin1String("qrc")) {
+ || scheme == "qrc"_L1) {
return new QNetworkReplyFileImpl(this, req, op);
}
- if (scheme == QLatin1String("data"))
+ if (scheme == "data"_L1)
return new QNetworkReplyDataImpl(this, req, op);
// A request with QNetworkRequest::AlwaysCache does not need any bearer management
@@ -1211,7 +1275,7 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
#ifdef Q_OS_WASM
Q_UNUSED(isLocalFile);
// Support http, https, and relative urls
- if (scheme == QLatin1String("http") || scheme == QLatin1String("https") || scheme.isEmpty()) {
+ if (scheme == "http"_L1 || scheme == "https"_L1 || scheme.isEmpty()) {
QNetworkReplyWasmImpl *reply = new QNetworkReplyWasmImpl(this);
QNetworkReplyWasmImplPrivate *priv = reply->d_func();
priv->manager = this;
@@ -1245,7 +1309,7 @@ QNetworkReply *QNetworkAccessManager::createRequest(QNetworkAccessManager::Opera
// MUST NOT add one.
if (stsUrl.port() == 80)
stsUrl.setPort(443);
- stsUrl.setScheme(QLatin1String("https"));
+ stsUrl.setScheme("https"_L1);
request.setUrl(stsUrl);
}
#endif
@@ -1396,38 +1460,59 @@ void QNetworkAccessManager::setAutoDeleteReplies(bool shouldAutoDelete)
}
/*!
+ \fn int QNetworkAccessManager::transferTimeout() const
\since 5.15
Returns the timeout used for transfers, in milliseconds.
- This timeout is zero if setTransferTimeout() hasn't been
- called, which means that the timeout is not used.
+ \sa setTransferTimeout()
+*/
+
+/*!
+ \fn void QNetworkAccessManager::setTransferTimeout(int timeout)
+ \since 5.15
+
+ Sets \a timeout as the transfer timeout in milliseconds.
+
+ \sa setTransferTimeout(std::chrono::milliseconds),
+ transferTimeout(), transferTimeoutAsDuration()
*/
-int QNetworkAccessManager::transferTimeout() const
+
+/*!
+ \since 6.7
+
+ Returns the timeout duration after which the transfer is aborted if no
+ data is exchanged.
+
+ The default duration is zero, which means that the timeout is not used.
+
+ \sa setTransferTimeout(std::chrono::milliseconds)
+ */
+std::chrono::milliseconds QNetworkAccessManager::transferTimeoutAsDuration() const
{
return d_func()->transferTimeout;
}
/*!
- \since 5.15
+ \since 6.7
- Sets \a timeout as the transfer timeout in milliseconds.
+ Sets the timeout \a duration to abort the transfer if no data is exchanged.
Transfers are aborted if no bytes are transferred before
the timeout expires. Zero means no timer is set. If no
argument is provided, the timeout is
- QNetworkRequest::DefaultTransferTimeoutConstant. If this function
+ QNetworkRequest::DefaultTransferTimeout. If this function
is not called, the timeout is disabled and has the
value zero. The request-specific non-zero timeouts set for
the requests that are executed override this value. This means
that if QNetworkAccessManager has an enabled timeout, it needs
to be disabled to execute a request without a timeout.
- \sa transferTimeout()
-*/
-void QNetworkAccessManager::setTransferTimeout(int timeout)
+ \sa transferTimeoutAsDuration()
+ */
+void QNetworkAccessManager::setTransferTimeout(std::chrono::milliseconds duration)
{
- d_func()->transferTimeout = timeout;
+ d_func()->transferTimeout = duration;
}
void QNetworkAccessManagerPrivate::_q_replyFinished(QNetworkReply *reply)
@@ -1665,7 +1750,7 @@ QNetworkRequest QNetworkAccessManagerPrivate::prepareMultipart(const QNetworkReq
// add Content-Type header if not there already
if (!request.header(QNetworkRequest::ContentTypeHeader).isValid()) {
QByteArray contentType;
- contentType.reserve(34 + multiPart->d_func()->boundary.count());
+ contentType.reserve(34 + multiPart->d_func()->boundary.size());
contentType += "multipart/";
switch (multiPart->d_func()->contentType) {
case QHttpMultiPart::RelatedType:
@@ -1688,9 +1773,9 @@ QNetworkRequest QNetworkAccessManagerPrivate::prepareMultipart(const QNetworkReq
// add MIME-Version header if not there already (we must include the header
// if the message conforms to RFC 2045, see section 4 of that RFC)
- QByteArray mimeHeader("MIME-Version");
+ auto mimeHeader = "MIME-Version"_ba;
if (!request.hasRawHeader(mimeHeader))
- newRequest.setRawHeader(mimeHeader, QByteArray("1.0"));
+ newRequest.setRawHeader(mimeHeader, "1.0"_ba);
QIODevice *device = multiPart->d_func()->device;
if (!device->isReadable()) {
@@ -1713,15 +1798,15 @@ QNetworkRequest QNetworkAccessManagerPrivate::prepareMultipart(const QNetworkReq
*/
void QNetworkAccessManagerPrivate::ensureBackendPluginsLoaded()
{
- static QBasicMutex mutex;
+ Q_CONSTINIT static QBasicMutex mutex;
std::unique_lock locker(mutex);
- if (!loader())
+ if (!qnabfLoader())
return;
#if QT_CONFIG(library)
- loader->update();
+ qnabfLoader->update();
#endif
int index = 0;
- while (loader->instance(index))
+ while (qnabfLoader->instance(index))
++index;
}
diff --git a/src/network/access/qnetworkaccessmanager.h b/src/network/access/qnetworkaccessmanager.h
index b03191505b..0d069b2a9b 100644
--- a/src/network/access/qnetworkaccessmanager.h
+++ b/src/network/access/qnetworkaccessmanager.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKACCESSMANAGER_H
#define QNETWORKACCESSMANAGER_H
@@ -116,10 +80,14 @@ public:
QNetworkReply *head(const QNetworkRequest &request);
QNetworkReply *get(const QNetworkRequest &request);
+ QNetworkReply *get(const QNetworkRequest &request, QIODevice *data);
+ QNetworkReply *get(const QNetworkRequest &request, const QByteArray &data);
QNetworkReply *post(const QNetworkRequest &request, QIODevice *data);
QNetworkReply *post(const QNetworkRequest &request, const QByteArray &data);
+ QNetworkReply *post(const QNetworkRequest &request, std::nullptr_t nptr);
QNetworkReply *put(const QNetworkRequest &request, QIODevice *data);
QNetworkReply *put(const QNetworkRequest &request, const QByteArray &data);
+ QNetworkReply *put(const QNetworkRequest &request, std::nullptr_t nptr);
QNetworkReply *deleteResource(const QNetworkRequest &request);
QNetworkReply *sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, QIODevice *data = nullptr);
QNetworkReply *sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, const QByteArray &data);
@@ -145,8 +113,14 @@ public:
bool autoDeleteReplies() const;
void setAutoDeleteReplies(bool autoDelete);
+ QT_NETWORK_INLINE_SINCE(6, 8)
int transferTimeout() const;
- void setTransferTimeout(int timeout = QNetworkRequest::DefaultTransferTimeoutConstant);
+ QT_NETWORK_INLINE_SINCE(6, 8)
+ void setTransferTimeout(int timeout);
+
+ std::chrono::milliseconds transferTimeoutAsDuration() const;
+ void setTransferTimeout(std::chrono::milliseconds duration =
+ QNetworkRequest::DefaultTransferTimeout);
Q_SIGNALS:
#ifndef QT_NO_NETWORKPROXY
@@ -183,6 +157,18 @@ private:
#endif
};
+#if QT_NETWORK_INLINE_IMPL_SINCE(6, 8)
+int QNetworkAccessManager::transferTimeout() const
+{
+ return int(transferTimeoutAsDuration().count());
+}
+
+void QNetworkAccessManager::setTransferTimeout(int timeout)
+{
+ setTransferTimeout(std::chrono::milliseconds(timeout));
+}
+#endif // INLINE_SINCE 6.8
+
QT_END_NAMESPACE
#endif
diff --git a/src/network/access/qnetworkaccessmanager_p.h b/src/network/access/qnetworkaccessmanager_p.h
index ad03cab9a2..491a5acaa4 100644
--- a/src/network/access/qnetworkaccessmanager_p.h
+++ b/src/network/access/qnetworkaccessmanager_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKACCESSMANAGER_P_H
#define QNETWORKACCESSMANAGER_P_H
@@ -167,7 +131,7 @@ public:
bool autoDeleteReplies = false;
- int transferTimeout = 0;
+ std::chrono::milliseconds transferTimeout{0};
Q_DECLARE_PUBLIC(QNetworkAccessManager)
};
diff --git a/src/network/access/qnetworkcookie.cpp b/src/network/access/qnetworkcookie.cpp
index 81e321d86a..8ea5fdbe57 100644
--- a/src/network/access/qnetworkcookie.cpp
+++ b/src/network/access/qnetworkcookie.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qnetworkcookie.h"
#include "qnetworkcookie_p.h"
@@ -43,18 +7,24 @@
#include "qnetworkrequest.h"
#include "qnetworkreply.h"
#include "QtCore/qbytearray.h"
+#include "QtCore/qdatetime.h"
#include "QtCore/qdebug.h"
#include "QtCore/qlist.h"
#include "QtCore/qlocale.h"
#include <QtCore/qregularexpression.h>
#include "QtCore/qstring.h"
#include "QtCore/qstringlist.h"
+#include "QtCore/qtimezone.h"
#include "QtCore/qurl.h"
#include "QtNetwork/qhostaddress.h"
#include "private/qobject_p.h"
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
+QT_IMPL_METATYPE_EXTERN(QNetworkCookie)
+
/*!
\class QNetworkCookie
\since 4.4
@@ -406,13 +376,13 @@ void QNetworkCookie::setValue(const QByteArray &value)
}
// ### move this to qnetworkcookie_p.h and share with qnetworkaccesshttpbackend
-static QPair<QByteArray, QByteArray> nextField(const QByteArray &text, int &position, bool isNameValue)
+static QPair<QByteArray, QByteArray> nextField(QByteArrayView text, int &position, bool isNameValue)
{
// format is one of:
// (1) token
// (2) token = token
// (3) token = quoted-string
- const int length = text.length();
+ const int length = text.size();
position = nextNonWhitespace(text, position);
int semiColonPosition = text.indexOf(';', position);
@@ -426,11 +396,11 @@ static QPair<QByteArray, QByteArray> nextField(const QByteArray &text, int &posi
equalsPosition = semiColonPosition; //no '=' means there is an attribute-name but no attribute-value
}
- QByteArray first = text.mid(position, equalsPosition - position).trimmed();
+ QByteArray first = text.mid(position, equalsPosition - position).trimmed().toByteArray();
QByteArray second;
int secondLength = semiColonPosition - equalsPosition - 1;
if (secondLength > 0)
- second = text.mid(equalsPosition + 1, secondLength).trimmed();
+ second = text.mid(equalsPosition + 1, secondLength).trimmed().toByteArray();
position = semiColonPosition;
return qMakePair(first, second);
@@ -473,29 +443,33 @@ static QPair<QByteArray, QByteArray> nextField(const QByteArray &text, int &posi
*/
namespace {
-QByteArray sameSiteToRawString(QNetworkCookie::SameSite samesite)
+
+constexpr QByteArrayView sameSiteNone() noexcept { return "None"; }
+constexpr QByteArrayView sameSiteLax() noexcept { return "Lax"; }
+constexpr QByteArrayView sameSiteStrict() noexcept { return "Strict"; }
+
+QByteArrayView sameSiteToRawString(QNetworkCookie::SameSite samesite) noexcept
{
switch (samesite) {
case QNetworkCookie::SameSite::None:
- return QByteArrayLiteral("None");
+ return sameSiteNone();
case QNetworkCookie::SameSite::Lax:
- return QByteArrayLiteral("Lax");
+ return sameSiteLax();
case QNetworkCookie::SameSite::Strict:
- return QByteArrayLiteral("Strict");
+ return sameSiteStrict();
case QNetworkCookie::SameSite::Default:
break;
}
- return QByteArray();
+ return QByteArrayView();
}
-QNetworkCookie::SameSite sameSiteFromRawString(QByteArray str)
+QNetworkCookie::SameSite sameSiteFromRawString(QByteArrayView str) noexcept
{
- str = str.toLower();
- if (str == QByteArrayLiteral("none"))
+ if (str.compare(sameSiteNone(), Qt::CaseInsensitive) == 0)
return QNetworkCookie::SameSite::None;
- if (str == QByteArrayLiteral("lax"))
+ if (str.compare(sameSiteLax(), Qt::CaseInsensitive) == 0)
return QNetworkCookie::SameSite::Lax;
- if (str == QByteArrayLiteral("strict"))
+ if (str.compare(sameSiteStrict(), Qt::CaseInsensitive) == 0)
return QNetworkCookie::SameSite::Strict;
return QNetworkCookie::SameSite::Default;
}
@@ -533,11 +507,11 @@ QByteArray QNetworkCookie::toRawForm(RawForm form) const
if (!isSessionCookie()) {
result += "; expires=";
result += QLocale::c().toString(d->expirationDate.toUTC(),
- QLatin1String("ddd, dd-MMM-yyyy hh:mm:ss 'GMT")).toLatin1();
+ "ddd, dd-MMM-yyyy hh:mm:ss 'GMT"_L1).toLatin1();
}
if (!d->domain.isEmpty()) {
result += "; domain=";
- if (d->domain.startsWith(QLatin1Char('.'))) {
+ if (d->domain.startsWith(u'.')) {
result += '.';
result += QUrl::toAce(d->domain.mid(1));
} else {
@@ -606,11 +580,11 @@ static inline bool isValueSeparator(char c)
static inline bool isWhitespace(char c)
{ return c == ' ' || c == '\t'; }
-static bool checkStaticArray(int &val, const QByteArray &dateString, int at, const char *array, int size)
+static bool checkStaticArray(int &val, QByteArrayView dateString, int at, const char *array, int size)
{
if (dateString[at] < 'a' || dateString[at] > 'z')
return false;
- if (val == -1 && dateString.length() >= at + 3) {
+ if (val == -1 && dateString.size() >= at + 3) {
int j = 0;
int i = 0;
while (i <= size) {
@@ -653,7 +627,7 @@ static bool checkStaticArray(int &val, const QByteArray &dateString, int at, con
Or in their own words:
"} // else what the hell is this."
*/
-static QDateTime parseDateString(const QByteArray &dateString)
+static QDateTime parseDateString(QByteArrayView dateString)
{
QTime time;
// placeholders for values when we are not sure it is a year, month or day
@@ -665,10 +639,10 @@ static QDateTime parseDateString(const QByteArray &dateString)
// hour:minute:second.ms pm
static const QRegularExpression timeRx(
- u"(\\d\\d?):(\\d\\d?)(?::(\\d\\d?)(?:\\.(\\d{1,3}))?)?(?:\\s*(am|pm))?"_qs);
+ u"(\\d\\d?):(\\d\\d?)(?::(\\d\\d?)(?:\\.(\\d{1,3}))?)?(?:\\s*(am|pm))?"_s);
int at = 0;
- while (at < dateString.length()) {
+ while (at < dateString.size()) {
#ifdef PARSEDATESTRINGDEBUG
qDebug() << dateString.mid(at);
#endif
@@ -709,20 +683,20 @@ static QDateTime parseDateString(const QByteArray &dateString)
&& (dateString[at - 1] == 't')))) {
int end = 1;
- while (end < 5 && dateString.length() > at+end
+ while (end < 5 && dateString.size() > at+end
&& dateString[at + end] >= '0' && dateString[at + end] <= '9')
++end;
int minutes = 0;
int hours = 0;
switch (end - 1) {
case 4:
- minutes = atoi(dateString.mid(at + 3, 2).constData());
+ minutes = dateString.mid(at + 3, 2).toInt();
Q_FALLTHROUGH();
case 2:
- hours = atoi(dateString.mid(at + 1, 2).constData());
+ hours = dateString.mid(at + 1, 2).toInt();
break;
case 1:
- hours = atoi(dateString.mid(at + 1, 1).constData());
+ hours = dateString.mid(at + 1, 1).toInt();
break;
default:
at += end;
@@ -741,7 +715,7 @@ static QDateTime parseDateString(const QByteArray &dateString)
// Time
if (isNum && time.isNull()
- && dateString.length() >= at + 3
+ && dateString.size() >= at + 3
&& (dateString[at + 2] == ':' || dateString[at + 1] == ':')) {
// While the date can be found all over the string the format
// for the time is set and a nice regexp can be used.
@@ -755,7 +729,7 @@ static QDateTime parseDateString(const QByteArray &dateString)
int ms = match.capturedView(4).toInt();
QStringView ampm = match.capturedView(5);
if (h < 12 && !ampm.isEmpty())
- if (ampm == QLatin1String("pm"))
+ if (ampm == "pm"_L1)
h += 12;
time = QTime(h, m, s, ms);
#ifdef PARSEDATESTRINGDEBUG
@@ -769,11 +743,11 @@ static QDateTime parseDateString(const QByteArray &dateString)
// 4 digit Year
if (isNum
&& year == -1
- && dateString.length() > at + 3) {
+ && dateString.size() > at + 3) {
if (isNumber(dateString[at + 1])
&& isNumber(dateString[at + 2])
&& isNumber(dateString[at + 3])) {
- year = atoi(dateString.mid(at, 4).constData());
+ year = dateString.mid(at, 4).toInt();
at += 4;
#ifdef PARSEDATESTRINGDEBUG
qDebug() << "Year:" << year;
@@ -786,10 +760,10 @@ static QDateTime parseDateString(const QByteArray &dateString)
// Could be month, day or year
if (isNum) {
int length = 1;
- if (dateString.length() > at + 1
+ if (dateString.size() > at + 1
&& isNumber(dateString[at + 1]))
++length;
- int x = atoi(dateString.mid(at, length).constData());
+ int x = dateString.mid(at, length).toInt();
if (year == -1 && (x > 31 || x == 0)) {
year = x;
} else {
@@ -938,11 +912,11 @@ static QDateTime parseDateString(const QByteArray &dateString)
if (!date.isValid())
date = QDate(day + y2k, month, year);
- QDateTime dateTime(date, time, Qt::UTC);
+ QDateTime dateTime(date, time, QTimeZone::UTC);
- if (zoneOffset != -1) {
+ if (zoneOffset != -1)
dateTime = dateTime.addSecs(zoneOffset);
- }
+
if (!dateTime.isValid())
return QDateTime();
return dateTime;
@@ -958,19 +932,19 @@ static QDateTime parseDateString(const QByteArray &dateString)
cookie that is parsed.
\sa toRawForm()
+ \note In Qt versions prior to 6.7, this function took QByteArray only.
*/
-QList<QNetworkCookie> QNetworkCookie::parseCookies(const QByteArray &cookieString)
+QList<QNetworkCookie> QNetworkCookie::parseCookies(QByteArrayView cookieString)
{
// cookieString can be a number of set-cookie header strings joined together
// by \n, parse each line separately.
QList<QNetworkCookie> cookies;
- QList<QByteArray> list = cookieString.split('\n');
- for (int a = 0; a < list.size(); a++)
- cookies += QNetworkCookiePrivate::parseSetCookieHeaderLine(list.at(a));
+ for (auto s : QLatin1StringView(cookieString).tokenize('\n'_L1))
+ cookies += QNetworkCookiePrivate::parseSetCookieHeaderLine(s);
return cookies;
}
-QList<QNetworkCookie> QNetworkCookiePrivate::parseSetCookieHeaderLine(const QByteArray &cookieString)
+QList<QNetworkCookie> QNetworkCookiePrivate::parseSetCookieHeaderLine(QByteArrayView cookieString)
{
// According to http://wp.netscape.com/newsref/std/cookie_spec.html,<
// the Set-Cookie response header is of the format:
@@ -985,7 +959,7 @@ QList<QNetworkCookie> QNetworkCookiePrivate::parseSetCookieHeaderLine(const QByt
const QDateTime now = QDateTime::currentDateTimeUtc();
int position = 0;
- const int length = cookieString.length();
+ const int length = cookieString.size();
while (position < length) {
QNetworkCookie cookie;
@@ -1003,28 +977,27 @@ QList<QNetworkCookie> QNetworkCookiePrivate::parseSetCookieHeaderLine(const QByt
case ';':
// new field in the cookie
field = nextField(cookieString, position, false);
- field.first = field.first.toLower(); // everything but the NAME=VALUE is case-insensitive
- if (field.first == "expires") {
- position -= field.second.length();
+ if (field.first.compare("expires", Qt::CaseInsensitive) == 0) {
+ position -= field.second.size();
int end;
for (end = position; end < length; ++end)
if (isValueSeparator(cookieString.at(end)))
break;
- QByteArray dateString = cookieString.mid(position, end - position).trimmed();
+ QByteArray dateString = cookieString.mid(position, end - position).trimmed().toByteArray().toLower();
position = end;
- QDateTime dt = parseDateString(dateString.toLower());
+ QDateTime dt = parseDateString(dateString);
if (dt.isValid())
cookie.setExpirationDate(dt);
//if unparsed, ignore the attribute but not the whole cookie (RFC6265 section 5.2.1)
- } else if (field.first == "domain") {
- QByteArray rawDomain = field.second;
+ } else if (field.first.compare("domain", Qt::CaseInsensitive) == 0) {
+ QByteArrayView rawDomain = field.second;
//empty domain should be ignored (RFC6265 section 5.2.3)
if (!rawDomain.isEmpty()) {
- QString maybeLeadingDot;
+ QLatin1StringView maybeLeadingDot;
if (rawDomain.startsWith('.')) {
- maybeLeadingDot = QLatin1Char('.');
+ maybeLeadingDot = "."_L1;
rawDomain = rawDomain.mid(1);
}
@@ -1039,7 +1012,7 @@ QList<QNetworkCookie> QNetworkCookiePrivate::parseSetCookieHeaderLine(const QByt
return result;
}
}
- } else if (field.first == "max-age") {
+ } else if (field.first.compare("max-age", Qt::CaseInsensitive) == 0) {
bool ok = false;
int secs = field.second.toInt(&ok);
if (ok) {
@@ -1051,7 +1024,7 @@ QList<QNetworkCookie> QNetworkCookiePrivate::parseSetCookieHeaderLine(const QByt
}
}
//if unparsed, ignore the attribute but not the whole cookie (RFC6265 section 5.2.2)
- } else if (field.first == "path") {
+ } else if (field.first.compare("path", Qt::CaseInsensitive) == 0) {
if (field.second.startsWith('/')) {
// ### we should treat cookie paths as an octet sequence internally
// However RFC6265 says we should assume UTF-8 for presentation as a string
@@ -1061,11 +1034,11 @@ QList<QNetworkCookie> QNetworkCookiePrivate::parseSetCookieHeaderLine(const QByt
// and also IETF test case path0030 which has valid and empty path in the same cookie
cookie.setPath(QString());
}
- } else if (field.first == "secure") {
+ } else if (field.first.compare("secure", Qt::CaseInsensitive) == 0) {
cookie.setSecure(true);
- } else if (field.first == "httponly") {
+ } else if (field.first.compare("httponly", Qt::CaseInsensitive) == 0) {
cookie.setHttpOnly(true);
- } else if (field.first == "samesite") {
+ } else if (field.first.compare("samesite", Qt::CaseInsensitive) == 0) {
cookie.setSameSitePolicy(sameSiteFromRawString(field.second));
} else {
// ignore unknown fields in the cookie (RFC6265 section 5.2, rule 6)
@@ -1092,9 +1065,9 @@ void QNetworkCookie::normalize(const QUrl &url)
// don't do path checking. See QTBUG-5815
if (d->path.isEmpty()) {
QString pathAndFileName = url.path();
- QString defaultPath = pathAndFileName.left(pathAndFileName.lastIndexOf(QLatin1Char('/'))+1);
+ QString defaultPath = pathAndFileName.left(pathAndFileName.lastIndexOf(u'/') + 1);
if (defaultPath.isEmpty())
- defaultPath = QLatin1Char('/');
+ defaultPath = u'/';
d->path = defaultPath;
}
@@ -1104,12 +1077,12 @@ void QNetworkCookie::normalize(const QUrl &url)
QHostAddress hostAddress(d->domain);
if (hostAddress.protocol() != QAbstractSocket::IPv4Protocol
&& hostAddress.protocol() != QAbstractSocket::IPv6Protocol
- && !d->domain.startsWith(QLatin1Char('.'))) {
+ && !d->domain.startsWith(u'.')) {
// Ensure the domain starts with a dot if its field was not empty
// in the HTTP header. There are some servers that forget the
// leading dot and this is actually forbidden according to RFC 2109,
// but all browsers accept it anyway so we do that as well.
- d->domain.prepend(QLatin1Char('.'));
+ d->domain.prepend(u'.');
}
}
}
@@ -1125,3 +1098,5 @@ QDebug operator<<(QDebug s, const QNetworkCookie &cookie)
#endif
QT_END_NAMESPACE
+
+#include "moc_qnetworkcookie.cpp"
diff --git a/src/network/access/qnetworkcookie.h b/src/network/access/qnetworkcookie.h
index 265f3a7124..aed9c8af12 100644
--- a/src/network/access/qnetworkcookie.h
+++ b/src/network/access/qnetworkcookie.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKCOOKIE_H
#define QNETWORKCOOKIE_H
@@ -77,7 +41,7 @@ public:
QNetworkCookie &operator=(QNetworkCookie &&other) noexcept { swap(other); return *this; }
QNetworkCookie &operator=(const QNetworkCookie &other);
- void swap(QNetworkCookie &other) noexcept { qSwap(d, other.d); }
+ void swap(QNetworkCookie &other) noexcept { d.swap(other.d); }
bool operator==(const QNetworkCookie &other) const;
inline bool operator!=(const QNetworkCookie &other) const
@@ -111,7 +75,10 @@ public:
bool hasSameIdentifier(const QNetworkCookie &other) const;
void normalize(const QUrl &url);
+#if QT_NETWORK_REMOVED_SINCE(6, 7)
static QList<QNetworkCookie> parseCookies(const QByteArray &cookieString);
+#endif
+ static QList<QNetworkCookie> parseCookies(QByteArrayView cookieString);
private:
QSharedDataPointer<QNetworkCookiePrivate> d;
@@ -127,6 +94,6 @@ Q_NETWORK_EXPORT QDebug operator<<(QDebug, const QNetworkCookie &);
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(QNetworkCookie)
+QT_DECL_METATYPE_EXTERN(QNetworkCookie, Q_NETWORK_EXPORT)
#endif
diff --git a/src/network/access/qnetworkcookie_p.h b/src/network/access/qnetworkcookie_p.h
index 0cf3442fee..ce4378fd64 100644
--- a/src/network/access/qnetworkcookie_p.h
+++ b/src/network/access/qnetworkcookie_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKCOOKIE_P_H
#define QNETWORKCOOKIE_P_H
@@ -61,7 +25,7 @@ class QNetworkCookiePrivate: public QSharedData
{
public:
QNetworkCookiePrivate() = default;
- static QList<QNetworkCookie> parseSetCookieHeaderLine(const QByteArray &cookieString);
+ static QList<QNetworkCookie> parseSetCookieHeaderLine(QByteArrayView cookieString);
QDateTime expirationDate;
QString domain;
@@ -79,13 +43,13 @@ static inline bool isLWS(char c)
return c == ' ' || c == '\t' || c == '\r' || c == '\n';
}
-static int nextNonWhitespace(const QByteArray &text, int from)
+static int nextNonWhitespace(QByteArrayView text, int from)
{
// RFC 2616 defines linear whitespace as:
// LWS = [CRLF] 1*( SP | HT )
// We ignore the fact that CRLF must come as a pair at this point
// It's an invalid HTTP header if that happens.
- while (from < text.length()) {
+ while (from < text.size()) {
if (isLWS(text.at(from)))
++from;
else
@@ -93,7 +57,7 @@ static int nextNonWhitespace(const QByteArray &text, int from)
}
// reached the end
- return text.length();
+ return text.size();
}
QT_END_NAMESPACE
diff --git a/src/network/access/qnetworkcookiejar.cpp b/src/network/access/qnetworkcookiejar.cpp
index c01bd339ca..82746f91b1 100644
--- a/src/network/access/qnetworkcookiejar.cpp
+++ b/src/network/access/qnetworkcookiejar.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qnetworkcookiejar.h"
#include "qnetworkcookiejar_p.h"
@@ -50,13 +14,15 @@ QT_BEGIN_NAMESPACE
static bool qIsEffectiveTLD(QStringView domain)
{
// provide minimal checking by not accepting cookies on real TLDs
- return !domain.contains(QLatin1Char('.'));
+ return !domain.contains(u'.');
}
QT_END_NAMESPACE
#endif
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
/*!
\class QNetworkCookieJar
\since 4.4
@@ -146,11 +112,11 @@ void QNetworkCookieJar::setAllCookies(const QList<QNetworkCookie> &cookieList)
d->allCookies = cookieList;
}
-static inline bool isParentPath(const QString &path, const QString &reference)
+static inline bool isParentPath(QStringView path, QStringView reference)
{
- if ((path.isEmpty() && reference == QLatin1String("/")) || path.startsWith(reference)) {
+ if ((path.isEmpty() && reference == "/"_L1) || path.startsWith(reference)) {
//The cookie-path and the request-path are identical.
- if (path.length() == reference.length())
+ if (path.size() == reference.size())
return true;
//The cookie-path is a prefix of the request-path, and the last
//character of the cookie-path is %x2F ("/").
@@ -159,18 +125,18 @@ static inline bool isParentPath(const QString &path, const QString &reference)
//The cookie-path is a prefix of the request-path, and the first
//character of the request-path that is not included in the cookie-
//path is a %x2F ("/") character.
- if (path.at(reference.length()) == u'/')
+ if (path.at(reference.size()) == u'/')
return true;
}
return false;
}
-static inline bool isParentDomain(const QString &domain, const QString &reference)
+static inline bool isParentDomain(QStringView domain, QStringView reference)
{
- if (!reference.startsWith(QLatin1Char('.')))
+ if (!reference.startsWith(u'.'))
return domain == reference;
- return domain.endsWith(reference) || domain == QStringView{reference}.mid(1);
+ return domain.endsWith(reference) || domain == reference.mid(1);
}
/*!
@@ -234,49 +200,38 @@ QList<QNetworkCookie> QNetworkCookieJar::cookiesForUrl(const QUrl &url) const
Q_D(const QNetworkCookieJar);
const QDateTime now = QDateTime::currentDateTimeUtc();
QList<QNetworkCookie> result;
- bool isEncrypted = url.scheme() == QLatin1String("https");
+ const bool isEncrypted = url.scheme() == "https"_L1;
// scan our cookies for something that matches
- QList<QNetworkCookie>::ConstIterator it = d->allCookies.constBegin(),
- end = d->allCookies.constEnd();
- for ( ; it != end; ++it) {
- if (!isParentDomain(url.host(), it->domain()))
+ for (const auto &cookie : std::as_const(d->allCookies)) {
+ if (!isEncrypted && cookie.isSecure())
continue;
- if (!isParentPath(url.path(), it->path()))
+ if (!cookie.isSessionCookie() && cookie.expirationDate() < now)
continue;
- if (!(*it).isSessionCookie() && (*it).expirationDate() < now)
+ const QString urlHost = url.host();
+ const QString cookieDomain = cookie.domain();
+ if (!isParentDomain(urlHost, cookieDomain))
continue;
- if ((*it).isSecure() && !isEncrypted)
+ if (!isParentPath(url.path(), cookie.path()))
continue;
- QString domain = it->domain();
- if (domain.startsWith(QLatin1Char('.'))) /// Qt6?: remove when compliant with RFC6265
- domain = domain.mid(1);
+ QStringView domain = cookieDomain;
+ if (domain.startsWith(u'.')) /// Qt6?: remove when compliant with RFC6265
+ domain = domain.sliced(1);
#if QT_CONFIG(topleveldomain)
- if (qIsEffectiveTLD(domain) && url.host() != domain)
+ if (urlHost != domain && qIsEffectiveTLD(domain))
continue;
#else
- if (!domain.contains(QLatin1Char('.')) && url.host() != domain)
+ if (!domain.contains(u'.') && urlHost != domain)
continue;
#endif // topleveldomain
- // insert this cookie into result, sorted by path
- QList<QNetworkCookie>::Iterator insertIt = result.begin();
- while (insertIt != result.end()) {
- if (insertIt->path().length() < it->path().length()) {
- // insert here
- insertIt = result.insert(insertIt, *it);
- break;
- } else {
- ++insertIt;
- }
- }
-
- // this is the shortest path yet, just append
- if (insertIt == result.end())
- result += *it;
+ result += cookie;
}
+ auto longerPath = [](const auto &c1, const auto &c2)
+ { return c1.path().size() > c2.path().size(); };
+ std::sort(result.begin(), result.end(), longerPath);
return result;
}
@@ -333,12 +288,11 @@ bool QNetworkCookieJar::updateCookie(const QNetworkCookie &cookie)
bool QNetworkCookieJar::deleteCookie(const QNetworkCookie &cookie)
{
Q_D(QNetworkCookieJar);
- QList<QNetworkCookie>::Iterator it;
- for (it = d->allCookies.begin(); it != d->allCookies.end(); ++it) {
- if (it->hasSameIdentifier(cookie)) {
- d->allCookies.erase(it);
- return true;
- }
+ const auto it = std::find_if(d->allCookies.cbegin(), d->allCookies.cend(),
+ [&cookie](const auto &c) { return c.hasSameIdentifier(cookie); });
+ if (it != d->allCookies.cend()) {
+ d->allCookies.erase(it);
+ return true;
}
return false;
}
@@ -351,13 +305,14 @@ bool QNetworkCookieJar::deleteCookie(const QNetworkCookie &cookie)
*/
bool QNetworkCookieJar::validateCookie(const QNetworkCookie &cookie, const QUrl &url) const
{
- QString domain = cookie.domain();
+ const QString cookieDomain = cookie.domain();
+ QStringView domain = cookieDomain;
const QString host = url.host();
if (!isParentDomain(domain, host) && !isParentDomain(host, domain))
return false; // not accepted
- if (domain.startsWith(QLatin1Char('.')))
- domain = domain.mid(1);
+ if (domain.startsWith(u'.'))
+ domain = domain.sliced(1);
// We shouldn't reject if:
// "[...] the domain-attribute is identical to the canonicalized request-host"
@@ -373,3 +328,5 @@ bool QNetworkCookieJar::validateCookie(const QNetworkCookie &cookie, const QUrl
}
QT_END_NAMESPACE
+
+#include "moc_qnetworkcookiejar.cpp"
diff --git a/src/network/access/qnetworkcookiejar.h b/src/network/access/qnetworkcookiejar.h
index c3b2200443..71f2884a28 100644
--- a/src/network/access/qnetworkcookiejar.h
+++ b/src/network/access/qnetworkcookiejar.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKCOOKIEJAR_H
#define QNETWORKCOOKIEJAR_H
diff --git a/src/network/access/qnetworkcookiejar_p.h b/src/network/access/qnetworkcookiejar_p.h
index 43f189a40c..f2837ab83d 100644
--- a/src/network/access/qnetworkcookiejar_p.h
+++ b/src/network/access/qnetworkcookiejar_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKCOOKIEJAR_P_H
#define QNETWORKCOOKIEJAR_P_H
diff --git a/src/network/access/qnetworkdiskcache.cpp b/src/network/access/qnetworkdiskcache.cpp
index c6c20b742c..c883a61886 100644
--- a/src/network/access/qnetworkdiskcache.cpp
+++ b/src/network/access/qnetworkdiskcache.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//#define QNETWORKDISKCACHE_DEBUG
@@ -48,22 +12,23 @@
#include <qdir.h>
#include <qdatastream.h>
#include <qdatetime.h>
-#include <qdiriterator.h>
+#include <qdirlisting.h>
#include <qurl.h>
#include <qcryptographichash.h>
#include <qdebug.h>
#include <memory>
-#define CACHE_POSTFIX QLatin1String(".d")
-#define PREPARED_SLASH QLatin1String("prepared/")
+#define CACHE_POSTFIX ".d"_L1
#define CACHE_VERSION 8
-#define DATA_DIR QLatin1String("data")
+#define DATA_DIR "data"_L1
#define MAX_COMPRESSION_SIZE (1024 * 1024 * 3)
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
/*!
\class QNetworkDiskCache
\since 4.5
@@ -147,10 +112,10 @@ void QNetworkDiskCache::setCacheDirectory(const QString &cacheDir)
d->cacheDirectory = cacheDir;
QDir dir(d->cacheDirectory);
d->cacheDirectory = dir.absolutePath();
- if (!d->cacheDirectory.endsWith(QLatin1Char('/')))
- d->cacheDirectory += QLatin1Char('/');
+ if (!d->cacheDirectory.endsWith(u'/'))
+ d->cacheDirectory += u'/';
- d->dataDirectory = d->cacheDirectory + DATA_DIR + QString::number(CACHE_VERSION) + QLatin1Char('/');
+ d->dataDirectory = d->cacheDirectory + DATA_DIR + QString::number(CACHE_VERSION) + u'/';
d->prepareLayout();
}
@@ -206,13 +171,9 @@ QIODevice *QNetworkDiskCache::prepare(const QNetworkCacheMetaData &metaData)
cacheItem->data.open(QBuffer::ReadWrite);
device = &(cacheItem->data);
} else {
- QString templateName = d->tmpCacheFileName();
- QT_TRY {
- cacheItem->file = new QTemporaryFile(templateName, &cacheItem->data);
- } QT_CATCH(...) {
- cacheItem->file = nullptr;
- }
- if (!cacheItem->file || !cacheItem->file->open()) {
+ QString fileName = d->cacheFileName(cacheItem->metaData.url());
+ cacheItem->file = new(std::nothrow) QSaveFile(fileName, &cacheItem->data);
+ if (!cacheItem->file || !cacheItem->file->open(QFileDevice::WriteOnly)) {
qWarning("QNetworkDiskCache::prepare() unable to open temporary file");
cacheItem.reset();
return nullptr;
@@ -252,7 +213,6 @@ void QNetworkDiskCache::insert(QIODevice *device)
void QNetworkDiskCachePrivate::prepareLayout()
{
QDir helper;
- helper.mkpath(cacheDirectory + PREPARED_SLASH);
//Create directory and subdirectories 0-F
helper.mkpath(dataDirectory);
@@ -281,9 +241,8 @@ void QNetworkDiskCachePrivate::storeItem(QCacheItem *cacheItem)
currentCacheSize = q->expire();
if (!cacheItem->file) {
- QString templateName = tmpCacheFileName();
- cacheItem->file = new QTemporaryFile(templateName, &cacheItem->data);
- if (cacheItem->file->open()) {
+ cacheItem->file = new QSaveFile(fileName, &cacheItem->data);
+ if (cacheItem->file->open(QFileDevice::WriteOnly)) {
cacheItem->writeHeader(cacheItem->file);
cacheItem->writeCompressedData(cacheItem->file);
}
@@ -291,13 +250,15 @@ void QNetworkDiskCachePrivate::storeItem(QCacheItem *cacheItem)
if (cacheItem->file
&& cacheItem->file->isOpen()
- && cacheItem->file->error() == QFile::NoError) {
- cacheItem->file->setAutoRemove(false);
- // ### use atomic rename rather then remove & rename
- if (cacheItem->file->rename(fileName))
- currentCacheSize += cacheItem->file->size();
- else
- cacheItem->file->setAutoRemove(true);
+ && cacheItem->file->error() == QFileDevice::NoError) {
+ // We have to call size() here instead of inside the if-body because
+ // commit() invalidates the file-engine, and size() will create a new
+ // one, pointing at an empty filename.
+ qint64 size = cacheItem->file->size();
+ if (cacheItem->file->commit())
+ currentCacheSize += size;
+ // Delete and unset the QSaveFile, it's invalid now.
+ delete std::exchange(cacheItem->file, nullptr);
}
if (cacheItem->metaData.url() == lastItem.metaData.url())
lastItem.reset();
@@ -516,47 +477,45 @@ qint64 QNetworkDiskCache::expire()
// close file handle to prevent "in use" error when QFile::remove() is called
d->lastItem.reset();
- QDir::Filters filters = QDir::AllDirs | QDir:: Files | QDir::NoDotAndDotDot;
- QDirIterator it(cacheDirectory(), filters, QDirIterator::Subdirectories);
+ const QDir::Filters filters = QDir::AllDirs | QDir:: Files | QDir::NoDotAndDotDot;
- QMultiMap<QDateTime, QString> cacheItems;
+ struct CacheItem
+ {
+ std::chrono::milliseconds msecs;
+ QString path;
+ qint64 size = 0;
+ };
+ std::vector<CacheItem> cacheItems;
qint64 totalSize = 0;
- while (it.hasNext()) {
- QFileInfo info = it.nextFileInfo();
- QString path = info.filePath();
- QString fileName = info.fileName();
- if (fileName.endsWith(CACHE_POSTFIX)) {
- const QDateTime birthTime = info.fileTime(QFile::FileBirthTime);
- cacheItems.insert(birthTime.isValid() ? birthTime
- : info.fileTime(QFile::FileMetadataChangeTime), path);
- totalSize += info.size();
- }
+ using F = QDirListing::IteratorFlag;
+ for (const auto &dirEntry : QDirListing(cacheDirectory(), filters, F::Recursive)) {
+ if (!dirEntry.fileName().endsWith(CACHE_POSTFIX))
+ continue;
+
+ const QFileInfo &info = dirEntry.fileInfo();
+ QDateTime fileTime = info.birthTime(QTimeZone::UTC);
+ if (!fileTime.isValid())
+ fileTime = info.metadataChangeTime(QTimeZone::UTC);
+ const std::chrono::milliseconds msecs{fileTime.toMSecsSinceEpoch()};
+ const qint64 size = info.size();
+ cacheItems.push_back(CacheItem{msecs, info.filePath(), size});
+ totalSize += size;
}
- int removedFiles = 0;
- qint64 goal = (maximumCacheSize() * 9) / 10;
- QMultiMap<QDateTime, QString>::const_iterator i = cacheItems.constBegin();
- while (i != cacheItems.constEnd()) {
- if (totalSize < goal)
- break;
- QString name = i.value();
- QFile file(name);
-
- if (name.contains(PREPARED_SLASH)) {
- for (QCacheItem *item : qAsConst(d->inserting)) {
- if (item && item->file && item->file->fileName() == name) {
- delete item->file;
- item->file = nullptr;
- break;
- }
- }
- }
+ const qint64 goal = (maximumCacheSize() * 9) / 10;
+ if (totalSize < goal)
+ return totalSize; // Nothing to do
+
+ auto byFileTime = [&](const auto &a, const auto &b) { return a.msecs < b.msecs; };
+ std::sort(cacheItems.begin(), cacheItems.end(), byFileTime);
- qint64 size = file.size();
- file.remove();
- totalSize -= size;
+ [[maybe_unused]] int removedFiles = 0; // used under QNETWORKDISKCACHE_DEBUG
+ for (const CacheItem &cached : cacheItems) {
+ QFile::remove(cached.path);
++removedFiles;
- ++i;
+ totalSize -= cached.size;
+ if (totalSize < goal)
+ break;
}
#if defined(QNETWORKDISKCACHE_DEBUG)
if (removedFiles > 0) {
@@ -595,20 +554,13 @@ QString QNetworkDiskCachePrivate::uniqueFileName(const QUrl &url)
const QByteArray hash = QCryptographicHash::hash(cleanUrl.toEncoded(), QCryptographicHash::Sha1);
// convert sha1 to base36 form and return first 8 bytes for use as string
const QByteArray id = QByteArray::number(*(qlonglong*)hash.data(), 36).left(8);
- // generates <one-char subdir>/<8-char filname.d>
- uint code = (uint)id.at(id.length()-1) % 16;
- QString pathFragment = QString::number(code, 16) + QLatin1Char('/')
- + QLatin1String(id) + CACHE_POSTFIX;
+ // generates <one-char subdir>/<8-char filename.d>
+ uint code = (uint)id.at(id.size()-1) % 16;
+ QString pathFragment = QString::number(code, 16) + u'/' + QLatin1StringView(id) + CACHE_POSTFIX;
return pathFragment;
}
-QString QNetworkDiskCachePrivate::tmpCacheFileName() const
-{
- //The subdirectory is presumed to be already read for use.
- return cacheDirectory + PREPARED_SLASH + QLatin1String("XXXXXX") + CACHE_POSTFIX;
-}
-
/*!
Generates fully qualified path of cached resource from a URL.
*/
@@ -659,7 +611,7 @@ enum
CurrentCacheVersion = CACHE_VERSION
};
-void QCacheItem::writeHeader(QFile *device) const
+void QCacheItem::writeHeader(QFileDevice *device) const
{
QDataStream out(device);
@@ -671,7 +623,7 @@ void QCacheItem::writeHeader(QFile *device) const
out << compressed;
}
-void QCacheItem::writeCompressedData(QFile *device) const
+void QCacheItem::writeCompressedData(QFileDevice *device) const
{
QDataStream out(device);
@@ -682,7 +634,7 @@ void QCacheItem::writeCompressedData(QFile *device) const
Returns \c false if the file is a cache file,
but is an older version and should be removed otherwise true.
*/
-bool QCacheItem::read(QFile *device, bool readData)
+bool QCacheItem::read(QFileDevice *device, bool readData)
{
reset();
@@ -721,7 +673,9 @@ bool QCacheItem::read(QFile *device, bool readData)
if (!device->fileName().endsWith(expectedFilename))
return false;
- return metaData.isValid();
+ return metaData.isValid() && !metaData.rawHeaders().isEmpty();
}
QT_END_NAMESPACE
+
+#include "moc_qnetworkdiskcache.cpp"
diff --git a/src/network/access/qnetworkdiskcache.h b/src/network/access/qnetworkdiskcache.h
index ff7d3192e8..370f3d2b26 100644
--- a/src/network/access/qnetworkdiskcache.h
+++ b/src/network/access/qnetworkdiskcache.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKDISKCACHE_H
#define QNETWORKDISKCACHE_H
diff --git a/src/network/access/qnetworkdiskcache_p.h b/src/network/access/qnetworkdiskcache_p.h
index c797e63830..826f0a1d7d 100644
--- a/src/network/access/qnetworkdiskcache_p.h
+++ b/src/network/access/qnetworkdiskcache_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKDISKCACHE_P_H
#define QNETWORKDISKCACHE_P_H
@@ -56,20 +20,16 @@
#include <qbuffer.h>
#include <qhash.h>
-#include <qtemporaryfile.h>
+#include <qsavefile.h>
QT_REQUIRE_CONFIG(networkdiskcache);
QT_BEGIN_NAMESPACE
-class QFile;
-
class QCacheItem
{
public:
- QCacheItem() : file(nullptr)
- {
- }
+ QCacheItem() = default;
~QCacheItem()
{
reset();
@@ -77,7 +37,7 @@ public:
QNetworkCacheMetaData metaData;
QBuffer data;
- QTemporaryFile *file;
+ QSaveFile *file = nullptr;
inline qint64 size() const
{ return file ? file->size() : data.size(); }
@@ -87,9 +47,9 @@ public:
delete file;
file = nullptr;
}
- void writeHeader(QFile *device) const;
- void writeCompressedData(QFile *device) const;
- bool read(QFile *device, bool readData);
+ void writeHeader(QFileDevice *device) const;
+ void writeCompressedData(QFileDevice *device) const;
+ bool read(QFileDevice *device, bool readData);
bool canCompress() const;
};
@@ -105,7 +65,6 @@ public:
static QString uniqueFileName(const QUrl &url);
QString cacheFileName(const QUrl &url) const;
- QString tmpCacheFileName() const;
bool removeFile(const QString &file);
void storeItem(QCacheItem *item);
void prepareLayout();
diff --git a/src/network/access/qnetworkfile.cpp b/src/network/access/qnetworkfile.cpp
index b7c91f28d8..bfedf044de 100644
--- a/src/network/access/qnetworkfile.cpp
+++ b/src/network/access/qnetworkfile.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qnetworkfile_p.h"
@@ -90,3 +54,5 @@ void QNetworkFile::close()
}
QT_END_NAMESPACE
+
+#include "moc_qnetworkfile_p.cpp"
diff --git a/src/network/access/qnetworkfile_p.h b/src/network/access/qnetworkfile_p.h
index e788308d82..df772251b4 100644
--- a/src/network/access/qnetworkfile_p.h
+++ b/src/network/access/qnetworkfile_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKFILE_H
#define QNETWORKFILE_H
diff --git a/src/network/access/qnetworkreply.cpp b/src/network/access/qnetworkreply.cpp
index 09356e59cd..9334b01de6 100644
--- a/src/network/access/qnetworkreply.cpp
+++ b/src/network/access/qnetworkreply.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtNetwork/private/qtnetworkglobal_p.h>
@@ -45,6 +9,8 @@
QT_BEGIN_NAMESPACE
+QT_IMPL_METATYPE_EXTERN_TAGGED(QNetworkReply::NetworkError, QNetworkReply__NetworkError)
+
const int QNetworkReplyPrivate::progressSignalInterval = 100;
QNetworkReplyPrivate::QNetworkReplyPrivate()
@@ -76,7 +42,7 @@ QNetworkReplyPrivate::QNetworkReplyPrivate()
itself.
QNetworkReply is a sequential-access QIODevice, which means that
- once data is read from the object, it no longer kept by the
+ once data is read from the object, it is no longer kept by the
device. It is therefore the application's responsibility to keep
this data if it needs to. Whenever more data is received from the
network and processed, the readyRead() signal is emitted.
@@ -298,7 +264,7 @@ QNetworkReplyPrivate::QNetworkReplyPrivate()
\fn void QNetworkReply::redirected(const QUrl &url)
\since 5.6
- This signal is emitted if the QNetworkRequest::ManualRedirectPolicy was
+ This signal is emitted if the QNetworkRequest::ManualRedirectPolicy was not
set in the request and the server responded with a 3xx status (specifically
301, 302, 303, 305, 307 or 308 status code) with a valid url in the location
header, indicating a HTTP redirect. The \a url parameter contains the new
@@ -322,7 +288,7 @@ QNetworkReplyPrivate::QNetworkReplyPrivate()
*/
/*!
- \fn void QNetworkReply::socketConnecting()
+ \fn void QNetworkReply::socketStartedConnecting()
\since 6.3
This signal is emitted 0 or more times, when the socket
@@ -339,7 +305,7 @@ QNetworkReplyPrivate::QNetworkReplyPrivate()
This signal is emitted 1 or more times when the request was
sent. Useful for custom progress or timeout handling.
- \sa metaDataChanged(), socketConnecting()
+ \sa metaDataChanged(), socketStartedConnecting()
*/
/*!
@@ -365,7 +331,7 @@ QNetworkReplyPrivate::QNetworkReplyPrivate()
processing. After this signal is emitted, there will be no more
updates to the reply's data or metadata.
- Unless close() or abort() have been called, the reply will be still be opened
+ Unless close() or abort() have been called, the reply will still be opened
for reading, so the data can be retrieved by calls to read() or
readAll(). In particular, if no calls to read() were made as a
result of readyRead(), a call to readAll() will retrieve the full
@@ -646,8 +612,9 @@ QVariant QNetworkReply::header(QNetworkRequest::KnownHeaders header) const
the remote server
\sa rawHeader()
+ \note In Qt versions prior to 6.7, this function took QByteArray only.
*/
-bool QNetworkReply::hasRawHeader(const QByteArray &headerName) const
+bool QNetworkReply::hasRawHeader(QAnyStringView headerName) const
{
Q_D(const QNetworkReply);
return d->findRawHeader(headerName) != d->rawHeaders.constEnd();
@@ -661,13 +628,12 @@ bool QNetworkReply::hasRawHeader(const QByteArray &headerName) const
header field.
\sa setRawHeader(), hasRawHeader(), header()
+ \note In Qt versions prior to 6.7, this function took QByteArray only.
*/
-QByteArray QNetworkReply::rawHeader(const QByteArray &headerName) const
+QByteArray QNetworkReply::rawHeader(QAnyStringView headerName) const
{
Q_D(const QNetworkReply);
- QNetworkHeadersPrivate::RawHeadersList::ConstIterator it =
- d->findRawHeader(headerName);
- if (it != d->rawHeaders.constEnd())
+ if (const auto it = d->findRawHeader(headerName); it != d->rawHeaders.constEnd())
return it->second;
return QByteArray();
}
@@ -967,3 +933,5 @@ void QNetworkReply::setAttribute(QNetworkRequest::Attribute code, const QVariant
}
QT_END_NAMESPACE
+
+#include "moc_qnetworkreply.cpp"
diff --git a/src/network/access/qnetworkreply.h b/src/network/access/qnetworkreply.h
index 0cd01f6d4b..390a6f2f51 100644
--- a/src/network/access/qnetworkreply.h
+++ b/src/network/access/qnetworkreply.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKREPLY_H
#define QNETWORKREPLY_H
@@ -133,9 +97,15 @@ public:
QVariant header(QNetworkRequest::KnownHeaders header) const;
// raw headers:
+#if QT_NETWORK_REMOVED_SINCE(6, 7)
bool hasRawHeader(const QByteArray &headerName) const;
+#endif
+ bool hasRawHeader(QAnyStringView headerName) const;
QList<QByteArray> rawHeaderList() const;
+#if QT_NETWORK_REMOVED_SINCE(6, 7)
QByteArray rawHeader(const QByteArray &headerName) const;
+#endif
+ QByteArray rawHeader(QAnyStringView headerName) const;
typedef QPair<QByteArray, QByteArray> RawHeaderPair;
const QList<RawHeaderPair>& rawHeaderPairs() const;
@@ -154,7 +124,7 @@ public Q_SLOTS:
virtual void ignoreSslErrors();
Q_SIGNALS:
- void socketConnecting();
+ void socketStartedConnecting();
void requestSent();
void metaDataChanged();
void finished();
@@ -196,6 +166,7 @@ private:
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(QNetworkReply::NetworkError)
+QT_DECL_METATYPE_EXTERN_TAGGED(QNetworkReply::NetworkError,
+ QNetworkReply__NetworkError, Q_NETWORK_EXPORT)
#endif
diff --git a/src/network/access/qnetworkreply_p.h b/src/network/access/qnetworkreply_p.h
index 3b3bb3dfa4..f9bbfcbc6b 100644
--- a/src/network/access/qnetworkreply_p.h
+++ b/src/network/access/qnetworkreply_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKREPLY_P_H
#define QNETWORKREPLY_P_H
diff --git a/src/network/access/qnetworkreplydataimpl.cpp b/src/network/access/qnetworkreplydataimpl.cpp
index 2f75020446..7cb7621bca 100644
--- a/src/network/access/qnetworkreplydataimpl.cpp
+++ b/src/network/access/qnetworkreplydataimpl.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qnetworkreplydataimpl_p.h"
#include "private/qdataurl_p.h"
diff --git a/src/network/access/qnetworkreplydataimpl_p.h b/src/network/access/qnetworkreplydataimpl_p.h
index 81d2110d69..35fd38aece 100644
--- a/src/network/access/qnetworkreplydataimpl_p.h
+++ b/src/network/access/qnetworkreplydataimpl_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKREPLYDATAIMPL_H
#define QNETWORKREPLYDATAIMPL_H
diff --git a/src/network/access/qnetworkreplyfileimpl.cpp b/src/network/access/qnetworkreplyfileimpl.cpp
index b6be93147a..e6208a5c85 100644
--- a/src/network/access/qnetworkreplyfileimpl.cpp
+++ b/src/network/access/qnetworkreplyfileimpl.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qnetworkreplyfileimpl_p.h"
@@ -49,6 +13,10 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
+QT_IMPL_METATYPE_EXTERN_TAGGED(QNetworkRequest::KnownHeaders, QNetworkRequest__KnownHeaders)
+
QNetworkReplyFileImplPrivate::QNetworkReplyFileImplPrivate()
: QNetworkReplyPrivate(), managerPrivate(nullptr), realFile(nullptr)
{
@@ -80,7 +48,7 @@ QNetworkReplyFileImpl::QNetworkReplyFileImpl(QNetworkAccessManager *manager, con
d->managerPrivate = manager->d_func();
QUrl url = req.url();
- if (url.host() == QLatin1String("localhost"))
+ if (url.host() == "localhost"_L1)
url.setHost(QString());
#if !defined(Q_OS_WIN)
@@ -89,25 +57,26 @@ QNetworkReplyFileImpl::QNetworkReplyFileImpl(QNetworkAccessManager *manager, con
// we handle only local files
QString msg = QCoreApplication::translate("QNetworkAccessFileBackend", "Request for opening non-local file %1").arg(url.toString());
setError(QNetworkReply::ProtocolInvalidOperationError, msg);
+ setFinished(true); // We're finished, will emit finished() after ctor is done.
QMetaObject::invokeMethod(this, "errorOccurred", Qt::QueuedConnection,
Q_ARG(QNetworkReply::NetworkError, QNetworkReply::ProtocolInvalidOperationError));
- fileOpenFinished(false);
+ QMetaObject::invokeMethod(this, &QNetworkReplyFileImpl::fileOpenFinished, Qt::QueuedConnection, false);
return;
}
#endif
if (url.path().isEmpty())
- url.setPath(QLatin1String("/"));
+ url.setPath("/"_L1);
setUrl(url);
QString fileName = url.toLocalFile();
if (fileName.isEmpty()) {
const QString scheme = url.scheme();
- if (scheme == QLatin1String("qrc")) {
- fileName = QLatin1Char(':') + url.path();
+ if (scheme == "qrc"_L1) {
+ fileName = u':' + url.path();
} else {
#if defined(Q_OS_ANDROID)
- if (scheme == QLatin1String("assets"))
- fileName = QLatin1String("assets:") + url.path();
+ if (scheme == "assets"_L1)
+ fileName = "assets:"_L1 + url.path();
else
#endif
fileName = url.toString(QUrl::RemoveAuthority | QUrl::RemoveFragment | QUrl::RemoveQuery);
@@ -222,7 +191,7 @@ qint64 QNetworkReplyFileImpl::readData(char *data, qint64 maxlen)
return -1;
else {
setAttribute(QNetworkRequest::HttpStatusCodeAttribute, 200);
- setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, QLatin1String("OK"));
+ setAttribute(QNetworkRequest::HttpReasonPhraseAttribute, "OK"_L1);
return ret;
}
}
diff --git a/src/network/access/qnetworkreplyfileimpl_p.h b/src/network/access/qnetworkreplyfileimpl_p.h
index 48d82abd3f..6413903d8f 100644
--- a/src/network/access/qnetworkreplyfileimpl_p.h
+++ b/src/network/access/qnetworkreplyfileimpl_p.h
@@ -1,44 +1,8 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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$
-**
-****************************************************************************/
-
-#ifndef QNETWORKREPLYFILEIMPL_H
-#define QNETWORKREPLYFILEIMPL_H
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QNETWORKREPLYFILEIMPL_P_H
+#define QNETWORKREPLYFILEIMPL_P_H
//
// W A R N I N G
@@ -55,7 +19,9 @@
#include "qnetworkreply.h"
#include "qnetworkreply_p.h"
#include "qnetworkaccessmanager.h"
+
#include <QFile>
+#include <QtCore/qpointer.h>
#include <private/qabstractfileengine_p.h>
QT_BEGIN_NAMESPACE
@@ -97,6 +63,8 @@ public:
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(QNetworkRequest::KnownHeaders)
+// ### move to qnetworkrequest.h
+QT_DECL_METATYPE_EXTERN_TAGGED(QNetworkRequest::KnownHeaders,
+ QNetworkRequest__KnownHeaders, Q_NETWORK_EXPORT)
-#endif // QNETWORKREPLYFILEIMPL_H
+#endif // QNETWORKREPLYFILEIMPL_P_H
diff --git a/src/network/access/qnetworkreplyhttpimpl.cpp b/src/network/access/qnetworkreplyhttpimpl.cpp
index 502692084a..1eee98f834 100644
--- a/src/network/access/qnetworkreplyhttpimpl.cpp
+++ b/src/network/access/qnetworkreplyhttpimpl.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
//#define QNETWORKACCESSHTTPBACKEND_DEBUG
@@ -57,6 +21,7 @@
#include "QtCore/qcoreapplication.h"
#include <QtCore/private/qthread_p.h>
+#include <QtCore/private/qtools_p.h>
#include "qnetworkcookiejar.h"
#include "qnetconmonitor_p.h"
@@ -67,16 +32,18 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+using namespace QtMiscUtils;
+using namespace std::chrono_literals;
+
class QNetworkProxy;
-static inline bool isSeparator(char c)
-{
- static const char separators[] = "()<>@,;:\\\"/[]?={}";
- return isLWS(c) || strchr(separators, c) != nullptr;
-}
+static inline QByteArray rangeName() { return "Range"_ba; }
+static inline QByteArray cacheControlName() { return "Cache-Control"_ba; }
+static constexpr QByteArrayView bytesEqualPrefix() noexcept { return "bytes="; }
// ### merge with nextField in cookiejar.cpp
-static QHash<QByteArray, QByteArray> parseHttpOptionHeader(const QByteArray &header)
+static QHash<QByteArray, QByteArray> parseHttpOptionHeader(QByteArrayView header)
{
// The HTTP header is of the form:
// header = #1(directives)
@@ -88,7 +55,7 @@ static QHash<QByteArray, QByteArray> parseHttpOptionHeader(const QByteArray &hea
while (true) {
// skip spaces
pos = nextNonWhitespace(header, pos);
- if (pos == header.length())
+ if (pos == header.size())
return result; // end of parsing
// pos points to a non-whitespace
@@ -102,36 +69,36 @@ static QHash<QByteArray, QByteArray> parseHttpOptionHeader(const QByteArray &hea
// of the header, whichever comes first
int end = comma;
if (end == -1)
- end = header.length();
+ end = header.size();
if (equal != -1 && end > equal)
end = equal; // equal sign comes before comma/end
- QByteArray key = QByteArray(header.constData() + pos, end - pos).trimmed().toLower();
+ const auto key = header.sliced(pos, end - pos).trimmed();
pos = end + 1;
if (uint(equal) < uint(comma)) {
// case: token "=" (token | quoted-string)
// skip spaces
pos = nextNonWhitespace(header, pos);
- if (pos == header.length())
+ if (pos == header.size())
// huh? Broken header
return result;
QByteArray value;
- value.reserve(header.length() - pos);
+ value.reserve(header.size() - pos);
if (header.at(pos) == '"') {
// case: quoted-string
// quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
// qdtext = <any TEXT except <">>
// quoted-pair = "\" CHAR
++pos;
- while (pos < header.length()) {
+ while (pos < header.size()) {
char c = header.at(pos);
if (c == '"') {
// end of quoted text
break;
} else if (c == '\\') {
++pos;
- if (pos >= header.length())
+ if (pos >= header.size())
// broken header
return result;
c = header.at(pos);
@@ -141,8 +108,13 @@ static QHash<QByteArray, QByteArray> parseHttpOptionHeader(const QByteArray &hea
++pos;
}
} else {
+ const auto isSeparator = [](char c) {
+ static const char separators[] = "()<>@,;:\\\"/[]?={}";
+ return isLWS(c) || strchr(separators, c) != nullptr;
+ };
+
// case: token
- while (pos < header.length()) {
+ while (pos < header.size()) {
char c = header.at(pos);
if (isSeparator(c))
break;
@@ -151,7 +123,7 @@ static QHash<QByteArray, QByteArray> parseHttpOptionHeader(const QByteArray &hea
}
}
- result.insert(key, value);
+ result.insert(key.toByteArray().toLower(), value);
// find the comma now:
comma = header.indexOf(',', pos);
@@ -161,7 +133,7 @@ static QHash<QByteArray, QByteArray> parseHttpOptionHeader(const QByteArray &hea
} else {
// case: token
// key is already set
- result.insert(key, QByteArray());
+ result.insert(key.toByteArray().toLower(), QByteArray());
}
}
}
@@ -182,10 +154,13 @@ QNetworkReplyHttpImpl::QNetworkReplyHttpImpl(QNetworkAccessManager* const manage
d->outgoingData = outgoingData;
d->url = request.url();
#ifndef QT_NO_SSL
- if (request.url().scheme() == QLatin1String("https"))
+ if (request.url().scheme() == "https"_L1)
d->sslConfiguration.reset(new QSslConfiguration(request.sslConfiguration()));
#endif
+ QObjectPrivate::connect(this, &QNetworkReplyHttpImpl::redirectAllowed, d,
+ &QNetworkReplyHttpImplPrivate::followRedirect, Qt::QueuedConnection);
+
// FIXME Later maybe set to Unbuffered, especially if it is zerocopy or from cache?
QIODevice::open(QIODevice::ReadOnly);
@@ -357,9 +332,11 @@ qint64 QNetworkReplyHttpImpl::readData(char* data, qint64 maxlen)
return 0;
const qint64 bytesRead = d->decompressHelper.read(data, maxlen);
if (!d->decompressHelper.isValid()) {
- // error occurred, error copied from QHttpNetworkConnectionPrivate::errorDetail
- d->error(QNetworkReplyImpl::NetworkError::ProtocolFailure,
- QCoreApplication::translate("QHttp", "Data corrupted"));
+ d->error(QNetworkReplyImpl::NetworkError::UnknownContentError,
+ QCoreApplication::translate("QHttp", "Decompression failed: %1")
+ .arg(d->decompressHelper.errorString()));
+ d->decompressHelper.clear();
+ return -1;
}
if (d->cacheSaveDevice) {
// Need to write to the cache now that we have the data
@@ -504,16 +481,17 @@ bool QNetworkReplyHttpImplPrivate::loadFromCacheIfAllowed(QHttpNetworkRequest &h
if (CacheLoadControlAttribute == QNetworkRequest::AlwaysNetwork) {
// If the request does not already specify preferred cache-control
// force reload from the network and tell any caching proxy servers to reload too
- if (!request.rawHeaderList().contains("Cache-Control")) {
- httpRequest.setHeaderField("Cache-Control", "no-cache");
- httpRequest.setHeaderField("Pragma", "no-cache");
+ if (!request.rawHeaderList().contains(cacheControlName())) {
+ const auto noCache = "no-cache"_ba;
+ httpRequest.setHeaderField(cacheControlName(), noCache);
+ httpRequest.setHeaderField("Pragma"_ba, noCache);
}
return false;
}
// The disk cache API does not currently support partial content retrieval.
// That is why we don't use the disk cache for any such requests.
- if (request.hasRawHeader("Range"))
+ if (request.hasRawHeader(rangeName()))
return false;
QAbstractNetworkCache *nc = managerPrivate->networkCache;
@@ -531,20 +509,27 @@ bool QNetworkReplyHttpImplPrivate::loadFromCacheIfAllowed(QHttpNetworkRequest &h
QNetworkHeadersPrivate::RawHeadersList::ConstIterator it;
cacheHeaders.setAllRawHeaders(metaData.rawHeaders());
+ it = cacheHeaders.findRawHeader("content-length");
+ if (it != cacheHeaders.rawHeaders.constEnd()) {
+ std::unique_ptr<QIODevice> data(nc->data(httpRequest.url()));
+ if (!data || data->size() < it->second.toLongLong())
+ return false; // The data is smaller than the content-length specified
+ }
+
it = cacheHeaders.findRawHeader("etag");
if (it != cacheHeaders.rawHeaders.constEnd())
- httpRequest.setHeaderField("If-None-Match", it->second);
+ httpRequest.setHeaderField("If-None-Match"_ba, it->second);
QDateTime lastModified = metaData.lastModified();
if (lastModified.isValid())
- httpRequest.setHeaderField("If-Modified-Since", QNetworkHeadersPrivate::toHttpDate(lastModified));
+ httpRequest.setHeaderField("If-Modified-Since"_ba, QNetworkHeadersPrivate::toHttpDate(lastModified));
- it = cacheHeaders.findRawHeader("Cache-Control");
+ it = cacheHeaders.findRawHeader(cacheControlName());
if (it != cacheHeaders.rawHeaders.constEnd()) {
QHash<QByteArray, QByteArray> cacheControl = parseHttpOptionHeader(it->second);
- if (cacheControl.contains("must-revalidate"))
+ if (cacheControl.contains("must-revalidate"_ba))
return false;
- if (cacheControl.contains("no-cache"))
+ if (cacheControl.contains("no-cache"_ba))
return false;
}
@@ -599,10 +584,11 @@ bool QNetworkReplyHttpImplPrivate::loadFromCacheIfAllowed(QHttpNetworkRequest &h
if (lastModified.isValid() && dateHeader.isValid()) {
qint64 diff = lastModified.secsTo(dateHeader);
freshness_lifetime = diff / 10;
- if (httpRequest.headerField("Warning").isEmpty()) {
+ const auto warningHeader = "Warning"_ba;
+ if (httpRequest.headerField(warningHeader).isEmpty()) {
QDateTime dt = currentDateTime.addSecs(current_age);
if (currentDateTime.daysTo(dt) > 1)
- httpRequest.setHeaderField("Warning", "113");
+ httpRequest.setHeaderField(warningHeader, "113"_ba);
}
}
@@ -658,13 +644,11 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
httpRequest.setRedirectCount(newHttpRequest.maximumRedirectsAllowed());
QString scheme = url.scheme();
- bool ssl = (scheme == QLatin1String("https")
- || scheme == QLatin1String("preconnect-https"));
+ bool ssl = (scheme == "https"_L1 || scheme == "preconnect-https"_L1);
q->setAttribute(QNetworkRequest::ConnectionEncryptedAttribute, ssl);
httpRequest.setSsl(ssl);
- bool preConnect = (scheme == QLatin1String("preconnect-http")
- || scheme == QLatin1String("preconnect-https"));
+ bool preConnect = (scheme == "preconnect-http"_L1 || scheme == "preconnect-https"_L1);
httpRequest.setPreConnect(preConnect);
#ifndef QT_NO_NETWORKPROXY
@@ -711,12 +695,18 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
httpRequest.setRedirectPolicy(redirectPolicy);
httpRequest.setPriority(convert(newHttpRequest.priority()));
+ loadingFromCache = false;
switch (operation) {
case QNetworkAccessManager::GetOperation:
httpRequest.setOperation(QHttpNetworkRequest::Get);
- if (loadFromCacheIfAllowed(httpRequest))
+ // If the request has a body, createUploadByteDevice() and don't use caching
+ if (outgoingData) {
+ invalidateCache();
+ createUploadByteDevice();
+ } else if (loadFromCacheIfAllowed(httpRequest)) {
return; // no need to send the request! :)
+ }
break;
case QNetworkAccessManager::HeadOperation:
@@ -756,14 +746,15 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
QList<QByteArray> headers = newHttpRequest.rawHeaderList();
if (resumeOffset != 0) {
- const int rangeIndex = headers.indexOf("Range");
+ const int rangeIndex = headers.indexOf(rangeName());
if (rangeIndex != -1) {
// Need to adjust resume offset for user specified range
headers.removeAt(rangeIndex);
// We've already verified that requestRange starts with "bytes=", see canResume.
- QByteArray requestRange = newHttpRequest.rawHeader("Range").mid(6);
+ const auto rangeHeader = newHttpRequest.rawHeader(rangeName());
+ const auto requestRange = QByteArrayView(rangeHeader).mid(bytesEqualPrefix().size());
int index = requestRange.indexOf('-');
@@ -771,16 +762,16 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
quint64 requestEndOffset = requestRange.mid(index + 1).toULongLong();
// In case an end offset is not given it is skipped from the request range
- requestRange = "bytes=" + QByteArray::number(resumeOffset + requestStartOffset) +
+ QByteArray newRange = bytesEqualPrefix() + QByteArray::number(resumeOffset + requestStartOffset) +
'-' + (requestEndOffset ? QByteArray::number(requestEndOffset) : QByteArray());
- httpRequest.setHeaderField("Range", requestRange);
+ httpRequest.setHeaderField(rangeName(), newRange);
} else {
- httpRequest.setHeaderField("Range", "bytes=" + QByteArray::number(resumeOffset) + '-');
+ httpRequest.setHeaderField(rangeName(), bytesEqualPrefix() + QByteArray::number(resumeOffset) + '-');
}
}
- for (const QByteArray &header : qAsConst(headers))
+ for (const QByteArray &header : std::as_const(headers))
httpRequest.setHeaderField(header, newHttpRequest.rawHeader(header));
if (newHttpRequest.attribute(QNetworkRequest::HttpPipeliningAllowedAttribute).toBool())
@@ -790,6 +781,12 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
allowed.isValid() && allowed.canConvert<bool>()) {
httpRequest.setHTTP2Allowed(allowed.value<bool>());
}
+ auto h2cAttribute = request.attribute(QNetworkRequest::Http2CleartextAllowedAttribute);
+ // ### Qt7: Stop checking the environment variable
+ if (h2cAttribute.toBool()
+ || (!h2cAttribute.isValid() && qEnvironmentVariableIsSet("QT_NETWORK_H2C_ALLOWED"))) {
+ httpRequest.setH2cAllowed(true);
+ }
if (request.attribute(QNetworkRequest::Http2DirectAttribute).toBool()) {
// Intentionally mutually exclusive - cannot be both direct and 'allowed'
@@ -811,6 +808,7 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
QHttpThreadDelegate *delegate = new QHttpThreadDelegate;
// Propagate Http/2 settings:
delegate->http2Parameters = request.http2Configuration();
+ delegate->http1Parameters = request.http1Configuration();
if (request.attribute(QNetworkRequest::ConnectionCacheExpiryTimeoutSecondsAttribute).isValid())
delegate->connectionCacheExpiryTimeoutSeconds = request.attribute(QNetworkRequest::ConnectionCacheExpiryTimeoutSecondsAttribute).toInt();
@@ -872,8 +870,8 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
QObject::connect(delegate, SIGNAL(downloadFinished()),
q, SLOT(replyFinished()),
Qt::QueuedConnection);
- QObject::connect(delegate, &QHttpThreadDelegate::socketConnecting,
- q, &QNetworkReply::socketConnecting, Qt::QueuedConnection);
+ QObject::connect(delegate, &QHttpThreadDelegate::socketStartedConnecting,
+ q, &QNetworkReply::socketStartedConnecting, Qt::QueuedConnection);
QObject::connect(delegate, &QHttpThreadDelegate::requestSent,
q, &QNetworkReply::requestSent, Qt::QueuedConnection);
connect(delegate, &QHttpThreadDelegate::downloadMetaData, this,
@@ -888,9 +886,6 @@ void QNetworkReplyHttpImplPrivate::postRequest(const QNetworkRequest &newHttpReq
q, SLOT(onRedirected(QUrl,int,int)),
Qt::QueuedConnection);
- QObject::connect(q, SIGNAL(redirectAllowed()), q, SLOT(followRedirect()),
- Qt::QueuedConnection);
-
#ifndef QT_NO_SSL
QObject::connect(delegate, SIGNAL(sslConfigurationChanged(QSslConfiguration)),
q, SLOT(replySslConfigurationChanged(QSslConfiguration)),
@@ -1063,9 +1058,6 @@ void QNetworkReplyHttpImplPrivate::replyDownloadData(QByteArray d)
// cache this, we need it later and it's invalidated when dealing with compressed data
auto dataSize = d.size();
- // Grab this to compare later (only relevant for compressed data) in case none of the data
- // will be propagated to the user
- const qint64 previousBytesDownloaded = bytesDownloaded;
if (cacheEnabled && isCachingAllowed() && !cacheSaveDevice)
initCacheSaveDevice();
@@ -1078,9 +1070,10 @@ void QNetworkReplyHttpImplPrivate::replyDownloadData(QByteArray d)
decompressHelper.feed(std::move(d));
if (!decompressHelper.isValid()) {
- // error occurred, error copied from QHttpNetworkConnectionPrivate::errorDetail
- error(QNetworkReplyImpl::NetworkError::ProtocolFailure,
- QCoreApplication::translate("QHttp", "Data corrupted"));
+ error(QNetworkReplyImpl::NetworkError::UnknownContentError,
+ QCoreApplication::translate("QHttp", "Decompression failed: %1")
+ .arg(decompressHelper.errorString()));
+ decompressHelper.clear();
return;
}
@@ -1097,12 +1090,21 @@ void QNetworkReplyHttpImplPrivate::replyDownloadData(QByteArray d)
while (decompressHelper.hasData()) {
quint64 nextSize = quint64(d.size()) + quint64(increments);
if (nextSize > quint64(std::numeric_limits<QByteArray::size_type>::max())) {
- error(QNetworkReplyImpl::NetworkError::ProtocolFailure,
- QCoreApplication::translate("QHttp", "Data corrupted"));
+ error(QNetworkReplyImpl::NetworkError::UnknownContentError,
+ QCoreApplication::translate("QHttp",
+ "Data downloaded is too large to store"));
+ decompressHelper.clear();
return;
}
d.resize(nextSize);
bytesRead += decompressHelper.read(d.data() + bytesRead, increments);
+ if (!decompressHelper.isValid()) {
+ error(QNetworkReplyImpl::NetworkError::UnknownContentError,
+ QCoreApplication::translate("QHttp", "Decompression failed: %1")
+ .arg(decompressHelper.errorString()));
+ decompressHelper.clear();
+ return;
+ }
}
d.resize(bytesRead);
// we're synchronous so we're not calling this function again; reset the decompressHelper
@@ -1142,11 +1144,12 @@ void QNetworkReplyHttpImplPrivate::replyDownloadData(QByteArray d)
// This can occur when downloading compressed data as some of the data may be the content
// encoding's header. Don't emit anything for this.
- if (previousBytesDownloaded == bytesDownloaded) {
+ if (lastReadyReadEmittedSize == bytesDownloaded) {
if (readBufferMaxSize)
emit q->readBufferFreed(dataSize);
return;
}
+ lastReadyReadEmittedSize = bytesDownloaded;
QVariant totalSize = cookedHeaders.value(QNetworkRequest::ContentLengthHeader);
@@ -1229,13 +1232,12 @@ void QNetworkReplyHttpImplPrivate::onRedirected(const QUrl &redirectUrl, int htt
// equal to "80", the port component value MUST be preserved;
// otherwise, if the URI does not contain an explicit port
// component, the UA MUST NOT add one.
- url.setScheme(QLatin1String("https"));
+ url.setScheme("https"_L1);
if (url.port() == 80)
url.setPort(443);
}
- const bool isLessSafe = schemeBefore == QLatin1String("https")
- && url.scheme() == QLatin1String("http");
+ const bool isLessSafe = schemeBefore == "https"_L1 && url.scheme() == "http"_L1;
if (httpRequest.redirectPolicy() == QNetworkRequest::NoLessSafeRedirectPolicy
&& isLessSafe) {
error(QNetworkReply::InsecureRedirectError,
@@ -1243,13 +1245,18 @@ void QNetworkReplyHttpImplPrivate::onRedirected(const QUrl &redirectUrl, int htt
return;
}
+ // If the original operation was a GET with a body and the status code is either
+ // 307 or 308 then keep the message body
+ const bool getOperationKeepsBody = (operation == QNetworkAccessManager::GetOperation)
+ && (httpStatus == 307 || httpStatus == 308);
+
redirectRequest = createRedirectRequest(originalRequest, url, maxRedirectsRemaining);
operation = getRedirectOperation(operation, httpStatus);
// Clear stale headers, the relevant ones get set again later
httpRequest.clearHeaders();
- if (operation == QNetworkAccessManager::GetOperation
- || operation == QNetworkAccessManager::HeadOperation) {
+ if ((operation == QNetworkAccessManager::GetOperation
+ || operation == QNetworkAccessManager::HeadOperation) && !getOperationKeepsBody) {
// possibly changed from not-GET/HEAD to GET/HEAD, make sure to get rid of upload device
uploadByteDevice.reset();
uploadByteDevicePosition = 0;
@@ -1296,6 +1303,8 @@ void QNetworkReplyHttpImplPrivate::followRedirect()
q, [this]() { postRequest(redirectRequest); }, Qt::QueuedConnection);
}
+static constexpr QLatin1StringView locationHeader() noexcept { return "location"_L1; }
+
void QNetworkReplyHttpImplPrivate::checkForRedirect(const int statusCode)
{
Q_Q(QNetworkReplyHttpImpl);
@@ -1308,15 +1317,15 @@ void QNetworkReplyHttpImplPrivate::checkForRedirect(const int statusCode)
// What do we do about the caching of the HTML note?
// The response to a 303 MUST NOT be cached, while the response to
// all of the others is cacheable if the headers indicate it to be
- QByteArray header = q->rawHeader("location");
+ QByteArray header = q->rawHeader(locationHeader());
QUrl url = QUrl(QString::fromUtf8(header));
if (!url.isValid())
- url = QUrl(QLatin1String(header));
+ url = QUrl(QLatin1StringView(header));
q->setAttribute(QNetworkRequest::RedirectionTargetAttribute, url);
}
}
-void QNetworkReplyHttpImplPrivate::replyDownloadMetaData(const QList<QPair<QByteArray,QByteArray> > &hm,
+void QNetworkReplyHttpImplPrivate::replyDownloadMetaData(const QHttpHeaders &hm,
int sc, const QString &rp, bool pu,
QSharedPointer<char> db,
qint64 contentLength,
@@ -1335,7 +1344,7 @@ void QNetworkReplyHttpImplPrivate::replyDownloadMetaData(const QList<QPair<QByte
// RFC6797, 8.1
// If an HTTP response is received over insecure transport, the UA MUST
// ignore any present STS header field(s).
- if (url.scheme() == QLatin1String("https") && managerPrivate->stsEnabled)
+ if (url.scheme() == "https"_L1 && managerPrivate->stsEnabled)
managerPrivate->stsCache.updateFromHeaders(hm, url);
#endif
// Download buffer
@@ -1349,29 +1358,33 @@ void QNetworkReplyHttpImplPrivate::replyDownloadMetaData(const QList<QPair<QByte
q->setAttribute(QNetworkRequest::HttpPipeliningWasUsedAttribute, pu);
q->setAttribute(QNetworkRequest::Http2WasUsedAttribute, h2Used);
+ // A user having manually defined which encodings they accept is, for
+ // somwehat unknown (presumed legacy compatibility) reasons treated as
+ // disabling our decompression:
+ const bool autoDecompress = request.rawHeader("accept-encoding").isEmpty();
+ const bool shouldDecompress = isCompressed && autoDecompress;
// reconstruct the HTTP header
- QList<QPair<QByteArray, QByteArray> > headerMap = hm;
- QList<QPair<QByteArray, QByteArray> >::ConstIterator it = headerMap.constBegin(),
- end = headerMap.constEnd();
- for (; it != end; ++it) {
- QByteArray value = q->rawHeader(it->first);
+ for (qsizetype i = 0; i < hm.size(); ++i) {
+ const auto key = hm.nameAt(i);
+ const auto originValue = hm.valueAt(i);
+
+ QByteArray value = q->rawHeader(key);
// Reset any previous "location" header set in the reply. In case of
// redirects, we don't want to 'append' multiple location header values,
// rather we keep only the latest one
- if (it->first.toLower() == "location")
+ if (key == locationHeader())
value.clear();
- if (isCompressed && !decompressHelper.isValid()
- && it->first.compare("content-encoding", Qt::CaseInsensitive) == 0) {
-
+ if (shouldDecompress && !decompressHelper.isValid() && key == "content-encoding"_L1) {
if (!synchronous) // with synchronous all the data is expected to be handled at once
decompressHelper.setCountingBytesEnabled(true);
- if (!decompressHelper.setEncoding(it->second)) {
- // error occurred, error copied from QHttpNetworkConnectionPrivate::errorDetail
- error(QNetworkReplyImpl::NetworkError::ProtocolFailure,
- QCoreApplication::translate("QHttp", "Data corrupted"));
+ if (!decompressHelper.setEncoding(originValue)) {
+ error(QNetworkReplyImpl::NetworkError::UnknownContentError,
+ QCoreApplication::translate("QHttp", "Failed to initialize decompression: %1")
+ .arg(decompressHelper.errorString()));
+ return;
}
decompressHelper.setDecompressedSafetyCheckThreshold(
request.decompressedSafetyCheckThreshold());
@@ -1380,13 +1393,13 @@ void QNetworkReplyHttpImplPrivate::replyDownloadMetaData(const QList<QPair<QByte
if (!value.isEmpty()) {
// Why are we appending values for headers which are already
// present?
- if (it->first.compare("set-cookie", Qt::CaseInsensitive) == 0)
+ if (key == "set-cookie"_L1)
value += '\n';
else
value += ", ";
}
- value += it->second;
- q->setRawHeader(it->first, value);
+ value += originValue;
+ q->setRawHeader({key.data(), key.size()}, value);
}
q->setAttribute(QNetworkRequest::HttpStatusCodeAttribute, statusCode);
@@ -1405,11 +1418,11 @@ void QNetworkReplyHttpImplPrivate::replyDownloadMetaData(const QList<QPair<QByte
QNetworkHeadersPrivate cacheHeaders;
cacheHeaders.setAllRawHeaders(metaData.rawHeaders());
QNetworkHeadersPrivate::RawHeadersList::ConstIterator it;
- it = cacheHeaders.findRawHeader("Cache-Control");
+ it = cacheHeaders.findRawHeader(cacheControlName());
bool mustReValidate = false;
if (it != cacheHeaders.rawHeaders.constEnd()) {
QHash<QByteArray, QByteArray> cacheControl = parseHttpOptionHeader(it->second);
- if (cacheControl.contains("must-revalidate"))
+ if (cacheControl.contains("must-revalidate"_ba))
mustReValidate = true;
}
if (!mustReValidate && sendCacheContents(metaData))
@@ -1653,7 +1666,7 @@ bool QNetworkReplyHttpImplPrivate::sendCacheContents(const QNetworkCacheMetaData
QUrl redirectUrl;
for ( ; it != end; ++it) {
if (httpRequest.isFollowRedirects() &&
- !it->first.compare("location", Qt::CaseInsensitive))
+ !it->first.compare(locationHeader(), Qt::CaseInsensitive))
redirectUrl = QUrl::fromEncoded(it->second);
setRawHeader(it->first, it->second);
}
@@ -1690,6 +1703,27 @@ bool QNetworkReplyHttpImplPrivate::sendCacheContents(const QNetworkCacheMetaData
return true;
}
+static auto caseInsensitiveCompare(QByteArrayView value)
+{
+ return [value](QByteArrayView element)
+ {
+ return value.compare(element, Qt::CaseInsensitive) == 0;
+ };
+}
+
+static bool isHopByHop(QByteArrayView header)
+{
+ constexpr QByteArrayView headers[] = { "connection",
+ "keep-alive",
+ "proxy-authenticate",
+ "proxy-authorization",
+ "te",
+ "trailers",
+ "transfer-encoding",
+ "upgrade"};
+ return std::any_of(std::begin(headers), std::end(headers), caseInsensitiveCompare(header));
+}
+
QNetworkCacheMetaData QNetworkReplyHttpImplPrivate::fetchCacheMetaData(const QNetworkCacheMetaData &oldMetaData) const
{
Q_Q(const QNetworkReplyHttpImpl);
@@ -1701,22 +1735,11 @@ QNetworkCacheMetaData QNetworkReplyHttpImplPrivate::fetchCacheMetaData(const QNe
QNetworkHeadersPrivate::RawHeadersList::ConstIterator it;
const QList<QByteArray> newHeaders = q->rawHeaderList();
- for (QByteArray header : newHeaders) {
- QByteArray originalHeader = header;
- header = header.toLower();
- bool hop_by_hop =
- (header == "connection"
- || header == "keep-alive"
- || header == "proxy-authenticate"
- || header == "proxy-authorization"
- || header == "te"
- || header == "trailers"
- || header == "transfer-encoding"
- || header == "upgrade");
- if (hop_by_hop)
+ for (const QByteArray& header : newHeaders) {
+ if (isHopByHop(header))
continue;
- if (header == "set-cookie")
+ if (header.compare("set-cookie", Qt::CaseInsensitive) == 0)
continue;
// for 4.6.0, we were planning to not store the date header in the
@@ -1729,27 +1752,27 @@ QNetworkCacheMetaData QNetworkReplyHttpImplPrivate::fetchCacheMetaData(const QNe
//continue;
// Don't store Warning 1xx headers
- if (header == "warning") {
- QByteArray v = q->rawHeader(header);
- if (v.length() == 3
+ if (header.compare("warning", Qt::CaseInsensitive) == 0) {
+ const QByteArray v = q->rawHeader(header);
+ if (v.size() == 3
&& v[0] == '1'
- && v[1] >= '0' && v[1] <= '9'
- && v[2] >= '0' && v[2] <= '9')
+ && isAsciiDigit(v[1])
+ && isAsciiDigit(v[2]))
continue;
}
it = cacheHeaders.findRawHeader(header);
if (it != cacheHeaders.rawHeaders.constEnd()) {
// Match the behavior of Firefox and assume Cache-Control: "no-transform"
- if (header == "content-encoding"
- || header == "content-range"
- || header == "content-type")
+ constexpr QByteArrayView headers[]=
+ {"content-encoding", "content-range", "content-type"};
+ if (std::any_of(std::begin(headers), std::end(headers), caseInsensitiveCompare(header)))
continue;
}
// IIS has been known to send "Content-Length: 0" on 304 responses, so
// ignore this too
- if (header == "content-length" && statusCode == 304)
+ if (statusCode == 304 && header.compare("content-length", Qt::CaseInsensitive) == 0)
continue;
#if defined(QNETWORKACCESSHTTPBACKEND_DEBUG)
@@ -1757,23 +1780,23 @@ QNetworkCacheMetaData QNetworkReplyHttpImplPrivate::fetchCacheMetaData(const QNe
QByteArray o;
if (it != cacheHeaders.rawHeaders.constEnd())
o = (*it).second;
- if (n != o && header != "date") {
+ if (n != o && headerheader.compare("date", Qt::CaseInsensitive) != 0) {
qDebug() << "replacing" << header;
qDebug() << "new" << n;
qDebug() << "old" << o;
}
#endif
- cacheHeaders.setRawHeader(originalHeader, q->rawHeader(header));
+ cacheHeaders.setRawHeader(header, q->rawHeader(header));
}
metaData.setRawHeaders(cacheHeaders.rawHeaders);
bool checkExpired = true;
QHash<QByteArray, QByteArray> cacheControl;
- it = cacheHeaders.findRawHeader("Cache-Control");
+ it = cacheHeaders.findRawHeader(cacheControlName());
if (it != cacheHeaders.rawHeaders.constEnd()) {
cacheControl = parseHttpOptionHeader(it->second);
- QByteArray maxAge = cacheControl.value("max-age");
+ QByteArray maxAge = cacheControl.value("max-age"_ba);
if (!maxAge.isEmpty()) {
checkExpired = false;
QDateTime dt = QDateTime::currentDateTimeUtc();
@@ -1800,7 +1823,7 @@ QNetworkCacheMetaData QNetworkReplyHttpImplPrivate::fetchCacheMetaData(const QNe
canDiskCache = true;
// HTTP/1.1. Check the Cache-Control header
- if (cacheControl.contains("no-store"))
+ if (cacheControl.contains("no-store"_ba))
canDiskCache = false;
// responses to POST might be cacheable
@@ -1809,7 +1832,7 @@ QNetworkCacheMetaData QNetworkReplyHttpImplPrivate::fetchCacheMetaData(const QNe
canDiskCache = false;
// some pages contain "expires:" and "cache-control: no-cache" field,
// so we only might cache POST requests if we get "cache-control: max-age ..."
- if (cacheControl.contains("max-age"))
+ if (cacheControl.contains("max-age"_ba))
canDiskCache = true;
// responses to PUT and DELETE are not cacheable
@@ -1840,14 +1863,14 @@ bool QNetworkReplyHttpImplPrivate::canResume() const
return false;
// Can only resume if server/resource supports Range header.
- QByteArray acceptRangesheaderName("Accept-Ranges");
+ constexpr auto acceptRangesheaderName = QByteArrayView("Accept-Ranges");
if (!q->hasRawHeader(acceptRangesheaderName) || q->rawHeader(acceptRangesheaderName) == "none")
return false;
// We only support resuming for byte ranges.
- if (request.hasRawHeader("Range")) {
- QByteArray range = request.rawHeader("Range");
- if (!range.startsWith("bytes="))
+ if (request.hasRawHeader(rangeName())) {
+ QByteArray range = request.rawHeader(rangeName());
+ if (!range.startsWith(bytesEqualPrefix()))
return false;
}
@@ -1866,7 +1889,9 @@ void QNetworkReplyHttpImplPrivate::setResumeOffset(quint64 offset)
void QNetworkReplyHttpImplPrivate::_q_startOperation()
{
- if (state == Working) // ensure this function is only being called once
+ // Ensure this function is only being called once, and not at all if we were
+ // cancelled
+ if (state >= Working)
return;
state = Working;
@@ -2026,9 +2051,9 @@ void QNetworkReplyHttpImplPrivate::setupTransferTimeout()
Qt::QueuedConnection);
}
transferTimeout->stop();
- if (request.transferTimeout()) {
+ if (request.transferTimeoutAsDuration() > 0ms) {
transferTimeout->setSingleShot(true);
- transferTimeout->setInterval(request.transferTimeout());
+ transferTimeout->setInterval(request.transferTimeoutAsDuration());
QMetaObject::invokeMethod(transferTimeout, "start",
Qt::QueuedConnection);
@@ -2134,13 +2159,12 @@ void QNetworkReplyHttpImplPrivate::error(QNetworkReplyImpl::NetworkError code, c
Q_Q(QNetworkReplyHttpImpl);
// Can't set and emit multiple errors.
if (errorCode != QNetworkReply::NoError) {
- qWarning("QNetworkReplyImplPrivate::error: Internal problem, this method must only be called once.");
+ // But somewhat unavoidable if we have cancelled the request:
+ if (errorCode != QNetworkReply::OperationCanceledError)
+ qWarning("QNetworkReplyImplPrivate::error: Internal problem, this method must only be called once.");
return;
}
- if (decompressHelper.isValid())
- decompressHelper.clear(); // Just get rid of any data that might be stored
-
errorCode = code;
q->setErrorString(errorMessage);
@@ -2229,3 +2253,5 @@ void QNetworkReplyHttpImplPrivate::completeCacheSave()
}
QT_END_NAMESPACE
+
+#include "moc_qnetworkreplyhttpimpl_p.cpp"
diff --git a/src/network/access/qnetworkreplyhttpimpl_p.h b/src/network/access/qnetworkreplyhttpimpl_p.h
index c2394488bd..e00c43bdb3 100644
--- a/src/network/access/qnetworkreplyhttpimpl_p.h
+++ b/src/network/access/qnetworkreplyhttpimpl_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKREPLYHTTPIMPL_P_H
#define QNETWORKREPLYHTTPIMPL_P_H
@@ -236,6 +200,10 @@ public:
qint64 bytesDownloaded;
qint64 bytesBuffered;
+ // We use this to keep track of whether or not we need to emit readyRead
+ // when we deal with signal compression (delaying emission) + decompressing
+ // data (potentially receiving bytes that don't end up in the final output):
+ qint64 lastReadyReadEmittedSize = 0;
QTimer *transferTimeout;
@@ -276,7 +244,7 @@ public:
// From HTTP thread:
void replyDownloadData(QByteArray);
void replyFinished();
- void replyDownloadMetaData(const QList<QPair<QByteArray,QByteArray> > &, int, const QString &,
+ void replyDownloadMetaData(const QHttpHeaders &, int, const QString &,
bool, QSharedPointer<char>, qint64, qint64, bool, bool);
void replyDownloadProgressSlot(qint64,qint64);
void httpAuthenticationRequired(const QHttpNetworkRequest &request, QAuthenticator *auth);
diff --git a/src/network/access/qnetworkreplyimpl.cpp b/src/network/access/qnetworkreplyimpl.cpp
index 8915e2e543..8b2acfdb4e 100644
--- a/src/network/access/qnetworkreplyimpl.cpp
+++ b/src/network/access/qnetworkreplyimpl.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qnetworkreplyimpl_p.h"
#include "qnetworkaccessbackend_p.h"
@@ -51,6 +15,8 @@
QT_BEGIN_NAMESPACE
+QT_IMPL_METATYPE_EXTERN_TAGGED(QSharedPointer<char>, QSharedPointer_char)
+
inline QNetworkReplyImplPrivate::QNetworkReplyImplPrivate()
: backend(nullptr), outgoingData(nullptr),
copyDevice(nullptr),
@@ -315,7 +281,7 @@ void QNetworkReplyImplPrivate::handleNotifications()
if (notificationHandlingPaused)
return;
- for (InternalNotifications notification : qExchange(pendingNotifications, {})) {
+ for (InternalNotifications notification : std::exchange(pendingNotifications, {})) {
if (state != Working)
return;
switch (notification) {
@@ -492,7 +458,7 @@ void QNetworkReplyImplPrivate::appendDownstreamData(QByteDataBuffer &data)
}
qint64 bytesWritten = 0;
- for (int i = 0; i < data.bufferCount(); i++) {
+ for (qsizetype i = 0; i < data.bufferCount(); ++i) {
QByteArray const &item = data[i];
if (cacheSaveDevice)
@@ -553,11 +519,6 @@ void QNetworkReplyImplPrivate::appendDownstreamData(QIODevice *data)
_q_copyReadyRead();
}
-static void downloadBufferDeleter(char *ptr)
-{
- delete[] ptr;
-}
-
char* QNetworkReplyImplPrivate::getDownloadBuffer(qint64 size)
{
Q_Q(QNetworkReplyImpl);
@@ -570,7 +531,7 @@ char* QNetworkReplyImplPrivate::getDownloadBuffer(qint64 size)
downloadBufferCurrentSize = 0;
downloadBufferMaximumSize = size;
downloadBuffer = new char[downloadBufferMaximumSize]; // throws if allocation fails
- downloadBufferPointer = QSharedPointer<char>(downloadBuffer, downloadBufferDeleter);
+ downloadBufferPointer = QSharedPointer<char>(downloadBuffer, [](auto p) { delete[] p; });
q->setAttribute(QNetworkRequest::DownloadBufferAttribute, QVariant::fromValue<QSharedPointer<char> > (downloadBufferPointer));
}
diff --git a/src/network/access/qnetworkreplyimpl_p.h b/src/network/access/qnetworkreplyimpl_p.h
index e390acb69d..9648b8b57a 100644
--- a/src/network/access/qnetworkreplyimpl_p.h
+++ b/src/network/access/qnetworkreplyimpl_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKREPLYIMPL_P_H
#define QNETWORKREPLYIMPL_P_H
@@ -193,6 +157,4 @@ Q_DECLARE_TYPEINFO(QNetworkReplyImplPrivate::InternalNotifications, Q_PRIMITIVE_
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(QSharedPointer<char>)
-
#endif
diff --git a/src/network/access/qnetworkreplywasmimpl.cpp b/src/network/access/qnetworkreplywasmimpl.cpp
index 3f8c11c201..c02f0b4e61 100644
--- a/src/network/access/qnetworkreplywasmimpl.cpp
+++ b/src/network/access/qnetworkreplywasmimpl.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** 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) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qnetworkreplywasmimpl_p.h"
#include "qnetworkrequest.h"
@@ -45,6 +9,8 @@
#include <QtCore/qcoreapplication.h>
#include <QtCore/qfileinfo.h>
#include <QtCore/qthread.h>
+#include <QtCore/private/qoffsetstringarray_p.h>
+#include <QtCore/private/qtools_p.h>
#include <private/qnetworkaccessmanager_p.h>
#include <private/qnetworkfile_p.h>
@@ -54,6 +20,41 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
+namespace {
+
+static constexpr auto BannedHeaders = qOffsetStringArray(
+ "accept-charset",
+ "accept-encoding",
+ "access-control-request-headers",
+ "access-control-request-method",
+ "connection",
+ "content-length",
+ "cookie",
+ "cookie2",
+ "date",
+ "dnt",
+ "expect",
+ "host",
+ "keep-alive",
+ "origin",
+ "referer",
+ "te",
+ "trailer",
+ "transfer-encoding",
+ "upgrade",
+ "via"
+);
+
+bool isUnsafeHeader(QLatin1StringView header) noexcept
+{
+ return header.startsWith("proxy-"_L1, Qt::CaseInsensitive)
+ || header.startsWith("sec-"_L1, Qt::CaseInsensitive)
+ || BannedHeaders.contains(header, Qt::CaseInsensitive);
+}
+} // namespace
+
QNetworkReplyWasmImplPrivate::QNetworkReplyWasmImplPrivate()
: QNetworkReplyPrivate()
, managerPrivate(0)
@@ -61,7 +62,7 @@ QNetworkReplyWasmImplPrivate::QNetworkReplyWasmImplPrivate()
, downloadBufferCurrentSize(0)
, totalDownloadSize(0)
, percentFinished(0)
- , m_fetch(0)
+ , m_fetch(nullptr)
{
}
@@ -78,6 +79,9 @@ QNetworkReplyWasmImpl::QNetworkReplyWasmImpl(QObject *parent)
QNetworkReplyWasmImpl::~QNetworkReplyWasmImpl()
{
+ if (isRunning())
+ abort();
+ close();
}
QByteArray QNetworkReplyWasmImpl::methodName() const
@@ -104,22 +108,38 @@ QByteArray QNetworkReplyWasmImpl::methodName() const
void QNetworkReplyWasmImpl::close()
{
+ Q_D(QNetworkReplyWasmImpl);
+
+ if (d->state != QNetworkReplyPrivate::Aborted &&
+ d->state != QNetworkReplyPrivate::Finished &&
+ d->state != QNetworkReplyPrivate::Idle) {
+ d->state = QNetworkReplyPrivate::Finished;
+ d->setCanceled();
+ }
+
QNetworkReply::close();
- setFinished(true);
- emit finished();
}
void QNetworkReplyWasmImpl::abort()
{
Q_D(QNetworkReplyWasmImpl);
+
if (d->state == QNetworkReplyPrivate::Finished || d->state == QNetworkReplyPrivate::Aborted)
return;
d->state = QNetworkReplyPrivate::Aborted;
- d->m_fetch->userData = nullptr;
+ d->setCanceled();
+}
- d->emitReplyError(QNetworkReply::OperationCanceledError, QStringLiteral("Operation canceled"));
- close();
+void QNetworkReplyWasmImplPrivate::setCanceled()
+{
+ Q_Q(QNetworkReplyWasmImpl);
+ if (m_fetch)
+ m_fetch->userData = nullptr;
+
+ emitReplyError(QNetworkReply::OperationCanceledError, QStringLiteral("Operation canceled"));
+ q->setFinished(true);
+ emit q->finished();
}
qint64 QNetworkReplyWasmImpl::bytesAvailable() const
@@ -211,15 +231,22 @@ void QNetworkReplyWasmImplPrivate::doSendRequest()
QList<QByteArray> headersData = request.rawHeaderList();
int arrayLength = getArraySize(headersData.count());
- const char* customHeaders[arrayLength];
+ const char *customHeaders[arrayLength];
+ QStringList trimmedHeaders;
if (headersData.count() > 0) {
int i = 0;
- for (int j = 0; j < headersData.count(); j++) {
- customHeaders[i] = headersData[j].constData();
- i += 1;
- customHeaders[i] = request.rawHeader(headersData[j]).constData();
- i += 1;
+ for (const auto &headerName : headersData) {
+ if (isUnsafeHeader(QLatin1StringView(headerName.constData()))) {
+ trimmedHeaders.push_back(QString::fromLatin1(headerName));
+ } else {
+ customHeaders[i++] = headerName.constData();
+ customHeaders[i++] = request.rawHeader(headerName).constData();
+ }
+ }
+ if (!trimmedHeaders.isEmpty()) {
+ qWarning() << "Qt has trimmed the following forbidden headers from the request:"
+ << trimmedHeaders.join(QLatin1StringView(", "));
}
customHeaders[i] = nullptr;
attr.requestHeaders = customHeaders;
@@ -234,10 +261,13 @@ void QNetworkReplyWasmImplPrivate::doSendRequest()
}
}
+ QByteArray userName, password;
// username & password
if (!request.url().userInfo().isEmpty()) {
- attr.userName = request.url().userName().toUtf8();
- attr.password = request.url().password().toUtf8();
+ userName = request.url().userName().toUtf8();
+ password = request.url().password().toUtf8();
+ attr.userName = userName.constData();
+ attr.password = password.constData();
}
attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
@@ -257,6 +287,7 @@ void QNetworkReplyWasmImplPrivate::doSendRequest()
attr.attributes -= EMSCRIPTEN_FETCH_PERSIST_FILE;
}
+ attr.withCredentials = request.attribute(QNetworkRequest::UseCredentialsAttribute, false).toBool();
attr.onsuccess = QNetworkReplyWasmImplPrivate::downloadSucceeded;
attr.onerror = QNetworkReplyWasmImplPrivate::downloadFailed;
attr.onprogress = QNetworkReplyWasmImplPrivate::downloadProgress;
@@ -264,10 +295,12 @@ void QNetworkReplyWasmImplPrivate::doSendRequest()
attr.timeoutMSecs = request.transferTimeout();
attr.userData = reinterpret_cast<void *>(this);
- QString dPath = QStringLiteral("/home/web_user/") + request.url().fileName();
- attr.destinationPath = dPath.toUtf8();
+ QString dPath = "/home/web_user/"_L1 + request.url().fileName();
+ QByteArray destinationPath = dPath.toUtf8();
+ attr.destinationPath = destinationPath.constData();
- m_fetch = emscripten_fetch(&attr, request.url().toString().toUtf8());
+ m_fetch = emscripten_fetch(&attr, request.url().toString().toUtf8().constData());
+ state = Working;
}
void QNetworkReplyWasmImplPrivate::emitReplyError(QNetworkReply::NetworkError errorCode, const QString &errorString)
@@ -284,15 +317,16 @@ void QNetworkReplyWasmImplPrivate::emitDataReadProgress(qint64 bytesReceived, qi
totalDownloadSize = bytesTotal;
- percentFinished = (bytesReceived / bytesTotal) * 100;
+ percentFinished = bytesTotal ? (bytesReceived / bytesTotal) * 100 : 100;
emit q->downloadProgress(bytesReceived, bytesTotal);
}
-void QNetworkReplyWasmImplPrivate::dataReceived(const QByteArray &buffer, int bufferSize)
+void QNetworkReplyWasmImplPrivate::dataReceived(const QByteArray &buffer)
{
Q_Q(QNetworkReplyWasmImpl);
+ const qsizetype bufferSize = buffer.size();
if (bufferSize > 0)
q->setReadBufferSize(bufferSize);
@@ -305,7 +339,7 @@ void QNetworkReplyWasmImplPrivate::dataReceived(const QByteArray &buffer, int bu
totalDownloadSize = downloadBufferCurrentSize;
- downloadBuffer.append(buffer, bufferSize);
+ downloadBuffer.append(buffer);
emit q->readyRead();
}
@@ -316,32 +350,36 @@ static int parseHeaderName(const QByteArray &headerName)
if (headerName.isEmpty())
return -1;
- switch (tolower(headerName.at(0))) {
+ auto is = [&](const char *what) {
+ return qstrnicmp(headerName.data(), headerName.size(), what) == 0;
+ };
+
+ switch (QtMiscUtils::toAsciiLower(headerName.front())) {
case 'c':
- if (qstricmp(headerName.constData(), "content-type") == 0)
+ if (is("content-type"))
return QNetworkRequest::ContentTypeHeader;
- else if (qstricmp(headerName.constData(), "content-length") == 0)
+ else if (is("content-length"))
return QNetworkRequest::ContentLengthHeader;
- else if (qstricmp(headerName.constData(), "cookie") == 0)
+ else if (is("cookie"))
return QNetworkRequest::CookieHeader;
break;
case 'l':
- if (qstricmp(headerName.constData(), "location") == 0)
+ if (is("location"))
return QNetworkRequest::LocationHeader;
- else if (qstricmp(headerName.constData(), "last-modified") == 0)
+ else if (is("last-modified"))
return QNetworkRequest::LastModifiedHeader;
break;
case 's':
- if (qstricmp(headerName.constData(), "set-cookie") == 0)
+ if (is("set-cookie"))
return QNetworkRequest::SetCookieHeader;
- else if (qstricmp(headerName.constData(), "server") == 0)
+ else if (is("server"))
return QNetworkRequest::ServerHeader;
break;
case 'u':
- if (qstricmp(headerName.constData(), "user-agent") == 0)
+ if (is("user-agent"))
return QNetworkRequest::UserAgentHeader;
break;
}
@@ -443,7 +481,7 @@ void QNetworkReplyWasmImplPrivate::downloadSucceeded(emscripten_fetch_t *fetch)
if (reply) {
if (reply->state != QNetworkReplyPrivate::Aborted) {
QByteArray buffer(fetch->data, fetch->numBytes);
- reply->dataReceived(buffer, buffer.size());
+ reply->dataReceived(buffer);
QByteArray statusText(fetch->statusText);
reply->setStatusCode(fetch->status, statusText);
reply->setReplyFinished();
@@ -456,6 +494,7 @@ void QNetworkReplyWasmImplPrivate::downloadSucceeded(emscripten_fetch_t *fetch)
void QNetworkReplyWasmImplPrivate::setReplyFinished()
{
Q_Q(QNetworkReplyWasmImpl);
+ state = QNetworkReplyPrivate::Finished;
q->setFinished(true);
emit q->readChannelFinished();
emit q->finished();
@@ -505,9 +544,12 @@ void QNetworkReplyWasmImplPrivate::downloadFailed(emscripten_fetch_t *fetch)
reasonStr = QStringLiteral("Operation canceled");
else
reasonStr = QString::fromUtf8(fetch->statusText);
+ QByteArray buffer(fetch->data, fetch->numBytes);
+ reply->dataReceived(buffer);
QByteArray statusText(fetch->statusText);
reply->setStatusCode(fetch->status, statusText);
reply->emitReplyError(reply->statusCodeFromHttp(fetch->status, reply->request.url()), reasonStr);
+ reply->setReplyFinished();
}
reply->m_fetch = nullptr;
}
@@ -589,3 +631,5 @@ QNetworkReply::NetworkError QNetworkReplyWasmImplPrivate::statusCodeFromHttp(int
}
QT_END_NAMESPACE
+
+#include "moc_qnetworkreplywasmimpl_p.cpp"
diff --git a/src/network/access/qnetworkreplywasmimpl_p.h b/src/network/access/qnetworkreplywasmimpl_p.h
index 83e39faddc..ae167799d7 100644
--- a/src/network/access/qnetworkreplywasmimpl_p.h
+++ b/src/network/access/qnetworkreplywasmimpl_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** 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) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKREPLYWASMIMPL_H
#define QNETWORKREPLYWASMIMPL_H
@@ -93,7 +57,7 @@ public:
Q_PRIVATE_SLOT(d_func(), void emitReplyError(QNetworkReply::NetworkError errorCode, const QString &errorString))
Q_PRIVATE_SLOT(d_func(), void emitDataReadProgress(qint64 done, qint64 total))
- Q_PRIVATE_SLOT(d_func(), void dataReceived(char *buffer, int bufferSize))
+ Q_PRIVATE_SLOT(d_func(), void dataReceived(const QByteArray &buffer))
private:
QByteArray methodName() const;
@@ -111,7 +75,7 @@ public:
void emitReplyError(QNetworkReply::NetworkError errorCode, const QString &);
void emitDataReadProgress(qint64 done, qint64 total);
- void dataReceived(const QByteArray &buffer, int bufferSize);
+ void dataReceived(const QByteArray &buffer);
void headersReceived(const QByteArray &buffer);
void setStatusCode(int status, const QByteArray &statusText);
@@ -148,6 +112,7 @@ public:
emscripten_fetch_t *m_fetch;
void setReplyFinished();
+ void setCanceled();
Q_DECLARE_PUBLIC(QNetworkReplyWasmImpl)
};
diff --git a/src/network/access/qnetworkrequest.cpp b/src/network/access/qnetworkrequest.cpp
index 3e9c12ae9e..6f5a7ff19a 100644
--- a/src/network/access/qnetworkrequest.cpp
+++ b/src/network/access/qnetworkrequest.cpp
@@ -1,54 +1,22 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qnetworkrequest.h"
#include "qnetworkrequest_p.h"
#include "qplatformdefs.h"
#include "qnetworkcookie.h"
#include "qsslconfiguration.h"
-#if QT_CONFIG(http) || defined(Q_CLANG_QDOC)
+#if QT_CONFIG(http)
+#include "qhttp1configuration.h"
#include "qhttp2configuration.h"
#include "private/http2protocol_p.h"
#endif
-#include "QtCore/qshareddata.h"
-#include "QtCore/qlocale.h"
+
#include "QtCore/qdatetime.h"
+#include "QtCore/qlocale.h"
+#include "QtCore/qshareddata.h"
+#include "QtCore/qtimezone.h"
+#include "QtCore/private/qtools_p.h"
#include <ctype.h>
#if QT_CONFIG(datestring)
@@ -59,6 +27,14 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+using namespace std::chrono_literals;
+
+constexpr std::chrono::milliseconds QNetworkRequest::DefaultTransferTimeout;
+
+QT_IMPL_METATYPE_EXTERN(QNetworkRequest)
+QT_IMPL_METATYPE_EXTERN_TAGGED(QNetworkRequest::RedirectPolicy, QNetworkRequest__RedirectPolicy)
+
/*!
\class QNetworkRequest
\since 4.4
@@ -169,10 +145,10 @@ QT_BEGIN_NAMESPACE
Replies only, type: QMetaType::QUrl (no default)
If present, it indicates that the server is redirecting the
request to a different URL. The Network Access API does follow
- redirections by default, but if
- QNetworkRequest::ManualRedirectPolicy is enabled and
- the redirect was not handled in redirected() then this
- attribute will be present.
+ redirections by default, unless
+ QNetworkRequest::ManualRedirectPolicy is used. Additionally, if
+ QNetworkRequest::UserVerifiedRedirectPolicy is used, then this
+ attribute will be set if the redirect was not followed.
The returned URL might be relative. Use QUrl::resolved()
to create an absolute URL out of it.
@@ -248,7 +224,7 @@ QT_BEGIN_NAMESPACE
Requests only, type: QMetaType::Int (default: QNetworkRequest::Automatic)
Indicates whether to use cached authorization credentials in the request,
if available. If this is set to QNetworkRequest::Manual and the authentication
- mechanism is 'Basic' or 'Digest', Qt will not send an an 'Authorization' HTTP
+ mechanism is 'Basic' or 'Digest', Qt will not send an 'Authorization' HTTP
header with any cached credentials it may have for the request's URL.
This attribute is set to QNetworkRequest::Manual by Qt WebKit when creating a cross-origin
XMLHttpRequest where withCredentials has not been set explicitly to true by the
@@ -272,7 +248,8 @@ QT_BEGIN_NAMESPACE
Requests only, type: QMetaType::Bool (default: true)
Indicates whether the QNetworkAccessManager code is
allowed to use HTTP/2 with this request. This applies
- to SSL requests or 'cleartext' HTTP/2.
+ to SSL requests or 'cleartext' HTTP/2 if Http2CleartextAllowedAttribute
+ is set.
\value Http2WasUsedAttribute
Replies only, type: QMetaType::Bool (default: false)
@@ -304,8 +281,9 @@ QT_BEGIN_NAMESPACE
If set, this attribute will force QNetworkAccessManager to use
HTTP/2 protocol without initial HTTP/2 protocol negotiation.
Use of this attribute implies prior knowledge that a particular
- server supports HTTP/2. The attribute works with SSL or 'cleartext'
- HTTP/2. If a server turns out to not support HTTP/2, when HTTP/2 direct
+ server supports HTTP/2. The attribute works with SSL or with 'cleartext'
+ HTTP/2 if Http2CleartextAllowedAttribute is set.
+ If a server turns out to not support HTTP/2, when HTTP/2 direct
was specified, QNetworkAccessManager gives up, without attempting to
fall back to HTTP/1.1. If both Http2AllowedAttribute and
Http2DirectAttribute are set, Http2DirectAttribute takes priority.
@@ -325,6 +303,22 @@ QT_BEGIN_NAMESPACE
be closed after the last pending request had been processed.
(This value was introduced in 6.3.)
+ \value Http2CleartextAllowedAttribute
+ Requests only, type: QMetaType::Bool (default: false)
+ If set, this attribute will tell QNetworkAccessManager to attempt
+ an upgrade to HTTP/2 over cleartext (also known as h2c).
+ Until Qt 7 the default value for this attribute can be overridden
+ to true by setting the QT_NETWORK_H2C_ALLOWED environment variable.
+ This attribute is ignored if the Http2AllowedAttribute is not set.
+ (This value was introduced in 6.3.)
+
+ \value UseCredentialsAttribute
+ Requests only, type: QMetaType::Bool (default: false)
+ Indicates if the underlying XMLHttpRequest cross-site Access-Control
+ requests should be made using credentials. Has no effect on
+ same-origin requests. This only affects the WebAssembly platform.
+ (This value was introduced in 6.5.)
+
\value User
Special type. Additional information can be passed in
QVariants with types ranging from User to UserMax. The default
@@ -399,6 +393,11 @@ QT_BEGIN_NAMESPACE
for example, to ask the user whether to
accept the redirect, or to decide
based on some app-specific configuration.
+
+ \note When Qt handles redirects it will, for legacy and compatibility
+ reasons, issue the redirected request using GET when the server returns
+ a 301 or 302 response, regardless of the original method used, unless it was
+ HEAD.
*/
/*!
@@ -411,6 +410,16 @@ QT_BEGIN_NAMESPACE
\value DefaultTransferTimeoutConstant The transfer timeout in milliseconds.
Used if setTimeout() is called
without an argument.
+
+ \sa QNetworkRequest::DefaultTransferTimeout
+ */
+
+/*!
+ \variable QNetworkRequest::DefaultTransferTimeout
+
+ The transfer timeout with \l {QNetworkRequest::TransferTimeoutConstant}
+ milliseconds. Used if setTransferTimeout() is called without an
+ argument.
*/
class QNetworkRequestPrivate: public QSharedData, public QNetworkHeadersPrivate
@@ -423,7 +432,6 @@ public:
, sslConfiguration(nullptr)
#endif
, maxRedirectsAllowed(maxRedirectCount)
- , transferTimeout(0)
{ qRegisterMetaType<QNetworkRequest>(); }
~QNetworkRequestPrivate()
{
@@ -446,6 +454,7 @@ public:
#endif
peerVerifyName = other.peerVerifyName;
#if QT_CONFIG(http)
+ h1Configuration = other.h1Configuration;
h2Configuration = other.h2Configuration;
decompressedSafetyCheckThreshold = other.decompressedSafetyCheckThreshold;
#endif
@@ -461,6 +470,7 @@ public:
maxRedirectsAllowed == other.maxRedirectsAllowed &&
peerVerifyName == other.peerVerifyName
#if QT_CONFIG(http)
+ && h1Configuration == other.h1Configuration
&& h2Configuration == other.h2Configuration
&& decompressedSafetyCheckThreshold == other.decompressedSafetyCheckThreshold
#endif
@@ -477,10 +487,11 @@ public:
int maxRedirectsAllowed;
QString peerVerifyName;
#if QT_CONFIG(http)
+ QHttp1Configuration h1Configuration;
QHttp2Configuration h2Configuration;
qint64 decompressedSafetyCheckThreshold = 10ll * 1024ll * 1024ll;
#endif
- int transferTimeout;
+ std::chrono::milliseconds transferTimeout = 0ms;
};
/*!
@@ -496,10 +507,9 @@ QNetworkRequest::QNetworkRequest()
// Initial values proposed by RFC 7540 are quite draconian, but we
// know about servers configured with this value as maximum possible,
// rejecting our SETTINGS frame and sending us a GOAWAY frame with the
- // flow control error set. Unless an application sets its own parameters,
- // we don't send SETTINGS_INITIAL_WINDOW_SIZE, but increase
- // (via WINDOW_UPDATE) the session window size. These are our 'defaults':
- d->h2Configuration.setStreamReceiveWindowSize(Http2::defaultSessionWindowSize);
+ // flow control error set. If this causes a problem - the app should
+ // set a proper configuration. We'll use our defaults, as documented.
+ d->h2Configuration.setStreamReceiveWindowSize(Http2::qtDefaultStreamReceiveWindowSize);
d->h2Configuration.setSessionReceiveWindowSize(Http2::maxSessionReceiveWindowSize);
d->h2Configuration.setServerPushEnabled(false);
#endif // QT_CONFIG(http)
@@ -619,8 +629,9 @@ void QNetworkRequest::setHeader(KnownHeaders header, const QVariant &value)
network request.
\sa rawHeader(), setRawHeader()
+ \note In Qt versions prior to 6.7, this function took QByteArray only.
*/
-bool QNetworkRequest::hasRawHeader(const QByteArray &headerName) const
+bool QNetworkRequest::hasRawHeader(QAnyStringView headerName) const
{
return d->findRawHeader(headerName) != d->rawHeaders.constEnd();
}
@@ -634,12 +645,11 @@ bool QNetworkRequest::hasRawHeader(const QByteArray &headerName) const
Raw headers can be set with setRawHeader() or with setHeader().
\sa header(), setRawHeader()
+ \note In Qt versions prior to 6.7, this function took QByteArray only.
*/
-QByteArray QNetworkRequest::rawHeader(const QByteArray &headerName) const
+QByteArray QNetworkRequest::rawHeader(QAnyStringView headerName) const
{
- QNetworkHeadersPrivate::RawHeadersList::ConstIterator it =
- d->findRawHeader(headerName);
- if (it != d->rawHeaders.constEnd())
+ if (const auto it = d->findRawHeader(headerName); it != d->rawHeaders.constEnd())
return it->second;
return QByteArray();
}
@@ -726,9 +736,8 @@ QSslConfiguration QNetworkRequest::sslConfiguration() const
/*!
Sets this network request's SSL configuration to be \a config. The
settings that apply are the private key, the local certificate,
- the SSL protocol (SSLv2, SSLv3, TLSv1.0 where applicable), the CA
- certificates and the ciphers that the SSL backend is allowed to
- use.
+ the TLS protocol (e.g. TLS 1.3), the CA certificates and the ciphers that
+ the SSL backend is allowed to use.
\sa sslConfiguration(), QSslConfiguration::defaultConfiguration()
*/
@@ -862,7 +871,31 @@ void QNetworkRequest::setPeerVerifyName(const QString &peerName)
d->peerVerifyName = peerName;
}
-#if QT_CONFIG(http) || defined(Q_CLANG_QDOC)
+#if QT_CONFIG(http)
+/*!
+ \since 6.5
+
+ Returns the current parameters that QNetworkAccessManager is
+ using for the underlying HTTP/1 connection of this request.
+
+ \sa setHttp1Configuration
+*/
+QHttp1Configuration QNetworkRequest::http1Configuration() const
+{
+ return d->h1Configuration;
+}
+/*!
+ \since 6.5
+
+ Sets request's HTTP/1 parameters from \a configuration.
+
+ \sa http1Configuration, QNetworkAccessManager, QHttp1Configuration
+*/
+void QNetworkRequest::setHttp1Configuration(const QHttp1Configuration &configuration)
+{
+ d->h1Configuration = configuration;
+}
+
/*!
\since 5.14
@@ -875,7 +908,7 @@ void QNetworkRequest::setPeerVerifyName(const QString &peerName)
\list
\li Window size for connection-level flowcontrol is 2147483647 octets
- \li Window size for stream-level flowcontrol is 21474836 octets
+ \li Window size for stream-level flowcontrol is 214748364 octets
\li Max frame size is 16384
\endlist
@@ -948,85 +981,104 @@ void QNetworkRequest::setDecompressedSafetyCheckThreshold(qint64 threshold)
{
d->decompressedSafetyCheckThreshold = threshold;
}
-#endif // QT_CONFIG(http) || defined(Q_CLANG_QDOC)
+#endif // QT_CONFIG(http)
-#if QT_CONFIG(http) || defined(Q_CLANG_QDOC) || defined (Q_OS_WASM)
+#if QT_CONFIG(http) || defined (Q_OS_WASM)
/*!
+ \fn int QNetworkRequest::transferTimeout() const
\since 5.15
Returns the timeout used for transfers, in milliseconds.
- This timeout is zero if setTransferTimeout hasn't been
- called, which means that the timeout is not used.
+ \sa setTransferTimeout()
+*/
+
+/*!
+ \fn void QNetworkRequest::setTransferTimeout(int timeout)
+ \since 5.15
- \sa setTransferTimeout
+ Sets \a timeout as the transfer timeout in milliseconds.
+
+ \sa setTransferTimeout(std::chrono::milliseconds),
+ transferTimeout(), transferTimeoutAsDuration()
+*/
+
+/*!
+ \since 6.7
+
+ Returns the timeout duration after which the transfer is aborted if no
+ data is exchanged.
+
+ The default duration is zero, which means that the timeout is not used.
+
+ \sa setTransferTimeout(std::chrono::milliseconds)
*/
-int QNetworkRequest::transferTimeout() const
+std::chrono::milliseconds QNetworkRequest::transferTimeoutAsDuration() const
{
return d->transferTimeout;
}
/*!
- \since 5.15
+ \since 6.7
- Sets \a timeout as the transfer timeout in milliseconds.
+ Sets the timeout \a duration to abort the transfer if no data is exchanged.
Transfers are aborted if no bytes are transferred before
the timeout expires. Zero means no timer is set. If no
argument is provided, the timeout is
- QNetworkRequest::DefaultTransferTimeoutConstant. If this function
+ QNetworkRequest::DefaultTransferTimeout. If this function
is not called, the timeout is disabled and has the
value zero.
- \sa transferTimeout
+ \sa transferTimeoutAsDuration()
*/
-void QNetworkRequest::setTransferTimeout(int timeout)
+void QNetworkRequest::setTransferTimeout(std::chrono::milliseconds duration)
{
- d->transferTimeout = timeout;
+ d->transferTimeout = duration;
}
-#endif // QT_CONFIG(http) || defined(Q_CLANG_QDOC) || defined (Q_OS_WASM)
+#endif // QT_CONFIG(http) || defined (Q_OS_WASM)
static QByteArray headerName(QNetworkRequest::KnownHeaders header)
{
switch (header) {
case QNetworkRequest::ContentTypeHeader:
- return "Content-Type";
+ return "Content-Type"_ba;
case QNetworkRequest::ContentLengthHeader:
- return "Content-Length";
+ return "Content-Length"_ba;
case QNetworkRequest::LocationHeader:
- return "Location";
+ return "Location"_ba;
case QNetworkRequest::LastModifiedHeader:
- return "Last-Modified";
+ return "Last-Modified"_ba;
case QNetworkRequest::IfModifiedSinceHeader:
- return "If-Modified-Since";
+ return "If-Modified-Since"_ba;
case QNetworkRequest::ETagHeader:
- return "ETag";
+ return "ETag"_ba;
case QNetworkRequest::IfMatchHeader:
- return "If-Match";
+ return "If-Match"_ba;
case QNetworkRequest::IfNoneMatchHeader:
- return "If-None-Match";
+ return "If-None-Match"_ba;
case QNetworkRequest::CookieHeader:
- return "Cookie";
+ return "Cookie"_ba;
case QNetworkRequest::SetCookieHeader:
- return "Set-Cookie";
+ return "Set-Cookie"_ba;
case QNetworkRequest::ContentDispositionHeader:
- return "Content-Disposition";
+ return "Content-Disposition"_ba;
case QNetworkRequest::UserAgentHeader:
- return "User-Agent";
+ return "User-Agent"_ba;
case QNetworkRequest::ServerHeader:
- return "Server";
+ return "Server"_ba;
// no default:
// if new values are added, this will generate a compiler warning
@@ -1035,6 +1087,22 @@ static QByteArray headerName(QNetworkRequest::KnownHeaders header)
return QByteArray();
}
+static QByteArray makeCookieHeader(const QVariant &value, QNetworkCookie::RawForm type, QByteArrayView separator)
+{
+ QList<QNetworkCookie> cookies = qvariant_cast<QList<QNetworkCookie> >(value);
+ if (cookies.isEmpty() && value.userType() == qMetaTypeId<QNetworkCookie>())
+ cookies << qvariant_cast<QNetworkCookie>(value);
+
+ QByteArray result;
+ for (const QNetworkCookie &cookie : std::as_const(cookies)) {
+ result += cookie.toRawForm(type);
+ result += separator;
+ }
+ if (!result.isEmpty())
+ result.chop(separator.size());
+ return result;
+}
+
static QByteArray headerValue(QNetworkRequest::KnownHeaders header, const QVariant &value)
{
switch (header) {
@@ -1060,98 +1128,77 @@ static QByteArray headerValue(QNetworkRequest::KnownHeaders header, const QVaria
case QNetworkRequest::LastModifiedHeader:
case QNetworkRequest::IfModifiedSinceHeader:
switch (value.userType()) {
+ // Generate RFC 1123/822 dates:
case QMetaType::QDate:
+ return QNetworkHeadersPrivate::toHttpDate(value.toDate().startOfDay(QTimeZone::UTC));
case QMetaType::QDateTime:
- // generate RFC 1123/822 dates:
return QNetworkHeadersPrivate::toHttpDate(value.toDateTime());
default:
return value.toByteArray();
}
- case QNetworkRequest::CookieHeader: {
- QList<QNetworkCookie> cookies = qvariant_cast<QList<QNetworkCookie> >(value);
- if (cookies.isEmpty() && value.userType() == qMetaTypeId<QNetworkCookie>())
- cookies << qvariant_cast<QNetworkCookie>(value);
-
- QByteArray result;
- bool first = true;
- for (const QNetworkCookie &cookie : qAsConst(cookies)) {
- if (!first)
- result += "; ";
- first = false;
- result += cookie.toRawForm(QNetworkCookie::NameAndValueOnly);
- }
- return result;
- }
+ case QNetworkRequest::CookieHeader:
+ return makeCookieHeader(value, QNetworkCookie::NameAndValueOnly, "; ");
- case QNetworkRequest::SetCookieHeader: {
- QList<QNetworkCookie> cookies = qvariant_cast<QList<QNetworkCookie> >(value);
- if (cookies.isEmpty() && value.userType() == qMetaTypeId<QNetworkCookie>())
- cookies << qvariant_cast<QNetworkCookie>(value);
-
- QByteArray result;
- bool first = true;
- for (const QNetworkCookie &cookie : qAsConst(cookies)) {
- if (!first)
- result += ", ";
- first = false;
- result += cookie.toRawForm(QNetworkCookie::Full);
- }
- return result;
- }
+ case QNetworkRequest::SetCookieHeader:
+ return makeCookieHeader(value, QNetworkCookie::Full, ", ");
}
return QByteArray();
}
-static int parseHeaderName(const QByteArray &headerName)
+static int parseHeaderName(QByteArrayView headerName)
{
if (headerName.isEmpty())
return -1;
- switch (tolower(headerName.at(0))) {
+ auto is = [headerName](QByteArrayView what) {
+ return headerName.compare(what, Qt::CaseInsensitive) == 0;
+ };
+
+ switch (QtMiscUtils::toAsciiLower(headerName.front())) {
case 'c':
- if (headerName.compare("content-type", Qt::CaseInsensitive) == 0)
+ if (is("content-type"))
return QNetworkRequest::ContentTypeHeader;
- else if (headerName.compare("content-length", Qt::CaseInsensitive) == 0)
+ else if (is("content-length"))
return QNetworkRequest::ContentLengthHeader;
- else if (headerName.compare("cookie", Qt::CaseInsensitive) == 0)
+ else if (is("cookie"))
return QNetworkRequest::CookieHeader;
- else if (qstricmp(headerName.constData(), "content-disposition") == 0)
+ else if (is("content-disposition"))
return QNetworkRequest::ContentDispositionHeader;
break;
case 'e':
- if (qstricmp(headerName.constData(), "etag") == 0)
+ if (is("etag"))
return QNetworkRequest::ETagHeader;
break;
case 'i':
- if (qstricmp(headerName.constData(), "if-modified-since") == 0)
+ if (is("if-modified-since"))
return QNetworkRequest::IfModifiedSinceHeader;
- if (qstricmp(headerName.constData(), "if-match") == 0)
+ if (is("if-match"))
return QNetworkRequest::IfMatchHeader;
- if (qstricmp(headerName.constData(), "if-none-match") == 0)
+ if (is("if-none-match"))
return QNetworkRequest::IfNoneMatchHeader;
break;
case 'l':
- if (headerName.compare("location", Qt::CaseInsensitive) == 0)
+ if (is("location"))
return QNetworkRequest::LocationHeader;
- else if (headerName.compare("last-modified", Qt::CaseInsensitive) == 0)
+ else if (is("last-modified"))
return QNetworkRequest::LastModifiedHeader;
break;
case 's':
- if (headerName.compare("set-cookie", Qt::CaseInsensitive) == 0)
+ if (is("set-cookie"))
return QNetworkRequest::SetCookieHeader;
- else if (headerName.compare("server", Qt::CaseInsensitive) == 0)
+ else if (is("server"))
return QNetworkRequest::ServerHeader;
break;
case 'u':
- if (headerName.compare("user-agent", Qt::CaseInsensitive) == 0)
+ if (is("user-agent"))
return QNetworkRequest::UserAgentHeader;
break;
}
@@ -1167,13 +1214,12 @@ static QVariant parseHttpDate(const QByteArray &raw)
return QVariant(); // transform an invalid QDateTime into a null QVariant
}
-static QVariant parseCookieHeader(const QByteArray &raw)
+static QVariant parseCookieHeader(QByteArrayView raw)
{
QList<QNetworkCookie> result;
- const QList<QByteArray> cookieList = raw.split(';');
- for (const QByteArray &cookie : cookieList) {
+ for (auto cookie : QLatin1StringView(raw).tokenize(';'_L1)) {
QList<QNetworkCookie> parsed = QNetworkCookie::parseCookies(cookie.trimmed());
- if (parsed.count() != 1)
+ if (parsed.size() != 1)
return QVariant(); // invalid Cookie: header
result += parsed;
@@ -1182,9 +1228,9 @@ static QVariant parseCookieHeader(const QByteArray &raw)
return QVariant::fromValue(result);
}
-static QVariant parseETag(const QByteArray &raw)
+static QVariant parseETag(QByteArrayView raw)
{
- const QByteArray trimmed = raw.trimmed();
+ const QByteArrayView trimmed = raw.trimmed();
if (!trimmed.startsWith('"') && !trimmed.startsWith(R"(W/")"))
return QVariant();
@@ -1194,46 +1240,34 @@ static QVariant parseETag(const QByteArray &raw)
return QString::fromLatin1(trimmed);
}
-static QVariant parseIfMatch(const QByteArray &raw)
+template<typename T>
+static QVariant parseMatchImpl(QByteArrayView raw, T op)
{
- const QByteArray trimmedRaw = raw.trimmed();
+ const QByteArrayView trimmedRaw = raw.trimmed();
if (trimmedRaw == "*")
return QStringList(QStringLiteral("*"));
QStringList tags;
- const QList<QByteArray> split = trimmedRaw.split(',');
- for (const QByteArray &element : split) {
- const QByteArray trimmed = element.trimmed();
- if (!trimmed.startsWith('"'))
- continue;
-
- if (!trimmed.endsWith('"'))
- continue;
-
- tags += QString::fromLatin1(trimmed);
+ for (auto &element : QLatin1StringView(trimmedRaw).tokenize(','_L1)) {
+ if (const auto trimmed = element.trimmed(); op(trimmed))
+ tags += QString::fromLatin1(trimmed);
}
return tags;
}
-static QVariant parseIfNoneMatch(const QByteArray &raw)
-{
- const QByteArray trimmedRaw = raw.trimmed();
- if (trimmedRaw == "*")
- return QStringList(QStringLiteral("*"));
-
- QStringList tags;
- const QList<QByteArray> split = trimmedRaw.split(',');
- for (const QByteArray &element : split) {
- const QByteArray trimmed = element.trimmed();
- if (!trimmed.startsWith('"') && !trimmed.startsWith(R"(W/")"))
- continue;
- if (!trimmed.endsWith('"'))
- continue;
+static QVariant parseIfMatch(QByteArrayView raw)
+{
+ return parseMatchImpl(raw, [](QByteArrayView element) {
+ return element.startsWith('"') && element.endsWith('"');
+ });
+}
- tags += QString::fromLatin1(trimmed);
- }
- return tags;
+static QVariant parseIfNoneMatch(QByteArrayView raw)
+{
+ return parseMatchImpl(raw, [](QByteArrayView element) {
+ return (element.startsWith('"') || element.startsWith(R"(W/")")) && element.endsWith('"');
+ });
}
@@ -1250,7 +1284,7 @@ static QVariant parseHeaderValue(QNetworkRequest::KnownHeaders header, const QBy
case QNetworkRequest::ContentLengthHeader: {
bool ok;
- qint64 result = value.trimmed().toLongLong(&ok);
+ qint64 result = QByteArrayView(value).trimmed().toLongLong(&ok);
if (ok)
return result;
return QVariant();
@@ -1289,15 +1323,14 @@ static QVariant parseHeaderValue(QNetworkRequest::KnownHeaders header, const QBy
}
QNetworkHeadersPrivate::RawHeadersList::ConstIterator
-QNetworkHeadersPrivate::findRawHeader(const QByteArray &key) const
+QNetworkHeadersPrivate::findRawHeader(QAnyStringView key) const
{
- RawHeadersList::ConstIterator it = rawHeaders.constBegin();
- RawHeadersList::ConstIterator end = rawHeaders.constEnd();
- for ( ; it != end; ++it)
- if (it->first.compare(key, Qt::CaseInsensitive) == 0)
- return it;
-
- return end; // not found
+ auto isKeyEqual = [key](const auto &headerPair)
+ {
+ QLatin1StringView name{headerPair.first};
+ return QAnyStringView::compare(name, key, Qt::CaseInsensitive) == 0;
+ };
+ return std::find_if(rawHeaders.begin(), rawHeaders.end(), isKeyEqual);
}
QNetworkHeadersPrivate::RawHeadersList QNetworkHeadersPrivate::allRawHeaders() const
@@ -1309,10 +1342,8 @@ QList<QByteArray> QNetworkHeadersPrivate::rawHeadersKeys() const
{
QList<QByteArray> result;
result.reserve(rawHeaders.size());
- RawHeadersList::ConstIterator it = rawHeaders.constBegin(),
- end = rawHeaders.constEnd();
- for ( ; it != end; ++it)
- result << it->first;
+ for (const auto &pair : rawHeaders)
+ result << pair.first;
return result;
}
@@ -1340,10 +1371,8 @@ void QNetworkHeadersPrivate::setAllRawHeaders(const RawHeadersList &list)
cookedHeaders.clear();
rawHeaders = list;
- RawHeadersList::ConstIterator it = rawHeaders.constBegin();
- RawHeadersList::ConstIterator end = rawHeaders.constEnd();
- for ( ; it != end; ++it)
- parseAndSetHeader(it->first, it->second);
+ for (const auto &[key, value] : std::as_const(rawHeaders))
+ parseAndSetHeader(key, value);
}
void QNetworkHeadersPrivate::setCookedHeader(QNetworkRequest::KnownHeaders header,
@@ -1492,20 +1521,21 @@ QDateTime QNetworkHeadersPrivate::fromHttpDate(const QByteArray &value)
// eat the weekday, the comma and the space following it
QString sansWeekday = QString::fromLatin1(value.constData() + pos + 2);
// must be RFC 850 date
- dt = c.toDateTime(sansWeekday, QLatin1String("dd-MMM-yy hh:mm:ss 'GMT'"));
+ dt = c.toDateTime(sansWeekday, "dd-MMM-yy hh:mm:ss 'GMT'"_L1);
}
}
#endif // datestring
if (dt.isValid())
- dt.setTimeSpec(Qt::UTC);
+ dt.setTimeZone(QTimeZone::UTC);
return dt;
}
QByteArray QNetworkHeadersPrivate::toHttpDate(const QDateTime &dt)
{
- return QLocale::c().toString(dt, u"ddd, dd MMM yyyy hh:mm:ss 'GMT'")
- .toLatin1();
+ return QLocale::c().toString(dt.toUTC(), u"ddd, dd MMM yyyy hh:mm:ss 'GMT'").toLatin1();
}
QT_END_NAMESPACE
+
+#include "moc_qnetworkrequest.cpp"
diff --git a/src/network/access/qnetworkrequest.h b/src/network/access/qnetworkrequest.h
index bdcb1c80a9..3ca61a2ee3 100644
--- a/src/network/access/qnetworkrequest.h
+++ b/src/network/access/qnetworkrequest.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKREQUEST_H
#define QNETWORKREQUEST_H
@@ -50,10 +14,12 @@ QT_BEGIN_NAMESPACE
class QSslConfiguration;
class QHttp2Configuration;
+class QHttp1Configuration;
class QNetworkRequestPrivate;
class Q_NETWORK_EXPORT QNetworkRequest
{
+ Q_GADGET
public:
enum KnownHeaders {
ContentTypeHeader,
@@ -70,6 +36,8 @@ public:
IfMatchHeader,
IfNoneMatchHeader
};
+ Q_ENUM(KnownHeaders)
+
enum Attribute {
HttpStatusCodeAttribute,
HttpReasonPhraseAttribute,
@@ -98,6 +66,8 @@ public:
ResourceTypeAttribute, // internal
AutoDeleteReplyOnFinishAttribute,
ConnectionCacheExpiryTimeoutSecondsAttribute,
+ Http2CleartextAllowedAttribute,
+ UseCredentialsAttribute,
User = 1000,
UserMax = 32767
@@ -130,6 +100,9 @@ public:
DefaultTransferTimeoutConstant = 30000
};
+ static constexpr auto DefaultTransferTimeout =
+ std::chrono::milliseconds(DefaultTransferTimeoutConstant);
+
QNetworkRequest();
explicit QNetworkRequest(const QUrl &url);
QNetworkRequest(const QNetworkRequest &other);
@@ -137,7 +110,7 @@ public:
QNetworkRequest &operator=(QNetworkRequest &&other) noexcept { swap(other); return *this; }
QNetworkRequest &operator=(const QNetworkRequest &other);
- void swap(QNetworkRequest &other) noexcept { qSwap(d, other.d); }
+ void swap(QNetworkRequest &other) noexcept { d.swap(other.d); }
bool operator==(const QNetworkRequest &other) const;
inline bool operator!=(const QNetworkRequest &other) const
@@ -151,9 +124,15 @@ public:
void setHeader(KnownHeaders header, const QVariant &value);
// raw headers:
+#if QT_NETWORK_REMOVED_SINCE(6, 7)
bool hasRawHeader(const QByteArray &headerName) const;
+#endif
+ bool hasRawHeader(QAnyStringView headerName) const;
QList<QByteArray> rawHeaderList() const;
+#if QT_NETWORK_REMOVED_SINCE(6, 7)
QByteArray rawHeader(const QByteArray &headerName) const;
+#endif
+ QByteArray rawHeader(QAnyStringView headerName) const;
void setRawHeader(const QByteArray &headerName, const QByteArray &value);
// attributes
@@ -177,18 +156,26 @@ public:
QString peerVerifyName() const;
void setPeerVerifyName(const QString &peerName);
-#if QT_CONFIG(http) || defined(Q_CLANG_QDOC)
+#if QT_CONFIG(http)
+ QHttp1Configuration http1Configuration() const;
+ void setHttp1Configuration(const QHttp1Configuration &configuration);
+
QHttp2Configuration http2Configuration() const;
void setHttp2Configuration(const QHttp2Configuration &configuration);
qint64 decompressedSafetyCheckThreshold() const;
void setDecompressedSafetyCheckThreshold(qint64 threshold);
-#endif // QT_CONFIG(http) || defined(Q_CLANG_QDOC)
+#endif // QT_CONFIG(http)
-#if QT_CONFIG(http) || defined(Q_CLANG_QDOC) || defined (Q_OS_WASM)
+#if QT_CONFIG(http) || defined (Q_OS_WASM)
+ QT_NETWORK_INLINE_SINCE(6, 8)
int transferTimeout() const;
- void setTransferTimeout(int timeout = DefaultTransferTimeoutConstant);
-#endif // QT_CONFIG(http) || defined(Q_CLANG_QDOC) || defined (Q_OS_WASM)
+ QT_NETWORK_INLINE_SINCE(6, 8)
+ void setTransferTimeout(int timeout);
+
+ std::chrono::milliseconds transferTimeoutAsDuration() const;
+ void setTransferTimeout(std::chrono::milliseconds duration = DefaultTransferTimeout);
+#endif // QT_CONFIG(http) || defined (Q_OS_WASM)
private:
QSharedDataPointer<QNetworkRequestPrivate> d;
friend class QNetworkRequestPrivate;
@@ -196,9 +183,24 @@ private:
Q_DECLARE_SHARED(QNetworkRequest)
+#if QT_NETWORK_INLINE_IMPL_SINCE(6, 8)
+#if QT_CONFIG(http) || defined (Q_OS_WASM)
+int QNetworkRequest::transferTimeout() const
+{
+ return int(transferTimeoutAsDuration().count());
+}
+
+void QNetworkRequest::setTransferTimeout(int timeout)
+{
+ setTransferTimeout(std::chrono::milliseconds(timeout));
+}
+#endif // QT_CONFIG(http) || defined (Q_OS_WASM)
+#endif // INLINE_SINCE 6.8
+
QT_END_NAMESPACE
-Q_DECLARE_METATYPE(QNetworkRequest)
-Q_DECLARE_METATYPE(QNetworkRequest::RedirectPolicy)
+QT_DECL_METATYPE_EXTERN(QNetworkRequest, Q_NETWORK_EXPORT)
+QT_DECL_METATYPE_EXTERN_TAGGED(QNetworkRequest::RedirectPolicy,
+ QNetworkRequest__RedirectPolicy, Q_NETWORK_EXPORT)
#endif
diff --git a/src/network/access/qnetworkrequest_p.h b/src/network/access/qnetworkrequest_p.h
index 4132e506d4..48fcdcf1ed 100644
--- a/src/network/access/qnetworkrequest_p.h
+++ b/src/network/access/qnetworkrequest_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** 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.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QNETWORKREQUEST_P_H
#define QNETWORKREQUEST_P_H
@@ -76,7 +40,7 @@ public:
AttributesMap attributes;
QPointer<QObject> originatingObject;
- RawHeadersList::ConstIterator findRawHeader(const QByteArray &key) const;
+ RawHeadersList::ConstIterator findRawHeader(QAnyStringView key) const;
RawHeadersList allRawHeaders() const;
QList<QByteArray> rawHeadersKeys() const;
void setRawHeader(const QByteArray &key, const QByteArray &value);
diff --git a/src/network/access/qnetworkrequestfactory.cpp b/src/network/access/qnetworkrequestfactory.cpp
new file mode 100644
index 0000000000..87738d9086
--- /dev/null
+++ b/src/network/access/qnetworkrequestfactory.cpp
@@ -0,0 +1,727 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qnetworkrequestfactory.h"
+#include "qnetworkrequestfactory_p.h"
+
+#if QT_CONFIG(ssl)
+#include <QtNetwork/qsslconfiguration.h>
+#endif
+
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qmap.h>
+
+QT_BEGIN_NAMESPACE
+
+QT_DEFINE_QESDP_SPECIALIZATION_DTOR(QNetworkRequestFactoryPrivate)
+
+using namespace Qt::StringLiterals;
+
+Q_LOGGING_CATEGORY(lcQrequestfactory, "qt.network.access.request.factory")
+
+/*!
+ \class QNetworkRequestFactory
+ \since 6.7
+ \ingroup shared
+ \inmodule QtNetwork
+
+ \brief Convenience class for grouping remote server endpoints that share
+ common network request properties.
+
+ \preliminary
+
+ REST servers often have endpoints that require the same headers and other data.
+ Grouping such endpoints with a QNetworkRequestFactory makes it more
+ convenient to issue requests to these endpoints; only the typically
+ varying parts such as \e path and \e query parameters are provided
+ when creating a new request.
+
+ Basic usage steps of QNetworkRequestFactory are as follows:
+ \list
+ \li Instantiation
+ \li Setting the data common to all requests
+ \li Issuing requests
+ \endlist
+
+ An example of usage:
+
+ \snippet code/src_network_access_qnetworkrequestfactory.cpp 0
+*/
+
+/*!
+ Creates a new QNetworkRequestFactory object.
+ Use setBaseUrl() to set a valid base URL for the requests.
+
+ \sa QNetworkRequestFactory(const QUrl &baseUrl), setBaseUrl()
+*/
+
+QNetworkRequestFactory::QNetworkRequestFactory()
+ : d(new QNetworkRequestFactoryPrivate)
+{
+}
+
+/*!
+ Creates a new QNetworkRequestFactory object, initializing the base URL to
+ \a baseUrl. The base URL is used to populate subsequent network
+ requests.
+
+ If the URL contains a \e path component, it will be extracted and used
+ as a base path in subsequent network requests. This means that any
+ paths provided when requesting individual requests will be appended
+ to this base path, as illustrated below:
+
+ \snippet code/src_network_access_qnetworkrequestfactory.cpp 1
+ */
+QNetworkRequestFactory::QNetworkRequestFactory(const QUrl &baseUrl)
+ : d(new QNetworkRequestFactoryPrivate(baseUrl))
+{
+}
+
+/*!
+ Destroys this QNetworkRequestFactory object.
+ */
+QNetworkRequestFactory::~QNetworkRequestFactory()
+ = default;
+
+/*!
+ Creates a copy of \a other.
+ */
+QNetworkRequestFactory::QNetworkRequestFactory(const QNetworkRequestFactory &other)
+ = default;
+
+/*!
+ Creates a copy of \a other and returns a reference to this factory.
+ */
+QNetworkRequestFactory &QNetworkRequestFactory::operator=(const QNetworkRequestFactory &other)
+ = default;
+
+/*!
+ \fn QNetworkRequestFactory::QNetworkRequestFactory(QNetworkRequestFactory &&other) noexcept
+
+ Move-constructs the factory from \a other.
+
+ \note The moved-from object \a other is placed in a
+ partially-formed state, in which the only valid operations are
+ destruction and assignment of a new value.
+*/
+
+/*!
+ \fn QNetworkRequestFactory &QNetworkRequestFactory::operator=(QNetworkRequestFactory &&other) noexcept
+
+ Move-assigns \a other and returns a reference to this factory.
+
+ \note The moved-from object \a other is placed in a
+ partially-formed state, in which the only valid operations are
+ destruction and assignment of a new value.
+ */
+
+/*!
+ \fn void QNetworkRequestFactory::swap(QNetworkRequestFactory &other)
+
+ Swaps this factory with \a other. This operation is
+ very fast and never fails.
+ */
+
+/*!
+ Returns the base URL used for the individual requests.
+
+ The base URL may contain a path component. This path is used
+ as path "prefix" for the paths that are provided when generating
+ individual requests.
+
+ \sa setBaseUrl()
+ */
+QUrl QNetworkRequestFactory::baseUrl() const
+{
+ return d->baseUrl;
+}
+
+/*!
+ Sets the base URL used in individual requests to \a url.
+
+ \sa baseUrl()
+ */
+void QNetworkRequestFactory::setBaseUrl(const QUrl &url)
+{
+ if (d->baseUrl == url)
+ return;
+
+ d.detach();
+ d->baseUrl = url;
+}
+
+#if QT_CONFIG(ssl)
+/*!
+ Returns the SSL configuration set to this factory. The SSL configuration
+ is set to each individual request.
+
+ \sa setSslConfiguration()
+ */
+QSslConfiguration QNetworkRequestFactory::sslConfiguration() const
+{
+ return d->sslConfig;
+}
+
+/*!
+ Sets the SSL configuration to \a configuration.
+
+ \sa sslConfiguration()
+ */
+void QNetworkRequestFactory::setSslConfiguration(const QSslConfiguration &configuration)
+{
+ if (d->sslConfig == configuration)
+ return;
+
+ d.detach();
+ d->sslConfig = configuration;
+}
+#endif
+
+/*!
+ Returns a QNetworkRequest.
+
+ The returned request is filled with the data that this factory
+ has been configured with.
+
+ \sa createRequest(const QUrlQuery&), createRequest(const QString&, const QUrlQuery&)
+*/
+
+QNetworkRequest QNetworkRequestFactory::createRequest() const
+{
+ return d->newRequest(d->requestUrl());
+}
+
+/*!
+ Returns a QNetworkRequest.
+
+ The returned request's URL is formed by appending the provided \a path
+ to the baseUrl (which may itself have a path component).
+
+ \sa createRequest(const QString &, const QUrlQuery &), createRequest(), baseUrl()
+*/
+QNetworkRequest QNetworkRequestFactory::createRequest(const QString &path) const
+{
+ return d->newRequest(d->requestUrl(&path));
+}
+
+/*!
+ Returns a QNetworkRequest.
+
+ The returned request's URL is formed by appending the provided \a query
+ to the baseUrl.
+
+ \sa createRequest(const QString &, const QUrlQuery &), createRequest(), baseUrl()
+*/
+QNetworkRequest QNetworkRequestFactory::createRequest(const QUrlQuery &query) const
+{
+ return d->newRequest(d->requestUrl(nullptr, &query));
+}
+
+/*!
+ Returns a QNetworkRequest.
+
+ The returned requests URL is formed by appending the provided \a path
+ and \a query to the baseUrl (which may have a path component).
+
+ If the provided \a path contains query items, they will be combined
+ with the items in \a query.
+
+ \sa createRequest(const QUrlQuery&), createRequest(), baseUrl()
+ */
+QNetworkRequest QNetworkRequestFactory::createRequest(const QString &path, const QUrlQuery &query) const
+{
+ return d->newRequest(d->requestUrl(&path, &query));
+}
+
+/*!
+ Sets \a headers that are common to all requests.
+
+ These headers are added to individual requests' headers.
+ This is a convenience mechanism for setting headers that
+ repeat across requests.
+
+ \sa commonHeaders(), clearCommonHeaders(), createRequest()
+ */
+void QNetworkRequestFactory::setCommonHeaders(const QHttpHeaders &headers)
+{
+ d.detach();
+ d->headers = headers;
+}
+
+/*!
+ Returns the currently set headers.
+
+ \sa setCommonHeaders(), clearCommonHeaders()
+ */
+QHttpHeaders QNetworkRequestFactory::commonHeaders() const
+{
+ return d->headers;
+}
+
+/*!
+ Clears current headers.
+
+ \sa commonHeaders(), setCommonHeaders()
+*/
+void QNetworkRequestFactory::clearCommonHeaders()
+{
+ if (d->headers.isEmpty())
+ return;
+ d.detach();
+ d->headers.clear();
+}
+
+/*!
+ Returns the bearer token that has been set.
+
+ The bearer token, if present, is used to set the
+ \c {Authorization: Bearer my_token} header for requests. This is a common
+ authorization convention and is provided as an additional convenience.
+
+ The means to acquire the bearer token vary. Standard methods include \c OAuth2
+ and the service provider's website/dashboard. It is expected that the bearer
+ token changes over time. For example, when updated with a refresh token,
+ always setting the new token again ensures that subsequent requests have
+ the latest, valid token.
+
+ The presence of the bearer token does not impact the \l commonHeaders()
+ listing. If the \l commonHeaders() also lists \c Authorization header, it
+ will be overwritten.
+
+ \sa setBearerToken(), commonHeaders()
+ */
+QByteArray QNetworkRequestFactory::bearerToken() const
+{
+ return d->bearerToken;
+}
+
+/*!
+ Sets the bearer token to \a token.
+
+ \sa bearerToken(), clearBearerToken()
+*/
+void QNetworkRequestFactory::setBearerToken(const QByteArray &token)
+{
+ if (d->bearerToken == token)
+ return;
+
+ d.detach();
+ d->bearerToken = token;
+}
+
+/*!
+ Clears the bearer token.
+
+ \sa bearerToken()
+*/
+void QNetworkRequestFactory::clearBearerToken()
+{
+ if (d->bearerToken.isEmpty())
+ return;
+
+ d.detach();
+ d->bearerToken.clear();
+}
+
+/*!
+ Returns the username set to this factory.
+
+ \sa setUserName(), clearUserName(), password()
+*/
+QString QNetworkRequestFactory::userName() const
+{
+ return d->userName;
+}
+
+/*!
+ Sets the username of this factory to \a userName.
+
+ The username is set in the request URL when \l createRequest() is called.
+ The QRestAccessManager / QNetworkAccessManager will attempt to use
+ these credentials when the server indicates that authentication
+ is required.
+
+ \sa userName(), clearUserName(), password()
+*/
+void QNetworkRequestFactory::setUserName(const QString &userName)
+{
+ if (d->userName == userName)
+ return;
+ d.detach();
+ d->userName = userName;
+}
+
+/*!
+ Clears the username set to this factory.
+*/
+void QNetworkRequestFactory::clearUserName()
+{
+ if (d->userName.isEmpty())
+ return;
+ d.detach();
+ d->userName.clear();
+}
+
+/*!
+ Returns the password set to this factory.
+
+ \sa password(), clearPassword(), userName()
+*/
+QString QNetworkRequestFactory::password() const
+{
+ return d->password;
+}
+
+/*!
+ Sets the password of this factory to \a password.
+
+ The password is set in the request URL when \l createRequest() is called.
+ The QRestAccessManager / QNetworkAccessManager will attempt to use
+ these credentials when the server indicates that authentication
+ is required.
+
+ \sa password(), clearPassword(), userName()
+*/
+void QNetworkRequestFactory::setPassword(const QString &password)
+{
+ if (d->password == password)
+ return;
+ d.detach();
+ d->password = password;
+}
+
+/*!
+ Clears the password set to this factory.
+
+ \sa password(), setPassword(), userName()
+*/
+void QNetworkRequestFactory::clearPassword()
+{
+ if (d->password.isEmpty())
+ return;
+ d.detach();
+ d->password.clear();
+}
+
+/*!
+ Sets \a timeout used for transfers.
+
+ \sa transferTimeout(), QNetworkRequest::setTransferTimeout(),
+ QNetworkAccessManager::setTransferTimeout()
+*/
+void QNetworkRequestFactory::setTransferTimeout(std::chrono::milliseconds timeout)
+{
+ if (d->transferTimeout == timeout)
+ return;
+
+ d.detach();
+ d->transferTimeout = timeout;
+}
+
+/*!
+ Returns the timeout used for transfers.
+
+ \sa setTransferTimeout(), QNetworkRequest::transferTimeout(),
+ QNetworkAccessManager::transferTimeout()
+*/
+std::chrono::milliseconds QNetworkRequestFactory::transferTimeout() const
+{
+ return d->transferTimeout;
+}
+
+/*!
+ Returns query parameters that are added to individual requests' query
+ parameters. The query parameters are added to any potential query
+ parameters provided with the individual \l createRequest() calls.
+
+ Use cases for using repeating query parameters are server dependent,
+ but typical examples include language setting \c {?lang=en}, format
+ specification \c {?format=json}, API version specification
+ \c {?version=1.0} and API key authentication.
+
+ \sa setQueryParameters(), clearQueryParameters(), createRequest()
+*/
+QUrlQuery QNetworkRequestFactory::queryParameters() const
+{
+ return d->queryParameters;
+}
+
+/*!
+ Sets \a query parameters that are added to individual requests' query
+ parameters.
+
+ \sa queryParameters(), clearQueryParameters()
+ */
+void QNetworkRequestFactory::setQueryParameters(const QUrlQuery &query)
+{
+ if (d->queryParameters == query)
+ return;
+
+ d.detach();
+ d->queryParameters = query;
+}
+
+/*!
+ Clears the query parameters.
+
+ \sa queryParameters()
+*/
+void QNetworkRequestFactory::clearQueryParameters()
+{
+ if (d->queryParameters.isEmpty())
+ return;
+
+ d.detach();
+ d->queryParameters.clear();
+}
+
+/*!
+ \since 6.8
+
+ Sets the priority for any future requests created by this factory to
+ \a priority.
+
+ The default priority is \l QNetworkRequest::NormalPriority.
+
+ \sa priority(), QNetworkRequest::setPriority()
+*/
+void QNetworkRequestFactory::setPriority(QNetworkRequest::Priority priority)
+{
+ if (d->priority == priority)
+ return;
+ d.detach();
+ d->priority = priority;
+}
+
+/*!
+ \since 6.8
+
+ Returns the priority assigned to any future requests created by this
+ factory.
+
+ \sa setPriority(), QNetworkRequest::priority()
+*/
+QNetworkRequest::Priority QNetworkRequestFactory::priority() const
+{
+ return d->priority;
+}
+
+/*!
+ \since 6.8
+
+ Sets the value associated with \a attribute to \a value.
+ If the attribute is already set, the previous value is
+ replaced. The attributes are set to any future requests
+ created by this factory.
+
+ \sa attribute(), clearAttribute(), clearAttributes(),
+ QNetworkRequest::Attribute
+*/
+void QNetworkRequestFactory::setAttribute(QNetworkRequest::Attribute attribute,
+ const QVariant &value)
+{
+ if (attribute == QNetworkRequest::HttpStatusCodeAttribute
+ || attribute == QNetworkRequest::HttpReasonPhraseAttribute
+ || attribute == QNetworkRequest::RedirectionTargetAttribute
+ || attribute == QNetworkRequest::ConnectionEncryptedAttribute
+ || attribute == QNetworkRequest::SourceIsFromCacheAttribute
+ || attribute == QNetworkRequest::HttpPipeliningWasUsedAttribute
+ || attribute == QNetworkRequest::Http2WasUsedAttribute
+ || attribute == QNetworkRequest::OriginalContentLengthAttribute)
+ {
+ qCWarning(lcQrequestfactory, "%i is a reply-only attribute, ignoring.", attribute);
+ return;
+ }
+ d.detach();
+ d->attributes.insert(attribute, value);
+}
+
+/*!
+ \since 6.8
+
+ Returns the value associated with \a attribute. If the
+ attribute has not been set, returns a default-constructed \l QVariant.
+
+ \sa attribute(QNetworkRequest::Attribute, const QVariant &),
+ setAttribute(), clearAttributes(), QNetworkRequest::Attribute
+
+*/
+QVariant QNetworkRequestFactory::attribute(QNetworkRequest::Attribute attribute) const
+{
+ return d->attributes.value(attribute);
+}
+
+/*!
+ \since 6.8
+
+ Returns the value associated with \a attribute. If the
+ attribute has not been set, returns \a defaultValue.
+
+ \sa attribute(), setAttribute(), clearAttributes(),
+ QNetworkRequest::Attribute
+*/
+QVariant QNetworkRequestFactory::attribute(QNetworkRequest::Attribute attribute,
+ const QVariant &defaultValue) const
+{
+ return d->attributes.value(attribute, defaultValue);
+}
+
+/*!
+ \since 6.8
+
+ Clears \a attribute set to this factory.
+
+ \sa attribute(), setAttribute()
+*/
+void QNetworkRequestFactory::clearAttribute(QNetworkRequest::Attribute attribute)
+{
+ if (!d->attributes.contains(attribute))
+ return;
+ d.detach();
+ d->attributes.remove(attribute);
+}
+
+/*!
+ \since 6.8
+
+ Clears any attributes set to this factory.
+
+ \sa attribute(), setAttribute()
+*/
+void QNetworkRequestFactory::clearAttributes()
+{
+ if (d->attributes.isEmpty())
+ return;
+ d.detach();
+ d->attributes.clear();
+}
+
+QNetworkRequestFactoryPrivate::QNetworkRequestFactoryPrivate()
+ = default;
+
+QNetworkRequestFactoryPrivate::QNetworkRequestFactoryPrivate(const QUrl &baseUrl)
+ : baseUrl(baseUrl)
+{
+}
+
+QNetworkRequestFactoryPrivate::~QNetworkRequestFactoryPrivate()
+ = default;
+
+QNetworkRequest QNetworkRequestFactoryPrivate::newRequest(const QUrl &url) const
+{
+ QNetworkRequest request;
+ request.setUrl(url);
+#if QT_CONFIG(ssl)
+ if (!sslConfig.isNull())
+ request.setSslConfiguration(sslConfig);
+#endif
+ // Set the header entries to the request. Combine values as there
+ // may be multiple values per name. Note: this would not necessarily
+ // produce right result for 'Set-Cookie' header if it has multiple values,
+ // but since it is a purely server-side (response) header, not relevant here.
+ const auto headerNames = headers.toMultiMap().uniqueKeys(); // ### fixme: port QNR to QHH
+ for (const auto &name : headerNames)
+ request.setRawHeader(name, headers.combinedValue(name));
+
+ constexpr char Bearer[] = "Bearer ";
+ if (!bearerToken.isEmpty())
+ request.setRawHeader("Authorization"_ba, Bearer + bearerToken);
+
+ request.setTransferTimeout(transferTimeout);
+ request.setPriority(priority);
+
+ for (const auto &[attribute, value] : attributes.asKeyValueRange())
+ request.setAttribute(attribute, value);
+
+ return request;
+}
+
+QUrl QNetworkRequestFactoryPrivate::requestUrl(const QString *path,
+ const QUrlQuery *query) const
+{
+ const QUrl providedPath = path ? QUrl(*path) : QUrl{};
+ const QUrlQuery providedQuery = query ? *query : QUrlQuery();
+
+ if (!providedPath.scheme().isEmpty() || !providedPath.host().isEmpty()) {
+ qCWarning(lcQrequestfactory, "The provided path %ls may only contain path and query item "
+ "components, and other parts will be ignored. Set the baseUrl instead",
+ qUtf16Printable(providedPath.toDisplayString()));
+ }
+
+ QUrl resultUrl = baseUrl;
+ QUrlQuery resultQuery(providedQuery);
+ QString basePath = baseUrl.path();
+
+ resultUrl.setUserName(userName);
+ resultUrl.setPassword(password);
+
+ // Separate the path and query parameters components on the application-provided path
+ const QString requestPath{providedPath.path()};
+ const QUrlQuery pathQueryItems{providedPath};
+
+ if (!pathQueryItems.isEmpty()) {
+ // Add any query items provided as part of the path
+ const auto items = pathQueryItems.queryItems(QUrl::ComponentFormattingOption::FullyEncoded);
+ for (const auto &[key, value]: items)
+ resultQuery.addQueryItem(key, value);
+ }
+
+ if (!queryParameters.isEmpty()) {
+ // Add any query items set to this factory
+ const QList<std::pair<QString,QString>> items =
+ queryParameters.queryItems(QUrl::ComponentFormattingOption::FullyEncoded);
+ for (const auto &item: items)
+ resultQuery.addQueryItem(item.first, item.second);
+ }
+
+ if (!resultQuery.isEmpty())
+ resultUrl.setQuery(resultQuery);
+
+ if (requestPath.isEmpty())
+ return resultUrl;
+
+ // Ensure that the "base path" (the path that may be present
+ // in the baseUrl), and the request path are joined with one '/'
+ // If both have it, remove one, if neither has it, add one
+ if (basePath.endsWith(u'/') && requestPath.startsWith(u'/'))
+ basePath.chop(1);
+ else if (!requestPath.startsWith(u'/') && !basePath.endsWith(u'/'))
+ basePath.append(u'/');
+
+ resultUrl.setPath(basePath.append(requestPath));
+ return resultUrl;
+}
+
+#ifndef QT_NO_DEBUG_STREAM
+/*!
+ \fn QDebug QNetworkRequestFactory::operator<<(QDebug debug,
+ const QNetworkRequestFactory &factory)
+
+ Writes \a factory into \a debug stream.
+
+ \sa {Debugging Techniques}
+*/
+QDebug operator<<(QDebug debug, const QNetworkRequestFactory &factory)
+{
+ const QDebugStateSaver saver(debug);
+ debug.resetFormat().nospace();
+
+ debug << "QNetworkRequestFactory(baseUrl = " << factory.baseUrl()
+ << ", headers = " << factory.commonHeaders()
+ << ", queryParameters = " << factory.queryParameters().queryItems()
+ << ", bearerToken = " << (factory.bearerToken().isEmpty() ? "(empty)" : "(is set)")
+ << ", transferTimeout = " << factory.transferTimeout()
+ << ", userName = " << (factory.userName().isEmpty() ? "(empty)" : "(is set)")
+ << ", password = " << (factory.password().isEmpty() ? "(empty)" : "(is set)")
+#if QT_CONFIG(ssl)
+ << ", SSL configuration"
+ << (factory.sslConfiguration().isNull() ? " is not set (default)" : " is set")
+#else
+ << ", no SSL support"
+#endif
+ << ")";
+ return debug;
+}
+#endif // QT_NO_DEBUG_STREAM
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qnetworkrequestfactory.h b/src/network/access/qnetworkrequestfactory.h
new file mode 100644
index 0000000000..9d955a51e7
--- /dev/null
+++ b/src/network/access/qnetworkrequestfactory.h
@@ -0,0 +1,100 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QNETWORKREQUESTFACTORY_H
+#define QNETWORKREQUESTFACTORY_H
+
+#include <QtNetwork/qnetworkrequest.h>
+#include <QtNetwork/qhttpheaders.h>
+
+#include <QtCore/qcompare.h>
+#include <QtCore/qshareddata.h>
+#include <QtCore/qurlquery.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qvariant.h>
+
+#include <chrono>
+
+QT_BEGIN_NAMESPACE
+
+class QDebug;
+#if QT_CONFIG(ssl)
+class QSslConfiguration;
+#endif
+
+class QNetworkRequestFactoryPrivate;
+QT_DECLARE_QESDP_SPECIALIZATION_DTOR_WITH_EXPORT(QNetworkRequestFactoryPrivate, Q_NETWORK_EXPORT)
+
+class QT_TECH_PREVIEW_API QNetworkRequestFactory
+{
+public:
+ Q_NETWORK_EXPORT QNetworkRequestFactory();
+ Q_NETWORK_EXPORT explicit QNetworkRequestFactory(const QUrl &baseUrl);
+ Q_NETWORK_EXPORT ~QNetworkRequestFactory();
+
+ Q_NETWORK_EXPORT QNetworkRequestFactory(const QNetworkRequestFactory &other);
+ QNetworkRequestFactory(QNetworkRequestFactory &&other) noexcept = default;
+ Q_NETWORK_EXPORT QNetworkRequestFactory &operator=(const QNetworkRequestFactory &other);
+
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QNetworkRequestFactory)
+ void swap(QNetworkRequestFactory &other) noexcept { d.swap(other.d); }
+
+ Q_NETWORK_EXPORT QUrl baseUrl() const;
+ Q_NETWORK_EXPORT void setBaseUrl(const QUrl &url);
+
+#if QT_CONFIG(ssl)
+ Q_NETWORK_EXPORT QSslConfiguration sslConfiguration() const;
+ Q_NETWORK_EXPORT void setSslConfiguration(const QSslConfiguration &configuration);
+#endif
+
+ Q_NETWORK_EXPORT QNetworkRequest createRequest() const;
+ Q_NETWORK_EXPORT QNetworkRequest createRequest(const QUrlQuery &query) const;
+ Q_NETWORK_EXPORT QNetworkRequest createRequest(const QString &path) const;
+ Q_NETWORK_EXPORT QNetworkRequest createRequest(const QString &path, const QUrlQuery &query) const;
+
+ Q_NETWORK_EXPORT void setCommonHeaders(const QHttpHeaders &headers);
+ Q_NETWORK_EXPORT QHttpHeaders commonHeaders() const;
+ Q_NETWORK_EXPORT void clearCommonHeaders();
+
+ Q_NETWORK_EXPORT QByteArray bearerToken() const;
+ Q_NETWORK_EXPORT void setBearerToken(const QByteArray &token);
+ Q_NETWORK_EXPORT void clearBearerToken();
+
+ Q_NETWORK_EXPORT QString userName() const;
+ Q_NETWORK_EXPORT void setUserName(const QString &userName);
+ Q_NETWORK_EXPORT void clearUserName();
+
+ Q_NETWORK_EXPORT QString password() const;
+ Q_NETWORK_EXPORT void setPassword(const QString &password);
+ Q_NETWORK_EXPORT void clearPassword();
+
+ Q_NETWORK_EXPORT void setTransferTimeout(std::chrono::milliseconds timeout);
+ Q_NETWORK_EXPORT std::chrono::milliseconds transferTimeout() const;
+
+ Q_NETWORK_EXPORT QUrlQuery queryParameters() const;
+ Q_NETWORK_EXPORT void setQueryParameters(const QUrlQuery &query);
+ Q_NETWORK_EXPORT void clearQueryParameters();
+
+ Q_NETWORK_EXPORT void setPriority(QNetworkRequest::Priority priority);
+ Q_NETWORK_EXPORT QNetworkRequest::Priority priority() const;
+
+ Q_NETWORK_EXPORT QVariant attribute(QNetworkRequest::Attribute attribute) const;
+ Q_NETWORK_EXPORT QVariant attribute(QNetworkRequest::Attribute attribute,
+ const QVariant &defaultValue) const;
+ Q_NETWORK_EXPORT void setAttribute(QNetworkRequest::Attribute attribute, const QVariant &value);
+ Q_NETWORK_EXPORT void clearAttribute(QNetworkRequest::Attribute attribute);
+ Q_NETWORK_EXPORT void clearAttributes();
+
+private:
+#ifndef QT_NO_DEBUG_STREAM
+ friend Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QNetworkRequestFactory &reply);
+#endif
+
+ QExplicitlySharedDataPointer<QNetworkRequestFactoryPrivate> d;
+};
+
+Q_DECLARE_SHARED(QNetworkRequestFactory)
+
+QT_END_NAMESPACE
+
+#endif // QNETWORKREQUESTFACTORY_H
diff --git a/src/network/access/qnetworkrequestfactory_p.h b/src/network/access/qnetworkrequestfactory_p.h
new file mode 100644
index 0000000000..4116669f21
--- /dev/null
+++ b/src/network/access/qnetworkrequestfactory_p.h
@@ -0,0 +1,56 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QNETWORKREQUESTFACTORY_P_H
+#define QNETWORKREQUESTFACTORY_P_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 framework. This header file may change from
+// version to version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include <QtNetwork/qhttpheaders.h>
+#include <QtNetwork/qnetworkrequest.h>
+#if QT_CONFIG(ssl)
+#include <QtNetwork/qsslconfiguration.h>
+#endif
+#include <QtCore/qhash.h>
+#include <QtCore/qshareddata.h>
+#include <QtCore/qurl.h>
+#include <QtCore/qurlquery.h>
+#include <QtCore/qvariant.h>
+
+QT_BEGIN_NAMESPACE
+
+class QNetworkRequestFactoryPrivate : public QSharedData
+{
+public:
+ QNetworkRequestFactoryPrivate();
+ explicit QNetworkRequestFactoryPrivate(const QUrl &baseUrl);
+ ~QNetworkRequestFactoryPrivate();
+ QNetworkRequest newRequest(const QUrl &url) const;
+ QUrl requestUrl(const QString *path = nullptr, const QUrlQuery *query = nullptr) const;
+
+#if QT_CONFIG(ssl)
+ QSslConfiguration sslConfig;
+#endif
+ QUrl baseUrl;
+ QHttpHeaders headers;
+ QByteArray bearerToken;
+ QString userName;
+ QString password;
+ QUrlQuery queryParameters;
+ QNetworkRequest::Priority priority = QNetworkRequest::NormalPriority;
+ std::chrono::milliseconds transferTimeout{0};
+ QHash<QNetworkRequest::Attribute, QVariant> attributes;
+};
+
+QT_END_NAMESPACE
+
+#endif // QNETWORKREQUESTFACTORY_P_H
diff --git a/src/network/access/qrestaccessmanager.cpp b/src/network/access/qrestaccessmanager.cpp
new file mode 100644
index 0000000000..7ef682e955
--- /dev/null
+++ b/src/network/access/qrestaccessmanager.cpp
@@ -0,0 +1,828 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qrestaccessmanager.h"
+#include "qrestaccessmanager_p.h"
+#include "qrestreply.h"
+
+#include <QtNetwork/qhttpmultipart.h>
+#include <QtNetwork/qnetworkaccessmanager.h>
+#include <QtNetwork/qnetworkreply.h>
+
+#include <QtCore/qjsondocument.h>
+#include <QtCore/qjsonobject.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qthread.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+Q_LOGGING_CATEGORY(lcQrest, "qt.network.access.rest")
+
+/*!
+
+ \class QRestAccessManager
+ \brief The QRestAccessManager is a convenience wrapper for
+ QNetworkAccessManager.
+ \since 6.7
+
+ \ingroup network
+ \inmodule QtNetwork
+ \reentrant
+
+ \preliminary
+
+ QRestAccessManager is a convenience wrapper on top of
+ QNetworkAccessManager. It amends datatypes and HTTP methods
+ that are useful for typical RESTful client applications.
+
+ The usual Qt networking features are accessible by configuring the
+ wrapped QNetworkAccessManager directly. QRestAccessManager does not
+ take ownership of the wrapped QNetworkAccessManager.
+
+ QRestAccessManager and related QRestReply classes can only be used in the
+ thread they live in. See \l {QObject#Thread Affinity}{QObject thread affinity}
+ for more information.
+
+ \section1 Issuing Network Requests and Handling Replies
+
+ Network requests are initiated with a function call corresponding to
+ the desired HTTP method, such as \c get() and \c post().
+
+ \section2 Using Signals and Slots
+
+ The function returns a QNetworkReply* object, whose signals can be used
+ to follow up on the completion of the request in a traditional
+ Qt-signals-and-slots way.
+
+ Here's an example of how you could send a GET request and handle the
+ response:
+
+ \snippet code/src_network_access_qrestaccessmanager.cpp 0
+
+ \section2 Using Callbacks and Context Objects
+
+ The functions also take a context object of QObject (subclass) type
+ and a callback function as parameters. The callback takes one QRestReply&
+ as a parameter. The callback can be any callable, including a
+ pointer-to-member-function.
+
+ These callbacks are invoked when the reply has finished processing
+ (also in the case the processing finished due to an error).
+
+ The context object can be \c nullptr, although, generally speaking,
+ this is discouraged. Using a valid context object ensures that if the
+ context object is destroyed during request processing, the callback will
+ not be called. Stray callbacks which access a destroyed context is a source
+ of application misbehavior.
+
+ Here's an example of how you could send a GET request and check the
+ response:
+
+ \snippet code/src_network_access_qrestaccessmanager.cpp 1
+
+ Many of the functions take in data for sending to a server. The data is
+ supplied as the second parameter after the request.
+
+ Here's an example of how you could send a POST request and check the
+ response:
+
+ \snippet code/src_network_access_qrestaccessmanager.cpp 2
+
+ The provided QRestReply& is valid only while the callback
+ executes. If you need it for longer, you can either move it
+ to another QRestReply, or construct a new one and initialize
+ it with the QNetworkReply (see QRestReply::networkReply()).
+
+ \section2 Supported data types
+
+ The following table summarizes the methods and the supported data types.
+ \c X means support.
+
+ \table
+ \header
+ \li Data type
+ \li \c get()
+ \li \c post()
+ \li \c put()
+ \li \c head()
+ \li \c patch()
+ \li \c deleteResource()
+ \li \c sendCustomRequest()
+ \row
+ \li No data
+ \li X
+ \li -
+ \li -
+ \li X
+ \li -
+ \li X
+ \li -
+ \row
+ \li QByteArray
+ \li X
+ \li X
+ \li X
+ \li -
+ \li X
+ \li -
+ \li X
+ \row
+ \li QJsonDocument *)
+ \li X
+ \li X
+ \li X
+ \li -
+ \li X
+ \li -
+ \li -
+ \row
+ \li QVariantMap **)
+ \li -
+ \li X
+ \li X
+ \li -
+ \li X
+ \li -
+ \li -
+ \row
+ \li QHttpMultiPart
+ \li -
+ \li X
+ \li X
+ \li -
+ \li -
+ \li -
+ \li X
+ \row
+ \li QIODevice
+ \li X
+ \li X
+ \li X
+ \li -
+ \li X
+ \li -
+ \li X
+ \endtable
+
+ *) QJsonDocument is sent in \l QJsonDocument::Compact format,
+ and the \c Content-Type header is set to \c {application/json} if the
+ \c Content-Type header was not set
+
+ **) QVariantMap is converted to and treated as a QJsonObject
+
+ \sa QRestReply, QNetworkRequestFactory, QNetworkAccessManager
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::get(
+ const QNetworkRequest &request,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ Issues an \c {HTTP GET} based on \a request.
+
+ The optional \a callback and \a context object can be provided for
+ handling the request completion as illustrated below:
+
+ \snippet code/src_network_access_qrestaccessmanager.cpp 3
+
+ Alternatively the signals of the returned QNetworkReply* object can be
+ used. For further information see
+ \l {Issuing Network Requests and Handling Replies}.
+
+ \sa QRestReply
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::get(
+ const QNetworkRequest &request, const QByteArray &data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ Issues an \c {HTTP GET} based on \a request and provided \a data.
+
+ The optional \a callback and \a context object can be provided for
+ handling the request completion as illustrated below:
+
+ \snippet code/src_network_access_qrestaccessmanager.cpp 4
+
+ Alternatively the signals of the returned QNetworkReply* object can be
+ used. For further information see
+ \l {Issuing Network Requests and Handling Replies}.
+
+ \sa QRestReply
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::get(
+ const QNetworkRequest &request, const QJsonDocument &data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::get(
+ const QNetworkRequest &request, QIODevice *data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::post(
+ const QNetworkRequest &request, const QJsonDocument &data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ Issues an \c {HTTP POST} based on \a request.
+
+ The optional \a callback and \a context object can be provided for
+ handling the request completion as illustrated below:
+
+ \snippet code/src_network_access_qrestaccessmanager.cpp 5
+
+ Alternatively, the signals of the returned QNetworkReply* object can be
+ used. For further information see
+ \l {Issuing Network Requests and Handling Replies}.
+
+ The \c post() method always requires \a data parameter. The following
+ data types are supported:
+ \list
+ \li QByteArray
+ \li QJsonDocument *)
+ \li QVariantMap **)
+ \li QHttpMultiPart*
+ \li QIODevice*
+ \endlist
+
+ *) Sent in \l QJsonDocument::Compact format, and the
+ \c Content-Type header is set to \c {application/json} if the
+ \c Content-Type header was not set
+ **) QVariantMap is converted to and treated as a QJsonObject
+
+ \sa QRestReply
+*/
+
+/*!
+
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::post(
+ const QNetworkRequest &request, const QVariantMap &data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::post(
+ const QNetworkRequest &request, const QByteArray &data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::post(
+ const QNetworkRequest &request, QHttpMultiPart *data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::post(
+ const QNetworkRequest &request, QIODevice *data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::put(
+ const QNetworkRequest &request, const QJsonDocument &data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ Issues an \c {HTTP PUT} based on \a request.
+
+ The optional \a callback and \a context object can be provided for
+ handling the request completion as illustrated below:
+
+ \snippet code/src_network_access_qrestaccessmanager.cpp 6
+
+ Alternatively the signals of the returned QNetworkReply* object can be
+ used. For further information see
+ \l {Issuing Network Requests and Handling Replies}.
+
+ The \c put() method always requires \a data parameter. The following
+ data types are supported:
+ \list
+ \li QByteArray
+ \li QJsonDocument *)
+ \li QVariantMap **)
+ \li QHttpMultiPart*
+ \li QIODevice*
+ \endlist
+
+ *) Sent in \l QJsonDocument::Compact format, and the
+ \c Content-Type header is set to \c {application/json} if the
+ \c Content-Type header was not set
+ **) QVariantMap is converted to and treated as a QJsonObject
+
+ \sa QRestReply
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::put(
+ const QNetworkRequest &request, const QVariantMap &data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::put(
+ const QNetworkRequest &request, const QByteArray &data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::put(
+ const QNetworkRequest &request, QHttpMultiPart *data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::put(
+ const QNetworkRequest &request, QIODevice *data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::patch(
+ const QNetworkRequest &request, const QJsonDocument &data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ Issues an \c {HTTP PATCH} based on \a request.
+
+ The optional \a callback and \a context object can be provided for
+ handling the request completion as illustrated below:
+
+ \snippet code/src_network_access_qrestaccessmanager.cpp 10
+
+ Alternatively the signals of the returned QNetworkReply* object can be
+ used. For further information see
+ \l {Issuing Network Requests and Handling Replies}.
+
+ The \c patch() method always requires \a data parameter. The following
+ data types are supported:
+ \list
+ \li QByteArray
+ \li QJsonDocument *)
+ \li QVariantMap **)
+ \li QIODevice*
+ \endlist
+
+ *) Sent in \l QJsonDocument::Compact format, and the
+ \c Content-Type header is set to \c {application/json} if the
+ \c Content-Type header was not set
+ **) QVariantMap is converted to and treated as a QJsonObject
+
+ \sa QRestReply
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::patch(
+ const QNetworkRequest &request, const QVariantMap &data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::patch(
+ const QNetworkRequest &request, const QByteArray &data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::patch(
+ const QNetworkRequest &request, QIODevice *data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::head(
+ const QNetworkRequest &request,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ Issues an \c {HTTP HEAD} based on \a request.
+
+ The optional \a callback and \a context object can be provided for
+ handling the request completion as illustrated below:
+
+ \snippet code/src_network_access_qrestaccessmanager.cpp 7
+
+ Alternatively the signals of the returned QNetworkReply* object can be
+ used. For further information see
+ \l {Issuing Network Requests and Handling Replies}.
+
+ \c head() request does not support providing data.
+
+ \sa QRestReply
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::deleteResource(
+ const QNetworkRequest &request,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ Issues an \c {HTTP DELETE} based on \a request.
+
+ The optional \a callback and \a context object can be provided for
+ handling the request completion as illustrated below:
+
+ \snippet code/src_network_access_qrestaccessmanager.cpp 8
+
+ Alternatively the signals of the returned QNetworkReply* object can be
+ used. For further information see
+ \l {Issuing Network Requests and Handling Replies}.
+
+ \c deleteResource() request does not support providing data.
+
+ \sa QRestReply
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::sendCustomRequest(
+ const QNetworkRequest& request, const QByteArray &method, const QByteArray &data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ Issues \a request based HTTP request with custom \a method and the
+ provided \a data.
+
+ The optional \a callback and \a context object can be provided for
+ handling the request completion as illustrated below:
+
+ \snippet code/src_network_access_qrestaccessmanager.cpp 9
+
+ Alternatively the signals of the returned QNetworkReply* object can be
+ used. For further information see
+ \l {Issuing Network Requests and Handling Replies}.
+
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::sendCustomRequest(
+ const QNetworkRequest& request, const QByteArray &method, QIODevice *data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ \fn template<typename Functor, QRestAccessManager::if_compatible_callback<Functor>> QNetworkReply *QRestAccessManager::sendCustomRequest(
+ const QNetworkRequest& request, const QByteArray &method, QHttpMultiPart *data,
+ const ContextTypeForFunctor<Functor> *context,
+ Functor &&callback)
+
+ \overload
+*/
+
+/*!
+ Constructs a QRestAccessManager object and sets \a parent as the parent
+ object, and \a manager as the underlying QNetworkAccessManager which
+ is used for communication.
+
+ \sa networkAccessManager()
+*/
+
+QRestAccessManager::QRestAccessManager(QNetworkAccessManager *manager, QObject *parent)
+ : QObject(*new QRestAccessManagerPrivate, parent)
+{
+ Q_D(QRestAccessManager);
+ d->qnam = manager;
+ if (!d->qnam)
+ qCWarning(lcQrest, "QRestAccessManager: QNetworkAccesManager is nullptr");
+}
+
+/*!
+ Destroys the QRestAccessManager object.
+*/
+QRestAccessManager::~QRestAccessManager()
+ = default;
+
+/*!
+ Returns the underlying QNetworkAccessManager instance.
+
+ \sa QNetworkAccessManager
+*/
+QNetworkAccessManager *QRestAccessManager::networkAccessManager() const
+{
+ Q_D(const QRestAccessManager);
+ return d->qnam;
+}
+
+QRestAccessManagerPrivate::QRestAccessManagerPrivate()
+ = default;
+
+QRestAccessManagerPrivate::~QRestAccessManagerPrivate()
+{
+ if (!activeRequests.isEmpty()) {
+ qCWarning(lcQrest, "Access manager destroyed while %lld requests were still in progress",
+ qlonglong(activeRequests.size()));
+ }
+}
+
+QNetworkReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request,
+ const QJsonDocument &data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([](auto qnam, auto req, auto data) { return qnam->post(req, data); },
+ data, request, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request,
+ const QVariantMap &data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ return postWithDataImpl(request, QJsonDocument::fromVariant(data), context, slot);
+}
+
+QNetworkReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request,
+ const QByteArray &data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->post(request, data); }, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request,
+ QHttpMultiPart *data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->post(request, data); }, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::postWithDataImpl(const QNetworkRequest &request,
+ QIODevice *data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->post(request, data); }, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::getNoDataImpl(const QNetworkRequest &request,
+ const QObject *context, QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->get(request); }, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::getWithDataImpl(const QNetworkRequest &request,
+ const QByteArray &data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->get(request, data); }, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::getWithDataImpl(const QNetworkRequest &request,
+ const QJsonDocument &data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([](auto qnam, auto req, auto data) { return qnam->get(req, data); },
+ data, request, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::getWithDataImpl(const QNetworkRequest &request,
+ QIODevice *data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->get(request, data); }, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::deleteResourceNoDataImpl(const QNetworkRequest &request,
+ const QObject *context, QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->deleteResource(request); }, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::headNoDataImpl(const QNetworkRequest &request,
+ const QObject *context, QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->head(request); }, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request,
+ const QJsonDocument &data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([](auto qnam, auto req, auto data) { return qnam->put(req, data); },
+ data, request, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request,
+ const QVariantMap &data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ return putWithDataImpl(request, QJsonDocument::fromVariant(data), context, slot);
+}
+
+QNetworkReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request,
+ const QByteArray &data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->put(request, data); }, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request,
+ QHttpMultiPart *data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->put(request, data); }, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::putWithDataImpl(const QNetworkRequest &request, QIODevice *data,
+ const QObject *context, QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->put(request, data); }, context, slot);
+}
+
+static const auto PATCH = "PATCH"_ba;
+
+QNetworkReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request,
+ const QJsonDocument &data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest(
+ [](auto qnam, auto req, auto data) { return qnam->sendCustomRequest(req, PATCH, data); },
+ data, request, context, slot);
+}
+
+QNetworkReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request,
+ const QVariantMap &data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ return patchWithDataImpl(request, QJsonDocument::fromVariant(data), context, slot);
+}
+
+QNetworkReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request,
+ const QByteArray &data, const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->sendCustomRequest(request, PATCH, data); },
+ context, slot);
+}
+
+QNetworkReply *QRestAccessManager::patchWithDataImpl(const QNetworkRequest &request, QIODevice *data,
+ const QObject *context, QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->sendCustomRequest(request, PATCH, data); },
+ context, slot);
+}
+
+QNetworkReply *QRestAccessManager::customWithDataImpl(const QNetworkRequest &request,
+ const QByteArray& method, const QByteArray &data,
+ const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->sendCustomRequest(request, method, data); },
+ context, slot);
+}
+
+QNetworkReply *QRestAccessManager::customWithDataImpl(const QNetworkRequest &request,
+ const QByteArray& method, QIODevice *data,
+ const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->sendCustomRequest(request, method, data); },
+ context, slot);
+}
+
+QNetworkReply *QRestAccessManager::customWithDataImpl(const QNetworkRequest &request,
+ const QByteArray& method, QHttpMultiPart *data,
+ const QObject *context,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_D(QRestAccessManager);
+ return d->executeRequest([&](auto qnam) { return qnam->sendCustomRequest(request, method, data); },
+ context, slot);
+}
+
+QNetworkReply *QRestAccessManagerPrivate::createActiveRequest(QNetworkReply *reply,
+ const QObject *contextObject,
+ QtPrivate::QSlotObjectBase *slot)
+{
+ Q_Q(QRestAccessManager);
+ Q_ASSERT(reply);
+ QtPrivate::SlotObjSharedPtr slotPtr(QtPrivate::SlotObjUniquePtr{slot}); // adopts
+ activeRequests.insert(reply, CallerInfo{contextObject, slotPtr});
+ // The signal connections below are made to 'q' to avoid stray signal
+ // handling upon its destruction while requests were still in progress
+
+ QObject::connect(reply, &QNetworkReply::finished, q, [reply, this]() {
+ handleReplyFinished(reply);
+ });
+ // Safe guard in case reply is destroyed before it's finished
+ QObject::connect(reply, &QObject::destroyed, q, [reply, this]() {
+ activeRequests.remove(reply);
+ });
+ // If context object is destroyed, clean up any possible replies it had associated with it
+ if (contextObject) {
+ QObject::connect(contextObject, &QObject::destroyed, q, [reply, this]() {
+ activeRequests.remove(reply);
+ });
+ }
+ return reply;
+}
+
+void QRestAccessManagerPrivate::verifyThreadAffinity(const QObject *contextObject)
+{
+ Q_Q(QRestAccessManager);
+ if (QThread::currentThread() != q->thread()) {
+ qCWarning(lcQrest, "QRestAccessManager can only be called in the thread it belongs to");
+ Q_ASSERT(false);
+ }
+ if (contextObject && (contextObject->thread() != q->thread())) {
+ qCWarning(lcQrest, "QRestAccessManager: the context object must reside in the same thread");
+ Q_ASSERT(false);
+ }
+}
+
+QNetworkReply* QRestAccessManagerPrivate::warnNoAccessManager()
+{
+ qCWarning(lcQrest, "QRestAccessManager: QNetworkAccessManager not set");
+ return nullptr;
+}
+
+void QRestAccessManagerPrivate::handleReplyFinished(QNetworkReply *reply)
+{
+ auto request = activeRequests.find(reply);
+ if (request == activeRequests.end()) {
+ qCDebug(lcQrest, "QRestAccessManager: Unexpected reply received, ignoring");
+ return;
+ }
+
+ CallerInfo caller = request.value();
+ activeRequests.erase(request);
+
+ if (caller.slot) {
+ // Callback was provided
+ QRestReply restReply(reply);
+ void *argv[] = { nullptr, &restReply };
+ // If we have context object, use it
+ QObject *context = caller.contextObject
+ ? const_cast<QObject*>(caller.contextObject.get()) : nullptr;
+ caller.slot->call(context, argv);
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qrestaccessmanager.cpp"
diff --git a/src/network/access/qrestaccessmanager.h b/src/network/access/qrestaccessmanager.h
new file mode 100644
index 0000000000..3245b41785
--- /dev/null
+++ b/src/network/access/qrestaccessmanager.h
@@ -0,0 +1,127 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QRESTACCESSMANAGER_H
+#define QRESTACCESSMANAGER_H
+
+#if 0
+#pragma qt_class(QRestAccessManager)
+#endif
+
+#include <QtNetwork/qnetworkaccessmanager.h>
+
+QT_BEGIN_NAMESPACE
+
+class QDebug;
+class QRestReply;
+
+#define QREST_METHOD_WITH_DATA(METHOD, DATA) \
+public: \
+template <typename Functor, if_compatible_callback<Functor> = true> \
+QNetworkReply *METHOD(const QNetworkRequest &request, DATA data, \
+ const ContextTypeForFunctor<Functor> *context, \
+ Functor &&callback) \
+{ \
+ return METHOD##WithDataImpl(request, data, context, \
+ QtPrivate::makeCallableObject<CallbackPrototype>(std::forward<Functor>(callback))); \
+} \
+QNetworkReply *METHOD(const QNetworkRequest &request, DATA data) \
+{ \
+ return METHOD##WithDataImpl(request, data, nullptr, nullptr); \
+} \
+private: \
+QNetworkReply *METHOD##WithDataImpl(const QNetworkRequest &request, DATA data, \
+ const QObject *context, QtPrivate::QSlotObjectBase *slot); \
+/* end */
+
+#define QREST_METHOD_NO_DATA(METHOD) \
+public: \
+template <typename Functor, if_compatible_callback<Functor> = true> \
+QNetworkReply *METHOD(const QNetworkRequest &request, \
+ const ContextTypeForFunctor<Functor> *context, \
+ Functor &&callback) \
+{ \
+ return METHOD##NoDataImpl(request, context, \
+ QtPrivate::makeCallableObject<CallbackPrototype>(std::forward<Functor>(callback))); \
+} \
+QNetworkReply *METHOD(const QNetworkRequest &request) \
+{ \
+ return METHOD##NoDataImpl(request, nullptr, nullptr); \
+} \
+private: \
+QNetworkReply *METHOD##NoDataImpl(const QNetworkRequest &request, \
+ const QObject *context, QtPrivate::QSlotObjectBase *slot); \
+/* end */
+
+#define QREST_METHOD_CUSTOM_WITH_DATA(DATA) \
+public: \
+template <typename Functor, if_compatible_callback<Functor> = true> \
+QNetworkReply *sendCustomRequest(const QNetworkRequest& request, const QByteArray &method, DATA data, \
+ const ContextTypeForFunctor<Functor> *context, \
+ Functor &&callback) \
+{ \
+ return customWithDataImpl(request, method, data, context, \
+ QtPrivate::makeCallableObject<CallbackPrototype>(std::forward<Functor>(callback))); \
+} \
+QNetworkReply *sendCustomRequest(const QNetworkRequest& request, const QByteArray &method, DATA data) \
+{ \
+ return customWithDataImpl(request, method, data, nullptr, nullptr); \
+} \
+private: \
+QNetworkReply *customWithDataImpl(const QNetworkRequest& request, const QByteArray &method, \
+ DATA data, const QObject* context, \
+ QtPrivate::QSlotObjectBase *slot); \
+/* end */
+
+class QRestAccessManagerPrivate;
+class QT_TECH_PREVIEW_API Q_NETWORK_EXPORT QRestAccessManager : public QObject
+{
+ Q_OBJECT
+ using CallbackPrototype = void(*)(QRestReply&);
+ template <typename Functor>
+ using ContextTypeForFunctor = typename QtPrivate::ContextTypeForFunctor<Functor>::ContextType;
+ template <typename Functor>
+ using if_compatible_callback = std::enable_if_t<
+ QtPrivate::AreFunctionsCompatible<CallbackPrototype, Functor>::value, bool>;
+public:
+ explicit QRestAccessManager(QNetworkAccessManager *manager, QObject *parent = nullptr);
+ ~QRestAccessManager() override;
+
+ QNetworkAccessManager *networkAccessManager() const;
+
+ QREST_METHOD_NO_DATA(deleteResource)
+ QREST_METHOD_NO_DATA(head)
+ QREST_METHOD_NO_DATA(get)
+ QREST_METHOD_WITH_DATA(get, const QByteArray &)
+ QREST_METHOD_WITH_DATA(get, const QJsonDocument &)
+ QREST_METHOD_WITH_DATA(get, QIODevice *)
+ QREST_METHOD_WITH_DATA(post, const QJsonDocument &)
+ QREST_METHOD_WITH_DATA(post, const QVariantMap &)
+ QREST_METHOD_WITH_DATA(post, const QByteArray &)
+ QREST_METHOD_WITH_DATA(post, QHttpMultiPart *)
+ QREST_METHOD_WITH_DATA(post, QIODevice *)
+ QREST_METHOD_WITH_DATA(put, const QJsonDocument &)
+ QREST_METHOD_WITH_DATA(put, const QVariantMap &)
+ QREST_METHOD_WITH_DATA(put, const QByteArray &)
+ QREST_METHOD_WITH_DATA(put, QHttpMultiPart *)
+ QREST_METHOD_WITH_DATA(put, QIODevice *)
+ QREST_METHOD_WITH_DATA(patch, const QJsonDocument &)
+ QREST_METHOD_WITH_DATA(patch, const QVariantMap &)
+ QREST_METHOD_WITH_DATA(patch, const QByteArray &)
+ QREST_METHOD_WITH_DATA(patch, QIODevice *)
+ QREST_METHOD_CUSTOM_WITH_DATA(const QByteArray &)
+ QREST_METHOD_CUSTOM_WITH_DATA(QIODevice *)
+ QREST_METHOD_CUSTOM_WITH_DATA(QHttpMultiPart *)
+
+private:
+ Q_DECLARE_PRIVATE(QRestAccessManager)
+ Q_DISABLE_COPY(QRestAccessManager)
+};
+
+#undef QREST_METHOD_NO_DATA
+#undef QREST_METHOD_WITH_DATA
+#undef QREST_METHOD_CUSTOM_WITH_DATA
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/access/qrestaccessmanager_p.h b/src/network/access/qrestaccessmanager_p.h
new file mode 100644
index 0000000000..51af299f5c
--- /dev/null
+++ b/src/network/access/qrestaccessmanager_p.h
@@ -0,0 +1,89 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QRESTACCESSMANAGER_P_H
+#define QRESTACCESSMANAGER_P_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 "qrestaccessmanager.h"
+#include "private/qobject_p.h"
+
+#include <QtNetwork/qnetworkaccessmanager.h>
+
+#include <QtCore/qjsonarray.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qjsondocument.h>
+#include <QtCore/qjsonobject.h>
+#include <QtCore/qxpfunctional.h>
+
+QT_BEGIN_NAMESPACE
+
+class QRestReply;
+class QRestAccessManagerPrivate : public QObjectPrivate
+{
+public:
+ QRestAccessManagerPrivate();
+ ~QRestAccessManagerPrivate() override;
+
+ QNetworkReply* createActiveRequest(QNetworkReply *reply, const QObject *contextObject,
+ QtPrivate::QSlotObjectBase *slot);
+ void handleReplyFinished(QNetworkReply *reply);
+
+ using ReqOpRef = qxp::function_ref<QNetworkReply*(QNetworkAccessManager*) const>;
+ QNetworkReply *executeRequest(ReqOpRef requestOperation,
+ const QObject *context, QtPrivate::QSlotObjectBase *slot)
+ {
+ if (!qnam)
+ return warnNoAccessManager();
+ verifyThreadAffinity(context);
+ QNetworkReply *reply = requestOperation(qnam);
+ return createActiveRequest(reply, context, slot);
+ }
+
+ using ReqOpRefJson = qxp::function_ref<QNetworkReply*(QNetworkAccessManager*,
+ const QNetworkRequest &,
+ const QByteArray &) const>;
+ QNetworkReply *executeRequest(ReqOpRefJson requestOperation, const QJsonDocument &jsonDoc,
+ const QNetworkRequest &request,
+ const QObject *context, QtPrivate::QSlotObjectBase *slot)
+ {
+ if (!qnam)
+ return warnNoAccessManager();
+ verifyThreadAffinity(context);
+ QNetworkRequest req(request);
+ if (!request.header(QNetworkRequest::ContentTypeHeader).isValid()) {
+ req.setHeader(QNetworkRequest::ContentTypeHeader,
+ QLatin1StringView{"application/json"});
+ }
+ QNetworkReply *reply = requestOperation(qnam, req, jsonDoc.toJson(QJsonDocument::Compact));
+ return createActiveRequest(reply, context, slot);
+ }
+
+ void verifyThreadAffinity(const QObject *contextObject);
+ Q_DECL_COLD_FUNCTION
+ QNetworkReply* warnNoAccessManager();
+
+ struct CallerInfo {
+ QPointer<const QObject> contextObject = nullptr;
+ QtPrivate::SlotObjSharedPtr slot;
+ };
+ QHash<QNetworkReply*, CallerInfo> activeRequests;
+
+ QNetworkAccessManager *qnam = nullptr;
+ bool deletesRepliesOnFinished = true;
+ Q_DECLARE_PUBLIC(QRestAccessManager)
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/access/qrestreply.cpp b/src/network/access/qrestreply.cpp
new file mode 100644
index 0000000000..155e5877fd
--- /dev/null
+++ b/src/network/access/qrestreply.cpp
@@ -0,0 +1,560 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qrestreply.h"
+#include "qrestreply_p.h"
+
+#include <QtNetwork/private/qnetworkreply_p.h>
+
+#include <QtCore/qbytearrayview.h>
+#include <QtCore/qjsondocument.h>
+#include <QtCore/qlatin1stringmatcher.h>
+#include <QtCore/qlatin1stringview.h>
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qstringconverter.h>
+
+#include <QtCore/qxpfunctional.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+Q_DECLARE_LOGGING_CATEGORY(lcQrest)
+
+/*!
+ \class QRestReply
+ \since 6.7
+ \brief QRestReply is a convenience wrapper for QNetworkReply.
+
+ \reentrant
+ \ingroup network
+ \inmodule QtNetwork
+
+ \preliminary
+
+ QRestReply wraps a QNetworkReply and provides convenience methods for data
+ and status handling. The methods provide convenience for typical RESTful
+ client applications.
+
+ QRestReply doesn't take ownership of the wrapped QNetworkReply, and the
+ lifetime and ownership of the reply is as defined by QNetworkAccessManager
+ documentation.
+
+ QRestReply object is not copyable, but is movable.
+
+ \sa QRestAccessManager, QNetworkReply, QNetworkAccessManager,
+ QNetworkAccessManager::setAutoDeleteReplies()
+*/
+
+/*!
+ Creates a QRestReply and initializes the wrapped QNetworkReply to \a reply.
+*/
+QRestReply::QRestReply(QNetworkReply *reply)
+ : wrapped(reply)
+{
+ if (!wrapped)
+ qCWarning(lcQrest, "QRestReply: QNetworkReply is nullptr");
+}
+
+/*!
+ Destroys this QRestReply object.
+*/
+QRestReply::~QRestReply()
+{
+ delete d;
+}
+
+/*!
+ \fn QRestReply::QRestReply(QRestReply &&other) noexcept
+
+ Move-constructs the reply from \a other.
+
+ \note The moved-from object \a other is placed in a
+ partially-formed state, in which the only valid operations are
+ destruction and assignment of a new value.
+*/
+
+/*!
+ \fn QRestReply &QRestReply::operator=(QRestReply &&other) noexcept
+
+ Move-assigns \a other and returns a reference to this reply.
+
+ \note The moved-from object \a other is placed in a
+ partially-formed state, in which the only valid operations are
+ destruction and assignment of a new value.
+*/
+
+/*!
+ Returns a pointer to the underlying QNetworkReply wrapped by this object.
+*/
+QNetworkReply *QRestReply::networkReply() const
+{
+ return wrapped;
+}
+
+/*!
+ Returns the received data as a QJsonDocument.
+
+ The returned value is wrapped in \c std::optional. If the conversion
+ from the received data fails (empty data or JSON parsing error),
+ \c std::nullopt is returned, and \a error is filled with details.
+
+ Calling this function consumes the received data, and any further calls
+ to get response data will return empty.
+
+ This function returns \c {std::nullopt} and will not consume
+ any data if the reply is not finished. If \a error is passed, it will be
+ set to QJsonParseError::NoError to distinguish this case from an actual
+ error.
+
+ \sa readBody(), readText()
+*/
+std::optional<QJsonDocument> QRestReply::readJson(QJsonParseError *error)
+{
+ if (!wrapped) {
+ if (error)
+ *error = {0, QJsonParseError::ParseError::NoError};
+ return std::nullopt;
+ }
+
+ if (!wrapped->isFinished()) {
+ qCWarning(lcQrest, "readJson() called on an unfinished reply, ignoring");
+ if (error)
+ *error = {0, QJsonParseError::ParseError::NoError};
+ return std::nullopt;
+ }
+ QJsonParseError parseError;
+ const QByteArray data = wrapped->readAll();
+ const QJsonDocument doc = QJsonDocument::fromJson(data, &parseError);
+ if (error)
+ *error = parseError;
+ if (parseError.error)
+ return std::nullopt;
+ return doc;
+}
+
+/*!
+ Returns the received data as a QByteArray.
+
+ Calling this function consumes the data received so far, and any further
+ calls to get response data will return empty until further data has been
+ received.
+
+ \sa readJson(), readText(), QNetworkReply::bytesAvailable(),
+ QNetworkReply::readyRead()
+*/
+QByteArray QRestReply::readBody()
+{
+ return wrapped ? wrapped->readAll() : QByteArray{};
+}
+
+/*!
+ Returns the received data as a QString.
+
+ The received data is decoded into a QString (UTF-16). If available, the decoding
+ uses the \e Content-Type header's \e charset parameter to determine the
+ source encoding. If the encoding information is not available or not supported
+ by \l QStringConverter, UTF-8 is used by default.
+
+ Calling this function consumes the data received so far. Returns
+ a default constructed value if no new data is available, or if the
+ decoding is not supported by \l QStringConverter, or if the decoding
+ has errors (for example invalid characters).
+
+ \sa readJson(), readBody(), QNetworkReply::readyRead()
+*/
+QString QRestReply::readText()
+{
+ QString result;
+ if (!wrapped)
+ return result;
+
+ QByteArray data = wrapped->readAll();
+ if (data.isEmpty())
+ return result;
+
+ // Text decoding needs to persist decoding state across calls to this function,
+ // so allocate decoder if not yet allocated.
+ if (!d)
+ d = new QRestReplyPrivate;
+
+ if (!d->decoder) {
+ const QByteArray charset = QRestReplyPrivate::contentCharset(wrapped);
+ d->decoder.emplace(charset.constData());
+ if (!d->decoder->isValid()) { // the decoder may not support the mimetype's charset
+ qCWarning(lcQrest, "readText(): Charset \"%s\" is not supported", charset.constData());
+ return result;
+ }
+ }
+ // Check if the decoder already had an error, or has errors after decoding current data chunk
+ if (d->decoder->hasError() || (result = (*d->decoder)(data), d->decoder->hasError())) {
+ qCWarning(lcQrest, "readText(): Decoding error occurred");
+ return {};
+ }
+ return result;
+}
+
+/*!
+ Returns the HTTP status received in the server response.
+ The value is \e 0 if not available (the status line has not been received,
+ yet).
+
+ \note The HTTP status is reported as indicated by the received HTTP
+ response. An error() may occur after receiving the status, for instance
+ due to network disconnection while receiving a long response.
+ These potential subsequent errors are not represented by the reported
+ HTTP status.
+
+ \sa isSuccess(), hasError(), error()
+*/
+int QRestReply::httpStatus() const
+{
+ return wrapped ? wrapped->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() : 0;
+}
+
+/*!
+ \fn bool QRestReply::isSuccess() const
+
+ Returns whether the HTTP status is between 200..299 and no
+ further errors have occurred while receiving the response (for example,
+ abrupt disconnection while receiving the body data). This function
+ is a convenient way to check whether the response is considered successful.
+
+ \sa httpStatus(), hasError(), error()
+*/
+
+/*!
+ Returns whether the HTTP status is between 200..299.
+
+ \sa isSuccess(), httpStatus(), hasError(), error()
+*/
+bool QRestReply::isHttpStatusSuccess() const
+{
+ const int status = httpStatus();
+ return status >= 200 && status < 300;
+}
+
+/*!
+ Returns whether an error has occurred. This includes errors such as
+ network and protocol errors, but excludes cases where the server
+ successfully responded with an HTTP error status (for example
+ \c {500 Internal Server Error}). Use \l httpStatus() or
+ \l isHttpStatusSuccess() to get the HTTP status information.
+
+ \sa httpStatus(), isSuccess(), error(), errorString()
+*/
+bool QRestReply::hasError() const
+{
+ if (!wrapped)
+ return false;
+
+ const int status = httpStatus();
+ if (status > 0) {
+ // The HTTP status is set upon receiving the response headers, but the
+ // connection might still fail later while receiving the body data.
+ return wrapped->error() == QNetworkReply::RemoteHostClosedError;
+ }
+ return wrapped->error() != QNetworkReply::NoError;
+}
+
+/*!
+ Returns the last error, if any. The errors include
+ errors such as network and protocol errors, but exclude
+ cases when the server successfully responded with an HTTP status.
+
+ \sa httpStatus(), isSuccess(), hasError(), errorString()
+*/
+QNetworkReply::NetworkError QRestReply::error() const
+{
+ if (!hasError())
+ return QNetworkReply::NetworkError::NoError;
+ return wrapped->error();
+}
+
+/*!
+ Returns a human-readable description of the last network error.
+
+ \sa httpStatus(), isSuccess(), hasError(), error()
+*/
+QString QRestReply::errorString() const
+{
+ if (hasError())
+ return wrapped->errorString();
+ return {};
+}
+
+QRestReplyPrivate::QRestReplyPrivate()
+ = default;
+
+QRestReplyPrivate::~QRestReplyPrivate()
+ = default;
+
+#ifndef QT_NO_DEBUG_STREAM
+static QLatin1StringView operationName(QNetworkAccessManager::Operation operation)
+{
+ switch (operation) {
+ case QNetworkAccessManager::Operation::GetOperation:
+ return "GET"_L1;
+ case QNetworkAccessManager::Operation::HeadOperation:
+ return "HEAD"_L1;
+ case QNetworkAccessManager::Operation::PostOperation:
+ return "POST"_L1;
+ case QNetworkAccessManager::Operation::PutOperation:
+ return "PUT"_L1;
+ case QNetworkAccessManager::Operation::DeleteOperation:
+ return "DELETE"_L1;
+ case QNetworkAccessManager::Operation::CustomOperation:
+ return "CUSTOM"_L1;
+ case QNetworkAccessManager::Operation::UnknownOperation:
+ return "UNKNOWN"_L1;
+ }
+ Q_UNREACHABLE_RETURN({});
+}
+
+/*!
+ \fn QDebug QRestReply::operator<<(QDebug debug, const QRestReply &reply)
+
+ Writes the \a reply into the \a debug object for debugging purposes.
+
+ \sa {Debugging Techniques}
+*/
+QDebug operator<<(QDebug debug, const QRestReply &reply)
+{
+ const QDebugStateSaver saver(debug);
+ debug.resetFormat().nospace();
+ if (!reply.networkReply()) {
+ debug << "QRestReply(no network reply)";
+ return debug;
+ }
+ debug << "QRestReply(isSuccess = " << reply.isSuccess()
+ << ", httpStatus = " << reply.httpStatus()
+ << ", isHttpStatusSuccess = " << reply.isHttpStatusSuccess()
+ << ", hasError = " << reply.hasError()
+ << ", errorString = " << reply.errorString()
+ << ", error = " << reply.error()
+ << ", isFinished = " << reply.networkReply()->isFinished()
+ << ", bytesAvailable = " << reply.networkReply()->bytesAvailable()
+ << ", url " << reply.networkReply()->url()
+ << ", operation = " << operationName(reply.networkReply()->operation())
+ << ", reply headers = " << reply.networkReply()->rawHeaderPairs()
+ << ")";
+ return debug;
+}
+#endif // QT_NO_DEBUG_STREAM
+
+static constexpr auto parse_OWS(QByteArrayView data) noexcept
+{
+ struct R {
+ QByteArrayView ows, tail;
+ };
+
+ constexpr auto is_OWS_char = [](auto ch) { return ch == ' ' || ch == '\t'; };
+
+ qsizetype i = 0;
+ while (i < data.size() && is_OWS_char(data[i]))
+ ++i;
+
+ return R{data.first(i), data.sliced(i)};
+}
+
+static constexpr void eat_OWS(QByteArrayView &data) noexcept
+{
+ data = parse_OWS(data).tail;
+}
+
+static constexpr auto parse_quoted_string(QByteArrayView data, qxp::function_ref<void(char) const> yield)
+{
+ struct R {
+ QByteArrayView quotedString, tail;
+ constexpr explicit operator bool() const noexcept { return !quotedString.isEmpty(); }
+ };
+
+ if (!data.startsWith('"'))
+ return R{{}, data};
+
+ qsizetype i = 1; // one past initial DQUOTE
+ while (i < data.size()) {
+ switch (auto ch = data[i++]) {
+ case '"': // final DQUOTE -> end of string
+ return R{data.first(i), data.sliced(i)};
+ case '\\': // quoted-pair
+ // https://www.rfc-editor.org/rfc/rfc9110.html#section-5.6.4-3:
+ // Recipients that process the value of a quoted-string MUST handle a
+ // quoted-pair as if it were replaced by the octet following the backslash.
+ if (i == data.size())
+ break; // premature end
+ ch = data[i++]; // eat '\\'
+ [[fallthrough]];
+ default:
+ // we don't validate quoted-string octets to be only qdtext (Postel's Law)
+ yield(ch);
+ }
+ }
+
+ return R{{}, data}; // premature end
+}
+
+static constexpr bool is_tchar(char ch) noexcept
+{
+ // ### optimize
+ switch (ch) {
+ case '!':
+ case '#':
+ case '$':
+ case '%':
+ case '&':
+ case '\'':
+ case '*':
+ case '+':
+ case '-':
+ case '.':
+ case '^':
+ case '_':
+ case '`':
+ case '|':
+ case '~':
+ return true;
+ default:
+ return (ch >= 'a' && ch <= 'z')
+ || (ch >= '0' && ch <= '9')
+ || (ch >= 'A' && ch <= 'Z');
+ }
+}
+
+static constexpr auto parse_token(QByteArrayView data) noexcept
+{
+ struct R {
+ QByteArrayView token, tail;
+ constexpr explicit operator bool() const noexcept { return !token.isEmpty(); }
+ };
+
+ qsizetype i = 0;
+ while (i < data.size() && is_tchar(data[i]))
+ ++i;
+
+ return R{data.first(i), data.sliced(i)};
+}
+
+static constexpr auto parse_parameter(QByteArrayView data, qxp::function_ref<void(char) const> yield)
+{
+ struct R {
+ QLatin1StringView name; QByteArrayView value; QByteArrayView tail;
+ constexpr explicit operator bool() const noexcept { return !name.isEmpty(); }
+ };
+
+ const auto invalid = R{{}, {}, data}; // preserves original `data`
+
+ // parameter = parameter-name "=" parameter-value
+ // parameter-name = token
+ // parameter-value = ( token / quoted-string )
+
+ const auto name = parse_token(data);
+ if (!name)
+ return invalid;
+ data = name.tail;
+
+ eat_OWS(data); // not in the grammar, but accepted under Postel's Law
+
+ if (!data.startsWith('='))
+ return invalid;
+ data = data.sliced(1);
+
+ eat_OWS(data); // not in the grammar, but accepted under Postel's Law
+
+ if (Q_UNLIKELY(data.startsWith('"'))) { // value is a quoted-string
+
+ const auto value = parse_quoted_string(data, yield);
+ if (!value)
+ return invalid;
+ data = value.tail;
+
+ return R{QLatin1StringView{name.token}, value.quotedString, data};
+
+ } else { // value is a token
+
+ const auto value = parse_token(data);
+ if (!value)
+ return invalid;
+ data = value.tail;
+
+ return R{QLatin1StringView{name.token}, value.token, data};
+ }
+}
+
+static auto parse_content_type(QByteArrayView data)
+{
+ struct R {
+ QLatin1StringView type, subtype;
+ std::string charset;
+ constexpr explicit operator bool() const noexcept { return !type.isEmpty(); }
+ };
+
+ eat_OWS(data); // not in the grammar, but accepted under Postel's Law
+
+ const auto type = parse_token(data);
+ if (!type)
+ return R{};
+ data = type.tail;
+
+ eat_OWS(data); // not in the grammar, but accepted under Postel's Law
+
+ if (!data.startsWith('/'))
+ return R{};
+ data = data.sliced(1);
+
+ eat_OWS(data); // not in the grammar, but accepted under Postel's Law
+
+ const auto subtype = parse_token(data);
+ if (!subtype)
+ return R{};
+ data = subtype.tail;
+
+ eat_OWS(data);
+
+ auto r = R{QLatin1StringView{type.token}, QLatin1StringView{subtype.token}, {}};
+
+ while (data.startsWith(';')) {
+
+ data = data.sliced(1); // eat ';'
+
+ eat_OWS(data);
+
+ const auto param = parse_parameter(data, [&](char ch) { r.charset.append(1, ch); });
+ if (param.name.compare("charset"_L1, Qt::CaseInsensitive) == 0) {
+ if (r.charset.empty() && !param.value.startsWith('"')) // wasn't a quoted-string
+ r.charset.assign(param.value.begin(), param.value.end());
+ return r; // charset found
+ }
+ r.charset.clear(); // wasn't an actual charset
+ if (param.tail.size() == data.size()) // no progress was made
+ break; // returns {type, subtype}
+ // otherwise, continue (accepting e.g. `;;`)
+ data = param.tail;
+
+ eat_OWS(data);
+ }
+
+ return r; // no charset found
+}
+
+QByteArray QRestReplyPrivate::contentCharset(const QNetworkReply* reply)
+{
+ // Content-type consists of mimetype and optional parameters, of which one may be 'charset'
+ // Example values and their combinations below are all valid, see RFC 7231 section 3.1.1.5
+ // and RFC 2045 section 5.1
+ //
+ // text/plain; charset=utf-8
+ // text/plain; charset=utf-8;version=1.7
+ // text/plain; charset = utf-8
+ // text/plain; charset ="utf-8"
+
+ const QByteArray contentTypeValue =
+ reply->header(QNetworkRequest::KnownHeaders::ContentTypeHeader).toByteArray();
+
+ const auto r = parse_content_type(contentTypeValue);
+ if (r && !r.charset.empty())
+ return QByteArrayView(r.charset).toByteArray();
+ else
+ return "UTF-8"_ba; // Default to the most commonly used UTF-8.
+}
+
+QT_END_NAMESPACE
diff --git a/src/network/access/qrestreply.h b/src/network/access/qrestreply.h
new file mode 100644
index 0000000000..c32fff1d4e
--- /dev/null
+++ b/src/network/access/qrestreply.h
@@ -0,0 +1,71 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QRESTREPLY_H
+#define QRESTREPLY_H
+
+#include <QtNetwork/qnetworkreply.h>
+
+#include <QtCore/qpointer.h>
+
+#include <optional>
+#include <utility>
+
+QT_BEGIN_NAMESPACE
+
+class QByteArray;
+class QDebug;
+struct QJsonParseError;
+class QJsonDocument;
+class QString;
+
+class QRestReplyPrivate;
+class QT_TECH_PREVIEW_API QRestReply
+{
+public:
+ Q_NETWORK_EXPORT explicit QRestReply(QNetworkReply *reply);
+ Q_NETWORK_EXPORT ~QRestReply();
+
+ QRestReply(QRestReply &&other) noexcept
+ : wrapped(std::move(other.wrapped)),
+ d(std::exchange(other.d, nullptr))
+ {
+ }
+ QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QRestReply)
+ void swap(QRestReply &other) noexcept
+ {
+ wrapped.swap(other.wrapped);
+ qt_ptr_swap(d, other.d);
+ }
+
+ Q_NETWORK_EXPORT QNetworkReply *networkReply() const;
+
+ Q_NETWORK_EXPORT std::optional<QJsonDocument> readJson(QJsonParseError *error = nullptr);
+ Q_NETWORK_EXPORT QByteArray readBody();
+ Q_NETWORK_EXPORT QString readText();
+
+ bool isSuccess() const
+ {
+ return !hasError() && isHttpStatusSuccess();
+ }
+ Q_NETWORK_EXPORT int httpStatus() const;
+ Q_NETWORK_EXPORT bool isHttpStatusSuccess() const;
+
+ Q_NETWORK_EXPORT bool hasError() const;
+ Q_NETWORK_EXPORT QNetworkReply::NetworkError error() const;
+ Q_NETWORK_EXPORT QString errorString() const;
+
+private:
+#ifndef QT_NO_DEBUG_STREAM
+ friend Q_NETWORK_EXPORT QDebug operator<<(QDebug debug, const QRestReply &reply);
+#endif
+ QPointer<QNetworkReply> wrapped;
+ QRestReplyPrivate *d = nullptr;
+ Q_DISABLE_COPY(QRestReply)
+};
+
+Q_DECLARE_SHARED(QRestReply)
+
+QT_END_NAMESPACE
+
+#endif // QRESTREPLY_H
diff --git a/src/network/access/qrestreply_p.h b/src/network/access/qrestreply_p.h
new file mode 100644
index 0000000000..ec963cf168
--- /dev/null
+++ b/src/network/access/qrestreply_p.h
@@ -0,0 +1,40 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QRESTREPLY_P_H
+#define QRESTREPLY_P_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 <QtCore/private/qstringconverter_p.h>
+
+#include <optional>
+
+QT_BEGIN_NAMESPACE
+
+class QByteArray;
+class QNetworkReply;
+
+class QRestReplyPrivate
+{
+public:
+ QRestReplyPrivate();
+ ~QRestReplyPrivate();
+
+ std::optional<QStringDecoder> decoder;
+
+ static QByteArray contentCharset(const QNetworkReply *reply);
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/network/access/qsocketabstraction_p.h b/src/network/access/qsocketabstraction_p.h
new file mode 100644
index 0000000000..2b40b80244
--- /dev/null
+++ b/src/network/access/qsocketabstraction_p.h
@@ -0,0 +1,91 @@
+// Copyright (C) 2024 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QSOCKETABSTRACTION_P_H
+#define QSOCKETABSTRACTION_P_H
+
+#include <private/qtnetworkglobal_p.h>
+
+#include <QtNetwork/qabstractsocket.h>
+#include <QtNetwork/qlocalsocket.h>
+
+#include <QtCore/qxpfunctional.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.
+//
+
+QT_BEGIN_NAMESPACE
+
+// Helper functions to deal with a QIODevice that is either a socket or a local
+// socket.
+namespace QSocketAbstraction {
+template <typename Fn, typename... Args>
+auto visit(Fn &&fn, QIODevice *socket, Args &&...args)
+{
+ if (auto *s = qobject_cast<QAbstractSocket *>(socket))
+ return std::forward<Fn>(fn)(s, std::forward<Args>(args)...);
+ if (auto *s = qobject_cast<QLocalSocket *>(socket))
+ return std::forward<Fn>(fn)(s, std::forward<Args>(args)...);
+ Q_UNREACHABLE();
+}
+
+// Since QLocalSocket's LocalSocketState's values are defined as being equal
+// to some of QAbstractSocket's SocketState's values, we can use the superset
+// of the two as the return type.
+inline QAbstractSocket::SocketState socketState(QIODevice *device)
+{
+ auto getState = [](auto *s) {
+ using T = std::remove_pointer_t<decltype(s)>;
+ if constexpr (std::is_same_v<T, QAbstractSocket>) {
+ return s->state();
+ } else if constexpr (std::is_same_v<T, QLocalSocket>) {
+ QLocalSocket::LocalSocketState st = s->state();
+ return static_cast<QAbstractSocket::SocketState>(st);
+ }
+ Q_UNREACHABLE();
+ };
+ return visit(getState, device);
+}
+
+// Same as for socketState(), but for the errors
+inline QAbstractSocket::SocketError socketError(QIODevice *device)
+{
+ auto getError = [](auto *s) {
+ using T = std::remove_pointer_t<decltype(s)>;
+ if constexpr (std::is_same_v<T, QAbstractSocket>) {
+ return s->error();
+ } else if constexpr (std::is_same_v<T, QLocalSocket>) {
+ QLocalSocket::LocalSocketError st = s->error();
+ return static_cast<QAbstractSocket::SocketError>(st);
+ }
+ Q_UNREACHABLE();
+ };
+ return visit(getError, device);
+}
+
+inline QString socketPeerName(QIODevice *device)
+{
+ auto getPeerName = [](auto *s) {
+ using T = std::remove_pointer_t<decltype(s)>;
+ if constexpr (std::is_same_v<T, QAbstractSocket>) {
+ return s->peerName();
+ } else if constexpr (std::is_same_v<T, QLocalSocket>) {
+ return s->serverName();
+ }
+ Q_UNREACHABLE();
+ };
+ return visit(getPeerName, device);
+}
+} // namespace QSocketAbstraction
+
+QT_END_NAMESPACE
+
+#endif // QSOCKETABSTRACTION_P_H