From: Razvan Becheriu Date: Mon, 18 May 2020 16:45:29 +0000 (+0300) Subject: [#1239] addressed comments X-Git-Tag: Kea-1.7.8~69 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a55deb981993a7966d328316c0b6de2dfbcd8261;p=thirdparty%2Fkea.git [#1239] addressed comments --- diff --git a/src/lib/http/client.cc b/src/lib/http/client.cc index 686031e0b8..bde27af3c2 100644 --- a/src/lib/http/client.cc +++ b/src/lib/http/client.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2018-2019 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2018-2020 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this diff --git a/src/lib/http/tests/server_client_unittests.cc b/src/lib/http/tests/server_client_unittests.cc index 540f1b7356..9eaeb8f9f9 100644 --- a/src/lib/http/tests/server_client_unittests.cc +++ b/src/lib/http/tests/server_client_unittests.cc @@ -1,4 +1,4 @@ -// Copyright (C) 2017-2019 Internet Systems Consortium, Inc. ("ISC") +// Copyright (C) 2017-2020 Internet Systems Consortium, Inc. ("ISC") // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this @@ -245,8 +245,6 @@ protected: request_timeout_, idle_timeout_)); return (conn); } - - }; /// @brief Derivation of the @c HttpListener used in tests. @@ -335,7 +333,6 @@ public: // with this and adjust the data length. HttpConnection::socketWriteCallback(transaction, ec, length + 1); } - }; /// @brief Implementation of the @c HttpConnection which replaces @@ -343,7 +340,6 @@ public: class HttpConnectionTransactionChange : public HttpConnection { public: - /// @brief Constructor. /// /// @param io_service IO service to be used by the connection. @@ -1200,11 +1196,11 @@ public: /// @brief Destructor. ~HttpClientTest() { - MultiThreadingMgr::instance().setMode(false); listener_.stop(); listener2_.stop(); listener3_.stop(); io_service_.poll(); + MultiThreadingMgr::instance().setMode(false); } /// @brief Creates HTTP request with JSON body. @@ -1291,6 +1287,280 @@ public: EXPECT_NE(sequence1->intValue(), sequence2->intValue()); } + // Test that the client can communicate with two different destinations + // simultaneously. + void testMultipleDestinations() { + // Start two servers running on different ports. + ASSERT_NO_THROW(listener_.start()); + ASSERT_NO_THROW(listener2_.start()); + + // Create the client. It will be communicating with the two servers. + HttpClient client(io_service_); + + // Specify the URLs on which the servers are available. + Url url1("http://127.0.0.1:18123"); + Url url2("http://[::1]:18124"); + + // Create a request to the first server. + PostHttpRequestJsonPtr request1 = createRequest("sequence", 1); + HttpResponseJsonPtr response1(new HttpResponseJson()); + unsigned resp_num = 0; + ASSERT_NO_THROW(client.asyncSendRequest(url1, request1, response1, + [this, &resp_num](const boost::system::error_code& ec, + const HttpResponsePtr&, + const std::string&) { + if (++resp_num > 1) { + io_service_.stop(); + } + EXPECT_FALSE(ec); + })); + + // Create a request to the second server. + PostHttpRequestJsonPtr request2 = createRequest("sequence", 2); + HttpResponseJsonPtr response2(new HttpResponseJson()); + ASSERT_NO_THROW(client.asyncSendRequest(url2, request2, response2, + [this, &resp_num](const boost::system::error_code& ec, + const HttpResponsePtr&, + const std::string&) { + if (++resp_num > 1) { + io_service_.stop(); + } + EXPECT_FALSE(ec); + })); + + // Actually trigger the requests. + ASSERT_NO_THROW(runIOService()); + + // Make sure we have received two different responses. + ASSERT_TRUE(response1); + ConstElementPtr sequence1 = response1->getJsonElement("sequence"); + ASSERT_TRUE(sequence1); + + ASSERT_TRUE(response2); + ConstElementPtr sequence2 = response2->getJsonElement("sequence"); + ASSERT_TRUE(sequence2); + + EXPECT_NE(sequence1->intValue(), sequence2->intValue()); + } + + // Test that idle connection can be resumed for second request. + void testIdleConnection() { + // Start the server that has short idle timeout. It closes the idle + // connection after 200ms. + ASSERT_NO_THROW(listener3_.start()); + + // Create the client that will communicate with this server. + HttpClient client(io_service_); + + // Specify the URL of this server. + Url url("http://127.0.0.1:18125"); + + // Create the first request. + PostHttpRequestJsonPtr request1 = createRequest("sequence", 1); + HttpResponseJsonPtr response1(new HttpResponseJson()); + ASSERT_NO_THROW(client.asyncSendRequest(url, request1, response1, + [this](const boost::system::error_code& ec, const HttpResponsePtr&, + const std::string&) { + io_service_.stop(); + EXPECT_FALSE(ec); + })); + + // Run the IO service until the response is received. + ASSERT_NO_THROW(runIOService()); + + // Make sure the response has been received. + ASSERT_TRUE(response1); + ConstElementPtr sequence1 = response1->getJsonElement("sequence"); + ASSERT_TRUE(sequence1); + + // Delay the generation of the second request by 2x server idle timeout. + // This should be enough to cause the server to close the connection. + ASSERT_NO_THROW(runIOService(SHORT_IDLE_TIMEOUT * 2)); + + // Create another request. + PostHttpRequestJsonPtr request2 = createRequest("sequence", 2); + 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(); + EXPECT_FALSE(ec); + })); + + // Actually trigger the second request. + ASSERT_NO_THROW(runIOService()); + + // Make sire that the server has responded. + ASSERT_TRUE(response2); + ConstElementPtr sequence2 = response2->getJsonElement("sequence"); + ASSERT_TRUE(sequence2); + + // Make sure that two different responses have been received. + EXPECT_NE(sequence1->intValue(), sequence2->intValue()); + } + + // This test verifies that the client returns IO error code when the + // server is unreachable. + void testUnreachable () { + // Create the client. + HttpClient client(io_service_); + + // Specify the URL of the server. This server is down. + Url url("http://127.0.0.1:18123"); + + // Create the request. + PostHttpRequestJsonPtr request = createRequest("sequence", 1); + HttpResponseJsonPtr response(new HttpResponseJson()); + ASSERT_NO_THROW(client.asyncSendRequest(url, request, response, + [this](const boost::system::error_code& ec, + const HttpResponsePtr&, + const std::string&) { + io_service_.stop(); + // The server should have returned an IO error. + EXPECT_TRUE(ec); + })); + + // Actually trigger the request. + ASSERT_NO_THROW(runIOService()); + } + + // Test that an error is returned by the client if the server response is + // malformed. + void testMalformedResponse () { + // 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"); + + // The response is going to be malformed in such a way that it holds + // an invalid content type. We affect the content type by creating + // a request that holds a JSON parameter requesting a specific + // content type. + PostHttpRequestJsonPtr request = createRequest("requested-content-type", "text/html"); + HttpResponseJsonPtr response(new HttpResponseJson()); + ASSERT_NO_THROW(client.asyncSendRequest(url, request, response, + [this](const boost::system::error_code& ec, + const HttpResponsePtr& response, + const std::string& parsing_error) { + io_service_.stop(); + // There should be no IO error (answer from the server is received). + EXPECT_FALSE(ec); + // The response object is NULL because it couldn't be finalized. + EXPECT_FALSE(response); + // The message parsing error should be returned. + EXPECT_FALSE(parsing_error.empty()); + })); + + // Actually trigger the request. + ASSERT_NO_THROW(runIOService()); + } + + // Test that client times out when it doesn't receive the entire response + // from the server within a desired time. + void testClientRequestTimeout() { + // 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; + + // Create the request which asks the server to generate a partial + // (although well formed) response. The client will be waiting for the + // rest of the response to be provided and will eventually time out. + PostHttpRequestJsonPtr request1 = createRequest("partial-response", true); + HttpResponseJsonPtr response1(new HttpResponseJson()); + ASSERT_NO_THROW(client.asyncSendRequest(url, request1, response1, + [this, &cb_num](const boost::system::error_code& ec, + const HttpResponsePtr& response, + const std::string&) { + if (++cb_num > 1) { + io_service_.stop(); + } + // 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(100))); + + // Create another request after the timeout. It should be handled ok. + PostHttpRequestJsonPtr request2 = createRequest("sequence", 1); + HttpResponseJsonPtr response2(new HttpResponseJson()); + ASSERT_NO_THROW(client.asyncSendRequest(url, request2, response2, + [this, &cb_num](const boost::system::error_code& /*ec*/, const HttpResponsePtr&, + const std::string&) { + if (++cb_num > 1) { + io_service_.stop(); + } + })); + + // Actually trigger the requests. + ASSERT_NO_THROW(runIOService()); + } + + // Test that client times out when connection takes too long. + void testClientConnectTimeout() { + // 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; + + PostHttpRequestJsonPtr request = createRequest("sequence", 1); + HttpResponseJsonPtr response(new HttpResponseJson()); + ASSERT_NO_THROW(client.asyncSendRequest(url, request, response, + [this, &cb_num](const boost::system::error_code& ec, + const HttpResponsePtr& response, + const std::string&) { + if (++cb_num > 1) { + io_service_.stop(); + } + // 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(100), + + // This callback is invoked upon an attempt to connect to the + // server. The false value indicates to the HttpClient to not + // try to send a request to the server. This simulates the + // case of connect() taking very long and should eventually + // cause the transaction to time out. + [](const boost::system::error_code& /*ec*/, int) { + return (false); + })); + + // Create another request after the timeout. It should be handled ok. + ASSERT_NO_THROW(client.asyncSendRequest(url, request, response, + [this, &cb_num](const boost::system::error_code& /*ec*/, const HttpResponsePtr&, + const std::string&) { + if (++cb_num > 1) { + io_service_.stop(); + } + })); + + // Actually trigger the requests. + ASSERT_NO_THROW(runIOService()); + } + /// @brief Tests the behavior of the HTTP client when the premature /// timeout occurs. /// @@ -1694,448 +1964,64 @@ TEST_F(HttpClientTest, closeBetweenRequestsMultiThreading) { // Test that the client can communicate with two different destinations // simultaneously. TEST_F(HttpClientTest, multipleDestinations) { - // Start two servers running on different ports. - ASSERT_NO_THROW(listener_.start()); - ASSERT_NO_THROW(listener2_.start()); - - // Create the client. It will be communicating with the two servers. - HttpClient client(io_service_); - - // Specify the URLs on which the servers are available. - Url url1("http://127.0.0.1:18123"); - Url url2("http://[::1]:18124"); - - // Create a request to the first server. - PostHttpRequestJsonPtr request1 = createRequest("sequence", 1); - HttpResponseJsonPtr response1(new HttpResponseJson()); - unsigned resp_num = 0; - ASSERT_NO_THROW(client.asyncSendRequest(url1, request1, response1, - [this, &resp_num](const boost::system::error_code& ec, - const HttpResponsePtr&, - const std::string&) { - if (++resp_num > 1) { - io_service_.stop(); - } - EXPECT_FALSE(ec); - })); - - // Create a request to the second server. - PostHttpRequestJsonPtr request2 = createRequest("sequence", 2); - HttpResponseJsonPtr response2(new HttpResponseJson()); - ASSERT_NO_THROW(client.asyncSendRequest(url2, request2, response2, - [this, &resp_num](const boost::system::error_code& ec, - const HttpResponsePtr&, - const std::string&) { - if (++resp_num > 1) { - io_service_.stop(); - } - EXPECT_FALSE(ec); - })); - - // Actually trigger the requests. - ASSERT_NO_THROW(runIOService()); - - // Make sure we have received two different responses. - ASSERT_TRUE(response1); - ConstElementPtr sequence1 = response1->getJsonElement("sequence"); - ASSERT_TRUE(sequence1); - - ASSERT_TRUE(response2); - ConstElementPtr sequence2 = response2->getJsonElement("sequence"); - ASSERT_TRUE(sequence2); - - EXPECT_NE(sequence1->intValue(), sequence2->intValue()); + ASSERT_NO_FATAL_FAILURE(testMultipleDestinations()); } // Test that the client can communicate with two different destinations // simultaneously. TEST_F(HttpClientTest, multipleDestinationsMultiThreading) { MultiThreadingMgr::instance().setMode(true); - // Start two servers running on different ports. - ASSERT_NO_THROW(listener_.start()); - ASSERT_NO_THROW(listener2_.start()); - - // Create the client. It will be communicating with the two servers. - HttpClient client(io_service_); - - // Specify the URLs on which the servers are available. - Url url1("http://127.0.0.1:18123"); - Url url2("http://[::1]:18124"); - - // Create a request to the first server. - PostHttpRequestJsonPtr request1 = createRequest("sequence", 1); - HttpResponseJsonPtr response1(new HttpResponseJson()); - unsigned resp_num = 0; - ASSERT_NO_THROW(client.asyncSendRequest(url1, request1, response1, - [this, &resp_num](const boost::system::error_code& ec, - const HttpResponsePtr&, - const std::string&) { - if (++resp_num > 1) { - io_service_.stop(); - } - EXPECT_FALSE(ec); - })); - - // Create a request to the second server. - PostHttpRequestJsonPtr request2 = createRequest("sequence", 2); - HttpResponseJsonPtr response2(new HttpResponseJson()); - ASSERT_NO_THROW(client.asyncSendRequest(url2, request2, response2, - [this, &resp_num](const boost::system::error_code& ec, - const HttpResponsePtr&, - const std::string&) { - if (++resp_num > 1) { - io_service_.stop(); - } - EXPECT_FALSE(ec); - })); - - // Actually trigger the requests. - ASSERT_NO_THROW(runIOService()); - - // Make sure we have received two different responses. - ASSERT_TRUE(response1); - ConstElementPtr sequence1 = response1->getJsonElement("sequence"); - ASSERT_TRUE(sequence1); - - ASSERT_TRUE(response2); - ConstElementPtr sequence2 = response2->getJsonElement("sequence"); - ASSERT_TRUE(sequence2); - - EXPECT_NE(sequence1->intValue(), sequence2->intValue()); + ASSERT_NO_FATAL_FAILURE(testMultipleDestinations()); } // Test that idle connection can be resumed for second request. TEST_F(HttpClientTest, idleConnection) { - // Start the server that has short idle timeout. It closes the idle connection - // after 200ms. - ASSERT_NO_THROW(listener3_.start()); - - // Create the client that will communicate with this server. - HttpClient client(io_service_); - - // Specify the URL of this server. - Url url("http://127.0.0.1:18125"); - - // Create the first request. - PostHttpRequestJsonPtr request1 = createRequest("sequence", 1); - HttpResponseJsonPtr response1(new HttpResponseJson()); - ASSERT_NO_THROW(client.asyncSendRequest(url, request1, response1, - [this](const boost::system::error_code& ec, const HttpResponsePtr&, - const std::string&) { - io_service_.stop(); - EXPECT_FALSE(ec); - })); - - // Run the IO service until the response is received. - ASSERT_NO_THROW(runIOService()); - - // Make sure the response has been received. - ASSERT_TRUE(response1); - ConstElementPtr sequence1 = response1->getJsonElement("sequence"); - ASSERT_TRUE(sequence1); - - // Delay the generation of the second request by 2x server idle timeout. - // This should be enough to cause the server to close the connection. - ASSERT_NO_THROW(runIOService(SHORT_IDLE_TIMEOUT * 2)); - - // Create another request. - PostHttpRequestJsonPtr request2 = createRequest("sequence", 2); - 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(); - EXPECT_FALSE(ec); - })); - - // Actually trigger the second request. - ASSERT_NO_THROW(runIOService()); - - // Make sire that the server has responded. - ASSERT_TRUE(response2); - ConstElementPtr sequence2 = response2->getJsonElement("sequence"); - ASSERT_TRUE(sequence2); - - // Make sure that two different responses have been received. - EXPECT_NE(sequence1->intValue(), sequence2->intValue()); + ASSERT_NO_FATAL_FAILURE(testIdleConnection()); } // Test that idle connection can be resumed for second request. TEST_F(HttpClientTest, idleConnectionMultiThreading) { MultiThreadingMgr::instance().setMode(true); - // Start the server that has short idle timeout. It closes the idle connection - // after 200ms. - ASSERT_NO_THROW(listener3_.start()); - - // Create the client that will communicate with this server. - HttpClient client(io_service_); - - // Specify the URL of this server. - Url url("http://127.0.0.1:18125"); - - // Create the first request. - PostHttpRequestJsonPtr request1 = createRequest("sequence", 1); - HttpResponseJsonPtr response1(new HttpResponseJson()); - ASSERT_NO_THROW(client.asyncSendRequest(url, request1, response1, - [this](const boost::system::error_code& ec, const HttpResponsePtr&, - const std::string&) { - io_service_.stop(); - EXPECT_FALSE(ec); - })); - - // Run the IO service until the response is received. - ASSERT_NO_THROW(runIOService()); - - // Make sure the response has been received. - ASSERT_TRUE(response1); - ConstElementPtr sequence1 = response1->getJsonElement("sequence"); - ASSERT_TRUE(sequence1); - - // Delay the generation of the second request by 2x server idle timeout. - // This should be enough to cause the server to close the connection. - ASSERT_NO_THROW(runIOService(SHORT_IDLE_TIMEOUT * 2)); - - // Create another request. - PostHttpRequestJsonPtr request2 = createRequest("sequence", 2); - 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(); - EXPECT_FALSE(ec); - })); - - // Actually trigger the second request. - ASSERT_NO_THROW(runIOService()); - - // Make sire that the server has responded. - ASSERT_TRUE(response2); - ConstElementPtr sequence2 = response2->getJsonElement("sequence"); - ASSERT_TRUE(sequence2); - - // Make sure that two different responses have been received. - EXPECT_NE(sequence1->intValue(), sequence2->intValue()); + ASSERT_NO_FATAL_FAILURE(testIdleConnection()); } // This test verifies that the client returns IO error code when the // server is unreachable. TEST_F(HttpClientTest, unreachable) { - // Create the client. - HttpClient client(io_service_); - - // Specify the URL of the server. This server is down. - Url url("http://127.0.0.1:18123"); - - // Create the request. - PostHttpRequestJsonPtr request = createRequest("sequence", 1); - HttpResponseJsonPtr response(new HttpResponseJson()); - ASSERT_NO_THROW(client.asyncSendRequest(url, request, response, - [this](const boost::system::error_code& ec, - const HttpResponsePtr&, - const std::string&) { - io_service_.stop(); - // The server should have returned an IO error. - EXPECT_TRUE(ec); - })); - - // Actually trigger the request. - ASSERT_NO_THROW(runIOService()); + ASSERT_NO_FATAL_FAILURE(testUnreachable()); } // This test verifies that the client returns IO error code when the // server is unreachable. TEST_F(HttpClientTest, unreachableMultiThreading) { MultiThreadingMgr::instance().setMode(true); - // Create the client. - HttpClient client(io_service_); - - // Specify the URL of the server. This server is down. - Url url("http://127.0.0.1:18123"); - - // Create the request. - PostHttpRequestJsonPtr request = createRequest("sequence", 1); - HttpResponseJsonPtr response(new HttpResponseJson()); - ASSERT_NO_THROW(client.asyncSendRequest(url, request, response, - [this](const boost::system::error_code& ec, - const HttpResponsePtr&, - const std::string&) { - io_service_.stop(); - // The server should have returned an IO error. - EXPECT_TRUE(ec); - })); - - // Actually trigger the request. - ASSERT_NO_THROW(runIOService()); + ASSERT_NO_FATAL_FAILURE(testUnreachable()); } // Test that an error is returned by the client if the server response is // malformed. TEST_F(HttpClientTest, malformedResponse) { - // 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"); - - // The response is going to be malformed in such a way that it holds - // an invalid content type. We affect the content type by creating - // a request that holds a JSON parameter requesting a specific - // content type. - PostHttpRequestJsonPtr request = createRequest("requested-content-type", "text/html"); - HttpResponseJsonPtr response(new HttpResponseJson()); - ASSERT_NO_THROW(client.asyncSendRequest(url, request, response, - [this](const boost::system::error_code& ec, - const HttpResponsePtr& response, - const std::string& parsing_error) { - io_service_.stop(); - // There should be no IO error (answer from the server is received). - EXPECT_FALSE(ec); - // The response object is NULL because it couldn't be finalized. - EXPECT_FALSE(response); - // The message parsing error should be returned. - EXPECT_FALSE(parsing_error.empty()); - })); - - // Actually trigger the request. - ASSERT_NO_THROW(runIOService()); + ASSERT_NO_FATAL_FAILURE(testMalformedResponse()); } // Test that an error is returned by the client if the server response is // malformed. TEST_F(HttpClientTest, malformedResponseMultiThreading) { MultiThreadingMgr::instance().setMode(true); - // 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"); - - // The response is going to be malformed in such a way that it holds - // an invalid content type. We affect the content type by creating - // a request that holds a JSON parameter requesting a specific - // content type. - PostHttpRequestJsonPtr request = createRequest("requested-content-type", "text/html"); - HttpResponseJsonPtr response(new HttpResponseJson()); - ASSERT_NO_THROW(client.asyncSendRequest(url, request, response, - [this](const boost::system::error_code& ec, - const HttpResponsePtr& response, - const std::string& parsing_error) { - io_service_.stop(); - // There should be no IO error (answer from the server is received). - EXPECT_FALSE(ec); - // The response object is NULL because it couldn't be finalized. - EXPECT_FALSE(response); - // The message parsing error should be returned. - EXPECT_FALSE(parsing_error.empty()); - })); - - // Actually trigger the request. - ASSERT_NO_THROW(runIOService()); + ASSERT_NO_FATAL_FAILURE(testMalformedResponse()); } // Test that client times out when it doesn't receive the entire response // from the server within a desired time. TEST_F(HttpClientTest, clientRequestTimeout) { - // 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; - - // Create the request which asks the server to generate a partial - // (although well formed) response. The client will be waiting for the - // rest of the response to be provided and will eventually time out. - PostHttpRequestJsonPtr request1 = createRequest("partial-response", true); - HttpResponseJsonPtr response1(new HttpResponseJson()); - ASSERT_NO_THROW(client.asyncSendRequest(url, request1, response1, - [this, &cb_num](const boost::system::error_code& ec, - const HttpResponsePtr& response, - const std::string&) { - if (++cb_num > 1) { - io_service_.stop(); - } - // 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(100))); - - // Create another request after the timeout. It should be handled ok. - PostHttpRequestJsonPtr request2 = createRequest("sequence", 1); - HttpResponseJsonPtr response2(new HttpResponseJson()); - ASSERT_NO_THROW(client.asyncSendRequest(url, request2, response2, - [this, &cb_num](const boost::system::error_code& /*ec*/, const HttpResponsePtr&, - const std::string&) { - if (++cb_num > 1) { - io_service_.stop(); - } - })); - - // Actually trigger the requests. - ASSERT_NO_THROW(runIOService()); + ASSERT_NO_FATAL_FAILURE(testClientRequestTimeout()); } // Test that client times out when it doesn't receive the entire response // from the server within a desired time. TEST_F(HttpClientTest, clientRequestTimeoutMultiThreading) { MultiThreadingMgr::instance().setMode(true); - // 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; - - // Create the request which asks the server to generate a partial - // (although well formed) response. The client will be waiting for the - // rest of the response to be provided and will eventually time out. - PostHttpRequestJsonPtr request1 = createRequest("partial-response", true); - HttpResponseJsonPtr response1(new HttpResponseJson()); - ASSERT_NO_THROW(client.asyncSendRequest(url, request1, response1, - [this, &cb_num](const boost::system::error_code& ec, - const HttpResponsePtr& response, - const std::string&) { - if (++cb_num > 1) { - io_service_.stop(); - } - // 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(100))); - - // Create another request after the timeout. It should be handled ok. - PostHttpRequestJsonPtr request2 = createRequest("sequence", 1); - HttpResponseJsonPtr response2(new HttpResponseJson()); - ASSERT_NO_THROW(client.asyncSendRequest(url, request2, response2, - [this, &cb_num](const boost::system::error_code& /*ec*/, const HttpResponsePtr&, - const std::string&) { - if (++cb_num > 1) { - io_service_.stop(); - } - })); - - // Actually trigger the requests. - ASSERT_NO_THROW(runIOService()); + ASSERT_NO_FATAL_FAILURE(testClientRequestTimeout()); } // This test verifies the behavior of the HTTP client when the premature @@ -2170,109 +2056,13 @@ TEST_F(HttpClientTest, clientRequestLateStartQueueMultiThreading) { // Test that client times out when connection takes too long. TEST_F(HttpClientTest, clientConnectTimeout) { - // 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; - - PostHttpRequestJsonPtr request = createRequest("sequence", 1); - HttpResponseJsonPtr response(new HttpResponseJson()); - ASSERT_NO_THROW(client.asyncSendRequest(url, request, response, - [this, &cb_num](const boost::system::error_code& ec, - const HttpResponsePtr& response, - const std::string&) { - if (++cb_num > 1) { - io_service_.stop(); - } - // 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(100), - - // This callback is invoked upon an attempt to connect to the - // server. The false value indicates to the HttpClient to not - // try to send a request to the server. This simulates the - // case of connect() taking very long and should eventually - // cause the transaction to time out. - [](const boost::system::error_code& /*ec*/, int) { - return (false); - })); - - // Create another request after the timeout. It should be handled ok. - ASSERT_NO_THROW(client.asyncSendRequest(url, request, response, - [this, &cb_num](const boost::system::error_code& /*ec*/, const HttpResponsePtr&, - const std::string&) { - if (++cb_num > 1) { - io_service_.stop(); - } - })); - - // Actually trigger the requests. - ASSERT_NO_THROW(runIOService()); + ASSERT_NO_FATAL_FAILURE(testClientConnectTimeout()); } // Test that client times out when connection takes too long. TEST_F(HttpClientTest, clientConnectTimeoutMultiThreading) { MultiThreadingMgr::instance().setMode(true); - // 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; - - PostHttpRequestJsonPtr request = createRequest("sequence", 1); - HttpResponseJsonPtr response(new HttpResponseJson()); - ASSERT_NO_THROW(client.asyncSendRequest(url, request, response, - [this, &cb_num](const boost::system::error_code& ec, - const HttpResponsePtr& response, - const std::string&) { - if (++cb_num > 1) { - io_service_.stop(); - } - // 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(100), - - // This callback is invoked upon an attempt to connect to the - // server. The false value indicates to the HttpClient to not - // try to send a request to the server. This simulates the - // case of connect() taking very long and should eventually - // cause the transaction to time out. - [](const boost::system::error_code& /*ec*/, int) { - return (false); - })); - - // Create another request after the timeout. It should be handled ok. - ASSERT_NO_THROW(client.asyncSendRequest(url, request, response, - [this, &cb_num](const boost::system::error_code& /*ec*/, const HttpResponsePtr&, - const std::string&) { - if (++cb_num > 1) { - io_service_.stop(); - } - })); - - // Actually trigger the requests. - ASSERT_NO_THROW(runIOService()); + ASSERT_NO_FATAL_FAILURE(testClientConnectTimeout()); } /// Tests that connect and close callbacks work correctly.