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 

ColdBox Platform: http://www.coldbox.org 



On Fri, Jul 3, 2020 at 11:29 AM Brad Wood <bdw429s@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 

ColdBox Platform: http://www.coldbox.org 



On Fri, Jul 3, 2020 at 7:22 AM Bill O'Neil <bill@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@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 

ColdBox Platform: http://www.coldbox.org 



On Thu, Jul 2, 2020 at 9:22 PM Stuart Douglas <sdouglas@redhat.com> wrote:


On Fri, 3 Jul 2020 at 10:34, Brad Wood <bdw429s@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 

ColdBox Platform: http://www.coldbox.org 



On Thu, Jul 2, 2020 at 7:01 PM Stuart Douglas <sdouglas@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@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 

ColdBox Platform: http://www.coldbox.org 



On Thu, Jul 2, 2020 at 6:17 PM Stuart Douglas <sdouglas@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@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 

ColdBox Platform: http://www.coldbox.org 

_______________________________________________
undertow-dev mailing list
undertow-dev@lists.jboss.org
https://lists.jboss.org/mailman/listinfo/undertow-dev
_______________________________________________
undertow-dev mailing list
undertow-dev@lists.jboss.org
https://lists.jboss.org/mailman/listinfo/undertow-dev