Skip to content

Commit 67de8e0

Browse files
authored
#662 [enhancement] making HttpClientRequest Cloneable (#664)
Note that customAttributes a shallow copy is used
1 parent b550072 commit 67de8e0

File tree

7 files changed

+155
-5
lines changed

7 files changed

+155
-5
lines changed

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

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,41 @@ class DHttpClientRequest implements HttpClientRequest, HttpClientResponse {
6969
this.errorMapper = context.errorMapper();
7070
}
7171

72+
private DHttpClientRequest(DHttpClientRequest source) {
73+
this.context = source.context;
74+
this.url = source.url.clone();
75+
this.requestTimeout = source.requestTimeout;
76+
this.gzip = source.gzip;
77+
this.encodedRequestBody = source.encodedRequestBody;
78+
this.body = source.body;
79+
this.httpRequest = source.httpRequest;
80+
this.bodyFormEncoded = source.bodyFormEncoded;
81+
this.loggableResponseBody = source.loggableResponseBody;
82+
this.skipAuthToken = source.skipAuthToken;
83+
this.suppressLogging = source.suppressLogging;
84+
this.label = source.label;
85+
this.errorMapper = source.errorMapper;
86+
this.isRetry = source.isRetry;
87+
this.method = source.method;
88+
if (source.formParams != null) {
89+
this.formParams = new LinkedHashMap<>();
90+
source.formParams.forEach((k,v) -> formParams.put(k, new ArrayList<>(v)));
91+
}
92+
if (source.headers != null) {
93+
this.headers = new LinkedHashMap<>();
94+
source.headers.forEach((k,v) -> headers.put(k, new ArrayList<>(v)));
95+
}
96+
if (source.customAttributes != null) {
97+
// note that the values are not being deep copied here
98+
this.customAttributes = new HashMap<>(source.customAttributes);
99+
}
100+
}
101+
102+
@Override
103+
public HttpClientRequest clone() {
104+
return new DHttpClientRequest(this);
105+
}
106+
72107
@Override
73108
public String method() {
74109
return method;

http-client/src/main/java/io/avaje/http/client/DUrlBuilder.java

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,22 @@
55

66
final class DUrlBuilder implements UrlBuilder {
77

8-
private final StringBuilder buffer = new StringBuilder(100);
9-
8+
private final StringBuilder buffer;
109
private boolean hasParams;
1110

1211
DUrlBuilder(String base) {
13-
buffer.append(base);
12+
this.buffer = new StringBuilder(100);
13+
this.buffer.append(base);
14+
}
15+
16+
private DUrlBuilder(DUrlBuilder source) {
17+
this.hasParams = source.hasParams;
18+
this.buffer = new StringBuilder(source.buffer.toString());
19+
}
20+
21+
@Override
22+
public UrlBuilder clone() {
23+
return new DUrlBuilder(this);
1424
}
1525

1626
@Override

http-client/src/main/java/io/avaje/http/client/HttpClientRequest.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,12 @@
3131
*
3232
* @see HttpClient
3333
*/
34-
public interface HttpClientRequest {
34+
public interface HttpClientRequest extends Cloneable {
35+
36+
/**
37+
* Return a copy of the HttpClientRequest.
38+
*/
39+
HttpClientRequest clone();
3540

3641
/**
3742
* For this request skip using an Authorization token.

http-client/src/main/java/io/avaje/http/client/UrlBuilder.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
/**
88
* Build a URL typically using a base url and adding path and query parameters.
99
*/
10-
public interface UrlBuilder {
10+
public interface UrlBuilder extends Cloneable {
1111

1212
/**
1313
* URL encode the value.
@@ -23,6 +23,11 @@ static UrlBuilder of(String baseUrl) {
2323
return new DUrlBuilder(baseUrl);
2424
}
2525

26+
/**
27+
* Return a copy of the UrlBuilder.
28+
*/
29+
UrlBuilder clone();
30+
2631
/**
2732
* Set the url. This effectively replaces a base url.
2833
*/

http-client/src/test/java/io/avaje/http/client/DHttpClientRequestTest.java

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,54 @@ void suppressLogging_listenerEvent_expect_suppressedPayloadContent() {
2323
assertThat(event.responseBody()).isEqualTo("<suppressed response body>");
2424
}
2525

26+
@Test
27+
void clone_expect_deepCopiesOfOriginalRequest() {
28+
final DHttpClientRequest request = new DHttpClientRequest(context, Duration.ZERO);
29+
request.suppressLogging();
30+
request.path("patha");
31+
request.queryParam("queryParam", "b");
32+
request.header("aheader", "orig");
33+
request.body("SomeBody");
34+
request.setAttribute("foo", "one");
35+
request.formParam("fp", "1");
36+
request.formParam("fp", "2");
37+
38+
HttpClientRequest req0 = request.clone();
39+
HttpClientRequest req1 = request.clone();
40+
41+
request.queryParam("orig", "x");
42+
req0.queryParam("req0", "y");
43+
req1.queryParam("req1", "z");
44+
45+
request.header("aheader", "x");
46+
req0.header("aheader", "y");
47+
req0.formParam("fp2", "5");
48+
49+
req1.header("aheader", "z");
50+
req1.header("req1", "2");
51+
req1.setAttribute("bar", "two");
52+
req1.formParam("fp", "3");
53+
54+
assertThat(request.url()).isEqualTo("null/patha?queryParam=b&orig=x");
55+
assertThat(req0.url()).isEqualTo("null/patha?queryParam=b&req0=y");
56+
assertThat(req1.url()).isEqualTo("null/patha?queryParam=b&req1=z");
57+
58+
assertThat(request.headers()).containsOnlyKeys("aheader");
59+
assertThat(request.header("aheader")).containsOnly("orig", "x");
60+
61+
assertThat(req0.headers()).containsOnlyKeys("aheader");
62+
assertThat(req0.header("aheader")).containsOnly("orig", "y");
63+
64+
assertThat(req1.headers()).containsOnlyKeys("aheader", "req1");
65+
assertThat(req1.header("aheader")).containsOnly("orig", "z");
66+
67+
assertThat((String)req0.getAttribute("foo")).isEqualTo("one");
68+
assertThat((String)req0.getAttribute("bar")).isNull();
69+
70+
assertThat((String)req1.getAttribute("foo")).isEqualTo("one");
71+
assertThat((String)req1.getAttribute("bar")).isEqualTo("two");
72+
}
73+
2674
@Test
2775
void assertHeader() {
2876
final var request = new DHttpClientRequest(context, Duration.ZERO);

http-client/src/test/java/io/avaje/http/client/DUrlBuilderTest.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,31 @@
66

77
class DUrlBuilderTest {
88

9+
@Test
10+
void clone_expect_copyOfOriginal() {
11+
var uriBuilder = UrlBuilder.of("http://foo")
12+
.path("more");
13+
14+
UrlBuilder copy = uriBuilder.clone();
15+
copy.queryParam("a", "a");
16+
17+
uriBuilder.queryParam("b", "b");
18+
assertThat(uriBuilder.build()).isEqualTo("http://foo/more?b=b");
19+
assertThat(copy.build()).isEqualTo("http://foo/more?a=a");
20+
}
21+
22+
@Test
23+
void clone_hasParams_expect_copyOfOriginal() {
24+
var uriBuilder = UrlBuilder.of("http://foo").path("more").queryParam("orig",1);
25+
26+
UrlBuilder copy = uriBuilder.clone();
27+
copy.queryParam("a", "a");
28+
29+
uriBuilder.queryParam("b", "b");
30+
assertThat(uriBuilder.build()).isEqualTo("http://foo/more?orig=1&b=b");
31+
assertThat(copy.build()).isEqualTo("http://foo/more?orig=1&a=a");
32+
}
33+
934
@Test
1035
void url() {
1136
var uri = UrlBuilder.of("http://foo")

http-client/src/test/java/io/avaje/http/client/HelloControllerTest.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,6 +1052,28 @@ void post_bean_returningBean_usingExplicitConverters() {
10521052
assertEquals(12, bean.id);
10531053
}
10541054

1055+
@Test
1056+
void post_bean_clone() {
1057+
final BodyWriter from = clientContext.bodyAdapter().beanWriter(HelloDto.class);
1058+
final BodyReader<HelloDto> toDto = clientContext.bodyAdapter().beanReader(HelloDto.class);
1059+
1060+
final HelloDto dto0 = new HelloDto(12, "rob", "other");
1061+
HttpClientRequest origReq = clientContext.request()
1062+
.path("hello")
1063+
.body(from.write(dto0));
1064+
1065+
final HelloDto dto1 = new HelloDto(13, "bor", "rehto");
1066+
HttpClientRequest copyReq = origReq.clone().body(from.write(dto1));
1067+
1068+
var res0 = origReq.POST().read(toDto);
1069+
var res1 = copyReq.POST().read(toDto);
1070+
1071+
assertEquals("posted", res0.name);
1072+
assertEquals(12, res0.id);
1073+
assertEquals("posted", res1.name);
1074+
assertEquals(13, res1.id);
1075+
}
1076+
10551077
@Test
10561078
void post_bean_returningVoid() {
10571079

0 commit comments

Comments
 (0)