session_unittests.cc 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. // Copyright (C) 2009 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. // $Id: data_unittests.cc 1899 2010-05-21 12:03:59Z jelte $
  15. #include <config.h>
  16. // for some IPC/network system calls in asio/detail/pipe_select_interrupter.hpp
  17. #include <unistd.h>
  18. // XXX: the ASIO header must be included before others. See session.cc.
  19. #include <asio.hpp>
  20. #include <gtest/gtest.h>
  21. #include <boost/bind.hpp>
  22. #include <exceptions/exceptions.h>
  23. #include <cc/session.h>
  24. #include <cc/data.h>
  25. #include <session_unittests_config.h>
  26. using namespace isc::cc;
  27. TEST(AsioSession, establish) {
  28. asio::io_service io_service_;
  29. Session sess(io_service_);
  30. EXPECT_THROW(
  31. sess.establish("/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
  32. "/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
  33. "/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
  34. "/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
  35. "/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
  36. "/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
  37. "/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
  38. "/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
  39. "/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
  40. "/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
  41. ), isc::cc::SessionError
  42. );
  43. }
  44. // This class sets up a domain socket for the session to connect to
  45. // it will impersonate the msgq a tiny bit (if setSendLname() has
  46. // been called, it will send an 'answer' to the lname query that is
  47. // sent in the initialization of Session objects)
  48. class TestDomainSocket {
  49. public:
  50. TestDomainSocket(asio::io_service& io_service, const char* file) :
  51. io_service_(io_service),
  52. ep_(file),
  53. acceptor_(io_service_, ep_),
  54. socket_(io_service_)
  55. {
  56. acceptor_.async_accept(socket_,
  57. boost::bind(&TestDomainSocket::acceptHandler,
  58. this, _1));
  59. }
  60. ~TestDomainSocket() {
  61. socket_.close();
  62. unlink(BIND10_TEST_SOCKET_FILE);
  63. }
  64. void
  65. acceptHandler(const asio::error_code&) {
  66. }
  67. void
  68. sendmsg(isc::data::ElementPtr& env, isc::data::ElementPtr& msg) {
  69. const std::string header_wire = env->toWire();
  70. const std::string body_wire = msg->toWire();
  71. const unsigned int length = 2 + header_wire.length() +
  72. body_wire.length();
  73. const unsigned int length_net = htonl(length);
  74. const unsigned short header_length = header_wire.length();
  75. const unsigned short header_length_net = htons(header_length);
  76. socket_.send(asio::buffer(&length_net, sizeof(length_net)));
  77. socket_.send(asio::buffer(&header_length_net,
  78. sizeof(header_length_net)));
  79. socket_.send(asio::buffer(header_wire.data(), header_length));
  80. socket_.send(asio::buffer(body_wire.data(), body_wire.length()));
  81. }
  82. void
  83. sendLname() {
  84. isc::data::ElementPtr lname_answer1 =
  85. isc::data::Element::fromJSON("{ \"type\": \"lname\" }");
  86. isc::data::ElementPtr lname_answer2 =
  87. isc::data::Element::fromJSON("{ \"lname\": \"foobar\" }");
  88. sendmsg(lname_answer1, lname_answer2);
  89. }
  90. void
  91. setSendLname() {
  92. // ignore whatever data we get, send back an lname
  93. asio::async_read(socket_, asio::buffer(data_buf, 0),
  94. boost::bind(&TestDomainSocket::sendLname, this));
  95. }
  96. private:
  97. asio::io_service& io_service_;
  98. asio::local::stream_protocol::endpoint ep_;
  99. asio::local::stream_protocol::acceptor acceptor_;
  100. asio::local::stream_protocol::socket socket_;
  101. char data_buf[1024];
  102. };
  103. class SessionTest : public ::testing::Test {
  104. protected:
  105. SessionTest() : sess(my_io_service), work(my_io_service) {
  106. // The TestDomainSocket is held as a 'new'-ed pointer,
  107. // so we can call unlink() first.
  108. unlink(BIND10_TEST_SOCKET_FILE);
  109. tds = new TestDomainSocket(my_io_service, BIND10_TEST_SOCKET_FILE);
  110. }
  111. ~SessionTest() {
  112. delete tds;
  113. }
  114. public:
  115. // used in the handler test
  116. // This handler first reads (and ignores) whatever message caused
  117. // it to be invoked. Then it calls group_recv for a second message.
  118. // If this message is { "command": "stop" } it'll tell the
  119. // io_service it is done. Otherwise it'll re-register this handler
  120. void someHandler() {
  121. isc::data::ConstElementPtr env, msg;
  122. sess.group_recvmsg(env, msg, false, -1);
  123. sess.group_recvmsg(env, msg, false, -1);
  124. if (msg && msg->contains("command") &&
  125. msg->get("command")->stringValue() == "stop") {
  126. my_io_service.stop();
  127. } else {
  128. sess.startRead(boost::bind(&SessionTest::someHandler, this));
  129. }
  130. }
  131. protected:
  132. asio::io_service my_io_service;
  133. TestDomainSocket* tds;
  134. Session sess;
  135. // Keep run() from stopping right away by informing it it has work to do
  136. asio::io_service::work work;
  137. };
  138. TEST_F(SessionTest, timeout_on_connect) {
  139. // set to a short timeout so the test doesn't take too long
  140. EXPECT_EQ(4000, sess.getTimeout());
  141. sess.setTimeout(100);
  142. EXPECT_EQ(100, sess.getTimeout());
  143. // no answer, should timeout
  144. EXPECT_THROW(sess.establish(BIND10_TEST_SOCKET_FILE), SessionTimeout);
  145. }
  146. TEST_F(SessionTest, connect_ok) {
  147. tds->setSendLname();
  148. sess.establish(BIND10_TEST_SOCKET_FILE);
  149. }
  150. TEST_F(SessionTest, connect_ok_no_timeout) {
  151. tds->setSendLname();
  152. sess.setTimeout(0);
  153. sess.establish(BIND10_TEST_SOCKET_FILE);
  154. }
  155. TEST_F(SessionTest, connect_ok_connection_reset) {
  156. tds->setSendLname();
  157. sess.establish(BIND10_TEST_SOCKET_FILE);
  158. // Close the session again, so the next recv() should throw
  159. sess.disconnect();
  160. isc::data::ConstElementPtr env, msg;
  161. EXPECT_THROW(sess.group_recvmsg(env, msg, false, -1), SessionError);
  162. }
  163. TEST_F(SessionTest, run_with_handler) {
  164. tds->setSendLname();
  165. sess.establish(BIND10_TEST_SOCKET_FILE);
  166. sess.startRead(boost::bind(&SessionTest::someHandler, this));
  167. isc::data::ElementPtr env = isc::data::Element::fromJSON("{ \"to\": \"me\" }");
  168. isc::data::ElementPtr msg = isc::data::Element::fromJSON("{ \"some\": \"message\" }");
  169. tds->sendmsg(env, msg);
  170. msg = isc::data::Element::fromJSON("{ \"another\": \"message\" }");
  171. tds->sendmsg(env, msg);
  172. msg = isc::data::Element::fromJSON("{ \"a third\": \"message\" }");
  173. tds->sendmsg(env, msg);
  174. msg = isc::data::Element::fromJSON("{ \"command\": \"stop\" }");
  175. tds->sendmsg(env, msg);
  176. size_t count = my_io_service.run();
  177. ASSERT_EQ(2, count);
  178. }
  179. TEST_F(SessionTest, run_with_handler_timeout) {
  180. tds->setSendLname();
  181. sess.establish(BIND10_TEST_SOCKET_FILE);
  182. sess.startRead(boost::bind(&SessionTest::someHandler, this));
  183. sess.setTimeout(100);
  184. isc::data::ElementPtr env = isc::data::Element::fromJSON("{ \"to\": \"me\" }");
  185. isc::data::ElementPtr msg = isc::data::Element::fromJSON("{ \"some\": \"message\" }");
  186. tds->sendmsg(env, msg);
  187. msg = isc::data::Element::fromJSON("{ \"another\": \"message\" }");
  188. tds->sendmsg(env, msg);
  189. msg = isc::data::Element::fromJSON("{ \"a third\": \"message\" }");
  190. tds->sendmsg(env, msg);
  191. // No followup message, should time out.
  192. ASSERT_THROW(my_io_service.run(), SessionTimeout);
  193. }