forked from hoisie/web
-
Notifications
You must be signed in to change notification settings - Fork 1
/
servefile.go
111 lines (98 loc) · 2.79 KB
/
servefile.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
package web
import (
"crypto/md5"
"fmt"
"io"
"mime"
"os"
"path"
"strconv"
"strings"
"time"
"utf8"
)
func isText(b []byte) bool {
for len(b) > 0 && utf8.FullRune(b) {
rune, size := utf8.DecodeRune(b)
if size == 1 && rune == utf8.RuneError {
// decoding error
return false
}
if 0x7F <= rune && rune <= 0x9F {
return false
}
if rune < ' ' {
switch rune {
case '\n', '\r', '\t':
// okay
default:
// binary garbage
return false
}
}
b = b[size:]
}
return true
}
func getmd5(data string) string {
hash := md5.New()
hash.Write([]byte(data))
return fmt.Sprintf("%x", hash.Sum())
}
func serveFile(ctx *Context, name string) {
f, err := os.Open(name)
if err != nil {
ctx.Abort(404, "Invalid file")
return
}
defer f.Close()
info, _ := f.Stat()
size := strconv.Itoa64(info.Size)
mtime := strconv.Itoa64(info.Mtime_ns)
//set the last-modified header
lm := time.SecondsToUTC(info.Mtime_ns / 1e9)
ctx.SetHeader("Last-Modified", webTime(lm), true)
//generate a simple etag with heuristic MD5(filename, size, lastmod)
etagparts := []string{name, size, mtime}
etag := fmt.Sprintf(`"%s"`, getmd5(strings.Join(etagparts, "|")))
ctx.SetHeader("ETag", etag, true)
//the first 1024 bytes of the file, used to detect content-type
var firstChunk []byte
ext := path.Ext(name)
if ctype := mime.TypeByExtension(ext); ctype != "" {
ctx.SetHeader("Content-Type", ctype, true)
} else {
// read first chunk to decide between utf-8 text and binary
buf := make([]byte, 1024)
n, _ := io.ReadFull(f, buf)
firstChunk = buf[0:n]
if isText(firstChunk) {
ctx.SetHeader("Content-Type", "text/plain; charset=utf-8", true)
} else {
ctx.SetHeader("Content-Type", "application/octet-stream", true) // generic binary
}
}
if ctx.Request.Headers.Get("If-None-Match") != "" {
inm := ctx.Request.Headers.Get("If-None-Match")
if inm == etag {
ctx.NotModified()
return
}
}
if ctx.Request.Headers.Get("If-Modified-Since") != "" {
ims := ctx.Request.Headers.Get("If-Modified-Since")
imstime, err := time.Parse(time.RFC1123, ims)
if err == nil && imstime.Seconds() >= lm.Seconds() {
ctx.NotModified()
return
}
}
//set content-length
ctx.SetHeader("Content-Length", size, true)
if ctx.Request.Method != "HEAD" {
if len(firstChunk) > 0 {
ctx.Write(firstChunk)
}
io.Copy(ctx, f)
}
}