diff --git a/aws/middleware/middleware.go b/aws/middleware/middleware.go index 6d5f0079c2f1..e5acedccd077 100644 --- a/aws/middleware/middleware.go +++ b/aws/middleware/middleware.go @@ -62,6 +62,14 @@ func (a RecordResponseTiming) HandleDeserialize(ctx context.Context, in middlewa switch resp := out.RawResponse.(type) { case *smithyhttp.Response: + // If the response has an Age header, it was served from a cache (e.g. + // CloudFront). In that case the Date header reflects when the original + // response was generated, not the current server time, so using it for + // clock-skew calculation would produce incorrect results. Skip skew + // computation entirely for cached responses. + if resp.Header.Get("Age") != "" { + break + } respDateHeader := resp.Header.Get("Date") if len(respDateHeader) == 0 { break diff --git a/aws/middleware/middleware_test.go b/aws/middleware/middleware_test.go index e4a69c9c22a1..1fb5d8ed8102 100644 --- a/aws/middleware/middleware_test.go +++ b/aws/middleware/middleware_test.go @@ -144,6 +144,26 @@ func TestAttemptClockSkewHandler(t *testing.T) { ExpectServerTime: time.Date(2020, 3, 5, 22, 25, 15, 0, time.UTC), ExpectAttemptSkew: -2 * time.Second, }, + "cached response with Age header skips skew": { + Next: func(ctx context.Context, in smithymiddleware.DeserializeInput, + ) (out smithymiddleware.DeserializeOutput, m smithymiddleware.Metadata, err error) { + out.RawResponse = &smithyhttp.Response{ + Response: &http.Response{ + StatusCode: 200, + Header: http.Header{ + "Date": []string{"Thu, 05 Mar 2020 12:00:00 GMT"}, + "Age": []string{"14400"}, + }, + }, + } + return out, m, err + }, + ResponseAt: func() time.Time { + return time.Date(2020, 3, 5, 22, 25, 17, 0, time.UTC) + }, + ExpectResponseAt: time.Date(2020, 3, 5, 22, 25, 17, 0, time.UTC), + // ServerTime and AttemptSkew should NOT be set for cached responses + }, } for name, c := range cases {