blob: 4a6fab2c8cf1510ea4d808eea2a33432e9c51b21 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/fetch/fetch_request_data.h"
#include "net/base/request_priority.h"
#include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/web_http_body.h"
#include "third_party/blink/public/platform/web_url_request.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/fetch/blob_bytes_consumer.h"
#include "third_party/blink/renderer/core/fetch/fetch_header_list.h"
#include "third_party/blink/renderer/core/fetch/form_data_bytes_consumer.h"
#include "third_party/blink/renderer/core/loader/threadable_loader.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/bindings/script_state.h"
#include "third_party/blink/renderer/platform/heap/heap.h"
#include "third_party/blink/renderer/platform/loader/fetch/bytes_consumer.h"
#include "third_party/blink/renderer/platform/loader/fetch/data_pipe_bytes_consumer.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_loader_options.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_request.h"
#include "third_party/blink/renderer/platform/network/http_names.h"
namespace {
::blink::ResourceLoadPriority ConvertRequestPriorityToResourceLoadPriority(
net::RequestPriority priority) {
switch (priority) {
case net::RequestPriority::THROTTLED:
break;
case net::RequestPriority::IDLE:
return ::blink::ResourceLoadPriority::kVeryLow;
case net::RequestPriority::LOWEST:
return ::blink::ResourceLoadPriority::kLow;
case net::RequestPriority::LOW:
return ::blink::ResourceLoadPriority::kMedium;
case net::RequestPriority::MEDIUM:
return ::blink::ResourceLoadPriority::kHigh;
case net::RequestPriority::HIGHEST:
return ::blink::ResourceLoadPriority::kVeryHigh;
}
NOTREACHED() << priority;
return blink::ResourceLoadPriority::kUnresolved;
}
} // namespace
namespace blink {
namespace {
bool IsExcludedHeaderForServiceWorkerFetchEvent(const String& header_name) {
// Excluding Sec-Fetch-... headers as suggested in
// https://crbug.com/949997#c4.
if (header_name.StartsWithIgnoringASCIICase("sec-fetch-")) {
return true;
}
if (Platform::Current()->IsExcludedHeaderForServiceWorkerFetchEvent(
header_name)) {
return true;
}
return false;
}
void SignalError(
Persistent<DataPipeBytesConsumer::CompletionNotifier> notifier) {
notifier->SignalError(BytesConsumer::Error());
}
void SignalSize(
std::unique_ptr<mojo::Remote<network::mojom::blink::ChunkedDataPipeGetter>>,
Persistent<DataPipeBytesConsumer::CompletionNotifier> notifier,
int32_t status,
uint64_t size) {
if (status != 0) {
// error case
notifier->SignalError(BytesConsumer::Error());
return;
}
notifier->SignalSize(size);
}
} // namespace
FetchRequestData* FetchRequestData::Create(
ScriptState* script_state,
mojom::blink::FetchAPIRequestPtr fetch_api_request,
ForServiceWorkerFetchEvent for_service_worker_fetch_event) {
DCHECK(fetch_api_request);
FetchRequestData* request = MakeGarbageCollected<FetchRequestData>(
script_state ? ExecutionContext::From(script_state) : nullptr);
request->url_ = fetch_api_request->url;
request->method_ = AtomicString(fetch_api_request->method);
for (const auto& pair : fetch_api_request->headers) {
// TODO(leonhsl): Check sources of |fetch_api_request.headers| to make clear
// whether we really need this filter.
if (EqualIgnoringASCIICase(pair.key, "referer"))
continue;
if (for_service_worker_fetch_event == ForServiceWorkerFetchEvent::kTrue &&
IsExcludedHeaderForServiceWorkerFetchEvent(pair.key)) {
continue;
}
request->header_list_->Append(pair.key, pair.value);
}
if (fetch_api_request->blob) {
DCHECK(fetch_api_request->body.IsEmpty());
request->SetBuffer(BodyStreamBuffer::Create(
script_state,
MakeGarbageCollected<BlobBytesConsumer>(
ExecutionContext::From(script_state), fetch_api_request->blob),
nullptr /* AbortSignal */, /*cached_metadata_handler=*/nullptr));
} else if (fetch_api_request->body.FormBody()) {
request->SetBuffer(BodyStreamBuffer::Create(
script_state,
MakeGarbageCollected<FormDataBytesConsumer>(
ExecutionContext::From(script_state),
fetch_api_request->body.FormBody()),
nullptr /* AbortSignal */, /*cached_metadata_handler=*/nullptr));
} else if (fetch_api_request->body.StreamBody()) {
mojo::ScopedDataPipeConsumerHandle readable;
mojo::ScopedDataPipeProducerHandle writable;
MojoCreateDataPipeOptions options{sizeof(MojoCreateDataPipeOptions),
MOJO_CREATE_DATA_PIPE_FLAG_NONE, 1, 0};
const MojoResult result =
mojo::CreateDataPipe(&options, writable, readable);
if (result == MOJO_RESULT_OK) {
DataPipeBytesConsumer::CompletionNotifier* completion_notifier = nullptr;
// Explicitly creating a ReadableStream here in order to remember
// that the request is created from a ReadableStream.
auto* stream =
BodyStreamBuffer::Create(
script_state,
MakeGarbageCollected<DataPipeBytesConsumer>(
ExecutionContext::From(script_state)
->GetTaskRunner(TaskType::kNetworking),
std::move(readable), &completion_notifier),
/*AbortSignal=*/nullptr, /*cached_metadata_handler=*/nullptr)
->Stream();
request->SetBuffer(
MakeGarbageCollected<BodyStreamBuffer>(script_state, stream,
/*AbortSignal=*/nullptr));
auto body_remote = std::make_unique<
mojo::Remote<network::mojom::blink::ChunkedDataPipeGetter>>(
fetch_api_request->body.TakeStreamBody());
body_remote->set_disconnect_handler(
WTF::Bind(SignalError, WrapPersistent(completion_notifier)));
auto* body_remote_raw = body_remote.get();
(*body_remote_raw)
->GetSize(WTF::Bind(SignalSize, std::move(body_remote),
WrapPersistent(completion_notifier)));
(*body_remote_raw)->StartReading(std::move(writable));
} else {
request->SetBuffer(BodyStreamBuffer::Create(
script_state, BytesConsumer::CreateErrored(BytesConsumer::Error()),
nullptr /* AbortSignal */, /*cached_metadata_handler=*/nullptr));
}
}
// Context is always set to FETCH later, so we don't copy it
// from fetch_api_request here.
// TODO(crbug.com/1045925): Remove this comment too when
// we deprecate SetContext.
request->SetDestination(fetch_api_request->destination);
request->SetReferrerString(AtomicString(Referrer::NoReferrer()));
if (fetch_api_request->referrer) {
if (!fetch_api_request->referrer->url.IsEmpty()) {
request->SetReferrerString(
AtomicString(fetch_api_request->referrer->url));
}
request->SetReferrerPolicy(fetch_api_request->referrer->policy);
}
request->SetMode(fetch_api_request->mode);
request->SetCredentials(fetch_api_request->credentials_mode);
request->SetCacheMode(fetch_api_request->cache_mode);
request->SetRedirect(fetch_api_request->redirect_mode);
request->SetMimeType(request->header_list_->ExtractMIMEType());
request->SetIntegrity(fetch_api_request->integrity);
request->SetKeepalive(fetch_api_request->keepalive);
request->SetIsHistoryNavigation(fetch_api_request->is_history_navigation);
request->SetPriority(ConvertRequestPriorityToResourceLoadPriority(
fetch_api_request->priority));
if (fetch_api_request->fetch_window_id)
request->SetWindowId(fetch_api_request->fetch_window_id.value());
return request;
}
FetchRequestData* FetchRequestData::CloneExceptBody() {
auto* request = MakeGarbageCollected<FetchRequestData>(execution_context_);
request->url_ = url_;
request->method_ = method_;
request->header_list_ = header_list_->Clone();
request->origin_ = origin_;
request->isolated_world_origin_ = isolated_world_origin_;
request->destination_ = destination_;
request->referrer_string_ = referrer_string_;
request->referrer_policy_ = referrer_policy_;
request->mode_ = mode_;
request->credentials_ = credentials_;
request->cache_mode_ = cache_mode_;
request->redirect_ = redirect_;
request->mime_type_ = mime_type_;
request->integrity_ = integrity_;
request->priority_ = priority_;
request->importance_ = importance_;
request->keepalive_ = keepalive_;
request->is_history_navigation_ = is_history_navigation_;
request->window_id_ = window_id_;
request->trust_token_params_ = trust_token_params_;
request->allow_http1_for_streaming_upload_ =
allow_http1_for_streaming_upload_;
return request;
}
FetchRequestData* FetchRequestData::Clone(ScriptState* script_state,
ExceptionState& exception_state) {
FetchRequestData* request = FetchRequestData::CloneExceptBody();
if (buffer_) {
BodyStreamBuffer* new1 = nullptr;
BodyStreamBuffer* new2 = nullptr;
buffer_->Tee(&new1, &new2, exception_state);
if (exception_state.HadException())
return nullptr;
buffer_ = new1;
request->buffer_ = new2;
}
if (url_loader_factory_.is_bound()) {
url_loader_factory_->Clone(
request->url_loader_factory_.BindNewPipeAndPassReceiver(
ExecutionContext::From(script_state)
->GetTaskRunner(TaskType::kNetworking)));
}
return request;
}
FetchRequestData* FetchRequestData::Pass(ScriptState* script_state) {
FetchRequestData* request = FetchRequestData::CloneExceptBody();
if (buffer_) {
request->buffer_ = buffer_;
buffer_ = BodyStreamBuffer::Create(
script_state, BytesConsumer::CreateClosed(), nullptr /* AbortSignal */,
/*cached_metadata_handler=*/nullptr);
buffer_->CloseAndLockAndDisturb();
}
request->url_loader_factory_ = std::move(url_loader_factory_);
return request;
}
FetchRequestData::~FetchRequestData() {}
FetchRequestData::FetchRequestData(ExecutionContext* execution_context)
: method_(http_names::kGET),
header_list_(MakeGarbageCollected<FetchHeaderList>()),
destination_(network::mojom::RequestDestination::kEmpty),
referrer_string_(Referrer::ClientReferrerString()),
referrer_policy_(network::mojom::ReferrerPolicy::kDefault),
mode_(network::mojom::RequestMode::kNoCors),
credentials_(network::mojom::CredentialsMode::kOmit),
cache_mode_(mojom::FetchCacheMode::kDefault),
redirect_(network::mojom::RedirectMode::kFollow),
importance_(mojom::FetchImportanceMode::kImportanceAuto),
priority_(ResourceLoadPriority::kUnresolved),
keepalive_(false),
url_loader_factory_(execution_context),
execution_context_(execution_context) {}
void FetchRequestData::Trace(Visitor* visitor) const {
visitor->Trace(buffer_);
visitor->Trace(header_list_);
visitor->Trace(url_loader_factory_);
visitor->Trace(execution_context_);
}
} // namespace blink