jetty / jetty.project

Eclipse Jetty® - Web Container & Clients - supports HTTP/2, HTTP/1.1, HTTP/1.0, websocket, servlets, and more
https://eclipse.dev/jetty
Other
3.79k stars 1.91k forks source link

DefaultServlet configured for a subdir responds with 404 on existing files #11884

Open gluser1357 opened 4 weeks ago

gluser1357 commented 4 weeks ago

Jetty Environment: ee10 (Jetty 12.0.10) Java version: OpenJDK 17.0.3 OS: Windows 10

With Jetty 12 we have (in contrast to former Jetty versions like 10 and likely also 11) the following issue with ee10 that static files wont be served when the DefaultServlet is configured only for a subpath:

Configuration:

web.xml:

<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>/doc/*</url-pattern>
</servlet-mapping>

Code:

WebAppContext webappContext = new WebAppContext();
webappContext.setContextPath("/mywebapp");
webappContext.setBaseResourceAsString("c:/my/folder");
Server server = new Server(8080); 
server.setHandler(webappContext);
server.start();

Data:

Effects:

It seems that the file locations are not resolved correctly if the DefaultServlet is configured for a subpath only (via doc/* in web.xml). Is this a possible bug in ee10's DefaultServlet?

joakime commented 4 weeks ago

Since you are using Windows ... The String you use for webappContext.setBaseResourceAsString(...) must be accurate and not produce an alias.

Differences in case will trigger all kinds of security and resource alias issues. This is especially true for a DefaultServlet that is mapped to / (the default url-pattern), or using the name "default" which is a special name for the Servlet spec defaults.

Do this ...

Path base = Path.of("c:/my/folder");
String baseStr = base.toRealPath().toString(); // use the filesystem specific naming and case for this reference.
webappContext.setBaseResourceAsString(baseStr);
gluser1357 commented 4 weeks ago

Thanks. I tried this, but it makes no difference. Just want to add that the web.xml contains no further servlets or filters, and also the directory doesn't contain symlinks.

What else could cause this issue?

joakime commented 4 weeks ago

What else could cause this issue?

Alias issues probably. Try this, if it works, then you have still have security / alias issues to identify and resolve.

webappContext.clearAliasChecks();
joakime commented 4 weeks ago

Also, turn on server dump and see if the changes you made in the web.xml are being applied. (namely that the servlet "default" exists and is mapped according to what you want/need)

server.setDumpAfterStart(true);
server.start();
gluser1357 commented 4 weeks ago

Thanks for your help! Obviously there are no alias issues and no problem with Windows, but dumping helped:

Using WebAppContext (see the (adapted) configuration above) - does not work, the dump says:

servlets oeje10s.ServletHandler@56380231{STARTED} size=2
    default==org.eclipse.jetty.ee10.servlet.DefaultServlet@5c13d641{...}
    jsp==org.eclipse.jetty.ee10.servlet.NoJspServlet@19c47{...}
servletMappings oeje10s.ServletHandler@56380231{STARTED} size=3
    [/]=>default
    [*.jsp, *.jspf, *.jspx, *.xsp, *.JSP, *.JSPF, *.JSPX, *.XSP]=>jsp
    [/doc/*]=>default

Using ServletContextHandler (without web.xml) - does work:

ServletContextHandler context = new ServletContextHandler();
context.setContextPath("/" + webappName);
context.setBaseResourceAsString(resourceBase);

ServletHolder holderDoc = new ServletHolder("doc", DefaultServlet.class);
holderDoc.setInitParameter("resourceBase", new File(resourceBase, "doc").toURI().toASCIIString());
context.addServlet(holderDoc, "/doc/*");

Server server = new Server(port);
server.setHandler(context);
server.start();

The dump says:

servlets oeje10s.ServletHandler@299266e2{STARTED} size=2
  doc==org.eclipse.jetty.ee10.servlet.DefaultServlet@18538{...)
    initParams size=1
      resourceBase=file:/C:/my/folder/doc
  org.eclipse.jetty.ee10.servlet.ServletHandler$Default404Servlet-363f6148==...
servletMappings oeje10s.ServletHandler@299266e2{STARTED} size=2
  [/doc/*]=>doc
  [/]=>org.eclipse.jetty.ee10.servlet.ServletHandler$Default404Servlet-363f6148

When comparing both dumps,

My remaining questions are:

janbartel commented 3 weeks ago

@gluser1357 actually, the DefaultServlet is not really what you want. The DefaultServlet is intended to be the servlet of last resort for a web container, to be used when nothing else matches. What you're trying to do is to serve static content from a particular disk location. So what you really want is a "ResourceServlet". We have an open issue for that here: https://github.com/jetty/jetty.project/issues/10738. In past versions of jetty, people were misusing the DefaultServlet to achieve this static resource serving from servlet mappings other than the default /, and the code became a real mess (the pathInfoOnly setting is a nightmare to get right, especially in a general-purpose way that also works with forwards/includes, redirects etc). We are trying to clean this up in ee10, but haven't got the ResourceServlet ready yet. In the meanwhile, could maybe try configuring a ResourceHandler to serve your static content instead? See https://jetty.org/docs/jetty/12/programming-guide/server/http.html#handler-use-resource

gluser1357 commented 3 weeks ago

@janbartel thank you for clarification. Following the example at https://github.com/jetty/jetty-examples/blob/12.0.x/embedded/ee10-file-server/src/main/java/examples/ServletFileServerMultipleLocations.java and adapting it led to the working ServletContextHandler-based solution above which requires (Jetty-related) programmatic configuration of servlets, filters and context listeners. Now, in our context, we use Tomcat for production and Jetty for local and CI testing based on a general web.xml configuration that can be used one-to-one for both Tomcat and Jetty. Using a ResourceHandler seems to be, at first glance, just another (Jetty-specific) programmatic approach which - in our case - may not have advantages over the ServletContextHandler-based solution above. Or do I miss something? The ResourceServlet - sounds also interesting. Is it Jetty-specific?

Concerning the other related question above, could you give me also some feedback? That would be really great.

janbartel commented 3 weeks ago

My remaining questions are:

  • What do I have to adapt in web.xml and code to get my case working again with WebAppContext in Jetty 12? When I remember right then with Jetty 10 (and 11) the behaviour was different (and worked).

DefaultServlet will not work the way you want in ee10 as it expect that it is at the root of the document folder.

  • Does Jetty provide a way to read the web.xml (including all the servlet and filter definitions) of a project with ServletContextHandler, or is WebAppContext required for that?

You need WebAppContext.

  • Does Jetty provide a way to scan for @ServletFilter, @ContextListener and @webfilter annotations in the codebase with ServletContextHandler, or is WebAppContext required for that?

You need WebAppContext.

  • For the Maven case for WebAppContext - is it required to always re-build the webapp into target/mywebapp and restart the Jetty server after making changes, or is there possibly also a way to directly use target/classes, src/main/webapp/WEB-INF/web.xml and the compiled workspace resolution files, ideally without restarting?

This is what the jetty-maven-plugin is for.

  • For the Maven case for ServletContextHandler - is it generally required to restart the Jetty server after changing code or web.xml content?

If you're not using the jetty-maven-plugin then I guess normal maven class loading rules apply.

gluser1357 commented 3 weeks ago

@janbartel Thank you for referencing jetty-maven-plugin! That sounds great and I will test it.

We still look for a container-independent solution serving all url endpoints (url-pattern /*) by a certain servlet (in our case Jersey) except static content under (url-pattern /doc/*) that could be served so far by the DefaultServlet. Is the planned ResourceServlet the way to go, or are there alternatives not requiring Jetty-specific programmatic adaptions?