| // Copyright (c) 2012 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 "net/url_request/url_request_job_manager.h" |
| |
| #include <algorithm> |
| |
| #include "base/memory/singleton.h" |
| #include "build/build_config.h" |
| #include "base/string_util.h" |
| #include "net/base/load_flags.h" |
| #include "net/base/net_errors.h" |
| #include "net/base/network_delegate.h" |
| #include "net/url_request/url_request_about_job.h" |
| #include "net/url_request/url_request_context.h" |
| #include "net/url_request/url_request_data_job.h" |
| #include "net/url_request/url_request_error_job.h" |
| #include "net/url_request/url_request_file_job.h" |
| #if !defined(DISABLE_FTP_SUPPORT) |
| #include "net/url_request/url_request_ftp_job.h" |
| #endif |
| #include "net/url_request/url_request_http_job.h" |
| #include "net/url_request/url_request_job_factory.h" |
| |
| namespace net { |
| |
| // The built-in set of protocol factories |
| namespace { |
| |
| struct SchemeToFactory { |
| const char* scheme; |
| URLRequest::ProtocolFactory* factory; |
| }; |
| |
| } // namespace |
| |
| #if defined(COBALT) |
| static const SchemeToFactory kBuiltinFactories[] = { |
| {"https", URLRequestHttpJob::Factory}, |
| {"data", URLRequestDataJob::Factory}, |
| { "http", URLRequestHttpJob::Factory }, |
| #if defined(COBALT_ENABLE_FILE_SCHEME) |
| { "file", URLRequestFileJob::Factory }, |
| #endif |
| }; |
| #else |
| static const SchemeToFactory kBuiltinFactories[] = { |
| { "http", URLRequestHttpJob::Factory }, |
| { "https", URLRequestHttpJob::Factory }, |
| #if !defined(DISABLE_FTP_SUPPORT) |
| { "ftp", URLRequestFtpJob::Factory }, |
| #endif |
| { "file", URLRequestFileJob::Factory }, |
| { "about", URLRequestAboutJob::Factory }, |
| { "data", URLRequestDataJob::Factory }, |
| }; |
| #endif // defined(COBALT) |
| |
| // static |
| URLRequestJobManager* URLRequestJobManager::GetInstance() { |
| return Singleton<URLRequestJobManager>::get(); |
| } |
| |
| URLRequestJob* URLRequestJobManager::CreateJob( |
| URLRequest* request, NetworkDelegate* network_delegate) const { |
| DCHECK(IsAllowedThread()); |
| |
| // If we are given an invalid URL, then don't even try to inspect the scheme. |
| if (!request->url().is_valid()) |
| return new URLRequestErrorJob(request, network_delegate, ERR_INVALID_URL); |
| |
| // We do this here to avoid asking interceptors about unsupported schemes. |
| const URLRequestJobFactory* job_factory = NULL; |
| job_factory = request->context()->job_factory(); |
| |
| const std::string& scheme = request->url().scheme(); // already lowercase |
| if (job_factory) { |
| if (!job_factory->IsHandledProtocol(scheme)) { |
| return new URLRequestErrorJob( |
| request, network_delegate, ERR_UNKNOWN_URL_SCHEME); |
| } |
| } else if (!SupportsScheme(scheme)) { |
| return new URLRequestErrorJob( |
| request, network_delegate, ERR_UNKNOWN_URL_SCHEME); |
| } |
| |
| // THREAD-SAFETY NOTICE: |
| // We do not need to acquire the lock here since we are only reading our |
| // data structures. They should only be modified on the current thread. |
| |
| // See if the request should be intercepted. |
| // |
| |
| if (job_factory) { |
| URLRequestJob* job = job_factory->MaybeCreateJobWithInterceptor( |
| request, network_delegate); |
| if (job) |
| return job; |
| } |
| |
| // TODO(willchan): Remove this in favor of URLRequestJobFactory::Interceptor. |
| if (!(request->load_flags() & LOAD_DISABLE_INTERCEPT)) { |
| InterceptorList::const_iterator i; |
| for (i = interceptors_.begin(); i != interceptors_.end(); ++i) { |
| URLRequestJob* job = (*i)->MaybeIntercept(request, network_delegate); |
| if (job) |
| return job; |
| } |
| } |
| |
| if (job_factory) { |
| URLRequestJob* job = job_factory->MaybeCreateJobWithProtocolHandler( |
| scheme, request, network_delegate); |
| if (job) |
| return job; |
| } |
| |
| // TODO(willchan): Remove this in favor of |
| // URLRequestJobFactory::ProtocolHandler. |
| // See if the request should be handled by a registered protocol factory. |
| // If the registered factory returns null, then we want to fall-back to the |
| // built-in protocol factory. |
| FactoryMap::const_iterator i = factories_.find(scheme); |
| if (i != factories_.end()) { |
| URLRequestJob* job = i->second(request, network_delegate, scheme); |
| if (job) |
| return job; |
| } |
| |
| // See if the request should be handled by a built-in protocol factory. |
| for (size_t i = 0; i < arraysize(kBuiltinFactories); ++i) { |
| if (scheme == kBuiltinFactories[i].scheme) { |
| URLRequestJob* job = (kBuiltinFactories[i].factory)( |
| request, network_delegate, scheme); |
| DCHECK(job); // The built-in factories are not expected to fail! |
| return job; |
| } |
| } |
| |
| // If we reached here, then it means that a registered protocol factory |
| // wasn't interested in handling the URL. That is fairly unexpected, and we |
| // don't have a specific error to report here :-( |
| LOG(WARNING) << "Failed to map: " << request->url().spec(); |
| return new URLRequestErrorJob(request, network_delegate, ERR_FAILED); |
| } |
| |
| URLRequestJob* URLRequestJobManager::MaybeInterceptRedirect( |
| URLRequest* request, |
| NetworkDelegate* network_delegate, |
| const GURL& location) const { |
| DCHECK(IsAllowedThread()); |
| if (!request->url().is_valid() || |
| request->load_flags() & LOAD_DISABLE_INTERCEPT || |
| request->status().status() == URLRequestStatus::CANCELED) { |
| return NULL; |
| } |
| |
| const URLRequestJobFactory* job_factory = NULL; |
| job_factory = request->context()->job_factory(); |
| |
| const std::string& scheme = request->url().scheme(); // already lowercase |
| if (job_factory) { |
| if (!job_factory->IsHandledProtocol(scheme)) { |
| return NULL; |
| } |
| } else if (!SupportsScheme(scheme)) { |
| return NULL; |
| } |
| |
| URLRequestJob* job = NULL; |
| if (job_factory) |
| job = job_factory->MaybeInterceptRedirect( |
| location, request, network_delegate); |
| if (job) |
| return job; |
| |
| InterceptorList::const_iterator i; |
| for (i = interceptors_.begin(); i != interceptors_.end(); ++i) { |
| job = (*i)->MaybeInterceptRedirect(request, network_delegate, location); |
| if (job) |
| return job; |
| } |
| return NULL; |
| } |
| |
| URLRequestJob* URLRequestJobManager::MaybeInterceptResponse( |
| URLRequest* request, NetworkDelegate* network_delegate) const { |
| DCHECK(IsAllowedThread()); |
| if (!request->url().is_valid() || |
| request->load_flags() & LOAD_DISABLE_INTERCEPT || |
| request->status().status() == URLRequestStatus::CANCELED) { |
| return NULL; |
| } |
| |
| const URLRequestJobFactory* job_factory = NULL; |
| job_factory = request->context()->job_factory(); |
| |
| const std::string& scheme = request->url().scheme(); // already lowercase |
| if (job_factory) { |
| if (!job_factory->IsHandledProtocol(scheme)) { |
| return NULL; |
| } |
| } else if (!SupportsScheme(scheme)) { |
| return NULL; |
| } |
| |
| URLRequestJob* job = NULL; |
| if (job_factory) |
| job = job_factory->MaybeInterceptResponse(request, network_delegate); |
| if (job) |
| return job; |
| |
| InterceptorList::const_iterator i; |
| for (i = interceptors_.begin(); i != interceptors_.end(); ++i) { |
| job = (*i)->MaybeInterceptResponse(request, network_delegate); |
| if (job) |
| return job; |
| } |
| return NULL; |
| } |
| |
| bool URLRequestJobManager::SupportsScheme(const std::string& scheme) const { |
| // The set of registered factories may change on another thread. |
| { |
| base::AutoLock locked(lock_); |
| if (factories_.find(scheme) != factories_.end()) |
| return true; |
| } |
| |
| for (size_t i = 0; i < arraysize(kBuiltinFactories); ++i) |
| if (LowerCaseEqualsASCII(scheme, kBuiltinFactories[i].scheme)) |
| return true; |
| |
| return false; |
| } |
| |
| URLRequest::ProtocolFactory* URLRequestJobManager::RegisterProtocolFactory( |
| const std::string& scheme, |
| URLRequest::ProtocolFactory* factory) { |
| DCHECK(IsAllowedThread()); |
| |
| base::AutoLock locked(lock_); |
| |
| URLRequest::ProtocolFactory* old_factory; |
| FactoryMap::iterator i = factories_.find(scheme); |
| if (i != factories_.end()) { |
| old_factory = i->second; |
| } else { |
| old_factory = NULL; |
| } |
| if (factory) { |
| factories_[scheme] = factory; |
| } else if (i != factories_.end()) { // uninstall any old one |
| factories_.erase(i); |
| } |
| return old_factory; |
| } |
| |
| void URLRequestJobManager::RegisterRequestInterceptor( |
| URLRequest::Interceptor* interceptor) { |
| DCHECK(IsAllowedThread()); |
| |
| base::AutoLock locked(lock_); |
| |
| DCHECK(std::find(interceptors_.begin(), interceptors_.end(), interceptor) == |
| interceptors_.end()); |
| interceptors_.push_back(interceptor); |
| } |
| |
| void URLRequestJobManager::UnregisterRequestInterceptor( |
| URLRequest::Interceptor* interceptor) { |
| DCHECK(IsAllowedThread()); |
| |
| base::AutoLock locked(lock_); |
| |
| InterceptorList::iterator i = |
| std::find(interceptors_.begin(), interceptors_.end(), interceptor); |
| DCHECK(i != interceptors_.end()); |
| interceptors_.erase(i); |
| } |
| |
| URLRequestJobManager::URLRequestJobManager() |
| : allowed_thread_(0), |
| allowed_thread_initialized_(false) { |
| } |
| |
| URLRequestJobManager::~URLRequestJobManager() {} |
| |
| } // namespace net |