]> git.immae.eu Git - github/fretlink/terraform-provider-statuscake.git/blob - vendor/github.com/aws/aws-sdk-go/aws/request/request.go
Upgrade to 0.12
[github/fretlink/terraform-provider-statuscake.git] / vendor / github.com / aws / aws-sdk-go / aws / request / request.go
1 package request
2
3 import (
4 "bytes"
5 "fmt"
6 "io"
7 "net/http"
8 "net/url"
9 "reflect"
10 "strings"
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/client/metadata"
16 "github.com/aws/aws-sdk-go/internal/sdkio"
17 )
18
19 const (
20 // ErrCodeSerialization is the serialization error code that is received
21 // during protocol unmarshaling.
22 ErrCodeSerialization = "SerializationError"
23
24 // ErrCodeRead is an error that is returned during HTTP reads.
25 ErrCodeRead = "ReadError"
26
27 // ErrCodeResponseTimeout is the connection timeout error that is received
28 // during body reads.
29 ErrCodeResponseTimeout = "ResponseTimeout"
30
31 // ErrCodeInvalidPresignExpire is returned when the expire time provided to
32 // presign is invalid
33 ErrCodeInvalidPresignExpire = "InvalidPresignExpireError"
34
35 // CanceledErrorCode is the error code that will be returned by an
36 // API request that was canceled. Requests given a aws.Context may
37 // return this error when canceled.
38 CanceledErrorCode = "RequestCanceled"
39 )
40
41 // A Request is the service request to be made.
42 type Request struct {
43 Config aws.Config
44 ClientInfo metadata.ClientInfo
45 Handlers Handlers
46
47 Retryer
48 AttemptTime time.Time
49 Time time.Time
50 Operation *Operation
51 HTTPRequest *http.Request
52 HTTPResponse *http.Response
53 Body io.ReadSeeker
54 BodyStart int64 // offset from beginning of Body that the request body starts
55 Params interface{}
56 Error error
57 Data interface{}
58 RequestID string
59 RetryCount int
60 Retryable *bool
61 RetryDelay time.Duration
62 NotHoist bool
63 SignedHeaderVals http.Header
64 LastSignedAt time.Time
65 DisableFollowRedirects bool
66
67 // A value greater than 0 instructs the request to be signed as Presigned URL
68 // You should not set this field directly. Instead use Request's
69 // Presign or PresignRequest methods.
70 ExpireTime time.Duration
71
72 context aws.Context
73
74 built bool
75
76 // Need to persist an intermediate body between the input Body and HTTP
77 // request body because the HTTP Client's transport can maintain a reference
78 // to the HTTP request's body after the client has returned. This value is
79 // safe to use concurrently and wrap the input Body for each HTTP request.
80 safeBody *offsetReader
81 }
82
83 // An Operation is the service API operation to be made.
84 type Operation struct {
85 Name string
86 HTTPMethod string
87 HTTPPath string
88 *Paginator
89
90 BeforePresignFn func(r *Request) error
91 }
92
93 // New returns a new Request pointer for the service API
94 // operation and parameters.
95 //
96 // Params is any value of input parameters to be the request payload.
97 // Data is pointer value to an object which the request's response
98 // payload will be deserialized to.
99 func New(cfg aws.Config, clientInfo metadata.ClientInfo, handlers Handlers,
100 retryer Retryer, operation *Operation, params interface{}, data interface{}) *Request {
101
102 method := operation.HTTPMethod
103 if method == "" {
104 method = "POST"
105 }
106
107 httpReq, _ := http.NewRequest(method, "", nil)
108
109 var err error
110 httpReq.URL, err = url.Parse(clientInfo.Endpoint + operation.HTTPPath)
111 if err != nil {
112 httpReq.URL = &url.URL{}
113 err = awserr.New("InvalidEndpointURL", "invalid endpoint uri", err)
114 }
115
116 SanitizeHostForHeader(httpReq)
117
118 r := &Request{
119 Config: cfg,
120 ClientInfo: clientInfo,
121 Handlers: handlers.Copy(),
122
123 Retryer: retryer,
124 Time: time.Now(),
125 ExpireTime: 0,
126 Operation: operation,
127 HTTPRequest: httpReq,
128 Body: nil,
129 Params: params,
130 Error: err,
131 Data: data,
132 }
133 r.SetBufferBody([]byte{})
134
135 return r
136 }
137
138 // A Option is a functional option that can augment or modify a request when
139 // using a WithContext API operation method.
140 type Option func(*Request)
141
142 // WithGetResponseHeader builds a request Option which will retrieve a single
143 // header value from the HTTP Response. If there are multiple values for the
144 // header key use WithGetResponseHeaders instead to access the http.Header
145 // map directly. The passed in val pointer must be non-nil.
146 //
147 // This Option can be used multiple times with a single API operation.
148 //
149 // var id2, versionID string
150 // svc.PutObjectWithContext(ctx, params,
151 // request.WithGetResponseHeader("x-amz-id-2", &id2),
152 // request.WithGetResponseHeader("x-amz-version-id", &versionID),
153 // )
154 func WithGetResponseHeader(key string, val *string) Option {
155 return func(r *Request) {
156 r.Handlers.Complete.PushBack(func(req *Request) {
157 *val = req.HTTPResponse.Header.Get(key)
158 })
159 }
160 }
161
162 // WithGetResponseHeaders builds a request Option which will retrieve the
163 // headers from the HTTP response and assign them to the passed in headers
164 // variable. The passed in headers pointer must be non-nil.
165 //
166 // var headers http.Header
167 // svc.PutObjectWithContext(ctx, params, request.WithGetResponseHeaders(&headers))
168 func WithGetResponseHeaders(headers *http.Header) Option {
169 return func(r *Request) {
170 r.Handlers.Complete.PushBack(func(req *Request) {
171 *headers = req.HTTPResponse.Header
172 })
173 }
174 }
175
176 // WithLogLevel is a request option that will set the request to use a specific
177 // log level when the request is made.
178 //
179 // svc.PutObjectWithContext(ctx, params, request.WithLogLevel(aws.LogDebugWithHTTPBody)
180 func WithLogLevel(l aws.LogLevelType) Option {
181 return func(r *Request) {
182 r.Config.LogLevel = aws.LogLevel(l)
183 }
184 }
185
186 // ApplyOptions will apply each option to the request calling them in the order
187 // the were provided.
188 func (r *Request) ApplyOptions(opts ...Option) {
189 for _, opt := range opts {
190 opt(r)
191 }
192 }
193
194 // Context will always returns a non-nil context. If Request does not have a
195 // context aws.BackgroundContext will be returned.
196 func (r *Request) Context() aws.Context {
197 if r.context != nil {
198 return r.context
199 }
200 return aws.BackgroundContext()
201 }
202
203 // SetContext adds a Context to the current request that can be used to cancel
204 // a in-flight request. The Context value must not be nil, or this method will
205 // panic.
206 //
207 // Unlike http.Request.WithContext, SetContext does not return a copy of the
208 // Request. It is not safe to use use a single Request value for multiple
209 // requests. A new Request should be created for each API operation request.
210 //
211 // Go 1.6 and below:
212 // The http.Request's Cancel field will be set to the Done() value of
213 // the context. This will overwrite the Cancel field's value.
214 //
215 // Go 1.7 and above:
216 // The http.Request.WithContext will be used to set the context on the underlying
217 // http.Request. This will create a shallow copy of the http.Request. The SDK
218 // may create sub contexts in the future for nested requests such as retries.
219 func (r *Request) SetContext(ctx aws.Context) {
220 if ctx == nil {
221 panic("context cannot be nil")
222 }
223 setRequestContext(r, ctx)
224 }
225
226 // WillRetry returns if the request's can be retried.
227 func (r *Request) WillRetry() bool {
228 if !aws.IsReaderSeekable(r.Body) && r.HTTPRequest.Body != NoBody {
229 return false
230 }
231 return r.Error != nil && aws.BoolValue(r.Retryable) && r.RetryCount < r.MaxRetries()
232 }
233
234 // ParamsFilled returns if the request's parameters have been populated
235 // and the parameters are valid. False is returned if no parameters are
236 // provided or invalid.
237 func (r *Request) ParamsFilled() bool {
238 return r.Params != nil && reflect.ValueOf(r.Params).Elem().IsValid()
239 }
240
241 // DataFilled returns true if the request's data for response deserialization
242 // target has been set and is a valid. False is returned if data is not
243 // set, or is invalid.
244 func (r *Request) DataFilled() bool {
245 return r.Data != nil && reflect.ValueOf(r.Data).Elem().IsValid()
246 }
247
248 // SetBufferBody will set the request's body bytes that will be sent to
249 // the service API.
250 func (r *Request) SetBufferBody(buf []byte) {
251 r.SetReaderBody(bytes.NewReader(buf))
252 }
253
254 // SetStringBody sets the body of the request to be backed by a string.
255 func (r *Request) SetStringBody(s string) {
256 r.SetReaderBody(strings.NewReader(s))
257 }
258
259 // SetReaderBody will set the request's body reader.
260 func (r *Request) SetReaderBody(reader io.ReadSeeker) {
261 r.Body = reader
262 r.BodyStart, _ = reader.Seek(0, sdkio.SeekCurrent) // Get the Bodies current offset.
263 r.ResetBody()
264 }
265
266 // Presign returns the request's signed URL. Error will be returned
267 // if the signing fails. The expire parameter is only used for presigned Amazon
268 // S3 API requests. All other AWS services will use a fixed expiration
269 // time of 15 minutes.
270 //
271 // It is invalid to create a presigned URL with a expire duration 0 or less. An
272 // error is returned if expire duration is 0 or less.
273 func (r *Request) Presign(expire time.Duration) (string, error) {
274 r = r.copy()
275
276 // Presign requires all headers be hoisted. There is no way to retrieve
277 // the signed headers not hoisted without this. Making the presigned URL
278 // useless.
279 r.NotHoist = false
280
281 u, _, err := getPresignedURL(r, expire)
282 return u, err
283 }
284
285 // PresignRequest behaves just like presign, with the addition of returning a
286 // set of headers that were signed. The expire parameter is only used for
287 // presigned Amazon S3 API requests. All other AWS services will use a fixed
288 // expiration time of 15 minutes.
289 //
290 // It is invalid to create a presigned URL with a expire duration 0 or less. An
291 // error is returned if expire duration is 0 or less.
292 //
293 // Returns the URL string for the API operation with signature in the query string,
294 // and the HTTP headers that were included in the signature. These headers must
295 // be included in any HTTP request made with the presigned URL.
296 //
297 // To prevent hoisting any headers to the query string set NotHoist to true on
298 // this Request value prior to calling PresignRequest.
299 func (r *Request) PresignRequest(expire time.Duration) (string, http.Header, error) {
300 r = r.copy()
301 return getPresignedURL(r, expire)
302 }
303
304 // IsPresigned returns true if the request represents a presigned API url.
305 func (r *Request) IsPresigned() bool {
306 return r.ExpireTime != 0
307 }
308
309 func getPresignedURL(r *Request, expire time.Duration) (string, http.Header, error) {
310 if expire <= 0 {
311 return "", nil, awserr.New(
312 ErrCodeInvalidPresignExpire,
313 "presigned URL requires an expire duration greater than 0",
314 nil,
315 )
316 }
317
318 r.ExpireTime = expire
319
320 if r.Operation.BeforePresignFn != nil {
321 if err := r.Operation.BeforePresignFn(r); err != nil {
322 return "", nil, err
323 }
324 }
325
326 if err := r.Sign(); err != nil {
327 return "", nil, err
328 }
329
330 return r.HTTPRequest.URL.String(), r.SignedHeaderVals, nil
331 }
332
333 func debugLogReqError(r *Request, stage string, retrying bool, err error) {
334 if !r.Config.LogLevel.Matches(aws.LogDebugWithRequestErrors) {
335 return
336 }
337
338 retryStr := "not retrying"
339 if retrying {
340 retryStr = "will retry"
341 }
342
343 r.Config.Logger.Log(fmt.Sprintf("DEBUG: %s %s/%s failed, %s, error %v",
344 stage, r.ClientInfo.ServiceName, r.Operation.Name, retryStr, err))
345 }
346
347 // Build will build the request's object so it can be signed and sent
348 // to the service. Build will also validate all the request's parameters.
349 // Any additional build Handlers set on this request will be run
350 // in the order they were set.
351 //
352 // The request will only be built once. Multiple calls to build will have
353 // no effect.
354 //
355 // If any Validate or Build errors occur the build will stop and the error
356 // which occurred will be returned.
357 func (r *Request) Build() error {
358 if !r.built {
359 r.Handlers.Validate.Run(r)
360 if r.Error != nil {
361 debugLogReqError(r, "Validate Request", false, r.Error)
362 return r.Error
363 }
364 r.Handlers.Build.Run(r)
365 if r.Error != nil {
366 debugLogReqError(r, "Build Request", false, r.Error)
367 return r.Error
368 }
369 r.built = true
370 }
371
372 return r.Error
373 }
374
375 // Sign will sign the request, returning error if errors are encountered.
376 //
377 // Sign will build the request prior to signing. All Sign Handlers will
378 // be executed in the order they were set.
379 func (r *Request) Sign() error {
380 r.Build()
381 if r.Error != nil {
382 debugLogReqError(r, "Build Request", false, r.Error)
383 return r.Error
384 }
385
386 r.Handlers.Sign.Run(r)
387 return r.Error
388 }
389
390 func (r *Request) getNextRequestBody() (io.ReadCloser, error) {
391 if r.safeBody != nil {
392 r.safeBody.Close()
393 }
394
395 r.safeBody = newOffsetReader(r.Body, r.BodyStart)
396
397 // Go 1.8 tightened and clarified the rules code needs to use when building
398 // requests with the http package. Go 1.8 removed the automatic detection
399 // of if the Request.Body was empty, or actually had bytes in it. The SDK
400 // always sets the Request.Body even if it is empty and should not actually
401 // be sent. This is incorrect.
402 //
403 // Go 1.8 did add a http.NoBody value that the SDK can use to tell the http
404 // client that the request really should be sent without a body. The
405 // Request.Body cannot be set to nil, which is preferable, because the
406 // field is exported and could introduce nil pointer dereferences for users
407 // of the SDK if they used that field.
408 //
409 // Related golang/go#18257
410 l, err := aws.SeekerLen(r.Body)
411 if err != nil {
412 return nil, awserr.New(ErrCodeSerialization, "failed to compute request body size", err)
413 }
414
415 var body io.ReadCloser
416 if l == 0 {
417 body = NoBody
418 } else if l > 0 {
419 body = r.safeBody
420 } else {
421 // Hack to prevent sending bodies for methods where the body
422 // should be ignored by the server. Sending bodies on these
423 // methods without an associated ContentLength will cause the
424 // request to socket timeout because the server does not handle
425 // Transfer-Encoding: chunked bodies for these methods.
426 //
427 // This would only happen if a aws.ReaderSeekerCloser was used with
428 // a io.Reader that was not also an io.Seeker, or did not implement
429 // Len() method.
430 switch r.Operation.HTTPMethod {
431 case "GET", "HEAD", "DELETE":
432 body = NoBody
433 default:
434 body = r.safeBody
435 }
436 }
437
438 return body, nil
439 }
440
441 // GetBody will return an io.ReadSeeker of the Request's underlying
442 // input body with a concurrency safe wrapper.
443 func (r *Request) GetBody() io.ReadSeeker {
444 return r.safeBody
445 }
446
447 // Send will send the request, returning error if errors are encountered.
448 //
449 // Send will sign the request prior to sending. All Send Handlers will
450 // be executed in the order they were set.
451 //
452 // Canceling a request is non-deterministic. If a request has been canceled,
453 // then the transport will choose, randomly, one of the state channels during
454 // reads or getting the connection.
455 //
456 // readLoop() and getConn(req *Request, cm connectMethod)
457 // https://github.com/golang/go/blob/master/src/net/http/transport.go
458 //
459 // Send will not close the request.Request's body.
460 func (r *Request) Send() error {
461 defer func() {
462 // Regardless of success or failure of the request trigger the Complete
463 // request handlers.
464 r.Handlers.Complete.Run(r)
465 }()
466
467 if err := r.Error; err != nil {
468 return err
469 }
470
471 for {
472 r.Error = nil
473 r.AttemptTime = time.Now()
474
475 if err := r.Sign(); err != nil {
476 debugLogReqError(r, "Sign Request", false, err)
477 return err
478 }
479
480 if err := r.sendRequest(); err == nil {
481 return nil
482 } else if !shouldRetryCancel(r.Error) {
483 return err
484 } else {
485 r.Handlers.Retry.Run(r)
486 r.Handlers.AfterRetry.Run(r)
487
488 if r.Error != nil || !aws.BoolValue(r.Retryable) {
489 return r.Error
490 }
491
492 r.prepareRetry()
493 continue
494 }
495 }
496 }
497
498 func (r *Request) prepareRetry() {
499 if r.Config.LogLevel.Matches(aws.LogDebugWithRequestRetries) {
500 r.Config.Logger.Log(fmt.Sprintf("DEBUG: Retrying Request %s/%s, attempt %d",
501 r.ClientInfo.ServiceName, r.Operation.Name, r.RetryCount))
502 }
503
504 // The previous http.Request will have a reference to the r.Body
505 // and the HTTP Client's Transport may still be reading from
506 // the request's body even though the Client's Do returned.
507 r.HTTPRequest = copyHTTPRequest(r.HTTPRequest, nil)
508 r.ResetBody()
509
510 // Closing response body to ensure that no response body is leaked
511 // between retry attempts.
512 if r.HTTPResponse != nil && r.HTTPResponse.Body != nil {
513 r.HTTPResponse.Body.Close()
514 }
515 }
516
517 func (r *Request) sendRequest() (sendErr error) {
518 defer r.Handlers.CompleteAttempt.Run(r)
519
520 r.Retryable = nil
521 r.Handlers.Send.Run(r)
522 if r.Error != nil {
523 debugLogReqError(r, "Send Request", r.WillRetry(), r.Error)
524 return r.Error
525 }
526
527 r.Handlers.UnmarshalMeta.Run(r)
528 r.Handlers.ValidateResponse.Run(r)
529 if r.Error != nil {
530 r.Handlers.UnmarshalError.Run(r)
531 debugLogReqError(r, "Validate Response", r.WillRetry(), r.Error)
532 return r.Error
533 }
534
535 r.Handlers.Unmarshal.Run(r)
536 if r.Error != nil {
537 debugLogReqError(r, "Unmarshal Response", r.WillRetry(), r.Error)
538 return r.Error
539 }
540
541 return nil
542 }
543
544 // copy will copy a request which will allow for local manipulation of the
545 // request.
546 func (r *Request) copy() *Request {
547 req := &Request{}
548 *req = *r
549 req.Handlers = r.Handlers.Copy()
550 op := *r.Operation
551 req.Operation = &op
552 return req
553 }
554
555 // AddToUserAgent adds the string to the end of the request's current user agent.
556 func AddToUserAgent(r *Request, s string) {
557 curUA := r.HTTPRequest.Header.Get("User-Agent")
558 if len(curUA) > 0 {
559 s = curUA + " " + s
560 }
561 r.HTTPRequest.Header.Set("User-Agent", s)
562 }
563
564 type temporary interface {
565 Temporary() bool
566 }
567
568 func shouldRetryCancel(err error) bool {
569 switch err := err.(type) {
570 case awserr.Error:
571 if err.Code() == CanceledErrorCode {
572 return false
573 }
574 return shouldRetryCancel(err.OrigErr())
575 case *url.Error:
576 if strings.Contains(err.Error(), "connection refused") {
577 // Refused connections should be retried as the service may not yet
578 // be running on the port. Go TCP dial considers refused
579 // connections as not temporary.
580 return true
581 }
582 // *url.Error only implements Temporary after golang 1.6 but since
583 // url.Error only wraps the error:
584 return shouldRetryCancel(err.Err)
585 case temporary:
586 // If the error is temporary, we want to allow continuation of the
587 // retry process
588 return err.Temporary()
589 case nil:
590 // `awserr.Error.OrigErr()` can be nil, meaning there was an error but
591 // because we don't know the cause, it is marked as retriable. See
592 // TestRequest4xxUnretryable for an example.
593 return true
594 default:
595 switch err.Error() {
596 case "net/http: request canceled",
597 "net/http: request canceled while waiting for connection":
598 // known 1.5 error case when an http request is cancelled
599 return false
600 }
601 // here we don't know the error; so we allow a retry.
602 return true
603 }
604 }
605
606 // SanitizeHostForHeader removes default port from host and updates request.Host
607 func SanitizeHostForHeader(r *http.Request) {
608 host := getHost(r)
609 port := portOnly(host)
610 if port != "" && isDefaultPort(r.URL.Scheme, port) {
611 r.Host = stripPort(host)
612 }
613 }
614
615 // Returns host from request
616 func getHost(r *http.Request) string {
617 if r.Host != "" {
618 return r.Host
619 }
620
621 return r.URL.Host
622 }
623
624 // Hostname returns u.Host, without any port number.
625 //
626 // If Host is an IPv6 literal with a port number, Hostname returns the
627 // IPv6 literal without the square brackets. IPv6 literals may include
628 // a zone identifier.
629 //
630 // Copied from the Go 1.8 standard library (net/url)
631 func stripPort(hostport string) string {
632 colon := strings.IndexByte(hostport, ':')
633 if colon == -1 {
634 return hostport
635 }
636 if i := strings.IndexByte(hostport, ']'); i != -1 {
637 return strings.TrimPrefix(hostport[:i], "[")
638 }
639 return hostport[:colon]
640 }
641
642 // Port returns the port part of u.Host, without the leading colon.
643 // If u.Host doesn't contain a port, Port returns an empty string.
644 //
645 // Copied from the Go 1.8 standard library (net/url)
646 func portOnly(hostport string) string {
647 colon := strings.IndexByte(hostport, ':')
648 if colon == -1 {
649 return ""
650 }
651 if i := strings.Index(hostport, "]:"); i != -1 {
652 return hostport[i+len("]:"):]
653 }
654 if strings.Contains(hostport, "]") {
655 return ""
656 }
657 return hostport[colon+len(":"):]
658 }
659
660 // Returns true if the specified URI is using the standard port
661 // (i.e. port 80 for HTTP URIs or 443 for HTTPS URIs)
662 func isDefaultPort(scheme, port string) bool {
663 if port == "" {
664 return true
665 }
666
667 lowerCaseScheme := strings.ToLower(scheme)
668 if (lowerCaseScheme == "http" && port == "80") || (lowerCaseScheme == "https" && port == "443") {
669 return true
670 }
671
672 return false
673 }