EXPECT_NE(sequence1->intValue(), sequence2->intValue());
}
+ /// @brief Tests the behavior of the HTTP client when the premature
+ /// timeout occurs.
+ ///
+ /// The premature timeout may occur when the system clock is moved
+ /// during the transaction. This test simulates this behavior by
+ /// starting new transaction and waiting for the timeout to occur
+ /// before the IO service is ran. The timeout handler is invoked
+ /// first and it resets the transaction state. This test verifies
+ /// that the started transaction tears down gracefully after the
+ /// transaction state is reset.
+ ///
+ /// There are two variants of this test. The first variant schedules
+ /// one transaction before running the IO service. The second variant
+ /// schedules two transactions prior to running the IO service. The
+ /// second transaction is queued, so it is expected that it doesn't
+ /// time out and it runs successfully.
+ ///
+ /// @param queue_two_requests Boolean value indicating if a single
+ /// transaction should be queued (false), or two (true).
+ void testClientRequestLateStart(const bool queue_two_requests) {
+ // Start the server.
+ ASSERT_NO_THROW(listener_.start());
+
+ // Create the client.
+ HttpClient client(io_service_);
+
+ // Specify the URL of the server.
+ Url url("http://127.0.0.1:18123");
+
+ // Generate first request.
+ PostHttpRequestJsonPtr request1 = createRequest("sequence", 1);
+ HttpResponseJsonPtr response1(new HttpResponseJson());
+
+ // Use very short timeout to make sure that it occurs before we actually
+ // run the transaction.
+ ASSERT_NO_THROW(client.asyncSendRequest(url, request1, response1,
+ [](const boost::system::error_code& ec,
+ const HttpResponsePtr& response,
+ const std::string&) {
+
+ // In this particular case we know exactly the type of the
+ // IO error returned, because the client explicitly sets this
+ // error code.
+ EXPECT_TRUE(ec.value() == boost::asio::error::timed_out);
+ // There should be no response returned.
+ EXPECT_FALSE(response);
+ }, HttpClient::RequestTimeout(1)));
+
+ if (queue_two_requests) {
+ PostHttpRequestJsonPtr request2 = createRequest("sequence", 2);
+ HttpResponseJsonPtr response2(new HttpResponseJson());
+ ASSERT_NO_THROW(client.asyncSendRequest(url, request2, response2,
+ [](const boost::system::error_code& ec,
+ const HttpResponsePtr& response,
+ const std::string&) {
+
+ // This second request should be successful.
+ EXPECT_TRUE(ec.value() == 0);
+ EXPECT_TRUE(response);
+ }));
+ }
+
+ // This waits for 3ms to make sure that the timeout occurs before we
+ // run the transaction. This leads to an unusual situation that the
+ // transaction state is reset as a result of the timeout but the
+ // transaction is alive. We want to make sure that the client can
+ // gracefully deal with this situation.
+ usleep(3000);
+
+ // Run the transaction and hope it will gracefully tear down.
+ ASSERT_NO_THROW(runIOService(100));
+
+ // Now try to send another request to make sure that the client
+ // is healthy.
+ PostHttpRequestJsonPtr request3 = createRequest("sequence", 3);
+ HttpResponseJsonPtr response3(new HttpResponseJson());
+ ASSERT_NO_THROW(client.asyncSendRequest(url, request3, response3,
+ [this](const boost::system::error_code& ec,
+ const HttpResponsePtr&,
+ const std::string&) {
+ io_service_.stop();
+
+ // Everything should be ok.
+ EXPECT_TRUE(ec.value() == 0);
+ }));
+
+ // Actually trigger the requests.
+ ASSERT_NO_THROW(runIOService());
+ }
+
/// @brief Instance of the listener used in the tests.
HttpListener listener_;
// This test verifies the behavior of the HTTP client when the premature
// (and unexpected) timeout occurs. The premature timeout may be caused
// by the system clock move.
-TEST_F(HttpClientTest, clientRequestLateStart) {
- // Start the server.
- ASSERT_NO_THROW(listener_.start());
-
- // Create the client.
- HttpClient client(io_service_);
-
- // Specify the URL of the server.
- Url url("http://127.0.0.1:18123");
-
- unsigned cb_num = 0;
-
- // Generate first request.
- PostHttpRequestJsonPtr request1 = createRequest("partial-response", true);
- HttpResponseJsonPtr response1(new HttpResponseJson());
-
- // Use very short timeout to make sure that it occurs before we actually
- // run the transaction.
- ASSERT_NO_THROW(client.asyncSendRequest(url, request1, response1,
- [](const boost::system::error_code& ec,
- const HttpResponsePtr& response,
- const std::string&) {
- // In this particular case we know exactly the type of the
- // IO error returned, because the client explicitly sets this
- // error code.
- EXPECT_TRUE(ec.value() == boost::asio::error::timed_out);
- // There should be no response returned.
- EXPECT_FALSE(response);
- }, HttpClient::RequestTimeout(1)));
-
- // This waits for 3ms to make sure that the timeout occurs before we
- // run the transaction. This leads to an unusual situation that the
- // transaction state is reset as a result of the timeout but the
- // transaction is alive. We want to make sure that the client can
- // gracefully deal with this situation.
- usleep(3000);
-
- // Run the transaction and hope it will gracefully tear down.
- ASSERT_NO_THROW(runIOService(100));
-
- // Now try to send another request to make sure that the client
- // is healthy.
- PostHttpRequestJsonPtr request2 = createRequest("sequence", 1);
- HttpResponseJsonPtr response2(new HttpResponseJson());
- ASSERT_NO_THROW(client.asyncSendRequest(url, request2, response2,
- [this](const boost::system::error_code& ec,
- const HttpResponsePtr&,
- const std::string&) {
- io_service_.stop();
-
- // Everything should be ok.
- EXPECT_TRUE(ec.value() == 0);
- }));
+TEST_F(HttpClientTest, clientRequestLateStartNoQueue) {
+ testClientRequestLateStart(false);
+}
- // Actually trigger the requests.
- ASSERT_NO_THROW(runIOService());
+// This test verifies the behavior of the HTTP client when the premature
+// timeout occurs and there are requests queued after the request which
+// times out.
+TEST_F(HttpClientTest, clientRequestLateStartQueue) {
+ testClientRequestLateStart(true);
}
// Test that client times out when connection takes too long.