From c261ca3102414053677c40cf17f4b70b28b4b170 Mon Sep 17 00:00:00 2001 From: rstoyanchev Date: Tue, 15 Oct 2024 20:50:01 +0100 Subject: [PATCH] Ensure scheme not lowercased if URI variable Closes gh-33699 --- .../org/springframework/web/util/RfcUriParser.java | 11 ++++++++--- .../springframework/web/util/WhatWgUrlParser.java | 13 ++++++++----- .../web/util/UriComponentsBuilderTests.java | 10 ++++++++++ 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/util/RfcUriParser.java b/spring-web/src/main/java/org/springframework/web/util/RfcUriParser.java index 383014d970..76f9d72b31 100644 --- a/spring-web/src/main/java/org/springframework/web/util/RfcUriParser.java +++ b/spring-web/src/main/java/org/springframework/web/util/RfcUriParser.java @@ -16,6 +16,7 @@ package org.springframework.web.util; +import java.util.Locale; import java.util.Set; import org.apache.commons.logging.Log; @@ -508,7 +509,8 @@ abstract class RfcUriParser { } public InternalParser captureScheme() { - this.scheme = captureComponent("scheme").toLowerCase(); + String scheme = captureComponent("scheme"); + this.scheme = (!scheme.startsWith("{") ? scheme.toLowerCase(Locale.ROOT) : scheme); return this; } @@ -633,8 +635,11 @@ abstract class RfcUriParser { return true; } else if (c == '}') { - this.openCurlyBracketCount--; - return true; + if (this.openCurlyBracketCount > 0) { + this.openCurlyBracketCount--; + return true; + } + return false; } return (this.openCurlyBracketCount > 0); } diff --git a/spring-web/src/main/java/org/springframework/web/util/WhatWgUrlParser.java b/spring-web/src/main/java/org/springframework/web/util/WhatWgUrlParser.java index ffab1adea1..44cb470d30 100644 --- a/spring-web/src/main/java/org/springframework/web/util/WhatWgUrlParser.java +++ b/spring-web/src/main/java/org/springframework/web/util/WhatWgUrlParser.java @@ -235,7 +235,7 @@ final class WhatWgUrlParser { logger.trace("Changing state from " + this.state + " to " + newState + " (cur: " + c + ")"); } this.state = newState; - this.openCurlyBracketCount = 0; + this.openCurlyBracketCount = (this.buffer.toString().equals("{") ? this.openCurlyBracketCount : 0); } private boolean processCurlyBrackets(int c) { @@ -244,8 +244,11 @@ final class WhatWgUrlParser { return true; } if (c == '}') { - this.openCurlyBracketCount--; - return true; + if (this.openCurlyBracketCount > 0) { + this.openCurlyBracketCount--; + return true; + } + return false; } return (this.openCurlyBracketCount > 0 && c != EOF); } @@ -756,7 +759,7 @@ final class WhatWgUrlParser { public void handle(int c, UrlRecord url, WhatWgUrlParser p) { // If c is an ASCII alpha, append c, lowercased, to buffer, and set state to scheme state. if (isAsciiAlpha(c)) { - p.append(Character.toLowerCase((char) c)); + p.append(p.openCurlyBracketCount == 0 ? Character.toLowerCase((char) c) : c); p.setState(SCHEME); } // EXTRA: if c is '{', append to buffer and continue as SCHEME @@ -782,7 +785,7 @@ final class WhatWgUrlParser { public void handle(int c, UrlRecord url, WhatWgUrlParser p) { // If c is an ASCII alphanumeric, U+002B (+), U+002D (-), or U+002E (.), append c, lowercased, to buffer. if (isAsciiAlphaNumeric(c) || (c == '+' || c == '-' || c == '.')) { - p.append(Character.toLowerCase((char) c)); + p.append(p.openCurlyBracketCount == 0 ? Character.toLowerCase((char) c) : c); } // Otherwise, if c is U+003A (:), then: else if (c == ':') { diff --git a/spring-web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java b/spring-web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java index ded7176bbe..1af1ad3149 100644 --- a/spring-web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java +++ b/spring-web/src/test/java/org/springframework/web/util/UriComponentsBuilderTests.java @@ -629,6 +629,16 @@ class UriComponentsBuilderTests { assertThat(result.toUriString()).isEqualTo("mailto:foo@example.com"); } + @ParameterizedTest // gh-33699 + @EnumSource(value = ParserType.class) + void schemeVariableMixedCase(ParserType parserType) { + URI uri = UriComponentsBuilder + .fromUriString("{TheScheme}://example.org", parserType) + .buildAndExpand(Map.of("TheScheme", "ws")) + .toUri(); + assertThat(uri.toString()).isEqualTo("ws://example.org"); + } + @ParameterizedTest @EnumSource(value = ParserType.class) void queryParamWithValueWithEquals(ParserType parserType) {