This is an automated email from the ASF dual-hosted git repository.
btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git
The following commit(s) were added to refs/heads/master by this push:
new ebf10df JAMES-3662 Accept CORS headers without the JMAP API
restriction on "Accept" headers (#699)
ebf10df is described below
commit ebf10dfe499b79b6156a3e19adfb9968fa67c69a
Author: Benoit TELLIER <[email protected]>
AuthorDate: Tue Oct 19 08:25:38 2021 +0700
JAMES-3662 Accept CORS headers without the JMAP API restriction on "Accept"
headers (#699)
---
.../java/org/apache/james/jmap/JMAPServer.java | 29 ++++++++--
.../java/org/apache/james/jmap/JMAPServerTest.java | 63 ++++++++++++++++++++--
2 files changed, 82 insertions(+), 10 deletions(-)
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 e4a7938..d549cc3 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
@@ -22,8 +22,10 @@ package org.apache.james.jmap;
import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST;
import static io.netty.handler.codec.http.HttpResponseStatus.NOT_FOUND;
+import java.util.List;
import java.util.Optional;
import java.util.Set;
+import java.util.stream.Stream;
import javax.annotation.PreDestroy;
import javax.inject.Inject;
@@ -33,9 +35,11 @@ import org.apache.james.lifecycle.api.Startable;
import org.apache.james.util.Port;
import org.slf4j.LoggerFactory;
+import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.Multimap;
+import io.netty.handler.codec.http.HttpMethod;
import reactor.netty.DisposableServer;
import reactor.netty.http.server.HttpServer;
import reactor.netty.http.server.HttpServerRequest;
@@ -46,6 +50,7 @@ public class JMAPServer implements Startable {
private final JMAPConfiguration configuration;
private final VersionParser versionParser;
private final Multimap<Version, JMAPRoute> routes;
+ private final List<JMAPRoute> corsRoutes;
private Optional<DisposableServer> server;
@Inject
@@ -59,9 +64,16 @@ public class JMAPServer implements Startable {
.flatMap(version -> jmapRoutesHandlers.stream()
.flatMap(handler -> handler.routes(version)
.map(route -> Pair.of(version, route))))
+ .filter(route ->
!route.getRight().getEndpoint().getMethod().equals(HttpMethod.OPTIONS))
.collect(ImmutableListMultimap.toImmutableListMultimap(
Pair::getKey,
Pair::getValue));
+ this.corsRoutes = versionParser.getSupportedVersions()
+ .stream()
+ .flatMap(version -> jmapRoutesHandlers.stream()
+ .flatMap(handler -> handler.routes(version)))
+ .filter(route ->
route.getEndpoint().getMethod().equals(HttpMethod.OPTIONS))
+ .collect(ImmutableList.toImmutableList());
}
public Port getPort() {
@@ -87,19 +99,26 @@ public class JMAPServer implements Startable {
}
private JMAPRoute.Action handleVersionRoute(HttpServerRequest request) {
+ if (request.method().equals(HttpMethod.OPTIONS)) {
+ return retrieveMatchingAction(request, corsRoutes.stream());
+ }
try {
Version version = versionParser.parseRequestVersionHeader(request);
- return routes.get(version).stream()
- .filter(jmapRoute -> jmapRoute.matches(request))
- .map(JMAPRoute::getAction)
- .findFirst()
- .orElse((req, res) -> res.status(NOT_FOUND).send());
+ return retrieveMatchingAction(request,
routes.get(version).stream());
} catch (IllegalArgumentException e) {
return (req, res) -> res.status(BAD_REQUEST).send();
}
}
+ private JMAPRoute.Action retrieveMatchingAction(HttpServerRequest request,
Stream<JMAPRoute> routeStream) {
+ return routeStream
+ .filter(jmapRoute -> jmapRoute.matches(request))
+ .map(JMAPRoute::getAction)
+ .findFirst()
+ .orElse((req, res) -> res.status(NOT_FOUND).send());
+ }
+
@PreDestroy
public void stop() {
server.ifPresent(DisposableServer::disposeNow);
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 207e12a..7748951 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
@@ -22,6 +22,7 @@ package org.apache.james.jmap;
import static io.netty.handler.codec.http.HttpHeaderNames.ACCEPT;
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
import static io.restassured.RestAssured.given;
+import static io.restassured.RestAssured.when;
import static io.restassured.config.EncoderConfig.encoderConfig;
import static io.restassured.config.RestAssuredConfig.newConfig;
import static org.apache.james.jmap.HttpConstants.JSON_CONTENT_TYPE_UTF8;
@@ -37,8 +38,6 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
import com.google.common.collect.ImmutableSet;
@@ -81,6 +80,14 @@ class JMAPServerTest {
new FakeJMAPRoutes(AUTHENTICATION_ENDPOINTS, Version.RFC8621))
);
+ private static final ImmutableSet<JMAPRoutesHandler> CORS_ROUTES =
ImmutableSet.of(
+ new JMAPRoutesHandler(
+ Version.DRAFT,
+ new FakeJMAPRoutes(ImmutableSet.of(new
Endpoint(HttpMethod.OPTIONS, "/a")), Version.DRAFT)),
+ new JMAPRoutesHandler(
+ Version.RFC8621,
+ new FakeJMAPRoutes(ImmutableSet.of(new
Endpoint(HttpMethod.OPTIONS, "/b")), Version.RFC8621)));
+
private static final ImmutableSet<Version> SUPPORTED_VERSIONS =
ImmutableSet.of(
Version.DRAFT,
Version.RFC8621
@@ -222,9 +229,50 @@ class JMAPServerTest {
}
}
- private static class FakeJMAPRoutes implements JMAPRoutes {
- private static final Logger LOGGER =
LoggerFactory.getLogger(FakeJMAPRoutes.class);
+ @Nested
+ class CorsRouteVersioningTest {
+ JMAPServer server;
+
+ @BeforeEach
+ void setUp() {
+ VersionParser versionParser = new
VersionParser(SUPPORTED_VERSIONS, JMAPConfiguration.DEFAULT);
+ server = new JMAPServer(TEST_CONFIGURATION, CORS_ROUTES,
versionParser);
+ server.start();
+
+ RestAssured.requestSpecification = new RequestSpecBuilder()
+ .setContentType(ContentType.JSON)
+ .setAccept(ContentType.JSON)
+
.setConfig(newConfig().encoderConfig(encoderConfig().defaultContentCharset(StandardCharsets.UTF_8)))
+ .setPort(server.getPort().getValue())
+ .build();
+ }
+
+ @AfterEach
+ void tearDown() {
+ server.stop();
+ }
+ @Test
+ void corsRoutesOfAllVersionsShouldBeExposed() {
+ when()
+ .options("/b")
+ .then()
+ .statusCode(200)
+ .header("Access-Control-Allow-Origin", "*")
+ .header("Access-Control-Allow-Methods", "GET, POST, DELETE,
PUT")
+ .header("Access-Control-Allow-Headers", "Content-Type,
Authorization, Accept");
+
+ when()
+ .options("/a")
+ .then()
+ .statusCode(200)
+ .header("Access-Control-Allow-Origin", "*")
+ .header("Access-Control-Allow-Methods", "GET, POST, DELETE,
PUT")
+ .header("Access-Control-Allow-Headers", "Content-Type,
Authorization, Accept");
+ }
+ }
+
+ private static class FakeJMAPRoutes implements JMAPRoutes {
private final Set<Endpoint> endpoints;
private final Version version;
@@ -238,7 +286,12 @@ class JMAPServerTest {
return endpoints.stream()
.map(endpoint -> JMAPRoute.builder()
.endpoint(endpoint)
- .action((request, response) ->
sendVersionResponse(response))
+ .action((request, response) -> {
+ if (endpoint.getMethod().equals(HttpMethod.OPTIONS)) {
+ return
JMAPRoutes.CORS_CONTROL.handleRequest(request, response);
+ }
+ return sendVersionResponse(response);
+ })
.noCorsHeaders());
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]