package com.firefly.server.http2.router.impl; import com.firefly.codec.http2.model.HttpHeader; import com.firefly.server.http2.SimpleRequest; import com.firefly.server.http2.router.Matcher; import com.firefly.server.http2.router.Router; import com.firefly.server.http2.router.RouterManager; import com.firefly.server.http2.router.RoutingContext; import com.firefly.utils.StringUtils; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; /** * @author Pengtao Qiu */ public class RouterManagerImpl implements RouterManager { private AtomicInteger idGenerator = new AtomicInteger(); private final Map<Matcher.MatchType, List<Matcher>> matcherMap; private final Matcher precisePathMather; private final Matcher patternPathMatcher; private final Matcher regexPathMatcher; private final Matcher parameterPathMatcher; private final Matcher httpMethodMatcher; private final Matcher contentTypePreciseMatcher; private final Matcher contentTypePatternMatcher; private final Matcher acceptHeaderMatcher; public RouterManagerImpl() { matcherMap = new HashMap<>(); precisePathMather = new PrecisePathMatcher(); patternPathMatcher = new PatternPathMatcher(); parameterPathMatcher = new ParameterPathMatcher(); regexPathMatcher = new RegexPathMatcher(); matcherMap.put(Matcher.MatchType.PATH, Arrays.asList(precisePathMather, patternPathMatcher, parameterPathMatcher, regexPathMatcher)); httpMethodMatcher = new HTTPMethodMatcher(); matcherMap.put(Matcher.MatchType.METHOD, Collections.singletonList(httpMethodMatcher)); contentTypePreciseMatcher = new ContentTypePreciseMatcher(); contentTypePatternMatcher = new ContentTypePatternMatcher(); matcherMap.put(Matcher.MatchType.CONTENT_TYPE, Arrays.asList(contentTypePreciseMatcher, contentTypePatternMatcher)); acceptHeaderMatcher = new AcceptHeaderMatcher(); matcherMap.put(Matcher.MatchType.ACCEPT, Collections.singletonList(acceptHeaderMatcher)); } public Matcher getHttpMethodMatcher() { return httpMethodMatcher; } public Matcher getPrecisePathMather() { return precisePathMather; } public Matcher getPatternPathMatcher() { return patternPathMatcher; } public Matcher getRegexPathMatcher() { return regexPathMatcher; } public Matcher getParameterPathMatcher() { return parameterPathMatcher; } public Matcher getContentTypePreciseMatcher() { return contentTypePreciseMatcher; } public Matcher getAcceptHeaderMatcher() { return acceptHeaderMatcher; } public Matcher getContentTypePatternMatcher() { return contentTypePatternMatcher; } @Override public NavigableSet<RouterMatchResult> findRouter(String method, String path, String contentType, String accept) { Map<Router, Set<Matcher.MatchType>> routerMatchTypes = new HashMap<>(); Map<Router, Map<String, String>> routerParameters = new HashMap<>(); findRouter(method, Matcher.MatchType.METHOD, routerMatchTypes, routerParameters); findRouter(path, Matcher.MatchType.PATH, routerMatchTypes, routerParameters); findRouter(contentType, Matcher.MatchType.CONTENT_TYPE, routerMatchTypes, routerParameters); findRouter(accept, Matcher.MatchType.ACCEPT, routerMatchTypes, routerParameters); Map<Router, Set<Matcher.MatchType>> filtered = filter(routerMatchTypes); if (filtered.isEmpty()) { return Collections.emptyNavigableSet(); } else { NavigableSet<RouterMatchResult> ret = new TreeSet<>(); filtered.forEach((router, value) -> { Map<String, String> routerParam = routerParameters.get(router); ret.add(new RouterMatchResult(router, routerParam, value)); }); return ret; } } private Map<Router, Set<Matcher.MatchType>> filter(Map<Router, Set<Matcher.MatchType>> routerMatchTypes) { Map<Router, Set<Matcher.MatchType>> ret = new HashMap<>(); routerMatchTypes.entrySet() .stream() .filter(e -> e.getKey().isEnable()) .filter(e -> e.getKey().getMatchTypes().equals(e.getValue())) .forEach(e -> ret.computeIfAbsent(e.getKey(), k -> new HashSet<>()).addAll(e.getValue())); return ret; } private List<Matcher.MatchResult> findRouter(String value, Matcher.MatchType matchType, Map<Router, Set<Matcher.MatchType>> routerMatchTypes, Map<Router, Map<String, String>> routerParameters) { List<Matcher.MatchResult> r = findRouter(value, matchType); r.forEach(result -> result.getRouters().forEach(router -> { routerMatchTypes.computeIfAbsent(router, k -> new HashSet<>()).add(result.getMatchType()); if (result.getParameters() != null && !result.getParameters().isEmpty()) { routerParameters.computeIfAbsent(router, k -> new HashMap<>()) .putAll(result.getParameters().get(router)); } })); return r; } private List<Matcher.MatchResult> findRouter(String value, Matcher.MatchType matchType) { if (!StringUtils.hasText(value) || matchType == null) { return Collections.emptyList(); } List<Matcher.MatchResult> ret = matcherMap.get(matchType) .stream() .map(m -> m.match(value)) .filter(Objects::nonNull) .collect(Collectors.toList()); if (ret == null) { return Collections.emptyList(); } else { return ret; } } @Override public Router register() { return new RouterImpl(idGenerator.getAndIncrement(), this); } public Router registerLast() { return new RouterImpl(Integer.MAX_VALUE, this); } @Override public void accept(SimpleRequest request) { String method = request.getMethod(); String path = request.getURI().getPath(); String contentType = request.getFields().get(HttpHeader.CONTENT_TYPE); String accept = request.getFields().get(HttpHeader.ACCEPT); NavigableSet<RouterMatchResult> routers = findRouter(method, path, contentType, accept); RoutingContext routingContext = new RoutingContextImpl(request, routers); routingContext.next(); } }