Configuring CORS

Armeria provides a way to configure Cross-origin resource sharing (CORS) policy for specific origins or any origin via CorsServiceBuilder. For more information about CORS, visit Wikipedia's CORS page.

Allowing any origin

To configure CORS Service allowing any origin (*), use CorsService.builderForAnyOrigin(), e.g.

import com.linecorp.armeria.common.HttpMethod;
import com.linecorp.armeria.server.HttpService;
import com.linecorp.armeria.server.ServerBuilder;
import com.linecorp.armeria.server.cors.CorsService;

HttpService myService = (ctx, req) -> ...;
Function<? super HttpService, CorsService> corsService =
        CorsService.builderForAnyOrigin()
                   .allowCredentials()
                   .allowRequestMethods(HttpMethod.POST, HttpMethod.GET)
                   .allowRequestHeaders("allow_request_header")
                   .exposeHeaders("expose_header_1", "expose_header_2")
                   .preflightResponseHeader("x-preflight-cors", "Hello CORS")
                   .newDecorator();

ServerBuilder sb = Server.builder()
                         .service("/message", myService.decorate(corsService));

Allowing specific origins

To configure CORS Service allowing specific origins, use CorsService.builder(), e.g.

HttpService myService = (ctx, req) -> ...;
Function<? super HttpService, CorsService> corsService =
        CorsService.builder("http://example.com")
                   .allowCredentials()
                   .allowNullOrigin() // 'Origin: null' will be accepted.
                   .allowRequestMethods(HttpMethod.POST, HttpMethod.GET)
                   .allowRequestHeaders("allow_request_header")
                   .exposeHeaders("expose_header_1", "expose_header_2")
                   .preflightResponseHeader("x-preflight-cors", "Hello CORS")
                   .newDecorator();

ServerBuilder sb = Server.builder()
                         .service("/message", myService.decorate(corsService));

Applying different policies for different origins

To configure different policies for different groups of origins, use andForOrigins() or andForOrigin() method which creates a new ChainedCorsPolicyBuilder with the specific origins. Call and() to return to CorsServiceBuilder once you are done with building a policy, e.g.

HttpService myService = (ctx, req) -> ...;
Function<? super HttpService, CorsService> corsService =
        CorsService.builder("http://example.com")
                   .allowCredentials()
                   .allowNullOrigin() // 'Origin: null' will be accepted.
                   .allowRequestMethods(HttpMethod.POST, HttpMethod.GET)
                   .allowRequestHeaders("allow_request_header")
                   .exposeHeaders("expose_header_1", "expose_header_2")
                   .preflightResponseHeader("x-preflight-cors", "Hello CORS")
                   .maxAge(3600)
                   .andForOrigins("http://example2.com")
                   .allowCredentials()
                   .allowRequestMethods(HttpMethod.GET)
                   .allowRequestHeaders("allow_request_header2")
                   .exposeHeaders("expose_header_3", "expose_header_4")
                   .and()
                   .newDecorator();

ServerBuilder sb = Server.builder()
                         .service("/message", myService.decorate(corsService));

You can also directly add a CorsPolicy created by a CorsPolicyBuilder, e.g.

import com.linecorp.armeria.server.cors.CorsPolicyBuilder;

HttpService myService = (ctx, req) -> ...;
Function<? super HttpService, CorsService> corsService =
        CorsService.builder("http://example.com")
                   .allowCredentials()
                   .allowNullOrigin() // 'Origin: null' will be accepted.
                   .allowRequestMethods(HttpMethod.POST, HttpMethod.GET)
                   .allowRequestHeaders("allow_request_header")
                   .exposeHeaders("expose_header_1", "expose_header_2")
                   .preflightResponseHeader("x-preflight-cors", "Hello CORS")
                   .maxAge(3600)
                   .addPolicy(CorsPolicy.builder("http://example2.com")
                                        .allowCredentials()
                                        .allowRequestMethods(HttpMethod.GET)
                                        .allowRequestHeaders("allow_request_header2")
                                        .exposeHeaders("expose_header_3", "expose_header_4")
                                        .build())
                   .newDecorator();

ServerBuilder sb = Server.builder()
                         .service("/message", myService.decorate(corsService));

Applying a policy to the specific paths

To configure a policy to the specific paths, you can use route() methods in the CorsServiceBuilder and ChainedCorsPolicyBuilder. They can help you to adjust the scope that a policy is applied to, e.g.

HttpService myService = (ctx, req) -> ...;
Function<? super HttpService, CorsService> corsService =
        CorsService.builder("http://example.com")
                   // CORS policy will be applied for the path that starts with '/message/web/api/'.
                   .route("prefix:/message/web/api/")
                   .allowRequestMethods(HttpMethod.POST, HttpMethod.GET)
                   .newDecorator();
ServerBuilder sb = Server.builder()
                         .service("/message", myService.decorate(corsService));

Configuring CORS via annotation

You can also configure CORS for Annotated services using the @CorsDecorator annotation, e.g.

import com.linecorp.armeria.server.annotation.AdditionalHeader;
import com.linecorp.armeria.server.annotation.Get;
import com.linecorp.armeria.server.annotation.decorator.CorsDecorator;

Object annotatedService = new Object() {
    @Get("/get")
    @CorsDecorator(origins = "http://example.com", credentialsAllowed = true,
                   nullOriginAllowed = true, exposedHeaders = "expose_header",
                   allowedRequestMethods = HttpMethod.GET, allowedRequestHeaders = "allow_header",
                   preflightResponseHeaders = {
                       @AdditionalHeader(name = "preflight_header", value = "preflight_value")
                   })
    // In case you want to configure different CORS policies for different origins.
    @CorsDecorator(origins = "http://example2.com", credentialsAllowed = true)
    public HttpResponse get() {
        return HttpResponse.of(HttpStatus.OK);
    }

    @Post("/post")
    // In case you want to allow any origin (*):
    @CorsDecorator(origins = "*", exposedHeaders = "expose_header")
    // You can not add a policy after adding the decorator allowing any origin (*).
    public HttpResponse post() {
        return HttpResponse.of(HttpStatus.OK)
    }
};

Server s = Server.builder()
                 .annotatedService("/example", annotatedService)
                 .build();

You can also use @CorsDecorator at the class level to apply the decorator to all service methods in the class. Note that the @CorsDecorator annotation specified at the method level takes precedence over what's specified at the class level:

// This decorator will be ignored for the path "/post".
@CorsDecorator(origins = "http://example.com", credentialsAllowed = true)
class MyAnnotatedService {
    @Get("/get")
    public HttpResponse get() {
        return HttpResponse.of(HttpStatus.OK);
    }

    @Post("/post")
    @CorsDecorator(origins = "http://example2.com", credentialsAllowed = true)
    public HttpResponse post() {
        return HttpResponse.of(HttpStatus.OK);
    }
}

If you want to allow a CORS policy to the specific paths, you can use pathPatterns property:

// This policy will be applied only to the paths matched by the pattern.
@CorsDecorator(origins = "http://example.com", pathPatterns = "glob:/**/web/api", credentialsAllowed = true)
class MyAnnotatedService {
    ...
}