|
2 | 2 | // SPDX-License-Identifier: Apache-2.0
|
3 | 3 |
|
4 | 4 | #include <curl/curlver.h>
|
| 5 | +#include <gmock/gmock.h> |
5 | 6 | #include <gtest/gtest.h>
|
6 | 7 | #include <string.h>
|
7 | 8 | #include <atomic>
|
@@ -108,6 +109,17 @@ class FinishInCallbackHandler : public CustomEventHandler
|
108 | 109 | std::shared_ptr<http_client::Session> session_;
|
109 | 110 | };
|
110 | 111 |
|
| 112 | +class RetryEventHandler : public CustomEventHandler |
| 113 | +{ |
| 114 | + void OnResponse(http_client::Response &response) noexcept override |
| 115 | + { |
| 116 | + ASSERT_EQ(429, response.GetStatusCode()); |
| 117 | + ASSERT_EQ(response.GetBody().size(), 0); |
| 118 | + is_called_.store(true, std::memory_order_release); |
| 119 | + got_response_.store(true, std::memory_order_release); |
| 120 | + } |
| 121 | +}; |
| 122 | + |
111 | 123 | class BasicCurlHttpTests : public ::testing::Test, public HTTP_SERVER_NS::HttpRequestCallback
|
112 | 124 | {
|
113 | 125 | protected:
|
@@ -139,6 +151,7 @@ class BasicCurlHttpTests : public ::testing::Test, public HTTP_SERVER_NS::HttpRe
|
139 | 151 | server_.addHandler("/simple/", *this);
|
140 | 152 | server_.addHandler("/get/", *this);
|
141 | 153 | server_.addHandler("/post/", *this);
|
| 154 | + server_.addHandler("/retry/", *this); |
142 | 155 | server_.start();
|
143 | 156 | is_running_ = true;
|
144 | 157 | }
|
@@ -170,6 +183,13 @@ class BasicCurlHttpTests : public ::testing::Test, public HTTP_SERVER_NS::HttpRe
|
170 | 183 | response.body = "{'k1':'v1', 'k2':'v2', 'k3':'v3'}";
|
171 | 184 | response_status = 200;
|
172 | 185 | }
|
| 186 | + else if (request.uri == "/retry/") |
| 187 | + { |
| 188 | + std::unique_lock<std::mutex> lk1(mtx_requests); |
| 189 | + received_requests_.push_back(request); |
| 190 | + response.headers["Content-Type"] = "text/plain"; |
| 191 | + response_status = 429; |
| 192 | + } |
173 | 193 |
|
174 | 194 | cv_got_events.notify_one();
|
175 | 195 |
|
@@ -330,6 +350,52 @@ TEST_F(BasicCurlHttpTests, CurlHttpOperations)
|
330 | 350 | delete handler;
|
331 | 351 | }
|
332 | 352 |
|
| 353 | +TEST_F(BasicCurlHttpTests, ExponentialBackoffRetry) |
| 354 | +{ |
| 355 | + using ::testing::AllOf; |
| 356 | + using ::testing::Gt; |
| 357 | + using ::testing::Lt; |
| 358 | + |
| 359 | + RetryEventHandler handler; |
| 360 | + http_client::HttpSslOptions no_ssl; |
| 361 | + http_client::Body body; |
| 362 | + http_client::Headers headers; |
| 363 | + http_client::Compression compression = http_client::Compression::kNone; |
| 364 | + http_client::RetryPolicy retry_policy = {4, 1.0f, 5.0f, 2.0f}; |
| 365 | + |
| 366 | + curl::HttpOperation operation(http_client::Method::Post, "http://127.0.0.1:19000/retry/", no_ssl, |
| 367 | + &handler, headers, body, compression, false, |
| 368 | + curl::kDefaultHttpConnTimeout, false, false, retry_policy); |
| 369 | + |
| 370 | + auto first_attempt_time = std::chrono::system_clock::now(); |
| 371 | + ASSERT_EQ(CURLE_OK, operation.Send()); |
| 372 | + ASSERT_TRUE(operation.IsRetryable()); |
| 373 | + ASSERT_THAT( |
| 374 | + operation.NextRetryTime().time_since_epoch().count(), |
| 375 | + AllOf(Gt((first_attempt_time + std::chrono::milliseconds{750}).time_since_epoch().count()), |
| 376 | + Lt((first_attempt_time + std::chrono::milliseconds{1250}).time_since_epoch().count()))); |
| 377 | + |
| 378 | + auto second_attempt_time = std::chrono::system_clock::now(); |
| 379 | + ASSERT_EQ(CURLE_OK, operation.Send()); |
| 380 | + ASSERT_TRUE(operation.IsRetryable()); |
| 381 | + ASSERT_THAT( |
| 382 | + operation.NextRetryTime().time_since_epoch().count(), |
| 383 | + AllOf( |
| 384 | + Gt((second_attempt_time + std::chrono::milliseconds{1550}).time_since_epoch().count()), |
| 385 | + Lt((second_attempt_time + std::chrono::milliseconds{2450}).time_since_epoch().count()))); |
| 386 | + |
| 387 | + auto third_attempt_time = std::chrono::system_clock::now(); |
| 388 | + ASSERT_EQ(CURLE_OK, operation.Send()); |
| 389 | + ASSERT_TRUE(operation.IsRetryable()); |
| 390 | + ASSERT_THAT( |
| 391 | + operation.NextRetryTime().time_since_epoch().count(), |
| 392 | + AllOf(Gt((third_attempt_time + std::chrono::milliseconds{3150}).time_since_epoch().count()), |
| 393 | + Lt((third_attempt_time + std::chrono::milliseconds{4850}).time_since_epoch().count()))); |
| 394 | + |
| 395 | + ASSERT_EQ(CURLE_OK, operation.Send()); |
| 396 | + ASSERT_FALSE(operation.IsRetryable()); |
| 397 | +} |
| 398 | + |
333 | 399 | TEST_F(BasicCurlHttpTests, SendGetRequestSync)
|
334 | 400 | {
|
335 | 401 | received_requests_.clear();
|
@@ -559,6 +625,7 @@ TEST_F(BasicCurlHttpTests, ElegantQuitQuick)
|
559 | 625 | ASSERT_TRUE(handler->is_called_);
|
560 | 626 | ASSERT_TRUE(handler->got_response_);
|
561 | 627 | }
|
| 628 | + |
562 | 629 | TEST_F(BasicCurlHttpTests, BackgroundThreadWaitMore)
|
563 | 630 | {
|
564 | 631 | {
|
|
0 commit comments