From: Marcin Siodelski Date: Tue, 14 May 2019 07:22:49 +0000 (+0200) Subject: [#599,!320] Added another variant of the premature timeout test. X-Git-Tag: Kea-1.6.0-beta~162 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=e5a22d3e864b2c9fccec423cc349c6578a86d182;p=thirdparty%2Fkea.git [#599,!320] Added another variant of the premature timeout test. --- diff --git a/src/lib/http/tests/server_client_unittests.cc b/src/lib/http/tests/server_client_unittests.cc index 1478f5201f..ecd1928e2f 100644 --- a/src/lib/http/tests/server_client_unittests.cc +++ b/src/lib/http/tests/server_client_unittests.cc @@ -1003,6 +1003,96 @@ public: 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_; @@ -1252,62 +1342,15 @@ TEST_F(HttpClientTest, clientRequestTimeout) { // 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.