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, key string, 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("HGET", key, 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("HSET", key, 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) }) }