@@ -125,21 +125,91 @@ class RetryPolicyTest final : public RetryPolicy {
125125 }
126126};
127127
128- class RetryPolicyTest_CustomRetry_False final : public RetryPolicy {
128+ class RetryPolicyTest_CustomRetry_True final : public RetryPolicy {
129129public:
130- RetryPolicyTest_CustomRetry_False (RetryOptions const & retryOptions) : RetryPolicy(retryOptions) {}
130+ RetryPolicyTest_CustomRetry_True (RetryOptions const & retryOptions) : RetryPolicy(retryOptions) {}
131131
132132 std::unique_ptr<HttpPolicy> Clone () const override
133133 {
134- return std::make_unique<RetryPolicyTest_CustomRetry_False >(*this );
134+ return std::make_unique<RetryPolicyTest_CustomRetry_True >(*this );
135135 }
136136
137137protected:
138- bool ShouldRetry (std::unique_ptr<RawResponse> const & response, RetryOptions const & retryOptions)
139- const override
138+ bool ShouldRetry (std::unique_ptr<RawResponse> const &, RetryOptions const &) const override
140139 {
141- (void )response;
142- (void )retryOptions;
140+ return true ;
141+ }
142+ };
143+
144+ class RetryPolicyTest_CustomRetry_Throw final : public RetryPolicy {
145+ public:
146+ RetryPolicyTest_CustomRetry_Throw (RetryOptions const & retryOptions) : RetryPolicy(retryOptions) {}
147+
148+ std::unique_ptr<HttpPolicy> Clone () const override
149+ {
150+ return std::make_unique<RetryPolicyTest_CustomRetry_Throw>(*this );
151+ }
152+
153+ protected:
154+ bool ShouldRetry (std::unique_ptr<RawResponse> const &, RetryOptions const &) const override
155+ {
156+ throw TransportException (" Short-circuit evaluation means this should never be called." );
157+ }
158+ };
159+
160+ class RetryPolicyTest_CustomRetry_CopySource final : public RetryPolicy {
161+ public:
162+ RetryPolicyTest_CustomRetry_CopySource (RetryOptions const & retryOptions)
163+ : RetryPolicy(retryOptions)
164+ {
165+ }
166+
167+ std::unique_ptr<HttpPolicy> Clone () const override
168+ {
169+ return std::make_unique<RetryPolicyTest_CustomRetry_CopySource>(*this );
170+ }
171+
172+ protected:
173+ bool ShouldRetry (std::unique_ptr<RawResponse> const & response, RetryOptions const &) const override
174+ {
175+ if (response == nullptr )
176+ {
177+ return true ;
178+ }
179+
180+ if (static_cast <std::underlying_type_t <Azure::Core::Http::HttpStatusCode>>(
181+ response->GetStatusCode ())
182+ >= 400 )
183+ {
184+ const auto & headers = response->GetHeaders ();
185+ auto ite = headers.find (" x-ms-error-code" );
186+ if (ite != headers.end ())
187+ {
188+ const std::string error = ite->second ;
189+
190+ if (error == " InternalError" || error == " OperationTimedOut" || error == " ServerBusy" )
191+ {
192+ return true ;
193+ }
194+ }
195+ }
196+
197+ if (static_cast <std::underlying_type_t <Azure::Core::Http::HttpStatusCode>>(
198+ response->GetStatusCode ())
199+ >= 400 )
200+ {
201+ const auto & headers = response->GetHeaders ();
202+ auto ite = headers.find (" x-ms-copy-source-error-code" );
203+ if (ite != headers.end ())
204+ {
205+ const std::string error = ite->second ;
206+
207+ if (error == " InternalError" || error == " OperationTimedOut" || error == " ServerBusy" )
208+ {
209+ return true ;
210+ }
211+ }
212+ }
143213 return false ;
144214 }
145215};
@@ -150,10 +220,11 @@ TEST(RetryPolicy, ShouldRetry)
150220 using namespace std ::chrono_literals;
151221 RetryOptions const retryOptions{3 , 10ms, 60s, {HttpStatusCode::Ok}};
152222
153- // The default ShouldRetry implementation is to always return true,
154- // which means we will retry up to the retry count in the options.
223+ // The default ShouldRetry implementation is to always return false,
224+ // which means we will retry up to the retry count in the options, unless the status code isn't
225+ // one that is retriable.
155226 {
156- Azure::Core::Context context = Azure::Core::Context::ApplicationContext ;
227+ Azure::Core::Context context = Azure::Core::Context () ;
157228 auto policy = RetryPolicy (retryOptions);
158229
159230 RawResponse const * responsePtrSent = nullptr ;
@@ -163,7 +234,6 @@ TEST(RetryPolicy, ShouldRetry)
163234 policies.emplace_back (std::make_unique<RetryPolicy>(policy));
164235 policies.emplace_back (std::make_unique<TestTransportPolicy>([&]() {
165236 auto response = std::make_unique<RawResponse>(1 , 1 , HttpStatusCode::Ok, " Test" );
166-
167237 responsePtrSent = response.get ();
168238 retryCounter++;
169239 return response;
@@ -178,20 +248,46 @@ TEST(RetryPolicy, ShouldRetry)
178248 EXPECT_EQ (retryCounter, 3 );
179249 }
180250
181- // Overriding ShouldRetry to return false will mean we won't retry.
251+ // If the status code is retriable based on the options, ShouldRetry doesn't get called.
252+ // Testing short-circuit evaluation
182253 {
183254 Azure::Core::Context context = Azure::Core::Context ();
184- auto policy = RetryPolicyTest_CustomRetry_False (retryOptions);
255+ auto policy = RetryPolicyTest_CustomRetry_Throw (retryOptions);
185256
186257 RawResponse const * responsePtrSent = nullptr ;
187258 int32_t retryCounter = -1 ;
188259
189260 std::vector<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>> policies;
190- policies.emplace_back (std::make_unique<RetryPolicyTest_CustomRetry_False>(policy));
191-
261+ policies.emplace_back (std::make_unique<RetryPolicyTest_CustomRetry_Throw>(policy));
192262 policies.emplace_back (std::make_unique<TestTransportPolicy>([&]() {
193263 auto response = std::make_unique<RawResponse>(1 , 1 , HttpStatusCode::Ok, " Test" );
264+ responsePtrSent = response.get ();
265+ retryCounter++;
266+ return response;
267+ }));
268+
269+ Azure::Core::Http::_internal::HttpPipeline pipeline (policies);
270+
271+ Request request (HttpMethod::Get, Azure::Core::Url (" https://www.microsoft.com" ));
272+ pipeline.Send (request, context);
273+
274+ EXPECT_NE (responsePtrSent, nullptr );
275+ EXPECT_EQ (retryCounter, 3 );
276+ }
194277
278+ // If the status code isn't retriable based on the options, only then does ShouldRetry get called.
279+ // Since the default of ShouldRetry is false, we don't expect any retries.
280+ {
281+ Azure::Core::Context context = Azure::Core::Context ();
282+ auto policy = RetryPolicy (retryOptions);
283+
284+ RawResponse const * responsePtrSent = nullptr ;
285+ int32_t retryCounter = -1 ;
286+
287+ std::vector<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>> policies;
288+ policies.emplace_back (std::make_unique<RetryPolicy>(policy));
289+ policies.emplace_back (std::make_unique<TestTransportPolicy>([&]() {
290+ auto response = std::make_unique<RawResponse>(1 , 1 , HttpStatusCode::Accepted, " Test" );
195291 responsePtrSent = response.get ();
196292 retryCounter++;
197293 return response;
@@ -205,6 +301,60 @@ TEST(RetryPolicy, ShouldRetry)
205301 EXPECT_NE (responsePtrSent, nullptr );
206302 EXPECT_EQ (retryCounter, 0 );
207303 }
304+
305+ // Overriding ShouldRetry to return true will mean we will retry, when the status code isn't
306+ // retriable based on the options.
307+ {
308+ Azure::Core::Context context = Azure::Core::Context ();
309+ auto policy = RetryPolicyTest_CustomRetry_True (retryOptions);
310+
311+ RawResponse const * responsePtrSent = nullptr ;
312+ int32_t retryCounter = -1 ;
313+
314+ std::vector<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>> policies;
315+ policies.emplace_back (std::make_unique<RetryPolicyTest_CustomRetry_True>(policy));
316+ policies.emplace_back (std::make_unique<TestTransportPolicy>([&]() {
317+ auto response = std::make_unique<RawResponse>(1 , 1 , HttpStatusCode::Accepted, " Test" );
318+ responsePtrSent = response.get ();
319+ retryCounter++;
320+ return response;
321+ }));
322+
323+ Azure::Core::Http::_internal::HttpPipeline pipeline (policies);
324+
325+ Request request (HttpMethod::Get, Azure::Core::Url (" https://www.microsoft.com" ));
326+ pipeline.Send (request, context);
327+
328+ EXPECT_NE (responsePtrSent, nullptr );
329+ EXPECT_EQ (retryCounter, 3 );
330+ }
331+
332+ // Copy Status Code scenario (non-retriable HTTP status code)
333+ {
334+ Azure::Core::Context context = Azure::Core::Context ();
335+ auto policy = RetryPolicyTest_CustomRetry_CopySource (RetryOptions ());
336+
337+ RawResponse const * responsePtrSent = nullptr ;
338+ int32_t retryCounter = -1 ;
339+
340+ std::vector<std::unique_ptr<Azure::Core::Http::Policies::HttpPolicy>> policies;
341+ policies.emplace_back (std::make_unique<RetryPolicyTest_CustomRetry_CopySource>(policy));
342+ policies.emplace_back (std::make_unique<TestTransportPolicy>([&]() {
343+ auto response = std::make_unique<RawResponse>(1 , 1 , HttpStatusCode::Conflict, " Test" );
344+ response->SetHeader (" x-ms-copy-source-error-code" , " OperationTimedOut" );
345+ responsePtrSent = response.get ();
346+ retryCounter++;
347+ return response;
348+ }));
349+
350+ Azure::Core::Http::_internal::HttpPipeline pipeline (policies);
351+
352+ Request request (HttpMethod::Get, Azure::Core::Url (" https://www.microsoft.com" ));
353+ pipeline.Send (request, context);
354+
355+ EXPECT_NE (responsePtrSent, nullptr );
356+ EXPECT_EQ (retryCounter, 3 );
357+ }
208358}
209359
210360TEST (RetryPolicy, ShouldRetryOnResponse)
0 commit comments