Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #import "components/cronet/ios/Cronet.h" | 5 #import "components/cronet/ios/Cronet.h" |
| 6 | 6 |
| 7 #include <memory> | 7 #include <memory> |
| 8 | 8 |
| 9 #include "base/lazy_instance.h" | 9 #include "base/lazy_instance.h" |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| 11 #include "base/mac/bundle_locations.h" | |
| 12 #include "base/mac/scoped_block.h" | |
| 13 #include "base/memory/ptr_util.h" | |
| 11 #include "base/memory/scoped_vector.h" | 14 #include "base/memory/scoped_vector.h" |
| 12 #include "base/strings/sys_string_conversions.h" | 15 #include "base/strings/sys_string_conversions.h" |
| 13 #include "components/cronet/ios/cronet_environment.h" | 16 #include "components/cronet/ios/cronet_environment.h" |
| 14 #include "components/cronet/url_request_context_config.h" | 17 #include "components/cronet/url_request_context_config.h" |
| 18 #include "ios/net/crn_http_protocol_handler.h" | |
| 19 #include "ios/net/empty_nsurlcache.h" | |
| 20 #include "net/cert/cert_verifier.h" | |
| 21 #include "net/url_request/url_request_context_getter.h" | |
| 15 | 22 |
| 16 namespace { | 23 namespace { |
| 17 | 24 |
| 25 class CronetHttpProtocolHandlerDelegate; | |
| 26 | |
| 18 // Currently there is one and only one instance of CronetEnvironment, | 27 // Currently there is one and only one instance of CronetEnvironment, |
| 19 // which is leaked at the shutdown. We should consider allowing multiple | 28 // which is leaked at the shutdown. We should consider allowing multiple |
| 20 // instances if that makes sense in the future. | 29 // instances if that makes sense in the future. |
| 21 base::LazyInstance<std::unique_ptr<cronet::CronetEnvironment>>::Leaky | 30 base::LazyInstance<std::unique_ptr<cronet::CronetEnvironment>>::Leaky |
| 22 gChromeNet = LAZY_INSTANCE_INITIALIZER; | 31 gChromeNet = LAZY_INSTANCE_INITIALIZER; |
| 23 | 32 |
| 24 BOOL gHttp2Enabled = YES; | 33 BOOL gHttp2Enabled = YES; |
| 25 BOOL gQuicEnabled = NO; | 34 BOOL gQuicEnabled = NO; |
| 26 ScopedVector<cronet::URLRequestContextConfig::QuicHint> gQuicHints; | 35 ScopedVector<cronet::URLRequestContextConfig::QuicHint> gQuicHints; |
| 27 NSString* gUserAgent = nil; | 36 NSString* gUserAgent = nil; |
| 37 BOOL gUserAgentPartial = NO; | |
| 28 NSString* gSslKeyLogFileName = nil; | 38 NSString* gSslKeyLogFileName = nil; |
| 39 RequestFilterBlock gRequestFilterBlock = nil; | |
| 40 std::unique_ptr<CronetHttpProtocolHandlerDelegate> gHttpProtocolHandlerDelegate; | |
| 41 NSURLCache* gPreservedSharedURLCache = nil; | |
| 42 BOOL gEnableTestCertVerifierForTesting = FALSE; | |
| 43 NSString* gHostResolverRulesForTesting = @""; | |
| 44 | |
| 45 // CertVerifier, which allows any certificates for testing. | |
| 46 class TestCertVerifier : public net::CertVerifier { | |
| 47 int Verify(const RequestParams& params, | |
| 48 net::CRLSet* crl_set, | |
| 49 net::CertVerifyResult* verify_result, | |
| 50 const net::CompletionCallback& callback, | |
| 51 std::unique_ptr<Request>* out_req, | |
| 52 const net::NetLogWithSource& net_log) override { | |
| 53 net::Error result = net::OK; | |
| 54 verify_result->verified_cert = params.certificate(); | |
| 55 verify_result->cert_status = net::MapNetErrorToCertStatus(result); | |
| 56 return result; | |
| 57 } | |
| 58 }; | |
| 59 | |
| 60 // net::HTTPProtocolHandlerDelegate for Cronet. | |
| 61 class CronetHttpProtocolHandlerDelegate | |
| 62 : public net::HTTPProtocolHandlerDelegate { | |
| 63 public: | |
| 64 CronetHttpProtocolHandlerDelegate(net::URLRequestContextGetter* getter, | |
| 65 RequestFilterBlock filter) | |
| 66 : getter_(getter), filter_(filter, base::scoped_policy::RETAIN) {} | |
| 67 | |
| 68 void SetRequestFilterBlock(RequestFilterBlock filter) { | |
| 69 filter_.reset(filter); | |
| 70 } | |
| 71 | |
| 72 private: | |
| 73 // net::HTTPProtocolHandlerDelegate implementation: | |
| 74 bool CanHandleRequest(NSURLRequest* request) override { | |
| 75 if (filter_) { | |
| 76 RequestFilterBlock block = filter_.get(); | |
| 77 return block(request); | |
| 78 } | |
| 79 return true; | |
| 80 } | |
| 81 | |
| 82 bool IsRequestSupported(NSURLRequest* request) override { | |
| 83 NSString* scheme = [[request URL] scheme]; | |
| 84 if (!scheme) | |
| 85 return false; | |
| 86 return [scheme caseInsensitiveCompare:@"data"] == NSOrderedSame || | |
| 87 [scheme caseInsensitiveCompare:@"http"] == NSOrderedSame || | |
| 88 [scheme caseInsensitiveCompare:@"https"] == NSOrderedSame; | |
| 89 } | |
| 90 | |
| 91 net::URLRequestContextGetter* GetDefaultURLRequestContext() override { | |
| 92 return getter_.get(); | |
| 93 } | |
| 94 | |
| 95 scoped_refptr<net::URLRequestContextGetter> getter_; | |
| 96 base::mac::ScopedBlock<RequestFilterBlock> filter_; | |
| 97 }; | |
| 29 | 98 |
| 30 } // namespace | 99 } // namespace |
| 31 | 100 |
| 32 @implementation Cronet | 101 @implementation Cronet |
| 33 | 102 |
| 103 + (void)configureCronetEnvironmentForTesting: | |
| 104 (cronet::CronetEnvironment*)cronetEnvironment { | |
| 105 cronetEnvironment->set_host_resolver_rules( | |
| 106 [gHostResolverRulesForTesting UTF8String]); | |
| 107 if (gEnableTestCertVerifierForTesting) { | |
| 108 std::unique_ptr<TestCertVerifier> test_cert_verifier = | |
| 109 base::MakeUnique<TestCertVerifier>(); | |
| 110 cronetEnvironment->set_cert_verifier(std::move(test_cert_verifier)); | |
| 111 } | |
| 112 } | |
| 113 | |
| 114 + (NSString*)getAcceptLanguages { | |
| 115 // Use the framework bundle to search for resources. | |
| 116 NSBundle* frameworkBundle = [NSBundle bundleForClass:self]; | |
| 117 NSString* bundlePath = | |
| 118 [frameworkBundle pathForResource:@"cronet_resources" ofType:@"bundle"]; | |
| 119 NSBundle* bundle = [NSBundle bundleWithPath:bundlePath]; | |
| 120 NSString* acceptLanguages = NSLocalizedStringWithDefaultValue( | |
| 121 @"IDS_ACCEPT_LANGUAGES", @"Localizable", bundle, @"en-US,en", | |
| 122 @"These values are copied from Chrome's .xtb files, so the same " | |
| 123 "values are used in the |Accept-Language| header. Key name matches " | |
| 124 "Chrome's."); | |
| 125 if (acceptLanguages == Nil) | |
| 126 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
| |
| 127 return acceptLanguages; | |
| 128 } | |
| 129 | |
| 34 + (void)checkNotStarted { | 130 + (void)checkNotStarted { |
| 35 CHECK(gChromeNet == NULL) << "Cronet is already started."; | 131 CHECK(gChromeNet == NULL) << "Cronet is already started."; |
| 36 } | 132 } |
| 37 | 133 |
| 38 + (void)setHttp2Enabled:(BOOL)http2Enabled { | 134 + (void)setHttp2Enabled:(BOOL)http2Enabled { |
| 39 [self checkNotStarted]; | 135 [self checkNotStarted]; |
| 40 gHttp2Enabled = http2Enabled; | 136 gHttp2Enabled = http2Enabled; |
| 41 } | 137 } |
| 42 | 138 |
| 43 + (void)setQuicEnabled:(BOOL)quicEnabled { | 139 + (void)setQuicEnabled:(BOOL)quicEnabled { |
| 44 [self checkNotStarted]; | 140 [self checkNotStarted]; |
| 45 gQuicEnabled = quicEnabled; | 141 gQuicEnabled = quicEnabled; |
| 46 } | 142 } |
| 47 | 143 |
| 48 + (void)addQuicHint:(NSString*)host port:(int)port altPort:(int)altPort { | 144 + (void)addQuicHint:(NSString*)host port:(int)port altPort:(int)altPort { |
| 49 [self checkNotStarted]; | 145 [self checkNotStarted]; |
| 50 gQuicHints.push_back(new cronet::URLRequestContextConfig::QuicHint( | 146 gQuicHints.push_back(new cronet::URLRequestContextConfig::QuicHint( |
| 51 base::SysNSStringToUTF8(host), port, altPort)); | 147 base::SysNSStringToUTF8(host), port, altPort)); |
| 52 } | 148 } |
| 53 | 149 |
| 54 + (void)setPartialUserAgent:(NSString*)userAgent { | 150 + (void)setUserAgent:(NSString*)userAgent partial:(BOOL)partial { |
| 55 [self checkNotStarted]; | 151 [self checkNotStarted]; |
| 56 gUserAgent = userAgent; | 152 gUserAgent = userAgent; |
| 153 gUserAgentPartial = partial; | |
| 57 } | 154 } |
| 58 | 155 |
| 59 + (void)setSslKeyLogFileName:(NSString*)sslKeyLogFileName { | 156 + (void)setSslKeyLogFileName:(NSString*)sslKeyLogFileName { |
| 60 [self checkNotStarted]; | 157 [self checkNotStarted]; |
| 61 gSslKeyLogFileName = sslKeyLogFileName; | 158 gSslKeyLogFileName = sslKeyLogFileName; |
| 62 } | 159 } |
| 63 | 160 |
| 161 + (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.
| |
| 162 if (gHttpProtocolHandlerDelegate.get()) | |
| 163 gHttpProtocolHandlerDelegate.get()->SetRequestFilterBlock(block); | |
| 164 else | |
| 165 gRequestFilterBlock = block; | |
| 166 } | |
| 167 | |
| 64 + (void)startInternal { | 168 + (void)startInternal { |
| 65 cronet::CronetEnvironment::Initialize(); | 169 cronet::CronetEnvironment::Initialize(); |
| 66 std::string partialUserAgent = base::SysNSStringToUTF8(gUserAgent); | 170 std::string user_agent = base::SysNSStringToUTF8(gUserAgent); |
| 67 gChromeNet.Get().reset(new cronet::CronetEnvironment(partialUserAgent)); | 171 gChromeNet.Get().reset( |
| 172 new cronet::CronetEnvironment(user_agent, gUserAgentPartial)); | |
| 173 gChromeNet.Get()->set_accept_language( | |
| 174 base::SysNSStringToUTF8([self getAcceptLanguages])); | |
| 68 | 175 |
| 69 gChromeNet.Get()->set_http2_enabled(gHttp2Enabled); | 176 gChromeNet.Get()->set_http2_enabled(gHttp2Enabled); |
| 70 gChromeNet.Get()->set_quic_enabled(gQuicEnabled); | 177 gChromeNet.Get()->set_quic_enabled(gQuicEnabled); |
| 71 gChromeNet.Get()->set_ssl_key_log_file_name( | 178 gChromeNet.Get()->set_ssl_key_log_file_name( |
| 72 base::SysNSStringToUTF8(gSslKeyLogFileName)); | 179 base::SysNSStringToUTF8(gSslKeyLogFileName)); |
| 73 for (const auto* quicHint : gQuicHints) { | 180 for (const auto* quicHint : gQuicHints) { |
| 74 gChromeNet.Get()->AddQuicHint(quicHint->host, quicHint->port, | 181 gChromeNet.Get()->AddQuicHint(quicHint->host, quicHint->port, |
| 75 quicHint->alternate_port); | 182 quicHint->alternate_port); |
| 76 } | 183 } |
| 184 | |
| 185 [self configureCronetEnvironmentForTesting:gChromeNet.Get().get()]; | |
| 77 gChromeNet.Get()->Start(); | 186 gChromeNet.Get()->Start(); |
| 187 gHttpProtocolHandlerDelegate.reset(new CronetHttpProtocolHandlerDelegate( | |
| 188 gChromeNet.Get()->GetURLRequestContextGetter(), gRequestFilterBlock)); | |
| 189 net::HTTPProtocolHandlerDelegate::SetInstance( | |
| 190 gHttpProtocolHandlerDelegate.get()); | |
| 191 gRequestFilterBlock = nil; | |
| 78 } | 192 } |
| 79 | 193 |
| 80 + (void)start { | 194 + (void)start { |
| 81 static dispatch_once_t onceToken; | 195 static dispatch_once_t onceToken; |
| 82 dispatch_once(&onceToken, ^{ | 196 dispatch_once(&onceToken, ^{ |
| 83 if (![NSThread isMainThread]) { | 197 if (![NSThread isMainThread]) { |
| 84 dispatch_sync(dispatch_get_main_queue(), ^(void) { | 198 dispatch_sync(dispatch_get_main_queue(), ^(void) { |
| 85 [self startInternal]; | 199 [self startInternal]; |
| 86 }); | 200 }); |
| 87 } else { | 201 } else { |
| 88 [self startInternal]; | 202 [self startInternal]; |
| 89 } | 203 } |
| 90 }); | 204 }); |
| 91 } | 205 } |
| 92 | 206 |
| 207 + (void)registerHttpProtocolHandler { | |
| 208 if (gPreservedSharedURLCache == nil) { | |
| 209 gPreservedSharedURLCache = [NSURLCache sharedURLCache]; | |
| 210 } | |
| 211 // Disable the default cache. | |
| 212 [NSURLCache setSharedURLCache:[EmptyNSURLCache emptyNSURLCache]]; | |
| 213 // Register the chrome http protocol handler to replace the default one. | |
| 214 BOOL success = | |
| 215 [NSURLProtocol registerClass:[CRNPauseableHTTPProtocolHandler class]]; | |
| 216 DCHECK(success); | |
| 217 } | |
| 218 | |
| 219 + (void)unregisterHttpProtocolHandler { | |
| 220 // Set up SharedURLCache preserved in registerHttpProtocolHandler. | |
| 221 if (gPreservedSharedURLCache != nil) { | |
| 222 [NSURLCache setSharedURLCache:gPreservedSharedURLCache]; | |
| 223 gPreservedSharedURLCache = nil; | |
| 224 } | |
| 225 [NSURLProtocol unregisterClass:[CRNPauseableHTTPProtocolHandler class]]; | |
| 226 } | |
| 227 | |
| 228 + (void)installIntoSessionConfiguration:(NSURLSessionConfiguration*)config { | |
| 229 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://"
| |
| 230 } | |
| 231 | |
| 93 + (void)startNetLogToFile:(NSString*)fileName logBytes:(BOOL)logBytes { | 232 + (void)startNetLogToFile:(NSString*)fileName logBytes:(BOOL)logBytes { |
| 94 if (gChromeNet.Get().get() && [fileName length]) { | 233 if (gChromeNet.Get().get() && [fileName length]) { |
| 95 gChromeNet.Get()->StartNetLog([fileName UTF8String], logBytes); | 234 gChromeNet.Get()->StartNetLog([fileName UTF8String], logBytes); |
| 96 } | 235 } |
| 97 } | 236 } |
| 98 | 237 |
| 99 + (void)stopNetLog { | 238 + (void)stopNetLog { |
| 100 if (gChromeNet.Get().get()) { | 239 if (gChromeNet.Get().get()) { |
| 101 gChromeNet.Get()->StopNetLog(); | 240 gChromeNet.Get()->StopNetLog(); |
| 102 } | 241 } |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 114 + (cronet_engine*)getGlobalEngine { | 253 + (cronet_engine*)getGlobalEngine { |
| 115 DCHECK(gChromeNet.Get().get()); | 254 DCHECK(gChromeNet.Get().get()); |
| 116 if (gChromeNet.Get().get()) { | 255 if (gChromeNet.Get().get()) { |
| 117 static cronet_engine engine; | 256 static cronet_engine engine; |
| 118 engine.obj = gChromeNet.Get().get(); | 257 engine.obj = gChromeNet.Get().get(); |
| 119 return &engine; | 258 return &engine; |
| 120 } | 259 } |
| 121 return nil; | 260 return nil; |
| 122 } | 261 } |
| 123 | 262 |
| 263 + (NSData*)getGlobalMetricsDeltas { | |
| 264 if (!gChromeNet.Get().get()) { | |
| 265 return nil; | |
| 266 } | |
| 267 std::vector<uint8_t> deltas(gChromeNet.Get()->GetHistogramDeltas()); | |
| 268 return [NSData dataWithBytes:deltas.data() length:deltas.size()]; | |
| 269 } | |
| 270 | |
| 271 + (void)enableTestCertVerifierForTesting { | |
| 272 gEnableTestCertVerifierForTesting = TRUE; | |
|
kapishnikov
2016/10/19 18:28:53
TRUE -> YES
mef
2016/10/19 20:12:58
Done.
| |
| 273 } | |
| 274 | |
| 275 + (void)setHostResolverRulesForTesting:(NSString*)hostResolverRulesForTesting { | |
| 276 gHostResolverRulesForTesting = hostResolverRulesForTesting; | |
| 277 } | |
| 278 | |
| 124 // This is a non-public dummy method that prevents the linker from stripping out | 279 // This is a non-public dummy method that prevents the linker from stripping out |
| 125 // the otherwise non-referenced methods from 'cronet_bidirectional_stream.cc'. | 280 // the otherwise non-referenced methods from 'cronet_bidirectional_stream.cc'. |
| 126 + (void)preventStrippingCronetBidirectionalStream { | 281 + (void)preventStrippingCronetBidirectionalStream { |
| 127 cronet_bidirectional_stream_create(NULL, 0, 0); | 282 cronet_bidirectional_stream_create(NULL, 0, 0); |
| 128 } | 283 } |
| 129 | 284 |
| 130 @end | 285 @end |
| OLD | NEW |