This is an automated email from the ASF dual-hosted git repository. rcordier pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/james-project.git
commit 6d658f142300a03bcfc5ed60e2af883bad3d4052 Author: Rene Cordier <rcord...@linagora.com> AuthorDate: Tue Mar 24 14:03:02 2020 +0700 JAMES-3092 Simplify the Y versioning routing in jmap --- .../james/jmap/http/AuthenticationRoutes.java | 10 +- .../org/apache/james/jmap/http/DownloadRoutes.java | 14 +-- .../org/apache/james/jmap/http/JMAPApiRoutes.java | 6 +- .../org/apache/james/jmap/http/UploadRoutes.java | 6 +- .../apache/james/jmap/http/JMAPApiRoutesTest.java | 6 +- .../main/java/org/apache/james/jmap/Endpoint.java | 24 ++-- .../main/java/org/apache/james/jmap/JMAPRoute.java | 30 ++++- .../java/org/apache/james/jmap/JMAPRoutes.java | 2 +- .../java/org/apache/james/jmap/JMAPServer.java | 72 ++--------- .../org/apache/james/jmap/UriPathTemplate.java | 139 +++++++++++++++++++++ .../src/main/java/org/apache/james/jmap/Verb.java | 44 ------- .../java/org/apache/james/jmap/JMAPServerTest.java | 9 +- 12 files changed, 215 insertions(+), 147 deletions(-) diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/AuthenticationRoutes.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/AuthenticationRoutes.java index ed06cbf..9412861 100644 --- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/AuthenticationRoutes.java +++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/AuthenticationRoutes.java @@ -46,7 +46,6 @@ import org.apache.james.jmap.Endpoint; import org.apache.james.jmap.JMAPRoute; import org.apache.james.jmap.JMAPRoutes; import org.apache.james.jmap.JMAPUrls; -import org.apache.james.jmap.Verb; import org.apache.james.jmap.Version; import org.apache.james.jmap.api.access.AccessToken; import org.apache.james.jmap.draft.api.AccessTokenManager; @@ -70,6 +69,7 @@ import org.slf4j.LoggerFactory; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import io.netty.handler.codec.http.HttpMethod; import reactor.core.publisher.Mono; import reactor.core.scheduler.Schedulers; import reactor.netty.http.server.HttpServerRequest; @@ -108,10 +108,10 @@ public class AuthenticationRoutes implements JMAPRoutes { @Override public Stream<JMAPRoute> routes() { return Stream.of( - new JMAPRoute(new Endpoint(Verb.POST, AUTHENTICATION), Version.DRAFT, JMAPRoutes.corsHeaders(this::post)), - new JMAPRoute(new Endpoint(Verb.GET, AUTHENTICATION), Version.DRAFT, JMAPRoutes.corsHeaders(this::returnEndPointsResponse)), - new JMAPRoute(new Endpoint(Verb.DELETE, AUTHENTICATION), Version.DRAFT, JMAPRoutes.corsHeaders(this::delete)), - new JMAPRoute(new Endpoint(Verb.OPTIONS, AUTHENTICATION), Version.DRAFT, CORS_CONTROL) + new JMAPRoute(new Endpoint(HttpMethod.POST, AUTHENTICATION), Version.DRAFT, JMAPRoutes.corsHeaders(this::post)), + new JMAPRoute(new Endpoint(HttpMethod.GET, AUTHENTICATION), Version.DRAFT, JMAPRoutes.corsHeaders(this::returnEndPointsResponse)), + new JMAPRoute(new Endpoint(HttpMethod.DELETE, AUTHENTICATION), Version.DRAFT, JMAPRoutes.corsHeaders(this::delete)), + new JMAPRoute(new Endpoint(HttpMethod.OPTIONS, AUTHENTICATION), Version.DRAFT, CORS_CONTROL) ); } diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/DownloadRoutes.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/DownloadRoutes.java index 0f97b15..c8b35a2 100644 --- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/DownloadRoutes.java +++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/DownloadRoutes.java @@ -40,7 +40,6 @@ import javax.inject.Inject; import org.apache.james.jmap.Endpoint; import org.apache.james.jmap.JMAPRoute; import org.apache.james.jmap.JMAPRoutes; -import org.apache.james.jmap.Verb; import org.apache.james.jmap.Version; import org.apache.james.jmap.draft.api.SimpleTokenFactory; import org.apache.james.jmap.draft.exceptions.BadRequestException; @@ -66,6 +65,7 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.CharMatcher; import io.netty.buffer.Unpooled; +import io.netty.handler.codec.http.HttpMethod; import reactor.core.publisher.Mono; import reactor.core.scheduler.Schedulers; import reactor.netty.http.server.HttpServerRequest; @@ -101,12 +101,12 @@ public class DownloadRoutes implements JMAPRoutes { @Override public Stream<JMAPRoute> routes() { return Stream.of( - new JMAPRoute(new Endpoint(Verb.POST, DOWNLOAD_FROM_ID), Version.DRAFT, JMAPRoutes.corsHeaders(this::postFromId)), - new JMAPRoute(new Endpoint(Verb.GET, DOWNLOAD_FROM_ID), Version.DRAFT, JMAPRoutes.corsHeaders(this::getFromId)), - new JMAPRoute(new Endpoint(Verb.POST, DOWNLOAD_FROM_ID_AND_NAME), Version.DRAFT, JMAPRoutes.corsHeaders(this::postFromIdAndName)), - new JMAPRoute(new Endpoint(Verb.GET, DOWNLOAD_FROM_ID_AND_NAME), Version.DRAFT, JMAPRoutes.corsHeaders(this::getFromIdAndName)), - new JMAPRoute(new Endpoint(Verb.OPTIONS, DOWNLOAD_FROM_ID), Version.DRAFT, CORS_CONTROL), - new JMAPRoute(new Endpoint(Verb.OPTIONS, DOWNLOAD_FROM_ID_AND_NAME), Version.DRAFT, CORS_CONTROL) + new JMAPRoute(new Endpoint(HttpMethod.POST, DOWNLOAD_FROM_ID), Version.DRAFT, JMAPRoutes.corsHeaders(this::postFromId)), + new JMAPRoute(new Endpoint(HttpMethod.GET, DOWNLOAD_FROM_ID), Version.DRAFT, JMAPRoutes.corsHeaders(this::getFromId)), + new JMAPRoute(new Endpoint(HttpMethod.POST, DOWNLOAD_FROM_ID_AND_NAME), Version.DRAFT, JMAPRoutes.corsHeaders(this::postFromIdAndName)), + new JMAPRoute(new Endpoint(HttpMethod.GET, DOWNLOAD_FROM_ID_AND_NAME), Version.DRAFT, JMAPRoutes.corsHeaders(this::getFromIdAndName)), + new JMAPRoute(new Endpoint(HttpMethod.OPTIONS, DOWNLOAD_FROM_ID), Version.DRAFT, CORS_CONTROL), + new JMAPRoute(new Endpoint(HttpMethod.OPTIONS, DOWNLOAD_FROM_ID_AND_NAME), Version.DRAFT, CORS_CONTROL) ); } diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/JMAPApiRoutes.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/JMAPApiRoutes.java index 82b8669..f574bff 100644 --- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/JMAPApiRoutes.java +++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/JMAPApiRoutes.java @@ -34,7 +34,6 @@ import javax.inject.Inject; import org.apache.james.jmap.Endpoint; import org.apache.james.jmap.JMAPRoute; import org.apache.james.jmap.JMAPRoutes; -import org.apache.james.jmap.Verb; import org.apache.james.jmap.Version; import org.apache.james.jmap.draft.exceptions.BadRequestException; import org.apache.james.jmap.draft.exceptions.InternalErrorException; @@ -53,6 +52,7 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import io.netty.handler.codec.http.HttpMethod; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; import reactor.core.scheduler.Schedulers; @@ -88,8 +88,8 @@ public class JMAPApiRoutes implements JMAPRoutes { @Override public Stream<JMAPRoute> routes() { return Stream.of( - new JMAPRoute(new Endpoint(Verb.POST, JMAP), Version.DRAFT, JMAPRoutes.corsHeaders(this::post)), - new JMAPRoute(new Endpoint(Verb.OPTIONS, JMAP), Version.DRAFT, CORS_CONTROL) + new JMAPRoute(new Endpoint(HttpMethod.POST, JMAP), Version.DRAFT, JMAPRoutes.corsHeaders(this::post)), + new JMAPRoute(new Endpoint(HttpMethod.OPTIONS, JMAP), Version.DRAFT, CORS_CONTROL) ); } diff --git a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/UploadRoutes.java b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/UploadRoutes.java index 253a076..92c289d 100644 --- a/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/UploadRoutes.java +++ b/server/protocols/jmap-draft/src/main/java/org/apache/james/jmap/http/UploadRoutes.java @@ -38,7 +38,6 @@ import javax.inject.Inject; import org.apache.james.jmap.Endpoint; import org.apache.james.jmap.JMAPRoute; import org.apache.james.jmap.JMAPRoutes; -import org.apache.james.jmap.Verb; import org.apache.james.jmap.Version; import org.apache.james.jmap.draft.exceptions.BadRequestException; import org.apache.james.jmap.draft.exceptions.InternalErrorException; @@ -57,6 +56,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.base.Strings; import com.google.common.io.ByteStreams; +import io.netty.handler.codec.http.HttpMethod; import reactor.core.publisher.Mono; import reactor.core.scheduler.Schedulers; import reactor.netty.http.server.HttpServerRequest; @@ -90,8 +90,8 @@ public class UploadRoutes implements JMAPRoutes { @Override public Stream<JMAPRoute> routes() { return Stream.of( - new JMAPRoute(new Endpoint(Verb.POST, UPLOAD), Version.DRAFT, JMAPRoutes.corsHeaders(this::post)), - new JMAPRoute(new Endpoint(Verb.OPTIONS, UPLOAD), Version.DRAFT, CORS_CONTROL) + new JMAPRoute(new Endpoint(HttpMethod.POST, UPLOAD), Version.DRAFT, JMAPRoutes.corsHeaders(this::post)), + new JMAPRoute(new Endpoint(HttpMethod.OPTIONS, UPLOAD), Version.DRAFT, CORS_CONTROL) ); } diff --git a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/JMAPApiRoutesTest.java b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/JMAPApiRoutesTest.java index f688099..43f8fa5 100644 --- a/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/JMAPApiRoutesTest.java +++ b/server/protocols/jmap-draft/src/test/java/org/apache/james/jmap/http/JMAPApiRoutesTest.java @@ -32,7 +32,6 @@ import java.nio.charset.StandardCharsets; import org.apache.james.core.Username; import org.apache.james.jmap.JMAPRoute; -import org.apache.james.jmap.Verb; import org.apache.james.jmap.draft.methods.ErrorResponse; import org.apache.james.jmap.draft.methods.Method; import org.apache.james.jmap.draft.methods.RequestHandler; @@ -48,6 +47,7 @@ import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.ObjectNode; +import io.netty.handler.codec.http.HttpMethod; import io.restassured.RestAssured; import io.restassured.builder.RequestSpecBuilder; import io.restassured.http.ContentType; @@ -76,13 +76,13 @@ public class JMAPApiRoutesTest { mockedAuthFilter, mockedUserProvisionner, mockedMailboxesProvisionner); JMAPRoute postApiRoute = jmapApiRoutes.routes() - .filter(jmapRoute -> jmapRoute.getEndpoint().getVerb().equals(Verb.POST)) + .filter(jmapRoute -> jmapRoute.getEndpoint().getMethod().equals(HttpMethod.POST)) .findFirst() .get(); server = HttpServer.create() .port(RANDOM_PORT) - .route(routes -> routes.post(postApiRoute.getEndpoint().getPath(), (req, res) -> postApiRoute.getAction().apply(req, res))) + .route(routes -> routes.post(postApiRoute.getEndpoint().getPath(), (req, res) -> postApiRoute.getAction().handleRequest(req, res))) .bindNow(); RestAssured.requestSpecification = new RequestSpecBuilder() diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/Endpoint.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/Endpoint.java index 3c878df..222d40b 100644 --- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/Endpoint.java +++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/Endpoint.java @@ -21,27 +21,31 @@ package org.apache.james.jmap; import java.util.Objects; -import reactor.netty.http.server.HttpServerRoutes; +import io.netty.handler.codec.http.HttpMethod; +import reactor.netty.http.server.HttpServerRequest; public class Endpoint { - private final Verb verb; + private final HttpMethod method; private final String path; + private final UriPathTemplate uriPathTemplate; - public Endpoint(Verb verb, String path) { - this.verb = verb; + public Endpoint(HttpMethod method, String path) { + this.method = method; this.path = path; + this.uriPathTemplate = new UriPathTemplate(path); } - public Verb getVerb() { - return verb; + public HttpMethod getMethod() { + return method; } public String getPath() { return path; } - HttpServerRoutes registerRoute(HttpServerRoutes builder, JMAPRoute.Action action) { - return verb.registerRoute(builder, this.path, action); + public boolean matches(HttpServerRequest request) { + return method.equals(request.method()) + && uriPathTemplate.matches(request.uri()); } @Override @@ -49,7 +53,7 @@ public class Endpoint { if (o instanceof Endpoint) { Endpoint endpoint = (Endpoint) o; - return Objects.equals(this.verb, endpoint.verb) + return Objects.equals(this.method, endpoint.method) && Objects.equals(this.path, endpoint.path); } return false; @@ -57,6 +61,6 @@ public class Endpoint { @Override public final int hashCode() { - return Objects.hash(verb, path); + return Objects.hash(method, path); } } diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPRoute.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPRoute.java index d4d8986..aaa3909 100644 --- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPRoute.java +++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPRoute.java @@ -19,7 +19,11 @@ package org.apache.james.jmap; -import java.util.function.BiFunction; +import static io.netty.handler.codec.http.HttpHeaderNames.ACCEPT; + +import java.util.Arrays; +import java.util.Optional; +import java.util.stream.Stream; import org.reactivestreams.Publisher; @@ -27,10 +31,12 @@ import reactor.netty.http.server.HttpServerRequest; import reactor.netty.http.server.HttpServerResponse; public class JMAPRoute { - public interface Action extends BiFunction<HttpServerRequest, HttpServerResponse, Publisher<Void>> { - + public interface Action { + Publisher<Void> handleRequest(HttpServerRequest request, HttpServerResponse response); } + private static final String JMAP_VERSION_HEADER = "jmapVersion="; + private final Endpoint endpoint; private final Version version; private final Action action; @@ -52,4 +58,22 @@ public class JMAPRoute { public Action getAction() { return action; } + + public boolean matches(HttpServerRequest request) { + return getVersion().equals(extractRequestVersionHeader(request)) + && getEndpoint().matches(request); + } + + private Version extractRequestVersionHeader(HttpServerRequest request) { + return Optional.ofNullable(request.requestHeaders().get(ACCEPT)) + .map(s -> s.split(";")) + .map(Arrays::stream) + .orElse(Stream.of()) + .map(value -> value.trim().toLowerCase()) + .filter(value -> value.startsWith(JMAP_VERSION_HEADER.toLowerCase())) + .map(value -> value.substring(JMAP_VERSION_HEADER.length())) + .map(Version::of) + .findFirst() + .orElse(Version.DRAFT); + } } diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPRoutes.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPRoutes.java index 779c8ca..f2a8b09 100644 --- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPRoutes.java +++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPRoutes.java @@ -36,7 +36,7 @@ public interface JMAPRoutes { JMAPRoute.Action CORS_CONTROL = corsHeaders((req, res) -> res.send()); static JMAPRoute.Action corsHeaders(JMAPRoute.Action action) { - return (req, res) -> action.apply(req, res + return (req, res) -> action.handleRequest(req, res .header("Access-Control-Allow-Origin", "*") .header("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT") .header("Access-Control-Allow-Headers", "Content-Type, Authorization, Accept")); diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPServer.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPServer.java index 12426ed..6b354fe 100644 --- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPServer.java +++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/JMAPServer.java @@ -19,13 +19,9 @@ package org.apache.james.jmap; -import static io.netty.handler.codec.http.HttpHeaderNames.ACCEPT; import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST; import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND; -import java.util.Arrays; -import java.util.Collection; -import java.util.Comparator; import java.util.Optional; import java.util.Set; @@ -36,18 +32,12 @@ import org.apache.james.lifecycle.api.Startable; import org.apache.james.util.Port; import org.slf4j.LoggerFactory; -import com.github.steveash.guavate.Guavate; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.ImmutableListMultimap; - import reactor.netty.DisposableServer; import reactor.netty.http.server.HttpServer; import reactor.netty.http.server.HttpServerRequest; -import reactor.netty.http.server.HttpServerRoutes; public class JMAPServer implements Startable { private static final int RANDOM_PORT = 0; - private static final String JMAP_VERSION_HEADER = "jmapVersion="; private final JMAPConfiguration configuration; private final Set<JMAPRoutes> jmapRoutes; @@ -67,17 +57,12 @@ public class JMAPServer implements Startable { } public void start() { - ImmutableListMultimap<Endpoint, JMAPRoute> collect = jmapRoutes.stream() - .flatMap(JMAPRoutes::routes) - .collect(Guavate.toImmutableListMultimap(JMAPRoute::getEndpoint)); - if (configuration.isEnabled()) { server = Optional.of(HttpServer.create() .port(configuration.getPort() .map(Port::getValue) .orElse(RANDOM_PORT)) - .route(routes -> jmapRoutes.forEach(jmapRoute -> collect.asMap().forEach( - (endpoint, route) -> injectRoutes(routes, endpoint, route)))) + .handle((request, response) -> handleVersionRoute(request).handleRequest(request, response)) .wiretap(wireTapEnabled()) .bindNow()); } @@ -87,61 +72,20 @@ public class JMAPServer implements Startable { return LoggerFactory.getLogger("org.apache.james.jmap.wire").isTraceEnabled(); } - private HttpServerRoutes injectRoutes(HttpServerRoutes builder, Endpoint endpoint, Collection<JMAPRoute> routesList) { - if (routesList.size() == 1) { - JMAPRoute next = routesList.iterator().next(); - - return endpoint.registerRoute(builder, (req, res) -> - getExistingRoute(extractRequestVersionHeader(req), next).apply(req, res)); - } else if (routesList.size() == 2) { - ImmutableList<JMAPRoute> sorted = routesList.stream() - .sorted(Comparator.comparing(JMAPRoute::getVersion)) - .collect(Guavate.toImmutableList()); - JMAPRoute draftRoute = sorted.get(0); - JMAPRoute rfc8621Route = sorted.get(1); - - return endpoint.registerRoute(builder, (req, res) -> - chooseVersionRoute(extractRequestVersionHeader(req), draftRoute, rfc8621Route).apply(req, res)); - } - return builder; - } - - private JMAPRoute.Action getExistingRoute(String version, JMAPRoute route) { + private JMAPRoute.Action handleVersionRoute(HttpServerRequest request) { try { - if (Version.of(version).equals(route.getVersion())) { - return route.getAction(); - } + return jmapRoutes.stream() + .flatMap(JMAPRoutes::routes) + .filter(jmapRoute -> jmapRoute.matches(request)) + .map(JMAPRoute::getAction) + .findFirst() + .orElse((req, res) -> res.status(NOT_FOUND).send()); } catch (IllegalArgumentException e) { return (req, res) -> res.status(BAD_REQUEST).send(); } - return (req, res) -> res.status(NOT_FOUND).send(); } - private JMAPRoute.Action chooseVersionRoute(String version, JMAPRoute draftRoute, JMAPRoute rfc8621Route) { - try { - if (hasRfc8621AcceptHeader(version)) { - return rfc8621Route.getAction(); - } - } catch (IllegalArgumentException e) { - return (req, res) -> res.status(BAD_REQUEST).send(); - } - return draftRoute.getAction(); - } - private boolean hasRfc8621AcceptHeader(String version) { - return Version.of(version).equals(Version.RFC8621); - } - - private String extractRequestVersionHeader(HttpServerRequest request) { - return Arrays.stream(request.requestHeaders() - .get(ACCEPT) - .split(";")) - .map(value -> value.trim().toLowerCase()) - .filter(value -> value.startsWith(JMAP_VERSION_HEADER.toLowerCase())) - .map(value -> value.substring(JMAP_VERSION_HEADER.length())) - .findFirst() - .orElse(Version.DRAFT.getVersion()); - } @PreDestroy public void stop() { diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/UriPathTemplate.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/UriPathTemplate.java new file mode 100644 index 0000000..a9b3759 --- /dev/null +++ b/server/protocols/jmap/src/main/java/org/apache/james/jmap/UriPathTemplate.java @@ -0,0 +1,139 @@ +/**************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one * + * or more contributor license agreements. See the NOTICE file * + * distributed with this work for additional information * + * regarding copyright ownership. The ASF licenses this file * + * to you under the Apache License, Version 2.0 (the * + * "License"); you may not use this file except in compliance * + * with the License. You may obtain a copy of the License at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * Unless required by applicable law or agreed to in writing, * + * software distributed under the License is distributed on an * + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * + * KIND, either express or implied. See the License for the * + * specific language governing permissions and limitations * + * under the License. * + ****************************************************************/ + +package org.apache.james.jmap; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * This class is copied from io.projectreactor.netty:reactor-netty version0.9.0-RELEASE and was originaly license under + * Apache license version 2. Copied because of private access. + * <p> + * Represents a URI template. A URI template is a URI-like String that contains + * variables enclosed by braces (<code>{</code>, <code>}</code>), which can be + * expanded to produce an actual URI. + * + * @author Arjen Poutsma + * @author Juergen Hoeller + * @author Jon Brisbin + * @see <a href="https://tools.ietf.org/html/rfc6570">RFC 6570: URI Templates</a> + */ +public class UriPathTemplate { + private static final Pattern FULL_SPLAT_PATTERN = + Pattern.compile("[\\*][\\*]"); + private static final String FULL_SPLAT_REPLACEMENT = ".*"; + + private static final Pattern NAME_SPLAT_PATTERN = + Pattern.compile("\\{([^/]+?)\\}[\\*][\\*]"); + + private static final Pattern NAME_PATTERN = Pattern.compile("\\{([^/]+?)\\}"); + // JDK 6 doesn't support named capture groups + + private final List<String> pathVariables = + new ArrayList<>(); + private final HashMap<String, Matcher> matchers = + new HashMap<>(); + private final HashMap<String, Map<String, String>> vars = + new HashMap<>(); + + private final Pattern uriPattern; + + private static String getNameSplatReplacement(String name) { + return "(?<" + name + ">.*)"; + } + + private static String getNameReplacement(String name) { + return "(?<" + name + ">[^\\/]*)"; + } + + static String filterQueryParams(String uri) { + int hasQuery = uri.lastIndexOf('?'); + if (hasQuery != -1) { + return uri.substring(0, hasQuery); + } else { + return uri; + } + } + + /** + * Creates a new {@code UriPathTemplate} from the given {@code uriPattern}. + * + * @param uriPattern The pattern to be used by the template + */ + UriPathTemplate(String uriPattern) { + String s = "^" + filterQueryParams(uriPattern); + + Matcher m = NAME_SPLAT_PATTERN.matcher(s); + while (m.find()) { + for (int i = 1; i <= m.groupCount(); i++) { + String name = m.group(i); + pathVariables.add(name); + s = m.replaceFirst(getNameSplatReplacement(name)); + m.reset(s); + } + } + + m = NAME_PATTERN.matcher(s); + while (m.find()) { + for (int i = 1; i <= m.groupCount(); i++) { + String name = m.group(i); + pathVariables.add(name); + s = m.replaceFirst(getNameReplacement(name)); + m.reset(s); + } + } + + m = FULL_SPLAT_PATTERN.matcher(s); + while (m.find()) { + s = m.replaceAll(FULL_SPLAT_REPLACEMENT); + m.reset(s); + } + + this.uriPattern = Pattern.compile(s + "$"); + } + + /** + * Tests the given {@code uri} against this template, returning {@code true} if + * the uri matches the template, {@code false} otherwise. + * + * @param uri The uri to match + * @return {@code true} if there's a match, {@code false} otherwise + */ + public boolean matches(String uri) { + return matcher(uri).matches(); + } + + private Matcher matcher(String uri) { + uri = filterQueryParams(uri); + Matcher m = matchers.get(uri); + if (null == m) { + m = uriPattern.matcher(uri); + synchronized (matchers) { + matchers.put(uri, m); + } + } + return m; + } + +} diff --git a/server/protocols/jmap/src/main/java/org/apache/james/jmap/Verb.java b/server/protocols/jmap/src/main/java/org/apache/james/jmap/Verb.java deleted file mode 100644 index 7a94547..0000000 --- a/server/protocols/jmap/src/main/java/org/apache/james/jmap/Verb.java +++ /dev/null @@ -1,44 +0,0 @@ -/**************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one * - * or more contributor license agreements. See the NOTICE file * - * distributed with this work for additional information * - * regarding copyright ownership. The ASF licenses this file * - * to you under the Apache License, Version 2.0 (the * - * "License"); you may not use this file except in compliance * - * with the License. You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, * - * software distributed under the License is distributed on an * - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * - * KIND, either express or implied. See the License for the * - * specific language governing permissions and limitations * - * under the License. * - ****************************************************************/ - -package org.apache.james.jmap; - -import reactor.netty.http.server.HttpServerRoutes; - -public enum Verb { - GET, - POST, - DELETE, - OPTIONS; - - HttpServerRoutes registerRoute(HttpServerRoutes builder, String path, JMAPRoute.Action action) { - switch (this) { - case GET: - return builder.get(path, action); - case POST: - return builder.post(path, action); - case DELETE: - return builder.delete(path, action); - case OPTIONS: - return builder.options(path, action); - default: - return builder; - } - } -} diff --git a/server/protocols/jmap/src/test/java/org/apache/james/jmap/JMAPServerTest.java b/server/protocols/jmap/src/test/java/org/apache/james/jmap/JMAPServerTest.java index b03a836..a0eec47 100644 --- a/server/protocols/jmap/src/test/java/org/apache/james/jmap/JMAPServerTest.java +++ b/server/protocols/jmap/src/test/java/org/apache/james/jmap/JMAPServerTest.java @@ -42,6 +42,7 @@ import org.slf4j.LoggerFactory; import com.google.common.collect.ImmutableSet; +import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpResponseStatus; import io.restassured.RestAssured; import io.restassured.builder.RequestSpecBuilder; @@ -62,12 +63,12 @@ class JMAPServerTest { private static final ImmutableSet<JMAPRoutes> NO_ROUTES = ImmutableSet.of(); private static final ImmutableSet<Endpoint> AUTHENTICATION_ENDPOINTS = ImmutableSet.of( - new Endpoint(Verb.POST, JMAPUrls.AUTHENTICATION), - new Endpoint(Verb.GET, JMAPUrls.AUTHENTICATION) + new Endpoint(HttpMethod.POST, JMAPUrls.AUTHENTICATION), + new Endpoint(HttpMethod.GET, JMAPUrls.AUTHENTICATION) ); private static final ImmutableSet<Endpoint> JMAP_ENDPOINTS = ImmutableSet.of( - new Endpoint(Verb.POST, JMAPUrls.JMAP), - new Endpoint(Verb.DELETE, JMAPUrls.JMAP) + new Endpoint(HttpMethod.POST, JMAPUrls.JMAP), + new Endpoint(HttpMethod.DELETE, JMAPUrls.JMAP) ); private static final ImmutableSet<JMAPRoutes> FAKE_ROUTES = ImmutableSet.of( new FakeJMAPRoutes(AUTHENTICATION_ENDPOINTS, Version.DRAFT), --------------------------------------------------------------------- To unsubscribe, e-mail: server-dev-unsubscr...@james.apache.org For additional commands, e-mail: server-dev-h...@james.apache.org