Skip to content

Commit

Permalink
Route-aware request handling.
Browse files Browse the repository at this point in the history
  • Loading branch information
nmihajlovski committed Jul 26, 2016
1 parent fab5def commit 99c9918
Show file tree
Hide file tree
Showing 14 changed files with 113 additions and 53 deletions.
41 changes: 22 additions & 19 deletions rapidoid-http-fast/src/main/java/org/rapidoid/http/FastHttp.java
Expand Up @@ -51,19 +51,19 @@ public class FastHttp extends AbstractHttpProcessor {


private static final HttpParser HTTP_PARSER = new HttpParser(); private static final HttpParser HTTP_PARSER = new HttpParser();


private final HttpRoutesImpl[] routes; private final HttpRoutesImpl[] routeGroups;


private final Map<String, Object> attributes = Coll.synchronizedMap(); private final Map<String, Object> attributes = Coll.synchronizedMap();


public FastHttp(HttpRoutesImpl... routes) { public FastHttp(HttpRoutesImpl... routeGroups) {
this(routes, new Config()); this(routeGroups, new Config());
} }


public FastHttp(HttpRoutesImpl[] routes, Config serverConfig) { public FastHttp(HttpRoutesImpl[] routeGroups, Config serverConfig) {
super(null); super(null);


U.must(routes.length > 0, "Routes are missing!"); U.must(routeGroups.length > 0, "Routes are missing!");
this.routes = routes; this.routeGroups = routeGroups;
} }


@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Expand All @@ -86,22 +86,25 @@ public void onRequest(Channel channel, boolean isGet, boolean isKeepAlive, BufRa


HttpStatus status = HttpStatus.NOT_FOUND; HttpStatus status = HttpStatus.NOT_FOUND;


HttpRoutesImpl route = null; HttpRoutesImpl matchingRoutes = null;
Route matchingRoute = null;
HandlerMatch match = null; HandlerMatch match = null;


for (HttpRoutesImpl r : routes) { for (HttpRoutesImpl r : routeGroups) {
match = r.findHandler(buf, isGet, xverb, xpath); match = r.findHandler(buf, isGet, xverb, xpath);
if (match != null) { if (match != null) {
route = r; matchingRoutes = r;
matchingRoute = match.getRoute();
break; break;
} }
} }


if (match == null && isGet) { if (match == null && isGet) {
for (HttpRoutesImpl r : routes) { for (HttpRoutesImpl r : routeGroups) {
match = r.staticResourcesHandler(); match = r.staticResourcesHandler();
if (match != null) { if (match != null) {
route = r; matchingRoutes = r;
matchingRoute = match.getRoute();
break; break;
} }
} }
Expand Down Expand Up @@ -172,7 +175,7 @@ public void onRequest(Channel channel, boolean isGet, boolean isKeepAlive, BufRa
cookies = Collections.synchronizedMap(cookies); cookies = Collections.synchronizedMap(cookies);


req = new ReqImpl(this, channel, isKeepAlive, verb, uri, path, query, body, params, headers, cookies, req = new ReqImpl(this, channel, isKeepAlive, verb, uri, path, query, body, params, headers, cookies,
posted, files, pendingBodyParsing, contentType, segment, route); posted, files, pendingBodyParsing, contentType, segment, matchingRoutes, matchingRoute);


if (!attributes.isEmpty()) { if (!attributes.isEmpty()) {
req.attrs().putAll(attributes); req.attrs().putAll(attributes);
Expand Down Expand Up @@ -247,12 +250,12 @@ private String validateRequest(Buf input, BufRange verb, BufRange uri) {
} }


private HttpStatus tryGenericHandlers(Channel channel, boolean isKeepAlive, ReqImpl req) { private HttpStatus tryGenericHandlers(Channel channel, boolean isKeepAlive, ReqImpl req) {
for (HttpRoutesImpl route : routes) { for (HttpRoutesImpl routes : routeGroups) {


// trying with different routes // trying with different routes
req.routes(route); req.routes(routes);


for (HttpHandler handler : route.genericHandlers()) { for (HttpHandler handler : routes.genericHandlers()) {
HttpStatus status = handler.handle(channel, isKeepAlive, req, null); HttpStatus status = handler.handle(channel, isKeepAlive, req, null);


if (status != HttpStatus.NOT_FOUND) { if (status != HttpStatus.NOT_FOUND) {
Expand All @@ -267,7 +270,7 @@ private HttpStatus tryGenericHandlers(Channel channel, boolean isKeepAlive, ReqI
} }


public synchronized void resetConfig() { public synchronized void resetConfig() {
for (HttpRoutesImpl route : routes) { for (HttpRoutesImpl route : routeGroups) {
route.reset(); route.reset();
route.custom().reset(); route.custom().reset();
} }
Expand All @@ -277,7 +280,7 @@ public void notFound(Channel ctx, boolean isKeepAlive, HttpHandler fromHandler,
HttpStatus status = HttpStatus.NOT_FOUND; HttpStatus status = HttpStatus.NOT_FOUND;


tryRoutes: tryRoutes:
for (HttpRoutesImpl route : routes) { for (HttpRoutesImpl route : routeGroups) {
List<HttpHandler> genericHandlers = route.genericHandlers(); List<HttpHandler> genericHandlers = route.genericHandlers();
int count = genericHandlers.size(); int count = genericHandlers.size();


Expand Down Expand Up @@ -308,11 +311,11 @@ public Map<String, Object> attributes() {
} }


public HttpRoutesImpl[] routes() { public HttpRoutesImpl[] routes() {
return routes; return routeGroups;
} }


public boolean hasRouteOrResource(HttpVerb verb, String uri) { public boolean hasRouteOrResource(HttpVerb verb, String uri) {
for (HttpRoutesImpl route : routes) { for (HttpRoutesImpl route : routeGroups) {
if (route.hasRouteOrResource(verb, uri)) return true; if (route.hasRouteOrResource(verb, uri)) return true;
} }
return false; return false;
Expand Down
Expand Up @@ -120,13 +120,17 @@ public static boolean isPostReq(Req req) {
return req.verb().equalsIgnoreCase(POST); return req.verb().equalsIgnoreCase(POST);
} }


public static String resName(String path) { public static String resName(Req req) {
U.must(!path.contains("/."), "Private resources (starting with '.') cannot be accessed!"); return resName(req.route() != null ? req.route().path() : req.path());
U.must(!path.contains(".."), "Invalid resource path (contains '..')!"); }
U.must(!path.contains("*") && !path.contains("?"), "Wildcard characters ('*', '?') are not allowed in the resource path!");


public static String resName(String path) {
String res = Str.replace(path, PathPattern.PATH_PARAM_REGEX, PATH_PARAM_EXTRACTOR); String res = Str.replace(path, PathPattern.PATH_PARAM_REGEX, PATH_PARAM_EXTRACTOR);


U.must(!res.contains("/."), "Private resources (starting with '.') cannot be accessed!");
U.must(!res.contains(".."), "Invalid resource path (contains '..')!");
U.must(!res.contains("*") && !path.contains("?"), "Wildcard characters ('*', '?') are not allowed in the resource path!");

res = Str.triml(res, "/"); res = Str.triml(res, "/");


if (res.isEmpty()) { if (res.isEmpty()) {
Expand All @@ -140,10 +144,6 @@ public static String resName(String path) {
return res; return res;
} }


public static String defaultView(String path) {
return resName(path);
}

public static boolean hasExtension(String name) { public static boolean hasExtension(String name) {
int pos = name.lastIndexOf('.'); int pos = name.lastIndexOf('.');
return pos > 0 && pos < name.length() - 1; return pos > 0 && pos < name.length() - 1;
Expand All @@ -164,7 +164,7 @@ public static void setContentType(Resp resp, MediaType mediaType) {
} }


public static Res staticPage(Req req, String... possibleLocations) { public static Res staticPage(Req req, String... possibleLocations) {
String resName = resName(req.path()); String resName = resName(req);


if (hasExtension(resName)) { if (hasExtension(resName)) {
return Res.from(resName, possibleLocations); return Res.from(resName, possibleLocations);
Expand Down
6 changes: 6 additions & 0 deletions rapidoid-http-fast/src/main/java/org/rapidoid/http/Req.java
Expand Up @@ -335,6 +335,12 @@ public interface Req {
*/ */
HttpRoutes routes(); HttpRoutes routes();


/**
* Provides access to the <b>matching HTTP route (if any)</b> of the web application setup.<br>
* In case a generic handler handles the request, or no matching route was found, <code>null</code> is returned.
*/
Route route();

/** /**
* Provides access to the <b>customization</b> of the web application setup. * Provides access to the <b>customization</b> of the web application setup.
*/ */
Expand Down
Expand Up @@ -5,6 +5,7 @@
import org.rapidoid.annotation.Since; import org.rapidoid.annotation.Since;
import org.rapidoid.commons.MediaType; import org.rapidoid.commons.MediaType;
import org.rapidoid.http.HttpWrapper; import org.rapidoid.http.HttpWrapper;
import org.rapidoid.http.Route;
import org.rapidoid.http.impl.RouteOptions; import org.rapidoid.http.impl.RouteOptions;
import org.rapidoid.u.U; import org.rapidoid.u.U;


Expand Down Expand Up @@ -40,6 +41,8 @@ public abstract class AbstractHttpHandler extends RapidoidThing implements HttpH


protected final HttpWrapper[] wrappers; protected final HttpWrapper[] wrappers;


protected volatile Route route;

public AbstractHttpHandler(RouteOptions options) { public AbstractHttpHandler(RouteOptions options) {
this.options = options; this.options = options;
this.contentType = options.contentType(); this.contentType = options.contentType();
Expand Down Expand Up @@ -91,4 +94,15 @@ protected String contentTypeInfo(String inside) {


return U.frmt("%s(%s)", type, inside); return U.frmt("%s(%s)", type, inside);
} }

@Override
public void setRoute(Route route) {
this.route = route;
}

@Override
public Route getRoute() {
return route;
}

} }
Expand Up @@ -25,6 +25,7 @@
import org.rapidoid.commons.MediaType; import org.rapidoid.commons.MediaType;
import org.rapidoid.http.HttpStatus; import org.rapidoid.http.HttpStatus;
import org.rapidoid.http.Req; import org.rapidoid.http.Req;
import org.rapidoid.http.Route;
import org.rapidoid.http.impl.RouteOptions; import org.rapidoid.http.impl.RouteOptions;
import org.rapidoid.http.impl.HandlerMatch; import org.rapidoid.http.impl.HandlerMatch;
import org.rapidoid.net.abstracts.Channel; import org.rapidoid.net.abstracts.Channel;
Expand All @@ -41,4 +42,6 @@ public interface HttpHandler extends HandlerMatch {


RouteOptions options(); RouteOptions options();


void setRoute(Route route);

} }
Expand Up @@ -2,6 +2,7 @@


import org.rapidoid.annotation.Authors; import org.rapidoid.annotation.Authors;
import org.rapidoid.annotation.Since; import org.rapidoid.annotation.Since;
import org.rapidoid.http.Route;
import org.rapidoid.http.handler.HttpHandler; import org.rapidoid.http.handler.HttpHandler;


import java.util.Map; import java.util.Map;
Expand Down Expand Up @@ -34,4 +35,6 @@ public interface HandlerMatch {


Map<String, String> getParams(); Map<String, String> getParams();


Route getRoute();

} }
Expand Up @@ -3,6 +3,7 @@
import org.rapidoid.RapidoidThing; import org.rapidoid.RapidoidThing;
import org.rapidoid.annotation.Authors; import org.rapidoid.annotation.Authors;
import org.rapidoid.annotation.Since; import org.rapidoid.annotation.Since;
import org.rapidoid.http.Route;
import org.rapidoid.http.handler.HttpHandler; import org.rapidoid.http.handler.HttpHandler;


import java.util.Map; import java.util.Map;
Expand Down Expand Up @@ -35,9 +36,12 @@ public class HandlerMatchWithParams extends RapidoidThing implements HandlerMatc


public final Map<String, String> params; public final Map<String, String> params;


public HandlerMatchWithParams(HttpHandler handler, Map<String, String> params) { private final Route route;

public HandlerMatchWithParams(HttpHandler handler, Map<String, String> params, Route route) {
this.handler = handler; this.handler = handler;
this.params = params; this.params = params;
this.route = route;
} }


@Override @Override
Expand All @@ -50,4 +54,9 @@ public Map<String, String> getParams() {
return params; return params;
} }


@Override
public Route getRoute() {
return route;
}

} }
Expand Up @@ -102,7 +102,9 @@ private void register(HttpVerb verb, String path, HttpHandler handler) {
boolean isPattern = isPattern(path); boolean isPattern = isPattern(path);
PathPattern pathPattern = isPattern ? PathPattern.from(path) : null; PathPattern pathPattern = isPattern ? PathPattern.from(path) : null;


routes.add(new RouteImpl(verb, path, handler, handler.options())); RouteImpl route = new RouteImpl(verb, path, handler, handler.options());
handler.setRoute(route);
routes.add(route);


switch (verb) { switch (verb) {
case GET: case GET:
Expand Down Expand Up @@ -297,15 +299,18 @@ public HandlerMatch findHandler(Buf buf, boolean isGet, BufRange verb, BufRange
if (isGet) { if (isGet) {
if (path1 != null && BytesUtil.matches(bytes, path, path1, true)) { if (path1 != null && BytesUtil.matches(bytes, path, path1, true)) {
return handler1; return handler1;

} else if (path2 != null && BytesUtil.matches(bytes, path, path2, true)) { } else if (path2 != null && BytesUtil.matches(bytes, path, path2, true)) {
return handler2; return handler2;

} else if (path3 != null && BytesUtil.matches(bytes, path, path3, true)) { } else if (path3 != null && BytesUtil.matches(bytes, path, path3, true)) {
return handler3; return handler3;

} else { } else {
HandlerMatch handler = getHandlers.get(buf, path); HandlerMatch handler = getHandlers.get(buf, path);


if (handler == null && !paternGetHandlers.isEmpty()) { if (handler == null && !paternGetHandlers.isEmpty()) {
handler = matchByPattern(paternGetHandlers, buf.get(path)); handler = matchByPattern(HttpVerb.GET, paternGetHandlers, buf.get(path));
} }


return handler; return handler;
Expand All @@ -315,7 +320,7 @@ public HandlerMatch findHandler(Buf buf, boolean isGet, BufRange verb, BufRange
HandlerMatch handler = postHandlers.get(buf, path); HandlerMatch handler = postHandlers.get(buf, path);


if (handler == null && !paternPostHandlers.isEmpty()) { if (handler == null && !paternPostHandlers.isEmpty()) {
handler = matchByPattern(paternPostHandlers, buf.get(path)); handler = matchByPattern(HttpVerb.POST, paternPostHandlers, buf.get(path));
} }


return handler; return handler;
Expand All @@ -324,7 +329,7 @@ public HandlerMatch findHandler(Buf buf, boolean isGet, BufRange verb, BufRange
HandlerMatch handler = putHandlers.get(buf, path); HandlerMatch handler = putHandlers.get(buf, path);


if (handler == null && !paternPutHandlers.isEmpty()) { if (handler == null && !paternPutHandlers.isEmpty()) {
handler = matchByPattern(paternPutHandlers, buf.get(path)); handler = matchByPattern(HttpVerb.PUT, paternPutHandlers, buf.get(path));
} }


return handler; return handler;
Expand All @@ -333,7 +338,7 @@ public HandlerMatch findHandler(Buf buf, boolean isGet, BufRange verb, BufRange
HandlerMatch handler = deleteHandlers.get(buf, path); HandlerMatch handler = deleteHandlers.get(buf, path);


if (handler == null && !paternDeleteHandlers.isEmpty()) { if (handler == null && !paternDeleteHandlers.isEmpty()) {
handler = matchByPattern(paternDeleteHandlers, buf.get(path)); handler = matchByPattern(HttpVerb.DELETE, paternDeleteHandlers, buf.get(path));
} }


return handler; return handler;
Expand All @@ -342,7 +347,7 @@ public HandlerMatch findHandler(Buf buf, boolean isGet, BufRange verb, BufRange
HandlerMatch handler = patchHandlers.get(buf, path); HandlerMatch handler = patchHandlers.get(buf, path);


if (handler == null && !paternPatchHandlers.isEmpty()) { if (handler == null && !paternPatchHandlers.isEmpty()) {
handler = matchByPattern(paternPatchHandlers, buf.get(path)); handler = matchByPattern(HttpVerb.PATCH, paternPatchHandlers, buf.get(path));
} }


return handler; return handler;
Expand All @@ -351,7 +356,7 @@ public HandlerMatch findHandler(Buf buf, boolean isGet, BufRange verb, BufRange
HandlerMatch handler = optionsHandlers.get(buf, path); HandlerMatch handler = optionsHandlers.get(buf, path);


if (handler == null && !paternOptionsHandlers.isEmpty()) { if (handler == null && !paternOptionsHandlers.isEmpty()) {
handler = matchByPattern(paternOptionsHandlers, buf.get(path)); handler = matchByPattern(HttpVerb.OPTIONS, paternOptionsHandlers, buf.get(path));
} }


return handler; return handler;
Expand All @@ -360,7 +365,7 @@ public HandlerMatch findHandler(Buf buf, boolean isGet, BufRange verb, BufRange
HandlerMatch handler = headHandlers.get(buf, path); HandlerMatch handler = headHandlers.get(buf, path);


if (handler == null && !paternHeadHandlers.isEmpty()) { if (handler == null && !paternHeadHandlers.isEmpty()) {
handler = matchByPattern(paternHeadHandlers, buf.get(path)); handler = matchByPattern(HttpVerb.HEAD, paternHeadHandlers, buf.get(path));
} }


return handler; return handler;
Expand All @@ -369,7 +374,7 @@ public HandlerMatch findHandler(Buf buf, boolean isGet, BufRange verb, BufRange
HandlerMatch handler = traceHandlers.get(buf, path); HandlerMatch handler = traceHandlers.get(buf, path);


if (handler == null && !paternTraceHandlers.isEmpty()) { if (handler == null && !paternTraceHandlers.isEmpty()) {
handler = matchByPattern(paternTraceHandlers, buf.get(path)); handler = matchByPattern(HttpVerb.TRACE, paternTraceHandlers, buf.get(path));
} }


return handler; return handler;
Expand All @@ -378,14 +383,15 @@ public HandlerMatch findHandler(Buf buf, boolean isGet, BufRange verb, BufRange
return null; // no handler return null; // no handler
} }


private HandlerMatch matchByPattern(Map<PathPattern, HttpHandler> handlers, String path) { private HandlerMatch matchByPattern(HttpVerb verb, Map<PathPattern, HttpHandler> handlers, String path) {
for (Map.Entry<PathPattern, HttpHandler> e : handlers.entrySet()) { for (Map.Entry<PathPattern, HttpHandler> e : handlers.entrySet()) {


PathPattern pattern = e.getKey(); PathPattern pattern = e.getKey();
Map<String, String> params = pattern.match(path); Map<String, String> params = pattern.match(path);


if (params != null) { if (params != null) {
return new HandlerMatchWithParams(e.getValue(), params); RouteImpl route = new RouteImpl(verb, path, null, null);
return new HandlerMatchWithParams(e.getValue(), params, route);
} }
} }


Expand Down

0 comments on commit 99c9918

Please sign in to comment.