EkaSaffronGaruda
EkaSaffronGaruda

Reputation: 31

Embedded Jetty no filter named cross-origin

I am trying to implement cors in a embedded jetty server. I feel lost right now with this exception.

java.lang.IllegalStateException: No filter named cross-origin
    at org.eclipse.jetty.servlet.ServletHandler.updateMappings(ServletHandler.java:1260)
    at org.eclipse.jetty.servlet.ServletHandler.doStart(ServletHandler.java:179)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:93)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:171)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114)
    at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:89)
    at org.eclipse.jetty.server.handler.ScopedHandler.doStart(ScopedHandler.java:112)
    at org.eclipse.jetty.server.session.SessionHandler.doStart(SessionHandler.java:486)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:93)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:171)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114)
    at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:89)
    at org.eclipse.jetty.server.handler.ScopedHandler.doStart(ScopedHandler.java:112)
    at org.eclipse.jetty.server.handler.ContextHandler.startContext(ContextHandler.java:955)
    at org.eclipse.jetty.servlet.ServletContextHandler.startContext(ServletContextHandler.java:388)
    at org.eclipse.jetty.server.handler.ContextHandler.doStart(ContextHandler.java:896)
    at org.eclipse.jetty.servlet.ServletContextHandler.doStart(ServletContextHandler.java:306)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:93)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:171)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:121)
    at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:89)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:93)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.start(ContainerLifeCycle.java:171)
    at org.eclipse.jetty.server.Server.start(Server.java:469)
    at org.eclipse.jetty.util.component.ContainerLifeCycle.doStart(ContainerLifeCycle.java:114)
    at org.eclipse.jetty.server.handler.AbstractHandler.doStart(AbstractHandler.java:89)
    at org.eclipse.jetty.server.Server.doStart(Server.java:414)
    at org.eclipse.jetty.util.component.AbstractLifeCycle.start(AbstractLifeCycle.java:93)
    at org.projectsanatan.restsanatan.RestSanatan.start(RestSanatan.java:38)
    at org.projectsanatan.restsanatan.Main.main(Main.java:16)

My code is


    private void initServlets() {
        ServletContextHandler servletContextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);

        servletContextHandler.setContextPath("/");
        
        ResourceConfig resourceCfg = new ResourceConfig();
        resourceCfg.packages(BookResource.class.getPackage().getName(), "/*");
        
        ServletContainer servletContainer = new ServletContainer(resourceCfg);
        
        ServletHolder servletHolder = new ServletHolder(servletContainer);
        servletContextHandler.addServlet(servletHolder, "/api/*");
//        servletContextHandler.addFilter(null, KEY, null)
        
        FilterHolder corsFilter = new FilterHolder();
        corsFilter.setInitParameter("allowedOrigins", "https://example.com");
        corsFilter.setInitParameter("allowedMethods", "POST,GET,OPTIONS,PUT,DELETE,HEAD");
        corsFilter.setInitParameter("allowedHeaders", "X-PINGOTHER, Origin, X-Requested-With, Content-Type, Accept");
        corsFilter.setInitParameter("preflightMaxAge", "728000");
        corsFilter.setInitParameter("allowCredentials", "true");
        corsFilter.setInitParameter("chainPreflight", "false");
        
        CrossOriginFilter cors = new CrossOriginFilter();
        
        corsFilter.setFilter(cors);
        
        FilterMapping corsFilterMap = new FilterMapping();
        corsFilterMap.setFilterName("cross-origin");
        corsFilterMap.setPathSpec("/*");
        corsFilterMap.setDispatcherTypes(EnumSet.of(DispatcherType.INCLUDE, DispatcherType.REQUEST));
        
        servletContextHandler.getServletHandler().addFilter(corsFilter, corsFilterMap);
        
        this.handlerList.addHandler(servletContextHandler);
        
        this.setHandler(this.handlerList);
    }

I am not very much experienced with jetty and servlet stuff so I am confused, help would be really appreciated!

Upvotes: 2

Views: 838

Answers (1)

Joakim Erdfelt
Joakim Erdfelt

Reputation: 49462

There's a few issues.

  1. Mapping of both Servlets and Filters is done by the ServletContextHandler, not the ServletHandler.
  2. If you use a FilterHolder + Mapping with name, then you have to name your FilterHolder.
    Mapping with name is overly complicated for embedded-jetty use, it really only exists for WEB-INF/web.xml based configuration. Just use the FilterHolder directly.
  3. You are missing the required ServletContextHandler BaseResource declaration
  4. You are missing the required DefaultServlet on your ServletContext
  5. You are missing the DefaultHandler on your top level handler tree (usually set as last entry)

A correct setup would be like this ...

ServletContextHandler servletContextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
servletContextHandler.setContextPath("/");
// Base Resource is required for your setup
servletContextHandler.setBaseResource(Resource.newResource(urlToStaticContent));

ResourceConfig resourceCfg = new ResourceConfig();
resourceCfg.packages(BookResource.class.getPackage().getName(), "/*");

ServletContainer servletContainer = new ServletContainer(resourceCfg);

ServletHolder servletHolder = new ServletHolder(servletContainer);
servletContextHandler.addServlet(servletHolder, "/api/*");

FilterHolder corsHolder = servletContextHandler.addFilter(CrossOriginFilter.class,
    "/*",
    EnumSet.of(DispatcherType.INCLUDE, DispatcherType.REQUEST));

corsHolder.setInitParameter("allowedOrigins", "https://example.com");
corsHolder.setInitParameter("allowedMethods", "POST,GET,OPTIONS,PUT,DELETE,HEAD");
corsHolder.setInitParameter("allowedHeaders", "X-PINGOTHER, Origin, X-Requested-With, Content-Type, Accept");
corsHolder.setInitParameter("preflightMaxAge", "728000");
corsHolder.setInitParameter("allowCredentials", "true");
corsHolder.setInitParameter("chainPreflight", "false");

// Default Servlet is Required
ServletHolder defaultHolder = new ServletHolder("default", DefaultServlet.class);
servletContextHandler.addServlet(defaultHolder, "/"); // default url-pattern

HandlerList handlerList = new HandlerList();
handlerList.addHandler(servletContextHandler);
// Default Handler is for requests that don't match a context above
// Such as requests that don't have a path that start with "/"
// It is safe to leave active, even in production, and will help with troubleshooting issues
handlerList.addHandler(new DefaultHandler());

server.setHandler(handlerList);

The Filter mapping of your CrossOriginFilter has specified the DispatcherType set of INCLUDE and REQUEST, that's a really odd setup.

Doing CORS from an INCLUDE (or FORWARD) dispatch is super awkward, skip it.

But you didn't specify ASYNC, which is actually super common.

I would change the mapping this way ...

FilterHolder corsHolder = servletContextHandler.addFilter(CrossOriginFilter.class,
    "/*",
    EnumSet.of(DispatcherType.REQUEST, DispatcherType.ASYNC));

Things to keep in mind for using CrossOriginFilter.

  1. It will only produce HTTP Response headers based on your configuration.
  2. It does not "prevent" or "fail" any request that doesn't match your configuration.
  3. Requests without a Origin request header will not trigger the CrossOriginFilter.

Upvotes: 1

Related Questions