github.com/aavshr/aws-sdk-go@v1.41.3/aws/corehandlers/handlers.go (about)

     1  package corehandlers
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"net/http"
     8  	"net/url"
     9  	"regexp"
    10  	"strconv"
    11  	"time"
    12  
    13  	"github.com/aavshr/aws-sdk-go/aws"
    14  	"github.com/aavshr/aws-sdk-go/aws/awserr"
    15  	"github.com/aavshr/aws-sdk-go/aws/credentials"
    16  	"github.com/aavshr/aws-sdk-go/aws/request"
    17  )
    18  
    19  // Interface for matching types which also have a Len method.
    20  type 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.
    31  var 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 {
    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  			}
    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  
    56  var 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.
    62  var 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  
    75  		// 5 minutes to allow for some clock skew/delays in transmission.
    76  		// Would be improved with aws/aws-sdk-go#423
    77  		if signedTime.Add(5 * time.Minute).After(time.Now()) {
    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.
    87  var 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  
    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  
   111  		var err error
   112  		r.HTTPResponse, err = sender(r)
   113  		if err != nil {
   114  			handleSendError(r, err)
   115  		}
   116  	},
   117  }
   118  
   119  func sendFollowRedirects(r *request.Request) (*http.Response, error) {
   120  	return r.Config.HTTPClient.Do(r.HTTPRequest)
   121  }
   122  
   123  func 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  
   132  func 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 request errors, and let the default retrier determine
   163  	// if the error is retryable.
   164  	r.Error = awserr.New(request.ErrCodeRequestError, "send request failed", err)
   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.
   178  var 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", r.Error)
   182  	}
   183  }}
   184  
   185  // AfterRetryHandler performs final checks to determine if the request should
   186  // be retried and how long to delay.
   187  var AfterRetryHandler = request.NamedHandler{
   188  	Name: "core.AfterRetryHandler",
   189  	Fn: func(r *request.Request) {
   190  		// If one of the other handlers already set the retry state
   191  		// we don't want to override it based on the service's state
   192  		if r.Retryable == nil || aws.BoolValue(r.Config.EnforceShouldRetryCheck) {
   193  			r.Retryable = aws.Bool(r.ShouldRetry(r))
   194  		}
   195  
   196  		if r.WillRetry() {
   197  			r.RetryDelay = r.RetryRules(r)
   198  
   199  			if sleepFn := r.Config.SleepDelay; sleepFn != nil {
   200  				// Support SleepDelay for backwards compatibility and testing
   201  				sleepFn(r.RetryDelay)
   202  			} else if err := aws.SleepWithContext(r.Context(), r.RetryDelay); err != nil {
   203  				r.Error = awserr.New(request.CanceledErrorCode,
   204  					"request context canceled", err)
   205  				r.Retryable = aws.Bool(false)
   206  				return
   207  			}
   208  
   209  			// when the expired token exception occurs the credentials
   210  			// need to be expired locally so that the next request to
   211  			// get credentials will trigger a credentials refresh.
   212  			if r.IsErrorExpired() {
   213  				r.Config.Credentials.Expire()
   214  			}
   215  
   216  			r.RetryCount++
   217  			r.Error = nil
   218  		}
   219  	}}
   220  
   221  // ValidateEndpointHandler is a request handler to validate a request had the
   222  // appropriate Region and Endpoint set. Will set r.Error if the endpoint or
   223  // region is not valid.
   224  var ValidateEndpointHandler = request.NamedHandler{Name: "core.ValidateEndpointHandler", Fn: func(r *request.Request) {
   225  	if r.ClientInfo.SigningRegion == "" && aws.StringValue(r.Config.Region) == "" {
   226  		r.Error = aws.ErrMissingRegion
   227  	} else if r.ClientInfo.Endpoint == "" {
   228  		// Was any endpoint provided by the user, or one was derived by the
   229  		// SDK's endpoint resolver?
   230  		r.Error = aws.ErrMissingEndpoint
   231  	}
   232  }}