juxt / bidi

Bidirectional URI routing

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Matching can throw exceptions

solicode opened this issue · comments

match-route can throw exceptions due to just-path using java.net.URI to extract the path part of a URI. java.net.URI is rather strict, and will throw URISyntaxException quite aggressively.

I would expect match-route to never throw exceptions, and only return nil on failure for "no route matched". The exceptions seem like an implementation detail that is leaking. Even more so because:

  • java.net.URI and goog.Uri don't agree on what a valid URI is and when to throw an exception.
  • make-handler doesn't seem to take account that match-route* will throw exceptions. Because of this, these exceptions can't be properly handled by someone who uses make-handler because by then it's too late.

As for the exceptions, here are a few examples:

(match-route ["/" :index] "//") ; URISyntaxException: Expected authority at index 2
(match-route ["/" :index] "/%") ; URISyntaxException: Malformed escape pair at index 1
(match-route ["/" :index] ":a") ; URISyntaxException: Expected scheme name at index 0

Even for URIs that don't really make much sense, they do result in a valid HTTP request, and :uri in the request map is actually populated with /% and so on (it would depend on the server, but both aleph and http-kit do this).

The fix for this might be very simple, but it depends what path means in match-route. I take it to mean just the path part of a URI, excluding scheme, authority, query string, etc. In other words, I wouldn't expect this to be intended usage:

(match-route ["/" :index] "http://localhost/")

It actually returns {:handler :index} though.

And it gets stranger with things like:

(match-route ["/" :index] "///") ; {:handler :index}

Anyway, if my interpretation of the intended behavior is correct, it should be possible to write a fix for this where both Clojure and ClojureScript behave exactly the same.

I agree. We shouldn't be leaking implementation details there.
Exceptions in Clojure are hard, we really don't have a great error
handling methodology, especially for interop.

The behavior is essentially undefined for those cases. just-path is
essentially intended to return the url without query parameters & such.
We should probably have just-path internally return nil when an
exception is thrown.

A full URL works because of the parsing, I would guess. just-path most
likely coerces URLs to path form where it is possible. I'm not sure what
the best behaviour here would be. It's something that currently falls
into "undefined." We could do a check on whether things like host & such
were included, and return nil if so. Or we could continue making a best
effort of parsing, so we cover as many scenarios as possible.

On Fri, Mar 04, 2016 at 05:34:08AM -0800, Solicode wrote:

match-route can throw exceptions due to just-path using java.net.URI to extract the path part of a URI. java.net.URI is rather strict, and will throw URISyntaxException quite aggressively.

I would expect match-route to never throw exceptions, and only return nil on failure for "no route matched". The exceptions seem like an implementation detail that is leaking. Even more so because:

  • java.net.URI and goog.Uri don't agree on what a valid URI is and when to throw an exception.
  • make-handler doesn't seem to take account that match-route* will throw exceptions. Because of this, these exceptions can't be properly handled by someone who uses make-handler because by then it's too late.

As for the exceptions, here are a few examples:

(match-route ["/" :index] "//") ; URISyntaxException: Expected authority at index 2
(match-route ["/" :index] "/%") ; URISyntaxException: Malformed escape pair at index 1
(match-route ["/" :index] ":a") ; URISyntaxException: Expected scheme name at index 0

Even for URIs that don't really make much sense, they do result in a valid HTTP request, and :uri in the request map is actually populated with /% and so on (it would depend on the server, but both aleph and http-kit do this).

The fix for this might be very simple, but it depends what path means in match-route. I take it to mean just the path part of a URI, excluding scheme, authority, query string, etc. In other words, I wouldn't expect this to be intended usage:

(match-route ["/" :index] "http://localhost/")

It actually returns {:handler :index} though.

And it gets stranger with things like:

(match-route ["/" :index] "///") ; {:handler :index}

Anyway, if my interpretation of the intended behavior is correct, it should be possible to write a fix for this where both Clojure and ClojureScript behave exactly the same.


Reply to this email directly or view it on GitHub:
#129

Right, it does seem undefined right now. At least there doesn't seem to be anything in the README, documentation, or tests about what should happen in this case.

So like I said, it depends on what path is supposed to mean. If it means "path + query string" (where it's okay to pass query string with the expectation that it will be stripped), then I think the just-path proposed solution in the description of PR #128 (which I only found just now) would actually solve the problem. If stripping scheme, authority, and port is also expected, then it gets more complicated.

The only change I would suggest to that proposed solution is to use indexOf and substring instead of working with seqs on String for performance reasons.