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 OnCompleted(net::URLRequest
* request
, bool started
) override
{}
82 void OnURLRequestDestroyed(net::URLRequest
* request
) override
{}
84 void OnPACScriptError(int line_number
, const base::string16
& error
) override
{
87 NetworkDelegate::AuthRequiredResponse
OnAuthRequired(
88 net::URLRequest
* request
,
89 const net::AuthChallengeInfo
& auth_info
,
90 const AuthCallback
& callback
,
91 net::AuthCredentials
* credentials
) override
{
92 return net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_NO_ACTION
;
95 bool OnCanGetCookies(const net::URLRequest
& request
,
96 const net::CookieList
& cookie_list
) override
{
100 bool OnCanSetCookie(const net::URLRequest
& request
,
101 const std::string
& cookie_line
,
102 net::CookieOptions
* options
) override
{
106 bool OnCanAccessFile(const net::URLRequest
& request
,
107 const base::FilePath
& path
) const override
{
111 DISALLOW_COPY_AND_ASSIGN(BasicNetworkDelegate
);
118 // Explicitly register static JNI functions.
119 bool CronetUrlRequestContextAdapterRegisterJni(JNIEnv
* env
) {
120 return RegisterNativesImpl(env
);
123 CronetURLRequestContextAdapter::CronetURLRequestContextAdapter(
124 scoped_ptr
<URLRequestContextConfig
> context_config
)
125 : network_thread_(new base::Thread("network")),
126 http_server_properties_manager_(nullptr),
127 context_config_(context_config
.Pass()),
128 is_context_initialized_(false),
129 default_load_flags_(net::LOAD_NORMAL
) {
130 base::Thread::Options options
;
131 options
.message_loop_type
= base::MessageLoop::TYPE_IO
;
132 network_thread_
->StartWithOptions(options
);
135 CronetURLRequestContextAdapter::~CronetURLRequestContextAdapter() {
136 DCHECK(GetNetworkTaskRunner()->BelongsToCurrentThread());
138 if (http_server_properties_manager_
)
139 http_server_properties_manager_
->ShutdownOnPrefThread();
141 pref_service_
->CommitPendingWrite();
142 StopNetLogOnNetworkThread();
145 void CronetURLRequestContextAdapter::InitRequestContextOnMainThread(
148 base::android::ScopedJavaGlobalRef
<jobject
> jcaller_ref
;
149 jcaller_ref
.Reset(env
, jcaller
);
150 proxy_config_service_
.reset(net::ProxyService::CreateSystemProxyConfigService(
151 GetNetworkTaskRunner(), nullptr));
152 GetNetworkTaskRunner()->PostTask(
154 base::Bind(&CronetURLRequestContextAdapter::InitializeOnNetworkThread
,
155 base::Unretained(this), Passed(&context_config_
),
159 void CronetURLRequestContextAdapter::InitializeOnNetworkThread(
160 scoped_ptr
<URLRequestContextConfig
> config
,
161 const base::android::ScopedJavaGlobalRef
<jobject
>&
162 jcronet_url_request_context
) {
163 DCHECK(GetNetworkTaskRunner()->BelongsToCurrentThread());
164 DCHECK(!is_context_initialized_
);
165 DCHECK(proxy_config_service_
);
166 // TODO(mmenke): Add method to have the builder enable SPDY.
167 net::URLRequestContextBuilder context_builder
;
169 scoped_ptr
<net::NetLog
> net_log(new net::NetLog
);
170 scoped_ptr
<net::NetworkDelegate
> network_delegate(new BasicNetworkDelegate());
171 #if defined(DATA_REDUCTION_PROXY_SUPPORT)
172 DCHECK(!data_reduction_proxy_
);
173 // For now, the choice to enable the data reduction proxy happens once,
174 // at initialization. It cannot be disabled thereafter.
175 if (!config
->data_reduction_proxy_key
.empty()) {
176 data_reduction_proxy_
.reset(
177 new CronetDataReductionProxy(
178 config
->data_reduction_proxy_key
,
179 config
->data_reduction_primary_proxy
,
180 config
->data_reduction_fallback_proxy
,
181 config
->data_reduction_secure_proxy_check_url
,
183 GetNetworkTaskRunner(),
186 data_reduction_proxy_
->CreateNetworkDelegate(network_delegate
.Pass());
187 ScopedVector
<net::URLRequestInterceptor
> interceptors
;
188 interceptors
.push_back(data_reduction_proxy_
->CreateInterceptor());
189 context_builder
.SetInterceptors(interceptors
.Pass());
191 #endif // defined(DATA_REDUCTION_PROXY_SUPPORT)
192 context_builder
.set_network_delegate(network_delegate
.release());
193 context_builder
.set_net_log(net_log
.release());
194 context_builder
.set_proxy_config_service(proxy_config_service_
.release());
195 config
->ConfigureURLRequestContextBuilder(&context_builder
);
197 // Set up pref file if storage path is specified.
198 if (!config
->storage_path
.empty()) {
199 base::FilePath
filepath(config
->storage_path
);
200 filepath
= filepath
.Append(FILE_PATH_LITERAL("local_prefs.json"));
201 json_pref_store_
= new JsonPrefStore(
202 filepath
, GetFileThread()->task_runner(), scoped_ptr
<PrefFilter
>());
203 context_builder
.SetFileTaskRunner(GetFileThread()->task_runner());
205 // Set up HttpServerPropertiesManager.
206 base::PrefServiceFactory factory
;
207 factory
.set_user_prefs(json_pref_store_
);
208 scoped_refptr
<PrefRegistrySimple
> registry(new PrefRegistrySimple());
209 registry
->RegisterDictionaryPref(kHttpServerProperties
,
210 new base::DictionaryValue());
211 pref_service_
= factory
.Create(registry
.get()).Pass();
213 scoped_ptr
<net::HttpServerPropertiesManager
> http_server_properties_manager(
214 new net::HttpServerPropertiesManager(pref_service_
.get(),
215 kHttpServerProperties
,
216 GetNetworkTaskRunner()));
217 http_server_properties_manager
->InitializeOnNetworkThread();
218 http_server_properties_manager_
= http_server_properties_manager
.get();
219 context_builder
.SetHttpServerProperties(
220 http_server_properties_manager
.Pass());
223 context_
.reset(context_builder
.Build());
225 default_load_flags_
= net::LOAD_DO_NOT_SAVE_COOKIES
|
226 net::LOAD_DO_NOT_SEND_COOKIES
;
227 if (config
->load_disable_cache
)
228 default_load_flags_
|= net::LOAD_DISABLE_CACHE
;
230 if (config
->enable_sdch
) {
231 DCHECK(context_
->sdch_manager());
233 new net::SdchOwner(context_
->sdch_manager(), context_
.get()));
234 if (json_pref_store_
)
235 sdch_owner_
->EnablePersistentStorage(json_pref_store_
.get());
238 // Currently (circa M39) enabling QUIC requires setting probability threshold.
239 if (config
->enable_quic
) {
240 context_
->http_server_properties()
241 ->SetAlternativeServiceProbabilityThreshold(0.0f
);
242 for (auto hint
= config
->quic_hints
.begin();
243 hint
!= config
->quic_hints
.end(); ++hint
) {
244 const URLRequestContextConfig::QuicHint
& quic_hint
= **hint
;
245 if (quic_hint
.host
.empty()) {
246 LOG(ERROR
) << "Empty QUIC hint host: " << quic_hint
.host
;
250 url::CanonHostInfo host_info
;
251 std::string
canon_host(net::CanonicalizeHost(quic_hint
.host
, &host_info
));
252 if (!host_info
.IsIPAddress() &&
253 !net::IsCanonicalizedHostCompliant(canon_host
)) {
254 LOG(ERROR
) << "Invalid QUIC hint host: " << quic_hint
.host
;
258 if (quic_hint
.port
<= std::numeric_limits
<uint16
>::min() ||
259 quic_hint
.port
> std::numeric_limits
<uint16
>::max()) {
260 LOG(ERROR
) << "Invalid QUIC hint port: "
265 if (quic_hint
.alternate_port
<= std::numeric_limits
<uint16
>::min() ||
266 quic_hint
.alternate_port
> std::numeric_limits
<uint16
>::max()) {
267 LOG(ERROR
) << "Invalid QUIC hint alternate port: "
268 << quic_hint
.alternate_port
;
272 net::HostPortPair
quic_hint_host_port_pair(canon_host
,
274 net::AlternativeService
alternative_service(
275 net::AlternateProtocol::QUIC
, "",
276 static_cast<uint16
>(quic_hint
.alternate_port
));
277 context_
->http_server_properties()->SetAlternativeService(
278 quic_hint_host_port_pair
, alternative_service
, 1.0f
,
283 JNIEnv
* env
= base::android::AttachCurrentThread();
284 Java_CronetUrlRequestContext_initNetworkThread(
285 env
, jcronet_url_request_context
.obj());
287 #if defined(DATA_REDUCTION_PROXY_SUPPORT)
288 if (data_reduction_proxy_
)
289 data_reduction_proxy_
->Init(true, GetURLRequestContext());
291 is_context_initialized_
= true;
292 while (!tasks_waiting_for_context_
.empty()) {
293 tasks_waiting_for_context_
.front().Run();
294 tasks_waiting_for_context_
.pop();
298 void CronetURLRequestContextAdapter::Destroy(JNIEnv
* env
, jobject jcaller
) {
299 DCHECK(!GetNetworkTaskRunner()->BelongsToCurrentThread());
300 // Stick network_thread_ in a local, as |this| may be destroyed from the
301 // network thread before delete network_thread is called.
302 base::Thread
* network_thread
= network_thread_
;
303 GetNetworkTaskRunner()->DeleteSoon(FROM_HERE
, this);
304 // Deleting thread stops it after all tasks are completed.
305 delete network_thread
;
308 net::URLRequestContext
* CronetURLRequestContextAdapter::GetURLRequestContext() {
310 LOG(ERROR
) << "URLRequestContext is not set up";
312 return context_
.get();
315 void CronetURLRequestContextAdapter::PostTaskToNetworkThread(
316 const tracked_objects::Location
& posted_from
,
317 const base::Closure
& callback
) {
318 GetNetworkTaskRunner()->PostTask(
319 posted_from
, base::Bind(&CronetURLRequestContextAdapter::
320 RunTaskAfterContextInitOnNetworkThread
,
321 base::Unretained(this), callback
));
324 void CronetURLRequestContextAdapter::RunTaskAfterContextInitOnNetworkThread(
325 const base::Closure
& task_to_run_after_context_init
) {
326 DCHECK(GetNetworkTaskRunner()->BelongsToCurrentThread());
327 if (is_context_initialized_
) {
328 DCHECK(tasks_waiting_for_context_
.empty());
329 task_to_run_after_context_init
.Run();
332 tasks_waiting_for_context_
.push(task_to_run_after_context_init
);
335 bool CronetURLRequestContextAdapter::IsOnNetworkThread() const {
336 return GetNetworkTaskRunner()->BelongsToCurrentThread();
339 scoped_refptr
<base::SingleThreadTaskRunner
>
340 CronetURLRequestContextAdapter::GetNetworkTaskRunner() const {
341 return network_thread_
->task_runner();
344 void CronetURLRequestContextAdapter::StartNetLogToFile(JNIEnv
* env
,
348 PostTaskToNetworkThread(
351 &CronetURLRequestContextAdapter::StartNetLogToFileOnNetworkThread
,
352 base::Unretained(this),
353 base::android::ConvertJavaStringToUTF8(env
, jfile_name
), jlog_all
));
356 void CronetURLRequestContextAdapter::StopNetLog(JNIEnv
* env
, jobject jcaller
) {
357 PostTaskToNetworkThread(
359 base::Bind(&CronetURLRequestContextAdapter::StopNetLogOnNetworkThread
,
360 base::Unretained(this)));
363 void CronetURLRequestContextAdapter::StartNetLogToFileOnNetworkThread(
364 const std::string
& file_name
, bool log_all
) {
365 DCHECK(GetNetworkTaskRunner()->BelongsToCurrentThread());
366 DCHECK(is_context_initialized_
);
368 // Do nothing if already logging to a file.
369 if (write_to_file_observer_
)
371 base::FilePath
file_path(file_name
);
372 base::ScopedFILE
file(base::OpenFile(file_path
, "w"));
376 write_to_file_observer_
.reset(new net::WriteToFileNetLogObserver());
378 write_to_file_observer_
->set_capture_mode(
379 net::NetLogCaptureMode::IncludeSocketBytes());
381 write_to_file_observer_
->StartObserving(context_
->net_log(), file
.Pass(),
382 nullptr, context_
.get());
385 void CronetURLRequestContextAdapter::StopNetLogOnNetworkThread() {
386 DCHECK(GetNetworkTaskRunner()->BelongsToCurrentThread());
387 if (write_to_file_observer_
) {
388 write_to_file_observer_
->StopObserving(context_
.get());
389 write_to_file_observer_
.reset();
393 base::Thread
* CronetURLRequestContextAdapter::GetFileThread() {
394 DCHECK(GetNetworkTaskRunner()->BelongsToCurrentThread());
396 file_thread_
.reset(new base::Thread("Network File Thread"));
397 file_thread_
->Start();
399 return file_thread_
.get();
402 // Creates RequestContextAdater if config is valid URLRequestContextConfig,
403 // returns 0 otherwise.
404 static jlong
CreateRequestContextAdapter(JNIEnv
* env
,
407 std::string config_string
=
408 base::android::ConvertJavaStringToUTF8(env
, jconfig
);
409 scoped_ptr
<URLRequestContextConfig
> context_config(
410 new URLRequestContextConfig());
411 if (!context_config
->LoadFromJSON(config_string
))
414 CronetURLRequestContextAdapter
* context_adapter
=
415 new CronetURLRequestContextAdapter(context_config
.Pass());
416 return reinterpret_cast<jlong
>(context_adapter
);
419 static jint
SetMinLogLevel(JNIEnv
* env
, jclass jcaller
, jint jlog_level
) {
420 jint old_log_level
= static_cast<jint
>(logging::GetMinLogLevel());
421 // MinLogLevel is global, shared by all URLRequestContexts.
422 logging::SetMinLogLevel(static_cast<int>(jlog_level
));
423 return old_log_level
;
426 } // namespace cronet