Lenient URI template encoding

URI template encoding ignores mismatched curly braces, treating them as
literal parts instead.

Issue: SPR-17630
This commit is contained in:
Rossen Stoyanchev
2019-01-02 16:36:08 -05:00
parent 4b24bcb799
commit 63984800e4
3 changed files with 48 additions and 30 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -756,6 +756,8 @@ final class HierarchicalUriComponents extends UriComponents {
private final StringBuilder currentLiteral = new StringBuilder();
private final StringBuilder currentVariable = new StringBuilder();
private final StringBuilder output = new StringBuilder();
@@ -767,22 +769,21 @@ final class HierarchicalUriComponents extends UriComponents {
@Override
public String apply(String source, Type type) {
// Only URI variable, nothing to encode..
// Only URI variable (nothing to encode)..
if (source.length() > 1 && source.charAt(0) == '{' && source.charAt(source.length() -1) == '}') {
return source;
}
// Only literal, encode all..
// Only literal (encode full source)..
if (source.indexOf('{') == -1) {
return encodeUriComponent(source, this.charset, type);
}
// Mixed, encode all except for URI variables..
// Mixed literal parts and URI variables, maybe (encode literal parts only)..
int level = 0;
clear(this.currentLiteral);
clear(this.currentVariable);
clear(this.output);
for (char c : source.toCharArray()) {
if (c == '{') {
level++;
@@ -790,21 +791,25 @@ final class HierarchicalUriComponents extends UriComponents {
encodeAndAppendCurrentLiteral(type);
}
}
if (c == '}') {
if (c == '}' && level > 0) {
level--;
Assert.isTrue(level >=0, "Mismatched open and close braces");
this.currentVariable.append('}');
if (level == 0) {
this.output.append(this.currentVariable);
clear(this.currentVariable);
}
}
if (level > 0 || (level == 0 && c == '}')) {
this.output.append(c);
else if (level > 0) {
this.currentVariable.append(c);
}
else {
this.currentLiteral.append(c);
}
}
Assert.isTrue(level == 0, "Mismatched open and close braces");
if (level > 0) {
this.currentLiteral.append(this.currentVariable);
}
encodeAndAppendCurrentLiteral(type);
return this.output.toString();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2018 the original author or authors.
* Copyright 2002-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -385,15 +385,22 @@ public class UriComponentsBuilder implements UriBuilder, Cloneable {
* @return the URI components
*/
public UriComponents build(boolean encoded) {
return buildInternal(encoded ?
EncodingHint.FULLY_ENCODED :
this.encodeTemplate ? EncodingHint.ENCODE_TEMPLATE : EncodingHint.NONE);
}
private UriComponents buildInternal(EncodingHint hint) {
UriComponents result;
if (this.ssp != null) {
result = new OpaqueUriComponents(this.scheme, this.ssp, this.fragment);
}
else {
HierarchicalUriComponents uric = new HierarchicalUriComponents(this.scheme, this.fragment,
this.userInfo, this.host, this.port, this.pathBuilder.build(), this.queryParams, encoded);
this.userInfo, this.host, this.port, this.pathBuilder.build(), this.queryParams,
hint == EncodingHint.FULLY_ENCODED);
result = this.encodeTemplate ? uric.encodeTemplate(this.charset) : uric;
result = hint == EncodingHint.ENCODE_TEMPLATE ? uric.encodeTemplate(this.charset) : uric;
}
if (!this.uriVariables.isEmpty()) {
result = result.expand(name -> this.uriVariables.getOrDefault(name, UriTemplateVariables.SKIP_VALUE));
@@ -425,24 +432,24 @@ public class UriComponentsBuilder implements UriBuilder, Cloneable {
@Override
public URI build(Object... uriVariables) {
return encode().buildAndExpand(uriVariables).toUri();
return buildInternal(EncodingHint.ENCODE_TEMPLATE).expand(uriVariables).toUri();
}
@Override
public URI build(Map<String, ?> uriVariables) {
return encode().buildAndExpand(uriVariables).toUri();
return buildInternal(EncodingHint.ENCODE_TEMPLATE).expand(uriVariables).toUri();
}
/**
* Build a URI String. This is a shortcut method which combines calls
* to {@link #build()}, then {@link UriComponents#encode()} and finally
* {@link UriComponents#toUriString()}.
* Build a URI String. This is a shortcut for:
* <pre>
* String uri = builder.encode().build().toUriString()
* </pre>
* @since 4.1
* @see UriComponents#toUriString()
*/
public String toUriString() {
return encode().build().toUriString();
return buildInternal(EncodingHint.ENCODE_TEMPLATE).toUriString();
}
@@ -1049,4 +1056,7 @@ public class UriComponentsBuilder implements UriBuilder, Cloneable {
}
}
private enum EncodingHint { ENCODE_TEMPLATE, FULLY_ENCODED, NONE }
}