CORS issue in vertx Application not working
Asked Answered
L

5

5

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();
Lapides answered 17/8, 2016 at 12:9 Comment(3)
Have you seen #33125125 question?Marquise
@Karol I tried but it throws error. failed: Error during WebSocket handshake: Unexpected response code: 400Lapides
What WebSocket has to do with this HTTP requests? :)Marquise
L
4

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();
Lapides answered 22/8, 2016 at 7:44 Comment(1)
why did you add .allowedHeader("Access-Control-Allow-Method") .allowedHeader("Access-Control-Allow-Origin") .allowedHeader("Access-Control-Allow-Credentials") => because allowedHeader() is what Browser uses to check HTTP request header before sending it from the origin Domain to the host domain. It made me confused when I read your codeBenildas
B
8

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());
Bendick answered 17/8, 2016 at 16:23 Comment(2)
If you set "Access-Control-Allow-Credentials" on the server, then you shouldn't remove withCredentials in the client. #24687813Bendick
your suggestion helped & I have posted my answer.Lapides
V
5

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+"))
Vinculum answered 6/8, 2019 at 18:38 Comment(1)
I have done with it. don't need to add .allowedHeader("Access-Control-Allow-Method") .allowedHeader("Access-Control-Allow-Origin") .allowedHeader("Access-Control-Allow-Credentials")Benildas
L
4

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();
Lapides answered 22/8, 2016 at 7:44 Comment(1)
why did you add .allowedHeader("Access-Control-Allow-Method") .allowedHeader("Access-Control-Allow-Origin") .allowedHeader("Access-Control-Allow-Credentials") => because allowedHeader() is what Browser uses to check HTTP request header before sending it from the origin Domain to the host domain. It made me confused when I read your codeBenildas
T
4

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!

Tressatressia answered 26/2, 2020 at 13:58 Comment(0)
M
2

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.

Marquise answered 19/8, 2016 at 8:11 Comment(1)
I have implemented your suggestion and also have updated the question according to it. Please take a look.Lapides

© 2022 - 2024 — McMap. All rights reserved.