diff options
| author | Mitchell Riedstra <mitch@riedstra.dev> | 2023-01-07 13:31:23 -0500 |
|---|---|---|
| committer | Mitchell Riedstra <mitch@riedstra.dev> | 2023-01-07 13:31:23 -0500 |
| commit | ca33a035c779ae14fb6330c8801c75f49dd1bb79 (patch) | |
| tree | deaabaf15d6d91079a68f247e46070399e4343ee /mapcache | |
| parent | 97dd660925434be537cd9a49a1d0c893b223e357 (diff) | |
| download | go-website-ca33a035c779ae14fb6330c8801c75f49dd1bb79.tar.gz go-website-ca33a035c779ae14fb6330c8801c75f49dd1bb79.tar.xz | |
Add an internal caching option. It performs quite well.v0.0.22
Also refactor and clean up most linter warnings.
Diffstat (limited to 'mapcache')
| -rw-r--r-- | mapcache/main.go | 119 |
1 files changed, 119 insertions, 0 deletions
diff --git a/mapcache/main.go b/mapcache/main.go new file mode 100644 index 0000000..0b6b697 --- /dev/null +++ b/mapcache/main.go @@ -0,0 +1,119 @@ +// 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) + }) +} |
