| Index: Source/modules/fetch/FetchManager.cpp
|
| diff --git a/Source/modules/fetch/FetchManager.cpp b/Source/modules/fetch/FetchManager.cpp
|
| index 7886a7c85b2cb4ed60ee7c5df9a8134f2da09de5..3bc85626789af65ff5d3f21a72cbd496518ea0c4 100644
|
| --- a/Source/modules/fetch/FetchManager.cpp
|
| +++ b/Source/modules/fetch/FetchManager.cpp
|
| @@ -15,6 +15,7 @@
|
| #include "core/fetch/FetchUtils.h"
|
| #include "core/fileapi/Blob.h"
|
| #include "core/frame/Frame.h"
|
| +#include "core/frame/SubresourceIntegrity.h"
|
| #include "core/frame/csp/ContentSecurityPolicy.h"
|
| #include "core/inspector/ConsoleMessage.h"
|
| #include "core/inspector/InspectorInstrumentation.h"
|
| @@ -24,7 +25,9 @@
|
| #include "core/page/Page.h"
|
| #include "modules/fetch/Body.h"
|
| #include "modules/fetch/BodyStreamBuffer.h"
|
| +#include "modules/fetch/CompositeDataConsumerHandle.h"
|
| #include "modules/fetch/DataConsumerHandleUtil.h"
|
| +#include "modules/fetch/FetchFormDataConsumerHandle.h"
|
| #include "modules/fetch/FetchRequestData.h"
|
| #include "modules/fetch/Response.h"
|
| #include "modules/fetch/ResponseInit.h"
|
| @@ -34,6 +37,8 @@
|
| #include "platform/weborigin/SecurityOrigin.h"
|
| #include "public/platform/WebURLRequest.h"
|
| #include "wtf/HashSet.h"
|
| +#include "wtf/Vector.h"
|
| +#include "wtf/text/WTFString.h"
|
|
|
| namespace blink {
|
|
|
| @@ -66,6 +71,83 @@ public:
|
| void start();
|
| void dispose();
|
|
|
| + class SRIVerifier final : public GarbageCollectedFinalized<SRIVerifier>, public WebDataConsumerHandle::Client {
|
| + public:
|
| + // SRIVerifier takes ownership of |handle| and |response|.
|
| + // |updater| must be garbage collected. The other arguments
|
| + // all must have the lifetime of the give loader.
|
| + SRIVerifier(PassOwnPtr<WebDataConsumerHandle> handle, CompositeDataConsumerHandle::Updater* updater, Response* response, FetchManager::Loader* loader, String integrityMetadata, const KURL& url)
|
| + : m_handle(handle)
|
| + , m_updater(updater)
|
| + , m_response(response)
|
| + , m_loader(loader)
|
| + , m_integrityMetadata(integrityMetadata)
|
| + , m_url(url)
|
| + , m_finished(false)
|
| + {
|
| + m_reader = m_handle->obtainReader(this);
|
| + }
|
| +
|
| + void didGetReadable() override
|
| + {
|
| + ASSERT(m_reader);
|
| + ASSERT(m_loader);
|
| + ASSERT(m_response);
|
| +
|
| + WebDataConsumerHandle::Result r = WebDataConsumerHandle::Ok;
|
| + while (r == WebDataConsumerHandle::Ok) {
|
| + const void* buffer;
|
| + size_t size;
|
| + r = m_reader->beginRead(&buffer, WebDataConsumerHandle::FlagNone, &size);
|
| + if (r == WebDataConsumerHandle::Ok) {
|
| + m_buffer.append(static_cast<const char*>(buffer), size);
|
| + m_reader->endRead(size);
|
| + }
|
| + }
|
| + if (r == WebDataConsumerHandle::ShouldWait)
|
| + return;
|
| + String errorMessage = "Unknown error occurred while trying to verify integrity.";
|
| + m_finished = true;
|
| + if (r == WebDataConsumerHandle::Done) {
|
| + if (SubresourceIntegrity::CheckSubresourceIntegrity(m_integrityMetadata, String(m_buffer.data(), m_buffer.size()), m_url, *m_loader->document(), errorMessage)) {
|
| + m_updater->update(FetchFormDataConsumerHandle::create(m_buffer.data(), m_buffer.size()));
|
| + m_loader->m_resolver->resolve(m_response);
|
| + m_loader->m_resolver.clear();
|
| + // FetchManager::Loader::didFinishLoading() can
|
| + // be called before didGetReadable() is called
|
| + // when the data is ready. In that case,
|
| + // didFinishLoading() doesn't clean up and call
|
| + // notifyFinished(), so it is necessary to
|
| + // explicitly finish the loader here.
|
| + if (m_loader->m_didFinishLoading)
|
| + m_loader->loadSucceeded();
|
| + return;
|
| + }
|
| + }
|
| + m_updater->update(createUnexpectedErrorDataConsumerHandle());
|
| + m_loader->performNetworkError(errorMessage);
|
| + }
|
| +
|
| + bool isFinished() const { return m_finished; }
|
| +
|
| + DEFINE_INLINE_TRACE()
|
| + {
|
| + visitor->trace(m_updater);
|
| + visitor->trace(m_response);
|
| + visitor->trace(m_loader);
|
| + }
|
| + private:
|
| + OwnPtr<WebDataConsumerHandle> m_handle;
|
| + Member<CompositeDataConsumerHandle::Updater> m_updater;
|
| + Member<Response> m_response;
|
| + RawPtrWillBeMember<FetchManager::Loader> m_loader;
|
| + String m_integrityMetadata;
|
| + KURL m_url;
|
| + OwnPtr<WebDataConsumerHandle::Reader> m_reader;
|
| + Vector<char> m_buffer;
|
| + bool m_finished;
|
| + };
|
| +
|
| private:
|
| Loader(ExecutionContext*, FetchManager*, ScriptPromiseResolver*, FetchRequestData*);
|
|
|
| @@ -75,6 +157,7 @@ private:
|
| void failed(const String& message);
|
| void notifyFinished();
|
| Document* document() const;
|
| + void loadSucceeded();
|
|
|
| RawPtrWillBeMember<FetchManager> m_fetchManager;
|
| PersistentWillBeMember<ScriptPromiseResolver> m_resolver;
|
| @@ -83,6 +166,8 @@ private:
|
| bool m_failed;
|
| bool m_finished;
|
| int m_responseHttpStatusCode;
|
| + PersistentWillBeMember<SRIVerifier> m_integrityVerifier;
|
| + bool m_didFinishLoading;
|
| };
|
|
|
| FetchManager::Loader::Loader(ExecutionContext* executionContext, FetchManager* fetchManager, ScriptPromiseResolver* resolver, FetchRequestData* request)
|
| @@ -93,6 +178,8 @@ FetchManager::Loader::Loader(ExecutionContext* executionContext, FetchManager* f
|
| , m_failed(false)
|
| , m_finished(false)
|
| , m_responseHttpStatusCode(0)
|
| + , m_integrityVerifier(nullptr)
|
| + , m_didFinishLoading(false)
|
| {
|
| }
|
|
|
| @@ -106,6 +193,7 @@ DEFINE_TRACE(FetchManager::Loader)
|
| visitor->trace(m_fetchManager);
|
| visitor->trace(m_resolver);
|
| visitor->trace(m_request);
|
| + visitor->trace(m_integrityVerifier);
|
| ContextLifecycleObserver::trace(visitor);
|
| }
|
|
|
| @@ -131,7 +219,13 @@ void FetchManager::Loader::didReceiveResponse(unsigned long, const ResourceRespo
|
| break;
|
| }
|
| }
|
| - FetchResponseData* responseData = FetchResponseData::createWithBuffer(new BodyStreamBuffer(createFetchDataConsumerHandleFromWebHandle(handle)));
|
| +
|
| + FetchResponseData* responseData = nullptr;
|
| + CompositeDataConsumerHandle::Updater* updater = nullptr;
|
| + if (m_request->integrity().isEmpty())
|
| + responseData = FetchResponseData::createWithBuffer(new BodyStreamBuffer(createFetchDataConsumerHandleFromWebHandle(handle)));
|
| + else
|
| + responseData = FetchResponseData::createWithBuffer(new BodyStreamBuffer(createFetchDataConsumerHandleFromWebHandle(CompositeDataConsumerHandle::create(createWaitingDataConsumerHandle(), &updater))));
|
| responseData->setStatus(response.httpStatusCode());
|
| responseData->setStatusMessage(response.httpStatusText());
|
| for (auto& it : response.httpHeaderFields())
|
| @@ -173,23 +267,29 @@ void FetchManager::Loader::didReceiveResponse(unsigned long, const ResourceRespo
|
| break;
|
| }
|
| }
|
| +
|
| Response* r = Response::create(m_resolver->executionContext(), taintedResponse);
|
| r->headers()->setGuard(Headers::ImmutableGuard);
|
| - m_resolver->resolve(r);
|
| - m_resolver.clear();
|
| +
|
| + if (m_request->integrity().isEmpty()) {
|
| + m_resolver->resolve(r);
|
| + m_resolver.clear();
|
| + } else {
|
| + ASSERT(!m_integrityVerifier);
|
| + m_integrityVerifier = new SRIVerifier(handle, updater, r, this, m_request->integrity(), response.url());
|
| + }
|
| }
|
|
|
| void FetchManager::Loader::didFinishLoading(unsigned long, double)
|
| {
|
| - ASSERT(!m_failed);
|
| - m_finished = true;
|
| + m_didFinishLoading = true;
|
| + // If there is an integrity verifier, and it has not already finished, it
|
| + // will take care of finishing the load or performing a network error when
|
| + // verification is complete.
|
| + if (m_integrityVerifier && !m_integrityVerifier->isFinished())
|
| + return;
|
|
|
| - if (document() && document()->frame() && document()->frame()->page()
|
| - && m_responseHttpStatusCode >= 200 && m_responseHttpStatusCode < 300) {
|
| - document()->frame()->page()->chromeClient().ajaxSucceeded(document()->frame());
|
| - }
|
| - InspectorInstrumentation::didFinishFetch(executionContext(), this, m_request->method(), m_request->url().string());
|
| - notifyFinished();
|
| + loadSucceeded();
|
| }
|
|
|
| void FetchManager::Loader::didFail(const ResourceError& error)
|
| @@ -221,6 +321,20 @@ Document* FetchManager::Loader::document() const
|
| return nullptr;
|
| }
|
|
|
| +void FetchManager::Loader::loadSucceeded()
|
| +{
|
| + ASSERT(!m_failed);
|
| +
|
| + m_finished = true;
|
| +
|
| + if (document() && document()->frame() && document()->frame()->page()
|
| + && m_responseHttpStatusCode >= 200 && m_responseHttpStatusCode < 300) {
|
| + document()->frame()->page()->chromeClient().ajaxSucceeded(document()->frame());
|
| + }
|
| + InspectorInstrumentation::didFinishFetch(executionContext(), this, m_request->method(), m_request->url().string());
|
| + notifyFinished();
|
| +}
|
| +
|
| void FetchManager::Loader::start()
|
| {
|
| // "1. If |request|'s url contains a Known HSTS Host, modify it per the
|
|
|