74 lines
1.5 KiB
Go
74 lines
1.5 KiB
Go
package main
|
|
|
|
import (
|
|
"net/http"
|
|
"runtime"
|
|
"unsafe"
|
|
)
|
|
|
|
type realCacheEntry struct {
|
|
r Response
|
|
cache map[string]cacheEntry
|
|
key string
|
|
}
|
|
|
|
// store a pointer to the real cache entry as an int here
|
|
// so that the garbage collector can collect it if needed
|
|
type cacheEntry struct {
|
|
p uintptr
|
|
//r Response
|
|
}
|
|
|
|
func (e cacheEntry) access() *Response {
|
|
ptr := unsafe.Pointer(e.p)
|
|
entry := (*realCacheEntry)(ptr)
|
|
return &entry.r
|
|
}
|
|
|
|
func addEntry(cache map[string]cacheEntry, s string, r Response) cacheEntry {
|
|
entry := new(realCacheEntry)
|
|
entry.r = r
|
|
entry.cache = cache
|
|
entry.key = s
|
|
|
|
cache[s] = cacheEntry{
|
|
p: (uintptr)(unsafe.Pointer(entry)),
|
|
}
|
|
runtime.SetFinalizer(entry, func(e *realCacheEntry) {
|
|
delete(e.cache, e.key)
|
|
})
|
|
|
|
return cache[s]
|
|
}
|
|
|
|
func Cache(h http.Handler) http.Handler {
|
|
c := make(map[string]cacheEntry)
|
|
|
|
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
|
if r.Method != "GET" {
|
|
rw.WriteHeader(403)
|
|
rw.Write([]byte("invalid request type"))
|
|
return
|
|
}
|
|
|
|
// NOTE: not threadsafe, TODO fix that
|
|
entry, exists := c[r.URL.String()]
|
|
if exists {
|
|
// NOTE: the finalizer can theoretically execute between the map read
|
|
// and here
|
|
entry.access().WriteResponse(rw)
|
|
} else {
|
|
rc := ResponseCollector{}
|
|
// copy request in case they modify it
|
|
req := *r
|
|
h.ServeHTTP(&rc, &req)
|
|
resp := rc.CollectResponse()
|
|
if resp.Code == 200 {
|
|
addEntry(c, r.URL.String(), resp)
|
|
}
|
|
resp.WriteResponse(rw)
|
|
}
|
|
// TODO bookkeeping for the cache here
|
|
})
|
|
}
|