Skip to content

Conversation

manuelsblanco
Copy link
Contributor

@manuelsblanco manuelsblanco commented Oct 11, 2025

User description

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.

🔗 Related Issues

Addresses the need for leveraging native Java 11 HTTP client capabilities in Selenium's HTTP infrastructure.

💥 What does this PR do?

This PR adds two new methods to the HttpClient interface that expose the native Java 11 java.net.http.HttpClient functionality:

  • sendAsyncNative(): Enables asynchronous HTTP requests using the native CompletableFuture API with full access to Java 11's HTTP/2 support and advanced BodyHandlers
  • sendNative(): Provides synchronous HTTP requests with native error handling and streaming capabilities

The implementation maintains 100% backward compatibility while offering developers a modern alternative that can:

  • Handle large file uploads/downloads efficiently through streaming
  • Leverage HTTP/2 multiplexing for better performance
  • Use native async patterns with CompletableFuture
  • Access advanced response handling options (String, File, Stream, Lines, Discarding)

🔧 Implementation Notes

Design Decisions:

  • Interface Extension: Added abstract methods to HttpClient interface requiring all implementations to provide native method support
  • JdkHttpClient Integration: The main implementation delegates directly to the underlying java.net.http.HttpClient for maximum performance and feature parity
  • Graceful Degradation: Test and utility classes throw UnsupportedOperationException for the native methods, clearly indicating they're not intended for production HTTP operations
  • Type Safety: Methods use java.net.http.HttpRequest and java.net.http.HttpResponse<String> directly to maintain type safety and IDE support

Alternative Approaches Considered:

  1. Default Methods: Could have provided default implementations, but this would miss the performance benefits of native delegation
  2. Separate Interface: Could have created a new interface, but this would fragment the API and reduce discoverability
  3. Factory Pattern: Could have used a factory for native clients, but this adds complexity without clear benefits

Follow-on Work:

  • Consider adding examples in documentation showing when to use native vs. traditional methods
  • Potential future enhancement: Add support for other BodyHandler types beyond String
  • Monitor usage patterns to see if additional native HTTP features should be exposed

Migration Path:

  • Existing code continues to work unchanged
  • Developers can gradually adopt native methods for performance-critical operations
  • Clear separation between Selenium's abstraction layer and native HTTP functionality

🔄 Types of changes

  • New feature (non-breaking change which adds functionality and tests!)

Files Modified:

  • HttpClient.java - Added new method signatures
  • JdkHttpClient.java - Implemented native delegation
  • TracedHttpClient.java - Added pass-through implementations
  • RemoteWebDriverBuilder.java - Added stub implementations
  • RoutableHttpClientFactory.java - Added stub implementations
  • ProtocolHandshakeTest.java - Added stub implementations
  • PassthroughHttpClient.java - Added stub implementations
  • NativeHttpClientMethodsTest.java - New comprehensive test suite

Test Coverage:

  • Unit tests for both synchronous and asynchronous operations
  • Error handling validation
  • Request parameter validation
  • BodyHandler variations
  • Mock implementations for reliable testing

PR Type

Enhancement


Description

  • Add native Java 11 HTTP client methods to HttpClient interface

  • Implement sendAsyncNative() and sendNative() methods for HTTP/2 support

  • Maintain backward compatibility with existing HTTP client implementations

  • Add comprehensive unit tests for new native HTTP functionality


Diagram Walkthrough

flowchart LR
  A["HttpClient Interface"] --> B["Add sendAsyncNative()"]
  A --> C["Add sendNative()"]
  B --> D["JdkHttpClient Implementation"]
  C --> D
  D --> E["Native java.net.http.HttpClient"]
  F["Test Classes"] --> G["UnsupportedOperationException"]
  H["TracedHttpClient"] --> I["Delegate to underlying client"]
Loading

File Walkthrough

Relevant files
Enhancement
HttpClient.java
Add native HTTP client method signatures                                 

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

  • Add sendAsyncNative() method returning CompletableFuture>
  • Add sendNative() method for synchronous HTTP requests
  • Both methods use java.net.http types for native HTTP/2 support
+8/-0     
JdkHttpClient.java
Implement native HTTP methods in JdkHttpClient                     

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

  • Implement sendAsyncNative() delegating to underlying
    java.net.http.HttpClient
  • Implement sendNative() with direct delegation for synchronous requests
  • Enable native HTTP/2 features and performance optimizations
+13/-0   
TracedHttpClient.java
Add native method delegation in TracedHttpClient                 

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

  • Add pass-through implementations for sendAsyncNative() and
    sendNative()
  • Delegate native method calls to underlying HttpClient implementation
+13/-0   
RoutableHttpClientFactory.java
Add unsupported native method stubs                                           

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

  • Add UnsupportedOperationException stubs for native HTTP methods
  • Maintain interface compliance while indicating unsupported operations
+16/-0   
RemoteWebDriverBuilder.java
Add native method stubs in RemoteWebDriverBuilder               

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

  • Add UnsupportedOperationException implementations for native methods
  • Ensure interface compliance in anonymous HttpClient implementation
+16/-0   
Tests
PassthroughHttpClient.java
Add native method stubs in test utility                                   

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

  • Add UnsupportedOperationException for sendAsyncNative() and
    sendNative()
  • Maintain test utility functionality while supporting new interface
+13/-0   
ProtocolHandshakeTest.java
Update test HttpClient with native method stubs                   

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

  • Add UnsupportedOperationException implementations for native HTTP
    methods
  • Update test HttpClient implementation to comply with new interface
+15/-0   
NativeHttpClientMethodsTest.java
Add comprehensive native HTTP method tests                             

java/test/org/openqa/selenium/remote/http/NativeHttpClientMethodsTest.java

  • Create comprehensive unit tests for native HTTP methods
  • Test synchronous and asynchronous operations with mock responses
  • Validate error handling, request parameters, and BodyHandler
    variations
  • Include test HttpClient implementation with configurable failure
    simulation
+298/-0 

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.
@selenium-ci selenium-ci added B-grid Everything grid and server related C-java Java Bindings labels Oct 11, 2025
Copy link
Contributor

qodo-merge-pro bot commented Oct 11, 2025

PR Compliance Guide 🔍

Below is a summary of compliance checks for this PR:

Security Compliance
🟢
No security concerns identified No security vulnerabilities detected by AI analysis. Human verification advised for critical code.
Ticket Compliance
🎫 No ticket provided
  • Create ticket/issue
Codebase Duplication Compliance
Codebase context is not defined

Follow the guide to enable codebase context checks.

Custom Compliance
No custom compliance provided

Follow the guide to enable custom compliance check.

  • Update
Compliance status legend 🟢 - Fully Compliant
🟡 - Partial Compliant
🔴 - Not Compliant
⚪ - Requires Further Human Verification
🏷️ - Compliance label

Copy link
Contributor

qodo-merge-pro bot commented Oct 11, 2025

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Generalize native methods for flexible BodyHandlers

The new sendNative and sendAsyncNative methods should be generic () to support
any HttpResponse.BodyHandler. This would allow handling various response types
like files or streams, not just strings.

Examples:

java/src/org/openqa/selenium/remote/http/HttpClient.java [43-48]
  CompletableFuture<java.net.http.HttpResponse<String>> sendAsyncNative(
      java.net.http.HttpRequest request, java.net.http.HttpResponse.BodyHandler<String> handler);

  java.net.http.HttpResponse<String> sendNative(
      java.net.http.HttpRequest request, java.net.http.HttpResponse.BodyHandler<String> handler)
      throws java.io.IOException, InterruptedException;
java/src/org/openqa/selenium/remote/http/jdk/JdkHttpClient.java [513-523]
  public CompletableFuture<java.net.http.HttpResponse<String>> sendAsyncNative(
      java.net.http.HttpRequest request, java.net.http.HttpResponse.BodyHandler<String> handler) {
    return client.sendAsync(request, handler);
  }

  @Override
  public java.net.http.HttpResponse<String> sendNative(
      java.net.http.HttpRequest request, java.net.http.HttpResponse.BodyHandler<String> handler)
      throws IOException, InterruptedException {
    return client.send(request, handler);

 ... (clipped 1 lines)

Solution Walkthrough:

Before:

public interface HttpClient extends HttpHandler {
  // ...
  
  CompletableFuture<java.net.http.HttpResponse<String>> sendAsyncNative(
      java.net.http.HttpRequest request, 
      java.net.http.HttpResponse.BodyHandler<String> handler);

  java.net.http.HttpResponse<String> sendNative(
      java.net.http.HttpRequest request, 
      java.net.http.HttpResponse.BodyHandler<String> handler)
      throws java.io.IOException, InterruptedException;
  
  // ...
}

After:

public interface HttpClient extends HttpHandler {
  // ...

  <T> CompletableFuture<java.net.http.HttpResponse<T>> sendAsyncNative(
      java.net.http.HttpRequest request, 
      java.net.http.HttpResponse.BodyHandler<T> handler);

  <T> java.net.http.HttpResponse<T> sendNative(
      java.net.http.HttpRequest request, 
      java.net.http.HttpResponse.BodyHandler<T> handler)
      throws java.io.IOException, InterruptedException;

  // ...
}
Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a major design limitation where the new methods are hardcoded to String responses, which contradicts the PR's goal of providing flexible response handling and fails to expose the full power of the native Java 11 HTTP client.

High
Possible issue
Add tracing to new methods

Add tracing logic to the sendAsyncNative and sendNative methods in
TracedHttpClient to ensure all HTTP operations are correctly traced.

java/src/org/openqa/selenium/remote/tracing/TracedHttpClient.java [60-71]

 @Override
 public java.util.concurrent.CompletableFuture<java.net.http.HttpResponse<String>> sendAsyncNative(
     java.net.http.HttpRequest request, java.net.http.HttpResponse.BodyHandler<String> handler) {
-  return delegate.sendAsyncNative(request, handler);
+  Span span = tracer.getCurrentContext().createSpan("http.sendAsyncNative");
+  KIND.accept(span, Span.Kind.CLIENT);
+  span.setAttribute("http.method", request.method());
+  span.setAttribute("http.url", request.uri().toString());
+
+  return delegate
+      .sendAsyncNative(request, handler)
+      .whenComplete(
+          (response, throwable) -> {
+            if (throwable != null) {
+              span.setAttribute("error", true);
+              span.setStatus(Status.UNKNOWN.withDescription(throwable.getMessage()));
+            }
+            if (response != null) {
+              span.setAttribute("http.status_code", response.statusCode());
+            }
+            span.close();
+          });
 }
 
 @Override
 public java.net.http.HttpResponse<String> sendNative(
     java.net.http.HttpRequest request, java.net.http.HttpResponse.BodyHandler<String> handler)
     throws java.io.IOException, InterruptedException {
-  return delegate.sendNative(request, handler);
+  Span span = tracer.getCurrentContext().createSpan("http.sendNative");
+  KIND.accept(span, Span.Kind.CLIENT);
+  span.setAttribute("http.method", request.method());
+  span.setAttribute("http.url", request.uri().toString());
+
+  try {
+    java.net.http.HttpResponse<String> response = delegate.sendNative(request, handler);
+    span.setAttribute("http.status_code", response.statusCode());
+    return response;
+  } catch (Throwable t) {
+    span.setAttribute("error", true);
+    span.setStatus(Status.UNKNOWN.withDescription(t.getMessage()));
+    throw t;
+  } finally {
+    span.close();
+  }
 }
  • Apply / Chat
Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies that the new native HTTP methods in TracedHttpClient are missing tracing logic, which is a significant functional omission that undermines the purpose of this class.

High
General
Add failure simulation to test

Implement failure simulation in the sendAsyncNative method of TestHttpClient by
checking the shouldFail flag and returning a failed CompletableFuture to enable
testing of asynchronous error handling.

java/test/org/openqa/selenium/remote/http/NativeHttpClientMethodsTest.java [193-242]

 @Override
 public CompletableFuture<java.net.http.HttpResponse<String>> sendAsyncNative(
     java.net.http.HttpRequest request, java.net.http.HttpResponse.BodyHandler<String> handler) {
+
+  if (shouldFail) {
+    return CompletableFuture.failedFuture(new IOException("Simulated network error"));
+  }
 
   // Create a mock response for testing asynchronous behavior
   java.net.http.HttpResponse<String> mockResponse =
       new java.net.http.HttpResponse<>() {
         @Override
         public int statusCode() {
           return 200;
         }
         ...
       };
 
   return CompletableFuture.completedFuture(mockResponse);
 }

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 6

__

Why: The suggestion correctly points out an inconsistency in the test helper class TestHttpClient, where the asynchronous method sendAsyncNative lacks failure simulation, limiting test coverage for error handling scenarios.

Low
Learned
best practice
Cancel in-flight async requests

Also track and cancel outstanding native async requests on close to avoid leaks;
maintain a concurrent set of futures and cancel them in close().

java/src/org/openqa/selenium/remote/http/jdk/JdkHttpClient.java [525-537]

+// Field (near other fields)
+private final java.util.Set<java.util.concurrent.CompletableFuture<java.net.http.HttpResponse<String>>> inFlight =
+    java.util.Collections.newSetFromMap(new java.util.concurrent.ConcurrentHashMap<>());
+
+@Override
+public CompletableFuture<java.net.http.HttpResponse<String>> sendAsyncNative(
+    java.net.http.HttpRequest request, java.net.http.HttpResponse.BodyHandler<String> handler) {
+  CompletableFuture<java.net.http.HttpResponse<String>> future = client.sendAsync(request, handler);
+  inFlight.add(future);
+  future.whenComplete((r, t) -> inFlight.remove(future));
+  return future;
+}
+
 @Override
 public void close() {
   if (this.client == null) {
     return;
   }
 
   for (WebSocket websocket : websockets) {
     try {
       websocket.close();
     } catch (Exception e) {
       LOG.log(Level.WARNING, "failed to close the websocket: " + websocket, e);
     }
   }
   websockets.clear();
+
+  // cancel any in-flight async calls
+  for (var f : inFlight) {
+    try {
+      f.cancel(true);
+    } catch (Exception ignore) {
+    }
+  }
+  inFlight.clear();
 }

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 6

__

Why:
Relevant best practice - Enforce deterministic cleanup by closing resources and canceling in-flight async operations during client shutdown.

Low
Safely handle async future

Ensure the async future is completed and closed/cancelled on all paths to avoid
resource leaks, using try/finally and cancel if not needed after assertions.

java/test/org/openqa/selenium/remote/http/NativeHttpClientMethodsTest.java [67-75]

 CompletableFuture<HttpResponse<String>> result =
     httpClient.sendAsyncNative(testRequest, HttpResponse.BodyHandlers.ofString());
+try {
+  assertNotNull(result, "Future should not be null");
+  HttpResponse<String> response = result.get(5, TimeUnit.SECONDS);
+  assertNotNull(response, "Response should not be null");
+  assertThat(response.statusCode()).isEqualTo(200);
+  assertThat(response.body()).isEqualTo("Test response body");
+} finally {
+  // ensure the future does not leak if still running
+  if (result != null && !result.isDone()) {
+    result.cancel(true);
+  }
+}
 
-// Assert - Verify the asynchronous response is correct
-assertNotNull(result, "Future should not be null");
-HttpResponse<String> response = result.get(5, TimeUnit.SECONDS);
-assertNotNull(response, "Response should not be null");
-assertThat(response.statusCode()).isEqualTo(200);
-assertThat(response.body()).isEqualTo("Test response body");
-
  • Apply / Chat
Suggestion importance[1-10]: 5

__

Why:
Relevant best practice - Enforce deterministic and safe resource handling in tests by closing/disposing resources via try/finally or context to prevent leaks.

Low
  • Update

…terface

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
@cgoldberg cgoldberg changed the title feat: Add native Java 11 HTTP client methods to HttpClient interface [Java] feat: Add native Java 11 HTTP client methods to HttpClient interface Oct 12, 2025
@cgoldberg cgoldberg changed the title [Java] feat: Add native Java 11 HTTP client methods to HttpClient interface [java] feat: Add native Java 11 HTTP client methods to HttpClient interface Oct 12, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

B-grid Everything grid and server related C-java Java Bindings Review effort 2/5

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants