Skip to content

Commit 86e2178

Browse files
committed
Fix flaky tests
Force the connections in wiremock tests to be closed. See wiremock/wiremock#485
1 parent 3eae8ab commit 86e2178

File tree

8 files changed

+54
-42
lines changed

8 files changed

+54
-42
lines changed

spring-boot-admin-server/src/main/java/de/codecentric/boot/admin/server/config/AdminServerWebConfiguration.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,11 @@ public ReactiveRestApiConfiguration(AdminServerProperties adminServerProperties)
7878
@Bean
7979
@ConditionalOnMissingBean
8080
public de.codecentric.boot.admin.server.web.reactive.InstancesProxyController instancesProxyController(
81-
82-
InstanceRegistry instanceRegistry, InstanceWebClient instanceWebClient) {
81+
InstanceRegistry instanceRegistry,
82+
InstanceWebClient instanceWebClient) {
8383
return new de.codecentric.boot.admin.server.web.reactive.InstancesProxyController(
8484
adminServerProperties.getContextPath(), adminServerProperties.getInstanceProxy().getIgnoredHeaders(),
85-
instanceRegistry, instanceWebClient, adminServerProperties.getMonitor().getReadTimeout());
85+
instanceRegistry, instanceWebClient);
8686
}
8787

8888
@Bean
@@ -110,8 +110,7 @@ public ServletRestApiConfirguation(AdminServerProperties adminServerProperties)
110110
public InstancesProxyController instancesProxyController(InstanceRegistry instanceRegistry,
111111
InstanceWebClient instanceWebClient) {
112112
return new InstancesProxyController(adminServerProperties.getContextPath(),
113-
adminServerProperties.getInstanceProxy().getIgnoredHeaders(), instanceRegistry, instanceWebClient,
114-
adminServerProperties.getMonitor().getReadTimeout());
113+
adminServerProperties.getInstanceProxy().getIgnoredHeaders(), instanceRegistry, instanceWebClient);
115114
}
116115

117116
@Bean

spring-boot-admin-server/src/main/java/de/codecentric/boot/admin/server/web/AbstractInstancesProxyController.java

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,12 @@
2121
import de.codecentric.boot.admin.server.services.InstanceRegistry;
2222
import de.codecentric.boot.admin.server.web.client.InstanceWebClient;
2323
import de.codecentric.boot.admin.server.web.client.exception.ResolveEndpointException;
24+
import io.netty.handler.timeout.ReadTimeoutException;
2425
import reactor.core.publisher.Mono;
2526

2627
import java.io.IOException;
2728
import java.net.ConnectException;
2829
import java.net.URI;
29-
import java.time.Duration;
3030
import java.util.Arrays;
3131
import java.util.Map;
3232
import java.util.Set;
@@ -55,21 +55,17 @@ public class AbstractInstancesProxyController {
5555
private final InstanceRegistry registry;
5656
private final InstanceWebClient instanceWebClient;
5757
private final Set<String> ignoredHeaders;
58-
private final Duration readTimeout;
5958
private final ExchangeStrategies strategies = ExchangeStrategies.withDefaults();
6059

6160
public AbstractInstancesProxyController(String adminContextPath,
6261
Set<String> ignoredHeaders,
63-
InstanceRegistry registry,
64-
InstanceWebClient instanceWebClient,
65-
Duration readTimeout) {
62+
InstanceRegistry registry, InstanceWebClient instanceWebClient) {
6663
this.ignoredHeaders = Stream.concat(ignoredHeaders.stream(), Arrays.stream(HOP_BY_HOP_HEADERS))
6764
.map(String::toLowerCase)
6865
.collect(Collectors.toSet());
6966
this.registry = registry;
7067
this.instanceWebClient = instanceWebClient;
7168
this.realRequestMappingPath = adminContextPath + REQUEST_MAPPING_PATH;
72-
this.readTimeout = readTimeout;
7369
}
7470

7571
protected Mono<ClientResponse> forward(String instanceId,
@@ -104,17 +100,17 @@ private Mono<ClientResponse> forward(Instance instance,
104100
}
105101
}
106102

107-
return headersSpec.exchange().timeout(this.readTimeout, Mono.fromSupplier(() -> {
103+
return headersSpec.exchange().onErrorResume(ReadTimeoutException.class, ex -> Mono.fromSupplier(() -> {
108104
log.trace("Timeout for Proxy-Request for instance {} with URL '{}'", instance.getId(), uri);
109105
return ClientResponse.create(HttpStatus.GATEWAY_TIMEOUT, strategies).build();
110106
})).onErrorResume(ResolveEndpointException.class, ex -> Mono.fromSupplier(() -> {
111107
log.trace("No Endpoint found for Proxy-Request for instance {} with URL '{}'", instance.getId(), uri);
112108
return ClientResponse.create(HttpStatus.NOT_FOUND, strategies).build();
113109
})).onErrorResume(IOException.class, ex -> Mono.fromSupplier(() -> {
114-
log.trace("Error for Proxy-Request for instance {} with URL '{}' errored", instance.getId(), uri, ex);
110+
log.trace("Proxy-Request for instance {} with URL '{}' errored", instance.getId(), uri, ex);
115111
return ClientResponse.create(HttpStatus.BAD_GATEWAY, strategies).build();
116112
})).onErrorResume(ConnectException.class, ex -> Mono.fromSupplier(() -> {
117-
log.trace("Error for Proxy-Request for instance {} with URL '{}' timed out", instance.getId(), uri, ex);
113+
log.trace("Connect for Proxy-Request for instance {} with URL '{}' failed", instance.getId(), uri, ex);
118114
return ClientResponse.create(HttpStatus.BAD_GATEWAY, strategies).build();
119115
}));
120116
}

spring-boot-admin-server/src/main/java/de/codecentric/boot/admin/server/web/client/InstanceWebClient.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,23 +63,23 @@ public InstanceWebClient(WebClient webClient, HttpHeadersProvider httpHeadersPro
6363
}
6464

6565
public WebClient instance(Mono<Instance> instance) {
66-
return webClient.mutate()//
67-
.filters(filters -> filters.add(0, InstanceExchangeFilterFunctions.setInstance(instance)))//
66+
return webClient.mutate()
67+
.filters(filters -> filters.add(0, InstanceExchangeFilterFunctions.setInstance(instance)))
6868
.build();
6969
}
7070

7171
public WebClient instance(Instance instance) {
72-
return webClient.mutate()//
73-
.filters(filters -> filters.add(0, InstanceExchangeFilterFunctions.setInstance(instance)))//
72+
return webClient.mutate()
73+
.filters(filters -> filters.add(0, InstanceExchangeFilterFunctions.setInstance(instance)))
7474
.build();
7575
}
7676

7777
private static WebClient createDefaultWebClient(Duration connectTimeout,
7878
Duration readTimeout,
7979
WebClientCustomizer customizer) {
8080
ReactorClientHttpConnector connector = new ReactorClientHttpConnector(
81-
options -> options.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, (int) connectTimeout.toMillis())//
82-
.compression(true)//
81+
options -> options.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, (int) connectTimeout.toMillis())
82+
.compression(true)
8383
.afterNettyContextInit(ctx -> {
8484
ctx.addHandlerLast(
8585
new ReadTimeoutHandler(readTimeout.toMillis(), TimeUnit.MILLISECONDS));

spring-boot-admin-server/src/main/java/de/codecentric/boot/admin/server/web/reactive/InstancesProxyController.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
import reactor.core.publisher.Mono;
2424

2525
import java.net.URI;
26-
import java.time.Duration;
2726
import java.util.Set;
2827
import org.springframework.http.server.reactive.ServerHttpRequest;
2928
import org.springframework.http.server.reactive.ServerHttpResponse;
@@ -41,10 +40,8 @@
4140
public class InstancesProxyController extends AbstractInstancesProxyController {
4241
public InstancesProxyController(String adminContextPath,
4342
Set<String> ignoredHeaders,
44-
InstanceRegistry registry,
45-
InstanceWebClient instanceWebClient,
46-
Duration readTimeout) {
47-
super(adminContextPath, ignoredHeaders, registry, instanceWebClient, readTimeout);
43+
InstanceRegistry registry, InstanceWebClient instanceWebClient) {
44+
super(adminContextPath, ignoredHeaders, registry, instanceWebClient);
4845
}
4946

5047
@RequestMapping(path = REQUEST_MAPPING_PATH, method = {RequestMethod.GET, RequestMethod.HEAD, RequestMethod.POST, RequestMethod.PUT, RequestMethod.PATCH, RequestMethod.DELETE, RequestMethod.OPTIONS})

spring-boot-admin-server/src/main/java/de/codecentric/boot/admin/server/web/servlet/InstancesProxyController.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import java.io.IOException;
2727
import java.io.OutputStream;
2828
import java.net.URI;
29-
import java.time.Duration;
3029
import java.util.Set;
3130
import javax.servlet.http.HttpServletRequest;
3231
import javax.servlet.http.HttpServletResponse;
@@ -57,10 +56,8 @@ public class InstancesProxyController extends AbstractInstancesProxyController {
5756

5857
public InstancesProxyController(String adminContextPath,
5958
Set<String> ignoredHeaders,
60-
InstanceRegistry registry,
61-
InstanceWebClient instanceWebClient,
62-
Duration readTimeout) {
63-
super(adminContextPath, ignoredHeaders, registry, instanceWebClient, readTimeout);
59+
InstanceRegistry registry, InstanceWebClient instanceWebClient) {
60+
super(adminContextPath, ignoredHeaders, registry, instanceWebClient);
6461
}
6562

6663
@ResponseBody

spring-boot-admin-server/src/test/java/de/codecentric/boot/admin/server/web/AbstractInstancesProxyControllerIntegrationTest.java

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,14 @@
3131
import org.springframework.http.MediaType;
3232
import org.springframework.test.web.reactive.server.EntityExchangeResult;
3333
import org.springframework.test.web.reactive.server.WebTestClient;
34+
import com.github.tomakehurst.wiremock.common.FileSource;
35+
import com.github.tomakehurst.wiremock.extension.Parameters;
36+
import com.github.tomakehurst.wiremock.extension.ResponseTransformer;
3437
import com.github.tomakehurst.wiremock.http.Fault;
38+
import com.github.tomakehurst.wiremock.http.HttpHeader;
39+
import com.github.tomakehurst.wiremock.http.HttpHeaders;
40+
import com.github.tomakehurst.wiremock.http.Request;
41+
import com.github.tomakehurst.wiremock.http.Response;
3542
import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
3643

3744
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
@@ -54,11 +61,8 @@ public abstract class AbstractInstancesProxyControllerIntegrationTest {
5461
private static ParameterizedTypeReference<Map<String, Object>> RESPONSE_TYPE = new ParameterizedTypeReference<Map<String, Object>>() {
5562
};
5663
@ClassRule
57-
public static WireMockClassRule wireMock = new WireMockClassRule(options().dynamicPort()
58-
.asynchronousResponseEnabled(true)
59-
.asynchronousResponseThreads(10)
60-
.jettyAcceptors(10)
61-
.containerThreads(20));
64+
public static WireMockClassRule wireMock = new WireMockClassRule(
65+
options().dynamicPort().extensions(new ConnectionCloseExtension()));
6266
private static WebTestClient client;
6367
private static String instanceId;
6468

@@ -87,12 +91,12 @@ public static void setUpClient(ConfigurableApplicationContext context) {
8791
wireMock.stubFor(get(urlEqualTo("/mgmt/timeout")).willReturn(ok().withFixedDelay(10000)));
8892
wireMock.stubFor(get(urlEqualTo("/mgmt/test")).willReturn(
8993
ok("{ \"foo\" : \"bar\" }").withHeader(CONTENT_TYPE, ACTUATOR_CONTENT_TYPE)));
94+
wireMock.stubFor(get(urlEqualTo("/mgmt/test/has%20spaces")).willReturn(
95+
ok("{ \"foo\" : \"bar-with-spaces\" }").withHeader(CONTENT_TYPE, ACTUATOR_CONTENT_TYPE)));
9096
wireMock.stubFor(post(urlEqualTo("/mgmt/test")).willReturn(ok()));
9197
wireMock.stubFor(delete(urlEqualTo("/mgmt/test")).willReturn(
9298
serverError().withBody("{\"error\": \"You're doing it wrong!\"}")
9399
.withHeader(CONTENT_TYPE, ACTUATOR_CONTENT_TYPE)));
94-
wireMock.stubFor(get(urlEqualTo("/mgmt/test/has%20spaces")).willReturn(
95-
ok("{ \"foo\" : \"bar-with-spaces\" }").withHeader(CONTENT_TYPE, ACTUATOR_CONTENT_TYPE)));
96100

97101
instanceId = registerInstance(managementUrl);
98102
}
@@ -139,7 +143,10 @@ public void should_forward_requests() {
139143
.uri("/instances/{instanceId}/actuator/test", instanceId)
140144
.accept(ACTUATOR_V2_MEDIATYPE)
141145
.exchange()
142-
.expectStatus().isEqualTo(HttpStatus.OK).expectBody(String.class).isEqualTo("{ \"foo\" : \"bar\" }");
146+
.expectStatus()
147+
.isEqualTo(HttpStatus.OK)
148+
.expectBody(String.class)
149+
.isEqualTo("{ \"foo\" : \"bar\" }");
143150

144151
client.post()
145152
.uri("/instances/{instanceId}/actuator/test", instanceId)
@@ -216,3 +223,20 @@ private static Flux<Map<String, Object>> getEventStream() {
216223
//@formatter:on
217224
}
218225
}
226+
227+
//Force the connections to be closed...
228+
//see https://github.com/tomakehurst/wiremock/issues/485
229+
class ConnectionCloseExtension extends ResponseTransformer {
230+
@Override
231+
public Response transform(Request request, Response response, FileSource files, Parameters parameters) {
232+
return Response.Builder.like(response)
233+
.headers(HttpHeaders.copyOf(response.getHeaders())
234+
.plus(new HttpHeader("Connection", "Close")))
235+
.build();
236+
}
237+
238+
@Override
239+
public String getName() {
240+
return "ConnectionCloseExtension";
241+
}
242+
}

spring-boot-admin-server/src/test/java/de/codecentric/boot/admin/server/web/client/InstanceExchangeFilterFunctionsTest.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@
3434
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
3535

3636
import static de.codecentric.boot.admin.server.web.client.InstanceExchangeFilterFunctions.ATTRIBUTE_ENDPOINT;
37-
import static io.netty.handler.codec.http.HttpHeaders.Values.APPLICATION_JSON;
3837
import static org.springframework.http.HttpHeaders.CONTENT_TYPE;
38+
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
3939

4040
public class InstanceExchangeFilterFunctionsTest {
4141
private static final DefaultDataBufferFactory BUFFER_FACTORY = new DefaultDataBufferFactory();
@@ -72,8 +72,7 @@ public void should_convert_json() {
7272
ClientRequest request = ClientRequest.create(HttpMethod.GET, URI.create("/test"))
7373
.attribute(ATTRIBUTE_ENDPOINT, "test")
7474
.build();
75-
ClientResponse response = ClientResponse.create(HttpStatus.OK)
76-
.header(CONTENT_TYPE, APPLICATION_JSON)
75+
ClientResponse response = ClientResponse.create(HttpStatus.OK).header(CONTENT_TYPE, APPLICATION_JSON_VALUE)
7776
.body(Flux.just(ORIGINAL))
7877
.build();
7978

spring-boot-admin-server/src/test/resources/application.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@ spring:
77
enabled: false
88
logging:
99
level:
10-
de.codecentric: DEBUG
10+
de.codecentric: DEBUG

0 commit comments

Comments
 (0)