diff --git a/core/src/main/java/feign/CollectionFormat.java b/core/src/main/java/feign/CollectionFormat.java index 14909da41..5623c5aa6 100644 --- a/core/src/main/java/feign/CollectionFormat.java +++ b/core/src/main/java/feign/CollectionFormat.java @@ -74,21 +74,21 @@ public CharSequence join(String field, Collection values, Charset charse if (separator == null) { // exploded builder.append(valueCount++ == 0 ? "" : "&"); - builder.append(UriUtils.queryEncode(field, charset)); + builder.append(UriUtils.encode(field, charset)); if (value != null) { builder.append('='); - builder.append(UriUtils.queryEncode(value, charset)); + builder.append(UriUtils.encode(value, charset)); } } else { // delimited with a separator character if (builder.length() == 0) { - builder.append(UriUtils.queryEncode(field, charset)); + builder.append(UriUtils.encode(field, charset)); } if (value == null) { continue; } builder.append(valueCount++ == 0 ? "=" : separator); - builder.append(UriUtils.queryEncode(value, charset)); + builder.append(UriUtils.encode(value, charset)); } } return builder; diff --git a/core/src/main/java/feign/RequestTemplate.java b/core/src/main/java/feign/RequestTemplate.java index bf089181c..facf969f2 100644 --- a/core/src/main/java/feign/RequestTemplate.java +++ b/core/src/main/java/feign/RequestTemplate.java @@ -164,7 +164,10 @@ public RequestTemplate resolve(Map variables) { this.uriTemplate = UriTemplate.create("", !this.decodeSlash, this.charset); } - uri.append(this.uriTemplate.expand(variables)); + String expanded = this.uriTemplate.expand(variables); + if (expanded != null) { + uri.append(expanded); + } /* * for simplicity, combine the queries into the uri and use the resulting uri to seed the diff --git a/core/src/main/java/feign/template/BodyTemplate.java b/core/src/main/java/feign/template/BodyTemplate.java index f4c32c3e0..19f879eb9 100644 --- a/core/src/main/java/feign/template/BodyTemplate.java +++ b/core/src/main/java/feign/template/BodyTemplate.java @@ -50,14 +50,9 @@ private BodyTemplate(String value, Charset charset) { public String expand(Map variables) { String expanded = super.expand(variables); if (this.json) { - /* decode only the first and last character */ - StringBuilder sb = new StringBuilder(); - sb.append(JSON_TOKEN_START); - sb.append(expanded, - expanded.indexOf(JSON_TOKEN_START_ENCODED) + JSON_TOKEN_START_ENCODED.length(), - expanded.lastIndexOf(JSON_TOKEN_END_ENCODED)); - sb.append(JSON_TOKEN_END); - return sb.toString(); + /* restore all start and end tokens */ + expanded = expanded.replaceAll(JSON_TOKEN_START_ENCODED, JSON_TOKEN_START); + expanded = expanded.replaceAll(JSON_TOKEN_END_ENCODED, JSON_TOKEN_END); } return expanded; } diff --git a/core/src/main/java/feign/template/Expression.java b/core/src/main/java/feign/template/Expression.java index 3040aa1c3..f4852df8c 100644 --- a/core/src/main/java/feign/template/Expression.java +++ b/core/src/main/java/feign/template/Expression.java @@ -13,8 +13,8 @@ */ package feign.template; +import feign.CollectionFormat; import java.util.Optional; -import java.util.regex.Matcher; import java.util.regex.Pattern; /** diff --git a/core/src/main/java/feign/template/Expressions.java b/core/src/main/java/feign/template/Expressions.java index 96dfe02dc..b67541eff 100644 --- a/core/src/main/java/feign/template/Expressions.java +++ b/core/src/main/java/feign/template/Expressions.java @@ -13,16 +13,13 @@ */ package feign.template; -import feign.Util; -import feign.template.UriUtils.FragmentType; -import java.util.ArrayList; import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; import java.util.regex.Matcher; import java.util.regex.Pattern; +import feign.Util; public final class Expressions { private static Map> expressions; @@ -38,11 +35,12 @@ public final class Expressions { * * see https://tools.ietf.org/html/rfc6570#section-2.3 for more information. */ - expressions.put(Pattern.compile("(\\w[-\\w.\\[\\]]*[ ]*)(:(.+))?"), + + expressions.put(Pattern.compile("^([+#./;?&]?)(.*)$"), SimpleExpression.class); } - public static Expression create(final String value, final FragmentType type) { + public static Expression create(final String value) { /* remove the start and end braces */ final String expression = stripBraces(value); @@ -70,14 +68,22 @@ public static Expression create(final String value, final FragmentType type) { Matcher matcher = expressionPattern.matcher(expression); if (matcher.matches()) { /* we have a valid variable expression, extract the name from the first group */ - variableName = matcher.group(1).trim(); - if (matcher.group(2) != null && matcher.group(3) != null) { - /* this variable contains an optional pattern */ - variablePattern = matcher.group(3); + variableName = matcher.group(2).trim(); + if (variableName.contains(":")) { + /* split on the colon */ + String[] parts = variableName.split(":"); + variableName = parts[0]; + variablePattern = parts[1]; + } + + /* look for nested expressions */ + if (variableName.contains("{")) { + /* nested, literal */ + return null; } } - return new SimpleExpression(variableName, variablePattern, type); + return new SimpleExpression(variableName, variablePattern); } private static String stripBraces(String expression) { @@ -96,26 +102,19 @@ private static String stripBraces(String expression) { */ static class SimpleExpression extends Expression { - private final FragmentType type; - - SimpleExpression(String expression, String pattern, FragmentType type) { + SimpleExpression(String expression, String pattern) { super(expression, pattern); - this.type = type; } String encode(Object value) { - return UriUtils.encodeReserved(value.toString(), type, Util.UTF_8); + return UriUtils.encode(value.toString(), Util.UTF_8); } @Override String expand(Object variable, boolean encode) { StringBuilder expanded = new StringBuilder(); if (Iterable.class.isAssignableFrom(variable.getClass())) { - List items = new ArrayList<>(); - for (Object item : ((Iterable) variable)) { - items.add((encode) ? encode(item) : item.toString()); - } - expanded.append(String.join(Template.COLLECTION_DELIMITER, items)); + expanded.append(this.expandIterable((Iterable) variable)); } else { expanded.append((encode) ? encode(variable) : variable); } @@ -128,5 +127,38 @@ String expand(Object variable, boolean encode) { } return result; } + + + private String expandIterable(Iterable values) { + StringBuilder result = new StringBuilder(); + for (Object value : values) { + if (value == null) { + /* skip */ + continue; + } + + /* expand the value */ + String expanded = this.encode(value); + if (expanded.isEmpty()) { + /* always append the separator */ + result.append(","); + } else { + if (result.length() != 0) { + if (!result.toString().equalsIgnoreCase(",")) { + result.append(","); + } + } + result.append(expanded); + } + } + + if (result.length() == 0) { + /* completely unresolved */ + return null; + } + + /* return the expanded value */ + return result.toString(); + } } } diff --git a/core/src/main/java/feign/template/QueryTemplate.java b/core/src/main/java/feign/template/QueryTemplate.java index c67735272..19e267c7d 100644 --- a/core/src/main/java/feign/template/QueryTemplate.java +++ b/core/src/main/java/feign/template/QueryTemplate.java @@ -13,26 +13,29 @@ */ package feign.template; -import feign.CollectionFormat; -import feign.Util; import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; -import java.util.Iterator; +import java.util.Collections; import java.util.List; import java.util.Map; -import java.util.Objects; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.stream.Collectors; import java.util.stream.StreamSupport; +import feign.CollectionFormat; +import feign.Util; +import feign.template.Template.EncodingOptions; +import feign.template.Template.ExpansionOptions; /** * Template for a Query String parameter. */ -public final class QueryTemplate extends Template { +public final class QueryTemplate { private static final String UNDEF = "undef"; - private List values; + private List