http.go 4.0 KB

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