<div dir="ltr">Hey all,<div><br></div><div>I added my own router handler that does everything I need it to do. It&#39;s pretty well documented and very well tested. I&#39;d be willing to submit a pull request to have this added to Undertow if that&#39;d be valuable to anyone. Here&#39;s a taste of the code (the core stuff is in PathTrie):</div><div><br></div><div><div>package com.analyticspot.uservices.server.router;</div><div><br></div><div>import com.analyticspot.httputils.HttpVerb;</div><div>import com.analyticspot.httputils.ResponseCodes;</div><div>import com.memoizrlabs.retrooptional.Optional;</div><div>import io.undertow.server.HttpHandler;</div><div>import io.undertow.server.HttpServerExchange;</div><div>import io.undertow.util.AttachmentKey;</div><div><br></div><div>import java.util.Map;</div><div>import java.util.TreeMap;</div><div><br></div><div>/**</div><div> * An HttpHandler that routes requests to other handlers based on paths and path templates. It is thread safe to</div><div> * search this class concurrently (match paths) but adding paths is not thread safe. Thus the expected use-case is</div><div> * that all paths are set up before the handler is added as an Undertow listener.</div><div> *</div><div> * &lt;p&gt;The rules for path matching are:</div><div> * &lt;ul&gt;</div><div> *   &lt;li&gt;All paths must begin with a &quot;/&quot;&lt;/li&gt;</div><div> *   &lt;li&gt;Wildcard matches are allowed by adding &quot;{X}&quot; to the path. Then {@code X} and what matched it are attached</div><div> *   to the request in a {@code Map} from {@code X} to the value that matched. For example, if the added path was</div><div> *   &quot;/foo/{name}/stuff/{age}&quot; and the observed path was &quot;/foo/bar/stuff/22&quot; then the attachment would have a map</div><div> *   like &quot;{name: bar, age: 22}.&lt;/li&gt;</div><div> *   &lt;li&gt;Prefix paths are allowed. Such paths will match anything that begins with the provided prefix.&lt;/li&gt;</div><div> *   &lt;li&gt;Exact matches and/or longer matches take precedence over both prefix and wildcard matches. Thus, given the</div><div> *   wildcard path &quot;/foo/{name}&quot;, the prefix path &quot;/foo/baz&quot;, and the exact path &quot;/foo/bar&quot;, we would expect the</div><div> *   following matches:</div><div> *   &lt;table&gt;</div><div> *     &lt;tr&gt;&lt;td&gt;Observed Path&lt;/td&gt;&lt;td&gt;Matched Path&lt;/td&gt;&lt;/tr&gt;</div><div> *     &lt;tr&gt;&lt;td&gt;/foo/bar&lt;/td&gt;&lt;td&gt;/foo/bar&lt;/td&gt;&lt;/tr&gt;</div><div> *     &lt;tr&gt;&lt;td&gt;/foo/baz&lt;/td&gt;&lt;td&gt;/foo/baz&lt;/td&gt;&lt;/tr&gt;</div><div> *     &lt;tr&gt;&lt;td&gt;/foo/baz/bump&lt;/td&gt;&lt;td&gt;/foo/baz&lt;/td&gt;&lt;/tr&gt;</div><div> *     &lt;tr&gt;&lt;td&gt;/foo/gourdy&lt;/td&gt;&lt;td&gt;/foo/{name}&lt;/td&gt;&lt;/tr&gt;</div><div> *   &lt;/table&gt;</div><div> *   &lt;/li&gt;</div><div> *   &lt;li&gt;This is not a &quot;backtracking&quot; matcher. Thus, given a prefix path of &quot;/foo/bar&quot; and an exact path</div><div> *   &quot;/foo/bar/baz&quot;, the observed path &quot;/foo/bar/baz/bizzle&quot; would have &lt;b&gt;no match&lt;/b&gt; because &quot;/foo/bar/baz&quot;</div><div> *   doesn&#39;t match and we do not backtrack to the shorter &quot;/foo/bar&quot; prefix to see if it matches.&lt;/li&gt;</div><div> *   &lt;li&gt;If both a prefix and a wildcard match, the wildcard takes precendence. Thus given prefix path &quot;/foo&quot; and</div><div> *   wildcard path &quot;/foo/{name}&quot;, the match for obeserved path &quot;/foo/bar&quot; would be the wildcard path.&lt;/li&gt;</div><div> * &lt;/ul&gt;</div><div> *</div><div> * &lt;p&gt;We added our own router due to limitations in the Undertow PathTemplateHandler and RoutingHandler. See</div><div> * <a href="http://lists.jboss.org/pipermail/undertow-dev/2016-September/001685.html">http://lists.jboss.org/pipermail/undertow-dev/2016-September/001685.html</a> for details.</div><div> */</div><div>public class PathRoutingHandler implements HttpHandler {</div><div>  public static AttachmentKey&lt;PathMatch&gt; PATH_MATCH_KEY = AttachmentKey.create(PathMatch.class);</div><div><br></div><div>  // Map from HTTP verb to the PathTrie for the handlers for that route.</div><div>  private final Map&lt;HttpVerb, PathTrie&gt; verbToPathTrie = new TreeMap&lt;&gt;();</div><div>  // If present and none of the paths match this handler will be called.</div><div>  Optional&lt;HttpHandler&gt; fallbackHandler;</div><div><br></div><div>  public PathRoutingHandler() {</div><div>    fallbackHandler = Optional.empty();</div><div>  }</div><div><br></div><div>  /**</div><div>   * Provides a fallback handler which will handle the request if nothing in the trie matches.</div><div>   */</div><div>  public PathRoutingHandler(HttpHandler fallbackHandler) {</div><div>    this.fallbackHandler = Optional.of(fallbackHandler);</div><div>  }</div><div><br></div><div>  /**</div><div>   * Adds a handler for the given URL/verb combination. The path can be a template as per the class comment.</div><div>   */</div><div>  public void addHandler(HttpVerb verb, String path, HttpHandler handler) {</div><div>    PathTrie trie = getTrie(verb);</div><div>    trie.addPath(path, handler);</div><div>  }</div><div><br></div><div>  /**</div><div>   * Like {@link #addHandler(HttpVerb, String, HttpHandler)}but add a prefix handler.</div><div>   */</div><div>  public void addPrefixHandler(HttpVerb verb, String path, HttpHandler handler) {</div><div>    PathTrie trie = getTrie(verb);</div><div>    trie.addPrefixPath(path, handler);</div><div>  }</div><div><br></div><div>  private PathTrie getTrie(HttpVerb verb) {</div><div>    if (!verbToPathTrie.containsKey(verb)) {</div><div>      PathTrie trie = new PathTrie();</div><div>      verbToPathTrie.put(verb, trie);</div><div>      return trie;</div><div>    } else {</div><div>      return verbToPathTrie.get(verb);</div><div>    }</div><div>  }</div><div><br></div><div>  @Override</div><div>  public void handleRequest(HttpServerExchange exchange) throws Exception {</div><div>    HttpVerb verb = HttpVerb.fromString(exchange.getRequestMethod().toString());</div><div>    PathTrie trie = verbToPathTrie.get(verb);</div><div>    if (trie == null) {</div><div>      handleNoMatch(exchange);</div><div>    } else {</div><div>      Optional&lt;PathMatch&gt; optMatch = trie.findMatch(exchange.getRelativePath());</div><div>      if (optMatch.isPresent()) {</div><div>        exchange.putAttachment(PATH_MATCH_KEY, optMatch.get());</div><div>        optMatch.get().getHandler().handleRequest(exchange);</div><div>      } else {</div><div>        handleNoMatch(exchange);</div><div>      }</div><div>    }</div><div>  }</div><div><br></div><div>  private void handleNoMatch(HttpServerExchange exchange) throws Exception {</div><div>    if (fallbackHandler.isPresent()) {</div><div>      fallbackHandler.get().handleRequest(exchange);</div><div>    } else {</div><div>      exchange.setStatusCode(ResponseCodes.NOT_FOUND.getCode());</div><div>      exchange.endExchange();</div><div>    }</div><div>  }</div><div>}</div></div><div><br></div><div>-- Oliver</div></div><br><div class="gmail_quote"><div dir="ltr">On Wed, Sep 7, 2016 at 11:35 AM Oliver Dain &lt;<a href="mailto:oliver@analyticspot.com">oliver@analyticspot.com</a>&gt; wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr">Thanks Greg and Bill!</div><br><div class="gmail_quote"><div dir="ltr">On Wed, Sep 7, 2016 at 6:20 AM Bill O&#39;Neil &lt;<a href="mailto:bill@dartalley.com" target="_blank">bill@dartalley.com</a>&gt; wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="ltr">I did something similar to Greg by delegating to a RoutingHandler which has a PathHandler fallback.<div><br></div><div><a href="https://gist.github.com/billoneil/08b1648a3b2a849e02c57e133bd6d45c" target="_blank">https://gist.github.com/billoneil/08b1648a3b2a849e02c57e133bd6d45c</a><br></div><div><br></div><div>This allows me to add prefix routes but still not flexible enough for all uses cases. The main drawback with this approach is the prefix handler doesn&#39;t route based on Verb but it has worked for all of my use cases so far.</div><div><br></div><div>I would also like to know if there is a way the RoutingHandler can be extended to have wildcard functionality.</div></div><div class="gmail_extra"><br><div class="gmail_quote">On Wed, Sep 7, 2016 at 8:08 AM, Greg Hellings <span dir="ltr">&lt;<a href="mailto:greg.hellings@gmail.com" target="_blank">greg.hellings@gmail.com</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">Oliver,<br>
<br>
I found it very difficult to work with the PathTemplateHandler and its<br>
set of matching operations seemed very minimal to me. I also found no<br>
efficient way to mix-and-match between full Paths and PathTemplate<br>
entries. So I created my own handler class that gave the ability to<br>
match based on both HTTP Verb and various path components.<br>
<br>
<a href="https://github.com/greg-hellings/gully/blob/master/src/main/java/com/thehellings/gully/Router.java" rel="noreferrer" target="_blank">https://github.com/greg-hellings/gully/blob/master/src/main/java/com/thehellings/gully/Router.java</a><br>
<a href="https://github.com/greg-hellings/gully/blob/master/src/main/java/com/thehellings/gully/PlainRouter.java" rel="noreferrer" target="_blank">https://github.com/greg-hellings/gully/blob/master/src/main/java/com/thehellings/gully/PlainRouter.java</a><br>
<br>
I haven&#39;t used it extensively, but in my own local testing it seems to<br>
work exactly the way I wanted - it&#39;s still just an HttpHandler<br>
underneath, so it can be placed anywhere in a handler chain. And it<br>
operates on the result of getRelativePath, so you can nest them within<br>
each other, or place them within other components in a handler chain<br>
and the class should operate properly.<br>
<br>
The whole set of handler and its dependent classes should be available<br>
as a Maven artifact if you want to play with it. If this sort of<br>
functionality exists somewhere in core Undertow, I&#39;ve been entirely<br>
unable to decipher it and locate its functioning.<br>
<br>
--Greg<br>
<div><div><br>
On Tue, Sep 6, 2016 at 9:41 PM, Oliver Dain &lt;<a href="mailto:oliver@analyticspot.com" target="_blank">oliver@analyticspot.com</a>&gt; wrote:<br>
&gt; Hey all,<br>
&gt;<br>
&gt; I haven&#39;t seen any documentation on the format of a template accepted by a<br>
&gt; PathTemplateHandler. I know the basics: &quot;/foo&quot;, &quot;/foo/{userId}&quot;, etc. but<br>
&gt; are wildcards allowed? Is there a way to specify a handler for anything with<br>
&gt; a certain prefix? If two handlers would match how is the tie broken, etc.<br>
&gt;<br>
&gt; Thanks,<br>
&gt; Oliver<br>
&gt; --<br>
&gt; CTO, Analytic Spot<br>
&gt; 44 West Broadway #222<br>
&gt; Eugene, OR 97401<br>
&gt; <a href="http://analyticspot.com" rel="noreferrer" target="_blank">analyticspot.com</a> • <a href="tel:425-296-6556" value="+14252966556" target="_blank">425-296-6556</a><br>
&gt; <a href="http://www.linkedin.com/in/oliverdain" rel="noreferrer" target="_blank">www.linkedin.com/in/oliverdain</a><br>
&gt;<br>
</div></div>&gt; _______________________________________________<br>
&gt; undertow-dev mailing list<br>
&gt; <a href="mailto:undertow-dev@lists.jboss.org" target="_blank">undertow-dev@lists.jboss.org</a><br>
&gt; <a href="https://lists.jboss.org/mailman/listinfo/undertow-dev" rel="noreferrer" target="_blank">https://lists.jboss.org/mailman/listinfo/undertow-dev</a><br>
<br>
_______________________________________________<br>
undertow-dev mailing list<br>
<a href="mailto:undertow-dev@lists.jboss.org" target="_blank">undertow-dev@lists.jboss.org</a><br>
<a href="https://lists.jboss.org/mailman/listinfo/undertow-dev" rel="noreferrer" target="_blank">https://lists.jboss.org/mailman/listinfo/undertow-dev</a></blockquote></div><br></div>
</blockquote></div><div dir="ltr">-- <br></div><div data-smartmail="gmail_signature"><div dir="ltr"><div style="font-size:small"><div style="font-size:13px;line-height:19.5px">CTO, Analytic Spot</div><div style="font-size:13px;line-height:19.5px">44 West Broadway #222</div><div style="font-size:13px;line-height:19.5px">Eugene, OR 97401<br></div><div style="font-size:13px;line-height:19.5px"><a href="http://analyticspot.com/" target="_blank">analyticspot.com</a> <span style="color:rgb(127,127,127);font-family:&#39;helvetica neue&#39;;font-size:11px;line-height:normal">• </span>425-296-6556</div></div><div style="font-size:small"><span style="line-height:19.5px"><a href="http://www.linkedin.com/in/oliverdain" target="_blank">www.linkedin.com/in/oliverdain</a></span></div></div></div></blockquote></div><div dir="ltr">-- <br></div><div data-smartmail="gmail_signature"><div dir="ltr"><div style="font-size:small"><div style="font-size:13px;line-height:19.5px">CTO, Analytic Spot</div><div style="font-size:13px;line-height:19.5px">44 West Broadway #222</div><div style="font-size:13px;line-height:19.5px">Eugene, OR 97401<br></div><div style="font-size:13px;line-height:19.5px"><a href="http://analyticspot.com/" style="z-index: 0;">analyticspot.com</a> <span style="color:rgb(127,127,127);font-family:&#39;helvetica neue&#39;;font-size:11px;line-height:normal">• </span>425-296-6556</div></div><div style="font-size:small"><span style="line-height:19.5px"><a href="http://www.linkedin.com/in/oliverdain" style="z-index: 0;">www.linkedin.com/in/oliverdain</a></span></div></div></div>