Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Swap out julienschmidt/httprouter #605

Merged
merged 6 commits into from
Jul 16, 2019
Merged

Swap out julienschmidt/httprouter #605

merged 6 commits into from
Jul 16, 2019

Conversation

ChuntaoLu
Copy link
Contributor

@ChuntaoLu ChuntaoLu commented Jul 2, 2019

This PR swaps out julienschmidt/httprouter with an inhouse router implementation that has similar interface to httprouter. The rationale is to overcome the limitations of julienschmidt/httprouter#6 and julienschmidt/httprouter#175.

runtime/router/router.go Outdated Show resolved Hide resolved
runtime/router/trie.go Outdated Show resolved Hide resolved
runtime/router/trie.go Outdated Show resolved Hide resolved
for k < n && path[k] != '/' {
k++
}
params = append(params, Param{t.key[oj:j], path[ok:k]})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: use continue rather than more blocks

runtime/router/trie.go Outdated Show resolved Hide resolved
runtime/router/trie.go Outdated Show resolved Hide resolved
@coveralls
Copy link

coveralls commented Jul 2, 2019

Coverage Status

Coverage increased (+0.5%) to 67.606% when pulling e7171ab on lu.router into 9df42fb on master.

if j < m {
// path matches up to node key's second to last character,
// the last char of node key is "*" and path is no shorter than longest matched prefix
if t.key[j:] == "*" && k < n {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does registering /a* match /ab/c?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// validate "*"
if strings.Contains(path, "*") && !strings.HasSuffix(path, "/*") {
return errors.New("/* must be the last path segment")
}
if strings.Count(path, "*") > 1 {
return errors.New("path can not contain more than one *")
}
guards against registering /a*

runtime/router/trie.go Outdated Show resolved Hide resolved
runtime/router/trie.go Outdated Show resolved Hide resolved
runtime/router/trie.go Outdated Show resolved Hide resolved
runtime/router/trie.go Outdated Show resolved Hide resolved
runtime/router/trie.go Outdated Show resolved Hide resolved
runtime/router/trie.go Outdated Show resolved Hide resolved
// conflicts caused by ":" is only possible when j == m
if j == m {
for _, c := range t.children {
if c.key[0] == path[k] || c.key[0] == ':' || path[k] == ':' {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this logic be covered by c.get already?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is, mostly thinking of minimizing the call stack. Removed this check with the updated code.

for _, c := range t.children {
if c.key[0] == path[k] || c.key[0] == ':' || path[k] == ':' {
if _, _, err := c.get(path[k:]); err == nil {
return errExist
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You will probably want to print out the match that conflicted for diagnostic purposes.

// It implements a similar interface to the one in github.com/julienschmidt/httprouter,
// the main differences are:
// 1. this router does not treat "/a/:b" and "/a/b/c" as conflicts (https://github.com/julienschmidt/httprouter/issues/175)
// 2. this router does not treat "/a/:b" and "/a/:c" as different routes (https://github.com/julienschmidt/httprouter/issues/6)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this router does not allow /a/:b and /a/:c to be registered at all.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

// and HTTP status code 405.
// If no other Method is allowed, the request is delegated to the NotFound
// handler.
HandleMethodNotAllowed bool
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to support this? When this flag is false, then we return 404. When this flag is true, we return 405. What's wrong with always returning 405 and getting rid of this flag?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The difference is that when both this flag and MethodNotAllowed is present, the request is handled by the MethodNotAllowed handler. We can totally get rid of this and always return 405, I don't feel strongly about it. How do you think?

i++
}

// Find the first character that differs between "path" and this node's key, if it exists.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code is quite confusing. I am guessing you are trying to have not just O(n) on the length of key but that you only process each character of key and path once and allocate no additional memory other than the indices.

I think it may be much more readable if you split the string into path components and processed the equality of path components in sequence. Splitting the string into path components will roughly double the total memory used to register routes, but it's probably a better trade-off because routes are only registered once within the application.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The trie does not necessarily split node on path segments, so splitting the path into segments and comparing equality doesn't work.

Register /ab/c
trie: [/ab/c]

Register /a
trie:

[/a]
  |
[b/c]

@ChuntaoLu
Copy link
Contributor Author

Benchmark between zanzibar router and http router:
https://github.com/ChuntaoLu/gorouter-bench

@ChuntaoLu ChuntaoLu merged commit 2ebd5a4 into master Jul 16, 2019
@ChuntaoLu ChuntaoLu deleted the lu.router branch July 16, 2019 22:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants