From 85dc7db33bdfdfef370702c7bf5caecf79c8b27a Mon Sep 17 00:00:00 2001 From: bitflicker64 Date: Sat, 24 Jan 2026 00:51:27 +0530 Subject: [PATCH 01/10] config: normalize scheme for selected URL options --- .../apache/hugegraph/config/HugeConfig.java | 57 +++++++++- .../hugegraph/unit/config/HugeConfigTest.java | 105 ++++++++++++++++-- 2 files changed, 152 insertions(+), 10 deletions(-) diff --git a/hugegraph-commons/hugegraph-common/src/main/java/org/apache/hugegraph/config/HugeConfig.java b/hugegraph-commons/hugegraph-common/src/main/java/org/apache/hugegraph/config/HugeConfig.java index 4837154563..34b3f07277 100644 --- a/hugegraph-commons/hugegraph-common/src/main/java/org/apache/hugegraph/config/HugeConfig.java +++ b/hugegraph-commons/hugegraph-common/src/main/java/org/apache/hugegraph/config/HugeConfig.java @@ -88,8 +88,12 @@ private void setLayoutIfNeeded(Configuration conf) { public R get(TypedOption option) { Object value = this.getProperty(option.name()); if (value == null) { - return option.defaultValue(); + value = option.defaultValue(); } + + // Normalize URL options if needed (add scheme like http://) + value = normalizeUrlOptionIfNeeded(option.name(), value); + return (R) value; } @@ -213,4 +217,55 @@ private static Configuration loadConfigFile(File configFile) { e, configFile); } } + + private static Object normalizeUrlOptionIfNeeded(String key, Object value) { + if (value == null) { + return null; + } + + String scheme = defaultSchemeFor(key); + if (scheme == null) { + return value; + } + + // URL options are defined as ConfigOption and normalized here. + if (value instanceof String) { + return prefixSchemeIfMissing((String) value, scheme); + } + + // If it ever hits here, it means config storage returned a non-string type; + // leave it unchanged (safer than forcing toString()). + return value; + } + + private static String defaultSchemeFor(String key) { + switch (key) { + case "restserver.url": + case "gremlinserver.url": + case "server.urls_to_pd": + return "http://"; + case "server.k8s_url": + return "https://"; + default: + return null; + } + } + + private static String prefixSchemeIfMissing(String raw, String scheme) { + if (raw == null) { + return null; + } + String s = raw.trim(); + if (s.isEmpty()) { + return s; + } + + // Keep original string if scheme already exists + String lower = s.toLowerCase(); + if (lower.startsWith("http://") || lower.startsWith("https://")) { + return s; + } + + return scheme + s; + } } diff --git a/hugegraph-commons/hugegraph-common/src/test/java/org/apache/hugegraph/unit/config/HugeConfigTest.java b/hugegraph-commons/hugegraph-common/src/test/java/org/apache/hugegraph/unit/config/HugeConfigTest.java index 4cc04cb4d0..1c8b24404f 100644 --- a/hugegraph-commons/hugegraph-common/src/test/java/org/apache/hugegraph/unit/config/HugeConfigTest.java +++ b/hugegraph-commons/hugegraph-common/src/test/java/org/apache/hugegraph/unit/config/HugeConfigTest.java @@ -32,6 +32,15 @@ import java.util.List; import java.util.Map; +import org.apache.commons.collections.IteratorUtils; +import org.apache.commons.configuration2.AbstractConfiguration; +import org.apache.commons.configuration2.Configuration; +import org.apache.commons.configuration2.MapConfiguration; +import org.apache.commons.configuration2.PropertiesConfiguration; +import org.apache.commons.configuration2.convert.DisabledListDelimiterHandler; +import org.apache.commons.configuration2.ex.ConfigurationException; +import org.apache.commons.configuration2.io.FileHandler; +import org.apache.commons.io.FileUtils; import org.apache.hugegraph.config.ConfigConvOption; import org.apache.hugegraph.config.ConfigException; import org.apache.hugegraph.config.ConfigListConvOption; @@ -42,15 +51,6 @@ import org.apache.hugegraph.config.OptionSpace; import org.apache.hugegraph.testutil.Assert; import org.apache.hugegraph.unit.BaseUnitTest; -import org.apache.commons.collections.IteratorUtils; -import org.apache.commons.configuration2.AbstractConfiguration; -import org.apache.commons.configuration2.Configuration; -import org.apache.commons.configuration2.MapConfiguration; -import org.apache.commons.configuration2.PropertiesConfiguration; -import org.apache.commons.configuration2.convert.DisabledListDelimiterHandler; -import org.apache.commons.configuration2.ex.ConfigurationException; -import org.apache.commons.configuration2.io.FileHandler; -import org.apache.commons.io.FileUtils; import org.junit.BeforeClass; import org.junit.Test; @@ -429,6 +429,45 @@ public void testFromMapConfigurationWithList() { Assert.assertTrue(values.contains("b")); } + @Test + public void testUrlOptionNormalizeAddsDefaultScheme() { + Map map = new HashMap<>(); + map.put("restserver.url", "127.0.0.1:8080"); + map.put("gremlinserver.url", "127.0.0.1:8182"); + map.put("server.urls_to_pd", "0.0.0.0:8080"); + map.put("server.k8s_url", "127.0.0.1:8888"); + + HugeConfig config = new HugeConfig(map); + + Assert.assertEquals("http://127.0.0.1:8080", + config.get(UrlOptions.restUrl)); + Assert.assertEquals("http://127.0.0.1:8182", + config.get(UrlOptions.gremlinUrl)); + Assert.assertEquals("http://0.0.0.0:8080", + config.get(UrlOptions.urlsToPd)); + + // critical corner case: must NOT downgrade to http + Assert.assertEquals("https://127.0.0.1:8888", + config.get(UrlOptions.k8sUrl)); + } + + @Test + public void testUrlOptionNormalizeKeepsExistingScheme() { + Map map = new HashMap<>(); + map.put("restserver.url", "https://127.0.0.1:8080"); + map.put("gremlinserver.url", "http://127.0.0.1:8182"); + map.put("server.k8s_url", "http://127.0.0.1:8888"); + + HugeConfig config = new HugeConfig(map); + + Assert.assertEquals("https://127.0.0.1:8080", + config.get(UrlOptions.restUrl)); + Assert.assertEquals("http://127.0.0.1:8182", + config.get(UrlOptions.gremlinUrl)); + Assert.assertEquals("http://127.0.0.1:8888", + config.get(UrlOptions.k8sUrl)); + } + public static class TestOptions extends OptionHolder { private static volatile TestOptions instance; @@ -586,6 +625,54 @@ public static class TestSubOptions extends TestOptions { ); } + /** + * Added: URL option holder to test HugeConfig URL normalization logic. + */ + public static class UrlOptions extends OptionHolder { + + private static volatile UrlOptions instance; + + public static synchronized UrlOptions instance() { + if (instance == null) { + instance = new UrlOptions(); + instance.registerOptions(); + } + return instance; + } + + public static final ConfigOption restUrl = + new ConfigOption<>( + "restserver.url", + "rest url", + disallowEmpty(), + "http://127.0.0.1:8080" + ); + + public static final ConfigOption gremlinUrl = + new ConfigOption<>( + "gremlinserver.url", + "gremlin url", + disallowEmpty(), + "http://127.0.0.1:8182" + ); + + public static final ConfigOption urlsToPd = + new ConfigOption<>( + "server.urls_to_pd", + "urls to pd", + disallowEmpty(), + "http://0.0.0.0:8080" + ); + + public static final ConfigOption k8sUrl = + new ConfigOption<>( + "server.k8s_url", + "k8s url", + disallowEmpty(), + "https://127.0.0.1:8888" + ); + } + public static class TestOptionsWithTypeError extends OptionHolder { private static volatile TestOptionsWithTypeError instance; From 8dff246ec770b439e103afd9551245c47cb6527c Mon Sep 17 00:00:00 2001 From: bitflicker64 Date: Tue, 27 Jan 2026 19:30:28 +0530 Subject: [PATCH 02/10] Add optional URL scheme normalization for config options --- .../apache/hugegraph/config/ConfigOption.java | 16 +++++++ .../apache/hugegraph/config/HugeConfig.java | 29 ++++++----- .../hugegraph/unit/config/HugeConfigTest.java | 48 +++++++++++++++---- .../hugegraph/config/ServerOptions.java | 8 ++-- 4 files changed, 75 insertions(+), 26 deletions(-) diff --git a/hugegraph-commons/hugegraph-common/src/main/java/org/apache/hugegraph/config/ConfigOption.java b/hugegraph-commons/hugegraph-common/src/main/java/org/apache/hugegraph/config/ConfigOption.java index 159f13901f..cffef28f19 100644 --- a/hugegraph-commons/hugegraph-common/src/main/java/org/apache/hugegraph/config/ConfigOption.java +++ b/hugegraph-commons/hugegraph-common/src/main/java/org/apache/hugegraph/config/ConfigOption.java @@ -20,6 +20,22 @@ import com.google.common.base.Predicate; public class ConfigOption extends TypedOption { + private boolean urlNormalize = false; + private String defaultScheme = null; + + public ConfigOption withUrlNormalization(String scheme) { + this.urlNormalize = true; + this.defaultScheme = scheme; + return this; + } + + public boolean needsUrlNormalization() { + return this.urlNormalize; + } + + public String getDefaultScheme() { + return this.defaultScheme; + } public ConfigOption(String name, String desc, T value) { this(name, desc, null, value); diff --git a/hugegraph-commons/hugegraph-common/src/main/java/org/apache/hugegraph/config/HugeConfig.java b/hugegraph-commons/hugegraph-common/src/main/java/org/apache/hugegraph/config/HugeConfig.java index 34b3f07277..eb626d766f 100644 --- a/hugegraph-commons/hugegraph-common/src/main/java/org/apache/hugegraph/config/HugeConfig.java +++ b/hugegraph-commons/hugegraph-common/src/main/java/org/apache/hugegraph/config/HugeConfig.java @@ -87,12 +87,16 @@ private void setLayoutIfNeeded(Configuration conf) { @SuppressWarnings("unchecked") public R get(TypedOption option) { Object value = this.getProperty(option.name()); + boolean fromDefault = false; + if (value == null) { value = option.defaultValue(); + fromDefault = true; } - // Normalize URL options if needed (add scheme like http://) - value = normalizeUrlOptionIfNeeded(option.name(), value); + if (!fromDefault) { + value = normalizeUrlOptionIfNeeded(option.name(), value); + } return (R) value; } @@ -228,7 +232,8 @@ private static Object normalizeUrlOptionIfNeeded(String key, Object value) { return value; } - // URL options are defined as ConfigOption and normalized here. + // Normalize URL config values by adding default scheme if missing. + // Only applies to allowlisted URL options (see defaultSchemeFor()). if (value instanceof String) { return prefixSchemeIfMissing((String) value, scheme); } @@ -239,16 +244,14 @@ private static Object normalizeUrlOptionIfNeeded(String key, Object value) { } private static String defaultSchemeFor(String key) { - switch (key) { - case "restserver.url": - case "gremlinserver.url": - case "server.urls_to_pd": - return "http://"; - case "server.k8s_url": - return "https://"; - default: - return null; + TypedOption option = OptionSpace.get(key); + if (option instanceof ConfigOption) { + ConfigOption configOption = (ConfigOption) option; + if (configOption.needsUrlNormalization()) { + return configOption.getDefaultScheme(); + } } + return null; } private static String prefixSchemeIfMissing(String raw, String scheme) { @@ -263,7 +266,7 @@ private static String prefixSchemeIfMissing(String raw, String scheme) { // Keep original string if scheme already exists String lower = s.toLowerCase(); if (lower.startsWith("http://") || lower.startsWith("https://")) { - return s; + return lower; } return scheme + s; diff --git a/hugegraph-commons/hugegraph-common/src/test/java/org/apache/hugegraph/unit/config/HugeConfigTest.java b/hugegraph-commons/hugegraph-common/src/test/java/org/apache/hugegraph/unit/config/HugeConfigTest.java index 1c8b24404f..ce625d25e6 100644 --- a/hugegraph-commons/hugegraph-common/src/test/java/org/apache/hugegraph/unit/config/HugeConfigTest.java +++ b/hugegraph-commons/hugegraph-common/src/test/java/org/apache/hugegraph/unit/config/HugeConfigTest.java @@ -1,3 +1,4 @@ + /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with this @@ -32,15 +33,6 @@ import java.util.List; import java.util.Map; -import org.apache.commons.collections.IteratorUtils; -import org.apache.commons.configuration2.AbstractConfiguration; -import org.apache.commons.configuration2.Configuration; -import org.apache.commons.configuration2.MapConfiguration; -import org.apache.commons.configuration2.PropertiesConfiguration; -import org.apache.commons.configuration2.convert.DisabledListDelimiterHandler; -import org.apache.commons.configuration2.ex.ConfigurationException; -import org.apache.commons.configuration2.io.FileHandler; -import org.apache.commons.io.FileUtils; import org.apache.hugegraph.config.ConfigConvOption; import org.apache.hugegraph.config.ConfigException; import org.apache.hugegraph.config.ConfigListConvOption; @@ -51,6 +43,15 @@ import org.apache.hugegraph.config.OptionSpace; import org.apache.hugegraph.testutil.Assert; import org.apache.hugegraph.unit.BaseUnitTest; +import org.apache.commons.collections.IteratorUtils; +import org.apache.commons.configuration2.AbstractConfiguration; +import org.apache.commons.configuration2.Configuration; +import org.apache.commons.configuration2.MapConfiguration; +import org.apache.commons.configuration2.PropertiesConfiguration; +import org.apache.commons.configuration2.convert.DisabledListDelimiterHandler; +import org.apache.commons.configuration2.ex.ConfigurationException; +import org.apache.commons.configuration2.io.FileHandler; +import org.apache.commons.io.FileUtils; import org.junit.BeforeClass; import org.junit.Test; @@ -468,6 +469,35 @@ public void testUrlOptionNormalizeKeepsExistingScheme() { config.get(UrlOptions.k8sUrl)); } + @Test + public void testUrlNormalizationEdgeCases() { + Map map = new HashMap<>(); + + // Whitespace handling + map.put("restserver.url", " 127.0.0.1:8080 "); + HugeConfig config = new HugeConfig(map); + Assert.assertEquals("http://127.0.0.1:8080", + config.get(UrlOptions.restUrl)); + + // Mixed case scheme preservation + map.put("restserver.url", "HTTP://127.0.0.1:8080"); + config = new HugeConfig(map); + Assert.assertEquals("http://127.0.0.1:8080", + config.get(UrlOptions.restUrl)); + + // IPv6 + map.put("restserver.url", "[::1]:8080"); + config = new HugeConfig(map); + Assert.assertEquals("http://[::1]:8080", + config.get(UrlOptions.restUrl)); + + // 5. Malformed URLs + map.put("restserver.url", "://invalid"); + config = new HugeConfig(map); + Assert.assertEquals("http://://invalid", + config.get(UrlOptions.restUrl)); + } + public static class TestOptions extends OptionHolder { private static volatile TestOptions instance; diff --git a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/config/ServerOptions.java b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/config/ServerOptions.java index 920d119d45..278542854b 100644 --- a/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/config/ServerOptions.java +++ b/hugegraph-server/hugegraph-api/src/main/java/org/apache/hugegraph/config/ServerOptions.java @@ -32,7 +32,7 @@ public class ServerOptions extends OptionHolder { "The url for listening of graph server.", disallowEmpty(), "http://127.0.0.1:8080" - ); + ).withUrlNormalization("http://"); public static final ConfigOption SERVER_EVENT_HUB_THREADS = new ConfigOption<>( @@ -118,7 +118,7 @@ public class ServerOptions extends OptionHolder { "The url of gremlin server.", disallowEmpty(), "http://127.0.0.1:8182" - ); + ).withUrlNormalization("http://"); public static final ConfigOption GREMLIN_SERVER_TIMEOUT = new ConfigOption<>( @@ -270,7 +270,7 @@ public class ServerOptions extends OptionHolder { "to clients. only used when starting the server in k8s.", disallowEmpty(), "http://0.0.0.0:8080" - ); + ).withUrlNormalization("http://"); public static final ConfigOption SERVER_K8S_URL = new ConfigOption<>( @@ -278,7 +278,7 @@ public class ServerOptions extends OptionHolder { "The url of k8s.", disallowEmpty(), "https://127.0.0.1:8888" - ); + ).withUrlNormalization("https://"); public static final ConfigOption SERVER_K8S_USE_CA = new ConfigOption<>( From 575a6428c6b6ccb357b0736296210244383de474 Mon Sep 17 00:00:00 2001 From: bitflicker64 Date: Tue, 27 Jan 2026 20:13:50 +0530 Subject: [PATCH 03/10] Add URL normalization for config options --- .../java/org/apache/hugegraph/unit/config/HugeConfigTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/hugegraph-commons/hugegraph-common/src/test/java/org/apache/hugegraph/unit/config/HugeConfigTest.java b/hugegraph-commons/hugegraph-common/src/test/java/org/apache/hugegraph/unit/config/HugeConfigTest.java index ce625d25e6..1faa95f252 100644 --- a/hugegraph-commons/hugegraph-common/src/test/java/org/apache/hugegraph/unit/config/HugeConfigTest.java +++ b/hugegraph-commons/hugegraph-common/src/test/java/org/apache/hugegraph/unit/config/HugeConfigTest.java @@ -68,6 +68,7 @@ public class HugeConfigTest extends BaseUnitTest { @BeforeClass public static void init() { OptionSpace.register("test", TestOptions.class.getName()); + UrlOptions.instance(); } @Test From a1b42cd4e7d0eef49938a777b4a888fd7d223883 Mon Sep 17 00:00:00 2001 From: bitflicker64 Date: Tue, 27 Jan 2026 20:21:43 +0530 Subject: [PATCH 04/10] Add URL normalization for config options --- .../src/main/java/org/apache/hugegraph/config/HugeConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hugegraph-commons/hugegraph-common/src/main/java/org/apache/hugegraph/config/HugeConfig.java b/hugegraph-commons/hugegraph-common/src/main/java/org/apache/hugegraph/config/HugeConfig.java index eb626d766f..fe46c692c6 100644 --- a/hugegraph-commons/hugegraph-common/src/main/java/org/apache/hugegraph/config/HugeConfig.java +++ b/hugegraph-commons/hugegraph-common/src/main/java/org/apache/hugegraph/config/HugeConfig.java @@ -266,7 +266,7 @@ private static String prefixSchemeIfMissing(String raw, String scheme) { // Keep original string if scheme already exists String lower = s.toLowerCase(); if (lower.startsWith("http://") || lower.startsWith("https://")) { - return lower; + return s; } return scheme + s; From 20d9cf273656ab34d5841dcb120e792da5fc4dcd Mon Sep 17 00:00:00 2001 From: bitflicker64 Date: Tue, 27 Jan 2026 20:42:29 +0530 Subject: [PATCH 05/10] Add URL normalization for config options --- .../hugegraph/unit/config/HugeConfigTest.java | 58 ++++++------------- 1 file changed, 17 insertions(+), 41 deletions(-) diff --git a/hugegraph-commons/hugegraph-common/src/test/java/org/apache/hugegraph/unit/config/HugeConfigTest.java b/hugegraph-commons/hugegraph-common/src/test/java/org/apache/hugegraph/unit/config/HugeConfigTest.java index 1faa95f252..477b63056f 100644 --- a/hugegraph-commons/hugegraph-common/src/test/java/org/apache/hugegraph/unit/config/HugeConfigTest.java +++ b/hugegraph-commons/hugegraph-common/src/test/java/org/apache/hugegraph/unit/config/HugeConfigTest.java @@ -433,13 +433,13 @@ public void testFromMapConfigurationWithList() { @Test public void testUrlOptionNormalizeAddsDefaultScheme() { - Map map = new HashMap<>(); - map.put("restserver.url", "127.0.0.1:8080"); - map.put("gremlinserver.url", "127.0.0.1:8182"); - map.put("server.urls_to_pd", "0.0.0.0:8080"); - map.put("server.k8s_url", "127.0.0.1:8888"); + PropertiesConfiguration conf = new PropertiesConfiguration(); + conf.setProperty("restserver.url", "127.0.0.1:8080"); + conf.setProperty("gremlinserver.url", "127.0.0.1:8182"); + conf.setProperty("server.urls_to_pd", "0.0.0.0:8080"); + conf.setProperty("server.k8s_url", "127.0.0.1:8888"); - HugeConfig config = new HugeConfig(map); + HugeConfig config = new HugeConfig(conf); Assert.assertEquals("http://127.0.0.1:8080", config.get(UrlOptions.restUrl)); @@ -447,56 +447,32 @@ public void testUrlOptionNormalizeAddsDefaultScheme() { config.get(UrlOptions.gremlinUrl)); Assert.assertEquals("http://0.0.0.0:8080", config.get(UrlOptions.urlsToPd)); - - // critical corner case: must NOT downgrade to http Assert.assertEquals("https://127.0.0.1:8888", config.get(UrlOptions.k8sUrl)); } - @Test - public void testUrlOptionNormalizeKeepsExistingScheme() { - Map map = new HashMap<>(); - map.put("restserver.url", "https://127.0.0.1:8080"); - map.put("gremlinserver.url", "http://127.0.0.1:8182"); - map.put("server.k8s_url", "http://127.0.0.1:8888"); - - HugeConfig config = new HugeConfig(map); - - Assert.assertEquals("https://127.0.0.1:8080", - config.get(UrlOptions.restUrl)); - Assert.assertEquals("http://127.0.0.1:8182", - config.get(UrlOptions.gremlinUrl)); - Assert.assertEquals("http://127.0.0.1:8888", - config.get(UrlOptions.k8sUrl)); - } - @Test public void testUrlNormalizationEdgeCases() { - Map map = new HashMap<>(); - // Whitespace handling - map.put("restserver.url", " 127.0.0.1:8080 "); - HugeConfig config = new HugeConfig(map); + PropertiesConfiguration conf = new PropertiesConfiguration(); + conf.setProperty("restserver.url", " 127.0.0.1:8080 "); + HugeConfig config = new HugeConfig(conf); Assert.assertEquals("http://127.0.0.1:8080", config.get(UrlOptions.restUrl)); - // Mixed case scheme preservation - map.put("restserver.url", "HTTP://127.0.0.1:8080"); - config = new HugeConfig(map); - Assert.assertEquals("http://127.0.0.1:8080", + // Mixed case scheme preservation (lowercase "http://" is kept as-is) + conf = new PropertiesConfiguration(); + conf.setProperty("restserver.url", "HTTP://127.0.0.1:8080"); + config = new HugeConfig(conf); + Assert.assertEquals("HTTP://127.0.0.1:8080", config.get(UrlOptions.restUrl)); // IPv6 - map.put("restserver.url", "[::1]:8080"); - config = new HugeConfig(map); + conf = new PropertiesConfiguration(); + conf.setProperty("restserver.url", "[::1]:8080"); + config = new HugeConfig(conf); Assert.assertEquals("http://[::1]:8080", config.get(UrlOptions.restUrl)); - - // 5. Malformed URLs - map.put("restserver.url", "://invalid"); - config = new HugeConfig(map); - Assert.assertEquals("http://://invalid", - config.get(UrlOptions.restUrl)); } public static class TestOptions extends OptionHolder { From f3b01e7a99b750ce3ba389a0d28fb9df1f692096 Mon Sep 17 00:00:00 2001 From: bitflicker64 Date: Tue, 27 Jan 2026 20:57:16 +0530 Subject: [PATCH 06/10] Add URL normalization for config options --- .../org/apache/hugegraph/unit/config/HugeConfigTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hugegraph-commons/hugegraph-common/src/test/java/org/apache/hugegraph/unit/config/HugeConfigTest.java b/hugegraph-commons/hugegraph-common/src/test/java/org/apache/hugegraph/unit/config/HugeConfigTest.java index 477b63056f..0ce581a230 100644 --- a/hugegraph-commons/hugegraph-common/src/test/java/org/apache/hugegraph/unit/config/HugeConfigTest.java +++ b/hugegraph-commons/hugegraph-common/src/test/java/org/apache/hugegraph/unit/config/HugeConfigTest.java @@ -653,7 +653,7 @@ public static synchronized UrlOptions instance() { "rest url", disallowEmpty(), "http://127.0.0.1:8080" - ); + ).withUrlNormalization("http://"); public static final ConfigOption gremlinUrl = new ConfigOption<>( @@ -661,7 +661,7 @@ public static synchronized UrlOptions instance() { "gremlin url", disallowEmpty(), "http://127.0.0.1:8182" - ); + ).withUrlNormalization("http://"); public static final ConfigOption urlsToPd = new ConfigOption<>( @@ -669,7 +669,7 @@ public static synchronized UrlOptions instance() { "urls to pd", disallowEmpty(), "http://0.0.0.0:8080" - ); + ).withUrlNormalization("http://"); public static final ConfigOption k8sUrl = new ConfigOption<>( @@ -677,7 +677,7 @@ public static synchronized UrlOptions instance() { "k8s url", disallowEmpty(), "https://127.0.0.1:8888" - ); + ).withUrlNormalization("https://"); } public static class TestOptionsWithTypeError extends OptionHolder { From 67a0c9d90093e36f2e7e96f2b00a781e56172ebf Mon Sep 17 00:00:00 2001 From: bitflicker64 Date: Tue, 27 Jan 2026 21:11:44 +0530 Subject: [PATCH 07/10] Add URL normalization for config options --- .../main/java/org/apache/hugegraph/config/HugeConfig.java | 5 ++--- .../org/apache/hugegraph/unit/config/HugeConfigTest.java | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/hugegraph-commons/hugegraph-common/src/main/java/org/apache/hugegraph/config/HugeConfig.java b/hugegraph-commons/hugegraph-common/src/main/java/org/apache/hugegraph/config/HugeConfig.java index fe46c692c6..41cd25bfb9 100644 --- a/hugegraph-commons/hugegraph-common/src/main/java/org/apache/hugegraph/config/HugeConfig.java +++ b/hugegraph-commons/hugegraph-common/src/main/java/org/apache/hugegraph/config/HugeConfig.java @@ -266,9 +266,8 @@ private static String prefixSchemeIfMissing(String raw, String scheme) { // Keep original string if scheme already exists String lower = s.toLowerCase(); if (lower.startsWith("http://") || lower.startsWith("https://")) { - return s; + return lower; // Return LOWERCASE version } - - return scheme + s; + return scheme + lower; // Return scheme + LOWERCASE input } } diff --git a/hugegraph-commons/hugegraph-common/src/test/java/org/apache/hugegraph/unit/config/HugeConfigTest.java b/hugegraph-commons/hugegraph-common/src/test/java/org/apache/hugegraph/unit/config/HugeConfigTest.java index 0ce581a230..6e918742c4 100644 --- a/hugegraph-commons/hugegraph-common/src/test/java/org/apache/hugegraph/unit/config/HugeConfigTest.java +++ b/hugegraph-commons/hugegraph-common/src/test/java/org/apache/hugegraph/unit/config/HugeConfigTest.java @@ -68,7 +68,7 @@ public class HugeConfigTest extends BaseUnitTest { @BeforeClass public static void init() { OptionSpace.register("test", TestOptions.class.getName()); - UrlOptions.instance(); + OptionSpace.register("url-test", UrlOptions.class.getName()); } @Test @@ -464,7 +464,7 @@ public void testUrlNormalizationEdgeCases() { conf = new PropertiesConfiguration(); conf.setProperty("restserver.url", "HTTP://127.0.0.1:8080"); config = new HugeConfig(conf); - Assert.assertEquals("HTTP://127.0.0.1:8080", + Assert.assertEquals("http://127.0.0.1:8080", config.get(UrlOptions.restUrl)); // IPv6 From 358d037cc8e010a50008e541efb685f1926c5509 Mon Sep 17 00:00:00 2001 From: bitflicker64 Date: Fri, 30 Jan 2026 17:06:52 +0530 Subject: [PATCH 08/10] Move ServerOptions URL normalization tests to server test module --- .../hugegraph/unit/config/HugeConfigTest.java | 94 ------------------- .../unit/config/ServerOptionsTest.java | 76 +++++++++++++++ 2 files changed, 76 insertions(+), 94 deletions(-) create mode 100644 hugegraph-server/hugegraph-test/src/test/java/org/apache/hugegraph/unit/config/ServerOptionsTest.java diff --git a/hugegraph-commons/hugegraph-common/src/test/java/org/apache/hugegraph/unit/config/HugeConfigTest.java b/hugegraph-commons/hugegraph-common/src/test/java/org/apache/hugegraph/unit/config/HugeConfigTest.java index 6e918742c4..4cc04cb4d0 100644 --- a/hugegraph-commons/hugegraph-common/src/test/java/org/apache/hugegraph/unit/config/HugeConfigTest.java +++ b/hugegraph-commons/hugegraph-common/src/test/java/org/apache/hugegraph/unit/config/HugeConfigTest.java @@ -1,4 +1,3 @@ - /* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with this @@ -68,7 +67,6 @@ public class HugeConfigTest extends BaseUnitTest { @BeforeClass public static void init() { OptionSpace.register("test", TestOptions.class.getName()); - OptionSpace.register("url-test", UrlOptions.class.getName()); } @Test @@ -431,50 +429,6 @@ public void testFromMapConfigurationWithList() { Assert.assertTrue(values.contains("b")); } - @Test - public void testUrlOptionNormalizeAddsDefaultScheme() { - PropertiesConfiguration conf = new PropertiesConfiguration(); - conf.setProperty("restserver.url", "127.0.0.1:8080"); - conf.setProperty("gremlinserver.url", "127.0.0.1:8182"); - conf.setProperty("server.urls_to_pd", "0.0.0.0:8080"); - conf.setProperty("server.k8s_url", "127.0.0.1:8888"); - - HugeConfig config = new HugeConfig(conf); - - Assert.assertEquals("http://127.0.0.1:8080", - config.get(UrlOptions.restUrl)); - Assert.assertEquals("http://127.0.0.1:8182", - config.get(UrlOptions.gremlinUrl)); - Assert.assertEquals("http://0.0.0.0:8080", - config.get(UrlOptions.urlsToPd)); - Assert.assertEquals("https://127.0.0.1:8888", - config.get(UrlOptions.k8sUrl)); - } - - @Test - public void testUrlNormalizationEdgeCases() { - // Whitespace handling - PropertiesConfiguration conf = new PropertiesConfiguration(); - conf.setProperty("restserver.url", " 127.0.0.1:8080 "); - HugeConfig config = new HugeConfig(conf); - Assert.assertEquals("http://127.0.0.1:8080", - config.get(UrlOptions.restUrl)); - - // Mixed case scheme preservation (lowercase "http://" is kept as-is) - conf = new PropertiesConfiguration(); - conf.setProperty("restserver.url", "HTTP://127.0.0.1:8080"); - config = new HugeConfig(conf); - Assert.assertEquals("http://127.0.0.1:8080", - config.get(UrlOptions.restUrl)); - - // IPv6 - conf = new PropertiesConfiguration(); - conf.setProperty("restserver.url", "[::1]:8080"); - config = new HugeConfig(conf); - Assert.assertEquals("http://[::1]:8080", - config.get(UrlOptions.restUrl)); - } - public static class TestOptions extends OptionHolder { private static volatile TestOptions instance; @@ -632,54 +586,6 @@ public static class TestSubOptions extends TestOptions { ); } - /** - * Added: URL option holder to test HugeConfig URL normalization logic. - */ - public static class UrlOptions extends OptionHolder { - - private static volatile UrlOptions instance; - - public static synchronized UrlOptions instance() { - if (instance == null) { - instance = new UrlOptions(); - instance.registerOptions(); - } - return instance; - } - - public static final ConfigOption restUrl = - new ConfigOption<>( - "restserver.url", - "rest url", - disallowEmpty(), - "http://127.0.0.1:8080" - ).withUrlNormalization("http://"); - - public static final ConfigOption gremlinUrl = - new ConfigOption<>( - "gremlinserver.url", - "gremlin url", - disallowEmpty(), - "http://127.0.0.1:8182" - ).withUrlNormalization("http://"); - - public static final ConfigOption urlsToPd = - new ConfigOption<>( - "server.urls_to_pd", - "urls to pd", - disallowEmpty(), - "http://0.0.0.0:8080" - ).withUrlNormalization("http://"); - - public static final ConfigOption k8sUrl = - new ConfigOption<>( - "server.k8s_url", - "k8s url", - disallowEmpty(), - "https://127.0.0.1:8888" - ).withUrlNormalization("https://"); - } - public static class TestOptionsWithTypeError extends OptionHolder { private static volatile TestOptionsWithTypeError instance; diff --git a/hugegraph-server/hugegraph-test/src/test/java/org/apache/hugegraph/unit/config/ServerOptionsTest.java b/hugegraph-server/hugegraph-test/src/test/java/org/apache/hugegraph/unit/config/ServerOptionsTest.java new file mode 100644 index 0000000000..70e9198d1e --- /dev/null +++ b/hugegraph-server/hugegraph-test/src/test/java/org/apache/hugegraph/unit/config/ServerOptionsTest.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with this + * work for additional information regarding copyright ownership. The ASF + * licenses this file to You under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package org.apache.hugegraph.unit.config; + +import org.apache.commons.configuration2.PropertiesConfiguration; +import org.apache.hugegraph.config.HugeConfig; +import org.apache.hugegraph.config.OptionSpace; +import org.apache.hugegraph.config.ServerOptions; +import org.apache.hugegraph.testutil.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +public class ServerOptionsTest { + + @BeforeClass + public static void init() { + OptionSpace.register("server", + ServerOptions.class.getName()); + } + + @Test + public void testUrlOptionNormalizeAddsDefaultScheme() { + PropertiesConfiguration conf = new PropertiesConfiguration(); + conf.setProperty("restserver.url", "127.0.0.1:8080"); + conf.setProperty("gremlinserver.url", "127.0.0.1:8182"); + conf.setProperty("server.urls_to_pd", "0.0.0.0:8080"); + conf.setProperty("server.k8s_url", "127.0.0.1:8888"); + + HugeConfig config = new HugeConfig(conf); + + Assert.assertEquals("http://127.0.0.1:8080", + config.get(ServerOptions.REST_SERVER_URL)); + Assert.assertEquals("http://127.0.0.1:8182", + config.get(ServerOptions.GREMLIN_SERVER_URL)); + Assert.assertEquals("http://0.0.0.0:8080", + config.get(ServerOptions.SERVER_URLS_TO_PD)); + Assert.assertEquals("https://127.0.0.1:8888", + config.get(ServerOptions.SERVER_K8S_URL)); + } + + @Test + public void testUrlNormalizationEdgeCases() { + PropertiesConfiguration conf = new PropertiesConfiguration(); + conf.setProperty("restserver.url", " 127.0.0.1:8080 "); + HugeConfig config = new HugeConfig(conf); + Assert.assertEquals("http://127.0.0.1:8080", + config.get(ServerOptions.REST_SERVER_URL)); + + conf = new PropertiesConfiguration(); + conf.setProperty("restserver.url", "HTTP://127.0.0.1:8080"); + config = new HugeConfig(conf); + Assert.assertEquals("http://127.0.0.1:8080", + config.get(ServerOptions.REST_SERVER_URL)); + + conf = new PropertiesConfiguration(); + conf.setProperty("restserver.url", "[::1]:8080"); + config = new HugeConfig(conf); + Assert.assertEquals("http://[::1]:8080", + config.get(ServerOptions.REST_SERVER_URL)); + } +} From d1de45709590f3bb24b898a2a069e515029f49d6 Mon Sep 17 00:00:00 2001 From: bitflicker64 Date: Sun, 1 Feb 2026 20:58:21 +0530 Subject: [PATCH 09/10] feat: add URL normalization with warning logs - Add URL normalization support for config options - Automatically prefix missing schemes (http://, https://) - Log warnings when auto-correcting user-provided values - Add comprehensive test coverage for normalization logic - Update config files to demonstrate the feature Changes: - ConfigOption: Add withUrlNormalization() builder method - ServerOptions: Apply normalization to REST, Gremlin, K8s URLs - HugeConfig: Implement lazy cache and normalization logic - Add ServerOptionsTest with 5 test cases - Simplify URLs in main and Docker config --- .../server1-conf/rest-server.properties | 4 +- .../server2-conf/rest-server.properties | 4 +- .../server3-conf/rest-server.properties | 4 +- .../apache/hugegraph/config/HugeConfig.java | 62 ++++++++++++++----- .../static/conf/rest-server.properties | 4 +- .../unit/config/ServerOptionsTest.java | 46 ++++++++++++++ 6 files changed, 102 insertions(+), 22 deletions(-) diff --git a/docker/configs/server1-conf/rest-server.properties b/docker/configs/server1-conf/rest-server.properties index 1fd064d88a..fce537bb1c 100644 --- a/docker/configs/server1-conf/rest-server.properties +++ b/docker/configs/server1-conf/rest-server.properties @@ -1,7 +1,7 @@ # bind url -restserver.url=http://127.0.0.1:8081 +restserver.url=127.0.0.1:8081 # gremlin server url, need to be consistent with host and port in gremlin-server.yaml -gremlinserver.url=http://127.0.0.1:8181 +gremlinserver.url=127.0.0.1:8181 graphs=./conf/graphs diff --git a/docker/configs/server2-conf/rest-server.properties b/docker/configs/server2-conf/rest-server.properties index cff9405f5c..0e296b17b4 100644 --- a/docker/configs/server2-conf/rest-server.properties +++ b/docker/configs/server2-conf/rest-server.properties @@ -1,7 +1,7 @@ # bind url -restserver.url=http://127.0.0.1:8082 +restserver.url=127.0.0.1:8082 # gremlin server url, need to be consistent with host and port in gremlin-server.yaml -gremlinserver.url=http://127.0.0.1:8182 +gremlinserver.url=127.0.0.1:8182 graphs=./conf/graphs diff --git a/docker/configs/server3-conf/rest-server.properties b/docker/configs/server3-conf/rest-server.properties index 6c158e6236..f628dc61b4 100644 --- a/docker/configs/server3-conf/rest-server.properties +++ b/docker/configs/server3-conf/rest-server.properties @@ -1,7 +1,7 @@ # bind url -restserver.url=http://127.0.0.1:8083 +restserver.url=127.0.0.1:8083 # gremlin server url, need to be consistent with host and port in gremlin-server.yaml -gremlinserver.url=http://127.0.0.1:8183 +gremlinserver.url=127.0.0.1:8183 graphs=./conf/graphs diff --git a/hugegraph-commons/hugegraph-common/src/main/java/org/apache/hugegraph/config/HugeConfig.java b/hugegraph-commons/hugegraph-common/src/main/java/org/apache/hugegraph/config/HugeConfig.java index 41cd25bfb9..9dc28052f9 100644 --- a/hugegraph-commons/hugegraph-common/src/main/java/org/apache/hugegraph/config/HugeConfig.java +++ b/hugegraph-commons/hugegraph-common/src/main/java/org/apache/hugegraph/config/HugeConfig.java @@ -43,6 +43,11 @@ public class HugeConfig extends PropertiesConfiguration { private static final Logger LOG = Log.logger(HugeConfig.class); + // Cache for url normalization metadata + // Populated lazily on first use to ensure OptionSpace is already registered + private static final Map URL_NORMALIZATIONS = new HashMap<>(); + private static volatile boolean cacheInitialized = false; + private String configPath; public HugeConfig(Configuration config) { @@ -232,10 +237,17 @@ private static Object normalizeUrlOptionIfNeeded(String key, Object value) { return value; } - // Normalize URL config values by adding default scheme if missing. - // Only applies to allowlisted URL options (see defaultSchemeFor()). + // Normalize URL options if configured with .withUrlNormalization() if (value instanceof String) { - return prefixSchemeIfMissing((String) value, scheme); + String original = (String) value; + String normalized = prefixSchemeIfMissing(original, scheme); + + if (!original.equals(normalized)) { + LOG.warn("Config '{}' is missing scheme, auto-corrected to '{}'", + key, normalized); + } + + return normalized; } // If it ever hits here, it means config storage returned a non-string type; @@ -244,14 +256,29 @@ private static Object normalizeUrlOptionIfNeeded(String key, Object value) { } private static String defaultSchemeFor(String key) { - TypedOption option = OptionSpace.get(key); - if (option instanceof ConfigOption) { - ConfigOption configOption = (ConfigOption) option; - if (configOption.needsUrlNormalization()) { - return configOption.getDefaultScheme(); + ensureCacheInitialized(); + return URL_NORMALIZATIONS.get(key); + } + + private static void ensureCacheInitialized() { + if (!cacheInitialized) { + synchronized (URL_NORMALIZATIONS) { + if (!cacheInitialized) { + // Populate cache from OptionSpace + for (String optionKey : OptionSpace.keys()) { + TypedOption option = OptionSpace.get(optionKey); + if (option instanceof ConfigOption) { + ConfigOption configOption = (ConfigOption) option; + if (configOption.needsUrlNormalization()) { + URL_NORMALIZATIONS.put(optionKey, + configOption.getDefaultScheme()); + } + } + } + cacheInitialized = true; + } } } - return null; } private static String prefixSchemeIfMissing(String raw, String scheme) { @@ -263,11 +290,18 @@ private static String prefixSchemeIfMissing(String raw, String scheme) { return s; } - // Keep original string if scheme already exists - String lower = s.toLowerCase(); - if (lower.startsWith("http://") || lower.startsWith("https://")) { - return lower; // Return LOWERCASE version + int scIdx = s.indexOf("://"); + if (scIdx > 0) { + // Normalize existing scheme to lowercase while preserving the rest + String existingScheme = s.substring(0, scIdx).toLowerCase(); + String rest = s.substring(scIdx + 3); // skip the "://" delimiter + return existingScheme + "://" + rest; + } + + String defaultScheme = scheme == null ? "" : scheme; + if (!defaultScheme.isEmpty() && !defaultScheme.endsWith("://")) { + defaultScheme = defaultScheme + "://"; } - return scheme + lower; // Return scheme + LOWERCASE input + return defaultScheme + s; } } diff --git a/hugegraph-server/hugegraph-dist/src/assembly/static/conf/rest-server.properties b/hugegraph-server/hugegraph-dist/src/assembly/static/conf/rest-server.properties index 0dce972719..ad3e2700f8 100644 --- a/hugegraph-server/hugegraph-dist/src/assembly/static/conf/rest-server.properties +++ b/hugegraph-server/hugegraph-dist/src/assembly/static/conf/rest-server.properties @@ -1,9 +1,9 @@ # bind url # could use '0.0.0.0' or specified (real)IP to expose external network access -restserver.url=http://127.0.0.1:8080 +restserver.url=127.0.0.1:8080 #restserver.enable_graphspaces_filter=false # gremlin server url, need to be consistent with host and port in gremlin-server.yaml -#gremlinserver.url=http://127.0.0.1:8182 +#gremlinserver.url=127.0.0.1:8182 graphs=./conf/graphs diff --git a/hugegraph-server/hugegraph-test/src/test/java/org/apache/hugegraph/unit/config/ServerOptionsTest.java b/hugegraph-server/hugegraph-test/src/test/java/org/apache/hugegraph/unit/config/ServerOptionsTest.java index 70e9198d1e..6113c748b0 100644 --- a/hugegraph-server/hugegraph-test/src/test/java/org/apache/hugegraph/unit/config/ServerOptionsTest.java +++ b/hugegraph-server/hugegraph-test/src/test/java/org/apache/hugegraph/unit/config/ServerOptionsTest.java @@ -55,22 +55,68 @@ public void testUrlOptionNormalizeAddsDefaultScheme() { @Test public void testUrlNormalizationEdgeCases() { + // Whitespace trimming PropertiesConfiguration conf = new PropertiesConfiguration(); conf.setProperty("restserver.url", " 127.0.0.1:8080 "); HugeConfig config = new HugeConfig(conf); Assert.assertEquals("http://127.0.0.1:8080", config.get(ServerOptions.REST_SERVER_URL)); + // Case normalization conf = new PropertiesConfiguration(); conf.setProperty("restserver.url", "HTTP://127.0.0.1:8080"); config = new HugeConfig(conf); Assert.assertEquals("http://127.0.0.1:8080", config.get(ServerOptions.REST_SERVER_URL)); + // IPv6 without scheme conf = new PropertiesConfiguration(); conf.setProperty("restserver.url", "[::1]:8080"); config = new HugeConfig(conf); Assert.assertEquals("http://[::1]:8080", config.get(ServerOptions.REST_SERVER_URL)); + + // IPv6 with existing scheme + conf = new PropertiesConfiguration(); + conf.setProperty("restserver.url", "http://[::1]:8080"); + config = new HugeConfig(conf); + Assert.assertEquals("http://[::1]:8080", + config.get(ServerOptions.REST_SERVER_URL)); + } + + @Test + public void testUrlNormalizationPreservesHostnameCase() { + // Uppercase scheme + mixed-case hostname + PropertiesConfiguration conf = new PropertiesConfiguration(); + conf.setProperty("restserver.url", "HTTP://MyServer:8080"); + HugeConfig config = new HugeConfig(conf); + // Should lowercase ONLY the scheme, preserve "MyServer" + Assert.assertEquals("http://MyServer:8080", + config.get(ServerOptions.REST_SERVER_URL)); + + // Use server.k8s_url for HTTPS test (it defaults to https://) + conf = new PropertiesConfiguration(); + conf.setProperty("server.k8s_url", "HTTPS://MyHost:8888"); + config = new HugeConfig(conf); + Assert.assertEquals("https://MyHost:8888", + config.get(ServerOptions.SERVER_K8S_URL)); + } + + @Test + public void testUrlNormalizationPreservesPathCase() { + PropertiesConfiguration conf = new PropertiesConfiguration(); + conf.setProperty("restserver.url", "http://127.0.0.1:8080/SomePath/CaseSensitive"); + HugeConfig config = new HugeConfig(conf); + Assert.assertEquals("http://127.0.0.1:8080/SomePath/CaseSensitive", + config.get(ServerOptions.REST_SERVER_URL)); + } + + @Test + public void testHttpsSchemeIsNotDowngraded() { + PropertiesConfiguration conf = new PropertiesConfiguration(); + conf.setProperty("restserver.url", "https://127.0.0.1:8080"); + HugeConfig config = new HugeConfig(conf); + Assert.assertEquals("https://127.0.0.1:8080", + config.get(ServerOptions.REST_SERVER_URL)); } } From fb5459c652f3cde0818c66152bdffc0b4d40dbbb Mon Sep 17 00:00:00 2001 From: bitflicker64 Date: Sun, 1 Feb 2026 22:27:00 +0530 Subject: [PATCH 10/10] repair --- .../apache/hugegraph/config/HugeConfig.java | 46 ++++++++++--------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/hugegraph-commons/hugegraph-common/src/main/java/org/apache/hugegraph/config/HugeConfig.java b/hugegraph-commons/hugegraph-common/src/main/java/org/apache/hugegraph/config/HugeConfig.java index 9dc28052f9..9f9ecdea4d 100644 --- a/hugegraph-commons/hugegraph-common/src/main/java/org/apache/hugegraph/config/HugeConfig.java +++ b/hugegraph-commons/hugegraph-common/src/main/java/org/apache/hugegraph/config/HugeConfig.java @@ -43,10 +43,8 @@ public class HugeConfig extends PropertiesConfiguration { private static final Logger LOG = Log.logger(HugeConfig.class); - // Cache for url normalization metadata - // Populated lazily on first use to ensure OptionSpace is already registered + // Cache for URL normalization metadata (populated lazily per key) private static final Map URL_NORMALIZATIONS = new HashMap<>(); - private static volatile boolean cacheInitialized = false; private String configPath; @@ -256,28 +254,32 @@ private static Object normalizeUrlOptionIfNeeded(String key, Object value) { } private static String defaultSchemeFor(String key) { - ensureCacheInitialized(); - return URL_NORMALIZATIONS.get(key); - } + // Check if we already cached this key's scheme + if (URL_NORMALIZATIONS.containsKey(key)) { + return URL_NORMALIZATIONS.get(key); + } + + // We don't know yet - look it up NOW from OptionSpace + synchronized (URL_NORMALIZATIONS) { + // Double-check after acquiring lock + if (URL_NORMALIZATIONS.containsKey(key)) { + return URL_NORMALIZATIONS.get(key); + } + + // Look up the option from OptionSpace + TypedOption option = OptionSpace.get(key); + String scheme = null; - private static void ensureCacheInitialized() { - if (!cacheInitialized) { - synchronized (URL_NORMALIZATIONS) { - if (!cacheInitialized) { - // Populate cache from OptionSpace - for (String optionKey : OptionSpace.keys()) { - TypedOption option = OptionSpace.get(optionKey); - if (option instanceof ConfigOption) { - ConfigOption configOption = (ConfigOption) option; - if (configOption.needsUrlNormalization()) { - URL_NORMALIZATIONS.put(optionKey, - configOption.getDefaultScheme()); - } - } - } - cacheInitialized = true; + if (option instanceof ConfigOption) { + ConfigOption configOption = (ConfigOption) option; + if (configOption.needsUrlNormalization()) { + scheme = configOption.getDefaultScheme(); } } + + // Cache it for next time (even if null) + URL_NORMALIZATIONS.put(key, scheme); + return scheme; } }