diff options
author | Yigit Akcay <yigit.akcay@qt.io> | 2023-04-05 15:36:06 +0200 |
---|---|---|
committer | Yigit Akcay <yigit.akcay@qt.io> | 2023-06-16 18:31:24 +0200 |
commit | 4ef4de4aad7264b4d75e1cc60b55e95b33ea0739 (patch) | |
tree | 2458b4e2c114facd48f824ce34d82886b6edf784 /src/core/net | |
parent | a565ffcc172da74ae6ca0f847a0b162ffd68e918 (diff) |
QIODevice for request body in QWebEngineUrlRequestInterceptor
This patch implements a QIODevice subclass that gives access to the
request body inside QWebEngineUrlRequestInterceptor::interceptRequest().
Fixes: QTBUG-61320
Change-Id: Ib6010dad908c65e070d63927b619eaed2347e317
Reviewed-by: Michal Klocek <michal.klocek@qt.io>
Diffstat (limited to 'src/core/net')
-rw-r--r-- | src/core/net/proxying_url_loader_factory_qt.cpp | 5 | ||||
-rw-r--r-- | src/core/net/resource_request_body_qt.cpp | 181 | ||||
-rw-r--r-- | src/core/net/resource_request_body_qt.h | 70 |
3 files changed, 255 insertions, 1 deletions
diff --git a/src/core/net/proxying_url_loader_factory_qt.cpp b/src/core/net/proxying_url_loader_factory_qt.cpp index 8a74b89c4..b0ecf9cea 100644 --- a/src/core/net/proxying_url_loader_factory_qt.cpp +++ b/src/core/net/proxying_url_loader_factory_qt.cpp @@ -25,6 +25,7 @@ #include "web_contents_adapter.h" #include "web_contents_adapter_client.h" #include "web_contents_view_qt.h" +#include "net/resource_request_body_qt.h" #include <QtWebEngineCore/QWebEngineUrlResponseInfo> @@ -173,6 +174,7 @@ private: // error didn't occur. int error_status_ = net::OK; network::ResourceRequest request_; + ResourceRequestBody request_body_; network::mojom::URLResponseHeadPtr current_response_; const net::MutableNetworkTrafficAnnotationTag traffic_annotation_; @@ -200,6 +202,7 @@ InterceptedRequest::InterceptedRequest(ProfileAdapter *profile_adapter, , request_id_(request_id) , options_(options) , request_(request) + , request_body_(ResourceRequestBody(request_.request_body.get())) , traffic_annotation_(traffic_annotation) , proxied_loader_receiver_(this, std::move(loader_receiver)) , target_client_(std::move(client)) @@ -389,7 +392,7 @@ void InterceptedRequest::Restart() auto info = new QWebEngineUrlRequestInfoPrivate( resourceType, navigationType, originalUrl, firstPartyUrl, initiator, - QByteArray::fromStdString(request_.method), headers); + QByteArray::fromStdString(request_.method), &request_body_, headers); Q_ASSERT(!request_info_); request_info_.reset(new QWebEngineUrlRequestInfo(info)); diff --git a/src/core/net/resource_request_body_qt.cpp b/src/core/net/resource_request_body_qt.cpp new file mode 100644 index 000000000..d0d54784d --- /dev/null +++ b/src/core/net/resource_request_body_qt.cpp @@ -0,0 +1,181 @@ +// 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 "resource_request_body_qt.h" +#include "type_conversion.h" + +#include "services/network/public/cpp/resource_request_body.h" +#include "services/network/public/mojom/data_pipe_getter.mojom.h" +#include "services/network/public/mojom/url_request.mojom-shared.h" +#include "mojo/public/cpp/bindings/remote.h" + +namespace QtWebEngineCore { + +ResourceRequestBody::ResourceRequestBody(network::ResourceRequestBody *requestBody, QObject *parent) + : QIODevice(parent) + , m_requestBody(requestBody) + , m_dataElementsIdx(0) + , m_dataElementBytesIdx(0) + , m_dataElementFileIdx(0) +{}; + +ResourceRequestBody::~ResourceRequestBody(){}; + +qint64 ResourceRequestBody::readData(char *data, qint64 maxSize) +{ + if (!m_requestBody) + return -1; + + const std::size_t dataElementsSize = m_requestBody->elements()->size(); + if (m_dataElementsIdx == dataElementsSize) + return -1; + + qint64 bytesRead = 0; + const std::vector<network::DataElement> *elements = m_requestBody->elements(); + while (bytesRead < maxSize && m_dataElementsIdx < dataElementsSize) { + const network::DataElement ¤tDataElement = elements->at(m_dataElementsIdx); + + switch (currentDataElement.type()) { + case network::mojom::DataElementDataView::Tag::kBytes: { + readDataElementBytes(currentDataElement.As<network::DataElementBytes>().bytes(), + bytesRead, maxSize, &data); + break; + } + case network::mojom::DataElementDataView::Tag::kFile: { + const network::DataElementFile file = currentDataElement.As<network::DataElementFile>(); + const qint64 offset = file.offset(); + const qint64 length = file.length(); + readDataElementFile(file.path(), offset, length, bytesRead, maxSize, &data); + break; + } + case network::mojom::DataElementDataView::Tag::kDataPipe: { + mojo::Remote<network::mojom::DataPipeGetter> pipeGetter; + pipeGetter.Bind( + currentDataElement.As<network::DataElementDataPipe>().CloneDataPipeGetter()); + const mojo::ScopedHandleBase<mojo::DataPipeConsumerHandle> consumerHandle = + getConsumerHandleFromPipeGetter(pipeGetter); + readDataElementPipe(consumerHandle, bytesRead, maxSize, &data); + break; + } + case network::mojom::DataElementDataView::Tag::kChunkedDataPipe: { + setErrorString(QStringLiteral("Chunked data pipe is used in request body upload, which " + "is currently not supported")); + // Nothing should come before or after DataElementChunkedDataPipe + return -1; + } + } + + if (bytesRead == maxSize || m_dataElementsIdx == dataElementsSize) + break; + } + + return bytesRead; +} + +// We don't want to write, ever +qint64 ResourceRequestBody::writeData(const char *data, qint64 maxSize) +{ + return -1; +} + +bool ResourceRequestBody::isSequential() const +{ + return true; +} + +void ResourceRequestBody::readDataElementBytes(const std::vector<uint8_t> &dataElement, + qint64 &bytesRead, const qint64 &maxSize, + char **data) +{ + const std::size_t dataElementSize = dataElement.size(); + const std::size_t bytesToRead = std::min(dataElementSize, static_cast<std::size_t>(maxSize)); + + std::memcpy(*data, dataElement.data(), bytesToRead); + *data += bytesToRead; + m_dataElementBytesIdx += bytesToRead; + bytesRead += bytesToRead; + + if (m_dataElementBytesIdx == dataElementSize) { + m_dataElementsIdx++; + m_dataElementBytesIdx = 0; + } +} + +void ResourceRequestBody::readDataElementFile(const base::FilePath &filePath, const qint64 &offset, + const qint64 &length, qint64 &bytesRead, + const qint64 &maxSize, char **data) +{ + QFile file(toQt(filePath.value())); + const qint64 realOffset = offset + m_dataElementFileIdx; + const std::size_t fileSize = std::min(file.size(), length) - realOffset; + const std::size_t bytesToRead = std::min(fileSize, static_cast<std::size_t>(maxSize)); + + file.open(QFile::ReadOnly); + file.seek(realOffset); + + std::memcpy(*data, file.read(bytesToRead).data(), bytesToRead); + *data += bytesToRead; + m_dataElementFileIdx += bytesToRead; + bytesRead += bytesToRead; + + file.close(); + + if (m_dataElementFileIdx == fileSize) { + m_dataElementsIdx++; + m_dataElementFileIdx = 0; + } +} + +mojo::ScopedHandleBase<mojo::DataPipeConsumerHandle> +ResourceRequestBody::getConsumerHandleFromPipeGetter( + mojo::Remote<network::mojom::DataPipeGetter> &pipeGetter) +{ + mojo::ScopedHandleBase<mojo::DataPipeProducerHandle> producerHandle; + mojo::ScopedHandleBase<mojo::DataPipeConsumerHandle> consumerHandle; + mojo::CreateDataPipe(nullptr, producerHandle, consumerHandle); + base::WeakPtrFactory<ResourceRequestBody> weakPtrFactory{ this }; + pipeGetter->Read(std::move(producerHandle), + base::BindOnce(&ResourceRequestBody::pipeGetterOnReadComplete, + weakPtrFactory.GetWeakPtr())); + + return consumerHandle; +} + +void ResourceRequestBody::readDataElementPipe( + const mojo::ScopedHandleBase<mojo::DataPipeConsumerHandle> &consumerHandle, + qint64 &bytesRead, const qint64 &maxSize, char **data) +{ + MojoResult result; + do { + uint32_t bytesToRead = 1; + result = consumerHandle->ReadData(*data, &bytesToRead, MOJO_READ_DATA_FLAG_NONE); + + if (result == MOJO_RESULT_OK) { + *data += bytesToRead; + bytesRead += bytesToRead; + } else if (result != MOJO_RESULT_SHOULD_WAIT && result != MOJO_RESULT_FAILED_PRECONDITION) { + setErrorString(QString::fromLatin1("Error while reading from data pipe, skipping" + "remaining content of data pipe. Mojo error code: ") + + QString::number(result)); + } + } while ((result == MOJO_RESULT_SHOULD_WAIT || result == MOJO_RESULT_OK) + && bytesRead < maxSize); + + m_dataElementsIdx++; +} + +void ResourceRequestBody::pipeGetterOnReadComplete(int32_t status, uint64_t size) { } + +void ResourceRequestBody::appendFilesForTest(const QString &path) +{ + if (!m_requestBody) + return; + + base::FilePath filePath = toFilePath(path); + m_requestBody->elements_mutable()->push_back(static_cast<network::DataElement>( + network::DataElementFile(filePath, 0, 23, base::Time()))); + m_requestBody->elements_mutable()->push_back(static_cast<network::DataElement>( + network::DataElementFile(filePath, 10, 23, base::Time()))); +} + +} // namespace QtWebEngineCore diff --git a/src/core/net/resource_request_body_qt.h b/src/core/net/resource_request_body_qt.h new file mode 100644 index 000000000..aa3af2d28 --- /dev/null +++ b/src/core/net/resource_request_body_qt.h @@ -0,0 +1,70 @@ +// 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 RESOURCEREQUESTBODY_QT_H +#define RESOURCEREQUESTBODY_QT_H + +#include <QtWebEngineCore/private/qtwebenginecoreglobal_p.h> +#include <QtCore/QIODevice> +#include <QtCore/QFile> +#include <QtCore/QUrl> + +namespace network { +class ResourceRequestBody; +namespace mojom { +class DataPipeGetter; +class ChunkedDataPipeGetter; +} +} + +namespace base { +class FilePath; +} + +namespace mojo { +template<typename T> +class Remote; +template<typename T> +class ScopedHandleBase; +class DataPipeConsumerHandle; +} + +namespace QtWebEngineCore { + +class Q_WEBENGINECORE_PRIVATE_EXPORT ResourceRequestBody : public QIODevice +{ + Q_OBJECT +public: + explicit ResourceRequestBody(network::ResourceRequestBody *requestBody, + QObject *parent = nullptr); + ~ResourceRequestBody(); + + qint64 readData(char *data, qint64 maxSize) override; + qint64 writeData(const char *data, qint64 maxSize) override; + bool isSequential() const override; + + void appendFilesForTest(const QString &path); + +private: + network::ResourceRequestBody *const m_requestBody; + + std::size_t m_dataElementsIdx; + std::size_t m_dataElementBytesIdx; + std::size_t m_dataElementFileIdx; + + void readDataElementBytes(const std::vector<uint8_t> &dataElement, qint64 &bytesRead, + const qint64 &maxSize, char **data); + void readDataElementFile(const base::FilePath &filePath, const qint64 &offset, + const qint64 &length, qint64 &bytesRead, const qint64 &maxSize, + char **data); + mojo::ScopedHandleBase<mojo::DataPipeConsumerHandle> + getConsumerHandleFromPipeGetter(mojo::Remote<network::mojom::DataPipeGetter> &pipeGetter); + void + readDataElementPipe(const mojo::ScopedHandleBase<mojo::DataPipeConsumerHandle> &consumerHandle, + qint64 &bytesRead, const qint64 &maxSize, char **data); + void pipeGetterOnReadComplete(int32_t status, uint64_t size); +}; + +} // namespace QtWebEngineCore + +#endif // RESOURCEREQUESTBODY_QT_H |