Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions dispatcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,16 @@
package httpdispatch

import (
"context"
"net/http"
)

type ContextKey int

const (
ContextKeyRoute ContextKey = iota
)

// Handler is an interface that can be registered to a route to handle HTTP
// requests. Like http.HandlerFunc, but has a third parameter for the values of
// wildcards (variables).
Expand Down Expand Up @@ -235,7 +242,8 @@ func (d *Dispatcher) ServeFiles(path string, fs http.FileSystem) {
// the same path with / without the trailing slash should be performed.
func (d *Dispatcher) Lookup(method, path string) (Handler, Params, bool) {
if root := d.trees[method]; root != nil {
return root.getValue(path)
handler, params, tsr, _ := root.getValue(path)
return handler, params, tsr
}

return nil, nil, false
Expand All @@ -250,10 +258,11 @@ func (d *Dispatcher) ServeHTTP(w http.ResponseWriter, r *http.Request) {
path := r.URL.Path

if root := d.trees[r.Method]; root != nil {
handler, params, tsr := root.getValue(path)
handler, params, tsr, route := root.getValue(path)

if handler != nil {
if !tsr || !d.RedirectTrailingSlash {
r = r.WithContext(context.WithValue(r.Context(), ContextKeyRoute, route))
handler.Handle(w, r, params)
return
}
Expand Down Expand Up @@ -382,7 +391,7 @@ func (d *Dispatcher) allowed(path, reqMethod string) (allow string) {
continue
}

handler, _, _ := d.trees[method].getValue(path)
handler, _, _, _ := d.trees[method].getValue(path)
if handler != nil {
// add request method to list of allowed methods
if len(allow) == 0 {
Expand Down
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module github.com/dolab/httpdispatch

go 1.12

require github.com/golib/assert v1.3.0
13 changes: 13 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23 h1:D21IyuvjDCshj1/qq+pCNd3VZOAEI9jy6Bi131YlXgI=
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
github.com/dolab/types v0.0.0-20181115071224-9f9f8147c117 h1:w7dIqNfQuaVBTSMCHx0q32k4gM3vN5ZRJKs73EhEnIA=
github.com/dolab/types v0.0.0-20181115071224-9f9f8147c117/go.mod h1:ye5M9z0YlIxn/I+vU4MlK18SuRdSl62pxjMI6CdlFGg=
github.com/golib/assert v1.3.0 h1:0zlb71NpB0q5FHMnYHyTnh+IS1+6YwW26ARpgN/0PA4=
github.com/golib/assert v1.3.0/go.mod h1:hyMJSCLv/DFMNpTYmEaAd+OYj1KqmB5pZ7WlKGj7sGo=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
9 changes: 8 additions & 1 deletion tree.go
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,8 @@ func (n *node) insertChild(numParams uint8, path, fullPath string, handle Handle
// made if a handle exists with an extra (without the) trailing slash for the
// given path.
// It returns handle also if a TSR is true. Its useful for quick fallback strategy.
func (n *node) getValue(path string) (handle Handler, p Params, tsr bool) {
func (n *node) getValue(path string) (handle Handler, p Params, tsr bool, route string) {
route = "/"
walk: // outer loop for walking the tree
for {
switch {
Expand All @@ -345,6 +346,7 @@ walk: // outer loop for walking the tree
for i := 0; i < len(n.indices); i++ {
if c == n.indices[i] {
n = n.children[i]
route += n.path
continue walk
}
}
Expand All @@ -354,6 +356,8 @@ walk: // outer loop for walking the tree

// handle wildcard child
n = n.children[0]
route += n.path

switch n.typo {
case param:
// save param value
Expand Down Expand Up @@ -387,6 +391,7 @@ walk: // outer loop for walking the tree
if len(n.children) > 0 {
path = path[end:]
n = n.children[0]
route += n.path

continue walk
}
Expand All @@ -404,6 +409,7 @@ walk: // outer loop for walking the tree
// trailing slash exists for TSR recommendation
if len(n.children) == 1 {
n = n.children[0]
route += n.path

tsr = (n.path == "/" && n.handle != nil)
if tsr {
Expand Down Expand Up @@ -457,6 +463,7 @@ walk: // outer loop for walking the tree
for i := 0; i < len(n.indices); i++ {
if n.indices[i] == '/' {
n = n.children[i]
route += n.path

tsr = (len(n.path) == 1 && n.handle != nil)
if tsr {
Expand Down
8 changes: 4 additions & 4 deletions tree_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ type testRequests []struct {

func checkRequests(t *testing.T, tree *node, requests testRequests) {
for _, request := range requests {
handler, ps, tsr := tree.getValue(request.path)
handler, ps, tsr, _ := tree.getValue(request.path)

if tsr {
if handler != nil {
Expand Down Expand Up @@ -470,7 +470,7 @@ func TestTreeTrailingSlashRedirect(t *testing.T) {
"/doc/",
}
for _, route := range tsrRoutes {
handler, _, tsr := tree.getValue(route)
handler, _, tsr, _ := tree.getValue(route)
if handler != nil && !tsr {
t.Fatalf("non-nil handler for TSR route '%s", route)
} else if !tsr {
Expand All @@ -487,7 +487,7 @@ func TestTreeTrailingSlashRedirect(t *testing.T) {
"/api/world/abc",
}
for _, route := range noTsrRoutes {
handler, _, tsr := tree.getValue(route)
handler, _, tsr, _ := tree.getValue(route)
if handler != nil {
t.Fatalf("non-nil handler for No-TSR route '%s", route)
} else if tsr {
Expand All @@ -506,7 +506,7 @@ func TestTreeRootTrailingSlashRedirect(t *testing.T) {
t.Fatalf("panic inserting test route: %v", recv)
}

handler, _, tsr := tree.getValue("/")
handler, _, tsr, _ := tree.getValue("/")
if handler != nil {
t.Fatalf("non-nil handler")
} else if tsr {
Expand Down