Skip to content

Commit

Permalink
Support for dynamic proxy configuration at the HTTP protocol level (#…
Browse files Browse the repository at this point in the history
…3593)

Fixes #2746

Signed-off-by: Violeta Georgieva <[email protected]>
Co-authored-by: Violeta Georgieva <[email protected]>
  • Loading branch information
raccoonback and violetagg authored Jan 22, 2025
1 parent 0a98086 commit 4b49d36
Show file tree
Hide file tree
Showing 5 changed files with 369 additions and 3 deletions.
13 changes: 13 additions & 0 deletions docs/modules/ROOT/partials/proxy.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,16 @@ The following example uses `ProxyProvider`:
include::{examples-dir}/proxy/Application.java[lines=18..42]
----
<1> Configures the connection establishment timeout to 20 seconds.


// tag::proxy-when-method[]
The `proxyWhen(...)` method allows for dynamic proxy configuration.

- `proxyWhen(...)` takes an `HttpClientConfig` and enables setting a proxy for each individual HTTP request, providing flexibility to choose different proxies depending on the request.
- Once the proxy is set using `proxyWhen(...)`, any previous proxy settings via `proxy(...)` or `noProxy()` will be ignored.
// end::proxy-when-method[]
{examples-link}/proxy/deferred/Application.java
[%unbreakable]
----
include::{examples-dir}/proxy/deferred/Application.java[lines=18..53]
----
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2020-2024 VMware, Inc. or its affiliates, All Rights Reserved.
* Copyright (c) 2020-2025 VMware, Inc. or its affiliates, All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -252,7 +252,7 @@ public T proxy(Consumer<? super ProxyProvider.TypeSpec> proxyOptions) {
Objects.requireNonNull(proxyOptions, "proxyOptions");
ProxyProvider.Build builder = (ProxyProvider.Build) ProxyProvider.builder();
proxyOptions.accept(builder);
return proxyWithProxyProviderSupplier(() -> builder.build());
return proxyWithProxyProviderSupplier(builder::build);
}

final T proxyWithProxyProvider(ProxyProvider proxy) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright (c) 2025 VMware, Inc. or its affiliates, All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package reactor.netty.examples.documentation.http.client.proxy.deferred;

import reactor.core.publisher.Mono;
import reactor.netty.http.client.HttpClient;
import reactor.netty.transport.ProxyProvider;

public class Application {

public static void main(String[] args) {
HttpClient client =
HttpClient.create()
.proxyWhen((httpClientConfig, spec) -> { // only applied
if (httpClientConfig.uri().startsWith("https://example.com")) {
return Mono.justOrEmpty(
spec.type(ProxyProvider.Proxy.HTTP)
.host("proxy")
.port(8080)
.nonProxyHosts("localhost")
.connectTimeoutMillis(20_000)
);
}

return Mono.empty();
})
.proxy(
spec -> spec.type(ProxyProvider.Proxy.HTTP)
.host("ignored-proxy-domain")
.port(9000)
.connectTimeoutMillis(20_000)
) // ignored
.noProxy(); // ignored

String response =
client.get()
.uri("https://example.com/")
.responseContent()
.aggregate()
.asString()
.block();

System.out.println("Response " + response);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@
import reactor.netty.tcp.SslProvider;
import reactor.netty.tcp.TcpClient;
import reactor.netty.transport.ClientTransport;
import reactor.netty.transport.ProxyProvider;
import reactor.util.Logger;
import reactor.util.Loggers;
import reactor.util.annotation.Incubating;
Expand Down Expand Up @@ -1640,6 +1641,38 @@ public Mono<Void> warmup() {
Mono.fromRunnable(OpenSsl::version));
}

/**
* Supports proxy configuration with a deferred approach.
*
* <p>When proxyWhen(...) is set, calls to proxy(...) and noProxy() methods are ignored.</p>
* <p>This method allows dynamic determination of proxy settings, applying or skipping the proxy based on the configured conditions.</p>
*
* @param proxyBuilder a consumer for proxy configuration
* @return a new {@link HttpClient} reference
*/
public final HttpClient proxyWhen(
BiFunction<HttpClientConfig, ? super ProxyProvider.TypeSpec, Mono<? extends ProxyProvider.Builder>> proxyBuilder) {
Objects.requireNonNull(proxyBuilder, "proxyBuilder");
HttpClient dup = duplicate();
dup.configuration()
.deferredConf(
config -> {
Mono<? extends ProxyProvider.Builder> mono = proxyBuilder.apply(config, ProxyProvider.builder());
if (mono == null || mono == Mono.<ProxyProvider.Builder>empty()) {
return Mono.just(config);
}

return mono.map(
builder -> {
config.proxyProvider(builder.build());
return config;
}
);
}
);
return dup;
}

/**
* HTTP Websocket to connect the {@link HttpClient}.
*
Expand Down
Loading

0 comments on commit 4b49d36

Please sign in to comment.