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.
5 // HTTP file system request handler
20 // Heuristic: b is text if it is valid UTF-8 and doesn't
21 // contain any unprintable ASCII or Unicode characters.
22 func isText(b
[]byte) bool {
23 for len(b
) > 0 && utf8
.FullRune(b
) {
24 rune
, size
:= utf8
.DecodeRune(b
)
25 if size
== 1 && rune
== utf8
.RuneError
{
29 if 0x80 <= rune
&& rune
<= 0x9F {
34 case '\n', '\r', '\t':
46 func dirList(w ResponseWriter
, f
*os
.File
) {
47 fmt
.Fprintf(w
, "<pre>\n")
49 dirs
, err
:= f
.Readdir(100)
50 if err
!= nil ||
len(dirs
) == 0 {
53 for _
, d
:= range dirs
{
59 fmt
.Fprintf(w
, "<a href=\"%s\">%s</a>\n", name
, name
)
62 fmt
.Fprintf(w
, "</pre>\n")
65 func serveFile(w ResponseWriter
, r
*Request
, name
string, redirect
bool) {
66 const indexPage
= "/index.html"
68 // redirect .../index.html to .../
69 if strings
.HasSuffix(r
.URL
.Path
, indexPage
) {
70 Redirect(w
, r
, r
.URL
.Path
[0:len(r
.URL
.Path
)-len(indexPage
)+1], StatusMovedPermanently
)
74 f
, err
:= os
.Open(name
, os
.O_RDONLY
, 0)
76 // TODO expose actual error?
84 // TODO expose actual error?
90 // redirect to canonical path: / at end of directory url
91 // r.URL.Path always begins with /
94 if url
[len(url
)-1] != '/' {
95 Redirect(w
, r
, url
+"/", StatusMovedPermanently
)
99 if url
[len(url
)-1] == '/' {
100 Redirect(w
, r
, url
[0:len(url
)-1], StatusMovedPermanently
)
106 if t
, _
:= time
.Parse(TimeFormat
, r
.Header
["If-Modified-Since"]); t
!= nil && d
.Mtime_ns
/1e9
<= t
.Seconds() {
107 w
.WriteHeader(StatusNotModified
)
110 w
.SetHeader("Last-Modified", time
.SecondsToUTC(d
.Mtime_ns
/1e9
).Format(TimeFormat
))
112 // use contents of index.html for directory, if present
114 index
:= name
+ indexPage
115 ff
, err
:= os
.Open(index
, os
.O_RDONLY
, 0)
133 // use extension to find content type.
134 ext
:= path
.Ext(name
)
135 if ctype
:= mime
.TypeByExtension(ext
); ctype
!= "" {
136 w
.SetHeader("Content-Type", ctype
)
138 // read first chunk to decide between utf-8 text and binary
140 n
, _
:= io
.ReadFull(f
, buf
[0:])
143 w
.SetHeader("Content-Type", "text-plain; charset=utf-8")
145 w
.SetHeader("Content-Type", "application/octet-stream") // generic binary
152 // ServeFile replies to the request with the contents of the named file or directory.
153 func ServeFile(w ResponseWriter
, r
*Request
, name
string) {
154 serveFile(w
, r
, name
, false)
157 type fileHandler
struct {
162 // FileServer returns a handler that serves HTTP requests
163 // with the contents of the file system rooted at root.
164 // It strips prefix from the incoming requests before
165 // looking up the file name in the file system.
166 func FileServer(root
, prefix
string) Handler
{ return &fileHandler
{root
, prefix
} }
168 func (f
*fileHandler
) ServeHTTP(w ResponseWriter
, r
*Request
) {
170 if !strings
.HasPrefix(path
, f
.prefix
) {
174 path
= path
[len(f
.prefix
):]
175 serveFile(w
, r
, f
.root
+"/"+path
, true)