// Copyright 2011 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "net/url_request/url_request_job_factory.h" #include "base/containers/contains.h" #include "net/base/net_errors.h" #include "net/net_buildflags.h" #include "net/url_request/url_request.h" #include "net/url_request/url_request_error_job.h" #include "net/url_request/url_request_http_job.h" #include "net/url_request/url_request_interceptor.h" #include "url/gurl.h" #include "url/url_constants.h" namespace net { namespace { URLRequestInterceptor* g_interceptor_for_testing = nullptr; // TODO(mmenke): Look into removing this class and // URLRequestJobFactory::ProtocolHandlers completely. The only other subclass // is iOS-only. class HttpProtocolHandler : public URLRequestJobFactory::ProtocolHandler { public: // URLRequest::is_for_websockets() must match `is_for_websockets`, or requests // will be failed. This is so that attempts to fetch WebSockets requests // fails, and attempts to use HTTP URLs for WebSockets also fail. explicit HttpProtocolHandler(bool is_for_websockets) : is_for_websockets_(is_for_websockets) {} HttpProtocolHandler(const HttpProtocolHandler&) = delete; HttpProtocolHandler& operator=(const HttpProtocolHandler&) = delete; ~HttpProtocolHandler() override = default; std::unique_ptr CreateJob(URLRequest* request) const override { if (request->is_for_websockets() != is_for_websockets_) { return std::make_unique(request, ERR_UNKNOWN_URL_SCHEME); } return URLRequestHttpJob::Create(request); } const bool is_for_websockets_; }; } // namespace URLRequestJobFactory::ProtocolHandler::~ProtocolHandler() = default; bool URLRequestJobFactory::ProtocolHandler::IsSafeRedirectTarget( const GURL& location) const { return true; } URLRequestJobFactory::URLRequestJobFactory() { SetProtocolHandler(url::kHttpScheme, std::make_unique( /*is_for_websockets=*/false)); SetProtocolHandler(url::kHttpsScheme, std::make_unique( /*is_for_websockets=*/false)); #if BUILDFLAG(ENABLE_WEBSOCKETS) SetProtocolHandler(url::kWsScheme, std::make_unique( /*is_for_websockets=*/true)); SetProtocolHandler(url::kWssScheme, std::make_unique( /*is_for_websockets=*/true)); #endif // BUILDFLAG(ENABLE_WEBSOCKETS) } URLRequestJobFactory::~URLRequestJobFactory() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); } bool URLRequestJobFactory::SetProtocolHandler( const std::string& scheme, std::unique_ptr protocol_handler) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); if (!protocol_handler) { auto it = protocol_handler_map_.find(scheme); if (it == protocol_handler_map_.end()) return false; protocol_handler_map_.erase(it); return true; } if (base::Contains(protocol_handler_map_, scheme)) return false; protocol_handler_map_[scheme] = std::move(protocol_handler); return true; } std::unique_ptr URLRequestJobFactory::CreateJob( URLRequest* request) const { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); // If we are given an invalid URL, then don't even try to inspect the scheme. if (!request->url().is_valid()) return std::make_unique(request, ERR_INVALID_URL); if (g_interceptor_for_testing) { std::unique_ptr job( g_interceptor_for_testing->MaybeInterceptRequest(request)); if (job) return job; } auto it = protocol_handler_map_.find(request->url().scheme()); if (it == protocol_handler_map_.end()) { return std::make_unique(request, ERR_UNKNOWN_URL_SCHEME); } return it->second->CreateJob(request); } bool URLRequestJobFactory::IsSafeRedirectTarget(const GURL& location) const { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); if (!location.is_valid()) { // Error cases are safely handled. return true; } auto it = protocol_handler_map_.find(location.scheme()); if (it == protocol_handler_map_.end()) { // Unhandled cases are safely handled. return true; } return it->second->IsSafeRedirectTarget(location); } void URLRequestJobFactory::SetInterceptorForTesting( URLRequestInterceptor* interceptor) { DCHECK(!interceptor || !g_interceptor_for_testing); g_interceptor_for_testing = interceptor; } } // namespace net