[undertow-dev] response-code handler bypasses configured error pages

Brad Wood bdw429s at gmail.com
Tue Jul 7 12:30:59 EDT 2020


Bill, does my bigger picture that I provided you help you understand my
issues and what I'm trying to accomplish?

Thanks!

~Brad

*Developer Advocate*
*Ortus Solutions, Corp *

E-mail: brad at coldbox.org
ColdBox Platform: http://www.coldbox.org
Blog: http://www.codersrevolution.com



On Fri, Jul 3, 2020 at 11:29 AM Brad Wood <bdw429s at gmail.com> wrote:

> *Can you give a bigger picture of exactly what you are trying to solve? *
>>
>
> Hi Bill.  I know I've talked with Stuart about my use of Undertow in the
> past and he's even helped review our code.  It's understandable you
> probably don't know where I'm coming from :)  I typically don't get into
> the weeds of exactly how I'm using Undertow since it's a long story, but
> since you asked I'll fill you in.  I'm the lead developer on a tool called
> CommandBox which is a
>
>    - CLI
>    - REPL
>    - package manager
>    - scaffold
>    - server for CFML applications (powered under the hood using
>    pre-packaged WARs and Undertow
>
> https://commandbox.ortusbooks.com/getting-started-guide
>
> Undertow is packaged quite nicely inside of a black box and frankly, my
> users don't know or care that it's there.  We use a custom resource manager
> that allows us to serve JIT'd CFML templates from a separate web root
> outside the WAR while powering the servlet from a pre-packaged WAR in a
> manner that is the same as Adobe ColdFusion or Lucee Server's Tomcat-based
> installations.  Overall, that works great and has been in use for 5-6 years
> now, powering roughly 50% of the docker usage in the CF community.
>
> All a CommandBox user needs to do to start up a CF server is (box has a
> built in interactive shell based on JLine)
>
> CommandBox> echo "Hello world" >  index.cfm
> CommandBox> server start port=8080
>
> And boom, a browser window opens up serving their hello world message.
> They can even use CommandBox/Undertow to serve up a static HTML server just
> like the "npm serve" command, but with no Node :)  CommandBox also has a
> singular JSON file that allows users to control every aspect of their server
>
>    - JRE - auto installed from the AdoptOpenJDK API
>    - CF engine/version - auto installed from Forgebox.io
>    - Ports (HTTP/HTTPS/AJP)
>    - SSL Certs
>    - virtual directories
>    - Lib directories to class load jars from for the app
>    - welcome pages
>    - Basic auth
>    - heap size
>    - URL rewrites (Currently powered by Tuckey)
>    - *Error pages*
>    - And coming soon-- rules defined in Undertow's predicate language
>    placed right into an array in the JSON file.
>
> Doc Reference for all configuration options:
> https://commandbox.ortusbooks.com/embedded-server/server.json
>
> So to review, my users spin up servers externally controlled by CLI
> parameters, local JSON files shipped with the app, or even global default
> settings they've baked into their CLI.  My users don't know or care about
> Undertow, it "just works" for them.
> The addition of the predicate language will give my users an option other
> than Tuckey to accomplish the following
>
>    - Add rewrites to their app
>    - Block common administrator paths from prying eyes
>    - Secure sensitive configuration files they don't want served from the
>    web root
>    - Configure reverse proxies and load balanced proxies on the fly
>
> I even plan on baking some of these security rules into the CommandBox
> core so their servers are better secured by default.
>
> So here is a totally realistic example of how I envision a server being
> started (The "server set" command simply modifies the server.json files for
> you)
>
> CommandBox> server set web.errorPages.404=notfound.html
> CommandBox> server set web.rules = '[
> "path(/box.json)->response-code(404)" ]'
> CommandBox> server start
>
> So, in that user's mind, they just
>
>    1. Configured all *404 *requests to use a custom *notfound.html* file
>    2. Placed a predicate rule that responds with a *404* to any request
>    to the secret file *box.json*
>    3. It would be the completely reasonable expectation of that user that
>    if they slap */box.json* in their browser, they will see the contents
>    of their *notFound.html* error page
>
> However, number 3 won't/doesn't happen for various behind-the-scenes
> technical reasons.  I get those reasons, but at the end of the day I need
> this behavior to be consistent in my usage of undertow.  I can't just tell
> people, "Oh yeah, your error pages don't work half the time, but don't
> worry, there's a really good reason for it!", lol.  The error pages in the
> JSON configuration for CommandBox are fed directly to the servlet's error
> pages deployment info and the web.rules are parsed into a
> PredicatesHandler.  But again, this is a black box so it needs to "just
> work" like people will expect.  That's what I'm after.
>
> So, now that the background stuff is out of the way, allow me also address
> the rest of your questions categorically:
>
> Where will your actual routing logic live?
>
>
> I'm not quite sure if you're asking where the actual text-based predicate
> language will be stored or if you're asking where in the handler chain the
> PredicatesHandler will be processed.  If you're asking about the rules,
> they will be provided by the users of my tool and can literally come from
> anywhere based on their needs.  If you're asking where in the handler chain
> I'll process them, I've covered previously in this thread having them
> "inside" the servlet initial handler and "outside" the servlet and both
> approaches have had issues.  Perhaps the short answer is, I'm willing to
> put it wherever the heck it needs to be in order to make this work :)
>
> What types of things are you expecting the predicate language to
>> accomplish for you?
>
>
> Everything.  Literally everything.  I am exposing Undertow's predicate
> language directly to the end users (developers, sysops guys, etc) for them
> to tap into and add whatever rules they deem necessary for rewrites,
> security, or even reverse proxies in their app.  I'm not even sure why this
> matters to the conversation.  But suffice it to say, I need
> Undertow's predicate language to be capable of doing everything it does,
> but I need consistency in how error status codes are handled in a manner
> that matches the rest of the server.
>
> Why do you need to be tied to servlets?
>
>
> I don't.  Quite frankly, I couldn't care less what it's tied to.  That
> said, servlets already have the ability to control error pages.  CommandBox
> will parse the *web.xml* in the WAR to look for any configured error
> pages (since user's can supply a custom WAR) as well as combine the
> configuration in the user's server.json file. Right now we tap into the
> error pages functionality of the servlet because it's simple, it's built
> in, and it just makes sense.  I need an error page functionality that is
> consistent across all uses of Undertow.  Not just firing in some scenarios
> on Tuesdays after 3pm depending on a complex internal routing of your
> handler chain.  Again, my users don't know or care how this works, if they
> configure a custom 404 page, they expect it to be used by default for all
> 404s that don't otherwise return content.
>
>
>> path(/box.json)->response-code(404)
>> This example seems pretty impractical because if you just left the line
>> out entirely then wouldn't your router decide this route doesn't exist and
>> handle the 404 in your servlet code?
>
>
> Sorry, but I'm not quite following you.  That line is a prime example of
> what I expect people to use the predicate language for in securing parts of
> their app they don't want served.  The way CFML apps work is you place all
> files in your web root (images, jss, css, html, and CFMs).  There is a
> default static handler that just serves up whatever files you request, and
> a servlet mapping for *.cfm files that route them through the CF servlet
> (which JIT compiles and processes them).  If I don't have the predicate
> rule above, a user would be able to directly hit that configuration file in
> the web root-- and being just a normal JSON file, it would be served up no
> problem.  The request would not be a 404 in the servlet because that file
> actually exists on disk and would be served.  Which, of course, is the
> entire purpose of this feature!
>
> With attributes and custom HttpHandlers you should be able to accomplish
>> pretty much anything you want
>
>
> Yes, I know.  Undertow is very powerful.  My questions here are two fold
>
>    - How in the heck do we accomplish it??
>    - Can Undertow be made better?  The default behavior of the
>    response-code handler in this case seems ... *not useful.  *I mean, in
>    what universe is it ok to respond to a request with a 404 status code but
>    no response body.  Surely we can agree, this stands to be improved.  But
>    the predicate language doesn't seem to provide a consistent way to tap into
>    the error pages in the deployment.
>
> is there a specific need to have some of this logic live in servlets?
>
>
> Like I said above, not really -- other than the fact that this is a *servlet
> deployment* making use of *servlet features* such as error pages.
>
> Thanks!
>
> ~Brad
>
> *Developer Advocate*
> *Ortus Solutions, Corp *
>
> E-mail: brad at coldbox.org
> ColdBox Platform: http://www.coldbox.org
> Blog: http://www.codersrevolution.com
>
>
>
> On Fri, Jul 3, 2020 at 7:22 AM Bill O'Neil <bill at dartalley.com> wrote:
>
>> Can you give a bigger picture of exactly what you are trying to solve?
>> Where will your actual routing logic live?
>> What types of things are you expecting the predicate language to
>> accomplish for you?
>> Why do you need to be tied to servlets?
>>
>> path(/box.json)->response-code(404)
>>>
>> This example seems pretty impractical because if you just left the line
>> out entirely then wouldn't your router decide this route doesn't exist and
>> handle the 404 in your servlet code?
>>
>> With attributes and custom HttpHandlers you should be able to accomplish
>> pretty much anything you want is there a specific need to have some of this
>> logic live in servlets?
>>
>> On Fri, Jul 3, 2020 at 3:20 AM Brad Wood <bdw429s at gmail.com> wrote:
>>
>>> So what is our solution here?  It seems I'm darned if I do and darned if
>>> I don't.
>>>
>>> If I move the predicates until after the servlet then I can call
>>> sendError() on the servlet response to trigger the error pages, however I
>>> lose the ability to rewrite the request (a deal breaker)
>>>
>>> But if I leave the predicates before the servlet, I can perform
>>> rewrites, but I can no longer call sendError() since, as you said, there is
>>> no servlet response yet!
>>>
>>> I understand the separation between the servlet and core, but it's
>>> killing me here.  This seems like such a simple concept.
>>>
>>>    - I have a servlet deployment with error page configured
>>>    - I want to have a predicated handler that uses the configured error
>>>    pages
>>>
>>> Can I accomplish this with a default response listener that accesses the
>>> error page configuration and dispatches the appropriate location?  Even
>>> that feels dirty. It seems there should be a handler invoked directly from
>>> the predicates that can dispatch the error pages configured in the
>>> servlet.  Even if all it did was look up the corresponding location for the
>>> given status code and forward the request there. But I can't even figure
>>> out how to access the deployment from the exchange.
>>>
>>> Thanks!
>>>
>>> ~Brad
>>>
>>> *Developer Advocate*
>>> *Ortus Solutions, Corp *
>>>
>>> E-mail: brad at coldbox.org
>>> ColdBox Platform: http://www.coldbox.org
>>> Blog: http://www.codersrevolution.com
>>>
>>>
>>>
>>> On Thu, Jul 2, 2020 at 9:22 PM Stuart Douglas <sdouglas at redhat.com>
>>> wrote:
>>>
>>>>
>>>>
>>>> On Fri, 3 Jul 2020 at 10:34, Brad Wood <bdw429s at gmail.com> wrote:
>>>>
>>>>> Before I give that workaround a try, a couple existential questions...
>>>>>
>>>>>    - Should the *response-code* handler also set the *errorCode* as
>>>>>    well in the servlet request context so it triggers the doErrorDispatch()
>>>>>    action?
>>>>>    - Or should there be another built-in handler called "send-error"
>>>>>    or similar that does this.
>>>>>
>>>>> Ex:
>>>>> path( /secret.json ) -> send-error ( 404 )
>>>>> path( /admin/ ) -> send-error( 503 )
>>>>>
>>>>> I'm sort of questioning what the usefulness is of the *response-code*
>>>>> handler however (at least in the context of the predicate language) if it's
>>>>> default behavior is NOT to trigger the error code and therefore the error
>>>>> page.
>>>>>
>>>>
>>>> These error pages are part of Servlet, while the predicate language is
>>>> part of core. The response-code handler does not really know anything about
>>>> the Servlet deployment.
>>>>
>>>> Generally the predicate language is run before Servlet (e.g. in
>>>> WildFly), so the HttpServletRequest/Response has not been created yet. This
>>>> gives a lot of flexibility in that you can make changes to route the
>>>> request to different contexts and potentially skip servlet altogether, but
>>>> the down side is that it is not tightly integrated with Servlet.
>>>>
>>>> Stuart
>>>>
>>>>
>>>>>
>>>>> Thanks!
>>>>>
>>>>> ~Brad
>>>>>
>>>>> *Developer Advocate*
>>>>> *Ortus Solutions, Corp *
>>>>>
>>>>> E-mail: brad at coldbox.org
>>>>> ColdBox Platform: http://www.coldbox.org
>>>>> Blog: http://www.codersrevolution.com
>>>>>
>>>>>
>>>>>
>>>>> On Thu, Jul 2, 2020 at 7:01 PM Stuart Douglas <sdouglas at redhat.com>
>>>>> wrote:
>>>>>
>>>>>> Hmm, this is because the servlet status code is only triggered by a
>>>>>> sendError method, not by just setting the code.
>>>>>>
>>>>>> You probably need to write a custom one that looks like this (plus
>>>>>> the relevant predicate languge bits):
>>>>>>
>>>>>> public class SendErrorHandler implements HttpHandler {
>>>>>>
>>>>>>     private final int code;
>>>>>>
>>>>>>     public SendErrorHandler(int code) {
>>>>>>         this.code = code;
>>>>>>     }
>>>>>>
>>>>>>     @Override
>>>>>>     public void handleRequest(HttpServerExchange exchange) throws
>>>>>> Exception {
>>>>>>         ServletRequestContext src =
>>>>>> exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
>>>>>>
>>>>>> ((HttpServletResponse)src.getServletResponse()).sendError(code);
>>>>>>     }
>>>>>> }
>>>>>>
>>>>>> Stuart
>>>>>>
>>>>>>
>>>>>>
>>>>>> On Fri, 3 Jul 2020 at 09:40, Brad Wood <bdw429s at gmail.com> wrote:
>>>>>>
>>>>>>> Thanks for the reply Stuart.  I've tried this with no success, but
>>>>>>> perhaps I'm doing it wrong.
>>>>>>>
>>>>>>> List<PredicatedHandler> ph =
>>>>>>> PredicatedHandlersParser.parse(predicatesLines, _classLoader);
>>>>>>> servletBuilder.addOuterHandlerChainWrapper(next ->
>>>>>>> Handlers.predicates(ph,next));
>>>>>>>
>>>>>>> When the response-code handler fires, I still get no response body.
>>>>>>>
>>>>>>> On a related note, when I move the predicates into an outer handler
>>>>>>> chain wrapper, my default response listener also doesn't fire at all.
>>>>>>>
>>>>>>> On an unrelated train of thought, I've been trying to see if I can
>>>>>>> get the default response listener to automatically dispatch the correct
>>>>>>> error page, but that hasn't been going well either.  If I don't use the
>>>>>>> outer handler chain idea, but try to capture the empty response in a
>>>>>>> default response listener, I can return a static message using the Sender
>>>>>>> class
>>>>>>>
>>>>>>> Sender sender = exchange.getResponseSender();
>>>>>>> sender.send(errorHTMLString);
>>>>>>>
>>>>>>> But if I try to run something this in my default response listener
>>>>>>> to invoke my error pages
>>>>>>>
>>>>>>> ServletRequestContext src =
>>>>>>> exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
>>>>>>> HttpServletResponseImpl response = src.getOriginalResponse();
>>>>>>> response.doErrorDispatch( exchange.getStatusCode(),
>>>>>>> exchange.getReasonPhrase() );
>>>>>>>
>>>>>>> Then it's as though nothing happens and I still get an empty
>>>>>>> response.
>>>>>>>
>>>>>>> Thanks!
>>>>>>>
>>>>>>> ~Brad
>>>>>>>
>>>>>>> *Developer Advocate*
>>>>>>> *Ortus Solutions, Corp *
>>>>>>>
>>>>>>> E-mail: brad at coldbox.org
>>>>>>> ColdBox Platform: http://www.coldbox.org
>>>>>>> Blog: http://www.codersrevolution.com
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>>> On Thu, Jul 2, 2020 at 6:17 PM Stuart Douglas <sdouglas at redhat.com>
>>>>>>> wrote:
>>>>>>>
>>>>>>>> The predicate languages are executed before the Servler handlers,
>>>>>>>> so they won't be handled by Servlet error pages.
>>>>>>>>
>>>>>>>> If you are setting this all up programmatically you could use
>>>>>>>> io.undertow.servlet.api.DeploymentInfo#addOuterHandlerChainWrapper to setup
>>>>>>>> the predicate handler after the initial servlet one, which should mean that
>>>>>>>> the servlet error handling will handle the response code.
>>>>>>>>
>>>>>>>> Stuart
>>>>>>>>
>>>>>>>> On Fri, 3 Jul 2020 at 08:25, Brad Wood <bdw429s at gmail.com> wrote:
>>>>>>>>
>>>>>>>>> When I configure an error page similar to this:
>>>>>>>>>
>>>>>>>>> servletBuilder.addErrorPage( new ErrorPage( "404.html", 404));
>>>>>>>>>
>>>>>>>>> This works great when I hit a path in my browser that doesn't
>>>>>>>>> exist.  The contents of the *404.html* file is served with a
>>>>>>>>> response code of *404*.
>>>>>>>>>
>>>>>>>>> However, if I also use the predicate language to define something
>>>>>>>>> like:
>>>>>>>>>
>>>>>>>>> path(/box.json)->response-code(404)
>>>>>>>>>
>>>>>>>>> and then I hit *localhost/box.json* in my browser, I get a *404*
>>>>>>>>> status code but with no response body.
>>>>>>>>>
>>>>>>>>>    - The docs say the response-code handler ends the exchange,
>>>>>>>>>    but should it still respect the error pages?
>>>>>>>>>    - How can I modify my use of Undertow to respect the error
>>>>>>>>>    pages when using the response-code handler?
>>>>>>>>>    - I've seen in the docs the ability to have a
>>>>>>>>>    *addDefaultResponseListener()* but I'm not sure if it is the
>>>>>>>>>    correct solution for this, nor how I would access the error page
>>>>>>>>>    configuration dynamically as to not need to duplicate my work.
>>>>>>>>>
>>>>>>>>> Thanks!
>>>>>>>>>
>>>>>>>>> ~Brad
>>>>>>>>>
>>>>>>>>> *Developer Advocate*
>>>>>>>>> *Ortus Solutions, Corp *
>>>>>>>>>
>>>>>>>>> E-mail: brad at coldbox.org
>>>>>>>>> ColdBox Platform: http://www.coldbox.org
>>>>>>>>> Blog: http://www.codersrevolution.com
>>>>>>>>>
>>>>>>>>> _______________________________________________
>>>>>>>>> undertow-dev mailing list
>>>>>>>>> undertow-dev at lists.jboss.org
>>>>>>>>> https://lists.jboss.org/mailman/listinfo/undertow-dev
>>>>>>>>
>>>>>>>> _______________________________________________
>>> undertow-dev mailing list
>>> undertow-dev at lists.jboss.org
>>> https://lists.jboss.org/mailman/listinfo/undertow-dev
>>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.jboss.org/pipermail/undertow-dev/attachments/20200707/7f27153e/attachment-0001.html 


More information about the undertow-dev mailing list