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 { |