From 0bd10b3f440c17911a40b4422b4991ff1413c9dd Mon Sep 17 00:00:00 2001 From: Santiago Pericasgeertsen Date: Tue, 27 Jun 2023 10:41:15 -0400 Subject: [PATCH 1/2] Support for lastEndpointUri() in Webclient responses. This method can be used to obtain the final redirect URI of the corresponding request. Tests updated. --- .../nima/http2/webclient/ClientRequestImpl.java | 2 +- .../nima/http2/webclient/ClientResponseImpl.java | 13 +++++++++++-- .../nima/webclient/http1/ClientRequestImplTest.java | 2 ++ .../io/helidon/nima/webclient/ClientResponse.java | 8 ++++++++ .../java/io/helidon/nima/webclient/UriHelper.java | 9 +++++++++ .../nima/webclient/http1/ClientRequestImpl.java | 1 + .../nima/webclient/http1/ClientResponseImpl.java | 9 +++++++++ 7 files changed, 41 insertions(+), 3 deletions(-) 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) { From e6a5784738b5332f9203c438700d8b4a27d2227c Mon Sep 17 00:00:00 2001 From: Santiago Pericasgeertsen Date: Tue, 27 Jun 2023 10:51:44 -0400 Subject: [PATCH 2/2] Lazy conversion to URI. --- .../io/helidon/nima/http2/webclient/ClientRequestImpl.java | 2 +- .../helidon/nima/http2/webclient/ClientResponseImpl.java | 7 ++++--- .../io/helidon/nima/webclient/http1/ClientRequestImpl.java | 2 +- .../helidon/nima/webclient/http1/ClientResponseImpl.java | 7 ++++--- 4 files changed, 10 insertions(+), 8 deletions(-) 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 b82cbc338fd..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, uri.toUri()); + 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 6a39e908420..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 @@ -23,14 +23,15 @@ 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 URI lastEndpointUri; + private final UriHelper lastEndpointUri; private Http2ClientStream stream; - ClientResponseImpl(Http2Headers headers, Http2ClientStream stream, URI lastEndpointUri) { + ClientResponseImpl(Http2Headers headers, Http2ClientStream stream, UriHelper lastEndpointUri) { this.responseStatus = headers.status(); this.responseHeaders = ClientResponseHeaders.create(headers.httpHeaders()); this.stream = stream; @@ -54,7 +55,7 @@ public ReadableEntity entity() { @Override public URI lastEndpointUri() { - return lastEndpointUri; + return lastEndpointUri.toUri(); } @Override 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 954df07026e..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,7 +381,7 @@ private ClientResponseImpl invokeServices(WebClientService.Chain callChain, serviceResponse.reader(), mediaContext, clientConfig.mediaTypeParserMode(), - uri.toUri(), + 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 d73d94d6f03..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 @@ -43,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; @@ -71,7 +72,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 final UriHelper lastEndpointUri; private ClientConnection connection; private long entityLength; @@ -85,7 +86,7 @@ class ClientResponseImpl implements Http1ClientResponse { DataReader reader, MediaContext mediaContext, ParserMode parserMode, - URI lastEndpointUri, + UriHelper lastEndpointUri, CompletableFuture whenComplete) { this.responseStatus = responseStatus; this.requestHeaders = requestHeaders; @@ -220,7 +221,7 @@ private ReadableEntity entity(ClientRequestHeaders requestHeaders, @Override public URI lastEndpointUri() { - return lastEndpointUri; + return lastEndpointUri.toUri(); } private BufferData readEntityChunked(int estimate) {