[undertow-dev] response-code handler bypasses configured error pages
Brad Wood
bdw429s at gmail.com
Wed Jul 8 04:13:16 EDT 2020
Ok, here is a JIRA ticket:
https://issues.redhat.com/browse/UNDERTOW-1747
And here is a pull request with my best stab at it:
https://github.com/undertow-io/undertow/pull/908
In my pull, I went with a predicate textual name of
set-error( 404 )
mostly because I used the Send Error Page handler name for the servlet
handler that actually looks for the status code.
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 Tue, Jul 7, 2020 at 8:55 PM Brad Wood <bdw429s at gmail.com> wrote:
> Forgot to reply to this:
>
> add a 'continue' param to the response code handler,
>
>
> I'm not against adding this, however I would be unlikely to use
>
> responseCode( value=404, continue=true )
>
> as I'd want to have a cleaner predicate language. I'd rather a new
> handler be introduced like the custom
>
> send-error-page( 404 )
>
> one I've been testing with that is readable and clear what it does.
>
> 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 Tue, Jul 7, 2020 at 8:51 PM Brad Wood <bdw429s at gmail.com> wrote:
>
>> Genius idea. In all my tests I hadn't thought of setting the response
>> code in the predicate and then letting it continue to the servlet and
>> caching it there. I just tested this and it does appear to work well.
>>
>> I added this outer handler chain wrapper to my deployment info
>>
>> deploymentInfo.addOuterHandlerChainWrapper(next -> new
>> HttpHandler() {
>> @Override
>> public void handleRequest(HttpServerExchange exchange) throws
>> Exception {
>> if( exchange.getStatusCode() > 399 &&
>> exchange.getResponseContentLength() == -1 ) {
>> ServletRequestContext src =
>> exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
>>
>> ((HttpServletResponse)src.getServletResponse()).sendError(exchange.getStatusCode());
>> } else {
>> next.handleRequest(exchange);
>> }
>> }
>> });
>>
>> (I don't know if the exchange.getResponseContentLength() check is needed
>> or the best way to do that)
>>
>> And then I registered a custom handler I called *send-error-page* that
>> has this handleRequest method:
>>
>> public void handleRequest(final HttpServerExchange exchange) throws
>> Exception {
>> exchange.setStatusCode(responseCode);
>> next.handleRequest(exchange);
>> }
>>
>> Then this predicate rule
>>
>> path(/box.json)->send-error-page(404)
>>
>> serves up my servlet's custom error page for that request.
>>
>> 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 Tue, Jul 7, 2020 at 6:39 PM Stuart Douglas <sdouglas at redhat.com>
>> wrote:
>>
>>> Basically I think the only way you can solve this is to attach the error
>>> code to the exchange somehow, then have a handler that reads the error code
>>> and calls sendError later.Basically write a new response-code handler that
>>> sets the code then calls the next handler (so the request gets to Servlet),
>>> then add a handler to the Servlet deployment that calls sendError if the
>>> exchange status code is > 400.
>>>
>>> We could actually add this to Undertow very simply, just add a
>>> 'continue' param to the response code handler, then always call sendError
>>> in servlet if the incoming request response is >400.
>>>
>>> Can you try this out, and if it works I can look at getting it into
>>> Undertow.
>>>
>>> Stuart
>>>
>>> On Wed, 8 Jul 2020 at 02:35, Brad Wood <bdw429s at gmail.com> wrote:
>>>
>>>> Stuart, any further suggestions or answers to my outstanding questions?
>>>> So far, everything you've suggested doesn't work for various reasons I've
>>>> shared here. I also have a number of outstanding questions such as how I
>>>> could access the deployment information for the servlet's error pages from
>>>> inside an HTTPHandler running in the initial handler chain that fires in
>>>> the XNIO worker.
>>>>
>>>> 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 3:55 PM Brad Wood <bdw429s at gmail.com> wrote:
>>>>
>>>>> After a few hours of experimenting, I was able to answer a few of my
>>>>> own questions, but not all of them.
>>>>>
>>>>> - Yes, it is possible to contribute a custom handler to the
>>>>> predicate language-- this is actually pretty sweet. I didn't realize just
>>>>> how generic the parser was.
>>>>> - In order to get "picked up", the builder class must be listed in
>>>>> the
>>>>> *META-INF/services/io.undertow.server.handlers.builder.HandlerBuilder*
>>>>> file
>>>>> - That custom handler suggested by Stuart in the previous message
>>>>> ONLY works if the predicate chain is wrapped "inside" the servlet initial
>>>>> handler. Otherwise, the servletcontext classes are missing. This
>>>>> unfortunately, makes it a no-go for me to be able to use.
>>>>>
>>>>> The rest of my questions still stand however in so far as how I can
>>>>> accomplish what I need.
>>>>>
>>>>> During my experiments today, I also played with the error-file
>>>>> handler, but it also is of no use. Since the response-code handler ends
>>>>> the exchange, any error pages declared in the error file handler are never
>>>>> used!
>>>>>
>>>>> error-file( response-codes={404},
>>>>> file="C:\sandbox\predicatetest\custom404.html" )
>>>>> path('/box.json') -> response-code(404)
>>>>>
>>>>> I even tried creating a custom handler that sets the status code but
>>>>> continues the exchange, but then that just serves the original file anyway,
>>>>> which causes the error-file handler to do nothing since the response stream
>>>>> is already started.
>>>>>
>>>>> 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 12:02 PM Brad Wood <bdw429s at gmail.com> wrote:
>>>>>
>>>>>> You probably need to write a custom one that looks like this (plus
>>>>>>> the relevant predicate languge bits):
>>>>>>
>>>>>>
>>>>>> Stuart, if I can branch the conversation here back to your idea of a
>>>>>> custom handler for a bit...
>>>>>>
>>>>>> I'm still toying with this, and while the handleRequest() code you
>>>>>> have makes perfect sense, I think I'm missing a lot of context on how you
>>>>>> were suggesting this would work.
>>>>>>
>>>>>> - The code you put in your message-- were you suggesting that
>>>>>> should be added to the Undertow core as a built in handler or just a custom
>>>>>> class I would put in my project?
>>>>>> - Would this handler require the predicates be processed
>>>>>> downstream of the servlet initial handler in order to work? Because if so,
>>>>>> that appears to be a no-go since handlers like the rewrite only appear to
>>>>>> work upstream of the servlet
>>>>>> - Is it possible to register custom handlers of my own creation
>>>>>> with the predicate language or were you suggesting a handler that could
>>>>>> only be manually configured? I'm familiar with the handler builder pattern
>>>>>> that the built in handlers use, but I'm unclear on how builders get
>>>>>> registered with the predicate handler parser.
>>>>>> - Actually, I just reviewed the code again now and I see
>>>>>> the loadHandlerBuilders() method in the PredicatedhandlersParser class
>>>>>> appears to be scanning the classpath for builders. Interesting.... Does
>>>>>> Undertow need to be loaded by the same class loader that loaded my custom
>>>>>> handlers for this to work? I'm not super familiar with the ServiceLoader
>>>>>> stuff
>>>>>> - I'm not against building a custom handler that I ship alongside
>>>>>> Undertow that provides what I'm looking for, but I'm still interested in
>>>>>> having the conversation of how we can improve undertow's core offering.
>>>>>>
>>>>>> 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
>>>>>>>>>
>>>>>>>>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.jboss.org/pipermail/undertow-dev/attachments/20200708/cef09c7d/attachment-0001.html
More information about the undertow-dev
mailing list