github.com/dougm/docker@v1.5.0/registry/v2/urls.go (about)

     1  package v2
     2  
     3  import (
     4  	"net/http"
     5  	"net/url"
     6  
     7  	"github.com/gorilla/mux"
     8  )
     9  
    10  // URLBuilder creates registry API urls from a single base endpoint. It can be
    11  // used to create urls for use in a registry client or server.
    12  //
    13  // All urls will be created from the given base, including the api version.
    14  // For example, if a root of "/foo/" is provided, urls generated will be fall
    15  // under "/foo/v2/...". Most application will only provide a schema, host and
    16  // port, such as "https://localhost:5000/".
    17  type URLBuilder struct {
    18  	root   *url.URL // url root (ie http://localhost/)
    19  	router *mux.Router
    20  }
    21  
    22  // NewURLBuilder creates a URLBuilder with provided root url object.
    23  func NewURLBuilder(root *url.URL) *URLBuilder {
    24  	return &URLBuilder{
    25  		root:   root,
    26  		router: Router(),
    27  	}
    28  }
    29  
    30  // NewURLBuilderFromString workes identically to NewURLBuilder except it takes
    31  // a string argument for the root, returning an error if it is not a valid
    32  // url.
    33  func NewURLBuilderFromString(root string) (*URLBuilder, error) {
    34  	u, err := url.Parse(root)
    35  	if err != nil {
    36  		return nil, err
    37  	}
    38  
    39  	return NewURLBuilder(u), nil
    40  }
    41  
    42  // NewURLBuilderFromRequest uses information from an *http.Request to
    43  // construct the root url.
    44  func NewURLBuilderFromRequest(r *http.Request) *URLBuilder {
    45  	u := &url.URL{
    46  		Scheme: r.URL.Scheme,
    47  		Host:   r.Host,
    48  	}
    49  
    50  	return NewURLBuilder(u)
    51  }
    52  
    53  // BuildBaseURL constructs a base url for the API, typically just "/v2/".
    54  func (ub *URLBuilder) BuildBaseURL() (string, error) {
    55  	route := ub.cloneRoute(RouteNameBase)
    56  
    57  	baseURL, err := route.URL()
    58  	if err != nil {
    59  		return "", err
    60  	}
    61  
    62  	return baseURL.String(), nil
    63  }
    64  
    65  // BuildTagsURL constructs a url to list the tags in the named repository.
    66  func (ub *URLBuilder) BuildTagsURL(name string) (string, error) {
    67  	route := ub.cloneRoute(RouteNameTags)
    68  
    69  	tagsURL, err := route.URL("name", name)
    70  	if err != nil {
    71  		return "", err
    72  	}
    73  
    74  	return tagsURL.String(), nil
    75  }
    76  
    77  // BuildManifestURL constructs a url for the manifest identified by name and tag.
    78  func (ub *URLBuilder) BuildManifestURL(name, tag string) (string, error) {
    79  	route := ub.cloneRoute(RouteNameManifest)
    80  
    81  	manifestURL, err := route.URL("name", name, "tag", tag)
    82  	if err != nil {
    83  		return "", err
    84  	}
    85  
    86  	return manifestURL.String(), nil
    87  }
    88  
    89  // BuildBlobURL constructs the url for the blob identified by name and dgst.
    90  func (ub *URLBuilder) BuildBlobURL(name string, dgst string) (string, error) {
    91  	route := ub.cloneRoute(RouteNameBlob)
    92  
    93  	layerURL, err := route.URL("name", name, "digest", dgst)
    94  	if err != nil {
    95  		return "", err
    96  	}
    97  
    98  	return layerURL.String(), nil
    99  }
   100  
   101  // BuildBlobUploadURL constructs a url to begin a blob upload in the
   102  // repository identified by name.
   103  func (ub *URLBuilder) BuildBlobUploadURL(name string, values ...url.Values) (string, error) {
   104  	route := ub.cloneRoute(RouteNameBlobUpload)
   105  
   106  	uploadURL, err := route.URL("name", name)
   107  	if err != nil {
   108  		return "", err
   109  	}
   110  
   111  	return appendValuesURL(uploadURL, values...).String(), nil
   112  }
   113  
   114  // BuildBlobUploadChunkURL constructs a url for the upload identified by uuid,
   115  // including any url values. This should generally not be used by clients, as
   116  // this url is provided by server implementations during the blob upload
   117  // process.
   118  func (ub *URLBuilder) BuildBlobUploadChunkURL(name, uuid string, values ...url.Values) (string, error) {
   119  	route := ub.cloneRoute(RouteNameBlobUploadChunk)
   120  
   121  	uploadURL, err := route.URL("name", name, "uuid", uuid)
   122  	if err != nil {
   123  		return "", err
   124  	}
   125  
   126  	return appendValuesURL(uploadURL, values...).String(), nil
   127  }
   128  
   129  // clondedRoute returns a clone of the named route from the router. Routes
   130  // must be cloned to avoid modifying them during url generation.
   131  func (ub *URLBuilder) cloneRoute(name string) clonedRoute {
   132  	route := new(mux.Route)
   133  	root := new(url.URL)
   134  
   135  	*route = *ub.router.GetRoute(name) // clone the route
   136  	*root = *ub.root
   137  
   138  	return clonedRoute{Route: route, root: root}
   139  }
   140  
   141  type clonedRoute struct {
   142  	*mux.Route
   143  	root *url.URL
   144  }
   145  
   146  func (cr clonedRoute) URL(pairs ...string) (*url.URL, error) {
   147  	routeURL, err := cr.Route.URL(pairs...)
   148  	if err != nil {
   149  		return nil, err
   150  	}
   151  
   152  	return cr.root.ResolveReference(routeURL), nil
   153  }
   154  
   155  // appendValuesURL appends the parameters to the url.
   156  func appendValuesURL(u *url.URL, values ...url.Values) *url.URL {
   157  	merged := u.Query()
   158  
   159  	for _, v := range values {
   160  		for k, vv := range v {
   161  			merged[k] = append(merged[k], vv...)
   162  		}
   163  	}
   164  
   165  	u.RawQuery = merged.Encode()
   166  	return u
   167  }
   168  
   169  // appendValues appends the parameters to the url. Panics if the string is not
   170  // a url.
   171  func appendValues(u string, values ...url.Values) string {
   172  	up, err := url.Parse(u)
   173  
   174  	if err != nil {
   175  		panic(err) // should never happen
   176  	}
   177  
   178  	return appendValuesURL(up, values...).String()
   179  }