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..b82cbc338fd 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.toUri()); } 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..6a39e908420 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,6 +16,8 @@ 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; @@ -25,12 +27,14 @@ class ClientResponseImpl implements Http2ClientResponse { private final Http.Status responseStatus; private final ClientResponseHeaders responseHeaders; + private final URI lastEndpointUri; private Http2ClientStream stream; - ClientResponseImpl(Http2Headers headers, Http2ClientStream stream) { + ClientResponseImpl(Http2Headers headers, Http2ClientStream stream, URI lastEndpointUri) { this.responseStatus = headers.status(); this.responseHeaders = ClientResponseHeaders.create(headers.httpHeaders()); this.stream = stream; + this.lastEndpointUri = lastEndpointUri; } @Override @@ -48,6 +52,11 @@ public ReadableEntity entity() { return stream.entity().copy(() -> this.stream = null); } + @Override + public URI lastEndpointUri() { + return lastEndpointUri; + } + @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..954df07026e 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.toUri(), 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..d73d94d6f03 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; @@ -70,6 +71,7 @@ class ClientResponseImpl implements Http1ClientResponse { private final List trailerNames; // Media type parsing mode configured on client. private final ParserMode parserMode; + private final URI lastEndpointUri; private ClientConnection connection; private long entityLength; @@ -83,6 +85,7 @@ class ClientResponseImpl implements Http1ClientResponse { DataReader reader, MediaContext mediaContext, ParserMode parserMode, + URI lastEndpointUri, CompletableFuture whenComplete) { this.responseStatus = responseStatus; this.requestHeaders = requestHeaders; @@ -92,6 +95,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 +218,11 @@ private ReadableEntity entity(ClientRequestHeaders requestHeaders, return ReadableEntityBase.empty(); } + @Override + public URI lastEndpointUri() { + return lastEndpointUri; + } + private BufferData readEntityChunked(int estimate) { int endOfChunkSize = reader.findNewLine(256); if (endOfChunkSize == 256) {