github.com/pritambaral/docker@v1.4.2-0.20150120174542-b2fe1b3dd952/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) *mux.Route {
   132  	route := new(mux.Route)
   133  	*route = *ub.router.GetRoute(name) // clone the route
   134  
   135  	return route.
   136  		Schemes(ub.root.Scheme).
   137  		Host(ub.root.Host)
   138  }
   139  
   140  // appendValuesURL appends the parameters to the url.
   141  func appendValuesURL(u *url.URL, values ...url.Values) *url.URL {
   142  	merged := u.Query()
   143  
   144  	for _, v := range values {
   145  		for k, vv := range v {
   146  			merged[k] = append(merged[k], vv...)
   147  		}
   148  	}
   149  
   150  	u.RawQuery = merged.Encode()
   151  	return u
   152  }
   153  
   154  // appendValues appends the parameters to the url. Panics if the string is not
   155  // a url.
   156  func appendValues(u string, values ...url.Values) string {
   157  	up, err := url.Parse(u)
   158  
   159  	if err != nil {
   160  		panic(err) // should never happen
   161  	}
   162  
   163  	return appendValuesURL(up, values...).String()
   164  }