| Index: components/cronet/ios/Cronet.mm | 
| diff --git a/components/cronet/ios/Cronet.mm b/components/cronet/ios/Cronet.mm | 
| index c397eefbdebd9035ae0f0189127e5caa26b961aa..150be2435ff32b9919574f60c8766995eb4f698b 100644 | 
| --- a/components/cronet/ios/Cronet.mm | 
| +++ b/components/cronet/ios/Cronet.mm | 
| @@ -8,13 +8,23 @@ | 
|  | 
| #include "base/lazy_instance.h" | 
| #include "base/logging.h" | 
| +#include "base/mac/bundle_locations.h" | 
| +#include "base/mac/scoped_block.h" | 
| +#include "base/memory/ptr_util.h" | 
| #include "base/memory/scoped_vector.h" | 
| #include "base/strings/sys_string_conversions.h" | 
| +#include "base/synchronization/lock.h" | 
| #include "components/cronet/ios/cronet_environment.h" | 
| #include "components/cronet/url_request_context_config.h" | 
| +#include "ios/net/crn_http_protocol_handler.h" | 
| +#include "ios/net/empty_nsurlcache.h" | 
| +#include "net/cert/cert_verifier.h" | 
| +#include "net/url_request/url_request_context_getter.h" | 
|  | 
| namespace { | 
|  | 
| +class CronetHttpProtocolHandlerDelegate; | 
| + | 
| // Currently there is one and only one instance of CronetEnvironment, | 
| // which is leaked at the shutdown. We should consider allowing multiple | 
| // instances if that makes sense in the future. | 
| @@ -25,12 +35,102 @@ BOOL gHttp2Enabled = YES; | 
| BOOL gQuicEnabled = NO; | 
| ScopedVector<cronet::URLRequestContextConfig::QuicHint> gQuicHints; | 
| NSString* gUserAgent = nil; | 
| +BOOL gUserAgentPartial = NO; | 
| NSString* gSslKeyLogFileName = nil; | 
| +RequestFilterBlock gRequestFilterBlock = nil; | 
| +std::unique_ptr<CronetHttpProtocolHandlerDelegate> gHttpProtocolHandlerDelegate; | 
| +NSURLCache* gPreservedSharedURLCache = nil; | 
| +BOOL gEnableTestCertVerifierForTesting = FALSE; | 
| +NSString* gHostResolverRulesForTesting = @""; | 
| + | 
| +// CertVerifier, which allows any certificates for testing. | 
| +class TestCertVerifier : public net::CertVerifier { | 
| +  int Verify(const RequestParams& params, | 
| +             net::CRLSet* crl_set, | 
| +             net::CertVerifyResult* verify_result, | 
| +             const net::CompletionCallback& callback, | 
| +             std::unique_ptr<Request>* out_req, | 
| +             const net::NetLogWithSource& net_log) override { | 
| +    net::Error result = net::OK; | 
| +    verify_result->verified_cert = params.certificate(); | 
| +    verify_result->cert_status = net::MapNetErrorToCertStatus(result); | 
| +    return result; | 
| +  } | 
| +}; | 
| + | 
| +// net::HTTPProtocolHandlerDelegate for Cronet. | 
| +class CronetHttpProtocolHandlerDelegate | 
| +    : public net::HTTPProtocolHandlerDelegate { | 
| + public: | 
| +  CronetHttpProtocolHandlerDelegate(net::URLRequestContextGetter* getter, | 
| +                                    RequestFilterBlock filter) | 
| +      : getter_(getter), filter_(filter, base::scoped_policy::RETAIN) {} | 
| + | 
| +  void SetRequestFilterBlock(RequestFilterBlock filter) { | 
| +    base::AutoLock auto_lock(lock_); | 
| +    filter_.reset(filter); | 
| +  } | 
| + | 
| + private: | 
| +  // net::HTTPProtocolHandlerDelegate implementation: | 
| +  bool CanHandleRequest(NSURLRequest* request) override { | 
| +    base::AutoLock auto_lock(lock_); | 
| +    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_; | 
| +  base::Lock lock_; | 
| +}; | 
|  | 
| }  // namespace | 
|  | 
| @implementation Cronet | 
|  | 
| ++ (void)configureCronetEnvironmentForTesting: | 
| +    (cronet::CronetEnvironment*)cronetEnvironment { | 
| +  cronetEnvironment->set_host_resolver_rules( | 
| +      [gHostResolverRulesForTesting UTF8String]); | 
| +  if (gEnableTestCertVerifierForTesting) { | 
| +    std::unique_ptr<TestCertVerifier> test_cert_verifier = | 
| +        base::MakeUnique<TestCertVerifier>(); | 
| +    cronetEnvironment->set_cert_verifier(std::move(test_cert_verifier)); | 
| +  } | 
| +} | 
| + | 
| ++ (NSString*)getAcceptLanguages { | 
| +  // Use the framework bundle to search for resources. | 
| +  NSBundle* frameworkBundle = [NSBundle bundleForClass:self]; | 
| +  NSString* bundlePath = | 
| +      [frameworkBundle pathForResource:@"cronet_resources" ofType:@"bundle"]; | 
| +  NSBundle* bundle = [NSBundle bundleWithPath:bundlePath]; | 
| +  NSString* acceptLanguages = 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."); | 
| +  if (acceptLanguages == Nil) | 
| +    acceptLanguages = @""; | 
| +  return acceptLanguages; | 
| +} | 
| + | 
| + (void)checkNotStarted { | 
| CHECK(gChromeNet == NULL) << "Cronet is already started."; | 
| } | 
| @@ -51,9 +151,10 @@ NSString* gSslKeyLogFileName = nil; | 
| base::SysNSStringToUTF8(host), port, altPort)); | 
| } | 
|  | 
| -+ (void)setPartialUserAgent:(NSString*)userAgent { | 
| ++ (void)setUserAgent:(NSString*)userAgent partial:(BOOL)partial { | 
| [self checkNotStarted]; | 
| gUserAgent = userAgent; | 
| +  gUserAgentPartial = partial; | 
| } | 
|  | 
| + (void)setSslKeyLogFileName:(NSString*)sslKeyLogFileName { | 
| @@ -61,10 +162,20 @@ NSString* gSslKeyLogFileName = nil; | 
| gSslKeyLogFileName = sslKeyLogFileName; | 
| } | 
|  | 
| ++ (void)setRequestFilterBlock:(RequestFilterBlock)block { | 
| +  if (gHttpProtocolHandlerDelegate.get()) | 
| +    gHttpProtocolHandlerDelegate.get()->SetRequestFilterBlock(block); | 
| +  else | 
| +    gRequestFilterBlock = block; | 
| +} | 
| + | 
| + (void)startInternal { | 
| cronet::CronetEnvironment::Initialize(); | 
| -  std::string partialUserAgent = base::SysNSStringToUTF8(gUserAgent); | 
| -  gChromeNet.Get().reset(new cronet::CronetEnvironment(partialUserAgent)); | 
| +  std::string user_agent = base::SysNSStringToUTF8(gUserAgent); | 
| +  gChromeNet.Get().reset( | 
| +      new cronet::CronetEnvironment(user_agent, gUserAgentPartial)); | 
| +  gChromeNet.Get()->set_accept_language( | 
| +      base::SysNSStringToUTF8([self getAcceptLanguages])); | 
|  | 
| gChromeNet.Get()->set_http2_enabled(gHttp2Enabled); | 
| gChromeNet.Get()->set_quic_enabled(gQuicEnabled); | 
| @@ -74,7 +185,14 @@ NSString* gSslKeyLogFileName = nil; | 
| gChromeNet.Get()->AddQuicHint(quicHint->host, quicHint->port, | 
| quicHint->alternate_port); | 
| } | 
| + | 
| +  [self configureCronetEnvironmentForTesting:gChromeNet.Get().get()]; | 
| gChromeNet.Get()->Start(); | 
| +  gHttpProtocolHandlerDelegate.reset(new CronetHttpProtocolHandlerDelegate( | 
| +      gChromeNet.Get()->GetURLRequestContextGetter(), gRequestFilterBlock)); | 
| +  net::HTTPProtocolHandlerDelegate::SetInstance( | 
| +      gHttpProtocolHandlerDelegate.get()); | 
| +  gRequestFilterBlock = nil; | 
| } | 
|  | 
| + (void)start { | 
| @@ -90,6 +208,31 @@ NSString* gSslKeyLogFileName = nil; | 
| }); | 
| } | 
|  | 
| ++ (void)registerHttpProtocolHandler { | 
| +  if (gPreservedSharedURLCache == nil) { | 
| +    gPreservedSharedURLCache = [NSURLCache sharedURLCache]; | 
| +  } | 
| +  // 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); | 
| +} | 
| + | 
| ++ (void)unregisterHttpProtocolHandler { | 
| +  // Set up SharedURLCache preserved in registerHttpProtocolHandler. | 
| +  if (gPreservedSharedURLCache != nil) { | 
| +    [NSURLCache setSharedURLCache:gPreservedSharedURLCache]; | 
| +    gPreservedSharedURLCache = nil; | 
| +  } | 
| +  [NSURLProtocol unregisterClass:[CRNPauseableHTTPProtocolHandler class]]; | 
| +} | 
| + | 
| ++ (void)installIntoSessionConfiguration:(NSURLSessionConfiguration*)config { | 
| +  config.protocolClasses = @[ [CRNPauseableHTTPProtocolHandler class] ]; | 
| +} | 
| + | 
| + (void)startNetLogToFile:(NSString*)fileName logBytes:(BOOL)logBytes { | 
| if (gChromeNet.Get().get() && [fileName length]) { | 
| gChromeNet.Get()->StartNetLog([fileName UTF8String], logBytes); | 
| @@ -121,6 +264,22 @@ NSString* gSslKeyLogFileName = nil; | 
| return nil; | 
| } | 
|  | 
| ++ (NSData*)getGlobalMetricsDeltas { | 
| +  if (!gChromeNet.Get().get()) { | 
| +    return nil; | 
| +  } | 
| +  std::vector<uint8_t> deltas(gChromeNet.Get()->GetHistogramDeltas()); | 
| +  return [NSData dataWithBytes:deltas.data() length:deltas.size()]; | 
| +} | 
| + | 
| ++ (void)enableTestCertVerifierForTesting { | 
| +  gEnableTestCertVerifierForTesting = YES; | 
| +} | 
| + | 
| ++ (void)setHostResolverRulesForTesting:(NSString*)hostResolverRulesForTesting { | 
| +  gHostResolverRulesForTesting = hostResolverRulesForTesting; | 
| +} | 
| + | 
| // This is a non-public dummy method that prevents the linker from stripping out | 
| // the otherwise non-referenced methods from 'cronet_bidirectional_stream.cc'. | 
| + (void)preventStrippingCronetBidirectionalStream { | 
|  |