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