Skip to content

Commit

Permalink
CAMEL-8306: Add support for wildcards to match on prefix
Browse files Browse the repository at this point in the history
  • Loading branch information
TUCJVXCB committed Oct 4, 2023
1 parent 50e85a9 commit 3b3bda2
Showing 1 changed file with 54 additions and 28 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,16 @@
*/
package org.apache.camel.support;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.OptionalInt;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
* A context path matcher when using rest-dsl that allows components to reuse the same matching logic.
* <p/>
* The component should use the {@link #matchBestPath(String, String, java.util.List)} with the request details and the
* matcher returns the best matched, or <tt>null</tt> if none could be determined.
* The component should use the {@link #matchBestPath(String, String, List)} with the request details and the matcher
* returns the best matched, or <tt>null</tt> if none could be determined.
* <p/>
* The {@link ConsumerPath} is used for the components to provide the details to the matcher.
*/
Expand Down Expand Up @@ -156,9 +151,9 @@ T> ConsumerPath<T> matchBestPath(String requestMethod, String requestPath, List<
}
}

// if there are no wildcards, then select the matching with the longest path
boolean noWildcards = candidates.stream().allMatch(p -> countWildcards(p.getConsumerPath()) == 0);
if (noWildcards) {
// if there are no uri template, then select the matching with the longest path
boolean noCurlyBraces = candidates.stream().allMatch(p -> countCurlyBraces(p.getConsumerPath()) == 0);
if (noCurlyBraces) {
// grab first which is the longest that matched the request path
answer = candidates.stream()
.filter(c -> matchPath(requestPath, c.getConsumerPath(), c.isMatchOnUriPrefix()))
Expand All @@ -172,6 +167,17 @@ T> ConsumerPath<T> matchBestPath(String requestMethod, String requestPath, List<

// then match by wildcard path
it = candidates.iterator();
while (it.hasNext()) {
ConsumerPath<T> consumer = it.next();
// filter non matching paths
if (matchWildCard(requestPath, consumer.getConsumerPath())) {
answer = consumer;
break;
}
}

// then match by uri template path
it = candidates.iterator();
while (it.hasNext()) {
ConsumerPath<?> consumer = it.next();
// filter non matching paths
Expand All @@ -180,16 +186,16 @@ T> ConsumerPath<T> matchBestPath(String requestMethod, String requestPath, List<
}
}

// if there is multiple candidates with wildcards then pick anyone with the least number of wildcards
// if there is multiple candidates with uri template then pick anyone with the least number of uri template
ConsumerPath<T> best = null;
Map<Integer, List<ConsumerPath<T>>> pathMap = new HashMap<>();
if (candidates.size() > 1) {
it = candidates.iterator();
while (it.hasNext()) {
ConsumerPath<T> entry = it.next();
int wildcards = countWildcards(entry.getConsumerPath());
if (wildcards > 0) {
List<ConsumerPath<T>> consumerPathsLst = pathMap.computeIfAbsent(wildcards, key -> new ArrayList<>());
int curlyBraces = countCurlyBraces(entry.getConsumerPath());
if (curlyBraces > 0) {
List<ConsumerPath<T>> consumerPathsLst = pathMap.computeIfAbsent(curlyBraces, key -> new ArrayList<>());
consumerPathsLst.add(entry);
}
}
Expand All @@ -206,12 +212,12 @@ T> ConsumerPath<T> matchBestPath(String requestMethod, String requestPath, List<
}

if (best != null) {
// pick the best among the wildcards
// pick the best among uri template
answer = best;
}
}

// if there is one left then its our answer
// if there is one left then it's our answer
if (answer == null && candidates.size() == 1) {
answer = candidates.get(0);
}
Expand Down Expand Up @@ -257,10 +263,10 @@ private static boolean isOptionsMethod(String method) {
* Matches the given request path with the configured consumer path
*
* @param requestPath the request path
* @param consumerPath the consumer path which may use { } tokens
* @param isUriTemplate the consumer path which may use { } tokens
* @return <tt>true</tt> if matched, <tt>false</tt> otherwise
*/
private static boolean matchRestPath(String requestPath, String consumerPath, boolean wildcard) {
private static boolean matchRestPath(String requestPath, String consumerPath, boolean isUriTemplate) {
// deal with null parameters
if (requestPath == null && consumerPath == null) {
return true;
Expand Down Expand Up @@ -297,7 +303,7 @@ private static boolean matchRestPath(String requestPath, String consumerPath, bo
String p1 = requestPaths[i];
String p2 = consumerPaths[i];

if (wildcard && p2.startsWith("{") && p2.endsWith("}")) {
if (isUriTemplate && p2.startsWith("{") && p2.endsWith("}")) {
// always matches
continue;
}
Expand All @@ -312,13 +318,13 @@ private static boolean matchRestPath(String requestPath, String consumerPath, bo
}

/**
* Counts the number of wildcards in the path
* Counts the number of uri template's curlyBraces in the path
*
* @param consumerPath the consumer path which may use { } tokens
* @return number of wildcards, or <tt>0</tt> if no wildcards
* @return number of curlyBraces, or <tt>0</tt> if no curlyBraces
*/
private static int countWildcards(String consumerPath) {
int wildcards = 0;
private static int countCurlyBraces(String consumerPath) {
int curlyBraces = 0;

// remove starting/ending slashes
if (consumerPath.startsWith("/")) {
Expand All @@ -331,11 +337,31 @@ private static int countWildcards(String consumerPath) {
String[] consumerPaths = consumerPath.split("/");
for (String p2 : consumerPaths) {
if (p2.startsWith("{") && p2.endsWith("}")) {
wildcards++;
curlyBraces++;
}
}

return wildcards;
return curlyBraces;
}

private static boolean matchWildCard(String requestPath, String consumerPath) {
if (!requestPath.endsWith("/")) {
requestPath = requestPath + "/";
}
// Convert URI template to a regex pattern
String regex = consumerPath
.replace("/", "\\/")
.replace("{", "(?<")
.replace("}", ">[^\\/]+)");

// Add support for wildcard * as path suffix
regex = regex.replace("*", ".*");

// Match the provided path against the regex pattern
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(requestPath);

return matcher.matches();
}

}

0 comments on commit 3b3bda2

Please sign in to comment.