Skip to content

Commit 2236122

Browse files
Copilotlaeubi
andauthored
Add HTTP caching documentation and test suite for JDK enhancement proposal (#18)
* Initial plan * Add HTTP caching documentation and test cases Co-authored-by: laeubi <1331477+laeubi@users.noreply.github.com> * Update README with HTTP caching test documentation Co-authored-by: laeubi <1331477+laeubi@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: laeubi <1331477+laeubi@users.noreply.github.com>
1 parent cc20165 commit 2236122

File tree

5 files changed

+1057
-66
lines changed

5 files changed

+1057
-66
lines changed

GITHUB_ISSUE_SUMMARY.md

Lines changed: 200 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
# GitHub Issue Summary: Add Transparent HTTP Compression Support to Java HTTP Client
1+
# GitHub Issue Summary: Add HTTP Caching Support to Java HTTP Client
22

33
## Title
4-
Add transparent HTTP compression support to java.net.http.HttpClient
4+
Add HTTP caching support to java.net.http.HttpClient
55

66
## Component
77
core-libs/java.net
@@ -11,107 +11,251 @@ Enhancement
1111

1212
## Summary
1313

14-
The Java HTTP Client (java.net.http.HttpClient) does not support transparent HTTP compression, requiring applications to manually implement both request header management and response decompression. This enhancement request proposes adding automatic compression support similar to other modern HTTP clients.
14+
The Java HTTP Client (java.net.http.HttpClient) does not support HTTP caching, requiring applications to manually implement cache storage, conditional requests, and response validation. This enhancement request proposes adding opt-in caching support similar to the historical java.net.ResponseCache but with a modern design aligned with current HTTP standards.
1515

1616
## Problem
1717

18-
HTTP compression is a fundamental HTTP feature providing significant benefits:
19-
- Reduced bandwidth usage (critical for mobile connections)
20-
- Faster request completion (allowing servers to handle more concurrent connections)
21-
- Better cache utilization (proxies often cache compressed content)
22-
- Improved security (fewer bytes to encrypt/decrypt)
18+
HTTP caching is a fundamental HTTP feature providing significant benefits:
19+
- Reduced server load (prevents redundant requests)
20+
- Faster response times (eliminates network round-trips)
21+
- Reduced bandwidth usage (conditional requests only transfer data when content changes)
22+
- Offline capabilities (can serve stale content when network unavailable)
23+
- Better user experience (instant responses from cache)
2324

2425
**Current limitations demonstrated by test suite:**
2526

26-
1. The HttpClient does NOT automatically add `Accept-Encoding` header to requests
27-
2. The HttpClient does NOT automatically decompress compressed responses
28-
3. Applications must manually implement both sides of compression support
27+
1. The HttpClient does NOT automatically handle `Cache-Control` headers
28+
2. The HttpClient does NOT automatically use ETags for conditional requests
29+
3. The HttpClient does NOT handle 304 Not Modified responses transparently
30+
4. Applications must manually implement complete caching lifecycle
2931

30-
This creates drawbacks:
31-
- Each application must implement compression independently
32-
- Implementation is non-trivial and error-prone
33-
- Compression is often forgotten or incompletely implemented
34-
- Transport-level compression is treated as an application concern
32+
This creates significant drawbacks:
33+
- Each application must implement caching independently
34+
- Implementation is non-trivial and error-prone (requires handling cache storage, ETags, Last-Modified, 304 responses, expiration)
35+
- Caching is often forgotten or incompletely implemented
36+
- HTTP-level caching is treated as an application concern
37+
38+
## Historical Context
39+
40+
Java has supported HTTP caching for the older java.net.URL connection mechanism since Java SE 6 via `java.net.ResponseCache`. However, this caching mechanism is **not available** for the modern `java.net.http.HttpClient` introduced in Java 11.
3541

3642
## Proposed Solution
3743

38-
Add transparent compression support with the following behavior:
44+
Add opt-in HTTP caching support with configurable behavior modes:
45+
46+
### Option 1: Fully Transparent Mode
47+
```java
48+
HttpCache cache = HttpCache.automatic();
49+
HttpClient client = HttpClient.newBuilder()
50+
.cache(cache)
51+
.build();
52+
53+
// Client automatically:
54+
// - Checks cache and returns cached content if valid
55+
// - Adds conditional headers (If-None-Match, If-Modified-Since)
56+
// - Handles 304 responses by returning cached content
57+
// - Returns content transparently as if it were a 200 response
58+
HttpResponse<String> response = client.send(request,
59+
HttpResponse.BodyHandlers.ofString());
60+
String content = response.body(); // From cache or server
61+
```
62+
63+
### Option 2: Semi-Automatic Mode
64+
```java
65+
HttpCache cache = HttpCache.semiAutomatic();
66+
HttpClient client = HttpClient.newBuilder()
67+
.cache(cache)
68+
.build();
69+
70+
// Client automatically:
71+
// - Adds conditional headers based on cached ETags/Last-Modified
72+
// - Sends request to server (does not serve from cache automatically)
73+
// - Returns actual 304 response to application for handling
74+
HttpResponse<String> response = client.send(request,
75+
HttpResponse.BodyHandlers.ofString());
76+
if (response.statusCode() == 304) {
77+
// Application retrieves cached content
78+
}
79+
```
80+
81+
### Option 3: Disabled (Default)
82+
```java
83+
// No cache configuration - current behavior
84+
HttpClient client = HttpClient.newBuilder().build();
85+
```
86+
87+
### Cache Configuration
88+
89+
Support flexible cache configuration:
90+
91+
```java
92+
// In-memory cache with size limit
93+
HttpCache cache = HttpCache.newBuilder()
94+
.maximumSize(100) // Max 100 cached responses
95+
.expireAfterWrite(Duration.ofHours(1))
96+
.build();
97+
98+
// Custom storage (e.g., disk-based)
99+
HttpCache cache = HttpCache.newBuilder()
100+
.storage(new CustomCacheStorage())
101+
.build();
102+
103+
// Shared cache across multiple HttpClient instances
104+
HttpCache sharedCache = HttpCache.shared();
105+
```
106+
107+
### Key Features
108+
109+
**Cache Control Directives:**
110+
- Respect `Cache-Control: no-store` (do not cache)
111+
- Respect `Cache-Control: no-cache` (cache but always revalidate)
112+
- Respect `Cache-Control: max-age=N` (cache for N seconds)
113+
- Support `Cache-Control: private` vs `public`
114+
115+
**Conditional Requests:**
116+
- Use `ETag` with `If-None-Match` for validation
117+
- Use `Last-Modified` with `If-Modified-Since` for validation
118+
- Handle 304 Not Modified responses appropriately
39119

40-
**Default (Transparent Mode):**
41-
- Client automatically adds `Accept-Encoding: gzip, deflate` when not explicitly set
42-
- Client automatically decompresses responses based on `Content-Encoding` header
43-
- BodyHandlers receive decompressed content transparently
120+
**Vary Header Support:**
121+
- Generate cache keys based on `Vary` header
122+
- Cache key: URI + relevant request header values
44123

45-
**Opt-Out Mechanism:**
46-
- Applications can set custom `Accept-Encoding` header to override default behavior
47-
- Setting `Accept-Encoding: identity` explicitly disables compression (per RFC 2616)
48-
- Custom Accept-Encoding values disable automatic decompression
124+
**Stale Content Handling:**
125+
- Support `stale-while-revalidate` (RFC 5861)
126+
- Return stale content while fetching fresh copy in background
127+
- Support `stale-if-error` for offline scenarios
49128

50129
## Benefits
51130

52-
- **Zero-code compression**: Applications get compression without any code changes
53-
- **Backward compatible**: Existing applications continue to work unchanged
54-
- **Standards compliant**: Follows RFC 2616 specifications
55-
- **Consistent with industry**: Matches behavior of curl, browsers, and popular HTTP libraries
131+
- **Standards compliant**: Follows RFC 7234 (HTTP Caching) and RFC 7232 (Conditional Requests)
132+
- **Flexible**: Multiple modes to suit different application needs
133+
- **Opt-in**: Backward compatible, existing applications unaffected
134+
- **Modern design**: Builder pattern, fluent API consistent with HttpClient
135+
- **Efficient**: Reduces server load and improves application performance
136+
- **Familiar**: Similar to java.net.ResponseCache but modernized
56137

57138
## Example Code
58139

59140
### Current Behavior (Manual Implementation Required)
60141
```java
61-
// Must manually add header
62-
HttpRequest request = HttpRequest.newBuilder()
142+
// Complex manual implementation required:
143+
// 1. Cache storage
144+
Map<URI, CacheEntry> cache = new HashMap<>();
145+
146+
// 2. First request
147+
HttpResponse<String> response = client.send(request,
148+
HttpResponse.BodyHandlers.ofString());
149+
String etag = response.headers().firstValue("etag").orElse(null);
150+
cache.put(uri, new CacheEntry(response.body(), etag));
151+
152+
// 3. Subsequent request - manually add conditional header
153+
HttpRequest request2 = HttpRequest.newBuilder()
63154
.uri(uri)
64-
.header("Accept-Encoding", "gzip")
155+
.header("If-None-Match", etag) // Manual!
65156
.GET()
66157
.build();
67158

68-
HttpResponse<byte[]> response = client.send(request, HttpResponse.BodyHandlers.ofByteArray());
159+
HttpResponse<String> response2 = client.send(request2,
160+
HttpResponse.BodyHandlers.ofString());
69161

70-
// Must manually check and decompress
71-
if ("gzip".equals(response.headers().firstValue("content-encoding").orElse(""))) {
72-
byte[] compressed = response.body();
73-
// ... manual decompression code (15+ lines) ...
162+
// 4. Manually handle 304
163+
if (response2.statusCode() == 304) {
164+
// Must manually retrieve from cache
165+
content = cache.get(uri).content;
166+
} else {
167+
content = response2.body();
74168
}
75169
```
76170

77171
### Proposed Behavior (Transparent)
78172
```java
79-
// No compression-specific code needed
80-
HttpRequest request = HttpRequest.newBuilder()
81-
.uri(uri)
82-
.GET()
173+
// Simple automatic caching:
174+
HttpCache cache = HttpCache.automatic();
175+
HttpClient client = HttpClient.newBuilder()
176+
.cache(cache)
83177
.build();
84178

85-
HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());
86-
String content = response.body(); // Already decompressed!
179+
HttpResponse<String> response = client.send(request,
180+
HttpResponse.BodyHandlers.ofString());
181+
String content = response.body(); // Automatically cached/validated!
87182
```
88183

89184
## Supporting Materials
90185

91-
- **Test Suite**: `JavaHttpClientCompressionTest` demonstrates current limitations (5 tests, all passing)
92-
- **Documentation**: `HTTP_COMPRESSION.md` provides detailed technical specification
93-
- **Test Server**: Enhanced `NettyHttp2Server` supports compression for testing
186+
- **Test Suite**: `JavaHttpClientCachingTest` demonstrates current limitations (6 tests, all passing)
187+
- **Documentation**: `HTTP_CACHING.md` provides detailed technical specification
188+
- **Test Server**: Enhanced `NettyHttp2Server` supports caching headers for testing
94189

95190
All tests validate that:
96-
- HttpClient does NOT add Accept-Encoding automatically (HTTP and HTTPS)
97-
- HttpClient does NOT decompress responses automatically
98-
- Manual implementation is required for compression support
191+
- HttpClient does NOT handle Cache-Control headers automatically
192+
- HttpClient does NOT use ETags for conditional requests automatically
193+
- HttpClient does NOT handle 304 responses transparently
194+
- Manual implementation is required with significant complexity
99195

100196
## References
101197

102-
- [RFC 2616 Section 14.3 - Accept-Encoding](https://datatracker.ietf.org/doc/html/rfc2616#section-14.3)
103-
- [RFC 2616 Section 14.41 - Content-Encoding](https://datatracker.ietf.org/doc/html/rfc2616#section-14.41)
104-
- [Wikipedia - HTTP compression](https://en.wikipedia.org/wiki/HTTP_compression)
198+
- [RFC 7234 - HTTP Caching](https://datatracker.ietf.org/doc/html/rfc7234)
199+
- [RFC 7232 - HTTP Conditional Requests](https://datatracker.ietf.org/doc/html/rfc7232)
200+
- [RFC 5861 - HTTP Cache-Control Extensions for Stale Content](https://datatracker.ietf.org/doc/html/rfc5861)
201+
- [Wikipedia - Web cache](https://en.wikipedia.org/wiki/Web_cache)
202+
- [MDN - HTTP Caching](https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching)
203+
- [Java SE 6+ ResponseCache](https://docs.oracle.com/javase/8/docs/technotes/guides/net/http-cache.html)
105204

106205
## Testing
107206

108207
The enhancement should include:
109-
- Unit tests for gzip and deflate compression/decompression
110-
- Integration tests for transparent mode behavior
111-
- Tests for opt-out mechanism (custom Accept-Encoding headers)
208+
- Unit tests for cache storage and retrieval
209+
- Tests for ETag and Last-Modified validation
210+
- Tests for Cache-Control directive handling
211+
- Integration tests for transparent and semi-automatic modes
212+
- Tests for Vary header support
213+
- Tests for stale content handling
112214
- Tests for both HTTP/1.1 and HTTP/2
113215
- Tests for both HTTP and HTTPS protocols
114216

217+
## Implementation Considerations
218+
219+
### Compatibility with java.net.ResponseCache
220+
221+
Two potential approaches:
222+
223+
**Option A: Separate Implementation (Recommended)**
224+
- Create new `java.net.http.HttpCache` API independent of `java.net.ResponseCache`
225+
- Modern design with builder pattern and fluent API
226+
- Better aligned with HttpClient design philosophy
227+
- Allows for HTTP/2-specific optimizations
228+
229+
**Option B: Compatibility Bridge**
230+
- Make HttpClient use existing `java.net.ResponseCache` if configured
231+
- Allows migration of existing ResponseCache implementations
232+
- May have design limitations due to older API
233+
234+
**Recommendation**: Option A with optional bridge for migration scenarios.
235+
236+
### Security Considerations
237+
238+
- Respect `private` vs `public` cache directives
239+
- Do not cache responses with authentication credentials by default
240+
- Validate cache keys to prevent cache poisoning
241+
- Consider size limits to prevent memory exhaustion
242+
- Support secure deletion of sensitive cached content
243+
244+
### Performance Considerations
245+
246+
- Use efficient cache key generation
247+
- Support concurrent access to cache
248+
- Minimize synchronization overhead
249+
- Consider using soft references for memory management
250+
- Support background revalidation to avoid blocking
251+
115252
## Priority Rationale
116253

117-
While HTTP compression is an important feature, applications can currently implement it manually (albeit with significant effort). This enhancement would primarily improve developer experience and reduce boilerplate code. Suggested priority: P4 (Nice to have).
254+
HTTP caching is a fundamental web feature supported by all major browsers and HTTP clients. While applications can implement caching manually, it's complex and error-prone. This enhancement would:
255+
- Reduce boilerplate code significantly
256+
- Improve application performance by default
257+
- Align Java HTTP Client with modern HTTP client implementations
258+
- Reduce server load across the ecosystem
259+
260+
Suggested priority: **P3** (Desirable feature that improves developer experience and application performance)
261+

0 commit comments

Comments
 (0)