diff --git a/storage/cloudfrontlayerhandler.go b/storage/cloudfrontlayerhandler.go index 3afc4c0e..2236f7be 100644 --- a/storage/cloudfrontlayerhandler.go +++ b/storage/cloudfrontlayerhandler.go @@ -19,6 +19,7 @@ import ( type cloudFrontLayerHandler struct { cloudfront *cloudfront.CloudFront delegateLayerHandler *delegateLayerHandler + duration time.Duration } var _ LayerHandler = &cloudFrontLayerHandler{} @@ -74,13 +75,28 @@ func newCloudFrontLayerHandler(storageDriver storagedriver.StorageDriver, option cf := cloudfront.New(baseURL, privateKey, keypairID) - return &cloudFrontLayerHandler{cloudfront: cf, delegateLayerHandler: dlh}, nil + duration := 20 * time.Minute + d, ok := options["duration"] + if ok { + switch d := d.(type) { + case time.Duration: + duration = d + case string: + dur, err := time.ParseDuration(d) + if err != nil { + return nil, fmt.Errorf("Invalid duration: %s", err) + } + duration = dur + } + } + + return &cloudFrontLayerHandler{cloudfront: cf, delegateLayerHandler: dlh, duration: duration}, nil } // Resolve returns an http.Handler which can serve the contents of the given // Layer, or an error if not supported by the storagedriver. func (lh *cloudFrontLayerHandler) Resolve(layer Layer) (http.Handler, error) { - layerURLStr, err := lh.delegateLayerHandler.urlFor(layer) + layerURLStr, err := lh.delegateLayerHandler.urlFor(layer, nil) if err != nil { return nil, err } @@ -90,7 +106,7 @@ func (lh *cloudFrontLayerHandler) Resolve(layer Layer) (http.Handler, error) { return nil, err } - cfURL, err := lh.cloudfront.CannedSignedURL(layerURL.Path, "", time.Now().Add(20*time.Minute)) + cfURL, err := lh.cloudfront.CannedSignedURL(layerURL.Path, "", time.Now().Add(lh.duration)) if err != nil { return nil, err } diff --git a/storage/delegatelayerhandler.go b/storage/delegatelayerhandler.go index 5c30f4db..7ed6d87b 100644 --- a/storage/delegatelayerhandler.go +++ b/storage/delegatelayerhandler.go @@ -41,19 +41,30 @@ func newDelegateLayerHandler(storageDriver storagedriver.StorageDriver, options // Resolve returns an http.Handler which can serve the contents of the given // Layer, or an error if not supported by the storagedriver. func (lh *delegateLayerHandler) Resolve(layer Layer) (http.Handler, error) { - layerURL, err := lh.urlFor(layer) + // TODO(bbland): This is just a sanity check to ensure that the + // storagedriver supports url generation. It would be nice if we didn't have + // to do this twice for non-GET requests. + layerURL, err := lh.urlFor(layer, map[string]interface{}{"method": "GET"}) if err != nil { return nil, err } return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method != "GET" { + layerURL, err = lh.urlFor(layer, map[string]interface{}{"method": r.Method}) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + } + http.Redirect(w, r, layerURL, http.StatusTemporaryRedirect) }), nil } // urlFor returns a download URL for the given layer, or the empty string if // unsupported. -func (lh *delegateLayerHandler) urlFor(layer Layer) (string, error) { +func (lh *delegateLayerHandler) urlFor(layer Layer, options map[string]interface{}) (string, error) { // Crack open the layer to get at the layerStore layerRd, ok := layer.(*layerReader) if !ok { @@ -64,7 +75,12 @@ func (lh *delegateLayerHandler) urlFor(layer Layer) (string, error) { return "", fmt.Errorf("unsupported layer type: cannot resolve blob path: %v", layer) } - layerURL, err := lh.storageDriver.URLFor(layerRd.path, map[string]interface{}{"expiry": time.Now().Add(lh.duration)}) + if options == nil { + options = make(map[string]interface{}) + } + options["expiry"] = time.Now().Add(lh.duration) + + layerURL, err := lh.storageDriver.URLFor(layerRd.path, options) if err != nil { return "", err }