Helidon 2.0 and CORS

Tim Quinn
Helidon
Published in
4 min readJun 25, 2020

Beginning with newly-released Helidon 2.0 [1], you can easily add CORS support to your application and to Helidon’s built-in services for health, metrics, and OpenAPI.

A same-origin policy [2], implemented in browsers and other well-behaved web clients, restricts a page or app from one origin — URL scheme, host, and port — from accessing resources from other origins.

But, in certain cases, relaxing that restriction makes sense. Cross-origin resource sharing (CORS) [3] is a standard approach using HTTP headers in which web clients ask for and servers grant (or refuse) controlled access to resources from one origin on behalf of a resource in a different origin.

With the recent release of Helidon 2.0, you can easily add CORS support to the endpoints in your own SE [4] and MP [5] applications as well as the endpoints of built-in Helidon services such as health, metrics, and OpenAPI.

The examples below are inspired by the Helidon greeting example app [6, 7]. We’ll assume that you want to permit resources from http://foo.com and http://there.com to have PUT and DELETE access to your app’s resources while granting simple access — GET, HEAD, etc. — to all other origins.

CORS in Helidon SE

Add this dependency to your pom.xml

<dependency>
<groupId>io.helidon.webserver</groupId>
<artifactId>helidon-webserver-cors</artifactId>
</dependency>

Create a CorsSupport instance in your code

Build a CorsSupport object to represent the CORS behavior you want for your app’s resources. You use two CrossOriginConfig objects, one for the PUT and DELETE access and a second for all other access:

CorsSupport corsSupport = CorsSupport.builder()
.addCrossOriginConfig(CrossOriginConfig.builder()
.allowOrigins("http://foo.com", "http://there.com")
.allowMethods("PUT", "DELETE")
.build())
.addCrossOriginConfig(CrossOriginConfig.create())
.build();

Use the CorsSupport instance

Use the instance of CorsSupport in the same register call that sets up routing for your application’s path:

return Routing.builder()
.register(JsonSupport.create())
.register(health) // Health at "/health"
.register(metrics) // Metrics at "/metrics"
.register("/greet", corsSupport, greetService)
.build();

Then rebuild and restart your app. That’s it. See below for some simple ways to see the CORS support in action with your app.

CORS in Helidon MP

Add this dependency to your pom.xml

<dependency>
<groupId>io.helidon.microprofile</groupId>
<artifactId>helidon-microprofile-cors</artifactId>
</dependency>

Use the @CrossOrigin annotation

In your JAX-RS resource class, add a no-op method for HTTP OPTIONS requests and annotate it:

@OPTIONS
@CrossOrigin(
allowMethods = {"PUT", "DELETE"},
allowOrigins = {"http://foo.com", "http://there.com"})

public void optionsForGreeting() {}

Then rebuild and restart your app.

Trying it out (SE and MP)

You can use curl to see Helidon’s CORS support in action for your app’s endpoints. The code snippets below highlight the CORS-related headers.

For “simple” operations — GET, HEAD, etc. — CORS simply adds headers to the normal HTTP request.

curl -i -X GET \
-H "Origin: http://foo.com" -H "Host: here.com" \
http://localhost:8080/greet

And to the response.

HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Content-Type: application/json
Date: Thu, 30 Apr 2020 17:25:51 -0500
Vary: Origin
connection: keep-alive
content-length: 27

{"greeting":"Hello World!"}

For so-called “non-simple” operations that can change the application’s server-side state, CORS uses a pre-flight request that lets the server “pre-approve” the desired access as declared by the client. Usually a browser sends pre-flight requests (and checks the responses) invisibly, but the followingcurl command simulates this step:

curl -i -X OPTIONS \
-H "Access-Control-Request-Method: PUT" \
-H "Origin: http://foo.com" \
-H "Host: here.com" \

http://localhost:8080/greet/greeting

The server indicates that it is OK with the requested cross-origin resource sharing:

HTTP/1.1 200 OK
Access-Control-Allow-Methods: PUT
Access-Control-Allow-Origin: http://foo.com
Date: Thu, 30 Apr 2020 17:30:59 -0500
transfer-encoding: chunked
connection: keep-alive

At this point the client issues the desired PUT request, in our example to change the greeting:

curl -i -X PUT \
-H "Origin: http://foo.com" \
-H "Host: here.com" \
-H "Access-Control-Allow-Methods: PUT" \
-H "Access-Control-Allow-Origin: http://foo.com" \

-H "Content-Type: application/json" \
-d "{ \"greeting\" : \"Cheers\" }" \
http://localhost:8080/greet/greeting

The server responds normally:

HTTP/1.1 204 No Content
Access-Control-Allow-Origin: http://foo.com
Date: Thu, 30 Apr 2020 17:32:55 -0500
Vary: Origin
connection: keep-alive

Bottom Line

With very little extra code, your Helidon SE or MP application’s endpoints can participate in the CORS protocol. And we’ve only hit the high points here. You can use configuration to simplify your CORS-related changes even more.

Further, you can easily turn on and control CORS for Helidon’s built-in services that add endpoints to your application: health, metrics, and OpenAPI.

For complete information, please refer to the documentation ([4], [5]).

References

[1] https://helidon.io
[2] https://www.w3.org/Security/wiki/Same_Origin_Policy
[3] https://www.w3.org/TR/cors/
[4] https://helidon.io/docs/latest/#/se/cors/01_introduction
[5] https://helidon.io/docs/latest/#/mp/cors/01_introduction
[6] https://github.com/oracle/helidon/tree/latest/examples/cors
[7] https://github.com/oracle/helidon/tree/latest/examples/microprofile/cors

--

--

Tim Quinn
Helidon
Writer for

Working for Oracle on Project Helidon. Previously, WebLogic Server multitenancy, GlassFish, database design, and high-throughput systems.