Update variable detection in UriComponentsBuilder#encode

This commit better aligns how URI variable placeholders are detected
in UriComponentsBuilder#encode (i.e. the pre-encoding of the literal
parts of a URI template) and how they are expanded later on.
The latter relies on a pattern that stops at the first closing '}'
which excludes the possibility for well-formed, nested placeholders
other than variables with regex syntax, e.g. "{year:\d{1,4}}".

UriComponentsBuilder#encode now also stops at the first closing '}' and
further ensures the placeholder is not empty and that it has '{' before
deciding to treat it as a URI variable.

Closes gh-26466
This commit is contained in:
Rossen Stoyanchev
2021-02-10 21:10:03 +00:00
parent c9147c4281
commit 8791928f61
5 changed files with 90 additions and 26 deletions

View File

@@ -25,6 +25,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiConsumer;
import org.junit.jupiter.api.Test;
@@ -1037,6 +1038,35 @@ class UriComponentsBuilderTests {
assertThat(result1.getSchemeSpecificPart()).isEqualTo(null);
}
@Test // gh-26466
void encodeTemplateWithInvalidPlaceholderSyntax() {
BiConsumer<String, String> tester = (in, out) ->
assertThat(UriComponentsBuilder.fromUriString(in).encode().toUriString()).isEqualTo(out);
// empty
tester.accept("{}", "%7B%7D");
tester.accept("{ \t}", "%7B%20%09%7D");
tester.accept("/a{}b", "/a%7B%7Db");
tester.accept("/a{ \t}b", "/a%7B%20%09%7Db");
// nested, matching
tester.accept("{foo{}}", "%7Bfoo%7B%7D%7D");
tester.accept("{foo{bar}baz}", "%7Bfoo%7Bbar%7Dbaz%7D");
tester.accept("/a{foo{}}b", "/a%7Bfoo%7B%7D%7Db");
tester.accept("/a{foo{bar}baz}b", "/a%7Bfoo%7Bbar%7Dbaz%7Db");
// mismatched
tester.accept("{foo{{}", "%7Bfoo%7B%7B%7D");
tester.accept("{foo}}", "{foo}%7D");
tester.accept("/a{foo{{}bar", "/a%7Bfoo%7B%7B%7Dbar");
tester.accept("/a{foo}}b", "/a{foo}%7Db");
// variable with regex
tester.accept("{year:\\d{1,4}}", "{year:\\d{1,4}}");
tester.accept("/a{year:\\d{1,4}}b", "/a{year:\\d{1,4}}b");
}
@Test // SPR-11856
void fromHttpRequestForwardedHeader() {
MockHttpServletRequest request = new MockHttpServletRequest();