Skip to content
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

Issue #11494 - PathMappingsHandler exposes PathSpec and Context based on PathSpec. #11497

Open
wants to merge 4 commits into
base: jetty-12.0.x
Choose a base branch
from

Conversation

joakime
Copy link
Contributor

@joakime joakime commented Mar 7, 2024

  • Introduces Context.Wrapper
  • Introduces PathMappingsHandler.PathSpecRequest (wrapper, with custom Context)

Fixes: #11494

@joakime joakime requested a review from gregw March 7, 2024 15:39
@joakime joakime self-assigned this Mar 7, 2024
Copy link
Contributor

@gregw gregw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the direction this is going.... but a few niggles/questions....

I'd also like to consider the option of setting the contexts base resource with the mapping... as then all the mappings could be to the same ResourceHandler and it would just use the context to find the mapping!

Comment on lines +120 to +121
PathSpecRequest pathSpecRequest = new PathSpecRequest(request, pathSpec);
boolean handled = handler.handle(pathSpecRequest, response, callback);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we make this optional? Perhaps not all usages will want the context set... specially if the handler mapped is itself a ContextHandler. Or maybe this is in a PathMappingsHandler.Contextual subclass?

super(request);
this.pathSpec = pathSpec;
setAttribute(PathSpec.class.getName(), this.pathSpec);
this.context = new Context.Wrapper(request.getContext())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be good to reuse this wrapper for every mapping to the same context. ... but maybe don't do this yet... just keep it in mind whilst we work out the other details.

Comment on lines 146 to 150
public String getPathInContext(String canonicallyEncodedPath)
{
MatchedPath matchedPath = pathSpec.matched(canonicallyEncodedPath);
return matchedPath.getPathInfo();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is correct. I think you can just use the default method.

if (LOG.isDebugEnabled())
LOG.debug("Matched {} to {} -> {}", pathInContext, matchedResource.getPathSpec(), handler);
boolean handled = handler.handle(request, response, callback);

PathSpecRequest pathSpecRequest = new PathSpecRequest(request, pathSpec);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the contextualization of a request make sense for non prefix matches? Perhaps this is better in a subclass that only allows prefix patterns?

@gregw
Copy link
Contributor

gregw commented Mar 9, 2024

@joakime maybe implement it like:

public class PathMappingsHandler
{
    // ...

    public static class Contextual extends PathMappingsHandler
    {
        @Override
        public void addMapping(PathSpec pathSpec, Handler handler)
        {
            addMapping(pathSpec, null, handler);
        }

        public void addMapping(PathSpec pathSpec, Resource baseResource, Handler handler)
        {
            switch (pathSpec.getGroup())
            {
                case PREFIX_GLOB, ROOT, EXACT, DEFAULT ->
                {
                }
                default -> throw new IllegalArgumentException("Only prefix patterns");
            }
            ContextHandler context = new ContextHandler(pathSpec.getPrefix());
            if (baseResource != null)
                context.setBaseResource(baseResource);
            context.setHandler(handler);
            super.addMapping(pathSpec, context);
        }
    }
}

So it ends up being a convenience class that wraps passed handlers with our existing ContextHandler? This will dump better and work if code looks for ContextHandlers

@gregw
Copy link
Contributor

gregw commented Mar 11, 2024

@joakime thoughts?

@joakime
Copy link
Contributor Author

joakime commented Mar 11, 2024

The truth of the context-path (and path info) is from the PathSpec, and while we have our own, and can make some good guesses on how it should behave, we are also making those assumptions based on what we experience.

We shouldn't assume that the pathInfo is always what's left over from the context-path.
We shouldn't even assume that the context-path is the first portion of the path.
The truth comes from the combination of the PathSpec and the resulting MatchedPath produced from the canonical path.

What if someone has the following RegexPathSpec ?

pathMappingsHandler.addMapping(new RegexPathSpec("^/rest/(?<name>.*)/ver-[0-9]+/(?<info>.*)$"), new ContextDumpHandler());

That will return on the input path /rest/api/users/ver-1/groupfoo/baruser :

  • contextPath = api/users
  • pathInContext = groupfoo/baruser

I added this example to the testcases in this PR.

@gregw
Copy link
Contributor

gregw commented Mar 11, 2024

@joakime

That will return on the input path /rest/api/users/ver-1/groupfoo/baruser :

  • contextPath = api/users
  • pathInContext = groupfoo/baruser

I added this example to the testcases in this PR

I think that breaks a fundamental invariant that we have (and probably should better document): HttpURI.path == contextPath + pathInContext.
This is a subset of the servlet spec invariant that HttpURI = contextPath + servletPath + pathInfo.

If we do not respect these invariants, then things will break. So we cannot use RegEx matching to determine a contextPath unless it is in the PREFIX group and meets the invariant.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: 👀 In review
Development

Successfully merging this pull request may close these issues.

PathMappingsHandler exposes PathSpec and Context based on PathSpec
2 participants