Skip to content

Commit e75345b

Browse files
committed
Add test for proxying options requests correclty
1 parent 4c7819b commit e75345b

File tree

3 files changed

+43
-22
lines changed

3 files changed

+43
-22
lines changed

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,7 @@ protected Mono<ClientResponse> forward(String instanceId,
7777

7878
return registry.getInstance(InstanceId.of(instanceId))
7979
.flatMap(instance -> forward(instance, uri, method, headers, bodyInserter))
80-
.switchIfEmpty(Mono.fromSupplier(
81-
() -> ClientResponse.create(HttpStatus.SERVICE_UNAVAILABLE, strategies).build()));
80+
.switchIfEmpty(Mono.fromSupplier(() -> ClientResponse.create(HttpStatus.SERVICE_UNAVAILABLE, strategies).build()));
8281
}
8382

8483
private Mono<ClientResponse> forward(Instance instance,

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

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ public class InstancesProxyController extends AbstractInstancesProxyController {
5656

5757
public InstancesProxyController(String adminContextPath,
5858
Set<String> ignoredHeaders,
59-
InstanceRegistry registry, InstanceWebClient instanceWebClient) {
59+
InstanceRegistry registry,
60+
InstanceWebClient instanceWebClient) {
6061
super(adminContextPath, ignoredHeaders, registry, instanceWebClient);
6162
}
6263

@@ -66,10 +67,9 @@ public Mono<Void> endpointProxy(@PathVariable("instanceId") String instanceId,
6667
HttpServletRequest servletRequest,
6768
HttpServletResponse servletResponse) throws IOException {
6869
ServerHttpRequest request = new ServletServerHttpRequest(servletRequest);
69-
ServerHttpResponse response = new ServletServerHttpResponse(servletResponse);
7070

71-
String pathWithinApplication = UriComponentsBuilder.fromPath(
72-
servletRequest.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE).toString()).toUriString();
71+
String pathWithinApplication = UriComponentsBuilder.fromPath(servletRequest.getAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE)
72+
.toString()).toUriString();
7373
String endpointLocalPath = getEndpointLocalPath(pathWithinApplication);
7474

7575
URI uri = UriComponentsBuilder.fromPath(endpointLocalPath)
@@ -79,18 +79,25 @@ public Mono<Void> endpointProxy(@PathVariable("instanceId") String instanceId,
7979

8080
//We need to explicitly block until the headers are recieved and write them before the async dispatch.
8181
//otherwise the FrameworkServlet will add wrong Allow header for OPTIONS request
82-
ClientResponse clientResponse = super.forward(instanceId, uri, request.getMethod(), request.getHeaders(),
83-
() -> BodyInserters.fromDataBuffers(
84-
DataBufferUtils.readInputStream(request::getBody, this.bufferFactory, 4096))).block();
82+
ClientResponse clientResponse = super.forward(instanceId,
83+
uri,
84+
request.getMethod(),
85+
request.getHeaders(),
86+
() -> BodyInserters.fromDataBuffers(DataBufferUtils.readInputStream(request::getBody,
87+
this.bufferFactory,
88+
4096
89+
))
90+
).block();
8591

92+
ServerHttpResponse response = new ServletServerHttpResponse(servletResponse);
8693
response.setStatusCode(clientResponse.statusCode());
8794
response.getHeaders().addAll(filterHeaders(clientResponse.headers().asHttpHeaders()));
8895
OutputStream responseBody = response.getBody();
8996
response.flush();
9097

9198
return clientResponse.body(BodyExtractors.toDataBuffers())
9299
.window(1)
93-
.flatMap(body -> writeAndFlush(body, responseBody))
100+
.concatMap(body -> writeAndFlush(body, responseBody))
94101
.then();
95102
}
96103

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

Lines changed: 27 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,13 @@
2929
import org.springframework.boot.actuate.endpoint.http.ActuatorMediaType;
3030
import org.springframework.context.ConfigurableApplicationContext;
3131
import org.springframework.core.ParameterizedTypeReference;
32+
import org.springframework.http.HttpMethod;
3233
import org.springframework.http.HttpStatus;
3334
import org.springframework.http.MediaType;
3435
import org.springframework.test.web.reactive.server.EntityExchangeResult;
3536
import org.springframework.test.web.reactive.server.WebTestClient;
3637
import com.github.tomakehurst.wiremock.common.FileSource;
38+
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
3739
import com.github.tomakehurst.wiremock.extension.Parameters;
3840
import com.github.tomakehurst.wiremock.extension.ResponseTransformer;
3941
import com.github.tomakehurst.wiremock.http.Fault;
@@ -49,21 +51,24 @@
4951
import static com.github.tomakehurst.wiremock.client.WireMock.get;
5052
import static com.github.tomakehurst.wiremock.client.WireMock.getRequestedFor;
5153
import static com.github.tomakehurst.wiremock.client.WireMock.ok;
54+
import static com.github.tomakehurst.wiremock.client.WireMock.options;
5255
import static com.github.tomakehurst.wiremock.client.WireMock.post;
5356
import static com.github.tomakehurst.wiremock.client.WireMock.postRequestedFor;
5457
import static com.github.tomakehurst.wiremock.client.WireMock.serverError;
5558
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
56-
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
5759
import static de.codecentric.boot.admin.server.utils.MediaType.ACTUATOR_V2_MEDIATYPE;
5860
import static org.assertj.core.api.Assertions.assertThat;
61+
import static org.springframework.http.HttpHeaders.ALLOW;
5962
import static org.springframework.http.HttpHeaders.CONTENT_TYPE;
6063

6164
public abstract class AbstractInstancesProxyControllerIntegrationTest {
6265
private static final String ACTUATOR_CONTENT_TYPE = ActuatorMediaType.V2_JSON + ";charset=UTF-8";
6366
private static ParameterizedTypeReference<Map<String, Object>> RESPONSE_TYPE = new ParameterizedTypeReference<Map<String, Object>>() {
6467
};
6568
@Rule
66-
public WireMockRule wireMock = new WireMockRule(options().dynamicPort().extensions(new ConnectionCloseExtension()));
69+
public WireMockRule wireMock = new WireMockRule(WireMockConfiguration.options()
70+
.dynamicPort()
71+
.extensions(new ConnectionCloseExtension()));
6772

6873
private static WebTestClient client;
6974
private static String instanceId;
@@ -88,31 +93,32 @@ public void setUpClient(ConfigurableApplicationContext context) {
8893
String managementUrl = "http://localhost:" + wireMock.port() + "/mgmt";
8994
//@formatter:off
9095
String actuatorIndex = "{ \"_links\": { " +
96+
"\"env\": { \"href\": \"" + managementUrl + "/env\", \"templated\": false }," +
9197
"\"test\": { \"href\": \"" + managementUrl + "/test\", \"templated\": false }," +
9298
"\"invalid\": { \"href\": \"" + managementUrl + "/invalid\", \"templated\": false }," +
9399
"\"timeout\": { \"href\": \"" + managementUrl + "/timeout\", \"templated\": false }" +
94100
" } }";
95101
//@formatter:on
96-
wireMock.stubFor(get(urlEqualTo("/mgmt/health")).willReturn(ok("{ \"status\" : \"UP\" }").withHeader(
97-
CONTENT_TYPE,
102+
wireMock.stubFor(get(urlEqualTo("/mgmt/health")).willReturn(ok("{ \"status\" : \"UP\" }").withHeader(CONTENT_TYPE,
98103
ActuatorMediaType.V2_JSON
99104
)));
100-
wireMock.stubFor(get(urlEqualTo("/mgmt/info")).willReturn(ok("{ }").withHeader(
101-
CONTENT_TYPE,
105+
wireMock.stubFor(get(urlEqualTo("/mgmt/info")).willReturn(ok("{ }").withHeader(CONTENT_TYPE,
102106
ACTUATOR_CONTENT_TYPE
103107
)));
104-
wireMock.stubFor(get(urlEqualTo("/mgmt")).willReturn(ok(actuatorIndex).withHeader(
105-
CONTENT_TYPE,
108+
wireMock.stubFor(options(urlEqualTo("/mgmt/env")).willReturn(ok().withHeader(ALLOW,
109+
HttpMethod.HEAD.name(),
110+
HttpMethod.GET.name(),
111+
HttpMethod.OPTIONS.name()
112+
)));
113+
wireMock.stubFor(get(urlEqualTo("/mgmt")).willReturn(ok(actuatorIndex).withHeader(CONTENT_TYPE,
106114
ACTUATOR_CONTENT_TYPE
107115
)));
108116
wireMock.stubFor(get(urlEqualTo("/mgmt/invalid")).willReturn(aResponse().withFault(Fault.EMPTY_RESPONSE)));
109117
wireMock.stubFor(get(urlEqualTo("/mgmt/timeout")).willReturn(ok().withFixedDelay(10000)));
110-
wireMock.stubFor(get(urlEqualTo("/mgmt/test")).willReturn(ok("{ \"foo\" : \"bar\" }").withHeader(
111-
CONTENT_TYPE,
118+
wireMock.stubFor(get(urlEqualTo("/mgmt/test")).willReturn(ok("{ \"foo\" : \"bar\" }").withHeader(CONTENT_TYPE,
112119
ACTUATOR_CONTENT_TYPE
113120
)));
114-
wireMock.stubFor(get(urlEqualTo("/mgmt/test/has%20spaces")).willReturn(ok("{ \"foo\" : \"bar-with-spaces\" }").withHeader(
115-
CONTENT_TYPE,
121+
wireMock.stubFor(get(urlEqualTo("/mgmt/test/has%20spaces")).willReturn(ok("{ \"foo\" : \"bar-with-spaces\" }").withHeader(CONTENT_TYPE,
116122
ACTUATOR_CONTENT_TYPE
117123
)));
118124
wireMock.stubFor(post(urlEqualTo("/mgmt/test")).willReturn(ok()));
@@ -162,6 +168,15 @@ public void should_return_status_502_504() {
162168

163169
@Test
164170
public void should_forward_requests() {
171+
client.options()
172+
.uri("/instances/{instanceId}/actuator/env", instanceId)
173+
.accept(ACTUATOR_V2_MEDIATYPE)
174+
.exchange()
175+
.expectStatus()
176+
.isEqualTo(HttpStatus.OK)
177+
.expectHeader()
178+
.valueEquals(ALLOW, HttpMethod.HEAD.name(), HttpMethod.GET.name(), HttpMethod.OPTIONS.name());
179+
165180
client.get()
166181
.uri("/instances/{instanceId}/actuator/test", instanceId)
167182
.accept(ACTUATOR_V2_MEDIATYPE)

0 commit comments

Comments
 (0)