Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support HTTP redirects with VertxHttpClientHTTPConduit #1617

Merged
merged 1 commit into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions docs/modules/ROOT/examples/client-server/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -89,5 +89,38 @@ quarkus.cxf.client.largeSlow.service-interface = io.quarkiverse.cxf.it.large.slo
#quarkus.cxf.codegen.wsdl2java.large-slow.package-names = io.quarkiverse.cxf.it.large.slow.generated
#quarkus.cxf.codegen.wsdl2java.large-slow.additional-params = -b,src/main/resources/wsdl/LargeSlow-async-binding.xml

quarkus.cxf.client.singleRedirect.client-endpoint-url = http://localhost:${quarkus.http.test-port}/RedirectRest/singleRedirect
quarkus.cxf.client.singleRedirect.service-interface = io.quarkiverse.cxf.it.large.slow.generated.LargeSlowService
quarkus.cxf.client.singleRedirect.auto-redirect = true

quarkus.cxf.client.doubleRedirect.client-endpoint-url = http://localhost:${quarkus.http.test-port}/RedirectRest/doubleRedirect
quarkus.cxf.client.doubleRedirect.service-interface = io.quarkiverse.cxf.it.large.slow.generated.LargeSlowService
quarkus.cxf.client.doubleRedirect.auto-redirect = true
# /RedirectRest/doubleRedirect redirects to the relative URI /RedirectRest/singleRedirect, so we have to allow that
quarkus.cxf.client.doubleRedirect.redirect-relative-uri = true

quarkus.cxf.client.tripleRedirect.client-endpoint-url = http://localhost:${quarkus.http.test-port}/RedirectRest/tripleRedirect
quarkus.cxf.client.tripleRedirect.service-interface = io.quarkiverse.cxf.it.large.slow.generated.LargeSlowService
quarkus.cxf.client.tripleRedirect.auto-redirect = true
quarkus.cxf.client.tripleRedirect.redirect-relative-uri = true

quarkus.cxf.client.noAutoRedirect.client-endpoint-url = http://localhost:${quarkus.http.test-port}/RedirectRest/singleRedirect
quarkus.cxf.client.noAutoRedirect.service-interface = io.quarkiverse.cxf.it.large.slow.generated.LargeSlowService

quarkus.cxf.client.doubleRedirectMaxRetransmits1.client-endpoint-url = http://localhost:${quarkus.http.test-port}/RedirectRest/doubleRedirect
quarkus.cxf.client.doubleRedirectMaxRetransmits1.service-interface = io.quarkiverse.cxf.it.large.slow.generated.LargeSlowService
quarkus.cxf.client.doubleRedirectMaxRetransmits1.redirect-relative-uri = true
quarkus.cxf.client.doubleRedirectMaxRetransmits1.max-retransmits = 1
quarkus.cxf.client.doubleRedirectMaxRetransmits1.auto-redirect = true

quarkus.cxf.client.doubleRedirectMaxRetransmits2.client-endpoint-url = http://localhost:${quarkus.http.test-port}/RedirectRest/doubleRedirect
quarkus.cxf.client.doubleRedirectMaxRetransmits2.service-interface = io.quarkiverse.cxf.it.large.slow.generated.LargeSlowService
quarkus.cxf.client.doubleRedirectMaxRetransmits2.redirect-relative-uri = true
quarkus.cxf.client.doubleRedirectMaxRetransmits2.max-retransmits = 2
quarkus.cxf.client.doubleRedirectMaxRetransmits2.auto-redirect = true

quarkus.cxf.client.loop.client-endpoint-url = http://localhost:${quarkus.http.test-port}/RedirectRest/loop1
quarkus.cxf.client.loop.service-interface = io.quarkiverse.cxf.it.large.slow.generated.LargeSlowService
quarkus.cxf.client.loop.auto-redirect = true

quarkus.default-locale = en_US
34 changes: 33 additions & 1 deletion docs/modules/ROOT/pages/reference/extensions/quarkus-cxf.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1655,11 +1655,39 @@ appliable). 0 is infinite.
.<| `boolean`
.<| `false`

3+a|Specifies if the consumer will automatically follow a server issued redirection. (name is not part of standard)
3+a|If `true` this CXF client will follow HTTP redirects (HTTP status codes 301, 302, 303 and 307 with `Location` response
header); otherwise HTTP redirects will not be followed.

[WARNING]
====
Enabling this option may increase memory requirements of your application substantially as request bodies will
have to be cached for retransmission.
====

See also:

* `xref:reference/extensions/quarkus-cxf.adoc#quarkus-cxf_quarkus-cxf-client-client-name-redirect-relative-uri[quarkus.cxf.client."client-name".redirect-relative-uri]`
* `xref:reference/extensions/quarkus-cxf.adoc#quarkus-cxf_quarkus-cxf-client-client-name-max-retransmits[quarkus.cxf.client."client-name".max-retransmits]`

*Environment variable*: `+++QUARKUS_CXF_CLIENT__CLIENT_NAME__AUTO_REDIRECT+++` +
*Since Quarkus CXF*: 2.2.3

.<| [[quarkus-cxf_quarkus-cxf-client-client-name-redirect-relative-uri]]`link:#quarkus-cxf_quarkus-cxf-client-client-name-redirect-relative-uri[quarkus.cxf.client."client-name".redirect-relative-uri]`
.<| `boolean`
.<| `false`

3+a|If `true` relative URIs, such as `/folder/service` received via `Location` response header will be honored when
redirecting; otherwise only absolute URIs will be honored and an exception will be thrown for relative redirects.

This is equivalent to setting `http.redirect.relative.uri` property to `true` on the CXF client request context.

See also:

* `xref:reference/extensions/quarkus-cxf.adoc#quarkus-cxf_quarkus-cxf-client-client-name-auto-redirect[quarkus.cxf.client."client-name".auto-redirect]`

*Environment variable*: `+++QUARKUS_CXF_CLIENT__CLIENT_NAME__REDIRECT_RELATIVE_URI+++` +
*Since Quarkus CXF*: 3.17.0

.<| [[quarkus-cxf_quarkus-cxf-client-client-name-max-retransmits]]`link:#quarkus-cxf_quarkus-cxf-client-client-name-max-retransmits[quarkus.cxf.client."client-name".max-retransmits]`
.<| `int`
.<| `-1`
Expand All @@ -1669,6 +1697,10 @@ the retransmit count. Each redirect may cause another retransmit for a UNAUTHORI
number indicates unlimited retransmits, although, loop protection is provided. The default is unlimited. (name is not
part of standard)

See also:

* `xref:reference/extensions/quarkus-cxf.adoc#quarkus-cxf_quarkus-cxf-client-client-name-auto-redirect[quarkus.cxf.client."client-name".auto-redirect]`

*Environment variable*: `+++QUARKUS_CXF_CLIENT__CLIENT_NAME__MAX_RETRANSMITS+++` +
*Since Quarkus CXF*: 2.2.3

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkiverse.cxf.annotation.CXFClient;
import io.quarkus.logging.Log;
import io.quarkus.test.QuarkusUnitTest;
import io.restassured.RestAssured;
import io.vertx.ext.web.Router;
Expand Down Expand Up @@ -50,7 +51,14 @@ public class Client3xx4xx5xxTest {
.overrideConfigKey("quarkus.cxf.client.endpointUri404.client-endpoint-url",
"http://localhost:8081/services/no-such-service")
.overrideConfigKey("quarkus.cxf.client.endpointUri404.service-interface", HelloService.class.getName())
.overrideConfigKey("quarkus.cxf.client.endpointUri404.logging.enabled", "true");
.overrideConfigKey("quarkus.cxf.client.endpointUri404.logging.enabled", "true")

/* Bad service endpoint URI */
.overrideConfigKey("quarkus.cxf.client.endpointUri302.client-endpoint-url",
"http://localhost:8081/vertx-redirect")
.overrideConfigKey("quarkus.cxf.client.endpointUri302.service-interface", HelloService.class.getName())
.overrideConfigKey("quarkus.cxf.client.endpointUri302.auto-redirect", "true")
.overrideConfigKey("quarkus.cxf.client.endpointUri302.logging.enabled", "true");

@CXFClient("wsdlUri200")
// Use Instance to avoid greedy initialization
Expand All @@ -62,6 +70,9 @@ public class Client3xx4xx5xxTest {
@CXFClient("endpointUri404")
Instance<HelloService> endpointUri404;

@CXFClient("endpointUri302")
Instance<HelloService> endpointUri302;

Instance<HelloService> getClient(String clientName) {
switch (clientName) {
case "wsdlUri200": {
Expand All @@ -73,6 +84,9 @@ Instance<HelloService> getClient(String clientName) {
case "endpointUri404": {
return endpointUri404;
}
case "endpointUri302": {
return endpointUri302;
}
default:
throw new IllegalArgumentException("Unexpected client name: " + clientName);
}
Expand All @@ -97,7 +111,7 @@ void endpointUri404() {
"HTTP response '404: Not Found' when communicating with http://localhost:8081/services/no-such-service");
}

public void init(@Observes Router router) {
void init(@Observes Router router) {
router.route().handler(BodyHandler.create());
router.post("/vertx-blocking/:client").blockingHandler(ctx -> {
final String person = ctx.body().asString();
Expand All @@ -114,6 +128,10 @@ public void init(@Observes Router router) {
ctx.response().setStatusCode(500).end(r.getClass().getName() + " " + r.getMessage());
}
});
router.post("/vertx-redirect").handler(ctx -> {
Log.info("Redirecting");
ctx.redirect("http://localhost:8081/services/hello");
});
}

@Test
Expand Down Expand Up @@ -187,8 +205,17 @@ void endpointUri404OnEventLoop() throws InterruptedException {

}

@Test
void endpointUri302OnWorkerThread() {
RestAssured.given()
.body("Joe")
.post("http://localhost:8081/vertx-blocking/endpointUri302")
.then()
.statusCode(200)
.body(Matchers.is("Hello Joe"));
}

private static Throwable rootCause(Exception e) {
e.printStackTrace();
Throwable result = e;
while (result.getCause() != null) {
result = result.getCause();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,14 @@ public class CXFClientInfo {
* (name is not part of standard)
*/
private final boolean autoRedirect;

/**
* If `true` relative URIs, such as `/folder/service` received via `Location` response header will be honored when
* redirecting; otherwise only absolute URIs will be honored and an exception will be thrown for relative redirects.
*
* This is equivalent to setting `http.redirect.relative.uri` property to `true` on the CXF client request context.
*/
private final boolean redirectRelativeUri;
/**
* Specifies the maximum amount of retransmits that are allowed for redirects. Retransmits for
* authorization is included in the retransmit count. Each redirect may cause another
Expand Down Expand Up @@ -218,6 +226,7 @@ public CXFClientInfo(CXFClientData other, CxfConfig cxfConfig, CxfClientConfig c
this.receiveTimeout = config.receiveTimeout();
this.connectionRequestTimeout = config.connectionRequestTimeout();
this.autoRedirect = config.autoRedirect();
this.redirectRelativeUri = config.redirectRelativeUri();
this.maxRetransmits = config.maxRetransmits();
this.allowChunking = config.allowChunking();
this.chunkingThreshold = config.chunkingThreshold();
Expand Down Expand Up @@ -473,6 +482,10 @@ public boolean isAutoRedirect() {
return autoRedirect;
}

public boolean isRedirectRelativeUri() {
return redirectRelativeUri;
}

public int getMaxRetransmits() {
return maxRetransmits;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,24 +187,65 @@ public interface CxfClientConfig {
@WithDefault("60000")
public long connectionRequestTimeout();

// The formatter breaks the list with long items
// @formatter:off
/**
* Specifies if the consumer will automatically follow a server issued redirection. (name is not part of standard)
* If `true` this CXF client will follow HTTP redirects (HTTP status codes 301, 302, 303 and 307 with `Location` response
* header); otherwise HTTP redirects will not be followed.
*
* [WARNING]
* ====
* Enabling this option may increase memory requirements of your application substantially as request bodies will
* have to be cached for retransmission.
* ====
*
* See also:
*
* * `xref:reference/extensions/quarkus-cxf.adoc#quarkus-cxf_quarkus-cxf-client-client-name-redirect-relative-uri[quarkus.cxf.client."client-name".redirect-relative-uri]`
* * `xref:reference/extensions/quarkus-cxf.adoc#quarkus-cxf_quarkus-cxf-client-client-name-max-retransmits[quarkus.cxf.client."client-name".max-retransmits]`
*
* @since 2.2.3
* @asciidoclet
*/
// @formatter:on
@WithDefault("false")
public boolean autoRedirect();

// The formatter breaks the list with long items
// @formatter:off
/**
* If `true` relative URIs, such as `/folder/service` received via `Location` response header will be honored when
* redirecting; otherwise only absolute URIs will be honored and an exception will be thrown for relative redirects.
*
* This is equivalent to setting `http.redirect.relative.uri` property to `true` on the CXF client request context.
*
* See also:
*
* * `xref:reference/extensions/quarkus-cxf.adoc#quarkus-cxf_quarkus-cxf-client-client-name-auto-redirect[quarkus.cxf.client."client-name".auto-redirect]`
*
* @since 3.17.0
* @asciidoclet
*/
// @formatter:on
@WithDefault("false")
public boolean redirectRelativeUri();

// The formatter breaks the list with long items
// @formatter:off
/**
* Specifies the maximum amount of retransmits that are allowed for redirects. Retransmits for authorization is included in
* the retransmit count. Each redirect may cause another retransmit for a UNAUTHORIZED response code, ie. 401. Any negative
* number indicates unlimited retransmits, although, loop protection is provided. The default is unlimited. (name is not
* part of standard)
*
* See also:
*
* * `xref:reference/extensions/quarkus-cxf.adoc#quarkus-cxf_quarkus-cxf-client-client-name-auto-redirect[quarkus.cxf.client."client-name".auto-redirect]`
*
* @since 2.2.3
* @asciidoclet
*/
// @formatter:on
@WithDefault("-1")
public int maxRetransmits();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import io.quarkiverse.cxf.annotation.CXFClient;
import io.quarkiverse.cxf.logging.LoggingFactoryCustomizer;
import io.quarkiverse.cxf.vertx.http.client.HttpClientPool;
import io.quarkiverse.cxf.vertx.http.client.VertxHttpClientHTTPConduit;
import io.vertx.core.Vertx;

/**
Expand Down Expand Up @@ -178,6 +179,9 @@ private Object produceCxfClient(CXFClientInfo cxfClientInfo) {
props.put(WSAContextUtils.DECOUPLED_ENDPOINT_BASE_PROPERTY, value);
}
}
if (cxfClientInfo.isRedirectRelativeUri()) {
props.put(VertxHttpClientHTTPConduit.AUTO_REDIRECT_ALLOW_REL_URI, Boolean.TRUE);
}

loggingFactoryCustomizer.customize(cxfClientInfo, factory);
customizers.forEach(customizer -> customizer.customize(cxfClientInfo, factory));
Expand Down
Loading
Loading