| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/cronet/android/cronet_url_request_context_adapter.h" |
| |
| #include <limits.h> |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <limits> |
| #include <map> |
| #include <set> |
| #include <utility> |
| #include <vector> |
| |
| #include "base/android/jni_android.h" |
| #include "base/android/jni_array.h" |
| #include "base/android/jni_string.h" |
| #include "base/base64.h" |
| #include "base/bind.h" |
| #include "base/callback.h" |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/files/scoped_file.h" |
| #include "base/lazy_instance.h" |
| #include "base/logging.h" |
| #include "base/macros.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/threading/thread_restrictions.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "base/time/time.h" |
| #include "base/values.h" |
| #include "components/cronet/android/buildflags.h" |
| #include "components/cronet/android/cronet_jni_headers/CronetUrlRequestContext_jni.h" |
| #include "components/cronet/android/cronet_library_loader.h" |
| #include "components/cronet/cronet_prefs_manager.h" |
| #include "components/cronet/histogram_manager.h" |
| #include "components/cronet/host_cache_persistence_manager.h" |
| #include "components/cronet/url_request_context_config.h" |
| #include "net/base/load_flags.h" |
| #include "net/base/logging_network_change_observer.h" |
| #include "net/base/net_errors.h" |
| #include "net/base/network_delegate_impl.h" |
| #include "net/base/url_util.h" |
| #include "net/cert/caching_cert_verifier.h" |
| #include "net/cert/cert_verifier.h" |
| #include "net/cookies/cookie_monster.h" |
| #include "net/http/http_auth_handler_factory.h" |
| #include "net/log/file_net_log_observer.h" |
| #include "net/log/net_log_util.h" |
| #include "net/nqe/network_quality_estimator_params.h" |
| #include "net/proxy_resolution/proxy_config_service_android.h" |
| #include "net/proxy_resolution/proxy_resolution_service.h" |
| #include "net/third_party/quiche/src/quic/core/quic_versions.h" |
| #include "net/url_request/url_request_context.h" |
| #include "net/url_request/url_request_context_builder.h" |
| #include "net/url_request/url_request_interceptor.h" |
| |
| #if BUILDFLAG(INTEGRATED_MODE) |
| #include "components/cronet/android/cronet_integrated_mode_state.h" |
| #endif |
| |
| using base::android::JavaParamRef; |
| using base::android::ScopedJavaLocalRef; |
| |
| namespace { |
| |
| // Helper method that takes a Java string that can be null, in which case it |
| // will get converted to an empty string. |
| std::string ConvertNullableJavaStringToUTF8(JNIEnv* env, |
| const JavaParamRef<jstring>& jstr) { |
| std::string str; |
| if (!jstr.is_null()) |
| base::android::ConvertJavaStringToUTF8(env, jstr, &str); |
| return str; |
| } |
| |
| } // namespace |
| |
| namespace cronet { |
| |
| CronetURLRequestContextAdapter::CronetURLRequestContextAdapter( |
| std::unique_ptr<URLRequestContextConfig> context_config) { |
| // Create context and pass ownership of |this| (self) to the context. |
| std::unique_ptr<CronetURLRequestContextAdapter> self(this); |
| #if BUILDFLAG(INTEGRATED_MODE) |
| // Create CronetURLRequestContext running in integrated network task runner. |
| context_ = |
| new CronetURLRequestContext(std::move(context_config), std::move(self), |
| GetIntegratedModeNetworkTaskRunner()); |
| #else |
| context_ = |
| new CronetURLRequestContext(std::move(context_config), std::move(self)); |
| #endif |
| } |
| |
| CronetURLRequestContextAdapter::~CronetURLRequestContextAdapter() = default; |
| |
| void CronetURLRequestContextAdapter::InitRequestContextOnInitThread( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& jcaller) { |
| jcronet_url_request_context_.Reset(env, jcaller); |
| context_->InitRequestContextOnInitThread(); |
| } |
| |
| void CronetURLRequestContextAdapter::ConfigureNetworkQualityEstimatorForTesting( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& jcaller, |
| jboolean use_local_host_requests, |
| jboolean use_smaller_responses, |
| jboolean disable_offline_check) { |
| context_->ConfigureNetworkQualityEstimatorForTesting( |
| use_local_host_requests == JNI_TRUE, use_smaller_responses == JNI_TRUE, |
| disable_offline_check == JNI_TRUE); |
| } |
| |
| void CronetURLRequestContextAdapter::ProvideRTTObservations( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& jcaller, |
| bool should) { |
| context_->ProvideRTTObservations(should == JNI_TRUE); |
| } |
| |
| void CronetURLRequestContextAdapter::ProvideThroughputObservations( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& jcaller, |
| bool should) { |
| context_->ProvideThroughputObservations(should == JNI_TRUE); |
| } |
| |
| void CronetURLRequestContextAdapter::OnInitNetworkThread() { |
| JNIEnv* env = base::android::AttachCurrentThread(); |
| Java_CronetUrlRequestContext_initNetworkThread(env, |
| jcronet_url_request_context_); |
| } |
| |
| void CronetURLRequestContextAdapter::OnDestroyNetworkThread() { |
| // The |context_| is destroyed. |
| context_ = nullptr; |
| } |
| |
| void CronetURLRequestContextAdapter::OnEffectiveConnectionTypeChanged( |
| net::EffectiveConnectionType effective_connection_type) { |
| Java_CronetUrlRequestContext_onEffectiveConnectionTypeChanged( |
| base::android::AttachCurrentThread(), jcronet_url_request_context_, |
| effective_connection_type); |
| } |
| |
| void CronetURLRequestContextAdapter::OnRTTOrThroughputEstimatesComputed( |
| int32_t http_rtt_ms, |
| int32_t transport_rtt_ms, |
| int32_t downstream_throughput_kbps) { |
| Java_CronetUrlRequestContext_onRTTOrThroughputEstimatesComputed( |
| base::android::AttachCurrentThread(), jcronet_url_request_context_, |
| http_rtt_ms, transport_rtt_ms, downstream_throughput_kbps); |
| } |
| |
| void CronetURLRequestContextAdapter::OnRTTObservation( |
| int32_t rtt_ms, |
| int32_t timestamp_ms, |
| net::NetworkQualityObservationSource source) { |
| Java_CronetUrlRequestContext_onRttObservation( |
| base::android::AttachCurrentThread(), jcronet_url_request_context_, |
| rtt_ms, timestamp_ms, source); |
| } |
| |
| void CronetURLRequestContextAdapter::OnThroughputObservation( |
| int32_t throughput_kbps, |
| int32_t timestamp_ms, |
| net::NetworkQualityObservationSource source) { |
| Java_CronetUrlRequestContext_onThroughputObservation( |
| base::android::AttachCurrentThread(), jcronet_url_request_context_, |
| throughput_kbps, timestamp_ms, source); |
| } |
| |
| void CronetURLRequestContextAdapter::OnStopNetLogCompleted() { |
| Java_CronetUrlRequestContext_stopNetLogCompleted( |
| base::android::AttachCurrentThread(), jcronet_url_request_context_); |
| } |
| |
| void CronetURLRequestContextAdapter::Destroy( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& jcaller) { |
| // Deleting |context_| on client thread will post cleanup onto network thread, |
| // which will in turn delete |this| on network thread. |
| delete context_; |
| } |
| |
| net::URLRequestContext* CronetURLRequestContextAdapter::GetURLRequestContext() { |
| return context_->GetURLRequestContext(); |
| } |
| |
| void CronetURLRequestContextAdapter::PostTaskToNetworkThread( |
| const base::Location& posted_from, |
| base::OnceClosure callback) { |
| context_->PostTaskToNetworkThread(posted_from, std::move(callback)); |
| } |
| |
| bool CronetURLRequestContextAdapter::IsOnNetworkThread() const { |
| return context_->IsOnNetworkThread(); |
| } |
| |
| bool CronetURLRequestContextAdapter::StartNetLogToFile( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& jcaller, |
| const JavaParamRef<jstring>& jfile_name, |
| jboolean jlog_all) { |
| std::string file_name( |
| base::android::ConvertJavaStringToUTF8(env, jfile_name)); |
| return context_->StartNetLogToFile(file_name, jlog_all == JNI_TRUE); |
| } |
| |
| void CronetURLRequestContextAdapter::StartNetLogToDisk( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& jcaller, |
| const JavaParamRef<jstring>& jdir_name, |
| jboolean jlog_all, |
| jint jmax_size) { |
| std::string dir_name(base::android::ConvertJavaStringToUTF8(env, jdir_name)); |
| context_->StartNetLogToDisk(dir_name, jlog_all == JNI_TRUE, jmax_size); |
| } |
| |
| void CronetURLRequestContextAdapter::StopNetLog( |
| JNIEnv* env, |
| const JavaParamRef<jobject>& jcaller) { |
| context_->StopNetLog(); |
| } |
| |
| int CronetURLRequestContextAdapter::default_load_flags() const { |
| return context_->default_load_flags(); |
| } |
| |
| // Create a URLRequestContextConfig from the given parameters. |
| static jlong JNI_CronetUrlRequestContext_CreateRequestContextConfig( |
| JNIEnv* env, |
| const JavaParamRef<jstring>& juser_agent, |
| const JavaParamRef<jstring>& jstorage_path, |
| jboolean jquic_enabled, |
| const JavaParamRef<jstring>& jquic_default_user_agent_id, |
| jboolean jhttp2_enabled, |
| jboolean jbrotli_enabled, |
| jboolean jdisable_cache, |
| jint jhttp_cache_mode, |
| jlong jhttp_cache_max_size, |
| const JavaParamRef<jstring>& jexperimental_quic_connection_options, |
| jlong jmock_cert_verifier, |
| jboolean jenable_network_quality_estimator, |
| jboolean jbypass_public_key_pinning_for_local_trust_anchors, |
| jint jnetwork_thread_priority) { |
| return reinterpret_cast<jlong>(new URLRequestContextConfig( |
| jquic_enabled, |
| ConvertNullableJavaStringToUTF8(env, jquic_default_user_agent_id), |
| jhttp2_enabled, jbrotli_enabled, |
| static_cast<URLRequestContextConfig::HttpCacheType>(jhttp_cache_mode), |
| jhttp_cache_max_size, jdisable_cache, |
| ConvertNullableJavaStringToUTF8(env, jstorage_path), |
| /* accept_languages */ std::string(), |
| ConvertNullableJavaStringToUTF8(env, juser_agent), |
| ConvertNullableJavaStringToUTF8(env, |
| jexperimental_quic_connection_options), |
| base::WrapUnique( |
| reinterpret_cast<net::CertVerifier*>(jmock_cert_verifier)), |
| jenable_network_quality_estimator, |
| jbypass_public_key_pinning_for_local_trust_anchors, |
| jnetwork_thread_priority >= -20 && jnetwork_thread_priority <= 19 |
| ? base::Optional<double>(jnetwork_thread_priority) |
| : base::Optional<double>())); |
| } |
| |
| // Add a QUIC hint to a URLRequestContextConfig. |
| static void JNI_CronetUrlRequestContext_AddQuicHint( |
| JNIEnv* env, |
| jlong jurl_request_context_config, |
| const JavaParamRef<jstring>& jhost, |
| jint jport, |
| jint jalternate_port) { |
| URLRequestContextConfig* config = |
| reinterpret_cast<URLRequestContextConfig*>(jurl_request_context_config); |
| config->quic_hints.push_back( |
| std::make_unique<URLRequestContextConfig::QuicHint>( |
| base::android::ConvertJavaStringToUTF8(env, jhost), jport, |
| jalternate_port)); |
| } |
| |
| // Add a public key pin to URLRequestContextConfig. |
| // |jhost| is the host to apply the pin to. |
| // |jhashes| is an array of jbyte[32] representing SHA256 key hashes. |
| // |jinclude_subdomains| indicates if pin should be applied to subdomains. |
| // |jexpiration_time| is the time that the pin expires, in milliseconds since |
| // Jan. 1, 1970, midnight GMT. |
| static void JNI_CronetUrlRequestContext_AddPkp( |
| JNIEnv* env, |
| jlong jurl_request_context_config, |
| const JavaParamRef<jstring>& jhost, |
| const JavaParamRef<jobjectArray>& jhashes, |
| jboolean jinclude_subdomains, |
| jlong jexpiration_time) { |
| URLRequestContextConfig* config = |
| reinterpret_cast<URLRequestContextConfig*>(jurl_request_context_config); |
| std::unique_ptr<URLRequestContextConfig::Pkp> pkp( |
| new URLRequestContextConfig::Pkp( |
| base::android::ConvertJavaStringToUTF8(env, jhost), |
| jinclude_subdomains, |
| base::Time::UnixEpoch() + |
| base::TimeDelta::FromMilliseconds(jexpiration_time))); |
| for (auto bytes_array : jhashes.ReadElements<jbyteArray>()) { |
| static_assert(std::is_pod<net::SHA256HashValue>::value, |
| "net::SHA256HashValue is not POD"); |
| static_assert(sizeof(net::SHA256HashValue) * CHAR_BIT == 256, |
| "net::SHA256HashValue contains overhead"); |
| if (env->GetArrayLength(bytes_array.obj()) != |
| sizeof(net::SHA256HashValue)) { |
| LOG(ERROR) << "Unable to add public key hash value."; |
| continue; |
| } |
| jbyte* bytes = env->GetByteArrayElements(bytes_array.obj(), nullptr); |
| net::HashValue hash(*reinterpret_cast<net::SHA256HashValue*>(bytes)); |
| pkp->pin_hashes.push_back(hash); |
| env->ReleaseByteArrayElements(bytes_array.obj(), bytes, JNI_ABORT); |
| } |
| config->pkp_list.push_back(std::move(pkp)); |
| } |
| |
| // Creates RequestContextAdater if config is valid URLRequestContextConfig, |
| // returns 0 otherwise. |
| static jlong JNI_CronetUrlRequestContext_CreateRequestContextAdapter( |
| JNIEnv* env, |
| jlong jconfig) { |
| std::unique_ptr<URLRequestContextConfig> context_config( |
| reinterpret_cast<URLRequestContextConfig*>(jconfig)); |
| |
| CronetURLRequestContextAdapter* context_adapter = |
| new CronetURLRequestContextAdapter(std::move(context_config)); |
| return reinterpret_cast<jlong>(context_adapter); |
| } |
| |
| static jint JNI_CronetUrlRequestContext_SetMinLogLevel( |
| JNIEnv* env, |
| jint jlog_level) { |
| jint old_log_level = static_cast<jint>(logging::GetMinLogLevel()); |
| // MinLogLevel is global, shared by all URLRequestContexts. |
| logging::SetMinLogLevel(static_cast<int>(jlog_level)); |
| return old_log_level; |
| } |
| |
| static ScopedJavaLocalRef<jbyteArray> |
| JNI_CronetUrlRequestContext_GetHistogramDeltas(JNIEnv* env) { |
| std::vector<uint8_t> data; |
| if (!HistogramManager::GetInstance()->GetDeltas(&data)) |
| return ScopedJavaLocalRef<jbyteArray>(); |
| return base::android::ToJavaByteArray(env, &data[0], data.size()); |
| } |
| |
| } // namespace cronet |