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 }) }