// Package mapcache is very similar to rediscache, except there are no // external dependencies and the page output is saved in a map package mapcache import ( "bytes" "net/http" "sync" ) type responseWriter struct { Headers http.Header StatusCode int Data []byte buf *bytes.Buffer } func (rw *responseWriter) Header() http.Header { if rw.Headers == nil { rw.Headers = http.Header{} } return rw.Headers } func (rw *responseWriter) Write(msg []byte) (int, error) { if rw.buf == nil { rw.buf = &bytes.Buffer{} } return rw.buf.Write(msg) //nolint:wrapcheck } // Simply for satisfying the http.ResponseWriter interface. func (rw *responseWriter) WriteHeader(code int) { rw.StatusCode = code } // WriteData takes the internal buffer and writes out the entire // contents to the 'Data' field for storage in Redis. func (rw *responseWriter) WriteData() { rw.Data = rw.buf.Bytes() } func New() *Cache { return &Cache{ m: &sync.RWMutex{}, cached: map[string]*responseWriter{}, } } type Cache struct { m *sync.RWMutex cached map[string]*responseWriter } func (c *Cache) Remove(path string) { c.m.Lock() delete(c.cached, path) c.m.Unlock() } func (c *Cache) Clear() { c.m.Lock() c.cached = map[string]*responseWriter{} c.m.Unlock() } // HandleWithParams is the same as Handle but caches for the GET params // rather than discarding them. func (c *Cache) HandleWithParams(next http.Handler) http.Handler { return c.handle(true, next) } // Handle is a Simple function that will cache the response for given handler in // redis and instead of responding with the result from the handler it will // simply dump the contents of the redis key if it exists. func (c *Cache) Handle(next http.Handler) http.Handler { return c.handle(false, next) } func (c *Cache) handle(params bool, next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { subkey := r.URL.Path if params { subkey = r.URL.Path + "?" + r.URL.RawQuery } content: c.m.RLock() rw, ok := c.cached[subkey] c.m.RUnlock() if !ok { rw := &responseWriter{} next.ServeHTTP(rw, r) rw.WriteData() c.m.Lock() c.cached[subkey] = rw c.m.Unlock() // We got the content, let's go back around again and dump // it out from the cache goto content } if rw.Headers != nil { for k, v := range rw.Headers { w.Header()[k] = v } } if rw.StatusCode != 0 { w.WriteHeader(rw.StatusCode) } _, _ = w.Write(rw.Data) }) }