Include all dupe types (event when value is zero) in scan stats.
[chromium-blink-merge.git] / ios / crnet / crnet_environment.mm
blobd216159005e32badc7ec32531358694e6ba0f0c1
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ios/crnet/crnet_environment.h"
7 #import <Foundation/Foundation.h>
9 #include "base/at_exit.h"
10 #include "base/command_line.h"
11 #include "base/i18n/icu_util.h"
12 #include "base/json/json_writer.h"
13 #include "base/mac/bind_objc_block.h"
14 #include "base/mac/foundation_util.h"
15 #include "base/mac/scoped_block.h"
16 #include "base/path_service.h"
17 #include "base/threading/worker_pool.h"
18 #import "components/webp_transcode/webp_network_client_factory.h"
19 #include "crypto/nss_util.h"
20 #include "ios/net/cookies/cookie_store_ios.h"
21 #include "ios/net/crn_http_protocol_handler.h"
22 #include "ios/net/request_tracker.h"
23 #include "ios/web/public/user_agent.h"
24 #include "net/base/net_errors.h"
25 #include "net/base/network_change_notifier.h"
26 #include "net/base/sdch_manager.h"
27 #include "net/cert/cert_verifier.h"
28 #include "net/cert_net/nss_ocsp.h"
29 #include "net/disk_cache/disk_cache.h"
30 #include "net/http/http_auth_handler_factory.h"
31 #include "net/http/http_cache.h"
32 #include "net/http/http_server_properties_impl.h"
33 #include "net/http/http_stream_factory.h"
34 #include "net/http/http_util.h"
35 #include "net/proxy/proxy_service.h"
36 #include "net/socket/next_proto.h"
37 #include "net/ssl/channel_id_service.h"
38 #include "net/ssl/default_channel_id_store.h"
39 #include "net/ssl/ssl_config_service_defaults.h"
40 #include "net/url_request/data_protocol_handler.h"
41 #include "net/url_request/file_protocol_handler.h"
42 #include "net/url_request/sdch_dictionary_fetcher.h"
43 #include "net/url_request/static_http_user_agent_settings.h"
44 #include "net/url_request/url_request_context_getter.h"
45 #include "net/url_request/url_request_context_storage.h"
46 #include "net/url_request/url_request_job_factory_impl.h"
47 #include "url/url_util.h"
49 namespace {
51 base::AtExitManager* g_at_exit_ = nullptr;
53 // Request context getter for CrNet.
54 class CrNetURLRequestContextGetter : public net::URLRequestContextGetter {
55  public:
56   CrNetURLRequestContextGetter(
57       net::URLRequestContext* context,
58       const scoped_refptr<base::MessageLoopProxy>& loop)
59       : context_(context), loop_(loop) {}
61   net::URLRequestContext* GetURLRequestContext() override { return context_; }
63   scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner()
64       const override {
65     return loop_;
66   }
67  private:
68   // Must be called on the IO thread.
69   ~CrNetURLRequestContextGetter() override {}
71   net::URLRequestContext* context_;
72   scoped_refptr<base::MessageLoopProxy> loop_;
73   DISALLOW_COPY_AND_ASSIGN(CrNetURLRequestContextGetter);
76 }  // namespace
78 // net::HTTPProtocolHandlerDelegate for CrNet.
79 class CrNetHttpProtocolHandlerDelegate
80     : public net::HTTPProtocolHandlerDelegate {
81  public:
82   CrNetHttpProtocolHandlerDelegate(net::URLRequestContextGetter* getter,
83                                    RequestFilterBlock filter)
84       : getter_(getter), filter_(filter, base::scoped_policy::RETAIN) {}
86  private:
87   // net::HTTPProtocolHandlerDelegate implementation:
88   bool CanHandleRequest(NSURLRequest* request) override {
89     // Don't advertise support for file:// URLs for now.
90     // This broke some time ago but it's not clear how to fix it at the moment.
91     // http://crbug.com/480620
92     if ([[[request URL] scheme] caseInsensitiveCompare:@"file"] ==
93         NSOrderedSame) {
94       return false;
95     }
96     if (filter_) {
97       RequestFilterBlock block = filter_.get();
98       return block(request);
99     }
100     return true;
101   }
103   bool IsRequestSupported(NSURLRequest* request) override {
104     NSString* scheme = [[request URL] scheme];
105     if (!scheme)
106       return false;
107     return [scheme caseInsensitiveCompare:@"data"] == NSOrderedSame ||
108            [scheme caseInsensitiveCompare:@"http"] == NSOrderedSame ||
109            [scheme caseInsensitiveCompare:@"https"] == NSOrderedSame;
110   }
112   net::URLRequestContextGetter* GetDefaultURLRequestContext() override {
113     return getter_.get();
114   }
116   scoped_refptr<net::URLRequestContextGetter> getter_;
117   base::mac::ScopedBlock<RequestFilterBlock> filter_;
120 void CrNetEnvironment::PostToNetworkThread(
121     const tracked_objects::Location& from_here,
122     const base::Closure& task) {
123   network_io_thread_->message_loop()->PostTask(from_here, task);
126 void CrNetEnvironment::PostToFileUserBlockingThread(
127     const tracked_objects::Location& from_here,
128     const base::Closure& task) {
129   file_user_blocking_thread_->message_loop()->PostTask(from_here, task);
132 // static
133 void CrNetEnvironment::Initialize() {
134   DCHECK_EQ([NSThread currentThread], [NSThread mainThread]);
135   if (!g_at_exit_)
136     g_at_exit_ = new base::AtExitManager;
138   CHECK(base::i18n::InitializeICU());
139   url::Initialize();
140   base::CommandLine::Init(0, nullptr);
141   // This needs to happen on the main thread. NSPR's initialization sets up its
142   // memory allocator; if this is not done before other threads are created,
143   // this initialization can race to cause accidental free/allocation
144   // mismatches.
145   crypto::EnsureNSPRInit();
147   // Create a message loop on the UI thread.
148   base::MessageLoop* main_message_loop =
149       new base::MessageLoop(base::MessageLoop::TYPE_UI);
150 #pragma unused(main_message_loop)
151   base::MessageLoopForUI::current()->Attach();
154 void CrNetEnvironment::StartNetLog(base::FilePath::StringType file_name,
155     bool log_bytes) {
156   DCHECK(file_name.length());
157   base::AutoLock lock(net_log_lock_);
158   if (net_log_started_) {
159     return;
160   }
161   net_log_started_ = true;
162   PostToFileUserBlockingThread(FROM_HERE,
163       base::Bind(&CrNetEnvironment::StartNetLogInternal,
164                  base::Unretained(this), file_name, log_bytes));
167 void CrNetEnvironment::StartNetLogInternal(
168     base::FilePath::StringType file_name, bool log_bytes) {
169   DCHECK(base::MessageLoop::current() ==
170          file_user_blocking_thread_->message_loop());
171   DCHECK(file_name.length());
172   if (!net_log_.get()) {
173     net_log_.reset(new CrNetNetLog());
174     main_context_.get()->set_net_log(net_log_.get());
175   }
176   CrNetNetLog::Mode mode = log_bytes ? CrNetNetLog::LOG_ALL_BYTES :
177                                        CrNetNetLog::LOG_STRIP_PRIVATE_DATA;
178   net_log_->Start(base::FilePath(file_name), mode);
181 void CrNetEnvironment::StopNetLog() {
182   base::AutoLock lock(net_log_lock_);
183   if (!net_log_started_) {
184     return;
185   }
186   net_log_started_ = false;
187   PostToFileUserBlockingThread(FROM_HERE,
188       base::Bind(&CrNetEnvironment::StopNetLogInternal,
189       base::Unretained(this)));
192 void CrNetEnvironment::StopNetLogInternal() {
193   DCHECK(base::MessageLoop::current() ==
194          file_user_blocking_thread_->message_loop());
195   if (net_log_.get()) {
196     net_log_->Stop();
197   }
200 void CrNetEnvironment::CloseAllSpdySessions() {
201   PostToNetworkThread(FROM_HERE,
202       base::Bind(&CrNetEnvironment::CloseAllSpdySessionsInternal,
203       base::Unretained(this)));
206 void CrNetEnvironment::SetRequestFilterBlock(RequestFilterBlock block) {
207   http_protocol_handler_delegate_.reset(
208       new CrNetHttpProtocolHandlerDelegate(main_context_getter_.get(), block));
209   net::HTTPProtocolHandlerDelegate::SetInstance(
210       http_protocol_handler_delegate_.get());
213 net::HttpNetworkSession* CrNetEnvironment::GetHttpNetworkSession(
214     net::URLRequestContext* context) {
215   DCHECK(context);
216   if (!context->http_transaction_factory())
217     return nullptr;
219   return context->http_transaction_factory()->GetSession();
222 void CrNetEnvironment::CloseAllSpdySessionsInternal() {
223   DCHECK(base::MessageLoop::current() ==
224          network_io_thread_->message_loop());
226   net::HttpNetworkSession* http_network_session =
227       GetHttpNetworkSession(GetMainContextGetter()->GetURLRequestContext());
229   if (http_network_session) {
230     net::SpdySessionPool *spdy_session_pool =
231         http_network_session->spdy_session_pool();
232     if (spdy_session_pool)
233       spdy_session_pool->CloseCurrentSessions(net::ERR_ABORTED);
234   }
237 CrNetEnvironment::CrNetEnvironment(std::string user_agent_product_name)
238     : main_context_(new net::URLRequestContext),
239       user_agent_product_name_(user_agent_product_name) {
243 void CrNetEnvironment::Install() {
244   // Threads setup.
245   network_cache_thread_.reset(new base::Thread("Chrome Network Cache Thread"));
246   network_cache_thread_->StartWithOptions(
247       base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
248   network_io_thread_.reset(new base::Thread("Chrome Network IO Thread"));
249   network_io_thread_->StartWithOptions(
250       base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
251   file_thread_.reset(new base::Thread("Chrome File Thread"));
252   file_thread_->StartWithOptions(
253       base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
254   file_user_blocking_thread_.reset(
255       new base::Thread("Chrome File User Blocking Thread"));
256   file_user_blocking_thread_->StartWithOptions(
257       base::Thread::Options(base::MessageLoop::TYPE_IO, 0));
259   // The network change notifier must be initialized so that registered
260   // delegates will receive callbacks.
261   network_change_notifier_.reset(net::NetworkChangeNotifier::Create());
262   proxy_config_service_.reset(net::ProxyService::CreateSystemProxyConfigService(
263       network_io_thread_->message_loop_proxy(), nullptr));
265   PostToNetworkThread(FROM_HERE,
266       base::Bind(&CrNetEnvironment::InitializeOnNetworkThread,
267                  base::Unretained(this)));
269   net::SetURLRequestContextForNSSHttpIO(main_context_.get());
270   main_context_getter_ = new CrNetURLRequestContextGetter(
271       main_context_.get(), network_io_thread_->message_loop_proxy());
272   SetRequestFilterBlock(nil);
273   net_log_started_ = false;
276 void CrNetEnvironment::InstallIntoSessionConfiguration(
277     NSURLSessionConfiguration* config) {
278   config.protocolClasses = @[ [CRNHTTPProtocolHandler class] ];
281 CrNetEnvironment::~CrNetEnvironment() {
282   net::HTTPProtocolHandlerDelegate::SetInstance(nullptr);
283   net::SetURLRequestContextForNSSHttpIO(nullptr);
286 net::URLRequestContextGetter* CrNetEnvironment::GetMainContextGetter() {
287   return main_context_getter_.get();
290 void CrNetEnvironment::SetHTTPProtocolHandlerRegistered(bool registered) {
291   if (registered) {
292     // Disable the default cache.
293     [NSURLCache setSharedURLCache:nil];
294     // Register the chrome http protocol handler to replace the default one.
295     BOOL success = [NSURLProtocol registerClass:[CRNHTTPProtocolHandler class]];
296     DCHECK(success);
297   } else {
298     // Set up an empty default cache, with default size.
299     // TODO(droger): If the NSURLCache is to be used, its size should most
300     // likely be changed. On an iPod2 with iOS4, the default size is 512k.
301     [NSURLCache setSharedURLCache:[[[NSURLCache alloc] init] autorelease]];
302     [NSURLProtocol unregisterClass:[CRNHTTPProtocolHandler class]];
303   }
306 void CrNetEnvironment::InitializeOnNetworkThread() {
307   DCHECK(base::MessageLoop::current() == network_io_thread_->message_loop());
309   // Register network clients.
310   net::RequestTracker::AddGlobalNetworkClientFactory(
311       [[[WebPNetworkClientFactory alloc]
312           initWithTaskRunner:file_user_blocking_thread_
313                                  ->message_loop_proxy()] autorelease]);
315 #if 0
316   // TODO(huey): Re-enable this once SDCH supports SSL and dictionaries from
317   // previous sessions can be used on the first request after a fresh launch.
318   sdch_manager_.reset(new net::SdchManager());
319   sdch_manager_->set_sdch_fetcher(
320       new SdchDictionaryFetcher(main_context_getter_));
321 #else
322   // Otherwise, explicitly disable SDCH to avoid a crash.
323   net::SdchManager::EnableSdchSupport(false);
324 #endif
326   NSString* bundlePath =
327       [[NSBundle mainBundle] pathForResource:@"crnet_resources"
328                                       ofType:@"bundle"];
329   NSBundle* bundle = [NSBundle bundleWithPath:bundlePath];
330   NSString* acceptableLanguages = NSLocalizedStringWithDefaultValue(
331       @"IDS_ACCEPT_LANGUAGES",
332       @"Localizable",
333       bundle,
334       @"en-US,en",
335       @"These values are copied from Chrome's .xtb files, so the same "
336        "values are used in the |Accept-Language| header. Key name matches "
337        "Chrome's.");
338   DCHECK(acceptableLanguages);
339   std::string acceptable_languages =
340       [acceptableLanguages cStringUsingEncoding:NSUTF8StringEncoding];
341   std::string user_agent =
342       web::BuildUserAgentFromProduct(user_agent_product_name_);
343   // Set the user agent through NSUserDefaults. This sets it for both
344   // UIWebViews and WKWebViews, and javascript calls to navigator.userAgent
345   // return this value.
346   [[NSUserDefaults standardUserDefaults] registerDefaults:@{
347     @"UserAgent" : [NSString stringWithUTF8String:user_agent.c_str()]
348   }];
349   main_context_->set_http_user_agent_settings(
350       new net::StaticHttpUserAgentSettings(acceptable_languages, user_agent));
352   main_context_->set_ssl_config_service(new net::SSLConfigServiceDefaults);
353   main_context_->set_transport_security_state(
354       new net::TransportSecurityState());
355   http_server_properties_.reset(new net::HttpServerPropertiesImpl());
356   main_context_->set_http_server_properties(
357       http_server_properties_->GetWeakPtr());
358   main_context_->set_host_resolver(
359       net::HostResolver::CreateDefaultResolver(nullptr).release());
360   main_context_->set_cert_verifier(net::CertVerifier::CreateDefault());
361   main_context_->set_http_auth_handler_factory(
362       net::HttpAuthHandlerRegistryFactory::CreateDefault(
363           main_context_->host_resolver()));
364   main_context_->set_proxy_service(
365       net::ProxyService::CreateUsingSystemProxyResolver(
366           proxy_config_service_.get(), 0, nullptr));
368   // Cache
369   NSArray* dirs = NSSearchPathForDirectoriesInDomains(NSCachesDirectory,
370                                                       NSUserDomainMask,
371                                                       YES);
372   base::FilePath cache_path =
373       base::mac::NSStringToFilePath([dirs objectAtIndex:0]);
374   cache_path = cache_path.Append(FILE_PATH_LITERAL("crnet"));
375   net::HttpCache::DefaultBackend* main_backend =
376       new net::HttpCache::DefaultBackend(
377           net::DISK_CACHE,
378           net::CACHE_BACKEND_DEFAULT,
379           cache_path,
380           0,  // Default cache size.
381           network_cache_thread_->message_loop_proxy());
383   net::HttpNetworkSession::Params params;
384   params.host_resolver = main_context_->host_resolver();
385   params.cert_verifier = main_context_->cert_verifier();
386   params.channel_id_service = main_context_->channel_id_service();
387   params.transport_security_state = main_context_->transport_security_state();
388   params.proxy_service = main_context_->proxy_service();
389   params.ssl_session_cache_shard = "";
390   params.ssl_config_service = main_context_->ssl_config_service();
391   params.http_auth_handler_factory = main_context_->http_auth_handler_factory();
392   params.network_delegate = main_context_->network_delegate();
393   params.http_server_properties = main_context_->http_server_properties();
394   params.net_log = main_context_->net_log();
395   params.next_protos =
396       net::NextProtosWithSpdyAndQuic(spdy_enabled(), quic_enabled());
397   params.use_alternate_protocols = true;
398   params.enable_quic = quic_enabled();
399   params.alternative_service_probability_threshold =
400       alternate_protocol_threshold_;
402   if (!params.channel_id_service) {
403     // The main context may not have a ChannelIDService, since it is lazily
404     // constructed. If not, build an ephemeral ChannelIDService with no backing
405     // disk store.
406     // TODO(ellyjones): support persisting ChannelID.
407     params.channel_id_service = new net::ChannelIDService(
408         new net::DefaultChannelIDStore(NULL),
409         base::WorkerPool::GetTaskRunner(true));
410   }
412   net::HttpCache* main_cache = new net::HttpCache(params, main_backend);
413   main_context_->set_http_transaction_factory(main_cache);
415   // Cookies
416   scoped_refptr<net::CookieStore> cookie_store =
417       net::CookieStoreIOS::CreateCookieStoreFromNSHTTPCookieStorage();
418   main_context_->set_cookie_store(cookie_store.get());
420   net::URLRequestJobFactoryImpl* job_factory =
421       new net::URLRequestJobFactoryImpl;
422   job_factory->SetProtocolHandler("data", new net::DataProtocolHandler);
423   job_factory->SetProtocolHandler(
424       "file", new net::FileProtocolHandler(file_thread_->message_loop_proxy()));
425   main_context_->set_job_factory(job_factory);
428 std::string CrNetEnvironment::user_agent() {
429   const net::HttpUserAgentSettings* user_agent_settings =
430       main_context_->http_user_agent_settings();
431   if (!user_agent_settings) {
432     return nullptr;
433   }
435   return user_agent_settings->GetUserAgent();
438 void CrNetEnvironment::ClearCache(ClearCacheCallback callback) {
439   PostToNetworkThread(FROM_HERE,
440       base::Bind(&CrNetEnvironment::ClearCacheOnNetworkThread,
441                  base::Unretained(this),
442                  callback));
445 void CrNetEnvironment::ClearCacheOnNetworkThread(ClearCacheCallback callback) {
446   DCHECK(base::MessageLoop::current() == network_io_thread_->message_loop());
447   __block disk_cache::Backend* backend = nullptr;
448   net::HttpCache* cache = main_context_->http_transaction_factory()->GetCache();
449   net::CompletionCallback client_callback = base::BindBlock(^(int error) {
450     if (callback != nil) {
451       callback(error);
452     }
453   });
454   net::CompletionCallback doom_callback = base::BindBlock(^(int error) {
455       if (backend)
456         backend->DoomAllEntries(client_callback);
457   });
458   int rc = cache->GetBackend(&backend, doom_callback);
459   if (rc != net::ERR_IO_PENDING) {
460     // GetBackend doesn't call the callback if it completes synchronously, so
461     // call it directly here.
462     doom_callback.Run(rc);
463   }