Skip to content

Commit eafdf79

Browse files
jaycee-licopybara-github
authored andcommitted
feat: Add ProxyOptions in ClientOptions for configuring proxies
PiperOrigin-RevId: 841878724
1 parent febac7f commit eafdf79

File tree

6 files changed

+590
-7
lines changed

6 files changed

+590
-7
lines changed

README.md

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -188,20 +188,52 @@ retry settings configured on the client.
188188

189189
### ClientOptions
190190
[ClientOptions](https://github.com/googleapis/java-genai/blob/main/src/main/java/com/google/genai/types/ClientOptions.java)
191-
enables you to customize the behavior of the HTTP client. It currently supports
192-
configuring the connection pool via `maxConnections` (total maximum connections)
193-
and `maxConnectionsPerHost` (maximum connections to a single host).
191+
enables you to customize the behavior of the HTTP client, including connection
192+
pool settings and proxy configurations.
193+
194+
#### Connection Pool
195+
You can configure the connection pool via `maxConnections` (total maximum
196+
connections) and `maxConnectionsPerHost` (maximum connections to a single host).
194197

195198
```java
196199
import com.google.genai.Client;
197200
import com.google.genai.types.ClientOptions;
198201

199-
Client client = Client.builder()
200-
.apiKey("your-api-key")
201-
.clientOptions(ClientOptions.builder().maxConnections(64).maxConnectionsPerHost(16))
202-
.build();
202+
Client client =
203+
Client.builder()
204+
.apiKey("your-api-key")
205+
.clientOptions(
206+
ClientOptions.builder().maxConnections(64).maxConnectionsPerHost(16).build())
207+
.build();
203208
```
204209

210+
#### Proxy
211+
If your environment requires connecting through a proxy, you can configure it
212+
using `ProxyOptions`. The SDK supports `HTTP`, `SOCKS`, and `DIRECT` (no proxy)
213+
connection types, along with basic proxy authentication.
214+
215+
```java
216+
import com.google.genai.Client;
217+
import com.google.genai.types.ClientOptions;
218+
import com.google.genai.types.ProxyOptions;
219+
import com.google.genai.types.ProxyType;
220+
221+
ClientOptions clientOptions =
222+
ClientOptions.builder()
223+
.proxyOptions(
224+
ProxyOptions.builder()
225+
.type(ProxyType.Known.HTTP)
226+
.host("your-proxy-host")
227+
.port(8080)
228+
.username("your-proxy-username")
229+
.password("your-proxy-password"))
230+
.build();
231+
Client client = Client.builder().apiKey("your-api-key").clientOptions(clientOptions).build();
232+
```
233+
234+
If `ProxyOptions` is provided with `type` set to `DIRECT`, it will enforce a
235+
direct connection, bypassing any system-level proxy settings.
236+
205237
### Interact with models
206238
The Google Gen AI Java SDK allows you to access the service programmatically.
207239
The following code snippets are some basic usages of model inferencing.

src/main/java/com/google/genai/ApiClient.java

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,19 @@
3030
import com.google.genai.types.ClientOptions;
3131
import com.google.genai.types.HttpOptions;
3232
import com.google.genai.types.HttpRetryOptions;
33+
import com.google.genai.types.ProxyOptions;
34+
import com.google.genai.types.ProxyType;
3335
import java.io.IOException;
36+
import java.net.InetSocketAddress;
37+
import java.net.Proxy;
3438
import java.time.Duration;
3539
import java.util.List;
3640
import java.util.Map;
3741
import java.util.Optional;
3842
import java.util.concurrent.CompletableFuture;
3943
import java.util.logging.Logger;
4044
import java.util.stream.Stream;
45+
import okhttp3.Credentials;
4146
import okhttp3.Dispatcher;
4247
import okhttp3.MediaType;
4348
import okhttp3.OkHttpClient;
@@ -245,11 +250,74 @@ private OkHttpClient createHttpClient(
245250
options.maxConnections().ifPresent(dispatcher::setMaxRequests);
246251
options.maxConnectionsPerHost().ifPresent(dispatcher::setMaxRequestsPerHost);
247252
builder.dispatcher(dispatcher);
253+
options
254+
.proxyOptions()
255+
.ifPresent(
256+
proxyOptions -> {
257+
applyProxyOptions(proxyOptions, builder);
258+
});
248259
});
249260

250261
return builder.build();
251262
}
252263

264+
/** Applies the proxy options to the OkHttpClient builder. */
265+
private void applyProxyOptions(ProxyOptions proxyOptions, OkHttpClient.Builder builder) {
266+
final ProxyType proxyType = proxyOptions.type().orElse(new ProxyType("HTTP"));
267+
final Proxy.Type type;
268+
269+
switch (proxyType.knownEnum()) {
270+
case SOCKS:
271+
type = Proxy.Type.SOCKS;
272+
break;
273+
case HTTP:
274+
type = Proxy.Type.HTTP;
275+
break;
276+
case DIRECT:
277+
builder.proxy(Proxy.NO_PROXY);
278+
return;
279+
default:
280+
throw new IllegalArgumentException("Unsupported proxy type: " + proxyType);
281+
}
282+
// Set the proxy for non-direct types.
283+
String host =
284+
proxyOptions
285+
.host()
286+
.orElseThrow(
287+
() -> new IllegalArgumentException("Proxy host is required in the ProxyOptions."));
288+
int port =
289+
proxyOptions
290+
.port()
291+
.orElseThrow(
292+
() -> new IllegalArgumentException("Proxy port is required in the ProxyOptions."));
293+
294+
builder.proxy(new Proxy(type, new InetSocketAddress(host, port)));
295+
296+
// Set the proxy authenticator if username and password are provided.
297+
boolean userPresent = proxyOptions.username().isPresent();
298+
boolean passPresent = proxyOptions.password().isPresent();
299+
300+
if (userPresent != passPresent) {
301+
throw new IllegalArgumentException(
302+
"Proxy username and password must both be provided or not at all.");
303+
}
304+
if (userPresent && passPresent) {
305+
final String credential =
306+
Credentials.basic(proxyOptions.username().get(), proxyOptions.password().get());
307+
builder.proxyAuthenticator(
308+
(route, response) -> {
309+
if (response.request().header("Proxy-Authorization") != null) {
310+
return null;
311+
}
312+
return response
313+
.request()
314+
.newBuilder()
315+
.header("Proxy-Authorization", credential)
316+
.build();
317+
});
318+
}
319+
}
320+
253321
/** Builds a HTTP request given the http method, path, and request json string. */
254322
@SuppressWarnings("unchecked")
255323
protected Request buildRequest(

src/main/java/com/google/genai/types/ClientOptions.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ public abstract class ClientOptions extends JsonSerializable {
3838
@JsonProperty("maxConnectionsPerHost")
3939
public abstract Optional<Integer> maxConnectionsPerHost();
4040

41+
/** Proxy configuration to be used in the client. */
42+
@JsonProperty("proxyOptions")
43+
public abstract Optional<ProxyOptions> proxyOptions();
44+
4145
/** Instantiates a builder for ClientOptions. */
4246
@ExcludeFromGeneratedCoverageReport
4347
public static Builder builder() {
@@ -92,6 +96,34 @@ public Builder clearMaxConnectionsPerHost() {
9296
return maxConnectionsPerHost(Optional.empty());
9397
}
9498

99+
/**
100+
* Setter for proxyOptions.
101+
*
102+
* <p>proxyOptions: Proxy configuration to be used in the client.
103+
*/
104+
@JsonProperty("proxyOptions")
105+
public abstract Builder proxyOptions(ProxyOptions proxyOptions);
106+
107+
/**
108+
* Setter for proxyOptions builder.
109+
*
110+
* <p>proxyOptions: Proxy configuration to be used in the client.
111+
*/
112+
@CanIgnoreReturnValue
113+
public Builder proxyOptions(ProxyOptions.Builder proxyOptionsBuilder) {
114+
return proxyOptions(proxyOptionsBuilder.build());
115+
}
116+
117+
@ExcludeFromGeneratedCoverageReport
118+
abstract Builder proxyOptions(Optional<ProxyOptions> proxyOptions);
119+
120+
/** Clears the value of proxyOptions field. */
121+
@ExcludeFromGeneratedCoverageReport
122+
@CanIgnoreReturnValue
123+
public Builder clearProxyOptions() {
124+
return proxyOptions(Optional.empty());
125+
}
126+
95127
public abstract ClientOptions build();
96128
}
97129

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
/*
2+
* Copyright 2025 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
// Auto-generated code. Do not edit.
18+
19+
package com.google.genai.types;
20+
21+
import com.fasterxml.jackson.annotation.JsonCreator;
22+
import com.fasterxml.jackson.annotation.JsonProperty;
23+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
24+
import com.google.auto.value.AutoValue;
25+
import com.google.errorprone.annotations.CanIgnoreReturnValue;
26+
import com.google.genai.JsonSerializable;
27+
import java.util.Optional;
28+
29+
/** Proxy configuration for the client. */
30+
@AutoValue
31+
@JsonDeserialize(builder = ProxyOptions.Builder.class)
32+
public abstract class ProxyOptions extends JsonSerializable {
33+
/** Proxy type. Defaults to HTTP if unspecified. */
34+
@JsonProperty("type")
35+
public abstract Optional<ProxyType> type();
36+
37+
/** Proxy server hostname or IP address. */
38+
@JsonProperty("host")
39+
public abstract Optional<String> host();
40+
41+
/** Proxy server port. */
42+
@JsonProperty("port")
43+
public abstract Optional<Integer> port();
44+
45+
/** Username for proxy authentication. If provided, `password` must also be specified. */
46+
@JsonProperty("username")
47+
public abstract Optional<String> username();
48+
49+
/** Password for proxy authentication. If provided, `username` must also be specified. */
50+
@JsonProperty("password")
51+
public abstract Optional<String> password();
52+
53+
/** Instantiates a builder for ProxyOptions. */
54+
@ExcludeFromGeneratedCoverageReport
55+
public static Builder builder() {
56+
return new AutoValue_ProxyOptions.Builder();
57+
}
58+
59+
/** Creates a builder with the same values as this instance. */
60+
public abstract Builder toBuilder();
61+
62+
/** Builder for ProxyOptions. */
63+
@AutoValue.Builder
64+
public abstract static class Builder {
65+
/** For internal usage. Please use `ProxyOptions.builder()` for instantiation. */
66+
@JsonCreator
67+
private static Builder create() {
68+
return new AutoValue_ProxyOptions.Builder();
69+
}
70+
71+
/**
72+
* Setter for type.
73+
*
74+
* <p>type: Proxy type. Defaults to HTTP if unspecified.
75+
*/
76+
@JsonProperty("type")
77+
public abstract Builder type(ProxyType type);
78+
79+
@ExcludeFromGeneratedCoverageReport
80+
abstract Builder type(Optional<ProxyType> type);
81+
82+
/** Clears the value of type field. */
83+
@ExcludeFromGeneratedCoverageReport
84+
@CanIgnoreReturnValue
85+
public Builder clearType() {
86+
return type(Optional.empty());
87+
}
88+
89+
/**
90+
* Setter for type given a known enum.
91+
*
92+
* <p>type: Proxy type. Defaults to HTTP if unspecified.
93+
*/
94+
@CanIgnoreReturnValue
95+
public Builder type(ProxyType.Known knownType) {
96+
return type(new ProxyType(knownType));
97+
}
98+
99+
/**
100+
* Setter for type given a string.
101+
*
102+
* <p>type: Proxy type. Defaults to HTTP if unspecified.
103+
*/
104+
@CanIgnoreReturnValue
105+
public Builder type(String type) {
106+
return type(new ProxyType(type));
107+
}
108+
109+
/**
110+
* Setter for host.
111+
*
112+
* <p>host: Proxy server hostname or IP address.
113+
*/
114+
@JsonProperty("host")
115+
public abstract Builder host(String host);
116+
117+
@ExcludeFromGeneratedCoverageReport
118+
abstract Builder host(Optional<String> host);
119+
120+
/** Clears the value of host field. */
121+
@ExcludeFromGeneratedCoverageReport
122+
@CanIgnoreReturnValue
123+
public Builder clearHost() {
124+
return host(Optional.empty());
125+
}
126+
127+
/**
128+
* Setter for port.
129+
*
130+
* <p>port: Proxy server port.
131+
*/
132+
@JsonProperty("port")
133+
public abstract Builder port(Integer port);
134+
135+
@ExcludeFromGeneratedCoverageReport
136+
abstract Builder port(Optional<Integer> port);
137+
138+
/** Clears the value of port field. */
139+
@ExcludeFromGeneratedCoverageReport
140+
@CanIgnoreReturnValue
141+
public Builder clearPort() {
142+
return port(Optional.empty());
143+
}
144+
145+
/**
146+
* Setter for username.
147+
*
148+
* <p>username: Username for proxy authentication. If provided, `password` must also be
149+
* specified.
150+
*/
151+
@JsonProperty("username")
152+
public abstract Builder username(String username);
153+
154+
@ExcludeFromGeneratedCoverageReport
155+
abstract Builder username(Optional<String> username);
156+
157+
/** Clears the value of username field. */
158+
@ExcludeFromGeneratedCoverageReport
159+
@CanIgnoreReturnValue
160+
public Builder clearUsername() {
161+
return username(Optional.empty());
162+
}
163+
164+
/**
165+
* Setter for password.
166+
*
167+
* <p>password: Password for proxy authentication. If provided, `username` must also be
168+
* specified.
169+
*/
170+
@JsonProperty("password")
171+
public abstract Builder password(String password);
172+
173+
@ExcludeFromGeneratedCoverageReport
174+
abstract Builder password(Optional<String> password);
175+
176+
/** Clears the value of password field. */
177+
@ExcludeFromGeneratedCoverageReport
178+
@CanIgnoreReturnValue
179+
public Builder clearPassword() {
180+
return password(Optional.empty());
181+
}
182+
183+
public abstract ProxyOptions build();
184+
}
185+
186+
/** Deserializes a JSON string to a ProxyOptions object. */
187+
@ExcludeFromGeneratedCoverageReport
188+
public static ProxyOptions fromJson(String jsonString) {
189+
return JsonSerializable.fromJsonString(jsonString, ProxyOptions.class);
190+
}
191+
}

0 commit comments

Comments
 (0)