How do I configure embedded Jetty to handle OPTIONS preflight requests?

10,155

Solution 1

Documentation for CrossOriginFilter:

http://www.eclipse.org/jetty/documentation/current/cross-origin-filter.html

Javadoc for CrossOriginFilter:

http://download.eclipse.org/jetty/stable-9/apidocs/org/eclipse/jetty/servlets/CrossOriginFilter.html

Actual Source Code: (sometimes this helps people understand too):

https://github.com/eclipse/jetty.project/blob/jetty-9.2.3.v20140905/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CrossOriginFilter.java

In short, you'll likely want to add OPTIONS to the allowed methods.

(Just like the javadoc says)

FilterHolder holder = new FilterHolder(new CrossOriginFilter());
holder.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "GET,POST,HEAD,OPTIONS");
appHandler.addFilter(holder, "/*", EnumSet.of(DispatcherType.REQUEST));

Now, to address another bug you have ...

This does nothing ...

holder.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_CREDENTIALS_HEADER,
  "true");

That is not an init parameter key. (In fact that is a header name constant for the Access-Control-Allow-Credentials) if you want to allow credentials, then do as the javadoc says.

holder.setInitParameter(CrossOriginFilter.ALLOW_CREDENTIALS_PARAM, "true");

Solution 2

I solved this issue by using the following configuration for the FilterHolder:

FilterHolder cors = new FilterHolder(CrossOriginFilter.class);
cors.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM, "*");
cors.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, "*");
cors.setInitParameter(CrossOriginFilter.ALLOWED_METHODS_PARAM, "OPTIONS,GET,POST,HEAD");
cors.setInitParameter(CrossOriginFilter.ALLOWED_HEADERS_PARAM, "X-Requested-With,Content-Type,Accept,Origin,Cache-Control");
cors.setInitParameter(CrossOriginFilter.CHAIN_PREFLIGHT_PARAM, "false");

Chrome sends a "Cache-Control" header, if you do not allow this header with your CORS filter, then the OPTIONS request will not be responded to with the correct headers. Most examples of the CrossOriginFilter online do not include this header.

You can optionally set CHAIN_PREFLIGHT_PARAM to false (default is true). If you set it to false, the filter will respond to the request without sending the request to the Servlet. If you would like to handle the OPTIONS request yourself, you do not need to set this param.

Share:
10,155
Jer
Author by

Jer

Updated on July 13, 2022

Comments

  • Jer
    Jer almost 2 years

    I am working on a project which is using embedded Jetty (unfortunately I just "inherited" the server side of the project and am not very familiar with the use of Jetty and its configuration).

    An odd case just popped up - I'll do my best to describe:

    A web-based UI (using AngularJS, from a different domain, so CORS is used) sends a POST request to change the state of something on the server. This worked at some point in the past (it was last used probably a month or so ago).

    Yesterday this stopped working. Inspecting the REST calls, I saw that an OPTIONS request is first being made. The content type of the POST is application/json, so based on what I have read, this is correct. I am not sure why it was previously not sent - it is possible that the company had its version of Chrome updated recently, and the old version did not send preflight requests, but that's just speculation. In any case, here's what I think is the relevant code in my application for configuring Jetty for CORS:

    FilterHolder holder = new FilterHolder(new CrossOriginFilter());
    holder.setInitParameter(CrossOriginFilter.ALLOWED_ORIGINS_PARAM,  "*");
    holder.setInitParameter(CrossOriginFilter.ACCESS_CONTROL_ALLOW_CREDENTIALS_HEADER, "true");
    appHandler.addFilter(holder, "/*", EnumSet.of(DispatcherType.REQUEST));
    

    Everything works fine for POST requests. I can verify this by starting Chrome with the --disable-web-security flag. No OPTIONS request is sent, and the POST works as it should.

    My thinking is that since it works for the POST, it's not an authorization or security issue - it's just that Jetty is not configured properly to handle the preflight request (it just returns 401).

    I can't find much documentation for embedded Jetty, and which of the CrossOriginFilter constants to use as property keys in the calls to setInitParameter (and furthermore, since the 2nd argument to that method call is a String, I really have no idea how to format the values).

    What parameters should I be setting on CrossOriginFilter to property handle OPTIONS requests? And if I have said anything erroneous above or made any false assumptions, please correct me! I've got very limited experience with this.

  • Jer
    Jer over 9 years
    Thanks - I had found that but I'm really still clueless about why I'm getting a 401 in response to the OPTIONS request.
  • Jer
    Jer over 9 years
    I'm not sure if technically the OPTIONS request falls under the "umbrella" of CORS, but the OPTIONS request is still coming from a different domain, of course (since the POST is attempted from a different domain). I'm not sure what that code is supposed to show. It's just saying that in order to be a preflight request, the method must be OPTIONS. I might be missing something but nothing is being rejected - if anything, everything but OPTIONS is being rejected.
  • Jer
    Jer over 9 years
    Sorry, I didn't include enough code. appHandler is the ServletContextHandler it seems: ServletContextHandler appHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
  • daka
    daka about 6 years
    You can use CrossOriginFilter.ALLOWED_METHODS_PARAM in place of allowedMethods