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_service.h"
31 #include "net/sdch/sdch_owner.h"
32 #include "net/url_request/url_request_context.h"
33 #include "net/url_request/url_request_context_builder.h"
34 #include "net/url_request/url_request_interceptor.h"
36 #if defined(DATA_REDUCTION_PROXY_SUPPORT)
37 #include "components/cronet/android/cronet_data_reduction_proxy.h"
42 const char kHttpServerProperties
[] = "net.http_server_properties";
44 class BasicNetworkDelegate
: public net::NetworkDelegateImpl
{
46 BasicNetworkDelegate() {}
47 ~BasicNetworkDelegate() override
{}
50 // net::NetworkDelegate implementation.
51 int OnBeforeURLRequest(net::URLRequest
* request
,
52 const net::CompletionCallback
& callback
,
53 GURL
* new_url
) override
{
57 int OnBeforeSendHeaders(net::URLRequest
* request
,
58 const net::CompletionCallback
& callback
,
59 net::HttpRequestHeaders
* headers
) override
{
63 void OnSendHeaders(net::URLRequest
* request
,
64 const net::HttpRequestHeaders
& headers
) override
{}
66 int OnHeadersReceived(
67 net::URLRequest
* request
,
68 const net::CompletionCallback
& callback
,
69 const net::HttpResponseHeaders
* original_response_headers
,
70 scoped_refptr
<net::HttpResponseHeaders
>* _response_headers
,
71 GURL
* allowed_unsafe_redirect_url
) override
{
75 void OnBeforeRedirect(net::URLRequest
* request
,
76 const GURL
& new_location
) override
{}
78 void OnResponseStarted(net::URLRequest
* request
) override
{}
80 void OnRawBytesRead(const net::URLRequest
& request
, int bytes_read
) override
{
83 void OnCompleted(net::URLRequest
* request
, bool started
) override
{}
85 void OnURLRequestDestroyed(net::URLRequest
* request
) override
{}
87 void OnPACScriptError(int line_number
, const base::string16
& error
) override
{
90 NetworkDelegate::AuthRequiredResponse
OnAuthRequired(
91 net::URLRequest
* request
,
92 const net::AuthChallengeInfo
& auth_info
,
93 const AuthCallback
& callback
,
94 net::AuthCredentials
* credentials
) override
{
95 return net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_NO_ACTION
;
98 bool OnCanGetCookies(const net::URLRequest
& request
,
99 const net::CookieList
& cookie_list
) override
{
103 bool OnCanSetCookie(const net::URLRequest
& request
,
104 const std::string
& cookie_line
,
105 net::CookieOptions
* options
) override
{
109 bool OnCanAccessFile(const net::URLRequest
& request
,
110 const base::FilePath
& path
) const override
{
114 DISALLOW_COPY_AND_ASSIGN(BasicNetworkDelegate
);
121 // Explicitly register static JNI functions.
122 bool CronetUrlRequestContextAdapterRegisterJni(JNIEnv
* env
) {
123 return RegisterNativesImpl(env
);
126 CronetURLRequestContextAdapter::CronetURLRequestContextAdapter(
127 scoped_ptr
<URLRequestContextConfig
> context_config
)
128 : network_thread_(new base::Thread("network")),
129 http_server_properties_manager_(nullptr),
130 context_config_(context_config
.Pass()),
131 is_context_initialized_(false),
132 default_load_flags_(net::LOAD_NORMAL
) {
133 base::Thread::Options options
;
134 options
.message_loop_type
= base::MessageLoop::TYPE_IO
;
135 network_thread_
->StartWithOptions(options
);
138 CronetURLRequestContextAdapter::~CronetURLRequestContextAdapter() {
139 DCHECK(GetNetworkTaskRunner()->BelongsToCurrentThread());
141 if (http_server_properties_manager_
)
142 http_server_properties_manager_
->ShutdownOnPrefThread();
144 pref_service_
->CommitPendingWrite();
145 StopNetLogOnNetworkThread();
148 void CronetURLRequestContextAdapter::InitRequestContextOnMainThread(
151 base::android::ScopedJavaGlobalRef
<jobject
> jcaller_ref
;
152 jcaller_ref
.Reset(env
, jcaller
);
153 proxy_config_service_
.reset(net::ProxyService::CreateSystemProxyConfigService(
154 GetNetworkTaskRunner(), nullptr));
155 GetNetworkTaskRunner()->PostTask(
157 base::Bind(&CronetURLRequestContextAdapter::InitializeOnNetworkThread
,
158 base::Unretained(this), Passed(&context_config_
),
162 void CronetURLRequestContextAdapter::InitializeOnNetworkThread(
163 scoped_ptr
<URLRequestContextConfig
> config
,
164 const base::android::ScopedJavaGlobalRef
<jobject
>&
165 jcronet_url_request_context
) {
166 DCHECK(GetNetworkTaskRunner()->BelongsToCurrentThread());
167 DCHECK(!is_context_initialized_
);
168 DCHECK(proxy_config_service_
);
169 // TODO(mmenke): Add method to have the builder enable SPDY.
170 net::URLRequestContextBuilder context_builder
;
172 scoped_ptr
<net::NetLog
> net_log(new net::NetLog
);
173 scoped_ptr
<net::NetworkDelegate
> network_delegate(new BasicNetworkDelegate());
174 #if defined(DATA_REDUCTION_PROXY_SUPPORT)
175 DCHECK(!data_reduction_proxy_
);
176 // For now, the choice to enable the data reduction proxy happens once,
177 // at initialization. It cannot be disabled thereafter.
178 if (!config
->data_reduction_proxy_key
.empty()) {
179 data_reduction_proxy_
.reset(
180 new CronetDataReductionProxy(
181 config
->data_reduction_proxy_key
,
182 config
->data_reduction_primary_proxy
,
183 config
->data_reduction_fallback_proxy
,
184 config
->data_reduction_secure_proxy_check_url
,
186 GetNetworkTaskRunner(),
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
.release());
196 context_builder
.set_net_log(net_log
.release());
197 context_builder
.set_proxy_config_service(proxy_config_service_
.release());
198 config
->ConfigureURLRequestContextBuilder(&context_builder
);
200 // Set up pref file if storage path is specified.
201 if (!config
->storage_path
.empty()) {
202 base::FilePath
filepath(config
->storage_path
);
203 filepath
= filepath
.Append(FILE_PATH_LITERAL("local_prefs.json"));
204 json_pref_store_
= new JsonPrefStore(
205 filepath
, GetFileThread()->task_runner(), scoped_ptr
<PrefFilter
>());
206 context_builder
.SetFileTaskRunner(GetFileThread()->task_runner());
208 // Set up HttpServerPropertiesManager.
209 base::PrefServiceFactory factory
;
210 factory
.set_user_prefs(json_pref_store_
);
211 scoped_refptr
<PrefRegistrySimple
> registry(new PrefRegistrySimple());
212 registry
->RegisterDictionaryPref(kHttpServerProperties
,
213 new base::DictionaryValue());
214 pref_service_
= factory
.Create(registry
.get()).Pass();
216 scoped_ptr
<net::HttpServerPropertiesManager
> http_server_properties_manager(
217 new net::HttpServerPropertiesManager(pref_service_
.get(),
218 kHttpServerProperties
,
219 GetNetworkTaskRunner()));
220 http_server_properties_manager
->InitializeOnNetworkThread();
221 http_server_properties_manager_
= http_server_properties_manager
.get();
222 context_builder
.SetHttpServerProperties(
223 http_server_properties_manager
.Pass());
226 context_
.reset(context_builder
.Build());
228 default_load_flags_
= net::LOAD_DO_NOT_SAVE_COOKIES
|
229 net::LOAD_DO_NOT_SEND_COOKIES
;
230 if (config
->load_disable_cache
)
231 default_load_flags_
|= net::LOAD_DISABLE_CACHE
;
233 if (config
->enable_sdch
) {
234 DCHECK(context_
->sdch_manager());
236 new net::SdchOwner(context_
->sdch_manager(), context_
.get()));
237 if (json_pref_store_
)
238 sdch_owner_
->EnablePersistentStorage(json_pref_store_
.get());
241 // Currently (circa M39) enabling QUIC requires setting probability threshold.
242 if (config
->enable_quic
) {
243 context_
->http_server_properties()
244 ->SetAlternativeServiceProbabilityThreshold(0.0f
);
245 for (auto hint
= config
->quic_hints
.begin();
246 hint
!= config
->quic_hints
.end(); ++hint
) {
247 const URLRequestContextConfig::QuicHint
& quic_hint
= **hint
;
248 if (quic_hint
.host
.empty()) {
249 LOG(ERROR
) << "Empty QUIC hint host: " << quic_hint
.host
;
253 url::CanonHostInfo host_info
;
254 std::string
canon_host(net::CanonicalizeHost(quic_hint
.host
, &host_info
));
255 if (!host_info
.IsIPAddress() &&
256 !net::IsCanonicalizedHostCompliant(canon_host
)) {
257 LOG(ERROR
) << "Invalid QUIC hint host: " << quic_hint
.host
;
261 if (quic_hint
.port
<= std::numeric_limits
<uint16
>::min() ||
262 quic_hint
.port
> std::numeric_limits
<uint16
>::max()) {
263 LOG(ERROR
) << "Invalid QUIC hint port: "
268 if (quic_hint
.alternate_port
<= std::numeric_limits
<uint16
>::min() ||
269 quic_hint
.alternate_port
> std::numeric_limits
<uint16
>::max()) {
270 LOG(ERROR
) << "Invalid QUIC hint alternate port: "
271 << quic_hint
.alternate_port
;
275 net::HostPortPair
quic_hint_host_port_pair(canon_host
,
277 net::AlternativeService
alternative_service(
278 net::AlternateProtocol::QUIC
, "",
279 static_cast<uint16
>(quic_hint
.alternate_port
));
280 context_
->http_server_properties()->SetAlternativeService(
281 quic_hint_host_port_pair
, alternative_service
, 1.0f
,
286 JNIEnv
* env
= base::android::AttachCurrentThread();
287 Java_CronetUrlRequestContext_initNetworkThread(
288 env
, jcronet_url_request_context
.obj());
290 #if defined(DATA_REDUCTION_PROXY_SUPPORT)
291 if (data_reduction_proxy_
)
292 data_reduction_proxy_
->Init(true, GetURLRequestContext());
294 is_context_initialized_
= true;
295 while (!tasks_waiting_for_context_
.empty()) {
296 tasks_waiting_for_context_
.front().Run();
297 tasks_waiting_for_context_
.pop();
301 void CronetURLRequestContextAdapter::Destroy(JNIEnv
* env
, jobject jcaller
) {
302 DCHECK(!GetNetworkTaskRunner()->BelongsToCurrentThread());
303 // Stick network_thread_ in a local, as |this| may be destroyed from the
304 // network thread before delete network_thread is called.
305 base::Thread
* network_thread
= network_thread_
;
306 GetNetworkTaskRunner()->DeleteSoon(FROM_HERE
, this);
307 // Deleting thread stops it after all tasks are completed.
308 delete network_thread
;
311 net::URLRequestContext
* CronetURLRequestContextAdapter::GetURLRequestContext() {
313 LOG(ERROR
) << "URLRequestContext is not set up";
315 return context_
.get();
318 void CronetURLRequestContextAdapter::PostTaskToNetworkThread(
319 const tracked_objects::Location
& posted_from
,
320 const base::Closure
& callback
) {
321 GetNetworkTaskRunner()->PostTask(
322 posted_from
, base::Bind(&CronetURLRequestContextAdapter::
323 RunTaskAfterContextInitOnNetworkThread
,
324 base::Unretained(this), callback
));
327 void CronetURLRequestContextAdapter::RunTaskAfterContextInitOnNetworkThread(
328 const base::Closure
& task_to_run_after_context_init
) {
329 DCHECK(GetNetworkTaskRunner()->BelongsToCurrentThread());
330 if (is_context_initialized_
) {
331 DCHECK(tasks_waiting_for_context_
.empty());
332 task_to_run_after_context_init
.Run();
335 tasks_waiting_for_context_
.push(task_to_run_after_context_init
);
338 bool CronetURLRequestContextAdapter::IsOnNetworkThread() const {
339 return GetNetworkTaskRunner()->BelongsToCurrentThread();
342 scoped_refptr
<base::SingleThreadTaskRunner
>
343 CronetURLRequestContextAdapter::GetNetworkTaskRunner() const {
344 return network_thread_
->task_runner();
347 void CronetURLRequestContextAdapter::StartNetLogToFile(JNIEnv
* env
,
351 PostTaskToNetworkThread(
354 &CronetURLRequestContextAdapter::StartNetLogToFileOnNetworkThread
,
355 base::Unretained(this),
356 base::android::ConvertJavaStringToUTF8(env
, jfile_name
), jlog_all
));
359 void CronetURLRequestContextAdapter::StopNetLog(JNIEnv
* env
, jobject jcaller
) {
360 PostTaskToNetworkThread(
362 base::Bind(&CronetURLRequestContextAdapter::StopNetLogOnNetworkThread
,
363 base::Unretained(this)));
366 void CronetURLRequestContextAdapter::StartNetLogToFileOnNetworkThread(
367 const std::string
& file_name
, bool log_all
) {
368 DCHECK(GetNetworkTaskRunner()->BelongsToCurrentThread());
369 DCHECK(is_context_initialized_
);
371 // Do nothing if already logging to a file.
372 if (write_to_file_observer_
)
374 base::FilePath
file_path(file_name
);
375 base::ScopedFILE
file(base::OpenFile(file_path
, "w"));
379 write_to_file_observer_
.reset(new net::WriteToFileNetLogObserver());
381 write_to_file_observer_
->set_capture_mode(
382 net::NetLogCaptureMode::IncludeSocketBytes());
384 write_to_file_observer_
->StartObserving(context_
->net_log(), file
.Pass(),
385 nullptr, context_
.get());
388 void CronetURLRequestContextAdapter::StopNetLogOnNetworkThread() {
389 DCHECK(GetNetworkTaskRunner()->BelongsToCurrentThread());
390 if (write_to_file_observer_
) {
391 write_to_file_observer_
->StopObserving(context_
.get());
392 write_to_file_observer_
.reset();
396 base::Thread
* CronetURLRequestContextAdapter::GetFileThread() {
397 DCHECK(GetNetworkTaskRunner()->BelongsToCurrentThread());
399 file_thread_
.reset(new base::Thread("Network File Thread"));
400 file_thread_
->Start();
402 return file_thread_
.get();
405 // Creates RequestContextAdater if config is valid URLRequestContextConfig,
406 // returns 0 otherwise.
407 static jlong
CreateRequestContextAdapter(JNIEnv
* env
,
410 std::string config_string
=
411 base::android::ConvertJavaStringToUTF8(env
, jconfig
);
412 scoped_ptr
<URLRequestContextConfig
> context_config(
413 new URLRequestContextConfig());
414 if (!context_config
->LoadFromJSON(config_string
))
417 CronetURLRequestContextAdapter
* context_adapter
=
418 new CronetURLRequestContextAdapter(context_config
.Pass());
419 return reinterpret_cast<jlong
>(context_adapter
);
422 static jint
SetMinLogLevel(JNIEnv
* env
, jclass jcaller
, jint jlog_level
) {
423 jint old_log_level
= static_cast<jint
>(logging::GetMinLogLevel());
424 // MinLogLevel is global, shared by all URLRequestContexts.
425 logging::SetMinLogLevel(static_cast<int>(jlog_level
));
426 return old_log_level
;
429 } // namespace cronet