Merge from mainline (167278:168000).
[official-gcc/graphite-test-results.git] / libgo / go / http / fs.go
blobb3047f18275243edef48fe824b4b3a89d4583951
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
7 package http
9 import (
10 "fmt"
11 "io"
12 "mime"
13 "os"
14 "path"
15 "strings"
16 "time"
17 "utf8"
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 {
26 // decoding error
27 return false
29 if 0x80 <= rune && rune <= 0x9F {
30 return false
32 if rune < ' ' {
33 switch rune {
34 case '\n', '\r', '\t':
35 // okay
36 default:
37 // binary garbage
38 return false
41 b = b[size:]
43 return true
46 func dirList(w ResponseWriter, f *os.File) {
47 fmt.Fprintf(w, "<pre>\n")
48 for {
49 dirs, err := f.Readdir(100)
50 if err != nil || len(dirs) == 0 {
51 break
53 for _, d := range dirs {
54 name := d.Name
55 if d.IsDirectory() {
56 name += "/"
58 // TODO htmlescape
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)
71 return
74 f, err := os.Open(name, os.O_RDONLY, 0)
75 if err != nil {
76 // TODO expose actual error?
77 NotFound(w, r)
78 return
80 defer f.Close()
82 d, err1 := f.Stat()
83 if err1 != nil {
84 // TODO expose actual error?
85 NotFound(w, r)
86 return
89 if redirect {
90 // redirect to canonical path: / at end of directory url
91 // r.URL.Path always begins with /
92 url := r.URL.Path
93 if d.IsDirectory() {
94 if url[len(url)-1] != '/' {
95 Redirect(w, r, url+"/", StatusMovedPermanently)
96 return
98 } else {
99 if url[len(url)-1] == '/' {
100 Redirect(w, r, url[0:len(url)-1], StatusMovedPermanently)
101 return
106 if t, _ := time.Parse(TimeFormat, r.Header["If-Modified-Since"]); t != nil && d.Mtime_ns/1e9 <= t.Seconds() {
107 w.WriteHeader(StatusNotModified)
108 return
110 w.SetHeader("Last-Modified", time.SecondsToUTC(d.Mtime_ns/1e9).Format(TimeFormat))
112 // use contents of index.html for directory, if present
113 if d.IsDirectory() {
114 index := name + indexPage
115 ff, err := os.Open(index, os.O_RDONLY, 0)
116 if err == nil {
117 defer ff.Close()
118 dd, err := ff.Stat()
119 if err == nil {
120 name = index
121 d = dd
122 f = ff
127 if d.IsDirectory() {
128 dirList(w, f)
129 return
132 // serve file
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)
137 } else {
138 // read first chunk to decide between utf-8 text and binary
139 var buf [1024]byte
140 n, _ := io.ReadFull(f, buf[0:])
141 b := buf[0:n]
142 if isText(b) {
143 w.SetHeader("Content-Type", "text-plain; charset=utf-8")
144 } else {
145 w.SetHeader("Content-Type", "application/octet-stream") // generic binary
147 w.Write(b)
149 io.Copy(w, f)
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 {
158 root string
159 prefix string
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) {
169 path := r.URL.Path
170 if !strings.HasPrefix(path, f.prefix) {
171 NotFound(w, r)
172 return
174 path = path[len(f.prefix):]
175 serveFile(w, r, f.root+"/"+path, true)