OLD | NEW |
1 // Copyright 2009 The Go Authors. All rights reserved. | 1 // Copyright 2009 The Go Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style | 2 // Use of this source code is governed by a BSD-style |
3 // license that can be found in the LICENSE file. | 3 // license that can be found in the LICENSE file. |
4 | 4 |
5 // HTTP file system request handler | 5 // HTTP file system request handler |
6 | 6 |
7 package http | 7 package http |
8 | 8 |
9 import ( | 9 import ( |
10 "fmt" | 10 "fmt" |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
94 } | 94 } |
95 } | 95 } |
96 fmt.Fprintf(w, "</pre>\n") | 96 fmt.Fprintf(w, "</pre>\n") |
97 } | 97 } |
98 | 98 |
99 // name is '/'-separated, not filepath.Separator. | 99 // name is '/'-separated, not filepath.Separator. |
100 func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirec
t bool) { | 100 func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirec
t bool) { |
101 const indexPage = "/index.html" | 101 const indexPage = "/index.html" |
102 | 102 |
103 // redirect .../index.html to .../ | 103 // redirect .../index.html to .../ |
| 104 // can't use Redirect() because that would make the path absolute, |
| 105 // which would be a problem running under StripPrefix |
104 if strings.HasSuffix(r.URL.Path, indexPage) { | 106 if strings.HasSuffix(r.URL.Path, indexPage) { |
105 » » Redirect(w, r, r.URL.Path[0:len(r.URL.Path)-len(indexPage)+1], S
tatusMovedPermanently) | 107 » » localRedirect(w, r, "./") |
106 return | 108 return |
107 } | 109 } |
108 | 110 |
109 f, err := fs.Open(name) | 111 f, err := fs.Open(name) |
110 if err != nil { | 112 if err != nil { |
111 // TODO expose actual error? | 113 // TODO expose actual error? |
112 NotFound(w, r) | 114 NotFound(w, r) |
113 return | 115 return |
114 } | 116 } |
115 defer f.Close() | 117 defer f.Close() |
116 | 118 |
117 d, err1 := f.Stat() | 119 d, err1 := f.Stat() |
118 if err1 != nil { | 120 if err1 != nil { |
119 // TODO expose actual error? | 121 // TODO expose actual error? |
120 NotFound(w, r) | 122 NotFound(w, r) |
121 return | 123 return |
122 } | 124 } |
123 | 125 |
124 if redirect { | 126 if redirect { |
125 // redirect to canonical path: / at end of directory url | 127 // redirect to canonical path: / at end of directory url |
126 // r.URL.Path always begins with / | 128 // r.URL.Path always begins with / |
127 url := r.URL.Path | 129 url := r.URL.Path |
128 if d.IsDirectory() { | 130 if d.IsDirectory() { |
129 if url[len(url)-1] != '/' { | 131 if url[len(url)-1] != '/' { |
130 » » » » Redirect(w, r, url+"/", StatusMovedPermanently) | 132 » » » » localRedirect(w, r, path.Base(url)+"/") |
131 return | 133 return |
132 } | 134 } |
133 } else { | 135 } else { |
134 if url[len(url)-1] == '/' { | 136 if url[len(url)-1] == '/' { |
135 » » » » Redirect(w, r, url[0:len(url)-1], StatusMovedPer
manently) | 137 » » » » localRedirect(w, r, "../"+path.Base(url)) |
136 return | 138 return |
137 } | 139 } |
138 } | 140 } |
139 } | 141 } |
140 | 142 |
141 if t, _ := time.Parse(TimeFormat, r.Header.Get("If-Modified-Since")); t
!= nil && d.Mtime_ns/1e9 <= t.Seconds() { | 143 if t, _ := time.Parse(TimeFormat, r.Header.Get("If-Modified-Since")); t
!= nil && d.Mtime_ns/1e9 <= t.Seconds() { |
142 w.WriteHeader(StatusNotModified) | 144 w.WriteHeader(StatusNotModified) |
143 return | 145 return |
144 } | 146 } |
145 w.Header().Set("Last-Modified", time.SecondsToUTC(d.Mtime_ns/1e9).Format
(TimeFormat)) | 147 w.Header().Set("Last-Modified", time.SecondsToUTC(d.Mtime_ns/1e9).Format
(TimeFormat)) |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
213 w.Header().Set("Content-Length", strconv.Itoa64(size)) | 215 w.Header().Set("Content-Length", strconv.Itoa64(size)) |
214 } | 216 } |
215 | 217 |
216 w.WriteHeader(code) | 218 w.WriteHeader(code) |
217 | 219 |
218 if r.Method != "HEAD" { | 220 if r.Method != "HEAD" { |
219 io.Copyn(w, f, size) | 221 io.Copyn(w, f, size) |
220 } | 222 } |
221 } | 223 } |
222 | 224 |
| 225 // localRedirect gives a Moved Permanently response. |
| 226 // It does not convert relative paths to absolute paths like Redirect does. |
| 227 func localRedirect(w ResponseWriter, r *Request, newPath string) { |
| 228 if q := r.URL.RawQuery; q != "" { |
| 229 newPath += "?" + q |
| 230 } |
| 231 w.Header().Set("Location", newPath) |
| 232 w.WriteHeader(StatusMovedPermanently) |
| 233 } |
| 234 |
223 // ServeFile replies to the request with the contents of the named file or direc
tory. | 235 // ServeFile replies to the request with the contents of the named file or direc
tory. |
224 func ServeFile(w ResponseWriter, r *Request, name string) { | 236 func ServeFile(w ResponseWriter, r *Request, name string) { |
225 dir, file := filepath.Split(name) | 237 dir, file := filepath.Split(name) |
226 serveFile(w, r, Dir(dir), file, false) | 238 serveFile(w, r, Dir(dir), file, false) |
227 } | 239 } |
228 | 240 |
229 type fileHandler struct { | 241 type fileHandler struct { |
230 root FileSystem | 242 root FileSystem |
231 } | 243 } |
232 | 244 |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
301 if i >= size { | 313 if i >= size { |
302 i = size - 1 | 314 i = size - 1 |
303 } | 315 } |
304 r.length = i - r.start + 1 | 316 r.length = i - r.start + 1 |
305 } | 317 } |
306 } | 318 } |
307 ranges = append(ranges, r) | 319 ranges = append(ranges, r) |
308 } | 320 } |
309 return ranges, nil | 321 return ranges, nil |
310 } | 322 } |
OLD | NEW |