Chromium Code Reviews| Index: components/cronet/ios/Cronet.mm |
| diff --git a/components/cronet/ios/Cronet.mm b/components/cronet/ios/Cronet.mm |
| index c397eefbdebd9035ae0f0189127e5caa26b961aa..206ef3a0d7e75f7200d4c7ac6fb871380c5b6d11 100644 |
| --- a/components/cronet/ios/Cronet.mm |
| +++ b/components/cronet/ios/Cronet.mm |
| @@ -8,13 +8,22 @@ |
| #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 "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 +34,99 @@ 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) { |
| + filter_.reset(filter); |
| + } |
| + |
| + private: |
| + // net::HTTPProtocolHandlerDelegate implementation: |
| + bool CanHandleRequest(NSURLRequest* request) override { |
| + 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_; |
| +}; |
| } // 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 = @""; |
|
kapishnikov
2016/10/19 18:28:53
Why don't we set @"en-US,en" here? Also, should we
mef
2016/10/19 20:12:59
My motivation is that if we run into some unknown
kapishnikov
2016/10/19 21:12:29
I think it is a good idea to give the client an op
|
| + return acceptLanguages; |
| +} |
| + |
| + (void)checkNotStarted { |
| CHECK(gChromeNet == NULL) << "Cronet is already started."; |
| } |
| @@ -51,9 +147,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 +158,20 @@ NSString* gSslKeyLogFileName = nil; |
| gSslKeyLogFileName = sslKeyLogFileName; |
| } |
| ++ (void)setRequestFilterBlock:(RequestFilterBlock)block { |
|
kapishnikov
2016/10/19 18:28:53
Is this method thread safe? What if there are pend
mef
2016/10/19 20:12:59
Good catch! Done.
|
| + 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 +181,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 +204,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] ]; |
|
kapishnikov
2016/10/19 18:28:53
Should we append to the list of protocols rather t
mef
2016/10/19 20:12:59
But wouldn't this require Cronet to handle that cu
kapishnikov
2016/10/19 21:12:29
Cronet handles "http://", "https://" and "data://"
|
| +} |
| + |
| + (void)startNetLogToFile:(NSString*)fileName logBytes:(BOOL)logBytes { |
| if (gChromeNet.Get().get() && [fileName length]) { |
| gChromeNet.Get()->StartNetLog([fileName UTF8String], logBytes); |
| @@ -121,6 +260,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 = TRUE; |
|
kapishnikov
2016/10/19 18:28:53
TRUE -> YES
mef
2016/10/19 20:12:58
Done.
|
| +} |
| + |
| ++ (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 { |