http.go 4.1 KB

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