-
Notifications
You must be signed in to change notification settings - Fork 3
/
cache.go
108 lines (88 loc) · 2.14 KB
/
cache.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
package main
import (
"bytes"
"net/http"
"sync"
"time"
"github.com/COSI-Lab/logging"
)
type ProxyWriter struct {
header http.Header
buffer bytes.Buffer
status int
}
func (p *ProxyWriter) Header() http.Header {
return p.header
}
func (p *ProxyWriter) Write(bytes []byte) (int, error) {
return p.buffer.Write(bytes)
}
func (p *ProxyWriter) WriteHeader(status int) {
p.status = status
}
type CacheEntry struct {
header http.Header
body []byte
status int
time time.Time
}
func (c *CacheEntry) WriteTo(w http.ResponseWriter) (int, error) {
header := w.Header()
for k, v := range c.header {
header[k] = v
}
if c.status != 0 {
w.WriteHeader(c.status)
}
return w.Write(c.body)
}
// Caches the responses from the webserver
var cache = map[string]*CacheEntry{}
var cacheLock = &sync.RWMutex{}
func cachingMiddleware(next func(w http.ResponseWriter, r *http.Request)) http.Handler {
if !webServerCache {
logging.Info("Caching disabled")
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
logging.Info(r.Method, r.URL.Path)
next(w, r)
})
}
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
// Check if the request is cached
cacheLock.RLock()
if entry, ok := cache[r.RequestURI]; ok && r.Method == "GET" {
// Check that the cache entry is still valid
if time.Since(entry.time) < time.Hour {
// Send the cached response
entry.WriteTo(w)
cacheLock.RUnlock()
logging.Info(r.Method, r.RequestURI, "in", time.Since(start), "; cached")
return
}
}
cacheLock.RUnlock()
// Create a new response writer
proxyWriter := &ProxyWriter{
header: make(http.Header),
}
// Call the next handler
next(proxyWriter, r)
// Create the response cache entry
entry := &CacheEntry{
header: proxyWriter.header,
body: proxyWriter.buffer.Bytes(),
status: proxyWriter.status,
time: time.Now(),
}
// Cache the response
go func() {
cacheLock.Lock()
cache[r.RequestURI] = entry
cacheLock.Unlock()
}()
// Send the response to client
entry.WriteTo(w)
logging.Info(r.Method, r.RequestURI, "in", time.Since(start))
})
}