Skip to content

Enhanced HttpClient

Methanol has a special HttpClient that extends the standard one with interesting new features. Unsurprisingly, the client is named Methanol.

Usage

In addition to interceptors and caching, Methanol can apply default properties to your requests. Think resolving with a base URI, adding default request headers, default timeouts, etc.

var builder = Methanol.newBuilder()
    .cache(...)
    .interceptor(...)
    .userAgent("Will Smith")                     // Custom User-Agent
    .baseUri("https://api.github.com")           // Base URI to resolve requests' URI against
    .defaultHeader("Accept", "application/json") // Default request headers
    .requestTimeout(Duration.ofSeconds(20))      // Default request timeout
    .headersTimeout(Duration.ofSeconds(5))       // Timeout for receiving response headers
    .readTimeout(Duration.ofSeconds(5))          // Timeout for single reads
    .autoAcceptEncoding(true);                   // Transparent response compression, this is true by default

// Continue using as a standard HttpClient.Builder!
var client = builder.executor(...)
    .executor(Executors.newFixedThreadPool(16))
    .connectTimeout(Duration.ofSeconds(30))
    ...
    .build();

You can also build from an existing HttpClient instance. However, you can't install an HttpCache in such case.

HttpClient prebuiltClient = ...
var client = Methanol.newBuilder(prebuiltClient)
    .interceptor(...)
    .userAgent("Will Smith")
     ...
    .build();

Tip

Methanol is an HttpClient. It implements the same API like send & sendAsync, which you can continue using as usual.

Note

Default properties don't override those the request already has. For instance, a client with a default Accept: text/html will not override a request's Accept: application/json.

Transparent Compression

If autoAcceptEncoding is enabled, the client complements requests with an Accept-Encoding header which accepts all supported encodings (i.e. available BodyDecoder providers). Additionally, the response is transparently decompressed according to its Content-Encoding.

Since deflate & gzip are supported out of the box, they're always included in Accept-Encoding. For instance, if brotli is installed, requests will typically have: Accept-Encoding: deflate, gzip, br. If you want specific encodings to be applied, add Accept-Encoding as a default header or explicitly set one in your request.

// Advertise brotli decompression
var client = Methanol.newBuilder()
    .defaultHeader("Accept-Encoding", "br")
    .build();
// Advertise brotli decompression
var request = MutableRequest.GET(uri)
    .header("Accept-Encoding", "br");

MimeBodyPublisher

Methanol automatically sets a request's Content-Type if it has a MimeBodyPublisher. If the request already has a Content-Type, it's overwritten. This makes sense as a body knows its media type better than a containing request mistakenly setting a different one.

Reactive Dispatching

If you like reactive streams, use Methanol::exchange, which is like sendAsync but returns Publisher<HttpResponse<T>> sources instead.

var client = Methanol.create();

var request = MutableRequest.GET("https://http2-push.appspot.com/?nopush");
var publisher = client.exchange(request, BodyHandlers.ofFile(Path.of("page.html")));

JdkFlowAdapter.flowPublisherToFlux(publisher)
    .doOnNext(response -> System.out.printf("%s: %s", response, response.body()))
    .blockLast();
var client = Methanol.create();

var request = MutableRequest.GET("https://http2-push.appspot.com");
var publisher = client.exchange(
    request, 
    BodyHandlers.ofFile(Path.of("page.html")), 
    pushPromise -> BodyHandlers.ofFile(Path.of(pushPromise.uri().getPath()).getFileName()));

JdkFlowAdapter.flowPublisherToFlux(publisher)
    .doOnNext(response -> System.out.printf("%s: %s%n", response, response.body()))
    .blockLast();

MutableRequest

MutableRequest is an HttpRequest that implements HttpRequest.Builder for settings request's properties. This drops immutability in favor of some convenience when the request is sent immediately.

var response = client.send(MutableReqeust.GET(uri), BodyHandlers.ofString());

Additionally, MutableRequest accepts relative URIs (standard HttpRequest.Builder doesn't). This is a complementing feature to Methanol's base URIs, against which relative ones are resolved.

Tip

You can use MutableRequest::toImmutableRequest to get an immutable HttpRequest snapshot.