mirror of https://github.com/docker/docs.git
				
				
				
			
		
			
				
	
	
		
			173 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			173 lines
		
	
	
		
			4.0 KiB
		
	
	
	
		
			Go
		
	
	
	
// 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
 | 
						|
}
 |