Skip to content

Commit 03800c9

Browse files
committed
Implemented support for case-insensitive routing
1 parent 08fc77f commit 03800c9

4 files changed

Lines changed: 102 additions & 18 deletions

File tree

src/main/java/org/javawebstack/http/router/Exchange.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -294,8 +294,8 @@ protected static AbstractElement getPathElement(AbstractElement source, String p
294294
private HTTPMethod getRequestMethodFromSocket(IHTTPSocket socket) {
295295
if ("websocket".equalsIgnoreCase(socket.getRequestHeader("upgrade")))
296296
return HTTPMethod.WEBSOCKET;
297-
if (router.isFormMethods() && (socket.getRequestMethod() == HTTPMethod.GET || socket.getRequestMethod() == HTTPMethod.POST) && getMimeType() == MimeType.X_WWW_FORM_URLENCODED) {
298-
AbstractElement e = getBodyPathElement("_method");
297+
if (router.getRoutingOptions().hasFormMethodParameter() && (socket.getRequestMethod() == HTTPMethod.GET || socket.getRequestMethod() == HTTPMethod.POST) && getMimeType() == MimeType.X_WWW_FORM_URLENCODED) {
298+
AbstractElement e = getBodyPathElement(router.getRoutingOptions().getFormMethodParameter());
299299
if (e != null) {
300300
try {
301301
return HTTPMethod.valueOf(e.string());

src/main/java/org/javawebstack/http/router/HTTPRouter.java

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ public class HTTPRouter implements RouteParamTransformerProvider {
4444
private final Map<String, AfterRequestHandler> afterMiddleware = new HashMap<>();
4545
private Function<Class<?>, Object> controllerInitiator = this::defaultControllerInitiator;
4646
private boolean formMethods = true;
47+
private HTTPRoutingOptions routingOptions = new HTTPRoutingOptions();
4748
private PartContentCache multipartContentCache;
4849

4950
public HTTPRouter(IHTTPSocketServer server) {
@@ -154,17 +155,17 @@ public HTTPRouter afterDelete(String pattern, AfterRequestHandler... handlers) {
154155
}
155156

156157
public HTTPRouter route(HTTPMethod method, String pattern, RequestHandler... handlers) {
157-
routes.add(new Route(this, method, pattern, Arrays.asList(handlers)));
158+
routes.add(new Route(this, method, pattern, routingOptions, Arrays.asList(handlers)));
158159
return this;
159160
}
160161

161162
public HTTPRouter beforeRoute(HTTPMethod method, String pattern, RequestHandler... handlers) {
162-
beforeRoutes.add(new Route(this, method, pattern, Arrays.asList(handlers)));
163+
beforeRoutes.add(new Route(this, method, pattern, routingOptions, Arrays.asList(handlers)));
163164
return this;
164165
}
165166

166167
public HTTPRouter afterRoute(HTTPMethod method, String pattern, AfterRequestHandler... handlers) {
167-
afterRoutes.add(new Route(this, method, pattern, null).setAfterHandlers(Arrays.asList(handlers)));
168+
afterRoutes.add(new Route(this, method, pattern, routingOptions, null).setAfterHandlers(Arrays.asList(handlers)));
168169
return this;
169170
}
170171

@@ -426,12 +427,26 @@ public HTTPRouter enableMultipart(PartContentCache cache) {
426427
return this;
427428
}
428429

429-
public boolean isFormMethods() {
430-
return formMethods;
430+
public HTTPRouter caseInsensitiveRouting() {
431+
return caseInsensitiveRouting(true);
431432
}
432433

434+
public HTTPRouter caseInsensitiveRouting(boolean caseInsensitiveRouting) {
435+
routingOptions.caseInsensitive(caseInsensitiveRouting);
436+
return this;
437+
}
438+
439+
public HTTPRoutingOptions getRoutingOptions() {
440+
return routingOptions;
441+
}
442+
443+
@Deprecated
433444
public HTTPRouter disableFormMethods() {
434-
formMethods = false;
445+
return formMethodParameter(null);
446+
}
447+
448+
public HTTPRouter formMethodParameter(String parameter) {
449+
routingOptions.formMethodParameter(parameter);
435450
return this;
436451
}
437452

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package org.javawebstack.http.router;
2+
3+
public class HTTPRoutingOptions {
4+
5+
private boolean caseInsensitive;
6+
private boolean ignoreTrailingSlash = true;
7+
private String formMethodParameter = "_method";
8+
9+
public HTTPRoutingOptions formMethodParameter(String formMethodParameter) {
10+
this.formMethodParameter = formMethodParameter;
11+
return this;
12+
}
13+
14+
public HTTPRoutingOptions caseInsensitive(boolean caseInsensitive) {
15+
this.caseInsensitive = caseInsensitive;
16+
return this;
17+
}
18+
19+
public HTTPRoutingOptions ignoreTrailingSlash(boolean ignoreTrailingSlash) {
20+
this.ignoreTrailingSlash = ignoreTrailingSlash;
21+
return this;
22+
}
23+
24+
public boolean isCaseInsensitive() {
25+
return caseInsensitive;
26+
}
27+
28+
public boolean isIgnoreTrailingSlash() {
29+
return ignoreTrailingSlash;
30+
}
31+
32+
public String getFormMethodParameter() {
33+
return formMethodParameter;
34+
}
35+
36+
public boolean hasFormMethodParameter() {
37+
return formMethodParameter != null;
38+
}
39+
40+
}

src/main/java/org/javawebstack/http/router/router/Route.java

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import org.javawebstack.http.router.Exchange;
44
import org.javawebstack.http.router.HTTPMethod;
5+
import org.javawebstack.http.router.HTTPRoutingOptions;
56
import org.javawebstack.http.router.handler.AfterRequestHandler;
67
import org.javawebstack.http.router.handler.RequestHandler;
78
import org.javawebstack.http.router.transformer.route.RouteParamTransformerProvider;
@@ -21,37 +22,40 @@ public class Route {
2122
private List<RequestHandler> handlers;
2223
private List<AfterRequestHandler> afterHandlers;
2324

24-
public Route(RouteParamTransformerProvider routeParamTransformerProvider, HTTPMethod method, String pattern, List<RequestHandler> handlers) {
25-
this(routeParamTransformerProvider, method, pattern, ":", handlers);
25+
public Route(RouteParamTransformerProvider routeParamTransformerProvider, HTTPMethod method, String pattern, HTTPRoutingOptions options, List<RequestHandler> handlers) {
26+
this(routeParamTransformerProvider, method, pattern, options, ":", handlers);
2627
}
2728

28-
public Route(RouteParamTransformerProvider routeParamTransformerProvider, HTTPMethod method, String pattern, String variableDelimiter, List<RequestHandler> handlers) {
29+
public Route(RouteParamTransformerProvider routeParamTransformerProvider, HTTPMethod method, String pattern, HTTPRoutingOptions options, String variableDelimiter, List<RequestHandler> handlers) {
2930
this.handlers = handlers;
3031
this.method = method;
3132
this.routeParamTransformerProvider = routeParamTransformerProvider;
3233
pattern = pattern.toLowerCase(Locale.ENGLISH);
33-
if (pattern.endsWith("/"))
34-
pattern = pattern.substring(0, pattern.length() - 1);
34+
if(options.isIgnoreTrailingSlash()) {
35+
if (pattern.endsWith("/"))
36+
pattern = pattern.substring(0, pattern.length() - 1);
37+
}
3538
if (!pattern.startsWith("/"))
3639
pattern = "/" + pattern;
3740
int pos = 0;
3841
StringBuilder sb = new StringBuilder();
3942
StringBuilder text = new StringBuilder();
4043
boolean inVar = false;
4144
while (pos < pattern.length()) {
42-
if (pattern.charAt(pos) == '{') {
45+
char c = pattern.charAt(pos);
46+
if (c == '{') {
4347
if (inVar) {
4448
throw new RuntimeException("Unexpected character '{' in route at position " + pos);
4549
}
4650
if (text.length() > 0) {
47-
sb.append("(" + regexEscape(text.toString()) + ")");
51+
sb.append("(" + prepareRegex(options, text.toString()) + ")");
4852
text = new StringBuilder();
4953
}
5054
inVar = true;
5155
pos++;
5256
continue;
5357
}
54-
if (pattern.charAt(pos) == '}') {
58+
if (c == '}') {
5559
if (!inVar) {
5660
throw new RuntimeException("Unexpected character '}' in route at position " + pos);
5761
}
@@ -74,14 +78,17 @@ public Route(RouteParamTransformerProvider routeParamTransformerProvider, HTTPMe
7478
pos++;
7579
continue;
7680
}
77-
text.append(pattern.charAt(pos));
81+
text.append(c);
7882
pos++;
7983
}
8084
if (inVar) {
8185
throw new RuntimeException("Unexpected end in route");
8286
}
8387
if (text.length() > 0) {
84-
sb.append("(" + regexEscape(text.toString()) + ")");
88+
sb.append("(" + prepareRegex(options, text.toString()) + ")");
89+
}
90+
if(options.isIgnoreTrailingSlash()) {
91+
sb.append("/?");
8592
}
8693
this.pattern = Pattern.compile(sb.toString());
8794
}
@@ -117,6 +124,28 @@ public List<AfterRequestHandler> getAfterHandlers() {
117124
return afterHandlers;
118125
}
119126

127+
private static String prepareRegex(HTTPRoutingOptions options, String text) {
128+
text = regexEscape(text);
129+
if(options.isCaseInsensitive())
130+
text = ignoreCase(text);
131+
return text;
132+
}
133+
134+
private static String ignoreCase(String s) {
135+
StringBuilder sb = new StringBuilder();
136+
for(char c : s.toCharArray()) {
137+
if(Character.isAlphabetic(c)) {
138+
char inverted = Character.isUpperCase(c) ? Character.toLowerCase(c) : Character.toUpperCase(c);
139+
if(c != inverted) {
140+
sb.append("[").append(c).append(inverted).append("]");
141+
continue;
142+
}
143+
}
144+
sb.append(c);
145+
}
146+
return sb.toString();
147+
}
148+
120149
private static String regexEscape(String s) {
121150
for (char c : "\\<([{^-=$!|]})?*+.>".toCharArray()) {
122151
s = s.replace(String.valueOf(c), "\\" + c);

0 commit comments

Comments
 (0)