package io.vertx.ext.web.impl;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.http.HttpMethod;
import io.vertx.core.http.HttpServerRequest;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;
import io.vertx.ext.web.Route;
import io.vertx.ext.web.Router;
import io.vertx.ext.web.RoutingContext;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.atomic.AtomicInteger;
public class RouterImpl implements Router {
private static final Comparator<RouteImpl> routeComparator = (RouteImpl o1, RouteImpl o2) -> {
final int compare = Integer.compare(o1.order(), o2.order());
if (compare == 0) {
if (o1.equals(o2)) {
return 0;
}
return 1;
}
return compare;
};
private static final Logger log = LoggerFactory.getLogger(RouterImpl.class);
private final Vertx vertx;
private final Set<RouteImpl> routes = new ConcurrentSkipListSet<>(routeComparator);
public RouterImpl(Vertx vertx) {
this.vertx = vertx;
}
private final AtomicInteger orderSequence = new AtomicInteger();
private Map<Integer, Handler<RoutingContext>> errorHandlers = new ConcurrentHashMap<>();
@Override
public void handle(HttpServerRequest request) {
if (log.isTraceEnabled()) log.trace("Router: " + System.identityHashCode(this) +
" accepting request " + request.method() + " " + request.absoluteURI());
new RoutingContextImpl(null, this, request, routes).next();
}
@Override
public Route route() {
return new RouteImpl(this, orderSequence.getAndIncrement());
}
@Override
public Route route(HttpMethod method, String path) {
return new RouteImpl(this, orderSequence.getAndIncrement(), method, path);
}
@Override
public Route route(String path) {
return new RouteImpl(this, orderSequence.getAndIncrement(), path);
}
@Override
public Route routeWithRegex(HttpMethod method, String regex) {
return new RouteImpl(this, orderSequence.getAndIncrement(), method, regex, true);
}
@Override
public Route routeWithRegex(String regex) {
return new RouteImpl(this, orderSequence.getAndIncrement(), regex, true);
}
@Override
public Route get() {
return route().method(HttpMethod.GET);
}
@Override
public Route get(String path) {
return route(HttpMethod.GET, path);
}
@Override
public Route getWithRegex(String path) {
return route().method(HttpMethod.GET).pathRegex(path);
}
@Override
public Route head() {
return route().method(HttpMethod.HEAD);
}
@Override
public Route head(String path) {
return route(HttpMethod.HEAD, path);
}
@Override
public Route headWithRegex(String path) {
return route().method(HttpMethod.HEAD).pathRegex(path);
}
@Override
public Route options() {
return route().method(HttpMethod.OPTIONS);
}
@Override
public Route options(String path) {
return route(HttpMethod.OPTIONS, path);
}
@Override
public Route optionsWithRegex(String path) {
return route().method(HttpMethod.OPTIONS).pathRegex(path);
}
@Override
public Route put() {
return route().method(HttpMethod.PUT);
}
@Override
public Route put(String path) {
return route(HttpMethod.PUT, path);
}
@Override
public Route putWithRegex(String path) {
return route().method(HttpMethod.PUT).pathRegex(path);
}
@Override
public Route post() {
return route().method(HttpMethod.POST);
}
@Override
public Route post(String path) {
return route(HttpMethod.POST, path);
}
@Override
public Route postWithRegex(String path) {
return route().method(HttpMethod.POST).pathRegex(path);
}
@Override
public Route delete() {
return route().method(HttpMethod.DELETE);
}
@Override
public Route delete(String path) {
return route(HttpMethod.DELETE, path);
}
@Override
public Route deleteWithRegex(String path) {
return route().method(HttpMethod.DELETE).pathRegex(path);
}
@Override
public Route trace() {
return route().method(HttpMethod.TRACE);
}
@Override
public Route trace(String path) {
return route(HttpMethod.TRACE, path);
}
@Override
public Route traceWithRegex(String path) {
return route().method(HttpMethod.TRACE).pathRegex(path);
}
@Override
public Route connect() {
return route().method(HttpMethod.CONNECT);
}
@Override
public Route connect(String path) {
return route(HttpMethod.CONNECT, path);
}
@Override
public Route connectWithRegex(String path) {
return route().method(HttpMethod.CONNECT).pathRegex(path);
}
@Override
public Route patch() {
return route().method(HttpMethod.PATCH);
}
@Override
public Route patch(String path) {
return route(HttpMethod.PATCH, path);
}
@Override
public Route patchWithRegex(String path) {
return route().method(HttpMethod.PATCH).pathRegex(path);
}
@Override
public List<Route> getRoutes() {
return new ArrayList<>(routes);
}
@Override
public Router clear() {
routes.clear();
return this;
}
@Override
public void handleContext(RoutingContext ctx) {
new RoutingContextWrapper(getAndCheckRoutePath(ctx), ctx.request(), routes, ctx).next();
}
@Override
public void handleFailure(RoutingContext ctx) {
new RoutingContextWrapper(getAndCheckRoutePath(ctx), ctx.request(), routes, ctx).next();
}
@Override
public Router mountSubRouter(String mountPoint, Router subRouter) {
if (mountPoint.endsWith("*")) {
throw new IllegalArgumentException("Don't include * when mounting subrouter");
}
if (mountPoint.contains(":")) {
throw new IllegalArgumentException("Can't use patterns in subrouter mounts");
}
route(mountPoint + "*").handler(subRouter::handleContext).failureHandler(subRouter::handleFailure);
return this;
}
@Deprecated
@Override
public synchronized Router exceptionHandler(Handler<Throwable> exceptionHandler) {
if (exceptionHandler != null) {
this.errorHandler(500, routingContext -> exceptionHandler.handle(routingContext.failure()));
}
return this;
}
@Override
public Router errorHandler(int statusCode, Handler<RoutingContext> errorHandler) {
Objects.requireNonNull(errorHandler);
this.errorHandlers.put(statusCode, errorHandler);
return this;
}
void add(RouteImpl route) {
routes.add(route);
}
void remove(RouteImpl route) {
routes.remove(route);
}
Vertx vertx() {
return vertx;
}
Iterator<RouteImpl> iterator() {
return routes.iterator();
}
Handler<RoutingContext> getErrorHandlerByStatusCode(int statusCode) {
return errorHandlers.get(statusCode);
}
private String getAndCheckRoutePath(RoutingContext ctx) {
Route currentRoute = ctx.currentRoute();
String path = currentRoute.getPath();
if (path == null) {
throw new IllegalStateException("Sub routers must be mounted on constant paths (no regex or patterns)");
}
return path;
}
}