Support semicolons in query parameters as explain in the W3C recommentation (#9701)
Motivation: Support semicolons in query parameters as explain in the W3C recommentation: https://www.w3.org/TR/2014/REC-html5-20141028/forms.html#url-encoded-form-data Modification: - Add a new constructor arg that can be used to "switch" modes for decoding ; - Add unit test Result: Fixes #8855
This commit is contained in:
parent
e8e7a206b3
commit
7d6d953153
@ -68,6 +68,7 @@ public class QueryStringDecoder {
|
||||
private final Charset charset;
|
||||
private final String uri;
|
||||
private final int maxParams;
|
||||
private final boolean semicolonIsNormalChar;
|
||||
private int pathEndIdx;
|
||||
private String path;
|
||||
private Map<String, List<String>> params;
|
||||
@ -109,9 +110,19 @@ public class QueryStringDecoder {
|
||||
* specified charset.
|
||||
*/
|
||||
public QueryStringDecoder(String uri, Charset charset, boolean hasPath, int maxParams) {
|
||||
this(uri, charset, hasPath, maxParams, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new decoder that decodes the specified URI encoded in the
|
||||
* specified charset.
|
||||
*/
|
||||
public QueryStringDecoder(String uri, Charset charset, boolean hasPath,
|
||||
int maxParams, boolean semicolonIsNormalChar) {
|
||||
this.uri = checkNotNull(uri, "uri");
|
||||
this.charset = checkNotNull(charset, "charset");
|
||||
this.maxParams = checkPositive(maxParams, "maxParams");
|
||||
this.semicolonIsNormalChar = semicolonIsNormalChar;
|
||||
|
||||
// `-1` means that path end index will be initialized lazily
|
||||
pathEndIdx = hasPath ? -1 : 0;
|
||||
@ -138,6 +149,14 @@ public class QueryStringDecoder {
|
||||
* specified charset.
|
||||
*/
|
||||
public QueryStringDecoder(URI uri, Charset charset, int maxParams) {
|
||||
this(uri, charset, maxParams, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new decoder that decodes the specified URI encoded in the
|
||||
* specified charset.
|
||||
*/
|
||||
public QueryStringDecoder(URI uri, Charset charset, int maxParams, boolean semicolonIsNormalChar) {
|
||||
String rawPath = uri.getRawPath();
|
||||
if (rawPath == null) {
|
||||
rawPath = EMPTY_STRING;
|
||||
@ -147,6 +166,7 @@ public class QueryStringDecoder {
|
||||
this.uri = rawQuery == null? rawPath : rawPath + '?' + rawQuery;
|
||||
this.charset = checkNotNull(charset, "charset");
|
||||
this.maxParams = checkPositive(maxParams, "maxParams");
|
||||
this.semicolonIsNormalChar = semicolonIsNormalChar;
|
||||
pathEndIdx = rawPath.length();
|
||||
}
|
||||
|
||||
@ -177,7 +197,7 @@ public class QueryStringDecoder {
|
||||
*/
|
||||
public Map<String, List<String>> parameters() {
|
||||
if (params == null) {
|
||||
params = decodeParams(uri, pathEndIdx(), charset, maxParams);
|
||||
params = decodeParams(uri, pathEndIdx(), charset, maxParams, semicolonIsNormalChar);
|
||||
}
|
||||
return params;
|
||||
}
|
||||
@ -204,7 +224,8 @@ public class QueryStringDecoder {
|
||||
return pathEndIdx;
|
||||
}
|
||||
|
||||
private static Map<String, List<String>> decodeParams(String s, int from, Charset charset, int paramsLimit) {
|
||||
private static Map<String, List<String>> decodeParams(String s, int from, Charset charset, int paramsLimit,
|
||||
boolean semicolonIsNormalChar) {
|
||||
int len = s.length();
|
||||
if (from >= len) {
|
||||
return Collections.emptyMap();
|
||||
@ -226,8 +247,12 @@ public class QueryStringDecoder {
|
||||
valueStart = i + 1;
|
||||
}
|
||||
break;
|
||||
case '&':
|
||||
case ';':
|
||||
if (semicolonIsNormalChar) {
|
||||
continue;
|
||||
}
|
||||
// fall-through
|
||||
case '&':
|
||||
if (addParam(s, nameStart, valueStart, i, params, charset)) {
|
||||
paramsLimit--;
|
||||
if (paramsLimit == 0) {
|
||||
|
@ -129,6 +129,13 @@ public class QueryStringDecoderTest {
|
||||
assertQueryString("/foo?a=1&a=&a=", "/foo?a=1&a&a=");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSemicolon() {
|
||||
assertQueryString("/foo?a=1;2", "/foo?a=1;2", false);
|
||||
// ";" should be treated as a normal character, see #8855
|
||||
assertQueryString("/foo?a=1;2", "/foo?a=1%3B2", true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPathSpecific() {
|
||||
// decode escaped characters
|
||||
@ -225,8 +232,14 @@ public class QueryStringDecoderTest {
|
||||
}
|
||||
|
||||
private static void assertQueryString(String expected, String actual) {
|
||||
QueryStringDecoder ed = new QueryStringDecoder(expected, CharsetUtil.UTF_8);
|
||||
QueryStringDecoder ad = new QueryStringDecoder(actual, CharsetUtil.UTF_8);
|
||||
assertQueryString(expected, actual, false);
|
||||
}
|
||||
|
||||
private static void assertQueryString(String expected, String actual, boolean semicolonIsNormalChar) {
|
||||
QueryStringDecoder ed = new QueryStringDecoder(expected, CharsetUtil.UTF_8, true,
|
||||
1024, semicolonIsNormalChar);
|
||||
QueryStringDecoder ad = new QueryStringDecoder(actual, CharsetUtil.UTF_8, true,
|
||||
1024, semicolonIsNormalChar);
|
||||
Assert.assertEquals(ed.path(), ad.path());
|
||||
Assert.assertEquals(ed.parameters(), ad.parameters());
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user