Skip to content

Commit 08c77de

Browse files
authored
Merge pull request #113 from jnichols-git/alpha-fixes
fix permitted characters, route matching, trailing slash
2 parents 9cf98f3 + 1a12f46 commit 08c77de

File tree

4 files changed

+78
-46
lines changed

4 files changed

+78
-46
lines changed

internal/path/path_test.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,16 @@ func TestNext(t *testing.T) {
3737
}
3838
i++
3939
}
40+
path = "/trailing/slash/"
41+
expected = []string{"/trailing", "/slash", "/"}
42+
i = 0
43+
for next = 0; next != -1; {
44+
tk, next = Next(path, next)
45+
if tk != expected[i] {
46+
t.Errorf("Expected '%s' at %d, got '%s'", expected[i], i, tk)
47+
}
48+
i++
49+
}
4050
}
4151

4252
func BenchmarkNext(b *testing.B) {

internal/route/part.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const (
1111
// Part matching
1212
regexp_wildcard = string(`/\[(.*?)\](.*)`)
1313
regexp_regex = string(`[/\]]{(.*)}`)
14-
regexp_part = string(`^(?:(\/\w*)|\/(\{.+\})?(\[.+\])?(\+)?)$`)
14+
regexp_part = string(`^(?:(\/[\w\.\~]*)|\/(\{.+\})?(\[.+\])?(\+)?)$`)
1515
)
1616

1717
// Regex used for parsing tokens

internal/router/router_test.go

Lines changed: 63 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -145,52 +145,71 @@ func runEvalRequest(t *testing.T,
145145
})
146146
}
147147

148+
// Test routes; method, route, positive test, negative test, expected response
149+
var testRoutes = [][5]string{
150+
{http.MethodGet, `/`, `/`, `/a`, "Got root"},
151+
{http.MethodGet, `/hello/world`, "/hello/world", "/hello", "Hello, World!"},
152+
{http.MethodGet, `/collection/`, "/collection/", "/collection", "Got root of /collection/"},
153+
{http.MethodGet, `/collection/{item}`, "/collection/testItem", "/collection/testItem/more", "Got collection item testItem"},
154+
{http.MethodGet, `/users/{id}[\d{4}]`, "/users/1234", "/users/abcd", "Got user 1234"},
155+
{http.MethodGet, `/file.txt`, "/file.txt", "/file", "Got file.txt"},
156+
{http.MethodGet, `/files/`, "/files/", "/files", "Got root of /files/"},
157+
{http.MethodGet, `/files/{filename}+`, "/files/test/file.txt", "/files/test/other.txt", "Got file /test/file.txt"},
158+
}
159+
160+
var testHandlers = []http.HandlerFunc{
161+
func(w http.ResponseWriter, r *http.Request) {
162+
w.Write([]byte("Got root"))
163+
},
164+
func(w http.ResponseWriter, r *http.Request) {
165+
w.Write([]byte("Hello, World!"))
166+
},
167+
func(w http.ResponseWriter, r *http.Request) {
168+
w.Write([]byte("Got root of /collection/"))
169+
},
170+
func(w http.ResponseWriter, r *http.Request) {
171+
w.Write([]byte("Got collection item " + rctx.GetParam(r.Context(), "item")))
172+
},
173+
func(w http.ResponseWriter, r *http.Request) {
174+
w.Write([]byte("Got user " + rctx.GetParam(r.Context(), "id")))
175+
},
176+
func(w http.ResponseWriter, r *http.Request) {
177+
w.Write([]byte("Got file.txt"))
178+
},
179+
func(w http.ResponseWriter, r *http.Request) {
180+
w.Write([]byte("Got root of /files/"))
181+
},
182+
func(w http.ResponseWriter, r *http.Request) {
183+
w.Write([]byte("Got file " + rctx.GetParam(r.Context(), "filename")))
184+
},
185+
}
186+
148187
func TestBasicRoutes(t *testing.T) {
149-
r := Default()
150-
r.Handle(http.MethodGet, "/", okHandler("root"))
151-
r.HandleFunc(http.MethodGet, "/middlewareTest", genericValueHandler("mwkey"))
152-
r.HandleRoute(route.Declare(http.MethodGet, "/{wildcard}"), rpHandler("wildcard"))
153-
r.HandleRouteFunc(route.Declare(http.MethodGet, `/route/[[a-zA-Z]+]`), okHandler("letters"))
154-
r.Handle(http.MethodGet, `/route/{id}[[\w]{4}]`, rpHandler("id"))
155-
r.HandleFunc(http.MethodGet, `/static/file/{filename}[\w+(?:\.\w+)?]+`, rpHandler("filename"))
156-
r.Use(testMiddleware)
188+
rt := Default()
189+
for i, tr := range testRoutes {
190+
rt.HandleFunc(tr[0], tr[1], testHandlers[i])
191+
}
157192

158-
s := httptest.NewServer(r)
159-
runEvalRequest(t, s, "", reqGen(http.MethodGet), map[string]any{
160-
"code": http.StatusOK,
161-
"body": "root",
162-
})
163-
runEvalRequest(t, s, "/", reqGen(http.MethodGet), map[string]any{
164-
"code": http.StatusOK,
165-
"body": "root",
166-
})
167-
runEvalRequest(t, s, "/test", reqGen(http.MethodGet), map[string]any{
168-
"code": http.StatusOK,
169-
"body": "test",
170-
})
171-
runEvalRequest(t, s, "/route/word", reqGen(http.MethodGet), map[string]any{
172-
"code": http.StatusOK,
173-
"body": "letters",
174-
})
175-
runEvalRequest(t, s, "/route/id01", reqGen(http.MethodGet), map[string]any{
176-
"code": http.StatusOK,
177-
"body": "id01",
178-
})
179-
runEvalRequest(t, s, "/route/n0tID", reqGen(http.MethodGet), map[string]any{
180-
"code": http.StatusNotFound,
181-
})
182-
runEvalRequest(t, s, "/static/file/docs/README.md", reqGen(http.MethodGet), map[string]any{
183-
"code": http.StatusOK,
184-
"body": "/docs/README.md",
185-
})
186-
runEvalRequest(t, s, "/static/file", reqGen(http.MethodGet), map[string]any{
187-
"code": http.StatusInternalServerError,
188-
"body": "router param filename not found",
189-
})
190-
runEvalRequest(t, s, "/middlewareTest", reqGen(http.MethodGet), map[string]any{
191-
"code": http.StatusOK,
192-
"body": "mwval",
193-
})
193+
for _, tr := range testRoutes {
194+
t.Run(tr[1], func(t *testing.T) {
195+
// Test positive case
196+
w := httptest.NewRecorder()
197+
req := httptest.NewRequest(tr[0], tr[2], nil)
198+
rt.ServeHTTP(w, req)
199+
res := w.Body.String()
200+
if w.Result().StatusCode != http.StatusOK || res != tr[4] {
201+
t.Error(tr[4], res)
202+
}
203+
// Test negative case
204+
w = httptest.NewRecorder()
205+
req = httptest.NewRequest(tr[0], tr[3], nil)
206+
rt.ServeHTTP(w, req)
207+
res = w.Body.String()
208+
if w.Result().StatusCode == http.StatusOK && res == tr[4] {
209+
t.Error(tr[3], "should have failed")
210+
}
211+
})
212+
}
194213
}
195214

196215
func TestEdgeCaseRoutes(t *testing.T) {

internal/tree/tree.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,6 @@ func (n *node) propagate(r *route.Route, ps []route.Part, leaf_id int64) {
6161
}
6262
child := createNode(next)
6363
child.propagate(r, ps[1:], leaf_id)
64-
child.propagate(r, ps[1:], leaf_id)
6564
n.children = append(n.children, child)
6665
}
6766

@@ -92,6 +91,10 @@ func (n *node) match(req *http.Request, expr string, last int) int64 {
9291
return NO_LEAF_ID
9392
}
9493
}
94+
// If next is -1, we've exhausted the path without matching.
95+
if next == -1 {
96+
return NO_LEAF_ID
97+
}
9598
// Iterate through the children of this node.
9699
for _, child := range n.children {
97100
if match_leaf_id := child.match(req, expr, next); match_leaf_id != NO_LEAF_ID {

0 commit comments

Comments
 (0)