| 
									
										
										
										
											2019-02-11 15:07:36 +08:00
										 |  |  | package middleware | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2019-03-04 17:48:32 +08:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2019-02-11 15:07:36 +08:00
										 |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"net/url" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-04 17:48:32 +08:00
										 |  |  | 	dcontext "github.com/docker/distribution/context" | 
					
						
							| 
									
										
										
										
											2019-02-11 15:07:36 +08:00
										 |  |  | 	storagedriver "github.com/docker/distribution/registry/storage/driver" | 
					
						
							|  |  |  | 	storagemiddleware "github.com/docker/distribution/registry/storage/driver/middleware" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/denverdino/aliyungo/cdn/auth" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-11 19:20:47 +08:00
										 |  |  | // aliCDNStorageMiddleware provides a simple implementation of layerHandler that | 
					
						
							| 
									
										
										
										
											2019-02-11 15:07:36 +08:00
										 |  |  | // constructs temporary signed AliCDN URLs from the storagedriver layer URL, | 
					
						
							|  |  |  | // then issues HTTP Temporary Redirects to this AliCDN content URL. | 
					
						
							| 
									
										
										
										
											2019-02-11 19:20:47 +08:00
										 |  |  | type aliCDNStorageMiddleware struct { | 
					
						
							| 
									
										
										
										
											2019-02-11 15:07:36 +08:00
										 |  |  | 	storagedriver.StorageDriver | 
					
						
							|  |  |  | 	baseURL   string | 
					
						
							|  |  |  | 	urlSigner *auth.URLSigner | 
					
						
							|  |  |  | 	duration  time.Duration | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-11 19:20:47 +08:00
										 |  |  | var _ storagedriver.StorageDriver = &aliCDNStorageMiddleware{} | 
					
						
							| 
									
										
										
										
											2019-02-11 15:07:36 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-11 19:20:47 +08:00
										 |  |  | // newAliCDNStorageMiddleware constructs and returns a new AliCDN | 
					
						
							|  |  |  | // layerHandler implementation. | 
					
						
							| 
									
										
										
										
											2019-02-11 15:07:36 +08:00
										 |  |  | // Required options: baseurl, authtype, privatekey | 
					
						
							|  |  |  | // Optional options: duration | 
					
						
							| 
									
										
										
										
											2019-02-11 19:20:47 +08:00
										 |  |  | func newAliCDNStorageMiddleware(storageDriver storagedriver.StorageDriver, options map[string]interface{}) (storagedriver.StorageDriver, error) { | 
					
						
							| 
									
										
										
										
											2019-02-11 15:07:36 +08:00
										 |  |  | 	// parse baseurl | 
					
						
							|  |  |  | 	base, ok := options["baseurl"] | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("no baseurl provided") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	baseURL, ok := base.(string) | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("baseurl must be a string") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if !strings.Contains(baseURL, "://") { | 
					
						
							|  |  |  | 		baseURL = "https://" + baseURL | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if _, err := url.Parse(baseURL); err != nil { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("invalid baseurl: %v", err) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// parse authtype | 
					
						
							|  |  |  | 	at, ok := options["authtype"] | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("no authtype provided") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	authType, ok := at.(string) | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("authtype must be a string") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if authType != "a" && authType != "b" && authType != "c" { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("invalid authentication type") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// parse privatekey | 
					
						
							|  |  |  | 	pk, ok := options["privatekey"] | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("no privatekey provided") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	privateKey, ok := pk.(string) | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("privatekey must be a string") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	urlSigner := auth.NewURLSigner(authType, privateKey) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// parse duration | 
					
						
							| 
									
										
										
										
											2019-03-04 14:53:48 +08:00
										 |  |  | 	duration := 20 * time.Minute | 
					
						
							| 
									
										
										
										
											2019-02-11 15:07:36 +08:00
										 |  |  | 	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 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-02-11 19:20:47 +08:00
										 |  |  | 	return &aliCDNStorageMiddleware{ | 
					
						
							| 
									
										
										
										
											2019-02-11 15:07:36 +08:00
										 |  |  | 		StorageDriver: storageDriver, | 
					
						
							|  |  |  | 		baseURL:       baseURL, | 
					
						
							|  |  |  | 		urlSigner:     urlSigner, | 
					
						
							|  |  |  | 		duration:      duration, | 
					
						
							|  |  |  | 	}, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // URLFor attempts to find a url which may be used to retrieve the file at the given path. | 
					
						
							| 
									
										
										
										
											2019-02-11 19:20:47 +08:00
										 |  |  | func (ac *aliCDNStorageMiddleware) URLFor(ctx context.Context, path string, options map[string]interface{}) (string, error) { | 
					
						
							| 
									
										
										
										
											2019-02-11 15:07:36 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if ac.StorageDriver.Name() != "oss" { | 
					
						
							| 
									
										
										
										
											2019-03-04 17:48:32 +08:00
										 |  |  | 		dcontext.GetLogger(ctx).Warn("the AliCDN middleware does not support this backend storage driver") | 
					
						
							| 
									
										
										
										
											2019-02-11 15:07:36 +08:00
										 |  |  | 		return ac.StorageDriver.URLFor(ctx, path, options) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	acURL, err := ac.urlSigner.Sign(ac.baseURL+path, time.Now().Add(ac.duration)) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return "", err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return acURL, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // init registers the alicdn layerHandler backend. | 
					
						
							|  |  |  | func init() { | 
					
						
							| 
									
										
										
										
											2019-02-11 19:20:47 +08:00
										 |  |  | 	storagemiddleware.Register("alicdn", storagemiddleware.InitFunc(newAliCDNStorageMiddleware)) | 
					
						
							| 
									
										
										
										
											2019-02-11 15:07:36 +08:00
										 |  |  | } |