diff options
Diffstat (limited to 'rediscache/main.go')
| -rw-r--r-- | rediscache/main.go | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/rediscache/main.go b/rediscache/main.go new file mode 100644 index 0000000..e7564c8 --- /dev/null +++ b/rediscache/main.go @@ -0,0 +1,118 @@ +package rediscache + +import ( + "bytes" + "log" + "net/http" + "os" + + "github.com/gomodule/redigo/redis" + "github.com/vmihailenco/msgpack" +) + +// Logger is the default logger used for this package, feel free to +// override. +var Logger = log.New(os.Stderr, "REDIS: ", log.LstdFlags) + +// redisHTTPResponseWriter is essentially a fake http.ResponseWriter that +// is going to let us suck out information and chuck it into redis +// implements the interface as defined in net/http. +type redisHTTPResponseWriter struct { + Headers http.Header + StatusCode int + Data []byte + + buf *bytes.Buffer +} + +// Simply for satisfying the http.ResponseWriter interface. +func (rw *redisHTTPResponseWriter) Header() http.Header { + if rw.Headers == nil { + rw.Headers = http.Header{} + } + + return rw.Headers +} + +// Writes to the internal buffer. +func (rw *redisHTTPResponseWriter) Write(msg []byte) (int, error) { + if rw.buf == nil { + rw.buf = &bytes.Buffer{} + } + + return rw.buf.Write(msg) +} + +// Simply for satisfying the http.ResponseWriter interface. +func (rw *redisHTTPResponseWriter) 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 *redisHTTPResponseWriter) WriteData() { + rw.Data = rw.buf.Bytes() +} + +// 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 Handle(pool *redis.Pool, next http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + client := pool.Get() + defer client.Close() + + content: + data, err := client.Do("GET", r.URL.Path) + if err != nil { + // Assume something bad has happened with redis, we're + // just going to log this and then pass through the + // request as normal. + Logger.Println("ERROR: ", err) + next.ServeHTTP(w, r) + + return + } else if data == nil { + rw := &redisHTTPResponseWriter{} + next.ServeHTTP(rw, r) + + rw.WriteData() + b, err := msgpack.Marshal(rw) + if err != nil { + Logger.Println("ERROR: marshaling: ", err) + + return + } + _, err = client.Do("SET", r.URL.Path, b) + if err != nil { + Logger.Println("ERROR: during set: ", err) + + return + } + + // We got the content, let's go back around again and dump + // it out from redis + goto content + } + + rw := &redisHTTPResponseWriter{} + + err = msgpack.Unmarshal(data.([]byte), rw) + if err != nil { + Logger.Println("ERROR: unmarshaling: ", err) + + return + } + + 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) + }) +} |
