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 |