Source file
src/net/http/servemux121.go
1
2
3
4
5 package http
6
7
8
9
10
11
12
13 import (
14 "internal/godebug"
15 "net/url"
16 "sort"
17 "strings"
18 "sync"
19 )
20
21 var httpmuxgo121 = godebug.New("httpmuxgo121")
22
23 var use121 bool
24
25
26
27 func init() {
28 if httpmuxgo121.Value() == "1" {
29 use121 = true
30 httpmuxgo121.IncNonDefault()
31 }
32 }
33
34
35 type serveMux121 struct {
36 mu sync.RWMutex
37 m map[string]muxEntry
38 es []muxEntry
39 hosts bool
40 }
41
42 type muxEntry struct {
43 h Handler
44 pattern string
45 }
46
47
48 func (mux *serveMux121) handle(pattern string, handler Handler) {
49 mux.mu.Lock()
50 defer mux.mu.Unlock()
51
52 if pattern == "" {
53 panic("http: invalid pattern")
54 }
55 if handler == nil {
56 panic("http: nil handler")
57 }
58 if _, exist := mux.m[pattern]; exist {
59 panic("http: multiple registrations for " + pattern)
60 }
61
62 if mux.m == nil {
63 mux.m = make(map[string]muxEntry)
64 }
65 e := muxEntry{h: handler, pattern: pattern}
66 mux.m[pattern] = e
67 if pattern[len(pattern)-1] == '/' {
68 mux.es = appendSorted(mux.es, e)
69 }
70
71 if pattern[0] != '/' {
72 mux.hosts = true
73 }
74 }
75
76 func appendSorted(es []muxEntry, e muxEntry) []muxEntry {
77 n := len(es)
78 i := sort.Search(n, func(i int) bool {
79 return len(es[i].pattern) < len(e.pattern)
80 })
81 if i == n {
82 return append(es, e)
83 }
84
85 es = append(es, muxEntry{})
86 copy(es[i+1:], es[i:])
87 es[i] = e
88 return es
89 }
90
91
92 func (mux *serveMux121) handleFunc(pattern string, handler func(ResponseWriter, *Request)) {
93 if handler == nil {
94 panic("http: nil handler")
95 }
96 mux.handle(pattern, HandlerFunc(handler))
97 }
98
99
100 func (mux *serveMux121) findHandler(r *Request) (h Handler, pattern string) {
101
102
103 if r.Method == "CONNECT" {
104
105
106
107 if u, ok := mux.redirectToPathSlash(r.URL.Host, r.URL.Path, r.URL); ok {
108 return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
109 }
110
111 return mux.handler(r.Host, r.URL.Path)
112 }
113
114
115
116 host := stripHostPort(r.Host)
117 path := cleanPath(r.URL.Path)
118
119
120
121 if u, ok := mux.redirectToPathSlash(host, path, r.URL); ok {
122 return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
123 }
124
125 if path != r.URL.Path {
126 _, pattern = mux.handler(host, path)
127 u := &url.URL{Path: path, RawQuery: r.URL.RawQuery}
128 return RedirectHandler(u.String(), StatusMovedPermanently), pattern
129 }
130
131 return mux.handler(host, r.URL.Path)
132 }
133
134
135
136 func (mux *serveMux121) handler(host, path string) (h Handler, pattern string) {
137 mux.mu.RLock()
138 defer mux.mu.RUnlock()
139
140
141 if mux.hosts {
142 h, pattern = mux.match(host + path)
143 }
144 if h == nil {
145 h, pattern = mux.match(path)
146 }
147 if h == nil {
148 h, pattern = NotFoundHandler(), ""
149 }
150 return
151 }
152
153
154
155 func (mux *serveMux121) match(path string) (h Handler, pattern string) {
156
157 v, ok := mux.m[path]
158 if ok {
159 return v.h, v.pattern
160 }
161
162
163
164 for _, e := range mux.es {
165 if strings.HasPrefix(path, e.pattern) {
166 return e.h, e.pattern
167 }
168 }
169 return nil, ""
170 }
171
172
173
174
175
176 func (mux *serveMux121) redirectToPathSlash(host, path string, u *url.URL) (*url.URL, bool) {
177 mux.mu.RLock()
178 shouldRedirect := mux.shouldRedirectRLocked(host, path)
179 mux.mu.RUnlock()
180 if !shouldRedirect {
181 return u, false
182 }
183 path = path + "/"
184 u = &url.URL{Path: path, RawQuery: u.RawQuery}
185 return u, true
186 }
187
188
189
190
191 func (mux *serveMux121) shouldRedirectRLocked(host, path string) bool {
192 p := []string{path, host + path}
193
194 for _, c := range p {
195 if _, exist := mux.m[c]; exist {
196 return false
197 }
198 }
199
200 n := len(path)
201 if n == 0 {
202 return false
203 }
204 for _, c := range p {
205 if _, exist := mux.m[c+"/"]; exist {
206 return path[n-1] != '/'
207 }
208 }
209
210 return false
211 }
212
View as plain text