Skip to content

Commit d970136

Browse files
committed
Merge branch 'master' of github.com:avaje/avaje-http-client
2 parents a6eb35a + d015841 commit d970136

File tree

6 files changed

+82
-13
lines changed

6 files changed

+82
-13
lines changed

client/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
<dependency>
3535
<groupId>io.avaje</groupId>
3636
<artifactId>avaje-jsonb</artifactId>
37-
<version>0.5</version>
37+
<version>0.8</version>
3838
<optional>true</optional>
3939
</dependency>
4040

client/src/main/java/io/avaje/http/client/DHttpClientContext.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
import java.util.List;
1212
import java.util.Map;
1313
import java.util.concurrent.CompletableFuture;
14+
import java.util.concurrent.atomic.AtomicLong;
1415
import java.util.concurrent.atomic.AtomicReference;
16+
import java.util.concurrent.locks.LockSupport;
1517

1618
class DHttpClientContext implements HttpClientContext {
1719

@@ -31,6 +33,7 @@ class DHttpClientContext implements HttpClientContext {
3133
private final boolean withAuthToken;
3234
private final AuthTokenProvider authTokenProvider;
3335
private final AtomicReference<AuthToken> tokenRef = new AtomicReference<>();
36+
private final AtomicLong activeAsync = new AtomicLong();
3437
private int loggingMaxBody = 1_000;
3538

3639
DHttpClientContext(HttpClient httpClient, String baseUrl, Duration requestTimeout, BodyAdapter bodyAdapter, RetryHandler retryHandler, RequestListener requestListener, AuthTokenProvider authTokenProvider, RequestIntercept intercept) {
@@ -173,6 +176,7 @@ <T> HttpResponse<T> send(HttpRequest.Builder requestBuilder, HttpResponse.BodyHa
173176
}
174177

175178
<T> CompletableFuture<HttpResponse<T>> sendAsync(HttpRequest.Builder requestBuilder, HttpResponse.BodyHandler<T> bodyHandler) {
179+
activeAsync.incrementAndGet();
176180
return httpClient.sendAsync(requestBuilder.build(), bodyHandler);
177181
}
178182

@@ -192,13 +196,28 @@ <T> List<T> readList(Class<T> cls, BodyContent content) {
192196
return bodyAdapter.listReader(cls).read(content);
193197
}
194198

199+
@Override
200+
public boolean waitForAsync(long millis) {
201+
final long until = System.currentTimeMillis() + millis;
202+
do {
203+
if (activeAsync.get() <= 0) {
204+
return true;
205+
}
206+
LockSupport.parkNanos(10_000_000);
207+
} while (System.currentTimeMillis() < until);
208+
return false;
209+
}
210+
195211
void afterResponse(DHttpClientRequest request) {
196212
if (requestListener != null) {
197213
requestListener.response(request.listenerEvent());
198214
}
199215
if (requestIntercept != null) {
200216
requestIntercept.afterResponse(request.response(), request);
201217
}
218+
if (request.startAsyncNanos > 0) {
219+
activeAsync.decrementAndGet();
220+
}
202221
}
203222

204223
void beforeRequest(DHttpClientRequest request) {

client/src/main/java/io/avaje/http/client/DHttpClientRequest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ class DHttpClientRequest implements HttpClientRequest, HttpClientResponse {
5050
private boolean loggableResponseBody;
5151
private boolean skipAuthToken;
5252
private boolean suppressLogging;
53-
private long startAsyncNanos;
53+
protected long startAsyncNanos;
5454
private String label;
5555
private Map<String, Object> customAttributes;
5656

client/src/main/java/io/avaje/http/client/HttpClientContext.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,14 @@ static HttpClientContext.Builder newBuilder() {
115115
*/
116116
byte[] decodeContent(String encoding, byte[] content);
117117

118+
/**
119+
* Wait for any submitted async requests with a given maximum wait time.
120+
*
121+
* @param maxWaitMillis The maximum time to wait in milliseconds
122+
* @return True if waiting was successful or false if there are still async requests that have not yet come back for completion.
123+
*/
124+
boolean waitForAsync(long maxWaitMillis);
125+
118126
/**
119127
* Builds the HttpClientContext.
120128
*
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package io.avaje.http.client;
2+
3+
import org.junit.jupiter.api.Test;
4+
5+
import java.net.http.HttpResponse;
6+
import java.util.List;
7+
import java.util.concurrent.CompletableFuture;
8+
import java.util.concurrent.atomic.AtomicBoolean;
9+
import java.util.stream.Collectors;
10+
import java.util.stream.Stream;
11+
12+
import static org.assertj.core.api.Assertions.assertThat;
13+
14+
class AsyncTest extends BaseWebTest {
15+
16+
final HttpClientContext clientContext = client();
17+
18+
@Test
19+
void waitForAsync() {
20+
final CompletableFuture<HttpResponse<Stream<String>>> future = clientContext.request()
21+
.path("hello").path("stream")
22+
.GET()
23+
.async()
24+
.asLines();
25+
26+
final AtomicBoolean flag = new AtomicBoolean();
27+
future.whenComplete((hres, throwable) -> {
28+
flag.set(true);
29+
assertThat(hres.statusCode()).isEqualTo(200);
30+
List<String> lines = hres.body().collect(Collectors.toList());
31+
assertThat(lines).hasSize(4);
32+
assertThat(lines.get(0)).contains("{\"id\":1, \"name\":\"one\"}");
33+
});
34+
35+
assertThat(flag).isFalse();
36+
assertThat(clientContext.waitForAsync(1_000)).isTrue();
37+
assertThat(flag).isTrue();
38+
}
39+
40+
}

client/src/test/java/org/example/github/GithubTest.java

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,32 +13,34 @@ public class GithubTest {
1313

1414
@Test
1515
@Disabled
16-
void test() throws InterruptedException {
16+
void test() {
1717

1818
final HttpClientContext clientContext = HttpClientContext.newBuilder()
1919
.baseUrl("https://api.github.com")
2020
.bodyAdapter(new JacksonBodyAdapter())
2121
.requestLogging(false)
2222
.build();
2323

24+
// will not work under module classpath without registering the HttpApiProvider
25+
final Simple simple = clientContext.create(Simple.class);
26+
27+
final List<Repo> repos = simple.listRepos("rbygrave", "junk");
28+
assertThat(repos).isNotEmpty();
29+
2430
clientContext.request()
2531
.path("users").path("rbygrave").path("repos")
2632
.GET()
2733
.async()
2834
.asString()
2935
.thenAccept(res -> {
30-
31-
System.out.println("RES: "+res.statusCode());
32-
System.out.println("BODY: "+res.body());
36+
System.out.println("RES: " + res.statusCode());
37+
System.out.println("BODY: " + res.body().substring(0, 150) + "...");
3338
});
3439

35-
Thread.sleep(1_000);
36-
37-
// will not work under module classpath without registering the HttpApiProvider
38-
final Simple simple = clientContext.create(Simple.class);
39-
40-
final List<Repo> repos = simple.listRepos("rbygrave", "junk");
41-
assertThat(repos).isNotEmpty();
40+
long st = System.currentTimeMillis();
41+
System.out.println("waitForAsync");
42+
boolean waitSuccess = clientContext.waitForAsync(2_000);
43+
System.out.println("waitForAsync waitSuccess:" + waitSuccess + " waitMillis: " + (System.currentTimeMillis() - st));
4244
}
4345

4446
}

0 commit comments

Comments
 (0)