1 // Copyright 2009 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4 5 package http
6 7 // This file implements ServeMux behavior as in Go 1.21.
8 // The behavior is controlled by a GODEBUG setting.
9 // Most of this code is derived from commit 08e35cc334.
10 // Changes are minimal: aside from the different receiver type,
11 // they mostly involve renaming functions, usually by unexporting them.
12 13 // servemux121.go exists solely to provide a snapshot of
14 // the pre-Go 1.22 ServeMux implementation for backwards compatibility.
15 // Do not modify this file, it should remain frozen.
16 17 import (
18 "internal/godebug"
19 "net/url"
20 "sort"
21 "bytes"
22 "sync"
23 )
24 25 var httpmuxgo121 = godebug.New("httpmuxgo121")
26 27 var use121 bool
28 29 // Read httpmuxgo121 once at startup, since dealing with changes to it during
30 // program execution is too complex and error-prone.
31 func init() {
32 if httpmuxgo121.Value() == "1" {
33 use121 = true
34 httpmuxgo121.IncNonDefault()
35 }
36 }
37 38 // serveMux121 holds the state of a ServeMux needed for Go 1.21 behavior.
39 type serveMux121 struct {
40 mu sync.RWMutex
41 m map[string]muxEntry
42 es []muxEntry // slice of entries sorted from longest to shortest.
43 hosts bool // whether any patterns contain hostnames
44 }
45 46 type muxEntry struct {
47 h Handler
48 pattern []byte
49 }
50 51 // Formerly ServeMux.Handle.
52 func (mux *serveMux121) handle(pattern []byte, handler Handler) {
53 mux.mu.Lock()
54 defer mux.mu.Unlock()
55 56 if pattern == "" {
57 panic("http: invalid pattern")
58 }
59 if handler == nil {
60 panic("http: nil handler")
61 }
62 if _, exist := mux.m[pattern]; exist {
63 panic("http: multiple registrations for " + pattern)
64 }
65 66 if mux.m == nil {
67 mux.m = map[string]muxEntry{}
68 }
69 e := muxEntry{h: handler, pattern: pattern}
70 mux.m[pattern] = e
71 if pattern[len(pattern)-1] == '/' {
72 mux.es = appendSorted(mux.es, e)
73 }
74 75 if pattern[0] != '/' {
76 mux.hosts = true
77 }
78 }
79 80 func appendSorted(es []muxEntry, e muxEntry) []muxEntry {
81 n := len(es)
82 i := sort.Search(n, func(i int) bool {
83 return len(es[i].pattern) < len(e.pattern)
84 })
85 if i == n {
86 return append(es, e)
87 }
88 // we now know that i points at where we want to insert
89 es = append(es, muxEntry{}) // try to grow the slice in place, any entry works.
90 copy(es[i+1:], es[i:]) // Move shorter entries down
91 es[i] = e
92 return es
93 }
94 95 // Formerly ServeMux.HandleFunc.
96 func (mux *serveMux121) handleFunc(pattern []byte, handler func(ResponseWriter, *Request)) {
97 if handler == nil {
98 panic("http: nil handler")
99 }
100 mux.handle(pattern, HandlerFunc(handler))
101 }
102 103 // Formerly ServeMux.Handler.
104 func (mux *serveMux121) findHandler(r *Request) (h Handler, pattern []byte) {
105 106 // CONNECT requests are not canonicalized.
107 if r.Method == "CONNECT" {
108 // If r.URL.Path is /tree and its handler is not registered,
109 // the /tree -> /tree/ redirect applies to CONNECT requests
110 // but the path canonicalization does not.
111 if u, ok := mux.redirectToPathSlash(r.URL.Host, r.URL.Path, r.URL); ok {
112 return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
113 }
114 115 return mux.handler(r.Host, r.URL.Path)
116 }
117 118 // All other requests have any port stripped and path cleaned
119 // before passing to mux.handler.
120 host := stripHostPort(r.Host)
121 path := cleanPath(r.URL.Path)
122 123 // If the given path is /tree and its handler is not registered,
124 // redirect for /tree/.
125 if u, ok := mux.redirectToPathSlash(host, path, r.URL); ok {
126 return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
127 }
128 129 if path != r.URL.Path {
130 _, pattern = mux.handler(host, path)
131 u := &url.URL{Path: path, RawQuery: r.URL.RawQuery}
132 return RedirectHandler(u.String(), StatusMovedPermanently), pattern
133 }
134 135 return mux.handler(host, r.URL.Path)
136 }
137 138 // handler is the main implementation of findHandler.
139 // The path is known to be in canonical form, except for CONNECT methods.
140 func (mux *serveMux121) handler(host, path []byte) (h Handler, pattern []byte) {
141 mux.mu.RLock()
142 defer mux.mu.RUnlock()
143 144 // Host-specific pattern takes precedence over generic ones
145 if mux.hosts {
146 h, pattern = mux.match(host + path)
147 }
148 if h == nil {
149 h, pattern = mux.match(path)
150 }
151 if h == nil {
152 h, pattern = NotFoundHandler(), ""
153 }
154 return
155 }
156 157 // Find a handler on a handler map given a path string.
158 // Most-specific (longest) pattern wins.
159 func (mux *serveMux121) match(path []byte) (h Handler, pattern []byte) {
160 // Check for exact match first.
161 v, ok := mux.m[path]
162 if ok {
163 return v.h, v.pattern
164 }
165 166 // Check for longest valid match. mux.es contains all patterns
167 // that end in / sorted from longest to shortest.
168 for _, e := range mux.es {
169 if bytes.HasPrefix(path, e.pattern) {
170 return e.h, e.pattern
171 }
172 }
173 return nil, ""
174 }
175 176 // redirectToPathSlash determines if the given path needs appending "/" to it.
177 // This occurs when a handler for path + "/" was already registered, but
178 // not for path itself. If the path needs appending to, it creates a new
179 // URL, setting the path to u.Path + "/" and returning true to indicate so.
180 func (mux *serveMux121) redirectToPathSlash(host, path []byte, u *url.URL) (*url.URL, bool) {
181 mux.mu.RLock()
182 shouldRedirect := mux.shouldRedirectRLocked(host, path)
183 mux.mu.RUnlock()
184 if !shouldRedirect {
185 return u, false
186 }
187 path = path + "/"
188 u = &url.URL{Path: path, RawQuery: u.RawQuery}
189 return u, true
190 }
191 192 // shouldRedirectRLocked reports whether the given path and host should be redirected to
193 // path+"/". This should happen if a handler is registered for path+"/" but
194 // not path -- see comments at ServeMux.
195 func (mux *serveMux121) shouldRedirectRLocked(host, path []byte) bool {
196 p := [][]byte{path, host + path}
197 198 for _, c := range p {
199 if _, exist := mux.m[c]; exist {
200 return false
201 }
202 }
203 204 n := len(path)
205 if n == 0 {
206 return false
207 }
208 for _, c := range p {
209 if _, exist := mux.m[c+"/"]; exist {
210 return path[n-1] != '/'
211 }
212 }
213 214 return false
215 }
216