distribution/api/v2/urls.go

166 lines
4.4 KiB
Go
Raw Normal View History

package v2
2014-11-26 20:16:58 +00:00
import (
"net/http"
"net/url"
"github.com/docker/docker-registry/digest"
"github.com/gorilla/mux"
)
// URLBuilder creates registry API urls from a single base endpoint. It can be
// used to create urls for use in a registry client or server.
//
// All urls will be created from the given base, including the api version.
// For example, if a root of "/foo/" is provided, urls generated will be fall
// under "/foo/v2/...". Most application will only provide a schema, host and
// port, such as "https://localhost:5000/".
type URLBuilder struct {
root *url.URL // url root (ie http://localhost/)
2014-11-26 20:16:58 +00:00
router *mux.Router
}
// NewURLBuilder creates a URLBuilder with provided root url object.
func NewURLBuilder(root *url.URL) *URLBuilder {
return &URLBuilder{
root: root,
router: Router(),
2014-11-26 20:16:58 +00:00
}
}
// NewURLBuilderFromString workes identically to NewURLBuilder except it takes
// a string argument for the root, returning an error if it is not a valid
// url.
func NewURLBuilderFromString(root string) (*URLBuilder, error) {
u, err := url.Parse(root)
if err != nil {
return nil, err
2014-11-26 20:16:58 +00:00
}
return NewURLBuilder(u), nil
2014-11-26 20:16:58 +00:00
}
// NewURLBuilderFromRequest uses information from an *http.Request to
// construct the root url.
func NewURLBuilderFromRequest(r *http.Request) *URLBuilder {
u := &url.URL{
Scheme: r.URL.Scheme,
Host: r.Host,
2014-11-26 20:16:58 +00:00
}
return NewURLBuilder(u)
2014-11-26 20:16:58 +00:00
}
// BuildBaseURL constructs a base url for the API, typically just "/v2/".
func (ub *URLBuilder) BuildBaseURL() (string, error) {
route := ub.cloneRoute(RouteNameBase)
baseURL, err := route.URL()
if err != nil {
return "", err
}
return baseURL.String(), nil
}
// BuildTagsURL constructs a url to list the tags in the named repository.
func (ub *URLBuilder) BuildTagsURL(name string) (string, error) {
route := ub.cloneRoute(RouteNameTags)
tagsURL, err := route.URL("name", name)
if err != nil {
return "", err
}
return tagsURL.String(), nil
}
// BuildManifestURL constructs a url for the manifest identified by name and tag.
func (ub *URLBuilder) BuildManifestURL(name, tag string) (string, error) {
route := ub.cloneRoute(RouteNameManifest)
2014-11-26 20:16:58 +00:00
manifestURL, err := route.URL("name", name, "tag", tag)
2014-11-26 20:16:58 +00:00
if err != nil {
return "", err
}
return manifestURL.String(), nil
}
// BuildBlobURL constructs the url for the blob identified by name and dgst.
func (ub *URLBuilder) BuildBlobURL(name string, dgst digest.Digest) (string, error) {
route := ub.cloneRoute(RouteNameBlob)
2014-11-26 20:16:58 +00:00
layerURL, err := route.URL("name", name, "digest", dgst.String())
2014-11-26 20:16:58 +00:00
if err != nil {
return "", err
}
return layerURL.String(), nil
}
// BuildBlobUploadURL constructs a url to begin a blob upload in the
// repository identified by name.
func (ub *URLBuilder) BuildBlobUploadURL(name string, values ...url.Values) (string, error) {
route := ub.cloneRoute(RouteNameBlobUpload)
2014-11-26 20:16:58 +00:00
uploadURL, err := route.URL("name", name)
2014-11-26 20:16:58 +00:00
if err != nil {
return "", err
}
return appendValuesURL(uploadURL, values...).String(), nil
2014-11-26 20:16:58 +00:00
}
// BuildBlobUploadChunkURL constructs a url for the upload identified by uuid,
// including any url values. This should generally not be used by clients, as
// this url is provided by server implementations during the blob upload
// process.
func (ub *URLBuilder) BuildBlobUploadChunkURL(name, uuid string, values ...url.Values) (string, error) {
route := ub.cloneRoute(RouteNameBlobUploadChunk)
2014-11-26 20:16:58 +00:00
uploadURL, err := route.URL("name", name, "uuid", uuid)
2014-11-26 20:16:58 +00:00
if err != nil {
return "", err
}
return appendValuesURL(uploadURL, values...).String(), nil
}
// clondedRoute returns a clone of the named route from the router. Routes
// must be cloned to avoid modifying them during url generation.
func (ub *URLBuilder) cloneRoute(name string) *mux.Route {
route := new(mux.Route)
*route = *ub.router.GetRoute(name) // clone the route
return route.
Schemes(ub.root.Scheme).
Host(ub.root.Host)
}
2014-11-26 20:16:58 +00:00
// appendValuesURL appends the parameters to the url.
func appendValuesURL(u *url.URL, values ...url.Values) *url.URL {
merged := u.Query()
for _, v := range values {
for k, vv := range v {
merged[k] = append(merged[k], vv...)
}
}
u.RawQuery = merged.Encode()
return u
}
// appendValues appends the parameters to the url. Panics if the string is not
// a url.
func appendValues(u string, values ...url.Values) string {
up, err := url.Parse(u)
if err != nil {
panic(err) // should never happen
}
return appendValuesURL(up, values...).String()
}