Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(402)

Unified Diff: ios/crnet/crnet_environment.mm

Issue 1125293004: ios: add CrNet (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: ? Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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);
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698