| // Copyright 2018 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 "services/network/cors/cors_url_loader_factory.h" |
| |
| #include "base/bind.h" |
| #include "base/logging.h" |
| #include "net/base/load_flags.h" |
| #include "services/network/cors/cors_url_loader.h" |
| #include "services/network/cors/preflight_controller.h" |
| #include "services/network/network_context.h" |
| #include "services/network/public/cpp/cors/cors.h" |
| #include "services/network/public/cpp/features.h" |
| #include "services/network/public/cpp/resource_request.h" |
| #include "services/network/public/mojom/fetch_api.mojom.h" |
| #include "services/network/resource_scheduler_client.h" |
| #include "services/network/url_loader_factory.h" |
| |
| namespace network { |
| |
| namespace cors { |
| |
| CorsURLLoaderFactory::CorsURLLoaderFactory( |
| NetworkContext* context, |
| mojom::URLLoaderFactoryParamsPtr params, |
| scoped_refptr<ResourceSchedulerClient> resource_scheduler_client, |
| mojom::URLLoaderFactoryRequest request, |
| const OriginAccessList* origin_access_list, |
| std::unique_ptr<mojom::URLLoaderFactory> network_loader_factory_for_testing) |
| : context_(context), |
| disable_web_security_(params->disable_web_security), |
| process_id_(params->process_id), |
| origin_access_list_(origin_access_list) { |
| DCHECK(context_); |
| DCHECK(origin_access_list_); |
| factory_bound_origin_access_list_ = std::make_unique<OriginAccessList>(); |
| if (params->factory_bound_allow_patterns.size()) { |
| DCHECK(params->request_initiator_site_lock); |
| factory_bound_origin_access_list_->SetAllowListForOrigin( |
| *params->request_initiator_site_lock, |
| params->factory_bound_allow_patterns); |
| } |
| network_loader_factory_ = |
| network_loader_factory_for_testing |
| ? std::move(network_loader_factory_for_testing) |
| : std::make_unique<network::URLLoaderFactory>( |
| context, std::move(params), |
| std::move(resource_scheduler_client), this); |
| |
| bindings_.AddBinding(this, std::move(request)); |
| bindings_.set_connection_error_handler(base::BindRepeating( |
| &CorsURLLoaderFactory::DeleteIfNeeded, base::Unretained(this))); |
| } |
| |
| CorsURLLoaderFactory::~CorsURLLoaderFactory() = default; |
| |
| void CorsURLLoaderFactory::OnLoaderCreated( |
| std::unique_ptr<mojom::URLLoader> loader) { |
| if (context_) |
| context_->LoaderCreated(process_id_); |
| loaders_.insert(std::move(loader)); |
| } |
| |
| void CorsURLLoaderFactory::DestroyURLLoader(mojom::URLLoader* loader) { |
| if (context_) |
| context_->LoaderDestroyed(process_id_); |
| auto it = loaders_.find(loader); |
| DCHECK(it != loaders_.end()); |
| loaders_.erase(it); |
| |
| DeleteIfNeeded(); |
| } |
| |
| void CorsURLLoaderFactory::CreateLoaderAndStart( |
| mojom::URLLoaderRequest request, |
| int32_t routing_id, |
| int32_t request_id, |
| uint32_t options, |
| const ResourceRequest& resource_request, |
| mojom::URLLoaderClientPtr client, |
| const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) { |
| if (!IsSane(context_, resource_request)) { |
| client->OnComplete(URLLoaderCompletionStatus(net::ERR_INVALID_ARGUMENT)); |
| return; |
| } |
| |
| if (features::ShouldEnableOutOfBlinkCors() && !disable_web_security_) { |
| auto loader = std::make_unique<CorsURLLoader>( |
| std::move(request), routing_id, request_id, options, |
| base::BindOnce(&CorsURLLoaderFactory::DestroyURLLoader, |
| base::Unretained(this)), |
| resource_request, std::move(client), traffic_annotation, |
| network_loader_factory_.get(), origin_access_list_, |
| factory_bound_origin_access_list_.get(), |
| context_->cors_preflight_controller()); |
| auto* raw_loader = loader.get(); |
| OnLoaderCreated(std::move(loader)); |
| raw_loader->Start(); |
| } else { |
| network_loader_factory_->CreateLoaderAndStart( |
| std::move(request), routing_id, request_id, options, resource_request, |
| std::move(client), traffic_annotation); |
| } |
| } |
| |
| void CorsURLLoaderFactory::Clone(mojom::URLLoaderFactoryRequest request) { |
| // The cloned factories stop working when this factory is destructed. |
| bindings_.AddBinding(this, std::move(request)); |
| } |
| |
| void CorsURLLoaderFactory::ClearBindings() { |
| bindings_.CloseAllBindings(); |
| } |
| |
| void CorsURLLoaderFactory::DeleteIfNeeded() { |
| if (!context_) |
| return; |
| if (bindings_.empty() && loaders_.empty()) |
| context_->DestroyURLLoaderFactory(this); |
| } |
| |
| bool CorsURLLoaderFactory::IsSane(const NetworkContext* context, |
| const ResourceRequest& request) { |
| // CORS needs a proper origin (including a unique opaque origin). If the |
| // request doesn't have one, CORS cannot work. |
| if (!request.request_initiator && |
| request.fetch_request_mode != mojom::FetchRequestMode::kNavigate && |
| request.fetch_request_mode != mojom::FetchRequestMode::kNoCors) { |
| LOG(WARNING) << "|fetch_request_mode| is " << request.fetch_request_mode |
| << ", but |request_initiator| is not set."; |
| return false; |
| } |
| |
| const auto load_flags_pattern = net::LOAD_DO_NOT_SAVE_COOKIES | |
| net::LOAD_DO_NOT_SEND_COOKIES | |
| net::LOAD_DO_NOT_SEND_AUTH_DATA; |
| // The credentials mode and load_flags should match. |
| if (request.fetch_credentials_mode == mojom::FetchCredentialsMode::kOmit && |
| (request.load_flags & load_flags_pattern) != load_flags_pattern) { |
| LOG(WARNING) << "|fetch_credentials_mode| and |load_flags| contradict each " |
| "other."; |
| return false; |
| } |
| |
| if (context) { |
| net::HttpRequestHeaders::Iterator header_iterator( |
| request.cors_exempt_headers); |
| const auto& allowed_exempt_headers = context->cors_exempt_header_list(); |
| while (header_iterator.GetNext()) { |
| if (allowed_exempt_headers.find(header_iterator.name()) != |
| allowed_exempt_headers.end()) { |
| continue; |
| } |
| LOG(WARNING) << "|cors_exempt_headers| contains unexpected key: " |
| << header_iterator.name(); |
| return false; |
| } |
| } |
| |
| // TODO(yhirano): If the request mode is "no-cors", the redirect mode should |
| // be "follow". |
| return true; |
| } |
| |
| } // namespace cors |
| |
| } // namespace network |