handler_functions.go 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. package aws
  2. import (
  3. "bytes"
  4. "fmt"
  5. "github.com/ks3sdklib/aws-sdk-go/aws/retry"
  6. "io"
  7. "io/ioutil"
  8. "net/http"
  9. "net/url"
  10. "regexp"
  11. "strconv"
  12. "time"
  13. "github.com/ks3sdklib/aws-sdk-go/aws/awserr"
  14. "github.com/ks3sdklib/aws-sdk-go/internal/apierr"
  15. )
  16. var sleepDelay = func(delay time.Duration) {
  17. time.Sleep(delay)
  18. }
  19. // Interface for matching types which also have a Len method.
  20. type lener interface {
  21. Len() int
  22. }
  23. // BuildContentLength builds the content length of a request based on the body,
  24. // or will use the HTTPRequest.Header's "Content-Length" if defined. If unable
  25. // to determine request body length and no "Content-Length" was specified it will panic.
  26. func BuildContentLength(r *Request) {
  27. if slength := r.HTTPRequest.Header.Get("Content-Length"); slength != "" {
  28. length, _ := strconv.ParseInt(slength, 10, 64)
  29. r.HTTPRequest.ContentLength = length
  30. return
  31. }
  32. var length int64
  33. switch body := r.Body.(type) {
  34. case nil:
  35. length = 0
  36. case lener:
  37. length = int64(body.Len())
  38. case io.Seeker:
  39. r.bodyStart, _ = body.Seek(0, 1)
  40. end, _ := body.Seek(0, 2)
  41. body.Seek(r.bodyStart, 0) // make sure to seek back to original location
  42. length = end - r.bodyStart
  43. default:
  44. panic("Cannot get length of body, must provide `ContentLength`")
  45. }
  46. r.HTTPRequest.ContentLength = length
  47. r.HTTPRequest.Header.Set("Content-Length", fmt.Sprintf("%d", length))
  48. }
  49. // UserAgentHandler is a request handler for injecting User agent into requests.
  50. func UserAgentHandler(r *Request) {
  51. r.HTTPRequest.Header.Set("User-Agent", SDKName+"/"+SDKVersion)
  52. }
  53. func ContentTypeHandler(r *Request) {
  54. if len(r.HTTPRequest.Header["Content-Type"]) == 0 {
  55. r.HTTPRequest.Header.Set("Content-Type", "application/xml")
  56. }
  57. }
  58. var reStatusCode = regexp.MustCompile(`^(\d+)`)
  59. // SendHandler is a request handler to send service request using HTTP client.
  60. func SendHandler(r *Request) {
  61. var err error
  62. if r.HTTPRequest.ContentLength <= 0 {
  63. r.HTTPRequest.Body = http.NoBody
  64. }
  65. r.HTTPResponse, err = r.Service.Config.HTTPClient.Do(r.HTTPRequest)
  66. if err != nil {
  67. // Capture the case where url.Error is returned for error processing
  68. // response. e.g. 301 without location header comes back as string
  69. // error and r.HTTPResponse is nil. Other url redirect errors will
  70. // comeback in a similar method.
  71. if e, ok := err.(*url.Error); ok {
  72. if s := reStatusCode.FindStringSubmatch(e.Error()); s != nil {
  73. code, _ := strconv.ParseInt(s[1], 10, 64)
  74. r.HTTPResponse = &http.Response{
  75. StatusCode: int(code),
  76. Status: http.StatusText(int(code)),
  77. Body: ioutil.NopCloser(bytes.NewReader([]byte{})),
  78. }
  79. return
  80. }
  81. }
  82. // Catch all other request errors.
  83. r.Error = apierr.New("RequestError", "send request failed", err)
  84. r.Retryable.Set(true) // network errors are retryable
  85. }
  86. }
  87. // ValidateResponseHandler is a request handler to validate service response.
  88. func ValidateResponseHandler(r *Request) {
  89. if r.HTTPResponse.StatusCode == 0 || r.HTTPResponse.StatusCode >= 300 {
  90. // this may be replaced by an UnmarshalError handler
  91. r.Error = apierr.New("UnknownError", "unknown error", nil)
  92. }
  93. }
  94. // AfterRetryHandler performs final checks to determine if the request should
  95. // be retried and how long to delay.
  96. func AfterRetryHandler(r *Request) {
  97. // If one of the other handlers already set the retry state
  98. // we don't want to override it based on the service's state
  99. if !r.Retryable.IsSet() {
  100. r.Retryable.Set(r.Service.ShouldRetry(r.Error))
  101. }
  102. if r.WillRetry() {
  103. r.RetryCount++
  104. delay := r.Service.RetryRule.GetDelay(int(r.RetryCount))
  105. if delay < 0 {
  106. delay = 0
  107. }
  108. r.RetryDelay = delay
  109. r.Config.LogWarn("Tried %d times, will retry in %d ms.", r.RetryCount, r.RetryDelay.Milliseconds())
  110. sleepDelay(r.RetryDelay)
  111. // when the expired token exception occurs the credentials
  112. // need to be expired locally so that the next request to
  113. // get credentials will trigger a credentials refresh.
  114. if r.Error != nil {
  115. if err, ok := r.Error.(awserr.Error); ok {
  116. if retry.IsCodeExpiredCreds(err.Code()) {
  117. r.Config.Credentials.Expire()
  118. }
  119. }
  120. }
  121. r.Error = nil
  122. }
  123. }
  124. var (
  125. // ErrMissingRegion is an error that is returned if region configuration is
  126. // not found.
  127. ErrMissingRegion error = apierr.New("MissingRegion", "could not find region configuration", nil)
  128. // ErrMissingEndpoint is an error that is returned if an endpoint cannot be
  129. // resolved for a service.
  130. ErrMissingEndpoint error = apierr.New("MissingEndpoint", "'Endpoint' configuration is required for this service", nil)
  131. )
  132. // ValidateEndpointHandler is a request handler to validate a request had the
  133. // appropriate Region and Endpoint set. Will set r.Error if the endpoint or
  134. // region is not valid.
  135. func ValidateEndpointHandler(r *Request) {
  136. if r.Service.SigningRegion == "" && r.Service.Config.Region == "" {
  137. r.Error = ErrMissingRegion
  138. } else if r.Service.Endpoint == "" {
  139. r.Error = ErrMissingEndpoint
  140. }
  141. }