Ether Framework
Unified API docs for Ether modules
Loading...
Searching...
No Matches
DefaultEtherHttpClient.java
Go to the documentation of this file.
1package dev.rafex.ether.http.client.impl;
2
3/*-
4 * #%L
5 * ether-http-client
6 * %%
7 * Copyright (C) 2025 - 2026 Raúl Eduardo González Argote
8 * %%
9 * Permission is hereby granted, free of charge, to any person obtaining a copy
10 * of this software and associated documentation files (the "Software"), to deal
11 * in the Software without restriction, including without limitation the rights
12 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 * copies of the Software, and to permit persons to whom the Software is
14 * furnished to do so, subject to the following conditions:
15 *
16 * The above copyright notice and this permission notice shall be included in
17 * all copies or substantial portions of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 * THE SOFTWARE.
26 * #L%
27 */
28
29import java.io.IOException;
30import java.net.URI;
31import java.net.http.HttpClient;
32import java.net.http.HttpRequest;
33import java.net.http.HttpResponse;
34import java.util.List;
35import java.util.Map;
36
37import com.fasterxml.jackson.core.type.TypeReference;
38
39import dev.rafex.ether.http.client.EtherHttpClient;
40import dev.rafex.ether.http.client.config.HttpClientConfig;
41import dev.rafex.ether.http.client.model.HttpMethod;
42import dev.rafex.ether.http.client.model.HttpRequestSpec;
43import dev.rafex.ether.http.client.model.HttpResponseSpec;
44import dev.rafex.ether.json.JsonCodec;
45import dev.rafex.ether.json.JsonUtils;
46
47public final class DefaultEtherHttpClient implements EtherHttpClient {
48
49 private final HttpClient httpClient;
50 private final HttpClientConfig config;
51 private final JsonCodec jsonCodec;
52
53 public DefaultEtherHttpClient(final HttpClientConfig config) {
54 this(config, JsonUtils.codec());
55 }
56
57 public DefaultEtherHttpClient(final HttpClientConfig config, final JsonCodec jsonCodec) {
58 this.config = config == null ? HttpClientConfig.defaults() : config;
59 this.jsonCodec = jsonCodec == null ? JsonUtils.codec() : jsonCodec;
60 this.httpClient = HttpClient.newBuilder().connectTimeout(this.config.connectTimeout())
61 .followRedirects(this.config.redirectPolicy()).build();
62 }
63
65 return new DefaultEtherHttpClient(HttpClientConfig.defaults());
66 }
67
68 public static DefaultEtherHttpClient create(final HttpClientConfig config) {
69 return new DefaultEtherHttpClient(config);
70 }
71
72 public static DefaultEtherHttpClient create(final JsonCodec jsonCodec) {
73 return new DefaultEtherHttpClient(HttpClientConfig.defaults(), jsonCodec);
74 }
75
76 @Override
77 public HttpResponseSpec send(final HttpRequestSpec request) throws IOException, InterruptedException {
78 final var response = httpClient.send(buildRequest(request), HttpResponse.BodyHandlers.ofByteArray());
79 return new HttpResponseSpec(response.statusCode(), response.headers().map(), response.body());
80 }
81
82 @Override
83 public <T> T sendJson(final HttpRequestSpec request, final TypeReference<T> typeReference)
84 throws IOException, InterruptedException {
85 final var response = send(request);
86 return jsonCodec.readValue(response.body(), typeReference);
87 }
88
89 @Override
90 public <T> T sendJson(final HttpRequestSpec request, final Class<T> type) throws IOException, InterruptedException {
91 final var response = send(request);
92 return jsonCodec.readValue(response.body(), type);
93 }
94
95 public <T> T sendJson(final HttpMethod method, final URI uri, final Object body, final Class<T> responseType)
96 throws IOException, InterruptedException {
97 return sendJson(HttpRequestSpec.builder(method, uri).jsonBody(body, jsonCodec).build(), responseType);
98 }
99
100 public <T> T sendJson(final HttpMethod method, final URI uri, final Object body,
101 final TypeReference<T> responseType) throws IOException, InterruptedException {
102 return sendJson(HttpRequestSpec.builder(method, uri).jsonBody(body, jsonCodec).build(), responseType);
103 }
104
105 public HttpResponseSpec sendText(final HttpMethod method, final URI uri, final String body)
106 throws IOException, InterruptedException {
107 return send(HttpRequestSpec.builder(method, uri).body(body).contentType("text/plain; charset=utf-8").build());
108 }
109
110 private HttpRequest buildRequest(final HttpRequestSpec request) {
111 final var builder = HttpRequest.newBuilder(request.uri())
112 .method(request.method().verb(), requestBodyPublisher(request))
113 .timeout(request.timeout() != null ? request.timeout() : config.requestTimeout());
114
115 applyHeaders(builder, config.defaultHeaders());
116 applyHeaders(builder, request.headers());
117 if (config.userAgent() != null && !config.userAgent().isBlank()) {
118 builder.header("User-Agent", config.userAgent());
119 }
120 return builder.build();
121 }
122
123 private static void applyHeaders(final HttpRequest.Builder builder, final Map<String, List<String>> headers) {
124 for (final var entry : headers.entrySet()) {
125 for (final var value : entry.getValue()) {
126 builder.header(entry.getKey(), value);
127 }
128 }
129 }
130
131 private static HttpRequest.BodyPublisher requestBodyPublisher(final HttpRequestSpec request) {
132 final var body = request.body();
133 if (body == null || body.length == 0) {
134 return HttpRequest.BodyPublishers.noBody();
135 }
136 return HttpRequest.BodyPublishers.ofByteArray(body);
137 }
138}
HttpResponseSpec sendText(final HttpMethod method, final URI uri, final String body)
HttpResponseSpec send(final HttpRequestSpec request)
DefaultEtherHttpClient(final HttpClientConfig config, final JsonCodec jsonCodec)
static DefaultEtherHttpClient create(final HttpClientConfig config)
static DefaultEtherHttpClient create(final JsonCodec jsonCodec)
< T > T readValue(InputStream input, Class< T > type)
HTTP client configuration models and defaults.