-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Inconsistent handling of welcome files between Jetty 10 and 12 #9910
Comments
This is the place where either
|
@janbartel and I have had a look at this one. I think the problem is that currently in ee10 DefaultServlet, we work out the The fix is that once we have worked out the content that we wish to serve, then the request wrapper we make should have the correct pathInContext to represent that resource. So we need to pass that info into the constructor of I think @janbartel has a test harness that reproduces this problem and will soon make a branch. @lorban as we'd like you to take the lead with resources, can you look into this one..... however, I think @lachlan-roberts also did some work on this area, so you might like to delegate and/or collaborate with him on this? |
@lorban I've checked in a branch |
I've added a review comment under #9934, because I believe |
@grgrzybek what's your reasoning there? |
My reasoning is that Jetty 10 (and I think 11 too) is handling welcome files + "path info only" by delegating to request dispatcher for I added more details under #9934. |
@grgrzybek I can't see anything extra under #9934? |
Hmm, maybe it's not starting the review for closed issue? Here it is: @lorban @gregw @janbartel I think #9910 is not properly handled here - the behavior is different than in Jetty 10. I've just checked with Jetty 12 SNAPSHOT. In Jetty 10 (using servlet names from this
if (welcome != null)
{
String servletPath = included ? (String)request.getAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH)
: request.getServletPath();
if (_pathInfoOnly)
welcome = URIUtil.addPaths(servletPath, welcome);
In Jetty 12 with this #9934 fix:
|
I'm currently on a train, so will look properly a bit later on. However, the jetty-12 behaviour now seems correct to me. I don't think that jetty-10 should be returning a 404 when there is a servlet mapping for *.y and *.x. |
I agree that this is not an obvious case. It's not about
Chapter There indeed is a mapping for The point is that in Jetty 10, The path info only is something Jetty specific and isn't covered by the spec. So I could only consider it comparing to what was in Jetty 9 and 10 (and probably 11 too). |
I think in jetty-10 we weren't really handling the |
Tomorrow I'll check how I test this in Pax Web for Tomcat and Undertow ;) Thanks for your patience! |
I have 3 matching tests called
These tests:
In Jetty 9, Jetty 10, Tomcat 10.1 and Undertow 2.3 the scenario with welcome files works in the same way - Only Jetty 12 works differently... If you have time and are interested in this issue, here are the steps to check:
The test should fail at this assertion: // "/r" - no "/index.x" or "/index.y" physical resource, but existing mapping for *.y to indexx servlet
// forward is performed implicitly by Jetty's DefaultServlet (even if mapped to /r/*), forward URI is
// "/r/index.y" (first welcome), but this time, "/r/*" is a mapping with higher priority than "*.y"
// (with "/" servlet, "*.y" had higher priority than "/"), so "resource" servlet is called, this time
// with full URI (no welcome files are checked). Such resource is not found, so we have 404
response = send(port, "/c/r/");
// TODO: https://github.com/eclipse/jetty.project/issues/9910
assertTrue(response.startsWith("HTTP/1.1 404")); Similar pax-web-tomcat and pax-web-undertow tests work fine. |
@grgrzybek I've had a look at your test cases and code. It seems that Tomcat doesn't support the I think this was to fix the path to which the welcome would be forwarded. Bearing in mind that the I don't think jetty-10 was correct to re-add the In jetty-12, if we have matched a welcome, then we use the original In the case of a static resource, we will forward to In the case where we matched a servlet as the welcome, and it is not this servlet, we will forward you to the reduced As I think forwarding to So in short, I think jetty-10 was wrong, and we've got it right in jetty-12 :) If we proceed with jetty-12 ee10 the way it is, what is the impact on your project? In other words, how heavily do you rely on the jetty-10 (incorrect IMHO) behaviour for correct functioning? Is there any other way you could achieve the same function without relying on that behaviour? |
Thanks @janbartel for detailed answer. I'll check everything again. One more think I didn't mention - in Pax Web I have to set |
@grgrzybek There surely should be a more standard way of achieving what you want rather than extending the default servlet of every container. If I'm understanding correctly you have static content that is at Surely a better way to do this would be for the servlet at |
Sure sure @gregw ;) I'm aware that my Pax Web Test case is very artificial, Servlet spec doesn't say very much about welcome pages (except basic info) and OSGi CMPN specifications say zero. And I extend Jetty/Tomcat/Undertow "default servlets" definitely not to change their behavior but to cover some corner cases (like handling "dir" entries for So I'll check again tomorrow. I simply didn't feel comfortable seeing differences between Jetty 10/11 and 12. I had to react - I hope you don't mind ;) |
According to
But each of the 3 components has some variants:
According to
So with this rule, if we have non root context and But back to Jetty 12 vs Jetty <12, Tomcat and Undertow case.
If we analyze from the start, let's check javadoc for
If we analyze only this statement, I'd say:
OK, using only the last point and root context (because it's not relevant here), we have simple resource-related scenario:
Finally let's add "welcome file servlets" to the analysis. Mind that Servlets specification doesn't include Dissecting
This quote confirms the expected behavior when default servlet is mapped to
It doesn't mention
Again, default servlet is assumed here. Also "partial request" is full Finally:
And after this very long (and hopefully interesting when reading in the future) analysis, I think you're right @janbartel:
Thanks for your patience ;) |
Yeah the servlet specification matching algorithms show you that evolution is a weird thing, specially when you start with something non-optimal!
Thanks for the extra detail. But I still need a bit more to fully understand what we should do... Assuming context of "/ctx" and that you have a DefaultServlet mapped to "/s/*", plus an "index.xyz" welcome file. Then the issue is what to do with a request to something like "/ctx/s/dir/"?
So in my reading of the code, we are inconsistent in what we return from "getWelcomeTarget": sometimes it has the servletPath, sometimes it does not. It is also hard to see how this string can be used independently of the welcome file mode if the mode is SERVE, then we have already added the welcome to the Content resource and found the welcome resource... we then throw that resource away, returning a string that will be passed to getResource(String) to get it again... but the same string will be used to do a redirect in different mode... so it can be in two different spaces and can't be right for both. Can we even support servlet welcome files if the mode is SERVE? Should we force the mode to REHANDLE if the found welcome is a servlet? I don't see how we can just return a single string without the mode and expect it to work? Perhaps we should return a record that contains:
Anyway, I think we still have work to do here to clean this up.... unfortunately it will not make beta3, so we might need a beta4 or sneak something in for 12.0.0 |
@lorban TL;DR; summary of above: The
|
I don't exactly understand found in the getResource... My understanding of the logic is: resource existence phase of welcome file resolution:
servlet mapping phase of welcome file resolution:
and actually I agree that Jetty 12 is fine - with:
Jetty 12 does this:
While in Jetty <=11, the last point is:
This is the biggest difference and my initial concern. In Pax Web I assumed:
From my (user) perspective, I think
It has to effectively behave like this:
(that's working description of course) And somehow it has to be emphasized that in Jetty <=11, it wasn't consistent. In yet another words, there are two stages of welcome file resolution:
In Jetty 12, pathInfoOnly=true is used in both stages. |
@grgrzybek is that a good summary of what you are saying?
|
@lorban that's what seems to be the best summary ;) I don't want to decide about Jetty 11 - pathInfoOnly is proprietary and I can't estimate the impact of the change for older Jetty versions. |
I don't think 12 is consistent either, as per my previous comment. We definitely need to clean this feature up and make it consistent. I propose that we proceed by making a very exhaustive unit test that checks:
We can then review the expected results and agree what they should be. Only then do we update the implementation to make the test pass. |
@lorban I think 12 is correct only in some combinations. I'm pretty sure it is inconsistent if we give it other combinations. See my proposed unit test above, as I think we need that to get some clarity. Can you write that? |
I personally declare that every morning, when I don't do other tasks, I will fetch |
@gregw I'm on it. |
I've just pushed the changes after adjustments with Jetty 12 changes (see the comments in the class). Currently I marked the failing assert with: // >>> current failure (HTTP 500 is only when the resource doesn't exist) <<< While I adjusted the scenario where the forward is for As you can see the key part of my test is the "gateway" servlet which performs forward/include based on query params. If you like, please take this test simply replacing: JettyResourceServlet jrs1 = new JettyResourceServlet(p1, null); with (fqcn is DefaultServlet jrs1 = new DefaultServlet(p1, null); |
For example, when fetching if (isPathInfoOnly() && !isIncluded(getServletRequest(coreRequest)))
welcomeTarget = URIUtil.addPaths(getServletRequest(coreRequest).getPathInfo(), welcome); and but when I use |
Signed-off-by: Ludovic Orban <[email protected]>
Signed-off-by: Ludovic Orban <[email protected]>
Signed-off-by: Ludovic Orban <[email protected]>
Signed-off-by: Ludovic Orban <[email protected]>
Signed-off-by: Ludovic Orban <[email protected]>
Signed-off-by: Ludovic Orban <[email protected]>
Signed-off-by: Ludovic Orban <[email protected]>
Signed-off-by: Ludovic Orban <[email protected]>
* Added DefaultServlet combinations tests. * Removed pathInfoOnly handling present in Jetty 11, because it is always true for mapping to "/", and always false for other mappings. Signed-off-by: Ludovic Orban <[email protected]>
Jetty version(s)
12.0.0.beta1
Java version/vendor
(use: java -version)
OS type/version
Description
When working on Pax Web welcome file handling, I found some differences between Jetty 10 and Jetty 12.
How to reproduce?
I have a quite complicated setup related to welcome file handling. For Pax Web 9 / Jetty 10 it is:
https://github.com/ops4j/org.ops4j.pax.web/blob/web-9.0.9/pax-web-jetty/src/test/java/org/ops4j/pax/web/service/jetty/internal/UnifiedJettyTest.java#L659
Pax Web provides a copy of Jetty's DefaultServlet just to unprivate some methods and uses its extension called
org.ops4j.pax.web.service.jetty.internal.web.JettyResourceServlet
which is more aware of OSGi resource handling.In this setup (
paxWebWelcomePagesWithDifferentContext()
test) I have:org.eclipse.jetty.ee10.servlet.ServletContextHandler
with/c
context path and{ "index.y", "index.x" }
welcome filesorg.eclipse.jetty.ee10.servlet.DefaultServlet
mapped to/
(redirectWelcome=false, welcomeServlets=true, pathInfoOnly=false, baseResource="target/b1")org.eclipse.jetty.ee10.servlet.DefaultServlet
mapped to/r/*
(redirectWelcome=false, welcomeServlets=true, pathInfoOnly=true, baseResource="target/b2")org.eclipse.jetty.ee10.servlet.DefaultServlet
mapped to/s/*
(redirectWelcome=true, welcomeServlets=true, pathInfoOnly=true baseResource="target/b3"){ "*.x", "*.y" }
to print out values of attributes likejakarta.servlet.forward.request_uri
/gateway/*
which can be controlled to do redirect, forward or includeWhile almost everything works like it worked with Jetty 10, there's a difference when handling
/c/r/
URI.In Jetty 10:
/r/*
is invoked firstservletPath
is set to/
because pathInfoOnly is truejavax.servlet.http.HttpServletRequest#getPathInfo()
returns/
pathInContext = URIUtil.addPaths(servletPath, pathInfo);
is/
because both servletPath and pathInfo are/
org.eclipse.jetty.http.HttpContent.ContentFactory#getContent()
returns a resource which is directory, soorg.eclipse.jetty.server.ResourceService#sendWelcome()
is calledorg.eclipse.jetty.server.ResourceService.WelcomeFactory#getWelcomeFile()
is called with/
_welcomes == ["index.y", "index.x"]
, but there are no such resources, soorg.eclipse.jetty.servlet.ServletHandler#getMatchedServlet()
is called with/index.y
becauseURIUtil.addPaths(pathInContext, s)
returned/index.y
for/
andindex.y
{ "*.x", "*.y" }
is returned.In Jetty 12:
/r/*
is invoked firstorg.eclipse.jetty.ee10.servlet.DefaultServlet#doGet()
:isPathInfoOnly()
returns true (as configured)jakarta.servlet.http.HttpServletRequest#getPathInfo()
returns/
(as in Jetty 10)URIUtil.encodePath(req.getPathInfo())
returns/
(correct)org.eclipse.jetty.server.ResourceService#getContent()
returns a directory resource (correct)org.eclipse.jetty.server.ResourceService#doGet()
continues and because the content is directory,org.eclipse.jetty.server.ResourceService#sendWelcome()
is calledorg.eclipse.jetty.server.ResourceService#welcome()
is calledorg.eclipse.jetty.server.ResourceService#processWelcome()
is calledorg.eclipse.jetty.server.ResourceService.WelcomeFactory#getWelcomeTarget()
is calledrequest.getPathInfo()
is/
(ok)org.ops4j.pax.web.service.jetty.internal.web.DefaultServlet#isPathInfoOnly()
returns true (as configured)requestTarget
is set to/
pathInContext
is set to the result oforg.eclipse.jetty.server.Request#getPathInContext()
and it's/r/
URIUtil.addPaths(pathInContext, welcome)
returns/r/index.y
for the first welcome file instead of expected/index.y
So while Jetty 10 consistently uses "path info" (full URI minus context path minus servlet path), in Jetty 12, the
path in context
is different in various places:org.eclipse.jetty.ee10.servlet.DefaultServlet#doGet()
,encodedPathInContext
is set to the result ofURIUtil.encodePath(req.getPathInfo())
which is/
(because pathInfoOnly is set to true and "path info" is/
)org.ops4j.pax.web.service.jetty.internal.web.DefaultServlet.ServletResourceService#getWelcomeTarget()
,pathInContext
varilable is set to the result ofRequest.getPathInContext(coreRequest)
which doesn't bother with the setting ofpathInfoOnly
.requestTarget
variable is correct:requestTarget = isPathInfoOnly() ? request.getPathInfo() : pathInContext
, but the welcome file is calculated usingString welcomeInContext = URIUtil.addPaths(pathInContext, welcome)
- IMO,requestTarget
should be used here instead ofpathInContext
.The text was updated successfully, but these errors were encountered: