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