Skip to content

Commit

Permalink
added quarkus property to set the http.redirect.max.same.uri.count pr…
Browse files Browse the repository at this point in the history
…operty
  • Loading branch information
dcheng1248 authored and ppalaga committed Dec 20, 2024
1 parent 8e265dd commit 66dc7ba
Show file tree
Hide file tree
Showing 9 changed files with 114 additions and 57 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,13 @@ 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.selfRedirect.client-endpoint-url = http://localhost:${quarkus.http.test-port}/RedirectRest/selfRedirect
quarkus.cxf.client.selfRedirect.service-interface = io.quarkiverse.cxf.it.large.slow.generated.LargeSlowService
quarkus.cxf.client.selfRedirect.redirect-relative-uri = true
quarkus.cxf.client.selfRedirect.max-retransmits = 4
quarkus.cxf.client.selfRedirect.max-same-uri = 3
quarkus.cxf.client.selfRedirect.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
Expand Down
17 changes: 17 additions & 0 deletions docs/modules/ROOT/pages/reference/extensions/quarkus-cxf.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -1708,6 +1708,23 @@ See also:
*Environment variable*: `+++QUARKUS_CXF_CLIENT__CLIENT_NAME__MAX_RETRANSMITS+++` +
*Since Quarkus CXF*: 2.2.3

.<| [[quarkus-cxf_quarkus-cxf-client-client-name-max-same-uri]]`link:#quarkus-cxf_quarkus-cxf-client-client-name-max-same-uri[quarkus.cxf.client."client-name".max-same-uri]`
.<| `int`
.<| `0`

3+a|Specifies the maximum amount of retransmits to the same uri 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]`

*Environment variable*: `+++QUARKUS_CXF_CLIENT__CLIENT_NAME__MAX_SAME_URI+++` +
*Since Quarkus CXF*: 3.17.4

.<| [[quarkus-cxf_quarkus-cxf-client-client-name-allow-chunking]]`link:#quarkus-cxf_quarkus-cxf-client-client-name-allow-chunking[quarkus.cxf.client."client-name".allow-chunking]`
.<| `boolean`
.<| `true`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
package io.quarkiverse.cxf;

import java.net.URL;
import java.security.KeyStore;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.Optional;
import java.util.*;

import org.apache.cxf.annotations.SchemaValidation.SchemaValidationType;
import org.apache.cxf.transports.http.configuration.ConnectionType;
Expand Down Expand Up @@ -88,6 +83,16 @@ public class CXFClientInfo {
* (name is not part of standard)
*/
private final int maxRetransmits;
/**
* Specifies the maximum amount of retransmits to the same uri 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 0 retransmits
* to the same uri allowed.
* The default is 0.
*
* This is equivalent to setting `http.redirect.max.same.uri.count` property on the CXF client request context.
*/
private final int maxSameUri;
/**
* If true, the client is free to use chunking streams if it wants, but it is not
* required to use chunking streams. If false, the client
Expand Down Expand Up @@ -228,6 +233,7 @@ public CXFClientInfo(CXFClientData other, CxfConfig cxfConfig, CxfClientConfig c
this.autoRedirect = config.autoRedirect();
this.redirectRelativeUri = config.redirectRelativeUri();
this.maxRetransmits = config.maxRetransmits();
this.maxSameUri = config.maxSameUri();
this.allowChunking = config.allowChunking();
this.chunkingThreshold = config.chunkingThreshold();
this.chunkLength = config.chunkLength();
Expand All @@ -248,7 +254,6 @@ public CXFClientInfo(CXFClientData other, CxfConfig cxfConfig, CxfClientConfig c
this.proxyServerType = config.proxyServerType();
this.proxyUsername = config.proxyUsername().orElse(null);
this.proxyPassword = config.proxyPassword().orElse(null);

this.tlsConfigurationName = config.tlsConfigurationName().orElse(null);
this.tlsConfiguration = tlsConfiguration(vertx, config, configKey);
this.hostnameVerifier = config.hostnameVerifier().orElse(null);
Expand Down Expand Up @@ -490,6 +495,10 @@ public int getMaxRetransmits() {
return maxRetransmits;
}

public int getMaxSameUri() {
return maxSameUri;
}

public boolean isAllowChunking() {
return allowChunking;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,23 @@ public interface CxfClientConfig {
@WithDefault("-1")
public int maxRetransmits();

/**
* Specifies the maximum amount of retransmits to the same uri 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 3.17.4
* @asciidoclet
*/
@WithDefault("0")
public int maxSameUri();

/**
* If true, the client is free to use chunking streams if it wants, but it is not required to use chunking streams. If
* false, the client must use regular, non-chunked requests in all cases.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,10 @@ private Object produceCxfClient(CXFClientInfo cxfClientInfo) {
if (cxfClientInfo.isRedirectRelativeUri()) {
props.put(VertxHttpClientHTTPConduit.AUTO_REDIRECT_ALLOW_REL_URI, Boolean.TRUE);
}
{
final int value = cxfClientInfo.getMaxSameUri();
props.put(VertxHttpClientHTTPConduit.AUTO_REDIRECT_MAX_SAME_URI_COUNT, value);
}

loggingFactoryCustomizer.customize(cxfClientInfo, factory);
customizers.forEach(customizer -> customizer.customize(cxfClientInfo, factory));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,10 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PushbackInputStream;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.*;
import java.net.Proxy.Type;
import java.net.SocketTimeoutException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.Map.Entry;
import java.util.Queue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
Expand All @@ -61,12 +49,7 @@
import org.apache.cxf.service.model.EndpointInfo;
import org.apache.cxf.transport.Conduit;
import org.apache.cxf.transport.MessageObserver;
import org.apache.cxf.transport.http.Address;
import org.apache.cxf.transport.http.Cookies;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transport.http.HTTPException;
import org.apache.cxf.transport.http.Headers;
import org.apache.cxf.transport.http.MessageTrustDecider;
import org.apache.cxf.transport.http.*;
import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
import org.apache.cxf.version.Version;
import org.apache.cxf.ws.addressing.EndpointReferenceType;
Expand All @@ -80,19 +63,9 @@
import io.quarkus.arc.InstanceHandle;
import io.quarkus.logging.Log;
import io.quarkus.runtime.BlockingOperationControl;
import io.vertx.core.AsyncResult;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.MultiMap;
import io.vertx.core.Promise;
import io.vertx.core.*;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.http.HttpClient;
import io.vertx.core.http.HttpClientRequest;
import io.vertx.core.http.HttpClientResponse;
import io.vertx.core.http.HttpHeaders;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpVersion;
import io.vertx.core.http.RequestOptions;
import io.vertx.core.http.*;
import io.vertx.core.http.impl.HttpUtils;
import io.vertx.core.net.ProxyOptions;
import io.vertx.core.net.ProxyType;
Expand All @@ -104,7 +77,7 @@ public class VertxHttpClientHTTPConduit extends HTTPConduit {
private static final Logger log = Logger.getLogger(VertxHttpClientHTTPConduit.class);
public static final String USE_ASYNC = "use.async.http.conduit";
public static final String ENABLE_HTTP2 = "org.apache.cxf.transports.http2.enabled";
private static final String AUTO_REDIRECT_MAX_SAME_URI_COUNT = "http.redirect.max.same.uri.count";
public static final String AUTO_REDIRECT_MAX_SAME_URI_COUNT = "http.redirect.max.same.uri.count";
private static final String AUTO_REDIRECT_SAME_HOST_ONLY = "http.redirect.same.host.only";
private static final String AUTO_REDIRECT_ALLOWED_URI = "http.redirect.allowed.uri";
public static final String AUTO_REDIRECT_ALLOW_REL_URI = "http.redirect.relative.uri";
Expand Down Expand Up @@ -789,12 +762,14 @@ private static void detectRedirectLoop(String conduitName,
&& redirects.stream().filter(newURL::equals).count() > maxSameURICount.longValue()) {
final String msg = "Redirect loop detected on Conduit '"
+ conduitName + "' (with http.redirect.max.same.uri.count = " + maxSameURICount + "): "
+ redirects.stream().map(URI::toString).collect(Collectors.joining(" -> ")) + " -> " + newURL;
+ redirects.stream().map(URI::toString).collect(Collectors.joining(" -> ")) + " -> " + newURL
+ ". You may want to increase quarkus.cxf.client.\"client-name\".max-retransmits in application.properties"
+ " where \"client-name\" is the name of your client in application.properties";
throw new IOException(msg);
} else {
final String msg = "Redirect loop detected on Conduit '"
+ conduitName + "': " + redirects.stream().map(URI::toString).collect(Collectors.joining(" -> "))
+ " -> " + newURL;
} else if (maxSameURICount == null) {
final String msg = "Redirect loop detected on Conduit '" + conduitName
+ ". You may want to set quarkus.cxf.client.\"client-name\".max-retransmits in application.properties"
+ " where \"client-name\" is the name of your client in application.properties";
throw new IOException(msg);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
package io.quarkiverse.cxf.it.redirect;

import java.net.URI;
import java.util.concurrent.atomic.AtomicInteger;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.QueryParam;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.*;
import jakarta.ws.rs.core.*;

import io.quarkiverse.cxf.annotation.CXFClient;
import io.quarkiverse.cxf.it.large.slow.generated.LargeSlowService;
Expand All @@ -18,6 +13,9 @@
@Path("/RedirectRest")
public class RedirectRest {

private final AtomicInteger redirectCounter = new AtomicInteger(0);
private static final int NUM_SELF_REDIRECTS = 2;

@CXFClient("singleRedirect")
LargeSlowService singleRedirect;

Expand All @@ -36,6 +34,9 @@ public class RedirectRest {
@CXFClient("doubleRedirectMaxRetransmits2")
LargeSlowService doubleRedirectMaxRetransmits2;

@CXFClient("selfRedirect")
LargeSlowService selfRedirect;

@CXFClient("loop")
LargeSlowService loop;

Expand All @@ -59,6 +60,9 @@ LargeSlowService getClient(String clientName) {
case "doubleRedirectMaxRetransmits2": {
return doubleRedirectMaxRetransmits2;
}
case "selfRedirect": {
return selfRedirect;
}
case "loop": {
return loop;
}
Expand Down Expand Up @@ -89,6 +93,18 @@ public Response tripleRedirect() {
return Response.status(302).header("Location", "/RedirectRest/doubleRedirect").build();
}

@Path("/selfRedirect")
@POST
public Response selfRedirect(@Context HttpHeaders headers) {
int count = redirectCounter.incrementAndGet();
if (count <= NUM_SELF_REDIRECTS) {
return Response.status(302).header("Location", "/RedirectRest/selfRedirect").build();
} else {
redirectCounter.set(0);
return Response.status(302).header("Location", "/RedirectRest/singleRedirect").build();
}
}

@Path("/loop1")
@POST
public Response loop1() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,13 @@ 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.selfRedirect.client-endpoint-url = http://localhost:${quarkus.http.test-port}/RedirectRest/selfRedirect
quarkus.cxf.client.selfRedirect.service-interface = io.quarkiverse.cxf.it.large.slow.generated.LargeSlowService
quarkus.cxf.client.selfRedirect.redirect-relative-uri = true
quarkus.cxf.client.selfRedirect.max-retransmits = 4
quarkus.cxf.client.selfRedirect.max-same-uri = 3
quarkus.cxf.client.selfRedirect.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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,10 @@ class RedirectTest {
"async/tripleRedirect", //

"sync/doubleRedirectMaxRetransmits2", //
"async/doubleRedirectMaxRetransmits2" //
"async/doubleRedirectMaxRetransmits2", //

"sync/selfRedirect", //
"async/selfRedirect" //

})
void redirect(String endpoint) throws InterruptedException, ExecutionException {
Expand Down Expand Up @@ -75,12 +78,14 @@ void doubleRedirectMaxRetransmits1(String endpoint) {
"sync/loop", //
"async/loop" //
})
void loop(String endpoint) {
void redirectLoop(String endpoint) {
int sizeBytes = 16;
getResponse(endpoint, sizeBytes)
.statusCode(500)
.body(CoreMatchers.containsString(
"Redirect loop detected on Conduit '{https://quarkiverse.github.io/quarkiverse-docs/quarkus-cxf/test}LargeSlowServicePort.http-conduit'"));
.body(CoreMatchers.allOf(CoreMatchers.containsString(
"Redirect loop detected on Conduit '{https://quarkiverse.github.io/quarkiverse-docs/quarkus-cxf/test}LargeSlowServicePort.http-conduit' (with http.redirect.max.same.uri.count = 0):"),
CoreMatchers.containsString(
"You may want to increase quarkus.cxf.client.\"client-name\".max-retransmits in application.properties where \"client-name\" is the name of your client in application.properties")));
}

static ValidatableResponse getResponse(String endpoint, int sizeBytes) {
Expand Down

0 comments on commit 66dc7ba

Please sign in to comment.