Skip to content

Commit be05bdd

Browse files
author
Liviu Ilea
committed
Http-adapter: added timeout configuration + tests
1 parent 8d418f8 commit be05bdd

File tree

8 files changed

+176
-58
lines changed

8 files changed

+176
-58
lines changed

http-adapter/http-adapter-integration-test/src/test/java/org/codemental/samples/webflux/java/http/adapter/integration/config/CitrusConfiguration.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public HttpServer externalApiMock() {
3636
.server()
3737
.port(externalApiMockPort)
3838
.autoStart(true)
39-
.timeout(5000)
39+
.timeout(10000)
4040
.build();
4141
}
4242
}

http-adapter/http-adapter-integration-test/src/test/java/org/codemental/samples/webflux/java/http/adapter/integration/test/HttpAdapterIT.java

-55
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package org.codemental.samples.webflux.java.http.adapter.integration.test;
2+
3+
import com.consol.citrus.annotations.CitrusTest;
4+
import com.consol.citrus.dsl.testng.TestNGCitrusTestRunner;
5+
import com.consol.citrus.http.client.HttpClient;
6+
import com.consol.citrus.http.server.HttpServer;
7+
import org.springframework.beans.factory.annotation.Autowired;
8+
import org.springframework.http.HttpStatus;
9+
import org.springframework.http.MediaType;
10+
import org.testng.annotations.Test;
11+
12+
@Test
13+
public class InboundApiControllerIT extends TestNGCitrusTestRunner {
14+
15+
@Autowired
16+
private HttpClient httpClient;
17+
@Autowired
18+
private HttpServer httpServer;
19+
20+
@CitrusTest
21+
public void okTest() {
22+
http(action -> action.client(httpClient)
23+
.send()
24+
.post("inbound")
25+
.payload("{" +
26+
"\"userName\": \"Bruce.Wayne\"," +
27+
"\"operation\": \"call-a-pizza\"" +
28+
"}")
29+
.fork(true));
30+
31+
http(action -> action.server(httpServer)
32+
.receive()
33+
.post("/api")
34+
.contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)
35+
.payload("{" +
36+
"\"information\": \"User: Bruce.Wayne called operation: call-a-pizza\"," +
37+
"\"timestamp\": \"@ignore@\"" +
38+
"}"));
39+
40+
http(action -> action.server(httpServer)
41+
.respond(HttpStatus.OK)
42+
.contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)
43+
.payload("{" +
44+
"\"allowed\": true" +
45+
"}"));
46+
47+
http(action -> action.client(httpClient)
48+
.receive()
49+
.response(HttpStatus.OK)
50+
.contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)
51+
.payload("{" +
52+
"\"success\": true" +
53+
"}"));
54+
}
55+
56+
@CitrusTest
57+
public void timeoutTest() {
58+
http(action -> action.client(httpClient)
59+
.send()
60+
.post("inbound")
61+
.payload("{" +
62+
"\"userName\": \"Bruce.Wayne\"," +
63+
"\"operation\": \"call-a-pizza\"" +
64+
"}")
65+
.fork(true));
66+
67+
http(action -> action.server(httpServer)
68+
.receive()
69+
.post("/api")
70+
.contentType(MediaType.APPLICATION_JSON_UTF8_VALUE)
71+
.payload("{" +
72+
"\"information\": \"User: Bruce.Wayne called operation: call-a-pizza\"," +
73+
"\"timestamp\": \"@ignore@\"" +
74+
"}"));
75+
76+
//we send no response here from the backend
77+
78+
http(action -> action.client(httpClient)
79+
.receive()
80+
.response(HttpStatus.GATEWAY_TIMEOUT)
81+
.timeout(10000)
82+
.contentType(MediaType.APPLICATION_JSON_UTF8_VALUE));
83+
}
84+
85+
@CitrusTest
86+
public void connectionRefusedTest() {
87+
httpServer.stop();
88+
89+
http(action -> action.client(httpClient)
90+
.send()
91+
.post("inbound")
92+
.payload("{" +
93+
"\"userName\": \"Bruce.Wayne\"," +
94+
"\"operation\": \"call-a-pizza\"" +
95+
"}")
96+
.fork(true));
97+
98+
http(action -> action.client(httpClient)
99+
.receive()
100+
.response(HttpStatus.GATEWAY_TIMEOUT)
101+
.timeout(2000)
102+
.contentType(MediaType.APPLICATION_JSON_UTF8_VALUE));
103+
104+
httpServer.start();
105+
}
106+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package org.codemental.samples.webflux.java.http.adapter.errorhandling;
2+
3+
import io.netty.handler.timeout.ReadTimeoutException;
4+
import org.slf4j.Logger;
5+
import org.slf4j.LoggerFactory;
6+
import org.springframework.http.HttpStatus;
7+
import org.springframework.web.bind.annotation.ControllerAdvice;
8+
import org.springframework.web.bind.annotation.ExceptionHandler;
9+
import org.springframework.web.bind.annotation.ResponseStatus;
10+
11+
import java.net.ConnectException;
12+
13+
@ControllerAdvice
14+
public class BackendConnectionExceptionTranslator {
15+
16+
private final Logger logger = LoggerFactory.getLogger(this.getClass());
17+
18+
@ExceptionHandler({
19+
ReadTimeoutException.class,
20+
ConnectException.class})
21+
@ResponseStatus(HttpStatus.GATEWAY_TIMEOUT)
22+
public void handleBadRequest(Exception exception) {
23+
logger.error(exception.getMessage(), exception);
24+
;
25+
}
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package org.codemental.samples.webflux.java.http.adapter.main;
2+
3+
import org.codemental.samples.webflux.java.http.adapter.errorhandling.BackendConnectionExceptionTranslator;
4+
import org.springframework.context.annotation.Bean;
5+
6+
public class ErrorHandlingConfiguration {
7+
8+
@Bean
9+
public BackendConnectionExceptionTranslator backendConnectionExceptionTranslator() {
10+
return new BackendConnectionExceptionTranslator();
11+
}
12+
}

http-adapter/http-adapter-service/src/main/java/org/codemental/samples/webflux/java/http/adapter/main/HttpAdapterApplication.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
@EnableAutoConfiguration
1212
@EnableWebFlux
1313
@Profile({"default"})
14-
@Import({HttpConfiguration.class})
14+
@Import({ErrorHandlingConfiguration.class, HttpConfiguration.class})
1515
public class HttpAdapterApplication {
1616

1717
public static void main(String[] args) {
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,45 @@
11
package org.codemental.samples.webflux.java.http.adapter.main;
22

3+
import io.netty.channel.ChannelOption;
4+
import io.netty.handler.timeout.ReadTimeoutHandler;
35
import org.springframework.beans.factory.annotation.Value;
46
import org.springframework.context.annotation.Bean;
7+
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
58
import org.springframework.web.reactive.function.client.WebClient;
69
import org.codemental.samples.webflux.java.http.adapter.controller.InboundApiController;
710

11+
import java.util.concurrent.TimeUnit;
12+
813
public class HttpConfiguration {
914

1015
@Value("${external.api.url}")
1116
private String externalApiUrl;
1217

18+
@Value("${external.channel.connection.timeout}")
19+
private Integer externalChannelConnectionTimeout;
20+
21+
@Value("${external.channel.request.timeout}")
22+
private Integer externalChannelRequestTimeout;
23+
1324
@Bean
1425
public InboundApiController inboundApiController() {
1526
return new InboundApiController();
1627
}
1728

1829
@Bean
1930
public WebClient externalApiClient() {
20-
return WebClient.create(externalApiUrl);
31+
return WebClient.builder()
32+
.baseUrl(externalApiUrl)
33+
.clientConnector(createConnector())
34+
.build();
35+
}
36+
37+
private ReactorClientHttpConnector createConnector() {
38+
return new ReactorClientHttpConnector(
39+
options -> options.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, externalChannelConnectionTimeout)
40+
.compression(true)
41+
.afterNettyContextInit(ctx -> {
42+
ctx.addHandlerLast(new ReadTimeoutHandler(externalChannelRequestTimeout, TimeUnit.MILLISECONDS));
43+
}));
2144
}
2245
}

http-adapter/http-adapter-service/src/main/resources/application.yaml

+6
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,10 @@
11
external:
2+
channel:
3+
connection:
4+
timeout: 1000
5+
request:
6+
timeout: 3000
7+
28
api:
39
url: http://localhost:9090
410

0 commit comments

Comments
 (0)