session_unittests.cc 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  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. #include <config.h>
  15. // for some IPC/network system calls in asio/detail/pipe_select_interrupter.hpp
  16. #include <unistd.h>
  17. // XXX: the ASIO header must be included before others. See session.cc.
  18. #include <asio.hpp>
  19. #include <gtest/gtest.h>
  20. #include <boost/bind.hpp>
  21. #include <exceptions/exceptions.h>
  22. #include <cc/session.h>
  23. #include <cc/data.h>
  24. #include <session_unittests_config.h>
  25. using namespace isc::cc;
  26. TEST(AsioSession, establish) {
  27. asio::io_service io_service_;
  28. Session sess(io_service_);
  29. // can't return socket desciptor before session is established
  30. EXPECT_THROW(sess.getSocketDesc(), isc::InvalidOperation);
  31. EXPECT_THROW(
  32. sess.establish("/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. "/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/aaaaaaaaaa/"
  42. ), isc::cc::SessionError
  43. );
  44. }
  45. // This class sets up a domain socket for the session to connect to
  46. // it will impersonate the msgq a tiny bit (if setSendLname() has
  47. // been called, it will send an 'answer' to the lname query that is
  48. // sent in the initialization of Session objects)
  49. class TestDomainSocket {
  50. public:
  51. TestDomainSocket(asio::io_service& io_service, const char* file) :
  52. io_service_(io_service),
  53. ep_(file),
  54. acceptor_(io_service_, ep_),
  55. socket_(io_service_)
  56. {
  57. acceptor_.async_accept(socket_,
  58. boost::bind(&TestDomainSocket::acceptHandler,
  59. this, _1));
  60. }
  61. ~TestDomainSocket() {
  62. socket_.close();
  63. unlink(BIND10_TEST_SOCKET_FILE);
  64. }
  65. void
  66. acceptHandler(const asio::error_code&) const {
  67. }
  68. void
  69. sendmsg(isc::data::ElementPtr& env, isc::data::ElementPtr& msg) {
  70. const std::string header_wire = env->toWire();
  71. const std::string body_wire = msg->toWire();
  72. const unsigned int length = 2 + header_wire.length() +
  73. body_wire.length();
  74. const unsigned int length_net = htonl(length);
  75. const unsigned short header_length = header_wire.length();
  76. const unsigned short header_length_net = htons(header_length);
  77. socket_.send(asio::buffer(&length_net, sizeof(length_net)));
  78. socket_.send(asio::buffer(&header_length_net,
  79. sizeof(header_length_net)));
  80. socket_.send(asio::buffer(header_wire.data(), header_length));
  81. socket_.send(asio::buffer(body_wire.data(), body_wire.length()));
  82. }
  83. void
  84. sendLname() {
  85. isc::data::ElementPtr lname_answer1 =
  86. isc::data::Element::fromJSON("{ \"type\": \"lname\" }");
  87. isc::data::ElementPtr lname_answer2 =
  88. isc::data::Element::fromJSON("{ \"lname\": \"foobar\" }");
  89. sendmsg(lname_answer1, lname_answer2);
  90. }
  91. void
  92. setSendLname() {
  93. // ignore whatever data we get, send back an lname
  94. asio::async_read(socket_, asio::buffer(data_buf, 0),
  95. boost::bind(&TestDomainSocket::sendLname, this));
  96. }
  97. private:
  98. asio::io_service& io_service_;
  99. asio::local::stream_protocol::endpoint ep_;
  100. asio::local::stream_protocol::acceptor acceptor_;
  101. asio::local::stream_protocol::socket socket_;
  102. char data_buf[1024];
  103. };
  104. class SessionTest : public ::testing::Test {
  105. protected:
  106. SessionTest() : sess(my_io_service), work(my_io_service) {
  107. // The TestDomainSocket is held as a 'new'-ed pointer,
  108. // so we can call unlink() first.
  109. unlink(BIND10_TEST_SOCKET_FILE);
  110. tds = new TestDomainSocket(my_io_service, BIND10_TEST_SOCKET_FILE);
  111. }
  112. ~SessionTest() {
  113. delete tds;
  114. }
  115. public:
  116. // used in the handler test
  117. // This handler first reads (and ignores) whatever message caused
  118. // it to be invoked. Then it calls group_recv for a second message.
  119. // If this message is { "command": "stop" } it'll tell the
  120. // io_service it is done. Otherwise it'll re-register this handler
  121. void someHandler() {
  122. isc::data::ConstElementPtr env, msg;
  123. sess.group_recvmsg(env, msg, false, -1);
  124. sess.group_recvmsg(env, msg, false, -1);
  125. if (msg && msg->contains("command") &&
  126. msg->get("command")->stringValue() == "stop") {
  127. my_io_service.stop();
  128. } else {
  129. sess.startRead(boost::bind(&SessionTest::someHandler, this));
  130. }
  131. }
  132. protected:
  133. asio::io_service my_io_service;
  134. TestDomainSocket* tds;
  135. Session sess;
  136. // Keep run() from stopping right away by informing it it has work to do
  137. asio::io_service::work work;
  138. };
  139. TEST_F(SessionTest, timeout_on_connect) {
  140. // set to a short timeout so the test doesn't take too long
  141. EXPECT_EQ(4000, sess.getTimeout());
  142. sess.setTimeout(100);
  143. EXPECT_EQ(100, sess.getTimeout());
  144. // no answer, should timeout
  145. EXPECT_THROW(sess.establish(BIND10_TEST_SOCKET_FILE), SessionTimeout);
  146. }
  147. TEST_F(SessionTest, connect_ok) {
  148. tds->setSendLname();
  149. sess.establish(BIND10_TEST_SOCKET_FILE);
  150. }
  151. TEST_F(SessionTest, connect_ok_no_timeout) {
  152. tds->setSendLname();
  153. sess.setTimeout(0);
  154. sess.establish(BIND10_TEST_SOCKET_FILE);
  155. }
  156. TEST_F(SessionTest, connect_ok_connection_reset) {
  157. tds->setSendLname();
  158. sess.establish(BIND10_TEST_SOCKET_FILE);
  159. // Close the session again, so the next recv() should throw
  160. sess.disconnect();
  161. isc::data::ConstElementPtr env, msg;
  162. EXPECT_THROW(sess.group_recvmsg(env, msg, false, -1), SessionError);
  163. }
  164. TEST_F(SessionTest, run_with_handler) {
  165. tds->setSendLname();
  166. sess.establish(BIND10_TEST_SOCKET_FILE);
  167. sess.startRead(boost::bind(&SessionTest::someHandler, this));
  168. isc::data::ElementPtr env = isc::data::Element::fromJSON("{ \"to\": \"me\" }");
  169. isc::data::ElementPtr msg = isc::data::Element::fromJSON("{ \"some\": \"message\" }");
  170. tds->sendmsg(env, msg);
  171. msg = isc::data::Element::fromJSON("{ \"another\": \"message\" }");
  172. tds->sendmsg(env, msg);
  173. msg = isc::data::Element::fromJSON("{ \"a third\": \"message\" }");
  174. tds->sendmsg(env, msg);
  175. msg = isc::data::Element::fromJSON("{ \"command\": \"stop\" }");
  176. tds->sendmsg(env, msg);
  177. size_t count = my_io_service.run();
  178. ASSERT_EQ(2, count);
  179. }
  180. TEST_F(SessionTest, run_with_handler_timeout) {
  181. tds->setSendLname();
  182. sess.establish(BIND10_TEST_SOCKET_FILE);
  183. sess.startRead(boost::bind(&SessionTest::someHandler, this));
  184. sess.setTimeout(100);
  185. isc::data::ElementPtr env = isc::data::Element::fromJSON("{ \"to\": \"me\" }");
  186. isc::data::ElementPtr msg = isc::data::Element::fromJSON("{ \"some\": \"message\" }");
  187. tds->sendmsg(env, msg);
  188. msg = isc::data::Element::fromJSON("{ \"another\": \"message\" }");
  189. tds->sendmsg(env, msg);
  190. msg = isc::data::Element::fromJSON("{ \"a third\": \"message\" }");
  191. tds->sendmsg(env, msg);
  192. // No followup message, should time out.
  193. ASSERT_THROW(my_io_service.run(), SessionTimeout);
  194. }
  195. TEST_F(SessionTest, get_socket_descr) {
  196. tds->setSendLname();
  197. sess.establish(BIND10_TEST_SOCKET_FILE);
  198. int socket = 0;
  199. // session is established, so getSocketDesc() should work
  200. EXPECT_NO_THROW(socket = sess.getSocketDesc());
  201. // expect actual socket handle to be returned, not 0
  202. EXPECT_LT(0, socket);
  203. }