| OLD | NEW |
| (Empty) |
| 1 // Copyright 2016 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. | |
| 4 | |
| 5 #include "components/cronet/ios/cronet_environment.h" | |
| 6 | |
| 7 #include <utility> | |
| 8 | |
| 9 #include "base/at_exit.h" | |
| 10 #include "base/atomicops.h" | |
| 11 #include "base/command_line.h" | |
| 12 #include "base/feature_list.h" | |
| 13 #include "base/files/file_path.h" | |
| 14 #include "base/files/file_util.h" | |
| 15 #include "base/files/scoped_file.h" | |
| 16 #include "base/json/json_writer.h" | |
| 17 #include "base/mac/bind_objc_block.h" | |
| 18 #include "base/mac/foundation_util.h" | |
| 19 #include "base/macros.h" | |
| 20 #include "base/memory/ptr_util.h" | |
| 21 #include "base/metrics/statistics_recorder.h" | |
| 22 #include "base/path_service.h" | |
| 23 #include "base/single_thread_task_runner.h" | |
| 24 #include "base/synchronization/waitable_event.h" | |
| 25 #include "base/threading/worker_pool.h" | |
| 26 #include "components/cronet/histogram_manager.h" | |
| 27 #include "components/cronet/ios/version.h" | |
| 28 #include "components/prefs/json_pref_store.h" | |
| 29 #include "components/prefs/pref_filter.h" | |
| 30 #include "ios/web/public/user_agent.h" | |
| 31 #include "net/base/net_errors.h" | |
| 32 #include "net/base/network_change_notifier.h" | |
| 33 #include "net/cert/cert_verifier.h" | |
| 34 #include "net/cert/ct_known_logs.h" | |
| 35 #include "net/cert/ct_log_verifier.h" | |
| 36 #include "net/cert/ct_policy_enforcer.h" | |
| 37 #include "net/cert/ct_verifier.h" | |
| 38 #include "net/cert/multi_log_ct_verifier.h" | |
| 39 #include "net/dns/host_resolver.h" | |
| 40 #include "net/dns/mapped_host_resolver.h" | |
| 41 #include "net/http/http_auth_handler_factory.h" | |
| 42 #include "net/http/http_cache.h" | |
| 43 #include "net/http/http_response_headers.h" | |
| 44 #include "net/http/http_server_properties_impl.h" | |
| 45 #include "net/http/http_stream_factory.h" | |
| 46 #include "net/http/http_util.h" | |
| 47 #include "net/log/net_log.h" | |
| 48 #include "net/log/net_log_capture_mode.h" | |
| 49 #include "net/log/write_to_file_net_log_observer.h" | |
| 50 #include "net/proxy/proxy_service.h" | |
| 51 #include "net/socket/ssl_client_socket.h" | |
| 52 #include "net/ssl/channel_id_service.h" | |
| 53 #include "net/ssl/default_channel_id_store.h" | |
| 54 #include "net/ssl/ssl_config_service_defaults.h" | |
| 55 #include "net/url_request/static_http_user_agent_settings.h" | |
| 56 #include "net/url_request/url_request_context_storage.h" | |
| 57 #include "net/url_request/url_request_job_factory_impl.h" | |
| 58 #include "url/scheme_host_port.h" | |
| 59 #include "url/url_util.h" | |
| 60 | |
| 61 namespace { | |
| 62 | |
| 63 base::AtExitManager* g_at_exit_ = nullptr; | |
| 64 net::NetworkChangeNotifier* g_network_change_notifier = nullptr; | |
| 65 // MessageLoop on the main thread. | |
| 66 base::MessageLoop* g_main_message_loop = nullptr; | |
| 67 | |
| 68 // Request context getter for Cronet. | |
| 69 class CronetURLRequestContextGetter : public net::URLRequestContextGetter { | |
| 70 public: | |
| 71 CronetURLRequestContextGetter( | |
| 72 cronet::CronetEnvironment* environment, | |
| 73 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) | |
| 74 : environment_(environment), task_runner_(task_runner) {} | |
| 75 | |
| 76 net::URLRequestContext* GetURLRequestContext() override { | |
| 77 DCHECK(environment_); | |
| 78 return environment_->GetURLRequestContext(); | |
| 79 } | |
| 80 | |
| 81 scoped_refptr<base::SingleThreadTaskRunner> GetNetworkTaskRunner() | |
| 82 const override { | |
| 83 return task_runner_; | |
| 84 } | |
| 85 | |
| 86 private: | |
| 87 // Must be called on the IO thread. | |
| 88 ~CronetURLRequestContextGetter() override {} | |
| 89 | |
| 90 cronet::CronetEnvironment* environment_; | |
| 91 scoped_refptr<base::SingleThreadTaskRunner> task_runner_; | |
| 92 DISALLOW_COPY_AND_ASSIGN(CronetURLRequestContextGetter); | |
| 93 }; | |
| 94 | |
| 95 } // namespace | |
| 96 | |
| 97 namespace cronet { | |
| 98 | |
| 99 void CronetEnvironment::PostToNetworkThread( | |
| 100 const tracked_objects::Location& from_here, | |
| 101 const base::Closure& task) { | |
| 102 network_io_thread_->task_runner()->PostTask(from_here, task); | |
| 103 } | |
| 104 | |
| 105 void CronetEnvironment::PostToFileUserBlockingThread( | |
| 106 const tracked_objects::Location& from_here, | |
| 107 const base::Closure& task) { | |
| 108 file_user_blocking_thread_->task_runner()->PostTask(from_here, task); | |
| 109 } | |
| 110 | |
| 111 net::URLRequestContext* CronetEnvironment::GetURLRequestContext() const { | |
| 112 return main_context_.get(); | |
| 113 } | |
| 114 | |
| 115 net::URLRequestContextGetter* CronetEnvironment::GetURLRequestContextGetter() | |
| 116 const { | |
| 117 return main_context_getter_.get(); | |
| 118 } | |
| 119 | |
| 120 // static | |
| 121 void CronetEnvironment::Initialize() { | |
| 122 // DCHECK_EQ([NSThread currentThread], [NSThread mainThread]); | |
| 123 // This method must be called once from the main thread. | |
| 124 if (!g_at_exit_) | |
| 125 g_at_exit_ = new base::AtExitManager; | |
| 126 | |
| 127 url::Initialize(); | |
| 128 base::CommandLine::Init(0, nullptr); | |
| 129 | |
| 130 // Without doing this, StatisticsRecorder::FactoryGet() leaks one histogram | |
| 131 // per call after the first for a given name. | |
| 132 base::StatisticsRecorder::Initialize(); | |
| 133 | |
| 134 // Create a message loop on the UI thread. | |
| 135 DCHECK(!base::MessageLoop::current()); | |
| 136 DCHECK(!g_main_message_loop); | |
| 137 g_main_message_loop = new base::MessageLoopForUI(); | |
| 138 base::MessageLoopForUI::current()->Attach(); | |
| 139 // The network change notifier must be initialized so that registered | |
| 140 // delegates will receive callbacks. | |
| 141 DCHECK(!g_network_change_notifier); | |
| 142 g_network_change_notifier = net::NetworkChangeNotifier::Create(); | |
| 143 } | |
| 144 | |
| 145 void CronetEnvironment::StartNetLog(base::FilePath::StringType file_name, | |
| 146 bool log_bytes) { | |
| 147 DCHECK(file_name.length()); | |
| 148 PostToNetworkThread(FROM_HERE, | |
| 149 base::Bind(&CronetEnvironment::StartNetLogOnNetworkThread, | |
| 150 base::Unretained(this), file_name, log_bytes)); | |
| 151 } | |
| 152 | |
| 153 void CronetEnvironment::StartNetLogOnNetworkThread( | |
| 154 const base::FilePath::StringType& file_name, | |
| 155 bool log_bytes) { | |
| 156 DCHECK(file_name.length()); | |
| 157 DCHECK(net_log_); | |
| 158 | |
| 159 if (net_log_observer_) | |
| 160 return; | |
| 161 | |
| 162 base::FilePath files_root; | |
| 163 if (!PathService::Get(base::DIR_HOME, &files_root)) | |
| 164 return; | |
| 165 | |
| 166 base::FilePath full_path = files_root.Append(file_name); | |
| 167 base::ScopedFILE file(base::OpenFile(full_path, "w")); | |
| 168 if (!file) { | |
| 169 LOG(ERROR) << "Can not start NetLog to " << full_path.value(); | |
| 170 return; | |
| 171 } | |
| 172 | |
| 173 net::NetLogCaptureMode capture_mode = | |
| 174 log_bytes ? net::NetLogCaptureMode::IncludeSocketBytes() | |
| 175 : net::NetLogCaptureMode::Default(); | |
| 176 | |
| 177 net_log_observer_.reset(new net::WriteToFileNetLogObserver()); | |
| 178 net_log_observer_->set_capture_mode(capture_mode); | |
| 179 net_log_observer_->StartObserving(main_context_->net_log(), std::move(file), | |
| 180 nullptr, main_context_.get()); | |
| 181 LOG(WARNING) << "Started NetLog to " << full_path.value(); | |
| 182 } | |
| 183 | |
| 184 void CronetEnvironment::StopNetLog() { | |
| 185 base::WaitableEvent log_stopped_event( | |
| 186 base::WaitableEvent::ResetPolicy::MANUAL, | |
| 187 base::WaitableEvent::InitialState::NOT_SIGNALED); | |
| 188 PostToNetworkThread(FROM_HERE, | |
| 189 base::Bind(&CronetEnvironment::StopNetLogOnNetworkThread, | |
| 190 base::Unretained(this), &log_stopped_event)); | |
| 191 log_stopped_event.Wait(); | |
| 192 } | |
| 193 | |
| 194 void CronetEnvironment::StopNetLogOnNetworkThread( | |
| 195 base::WaitableEvent* log_stopped_event) { | |
| 196 if (net_log_observer_) { | |
| 197 DLOG(WARNING) << "Stopped NetLog."; | |
| 198 net_log_observer_->StopObserving(main_context_.get()); | |
| 199 net_log_observer_.reset(); | |
| 200 } | |
| 201 log_stopped_event->Signal(); | |
| 202 } | |
| 203 | |
| 204 net::HttpNetworkSession* CronetEnvironment::GetHttpNetworkSession( | |
| 205 net::URLRequestContext* context) { | |
| 206 DCHECK(context); | |
| 207 if (!context->http_transaction_factory()) | |
| 208 return nullptr; | |
| 209 | |
| 210 return context->http_transaction_factory()->GetSession(); | |
| 211 } | |
| 212 | |
| 213 void CronetEnvironment::AddQuicHint(const std::string& host, | |
| 214 int port, | |
| 215 int alternate_port) { | |
| 216 DCHECK(port == alternate_port); | |
| 217 quic_hints_.push_back(net::HostPortPair(host, port)); | |
| 218 } | |
| 219 | |
| 220 CronetEnvironment::CronetEnvironment(const std::string& user_agent, | |
| 221 bool user_agent_partial) | |
| 222 : http2_enabled_(false), | |
| 223 quic_enabled_(false), | |
| 224 user_agent_(user_agent), | |
| 225 user_agent_partial_(user_agent_partial), | |
| 226 net_log_(new net::NetLog) {} | |
| 227 | |
| 228 void CronetEnvironment::Start() { | |
| 229 // Threads setup. | |
| 230 network_cache_thread_.reset(new base::Thread("Chrome Network Cache Thread")); | |
| 231 network_cache_thread_->StartWithOptions( | |
| 232 base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); | |
| 233 network_io_thread_.reset(new base::Thread("Chrome Network IO Thread")); | |
| 234 network_io_thread_->StartWithOptions( | |
| 235 base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); | |
| 236 file_thread_.reset(new base::Thread("Chrome File Thread")); | |
| 237 file_thread_->StartWithOptions( | |
| 238 base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); | |
| 239 file_user_blocking_thread_.reset( | |
| 240 new base::Thread("Chrome File User Blocking Thread")); | |
| 241 file_user_blocking_thread_->StartWithOptions( | |
| 242 base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); | |
| 243 | |
| 244 static bool ssl_key_log_file_set = false; | |
| 245 if (!ssl_key_log_file_set && !ssl_key_log_file_name_.empty()) { | |
| 246 ssl_key_log_file_set = true; | |
| 247 base::FilePath ssl_key_log_file; | |
| 248 if (!PathService::Get(base::DIR_HOME, &ssl_key_log_file)) | |
| 249 return; | |
| 250 net::SSLClientSocket::SetSSLKeyLogFile( | |
| 251 ssl_key_log_file.Append(ssl_key_log_file_name_), | |
| 252 file_thread_->task_runner()); | |
| 253 } | |
| 254 | |
| 255 proxy_config_service_ = net::ProxyService::CreateSystemProxyConfigService( | |
| 256 network_io_thread_->task_runner(), nullptr); | |
| 257 main_context_getter_ = new CronetURLRequestContextGetter( | |
| 258 this, network_io_thread_->task_runner()); | |
| 259 base::subtle::MemoryBarrier(); | |
| 260 PostToNetworkThread(FROM_HERE, | |
| 261 base::Bind(&CronetEnvironment::InitializeOnNetworkThread, | |
| 262 base::Unretained(this))); | |
| 263 } | |
| 264 | |
| 265 CronetEnvironment::~CronetEnvironment() { | |
| 266 // net::HTTPProtocolHandlerDelegate::SetInstance(nullptr); | |
| 267 } | |
| 268 | |
| 269 void CronetEnvironment::InitializeOnNetworkThread() { | |
| 270 DCHECK(network_io_thread_->task_runner()->BelongsToCurrentThread()); | |
| 271 base::FeatureList::InitializeInstance(std::string(), std::string()); | |
| 272 // TODO(mef): Use net:UrlRequestContextBuilder instead of manual build. | |
| 273 main_context_.reset(new net::URLRequestContext); | |
| 274 main_context_->set_net_log(net_log_.get()); | |
| 275 | |
| 276 if (user_agent_partial_) | |
| 277 user_agent_ = web::BuildUserAgentFromProduct(user_agent_); | |
| 278 | |
| 279 main_context_->set_http_user_agent_settings( | |
| 280 new net::StaticHttpUserAgentSettings(accept_language_, user_agent_)); | |
| 281 | |
| 282 main_context_->set_ssl_config_service(new net::SSLConfigServiceDefaults); | |
| 283 main_context_->set_transport_security_state( | |
| 284 new net::TransportSecurityState()); | |
| 285 http_server_properties_.reset(new net::HttpServerPropertiesImpl()); | |
| 286 main_context_->set_http_server_properties(http_server_properties_.get()); | |
| 287 | |
| 288 // TODO(rdsmith): Note that the ".release()" calls below are leaking | |
| 289 // the objects in question; this should be fixed by having an object | |
| 290 // corresponding to URLRequestContextStorage that actually owns those | |
| 291 // objects. See http://crbug.com/523858. | |
| 292 std::unique_ptr<net::MappedHostResolver> mapped_host_resolver( | |
| 293 new net::MappedHostResolver( | |
| 294 net::HostResolver::CreateDefaultResolver(nullptr))); | |
| 295 | |
| 296 main_context_->set_host_resolver(mapped_host_resolver.release()); | |
| 297 | |
| 298 if (!cert_verifier_) | |
| 299 cert_verifier_ = net::CertVerifier::CreateDefault(); | |
| 300 main_context_->set_cert_verifier(cert_verifier_.get()); | |
| 301 | |
| 302 std::unique_ptr<net::MultiLogCTVerifier> ct_verifier = | |
| 303 base::MakeUnique<net::MultiLogCTVerifier>(); | |
| 304 ct_verifier->AddLogs(net::ct::CreateLogVerifiersForKnownLogs()); | |
| 305 main_context_->set_cert_transparency_verifier(ct_verifier.release()); | |
| 306 main_context_->set_ct_policy_enforcer(new net::CTPolicyEnforcer()); | |
| 307 | |
| 308 main_context_->set_http_auth_handler_factory( | |
| 309 net::HttpAuthHandlerRegistryFactory::CreateDefault( | |
| 310 main_context_->host_resolver()) | |
| 311 .release()); | |
| 312 main_context_->set_proxy_service( | |
| 313 net::ProxyService::CreateUsingSystemProxyResolver( | |
| 314 std::move(proxy_config_service_), 0, nullptr) | |
| 315 .release()); | |
| 316 | |
| 317 // Cache | |
| 318 base::FilePath cache_path; | |
| 319 if (!PathService::Get(base::DIR_CACHE, &cache_path)) | |
| 320 return; | |
| 321 cache_path = cache_path.Append(FILE_PATH_LITERAL("cronet")); | |
| 322 std::unique_ptr<net::HttpCache::DefaultBackend> main_backend( | |
| 323 new net::HttpCache::DefaultBackend(net::DISK_CACHE, | |
| 324 net::CACHE_BACKEND_SIMPLE, cache_path, | |
| 325 0, // Default cache size. | |
| 326 network_cache_thread_->task_runner())); | |
| 327 | |
| 328 net::HttpNetworkSession::Params params; | |
| 329 | |
| 330 params.host_resolver = main_context_->host_resolver(); | |
| 331 params.cert_verifier = main_context_->cert_verifier(); | |
| 332 params.cert_transparency_verifier = | |
| 333 main_context_->cert_transparency_verifier(); | |
| 334 params.ct_policy_enforcer = main_context_->ct_policy_enforcer(); | |
| 335 params.channel_id_service = main_context_->channel_id_service(); | |
| 336 params.transport_security_state = main_context_->transport_security_state(); | |
| 337 params.proxy_service = main_context_->proxy_service(); | |
| 338 params.ssl_config_service = main_context_->ssl_config_service(); | |
| 339 params.http_auth_handler_factory = main_context_->http_auth_handler_factory(); | |
| 340 params.http_server_properties = main_context_->http_server_properties(); | |
| 341 params.net_log = main_context_->net_log(); | |
| 342 params.enable_http2 = http2_enabled(); | |
| 343 params.enable_quic = quic_enabled(); | |
| 344 | |
| 345 for (const auto& quic_hint : quic_hints_) { | |
| 346 net::AlternativeService alternative_service(net::kProtoQUIC, "", | |
| 347 quic_hint.port()); | |
| 348 url::SchemeHostPort quic_hint_server("https", quic_hint.host(), | |
| 349 quic_hint.port()); | |
| 350 main_context_->http_server_properties()->SetAlternativeService( | |
| 351 quic_hint_server, alternative_service, base::Time::Max()); | |
| 352 params.quic_host_whitelist.insert(quic_hint.host()); | |
| 353 } | |
| 354 | |
| 355 if (!params.channel_id_service) { | |
| 356 // The main context may not have a ChannelIDService, since it is lazily | |
| 357 // constructed. If not, build an ephemeral ChannelIDService with no backing | |
| 358 // disk store. | |
| 359 // TODO(ellyjones): support persisting ChannelID. | |
| 360 params.channel_id_service = | |
| 361 new net::ChannelIDService(new net::DefaultChannelIDStore(NULL), | |
| 362 base::WorkerPool::GetTaskRunner(true)); | |
| 363 } | |
| 364 | |
| 365 // TODO(mmenke): These really shouldn't be leaked. | |
| 366 // See https://crbug.com/523858. | |
| 367 net::HttpNetworkSession* http_network_session = | |
| 368 new net::HttpNetworkSession(params); | |
| 369 net::HttpCache* main_cache = | |
| 370 new net::HttpCache(http_network_session, std::move(main_backend), | |
| 371 true /* set_up_quic_server_info */); | |
| 372 main_context_->set_http_transaction_factory(main_cache); | |
| 373 | |
| 374 net::URLRequestJobFactoryImpl* job_factory = | |
| 375 new net::URLRequestJobFactoryImpl; | |
| 376 main_context_->set_job_factory(job_factory); | |
| 377 main_context_->set_net_log(net_log_.get()); | |
| 378 } | |
| 379 | |
| 380 std::string CronetEnvironment::user_agent() { | |
| 381 const net::HttpUserAgentSettings* user_agent_settings = | |
| 382 main_context_->http_user_agent_settings(); | |
| 383 if (!user_agent_settings) { | |
| 384 return nullptr; | |
| 385 } | |
| 386 | |
| 387 return user_agent_settings->GetUserAgent(); | |
| 388 } | |
| 389 | |
| 390 std::vector<uint8_t> CronetEnvironment::GetHistogramDeltas() { | |
| 391 base::StatisticsRecorder::Initialize(); | |
| 392 std::vector<uint8_t> data; | |
| 393 if (!HistogramManager::GetInstance()->GetDeltas(&data)) | |
| 394 return std::vector<uint8_t>(); | |
| 395 return data; | |
| 396 } | |
| 397 | |
| 398 void CronetEnvironment::SetHostResolverRules(const std::string& rules) { | |
| 399 base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC, | |
| 400 base::WaitableEvent::InitialState::NOT_SIGNALED); | |
| 401 PostToNetworkThread( | |
| 402 FROM_HERE, | |
| 403 base::Bind(&CronetEnvironment::SetHostResolverRulesOnNetworkThread, | |
| 404 base::Unretained(this), rules, &event)); | |
| 405 event.Wait(); | |
| 406 } | |
| 407 | |
| 408 void CronetEnvironment::SetHostResolverRulesOnNetworkThread( | |
| 409 const std::string& rules, | |
| 410 base::WaitableEvent* event) { | |
| 411 static_cast<net::MappedHostResolver*>(main_context_->host_resolver()) | |
| 412 ->SetRulesFromString(rules); | |
| 413 event->Signal(); | |
| 414 } | |
| 415 | |
| 416 } // namespace cronet | |
| OLD | NEW |