http.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  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. ret := new(HTTPUserAgentDecorator)
  70. ret.versions = versions
  71. return ret
  72. }
  73. func (self *HTTPUserAgentDecorator) ChangeRequest(req *http.Request) (newReq *http.Request, err error) {
  74. if req == nil {
  75. return req, nil
  76. }
  77. userAgent := appendVersions(req.UserAgent(), self.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 (self *HTTPMetaHeadersDecorator) ChangeRequest(req *http.Request) (newReq *http.Request, err error) {
  87. if self.Headers == nil {
  88. return req, nil
  89. }
  90. for k, v := range self.Headers {
  91. req.Header[k] = v
  92. }
  93. return req, nil
  94. }
  95. // HTTPRequestFactory creates an HTTP request
  96. // and applies a list of decorators on the request.
  97. type HTTPRequestFactory struct {
  98. decorators []HTTPRequestDecorator
  99. }
  100. func NewHTTPRequestFactory(d ...HTTPRequestDecorator) *HTTPRequestFactory {
  101. ret := new(HTTPRequestFactory)
  102. ret.decorators = d
  103. return ret
  104. }
  105. // NewRequest() creates a new *http.Request,
  106. // applies all decorators in the HTTPRequestFactory on the request,
  107. // then applies decorators provided by d on the request.
  108. func (self *HTTPRequestFactory) NewRequest(method, urlStr string, body io.Reader, d ...HTTPRequestDecorator) (*http.Request, error) {
  109. req, err := http.NewRequest(method, urlStr, body)
  110. if err != nil {
  111. return nil, err
  112. }
  113. // By default, a nil factory should work.
  114. if self == nil {
  115. return req, nil
  116. }
  117. for _, dec := range self.decorators {
  118. req, err = dec.ChangeRequest(req)
  119. if err != nil {
  120. return nil, err
  121. }
  122. }
  123. for _, dec := range d {
  124. req, err = dec.ChangeRequest(req)
  125. if err != nil {
  126. return nil, err
  127. }
  128. }
  129. return req, err
  130. }