Saurav Dangol
Saurav Dangol

Reputation: 904

CORS issue in vertx Application not working

My Vertx Server resides in server A and client resides in server B. When i tried to access vertx server, CORS error pops in. I added some server side code to handle CORS issue but it's not working. Do we need to add some header in client side. what am i missing here? Can anyone help

Vertx Server Side:

Router router = Router.router(vertx);
router.route().handler(BodyHandler.create());
router.route().handler(io.vertx.rxjava.ext.web.handler.CorsHandler.create("*")
.allowedMethod(io.vertx.core.http.HttpMethod.GET)
.allowedMethod(io.vertx.core.http.HttpMethod.POST)
.allowedMethod(io.vertx.core.http.HttpMethod.OPTIONS)
.allowedHeader("Access-Control-Request-Method")
.allowedHeader("Access-Control-Allow-Credentials")
.allowedHeader("Access-Control-Allow-Origin")
.allowedHeader("Access-Control-Allow-Headers")
.allowedHeader("Content-Type"));

Client implementation:

    function(url, user) {
    eventBus = new EventBus(url);
    eventBus.onopen = function() {
            //Do Something
    }
 }

Update:

I removed the withCredential attribute in header.Now my code looks like

if (ar.succeeded()) {
routingContext.response().setStatusCode(200).setStatusMessage("OK")
.putHeader("content-type", "application/json")
.putHeader("Access-Control-Allow-Origin", "*")
.putHeader("Access-Control-Allow-Methods","GET, POST, OPTIONS")
.end(
Json.encodePrettily(ar.result().body())
//(String) ar.result().body()
);
routingContext.response().close(); 

but still following error pops up. Can you help?

XMLHttpRequest cannot load https://192.168.1.20:7070/Notify/571/rn4nh0r4/xhr_send?t=1471592391921. A wildcard '*' cannot be used in the 'Access-Control-Allow-Origin' header when the credentials flag is true. Origin 'https://login.com' is therefore not allowed access. The credentials mode of an XMLHttpRequest is controlled by the withCredentials attribute.

Update2: After adding my client address in

.putHeader("Access-Control-Allow-Origin", "*")

I got following log:

XMLHttpRequest cannot LOAD https://192.168.1.20:7070/Notify/773/k3zq1z2z/xhr_send?t=1471601521206. Credentials flag IS 'true', but the 'Access-Control-Allow-Credentials' header IS ''. It must be 'true' TO allow credentials. Origin 'https://login.com' IS therefore NOT allowed access.

My code is as follows:

 if (ar.succeeded()) {
routingContext.response().setStatusCode(200).setStatusMessage("OK")
.putHeader("content-type", "application/json")
.putHeader("Access-Control-Allow-Origin", "https://login.com")
.putHeader("Access-Control-Allow-Methods","GET, POST, OPTIONS")
.putHeader("Access-Control-Allow-Credentials", "true")
.end(
Json.encodePrettily(ar.result().body())
//(String) ar.result().body()
);
routingContext.response().close();

Upvotes: 5

Views: 15732

Answers (5)

grbulat
grbulat

Reputation: 136

I write an answer for this because non of this solutions helped due to another nature of my cors issue.

Issue: In Chrome browser (no issues in Firefox) I got random cors errors. One part of the cors requests (pre-flight OPTIONS request) was always fine, but second one, no matter it is GET, POST etc randomly failed with standard browser's cors error (Access to fetch at '' from origin '' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.) I have separate domain names for api and frontend.

Scheme: AWS CloudFront -> S3 (static website hosting) -> ELB -> api (in ec2)

Solution: First of all here is the final code for cors settings in vertx that is working:

    private void initCors(final Router router) {
    final Set<String> allowedHeaders = new HashSet<>();
    allowedHeaders.add("x-requested-with");
    allowedHeaders.add("Access-Control-Allow-Origin");
    allowedHeaders.add("Access-Control-Allow-Methods");
    allowedHeaders.add("Access-Control-Allow-Headers");
    allowedHeaders.add("Access-Control-Allow-Credentials");
    allowedHeaders.add("origin");
    allowedHeaders.add("Content-Type");
    allowedHeaders.add("accept");
    allowedHeaders.add("X-PINGARUNER");
    allowedHeaders.add("Authorization");

    final Set<HttpMethod> allowedMethods = new HashSet<>();
    allowedMethods.add(HttpMethod.GET);
    allowedMethods.add(HttpMethod.POST);
    allowedMethods.add(HttpMethod.OPTIONS);
    allowedMethods.add(HttpMethod.DELETE);
    allowedMethods.add(HttpMethod.PATCH);
    allowedMethods.add(HttpMethod.PUT);
    router.route().handler(CorsHandler.create(".*.")
            .allowCredentials(true)
            .allowedMethods(allowedMethods)
            .allowedHeaders(allowedHeaders));
}

But as I've said it wasn't enough. Then I enabled a logging for all routes:

    private void initApiLogging(Router router) {
    router.route().handler(event -> {
        String headers = "";
        if (event.request().headers() != null
                && !event.request().headers().isEmpty()) {
            headers = event.request().headers().entries().toString();
        }
        LOGGER.debug("{} request to {}, headers: {}",
                event.request().method(),
                event.request().absoluteURI(),
                headers);
        event.next();
    });
}

router.route().failureHandler(this::exceptionHandler);

AND for the whole http server:

private HttpServer server;

server.exceptionHandler(ex -> {
LOGGER.error("Http server exception handler.", ex);
});

And then during the cors error I got error message: io.netty.handler.codec.TooLongFrameException: HTTP header is larger than 8192 bytes. So increasing this value solved the issue.

    HttpServerOptions options = new HttpServerOptions();
    options.setMaxHeaderSize(1024 * 16);
    server = vertx.createHttpServer(options);

NOTE: If you are using AWS S3 too in your environment, then don't forget to add cors configuration for it:

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
    <AllowedOrigin>https://you-domain-here.com</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <AllowedMethod>HEAD</AllowedMethod>
    <AllowedMethod>POST</AllowedMethod>
    <AllowedMethod>DELETE</AllowedMethod>
    <AllowedMethod>PUT</AllowedMethod>
    <MaxAgeSeconds>3000</MaxAgeSeconds>
    <AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>

Hope it was helpful!

Upvotes: 4

RudyD
RudyD

Reputation: 747

I had the same problem and was finally able to solve it. You will need to provide a valid regex String to CorsHandler.create("Regex String Here"). So if you want to allow any protocol:host:port, aka "*", through CORS handling you can use.

router.route().handler(CorsHandler.create(".*.")  //note the "." surrounding "*"

If you want a fine-grained control of allowed protocol:host:port, you have flexibility with the Regex String. Example: CORS handling for either http:// or https:// from localhost and any port will look like this:

router.route().handler(CorsHandler.create("((http://)|(https://))localhost\:\d+")  
.allowedMethod(HttpMethod.GET)
.allowedMethod(HttpMethod.POST)
.allowedMethod(HttpMethod.OPTIONS)
.allowCredentials(true)
.allowedHeader("Access-Control-Allow-Method")
.allowedHeader("Access-Control-Allow-Origin")
.allowedHeader("Access-Control-Allow-Credentials")
.allowedHeader("Content-Type"));  //makes sure you add other headers expected in the request

To allow a list of specific clients only, you can concatenate with an OR operator "|" in your regex string.

router.route().handler(CorsHandler.create("http://localhost:8080" | "https://128.32.24.45:\\d+"))

Upvotes: 5

Saurav Dangol
Saurav Dangol

Reputation: 904

The problem was solved by adding withCredential flag to true inside the router handler.

My code is as follows

router.route().handler(io.vertx.rxjava.ext.web.handler.CorsHandler.create("https://login.com")
.allowedMethod(io.vertx.core.http.HttpMethod.GET)
.allowedMethod(io.vertx.core.http.HttpMethod.POST)
.allowedMethod(io.vertx.core.http.HttpMethod.OPTIONS)
.allowCredentials(true)
.allowedHeader("Access-Control-Allow-Method")
.allowedHeader("Access-Control-Allow-Origin")
.allowedHeader("Access-Control-Allow-Credentials")
.allowedHeader("Content-Type"));

previously it was set only in the response header

if (ar.succeeded()) {
routingContext.response().setStatusCode(200).setStatusMessage("OK")
.putHeader("content-type", "application/json")
.putHeader("Access-Control-Allow-Origin", "https://login.com")
.putHeader("Access-Control-Allow-Methods","GET, POST, OPTIONS")
.putHeader("Access-Control-Allow-Credentials", "true")
.end(
Json.encodePrettily(ar.result().body())
//(String) ar.result().body()
);
routingContext.response().close();

Upvotes: 4

Karol Klepacki
Karol Klepacki

Reputation: 2098

If your client connects to server, just add your client address in

.putHeader("Access-Control-Allow-Origin", "*")

(instead of *)

Therefore you'll be able to send request withCredentials from your browser.

Upvotes: 2

Alexey Soshin
Alexey Soshin

Reputation: 17721

That's because order matters when defining routes. Simply switch between your CORS and BodyHandler:

Router router = Router.router(vertx);
router.route().handler(io.vertx.rxjava.ext.web.handler.CorsHandler.create("*")
.allowedMethod(io.vertx.core.http.HttpMethod.GET)
.allowedMethod(io.vertx.core.http.HttpMethod.POST)
.allowedMethod(io.vertx.core.http.HttpMethod.OPTIONS)
.allowedHeader("Access-Control-Request-Method")
.allowedHeader("Access-Control-Allow-Credentials")
.allowedHeader("Access-Control-Allow-Origin")
.allowedHeader("Access-Control-Allow-Headers")
.allowedHeader("Content-Type"));
router.route().handler(BodyHandler.create());

Upvotes: 8

Related Questions