Skip to content

Commit

Permalink
Add CORS support for SE and MP applications to 2.x (helidon-io#1633)
Browse files Browse the repository at this point in the history
  • Loading branch information
tjquinno authored Apr 17, 2020
1 parent b1de962 commit c813a24
Show file tree
Hide file tree
Showing 41 changed files with 4,777 additions and 0 deletions.
16 changes: 16 additions & 0 deletions bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@
<artifactId>helidon-webserver-tyrus</artifactId>
<version>${helidon.version}</version>
</dependency>
<dependency>
<groupId>io.helidon.webserver</groupId>
<artifactId>helidon-webserver-cors</artifactId>
<version>${helidon.version}</version>
</dependency>
<!-- Helidon Jersey -->
<dependency>
<groupId>io.helidon.jersey</groupId>
Expand Down Expand Up @@ -161,6 +166,11 @@
<artifactId>helidon-microprofile-grpc-client</artifactId>
<version>${helidon.version}</version>
</dependency>
<dependency>
<groupId>io.helidon.microprofile</groupId>
<artifactId>helidon-microprofile-cors</artifactId>
<version>${helidon.version}</version>
</dependency>
<!-- media -->
<dependency>
<groupId>io.helidon.media</groupId>
Expand Down Expand Up @@ -808,6 +818,12 @@
<artifactId>helidon-microprofile-openapi</artifactId>
<version>${helidon.version}</version>
</dependency>
<!-- CORS support -->
<dependency>
<groupId>io.helidon.webserver.cors</groupId>
<artifactId>helidon-cors</artifactId>
<version>${helidon.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
4 changes: 4 additions & 0 deletions microprofile/bundles/helidon-microprofile/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@
<groupId>io.helidon.microprofile.tracing</groupId>
<artifactId>helidon-microprofile-tracing</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.microprofile</groupId>
<artifactId>helidon-microprofile-cors</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-binding</artifactId>
Expand Down
70 changes: 70 additions & 0 deletions microprofile/cors/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2019, 2020 Oracle and/or its affiliates.
Licensed 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.
-->

<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>io.helidon.microprofile</groupId>
<artifactId>helidon-microprofile-project</artifactId>
<version>2.0.0-SNAPSHOT</version>
</parent>

<artifactId>helidon-microprofile-cors</artifactId>
<name>Helidon Microprofile CORS</name>

<dependencies>
<dependency>
<groupId>javax.ws.rs</groupId>
<artifactId>javax.ws.rs-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.helidon.jersey</groupId>
<artifactId>helidon-jersey-common</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.webserver</groupId>
<artifactId>helidon-webserver</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.webserver</groupId>
<artifactId>helidon-webserver-jersey</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.microprofile.config</groupId>
<artifactId>helidon-microprofile-config</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.webserver</groupId>
<artifactId>helidon-webserver-cors</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.microprofile.bundles</groupId>
<artifactId>internal-test-libs</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.helidon.microprofile.server</groupId>
<artifactId>helidon-microprofile-server</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
/*
* Copyright (c) 2020 Oracle and/or its affiliates.
*
* Licensed 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 io.helidon.microprofile.cors;

import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;

import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.core.MultivaluedHashMap;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;

import io.helidon.webserver.cors.CorsSupportBase;
import io.helidon.webserver.cors.CrossOriginConfig;

/**
* MP implementation of {@link CorsSupportBase}.
*/
class CorsSupportMp extends CorsSupportBase {

/**
*
* @return a new builder of CorsSupportMp
*/
static Builder builder() {
return new Builder();
}

private CorsSupportMp(Builder builder) {
super(builder);
}

/**
* <em>Not for developer use.</em> Submits a request adapter and response adapter for CORS processing.
*
* @param requestAdapter wrapper around the request
* @param responseAdapter wrapper around the response
* @return Optional of the response type U; present if the response should be returned, empty if request processing should
* continue
*/
@Override
protected <T, U> Optional<U> processRequest(RequestAdapter<T> requestAdapter,
ResponseAdapter<U> responseAdapter) {
return super.processRequest(requestAdapter, responseAdapter);
}

/**
* <em>Not for developer user.</em> Gets a response ready to participate in the CORS protocol.
*
* @param requestAdapter wrapper around the request
* @param responseAdapter wrapper around the response
*/
@Override
protected <T, U> void prepareResponse(RequestAdapter<T> requestAdapter, ResponseAdapter<U> responseAdapter) {
super.prepareResponse(requestAdapter, responseAdapter);
}

static class Builder extends CorsSupportBase.Builder<CorsSupportMp, Builder> {

@Override
public CorsSupportMp build() {
return new CorsSupportMp(this);
}

@Override
protected Builder me() {
return this;
}

@Override
protected Builder secondaryLookupSupplier(
Supplier<Optional<CrossOriginConfig>> secondaryLookupSupplier) {
super.secondaryLookupSupplier(secondaryLookupSupplier);
return this;
}
}

static class RequestAdapterMp implements RequestAdapter<ContainerRequestContext> {

private final ContainerRequestContext requestContext;

RequestAdapterMp(ContainerRequestContext requestContext) {
this.requestContext = requestContext;
}

@Override
public String path() {
return requestContext.getUriInfo().getPath();
}

@Override
public Optional<String> firstHeader(String s) {
return Optional.ofNullable(requestContext.getHeaders().getFirst(s));
}

@Override
public boolean headerContainsKey(String s) {
return requestContext.getHeaders().containsKey(s);
}

@Override
public List<String> allHeaders(String s) {
return requestContext.getHeaders().get(s);
}

@Override
public String method() {
return requestContext.getMethod();
}

@Override
public ContainerRequestContext request() {
return requestContext;
}

@Override
public void next() {
}

@Override
public String toString() {
return String.format("RequestAdapterMp{path=%s, method=%s, headers=%s}", path(), method(),
requestContext.getHeaders());
}
}

static class ResponseAdapterMp implements ResponseAdapter<Response> {

private final MultivaluedMap<String, Object> headers;

ResponseAdapterMp(ContainerResponseContext responseContext) {
headers = responseContext.getHeaders();
}

ResponseAdapterMp() {
headers = new MultivaluedHashMap<>();
}

@Override
public ResponseAdapter<Response> header(String key, String value) {
headers.add(key, value);
return this;
}

@Override
public ResponseAdapter<Response> header(String key, Object value) {
headers.add(key, value);
return this;
}

@Override
public Response forbidden(String message) {
return Response.status(Response.Status.FORBIDDEN).entity(message).build();
}

@Override
public Response ok() {
Response.ResponseBuilder builder = Response.ok();
/*
* The Helidon CORS support code invokes ok() only for creating a CORS preflight response. In these cases no user
* code will have a chance to set headers in the response. That means we can use replaceAll here because the only
* headers needed in the response are the ones set using this adapter.
*/
builder.replaceAll(headers);
return builder.build();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright (c) 2020 Oracle and/or its affiliates.
*
* Licensed 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 io.helidon.microprofile.cors;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static io.helidon.webserver.cors.CrossOriginConfig.DEFAULT_AGE;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
* CrossOrigin annotation.
*/
@Target(METHOD)
@Retention(RUNTIME)
@Documented
public @interface CrossOrigin {

/**
* A list of origins that are allowed such as {@code "http://foo.com"} or
* {@code "*"} to allow all origins. Corresponds to header {@code
* Access-Control-Allow-Origin}.
*
* @return Allowed origins.
*/
String[] value() default {"*"};

/**
* A list of request headers that are allowed or {@code "*"} to allow all headers.
* Corresponds to {@code Access-Control-Allow-Headers}.
*
* @return Allowed headers.
*/
String[] allowHeaders() default {"*"};

/**
* A list of response headers allowed for clients other than the "standard"
* ones. Corresponds to {@code Access-Control-Expose-Headers}.
*
* @return Exposed headers.
*/
String[] exposeHeaders() default {};

/**
* A list of supported HTTP request methods. In response to pre-flight
* requests. Corresponds to {@code Access-Control-Allow-Methods}.
*
* @return Allowed methods.
*/
String[] allowMethods() default {"*"};

/**
* Whether the client can send cookies or credentials. Corresponds to {@code
* Access-Control-Allow-Credentials}.
*
* @return Allowed credentials.
*/
boolean allowCredentials() default false;

/**
* Pre-flight response duration in seconds. After time expires, a new pre-flight
* request is required. Corresponds to {@code Access-Control-Max-Age}.
*
* @return Max age.
*/
long maxAge() default DEFAULT_AGE;
}
Loading

0 comments on commit c813a24

Please sign in to comment.