bidi icon indicating copy to clipboard operation
bidi copied to clipboard

Matching can throw exceptions

Open solicode opened this issue 8 years ago • 2 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.

solicode avatar Mar 04 '16 13:03 solicode

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: https://github.com/juxt/bidi/issues/129

SevereOverfl0w avatar Mar 04 '16 13:03 SevereOverfl0w

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.

solicode avatar Mar 04 '16 14:03 solicode