diff options
author | Yigit Akcay <yigit.akcay@qt.io> | 2023-01-08 01:41:00 +0100 |
---|---|---|
committer | Yigit Akcay <yigit.akcay@qt.io> | 2023-01-26 16:54:42 +0100 |
commit | dc7a726e431bdeb11449093a002a2b6daea0f67b (patch) | |
tree | 3959b302af7b184cbb64ca482b267cc6990d3071 | |
parent | 614d6639b875f53b21eaabd2d5928b84b59af707 (diff) |
QWebEngineUrlRequestJob: Add function to set additional reponse headers
Add additional response headers field to URLRequestCustomJobDelegate.
Those fields can be set via
QWebEngineUrlRequestJob::setAdditionalResponseHeaders(
const QMap<QByteArray, QByteArray> &
). They are added to URLRequestCustomJobProxy::m_client when
URLRequestCustomJobProxy::reply(std::string, QIODevice) is called, and
added to the response headers within
CustomURLLoaderFactory::notifyHeadersComplete().
Fixes: QTBUG-106578
Change-Id: Ie0f0af07a5381c6f24ec0a1ee1b5bcb0e8c4fa5f
Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
-rw-r--r-- | src/core/api/qwebengineurlrequestjob.cpp | 12 | ||||
-rw-r--r-- | src/core/api/qwebengineurlrequestjob.h | 2 | ||||
-rw-r--r-- | src/core/net/custom_url_loader_factory.cpp | 5 | ||||
-rw-r--r-- | src/core/net/url_request_custom_job_delegate.cpp | 11 | ||||
-rw-r--r-- | src/core/net/url_request_custom_job_delegate.h | 3 | ||||
-rw-r--r-- | src/core/net/url_request_custom_job_proxy.cpp | 4 | ||||
-rw-r--r-- | src/core/net/url_request_custom_job_proxy.h | 6 | ||||
-rw-r--r-- | tests/auto/core/CMakeLists.txt | 1 | ||||
-rw-r--r-- | tests/auto/core/qwebengineurlrequestjob/CMakeLists.txt | 21 | ||||
-rw-r--r-- | tests/auto/core/qwebengineurlrequestjob/additionalResponseHeadersScript.html | 12 | ||||
-rw-r--r-- | tests/auto/core/qwebengineurlrequestjob/tst_qwebengineurlrequestjob.cpp | 119 |
11 files changed, 192 insertions, 4 deletions
diff --git a/src/core/api/qwebengineurlrequestjob.cpp b/src/core/api/qwebengineurlrequestjob.cpp index 6c0adfddf..099efef8e 100644 --- a/src/core/api/qwebengineurlrequestjob.cpp +++ b/src/core/api/qwebengineurlrequestjob.cpp @@ -112,6 +112,18 @@ QMap<QByteArray, QByteArray> QWebEngineUrlRequestJob::requestHeaders() const } /*! + \since 6.6 + Set \a additionalResponseHeaders. These additional headers of the response + are only used when QWebEngineUrlRequestJob::reply(const QByteArray&, QIODevice*) + is called. +*/ +void QWebEngineUrlRequestJob::setAdditionalResponseHeaders( + const QMap<QByteArray, QByteArray> &additionalResponseHeaders) const +{ + d_ptr->setAdditionalResponseHeaders(additionalResponseHeaders); +} + +/*! Replies to the request with \a device and the content type \a contentType. Content type is similar to the HTTP Content-Type header, and can either be a MIME type, or a MIME type and charset encoding combined like this: diff --git a/src/core/api/qwebengineurlrequestjob.h b/src/core/api/qwebengineurlrequestjob.h index 79947ccc4..75e065566 100644 --- a/src/core/api/qwebengineurlrequestjob.h +++ b/src/core/api/qwebengineurlrequestjob.h @@ -43,6 +43,8 @@ public: void reply(const QByteArray &contentType, QIODevice *device); void fail(Error error); void redirect(const QUrl &url); + void setAdditionalResponseHeaders( + const QMap<QByteArray, QByteArray> &additionalResponseHeaders) const; private: QWebEngineUrlRequestJob(QtWebEngineCore::URLRequestCustomJobDelegate *); diff --git a/src/core/net/custom_url_loader_factory.cpp b/src/core/net/custom_url_loader_factory.cpp index 19a1a884f..bd19aa436 100644 --- a/src/core/net/custom_url_loader_factory.cpp +++ b/src/core/net/custom_url_loader_factory.cpp @@ -262,6 +262,11 @@ private: headers += "Access-Control-Allow-Credentials: true\n"; } } + for (auto it = m_additionalResponseHeaders.cbegin(); + it != m_additionalResponseHeaders.cend(); ++it) { + headers += it.key().toLower().toStdString() + ": " + it.value().toLower().toStdString() + + "\n"; + } m_head->headers = base::MakeRefCounted<net::HttpResponseHeaders>(net::HttpUtil::AssembleRawHeaders(headers)); m_head->encoded_data_length = m_head->headers->raw_headers().length(); diff --git a/src/core/net/url_request_custom_job_delegate.cpp b/src/core/net/url_request_custom_job_delegate.cpp index 22d26f735..07a1de1ec 100644 --- a/src/core/net/url_request_custom_job_delegate.cpp +++ b/src/core/net/url_request_custom_job_delegate.cpp @@ -51,13 +51,20 @@ QMap<QByteArray, QByteArray> URLRequestCustomJobDelegate::requestHeaders() const return m_requestHeaders; } +void URLRequestCustomJobDelegate::setAdditionalResponseHeaders( + const QMap<QByteArray, QByteArray> &additionalResponseHeaders) +{ + m_additionalResponseHeaders = additionalResponseHeaders; +} + void URLRequestCustomJobDelegate::reply(const QByteArray &contentType, QIODevice *device) { if (device) QObject::connect(device, &QIODevice::readyRead, this, &URLRequestCustomJobDelegate::slotReadyRead); m_proxy->m_ioTaskRunner->PostTask(FROM_HERE, - base::BindOnce(&URLRequestCustomJobProxy::reply, - m_proxy, contentType.toStdString(),device)); + base::BindOnce(&URLRequestCustomJobProxy::reply, m_proxy, + contentType.toStdString(), device, + std::move(m_additionalResponseHeaders))); } void URLRequestCustomJobDelegate::slotReadyRead() diff --git a/src/core/net/url_request_custom_job_delegate.h b/src/core/net/url_request_custom_job_delegate.h index 7b0d6538c..6b6ee68cf 100644 --- a/src/core/net/url_request_custom_job_delegate.h +++ b/src/core/net/url_request_custom_job_delegate.h @@ -48,6 +48,8 @@ public: QUrl initiator() const; QMap<QByteArray, QByteArray> requestHeaders() const; + void + setAdditionalResponseHeaders(const QMap<QByteArray, QByteArray> &additionalResponseHeaders); void reply(const QByteArray &contentType, QIODevice *device); void redirect(const QUrl &url); void abort(); @@ -69,6 +71,7 @@ private: QByteArray m_method; QUrl m_initiatorOrigin; const QMap<QByteArray, QByteArray> m_requestHeaders; + QMap<QByteArray, QByteArray> m_additionalResponseHeaders; }; } // namespace diff --git a/src/core/net/url_request_custom_job_proxy.cpp b/src/core/net/url_request_custom_job_proxy.cpp index 45372f020..d7adcc7e8 100644 --- a/src/core/net/url_request_custom_job_proxy.cpp +++ b/src/core/net/url_request_custom_job_proxy.cpp @@ -40,7 +40,8 @@ void URLRequestCustomJobProxy::release() } } -void URLRequestCustomJobProxy::reply(std::string contentType, QIODevice *device) +void URLRequestCustomJobProxy::reply(std::string contentType, QIODevice *device, + QMap<QByteArray, QByteArray> additionalResponseHeaders) { if (!m_client) return; @@ -58,6 +59,7 @@ void URLRequestCustomJobProxy::reply(std::string contentType, QIODevice *device) } m_client->m_mimeType = qcontentType.toStdString(); m_client->m_device = device; + m_client->m_additionalResponseHeaders = additionalResponseHeaders; if (m_client->m_device && !m_client->m_device->isReadable()) m_client->m_device->open(QIODevice::ReadOnly); diff --git a/src/core/net/url_request_custom_job_proxy.h b/src/core/net/url_request_custom_job_proxy.h index 3795f7f14..1f8c77da2 100644 --- a/src/core/net/url_request_custom_job_proxy.h +++ b/src/core/net/url_request_custom_job_proxy.h @@ -10,6 +10,8 @@ #include "url/gurl.h" #include "url/origin.h" #include <QtCore/QPointer> +#include <QMap> +#include <QByteArray> QT_FORWARD_DECLARE_CLASS(QIODevice) @@ -29,6 +31,7 @@ public: public: std::string m_mimeType; std::string m_charset; + QMap<QByteArray, QByteArray> m_additionalResponseHeaders; GURL m_redirect; QIODevice *m_device; int64_t m_firstBytePosition; @@ -49,7 +52,8 @@ public: // Called from URLRequestCustomJobDelegate via post: //void setReplyCharset(const std::string &); - void reply(std::string mimeType, QIODevice *device); + void reply(std::string mimeType, QIODevice *device, + QMap<QByteArray, QByteArray> additionalResponseHeaders); void redirect(GURL url); void abort(); void fail(int error); diff --git a/tests/auto/core/CMakeLists.txt b/tests/auto/core/CMakeLists.txt index 5615d6b2d..3f10e8303 100644 --- a/tests/auto/core/CMakeLists.txt +++ b/tests/auto/core/CMakeLists.txt @@ -4,6 +4,7 @@ add_subdirectory(qwebenginecookiestore) add_subdirectory(qwebenginesettings) add_subdirectory(qwebengineurlrequestinterceptor) +add_subdirectory(qwebengineurlrequestjob) add_subdirectory(origins) add_subdirectory(devtools) diff --git a/tests/auto/core/qwebengineurlrequestjob/CMakeLists.txt b/tests/auto/core/qwebengineurlrequestjob/CMakeLists.txt new file mode 100644 index 000000000..0a7699083 --- /dev/null +++ b/tests/auto/core/qwebengineurlrequestjob/CMakeLists.txt @@ -0,0 +1,21 @@ +# Copyright (C) 2023 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause + +qt_internal_add_test(tst_qwebengineurlrequestjob + SOURCES + tst_qwebengineurlrequestjob.cpp + LIBRARIES + Qt::WebEngineCore +) + +# Resources: +set(tst_qwebengineurlrequestjob_resource_files + "additionalResponseHeadersScript.html" +) + +qt_add_resources(tst_qwebengineurlrequestjob "tst_qwebengineurlrequestjob" + PREFIX + "/" + FILES + ${tst_qwebengineurlrequestjob_resource_files} +) diff --git a/tests/auto/core/qwebengineurlrequestjob/additionalResponseHeadersScript.html b/tests/auto/core/qwebengineurlrequestjob/additionalResponseHeadersScript.html new file mode 100644 index 000000000..a1b8a92d3 --- /dev/null +++ b/tests/auto/core/qwebengineurlrequestjob/additionalResponseHeadersScript.html @@ -0,0 +1,12 @@ +<!doctype html> +<html> +<body> + <script type="text/javascript"> + var request = new XMLHttpRequest(); + request.open('GET', 'additionalresponseheadershandler:about', /* async = */ false); + request.send(); + var headers = request.getAllResponseHeaders(); + console.log("TST_ADDITIONALRESPONSEHEADERS;" + headers); + </script> +</body> +</html> diff --git a/tests/auto/core/qwebengineurlrequestjob/tst_qwebengineurlrequestjob.cpp b/tests/auto/core/qwebengineurlrequestjob/tst_qwebengineurlrequestjob.cpp new file mode 100644 index 000000000..5f679e912 --- /dev/null +++ b/tests/auto/core/qwebengineurlrequestjob/tst_qwebengineurlrequestjob.cpp @@ -0,0 +1,119 @@ +// Copyright (C) 2023 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 + +#include <QtTest/QtTest> +#include <QtWebEngineCore/qwebengineurlschemehandler.h> +#include <QtWebEngineCore/qwebengineurlscheme.h> +#include <QtWebEngineCore/qwebengineurlrequestjob.h> +#include <QtWebEngineCore/qwebengineprofile.h> +#include <QtWebEngineCore/qwebenginepage.h> +#include <QtWebEngineCore/qwebenginehttprequest.h> +#include <QtWebEngineCore/qwebenginesettings.h> + +class AdditionalResponseHeadersHandler : public QWebEngineUrlSchemeHandler +{ + Q_OBJECT +public: + AdditionalResponseHeadersHandler(bool addAdditionalResponseHeaders, QObject *parent = nullptr) + : QWebEngineUrlSchemeHandler(parent) + , m_addAdditionalResponseHeaders(addAdditionalResponseHeaders) + { + } + + void requestStarted(QWebEngineUrlRequestJob *job) override + { + QCOMPARE(job->requestUrl(), QUrl(schemeName + ":about")); + + QMap<QByteArray, QByteArray> additionalResponseHeaders; + if (m_addAdditionalResponseHeaders) { + additionalResponseHeaders.insert(QByteArray::fromStdString("test1"), + QByteArray::fromStdString("test1VALUE")); + additionalResponseHeaders.insert(QByteArray::fromStdString("test2"), + QByteArray::fromStdString("test2VALUE")); + } + job->setAdditionalResponseHeaders(additionalResponseHeaders); + + QFile *file = new QFile(QStringLiteral(":additionalResponseHeadersScript.html"), job); + file->open(QIODevice::ReadOnly); + + job->reply(QByteArrayLiteral("text/html"), file); + } + + static void registerUrlScheme() + { + QWebEngineUrlScheme webUiScheme(schemeName); + webUiScheme.setFlags(QWebEngineUrlScheme::CorsEnabled); + QWebEngineUrlScheme::registerScheme(webUiScheme); + } + + const static inline QByteArray schemeName = + QByteArrayLiteral("additionalresponseheadershandler"); + +private: + bool m_addAdditionalResponseHeaders; +}; + +class AdditionalResponseHeadersPage : public QWebEnginePage +{ + Q_OBJECT + +public: + AdditionalResponseHeadersPage(QWebEngineProfile *profile, QString compareString, + QObject *parent = nullptr) + : QWebEnginePage(profile, parent), m_compareString(compareString) + { + } + +protected: + void javaScriptConsoleMessage(QWebEnginePage::JavaScriptConsoleMessageLevel level, + const QString &message, int lineNumber, + const QString &sourceID) override + { + auto splitMessage = message.split(";"); + if (splitMessage[0] == QString("TST_ADDITIONALRESPONSEHEADERS")) + QCOMPARE(splitMessage[1], m_compareString); + } + +private: + QString m_compareString; +}; + +class tst_QWebEngineUrlRequestJob : public QObject +{ + Q_OBJECT + +public: + tst_QWebEngineUrlRequestJob() { } + +private Q_SLOTS: + void initTestCase() { AdditionalResponseHeadersHandler::registerUrlScheme(); } + + void withAdditionalResponseHeaders_data() + { + QTest::addColumn<bool>("withHeaders"); + QTest::addColumn<QString>("expectedHeaders"); + QTest::newRow("headers enabled") + << true << "content-type: text/html\r\ntest1: test1value\r\ntest2: test2value\r\n"; + QTest::newRow("headers disabled") << false << "content-type: text/html\r\n"; + } + + void withAdditionalResponseHeaders() + { + QFETCH(bool, withHeaders); + QFETCH(QString, expectedHeaders); + + QWebEngineProfile profile; + + AdditionalResponseHeadersHandler handler(withHeaders); + profile.installUrlSchemeHandler(AdditionalResponseHeadersHandler::schemeName, &handler); + + AdditionalResponseHeadersPage page(&profile, expectedHeaders); + QSignalSpy spy(&page, SIGNAL(loadFinished(bool))); + + page.load(QUrl("qrc:///additionalResponseHeadersScript.html")); + QVERIFY(spy.wait()); + } +}; + +QTEST_MAIN(tst_QWebEngineUrlRequestJob) +#include "tst_qwebengineurlrequestjob.moc" |