1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package org.apache.hc.core5.http.impl.routing;
29
30 import java.util.ArrayList;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.function.BiFunction;
34 import java.util.function.Function;
35 import java.util.stream.Collectors;
36
37 import org.apache.hc.core5.annotation.Contract;
38 import org.apache.hc.core5.annotation.Internal;
39 import org.apache.hc.core5.annotation.ThreadingBehavior;
40 import org.apache.hc.core5.http.HttpException;
41 import org.apache.hc.core5.http.HttpRequest;
42 import org.apache.hc.core5.http.HttpRequestMapper;
43 import org.apache.hc.core5.http.MisdirectedRequestException;
44 import org.apache.hc.core5.http.protocol.HttpContext;
45 import org.apache.hc.core5.http.protocol.UriPatternType;
46 import org.apache.hc.core5.net.URIAuthority;
47 import org.apache.hc.core5.util.Args;
48
49
50
51
52
53
54
55 @Contract(threading = ThreadingBehavior.IMMUTABLE)
56 public class RequestRouter<T> implements HttpRequestMapper<T> {
57
58 @Internal
59 public final static class Entry<T> {
60
61 public final URIAuthority uriAuthority;
62 public final PathRoute<String, T> route;
63
64 public Entry(final URIAuthority uriAuthority, final String pathPattern, final T handler) {
65 this.uriAuthority = uriAuthority;
66 this.route = new PathRoute<>(pathPattern, handler);
67 }
68
69 public Entry(final String hostname, final String pathPattern, final T handler) {
70 this(new URIAuthority(hostname), pathPattern, handler);
71 }
72
73 public Entry(final String pathPattern, final T handler) {
74 this((URIAuthority) null, pathPattern, handler);
75 }
76
77 @Override
78 public String toString() {
79 return uriAuthority + "/" + route;
80 }
81
82 }
83
84 static class SingleAuthorityResolver<T> implements Function<URIAuthority, T> {
85
86 private final URIAuthority singleAuthority;
87 private final T router;
88
89 SingleAuthorityResolver(final URIAuthority singleAuthority, final T router) {
90 this.singleAuthority = singleAuthority;
91 this.router = router;
92 }
93
94 @Override
95 public T apply(final URIAuthority authority) {
96 return singleAuthority.equals(authority) ? router : null;
97 }
98
99 }
100
101 static class NoAuthorityResolver<T> implements Function<URIAuthority, T> {
102
103 @Override
104 public T apply(final URIAuthority authority) {
105 return null;
106 }
107
108 }
109
110 @Internal
111 public static <T> RequestRouter<T> create(final URIAuthority primaryAuthority,
112 final UriPatternType patternType,
113 final List<Entry<T>> handlerEntries,
114 final BiFunction<String, URIAuthority, URIAuthority> authorityResolver,
115 final HttpRequestMapper<T> downstream) {
116 final Map<URIAuthority, Function<String, T>> authorityMap = handlerEntries.stream()
117 .collect(Collectors.groupingBy(
118 e -> e.uriAuthority != null ? e.uriAuthority : primaryAuthority != null ? primaryAuthority : LOCAL_AUTHORITY,
119 Collectors.mapping(e -> e.route,
120 Collectors.collectingAndThen(Collectors.toList(), e -> {
121 switch (patternType) {
122 case URI_PATTERN:
123 return UriPathRouter.bestMatch(e);
124 case URI_PATTERN_IN_ORDER:
125 return UriPathRouter.ordered(e);
126 case REGEX:
127 return UriPathRouter.regEx(e);
128 default:
129 throw new IllegalStateException("Unexpected pattern type: " + patternType);
130 }
131 }))));
132 final Function<URIAuthority, Function<String, T>> authorityFunction;
133 if (authorityMap.isEmpty()) {
134 authorityFunction = new NoAuthorityResolver<>();
135 } else if (authorityMap.size() == 1) {
136 final Map.Entry<URIAuthority, Function<String, T>> entry = authorityMap.entrySet().iterator().next();
137 authorityFunction = new SingleAuthorityResolver<>(entry.getKey(), entry.getValue());
138 } else {
139 authorityFunction = authorityMap::get;
140 }
141 return new RequestRouter<>(authorityFunction, authorityResolver, downstream);
142 }
143
144 public static <T> Builder<T> builder(final UriPatternType patternType) {
145 return new Builder<>(patternType);
146 }
147
148 public static <T> Builder<T> builder() {
149 return new Builder<>(UriPatternType.URI_PATTERN);
150 }
151
152 public static final URIAuthority LOCAL_AUTHORITY = new URIAuthority("localhost");
153 public final static BiFunction<String, URIAuthority, URIAuthority> LOCAL_AUTHORITY_RESOLVER = (scheme, authority) -> LOCAL_AUTHORITY;
154 public final static BiFunction<String, URIAuthority, URIAuthority> IGNORE_PORT_AUTHORITY_RESOLVER = (scheme, authority) ->
155 authority != null && authority.getPort() != -1 ? new URIAuthority(authority.getHostName(), -1) : authority;
156
157 private final Function<URIAuthority, Function<String, T>> authorityRouter;
158 private final BiFunction<String, URIAuthority, URIAuthority> authorityResolver;
159 private final HttpRequestMapper<T> downstream;
160
161 RequestRouter(final Function<URIAuthority, Function<String, T>> authorityRouter,
162 final BiFunction<String, URIAuthority, URIAuthority> authorityResolver,
163 final HttpRequestMapper<T> downstream) {
164 this.authorityRouter = authorityRouter;
165 this.authorityResolver = authorityResolver;
166 this.downstream = downstream;
167 }
168
169 @Override
170 public T resolve(final HttpRequest request, final HttpContext context) throws HttpException {
171 final URIAuthority authority = authorityResolver != null ?
172 authorityResolver.apply(request.getScheme(), request.getAuthority()) : request.getAuthority();
173 final Function<String, T> pathRouter = authority != null ?
174 authorityRouter.apply(authority) : null;
175 if (pathRouter == null) {
176 if (downstream != null) {
177 return downstream.resolve(request, context);
178 } else {
179 throw new MisdirectedRequestException("Not authoritative");
180 }
181 }
182 String path = request.getPath();
183 final int i = path.indexOf('?');
184 if (i != -1) {
185 path = path.substring(0, i);
186 }
187 return pathRouter.apply(path);
188 }
189
190 public static class Builder<T> {
191
192 private final UriPatternType patternType;
193 private final List<Entry<T>> handlerEntries;
194 private BiFunction<String, URIAuthority, URIAuthority> authorityResolver;
195 private HttpRequestMapper<T> downstream;
196
197 Builder(final UriPatternType patternType) {
198 this.patternType = patternType != null ? patternType : UriPatternType.URI_PATTERN;
199 this.handlerEntries = new ArrayList<>();
200 }
201
202
203
204
205
206 public Builder<T> addRoute(final URIAuthority authority, final String pathPattern, final T handler) {
207 Args.notNull(authority, "URI authority");
208 Args.notBlank(pathPattern, "URI path pattern");
209 Args.notNull(handler, "Handler");
210 this.handlerEntries.add(new Entry<>(authority, pathPattern, handler));
211 return this;
212 }
213
214
215
216
217
218 public Builder<T> addRoute(final String hostname, final String pathPattern, final T handler) {
219 Args.notBlank(hostname, "Hostname");
220 Args.notBlank(pathPattern, "URI path pattern");
221 Args.notNull(handler, "Handler");
222 this.handlerEntries.add(new Entry<>(hostname, pathPattern, handler));
223 return this;
224 }
225
226
227
228
229
230
231
232 public Builder<T> resolveAuthority(final BiFunction<String, URIAuthority, URIAuthority> authorityResolver) {
233 this.authorityResolver = authorityResolver;
234 return this;
235 }
236
237
238
239
240
241
242 public Builder<T> downstream(final HttpRequestMapper<T> downstream) {
243 this.downstream = downstream;
244 return this;
245 }
246
247 public RequestRouter<T> build() {
248 return RequestRouter.create(null, patternType, handlerEntries, authorityResolver, downstream);
249 }
250
251 }
252
253 }