Skip to content

Commit

Permalink
2.x: Correctly handle IPv6 addresses for requested URI. (helidon-io#7479
Browse files Browse the repository at this point in the history
)

* 3.x: Correctly handle IPv6 addresses for requested URI. (helidon-io#7469)

* Correctly handle IPv6 addresses for requested URI.
* Validate redirection in Jersey works as expected

Signed-off-by: Tomas Langer <tomas.langer@oracle.com>

* 2.x: Correctly handle IPv6 addresses for requested URI. (cherry-pick helidon-io#7469)

* Correctly handle IPv6 addresses for requested URI.
* Validate redirection in Jersey works as expected

Signed-off-by: Tomas Langer <tomas.langer@oracle.com>

* Use javax instead of jakarta (backported...)

Signed-off-by: Tomas Langer <tomas.langer@oracle.com>

---------

Signed-off-by: Tomas Langer <tomas.langer@oracle.com>
  • Loading branch information
tomas-langer authored Aug 28, 2023
1 parent 3c0c45a commit 97de830
Show file tree
Hide file tree
Showing 3 changed files with 143 additions and 18 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 javax.enterprise.inject.spi.CDI;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.WebTarget;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.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 @@ -53,7 +53,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 @@ -390,14 +389,17 @@ private static class Authority {
private final 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 @@ -16,29 +16,31 @@

package io.helidon.webserver;

import java.net.URI;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;

import io.helidon.common.configurable.AllowList;
import io.helidon.common.http.Http;
import io.helidon.common.http.HttpRequest;
import io.helidon.common.http.UriInfo;
import io.helidon.common.reactive.Multi;

import io.helidon.webclient.WebClient;
import io.helidon.webclient.WebClientResponse;
import io.opentracing.SpanContext;
import io.opentracing.Tracer;
import io.opentracing.util.GlobalTracer;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;

import static io.helidon.webserver.SocketConfiguration.RequestedUriDiscoveryType.FORWARDED;
import static io.helidon.webserver.SocketConfiguration.RequestedUriDiscoveryType.HOST;
import static io.helidon.webserver.SocketConfiguration.RequestedUriDiscoveryType.X_FORWARDED;
import java.net.URI;
import java.time.Duration;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Stream;

import static io.helidon.webserver.SocketConfiguration.RequestedUriDiscoveryType.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

Expand All @@ -64,6 +66,41 @@ void requestedUriTestForwarded(TestData testData) {
assertThat(testData.testDescription(), request.requestedUri(), is(testData.expectedUriInfo()));
}

@Test
void ipV6Test() {
WebServer server = WebServer.builder()
.routing(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 97de830

Please sign in to comment.