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

Various cleanups of Handler.insertHandler #10792

Merged
merged 15 commits into from
Nov 13, 2023

Conversation

gregw
Copy link
Contributor

@gregw gregw commented Oct 26, 2023

Fixes #9297

@gregw gregw requested review from janbartel and sbordet October 26, 2023 06:48
@gregw gregw linked an issue Oct 26, 2023 that may be closed by this pull request
Copy link
Contributor

@joakime joakime left a comment

Choose a reason for hiding this comment

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

When you run the EE10 ServletContextHandler.testReplaceHandler() in this branch you get a new WARN logging event. (should probably rename this specific test case, as we don't really do "replace" anymore in that test case)

2023-10-26 10:56:49.405:WARN :oejes.ServletContextHandler:main: ServletContextHandler.setHandler should not be called directly. Use insertHandler or setSessionHandler etc.

Looks like Handler.insertHandler() winds up calling ServletContextHandler.setHandler() which triggers this warning.

That's a bit of a confusing warning.

@sbordet
Copy link
Contributor

sbordet commented Oct 26, 2023

Also, ServletContextHandlerTest.testReplaceHandler() fails if the ServletContextHandler in that test is configured with sessions or security.

What's the right semantic for ServletContextHandler.insertHandler()?

Given SCH -- [SessionH -- SecurityH -- ServletH], then insertHandler() of H -- X -- Y should result in SCH -- H -- X -- Y -- [SessionH -- SecurityH -- ServletH]?

And if you insertHandler() again with Z, is it after Y, or before H?

If that's the case, can you write that example in the javadocs of the overridden method?

@gregw
Copy link
Contributor Author

gregw commented Oct 26, 2023

@sbordet @joakime I can't get that failure locally? I've made some changes, but it is still a bit strange. I've punted to the next release cycle.

@gregw gregw requested review from joakime and sbordet October 27, 2023 01:44
@gregw
Copy link
Contributor Author

gregw commented Oct 31, 2023

@sbordet @joakime nudge

@gregw
Copy link
Contributor Author

gregw commented Oct 31, 2023

@sbordet any chance of a re-review before you go on vacation, now that I've explained how core 2 nested Handlers work?

@gregw gregw requested a review from sbordet October 31, 2023 20:42
Copy link
Contributor

@sbordet sbordet left a comment

Choose a reason for hiding this comment

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

@gregw I am still a bit confused by SCH interaction with setHandler() and insertHandler().

I would like the following:

  • Make ServletHandler extends Handler.Abstract so that it's clear that it has no child. It still can return false, only in particular cases where users configure it to not have a DefaultServlet (in which case we just return the "core" 404).
  • SCH.setHandler() always sets the first child after SCH (like all Singleton do), overwriting the existing one, with the special handling for "the triplet", i.e. Session|Security|ServletHandler, and relinks the triplet.
  • The triplet is always last in the chain, so SCH.getTail() always returns the ServletHandler.

Some example:

sch.setHandler(gzipH);
sch.insertHandler(wsUpgradeH); // => sch-wsuh-gziph-[sessionh]-[securityh]-servleth
sch.getHandler() == wsuh
sch.getTail() == servleth

With the current PR, the code above sets up the following chain, which I think is wrong: sch-wsuh-[sessionh]-[securityh]-servleth-gziph, as we would never gzip.

With the proposal above:

sch.setHandler(gziph);
sch.setHandler(wsuh); // sch-wsu-[sessionh]-[securityh]-servleth -- gziph is lost as expected.

sch.setHandler(B);
sch.getHandler() == B; // The current PR breaks this.

I am sure the proposal above is a change to what I said earlier, but I find it clearer.
Would it work for you?

@joakime
Copy link
Contributor

joakime commented Nov 6, 2023

A quick test of this PR and it's handler tree looks like this ...

ServletContextHandler servletContextHandler = new ServletContextHandler(ServletContextHandler.SECURITY);

ServletHolder sh = new ServletHolder(new TestServlet());
servletContextHandler.addServlet(sh, "/foo");

servletContextHandler.setHandler(new GzipHandler());
servletContextHandler.insertHandler(new XYHandler());

Handler handler = servletContextHandler.getHandler(); // what does this look like ?

This results in the Handler tree ...

XYHandler 
   .handler = ConstraintSecurityHandler
      .handler = ServletHandler
          .handler = GzipHandler

Seems odd to have GzipHandler under the ServletHandler like this.
Would have expected it before the ConstraintSecurityHandler.

@gregw
Copy link
Contributor Author

gregw commented Nov 6, 2023

@joakime the following code:

        Server server = new Server();
        ServletContextHandler servletContextHandler = new ServletContextHandler(ServletContextHandler.SECURITY);
        servletContextHandler.setHandler(new GzipHandler());
        servletContextHandler.insertHandler(new Handler.Wrapper());

        server.setHandler(servletContextHandler);
        server.start();
        servletContextHandler.dumpStdErr();

produces:

oeje10s.ServletContextHandler@740fb309{ROOT,/,b=null,a=AVAILABLE,h=oejs.Handler$Wrapper@7bd7d6d6{STARTED}} - STARTED
+= oejs.Handler$Wrapper@7bd7d6d6{STARTED} - STARTED
|  += oeje10ss.ConstraintSecurityHandler@28cda624{STARTED} - STARTED
|     += oeje10s.ServletHandler@4dc27487{STARTED} - STARTED
|     |  += GzipHandler@6a4f1a55{STARTED,min=32,inflate=-1} - STARTED

I think the insert behaviour is exactly what we want.
I agree it is a little strange that SCH.setHandler does some magic so that the GzipHandler is after the ServletHandler, but that is what @sbordet asked for. I'm a 60:40 for the previous behaviour where setHandler was always a set on the current handler with minimal magic. But there always was a little bit of magic and sometimes warnings.... so this approach just embraces the magic and avoids the warnings.

Either way, I don't think it is hugely important, as I can't see I real use case for wanting to do setHandler in this situation. However, with EE10 and beyond, it is slightly more important as users can no longer use a ServletHandler with a plain ContextHandler. It only works with a ServletContextHandler (to avoid the need for a ScopedHandler mechanism we establish servlet scope as we enter the context).

As I said, I'm 60:40... but as I've currently implemented/tested the 40... I'm happy to go with it if there is consensus???

@gregw
Copy link
Contributor Author

gregw commented Nov 6, 2023

@sbortdet

@gregw I am still a bit confused by SCH interaction with setHandler() and insertHandler().

I think we have always had some strangeness here where one handle carries a tuple of other handlers that must be in a specific structure. I'm not sure we can avoid t he situation. We can only address confusion by a bit better consistency and better documentation, which is what I think this PR does. We can't make the issue go away.

I would like the following:

  • Make ServletHandler extends Handler.Abstract so that it's clear that it has no child. It still can return false, only in particular cases where users configure it to not have a DefaultServlet (in which case we just return the "core" 404).

@sbordet there are definitely usages of ServletHandler as a wrapper (see below). This is not an option.

  • SCH.setHandler() always sets the first child after SCH (like all Singleton do), overwriting the existing one, with the special handling for "the triplet", i.e. Session|Security|ServletHandler, and relinks the triplet.

That's what I've now implemented for you. If it is not a Session|Security|ServletHandler, it always sets the handler of the ServletHandler, overwriting the existing one. So it is setting the first child after SCH.

  • The triplet is always last in the chain, so SCH.getTail() always returns the ServletHandler.

Not an option. There are valid use-cases for falling through a ServletHandler. For example we can put a ResourceHandler after the ServletHandler and not have a DefaultServlet(with all it's complexities).

Some example:

sch.setHandler(gzipH);
sch.insertHandler(wsUpgradeH); // => sch-wsuh-gziph-[sessionh]-[securityh]-servleth
sch.getHandler() == wsuh
sch.getTail() == servleth

With the current PR, the code above sets up the following chain, which I think is wrong: sch-wsuh-[sessionh]-[securityh]-servleth-gziph, as we would never gzip.

I said I was 60:40 in my reply to @joakime, but now I'm 95:5 for the former behaviour of setHandler with less magic since the following should always be true:

    sch.setHandler(xyzHandler);
    sch.getHandler() == xyzHandler

I think the less magic, then the less confusion. My intention is to go back to a simple setHandler with warnings for bad behaviours. Perhaps it should not even intercept Session|Security|ServletHandler

gregw added 3 commits November 7, 2023 08:21
# Conflicts:
#	jetty-ee10/jetty-ee10-servlet/src/main/java/org/eclipse/jetty/ee10/servlet/SessionHandler.java
@gregw gregw requested a review from sbordet November 7, 2023 05:11
@gregw
Copy link
Contributor Author

gregw commented Nov 8, 2023

@joakime nudge

joakime
joakime previously approved these changes Nov 8, 2023
@gregw
Copy link
Contributor Author

gregw commented Nov 9, 2023

@sbordet nudge

Copy link
Contributor

@sbordet sbordet left a comment

Choose a reason for hiding this comment

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

I am still confused.

I think we should stick to the behavior of the other Handlers and not make this one any special.

As I said, I was wrong wanting setHandler() to set the child of ServletHandler.
I think it should just do what any other Handler does, i.e. setting the immediate child.

insertHandler() would have the same semantic as others, i.e. setting the immediate child and shifting the current child.

The "triplet" always come last.

I'm fine for ServletHandler to be a Wrapper if there are use cases, but setting its child should always be done explicitly, i.e. sch.getServletHandler().setHandler(...), not via some "magic" in SCH.setHandler().

Comment on lines 1644 to 1649
if (handler instanceof SessionHandler && getHandler() instanceof SessionHandler)
setSessionHandler((SessionHandler)handler);
else if (handler instanceof SecurityHandler && getHandler() instanceof SecurityHandler)
setSecurityHandler((SecurityHandler)handler);
else if (handler instanceof ServletHandler && getHandler() instanceof ServletHandler)
setServletHandler((ServletHandler)handler);
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 logic is right.
Calling setHandler() with one of the 3 handlers will never work.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It has been this way for several years at least. So we are not making it special. It is already special.
I'm not sure we want to put a breaking change into the API without thought. I found a few places in the code that did rely on this behaviour, so other users may also.

I think if we want to change setHandler, that should be a different PR with more consideration.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@sbordet I've put the implementation of setHandler back to exactly what it has been for some time. However, this PR removes any usage of it that depends on that behaviour. If it is an issue, then we can make the method always warn if called on SCH.

@gregw gregw requested a review from sbordet November 10, 2023 19:41
gregw and others added 2 commits November 13, 2023 07:52
…super.

Moved call to relinkHandlers() in insertHandler(), as the various setSession|Security|ServletHandler() already call relinkHandlers().

Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
Copy link
Contributor

@sbordet sbordet left a comment

Choose a reason for hiding this comment

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

@gregw see my last commit where I added in one place and moved in another the call to relinkHandlers().
LGTM with the current behavior.

@gregw
Copy link
Contributor Author

gregw commented Nov 13, 2023

@gregw see my last commit where I added in one place and moved in another the call to relinkHandlers(). LGTM with the current behavior.

@sbordet

The move of the relink in insertHandler is good, as it saves doing it twice.

But the relinkHandlers in setHandler is wrong (or perhaps the relinkHandlers impl is not good enough. If I try to do a set chain of handlers A->B->C->SessionHandler->ServletHandler, then relinkHandlers will look at A, see that its next handler is not SessionHandler and replace it, so the chain will end up A->SessionHandler->ServletHandler and B->C will be dropped.

I'll make a unit test to check this and then try to fix relinkHandler.... but I just don't think we should try to do magic with anything other than the Session/Security/Servlet handlers. Even that magic is probably too much.

@gregw
Copy link
Contributor Author

gregw commented Nov 13, 2023

@sbordet OK I was wrong. relinkHandlers is good enough to follow the chain. But it is just more magic. I don't like it, but OK.

@gregw gregw merged commit 49b3442 into jetty-12.0.x Nov 13, 2023
7 checks passed
@gregw gregw deleted the fix/jetty-12.0.x/9297/insertHandler branch November 13, 2023 12:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
No open projects
Status: ✅ Done
Development

Successfully merging this pull request may close these issues.

Jetty-12 insertHandler review
3 participants