1515
1616import feign .Param .Expander ;
1717import feign .Util ;
18- import java .util .LinkedHashMap ;
1918import java .util .Map ;
2019import java .util .Map .Entry ;
2120import java .util .regex .Matcher ;
2221import java .util .regex .Pattern ;
2322
2423public final class Expressions {
2524
26- private static final String PATH_STYLE_MODIFIER = ";" ;
27- private static final Pattern EXPRESSION_PATTERN = Pattern .compile ("^([+#./;?&]?)(.*)$" );
25+ private static final String PATH_STYLE_OPERATOR = ";" ;
26+ /**
27+ * Literals may be present and preceded the expression.
28+ *
29+ * The expression part must start with a '{' and end with a '}'. The contents of the expression
30+ * may start with an RFC Operator or the operators reserved by the rfc: Level 2 Operators: '+' and
31+ * '#' Level 3 Operators: '.' and '/' and ';' and '?' and '&' Reserved Operators: '=' and ',' and
32+ * '!' and '@' and '|'
33+ *
34+ * The RFC specifies that '{' or '}' or '(' or ')' or'$' is are illegal characters. Feign does not
35+ * honor this portion of the RFC Expressions allow '$' characters for Collection expansions, and
36+ * all other characters are legal as a regular expression may be passed as a Value Modifier in
37+ * Feign
38+ *
39+ * This is not a complete implementation of the rfc
40+ *
41+ * <a href="https://www.rfc-editor.org/rfc/rfc6570#section-2.2>RFC 6570 Expressions</a>
42+ */
43+ private static final Pattern EXPRESSION_PATTERN =
44+ Pattern .compile ("^(\\ {([+#./;?&=,!@|]?)(.+)})$" );
45+
46+ // Partially From:
47+ // https://stackoverflow.com/questions/29494608/regex-for-uri-templates-rfc-6570-wanted -- I
48+ // suspect much of the codebase could be refactored around the example regex there
49+ /**
50+ * A pattern for matching possible variable names.
51+ *
52+ * This pattern accepts characters allowed in RFC 6570 Section 2.3 It also allows the characters
53+ * feign has allowed in the past "[]-$"
54+ *
55+ * The RFC specifies that a variable name followed by a ':' should be a max-length specification.
56+ * Feign deviates from the rfc in that the ':' value modifier is used to mark a regular
57+ * expression.
58+ *
59+ */
60+ private static final Pattern VARIABLE_LIST_PATTERN = Pattern .compile (
61+ "(([\\ w-\\ [\\ ]$]|%[0-9A-Fa-f]{2})(\\ .?([\\ w-\\ [\\ ]$]|%[0-9A-Fa-f]{2}))*(:.*|\\ *)?)(,(([\\ w-\\ [\\ ]$]|%[0-9A-Fa-f]{2})(\\ .?([\\ w-\\ [\\ ]$]|%[0-9A-Fa-f]{2}))*(:.*|\\ *)?))*" );
2862
2963 public static Expression create (final String value ) {
3064
@@ -37,14 +71,14 @@ public static Expression create(final String value) {
3771 /* create a new regular expression matcher for the expression */
3872 String variableName = null ;
3973 String variablePattern = null ;
40- String modifier = null ;
41- Matcher matcher = EXPRESSION_PATTERN .matcher (expression );
74+ String operator = null ;
75+ Matcher matcher = EXPRESSION_PATTERN .matcher (value );
4276 if (matcher .matches ()) {
43- /* grab the modifier */
44- modifier = matcher .group (1 ).trim ();
77+ /* grab the operator */
78+ operator = matcher .group (2 ).trim ();
4579
4680 /* we have a valid variable expression, extract the name from the first group */
47- variableName = matcher .group (2 ).trim ();
81+ variableName = matcher .group (3 ).trim ();
4882 if (variableName .contains (":" )) {
4983 /* split on the colon */
5084 String [] parts = variableName .split (":" );
@@ -59,13 +93,15 @@ public static Expression create(final String value) {
5993 }
6094 }
6195
62- /* check for a modifier */
63- if (PATH_STYLE_MODIFIER .equalsIgnoreCase (modifier )) {
96+ /* check for an operator */
97+ if (PATH_STYLE_OPERATOR .equalsIgnoreCase (operator )) {
6498 return new PathStyleExpression (variableName , variablePattern );
6599 }
66100
67101 /* default to simple */
68- return new SimpleExpression (variableName , variablePattern );
102+ return SimpleExpression .isSimpleExpression (value )
103+ ? new SimpleExpression (variableName , variablePattern )
104+ : null ; // Return null if it can't be validated as a Simple Expression -- Probably a Literal
69105 }
70106
71107 private static String stripBraces (String expression ) {
@@ -180,6 +216,13 @@ protected String expandMap(Map<String, ?> values) {
180216 }
181217 return result .toString ();
182218 }
219+
220+ protected static boolean isSimpleExpression (String expressionCandidate ) {
221+ final Matcher matcher = EXPRESSION_PATTERN .matcher (expressionCandidate );
222+ return matcher .matches ()
223+ && matcher .group (2 ).isEmpty () // Simple Expressions do not support any special operators
224+ && VARIABLE_LIST_PATTERN .matcher (matcher .group (3 )).matches ();
225+ }
183226 }
184227
185228 public static class PathStyleExpression extends SimpleExpression implements Expander {
0 commit comments