This is an automated email from the ASF dual-hosted git repository.
Aias00 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu.git
The following commit(s) were added to refs/heads/master by this push:
new 1e9b2e2996 fix(loadbalancer): normalize bare protocol values in the
upstream domain (#7804) (#7806)
1e9b2e2996 is described below
commit 1e9b2e29968c68481bfe73bd7c710b6edc2f0125
Author: hanzhe-one <[email protected]>
AuthorDate: Mon Oct 19 14:01:08 2026 +0800
fix(loadbalancer): normalize bare protocol values in the upstream domain
(#7804) (#7806)
* fix(loadbalancer): normalize bare protocol values in the upstream domain
(#7804)
A discovery upstream configured with protocol "http" produced
"http127.0.0.1:8080", because buildDomain() concatenated a non-blank
protocol without a "://" separator. Requests then failed with
UnknownHostException instead of a configuration error.
Normalize the protocol to a url prefix in buildDomain(), covering both
the stored protocol and the explicit-protocol overload used by the
websocket plugin.
* fix(loadbalancer): cache the normalized protocol and default empty schemes
Address review feedback on #7806: the normalized prefix is immutable, so
compute it once in the constructor instead of on every buildDomain() call
in the request path, and fall back to http:// for degenerate values such as
":" or "/" that strip down to an empty scheme.
The new field is transient so it stays out of the serialized upstream.
---
.../shenyu/loadbalancer/entity/Upstream.java | 36 +++++++++++---
.../shenyu/loadbalancer/entity/UpstreamTest.java | 56 ++++++++++++++++++++++
2 files changed, 85 insertions(+), 7 deletions(-)
diff --git
a/shenyu-loadbalancer/src/main/java/org/apache/shenyu/loadbalancer/entity/Upstream.java
b/shenyu-loadbalancer/src/main/java/org/apache/shenyu/loadbalancer/entity/Upstream.java
index d8788de44a..2121d891a8 100644
---
a/shenyu-loadbalancer/src/main/java/org/apache/shenyu/loadbalancer/entity/Upstream.java
+++
b/shenyu-loadbalancer/src/main/java/org/apache/shenyu/loadbalancer/entity/Upstream.java
@@ -30,11 +30,18 @@ import java.util.concurrent.atomic.AtomicLong;
*/
public final class Upstream {
+ private static final String DEFAULT_PROTOCOL = "http://";
+
/**
* protocol.
*/
private final String protocol;
+ /**
+ * Normalized protocol prefix, computed once because the protocol is
immutable.
+ */
+ private final transient String protocolPrefix;
+
/**
* url.
*/
@@ -126,6 +133,7 @@ public final class Upstream {
private Upstream(final Builder builder) {
this.protocol = builder.protocol;
+ this.protocolPrefix = normalizeProtocol(builder.protocol);
this.url = builder.url;
this.weight = builder.weight;
this.status = builder.status;
@@ -474,13 +482,9 @@ public final class Upstream {
* @return domain string
*/
public String buildDomain() {
- String protocol = this.getProtocol();
- if (StringUtils.isBlank(protocol)) {
- protocol = "http://";
- }
- return protocol +
Optional.ofNullable(this.getUrl()).map(String::trim).orElse(null);
+ return protocolPrefix +
Optional.ofNullable(this.getUrl()).map(String::trim).orElse(null);
}
-
+
/**
* Build request with protocol.
*
@@ -488,7 +492,25 @@ public final class Upstream {
* @return domain string
*/
public String buildDomain(final String protocol) {
- return protocol +
Optional.ofNullable(this.getUrl()).map(String::trim).orElse(null);
+ return normalizeProtocol(protocol) +
Optional.ofNullable(this.getUrl()).map(String::trim).orElse(null);
+ }
+
+ /**
+ * Normalize a protocol value to a url prefix, so a bare scheme such as
{@code http}
+ * cannot be concatenated directly with the upstream address.
+ *
+ * @param protocol protocol
+ * @return the normalized protocol, always ending with {@code ://}
+ */
+ private static String normalizeProtocol(final String protocol) {
+ if (StringUtils.isBlank(protocol)) {
+ return DEFAULT_PROTOCOL;
+ }
+ String scheme =
StringUtils.stripEnd(StringUtils.stripEnd(protocol.trim(), "/"), ":");
+ if (scheme.isEmpty()) {
+ return DEFAULT_PROTOCOL;
+ }
+ return scheme + "://";
}
/**
diff --git
a/shenyu-loadbalancer/src/test/java/org/apache/shenyu/loadbalancer/entity/UpstreamTest.java
b/shenyu-loadbalancer/src/test/java/org/apache/shenyu/loadbalancer/entity/UpstreamTest.java
index 1a3e4d99b8..55cddfc3e0 100644
---
a/shenyu-loadbalancer/src/test/java/org/apache/shenyu/loadbalancer/entity/UpstreamTest.java
+++
b/shenyu-loadbalancer/src/test/java/org/apache/shenyu/loadbalancer/entity/UpstreamTest.java
@@ -91,6 +91,61 @@ public class UpstreamTest {
Assertions.assertNotNull(upstream2.toString());
}
+ @Test
+ public void buildDomainShouldNormalizeBareProtocols() {
+ Assertions.assertEquals("http://127.0.0.1:8080", Upstream.builder()
+ .protocol("http")
+ .url("127.0.0.1:8080")
+ .build()
+ .buildDomain());
+ Assertions.assertEquals("https://127.0.0.1:8080", Upstream.builder()
+ .protocol("https:")
+ .url("127.0.0.1:8080")
+ .build()
+ .buildDomain());
+ Assertions.assertEquals("https://127.0.0.1:8080", Upstream.builder()
+ .protocol("https:/")
+ .url("127.0.0.1:8080")
+ .build()
+ .buildDomain());
+ Assertions.assertEquals("https://127.0.0.1:8080", Upstream.builder()
+ .protocol("https://")
+ .url("127.0.0.1:8080")
+ .build()
+ .buildDomain());
+ Assertions.assertEquals("http://127.0.0.1:8080", Upstream.builder()
+ .protocol(" http ")
+ .url(" 127.0.0.1:8080 ")
+ .build()
+ .buildDomain());
+ Assertions.assertEquals("http://127.0.0.1:8080", Upstream.builder()
+ .protocol("")
+ .url("127.0.0.1:8080")
+ .build()
+ .buildDomain());
+ Assertions.assertEquals("http://127.0.0.1:8080", Upstream.builder()
+ .protocol(":")
+ .url("127.0.0.1:8080")
+ .build()
+ .buildDomain());
+ Assertions.assertEquals("http://127.0.0.1:8080", Upstream.builder()
+ .protocol("/")
+ .url("127.0.0.1:8080")
+ .build()
+ .buildDomain());
+ }
+
+ @Test
+ public void buildDomainWithExplicitProtocolShouldNormalizeBareSchemes() {
+ Upstream upstream = Upstream.builder()
+ .url("127.0.0.1:8080")
+ .build();
+ Assertions.assertEquals("ws://127.0.0.1:8080",
upstream.buildDomain("ws"));
+ Assertions.assertEquals("ws://127.0.0.1:8080",
upstream.buildDomain("ws://"));
+ Assertions.assertEquals("http://127.0.0.1:8080",
upstream.buildDomain(null));
+ Assertions.assertEquals("http://127.0.0.1:8080",
upstream.buildDomain(":"));
+ }
+
@Test
public void identityShouldRemainAnImmutableSnapshot() {
final Upstream upstream =
Upstream.builder().protocol("dubbo://").url("127.0.0.1:20880")
@@ -125,6 +180,7 @@ public class UpstreamTest {
final JsonObject gsonFields =
GsonUtils.getInstance().fromJson(GsonUtils.getInstance().toJson(upstream),
JsonObject.class);
Assertions.assertFalse(gsonFields.has("identity"));
+ Assertions.assertFalse(gsonFields.has("protocolPrefix"));
Assertions.assertEquals("127.0.0.2:20880",
gsonFields.get("url").getAsString());
Assertions.assertEquals("green",
gsonFields.get("group").getAsString());
Assertions.assertEquals("v2", gsonFields.get("version").getAsString());