<commit_before>// 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/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_file.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/empty_nsurlcache.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/log/net_log.h"
#include "net/log/write_to_file_net_log_observer.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::SingleThreadTaskRunner>& task_runner)
      : context_(context), task_runner_(task_runner) {}

  net::URLRequestContext* GetURLRequestContext() override { return context_; }

  scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner()
      const override {
    return task_runner_;
  }
 private:
  // Must be called on the IO thread.
  ~CrNetURLRequestContextGetter() override {}

  net::URLRequestContext* context_;
  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
  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());
  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());
  DCHECK(net_log_);

  if (net_log_observer_)
    return;

  base::FilePath temp_dir;
  if (!base::GetTempDir(&temp_dir))
    return;

  base::FilePath full_path = temp_dir.Append(file_name);
  base::ScopedFILE file(base::OpenFile(full_path, "w"));
  if (!file)
    return;

  net::NetLogCaptureMode capture_mode = log_bytes ?
      net::NetLogCaptureMode::IncludeSocketBytes() :
      net::NetLogCaptureMode::Default();

  net_log_observer_.reset(new net::WriteToFileNetLogObserver());
  net_log_observer_->set_capture_mode(capture_mode);
  net_log_observer_->StartObserving(net_log_.get(), file.Pass(), nullptr,
                                    nullptr);
}

void CrNetEnvironment::StopNetLog() {
  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_observer_) {
    net_log_observer_->StopObserving(nullptr);
    net_log_observer_.reset();
  }
}

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_->task_runner(), 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_->task_runner());
  SetRequestFilterBlock(nil);
}

void CrNetEnvironment::InstallIntoSessionConfiguration(
    NSURLSessionConfiguration* config) {
  config.protocolClasses = @[ [CRNPauseableHTTPProtocolHandler 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:[EmptyNSURLCache emptyNSURLCache]];
    // Register the chrome http protocol handler to replace the default one.
    BOOL success =
        [NSURLProtocol registerClass:[CRNPauseableHTTPProtocolHandler 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:[CRNPauseableHTTPProtocolHandler 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_
                                 ->task_runner()] 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_->task_runner());

  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_->task_runner()));
  main_context_->set_job_factory(job_factory);

  net_log_.reset(new net::NetLog());
  main_context_->set_net_log(net_log_.get());
}

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);
  }
}
<commit_msg>CrNet: initialize StatisticsRecorder.<commit_after>// 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/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_file.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/metrics/statistics_recorder.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/empty_nsurlcache.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/log/net_log.h"
#include "net/log/write_to_file_net_log_observer.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::SingleThreadTaskRunner>& task_runner)
      : context_(context), task_runner_(task_runner) {}

  net::URLRequestContext* GetURLRequestContext() override { return context_; }

  scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner()
      const override {
    return task_runner_;
  }
 private:
  // Must be called on the IO thread.
  ~CrNetURLRequestContextGetter() override {}

  net::URLRequestContext* context_;
  scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
  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();
  // Without doing this, StatisticsRecorder::FactoryGet() leaks one histogram
  // per call after the first for a given name.
  base::StatisticsRecorder::Initialize();

  // 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());
  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());
  DCHECK(net_log_);

  if (net_log_observer_)
    return;

  base::FilePath temp_dir;
  if (!base::GetTempDir(&temp_dir))
    return;

  base::FilePath full_path = temp_dir.Append(file_name);
  base::ScopedFILE file(base::OpenFile(full_path, "w"));
  if (!file)
    return;

  net::NetLogCaptureMode capture_mode = log_bytes ?
      net::NetLogCaptureMode::IncludeSocketBytes() :
      net::NetLogCaptureMode::Default();

  net_log_observer_.reset(new net::WriteToFileNetLogObserver());
  net_log_observer_->set_capture_mode(capture_mode);
  net_log_observer_->StartObserving(net_log_.get(), file.Pass(), nullptr,
                                    nullptr);
}

void CrNetEnvironment::StopNetLog() {
  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_observer_) {
    net_log_observer_->StopObserving(nullptr);
    net_log_observer_.reset();
  }
}

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_->task_runner(), 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_->task_runner());
  SetRequestFilterBlock(nil);
}

void CrNetEnvironment::InstallIntoSessionConfiguration(
    NSURLSessionConfiguration* config) {
  config.protocolClasses = @[ [CRNPauseableHTTPProtocolHandler 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:[EmptyNSURLCache emptyNSURLCache]];
    // Register the chrome http protocol handler to replace the default one.
    BOOL success =
        [NSURLProtocol registerClass:[CRNPauseableHTTPProtocolHandler 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:[CRNPauseableHTTPProtocolHandler 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_
                                 ->task_runner()] 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_->task_runner());

  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_->task_runner()));
  main_context_->set_job_factory(job_factory);

  net_log_.reset(new net::NetLog());
  main_context_->set_net_log(net_log_.get());
}

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);
  }
}

{'fraction_non_alphanumeric': 0.11283632592916007, 'fraction_numerical': 0.0016372864705561316, 'mean_word_length': 4.954021121039805, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 139, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 28, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6790373', 'n_tokens_mistral': 11087, 'n_tokens_neox': 10901, 'n_words': 1921}