github.com/rseymour/docker@v1.5.0/utils/http.go (about)

     1  package utils
     2  
     3  import (
     4  	"io"
     5  	"net/http"
     6  	"strings"
     7  
     8  	log "github.com/Sirupsen/logrus"
     9  )
    10  
    11  // VersionInfo is used to model entities which has a version.
    12  // It is basically a tupple with name and version.
    13  type VersionInfo interface {
    14  	Name() string
    15  	Version() string
    16  }
    17  
    18  func validVersion(version VersionInfo) bool {
    19  	const stopChars = " \t\r\n/"
    20  	name := version.Name()
    21  	vers := version.Version()
    22  	if len(name) == 0 || strings.ContainsAny(name, stopChars) {
    23  		return false
    24  	}
    25  	if len(vers) == 0 || strings.ContainsAny(vers, stopChars) {
    26  		return false
    27  	}
    28  	return true
    29  }
    30  
    31  // Convert versions to a string and append the string to the string base.
    32  //
    33  // Each VersionInfo will be converted to a string in the format of
    34  // "product/version", where the "product" is get from the Name() method, while
    35  // version is get from the Version() method. Several pieces of verson information
    36  // will be concatinated and separated by space.
    37  func appendVersions(base string, versions ...VersionInfo) string {
    38  	if len(versions) == 0 {
    39  		return base
    40  	}
    41  
    42  	verstrs := make([]string, 0, 1+len(versions))
    43  	if len(base) > 0 {
    44  		verstrs = append(verstrs, base)
    45  	}
    46  
    47  	for _, v := range versions {
    48  		if !validVersion(v) {
    49  			continue
    50  		}
    51  		verstrs = append(verstrs, v.Name()+"/"+v.Version())
    52  	}
    53  	return strings.Join(verstrs, " ")
    54  }
    55  
    56  // HTTPRequestDecorator is used to change an instance of
    57  // http.Request. It could be used to add more header fields,
    58  // change body, etc.
    59  type HTTPRequestDecorator interface {
    60  	// ChangeRequest() changes the request accordingly.
    61  	// The changed request will be returned or err will be non-nil
    62  	// if an error occur.
    63  	ChangeRequest(req *http.Request) (newReq *http.Request, err error)
    64  }
    65  
    66  // HTTPUserAgentDecorator appends the product/version to the user agent field
    67  // of a request.
    68  type HTTPUserAgentDecorator struct {
    69  	versions []VersionInfo
    70  }
    71  
    72  func NewHTTPUserAgentDecorator(versions ...VersionInfo) HTTPRequestDecorator {
    73  	return &HTTPUserAgentDecorator{
    74  		versions: versions,
    75  	}
    76  }
    77  
    78  func (h *HTTPUserAgentDecorator) ChangeRequest(req *http.Request) (newReq *http.Request, err error) {
    79  	if req == nil {
    80  		return req, nil
    81  	}
    82  
    83  	userAgent := appendVersions(req.UserAgent(), h.versions...)
    84  	if len(userAgent) > 0 {
    85  		req.Header.Set("User-Agent", userAgent)
    86  	}
    87  	return req, nil
    88  }
    89  
    90  type HTTPMetaHeadersDecorator struct {
    91  	Headers map[string][]string
    92  }
    93  
    94  func (h *HTTPMetaHeadersDecorator) ChangeRequest(req *http.Request) (newReq *http.Request, err error) {
    95  	if h.Headers == nil {
    96  		return req, nil
    97  	}
    98  	for k, v := range h.Headers {
    99  		req.Header[k] = v
   100  	}
   101  	return req, nil
   102  }
   103  
   104  type HTTPAuthDecorator struct {
   105  	login    string
   106  	password string
   107  }
   108  
   109  func NewHTTPAuthDecorator(login, password string) HTTPRequestDecorator {
   110  	return &HTTPAuthDecorator{
   111  		login:    login,
   112  		password: password,
   113  	}
   114  }
   115  
   116  func (self *HTTPAuthDecorator) ChangeRequest(req *http.Request) (*http.Request, error) {
   117  	req.SetBasicAuth(self.login, self.password)
   118  	return req, nil
   119  }
   120  
   121  // HTTPRequestFactory creates an HTTP request
   122  // and applies a list of decorators on the request.
   123  type HTTPRequestFactory struct {
   124  	decorators []HTTPRequestDecorator
   125  }
   126  
   127  func NewHTTPRequestFactory(d ...HTTPRequestDecorator) *HTTPRequestFactory {
   128  	return &HTTPRequestFactory{
   129  		decorators: d,
   130  	}
   131  }
   132  
   133  func (self *HTTPRequestFactory) AddDecorator(d ...HTTPRequestDecorator) {
   134  	self.decorators = append(self.decorators, d...)
   135  }
   136  
   137  func (self *HTTPRequestFactory) GetDecorators() []HTTPRequestDecorator {
   138  	return self.decorators
   139  }
   140  
   141  // NewRequest() creates a new *http.Request,
   142  // applies all decorators in the HTTPRequestFactory on the request,
   143  // then applies decorators provided by d on the request.
   144  func (h *HTTPRequestFactory) NewRequest(method, urlStr string, body io.Reader, d ...HTTPRequestDecorator) (*http.Request, error) {
   145  	req, err := http.NewRequest(method, urlStr, body)
   146  	if err != nil {
   147  		return nil, err
   148  	}
   149  
   150  	// By default, a nil factory should work.
   151  	if h == nil {
   152  		return req, nil
   153  	}
   154  	for _, dec := range h.decorators {
   155  		req, err = dec.ChangeRequest(req)
   156  		if err != nil {
   157  			return nil, err
   158  		}
   159  	}
   160  	for _, dec := range d {
   161  		req, err = dec.ChangeRequest(req)
   162  		if err != nil {
   163  			return nil, err
   164  		}
   165  	}
   166  	log.Debugf("%v -- HEADERS: %v", req.URL, req.Header)
   167  	return req, err
   168  }