-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathfresh.go
More file actions
120 lines (108 loc) · 3.41 KB
/
Copy pathfresh.go
File metadata and controls
120 lines (108 loc) · 3.41 KB
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
112
113
114
115
116
117
118
119
120
// MIT License
// Copyright (c) 2021 Tree Xie
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
package elton
import (
"net/http"
"regexp"
"strings"
"time"
)
var noCacheReg = regexp.MustCompile(`(?:^|,)\s*?no-cache\s*?(?:,|$)`)
const weakTagPrefix = "W/"
func parseHTTPDate(date string) int64 {
t, err := time.Parse(time.RFC1123, date)
if err != nil {
return 0
}
return t.Unix()
}
// etagWeakMatch 弱ETag比较:W/"xxx" 与 "xxx" 任意一侧带W/前缀均视为匹配
// (弱比较语义,见RFC 7232 2.3.2)
func etagWeakMatch(match, etag string) bool {
if match == etag {
return true
}
if s, ok := strings.CutPrefix(match, weakTagPrefix); ok && s == etag {
return true
}
if s, ok := strings.CutPrefix(etag, weakTagPrefix); ok && s == match {
return true
}
return false
}
// isFresh returns true if the data is fresh
func isFresh(modifiedSince, noneMatch, cacheControl, lastModified, etag string) bool {
if modifiedSince == "" && noneMatch == "" {
return false
}
// 请求端指定no-cache时不使用缓存
if cacheControl != "" && noCacheReg.MatchString(cacheControl) {
return false
}
// if none match
// "*" 表示匹配任意etag,跳过比较
if noneMatch != "" && noneMatch != "*" {
if etag == "" {
return false
}
// If-None-Match可为逗号分隔的多个etag,任意一个弱匹配即为fresh。
// 手写分割避免 Split/SplitSeq 的中间切片分配。
etagStale := true
rest := noneMatch
for rest != "" {
var part string
if i := strings.IndexByte(rest, ','); i >= 0 {
part, rest = rest[:i], rest[i+1:]
} else {
part, rest = rest, ""
}
if etagWeakMatch(strings.TrimSpace(part), etag) {
etagStale = false
break
}
}
if etagStale {
return false
}
}
// if modified since
if modifiedSince != "" {
if lastModified == "" {
return false
}
lastModifiedUnix := parseHTTPDate(lastModified)
modifiedSinceUnix := parseHTTPDate(modifiedSince)
if lastModifiedUnix == 0 || modifiedSinceUnix == 0 {
return false
}
if modifiedSinceUnix < lastModifiedUnix {
return false
}
}
return true
}
// Fresh returns fresh status by judging request header and response header
func Fresh(reqHeader http.Header, resHeader http.Header) bool {
return isFresh(
reqHeader.Get(HeaderIfModifiedSince),
reqHeader.Get(HeaderIfNoneMatch),
reqHeader.Get(HeaderCacheControl),
resHeader.Get(HeaderLastModified),
resHeader.Get(HeaderETag),
)
}