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/prefs/pref_filter.h"
15 #include "base/single_thread_task_runner.h"
16 #include "base/values.h"
17 #include "components/cronet/url_request_context_config.h"
18 #include "jni/CronetUrlRequestContext_jni.h"
19 #include "net/base/load_flags.h"
20 #include "net/base/net_errors.h"
21 #include "net/base/network_delegate_impl.h"
22 #include "net/http/http_auth_handler_factory.h"
23 #include "net/log/write_to_file_net_log_observer.h"
24 #include "net/proxy/proxy_service.h"
25 #include "net/sdch/sdch_owner.h"
26 #include "net/url_request/url_request_context.h"
27 #include "net/url_request/url_request_context_builder.h"
28 #include "net/url_request/url_request_interceptor.h"
30 #if defined(DATA_REDUCTION_PROXY_SUPPORT)
31 #include "components/cronet/android/cronet_data_reduction_proxy.h"
36 class BasicNetworkDelegate
: public net::NetworkDelegateImpl
{
38 BasicNetworkDelegate() {}
39 ~BasicNetworkDelegate() override
{}
42 // net::NetworkDelegate implementation.
43 int OnBeforeURLRequest(net::URLRequest
* request
,
44 const net::CompletionCallback
& callback
,
45 GURL
* new_url
) override
{
49 int OnBeforeSendHeaders(net::URLRequest
* request
,
50 const net::CompletionCallback
& callback
,
51 net::HttpRequestHeaders
* headers
) override
{
55 void OnSendHeaders(net::URLRequest
* request
,
56 const net::HttpRequestHeaders
& headers
) override
{}
58 int OnHeadersReceived(
59 net::URLRequest
* request
,
60 const net::CompletionCallback
& callback
,
61 const net::HttpResponseHeaders
* original_response_headers
,
62 scoped_refptr
<net::HttpResponseHeaders
>* _response_headers
,
63 GURL
* allowed_unsafe_redirect_url
) override
{
67 void OnBeforeRedirect(net::URLRequest
* request
,
68 const GURL
& new_location
) override
{}
70 void OnResponseStarted(net::URLRequest
* request
) override
{}
72 void OnRawBytesRead(const net::URLRequest
& request
, int bytes_read
) override
{
75 void OnCompleted(net::URLRequest
* request
, bool started
) override
{}
77 void OnURLRequestDestroyed(net::URLRequest
* request
) override
{}
79 void OnPACScriptError(int line_number
, const base::string16
& error
) override
{
82 NetworkDelegate::AuthRequiredResponse
OnAuthRequired(
83 net::URLRequest
* request
,
84 const net::AuthChallengeInfo
& auth_info
,
85 const AuthCallback
& callback
,
86 net::AuthCredentials
* credentials
) override
{
87 return net::NetworkDelegate::AUTH_REQUIRED_RESPONSE_NO_ACTION
;
90 bool OnCanGetCookies(const net::URLRequest
& request
,
91 const net::CookieList
& cookie_list
) override
{
95 bool OnCanSetCookie(const net::URLRequest
& request
,
96 const std::string
& cookie_line
,
97 net::CookieOptions
* options
) override
{
101 bool OnCanAccessFile(const net::URLRequest
& request
,
102 const base::FilePath
& path
) const override
{
106 bool OnCanThrottleRequest(const net::URLRequest
& request
) const override
{
110 DISALLOW_COPY_AND_ASSIGN(BasicNetworkDelegate
);
117 // Explicitly register static JNI functions.
118 bool CronetUrlRequestContextAdapterRegisterJni(JNIEnv
* env
) {
119 return RegisterNativesImpl(env
);
122 CronetURLRequestContextAdapter::CronetURLRequestContextAdapter(
123 scoped_ptr
<URLRequestContextConfig
> context_config
)
124 : network_thread_(new base::Thread("network")),
125 context_config_(context_config
.Pass()),
126 is_context_initialized_(false),
127 default_load_flags_(net::LOAD_NORMAL
) {
128 base::Thread::Options options
;
129 options
.message_loop_type
= base::MessageLoop::TYPE_IO
;
130 network_thread_
->StartWithOptions(options
);
133 CronetURLRequestContextAdapter::~CronetURLRequestContextAdapter() {
134 DCHECK(GetNetworkTaskRunner()->BelongsToCurrentThread());
135 StopNetLogOnNetworkThread();
138 void CronetURLRequestContextAdapter::InitRequestContextOnMainThread(
141 base::android::ScopedJavaGlobalRef
<jobject
> jcaller_ref
;
142 jcaller_ref
.Reset(env
, jcaller
);
143 proxy_config_service_
.reset(net::ProxyService::CreateSystemProxyConfigService(
144 GetNetworkTaskRunner(), nullptr));
145 GetNetworkTaskRunner()->PostTask(
147 base::Bind(&CronetURLRequestContextAdapter::InitializeOnNetworkThread
,
148 base::Unretained(this), Passed(&context_config_
),
152 void CronetURLRequestContextAdapter::InitializeOnNetworkThread(
153 scoped_ptr
<URLRequestContextConfig
> config
,
154 const base::android::ScopedJavaGlobalRef
<jobject
>&
155 jcronet_url_request_context
) {
156 DCHECK(GetNetworkTaskRunner()->BelongsToCurrentThread());
157 DCHECK(!is_context_initialized_
);
158 DCHECK(proxy_config_service_
);
159 // TODO(mmenke): Add method to have the builder enable SPDY.
160 net::URLRequestContextBuilder context_builder
;
162 scoped_ptr
<net::NetLog
> net_log(new net::NetLog
);
163 scoped_ptr
<net::NetworkDelegate
> network_delegate(new BasicNetworkDelegate());
164 #if defined(DATA_REDUCTION_PROXY_SUPPORT)
165 DCHECK(!data_reduction_proxy_
);
166 // For now, the choice to enable the data reduction proxy happens once,
167 // at initialization. It cannot be disabled thereafter.
168 if (!config
->data_reduction_proxy_key
.empty()) {
169 data_reduction_proxy_
.reset(
170 new CronetDataReductionProxy(
171 config
->data_reduction_proxy_key
,
172 config
->data_reduction_primary_proxy
,
173 config
->data_reduction_fallback_proxy
,
174 config
->data_reduction_secure_proxy_check_url
,
176 GetNetworkTaskRunner(),
179 data_reduction_proxy_
->CreateNetworkDelegate(network_delegate
.Pass());
180 ScopedVector
<net::URLRequestInterceptor
> interceptors
;
181 interceptors
.push_back(data_reduction_proxy_
->CreateInterceptor());
182 context_builder
.SetInterceptors(interceptors
.Pass());
184 #endif // defined(DATA_REDUCTION_PROXY_SUPPORT)
185 context_builder
.set_network_delegate(network_delegate
.release());
186 context_builder
.set_net_log(net_log
.release());
187 context_builder
.set_proxy_config_service(proxy_config_service_
.release());
188 config
->ConfigureURLRequestContextBuilder(&context_builder
);
190 // Set up pref file if storage path is specified.
191 // TODO(xunjieli): maybe get rid of the condition on sdch.
192 if (!config
->storage_path
.empty() && config
->enable_sdch
) {
193 base::FilePath
filepath(config
->storage_path
);
194 filepath
= filepath
.Append(FILE_PATH_LITERAL("local_prefs.json"));
195 json_pref_store_
= new JsonPrefStore(
196 filepath
, GetFileThread()->task_runner(), scoped_ptr
<PrefFilter
>());
197 json_pref_store_
->ReadPrefsAsync(nullptr);
198 context_builder
.SetFileTaskRunner(GetFileThread()->task_runner());
201 context_
.reset(context_builder
.Build());
203 default_load_flags_
= net::LOAD_DO_NOT_SAVE_COOKIES
|
204 net::LOAD_DO_NOT_SEND_COOKIES
;
205 if (config
->load_disable_cache
)
206 default_load_flags_
|= net::LOAD_DISABLE_CACHE
;
208 if (config
->enable_sdch
) {
209 DCHECK(context_
->sdch_manager());
211 new net::SdchOwner(context_
->sdch_manager(), context_
.get()));
212 if (json_pref_store_
)
213 sdch_owner_
->EnablePersistentStorage(json_pref_store_
.get());
216 // Currently (circa M39) enabling QUIC requires setting probability threshold.
217 if (config
->enable_quic
) {
218 context_
->http_server_properties()
219 ->SetAlternativeServiceProbabilityThreshold(0.0f
);
220 for (auto hint
= config
->quic_hints
.begin();
221 hint
!= config
->quic_hints
.end(); ++hint
) {
222 const URLRequestContextConfig::QuicHint
& quic_hint
= **hint
;
223 if (quic_hint
.host
.empty()) {
224 LOG(ERROR
) << "Empty QUIC hint host: " << quic_hint
.host
;
228 url::CanonHostInfo host_info
;
229 std::string
canon_host(net::CanonicalizeHost(quic_hint
.host
, &host_info
));
230 if (!host_info
.IsIPAddress() &&
231 !net::IsCanonicalizedHostCompliant(canon_host
)) {
232 LOG(ERROR
) << "Invalid QUIC hint host: " << quic_hint
.host
;
236 if (quic_hint
.port
<= std::numeric_limits
<uint16
>::min() ||
237 quic_hint
.port
> std::numeric_limits
<uint16
>::max()) {
238 LOG(ERROR
) << "Invalid QUIC hint port: "
243 if (quic_hint
.alternate_port
<= std::numeric_limits
<uint16
>::min() ||
244 quic_hint
.alternate_port
> std::numeric_limits
<uint16
>::max()) {
245 LOG(ERROR
) << "Invalid QUIC hint alternate port: "
246 << quic_hint
.alternate_port
;
250 net::HostPortPair
quic_hint_host_port_pair(canon_host
,
252 net::AlternativeService
alternative_service(
253 net::AlternateProtocol::QUIC
, "",
254 static_cast<uint16
>(quic_hint
.alternate_port
));
255 context_
->http_server_properties()->SetAlternativeService(
256 quic_hint_host_port_pair
, alternative_service
, 1.0f
);
260 JNIEnv
* env
= base::android::AttachCurrentThread();
261 Java_CronetUrlRequestContext_initNetworkThread(
262 env
, jcronet_url_request_context
.obj());
264 #if defined(DATA_REDUCTION_PROXY_SUPPORT)
265 if (data_reduction_proxy_
)
266 data_reduction_proxy_
->Init(true, GetURLRequestContext());
268 is_context_initialized_
= true;
269 while (!tasks_waiting_for_context_
.empty()) {
270 tasks_waiting_for_context_
.front().Run();
271 tasks_waiting_for_context_
.pop();
275 void CronetURLRequestContextAdapter::Destroy(JNIEnv
* env
, jobject jcaller
) {
276 DCHECK(!GetNetworkTaskRunner()->BelongsToCurrentThread());
277 // Stick network_thread_ in a local, as |this| may be destroyed from the
278 // network thread before delete network_thread is called.
279 base::Thread
* network_thread
= network_thread_
;
280 GetNetworkTaskRunner()->DeleteSoon(FROM_HERE
, this);
281 // Deleting thread stops it after all tasks are completed.
282 delete network_thread
;
285 net::URLRequestContext
* CronetURLRequestContextAdapter::GetURLRequestContext() {
287 LOG(ERROR
) << "URLRequestContext is not set up";
289 return context_
.get();
292 void CronetURLRequestContextAdapter::PostTaskToNetworkThread(
293 const tracked_objects::Location
& posted_from
,
294 const base::Closure
& callback
) {
295 GetNetworkTaskRunner()->PostTask(
296 posted_from
, base::Bind(&CronetURLRequestContextAdapter::
297 RunTaskAfterContextInitOnNetworkThread
,
298 base::Unretained(this), callback
));
301 void CronetURLRequestContextAdapter::RunTaskAfterContextInitOnNetworkThread(
302 const base::Closure
& task_to_run_after_context_init
) {
303 DCHECK(GetNetworkTaskRunner()->BelongsToCurrentThread());
304 if (is_context_initialized_
) {
305 DCHECK(tasks_waiting_for_context_
.empty());
306 task_to_run_after_context_init
.Run();
309 tasks_waiting_for_context_
.push(task_to_run_after_context_init
);
312 bool CronetURLRequestContextAdapter::IsOnNetworkThread() const {
313 return GetNetworkTaskRunner()->BelongsToCurrentThread();
316 scoped_refptr
<base::SingleThreadTaskRunner
>
317 CronetURLRequestContextAdapter::GetNetworkTaskRunner() const {
318 return network_thread_
->task_runner();
321 void CronetURLRequestContextAdapter::StartNetLogToFile(JNIEnv
* env
,
325 PostTaskToNetworkThread(
328 &CronetURLRequestContextAdapter::StartNetLogToFileOnNetworkThread
,
329 base::Unretained(this),
330 base::android::ConvertJavaStringToUTF8(env
, jfile_name
), jlog_all
));
333 void CronetURLRequestContextAdapter::StopNetLog(JNIEnv
* env
, jobject jcaller
) {
334 PostTaskToNetworkThread(
336 base::Bind(&CronetURLRequestContextAdapter::StopNetLogOnNetworkThread
,
337 base::Unretained(this)));
340 void CronetURLRequestContextAdapter::StartNetLogToFileOnNetworkThread(
341 const std::string
& file_name
, bool log_all
) {
342 DCHECK(GetNetworkTaskRunner()->BelongsToCurrentThread());
343 DCHECK(is_context_initialized_
);
345 // Do nothing if already logging to a file.
346 if (write_to_file_observer_
)
348 base::FilePath
file_path(file_name
);
349 base::ScopedFILE
file(base::OpenFile(file_path
, "w"));
353 write_to_file_observer_
.reset(new net::WriteToFileNetLogObserver());
355 write_to_file_observer_
->set_capture_mode(
356 net::NetLogCaptureMode::IncludeSocketBytes());
358 write_to_file_observer_
->StartObserving(context_
->net_log(), file
.Pass(),
359 nullptr, context_
.get());
362 void CronetURLRequestContextAdapter::StopNetLogOnNetworkThread() {
363 DCHECK(GetNetworkTaskRunner()->BelongsToCurrentThread());
364 if (write_to_file_observer_
) {
365 write_to_file_observer_
->StopObserving(context_
.get());
366 write_to_file_observer_
.reset();
370 base::Thread
* CronetURLRequestContextAdapter::GetFileThread() {
371 DCHECK(GetNetworkTaskRunner()->BelongsToCurrentThread());
373 file_thread_
.reset(new base::Thread("Network File Thread"));
374 file_thread_
->Start();
376 return file_thread_
.get();
379 // Creates RequestContextAdater if config is valid URLRequestContextConfig,
380 // returns 0 otherwise.
381 static jlong
CreateRequestContextAdapter(JNIEnv
* env
,
384 std::string config_string
=
385 base::android::ConvertJavaStringToUTF8(env
, jconfig
);
386 scoped_ptr
<URLRequestContextConfig
> context_config(
387 new URLRequestContextConfig());
388 if (!context_config
->LoadFromJSON(config_string
))
391 CronetURLRequestContextAdapter
* context_adapter
=
392 new CronetURLRequestContextAdapter(context_config
.Pass());
393 return reinterpret_cast<jlong
>(context_adapter
);
396 static jint
SetMinLogLevel(JNIEnv
* env
, jclass jcaller
, jint jlog_level
) {
397 jint old_log_level
= static_cast<jint
>(logging::GetMinLogLevel());
398 // MinLogLevel is global, shared by all URLRequestContexts.
399 logging::SetMinLogLevel(static_cast<int>(jlog_level
));
400 return old_log_level
;
403 } // namespace cronet