diff --git a/nima/http2/webclient/src/main/java/io/helidon/nima/http2/webclient/ClientRequestImpl.java b/nima/http2/webclient/src/main/java/io/helidon/nima/http2/webclient/ClientRequestImpl.java index f6ec1e090bc..b999c87a212 100644 --- a/nima/http2/webclient/src/main/java/io/helidon/nima/http2/webclient/ClientRequestImpl.java +++ b/nima/http2/webclient/src/main/java/io/helidon/nima/http2/webclient/ClientRequestImpl.java @@ -286,7 +286,7 @@ UriHelper uriHelper() { private Http2ClientResponse readResponse(Http2ClientStream stream) { Http2Headers headers = stream.readHeaders(); - return new ClientResponseImpl(headers, stream); + return new ClientResponseImpl(headers, stream, uri); } private byte[] entityBytes(Object entity) { diff --git a/nima/http2/webclient/src/main/java/io/helidon/nima/http2/webclient/ClientResponseImpl.java b/nima/http2/webclient/src/main/java/io/helidon/nima/http2/webclient/ClientResponseImpl.java index f21f2766343..f798963d750 100644 --- a/nima/http2/webclient/src/main/java/io/helidon/nima/http2/webclient/ClientResponseImpl.java +++ b/nima/http2/webclient/src/main/java/io/helidon/nima/http2/webclient/ClientResponseImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 Oracle and/or its affiliates. + * Copyright (c) 2022, 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. @@ -16,21 +16,26 @@ package io.helidon.nima.http2.webclient; +import java.net.URI; + import io.helidon.common.http.ClientResponseHeaders; import io.helidon.common.http.Headers; import io.helidon.common.http.Http; import io.helidon.nima.http.media.ReadableEntity; import io.helidon.nima.http2.Http2Headers; +import io.helidon.nima.webclient.UriHelper; class ClientResponseImpl implements Http2ClientResponse { private final Http.Status responseStatus; private final ClientResponseHeaders responseHeaders; + private final UriHelper lastEndpointUri; private Http2ClientStream stream; - ClientResponseImpl(Http2Headers headers, Http2ClientStream stream) { + ClientResponseImpl(Http2Headers headers, Http2ClientStream stream, UriHelper lastEndpointUri) { this.responseStatus = headers.status(); this.responseHeaders = ClientResponseHeaders.create(headers.httpHeaders()); this.stream = stream; + this.lastEndpointUri = lastEndpointUri; } @Override @@ -48,6 +53,11 @@ public ReadableEntity entity() { return stream.entity().copy(() -> this.stream = null); } + @Override + public URI lastEndpointUri() { + return lastEndpointUri.toUri(); + } + @Override public void close() { if (stream != null) { diff --git a/nima/tests/integration/webclient/webclient/src/test/java/io/helidon/nima/webclient/http1/ClientRequestImplTest.java b/nima/tests/integration/webclient/webclient/src/test/java/io/helidon/nima/webclient/http1/ClientRequestImplTest.java index 47c3207dad7..0a4596dc049 100644 --- a/nima/tests/integration/webclient/webclient/src/test/java/io/helidon/nima/webclient/http1/ClientRequestImplTest.java +++ b/nima/tests/integration/webclient/webclient/src/test/java/io/helidon/nima/webclient/http1/ClientRequestImplTest.java @@ -300,6 +300,7 @@ void testRedirect() { try (Http1ClientResponse response = injectedHttp1client.put("/redirect") .submit("Test entity")) { assertThat(response.status(), is(Http.Status.OK_200)); + assertThat(response.lastEndpointUri().getPath(), is("/afterRedirect")); assertThat(response.as(String.class), is(EXPECTED_GET_AFTER_REDIRECT_STRING)); } } @@ -314,6 +315,7 @@ void testRedirectKeepMethod() { try (Http1ClientResponse response = injectedHttp1client.put("/redirectKeepMethod") .submit("Test entity")) { + assertThat(response.lastEndpointUri().getPath(), is("/afterRedirect")); assertThat(response.status(), is(Http.Status.NO_CONTENT_204)); } } diff --git a/nima/webclient/webclient/src/main/java/io/helidon/nima/webclient/ClientResponse.java b/nima/webclient/webclient/src/main/java/io/helidon/nima/webclient/ClientResponse.java index bdeca6fee7a..eed7d39d2d0 100644 --- a/nima/webclient/webclient/src/main/java/io/helidon/nima/webclient/ClientResponse.java +++ b/nima/webclient/webclient/src/main/java/io/helidon/nima/webclient/ClientResponse.java @@ -17,6 +17,7 @@ package io.helidon.nima.webclient; import java.io.InputStream; +import java.net.URI; import io.helidon.common.GenericType; import io.helidon.common.http.Headers; @@ -84,6 +85,13 @@ default > void source(GenericType sourceType, T source) { throw new UnsupportedOperationException("No source available for " + sourceType); } + /** + * URI of the last request. (after redirection) + * + * @return last URI + */ + URI lastEndpointUri(); + /** * Closes the response. * This may have no impact on the underlying connection. diff --git a/nima/webclient/webclient/src/main/java/io/helidon/nima/webclient/UriHelper.java b/nima/webclient/webclient/src/main/java/io/helidon/nima/webclient/UriHelper.java index 269ad56d055..bf9bd81d4e6 100644 --- a/nima/webclient/webclient/src/main/java/io/helidon/nima/webclient/UriHelper.java +++ b/nima/webclient/webclient/src/main/java/io/helidon/nima/webclient/UriHelper.java @@ -94,6 +94,15 @@ public String toString() { return scheme + "://" + authority + (path.startsWith("/") ? "" : "/") + path; } + /** + * Convert instance to {@link java.net.URI}. + * + * @return the converted URI + */ + public URI toUri() { + return URI.create(toString()); + } + /** * Scheme of this URI. * diff --git a/nima/webclient/webclient/src/main/java/io/helidon/nima/webclient/http1/ClientRequestImpl.java b/nima/webclient/webclient/src/main/java/io/helidon/nima/webclient/http1/ClientRequestImpl.java index 3bf48ce7829..d7ca0b1604a 100644 --- a/nima/webclient/webclient/src/main/java/io/helidon/nima/webclient/http1/ClientRequestImpl.java +++ b/nima/webclient/webclient/src/main/java/io/helidon/nima/webclient/http1/ClientRequestImpl.java @@ -381,6 +381,7 @@ private ClientResponseImpl invokeServices(WebClientService.Chain callChain, serviceResponse.reader(), mediaContext, clientConfig.mediaTypeParserMode(), + uri, complete); } diff --git a/nima/webclient/webclient/src/main/java/io/helidon/nima/webclient/http1/ClientResponseImpl.java b/nima/webclient/webclient/src/main/java/io/helidon/nima/webclient/http1/ClientResponseImpl.java index 62e8ef01d98..c25ec9a4cd8 100644 --- a/nima/webclient/webclient/src/main/java/io/helidon/nima/webclient/http1/ClientResponseImpl.java +++ b/nima/webclient/webclient/src/main/java/io/helidon/nima/webclient/http1/ClientResponseImpl.java @@ -16,6 +16,7 @@ package io.helidon.nima.webclient.http1; +import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.List; import java.util.ServiceLoader; @@ -42,6 +43,7 @@ import io.helidon.nima.http.media.ReadableEntityBase; import io.helidon.nima.webclient.ClientConnection; import io.helidon.nima.webclient.ClientResponseEntity; +import io.helidon.nima.webclient.UriHelper; import io.helidon.nima.webclient.http.spi.Source; import io.helidon.nima.webclient.http.spi.SourceHandlerProvider; @@ -70,6 +72,7 @@ class ClientResponseImpl implements Http1ClientResponse { private final List trailerNames; // Media type parsing mode configured on client. private final ParserMode parserMode; + private final UriHelper lastEndpointUri; private ClientConnection connection; private long entityLength; @@ -83,6 +86,7 @@ class ClientResponseImpl implements Http1ClientResponse { DataReader reader, MediaContext mediaContext, ParserMode parserMode, + UriHelper lastEndpointUri, CompletableFuture whenComplete) { this.responseStatus = responseStatus; this.requestHeaders = requestHeaders; @@ -92,6 +96,7 @@ class ClientResponseImpl implements Http1ClientResponse { this.mediaContext = mediaContext; this.parserMode = parserMode; this.channelId = connection.channelId(); + this.lastEndpointUri = lastEndpointUri; this.whenComplete = whenComplete; if (responseHeaders.contains(Header.CONTENT_LENGTH)) { @@ -214,6 +219,11 @@ private ReadableEntity entity(ClientRequestHeaders requestHeaders, return ReadableEntityBase.empty(); } + @Override + public URI lastEndpointUri() { + return lastEndpointUri.toUri(); + } + private BufferData readEntityChunked(int estimate) { int endOfChunkSize = reader.findNewLine(256); if (endOfChunkSize == 256) {