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());

Reply via email to