Skip to content

Commit

Permalink
Supporting multiple HTTP route groups per HTTP server setup.
Browse files Browse the repository at this point in the history
  • Loading branch information
nmihajlovski committed Jun 12, 2016
1 parent 8e7c9c5 commit d2105c5
Show file tree
Hide file tree
Showing 31 changed files with 308 additions and 235 deletions.
107 changes: 68 additions & 39 deletions rapidoid-http-fast/src/main/java/org/rapidoid/http/FastHttp.java
Expand Up @@ -51,28 +51,18 @@ 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[] routes;
private final Customization customization; private final Customization customization;


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


public FastHttp(HttpRoutesImpl routes) { public FastHttp(HttpRoutesImpl... routes) {
super(null); super(null);
this.routes = routes; U.must(routes.length > 0, "Routes are missing!");
this.customization = routes.custom();
}

public FastHttp(Customization customization) {
this(new HttpRoutesImpl(customization));
}


public synchronized void on(String verb, String path, HttpHandler handler) { this.routes = routes;
routes.on(verb, path, handler); this.customization = routes[0].custom();
}

public synchronized void on(String verb, String path, ReqHandler handler) {
routes.on(verb, path, handler);
} }


@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
Expand All @@ -94,7 +84,27 @@ public void onRequest(Channel channel, boolean isGet, boolean isKeepAlive, BufRa
} }


HttpStatus status = HttpStatus.NOT_FOUND; HttpStatus status = HttpStatus.NOT_FOUND;
HandlerMatch match = routes.findHandler(buf, isGet, xverb, xpath);
HttpRoutesImpl route = null;
HandlerMatch match = null;

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

if (match == null && isGet) {
for (HttpRoutesImpl r : routes) {
match = r.staticResourcesHandler();
if (match != null) {
route = r;
break;
}
}
}


HttpHandler handler = match != null ? match.getHandler() : null; HttpHandler handler = match != null ? match.getHandler() : null;
boolean noReq = (handler != null && !handler.needsParams()); boolean noReq = (handler != null && !handler.needsParams());
Expand Down Expand Up @@ -160,7 +170,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, contentType, segment, routes); posted, files, contentType, segment, route);


if (!attributes.isEmpty()) { if (!attributes.isEmpty()) {
req.attrs().putAll(attributes); req.attrs().putAll(attributes);
Expand Down Expand Up @@ -201,7 +211,7 @@ private boolean handleError(Channel channel, boolean isKeepAlive, ReqImpl req, M
} else { } else {
Log.error("Low-level HTTP handler error!", e); Log.error("Low-level HTTP handler error!", e);
HttpIO.startResponse(channel, 500, isKeepAlive, contentType); HttpIO.startResponse(channel, 500, isKeepAlive, contentType);
byte[] bytes = HttpUtils.responseToBytes("Internal Server Error!", contentType, custom().jsonResponseRenderer()); byte[] bytes = HttpUtils.responseToBytes("Internal Server Error!", contentType, routes()[0].custom().jsonResponseRenderer());
HttpIO.writeContentLengthAndBody(channel, bytes); HttpIO.writeContentLengthAndBody(channel, bytes);
HttpIO.done(channel, isKeepAlive); HttpIO.done(channel, isKeepAlive);
} }
Expand All @@ -221,37 +231,53 @@ private static String validateRequest(Buf input, BufRange verb, BufRange uri) {
return null; // OK, no error return null; // OK, no error
} }


private HttpStatus tryGenericHandlers(Channel channel, boolean isKeepAlive, Req req) { private HttpStatus tryGenericHandlers(Channel channel, boolean isKeepAlive, ReqImpl req) {
for (HttpHandler handler : routes.genericHandlers()) { for (HttpRoutesImpl route : routes) {

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


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


if (status != HttpStatus.NOT_FOUND) { if (status != HttpStatus.NOT_FOUND) {
return status; return status;
}
} }
} }


req.routes(null);

return HttpStatus.NOT_FOUND; return HttpStatus.NOT_FOUND;
} }


public synchronized void resetConfig() { public synchronized void resetConfig() {
routes.reset(); for (HttpRoutesImpl route : routes) {
customization.reset(); route.reset();
route.custom().reset();
}
} }


public void notFound(Channel ctx, boolean isKeepAlive, HttpHandler fromHandler, Req req) { public void notFound(Channel ctx, boolean isKeepAlive, HttpHandler fromHandler, Req req) {
List<HttpHandler> genericHandlers = routes.genericHandlers();
int count = genericHandlers.size();

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


for (int i = 0; i < count; i++) { tryRoutes:
HttpHandler handler = genericHandlers.get(i); for (HttpRoutesImpl route : routes) {
if (handler == fromHandler) { List<HttpHandler> genericHandlers = route.genericHandlers();
if (i < count - 1) { int count = genericHandlers.size();
HttpHandler nextHandler = genericHandlers.get(i + 1);
status = nextHandler.handle(ctx, isKeepAlive, req, null); for (int i = 0; i < count; i++) {
break; HttpHandler handler = genericHandlers.get(i);
if (handler == fromHandler) {
// a generic handler returned "not found" -> go to the next one
if (i < count - 1) {
// trying with different routes
((ReqImpl) req).routes(route);

HttpHandler nextHandler = genericHandlers.get(i + 1);
status = nextHandler.handle(ctx, isKeepAlive, req, null);
break tryRoutes;
}
} }
} }
} }
Expand All @@ -270,12 +296,15 @@ public Map<String, Serializable> session(String sessionId) {
return sessions.get(sessionId); return sessions.get(sessionId);
} }


public Customization custom() { public HttpRoutesImpl[] routes() {
return customization; return routes;
} }


public HttpRoutesImpl getRoutes() { public boolean hasRouteOrResource(HttpVerb verb, String uri) {
return routes; for (HttpRoutesImpl route : routes) {
if (route.hasRouteOrResource(verb, uri)) return true;
}
return false;
} }


} }
Expand Up @@ -52,4 +52,7 @@ public interface HttpRoutes {
Customization custom(); Customization custom();


Route find(HttpVerb verb, String path); Route find(HttpVerb verb, String path);

boolean hasRouteOrResource(HttpVerb verb, String uri);

} }
Expand Up @@ -45,9 +45,12 @@ public abstract class AbstractAsyncHttpHandler extends AbstractHttpHandler {


private final FastHttp http; private final FastHttp http;


public AbstractAsyncHttpHandler(FastHttp http, RouteOptions options) { private final HttpRoutes routes;

public AbstractAsyncHttpHandler(FastHttp http, HttpRoutes routes, RouteOptions options) {
super(options); super(options);
this.http = http; this.http = http;
this.routes = routes;
} }


@Override @Override
Expand All @@ -65,14 +68,14 @@ public HttpStatus handle(Channel ctx, boolean isKeepAlive, Req req, Object extra
req.response().logout(); req.response().logout();
} }


Set<String> roles = userRoles(username); Set<String> roles = userRoles(req, username);


TransactionMode txMode; TransactionMode txMode;
try { try {
txMode = before(req, username, roles); txMode = before(req, username, roles);


} catch (Throwable e) { } catch (Throwable e) {
HttpIO.errorAndDone(req, e, http.custom().errorHandler()); HttpIO.errorAndDone(req, e, req.routes().custom().errorHandler());
return HttpStatus.DONE; return HttpStatus.DONE;
} }


Expand All @@ -84,7 +87,7 @@ public HttpStatus handle(Channel ctx, boolean isKeepAlive, Req req, Object extra


} catch (Throwable e) { } catch (Throwable e) {
// if there was an error in the job scheduling: // if there was an error in the job scheduling:
HttpIO.errorAndDone(req, e, http.custom().errorHandler()); HttpIO.errorAndDone(req, e, req.custom().errorHandler());
return HttpStatus.DONE; return HttpStatus.DONE;
} }


Expand All @@ -110,10 +113,10 @@ private String getUser(Req req) {
} }
} }


private Set<String> userRoles(String username) { private Set<String> userRoles(Req req, String username) {
if (username != null) { if (username != null) {
try { try {
return http.custom().rolesProvider().getRolesForUser(username); return req.routes().custom().rolesProvider().getRolesForUser(username);
} catch (Exception e) { } catch (Exception e) {
throw U.rte(e); throw U.rte(e);
} }
Expand Down Expand Up @@ -197,7 +200,7 @@ public void run() {
try { try {
JPA.transaction(handleRequest, txMode == TransactionMode.READ_ONLY); JPA.transaction(handleRequest, txMode == TransactionMode.READ_ONLY);
} catch (Exception e) { } catch (Exception e) {
HttpIO.errorAndDone(req, e, http.custom().errorHandler()); HttpIO.errorAndDone(req, e, req.routes().custom().errorHandler());
} }
} }
}; };
Expand Down Expand Up @@ -256,7 +259,7 @@ public void complete(Channel ctx, boolean isKeepAlive, Req req, Object result) {
} }


if (result instanceof Throwable) { if (result instanceof Throwable) {
HttpIO.errorAndDone(req, (Throwable) result, http.custom().errorHandler()); HttpIO.errorAndDone(req, (Throwable) result, req.routes().custom().errorHandler());
return; return;


} else { } else {
Expand Down
Expand Up @@ -23,6 +23,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.FastHttp; import org.rapidoid.http.FastHttp;
import org.rapidoid.http.HttpRoutes;
import org.rapidoid.http.Req; import org.rapidoid.http.Req;
import org.rapidoid.http.ReqHandler; import org.rapidoid.http.ReqHandler;
import org.rapidoid.http.impl.RouteOptions; import org.rapidoid.http.impl.RouteOptions;
Expand All @@ -34,8 +35,8 @@ public class ParamsAwareReqHandler extends AbstractAsyncHttpHandler {


private final ReqHandler handler; private final ReqHandler handler;


public ParamsAwareReqHandler(FastHttp http, RouteOptions options, ReqHandler handler) { public ParamsAwareReqHandler(FastHttp http, HttpRoutes routes, RouteOptions options, ReqHandler handler) {
super(http, options); super(http, routes, options);
this.handler = handler; this.handler = handler;
} }


Expand Down
Expand Up @@ -23,6 +23,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.FastHttp; import org.rapidoid.http.FastHttp;
import org.rapidoid.http.HttpRoutes;
import org.rapidoid.http.Req; import org.rapidoid.http.Req;
import org.rapidoid.http.impl.RouteOptions; import org.rapidoid.http.impl.RouteOptions;
import org.rapidoid.net.abstracts.Channel; import org.rapidoid.net.abstracts.Channel;
Expand All @@ -33,8 +34,8 @@ public class PredefinedResponseHandler extends AbstractAsyncHttpHandler {


private final Object response; private final Object response;


public PredefinedResponseHandler(FastHttp http, RouteOptions options, Object response) { public PredefinedResponseHandler(FastHttp http, HttpRoutes routes, RouteOptions options, Object response) {
super(http, options); super(http, routes, options);
this.response = response; this.response = response;
} }


Expand Down
Expand Up @@ -21,6 +21,7 @@
import org.rapidoid.http.handler.HttpHandler; import org.rapidoid.http.handler.HttpHandler;
import org.rapidoid.http.handler.ParamsAwareReqHandler; import org.rapidoid.http.handler.ParamsAwareReqHandler;
import org.rapidoid.http.handler.StaticResourcesHandler; import org.rapidoid.http.handler.StaticResourcesHandler;
import org.rapidoid.io.Res;
import org.rapidoid.log.Log; import org.rapidoid.log.Log;
import org.rapidoid.u.U; import org.rapidoid.u.U;
import org.rapidoid.util.AnsiColor; import org.rapidoid.util.AnsiColor;
Expand Down Expand Up @@ -88,7 +89,7 @@ public class HttpRoutesImpl extends RapidoidThing implements HttpRoutes {


final List<HttpHandler> genericHandlers = Coll.synchronizedList(); final List<HttpHandler> genericHandlers = Coll.synchronizedList();


volatile HttpHandler staticResourcesHandler; private volatile HttpHandler staticResourcesHandler;


private final Set<Route> routes = Coll.synchronizedSet(); private final Set<Route> routes = Coll.synchronizedSet();


Expand Down Expand Up @@ -307,10 +308,6 @@ public HandlerMatch findHandler(Buf buf, boolean isGet, BufRange verb, BufRange
handler = matchByPattern(paternGetHandlers, buf.get(path)); handler = matchByPattern(paternGetHandlers, buf.get(path));
} }


if (handler == null) {
handler = staticResourcesHandler;
}

return handler; return handler;
} }


Expand Down Expand Up @@ -406,7 +403,7 @@ public synchronized void on(String verb, String path, ReqHandler handler) {
} }


public HttpHandler handler(ReqHandler reqHandler, RouteOptions options) { public HttpHandler handler(ReqHandler reqHandler, RouteOptions options) {
return new ParamsAwareReqHandler(null, options, reqHandler); return new ParamsAwareReqHandler(null, null, options, reqHandler);
} }


@Override @Override
Expand Down Expand Up @@ -528,7 +525,26 @@ public Route find(HttpVerb verb, String path) {
return null; return null;
} }


@Override
public boolean hasRouteOrResource(HttpVerb verb, String uri) {
if (verb == HttpVerb.GET) {
String[] staticFilesLocations = custom().staticFilesPath();
if (U.notEmpty(staticFilesLocations)) {
String filename = Str.triml(uri, '/');
if (filename.isEmpty()) filename = "index.html";
if (Res.from(filename, staticFilesLocations).exists()) return true;
}
}

return find(verb, uri) != null;
}

public List<HttpHandler> genericHandlers() { public List<HttpHandler> genericHandlers() {
return genericHandlers; return genericHandlers;
} }

public HttpHandler staticResourcesHandler() {
return staticResourcesHandler;
}

} }
Expand Up @@ -124,7 +124,7 @@ public String view() {
@Override @Override
public boolean hasRoute(HttpVerb verb, String uri) { public boolean hasRoute(HttpVerb verb, String uri) {
ReqImpl reqq = (ReqImpl) req(); ReqImpl reqq = (ReqImpl) req();
return reqq.hasRoute(verb, uri); return reqq.http().hasRouteOrResource(verb, uri);
} }


private Req req() { private Req req() {
Expand Down

0 comments on commit d2105c5

Please sign in to comment.