]> git.ipfire.org Git - thirdparty/kea.git/commitdiff
[#599,!320] Added another variant of the premature timeout test.
authorMarcin Siodelski <marcin@isc.org>
Tue, 14 May 2019 07:22:49 +0000 (09:22 +0200)
committerMarcin Siodelski <marcin@isc.org>
Tue, 14 May 2019 07:22:49 +0000 (09:22 +0200)
src/lib/http/tests/server_client_unittests.cc

index 1478f5201f74a806350486fc135925b76df04946..ecd1928e2f20af0e77b0462623fabdc49fc0b3c9 100644 (file)
@@ -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.