Revise URI template handling in WhatWgUrlParser
Closes gh-33673
This commit is contained in:
parent
1f4743af54
commit
f4967f202e
|
|
@ -482,6 +482,7 @@ abstract class RfcUriParser {
|
|||
"index=" + this.index + ", componentIndex=" + this.componentIndex);
|
||||
}
|
||||
this.state = state;
|
||||
this.openCurlyBracketCount = 0;
|
||||
}
|
||||
|
||||
public void advanceTo(State state, int componentIndex) {
|
||||
|
|
|
|||
|
|
@ -47,12 +47,9 @@ import org.springframework.util.Assert;
|
|||
* <p>Comments in this class correlate to the parsing algorithm.
|
||||
* The implementation differs from the spec in the following ways:
|
||||
* <ul>
|
||||
* <li>Support for URI templates has been added, through the
|
||||
* {@link State#URL_TEMPLATE} state</li>
|
||||
* <li>Consequentially, the {@linkplain UrlRecord#port() URL port} has been
|
||||
* changed from an integer to a string,</li>
|
||||
* <li>To ensure that trailing slashes are significant, this implementation
|
||||
* prepends a '/' to each segment.</li>
|
||||
* <li>Supports URI template variables within URI components.
|
||||
* <li>Consequently, the port is a String and not an integer.
|
||||
* <li>Prepends '/' to each segment to ensure trailing slashes are significant.
|
||||
* </ul>
|
||||
* All of these modifications have been indicated through comments that start
|
||||
* with {@code EXTRA}.
|
||||
|
|
@ -89,9 +86,6 @@ final class WhatWgUrlParser {
|
|||
@Nullable
|
||||
private State state;
|
||||
|
||||
@Nullable
|
||||
private State previousState;
|
||||
|
||||
@Nullable
|
||||
private State stateOverride;
|
||||
|
||||
|
|
@ -101,6 +95,8 @@ final class WhatWgUrlParser {
|
|||
|
||||
private boolean insideBrackets;
|
||||
|
||||
private int openCurlyBracketCount;
|
||||
|
||||
private boolean stopMainLoop = false;
|
||||
|
||||
|
||||
|
|
@ -235,12 +231,22 @@ final class WhatWgUrlParser {
|
|||
else {
|
||||
c = "EOF";
|
||||
}
|
||||
logger.trace("Changing state from " + this.state + " to " +
|
||||
newState + " (cur: " + c + " prev: " + this.previousState + ")");
|
||||
logger.trace("Changing state from " + this.state + " to " + newState + " (cur: " + c + ")");
|
||||
}
|
||||
// EXTRA: we keep the previous state, to ensure that the parser can escape from malformed URI templates
|
||||
this.previousState = this.state;
|
||||
this.state = newState;
|
||||
this.openCurlyBracketCount = 0;
|
||||
}
|
||||
|
||||
private boolean processCurlyBrackets(int c) {
|
||||
if (c == '{') {
|
||||
this.openCurlyBracketCount++;
|
||||
return true;
|
||||
}
|
||||
if (c == '}') {
|
||||
this.openCurlyBracketCount--;
|
||||
return true;
|
||||
}
|
||||
return (this.openCurlyBracketCount > 0 && c != EOF);
|
||||
}
|
||||
|
||||
private static LinkedList<String> strictSplit(String input, int delimiter) {
|
||||
|
|
@ -755,12 +761,11 @@ final class WhatWgUrlParser {
|
|||
p.append(Character.toLowerCase((char) c));
|
||||
p.setState(SCHEME);
|
||||
}
|
||||
// EXTRA: if c is '{', then append c to buffer, set previous state to scheme state,
|
||||
// and state to url template state.
|
||||
else if (p.previousState != URL_TEMPLATE && c == '{') {
|
||||
// EXTRA: if c is '{', append to buffer and continue as SCHEME
|
||||
else if (c == '{') {
|
||||
p.openCurlyBracketCount++;
|
||||
p.append(c);
|
||||
p.previousState = SCHEME;
|
||||
p.state = URL_TEMPLATE;
|
||||
p.setState(SCHEME);
|
||||
}
|
||||
// Otherwise, if state override is not given,
|
||||
// set state to no scheme state and decrease pointer by 1.
|
||||
|
|
@ -781,11 +786,6 @@ final class WhatWgUrlParser {
|
|||
if (isAsciiAlphaNumeric(c) || (c == '+' || c == '-' || c == '.')) {
|
||||
p.append(Character.toLowerCase((char) c));
|
||||
}
|
||||
// EXTRA: if c is '{', then append c to buffer, set state to url template state.
|
||||
else if (p.previousState != URL_TEMPLATE && c == '{') {
|
||||
p.append(c);
|
||||
p.setState(URL_TEMPLATE);
|
||||
}
|
||||
// Otherwise, if c is U+003A (:), then:
|
||||
else if (c == ':') {
|
||||
// If state override is given, then:
|
||||
|
|
@ -858,6 +858,10 @@ final class WhatWgUrlParser {
|
|||
p.setState(OPAQUE_PATH);
|
||||
}
|
||||
}
|
||||
// EXTRA: if c is within URI variable, keep appending
|
||||
else if (p.processCurlyBrackets(c)) {
|
||||
p.append(c);
|
||||
}
|
||||
// Otherwise, if state override is not given, set buffer to the empty string,
|
||||
// state to no scheme state, and start over (from the first code point in input).
|
||||
else if (p.stateOverride == null) {
|
||||
|
|
@ -1225,11 +1229,6 @@ final class WhatWgUrlParser {
|
|||
if (isAsciiDigit(c)) {
|
||||
p.append(c);
|
||||
}
|
||||
// EXTRA: if c is '{', then append c to buffer, set state to url template state.
|
||||
else if (p.previousState != URL_TEMPLATE && c == '{') {
|
||||
p.append(c);
|
||||
p.setState(URL_TEMPLATE);
|
||||
}
|
||||
// Otherwise, if one of the following is true:
|
||||
// - c is the EOF code point, U+002F (/), U+003F (?), or U+0023 (#)
|
||||
// - url is special and c is U+005C (\)
|
||||
|
|
@ -1279,6 +1278,10 @@ final class WhatWgUrlParser {
|
|||
p.setState(PATH_START);
|
||||
p.pointer--;
|
||||
}
|
||||
// EXTRA: if c is within URI variable, keep appending
|
||||
else if (p.processCurlyBrackets(c)) {
|
||||
p.append(c);
|
||||
}
|
||||
// Otherwise, port-invalid validation error, return failure.
|
||||
else {
|
||||
p.failure("Invalid port: \"" + Character.toString(c) + "\"");
|
||||
|
|
@ -1547,11 +1550,6 @@ final class WhatWgUrlParser {
|
|||
p.setState(FRAGMENT);
|
||||
}
|
||||
}
|
||||
// EXTRA: Otherwise, if c is '{', then append c to buffer, set state to url template state.
|
||||
else if (p.previousState != URL_TEMPLATE && c == '{') {
|
||||
p.append(c);
|
||||
p.setState(URL_TEMPLATE);
|
||||
}
|
||||
// Otherwise, run these steps:
|
||||
else {
|
||||
if (p.validate()) {
|
||||
|
|
@ -1582,12 +1580,6 @@ final class WhatWgUrlParser {
|
|||
OPAQUE_PATH {
|
||||
@Override
|
||||
public void handle(int c, UrlRecord url, WhatWgUrlParser p) {
|
||||
// EXTRA: if previous state is URL Template and the buffer is empty,
|
||||
// append buffer to url's path and empty the buffer
|
||||
if (p.previousState == URL_TEMPLATE && !p.buffer.isEmpty()) {
|
||||
url.path.append(p.buffer.toString());
|
||||
p.emptyBuffer();
|
||||
}
|
||||
// If c is U+003F (?), then set url’s query to the empty string and state to query state.
|
||||
if (c == '?') {
|
||||
url.query = new StringBuilder();
|
||||
|
|
@ -1599,11 +1591,6 @@ final class WhatWgUrlParser {
|
|||
url.fragment = new StringBuilder();
|
||||
p.setState(FRAGMENT);
|
||||
}
|
||||
// EXTRA: Otherwise, if c is '{', then append c to buffer, set state to url template state.
|
||||
else if (p.previousState != URL_TEMPLATE && c == '{') {
|
||||
p.append(c);
|
||||
p.setState(URL_TEMPLATE);
|
||||
}
|
||||
// Otherwise:
|
||||
else {
|
||||
if (p.validate()) {
|
||||
|
|
@ -1668,11 +1655,6 @@ final class WhatWgUrlParser {
|
|||
p.setState(FRAGMENT);
|
||||
}
|
||||
}
|
||||
// EXTRA: Otherwise, if c is '{', then append c to buffer, set state to url template state.
|
||||
else if (p.previousState != URL_TEMPLATE && c == '{') {
|
||||
p.append(c);
|
||||
p.setState(URL_TEMPLATE);
|
||||
}
|
||||
// Otherwise, if c is not the EOF code point:
|
||||
else if (c != EOF) {
|
||||
if (p.validate()) {
|
||||
|
|
@ -1725,24 +1707,6 @@ final class WhatWgUrlParser {
|
|||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
URL_TEMPLATE {
|
||||
@Override
|
||||
public void handle(int c, UrlRecord url, WhatWgUrlParser p) {
|
||||
Assert.state(p.previousState != null, "No previous state set");
|
||||
if (c == '}') {
|
||||
p.append(c);
|
||||
p.setState(p.previousState);
|
||||
}
|
||||
else if (c == EOF) {
|
||||
p.pointer -= p.buffer.length() + 1;
|
||||
p.emptyBuffer();
|
||||
p.setState(p.previousState);
|
||||
}
|
||||
else {
|
||||
p.append(c);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public abstract void handle(int c, UrlRecord url, WhatWgUrlParser p);
|
||||
|
|
|
|||
|
|
@ -603,6 +603,18 @@ class UriComponentsBuilderTests {
|
|||
assertThat(result.toUriString()).isEqualTo("/fooValue/barValue");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(value = ParserType.class)
|
||||
void parseBuildAndExpandHierarchical(ParserType parserType) {
|
||||
URI uri = UriComponentsBuilder
|
||||
.fromUriString("{scheme}://{host}:{port}/{segment}?{query}#{fragment}", parserType)
|
||||
.buildAndExpand(Map.of(
|
||||
"scheme", "ws", "host", "example.org", "port", "7777", "segment", "path",
|
||||
"query", "q=1", "fragment", "foo"))
|
||||
.toUri();
|
||||
assertThat(uri.toString()).isEqualTo("ws://example.org:7777/path?q=1#foo");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@EnumSource(value = ParserType.class)
|
||||
void buildAndExpandOpaque(ParserType parserType) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue