Check of Java HTTP Client Limitations with HTTP/2, GOAWAY frames, HTTP Compression, HTTP Caching, and HTTP Authentication
This project tests Java 11+ HTTP Client behavior with HTTP/2 protocol features, HTTP compression, HTTP caching, and HTTP authentication, specifically:
- HTTP/2 Upgrade from HTTP/1.1
- GOAWAY frame handling
- Connection management over HTTP and HTTPS
- ALPN negotiation and custom SSLParameters behavior
- HTTP compression support (or lack thereof)
- HTTP caching support (or lack thereof)
- HTTP authentication schemes support (Basic, Digest, NTLM, SPNEGO/Kerberos)
Tests the HTTP/2 upgrade mechanism where the client starts with HTTP/1.1 and upgrades to HTTP/2 using the Upgrade header (h2c - cleartext HTTP/2).
This is performed in JavaHttpClientUpgradeTest.testHttp2UpgradeOverHttp()
Tests how the Java HTTP Client handles GOAWAY frames sent by the server, which signal connection shutdown/timeout see https://datatracker.ietf.org/doc/html/rfc7540#section-6.8
Tests that connections receiving GOAWAY frames are not reused from the connection pool. Includes:
- Connection tracking with nonces to verify connection reuse
- Marking connections as stale and triggering GOAWAY
- Verifying that new connections are opened after GOAWAY
- Testing parallel requests with connection pooling
Tests are in JavaHttpClientConnectionReuseTest:
testConnectionReuseWithNonce()- Verifies basic connection reusetestGoawayOnStaleConnectionReuse()- Tests GOAWAY behaviortestParallelRequestsWithConnectionPooling()- Tests parallel requests
Tests the behavior when custom SSLParameters are provided to the HttpClient. See the Known Limitations section below for important findings.
Tests the behavior when connecting to an HTTPS server that does not support ALPN protocol negotiation.
Tests WebSocket connection support with Java HttpClient. Related to issue JDK-8361305.
Tests are in JavaHttpClientWebSocketTest:
testDirectWebSocketConnection()- Verifies direct WebSocket connection (ws:// URI) workstestWebSocketUpgradeAfterHttp2()- Tests WebSocket connection after HTTP connection to same host
The tests demonstrate that:
- Direct WebSocket connections using
HttpClient.newWebSocketBuilder()work correctly onws://URIs - WebSocket upgrade behavior when HTTP/2 connections already exist to the same host
Tests demonstrating that Java HTTP Client does NOT support transparent HTTP compression out of the box. See HTTP_COMPRESSION.md for detailed documentation.
Tests are in JavaHttpClientCompressionTest:
testNoAutomaticAcceptEncodingHeader()- Verifies client does NOT add Accept-Encoding automatically (HTTP)testNoAutomaticDecompression()- Verifies client does NOT decompress gzip responses automaticallytestManualCompressionRequired()- Demonstrates manual implementation required for compressiontestHttpsNoAutomaticAcceptEncodingHeader()- Verifies same behavior for HTTPStestHttpsManualCompression()- Shows manual compression works with HTTPS
The tests demonstrate that:
- Java HTTP Client does NOT automatically add
Accept-Encodingheader - Java HTTP Client does NOT automatically decompress compressed responses
- Applications must manually implement both request headers and response decompression
- Both HTTP and HTTPS behave identically regarding compression
Tests demonstrating that Java HTTP Client does NOT support HTTP caching out of the box. See HTTP_CACHING.md for detailed documentation.
Tests are in JavaHttpClientCachingTest:
testNoCacheControlHandling()- Verifies client does NOT handle Cache-Control headers automaticallytestNoAutomaticETagHandling()- Verifies client does NOT use ETags for conditional requeststestManualIfNoneMatchRequired()- Demonstrates manual If-None-Match header requiredtestNo304Handling()- Verifies client does NOT handle 304 Not Modified transparentlytestManualCachingRequired()- Shows complex manual implementation requiredtestHttpsNoCaching()- Verifies same behavior for HTTPS
The tests demonstrate that:
- Java HTTP Client does NOT automatically handle Cache-Control headers
- Java HTTP Client does NOT automatically use ETags for conditional requests
- Java HTTP Client does NOT handle 304 Not Modified responses transparently
- Applications must manually implement complete caching lifecycle
- Both HTTP and HTTPS behave identically regarding caching
Tests evaluating support for common HTTP authentication schemes. See HTTP_AUTHENTICATION.md for detailed documentation.
Tests are in JavaHttpClientAuthenticationTest:
testBasicAuthenticationNativeSupport()- Verifies native Basic auth via AuthenticatortestBasicAuthenticationManualImplementation()- Tests manual Basic auth implementationtestBasicAuthenticationHttps()- Tests Basic auth over HTTPStestBasicAuthenticationChallengeResponse()- Tests challenge-response flowtestDigestAuthenticationNativeSupport()- Tests Digest auth support (limited)testDigestAuthenticationManualImplementation()- Shows Digest implementation complexitytestNTLMAuthenticationNativeSupport()- Tests NTLM support (not supported)testNTLMAuthenticationManualImplementation()- Shows NTLM implementation complexitytestSPNEGOAuthenticationNativeSupport()- Tests SPNEGO/Kerberos (limited via JGSS)testSPNEGOAuthenticationManualImplementation()- Shows SPNEGO implementation requirementstestBearerTokenAuthentication()- Tests Bearer token (OAuth 2.0) implementationtestAuthenticationSchemeSummary()- Displays comprehensive support matrix
The tests demonstrate that:
- HTTP Basic: Fully supported natively via
java.net.Authenticator - HTTP Digest: Limited native support, varies by JDK version
- NTLM: Not supported natively, requires third-party libraries (Apache HttpClient + JCIFS)
- SPNEGO/Kerberos: Not supported via Authenticator, can use Java GSS-API with infrastructure
- Bearer Token (OAuth 2.0): Not supported natively, requires manual implementation
See GITHUB_ISSUE_SUMMARY.md for a concise summary suitable for submitting as a JDK enhancement request.
Issue: When you configure a Java HttpClient with custom SSLParameters using .sslParameters(), it overrides the client's internal ALPN configuration, preventing HTTP/2 negotiation.
Behavior:
- Even if you explicitly set ALPN protocols:
sslParameters.setApplicationProtocols(new String[] {"h2", "http/1.1"}) - The connection will fall back to HTTP/1.1 over TLS
- This happens because custom
SSLParametersoverride theHttpClient's internal ALPN configuration
Solution: Do NOT use .sslParameters() if you need HTTP/2 over HTTPS. Instead, let the HttpClient manage ALPN automatically by only setting:
HttpClient client = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_2)
.sslContext(customSslContext) // OK - only custom trust settings
// .sslParameters(...) // AVOID - breaks HTTP/2 ALPN
.build();Tests demonstrating this:
JavaHttpClientUpgradeTest.testHttp2WithCustomSSLParametersALPNAttempt()JavaHttpClientUpgradeTest.testHttp2WithCustomSSLParametersNoALPN()
Issue: Unlike cleartext HTTP which supports HTTP/2 upgrade using the Upgrade header, HTTPS connections require ALPN (Application-Layer Protocol Negotiation) for HTTP/2.
Behavior:
- Per RFC 7540, HTTP/2 over TLS requires ALPN during the TLS handshake
- There is NO upgrade mechanism from HTTP/1.1 to HTTP/2 over an existing HTTPS/TLS connection
- If the HTTPS server does not support ALPN, the connection will use HTTP/1.1
- If ALPN negotiation fails or is not configured, there is no fallback upgrade path
Tests demonstrating this:
JavaHttpClientUpgradeTest.testHttpsServerWithoutALPN()
- Java 11 or higher
- Maven 3.6 or higher
mvn clean compileRun all tests:
mvn testRun with verbose logging:
mvn test -Dorg.slf4j.simpleLogger.defaultLogLevel=debug├── src/main/java/io/github/laeubi/httpclient/
│ ├── NettyHttp2Server.java # Netty-based HTTP/2 test server with connection tracking
│ ├── NettyWebSocketServer.java # Netty-based WebSocket test server
│ └── NettyAuthenticationServer.java # Netty-based authentication test server
├── src/test/java/io/github/laeubi/httpclient/
│ ├── JavaHttpClientUpgradeTest.java # HTTP/2 upgrade and ALPN tests
│ ├── JavaHttpClientGoawayTest.java # GOAWAY frame handling tests
│ ├── JavaHttpClientConnectionReuseTest.java # Connection reuse and GOAWAY tests
│ ├── JavaHttpClientWebSocketTest.java # WebSocket connection tests
│ ├── JavaHttpClientCompressionTest.java # HTTP compression tests
│ ├── JavaHttpClientCachingTest.java # HTTP caching tests
│ ├── JavaHttpClientAuthenticationTest.java # HTTP authentication tests
│ └── JavaHttpClientBase.java # Base test class with utilities
├── src/main/resources/
│ └── simplelogger.properties # Logging configuration
├── .github/workflows/
│ └── ci.yml # GitHub Actions workflow
├── HTTP_COMPRESSION.md # HTTP compression documentation
├── HTTP_CACHING.md # HTTP caching documentation
├── HTTP_AUTHENTICATION.md # HTTP authentication documentation
├── GITHUB_ISSUE_SUMMARY.md # JDK enhancement request summary
└── pom.xml # Maven project configuration
The project uses SLF4J with the Simple Logger implementation. Log levels can be adjusted in src/main/resources/simplelogger.properties or via system properties:
mvn test -Dorg.slf4j.simpleLogger.log.io.github.laeubi.httpclient=debugThe project includes GitHub Actions workflows that run tests on multiple Java versions:
- Java 17.0.17+
- Java 21.0.8+
See .github/workflows/ci.yml for the complete CI configuration.