| Index: ios/crnet/crnet_environment.mm
|
| diff --git a/ios/crnet/crnet_environment.mm b/ios/crnet/crnet_environment.mm
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..d216159005e32badc7ec32531358694e6ba0f0c1
|
| --- /dev/null
|
| +++ b/ios/crnet/crnet_environment.mm
|
| @@ -0,0 +1,464 @@
|
| +// 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 "ios/crnet/crnet_environment.h"
|
| +
|
| +#import <Foundation/Foundation.h>
|
| +
|
| +#include "base/at_exit.h"
|
| +#include "base/command_line.h"
|
| +#include "base/i18n/icu_util.h"
|
| +#include "base/json/json_writer.h"
|
| +#include "base/mac/bind_objc_block.h"
|
| +#include "base/mac/foundation_util.h"
|
| +#include "base/mac/scoped_block.h"
|
| +#include "base/path_service.h"
|
| +#include "base/threading/worker_pool.h"
|
| +#import "components/webp_transcode/webp_network_client_factory.h"
|
| +#include "crypto/nss_util.h"
|
| +#include "ios/net/cookies/cookie_store_ios.h"
|
| +#include "ios/net/crn_http_protocol_handler.h"
|
| +#include "ios/net/request_tracker.h"
|
| +#include "ios/web/public/user_agent.h"
|
| +#include "net/base/net_errors.h"
|
| +#include "net/base/network_change_notifier.h"
|
| +#include "net/base/sdch_manager.h"
|
| +#include "net/cert/cert_verifier.h"
|
| +#include "net/cert_net/nss_ocsp.h"
|
| +#include "net/disk_cache/disk_cache.h"
|
| +#include "net/http/http_auth_handler_factory.h"
|
| +#include "net/http/http_cache.h"
|
| +#include "net/http/http_server_properties_impl.h"
|
| +#include "net/http/http_stream_factory.h"
|
| +#include "net/http/http_util.h"
|
| +#include "net/proxy/proxy_service.h"
|
| +#include "net/socket/next_proto.h"
|
| +#include "net/ssl/channel_id_service.h"
|
| +#include "net/ssl/default_channel_id_store.h"
|
| +#include "net/ssl/ssl_config_service_defaults.h"
|
| +#include "net/url_request/data_protocol_handler.h"
|
| +#include "net/url_request/file_protocol_handler.h"
|
| +#include "net/url_request/sdch_dictionary_fetcher.h"
|
| +#include "net/url_request/static_http_user_agent_settings.h"
|
| +#include "net/url_request/url_request_context_getter.h"
|
| +#include "net/url_request/url_request_context_storage.h"
|
| +#include "net/url_request/url_request_job_factory_impl.h"
|
| +#include "url/url_util.h"
|
| +
|
| +namespace {
|
| +
|
| +base::AtExitManager* g_at_exit_ = nullptr;
|
| +
|
| +// Request context getter for CrNet.
|
| +class CrNetURLRequestContextGetter : public net::URLRequestContextGetter {
|
| + public:
|
| + CrNetURLRequestContextGetter(
|
| + net::URLRequestContext* context,
|
| + const scoped_refptr<base::MessageLoopProxy>& loop)
|
| + : context_(context), loop_(loop) {}
|
| +
|
| + net::URLRequestContext* GetURLRequestContext() override { return context_; }
|
| +
|
| + scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner()
|
| + const override {
|
| + return loop_;
|
| + }
|
| + private:
|
| + // Must be called on the IO thread.
|
| + ~CrNetURLRequestContextGetter() override {}
|
| +
|
| + net::URLRequestContext* context_;
|
| + scoped_refptr<base::MessageLoopProxy> loop_;
|
| + DISALLOW_COPY_AND_ASSIGN(CrNetURLRequestContextGetter);
|
| +};
|
| +
|
| +} // namespace
|
| +
|
| +// net::HTTPProtocolHandlerDelegate for CrNet.
|
| +class CrNetHttpProtocolHandlerDelegate
|
| + : public net::HTTPProtocolHandlerDelegate {
|
| + public:
|
| + CrNetHttpProtocolHandlerDelegate(net::URLRequestContextGetter* getter,
|
| + RequestFilterBlock filter)
|
| + : getter_(getter), filter_(filter, base::scoped_policy::RETAIN) {}
|
| +
|
| + private:
|
| + // net::HTTPProtocolHandlerDelegate implementation:
|
| + bool CanHandleRequest(NSURLRequest* request) override {
|
| + // Don't advertise support for file:// URLs for now.
|
| + // This broke some time ago but it's not clear how to fix it at the moment.
|
| + // http://crbug.com/480620
|
| + if ([[[request URL] scheme] caseInsensitiveCompare:@"file"] ==
|
| + NSOrderedSame) {
|
| + return false;
|
| + }
|
| + if (filter_) {
|
| + RequestFilterBlock block = filter_.get();
|
| + return block(request);
|
| + }
|
| + return true;
|
| + }
|
| +
|
| + bool IsRequestSupported(NSURLRequest* request) override {
|
| + NSString* scheme = [[request URL] scheme];
|
| + if (!scheme)
|
| + return false;
|
| + return [scheme caseInsensitiveCompare:@"data"] == NSOrderedSame ||
|
| + [scheme caseInsensitiveCompare:@"http"] == NSOrderedSame ||
|
| + [scheme caseInsensitiveCompare:@"https"] == NSOrderedSame;
|
| + }
|
| +
|
| + net::URLRequestContextGetter* GetDefaultURLRequestContext() override {
|
| + return getter_.get();
|
| + }
|
| +
|
| + scoped_refptr<net::URLRequestContextGetter> getter_;
|
| + base::mac::ScopedBlock<RequestFilterBlock> filter_;
|
| +};
|
| +
|
| +void CrNetEnvironment::PostToNetworkThread(
|
| + const tracked_objects::Location& from_here,
|
| + const base::Closure& task) {
|
| + network_io_thread_->message_loop()->PostTask(from_here, task);
|
| +}
|
| +
|
| +void CrNetEnvironment::PostToFileUserBlockingThread(
|
| + const tracked_objects::Location& from_here,
|
| + const base::Closure& task) {
|
| + file_user_blocking_thread_->message_loop()->PostTask(from_here, task);
|
| +}
|
| +
|
| +// static
|
| +void CrNetEnvironment::Initialize() {
|
| + DCHECK_EQ([NSThread currentThread], [NSThread mainThread]);
|
| + if (!g_at_exit_)
|
| + g_at_exit_ = new base::AtExitManager;
|
| +
|
| + CHECK(base::i18n::InitializeICU());
|
| + url::Initialize();
|
| + base::CommandLine::Init(0, nullptr);
|
| + // This needs to happen on the main thread. NSPR's initialization sets up its
|
| + // memory allocator; if this is not done before other threads are created,
|
| + // this initialization can race to cause accidental free/allocation
|
| + // mismatches.
|
| + crypto::EnsureNSPRInit();
|
| +
|
| + // Create a message loop on the UI thread.
|
| + base::MessageLoop* main_message_loop =
|
| + new base::MessageLoop(base::MessageLoop::TYPE_UI);
|
| +#pragma unused(main_message_loop)
|
| + base::MessageLoopForUI::current()->Attach();
|
| +}
|
| +
|
| +void CrNetEnvironment::StartNetLog(base::FilePath::StringType file_name,
|
| + bool log_bytes) {
|
| + DCHECK(file_name.length());
|
| + base::AutoLock lock(net_log_lock_);
|
| + if (net_log_started_) {
|
| + return;
|
| + }
|
| + net_log_started_ = true;
|
| + PostToFileUserBlockingThread(FROM_HERE,
|
| + base::Bind(&CrNetEnvironment::StartNetLogInternal,
|
| + base::Unretained(this), file_name, log_bytes));
|
| +}
|
| +
|
| +void CrNetEnvironment::StartNetLogInternal(
|
| + base::FilePath::StringType file_name, bool log_bytes) {
|
| + DCHECK(base::MessageLoop::current() ==
|
| + file_user_blocking_thread_->message_loop());
|
| + DCHECK(file_name.length());
|
| + if (!net_log_.get()) {
|
| + net_log_.reset(new CrNetNetLog());
|
| + main_context_.get()->set_net_log(net_log_.get());
|
| + }
|
| + CrNetNetLog::Mode mode = log_bytes ? CrNetNetLog::LOG_ALL_BYTES :
|
| + CrNetNetLog::LOG_STRIP_PRIVATE_DATA;
|
| + net_log_->Start(base::FilePath(file_name), mode);
|
| +}
|
| +
|
| +void CrNetEnvironment::StopNetLog() {
|
| + base::AutoLock lock(net_log_lock_);
|
| + if (!net_log_started_) {
|
| + return;
|
| + }
|
| + net_log_started_ = false;
|
| + PostToFileUserBlockingThread(FROM_HERE,
|
| + base::Bind(&CrNetEnvironment::StopNetLogInternal,
|
| + base::Unretained(this)));
|
| +}
|
| +
|
| +void CrNetEnvironment::StopNetLogInternal() {
|
| + DCHECK(base::MessageLoop::current() ==
|
| + file_user_blocking_thread_->message_loop());
|
| + if (net_log_.get()) {
|
| + net_log_->Stop();
|
| + }
|
| +}
|
| +
|
| +void CrNetEnvironment::CloseAllSpdySessions() {
|
| + PostToNetworkThread(FROM_HERE,
|
| + base::Bind(&CrNetEnvironment::CloseAllSpdySessionsInternal,
|
| + base::Unretained(this)));
|
| +}
|
| +
|
| +void CrNetEnvironment::SetRequestFilterBlock(RequestFilterBlock block) {
|
| + http_protocol_handler_delegate_.reset(
|
| + new CrNetHttpProtocolHandlerDelegate(main_context_getter_.get(), block));
|
| + net::HTTPProtocolHandlerDelegate::SetInstance(
|
| + http_protocol_handler_delegate_.get());
|
| +}
|
| +
|
| +net::HttpNetworkSession* CrNetEnvironment::GetHttpNetworkSession(
|
| + net::URLRequestContext* context) {
|
| + DCHECK(context);
|
| + if (!context->http_transaction_factory())
|
| + return nullptr;
|
| +
|
| + return context->http_transaction_factory()->GetSession();
|
| +}
|
| +
|
| +void CrNetEnvironment::CloseAllSpdySessionsInternal() {
|
| + DCHECK(base::MessageLoop::current() ==
|
| + network_io_thread_->message_loop());
|
| +
|
| + net::HttpNetworkSession* http_network_session =
|
| + GetHttpNetworkSession(GetMainContextGetter()->GetURLRequestContext());
|
| +
|
| + if (http_network_session) {
|
| + net::SpdySessionPool *spdy_session_pool =
|
| + http_network_session->spdy_session_pool();
|
| + if (spdy_session_pool)
|
| + spdy_session_pool->CloseCurrentSessions(net::ERR_ABORTED);
|
| + }
|
| +}
|
| +
|
| +CrNetEnvironment::CrNetEnvironment(std::string user_agent_product_name)
|
| + : main_context_(new net::URLRequestContext),
|
| + user_agent_product_name_(user_agent_product_name) {
|
| +
|
| +}
|
| +
|
| +void CrNetEnvironment::Install() {
|
| + // Threads setup.
|
| + network_cache_thread_.reset(new base::Thread("Chrome Network Cache Thread"));
|
| + network_cache_thread_->StartWithOptions(
|
| + base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
|
| + network_io_thread_.reset(new base::Thread("Chrome Network IO Thread"));
|
| + network_io_thread_->StartWithOptions(
|
| + base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
|
| + file_thread_.reset(new base::Thread("Chrome File Thread"));
|
| + file_thread_->StartWithOptions(
|
| + base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
|
| + file_user_blocking_thread_.reset(
|
| + new base::Thread("Chrome File User Blocking Thread"));
|
| + file_user_blocking_thread_->StartWithOptions(
|
| + base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
|
| +
|
| + // The network change notifier must be initialized so that registered
|
| + // delegates will receive callbacks.
|
| + network_change_notifier_.reset(net::NetworkChangeNotifier::Create());
|
| + proxy_config_service_.reset(net::ProxyService::CreateSystemProxyConfigService(
|
| + network_io_thread_->message_loop_proxy(), nullptr));
|
| +
|
| + PostToNetworkThread(FROM_HERE,
|
| + base::Bind(&CrNetEnvironment::InitializeOnNetworkThread,
|
| + base::Unretained(this)));
|
| +
|
| + net::SetURLRequestContextForNSSHttpIO(main_context_.get());
|
| + main_context_getter_ = new CrNetURLRequestContextGetter(
|
| + main_context_.get(), network_io_thread_->message_loop_proxy());
|
| + SetRequestFilterBlock(nil);
|
| + net_log_started_ = false;
|
| +}
|
| +
|
| +void CrNetEnvironment::InstallIntoSessionConfiguration(
|
| + NSURLSessionConfiguration* config) {
|
| + config.protocolClasses = @[ [CRNHTTPProtocolHandler class] ];
|
| +}
|
| +
|
| +CrNetEnvironment::~CrNetEnvironment() {
|
| + net::HTTPProtocolHandlerDelegate::SetInstance(nullptr);
|
| + net::SetURLRequestContextForNSSHttpIO(nullptr);
|
| +}
|
| +
|
| +net::URLRequestContextGetter* CrNetEnvironment::GetMainContextGetter() {
|
| + return main_context_getter_.get();
|
| +}
|
| +
|
| +void CrNetEnvironment::SetHTTPProtocolHandlerRegistered(bool registered) {
|
| + if (registered) {
|
| + // Disable the default cache.
|
| + [NSURLCache setSharedURLCache:nil];
|
| + // Register the chrome http protocol handler to replace the default one.
|
| + BOOL success = [NSURLProtocol registerClass:[CRNHTTPProtocolHandler class]];
|
| + DCHECK(success);
|
| + } else {
|
| + // Set up an empty default cache, with default size.
|
| + // TODO(droger): If the NSURLCache is to be used, its size should most
|
| + // likely be changed. On an iPod2 with iOS4, the default size is 512k.
|
| + [NSURLCache setSharedURLCache:[[[NSURLCache alloc] init] autorelease]];
|
| + [NSURLProtocol unregisterClass:[CRNHTTPProtocolHandler class]];
|
| + }
|
| +}
|
| +
|
| +void CrNetEnvironment::InitializeOnNetworkThread() {
|
| + DCHECK(base::MessageLoop::current() == network_io_thread_->message_loop());
|
| +
|
| + // Register network clients.
|
| + net::RequestTracker::AddGlobalNetworkClientFactory(
|
| + [[[WebPNetworkClientFactory alloc]
|
| + initWithTaskRunner:file_user_blocking_thread_
|
| + ->message_loop_proxy()] autorelease]);
|
| +
|
| +#if 0
|
| + // TODO(huey): Re-enable this once SDCH supports SSL and dictionaries from
|
| + // previous sessions can be used on the first request after a fresh launch.
|
| + sdch_manager_.reset(new net::SdchManager());
|
| + sdch_manager_->set_sdch_fetcher(
|
| + new SdchDictionaryFetcher(main_context_getter_));
|
| +#else
|
| + // Otherwise, explicitly disable SDCH to avoid a crash.
|
| + net::SdchManager::EnableSdchSupport(false);
|
| +#endif
|
| +
|
| + NSString* bundlePath =
|
| + [[NSBundle mainBundle] pathForResource:@"crnet_resources"
|
| + ofType:@"bundle"];
|
| + NSBundle* bundle = [NSBundle bundleWithPath:bundlePath];
|
| + NSString* acceptableLanguages = NSLocalizedStringWithDefaultValue(
|
| + @"IDS_ACCEPT_LANGUAGES",
|
| + @"Localizable",
|
| + bundle,
|
| + @"en-US,en",
|
| + @"These values are copied from Chrome's .xtb files, so the same "
|
| + "values are used in the |Accept-Language| header. Key name matches "
|
| + "Chrome's.");
|
| + DCHECK(acceptableLanguages);
|
| + std::string acceptable_languages =
|
| + [acceptableLanguages cStringUsingEncoding:NSUTF8StringEncoding];
|
| + std::string user_agent =
|
| + web::BuildUserAgentFromProduct(user_agent_product_name_);
|
| + // Set the user agent through NSUserDefaults. This sets it for both
|
| + // UIWebViews and WKWebViews, and javascript calls to navigator.userAgent
|
| + // return this value.
|
| + [[NSUserDefaults standardUserDefaults] registerDefaults:@{
|
| + @"UserAgent" : [NSString stringWithUTF8String:user_agent.c_str()]
|
| + }];
|
| + main_context_->set_http_user_agent_settings(
|
| + new net::StaticHttpUserAgentSettings(acceptable_languages, user_agent));
|
| +
|
| + main_context_->set_ssl_config_service(new net::SSLConfigServiceDefaults);
|
| + main_context_->set_transport_security_state(
|
| + new net::TransportSecurityState());
|
| + http_server_properties_.reset(new net::HttpServerPropertiesImpl());
|
| + main_context_->set_http_server_properties(
|
| + http_server_properties_->GetWeakPtr());
|
| + main_context_->set_host_resolver(
|
| + net::HostResolver::CreateDefaultResolver(nullptr).release());
|
| + main_context_->set_cert_verifier(net::CertVerifier::CreateDefault());
|
| + main_context_->set_http_auth_handler_factory(
|
| + net::HttpAuthHandlerRegistryFactory::CreateDefault(
|
| + main_context_->host_resolver()));
|
| + main_context_->set_proxy_service(
|
| + net::ProxyService::CreateUsingSystemProxyResolver(
|
| + proxy_config_service_.get(), 0, nullptr));
|
| +
|
| + // Cache
|
| + NSArray* dirs = NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
|
| + NSUserDomainMask,
|
| + YES);
|
| + base::FilePath cache_path =
|
| + base::mac::NSStringToFilePath([dirs objectAtIndex:0]);
|
| + cache_path = cache_path.Append(FILE_PATH_LITERAL("crnet"));
|
| + net::HttpCache::DefaultBackend* main_backend =
|
| + new net::HttpCache::DefaultBackend(
|
| + net::DISK_CACHE,
|
| + net::CACHE_BACKEND_DEFAULT,
|
| + cache_path,
|
| + 0, // Default cache size.
|
| + network_cache_thread_->message_loop_proxy());
|
| +
|
| + net::HttpNetworkSession::Params params;
|
| + params.host_resolver = main_context_->host_resolver();
|
| + params.cert_verifier = main_context_->cert_verifier();
|
| + params.channel_id_service = main_context_->channel_id_service();
|
| + params.transport_security_state = main_context_->transport_security_state();
|
| + params.proxy_service = main_context_->proxy_service();
|
| + params.ssl_session_cache_shard = "";
|
| + params.ssl_config_service = main_context_->ssl_config_service();
|
| + params.http_auth_handler_factory = main_context_->http_auth_handler_factory();
|
| + params.network_delegate = main_context_->network_delegate();
|
| + params.http_server_properties = main_context_->http_server_properties();
|
| + params.net_log = main_context_->net_log();
|
| + params.next_protos =
|
| + net::NextProtosWithSpdyAndQuic(spdy_enabled(), quic_enabled());
|
| + params.use_alternate_protocols = true;
|
| + params.enable_quic = quic_enabled();
|
| + params.alternative_service_probability_threshold =
|
| + alternate_protocol_threshold_;
|
| +
|
| + if (!params.channel_id_service) {
|
| + // The main context may not have a ChannelIDService, since it is lazily
|
| + // constructed. If not, build an ephemeral ChannelIDService with no backing
|
| + // disk store.
|
| + // TODO(ellyjones): support persisting ChannelID.
|
| + params.channel_id_service = new net::ChannelIDService(
|
| + new net::DefaultChannelIDStore(NULL),
|
| + base::WorkerPool::GetTaskRunner(true));
|
| + }
|
| +
|
| + net::HttpCache* main_cache = new net::HttpCache(params, main_backend);
|
| + main_context_->set_http_transaction_factory(main_cache);
|
| +
|
| + // Cookies
|
| + scoped_refptr<net::CookieStore> cookie_store =
|
| + net::CookieStoreIOS::CreateCookieStoreFromNSHTTPCookieStorage();
|
| + main_context_->set_cookie_store(cookie_store.get());
|
| +
|
| + net::URLRequestJobFactoryImpl* job_factory =
|
| + new net::URLRequestJobFactoryImpl;
|
| + job_factory->SetProtocolHandler("data", new net::DataProtocolHandler);
|
| + job_factory->SetProtocolHandler(
|
| + "file", new net::FileProtocolHandler(file_thread_->message_loop_proxy()));
|
| + main_context_->set_job_factory(job_factory);
|
| +}
|
| +
|
| +std::string CrNetEnvironment::user_agent() {
|
| + const net::HttpUserAgentSettings* user_agent_settings =
|
| + main_context_->http_user_agent_settings();
|
| + if (!user_agent_settings) {
|
| + return nullptr;
|
| + }
|
| +
|
| + return user_agent_settings->GetUserAgent();
|
| +}
|
| +
|
| +void CrNetEnvironment::ClearCache(ClearCacheCallback callback) {
|
| + PostToNetworkThread(FROM_HERE,
|
| + base::Bind(&CrNetEnvironment::ClearCacheOnNetworkThread,
|
| + base::Unretained(this),
|
| + callback));
|
| +}
|
| +
|
| +void CrNetEnvironment::ClearCacheOnNetworkThread(ClearCacheCallback callback) {
|
| + DCHECK(base::MessageLoop::current() == network_io_thread_->message_loop());
|
| + __block disk_cache::Backend* backend = nullptr;
|
| + net::HttpCache* cache = main_context_->http_transaction_factory()->GetCache();
|
| + net::CompletionCallback client_callback = base::BindBlock(^(int error) {
|
| + if (callback != nil) {
|
| + callback(error);
|
| + }
|
| + });
|
| + net::CompletionCallback doom_callback = base::BindBlock(^(int error) {
|
| + if (backend)
|
| + backend->DoomAllEntries(client_callback);
|
| + });
|
| + int rc = cache->GetBackend(&backend, doom_callback);
|
| + if (rc != net::ERR_IO_PENDING) {
|
| + // GetBackend doesn't call the callback if it completes synchronously, so
|
| + // call it directly here.
|
| + doom_callback.Run(rc);
|
| + }
|
| +}
|
|
|