diff --git a/src/pkg/http/fs.go b/src/pkg/http/fs.go index 40bb3d1384..db80b2f4df 100644 --- a/src/pkg/http/fs.go +++ b/src/pkg/http/fs.go @@ -13,6 +13,7 @@ import ( "os" "path" "strings" + "time" "utf8" ) @@ -61,8 +62,7 @@ func dirList(c *Conn, f *os.File) { fmt.Fprintf(c, "\n") } - -func serveFileInternal(c *Conn, r *Request, name string, redirect bool) { +func serveFile(c *Conn, r *Request, name string, redirect bool) { const indexPage = "/index.html" // redirect .../index.html to .../ @@ -103,6 +103,12 @@ func serveFileInternal(c *Conn, r *Request, name string, redirect bool) { } } + if t, _ := time.Parse(TimeFormat, r.Header["If-Modified-Since"]); t != nil && d.Mtime_ns/1e9 <= t.Seconds() { + c.WriteHeader(StatusNotModified) + return + } + c.SetHeader("Last-Modified", time.SecondsToUTC(d.Mtime_ns/1e9).Format(TimeFormat)) + // use contents of index.html for directory, if present if d.IsDirectory() { index := name + indexPage @@ -145,7 +151,7 @@ func serveFileInternal(c *Conn, r *Request, name string, redirect bool) { // ServeFile replies to the request with the contents of the named file or directory. func ServeFile(c *Conn, r *Request, name string) { - serveFileInternal(c, r, name, false) + serveFile(c, r, name, false) } type fileHandler struct { @@ -166,5 +172,5 @@ func (f *fileHandler) ServeHTTP(c *Conn, r *Request) { return } path = path[len(f.prefix):] - serveFileInternal(c, r, f.root+"/"+path, true) + serveFile(c, r, f.root+"/"+path, true) } diff --git a/src/pkg/http/server.go b/src/pkg/http/server.go index 430f65ad9e..2de0748677 100644 --- a/src/pkg/http/server.go +++ b/src/pkg/http/server.go @@ -101,6 +101,12 @@ func (ecr *expectContinueReader) Close() os.Error { return ecr.readCloser.Close() } +// TimeFormat is the time format to use with +// time.Parse and time.Time.Format when parsing +// or generating times in HTTP headers. +// It is like time.RFC1123 but hard codes GMT as the time zone. +const TimeFormat = "Mon, 02 Jan 2006 15:04:05 GMT" + // Read next request from connection. func (c *Conn) readRequest() (req *Request, err os.Error) { if c.hijacked { @@ -124,6 +130,7 @@ func (c *Conn) readRequest() (req *Request, err os.Error) { // Default output is HTML encoded in UTF-8. c.SetHeader("Content-Type", "text/html; charset=utf-8") + c.SetHeader("Date", time.UTC().Format(TimeFormat)) if req.ProtoAtLeast(1, 1) { // HTTP/1.1 or greater: use chunked transfer encoding diff --git a/src/pkg/http/triv.go b/src/pkg/http/triv.go index 612b6161e3..957ca5f94e 100644 --- a/src/pkg/http/triv.go +++ b/src/pkg/http/triv.go @@ -52,25 +52,6 @@ func (ctr *Counter) ServeHTTP(c *http.Conn, req *http.Request) { fmt.Fprintf(c, "counter = %d\n", ctr.n) } -// simple file server -var webroot = flag.String("root", "/home/rsc", "web root directory") -var pathVar = expvar.NewMap("file-requests") - -func FileServer(c *http.Conn, req *http.Request) { - c.SetHeader("content-type", "text/plain; charset=utf-8") - pathVar.Add(req.URL.Path, 1) - path := *webroot + req.URL.Path // TODO: insecure: use os.CleanName - f, err := os.Open(path, os.O_RDONLY, 0) - if err != nil { - c.WriteHeader(http.StatusNotFound) - fmt.Fprintf(c, "open %s: %v\n", path, err) - return - } - n, _ := io.Copy(c, f) - fmt.Fprintf(c, "[%d bytes]\n", n) - f.Close() -} - // simple flag server var booleanflag = flag.Bool("boolean", true, "another flag for testing") @@ -144,6 +125,8 @@ func Logger(c *http.Conn, req *http.Request) { } +var webroot = flag.String("root", "/home/rsc", "web root directory") + func main() { flag.Parse() @@ -153,7 +136,7 @@ func main() { expvar.Publish("counter", ctr) http.Handle("/", http.HandlerFunc(Logger)) - http.Handle("/go/", http.HandlerFunc(FileServer)) + http.Handle("/go/", http.FileServer(*webroot, "/go/")) http.Handle("/flags", http.HandlerFunc(FlagServer)) http.Handle("/args", http.HandlerFunc(ArgServer)) http.Handle("/go/hello", http.HandlerFunc(HelloServer))