]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blame - vendor/github.com/aws/aws-sdk-go/aws/corehandlers/handlers.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / aws / aws-sdk-go / aws / corehandlers / handlers.go
CommitLineData
bae9f6d2
JC
1package corehandlers
2
3import (
4 "bytes"
5 "fmt"
bae9f6d2
JC
6 "io/ioutil"
7 "net/http"
8 "net/url"
9 "regexp"
bae9f6d2
JC
10 "strconv"
11 "time"
12
13 "github.com/aws/aws-sdk-go/aws"
14 "github.com/aws/aws-sdk-go/aws/awserr"
15 "github.com/aws/aws-sdk-go/aws/credentials"
16 "github.com/aws/aws-sdk-go/aws/request"
17)
18
19// Interface for matching types which also have a Len method.
20type lener interface {
21 Len() int
22}
23
24// BuildContentLengthHandler builds the content length of a request based on the body,
25// or will use the HTTPRequest.Header's "Content-Length" if defined. If unable
26// to determine request body length and no "Content-Length" was specified it will panic.
27//
28// The Content-Length will only be added to the request if the length of the body
29// is greater than 0. If the body is empty or the current `Content-Length`
30// header is <= 0, the header will also be stripped.
31var BuildContentLengthHandler = request.NamedHandler{Name: "core.BuildContentLengthHandler", Fn: func(r *request.Request) {
32 var length int64
33
34 if slength := r.HTTPRequest.Header.Get("Content-Length"); slength != "" {
35 length, _ = strconv.ParseInt(slength, 10, 64)
36 } else {
15c0b25d
AP
37 if r.Body != nil {
38 var err error
39 length, err = aws.SeekerLen(r.Body)
40 if err != nil {
41 r.Error = awserr.New(request.ErrCodeSerialization, "failed to get request body's length", err)
42 return
43 }
bae9f6d2
JC
44 }
45 }
46
47 if length > 0 {
48 r.HTTPRequest.ContentLength = length
49 r.HTTPRequest.Header.Set("Content-Length", fmt.Sprintf("%d", length))
50 } else {
51 r.HTTPRequest.ContentLength = 0
52 r.HTTPRequest.Header.Del("Content-Length")
53 }
54}}
55
bae9f6d2
JC
56var reStatusCode = regexp.MustCompile(`^(\d{3})`)
57
58// ValidateReqSigHandler is a request handler to ensure that the request's
59// signature doesn't expire before it is sent. This can happen when a request
60// is built and signed significantly before it is sent. Or significant delays
61// occur when retrying requests that would cause the signature to expire.
62var ValidateReqSigHandler = request.NamedHandler{
63 Name: "core.ValidateReqSigHandler",
64 Fn: func(r *request.Request) {
65 // Unsigned requests are not signed
66 if r.Config.Credentials == credentials.AnonymousCredentials {
67 return
68 }
69
70 signedTime := r.Time
71 if !r.LastSignedAt.IsZero() {
72 signedTime = r.LastSignedAt
73 }
74
107c1cdb 75 // 5 minutes to allow for some clock skew/delays in transmission.
bae9f6d2 76 // Would be improved with aws/aws-sdk-go#423
107c1cdb 77 if signedTime.Add(5 * time.Minute).After(time.Now()) {
bae9f6d2
JC
78 return
79 }
80
81 fmt.Println("request expired, resigning")
82 r.Sign()
83 },
84}
85
86// SendHandler is a request handler to send service request using HTTP client.
87var SendHandler = request.NamedHandler{
88 Name: "core.SendHandler",
89 Fn: func(r *request.Request) {
90 sender := sendFollowRedirects
91 if r.DisableFollowRedirects {
92 sender = sendWithoutFollowRedirects
93 }
94
9b12e4fe
JC
95 if request.NoBody == r.HTTPRequest.Body {
96 // Strip off the request body if the NoBody reader was used as a
97 // place holder for a request body. This prevents the SDK from
98 // making requests with a request body when it would be invalid
99 // to do so.
100 //
101 // Use a shallow copy of the http.Request to ensure the race condition
102 // of transport on Body will not trigger
103 reqOrig, reqCopy := r.HTTPRequest, *r.HTTPRequest
104 reqCopy.Body = nil
105 r.HTTPRequest = &reqCopy
106 defer func() {
107 r.HTTPRequest = reqOrig
108 }()
109 }
110
bae9f6d2
JC
111 var err error
112 r.HTTPResponse, err = sender(r)
113 if err != nil {
114 handleSendError(r, err)
115 }
116 },
117}
118
119func sendFollowRedirects(r *request.Request) (*http.Response, error) {
120 return r.Config.HTTPClient.Do(r.HTTPRequest)
121}
122
123func sendWithoutFollowRedirects(r *request.Request) (*http.Response, error) {
124 transport := r.Config.HTTPClient.Transport
125 if transport == nil {
126 transport = http.DefaultTransport
127 }
128
129 return transport.RoundTrip(r.HTTPRequest)
130}
131
132func handleSendError(r *request.Request, err error) {
133 // Prevent leaking if an HTTPResponse was returned. Clean up
134 // the body.
135 if r.HTTPResponse != nil {
136 r.HTTPResponse.Body.Close()
137 }
138 // Capture the case where url.Error is returned for error processing
139 // response. e.g. 301 without location header comes back as string
140 // error and r.HTTPResponse is nil. Other URL redirect errors will
141 // comeback in a similar method.
142 if e, ok := err.(*url.Error); ok && e.Err != nil {
143 if s := reStatusCode.FindStringSubmatch(e.Err.Error()); s != nil {
144 code, _ := strconv.ParseInt(s[1], 10, 64)
145 r.HTTPResponse = &http.Response{
146 StatusCode: int(code),
147 Status: http.StatusText(int(code)),
148 Body: ioutil.NopCloser(bytes.NewReader([]byte{})),
149 }
150 return
151 }
152 }
153 if r.HTTPResponse == nil {
154 // Add a dummy request response object to ensure the HTTPResponse
155 // value is consistent.
156 r.HTTPResponse = &http.Response{
157 StatusCode: int(0),
158 Status: http.StatusText(int(0)),
159 Body: ioutil.NopCloser(bytes.NewReader([]byte{})),
160 }
161 }
162 // Catch all other request errors.
163 r.Error = awserr.New("RequestError", "send request failed", err)
164 r.Retryable = aws.Bool(true) // network errors are retryable
165
166 // Override the error with a context canceled error, if that was canceled.
167 ctx := r.Context()
168 select {
169 case <-ctx.Done():
170 r.Error = awserr.New(request.CanceledErrorCode,
171 "request context canceled", ctx.Err())
172 r.Retryable = aws.Bool(false)
173 default:
174 }
175}
176
177// ValidateResponseHandler is a request handler to validate service response.
178var ValidateResponseHandler = request.NamedHandler{Name: "core.ValidateResponseHandler", Fn: func(r *request.Request) {
179 if r.HTTPResponse.StatusCode == 0 || r.HTTPResponse.StatusCode >= 300 {
180 // this may be replaced by an UnmarshalError handler
181 r.Error = awserr.New("UnknownError", "unknown error", nil)
182 }
183}}
184
185// AfterRetryHandler performs final checks to determine if the request should
186// be retried and how long to delay.
187var AfterRetryHandler = request.NamedHandler{Name: "core.AfterRetryHandler", Fn: func(r *request.Request) {
188 // If one of the other handlers already set the retry state
189 // we don't want to override it based on the service's state
190 if r.Retryable == nil || aws.BoolValue(r.Config.EnforceShouldRetryCheck) {
191 r.Retryable = aws.Bool(r.ShouldRetry(r))
192 }
193
194 if r.WillRetry() {
195 r.RetryDelay = r.RetryRules(r)
196
197 if sleepFn := r.Config.SleepDelay; sleepFn != nil {
198 // Support SleepDelay for backwards compatibility and testing
199 sleepFn(r.RetryDelay)
200 } else if err := aws.SleepWithContext(r.Context(), r.RetryDelay); err != nil {
201 r.Error = awserr.New(request.CanceledErrorCode,
202 "request context canceled", err)
203 r.Retryable = aws.Bool(false)
204 return
205 }
206
207 // when the expired token exception occurs the credentials
208 // need to be expired locally so that the next request to
209 // get credentials will trigger a credentials refresh.
210 if r.IsErrorExpired() {
211 r.Config.Credentials.Expire()
212 }
213
214 r.RetryCount++
215 r.Error = nil
216 }
217}}
218
219// ValidateEndpointHandler is a request handler to validate a request had the
220// appropriate Region and Endpoint set. Will set r.Error if the endpoint or
221// region is not valid.
222var ValidateEndpointHandler = request.NamedHandler{Name: "core.ValidateEndpointHandler", Fn: func(r *request.Request) {
223 if r.ClientInfo.SigningRegion == "" && aws.StringValue(r.Config.Region) == "" {
224 r.Error = aws.ErrMissingRegion
225 } else if r.ClientInfo.Endpoint == "" {
226 r.Error = aws.ErrMissingEndpoint
227 }
228}}