diff --git a/cache/cache.go b/cache/cache.go deleted file mode 100644 index c528007..0000000 --- a/cache/cache.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2013 Google Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package cache implements a image cache. -package cache - -import "github.com/willnorris/go-imageproxy/data" - -// Cache provides a cache for image metadata and transformed variants of the -// image. -type Cache interface { - // Get retrieves the cached Image for the provided image URL. - Get(string) (image *data.Image, ok bool) - - // Put caches the provided Image. - Save(*data.Image) - - // Delete deletes the cached Image and all variants for the image at the specified URL. - Delete(string) -} diff --git a/cache/memory.go b/cache/memory.go deleted file mode 100644 index 1091822..0000000 --- a/cache/memory.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2013 Google Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package cache - -import "github.com/willnorris/go-imageproxy/data" - -// MemoryCache provides an in-memory Cache implementation. -type MemoryCache struct { - images map[string]*data.Image -} - -func NewMemoryCache() *MemoryCache { - return &MemoryCache{ - make(map[string]*data.Image), - } -} - -func (c MemoryCache) Get(u string) (*data.Image, bool) { - image, ok := c.images[u] - return image, ok -} - -func (c MemoryCache) Save(image *data.Image) { - c.images[image.URL] = image -} - -func (c MemoryCache) Delete(u string) { - delete(c.images, u) -} diff --git a/imageproxy.go b/imageproxy.go index 84cbe54..8697ee1 100644 --- a/imageproxy.go +++ b/imageproxy.go @@ -21,7 +21,7 @@ import ( "net/http" "strings" - "github.com/willnorris/go-imageproxy/cache" + "github.com/gregjones/httpcache" "github.com/willnorris/go-imageproxy/proxy" ) @@ -33,8 +33,8 @@ func main() { fmt.Printf("go-imageproxy listening on %s\n", *addr) - p := proxy.NewProxy(nil) - p.Cache = cache.NewMemoryCache() + c := httpcache.NewMemoryCache() + p := proxy.NewProxy(nil, c) p.MaxWidth = 2000 p.MaxHeight = 2000 if *whitelist != "" { diff --git a/cache/nop.go b/proxy/cache.go similarity index 56% rename from cache/nop.go rename to proxy/cache.go index f6e1cda..3de513d 100644 --- a/cache/nop.go +++ b/proxy/cache.go @@ -12,15 +12,26 @@ // See the License for the specific language governing permissions and // limitations under the License. -package cache +package proxy -import "github.com/willnorris/go-imageproxy/data" +// The Cache interface defines a cache for storing arbitrary data. The +// interface is designed to align with httpcache.Cache. +type Cache interface { + // Get retrieves the cached data for the provided key. + Get(key string) (data []byte, ok bool) + + // Set caches the provided data. + Set(key string, data []byte) + + // Delete deletes the cached data at the specified key. + Delete(key string) +} // NopCache provides a no-op cache implementation that doesn't actually cache anything. var NopCache = new(nopCache) type nopCache struct{} -func (c nopCache) Get(u string) (*data.Image, bool) { return nil, false } -func (c nopCache) Save(image *data.Image) {} -func (c nopCache) Delete(u string) {} +func (c nopCache) Get(string) ([]byte, bool) { return nil, false } +func (c nopCache) Set(string, []byte) {} +func (c nopCache) Delete(string) {} diff --git a/proxy/proxy.go b/proxy/proxy.go index 6eba36b..90053ad 100644 --- a/proxy/proxy.go +++ b/proxy/proxy.go @@ -26,7 +26,7 @@ import ( "time" "github.com/golang/glog" - "github.com/willnorris/go-imageproxy/cache" + "github.com/gregjones/httpcache" "github.com/willnorris/go-imageproxy/data" "github.com/willnorris/go-imageproxy/transform" ) @@ -79,7 +79,7 @@ func NewRequest(r *http.Request) (*data.Request, error) { // Proxy serves image requests. type Proxy struct { Client *http.Client // client used to fetch remote URLs - Cache cache.Cache + Cache Cache // Whitelist specifies a list of remote hosts that images can be proxied from. An empty list means all hosts are allowed. Whitelist []string @@ -90,11 +90,24 @@ type Proxy struct { // NewProxy constructs a new proxy. The provided http Client will be used to // fetch remote URLs. If nil is provided, http.DefaultClient will be used. -func NewProxy(client *http.Client) *Proxy { +func NewProxy(client *http.Client, cache Cache) *Proxy { if client == nil { client = http.DefaultClient } - return &Proxy{Client: client, Cache: cache.NopCache} + if cache == nil { + cache = NopCache + } + + return &Proxy{ + Client: &http.Client{ + Transport: &httpcache.Transport{ + Transport: client.Transport, + Cache: cache, + MarkCachedResponses: true, + }, + }, + Cache: cache, + } } // ServeHTTP handles image requests. @@ -122,23 +135,11 @@ func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } - image, ok := p.Cache.Get(u) - if !ok { - glog.Infof("image not cached") - image, err = p.fetchRemoteImage(u, nil) - if err != nil { - glog.Errorf("error fetching remote image: %v", err) - } - p.Cache.Save(image) - } else if time.Now().After(image.Expires) { - glog.Infof("cached image expired") - image, err = p.fetchRemoteImage(u, image) - if err != nil { - glog.Errorf("error fetching remote image: %v", err) - } - p.Cache.Save(image) - } else { - glog.Infof("serving from cache") + image, err := p.fetchRemoteImage(u) + if err != nil { + glog.Errorf("error fetching remote image: %v", err) + http.Error(w, fmt.Sprintf("Error fetching remote image: %v", err), http.StatusInternalServerError) + return } image, _ = transform.Transform(*image, req.Options) @@ -148,29 +149,13 @@ func (p *Proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) { w.Write(image.Bytes) } -func (p *Proxy) fetchRemoteImage(u string, cached *data.Image) (*data.Image, error) { +func (p *Proxy) fetchRemoteImage(u string) (*data.Image, error) { glog.Infof("fetching remote image: %s", u) - - req, err := http.NewRequest("GET", u, nil) + resp, err := p.Client.Get(u) if err != nil { return nil, err } - if cached != nil && cached.Etag != "" { - req.Header.Add("If-None-Match", cached.Etag) - } - - resp, err := p.Client.Do(req) - if err != nil { - return nil, err - } - - if resp.StatusCode == http.StatusNotModified { - glog.Infof("remote image not modified (304 response)") - cached.Expires = parseExpires(resp) - return cached, nil - } - if resp.StatusCode != http.StatusOK { return nil, errors.New(fmt.Sprintf("HTTP status not OK: %v", resp.Status)) }