1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/cronet/android/cronet_url_request_context_adapter.h"
7 #include "base/android/jni_android.h"
8 #include "base/android/jni_string.h"
10 #include "base/files/file_util.h"
11 #include "base/files/scoped_file.h"
12 #include "base/logging.h"
13 #include "base/memory/scoped_vector.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/prefs/pref_filter.h"
16 #include "base/prefs/pref_registry_simple.h"
17 #include "base/prefs/pref_service.h"
18 #include "base/prefs/pref_service_factory.h"
19 #include "base/single_thread_task_runner.h"
20 #include "base/time/time.h"
21 #include "base/values.h"
22 #include "components/cronet/url_request_context_config.h"
23 #include "jni/CronetUrlRequestContext_jni.h"
24 #include "net/base/load_flags.h"
25 #include "net/base/net_errors.h"
26 #include "net/base/network_delegate_impl.h"
27 #include "net/http/http_auth_handler_factory.h"
28 #include "net/http/http_server_properties_manager.h"
29 #include "net/log/write_to_file_net_log_observer.h"
30 #include "net/proxy/proxy_config_service_android.h"
31 #include "net/proxy/proxy_service.h"
32 #include "net/sdch/sdch_owner.h"
33 #include "net/url_request/url_request_context.h"
34 #include "net/url_request/url_request_context_builder.h"
35 #include "net/url_request/url_request_interceptor.h"
37 #if defined(DATA_REDUCTION_PROXY_SUPPORT)
38 #include "components/cronet/android/cronet_data_reduction_proxy.h"
43 const char kHttpServerProperties
[] = "net.http_server_properties";
45 class BasicNetworkDelegate
: public net::NetworkDelegateImpl
{
47 BasicNetworkDelegate() {}
48 ~BasicNetworkDelegate() override
{}
51 // net::NetworkDelegate implementation.
52 int OnBeforeURLRequest(net::URLRequest
* request
,
53 const net::CompletionCallback
& callback
,
54 GURL
* new_url
) override
{
58 int OnBeforeSendHeaders(net::URLRequest
* request
,
59 const net::CompletionCallback
& callback
,
60 net::HttpRequestHeaders
* headers
) override
{
64 void OnSendHeaders(net::URLRequest
* request
,
65 const net::HttpRequestHeaders
& headers
) override
{}
67 int OnHeadersReceived(
68 net::URLRequest
* request
,
69 const net::CompletionCallback
& callback
,
70 const net::HttpResponseHeaders
* original_response_headers
,
71 scoped_refptr
<net::HttpResponseHeaders
>* _response_headers
,
72 GURL
* allowed_unsafe_redirect_url
) override
{
76 void OnBeforeRedirect(net::URLRequest
* request
,
77 const GURL
& new_location
) override
{}
79 void OnResponseStarted(net::URLRequest
* request
) override
{}
81 void OnCompleted(net::URLRequest
* request
, bool started
) override
{}
83 void OnURLRequestDestroyed(net::URLRequest
* request
) override
{}
85 void OnPACScriptError(int line_number
, const base::string16
& error
) override
{
88 NetworkDelegate::AuthRequiredResponse
OnAuthRequired(
89 net::URLRequest
* request
,
90 const net::AuthChallengeInfo
& auth_info
,
91 const AuthCallback
& callback
,
92 net::AuthCredentials
* credentials
) override
{
93 return net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_NO_ACTION
;
96 bool OnCanGetCookies(const net::URLRequest
& request
,
97 const net::CookieList
& cookie_list
) override
{
101 bool OnCanSetCookie(const net::URLRequest
& request
,
102 const std::string
& cookie_line
,
103 net::CookieOptions
* options
) override
{
107 bool OnCanAccessFile(const net::URLRequest
& request
,
108 const base::FilePath
& path
) const override
{
112 DISALLOW_COPY_AND_ASSIGN(BasicNetworkDelegate
);
119 // Explicitly register static JNI functions.
120 bool CronetUrlRequestContextAdapterRegisterJni(JNIEnv
* env
) {
121 return RegisterNativesImpl(env
);
124 CronetURLRequestContextAdapter::CronetURLRequestContextAdapter(
125 scoped_ptr
<URLRequestContextConfig
> context_config
)
126 : network_thread_(new base::Thread("network")),
127 http_server_properties_manager_(nullptr),
128 context_config_(context_config
.Pass()),
129 is_context_initialized_(false),
130 default_load_flags_(net::LOAD_NORMAL
) {
131 base::Thread::Options options
;
132 options
.message_loop_type
= base::MessageLoop::TYPE_IO
;
133 network_thread_
->StartWithOptions(options
);
136 CronetURLRequestContextAdapter::~CronetURLRequestContextAdapter() {
137 DCHECK(GetNetworkTaskRunner()->BelongsToCurrentThread());
139 if (http_server_properties_manager_
)
140 http_server_properties_manager_
->ShutdownOnPrefThread();
142 pref_service_
->CommitPendingWrite();
143 StopNetLogOnNetworkThread();
146 void CronetURLRequestContextAdapter::InitRequestContextOnMainThread(
149 base::android::ScopedJavaGlobalRef
<jobject
> jcaller_ref
;
150 jcaller_ref
.Reset(env
, jcaller
);
151 net::ProxyConfigServiceAndroid
* android_proxy_config_service
=
152 static_cast<net::ProxyConfigServiceAndroid
*>(
153 net::ProxyService::CreateSystemProxyConfigService(
154 GetNetworkTaskRunner(), nullptr /* Ignored on Android */));
155 // If a PAC URL is present, ignore it and use the address and port of
156 // Android system's local HTTP proxy server. See: crbug.com/432539.
157 android_proxy_config_service
->set_exclude_pac_url(true);
158 proxy_config_service_
.reset(android_proxy_config_service
);
159 GetNetworkTaskRunner()->PostTask(
161 base::Bind(&CronetURLRequestContextAdapter::InitializeOnNetworkThread
,
162 base::Unretained(this), Passed(&context_config_
),
166 void CronetURLRequestContextAdapter::InitializeOnNetworkThread(
167 scoped_ptr
<URLRequestContextConfig
> config
,
168 const base::android::ScopedJavaGlobalRef
<jobject
>&
169 jcronet_url_request_context
) {
170 DCHECK(GetNetworkTaskRunner()->BelongsToCurrentThread());
171 DCHECK(!is_context_initialized_
);
172 DCHECK(proxy_config_service_
);
173 // TODO(mmenke): Add method to have the builder enable SPDY.
174 net::URLRequestContextBuilder context_builder
;
176 net_log_
.reset(new net::NetLog
);
177 scoped_ptr
<net::NetworkDelegate
> network_delegate(new BasicNetworkDelegate());
178 #if defined(DATA_REDUCTION_PROXY_SUPPORT)
179 DCHECK(!data_reduction_proxy_
);
180 // For now, the choice to enable the data reduction proxy happens once,
181 // at initialization. It cannot be disabled thereafter.
182 if (!config
->data_reduction_proxy_key
.empty()) {
183 data_reduction_proxy_
.reset(new CronetDataReductionProxy(
184 config
->data_reduction_proxy_key
, config
->data_reduction_primary_proxy
,
185 config
->data_reduction_fallback_proxy
,
186 config
->data_reduction_secure_proxy_check_url
, config
->user_agent
,
187 GetNetworkTaskRunner(), net_log_
.get()));
189 data_reduction_proxy_
->CreateNetworkDelegate(network_delegate
.Pass());
190 ScopedVector
<net::URLRequestInterceptor
> interceptors
;
191 interceptors
.push_back(data_reduction_proxy_
->CreateInterceptor());
192 context_builder
.SetInterceptors(interceptors
.Pass());
194 #endif // defined(DATA_REDUCTION_PROXY_SUPPORT)
195 context_builder
.set_network_delegate(network_delegate
.Pass());
196 context_builder
.set_net_log(net_log_
.get());
198 // Android provides a local HTTP proxy server that handles proxying when a PAC
199 // URL is present. Create a proxy service without a resolver and rely on this
200 // local HTTP proxy. See: crbug.com/432539.
201 context_builder
.set_proxy_service(
202 net::ProxyService::CreateWithoutProxyResolver(
203 proxy_config_service_
.release(), net_log_
.get()));
204 config
->ConfigureURLRequestContextBuilder(&context_builder
);
206 // Set up pref file if storage path is specified.
207 if (!config
->storage_path
.empty()) {
208 base::FilePath
filepath(config
->storage_path
);
209 filepath
= filepath
.Append(FILE_PATH_LITERAL("local_prefs.json"));
210 json_pref_store_
= new JsonPrefStore(
211 filepath
, GetFileThread()->task_runner(), scoped_ptr
<PrefFilter
>());
212 context_builder
.SetFileTaskRunner(GetFileThread()->task_runner());
214 // Set up HttpServerPropertiesManager.
215 base::PrefServiceFactory factory
;
216 factory
.set_user_prefs(json_pref_store_
);
217 scoped_refptr
<PrefRegistrySimple
> registry(new PrefRegistrySimple());
218 registry
->RegisterDictionaryPref(kHttpServerProperties
,
219 new base::DictionaryValue());
220 pref_service_
= factory
.Create(registry
.get()).Pass();
222 scoped_ptr
<net::HttpServerPropertiesManager
> http_server_properties_manager(
223 new net::HttpServerPropertiesManager(pref_service_
.get(),
224 kHttpServerProperties
,
225 GetNetworkTaskRunner()));
226 http_server_properties_manager
->InitializeOnNetworkThread();
227 http_server_properties_manager_
= http_server_properties_manager
.get();
228 context_builder
.SetHttpServerProperties(
229 http_server_properties_manager
.Pass());
232 context_
= context_builder
.Build().Pass();
234 default_load_flags_
= net::LOAD_DO_NOT_SAVE_COOKIES
|
235 net::LOAD_DO_NOT_SEND_COOKIES
;
236 if (config
->load_disable_cache
)
237 default_load_flags_
|= net::LOAD_DISABLE_CACHE
;
239 if (config
->enable_sdch
) {
240 DCHECK(context_
->sdch_manager());
242 new net::SdchOwner(context_
->sdch_manager(), context_
.get()));
243 if (json_pref_store_
)
244 sdch_owner_
->EnablePersistentStorage(json_pref_store_
.get());
247 // Currently (circa M39) enabling QUIC requires setting probability threshold.
248 if (config
->enable_quic
) {
249 context_
->http_server_properties()
250 ->SetAlternativeServiceProbabilityThreshold(0.0f
);
251 for (auto hint
= config
->quic_hints
.begin();
252 hint
!= config
->quic_hints
.end(); ++hint
) {
253 const URLRequestContextConfig::QuicHint
& quic_hint
= **hint
;
254 if (quic_hint
.host
.empty()) {
255 LOG(ERROR
) << "Empty QUIC hint host: " << quic_hint
.host
;
259 url::CanonHostInfo host_info
;
260 std::string
canon_host(net::CanonicalizeHost(quic_hint
.host
, &host_info
));
261 if (!host_info
.IsIPAddress() &&
262 !net::IsCanonicalizedHostCompliant(canon_host
)) {
263 LOG(ERROR
) << "Invalid QUIC hint host: " << quic_hint
.host
;
267 if (quic_hint
.port
<= std::numeric_limits
<uint16
>::min() ||
268 quic_hint
.port
> std::numeric_limits
<uint16
>::max()) {
269 LOG(ERROR
) << "Invalid QUIC hint port: "
274 if (quic_hint
.alternate_port
<= std::numeric_limits
<uint16
>::min() ||
275 quic_hint
.alternate_port
> std::numeric_limits
<uint16
>::max()) {
276 LOG(ERROR
) << "Invalid QUIC hint alternate port: "
277 << quic_hint
.alternate_port
;
281 net::HostPortPair
quic_hint_host_port_pair(canon_host
,
283 net::AlternativeService
alternative_service(
284 net::AlternateProtocol::QUIC
, "",
285 static_cast<uint16
>(quic_hint
.alternate_port
));
286 context_
->http_server_properties()->SetAlternativeService(
287 quic_hint_host_port_pair
, alternative_service
, 1.0f
,
292 JNIEnv
* env
= base::android::AttachCurrentThread();
293 Java_CronetUrlRequestContext_initNetworkThread(
294 env
, jcronet_url_request_context
.obj());
296 #if defined(DATA_REDUCTION_PROXY_SUPPORT)
297 if (data_reduction_proxy_
)
298 data_reduction_proxy_
->Init(true, GetURLRequestContext());
300 is_context_initialized_
= true;
301 while (!tasks_waiting_for_context_
.empty()) {
302 tasks_waiting_for_context_
.front().Run();
303 tasks_waiting_for_context_
.pop();
307 void CronetURLRequestContextAdapter::Destroy(JNIEnv
* env
, jobject jcaller
) {
308 DCHECK(!GetNetworkTaskRunner()->BelongsToCurrentThread());
309 // Stick network_thread_ in a local, as |this| may be destroyed from the
310 // network thread before delete network_thread is called.
311 base::Thread
* network_thread
= network_thread_
;
312 GetNetworkTaskRunner()->DeleteSoon(FROM_HERE
, this);
313 // Deleting thread stops it after all tasks are completed.
314 delete network_thread
;
317 net::URLRequestContext
* CronetURLRequestContextAdapter::GetURLRequestContext() {
319 LOG(ERROR
) << "URLRequestContext is not set up";
321 return context_
.get();
324 void CronetURLRequestContextAdapter::PostTaskToNetworkThread(
325 const tracked_objects::Location
& posted_from
,
326 const base::Closure
& callback
) {
327 GetNetworkTaskRunner()->PostTask(
328 posted_from
, base::Bind(&CronetURLRequestContextAdapter::
329 RunTaskAfterContextInitOnNetworkThread
,
330 base::Unretained(this), callback
));
333 void CronetURLRequestContextAdapter::RunTaskAfterContextInitOnNetworkThread(
334 const base::Closure
& task_to_run_after_context_init
) {
335 DCHECK(GetNetworkTaskRunner()->BelongsToCurrentThread());
336 if (is_context_initialized_
) {
337 DCHECK(tasks_waiting_for_context_
.empty());
338 task_to_run_after_context_init
.Run();
341 tasks_waiting_for_context_
.push(task_to_run_after_context_init
);
344 bool CronetURLRequestContextAdapter::IsOnNetworkThread() const {
345 return GetNetworkTaskRunner()->BelongsToCurrentThread();
348 scoped_refptr
<base::SingleThreadTaskRunner
>
349 CronetURLRequestContextAdapter::GetNetworkTaskRunner() const {
350 return network_thread_
->task_runner();
353 void CronetURLRequestContextAdapter::StartNetLogToFile(JNIEnv
* env
,
357 PostTaskToNetworkThread(
360 &CronetURLRequestContextAdapter::StartNetLogToFileOnNetworkThread
,
361 base::Unretained(this),
362 base::android::ConvertJavaStringToUTF8(env
, jfile_name
), jlog_all
));
365 void CronetURLRequestContextAdapter::StopNetLog(JNIEnv
* env
, jobject jcaller
) {
366 PostTaskToNetworkThread(
368 base::Bind(&CronetURLRequestContextAdapter::StopNetLogOnNetworkThread
,
369 base::Unretained(this)));
372 void CronetURLRequestContextAdapter::StartNetLogToFileOnNetworkThread(
373 const std::string
& file_name
, bool log_all
) {
374 DCHECK(GetNetworkTaskRunner()->BelongsToCurrentThread());
375 DCHECK(is_context_initialized_
);
377 // Do nothing if already logging to a file.
378 if (write_to_file_observer_
)
380 base::FilePath
file_path(file_name
);
381 base::ScopedFILE
file(base::OpenFile(file_path
, "w"));
385 write_to_file_observer_
.reset(new net::WriteToFileNetLogObserver());
387 write_to_file_observer_
->set_capture_mode(
388 net::NetLogCaptureMode::IncludeSocketBytes());
390 write_to_file_observer_
->StartObserving(context_
->net_log(), file
.Pass(),
391 nullptr, context_
.get());
394 void CronetURLRequestContextAdapter::StopNetLogOnNetworkThread() {
395 DCHECK(GetNetworkTaskRunner()->BelongsToCurrentThread());
396 if (write_to_file_observer_
) {
397 write_to_file_observer_
->StopObserving(context_
.get());
398 write_to_file_observer_
.reset();
402 base::Thread
* CronetURLRequestContextAdapter::GetFileThread() {
403 DCHECK(GetNetworkTaskRunner()->BelongsToCurrentThread());
405 file_thread_
.reset(new base::Thread("Network File Thread"));
406 file_thread_
->Start();
408 return file_thread_
.get();
411 // Creates RequestContextAdater if config is valid URLRequestContextConfig,
412 // returns 0 otherwise.
413 static jlong
CreateRequestContextAdapter(JNIEnv
* env
,
414 const JavaParamRef
<jclass
>& jcaller
,
415 const JavaParamRef
<jstring
>& jconfig
) {
416 std::string config_string
=
417 base::android::ConvertJavaStringToUTF8(env
, jconfig
);
418 scoped_ptr
<URLRequestContextConfig
> context_config(
419 new URLRequestContextConfig());
420 if (!context_config
->LoadFromJSON(config_string
))
423 CronetURLRequestContextAdapter
* context_adapter
=
424 new CronetURLRequestContextAdapter(context_config
.Pass());
425 return reinterpret_cast<jlong
>(context_adapter
);
428 static jint
SetMinLogLevel(JNIEnv
* env
,
429 const JavaParamRef
<jclass
>& jcaller
,
431 jint old_log_level
= static_cast<jint
>(logging::GetMinLogLevel());
432 // MinLogLevel is global, shared by all URLRequestContexts.
433 logging::SetMinLogLevel(static_cast<int>(jlog_level
));
434 return old_log_level
;
437 } // namespace cronet