/atlassian-pretty-urls-plugin/src/main/java/com/atlassian/prettyurls/internal/route/UrlRouterImpl.java
Java | 202 lines | 147 code | 29 blank | 26 comment | 13 complexity | a59c7c4bdb2079a7b7847920016d1604 MD5 | raw file
- package com.atlassian.prettyurls.internal.route;
- import com.atlassian.ozymandias.SafePluginPointAccess;
- import com.atlassian.plugin.servlet.filter.FilterLocation;
- import com.atlassian.prettyurls.api.route.RoutePredicate;
- import com.atlassian.prettyurls.api.route.RouteService;
- import com.atlassian.prettyurls.api.route.UrlRouteRule;
- import com.atlassian.prettyurls.api.route.UrlRouteRuleSet;
- import com.google.common.annotations.VisibleForTesting;
- import com.google.common.collect.Maps;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Component;
- import javax.servlet.http.HttpServletRequest;
- import javax.ws.rs.core.UriBuilder;
- import java.net.URI;
- import java.net.URISyntaxException;
- import java.util.Collections;
- import java.util.List;
- import java.util.Map;
- import java.util.Set;
- import static com.atlassian.prettyurls.internal.route.UrlMatcher.Strategy;
- import static com.atlassian.prettyurls.internal.util.UrlUtils.startWithSlash;
- import static java.util.stream.Collectors.toList;
- import static java.util.stream.Collectors.toSet;
- /**
- * The implementation of URL routing
- */
- @Component
- public class UrlRouterImpl implements UrlRouter {
- private final static Logger log = LoggerFactory.getLogger(UrlRouterImpl.class);
- private final RouteService routeService;
- @Autowired
- public UrlRouterImpl(final RouteService routeService) {
- this.routeService = routeService;
- }
- @Override
- public Result route(HttpServletRequest httpRequest, FilterLocation filterLocation) {
- String requestURI;
- try {
- requestURI = makeRequestURI(httpRequest);
- } catch (URISyntaxException e) {
- log.error("Failed to create requestUri {} due to: {}", httpRequest.getRequestURI(), e.getMessage());
- return new Result(null, false);
- }
- Set<UrlRouteRuleSet> urlRouteRuleSets = getRouteRuleSets(filterLocation, requestURI);
- //
- // filter away disabled rule sets
- urlRouteRuleSets = urlRouteRuleSets.stream()
- .filter(ruleSet -> ruleSetEnabled(httpRequest, ruleSet))
- .collect(toSet());
- //
- // if we have multiple competing rule sets for the same top level request then we have a route resolution
- // challenge and we move to JAX-RS matching
- //
- // https://jsr311.java.net//nonav/releases/1.0/spec/spec3.html#x3-350003.7.2
- //
- final Strategy matchStrategy = urlRouteRuleSets.size() > 1 ? Strategy.JAX_RS_MATCHING : Strategy.LIST_ORDER_MATCHING;
- final List<UrlRouteRule> urlRouteRules = urlRouteRuleSets
- .stream()
- //
- // now collect all the rules in the rule set
- .flatMap(ruleSet -> ruleSet.getUrlRouteRules().stream())
- //
- // filter away the disabled rules
- .filter(rule -> ruleEnabled(httpRequest, rule))
- //
- // filter away the non matching http verbs
- .filter(rule -> httpVerbsMatch(httpRequest, rule))
- //
- // and we have our list of enabled rules
- .collect(toList());
- final UrlMatcher.Result matchResult = new UrlMatcher().getMatchingRule(requestURI, urlRouteRules, matchStrategy);
- if (matchResult.matches()) {
- // we have a winner
- String toURI = buildToURI(matchResult.getMatchingRule().get(), httpRequest, matchResult.getParsedVariableValues());
- return new Result(toURI, true);
- }
- return new Result(null, false);
- }
- private boolean httpVerbsMatch(HttpServletRequest httpRequest, UrlRouteRule urlRouteRule) {
- List<String> httpVerbs = urlRouteRule.getHttpVerbs();
- if (httpVerbs.size() == 0) {
- return true;
- }
- String method = httpRequest.getMethod();
- method = (method == null) ? "" : method.toUpperCase();
- for (String httpVerb : httpVerbs) {
- if (method.equals(httpVerb)) {
- return true;
- }
- }
- return false;
- }
- private String buildToURI(UrlRouteRule urlRouteRule, HttpServletRequest request, Map<String, String> parsedFromValues) {
- UrlRouteRule.ParameterMode parameterMode = urlRouteRule.getParameterMode();
- String toURI = urlRouteRule.getToUriGenerator().generate(request, parsedFromValues);
- Map<String, String> parametersToPass;
- switch (parameterMode) {
- case PASS_ALL:
- parametersToPass = parsedFromValues;
- break;
- case PASS_UNMAPPED:
- Map<String, String> unmappedFromValues = Maps.newHashMap(parsedFromValues);
- if (urlRouteRule.getTo() != null) {
- for (String variable : urlRouteRule.getTo().getTemplateVariables()) {
- unmappedFromValues.remove(variable);
- }
- }
- parametersToPass = unmappedFromValues;
- break;
- case PASS_NONE:
- parametersToPass = Collections.emptyMap();
- break;
- default:
- throw new IllegalArgumentException("Unrecognized " + UrlRouteRule.ParameterMode.class.getSimpleName() + " value: " + parameterMode);
- }
- //
- // add all the parameters that are left into the destination URL as query parameters
- // and then that is what we redirect to. RequestDispatcher handles the fact that
- // the original query parameters are retained.
- //
- UriBuilder uriBuilder = UriBuilder.fromUri(startWithSlash(toURI));
- for (Map.Entry<String, String> entry : parametersToPass.entrySet()) {
- uriBuilder.queryParam(entry.getKey(), entry.getValue());
- }
- toURI = uriBuilder.build().toString();
- return toURI;
- }
- private boolean ruleSetEnabled(final HttpServletRequest httpRequest, final UrlRouteRuleSet urlRouteRuleSet) {
- final RoutePredicate<UrlRouteRuleSet> predicate = urlRouteRuleSet.getPredicate();
- return runPredicateSafely(httpRequest, urlRouteRuleSet, predicate);
- }
- private boolean ruleEnabled(final HttpServletRequest httpRequest, final UrlRouteRule urlRouteRule) {
- final RoutePredicate<UrlRouteRule> predicate = urlRouteRule.getPredicate();
- return runPredicateSafely(httpRequest, urlRouteRule, predicate);
- }
- private <T> boolean runPredicateSafely(final HttpServletRequest httpRequest, final T rule, final RoutePredicate<T> predicate) {
- // we protect against dodgy plugin code here by using ozymandias. Its not enabled if it throws an exception during predicate evaluation
- Boolean result = SafePluginPointAccess.to().callable(() -> predicate.apply(httpRequest, rule));
- return result == null ? false : result;
- }
- @VisibleForTesting
- Set<UrlRouteRuleSet> getRouteRuleSets(FilterLocation filterLocation, String requestURI) {
- return routeService.getRouteRuleSets(filterLocation, requestURI);
- }
- @VisibleForTesting
- String makeRequestURI(HttpServletRequest httpServletRequest)
- throws URISyntaxException {
- String requestURI = new URI(httpServletRequest.getRequestURI()).normalize().toString();
- String context = httpServletRequest.getContextPath();
- if (requestURI.startsWith(context)) {
- requestURI = requestURI.substring(context.length());
- }
- return requestURI;
- }
- static class Result implements UrlRouter.Result {
- private final String toURI;
- private final boolean routed;
- private Result(String toURI, boolean routed) {
- this.toURI = toURI;
- this.routed = routed;
- }
- @Override
- public String toURI() {
- return toURI;
- }
- @Override
- public boolean isRouted() {
- return routed;
- }
- }
- }