Skip to content

Commit 5b2c924

Browse files
committed
Initial add
1 parent 5e58d2e commit 5b2c924

35 files changed

+2463
-0
lines changed

.editorconfig

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# editorconfig.org
2+
3+
root = true
4+
5+
[*]
6+
charset = utf-8
7+
end_of_line = lf
8+
indent_size = 2
9+
indent_style = space
10+
insert_final_newline = true
11+
trim_trailing_whitespace = true
12+
spaces_around_operators = true
13+
max_line_length = 130

.gitignore

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
target/
2+
logs/
3+
log/
4+
5+
*.iml
6+
.idea/
7+
8+
.classpath
9+
.project
10+
.settings/
11+

client/pom.xml

+133
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<project xmlns="http://maven.apache.org/POM/4.0.0"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5+
<parent>
6+
<artifactId>java11-oss</artifactId>
7+
<groupId>org.avaje</groupId>
8+
<version>2.1.2</version>
9+
</parent>
10+
11+
<modelVersion>4.0.0</modelVersion>
12+
13+
<groupId>io.avaje</groupId>
14+
<artifactId>avaje-http-client</artifactId>
15+
<version>0.1-SNAPSHOT</version>
16+
17+
<dependencies>
18+
19+
<dependency>
20+
<groupId>org.slf4j</groupId>
21+
<artifactId>slf4j-api</artifactId>
22+
<version>1.7.25</version>
23+
</dependency>
24+
25+
<dependency>
26+
<groupId>com.fasterxml.jackson.core</groupId>
27+
<artifactId>jackson-databind</artifactId>
28+
<version>2.11.1</version>
29+
<optional>true</optional>
30+
</dependency>
31+
32+
33+
<!-- test dependencies -->
34+
35+
<dependency>
36+
<groupId>org.junit.jupiter</groupId>
37+
<artifactId>junit-jupiter-api</artifactId>
38+
<version>5.6.2</version>
39+
<scope>test</scope>
40+
</dependency>
41+
42+
<dependency>
43+
<groupId>org.junit.jupiter</groupId>
44+
<artifactId>junit-jupiter-engine</artifactId>
45+
<version>5.6.2</version>
46+
<scope>test</scope>
47+
</dependency>
48+
49+
<dependency>
50+
<groupId>org.assertj</groupId>
51+
<artifactId>assertj-core</artifactId>
52+
<version>3.16.1</version>
53+
<scope>test</scope>
54+
</dependency>
55+
56+
<dependency>
57+
<groupId>io.javalin</groupId>
58+
<artifactId>javalin</artifactId>
59+
<version>3.9.1</version>
60+
<scope>test</scope>
61+
</dependency>
62+
63+
<dependency>
64+
<groupId>io.dinject</groupId>
65+
<artifactId>dinject</artifactId>
66+
<version>2.3</version>
67+
<scope>test</scope>
68+
</dependency>
69+
70+
<dependency>
71+
<groupId>io.dinject</groupId>
72+
<artifactId>dinject-controller</artifactId>
73+
<version>1.21</version>
74+
<scope>test</scope>
75+
</dependency>
76+
77+
<dependency>
78+
<groupId>io.dinject</groupId>
79+
<artifactId>controller-validator-hibernate</artifactId>
80+
<version>1.1</version>
81+
<scope>test</scope>
82+
</dependency>
83+
84+
<dependency>
85+
<groupId>org.avaje.composite</groupId>
86+
<artifactId>logback</artifactId>
87+
<version>1.1</version>
88+
<scope>test</scope>
89+
</dependency>
90+
91+
<!-- test annotation processors -->
92+
93+
<dependency>
94+
<groupId>io.dinject</groupId>
95+
<artifactId>dinject-generator</artifactId>
96+
<version>2.3</version>
97+
<scope>test</scope>
98+
</dependency>
99+
100+
<dependency>
101+
<groupId>io.dinject</groupId>
102+
<artifactId>javalin-generator</artifactId>
103+
<version>1.21</version>
104+
<scope>test</scope>
105+
</dependency>
106+
107+
</dependencies>
108+
109+
<build>
110+
<plugins>
111+
<plugin>
112+
<groupId>org.apache.maven.plugins</groupId>
113+
<artifactId>maven-surefire-plugin</artifactId>
114+
<version>2.22.2</version>
115+
<configuration>
116+
<argLine>
117+
--illegal-access=permit
118+
</argLine>
119+
</configuration>
120+
</plugin>
121+
<plugin>
122+
<groupId>org.apache.maven.plugins</groupId>
123+
<artifactId>maven-failsafe-plugin</artifactId>
124+
<version>2.22.2</version>
125+
<configuration>
126+
<argLine>
127+
--illegal-access=permit
128+
</argLine>
129+
</configuration>
130+
</plugin>
131+
</plugins>
132+
</build>
133+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package io.avaje.http.client;
2+
3+
import java.util.List;
4+
5+
public interface BodyAdapter {
6+
7+
BodyWriter beanWriter(Class<?> cls);
8+
9+
<T> BodyReader<T> beanReader(Class<T> cls);
10+
11+
<T> BodyReader<List<T>> listReader(Class<T> cls);
12+
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package io.avaje.http.client;
2+
3+
public class BodyContent {
4+
5+
public static final String JSON_UTF8 = "application/json; charset=UTF-8";
6+
7+
private final String contentType;
8+
9+
private final byte[] content;
10+
11+
public static BodyContent asJson(byte[] content) {
12+
return new BodyContent(JSON_UTF8, content);
13+
}
14+
15+
public BodyContent(String contentType, byte[] content) {
16+
this.contentType = contentType;
17+
this.content = content;
18+
}
19+
20+
public String contentType() {
21+
return contentType;
22+
}
23+
24+
public byte[] content() {
25+
return content;
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package io.avaje.http.client;
2+
3+
public interface BodyReader<T> {
4+
5+
T read(BodyContent content);
6+
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package io.avaje.http.client;
2+
3+
/**
4+
* Writes beans as content for a specific content type.
5+
*/
6+
public interface BodyWriter {
7+
8+
/**
9+
* Write the bean as content using the default content type.
10+
* <p>
11+
* Used when all beans sent via POST, PUT, PATCH will be sent as
12+
* a single content type like <code>application/json; charset=utf8</code>.
13+
*/
14+
BodyContent write(Object bean);
15+
16+
/**
17+
* Write the bean as content with the requested content type.
18+
* <p>
19+
* The writer is expected to use the given contentType to determine
20+
* how to write the bean as content.
21+
*/
22+
BodyContent write(Object bean, String contentType);
23+
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
package io.avaje.http.client;
2+
3+
import java.io.IOException;
4+
import java.net.http.HttpClient;
5+
import java.net.http.HttpHeaders;
6+
import java.net.http.HttpRequest;
7+
import java.net.http.HttpResponse;
8+
import java.time.Duration;
9+
import java.util.List;
10+
import java.util.Map;
11+
12+
class DHttpClientContext implements HttpClientContext {
13+
14+
private final HttpClient httpClient;
15+
private final String baseUrl;
16+
private final Duration requestTimeout;
17+
private final BodyAdapter bodyAdapter;
18+
private final RequestListener requestListener;
19+
20+
DHttpClientContext(HttpClient httpClient, String baseUrl, Duration requestTimeout, BodyAdapter bodyAdapter, RequestListener requestListener) {
21+
this.httpClient = httpClient;
22+
this.baseUrl = baseUrl;
23+
this.requestTimeout = requestTimeout;
24+
this.bodyAdapter = bodyAdapter;
25+
this.requestListener = requestListener;
26+
}
27+
28+
@Override
29+
public HttpClientRequest request() {
30+
return new DHttpClientRequest(this, requestTimeout);
31+
}
32+
33+
@Override
34+
public BodyAdapter converters() {
35+
return bodyAdapter;
36+
}
37+
38+
@Override
39+
public UrlBuilder url() {
40+
return new UrlBuilder(baseUrl);
41+
}
42+
43+
@Override
44+
public HttpClient httpClient() {
45+
return httpClient;
46+
}
47+
48+
@Override
49+
public void checkResponse(HttpResponse<?> response) {
50+
if (response.statusCode() >= 300) {
51+
throw new HttpException(response, this);
52+
}
53+
}
54+
55+
void check(HttpResponse<byte[]> response) {
56+
if (response.statusCode() >= 300) {
57+
throw new HttpException(this, response);
58+
}
59+
}
60+
61+
@Override
62+
public BodyContent readContent(HttpResponse<byte[]> httpResponse) {
63+
byte[] bodyBytes = decodeContent(httpResponse);
64+
final String contentType = getContentType(httpResponse);
65+
return new BodyContent(contentType, bodyBytes);
66+
}
67+
68+
String getContentType(HttpResponse<byte[]> httpResponse) {
69+
return firstHeader(httpResponse.headers(), "Content-Type", "content-type");
70+
}
71+
72+
String getContentEncoding(HttpResponse<byte[]> httpResponse) {
73+
return firstHeader(httpResponse.headers(), "Content-Encoding", "content-encoding");
74+
}
75+
76+
@Override
77+
public byte[] decodeContent(String encoding, byte[] body) {
78+
if (encoding.equals("gzip")) {
79+
return GzipUtil.gzipDecode(body);
80+
}
81+
// todo: register decoders with context and use them
82+
return body;
83+
}
84+
85+
public byte[] decodeContent(HttpResponse<byte[]> httpResponse) {
86+
String encoding = getContentEncoding(httpResponse);
87+
return encoding == null ? httpResponse.body() : decodeContent(encoding, httpResponse.body());
88+
}
89+
90+
String firstHeader(HttpHeaders headers, String... names) {
91+
final Map<String, List<String>> map = headers.map();
92+
for (String key : names) {
93+
final List<String> values = map.get(key);
94+
if (values != null && !values.isEmpty()) {
95+
return values.get(0);
96+
}
97+
}
98+
return null;
99+
}
100+
101+
<T> HttpResponse<T> send(HttpRequest.Builder requestBuilder, HttpResponse.BodyHandler<T> bodyHandler) {
102+
final HttpRequest request = applyFilters(requestBuilder).build();
103+
try {
104+
return httpClient.send(request, bodyHandler);
105+
} catch (IOException e) {
106+
throw new HttpException(499, e);
107+
} catch (InterruptedException e) {
108+
Thread.currentThread().interrupt();
109+
throw new HttpException(499, e);
110+
}
111+
}
112+
113+
private HttpRequest.Builder applyFilters(HttpRequest.Builder hreq) {
114+
return hreq;
115+
}
116+
117+
BodyContent write(Object bean, String contentType) {
118+
return bodyAdapter.beanWriter(bean.getClass()).write(bean, contentType);
119+
}
120+
121+
<T> T readBean(Class<T> cls, BodyContent content) {
122+
return bodyAdapter.beanReader(cls).read(content);
123+
}
124+
125+
<T> List<T> readList(Class<T> cls, BodyContent content) {
126+
return bodyAdapter.listReader(cls).read(content);
127+
}
128+
129+
130+
void afterResponse(DHttpClientRequest request) {
131+
if (requestListener != null) {
132+
requestListener.response(request.listenerEvent());
133+
}
134+
}
135+
136+
void afterResponseHandler(DHttpClientRequest request) {
137+
if (requestListener != null) {
138+
requestListener.response(request.listenerEvent());
139+
}
140+
}
141+
}

0 commit comments

Comments
 (0)