http.go 4.1 KB

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