From 00c2df6cf42e1a78285ce625fb83b3887d4b2403 Mon Sep 17 00:00:00 2001 From: Steven McDonald Date: Wed, 4 Jun 2025 11:04:12 -0700 Subject: [PATCH 1/2] Cronet proxy support from GreatFire Envoy --- components/cronet/android/api.txt | 4 +- .../src/org/chromium/net/CronetEngine.java | 10 +++++ .../chromium/net/ICronetEngineBuilder.java | 2 + components/cronet/android/api_version.txt | 2 +- .../cronet/android/cronet_context_adapter.cc | 11 +++++ .../cronet/android/cronet_context_adapter.h | 8 ++++ .../cronet/android/cronet_library_loader.cc | 9 ++++ .../impl/AndroidHttpEngineBuilderWrapper.java | 7 +++ .../net/impl/CronetEngineBuilderImpl.java | 11 +++++ .../net/impl/CronetUrlRequestContext.java | 23 ++++++++-- .../proto/request_context_config.proto | 1 + .../CronetSampleApplication.java | 2 + ...xperimentalOptionsTranslationTestUtil.java | 5 +++ components/cronet/cronet_context.cc | 13 ++++++ components/cronet/cronet_context.h | 2 + components/cronet/cronet_global_state.h | 3 ++ .../cronet/cronet_global_state_stubs.cc | 6 +++ components/cronet/native/cronet.idl | 6 +++ components/cronet/native/engine.cc | 16 +++++-- .../cronet/native/generated/cronet.idl_c.h | 6 +++ .../generated/cronet.idl_impl_struct.cc | 12 ++++++ .../native/generated/cronet.idl_impl_struct.h | 1 + components/cronet/native/sample/main.cc | 1 + components/cronet/native/test/test_util.cc | 10 ++++- components/cronet/native/test/test_util.h | 1 + .../cronet/native/test/url_request_test.cc | 41 +++++++++++++++++- .../cronet/testing/test_server/test_server.cc | 14 ++++++ .../cronet/testing/test_server/test_server.h | 2 + .../cronet/url_request_context_config.cc | 7 ++- .../cronet/url_request_context_config.h | 9 ++++ net/proxy_resolution/proxy_config_service.cc | 43 +++++++++++++++++++ net/proxy_resolution/proxy_config_service.h | 6 +++ 32 files changed, 279 insertions(+), 15 deletions(-) diff --git a/components/cronet/android/api.txt b/components/cronet/android/api.txt index dd438268ceb942..c7c09db066010b 100644 --- a/components/cronet/android/api.txt +++ b/components/cronet/android/api.txt @@ -99,6 +99,7 @@ public class org.chromium.net.CronetEngine$Builder { public org.chromium.net.CronetEngine$Builder setDnsOptions(org.chromium.net.DnsOptions$Builder); public org.chromium.net.CronetEngine$Builder setDnsOptions(org.chromium.net.DnsOptions); public org.chromium.net.CronetEngine$Builder setLibraryLoader(org.chromium.net.CronetEngine$Builder$LibraryLoader); + public org.chromium.net.CronetEngine$Builder setProxyUrl(java.lang.String); public org.chromium.net.CronetEngine$Builder setQuicOptions(org.chromium.net.QuicOptions$Builder); public org.chromium.net.CronetEngine$Builder setQuicOptions(org.chromium.net.QuicOptions); public org.chromium.net.CronetEngine$Builder setStoragePath(java.lang.String); @@ -323,6 +324,7 @@ public abstract class org.chromium.net.ICronetEngineBuilder { public abstract org.chromium.net.ICronetEngineBuilder enableSdch(boolean); public abstract org.chromium.net.ICronetEngineBuilder setExperimentalOptions(java.lang.String); public abstract org.chromium.net.ICronetEngineBuilder setLibraryLoader(org.chromium.net.CronetEngine$Builder$LibraryLoader); + public abstract org.chromium.net.ICronetEngineBuilder setProxyUrl(java.lang.String); public abstract org.chromium.net.ICronetEngineBuilder setStoragePath(java.lang.String); public abstract org.chromium.net.ICronetEngineBuilder setUserAgent(java.lang.String); public org.chromium.net.ICronetEngineBuilder enableBrotli(boolean); @@ -640,4 +642,4 @@ public class org.chromium.net.apihelpers.UrlRequestCallbacks { public static org.chromium.net.apihelpers.UrlRequestCallbacks$CallbackAndResponseFuturePair forStringBody(org.chromium.net.apihelpers.RedirectHandler); public static org.chromium.net.apihelpers.UrlRequestCallbacks$CallbackAndResponseFuturePair forJsonBody(org.chromium.net.apihelpers.RedirectHandler); } -Stamp: ded30b9fbe22dff6cc175cd483aa1e4b +Stamp: 77c7e1b163bf63684a4a0fedc6505679 diff --git a/components/cronet/android/api/src/org/chromium/net/CronetEngine.java b/components/cronet/android/api/src/org/chromium/net/CronetEngine.java index 69794dee12b5c2..c7c9f946ecc256 100644 --- a/components/cronet/android/api/src/org/chromium/net/CronetEngine.java +++ b/components/cronet/android/api/src/org/chromium/net/CronetEngine.java @@ -136,6 +136,16 @@ public abstract static class LibraryLoader { /** Reference to the actual builder implementation. {@hide exclude from JavaDoc}. */ protected final ICronetEngineBuilder mBuilderDelegate; + /** + * Sets a proxy URL for the Engine. The proxy will be used for all requests + * + * @param proxyUrl URL of the proxy to use + */ + public Builder setProxyUrl(String proxyUrl) { + mBuilderDelegate.setProxyUrl(proxyUrl); + return this; + } + /** * Constructs a {@link Builder} object that facilitates creating a {@link CronetEngine}. The * default configuration enables HTTP/2 and QUIC, but disables the HTTP cache. diff --git a/components/cronet/android/api/src/org/chromium/net/ICronetEngineBuilder.java b/components/cronet/android/api/src/org/chromium/net/ICronetEngineBuilder.java index f340a87dc2ee15..427d0d0c224f6d 100644 --- a/components/cronet/android/api/src/org/chromium/net/ICronetEngineBuilder.java +++ b/components/cronet/android/api/src/org/chromium/net/ICronetEngineBuilder.java @@ -75,6 +75,8 @@ public abstract ICronetEngineBuilder setLibraryLoader( public abstract ICronetEngineBuilder setUserAgent(String userAgent); + public abstract ICronetEngineBuilder setProxyUrl(String proxy_url); + public abstract String getDefaultUserAgent(); public abstract ExperimentalCronetEngine build(); diff --git a/components/cronet/android/api_version.txt b/components/cronet/android/api_version.txt index 7facc89938bbc5..81b5c5d06cc0b8 100644 --- a/components/cronet/android/api_version.txt +++ b/components/cronet/android/api_version.txt @@ -1 +1 @@ -36 +37 diff --git a/components/cronet/android/cronet_context_adapter.cc b/components/cronet/android/cronet_context_adapter.cc index d0a82f4a345f7e..b092b818963ce7 100644 --- a/components/cronet/android/cronet_context_adapter.cc +++ b/components/cronet/android/cronet_context_adapter.cc @@ -80,6 +80,16 @@ void CronetContextAdapter::InitRequestContextOnInitThread( context_->InitRequestContextOnInitThread(); } +void CronetContextAdapter::InitRequestContextOnInitThreadWithUri( + JNIEnv* env, + const JavaParamRef& jcaller, + const JavaParamRef& juri) { + jcronet_url_request_context_.Reset(env, jcaller); + std::string uri( + base::android::ConvertJavaStringToUTF8(env, juri)); + context_->InitRequestContextOnInitThreadWithUri(uri); +} + void CronetContextAdapter::ConfigureNetworkQualityEstimatorForTesting( JNIEnv* env, const JavaParamRef& jcaller, @@ -243,6 +253,7 @@ static jlong JNI_CronetUrlRequestContext_CreateRequestContextConfig( configOptions.http_cache_max_size(), configOptions.disable_cache(), configOptions.storage_path(), /* accept_languages */ std::string(), configOptions.user_agent(), + configOptions.proxy_url(), configOptions.experimental_options(), base::WrapUnique(reinterpret_cast( configOptions.mock_cert_verifier())), diff --git a/components/cronet/android/cronet_context_adapter.h b/components/cronet/android/cronet_context_adapter.h index cfd99bcd5fb593..fa7269eeb977ae 100644 --- a/components/cronet/android/cronet_context_adapter.h +++ b/components/cronet/android/cronet_context_adapter.h @@ -50,6 +50,11 @@ class CronetContextAdapter : public CronetContext::Callback { JNIEnv* env, const base::android::JavaParamRef& jcaller); + void InitRequestContextOnInitThreadWithUri( + JNIEnv* env, + const base::android::JavaParamRef& jcaller, + const base::android::JavaParamRef& juri); + // Releases all resources for the request context and deletes the object. // Blocks until network thread is destroyed after running all pending tasks. void Destroy(JNIEnv* env, @@ -98,6 +103,9 @@ class CronetContextAdapter : public CronetContext::Callback { // Called on init Java thread to initialize URLRequestContext. void InitRequestContextOnInitThread(); + // Called on init Java thread to initialize URLRequestContext with a proxy. + void InitRequestContextOnInitThreadWithUri(const base::android::JavaParamRef& juri); + // Configures the network quality estimator to observe requests to localhost, // to use smaller responses when estimating throughput, and to disable the // device offline checks when computing the effective connection type or when diff --git a/components/cronet/android/cronet_library_loader.cc b/components/cronet/android/cronet_library_loader.cc index 0ee2b2a77712fb..7beab5692c0a82 100644 --- a/components/cronet/android/cronet_library_loader.cc +++ b/components/cronet/android/cronet_library_loader.cc @@ -322,6 +322,15 @@ std::unique_ptr CreateProxyConfigService( return service; } +// Creates a proxy config service with a fixed proxy URL. +std::unique_ptr CreateFixedProxyConfigService( + const scoped_refptr& io_task_runner, std::string_view uri) { + std::unique_ptr service = + net::ProxyConfigService::CreateFixedSystemProxyConfigService( + io_task_runner, uri); + return service; +} + // Creates a proxy resolution service appropriate for this platform. std::unique_ptr CreateProxyResolutionService( std::unique_ptr proxy_config_service, diff --git a/components/cronet/android/java/src/org/chromium/net/impl/AndroidHttpEngineBuilderWrapper.java b/components/cronet/android/java/src/org/chromium/net/impl/AndroidHttpEngineBuilderWrapper.java index a213d30854d163..d74fd2ff1b3c5d 100644 --- a/components/cronet/android/java/src/org/chromium/net/impl/AndroidHttpEngineBuilderWrapper.java +++ b/components/cronet/android/java/src/org/chromium/net/impl/AndroidHttpEngineBuilderWrapper.java @@ -47,6 +47,13 @@ public ICronetEngineBuilder setUserAgent(String userAgent) { return this; } + @Override + public ICronetEngineBuilder setProxyUrl(String proxyUrl) { + // HttpEngine.Builder doesn't have this method + // mBackend.setProxyUrl(proxyUrl); + return this; + } + @Override public ICronetEngineBuilder setStoragePath(String value) { mBackend.setStoragePath(value); diff --git a/components/cronet/android/java/src/org/chromium/net/impl/CronetEngineBuilderImpl.java b/components/cronet/android/java/src/org/chromium/net/impl/CronetEngineBuilderImpl.java index 5660d160231f1a..039123da985e92 100644 --- a/components/cronet/android/java/src/org/chromium/net/impl/CronetEngineBuilderImpl.java +++ b/components/cronet/android/java/src/org/chromium/net/impl/CronetEngineBuilderImpl.java @@ -137,6 +137,7 @@ static HttpCacheMode fromPublicBuilderCacheMode(@HttpCacheSetting int cacheMode) private final CronetSource mSource; private boolean mPublicKeyPinningBypassForLocalTrustAnchorsEnabled; private String mUserAgent; + private String mProxyUrl; private String mStoragePath; private boolean mQuicEnabled; private boolean mHttp2Enabled; @@ -226,6 +227,16 @@ String getUserAgent() { return mUserAgent; } + @Override + public CronetEngineBuilderImpl setProxyUrl(String proxyUrl) { + mProxyUrl = proxyUrl; + return this; + } + + public String getProxyUrl() { + return mProxyUrl; + } + @Override public CronetEngineBuilderImpl setStoragePath(String value) { if (!new File(value).isDirectory()) { diff --git a/components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequestContext.java b/components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequestContext.java index dcaf7703e79f93..7105e9f007b5e9 100644 --- a/components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequestContext.java +++ b/components/cronet/android/java/src/org/chromium/net/impl/CronetUrlRequestContext.java @@ -320,10 +320,18 @@ public void run() { // mUrlRequestContextAdapter is guaranteed to exist until // initialization on init and network threads completes and // initNetworkThread is called back on network thread. - CronetUrlRequestContextJni.get() - .initRequestContextOnInitThread( - mUrlRequestContextAdapter, - CronetUrlRequestContext.this); + if (builder.getProxyUrl() != null && builder.getProxyUrl().startsWith("socks5://")) { + CronetUrlRequestContextJni.get() + .initRequestContextOnInitThreadWithUri( + mUrlRequestContextAdapter, + CronetUrlRequestContext.this, + builder.getProxyUrl()); + } else { + CronetUrlRequestContextJni.get() + .initRequestContextOnInitThread( + mUrlRequestContextAdapter, + CronetUrlRequestContext.this); + } } } @@ -447,6 +455,10 @@ private static RequestContextConfigOptions createRequestContextConfigOptions( resultBuilder.setExperimentalOptions(engineBuilder.experimentalOptions()); } + if (engineBuilder.getProxyUrl() != null) { + resultBuilder.setProxyUrl(engineBuilder.getProxyUrl()); + } + return resultBuilder.build(); } @@ -1098,6 +1110,9 @@ void addPkp( @NativeClassQualifiedName("CronetContextAdapter") void destroy(long nativePtr, CronetUrlRequestContext caller); + @NativeClassQualifiedName("CronetContextAdapter") + void initRequestContextOnInitThreadWithUri(long nativePtr, CronetUrlRequestContext caller, String uri); + @NativeClassQualifiedName("CronetContextAdapter") boolean startNetLogToFile( long nativePtr, CronetUrlRequestContext caller, String fileName, boolean logAll); diff --git a/components/cronet/android/proto/request_context_config.proto b/components/cronet/android/proto/request_context_config.proto index 348bd84d5e23ef..f73db98ddc54c4 100644 --- a/components/cronet/android/proto/request_context_config.proto +++ b/components/cronet/android/proto/request_context_config.proto @@ -21,4 +21,5 @@ message RequestContextConfigOptions { optional bool enable_network_quality_estimator = 12; optional bool bypass_public_key_pinning_for_local_trust_anchors = 13; optional int32 network_thread_priority = 14; + optional string proxy_url = 15; } diff --git a/components/cronet/android/sample/src/org/chromium/cronet_sample_apk/CronetSampleApplication.java b/components/cronet/android/sample/src/org/chromium/cronet_sample_apk/CronetSampleApplication.java index 87436d5a2d90c4..c48cabe786d2a7 100644 --- a/components/cronet/android/sample/src/org/chromium/cronet_sample_apk/CronetSampleApplication.java +++ b/components/cronet/android/sample/src/org/chromium/cronet_sample_apk/CronetSampleApplication.java @@ -24,6 +24,7 @@ public void onCreate() { myBuilder .enableHttpCache(CronetEngine.Builder.HTTP_CACHE_IN_MEMORY, 100 * 1024) .enableHttp2(true) + .setProxyUrl("socks5://127.0.0.1:1080") .enableQuic(true); mCronetEngine = myBuilder.build(); } @@ -62,6 +63,7 @@ public void restartCronetEngine() { .enableHttpCache(CronetEngine.Builder.HTTP_CACHE_IN_MEMORY, 100 * 1024) .enableHttp2(true) .enableQuic(true) + .setProxyUrl("socks5://127.0.0.1:1080") .build(); } } diff --git a/components/cronet/android/test/javatests/src/org/chromium/net/ExperimentalOptionsTranslationTestUtil.java b/components/cronet/android/test/javatests/src/org/chromium/net/ExperimentalOptionsTranslationTestUtil.java index a070ec49d39596..82921ef41d97c3 100644 --- a/components/cronet/android/test/javatests/src/org/chromium/net/ExperimentalOptionsTranslationTestUtil.java +++ b/components/cronet/android/test/javatests/src/org/chromium/net/ExperimentalOptionsTranslationTestUtil.java @@ -190,6 +190,11 @@ public String getDefaultUserAgent() { throw new UnsupportedOperationException(); } + @Override + public ICronetEngineBuilder setProxyUrl(String value) { + throw new UnsupportedOperationException(); + } + @Override public ICronetEngineBuilder setConnectionMigrationOptions( ConnectionMigrationOptions options) { diff --git a/components/cronet/cronet_context.cc b/components/cronet/cronet_context.cc index 324706d8b4af07..0cfcadb4b94d0b 100644 --- a/components/cronet/cronet_context.cc +++ b/components/cronet/cronet_context.cc @@ -258,6 +258,19 @@ CronetContext::NetworkTasks::~NetworkTasks() { net::NetworkChangeNotifier::RemoveNetworkObserver(this); } +void CronetContext::InitRequestContextOnInitThreadWithUri(std::string_view uri) { + DCHECK(OnInitThread()); + auto proxy_config_service = + cronet::CreateFixedProxyConfigService(GetNetworkTaskRunner(), uri); + g_net_log.Get().EnsureInitializedOnInitThread(); + GetNetworkTaskRunner()->PostTask( + FROM_HERE, + base::BindOnce(&CronetContext::NetworkTasks::Initialize, + base::Unretained(network_tasks_), GetNetworkTaskRunner(), + GetFileThread()->task_runner(), + std::move(proxy_config_service))); +} + void CronetContext::InitRequestContextOnInitThread() { DCHECK(OnInitThread()); // Cannot create this inside Initialize because Android requires this to be diff --git a/components/cronet/cronet_context.h b/components/cronet/cronet_context.h index 6c372d57552cbe..f5566b77e4ae72 100644 --- a/components/cronet/cronet_context.h +++ b/components/cronet/cronet_context.h @@ -113,6 +113,8 @@ class CronetContext { // Blocks until network thread is destroyed after running all pending tasks. virtual ~CronetContext(); + void InitRequestContextOnInitThreadWithUri(std::string_view uri); + // Called on init thread to initialize URLRequestContext. void InitRequestContextOnInitThread(); diff --git a/components/cronet/cronet_global_state.h b/components/cronet/cronet_global_state.h index 3bd6201df9e504..fcb1cc32be5956 100644 --- a/components/cronet/cronet_global_state.h +++ b/components/cronet/cronet_global_state.h @@ -33,6 +33,9 @@ void PostTaskToInitThread(const base::Location& posted_from, // idempotent, and must complete initialization before returning. void EnsureInitialized(); +std::unique_ptr CreateFixedProxyConfigService( + const scoped_refptr& io_task_runner, std::string_view uri); + // Creates a proxy config service appropriate for this platform that fetches the // system proxy settings. Cronet will call this API only after a prior call // to EnsureInitialized() has returned. diff --git a/components/cronet/cronet_global_state_stubs.cc b/components/cronet/cronet_global_state_stubs.cc index e1162a786c688f..c8c132b35a7e3d 100644 --- a/components/cronet/cronet_global_state_stubs.cc +++ b/components/cronet/cronet_global_state_stubs.cc @@ -62,6 +62,12 @@ void PostTaskToInitThread(const base::Location& posted_from, InitTaskRunner()->PostTask(posted_from, std::move(task)); } +std::unique_ptr CreateFixedProxyConfigService( + const scoped_refptr& io_task_runner, std::string_view uri) { + return net::ProxyConfigService::CreateFixedSystemProxyConfigService( + io_task_runner, uri); +} + std::unique_ptr CreateProxyConfigService( const scoped_refptr& io_task_runner) { return net::ProxyConfigService::CreateSystemProxyConfigService( diff --git a/components/cronet/native/cronet.idl b/components/cronet/native/cronet.idl index 8e83cb0cf70a40..b119e18f598236 100644 --- a/components/cronet/native/cronet.idl +++ b/components/cronet/native/cronet.idl @@ -511,6 +511,12 @@ struct EngineParams { */ string user_agent; + /** + * Use an HTTP(S) or SOCKS proxy. Setting this will use the proxy for all + * connections + */ + string proxy_url; + /** * Sets a default value for the Accept-Language header value for UrlRequests * created by this engine. Explicitly setting the Accept-Language header diff --git a/components/cronet/native/engine.cc b/components/cronet/native/engine.cc index c35c4dbfc33ec1..df655bb256543d 100644 --- a/components/cronet/native/engine.cc +++ b/components/cronet/native/engine.cc @@ -149,6 +149,7 @@ Cronet_RESULT Cronet_EngineImpl::StartWithParams( context_config_builder.storage_path = params->storage_path; context_config_builder.accept_language = params->accept_language; context_config_builder.user_agent = params->user_agent; + context_config_builder.proxy_url = params->proxy_url; context_config_builder.experimental_options = params->experimental_options; context_config_builder.bypass_public_key_pinning_for_local_trust_anchors = params->enable_public_key_pinning_bypass_for_local_trust_anchors; @@ -199,10 +200,17 @@ Cronet_RESULT Cronet_EngineImpl::StartWithParams( // private and mark CronetLibraryLoader.postToInitThread() as // @VisibleForTesting (as the only external use will be in a test). - // Initialize context on the init thread. - cronet::PostTaskToInitThread( - FROM_HERE, base::BindOnce(&CronetContext::InitRequestContextOnInitThread, - base::Unretained(context_.get()))); + + // Initialize context on the init thread, potentially with a proxy. + if (params->proxy_url.empty()) { + cronet::PostTaskToInitThread( + FROM_HERE, base::BindOnce(&CronetContext::InitRequestContextOnInitThread, + base::Unretained(context_.get()))); + } else { + cronet::PostTaskToInitThread( + FROM_HERE, base::BindOnce(&CronetContext::InitRequestContextOnInitThreadWithUri, + base::Unretained(context_.get()), params->proxy_url)); + } return CheckResult(Cronet_RESULT_SUCCESS); } diff --git a/components/cronet/native/generated/cronet.idl_c.h b/components/cronet/native/generated/cronet.idl_c.h index 988e6efacb0f32..b451cdc686f062 100644 --- a/components/cronet/native/generated/cronet.idl_c.h +++ b/components/cronet/native/generated/cronet.idl_c.h @@ -795,6 +795,9 @@ CRONET_EXPORT void Cronet_EngineParams_user_agent_set(Cronet_EngineParamsPtr self, const Cronet_String user_agent); CRONET_EXPORT +void Cronet_EngineParams_proxy_url_set(Cronet_EngineParamsPtr self, + const Cronet_String proxy_url); +CRONET_EXPORT void Cronet_EngineParams_accept_language_set( Cronet_EngineParamsPtr self, const Cronet_String accept_language); @@ -845,6 +848,9 @@ CRONET_EXPORT Cronet_String Cronet_EngineParams_user_agent_get( const Cronet_EngineParamsPtr self); CRONET_EXPORT +Cronet_String Cronet_EngineParams_proxy_url_get( + const Cronet_EngineParamsPtr self); +CRONET_EXPORT Cronet_String Cronet_EngineParams_accept_language_get( const Cronet_EngineParamsPtr self); CRONET_EXPORT diff --git a/components/cronet/native/generated/cronet.idl_impl_struct.cc b/components/cronet/native/generated/cronet.idl_impl_struct.cc index b9120ff8c24f69..eb598226660d9a 100644 --- a/components/cronet/native/generated/cronet.idl_impl_struct.cc +++ b/components/cronet/native/generated/cronet.idl_impl_struct.cc @@ -249,6 +249,12 @@ void Cronet_EngineParams_user_agent_set(Cronet_EngineParamsPtr self, self->user_agent = user_agent; } +void Cronet_EngineParams_proxy_url_set(Cronet_EngineParamsPtr self, + const Cronet_String proxy_url) { + DCHECK(self); + self->proxy_url = proxy_url; +} + void Cronet_EngineParams_accept_language_set( Cronet_EngineParamsPtr self, const Cronet_String accept_language) { @@ -342,6 +348,12 @@ Cronet_String Cronet_EngineParams_user_agent_get( return self->user_agent.c_str(); } +Cronet_String Cronet_EngineParams_proxy_url_get( + const Cronet_EngineParamsPtr self) { + DCHECK(self); + return self->proxy_url.c_str(); +} + Cronet_String Cronet_EngineParams_accept_language_get( const Cronet_EngineParamsPtr self) { DCHECK(self); diff --git a/components/cronet/native/generated/cronet.idl_impl_struct.h b/components/cronet/native/generated/cronet.idl_impl_struct.h index badb341ce331cd..1677263ce638ff 100644 --- a/components/cronet/native/generated/cronet.idl_impl_struct.h +++ b/components/cronet/native/generated/cronet.idl_impl_struct.h @@ -82,6 +82,7 @@ struct Cronet_EngineParams { bool enable_check_result = true; std::string user_agent; + std::string proxy_url; std::string accept_language; std::string storage_path; bool enable_quic = true; diff --git a/components/cronet/native/sample/main.cc b/components/cronet/native/sample/main.cc index fe9ff6f1ee2791..0742ac675a5c8e 100644 --- a/components/cronet/native/sample/main.cc +++ b/components/cronet/native/sample/main.cc @@ -17,6 +17,7 @@ Cronet_EnginePtr CreateCronetEngine() { Cronet_EnginePtr cronet_engine = Cronet_Engine_Create(); Cronet_EngineParamsPtr engine_params = Cronet_EngineParams_Create(); Cronet_EngineParams_user_agent_set(engine_params, "CronetSample/1"); + Cronet_EngineParams_proxy_url_set(engine_params, "socks5://127.0.0.1:1080"); Cronet_EngineParams_enable_quic_set(engine_params, true); Cronet_Engine_StartWithParams(cronet_engine, engine_params); diff --git a/components/cronet/native/test/test_util.cc b/components/cronet/native/test/test_util.cc index 07a2aaadbf46a9..cd634f3ca528d2 100644 --- a/components/cronet/native/test/test_util.cc +++ b/components/cronet/native/test/test_util.cc @@ -52,7 +52,7 @@ class TestCertVerifier : public net::MockCertVerifier { namespace cronet { namespace test { -Cronet_EnginePtr CreateTestEngine(int quic_server_port) { +Cronet_EnginePtr CreateTestEngine(int quic_server_port, const std::string& proxy_url) { Cronet_EngineParamsPtr engine_params = Cronet_EngineParams_Create(); Cronet_EngineParams_user_agent_set(engine_params, "test"); // Add Host Resolver Rules. @@ -75,6 +75,9 @@ Cronet_EnginePtr CreateTestEngine(int quic_server_port) { Cronet_QuicHint_alternate_port_set(quic_hint, 443); Cronet_EngineParams_quic_hints_add(engine_params, quic_hint); Cronet_QuicHint_Destroy(quic_hint); + if(proxy_url != "") { + Cronet_EngineParams_proxy_url_set(engine_params, proxy_url.c_str()); + } // Create Cronet Engine. Cronet_EnginePtr cronet_engine = Cronet_Engine_Create(); // Set Mock Cert Verifier. @@ -87,6 +90,11 @@ Cronet_EnginePtr CreateTestEngine(int quic_server_port) { return cronet_engine; } +Cronet_EnginePtr CreateTestEngine(int quic_server_port) { + std::string foo = ""; + return CreateTestEngine(quic_server_port, foo); +} + Cronet_ExecutorPtr CreateTestExecutor() { return Cronet_Executor_CreateWith(TestExecutor_Execute); } diff --git a/components/cronet/native/test/test_util.h b/components/cronet/native/test/test_util.h index 767bb8149db51d..da19270dfee087 100644 --- a/components/cronet/native/test/test_util.h +++ b/components/cronet/native/test/test_util.h @@ -13,6 +13,7 @@ namespace cronet { namespace test { // Create an engine that is configured to support local test servers. +Cronet_EnginePtr CreateTestEngine(int quic_server_port, const std::string& proxy_url); Cronet_EnginePtr CreateTestEngine(int quic_server_port); // Create an executor that runs tasks on different background thread. diff --git a/components/cronet/native/test/url_request_test.cc b/components/cronet/native/test/url_request_test.cc index e65bfb14eaa4a8..2c00490896bfbd 100644 --- a/components/cronet/native/test/url_request_test.cc +++ b/components/cronet/native/test/url_request_test.cc @@ -306,8 +306,10 @@ class UrlRequestTest : public ::testing::TestWithParam< std::unique_ptr test_callback, const std::string& http_method, TestUploadDataProvider* test_upload_data_provider, - int remapped_port) { - Cronet_EnginePtr engine = cronet::test::CreateTestEngine(remapped_port); + int remapped_port, + const std::string& proxy_url) { + Cronet_EnginePtr engine = cronet::test::CreateTestEngine(remapped_port, + proxy_url); Cronet_UrlRequestPtr request = Cronet_UrlRequest_Create(); Cronet_UrlRequestParamsPtr request_params = Cronet_UrlRequestParams_Create(); @@ -363,6 +365,18 @@ class UrlRequestTest : public ::testing::TestWithParam< return test_callback; } + std::unique_ptr StartAndWaitForComplete( + const std::string& url, + std::unique_ptr test_callback, + const std::string& http_method, + TestUploadDataProvider* test_upload_data_provider, + int remapped_port) { + std::string empty = ""; + return StartAndWaitForComplete(url, std::move(test_callback), http_method, + test_upload_data_provider, remapped_port, + /* proxy_url */ empty); + } + std::unique_ptr StartAndWaitForComplete( const std::string& url, std::unique_ptr test_callback, @@ -669,6 +683,29 @@ TEST_P(UrlRequestTest, SimpleGet) { ExpectResponseInfoEquals(expected_response_info, *callback->response_info()); } +TEST_P(UrlRequestTest, ProxyGet) { + const std::string url = cronet::TestServer::GetEchoMethodURL(); + const std::string proxy_url = cronet::TestServer::GetConnectProxyURL(); + auto callback = StartAndWaitForComplete( + url, + std::make_unique(GetDirectExecutorParam()), // test_callback + std::string(), // http_method + nullptr, // test_upload_data_provider + 0, // remapped_port + proxy_url); + EXPECT_EQ(200, callback->response_info()->http_status_code); + // Default method is 'GET'. + EXPECT_EQ("GET", callback->response_as_string()); + EXPECT_EQ(0, callback->redirect_count()); + EXPECT_EQ(callback->response_step(), callback->ON_SUCCEEDED); + CheckResponseInfo(*callback->response_info(), url, 200, "OK"); + TestUrlRequestCallback::UrlResponseInfo expected_response_info( + std::vector({url}), "OK", 200, 86, + std::vector({"Connection", "close", "Content-Length", "3", + "Content-Type", "text/plain"})); + ExpectResponseInfoEquals(expected_response_info, *callback->response_info()); +} + TEST_P(UrlRequestTest, UploadEmptyBodySync) { const std::string url = cronet::TestServer::GetEchoRequestBodyURL(); TestUploadDataProvider data_provider(TestUploadDataProvider::SYNC, diff --git a/components/cronet/testing/test_server/test_server.cc b/components/cronet/testing/test_server/test_server.cc index d3d539998bd132..a40c2f3f1d58c8 100644 --- a/components/cronet/testing/test_server/test_server.cc +++ b/components/cronet/testing/test_server/test_server.cc @@ -42,9 +42,12 @@ const char kSetCookiePath[] = "/set_cookie?"; const char kBigDataPath[] = "/big_data?"; const char kUseEncodingPath[] = "/use_encoding?"; const char kEchoBodyPath[] = "/echo_body"; +const char kConnectProxyPath[] = "/connect_proxy"; const char kSimpleResponse[] = "The quick brown fox jumps over the lazy dog."; +const char kSimpleProxyResponse[] = "HTTP/1.1 200 Connection established\r\n\r\n"; + std::unique_ptr g_test_server; base::LazyInstance::Leaky g_big_data_body = LAZY_INSTANCE_INITIALIZER; @@ -166,6 +169,12 @@ std::unique_ptr CronetTestRequestHandler( return std::move(response); } + if (request.relative_url == kConnectProxyPath) { + response->set_code(net::HTTP_OK); + response->set_content(kSimpleProxyResponse); + return std::move(response); + } + // Unhandled requests result in the Embedded test server sending a 404. return nullptr; } @@ -267,6 +276,11 @@ std::string TestServer::GetExabyteResponseURL() { return GetFileURL("/exabyte_response"); } +/* static */ +std::string TestServer::GetConnectProxyURL() { + return GetFileURL(kConnectProxyPath); +} + /* static */ std::string TestServer::PrepareBigDataURL(size_t data_size) { DCHECK(g_test_server); diff --git a/components/cronet/testing/test_server/test_server.h b/components/cronet/testing/test_server/test_server.h index cd4d3635b4efaf..acb079605d57f2 100644 --- a/components/cronet/testing/test_server/test_server.h +++ b/components/cronet/testing/test_server/test_server.h @@ -61,6 +61,8 @@ class TestServer { static std::string GetRedirectToEchoBodyURL(); // Returns a URL that the server will return an Exabyte of data. static std::string GetExabyteResponseURL(); + // Returns A URL to use as a CONNECT proxy to the TestServer. + static std::string GetConnectProxyURL(); // Prepares response and returns URL which respond with |data_size| of bytes // in response body. static std::string PrepareBigDataURL(size_t data_size); diff --git a/components/cronet/url_request_context_config.cc b/components/cronet/url_request_context_config.cc index 8da024badd93ab..773e643029f7c3 100644 --- a/components/cronet/url_request_context_config.cc +++ b/components/cronet/url_request_context_config.cc @@ -311,6 +311,7 @@ URLRequestContextConfig::URLRequestContextConfig( const std::string& storage_path, const std::string& accept_language, const std::string& user_agent, + const std::string& proxy_url, base::Value::Dict experimental_options, std::unique_ptr mock_cert_verifier, bool enable_network_quality_estimator, @@ -325,6 +326,7 @@ URLRequestContextConfig::URLRequestContextConfig( storage_path(storage_path), accept_language(accept_language), user_agent(user_agent), + proxy_url(proxy_url), mock_cert_verifier(std::move(mock_cert_verifier)), enable_network_quality_estimator(enable_network_quality_estimator), bypass_public_key_pinning_for_local_trust_anchors( @@ -351,6 +353,7 @@ URLRequestContextConfig::CreateURLRequestContextConfig( const std::string& storage_path, const std::string& accept_language, const std::string& user_agent, + const std::string& proxy_url, const std::string& unparsed_experimental_options, std::unique_ptr mock_cert_verifier, bool enable_network_quality_estimator, @@ -368,7 +371,7 @@ URLRequestContextConfig::CreateURLRequestContextConfig( } return base::WrapUnique(new URLRequestContextConfig( enable_quic, enable_spdy, enable_brotli, http_cache, http_cache_max_size, - load_disable_cache, storage_path, accept_language, user_agent, + load_disable_cache, storage_path, accept_language, user_agent, proxy_url, std::move(experimental_options).value(), std::move(mock_cert_verifier), enable_network_quality_estimator, bypass_public_key_pinning_for_local_trust_anchors, @@ -908,7 +911,7 @@ std::unique_ptr URLRequestContextConfigBuilder::Build() { return URLRequestContextConfig::CreateURLRequestContextConfig( enable_quic, enable_spdy, enable_brotli, http_cache, http_cache_max_size, - load_disable_cache, storage_path, accept_language, user_agent, + load_disable_cache, storage_path, accept_language, user_agent, proxy_url, experimental_options, std::move(mock_cert_verifier), enable_network_quality_estimator, bypass_public_key_pinning_for_local_trust_anchors, diff --git a/components/cronet/url_request_context_config.h b/components/cronet/url_request_context_config.h index 324525466cf245..fe91814bd3df61 100644 --- a/components/cronet/url_request_context_config.h +++ b/components/cronet/url_request_context_config.h @@ -128,6 +128,9 @@ struct URLRequestContextConfig { // User-Agent request header field. const std::string user_agent; + // URL of proxy server + const std::string proxy_url; + // Certificate verifier for testing. std::unique_ptr mock_cert_verifier; @@ -200,6 +203,8 @@ struct URLRequestContextConfig { const std::string& accept_language, // User-Agent request header field. const std::string& user_agent, + // URL of proxy server + const std::string& proxy_url, // JSON encoded experimental options. const std::string& unparsed_experimental_options, // MockCertVerifier to use for testing purposes. @@ -234,6 +239,8 @@ struct URLRequestContextConfig { const std::string& accept_language, // User-Agent request header field. const std::string& user_agent, + // URL of proxy + const std::string& proxy_url, // Parsed experimental options. base::Value::Dict experimental_options, // MockCertVerifier to use for testing purposes. @@ -302,6 +309,8 @@ struct URLRequestContextConfigBuilder { std::string accept_language = ""; // User-Agent request header field. std::string user_agent = ""; + // URL of proxy + std::string proxy_url = ""; // Experimental options encoded as a string in a JSON format containing // experiments and their corresponding configuration options. The format // is a JSON object with the name of the experiment as the key, and the diff --git a/net/proxy_resolution/proxy_config_service.cc b/net/proxy_resolution/proxy_config_service.cc index e32f7dd46b4d08..02f51e4508cc1c 100644 --- a/net/proxy_resolution/proxy_config_service.cc +++ b/net/proxy_resolution/proxy_config_service.cc @@ -11,7 +11,9 @@ #include "base/task/sequenced_task_runner.h" #include "base/task/single_thread_task_runner.h" #include "build/build_config.h" +#include "net/base/proxy_string_util.h" #include "net/proxy_resolution/proxy_config_with_annotation.h" +#include "net/proxy_resolution/proxy_config_service_fixed.h" #if BUILDFLAG(IS_WIN) #include "net/proxy_resolution/win/proxy_config_service_win.h" @@ -58,6 +60,30 @@ constexpr net::NetworkTrafficAnnotationTag kSystemProxyConfigTrafficAnnotation = "Using 'ProxySettings' policy can set Chrome to use specific " "proxy settings and avoid system proxy." })"); +constexpr net::NetworkTrafficAnnotationTag kFixedProxyConfigTrafficAnnotation = + net::DefineNetworkTrafficAnnotation("proxy_config_fixed", R"( + semantics { + sender: "Proxy Config" + description: + "Establishing a connection through a proxy server using fixed proxy." + trigger: + "Whenever a network request is made when the fixed proxy settings " + "are used, and they indicate to use a proxy server." + data: + "Proxy configuration." + destination: OTHER + destination_other: + "The proxy server specified in the configuration." + } + policy { + cookies_allowed: NO + setting: + "User cannot override system proxy settings, but can change them " + "through 'Advanced/System/Open proxy settings'." + policy_exception_justification: + "Using 'ProxySettings' policy can set Chrome to use specific " + "proxy settings and avoid system proxy." + })"); #endif #if BUILDFLAG(IS_CHROMEOS) @@ -90,6 +116,23 @@ class ProxyConfigServiceDirect : public ProxyConfigService { } // namespace +// static +std::unique_ptr +ProxyConfigService::CreateFixedSystemProxyConfigService( + const scoped_refptr& main_task_runner, std::string_view uri) { + ProxyConfig raw_proxy_config; + raw_proxy_config.proxy_rules().type = ProxyConfig::ProxyRules::Type::PROXY_LIST; + raw_proxy_config.proxy_rules().single_proxies.SetSingleProxyServer(ProxyUriToProxyServer(uri, ProxyServer::SCHEME_SOCKS5)); + + // scm: we should probably use a unique annotation, but I'm failing to make that work +#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_LINUX) + ProxyConfigWithAnnotation proxy_config = ProxyConfigWithAnnotation(raw_proxy_config, kFixedProxyConfigTrafficAnnotation); +#else + ProxyConfigWithAnnotation proxy_config = ProxyConfigWithAnnotation(raw_proxy_config, NO_TRAFFIC_ANNOTATION_YET); +#endif + return std::make_unique(proxy_config); +} + // static std::unique_ptr ProxyConfigService::CreateSystemProxyConfigService( diff --git a/net/proxy_resolution/proxy_config_service.h b/net/proxy_resolution/proxy_config_service.h index 7d22c4e2d255a1..032909f1390c10 100644 --- a/net/proxy_resolution/proxy_config_service.h +++ b/net/proxy_resolution/proxy_config_service.h @@ -79,6 +79,12 @@ class NET_EXPORT ProxyConfigService { // consumer of the ProxyConfigService will live. static std::unique_ptr CreateSystemProxyConfigService( scoped_refptr main_task_runner); + + // Creats a config service that uses a (cronet) caller specified proxy. + // |main_task_runner| is the sequence where the consumer of the + // ProxyConfigService will live. |uri| is the URL of the proxy. + static std::unique_ptr CreateFixedSystemProxyConfigService( + const scoped_refptr& main_task_runner, std::string_view uri); }; } // namespace net From 0c8732bc125fec0289e54fedae9998c10549aad2 Mon Sep 17 00:00:00 2001 From: Steven McDonald Date: Wed, 4 Jun 2025 11:11:37 -0700 Subject: [PATCH 2/2] oops, this was a stray old comment --- net/proxy_resolution/proxy_config_service.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/net/proxy_resolution/proxy_config_service.cc b/net/proxy_resolution/proxy_config_service.cc index 02f51e4508cc1c..065e749f944678 100644 --- a/net/proxy_resolution/proxy_config_service.cc +++ b/net/proxy_resolution/proxy_config_service.cc @@ -124,7 +124,6 @@ ProxyConfigService::CreateFixedSystemProxyConfigService( raw_proxy_config.proxy_rules().type = ProxyConfig::ProxyRules::Type::PROXY_LIST; raw_proxy_config.proxy_rules().single_proxies.SetSingleProxyServer(ProxyUriToProxyServer(uri, ProxyServer::SCHEME_SOCKS5)); - // scm: we should probably use a unique annotation, but I'm failing to make that work #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_LINUX) ProxyConfigWithAnnotation proxy_config = ProxyConfigWithAnnotation(raw_proxy_config, kFixedProxyConfigTrafficAnnotation); #else