diff --git a/docs/modules/ROOT/examples/client-server/application.properties b/docs/modules/ROOT/examples/client-server/application.properties index 0fc5d0284..ec9fb9058 100644 --- a/docs/modules/ROOT/examples/client-server/application.properties +++ b/docs/modules/ROOT/examples/client-server/application.properties @@ -119,12 +119,33 @@ 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.doubleRedirectMaxRetransmits2MaxSameUri0.client-endpoint-url = http://localhost:${quarkus.http.test-port}/RedirectRest/doubleRedirect +quarkus.cxf.client.doubleRedirectMaxRetransmits2MaxSameUri0.service-interface = io.quarkiverse.cxf.it.large.slow.generated.LargeSlowService +quarkus.cxf.client.doubleRedirectMaxRetransmits2MaxSameUri0.redirect-relative-uri = true +quarkus.cxf.client.doubleRedirectMaxRetransmits2MaxSameUri0.max-retransmits = 2 +quarkus.cxf.client.doubleRedirectMaxRetransmits2MaxSameUri0.auto-redirect = true +quarkus.cxf.client.doubleRedirectMaxRetransmits2MaxSameUri0.max-same-uri = 0 + +quarkus.cxf.client.maxSameUri1.client-endpoint-url = http://localhost:${quarkus.http.test-port}/RedirectRest/selfRedirect/1 +quarkus.cxf.client.maxSameUri1.service-interface = io.quarkiverse.cxf.it.large.slow.generated.LargeSlowService +quarkus.cxf.client.maxSameUri1.redirect-relative-uri = true +quarkus.cxf.client.maxSameUri1.max-retransmits = 4 +quarkus.cxf.client.maxSameUri1.max-same-uri = 1 +quarkus.cxf.client.maxSameUri1.auto-redirect = true + +quarkus.cxf.client.maxSameUri2.client-endpoint-url = http://localhost:${quarkus.http.test-port}/RedirectRest/selfRedirect/3 +quarkus.cxf.client.maxSameUri2.service-interface = io.quarkiverse.cxf.it.large.slow.generated.LargeSlowService +quarkus.cxf.client.maxSameUri2.redirect-relative-uri = true +quarkus.cxf.client.maxSameUri2.max-retransmits = 4 +quarkus.cxf.client.maxSameUri2.max-same-uri = 2 +quarkus.cxf.client.maxSameUri2.auto-redirect = true + +quarkus.cxf.client.maxSameUri3.client-endpoint-url = http://localhost:${quarkus.http.test-port}/RedirectRest/selfRedirect/3 +quarkus.cxf.client.maxSameUri3.service-interface = io.quarkiverse.cxf.it.large.slow.generated.LargeSlowService +quarkus.cxf.client.maxSameUri3.redirect-relative-uri = true +quarkus.cxf.client.maxSameUri3.max-retransmits = 4 +quarkus.cxf.client.maxSameUri3.max-same-uri = 3 +quarkus.cxf.client.maxSameUri3.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 @@ -146,4 +167,5 @@ quarkus.cxf.endpoint."/soap12".implementor = io.quarkiverse.cxf.it.soap12.Soap12 quarkus.cxf.endpoint."/soap12".soap-binding = http://www.w3.org/2003/05/soap/bindings/HTTP/ quarkus.cxf.endpoint."/soap12".logging.enabled = true -quarkus.default-locale = en_US \ No newline at end of file +quarkus.default-locale = en_US +quarkus.log.category."io.quarkiverse.cxf.vertx.http.client.VertxHttpClientHTTPConduit".level=DEBUG diff --git a/docs/modules/ROOT/pages/reference/extensions/quarkus-cxf.adoc b/docs/modules/ROOT/pages/reference/extensions/quarkus-cxf.adoc index 3f129da7b..f26458fc7 100644 --- a/docs/modules/ROOT/pages/reference/extensions/quarkus-cxf.adoc +++ b/docs/modules/ROOT/pages/reference/extensions/quarkus-cxf.adoc @@ -1696,14 +1696,15 @@ See also: .<| `int` .<| `-1` -3+a|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) +3+a|Specifies the maximum allowed number of retransmits when following redirects. +Retransmits for authorization are included in the retransmit count. +Each 401 UNAUTHORIZED response may cause a new retransmit. +A negative value indicates unlimited retransmits, but even in that case redirect loop protection is provided. See also: * `xref:reference/extensions/quarkus-cxf.adoc#quarkus-cxf_quarkus-cxf-client-client-name-auto-redirect[quarkus.cxf.client."client-name".auto-redirect]` +* `xref:reference/extensions/quarkus-cxf.adoc#quarkus-cxf_quarkus-cxf-client-client-name-max-same-uri[quarkus.cxf.client."client-name".max-same-uri]` *Environment variable*: `+++QUARKUS_CXF_CLIENT__CLIENT_NAME__MAX_RETRANSMITS+++` + *Since Quarkus CXF*: 2.2.3 @@ -1712,18 +1713,16 @@ See also: .<| `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) +3+a|Specifies the maximum allowed number of retransmits to the same URI when following HTTP redirects. +The default behavior is to allow a no retransmits to the same URI. See also: -* -`xref:reference/extensions/quarkus-cxf.adoc#quarkus-cxf_quarkus-cxf-client-client-name-auto-redirect[quarkus.cxf.client."client-name".auto-redirect]` +* `xref:reference/extensions/quarkus-cxf.adoc#quarkus-cxf_quarkus-cxf-client-client-name-auto-redirect[quarkus.cxf.client."client-name".auto-redirect]` +* `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__MAX_SAME_URI+++` + -*Since Quarkus CXF*: 3.17.4 +*Since Quarkus CXF*: 3.18.0 .<| [[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` diff --git a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CXFClientInfo.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CXFClientInfo.java index a38cb9e9c..22d4c9446 100644 --- a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CXFClientInfo.java +++ b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CXFClientInfo.java @@ -1,7 +1,11 @@ package io.quarkiverse.cxf; import java.security.KeyStore; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.Optional; import org.apache.cxf.annotations.SchemaValidation.SchemaValidationType; import org.apache.cxf.transports.http.configuration.ConnectionType; diff --git a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfClientConfig.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfClientConfig.java index 864b30aca..59c866bd4 100644 --- a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfClientConfig.java +++ b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfClientConfig.java @@ -233,14 +233,15 @@ public interface CxfClientConfig { // 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) + * Specifies the maximum allowed number of retransmits when following redirects. + * Retransmits for authorization are included in the retransmit count. + * Each 401 UNAUTHORIZED response may cause a new retransmit. + * A negative value indicates unlimited retransmits, but even in that case redirect loop protection is provided. * * See also: * * * `xref:reference/extensions/quarkus-cxf.adoc#quarkus-cxf_quarkus-cxf-client-client-name-auto-redirect[quarkus.cxf.client."client-name".auto-redirect]` + * * `xref:reference/extensions/quarkus-cxf.adoc#quarkus-cxf_quarkus-cxf-client-client-name-max-same-uri[quarkus.cxf.client."client-name".max-same-uri]` * * @since 2.2.3 * @asciidoclet @@ -249,20 +250,21 @@ public interface CxfClientConfig { @WithDefault("-1") public int maxRetransmits(); + // The formatter breaks the list with long items + // @formatter:off /** - * 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) + * Specifies the maximum allowed number of retransmits to the same URI when following HTTP redirects. + * The default behavior is to allow a no retransmits to the same URI. * * See also: * - * * - * `xref:reference/extensions/quarkus-cxf.adoc#quarkus-cxf_quarkus-cxf-client-client-name-auto-redirect[quarkus.cxf.client."client-name".auto-redirect]` + * * `xref:reference/extensions/quarkus-cxf.adoc#quarkus-cxf_quarkus-cxf-client-client-name-auto-redirect[quarkus.cxf.client."client-name".auto-redirect]` + * * `xref:reference/extensions/quarkus-cxf.adoc#quarkus-cxf_quarkus-cxf-client-client-name-max-retransmits[quarkus.cxf.client."client-name".max-retransmits]` * - * @since 3.17.4 + * @since 3.18.0 * @asciidoclet */ + // @formatter:on @WithDefault("0") public int maxSameUri(); diff --git a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfClientProducer.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfClientProducer.java index 8fd2604d2..aba167f47 100644 --- a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfClientProducer.java +++ b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/CxfClientProducer.java @@ -184,7 +184,10 @@ private Object produceCxfClient(CXFClientInfo cxfClientInfo) { } { final int value = cxfClientInfo.getMaxSameUri(); - props.put(VertxHttpClientHTTPConduit.AUTO_REDIRECT_MAX_SAME_URI_COUNT, value); + if (value > 0) { + /* 0 is the deafult that makes no difference in the handling so we can ignore it here */ + props.put(VertxHttpClientHTTPConduit.AUTO_REDIRECT_MAX_SAME_URI_COUNT, value); + } } loggingFactoryCustomizer.customize(cxfClientInfo, factory); diff --git a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/HTTPConduitImpl.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/HTTPConduitImpl.java index dfcf995f2..68e2da542 100644 --- a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/HTTPConduitImpl.java +++ b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/HTTPConduitImpl.java @@ -48,9 +48,9 @@ public HTTPConduitImpl resolveDefault() { @ConfigDocEnumValue("VertxHttpClientHTTPConduitFactory") VertxHttpClientHTTPConduitFactory { @Override - public HTTPConduit createConduit(HttpClientPool httpClientPool, Bus b, EndpointInfo localInfo, + public HTTPConduit createConduit(String configKey, HttpClientPool httpClientPool, Bus b, EndpointInfo localInfo, EndpointReferenceType target) throws IOException { - return new VertxHttpClientHTTPConduit(b, localInfo, target, httpClientPool); + return new VertxHttpClientHTTPConduit(configKey, b, localInfo, target, httpClientPool); } @Override @@ -71,7 +71,7 @@ public TLSClientParameters createTLSClientParameters(CXFClientInfo cxfClientInfo @ConfigDocEnumValue("HttpClientHTTPConduitFactory") HttpClientHTTPConduitFactory { @Override - public HTTPConduit createConduit(HttpClientPool httpClientPool, Bus b, EndpointInfo localInfo, + public HTTPConduit createConduit(String configKey, HttpClientPool httpClientPool, Bus b, EndpointInfo localInfo, EndpointReferenceType target) throws IOException { return new HttpClientHTTPConduit(b, localInfo, target); } @@ -79,7 +79,7 @@ public HTTPConduit createConduit(HttpClientPool httpClientPool, Bus b, EndpointI @ConfigDocEnumValue("URLConnectionHTTPConduitFactory") URLConnectionHTTPConduitFactory { @Override - public HTTPConduit createConduit(HttpClientPool httpClientPool, Bus b, EndpointInfo localInfo, + public HTTPConduit createConduit(String configKey, HttpClientPool httpClientPool, Bus b, EndpointInfo localInfo, EndpointReferenceType target) throws IOException { return new URLConnectionHTTPConduit(b, localInfo, target); } @@ -96,7 +96,8 @@ public static HTTPConduitImpl findDefaultHTTPConduitImpl() { } @Override - public HTTPConduit createConduit(HttpClientPool httpClientPool, Bus b, EndpointInfo localInfo, EndpointReferenceType target) + public HTTPConduit createConduit(String configKey, HttpClientPool httpClientPool, Bus b, EndpointInfo localInfo, + EndpointReferenceType target) throws IOException { throw new IllegalStateException( "Call " + HTTPConduitImpl.class.getName() + ".resolveDefault() before calling createConduit()"); diff --git a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/HTTPConduitSpec.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/HTTPConduitSpec.java index 3230b6aea..c36f077a6 100644 --- a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/HTTPConduitSpec.java +++ b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/HTTPConduitSpec.java @@ -27,7 +27,8 @@ default HTTPConduitSpec resolveDefault() { return this; } - HTTPConduit createConduit(HttpClientPool httpClientPool, Bus b, EndpointInfo localInfo, EndpointReferenceType target) + HTTPConduit createConduit(String configKey, HttpClientPool httpClientPool, Bus b, EndpointInfo localInfo, + EndpointReferenceType target) throws IOException; default Optional tlsClientParameters(CXFClientInfo cxfClientInfo, Vertx vertx) { diff --git a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/QuarkusCxfUtils.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/QuarkusCxfUtils.java new file mode 100644 index 000000000..59fbaf870 --- /dev/null +++ b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/QuarkusCxfUtils.java @@ -0,0 +1,21 @@ +package io.quarkiverse.cxf; + +import io.smallrye.config.NameIterator; + +public class QuarkusCxfUtils { + private QuarkusCxfUtils() { + } + + /** + * Keys occurring in free form configuration maps may need quoting with double quotes, if they contain periods. + * + * @param key a key occurring in a free form configuration map, such as {@code "client-name"} in + * {@code quarkus.cxf.client."client-name".logging.enabled}. + * @return a possibly quoted key + */ + public static String quoteCongurationKeyIfNeeded(String key) { + final NameIterator keyIterator = new NameIterator(key); + keyIterator.next(); + return keyIterator.hasNext() ? "\"" + key + "\"" : key; + } +} diff --git a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/QuarkusHTTPConduitFactory.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/QuarkusHTTPConduitFactory.java index e0d5ce358..4004b6994 100644 --- a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/QuarkusHTTPConduitFactory.java +++ b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/QuarkusHTTPConduitFactory.java @@ -77,7 +77,12 @@ public HTTPConduit createConduit(HTTPTransportFactory f, Bus b, EndpointInfo loc private HTTPConduit configure(HTTPConduitSpec httpConduitImpl, CXFClientInfo cxfClientInfo, Bus b, EndpointInfo localInfo, EndpointReferenceType target) throws IOException { - final HTTPConduit httpConduit = httpConduitImpl.createConduit(httpClientPool, b, localInfo, target); + final HTTPConduit httpConduit = httpConduitImpl.createConduit( + cxfClientInfo.getConfigKey(), + httpClientPool, + b, + localInfo, + target); if (httpConduit instanceof HttpClientHTTPConduit) { Log.warnf("Usage of %s is deprecated since Quarkus CXF 3.18.0." + " You may want to review the options quarkus.cxf.http-conduit-factory and/or quarkus.cxf.client.\"%s\".http-conduit-factory", diff --git a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/vertx/http/client/VertxHttpClientHTTPConduit.java b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/vertx/http/client/VertxHttpClientHTTPConduit.java index a2bb46204..8f2407b21 100644 --- a/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/vertx/http/client/VertxHttpClientHTTPConduit.java +++ b/extensions/core/runtime/src/main/java/io/quarkiverse/cxf/vertx/http/client/VertxHttpClientHTTPConduit.java @@ -25,14 +25,25 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.PushbackInputStream; -import java.net.*; +import java.net.HttpURLConnection; +import java.net.InetSocketAddress; +import java.net.Proxy; import java.net.Proxy.Type; -import java.util.*; +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.Map.Entry; +import java.util.Queue; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; -import java.util.logging.Level; import java.util.stream.Collectors; import org.apache.cxf.Bus; @@ -49,23 +60,38 @@ 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.*; +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.transports.http.configuration.HTTPClientPolicy; import org.apache.cxf.version.Version; import org.apache.cxf.ws.addressing.EndpointReferenceType; import org.eclipse.microprofile.context.ManagedExecutor; import org.jboss.logging.Logger; +import io.quarkiverse.cxf.QuarkusCxfUtils; import io.quarkiverse.cxf.QuarkusTLSClientParameters; import io.quarkiverse.cxf.vertx.http.client.HttpClientPool.ClientSpec; import io.quarkiverse.cxf.vertx.http.client.VertxHttpClientHTTPConduit.RequestBodyEvent.RequestBodyEventType; import io.quarkus.arc.Arc; import io.quarkus.arc.InstanceHandle; -import io.quarkus.logging.Log; import io.quarkus.runtime.BlockingOperationControl; -import io.vertx.core.*; +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.buffer.Buffer; -import io.vertx.core.http.*; +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.impl.HttpUtils; import io.vertx.core.net.ProxyOptions; import io.vertx.core.net.ProxyType; @@ -84,10 +110,13 @@ public class VertxHttpClientHTTPConduit extends HTTPConduit { private final HttpClientPool httpClientPool; private final String userAgent; + private final String configKey; - public VertxHttpClientHTTPConduit(Bus b, EndpointInfo ei, EndpointReferenceType t, HttpClientPool httpClientPool) + public VertxHttpClientHTTPConduit(String configKey, Bus b, EndpointInfo ei, EndpointReferenceType t, + HttpClientPool httpClientPool) throws IOException { super(b, ei, t); + this.configKey = configKey; this.httpClientPool = httpClientPool; this.userAgent = Version.getCompleteVersionString(); } @@ -169,6 +198,7 @@ protected void setupConnection(Message message, Address address, HTTPClientPolic } final RequestContext requestContext = new RequestContext( + configKey, uri, requestOptions, clientParameters != null @@ -223,7 +253,7 @@ protected OutputStream createOutputStream( incomingObserver); final IOEHandler requestBodyHandler = new RequestBodyHandler( - getConduitName(), + requestContext.configKey, message, requestContext.uri, cookies, @@ -235,8 +265,7 @@ protected OutputStream createOutputStream( responseHandler, requestContext.async, requestContext.autoRedirect, // we do not support authorizationRetransmit yet possibleRetransmit, - requestContext.maxRetransmits, - 0); + requestContext.maxRetransmits); return new RequestBodyOutputStream(chunkThreshold, requestBodyHandler); } @@ -314,6 +343,7 @@ public void setTlsClientParameters(TLSClientParameters params) { } static record RequestContext( + String configKey, URI uri, RequestOptions requestOptions, ClientSpec clientSpec, @@ -421,8 +451,7 @@ static class RequestBodyHandler implements IOEHandler { private List bodyRecorder; private List redirects; private final int maxRetransmits; - private int performedRetransmits; - private final String conduitName; + private final String configKey; /* Locks and conditions */ private final ReentrantLock lock = new ReentrantLock(); @@ -435,7 +464,7 @@ static class RequestBodyHandler implements IOEHandler { private Mode mode; public RequestBodyHandler( - String conduitName, + String configKey, Message outMessage, URI url, Cookies cookies, @@ -447,10 +476,9 @@ public RequestBodyHandler( IOEHandler responseHandler, boolean isAsync, boolean possibleRetransmit, - int maxRetransmits, - int performedRetransmits) { + int maxRetransmits) { super(); - this.conduitName = conduitName; + this.configKey = configKey; this.outMessage = outMessage; this.url = url; this.cookies = cookies; @@ -466,7 +494,6 @@ public RequestBodyHandler( this.possibleRetransmit = possibleRetransmit; this.maxRetransmits = maxRetransmits; - this.performedRetransmits = performedRetransmits; } @Override @@ -586,57 +613,66 @@ void finishRequest(HttpClientRequest req, Buffer buffer) { /* need to retransmit? */ final boolean isRedirect = isRedirect(response.statusCode()); if (possibleRetransmit - && (maxRetransmits < 0 || performedRetransmits < maxRetransmits) - && isRedirect) { - performedRetransmits++; + && isRedirect + && (maxRetransmits < 0 || performedRetransmits(redirects) < maxRetransmits)) { ResponseHandler.updateResponseHeaders(response, outMessage, cookies); final String loc = response.getHeader("Location"); try { if (loc != null && !loc.startsWith("http") && !MessageUtils.getContextualBoolean(outMessage, AUTO_REDIRECT_ALLOW_REL_URI)) { + final String qKey = QuarkusCxfUtils.quoteCongurationKeyIfNeeded(configKey); throw new IOException( - "Relative Redirect detected on Conduit '" + conduitName + "' on '" + loc + "'." - + " You may want to set quarkus.cxf.client.\"client-name\".redirect-relative-uri = true," - + " where \"client-name\" is the name of your client in application.properties"); + "Illegal relative redirect " + loc + " detected by client " + qKey + + "; you may want to set quarkus.cxf.client." + + qKey + ".redirect-relative-uri = true"); } final URI previousUri = redirects.get(redirects.size() - 1); final URI newUri = HttpUtils.resolveURIReference(previousUri, loc); - detectRedirectLoop(conduitName, redirects, newUri, outMessage); + detectRedirectLoop(configKey, redirects, newUri, outMessage); redirects.add(newUri); - checkAllowedRedirectUri(conduitName, previousUri, newUri, outMessage); + checkAllowedRedirectUri(configKey, previousUri, newUri, outMessage); redirectRetransmit(newUri); } catch (IOException e) { sink.setException((IOException) e); - mode.responseReady(new Result<>(new ResponseEvent(response, sink), ar.cause())); + mode.responseReady(new Result<>(new ResponseEvent(response, sink), e)); } catch (URISyntaxException e) { - sink.setException(new IOException( - "Could not resolve redirect Location " + loc + " relative to " + url, e)); - mode.responseReady(new Result<>(new ResponseEvent(response, sink), ar.cause())); + final IOException ioe = new IOException( + "Could not resolve redirect Location " + loc + " relative to " + url, e); + sink.setException(ioe); + mode.responseReady(new Result<>(new ResponseEvent(response, sink), ioe)); } catch (Exception e) { - sink.setException(new IOException(e)); - mode.responseReady(new Result<>(new ResponseEvent(response, sink), ar.cause())); + final IOException ioe = new IOException(e); + sink.setException(ioe); + mode.responseReady(new Result<>(new ResponseEvent(response, sink), ioe)); } return; } else { if (!possibleRetransmit && isRedirect) { - Log.warnf( - "Received redirection status %d from %s, but following redirects is not" - + " enabled for this CXF client. You may want to set" - + " quarkus.cxf.client.\"client-name\".auto-redirect = true," - + " where \"client-name\" is the name of your client in application.properties", - response.statusCode(), - url); + final String qKey = QuarkusCxfUtils.quoteCongurationKeyIfNeeded(configKey); + final IOException ioe = new IOException( + "Received redirection status " + response.statusCode() + + " from " + url + " by client " + qKey + + " but following redirects is not enabled for this client." + + " You may want to set quarkus.cxf.client." + qKey + + ".auto-redirect = true"); + sink.setException(ioe); + mode.responseReady(new Result<>(new ResponseEvent(response, sink), ioe)); + return; } if (possibleRetransmit && isRedirect && maxRetransmits >= 0 - && maxRetransmits <= performedRetransmits) { - Log.warnf( - "Received redirection status %d from %s, but already performed maximum" - + " number %d of allowed retransmits for this exchange; you may want to" - + " increase quarkus.cxf.client.\"client-name\".max-retransmits in application.properties", - response.statusCode(), - redirects.get(redirects.size() - 1), - maxRetransmits); + && maxRetransmits <= performedRetransmits(redirects)) { + final String qKey = QuarkusCxfUtils.quoteCongurationKeyIfNeeded(configKey); + final IOException ioe = new IOException("Received redirection status " + + response.statusCode() + " from " + redirects.get(redirects.size() - 1) + + " by client " + qKey + ", but already performed maximum" + + " number " + maxRetransmits + + " of allowed retransmits; you may want to" + + " increase quarkus.cxf.client." + qKey + ".max-retransmits. Visited URIs: " + + redirects.stream().map(URI::toString).collect(Collectors.joining(" -> "))); + sink.setException(ioe); + mode.responseReady(new Result<>(new ResponseEvent(response, sink), ioe)); + return; } /* No retransmit */ /* Pass the body back to CXF */ @@ -657,11 +693,15 @@ void finishRequest(HttpClientRequest req, Buffer buffer) { } + private static int performedRetransmits(List retransmits) { + /* The first element in the retransmits list is the original URI that we do not count as a retransmit */ + return retransmits.size() - 1; + } + void redirectRetransmit(URI newURL) throws IOException { - if (Log.isDebugEnabled()) { - final int i = redirects.size() - 2; - final String previousUrl = i >= 0 ? redirects.get(i).toString() : "null"; - Log.infof("Redirect retransmit from %s to %s", previousUrl, newURL); + if (log.isDebugEnabled()) { + log.debugf("Redirect retransmit: %s", + redirects.stream().map(URI::toString).collect(Collectors.joining(" -> "))); } boolean ssl; int port = newURL.getPort(); @@ -752,30 +792,44 @@ private static boolean isRedirect(int statusCode) { && (statusCode == 302 || statusCode == 301 || statusCode == 303 || statusCode == 307); } - private static void detectRedirectLoop(String conduitName, + private static void detectRedirectLoop( + String configKey, List redirects, URI newURL, Message message) throws IOException { if (redirects.contains(newURL)) { final Integer maxSameURICount = PropertyUtils.getInteger(message, AUTO_REDIRECT_MAX_SAME_URI_COUNT); - if (maxSameURICount != null - && 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 - + ". 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 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); + final String qKey = QuarkusCxfUtils.quoteCongurationKeyIfNeeded(configKey); + if (maxSameURICount != null) { + final long sameUriRetransmitsToBePerformed = redirects.stream() + .skip(1) // the first element is not a retransmit + .filter(newURL::equals) + .count() + + 1 // +1 because newURL was not added to redirects yet + ; + if (sameUriRetransmitsToBePerformed > maxSameURICount.longValue()) { + final String msg = "Redirect chain with too many same URIs " + newURL + + " (found " + sameUriRetransmitsToBePerformed + ", allowed <= " + maxSameURICount.longValue() + + ")" + + " detected by client " + qKey + ": " + + redirects.stream().map(URI::toString).collect(Collectors.joining(" -> ")) + + " -> " + newURL + + ". You may want to increase quarkus.cxf.client." + qKey + + ".max-same-uri"; + throw new IOException(msg); + } + /* Allowed number of same URI */ + return; } + final String msg = "Redirect loop detected by client " + qKey + ": " + + redirects.stream().map(URI::toString).collect(Collectors.joining(" -> ")) + " -> " + newURL + + ". You may want to increase quarkus.cxf.client." + qKey + + ".max-same-uri"; + throw new IOException(msg); } } - private static void checkAllowedRedirectUri(String conduitName, + private static void checkAllowedRedirectUri(String configKey, URI lastUri, URI newUri, Message message) throws IOException { @@ -786,17 +840,18 @@ private static void checkAllowedRedirectUri(String conduitName, if (!newUri.getScheme().equals(lastUri.getScheme()) || !newUri.getHost().equals(lastUri.getHost())) { - String msg = "Different HTTP Scheme or Host Redirect detected on Conduit '" - + conduitName + "' on '" + newUri + "'"; - LOG.log(Level.INFO, msg); + final String qKey = QuarkusCxfUtils.quoteCongurationKeyIfNeeded(configKey); + String msg = "Different HTTP scheme or different host detected in redirect URI " + newUri + + " compared to original URI " + lastUri + " by client " + qKey; throw new IOException(msg); } } String allowedRedirectURI = (String) message.getContextualProperty(AUTO_REDIRECT_ALLOWED_URI); if (allowedRedirectURI != null && !newUri.toString().startsWith(allowedRedirectURI)) { - String msg = "Forbidden Redirect URI " + newUri + "detected on Conduit '" + conduitName; - LOG.log(Level.INFO, msg); + final String qKey = QuarkusCxfUtils.quoteCongurationKeyIfNeeded(configKey); + String msg = "Illegal redirect URI " + newUri + " detected by client " + qKey + + "; expected to start with " + allowedRedirectURI; throw new IOException(msg); } } diff --git a/extensions/transports-http-hc5/runtime/src/main/java/io/quarkiverse/cxf/transport/http/hc5/Hc5Recorder.java b/extensions/transports-http-hc5/runtime/src/main/java/io/quarkiverse/cxf/transport/http/hc5/Hc5Recorder.java index ee45fef94..9b116e6e1 100644 --- a/extensions/transports-http-hc5/runtime/src/main/java/io/quarkiverse/cxf/transport/http/hc5/Hc5Recorder.java +++ b/extensions/transports-http-hc5/runtime/src/main/java/io/quarkiverse/cxf/transport/http/hc5/Hc5Recorder.java @@ -48,7 +48,7 @@ public static class Hc5HTTPConduitImpl implements HTTPConduitSpec { private AsyncHTTPConduitFactory asyncHTTPConduitFactory; @Override - public HTTPConduit createConduit(HttpClientPool httpClientPool, Bus b, EndpointInfo localInfo, + public HTTPConduit createConduit(String configKey, HttpClientPool httpClientPool, Bus b, EndpointInfo localInfo, EndpointReferenceType target) throws IOException { AsyncHTTPConduitFactory factory; diff --git a/integration-tests/client-server/src/main/java/io/quarkiverse/cxf/it/redirect/RedirectRest.java b/integration-tests/client-server/src/main/java/io/quarkiverse/cxf/it/redirect/RedirectRest.java index 41adf5891..fc5dc7d30 100644 --- a/integration-tests/client-server/src/main/java/io/quarkiverse/cxf/it/redirect/RedirectRest.java +++ b/integration-tests/client-server/src/main/java/io/quarkiverse/cxf/it/redirect/RedirectRest.java @@ -3,18 +3,25 @@ import java.net.URI; import java.util.concurrent.atomic.AtomicInteger; -import jakarta.ws.rs.*; -import jakarta.ws.rs.core.*; +import jakarta.ws.rs.DELETE; +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 io.quarkiverse.cxf.annotation.CXFClient; import io.quarkiverse.cxf.it.large.slow.generated.LargeSlowService; +import io.quarkus.logging.Log; import io.smallrye.mutiny.Uni; @Path("/RedirectRest") public class RedirectRest { private final AtomicInteger redirectCounter = new AtomicInteger(0); - private static final int NUM_SELF_REDIRECTS = 2; @CXFClient("singleRedirect") LargeSlowService singleRedirect; @@ -34,8 +41,17 @@ public class RedirectRest { @CXFClient("doubleRedirectMaxRetransmits2") LargeSlowService doubleRedirectMaxRetransmits2; - @CXFClient("selfRedirect") - LargeSlowService selfRedirect; + @CXFClient("doubleRedirectMaxRetransmits2MaxSameUri0") + LargeSlowService doubleRedirectMaxRetransmits2MaxSameUri0; + + @CXFClient("maxSameUri1") + LargeSlowService maxSameUri1; + + @CXFClient("maxSameUri2") + LargeSlowService maxSameUri2; + + @CXFClient("maxSameUri3") + LargeSlowService maxSameUri3; @CXFClient("loop") LargeSlowService loop; @@ -60,8 +76,17 @@ LargeSlowService getClient(String clientName) { case "doubleRedirectMaxRetransmits2": { return doubleRedirectMaxRetransmits2; } - case "selfRedirect": { - return selfRedirect; + case "doubleRedirectMaxRetransmits2MaxSameUri0": { + return doubleRedirectMaxRetransmits2MaxSameUri0; + } + case "maxSameUri1": { + return maxSameUri1; + } + case "maxSameUri2": { + return maxSameUri2; + } + case "maxSameUri3": { + return maxSameUri3; } case "loop": { return loop; @@ -94,15 +119,25 @@ public Response tripleRedirect() { } @Path("/selfRedirect") + @DELETE + public void selfRedirect() { + Log.info("Resetting redirectCounter"); + redirectCounter.set(0); + } + + @Path("/selfRedirect/{selfRedirectsCount}") @POST - public Response selfRedirect(@Context HttpHeaders headers) { + public Response selfRedirect(@PathParam("selfRedirectsCount") int selfRedirectsCount) { int count = redirectCounter.incrementAndGet(); - if (count <= NUM_SELF_REDIRECTS) { - return Response.status(302).header("Location", "/RedirectRest/selfRedirect").build(); + + final String loc; + if (count <= selfRedirectsCount) { + loc = "/RedirectRest/selfRedirect/" + selfRedirectsCount; } else { - redirectCounter.set(0); - return Response.status(302).header("Location", "/RedirectRest/singleRedirect").build(); + loc = "/soap/largeSlow"; } + Log.infof("redirectCounter at %d: sending 302 with Location %s", count, loc); + return Response.status(302).header("Location", loc).build(); } @Path("/loop1") diff --git a/integration-tests/client-server/src/main/resources/application.properties b/integration-tests/client-server/src/main/resources/application.properties index 0fc5d0284..ec9fb9058 100644 --- a/integration-tests/client-server/src/main/resources/application.properties +++ b/integration-tests/client-server/src/main/resources/application.properties @@ -119,12 +119,33 @@ 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.doubleRedirectMaxRetransmits2MaxSameUri0.client-endpoint-url = http://localhost:${quarkus.http.test-port}/RedirectRest/doubleRedirect +quarkus.cxf.client.doubleRedirectMaxRetransmits2MaxSameUri0.service-interface = io.quarkiverse.cxf.it.large.slow.generated.LargeSlowService +quarkus.cxf.client.doubleRedirectMaxRetransmits2MaxSameUri0.redirect-relative-uri = true +quarkus.cxf.client.doubleRedirectMaxRetransmits2MaxSameUri0.max-retransmits = 2 +quarkus.cxf.client.doubleRedirectMaxRetransmits2MaxSameUri0.auto-redirect = true +quarkus.cxf.client.doubleRedirectMaxRetransmits2MaxSameUri0.max-same-uri = 0 + +quarkus.cxf.client.maxSameUri1.client-endpoint-url = http://localhost:${quarkus.http.test-port}/RedirectRest/selfRedirect/1 +quarkus.cxf.client.maxSameUri1.service-interface = io.quarkiverse.cxf.it.large.slow.generated.LargeSlowService +quarkus.cxf.client.maxSameUri1.redirect-relative-uri = true +quarkus.cxf.client.maxSameUri1.max-retransmits = 4 +quarkus.cxf.client.maxSameUri1.max-same-uri = 1 +quarkus.cxf.client.maxSameUri1.auto-redirect = true + +quarkus.cxf.client.maxSameUri2.client-endpoint-url = http://localhost:${quarkus.http.test-port}/RedirectRest/selfRedirect/3 +quarkus.cxf.client.maxSameUri2.service-interface = io.quarkiverse.cxf.it.large.slow.generated.LargeSlowService +quarkus.cxf.client.maxSameUri2.redirect-relative-uri = true +quarkus.cxf.client.maxSameUri2.max-retransmits = 4 +quarkus.cxf.client.maxSameUri2.max-same-uri = 2 +quarkus.cxf.client.maxSameUri2.auto-redirect = true + +quarkus.cxf.client.maxSameUri3.client-endpoint-url = http://localhost:${quarkus.http.test-port}/RedirectRest/selfRedirect/3 +quarkus.cxf.client.maxSameUri3.service-interface = io.quarkiverse.cxf.it.large.slow.generated.LargeSlowService +quarkus.cxf.client.maxSameUri3.redirect-relative-uri = true +quarkus.cxf.client.maxSameUri3.max-retransmits = 4 +quarkus.cxf.client.maxSameUri3.max-same-uri = 3 +quarkus.cxf.client.maxSameUri3.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 @@ -146,4 +167,5 @@ quarkus.cxf.endpoint."/soap12".implementor = io.quarkiverse.cxf.it.soap12.Soap12 quarkus.cxf.endpoint."/soap12".soap-binding = http://www.w3.org/2003/05/soap/bindings/HTTP/ quarkus.cxf.endpoint."/soap12".logging.enabled = true -quarkus.default-locale = en_US \ No newline at end of file +quarkus.default-locale = en_US +quarkus.log.category."io.quarkiverse.cxf.vertx.http.client.VertxHttpClientHTTPConduit".level=DEBUG diff --git a/integration-tests/client-server/src/test/java/io/quarkiverse/cxf/it/redirect/RedirectTest.java b/integration-tests/client-server/src/test/java/io/quarkiverse/cxf/it/redirect/RedirectTest.java index 82c19f230..ded49e00d 100644 --- a/integration-tests/client-server/src/test/java/io/quarkiverse/cxf/it/redirect/RedirectTest.java +++ b/integration-tests/client-server/src/test/java/io/quarkiverse/cxf/it/redirect/RedirectTest.java @@ -5,6 +5,8 @@ import org.assertj.core.api.Assumptions; import org.hamcrest.CoreMatchers; import org.hamcrest.Matchers; +import org.jboss.logging.Logger; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; @@ -17,6 +19,16 @@ @QuarkusTest class RedirectTest { + private static final Logger log = Logger.getLogger(RedirectTest.class); + + @BeforeEach + void beforeEach() { + RestAssured.given() + .delete("/RedirectRest/selfRedirect") + .then() + .statusCode(204); + } + @ParameterizedTest @ValueSource(strings = { // "sync/singleRedirect", // @@ -31,22 +43,43 @@ class RedirectTest { "sync/doubleRedirectMaxRetransmits2", // "async/doubleRedirectMaxRetransmits2", // - "sync/selfRedirect", // - "async/selfRedirect" // + "sync/doubleRedirectMaxRetransmits2MaxSameUri0", // + "async/doubleRedirectMaxRetransmits2MaxSameUri0", // + + "sync/maxSameUri1", // + "async/maxSameUri1", // + + "sync/maxSameUri3", // + "async/maxSameUri3" // }) void redirect(String endpoint) throws InterruptedException, ExecutionException { - int sizeBytes = 16; // smallish, suits single buffer in - // VertxHttpClientHTTPConduit.RequestBodyHandler.bodyRecorder - getResponse(endpoint, sizeBytes) - .statusCode(200) - .body(Matchers.is(LargeSlowServiceImpl.largeString(sizeBytes))); - - sizeBytes = 9 * 1024; // biggish, forces multiple buffers in - // VertxHttpClientHTTPConduit.RequestBodyHandler.bodyRecorder - getResponse(endpoint, sizeBytes) - .statusCode(200) - .body(Matchers.is(LargeSlowServiceImpl.largeString(sizeBytes))); + log.infof("Testing endpoint %s", endpoint); + + try { + final int sizeBytes = 16; // smallish, suits single buffer in + // VertxHttpClientHTTPConduit.RequestBodyHandler.bodyRecorder + getResponse(endpoint, sizeBytes) + .statusCode(200) + .body(Matchers.is(LargeSlowServiceImpl.largeString(sizeBytes))); + } finally { + RestAssured.given() + .delete("/RedirectRest/selfRedirect") + .then() + .statusCode(204); + } + try { + final int sizeBytes = 9 * 1024; // biggish, forces multiple buffers in + // VertxHttpClientHTTPConduit.RequestBodyHandler.bodyRecorder + getResponse(endpoint, sizeBytes) + .statusCode(200) + .body(Matchers.is(LargeSlowServiceImpl.largeString(sizeBytes))); + } finally { + RestAssured.given() + .delete("/RedirectRest/selfRedirect") + .then() + .statusCode(204); + } } @ParameterizedTest @@ -58,7 +91,13 @@ void noAutoRedirect(String endpoint) { int sizeBytes = 16; getResponse(endpoint, sizeBytes) .statusCode(500) - .body(CoreMatchers.containsString("Unexpected EOF in prolog")); + .body( + CoreMatchers.either( + Matchers.matchesPattern( + "\\QReceived redirection status 307 from http://localhost:\\E[0-9]+\\Q/RedirectRest/singleRedirect" + + " by client noAutoRedirect but following redirects is not enabled for this client." + + " You may want to set quarkus.cxf.client.noAutoRedirect.auto-redirect = true\\E")) + .or(CoreMatchers.containsString("Unexpected EOF in prolog"))); } @ParameterizedTest @@ -70,7 +109,15 @@ void doubleRedirectMaxRetransmits1(String endpoint) { int sizeBytes = 16; getResponse(endpoint, sizeBytes) .statusCode(500) - .body(CoreMatchers.containsString("Unexpected EOF in prolog")); + .body( + CoreMatchers.either( + Matchers.matchesPattern("\\QReceived redirection status 307 from" + + " http://localhost:\\E[0-9]+\\Q/RedirectRest/singleRedirect" + + " by client doubleRedirectMaxRetransmits1," + + " but already performed maximum number 1 of allowed retransmits;" + + " you may want to increase quarkus.cxf.client.doubleRedirectMaxRetransmits1.max-retransmits." + + " Visited URIs: http://localhost:\\E[0-9]+\\Q/RedirectRest/doubleRedirect -> http://localhost:\\E[0-9]+\\Q/RedirectRest/singleRedirect\\E")) + .or(CoreMatchers.containsString("Unexpected EOF in prolog"))); } @ParameterizedTest @@ -82,10 +129,39 @@ void redirectLoop(String endpoint) { int sizeBytes = 16; getResponse(endpoint, sizeBytes) .statusCode(500) - .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"))); + .body( + CoreMatchers.either( + Matchers.matchesPattern( + "\\QRedirect loop detected by client loop: " + + "http://localhost:\\E[0-9]+\\Q/RedirectRest/loop1 -> " + + "http://localhost:\\E[0-9]+\\Q/RedirectRest/loop2 -> " + + "http://localhost:\\E[0-9]+\\Q/RedirectRest/loop1." + + " You may want to increase quarkus.cxf.client.loop.max-same-uri\\E")) + .or(Matchers.matchesPattern( + "\\QRedirect loop detected on Conduit '{https://quarkiverse.github.io/quarkiverse-docs/quarkus-cxf/test}LargeSlowServicePort.http-conduit' on 'http://localhost:\\E[0-9]+\\Q/RedirectRest/loop1'\\E"))); + } + + @ParameterizedTest + @ValueSource(strings = { // + "sync/maxSameUri2", // + "async/maxSameUri2" // + }) + void tooManySameUri(String endpoint) { + int sizeBytes = 16; + getResponse(endpoint, sizeBytes) + .statusCode(500) + .body( + CoreMatchers.either( + Matchers.matchesPattern( + "\\QRedirect chain with too many same URIs http://localhost:\\E[0-9]+\\Q/RedirectRest/selfRedirect/3" + + " (found 3, allowed <= 2) detected by client maxSameUri2: " + + "http://localhost:\\E[0-9]+\\Q/RedirectRest/selfRedirect/3 -> " + + "http://localhost:\\E[0-9]+\\Q/RedirectRest/selfRedirect/3 -> " + + "http://localhost:\\E[0-9]+\\Q/RedirectRest/selfRedirect/3 -> " + + "http://localhost:\\E[0-9]+\\Q/RedirectRest/selfRedirect/3. " + + "You may want to increase quarkus.cxf.client.maxSameUri2.max-same-uri\\E")) + .or(Matchers.matchesPattern( + "\\QRedirect loop detected on Conduit '{https://quarkiverse.github.io/quarkiverse-docs/quarkus-cxf/test}LargeSlowServicePort.http-conduit' on 'http://localhost:\\E[0-9]+\\Q/RedirectRest/selfRedirect/3'\\E"))); } static ValidatableResponse getResponse(String endpoint, int sizeBytes) {