Skip to content

Commit

Permalink
3.x: Correctly handle IPv6 addresses for requested URI. (#7469)
Browse files Browse the repository at this point in the history
* Correctly handle IPv6 addresses for requested URI.
* Validate redirection in Jersey works as expected
  • Loading branch information
tomas-langer authored Aug 27, 2023
1 parent 7f0ad54 commit 2366fbc
Show file tree
Hide file tree
Showing 3 changed files with 136 additions and 8 deletions.
Original file line number Diff line number Diff line change
@@ -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();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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(':');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

Expand All @@ -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;

Expand All @@ -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> testData() {
return Stream.of(
new TestData("Forwarded header",
Expand Down

0 comments on commit 2366fbc

Please sign in to comment.