diff --git a/microprofile/server/src/test/java/io/helidon/microprofile/server/RedirectionTest.java b/microprofile/server/src/test/java/io/helidon/microprofile/server/RedirectionTest.java new file mode 100644 index 00000000000..501a104d0b0 --- /dev/null +++ b/microprofile/server/src/test/java/io/helidon/microprofile/server/RedirectionTest.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2023 Oracle and/or its affiliates. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.helidon.microprofile.server; + +import java.net.URI; + +import io.helidon.microprofile.tests.junit5.AddBean; +import io.helidon.microprofile.tests.junit5.AddExtension; +import io.helidon.microprofile.tests.junit5.DisableDiscovery; +import io.helidon.microprofile.tests.junit5.HelidonTest; + +import jakarta.enterprise.inject.spi.CDI; +import jakarta.ws.rs.GET; +import jakarta.ws.rs.Path; +import jakarta.ws.rs.client.Client; +import jakarta.ws.rs.client.ClientBuilder; +import jakarta.ws.rs.client.WebTarget; +import jakarta.ws.rs.core.Context; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.UriInfo; +import org.glassfish.jersey.ext.cdi1x.internal.CdiComponentProvider; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.MatcherAssert.assertThat; + +@HelidonTest +@DisableDiscovery +@AddBean(RedirectionTest.TestResource.class) +@AddExtension(ServerCdiExtension.class) +@AddExtension(JaxRsCdiExtension.class) +@AddExtension(CdiComponentProvider.class) +class RedirectionTest { + @Test + void streamingOutput() { + Client client = ClientBuilder.newClient(); + + int port = CDI.current().getBeanManager().getExtension(ServerCdiExtension.class).port(); + WebTarget baseTarget = client.target("http://[::1]:" + port); + + Response response = baseTarget.path("/uri") + .request() + .get(); + + assertThat(response.readEntity(String.class), is("http://[0:0:0:0:0:0:0:1]:" + port + "/uri")); + + response = baseTarget.path("/redirect") + .request() + .get(); + + assertThat(response.getStatus(), is(Response.Status.OK.getStatusCode())); + assertThat(response.readEntity(String.class), is("http://[0:0:0:0:0:0:0:1]:" + port + "/uri")); + } + + @Path("/") + public static class TestResource { + + @GET + @Path("/uri") + public String uri(@Context UriInfo uriInfo) { + return uriInfo.getRequestUri().toString(); + } + + @GET + @Path("/redirect") + public Response redirect() { + return Response + .seeOther(URI.create("/uri")) + .build(); + } + } +} \ No newline at end of file diff --git a/webserver/webserver/src/main/java/io/helidon/webserver/Request.java b/webserver/webserver/src/main/java/io/helidon/webserver/Request.java index 037cbfe0e33..711eb74319c 100644 --- a/webserver/webserver/src/main/java/io/helidon/webserver/Request.java +++ b/webserver/webserver/src/main/java/io/helidon/webserver/Request.java @@ -50,7 +50,6 @@ /** * The basic abstract implementation of {@link ServerRequest}. */ -@SuppressWarnings("deprecation") abstract class Request implements ServerRequest { private static final String TRACING_CONTENT_READ_NAME = "content-read"; @@ -383,14 +382,17 @@ private XForwardedDiscovery discoverUsingXForwarded(RequestHeaders headers, Allo private record Authority(String host, int port) { static Authority create(String hostHeader) { - int colon = hostHeader.indexOf(':'); - if (colon == -1) { - // we do not know the protocol, and there is no port defined - return new Authority(hostHeader, -1); + // this may be an IPv6 address, such as [::1]:port, or [::1] + int colon = hostHeader.lastIndexOf(':'); + int closingBrackets = hostHeader.lastIndexOf(']'); + if (colon > closingBrackets) { + // there is a port + String hostString = hostHeader.substring(0, colon); + String portString = hostHeader.substring(colon + 1); + return new Authority(hostString, Integer.parseInt(portString)); } - String hostString = hostHeader.substring(0, colon); - String portString = hostHeader.substring(colon + 1); - return new Authority(hostString, Integer.parseInt(portString)); + // there is no port + return new Authority(hostHeader, -1); } static Authority create(String scheme, String hostHeader) { int colon = hostHeader.indexOf(':'); diff --git a/webserver/webserver/src/test/java/io/helidon/webserver/RequestedUriTest.java b/webserver/webserver/src/test/java/io/helidon/webserver/RequestedUriTest.java index f2e37627da9..167de7bdf07 100644 --- a/webserver/webserver/src/test/java/io/helidon/webserver/RequestedUriTest.java +++ b/webserver/webserver/src/test/java/io/helidon/webserver/RequestedUriTest.java @@ -17,6 +17,7 @@ package io.helidon.webserver; import java.net.URI; +import java.time.Duration; import java.util.List; import java.util.Map; import java.util.Optional; @@ -29,7 +30,10 @@ import io.helidon.common.reactive.Multi; import io.helidon.tracing.SpanContext; import io.helidon.tracing.Tracer; +import io.helidon.webclient.WebClient; +import io.helidon.webclient.WebClientResponse; +import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; @@ -38,6 +42,7 @@ import static io.helidon.webserver.SocketConfiguration.RequestedUriDiscoveryType.X_FORWARDED; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; +import static org.junit.jupiter.api.Assertions.assertAll; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -63,6 +68,41 @@ void requestedUriTestForwarded(TestData testData) { assertThat(testData.testDescription(), request.requestedUri(), is(testData.expectedUriInfo())); } + @Test + void ipV6Test() { + WebServer server = WebServer.builder() + .addRouting(Routing.builder() + .get("/uri", (req, res) -> { + try { + res.send(req.requestedUri().toUri().toString()); + } catch (Exception e) { + e.printStackTrace(); + res.status(Http.Status.INTERNAL_SERVER_ERROR_500) + .send(e.getClass().getName() + ":" + e.getMessage()); + } + }) + ) + .build() + .start() + .await(); + + int port = server.port(); + WebClient client = WebClient.builder() + .followRedirects(true) + .baseUri("http://[::1]:" + port) + .build(); + + WebClientResponse response = client.get() + .path("/uri") + .request() + .await(Duration.ofSeconds(10)); + + assertAll( + () -> assertThat(response.content().as(String.class).await(), is("http://[::1]:" + port + "/uri")), + () -> assertThat(response.status(), is(Http.Status.OK_200)) + ); + } + private static Stream testData() { return Stream.of( new TestData("Forwarded header",