The project provides useful extensions to JDK's HttpClient.
Type | Status |
---|---|
Build | |
Artifact | |
Javadoc | |
Code coverage | |
LGTM |
- Java 11+
- Gradle for building the project.
implementation 'io.github.nstdio:http-client-ext:2.3.2'
<dependency>
<groupId>io.github.nstdio</groupId>
<artifactId>http-client-ext</artifactId>
<version>2.3.2</version>
</dependency>
- Caching, both in memory and disk.
- Decompression:
gzip, deflate
- JSON mappings
The ExtendedHttpClient
implements client part of RFC7234
There are two types of cache:
// backed by volotile in-memory storage
Cache mem = Cache.newInMemoryCacheBuilder().build()
// and persistent storage
Cache disk = Cache.newDiskCacheBuilder().dir(Path.of("...")).build()
Here is the example of creating client with in memory cache:
HttpClient client = ExtendedHttpClient.newBuilder()
.cache(Cache.newInMemoryCacheBuilder().build())
.build();
URI uri = URI.create("https://api.github.com/users/defunkt");
HttpRequest request = HttpRequest.newBuilder(uri).build();
HttpResponse<String> networkResponse = client.send(request, ofString());
HttpResponse<String> cachedResponse = client.send(request, ofString());
and all available configurations for in memory cache:
Cache inMemory = Cache.newInMemoryCacheBuilder()
.maxItems(4096) // number of responses can be cached
.size(10 * 1000 * 1000) // maximum size of the entire cache in bytes, -1 for no constraint
.requestFilter(request -> request.uri().getHost().equals("api.github.com")) // cache only requests that match given predicate
.responseFilter(response -> response.statusCode() == 200) // cache only responses that match given predicate
.build();
Above-mentioned configurations also applies to persistent cache with some additions
Path cacheDir = ...
Cache disk = Cache.newDiskCacheBuilder()
.dir(cacheDir)
.build();
If request/response contains sensitive information one might want to store it encrypted:
Path cacheDir = ...
SecretKey secretKey = ...
Cache encrypted = Cache.newDiskCacheBuilder()
.dir(cacheDir)
.encrypted()
.key(secretKey)
.cipherAlgorithm("AES")
.build();
will create persistent cache which encrypts data by user provided key.
Here is an example of transparent encoding feature
HttpClient client = ExtendedHttpClient.newBuilder()
.transparentEncoding(true)
.build();
URI uri = URI.create("https://api.github.com/users/defunkt");
HttpRequest request = HttpRequest.newBuilder(uri).build();
// Client will add `Accept-Encoding: gzip, deflate` header to the request
// then decompress response body transparently of the user
HttpResponse<String> response = client.send(request, ofString());
there is also dedicated BodyHandler
for that, but in this case user should add Accept-Encoding
header manually
HttpClient client = HttpClient.newClient();
URI uri = URI.create("https://api.github.com/users/defunkt");
HttpRequest request = HttpRequest.newBuilder(uri)
.header("Accept-Encoding", "gzip, deflate")
.build();
HttpResponse<String> response = client.send(request, BodyHandlers.ofDecompressing(ofString()));
Out of the box support for gzip
and deflate
is provided by JDK itself. For br
(brotli) compression please add
one of following dependencies to your project:
service loader will pick up correct dependency. If none of these preferred there is always an options to extend via SPI by providing CompressionFactory
Currently, two libraries are supported
just drop one of them as dependency and voilà
// will create object using Jackson or Gson
User user = client.send(request, BodyHandlers.ofJson(User.class));
// or send JSON
Object user = null;
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://example.com/users"))
.POST(BodyPublishers.ofJson(user))
.build();
ExtendedHttpClient client = ExtendedHttpClient.newHttpClient();
HttpResponse<User> response = client.send(request, BodyHandlers.ofJson(User.class));
And if special configuration required
package com.example;
class JacksonMappingProvider implements JsonMappingProvider {
@Override
public JsonMapping get() {
// configure jackson
ObjectMapper mapper = ...
return new JacksonJsonMapping(mapper);
}
}
then standard SPI registration can be used or custom provider can be registered manually:
JacksonMappingProvider jackson = ...
JsonMappingProvider.addProvider(jackson);