You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by rc...@apache.org on 2020/04/10 02:02:18 UTC

[james-project] 10/15: JAMES-3092 Simplify the Y versioning routing in jmap

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 <rc...@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-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org