mirror of https://github.com/docker/docs.git
Refactor utils/http.go, fixes #11899
Signed-off-by: Antonio Murdaca <me@runcom.ninja>
This commit is contained in:
parent
d95fae062b
commit
0995ab5946
|
@ -0,0 +1,2 @@
|
||||||
|
This package provides helper functions for decorating a request with user agent
|
||||||
|
versions, auth, meta headers.
|
|
@ -0,0 +1,172 @@
|
||||||
|
// Package requestdecorator provides helper functions to decorate a request with
|
||||||
|
// user agent versions, auth, meta headers.
|
||||||
|
package requestdecorator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/Sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrNilRequest = errors.New("request cannot be nil")
|
||||||
|
)
|
||||||
|
|
||||||
|
// UAVersionInfo is used to model UserAgent versions.
|
||||||
|
type UAVersionInfo struct {
|
||||||
|
Name string
|
||||||
|
Version string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUAVersionInfo(name, version string) UAVersionInfo {
|
||||||
|
return UAVersionInfo{
|
||||||
|
Name: name,
|
||||||
|
Version: version,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (vi *UAVersionInfo) isValid() bool {
|
||||||
|
const stopChars = " \t\r\n/"
|
||||||
|
name := vi.Name
|
||||||
|
vers := vi.Version
|
||||||
|
if len(name) == 0 || strings.ContainsAny(name, stopChars) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if len(vers) == 0 || strings.ContainsAny(vers, stopChars) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert versions to a string and append the string to the string base.
|
||||||
|
//
|
||||||
|
// Each UAVersionInfo will be converted to a string in the format of
|
||||||
|
// "product/version", where the "product" is get from the name field, while
|
||||||
|
// version is get from the version field. Several pieces of verson information
|
||||||
|
// will be concatinated and separated by space.
|
||||||
|
func appendVersions(base string, versions ...UAVersionInfo) string {
|
||||||
|
if len(versions) == 0 {
|
||||||
|
return base
|
||||||
|
}
|
||||||
|
|
||||||
|
verstrs := make([]string, 0, 1+len(versions))
|
||||||
|
if len(base) > 0 {
|
||||||
|
verstrs = append(verstrs, base)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, v := range versions {
|
||||||
|
if !v.isValid() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
verstrs = append(verstrs, v.Name+"/"+v.Version)
|
||||||
|
}
|
||||||
|
return strings.Join(verstrs, " ")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decorator is used to change an instance of
|
||||||
|
// http.Request. It could be used to add more header fields,
|
||||||
|
// change body, etc.
|
||||||
|
type Decorator interface {
|
||||||
|
// ChangeRequest() changes the request accordingly.
|
||||||
|
// The changed request will be returned or err will be non-nil
|
||||||
|
// if an error occur.
|
||||||
|
ChangeRequest(req *http.Request) (newReq *http.Request, err error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UserAgentDecorator appends the product/version to the user agent field
|
||||||
|
// of a request.
|
||||||
|
type UserAgentDecorator struct {
|
||||||
|
Versions []UAVersionInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *UserAgentDecorator) ChangeRequest(req *http.Request) (*http.Request, error) {
|
||||||
|
if req == nil {
|
||||||
|
return req, ErrNilRequest
|
||||||
|
}
|
||||||
|
|
||||||
|
userAgent := appendVersions(req.UserAgent(), h.Versions...)
|
||||||
|
if len(userAgent) > 0 {
|
||||||
|
req.Header.Set("User-Agent", userAgent)
|
||||||
|
}
|
||||||
|
return req, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type MetaHeadersDecorator struct {
|
||||||
|
Headers map[string][]string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *MetaHeadersDecorator) ChangeRequest(req *http.Request) (*http.Request, error) {
|
||||||
|
if h.Headers == nil {
|
||||||
|
return req, ErrNilRequest
|
||||||
|
}
|
||||||
|
for k, v := range h.Headers {
|
||||||
|
req.Header[k] = v
|
||||||
|
}
|
||||||
|
return req, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type AuthDecorator struct {
|
||||||
|
login string
|
||||||
|
password string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAuthDecorator(login, password string) Decorator {
|
||||||
|
return &AuthDecorator{
|
||||||
|
login: login,
|
||||||
|
password: password,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *AuthDecorator) ChangeRequest(req *http.Request) (*http.Request, error) {
|
||||||
|
if req == nil {
|
||||||
|
return req, ErrNilRequest
|
||||||
|
}
|
||||||
|
req.SetBasicAuth(self.login, self.password)
|
||||||
|
return req, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RequestFactory creates an HTTP request
|
||||||
|
// and applies a list of decorators on the request.
|
||||||
|
type RequestFactory struct {
|
||||||
|
decorators []Decorator
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRequestFactory(d ...Decorator) *RequestFactory {
|
||||||
|
return &RequestFactory{
|
||||||
|
decorators: d,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *RequestFactory) AddDecorator(d ...Decorator) {
|
||||||
|
f.decorators = append(f.decorators, d...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *RequestFactory) GetDecorators() []Decorator {
|
||||||
|
return f.decorators
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRequest() creates a new *http.Request,
|
||||||
|
// applies all decorators in the Factory on the request,
|
||||||
|
// then applies decorators provided by d on the request.
|
||||||
|
func (h *RequestFactory) NewRequest(method, urlStr string, body io.Reader, d ...Decorator) (*http.Request, error) {
|
||||||
|
req, err := http.NewRequest(method, urlStr, body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// By default, a nil factory should work.
|
||||||
|
if h == nil {
|
||||||
|
return req, nil
|
||||||
|
}
|
||||||
|
for _, dec := range h.decorators {
|
||||||
|
req, _ = dec.ChangeRequest(req)
|
||||||
|
}
|
||||||
|
for _, dec := range d {
|
||||||
|
req, _ = dec.ChangeRequest(req)
|
||||||
|
}
|
||||||
|
logrus.Debugf("%v -- HEADERS: %v", req.URL, req.Header)
|
||||||
|
return req, err
|
||||||
|
}
|
|
@ -0,0 +1,222 @@
|
||||||
|
package requestdecorator
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUAVersionInfo(t *testing.T) {
|
||||||
|
uavi := NewUAVersionInfo("foo", "bar")
|
||||||
|
if !uavi.isValid() {
|
||||||
|
t.Fatalf("UAVersionInfo should be valid")
|
||||||
|
}
|
||||||
|
uavi = NewUAVersionInfo("", "bar")
|
||||||
|
if uavi.isValid() {
|
||||||
|
t.Fatalf("Expected UAVersionInfo to be invalid")
|
||||||
|
}
|
||||||
|
uavi = NewUAVersionInfo("foo", "")
|
||||||
|
if uavi.isValid() {
|
||||||
|
t.Fatalf("Expected UAVersionInfo to be invalid")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUserAgentDecorator(t *testing.T) {
|
||||||
|
httpVersion := make([]UAVersionInfo, 2)
|
||||||
|
httpVersion = append(httpVersion, NewUAVersionInfo("testname", "testversion"))
|
||||||
|
httpVersion = append(httpVersion, NewUAVersionInfo("name", "version"))
|
||||||
|
uad := &UserAgentDecorator{
|
||||||
|
Versions: httpVersion,
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest("GET", "/something", strings.NewReader("test"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
reqDecorated, err := uad.ChangeRequest(req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if reqDecorated.Header.Get("User-Agent") != "testname/testversion name/version" {
|
||||||
|
t.Fatalf("Request should have User-Agent 'testname/testversion name/version'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUserAgentDecoratorErr(t *testing.T) {
|
||||||
|
httpVersion := make([]UAVersionInfo, 0)
|
||||||
|
uad := &UserAgentDecorator{
|
||||||
|
Versions: httpVersion,
|
||||||
|
}
|
||||||
|
|
||||||
|
var req *http.Request
|
||||||
|
_, err := uad.ChangeRequest(req)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Expected to get ErrNilRequest instead no error was returned")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMetaHeadersDecorator(t *testing.T) {
|
||||||
|
var headers = map[string][]string{
|
||||||
|
"key1": {"value1"},
|
||||||
|
"key2": {"value2"},
|
||||||
|
}
|
||||||
|
mhd := &MetaHeadersDecorator{
|
||||||
|
Headers: headers,
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := http.NewRequest("GET", "/something", strings.NewReader("test"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
reqDecorated, err := mhd.ChangeRequest(req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
v, ok := reqDecorated.Header["key1"]
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("Expected to have header key1")
|
||||||
|
}
|
||||||
|
if v[0] != "value1" {
|
||||||
|
t.Fatalf("Expected value for key1 isn't value1")
|
||||||
|
}
|
||||||
|
|
||||||
|
v, ok = reqDecorated.Header["key2"]
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("Expected to have header key2")
|
||||||
|
}
|
||||||
|
if v[0] != "value2" {
|
||||||
|
t.Fatalf("Expected value for key2 isn't value2")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMetaHeadersDecoratorErr(t *testing.T) {
|
||||||
|
mhd := &MetaHeadersDecorator{}
|
||||||
|
|
||||||
|
var req *http.Request
|
||||||
|
_, err := mhd.ChangeRequest(req)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Expected to get ErrNilRequest instead no error was returned")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAuthDecorator(t *testing.T) {
|
||||||
|
ad := NewAuthDecorator("test", "password")
|
||||||
|
|
||||||
|
req, err := http.NewRequest("GET", "/something", strings.NewReader("test"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
reqDecorated, err := ad.ChangeRequest(req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
username, password, ok := reqDecorated.BasicAuth()
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("Cannot retrieve basic auth info from request")
|
||||||
|
}
|
||||||
|
if username != "test" {
|
||||||
|
t.Fatalf("Expected username to be test, got %s", username)
|
||||||
|
}
|
||||||
|
if password != "password" {
|
||||||
|
t.Fatalf("Expected password to be password, got %s", password)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAuthDecoratorErr(t *testing.T) {
|
||||||
|
ad := &AuthDecorator{}
|
||||||
|
|
||||||
|
var req *http.Request
|
||||||
|
_, err := ad.ChangeRequest(req)
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("Expected to get ErrNilRequest instead no error was returned")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRequestFactory(t *testing.T) {
|
||||||
|
ad := NewAuthDecorator("test", "password")
|
||||||
|
httpVersion := make([]UAVersionInfo, 2)
|
||||||
|
httpVersion = append(httpVersion, NewUAVersionInfo("testname", "testversion"))
|
||||||
|
httpVersion = append(httpVersion, NewUAVersionInfo("name", "version"))
|
||||||
|
uad := &UserAgentDecorator{
|
||||||
|
Versions: httpVersion,
|
||||||
|
}
|
||||||
|
|
||||||
|
requestFactory := NewRequestFactory(ad, uad)
|
||||||
|
|
||||||
|
if dlen := requestFactory.GetDecorators(); len(dlen) != 2 {
|
||||||
|
t.Fatalf("Expected to have two decorators, got %d", dlen)
|
||||||
|
}
|
||||||
|
|
||||||
|
req, err := requestFactory.NewRequest("GET", "/test", strings.NewReader("test"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
username, password, ok := req.BasicAuth()
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("Cannot retrieve basic auth info from request")
|
||||||
|
}
|
||||||
|
if username != "test" {
|
||||||
|
t.Fatalf("Expected username to be test, got %s", username)
|
||||||
|
}
|
||||||
|
if password != "password" {
|
||||||
|
t.Fatalf("Expected password to be password, got %s", password)
|
||||||
|
}
|
||||||
|
if req.Header.Get("User-Agent") != "testname/testversion name/version" {
|
||||||
|
t.Fatalf("Request should have User-Agent 'testname/testversion name/version'")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRequestFactoryNewRequestWithDecorators(t *testing.T) {
|
||||||
|
ad := NewAuthDecorator("test", "password")
|
||||||
|
|
||||||
|
requestFactory := NewRequestFactory(ad)
|
||||||
|
|
||||||
|
if dlen := requestFactory.GetDecorators(); len(dlen) != 1 {
|
||||||
|
t.Fatalf("Expected to have one decorators, got %d", dlen)
|
||||||
|
}
|
||||||
|
|
||||||
|
ad2 := NewAuthDecorator("test2", "password2")
|
||||||
|
|
||||||
|
req, err := requestFactory.NewRequest("GET", "/test", strings.NewReader("test"), ad2)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
username, password, ok := req.BasicAuth()
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("Cannot retrieve basic auth info from request")
|
||||||
|
}
|
||||||
|
if username != "test2" {
|
||||||
|
t.Fatalf("Expected username to be test, got %s", username)
|
||||||
|
}
|
||||||
|
if password != "password2" {
|
||||||
|
t.Fatalf("Expected password to be password, got %s", password)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRequestFactoryAddDecorator(t *testing.T) {
|
||||||
|
requestFactory := NewRequestFactory()
|
||||||
|
|
||||||
|
if dlen := requestFactory.GetDecorators(); len(dlen) != 0 {
|
||||||
|
t.Fatalf("Expected to have zero decorators, got %d", dlen)
|
||||||
|
}
|
||||||
|
|
||||||
|
ad := NewAuthDecorator("test", "password")
|
||||||
|
requestFactory.AddDecorator(ad)
|
||||||
|
|
||||||
|
if dlen := requestFactory.GetDecorators(); len(dlen) != 1 {
|
||||||
|
t.Fatalf("Expected to have one decorators, got %d", dlen)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRequestFactoryNil(t *testing.T) {
|
||||||
|
var requestFactory RequestFactory
|
||||||
|
_, err := requestFactory.NewRequest("GET", "/test", strings.NewReader("test"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Expected not to get and error, got %s", err)
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,7 +14,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/docker/docker/utils"
|
"github.com/docker/docker/pkg/requestdecorator"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -225,7 +225,7 @@ func SaveConfig(configFile *ConfigFile) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Login tries to register/login to the registry server.
|
// Login tries to register/login to the registry server.
|
||||||
func Login(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *utils.HTTPRequestFactory) (string, error) {
|
func Login(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *requestdecorator.RequestFactory) (string, error) {
|
||||||
// Separates the v2 registry login logic from the v1 logic.
|
// Separates the v2 registry login logic from the v1 logic.
|
||||||
if registryEndpoint.Version == APIVersion2 {
|
if registryEndpoint.Version == APIVersion2 {
|
||||||
return loginV2(authConfig, registryEndpoint, factory)
|
return loginV2(authConfig, registryEndpoint, factory)
|
||||||
|
@ -235,7 +235,7 @@ func Login(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *utils.HT
|
||||||
}
|
}
|
||||||
|
|
||||||
// loginV1 tries to register/login to the v1 registry server.
|
// loginV1 tries to register/login to the v1 registry server.
|
||||||
func loginV1(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *utils.HTTPRequestFactory) (string, error) {
|
func loginV1(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *requestdecorator.RequestFactory) (string, error) {
|
||||||
var (
|
var (
|
||||||
status string
|
status string
|
||||||
reqBody []byte
|
reqBody []byte
|
||||||
|
@ -348,7 +348,7 @@ func loginV1(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *utils.
|
||||||
// now, users should create their account through other means like directly from a web page
|
// now, users should create their account through other means like directly from a web page
|
||||||
// served by the v2 registry service provider. Whether this will be supported in the future
|
// served by the v2 registry service provider. Whether this will be supported in the future
|
||||||
// is to be determined.
|
// is to be determined.
|
||||||
func loginV2(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *utils.HTTPRequestFactory) (string, error) {
|
func loginV2(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *requestdecorator.RequestFactory) (string, error) {
|
||||||
logrus.Debugf("attempting v2 login to registry endpoint %s", registryEndpoint)
|
logrus.Debugf("attempting v2 login to registry endpoint %s", registryEndpoint)
|
||||||
var (
|
var (
|
||||||
err error
|
err error
|
||||||
|
@ -381,7 +381,7 @@ func loginV2(authConfig *AuthConfig, registryEndpoint *Endpoint, factory *utils.
|
||||||
return "", fmt.Errorf("no successful auth challenge for %s - errors: %s", registryEndpoint, allErrors)
|
return "", fmt.Errorf("no successful auth challenge for %s - errors: %s", registryEndpoint, allErrors)
|
||||||
}
|
}
|
||||||
|
|
||||||
func tryV2BasicAuthLogin(authConfig *AuthConfig, params map[string]string, registryEndpoint *Endpoint, client *http.Client, factory *utils.HTTPRequestFactory) error {
|
func tryV2BasicAuthLogin(authConfig *AuthConfig, params map[string]string, registryEndpoint *Endpoint, client *http.Client, factory *requestdecorator.RequestFactory) error {
|
||||||
req, err := factory.NewRequest("GET", registryEndpoint.Path(""), nil)
|
req, err := factory.NewRequest("GET", registryEndpoint.Path(""), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -402,7 +402,7 @@ func tryV2BasicAuthLogin(authConfig *AuthConfig, params map[string]string, regis
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func tryV2TokenAuthLogin(authConfig *AuthConfig, params map[string]string, registryEndpoint *Endpoint, client *http.Client, factory *utils.HTTPRequestFactory) error {
|
func tryV2TokenAuthLogin(authConfig *AuthConfig, params map[string]string, registryEndpoint *Endpoint, client *http.Client, factory *requestdecorator.RequestFactory) error {
|
||||||
token, err := getToken(authConfig.Username, authConfig.Password, params, registryEndpoint, client, factory)
|
token, err := getToken(authConfig.Username, authConfig.Password, params, registryEndpoint, client, factory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -11,8 +11,8 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
|
"github.com/docker/docker/pkg/requestdecorator"
|
||||||
"github.com/docker/docker/registry/v2"
|
"github.com/docker/docker/registry/v2"
|
||||||
"github.com/docker/docker/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// for mocking in unit tests
|
// for mocking in unit tests
|
||||||
|
@ -162,7 +162,7 @@ func (e *Endpoint) Ping() (RegistryInfo, error) {
|
||||||
return RegistryInfo{}, fmt.Errorf("unable to ping registry endpoint %s\nv2 ping attempt failed with error: %s\n v1 ping attempt failed with error: %s", e, errV2, errV1)
|
return RegistryInfo{}, fmt.Errorf("unable to ping registry endpoint %s\nv2 ping attempt failed with error: %s\n v1 ping attempt failed with error: %s", e, errV2, errV1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Endpoint) pingV1(factory *utils.HTTPRequestFactory) (RegistryInfo, error) {
|
func (e *Endpoint) pingV1(factory *requestdecorator.RequestFactory) (RegistryInfo, error) {
|
||||||
logrus.Debugf("attempting v1 ping for registry endpoint %s", e)
|
logrus.Debugf("attempting v1 ping for registry endpoint %s", e)
|
||||||
|
|
||||||
if e.String() == IndexServerAddress() {
|
if e.String() == IndexServerAddress() {
|
||||||
|
@ -216,7 +216,7 @@ func (e *Endpoint) pingV1(factory *utils.HTTPRequestFactory) (RegistryInfo, erro
|
||||||
return info, nil
|
return info, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *Endpoint) pingV2(factory *utils.HTTPRequestFactory) (RegistryInfo, error) {
|
func (e *Endpoint) pingV2(factory *requestdecorator.RequestFactory) (RegistryInfo, error) {
|
||||||
logrus.Debugf("attempting v2 ping for registry endpoint %s", e)
|
logrus.Debugf("attempting v2 ping for registry endpoint %s", e)
|
||||||
|
|
||||||
req, err := factory.NewRequest("GET", e.Path(""), nil)
|
req, err := factory.NewRequest("GET", e.Path(""), nil)
|
||||||
|
|
|
@ -5,42 +5,26 @@ import (
|
||||||
|
|
||||||
"github.com/docker/docker/autogen/dockerversion"
|
"github.com/docker/docker/autogen/dockerversion"
|
||||||
"github.com/docker/docker/pkg/parsers/kernel"
|
"github.com/docker/docker/pkg/parsers/kernel"
|
||||||
"github.com/docker/docker/utils"
|
"github.com/docker/docker/pkg/requestdecorator"
|
||||||
)
|
)
|
||||||
|
|
||||||
func HTTPRequestFactory(metaHeaders map[string][]string) *utils.HTTPRequestFactory {
|
func HTTPRequestFactory(metaHeaders map[string][]string) *requestdecorator.RequestFactory {
|
||||||
// FIXME: this replicates the 'info' job.
|
// FIXME: this replicates the 'info' job.
|
||||||
httpVersion := make([]utils.VersionInfo, 0, 4)
|
httpVersion := make([]requestdecorator.UAVersionInfo, 0, 4)
|
||||||
httpVersion = append(httpVersion, &simpleVersionInfo{"docker", dockerversion.VERSION})
|
httpVersion = append(httpVersion, requestdecorator.NewUAVersionInfo("docker", dockerversion.VERSION))
|
||||||
httpVersion = append(httpVersion, &simpleVersionInfo{"go", runtime.Version()})
|
httpVersion = append(httpVersion, requestdecorator.NewUAVersionInfo("go", runtime.Version()))
|
||||||
httpVersion = append(httpVersion, &simpleVersionInfo{"git-commit", dockerversion.GITCOMMIT})
|
httpVersion = append(httpVersion, requestdecorator.NewUAVersionInfo("git-commit", dockerversion.GITCOMMIT))
|
||||||
if kernelVersion, err := kernel.GetKernelVersion(); err == nil {
|
if kernelVersion, err := kernel.GetKernelVersion(); err == nil {
|
||||||
httpVersion = append(httpVersion, &simpleVersionInfo{"kernel", kernelVersion.String()})
|
httpVersion = append(httpVersion, requestdecorator.NewUAVersionInfo("kernel", kernelVersion.String()))
|
||||||
}
|
}
|
||||||
httpVersion = append(httpVersion, &simpleVersionInfo{"os", runtime.GOOS})
|
httpVersion = append(httpVersion, requestdecorator.NewUAVersionInfo("os", runtime.GOOS))
|
||||||
httpVersion = append(httpVersion, &simpleVersionInfo{"arch", runtime.GOARCH})
|
httpVersion = append(httpVersion, requestdecorator.NewUAVersionInfo("arch", runtime.GOARCH))
|
||||||
ud := utils.NewHTTPUserAgentDecorator(httpVersion...)
|
uad := &requestdecorator.UserAgentDecorator{
|
||||||
md := &utils.HTTPMetaHeadersDecorator{
|
Versions: httpVersion,
|
||||||
|
}
|
||||||
|
mhd := &requestdecorator.MetaHeadersDecorator{
|
||||||
Headers: metaHeaders,
|
Headers: metaHeaders,
|
||||||
}
|
}
|
||||||
factory := utils.NewHTTPRequestFactory(ud, md)
|
factory := requestdecorator.NewRequestFactory(uad, mhd)
|
||||||
return factory
|
return factory
|
||||||
}
|
}
|
||||||
|
|
||||||
// simpleVersionInfo is a simple implementation of
|
|
||||||
// the interface VersionInfo, which is used
|
|
||||||
// to provide version information for some product,
|
|
||||||
// component, etc. It stores the product name and the version
|
|
||||||
// in string and returns them on calls to Name() and Version().
|
|
||||||
type simpleVersionInfo struct {
|
|
||||||
name string
|
|
||||||
version string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *simpleVersionInfo) Name() string {
|
|
||||||
return v.name
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v *simpleVersionInfo) Version() string {
|
|
||||||
return v.version
|
|
||||||
}
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/docker/docker/utils"
|
"github.com/docker/docker/pkg/requestdecorator"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -25,7 +25,7 @@ func spawnTestRegistrySession(t *testing.T) *Session {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
r, err := NewSession(authConfig, utils.NewHTTPRequestFactory(), endpoint, true)
|
r, err := NewSession(authConfig, requestdecorator.NewRequestFactory(), endpoint, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ func TestPublicSession(t *testing.T) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
r, err := NewSession(authConfig, utils.NewHTTPRequestFactory(), endpoint, true)
|
r, err := NewSession(authConfig, requestdecorator.NewRequestFactory(), endpoint, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,19 +19,20 @@ import (
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
"github.com/Sirupsen/logrus"
|
||||||
"github.com/docker/docker/pkg/httputils"
|
"github.com/docker/docker/pkg/httputils"
|
||||||
|
"github.com/docker/docker/pkg/requestdecorator"
|
||||||
"github.com/docker/docker/pkg/tarsum"
|
"github.com/docker/docker/pkg/tarsum"
|
||||||
"github.com/docker/docker/utils"
|
"github.com/docker/docker/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Session struct {
|
type Session struct {
|
||||||
authConfig *AuthConfig
|
authConfig *AuthConfig
|
||||||
reqFactory *utils.HTTPRequestFactory
|
reqFactory *requestdecorator.RequestFactory
|
||||||
indexEndpoint *Endpoint
|
indexEndpoint *Endpoint
|
||||||
jar *cookiejar.Jar
|
jar *cookiejar.Jar
|
||||||
timeout TimeoutType
|
timeout TimeoutType
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSession(authConfig *AuthConfig, factory *utils.HTTPRequestFactory, endpoint *Endpoint, timeout bool) (r *Session, err error) {
|
func NewSession(authConfig *AuthConfig, factory *requestdecorator.RequestFactory, endpoint *Endpoint, timeout bool) (r *Session, err error) {
|
||||||
r = &Session{
|
r = &Session{
|
||||||
authConfig: authConfig,
|
authConfig: authConfig,
|
||||||
indexEndpoint: endpoint,
|
indexEndpoint: endpoint,
|
||||||
|
@ -55,7 +56,7 @@ func NewSession(authConfig *AuthConfig, factory *utils.HTTPRequestFactory, endpo
|
||||||
}
|
}
|
||||||
if info.Standalone {
|
if info.Standalone {
|
||||||
logrus.Debugf("Endpoint %s is eligible for private registry. Enabling decorator.", r.indexEndpoint.String())
|
logrus.Debugf("Endpoint %s is eligible for private registry. Enabling decorator.", r.indexEndpoint.String())
|
||||||
dec := utils.NewHTTPAuthDecorator(authConfig.Username, authConfig.Password)
|
dec := requestdecorator.NewAuthDecorator(authConfig.Username, authConfig.Password)
|
||||||
factory.AddDecorator(dec)
|
factory.AddDecorator(dec)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,14 +8,14 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/docker/utils"
|
"github.com/docker/docker/pkg/requestdecorator"
|
||||||
)
|
)
|
||||||
|
|
||||||
type tokenResponse struct {
|
type tokenResponse struct {
|
||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func getToken(username, password string, params map[string]string, registryEndpoint *Endpoint, client *http.Client, factory *utils.HTTPRequestFactory) (token string, err error) {
|
func getToken(username, password string, params map[string]string, registryEndpoint *Endpoint, client *http.Client, factory *requestdecorator.RequestFactory) (token string, err error) {
|
||||||
realm, ok := params["realm"]
|
realm, ok := params["realm"]
|
||||||
if !ok {
|
if !ok {
|
||||||
return "", errors.New("no realm specified for token auth challenge")
|
return "", errors.New("no realm specified for token auth challenge")
|
||||||
|
|
168
utils/http.go
168
utils/http.go
|
@ -1,168 +0,0 @@
|
||||||
package utils
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/Sirupsen/logrus"
|
|
||||||
)
|
|
||||||
|
|
||||||
// VersionInfo is used to model entities which has a version.
|
|
||||||
// It is basically a tupple with name and version.
|
|
||||||
type VersionInfo interface {
|
|
||||||
Name() string
|
|
||||||
Version() string
|
|
||||||
}
|
|
||||||
|
|
||||||
func validVersion(version VersionInfo) bool {
|
|
||||||
const stopChars = " \t\r\n/"
|
|
||||||
name := version.Name()
|
|
||||||
vers := version.Version()
|
|
||||||
if len(name) == 0 || strings.ContainsAny(name, stopChars) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if len(vers) == 0 || strings.ContainsAny(vers, stopChars) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert versions to a string and append the string to the string base.
|
|
||||||
//
|
|
||||||
// Each VersionInfo will be converted to a string in the format of
|
|
||||||
// "product/version", where the "product" is get from the Name() method, while
|
|
||||||
// version is get from the Version() method. Several pieces of verson information
|
|
||||||
// will be concatinated and separated by space.
|
|
||||||
func appendVersions(base string, versions ...VersionInfo) string {
|
|
||||||
if len(versions) == 0 {
|
|
||||||
return base
|
|
||||||
}
|
|
||||||
|
|
||||||
verstrs := make([]string, 0, 1+len(versions))
|
|
||||||
if len(base) > 0 {
|
|
||||||
verstrs = append(verstrs, base)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, v := range versions {
|
|
||||||
if !validVersion(v) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
verstrs = append(verstrs, v.Name()+"/"+v.Version())
|
|
||||||
}
|
|
||||||
return strings.Join(verstrs, " ")
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTPRequestDecorator is used to change an instance of
|
|
||||||
// http.Request. It could be used to add more header fields,
|
|
||||||
// change body, etc.
|
|
||||||
type HTTPRequestDecorator interface {
|
|
||||||
// ChangeRequest() changes the request accordingly.
|
|
||||||
// The changed request will be returned or err will be non-nil
|
|
||||||
// if an error occur.
|
|
||||||
ChangeRequest(req *http.Request) (newReq *http.Request, err error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTPUserAgentDecorator appends the product/version to the user agent field
|
|
||||||
// of a request.
|
|
||||||
type HTTPUserAgentDecorator struct {
|
|
||||||
versions []VersionInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewHTTPUserAgentDecorator(versions ...VersionInfo) HTTPRequestDecorator {
|
|
||||||
return &HTTPUserAgentDecorator{
|
|
||||||
versions: versions,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *HTTPUserAgentDecorator) ChangeRequest(req *http.Request) (newReq *http.Request, err error) {
|
|
||||||
if req == nil {
|
|
||||||
return req, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
userAgent := appendVersions(req.UserAgent(), h.versions...)
|
|
||||||
if len(userAgent) > 0 {
|
|
||||||
req.Header.Set("User-Agent", userAgent)
|
|
||||||
}
|
|
||||||
return req, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type HTTPMetaHeadersDecorator struct {
|
|
||||||
Headers map[string][]string
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *HTTPMetaHeadersDecorator) ChangeRequest(req *http.Request) (newReq *http.Request, err error) {
|
|
||||||
if h.Headers == nil {
|
|
||||||
return req, nil
|
|
||||||
}
|
|
||||||
for k, v := range h.Headers {
|
|
||||||
req.Header[k] = v
|
|
||||||
}
|
|
||||||
return req, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type HTTPAuthDecorator struct {
|
|
||||||
login string
|
|
||||||
password string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewHTTPAuthDecorator(login, password string) HTTPRequestDecorator {
|
|
||||||
return &HTTPAuthDecorator{
|
|
||||||
login: login,
|
|
||||||
password: password,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *HTTPAuthDecorator) ChangeRequest(req *http.Request) (*http.Request, error) {
|
|
||||||
req.SetBasicAuth(self.login, self.password)
|
|
||||||
return req, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTPRequestFactory creates an HTTP request
|
|
||||||
// and applies a list of decorators on the request.
|
|
||||||
type HTTPRequestFactory struct {
|
|
||||||
decorators []HTTPRequestDecorator
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewHTTPRequestFactory(d ...HTTPRequestDecorator) *HTTPRequestFactory {
|
|
||||||
return &HTTPRequestFactory{
|
|
||||||
decorators: d,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *HTTPRequestFactory) AddDecorator(d ...HTTPRequestDecorator) {
|
|
||||||
self.decorators = append(self.decorators, d...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *HTTPRequestFactory) GetDecorators() []HTTPRequestDecorator {
|
|
||||||
return self.decorators
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewRequest() creates a new *http.Request,
|
|
||||||
// applies all decorators in the HTTPRequestFactory on the request,
|
|
||||||
// then applies decorators provided by d on the request.
|
|
||||||
func (h *HTTPRequestFactory) NewRequest(method, urlStr string, body io.Reader, d ...HTTPRequestDecorator) (*http.Request, error) {
|
|
||||||
req, err := http.NewRequest(method, urlStr, body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// By default, a nil factory should work.
|
|
||||||
if h == nil {
|
|
||||||
return req, nil
|
|
||||||
}
|
|
||||||
for _, dec := range h.decorators {
|
|
||||||
req, err = dec.ChangeRequest(req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for _, dec := range d {
|
|
||||||
req, err = dec.ChangeRequest(req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logrus.Debugf("%v -- HEADERS: %v", req.URL, req.Header)
|
|
||||||
return req, err
|
|
||||||
}
|
|
Loading…
Reference in New Issue