Skip to content

Commit 4140a0f

Browse files
[java] feat: Add native Java 11 HTTP client methods to HttpClient interface (#16412)
* feat: Add native Java 11 HTTP client methods to HttpClient interface Add sendAsyncNative() and sendNative() methods to leverage java.net.http.HttpClient capabilities while maintaining backward compatibility with existing executeAsync(). ## New Methods Added: - sendAsyncNative(): Asynchronous HTTP requests using CompletableFuture - sendNative(): Synchronous HTTP requests with native error handling ## Benefits: - HTTP/2 support with automatic protocol negotiation and multiplexing - Efficient streaming for large files without memory overhead - Native async operations with CompletableFuture integration - Flexible response handling via BodyHandler (String, File, Stream, Lines) - Better error handling with specific HTTP exceptions (IOException, InterruptedException) - Improved performance for concurrent requests ## Implementation Details: - Added method signatures to HttpClient interface - Implemented native delegation in JdkHttpClient using underlying java.net.http.HttpClient - Added pass-through implementations in TracedHttpClient - Added UnsupportedOperationException stubs in test classes and utility clients - Maintained full backward compatibility with existing methods ## Testing: - Created comprehensive unit tests (NativeHttpClientMethodsTest) - Tests cover both synchronous and asynchronous operations - Exception handling validation for IOException and InterruptedException - Request parameter validation for different HTTP methods (GET, POST) - BodyHandler variations testing (String, Void, Stream) - Mock implementations for reliable testing without external dependencies The new methods provide a migration path towards modern Java 11 HTTP APIs without breaking current implementations, enabling developers to leverage native HTTP/2 features, better async handling, and improved performance when using Selenium's HTTP client infrastructure. * feat: Add generic native Java 11 HTTP client methods to HttpClient interface Add sendAsyncNative() and sendNative() generic methods to leverage java.net.http.HttpClient capabilities with full BodyHandler flexibility while maintaining backward compatibility. ## New Methods Added: - <T> sendAsyncNative(): Generic asynchronous HTTP requests using CompletableFuture - <T> sendNative(): Generic synchronous HTTP requests with native error handling ## Key Features: - **Full BodyHandler Support**: String, File, Stream, Lines, ByteArray, Void (discarding) - **HTTP/2 Support**: Automatic protocol negotiation and multiplexing - **Efficient Streaming**: Large files without memory overhead via BodyHandlers.ofFile() - **Native Async Operations**: CompletableFuture integration with proper type safety - **Flexible Response Handling**: Type-safe responses based on BodyHandler type - **Better Error Handling**: Specific HTTP exceptions (IOException, InterruptedException) - **Improved Performance**: Concurrent requests and HTTP/2 optimizations ## Implementation Details: - Added generic method signatures to HttpClient interface with proper JavaDoc - Implemented native delegation in JdkHttpClient using underlying java.net.http.HttpClient - Added pass-through generic implementations in TracedHttpClient - Added UnsupportedOperationException stubs in test classes and utility clients - Maintained full backward compatibility with existing executeAsync() method - Enhanced type safety with proper generic constraints --------- Co-authored-by: Diego Molina <[email protected]>
1 parent b0dade7 commit 4140a0f

File tree

8 files changed

+413
-0
lines changed

8 files changed

+413
-0
lines changed

java/src/org/openqa/selenium/grid/web/RoutableHttpClientFactory.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,21 @@ public HttpResponse execute(HttpRequest request) throws UncheckedIOException {
7575
public WebSocket openSocket(HttpRequest request, WebSocket.Listener listener) {
7676
throw new UnsupportedOperationException("openSocket");
7777
}
78+
79+
@Override
80+
public <T>
81+
java.util.concurrent.CompletableFuture<java.net.http.HttpResponse<T>> sendAsyncNative(
82+
java.net.http.HttpRequest request,
83+
java.net.http.HttpResponse.BodyHandler<T> handler) {
84+
throw new UnsupportedOperationException("sendAsyncNative is not supported");
85+
}
86+
87+
@Override
88+
public <T> java.net.http.HttpResponse<T> sendNative(
89+
java.net.http.HttpRequest request, java.net.http.HttpResponse.BodyHandler<T> handler)
90+
throws java.io.IOException, InterruptedException {
91+
throw new UnsupportedOperationException("sendNative is not supported");
92+
}
7893
};
7994
}
8095

java/src/org/openqa/selenium/remote/RemoteWebDriverBuilder.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,23 @@ public WebSocket openSocket(HttpRequest request, WebSocket.Listener listener) {
297297
public HttpResponse execute(HttpRequest req) throws UncheckedIOException {
298298
return handler.execute(req);
299299
}
300+
301+
@Override
302+
public <T>
303+
java.util.concurrent.CompletableFuture<java.net.http.HttpResponse<T>>
304+
sendAsyncNative(
305+
java.net.http.HttpRequest request,
306+
java.net.http.HttpResponse.BodyHandler<T> handler) {
307+
throw new UnsupportedOperationException("sendAsyncNative is not supported");
308+
}
309+
310+
@Override
311+
public <T> java.net.http.HttpResponse<T> sendNative(
312+
java.net.http.HttpRequest request,
313+
java.net.http.HttpResponse.BodyHandler<T> handler)
314+
throws java.io.IOException, InterruptedException {
315+
throw new UnsupportedOperationException("sendNative is not supported");
316+
}
300317
};
301318
}
302319
};

java/src/org/openqa/selenium/remote/http/HttpClient.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,31 @@ default CompletableFuture<HttpResponse> executeAsync(HttpRequest req) {
3939

4040
default void close() {}
4141

42+
/**
43+
* Sends an HTTP request using java.net.http.HttpClient and allows specifying the BodyHandler.
44+
*
45+
* @param <T> the response body type
46+
* @param request the HTTP request to send
47+
* @param handler the BodyHandler that determines how to handle the response body
48+
* @return a CompletableFuture containing the HTTP response
49+
*/
50+
<T> CompletableFuture<java.net.http.HttpResponse<T>> sendAsyncNative(
51+
java.net.http.HttpRequest request, java.net.http.HttpResponse.BodyHandler<T> handler);
52+
53+
/**
54+
* Sends an HTTP request using java.net.http.HttpClient and allows specifying the BodyHandler.
55+
*
56+
* @param <T> the response body type
57+
* @param request the HTTP request to send
58+
* @param handler the BodyHandler that determines how to handle the response body
59+
* @return the HTTP response
60+
* @throws java.io.IOException if an I/O error occurs
61+
* @throws InterruptedException if the operation is interrupted
62+
*/
63+
<T> java.net.http.HttpResponse<T> sendNative(
64+
java.net.http.HttpRequest request, java.net.http.HttpResponse.BodyHandler<T> handler)
65+
throws java.io.IOException, InterruptedException;
66+
4267
interface Factory {
4368

4469
/**

java/src/org/openqa/selenium/remote/http/jdk/JdkHttpClient.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,19 @@ private HttpResponse execute0(HttpRequest req) throws UncheckedIOException {
509509
}
510510
}
511511

512+
@Override
513+
public <T> CompletableFuture<java.net.http.HttpResponse<T>> sendAsyncNative(
514+
java.net.http.HttpRequest request, java.net.http.HttpResponse.BodyHandler<T> handler) {
515+
return client.sendAsync(request, handler);
516+
}
517+
518+
@Override
519+
public <T> java.net.http.HttpResponse<T> sendNative(
520+
java.net.http.HttpRequest request, java.net.http.HttpResponse.BodyHandler<T> handler)
521+
throws IOException, InterruptedException {
522+
return client.send(request, handler);
523+
}
524+
512525
@Override
513526
public void close() {
514527
if (this.client == null) {

java/src/org/openqa/selenium/remote/tracing/TracedHttpClient.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,19 @@ public HttpResponse execute(HttpRequest req) {
5757
}
5858
}
5959

60+
@Override
61+
public <T> java.util.concurrent.CompletableFuture<java.net.http.HttpResponse<T>> sendAsyncNative(
62+
java.net.http.HttpRequest request, java.net.http.HttpResponse.BodyHandler<T> handler) {
63+
return delegate.sendAsyncNative(request, handler);
64+
}
65+
66+
@Override
67+
public <T> java.net.http.HttpResponse<T> sendNative(
68+
java.net.http.HttpRequest request, java.net.http.HttpResponse.BodyHandler<T> handler)
69+
throws java.io.IOException, InterruptedException {
70+
return delegate.sendNative(request, handler);
71+
}
72+
6073
@Override
6174
public void close() {
6275
delegate.close();

java/test/org/openqa/selenium/grid/testing/PassthroughHttpClient.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,19 @@ public WebSocket openSocket(HttpRequest request, WebSocket.Listener listener) {
4848
throw new UnsupportedOperationException("openSocket");
4949
}
5050

51+
@Override
52+
public <T> java.util.concurrent.CompletableFuture<java.net.http.HttpResponse<T>> sendAsyncNative(
53+
java.net.http.HttpRequest request, java.net.http.HttpResponse.BodyHandler<T> handler) {
54+
throw new UnsupportedOperationException("sendAsyncNative");
55+
}
56+
57+
@Override
58+
public <T> java.net.http.HttpResponse<T> sendNative(
59+
java.net.http.HttpRequest request, java.net.http.HttpResponse.BodyHandler<T> handler)
60+
throws java.io.IOException, InterruptedException {
61+
throw new UnsupportedOperationException("sendNative");
62+
}
63+
5164
public static class Factory implements HttpClient.Factory {
5265

5366
private final Routable handler;

java/test/org/openqa/selenium/remote/ProtocolHandshakeTest.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,5 +194,19 @@ String getRequestPayload() {
194194
public WebSocket openSocket(HttpRequest request, WebSocket.Listener listener) {
195195
throw new UnsupportedOperationException("openSocket");
196196
}
197+
198+
@Override
199+
public <T>
200+
java.util.concurrent.CompletableFuture<java.net.http.HttpResponse<T>> sendAsyncNative(
201+
java.net.http.HttpRequest request, java.net.http.HttpResponse.BodyHandler<T> handler) {
202+
throw new UnsupportedOperationException("sendAsyncNative");
203+
}
204+
205+
@Override
206+
public <T> java.net.http.HttpResponse<T> sendNative(
207+
java.net.http.HttpRequest request, java.net.http.HttpResponse.BodyHandler<T> handler)
208+
throws java.io.IOException, InterruptedException {
209+
throw new UnsupportedOperationException("sendNative");
210+
}
197211
}
198212
}

0 commit comments

Comments
 (0)