Skip to content

Commit cf05873

Browse files
committed
net/http: represent multi wildcards properly
The routing tree used for matching ServeMux patterns used the key "*" to hold a child node for a multi-segment wildcard. The problem is that "*" is a valid path segment, which confused the matching algorithm: it would fetch the multi wildcard child when looking for the literal child for "*". Eschew clever encodings. Use a separate field in the node to represent the multi wildcard child. Fixes #67067. Change-Id: I300ca08b8628f5367626cf41979f6c238ed8c831 Reviewed-on: https://go-review.googlesource.com/c/go/+/582115 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Damien Neil <[email protected]>
1 parent 8509f69 commit cf05873

File tree

3 files changed

+22
-5
lines changed

3 files changed

+22
-5
lines changed

src/net/http/request_test.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1559,6 +1559,14 @@ func TestPathValue(t *testing.T) {
15591559
"other": "there/is//more",
15601560
},
15611561
},
1562+
{
1563+
"/names/{name}/{other...}",
1564+
"/names/n/*",
1565+
map[string]string{
1566+
"name": "n",
1567+
"other": "*",
1568+
},
1569+
},
15621570
} {
15631571
mux := NewServeMux()
15641572
mux.HandleFunc(test.pattern, func(w ResponseWriter, r *Request) {

src/net/http/routing_tree.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ type routingNode struct {
3434
// special children keys:
3535
// "/" trailing slash (resulting from {$})
3636
// "" single wildcard
37-
// "*" multi wildcard
3837
children mapping[string, *routingNode]
38+
multiChild *routingNode // child with multi wildcard
3939
emptyChild *routingNode // optimization: child with key ""
4040
}
4141

@@ -63,7 +63,9 @@ func (n *routingNode) addSegments(segs []segment, p *pattern, h Handler) {
6363
if len(segs) != 1 {
6464
panic("multi wildcard not last")
6565
}
66-
n.addChild("*").set(p, h)
66+
c := &routingNode{}
67+
n.multiChild = c
68+
c.set(p, h)
6769
} else if seg.wild {
6870
n.addChild("").addSegments(segs[1:], p, h)
6971
} else {
@@ -185,7 +187,7 @@ func (n *routingNode) matchPath(path string, matches []string) (*routingNode, []
185187
}
186188
// Lastly, match the pattern (there can be at most one) that has a multi
187189
// wildcard in this position to the rest of the path.
188-
if c := n.findChild("*"); c != nil {
190+
if c := n.multiChild; c != nil {
189191
// Don't record a match for a nameless wildcard (which arises from a
190192
// trailing slash in the pattern).
191193
if c.pattern.lastSegment().s != "" {

src/net/http/routing_tree_test.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,10 @@ func TestRoutingAddPattern(t *testing.T) {
7272
"/a/b"
7373
"":
7474
"/a/b/{y}"
75-
"*":
76-
"/a/b/{x...}"
7775
"/":
7876
"/a/b/{$}"
77+
MULTI:
78+
"/a/b/{x...}"
7979
"g":
8080
"":
8181
"j":
@@ -172,6 +172,8 @@ func TestRoutingNodeMatch(t *testing.T) {
172172
"HEAD /headwins", nil},
173173
{"GET", "", "/path/to/file",
174174
"/path/{p...}", []string{"to/file"}},
175+
{"GET", "", "/path/*",
176+
"/path/{p...}", []string{"*"}},
175177
})
176178

177179
// A pattern ending in {$} should only match URLS with a trailing slash.
@@ -291,4 +293,9 @@ func (n *routingNode) print(w io.Writer, level int) {
291293
n, _ := n.children.find(k)
292294
n.print(w, level+1)
293295
}
296+
297+
if n.multiChild != nil {
298+
fmt.Fprintf(w, "%sMULTI:\n", indent)
299+
n.multiChild.print(w, level+1)
300+
}
294301
}

0 commit comments

Comments
 (0)