Skip to content

Commit

Permalink
Support HTTP redirects with VertxHttpClientHTTPConduit, fix #1609
Browse files Browse the repository at this point in the history
  • Loading branch information
ppalaga committed Nov 26, 2024
1 parent 0e78651 commit fb3d50d
Show file tree
Hide file tree
Showing 15 changed files with 1,184 additions and 474 deletions.
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

0 comments on commit fb3d50d

Please sign in to comment.