AWS SDK for C++

AWS SDK for C++ Version 1.11.925

Loading...
Searching...
No Matches
ClockSkew.h
1
6#pragma once
7
8#include <aws/core/Core_EXPORTS.h>
9#include <aws/core/http/HttpRequest.h>
10#include <aws/core/utils/Chrono.h>
11#include <aws/core/utils/DateTime.h>
12#include <aws/core/utils/StringUtils.h>
13#include <aws/core/client/AWSError.h>
14#include <aws/core/client/CoreErrors.h>
15#include <aws/crt/Optional.h>
16#include <atomic>
17#include <chrono>
18
19namespace Aws
20{
21 namespace Internal
22 {
23 // AttemptSkew is owned by the client, not here.
24
25 static const std::chrono::milliseconds CLOCK_SKEW_DETECTION_THRESHOLD = std::chrono::minutes(4);
26 static const std::chrono::milliseconds CLOCK_SKEW_MAX_TRUSTED_REQUEST_DURATION = std::chrono::minutes(15);
27 static const char CLOCK_SKEW_AGE_HEADER[] = "age";
28
30 {
31 Aws::Crt::Optional<Aws::Utils::DateTime> serverTime; // empty when the response carries no usable Date header
35
44 };
45
47 const Aws::Utils::DateTime& timeRequestSent,
48 const Aws::Utils::DateTime& timeResponseReceived)
49 {
50 const auto dateIt = headers.find(Aws::Utils::StringUtils::ToLower(Aws::Http::DATE_HEADER));
51 Aws::Crt::Optional<Aws::Utils::DateTime> serverTime;
52 if (dateIt != headers.end())
53 {
54 const Aws::Utils::DateTime parsed(dateIt->second.c_str(), Aws::Utils::DateFormat::AutoDetect);
55 if (parsed.WasParseSuccessful())
56 {
57 serverTime = parsed;
58 }
59 }
60 const bool servedFromCache = headers.find(CLOCK_SKEW_AGE_HEADER) != headers.end();
61 return ClockSkewMeasurement(serverTime, timeRequestSent, timeResponseReceived, servedFromCache);
62 }
63
64 inline Aws::Crt::Optional<std::chrono::milliseconds> ComputeClockSkewCandidate(const ClockSkewMeasurement& measurement)
65 {
66 if (!measurement.serverTime.has_value() || measurement.servedFromCache)
67 {
68 return Aws::Crt::Optional<std::chrono::milliseconds>{};
69 }
70
71 const int64_t sentMs = measurement.timeRequestSent.Millis();
72 const int64_t receivedMs = measurement.timeResponseReceived.Millis();
73 const int64_t serverMs = measurement.serverTime.value().Millis();
74
75 const std::chrono::milliseconds elapsed{receivedMs - sentMs};
77 {
78 return Aws::Crt::Optional<std::chrono::milliseconds>{};
79 }
80
81 // The server's Date is compared with the moment its response headers reached the client.
82 return Aws::Crt::Optional<std::chrono::milliseconds>(std::chrono::milliseconds{serverMs - receivedMs});
83 }
84
85 // Time the response headers arrived, if the HTTP client recorded it. Otherwise now (after the body),
86 // the legacy behaviour.
88 const auto& stamped = request.GetResponseHeadersReceivedTime();
89 return stamped.has_value() ? stamped.value() : Aws::Utils::DateTime::Now();
90 }
91
92 // Clock-skew error codes. Matched by CoreErrors enum where one exists; AuthFailure (EC2) has no
93 // CoreErrors enum, so it is matched by name -- the same enum+name shape as IsThrottlingResponse.
95 {
96 switch (error.GetErrorType())
97 {
102 return true;
103 default:
104 return error.GetExceptionName() == "AuthFailure";
105 }
106 }
107
109 {
110 bool skewExceedsThreshold = false; // caller ANDs this with the error-code check before retrying
111 std::chrono::milliseconds skew{0};
112 };
113
114 class AWS_CORE_LOCAL ClientSkew
115 {
116 public:
117 explicit ClientSkew(std::chrono::milliseconds initial) : m_skew(initial) {}
118
119 std::chrono::milliseconds Load() const { return m_skew.load(); }
120
121 // Runs on every response; a surviving candidate is stored, so a stale value self-heals.
122 Aws::Crt::Optional<std::chrono::milliseconds> RecordResponse(const ClockSkewMeasurement& measurement)
123 {
124 Aws::Crt::Optional<std::chrono::milliseconds> candidate = ComputeClockSkewCandidate(measurement);
125 if (candidate.has_value())
126 {
127 m_skew.store(candidate.value());
128 }
129 return candidate;
130 }
131
132 ClockSkewAdjustment EvaluateFailure(const ClockSkewMeasurement& measurement, std::chrono::milliseconds attemptSkew)
133 {
134 const Aws::Crt::Optional<std::chrono::milliseconds> candidate = RecordResponse(measurement);
135 ClockSkewAdjustment adjustment;
136 adjustment.skew = m_skew.load();
137 // Retryable when the applied skew is off from the observed skew by more than the threshold.
138 if (candidate.has_value())
139 {
140 const std::chrono::milliseconds absDelta = Aws::chrono::abs(attemptSkew - candidate.value());
142 }
143 return adjustment;
144 }
145
146 private:
147 std::atomic<std::chrono::milliseconds> m_skew;
148 };
149 } // namespace Internal
150} // namespace Aws
const Aws::String & GetExceptionName() const
Definition AWSError.h:141
const ERROR_TYPE GetErrorType() const
Definition AWSError.h:137
const Aws::Crt::Optional< Aws::Utils::DateTime > & GetResponseHeadersReceivedTime() const
std::chrono::milliseconds Load() const
Definition ClockSkew.h:119
Aws::Crt::Optional< std::chrono::milliseconds > RecordResponse(const ClockSkewMeasurement &measurement)
Definition ClockSkew.h:122
ClockSkewAdjustment EvaluateFailure(const ClockSkewMeasurement &measurement, std::chrono::milliseconds attemptSkew)
Definition ClockSkew.h:132
ClientSkew(std::chrono::milliseconds initial)
Definition ClockSkew.h:117
static DateTime Now()
int64_t Millis() const
bool WasParseSuccessful() const
Definition DateTime.h:131
static Aws::String ToLower(const char *source)
AWS_CORE_API const char DATE_HEADER[]
Aws::Map< Aws::String, Aws::String > HeaderValueCollection
Definition HttpTypes.h:56
ClockSkewMeasurement MakeClockSkewMeasurement(const Aws::Http::HeaderValueCollection &headers, const Aws::Utils::DateTime &timeRequestSent, const Aws::Utils::DateTime &timeResponseReceived)
Definition ClockSkew.h:46
Aws::Utils::DateTime GetTimeResponseReceived(const Aws::Http::HttpRequest &request)
Definition ClockSkew.h:87
static const char CLOCK_SKEW_AGE_HEADER[]
Definition ClockSkew.h:27
static const std::chrono::milliseconds CLOCK_SKEW_DETECTION_THRESHOLD
Definition ClockSkew.h:25
static const std::chrono::milliseconds CLOCK_SKEW_MAX_TRUSTED_REQUEST_DURATION
Definition ClockSkew.h:26
bool IsClockSkewError(const Aws::Client::AWSError< Aws::Client::CoreErrors > &error)
Definition ClockSkew.h:94
Aws::Crt::Optional< std::chrono::milliseconds > ComputeClockSkewCandidate(const ClockSkewMeasurement &measurement)
Definition ClockSkew.h:64
constexpr std::enable_if< std::numeric_limits< Rep >::is_signed, std::chrono::duration< Rep, Period > >::type abs(std::chrono::duration< Rep, Period > d)
Definition Chrono.h:33
std::chrono::milliseconds skew
Definition ClockSkew.h:111
Aws::Utils::DateTime timeResponseReceived
Definition ClockSkew.h:33
Aws::Utils::DateTime timeRequestSent
Definition ClockSkew.h:32
ClockSkewMeasurement(const Aws::Crt::Optional< Aws::Utils::DateTime > &serverTime, const Aws::Utils::DateTime &timeRequestSent, const Aws::Utils::DateTime &timeResponseReceived, bool servedFromCache)
Definition ClockSkew.h:36
Aws::Crt::Optional< Aws::Utils::DateTime > serverTime
Definition ClockSkew.h:31