blob: 5c4f433cb839ff6e99ac447dbc6dd2f3f2dc1fb2 [file] [log] [blame]
Eric Newberrya98bf932015-09-21 00:58:47 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Junxiao Shi84d62cb2017-07-12 16:15:18 +00002/*
Davide Pesavento00335782018-02-10 22:31:33 -05003 * Copyright (c) 2014-2018, Regents of the University of California,
Eric Newberrya98bf932015-09-21 00:58:47 -07004 * Arizona Board of Regents,
5 * Colorado State University,
6 * University Pierre & Marie Curie, Sorbonne University,
7 * Washington University in St. Louis,
8 * Beijing Institute of Technology,
9 * The University of Memphis.
10 *
11 * This file is part of NFD (Named Data Networking Forwarding Daemon).
12 * See AUTHORS.md for complete list of NFD authors and contributors.
13 *
14 * NFD is free software: you can redistribute it and/or modify it under the terms
15 * of the GNU General Public License as published by the Free Software Foundation,
16 * either version 3 of the License, or (at your option) any later version.
17 *
18 * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
19 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
20 * PURPOSE. See the GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License along with
23 * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
24 */
25
Junxiao Shicde37ad2015-12-24 01:02:05 -070026#include "face/websocket-channel.hpp"
Weiwei Liu280d7dd2016-03-02 23:19:26 -070027#include "face/websocket-transport.hpp"
Junxiao Shicde37ad2015-12-24 01:02:05 -070028
Davide Pesavento9a00fab2016-09-27 11:22:46 +020029#include "channel-fixture.hpp"
Davide Pesavento22fba352017-10-17 15:53:51 -040030#include "test-ip.hpp"
Eric Newberrya98bf932015-09-21 00:58:47 -070031
32namespace nfd {
Davide Pesavento8fd15e62017-04-06 19:58:54 -040033namespace face {
Junxiao Shicde37ad2015-12-24 01:02:05 -070034namespace tests {
Eric Newberrya98bf932015-09-21 00:58:47 -070035
Weiwei Liu280d7dd2016-03-02 23:19:26 -070036namespace ip = boost::asio::ip;
Junxiao Shicde37ad2015-12-24 01:02:05 -070037
Davide Pesavento9a00fab2016-09-27 11:22:46 +020038class WebSocketChannelFixture : public ChannelFixture<WebSocketChannel, websocket::Endpoint>
Weiwei Liu280d7dd2016-03-02 23:19:26 -070039{
40protected:
Davide Pesavento8fd15e62017-04-06 19:58:54 -040041 unique_ptr<WebSocketChannel>
Davide Pesavento9a00fab2016-09-27 11:22:46 +020042 makeChannel(const ip::address& addr, uint16_t port = 0) final
Weiwei Liu280d7dd2016-03-02 23:19:26 -070043 {
44 if (port == 0)
Davide Pesavento9a00fab2016-09-27 11:22:46 +020045 port = getNextPort();
Weiwei Liu280d7dd2016-03-02 23:19:26 -070046
47 return make_unique<WebSocketChannel>(websocket::Endpoint(addr, port));
48 }
49
50 void
51 listen(const ip::address& addr,
Davide Pesavento00335782018-02-10 22:31:33 -050052 const time::milliseconds& pingInterval = 10_s,
53 const time::milliseconds& pongTimeout = 1_s)
Weiwei Liu280d7dd2016-03-02 23:19:26 -070054 {
55 listenerEp = websocket::Endpoint(addr, 20030);
56 listenerChannel = makeChannel(addr, 20030);
57 listenerChannel->setPingInterval(pingInterval);
58 listenerChannel->setPongTimeout(pongTimeout);
59 listenerChannel->listen(bind(&WebSocketChannelFixture::listenerOnFaceCreated, this, _1));
60 }
61
62 void
63 clientConnect(websocket::Client& client)
64 {
65 client.clear_access_channels(websocketpp::log::alevel::all);
66 client.clear_error_channels(websocketpp::log::elevel::all);
67
68 client.init_asio(&g_io);
69 client.set_open_handler(bind(&WebSocketChannelFixture::clientHandleOpen, this, _1));
70 client.set_message_handler(bind(&WebSocketChannelFixture::clientHandleMessage, this, _1, _2));
71 client.set_ping_handler(bind(&WebSocketChannelFixture::clientHandlePing, this, _1, _2));
72
73 std::string uri = "ws://" + listenerEp.address().to_string() + ":" + to_string(listenerEp.port());
74 websocketpp::lib::error_code ec;
Davide Pesavento9a00fab2016-09-27 11:22:46 +020075 auto con = client.get_connection(uri, ec);
Weiwei Liu280d7dd2016-03-02 23:19:26 -070076 BOOST_REQUIRE_EQUAL(ec, websocketpp::lib::error_code());
77
78 client.connect(con);
79 }
80
81 void
82 initialize(const ip::address& addr,
Davide Pesavento00335782018-02-10 22:31:33 -050083 const time::milliseconds& pingInterval = 10_s,
84 const time::milliseconds& pongTimeout = 1_s)
Weiwei Liu280d7dd2016-03-02 23:19:26 -070085 {
86 listen(addr, pingInterval, pongTimeout);
87 clientConnect(client);
88 BOOST_REQUIRE_EQUAL(limitedIo.run(2, // listenerOnFaceCreated, clientHandleOpen
Davide Pesavento00335782018-02-10 22:31:33 -050089 1_s), LimitedIo::EXCEED_OPS);
Weiwei Liu280d7dd2016-03-02 23:19:26 -070090 BOOST_REQUIRE_EQUAL(listenerChannel->size(), 1);
91 }
92
93 void
94 clientSendInterest(const Interest& interest)
95 {
96 const Block& payload = interest.wireEncode();
97 client.send(clientHandle, payload.wire(), payload.size(), websocketpp::frame::opcode::binary);
98 }
99
100private:
101 void
102 listenerOnFaceCreated(const shared_ptr<Face>& newFace)
103 {
104 BOOST_REQUIRE(newFace != nullptr);
105 newFace->afterReceiveInterest.connect(bind(&WebSocketChannelFixture::faceAfterReceiveInterest, this, _1));
106 connectFaceClosedSignal(*newFace, [this] { limitedIo.afterOp(); });
107 listenerFaces.push_back(newFace);
108 limitedIo.afterOp();
109 }
110
111 void
112 faceAfterReceiveInterest(const Interest& interest)
113 {
114 faceReceivedInterests.push_back(interest);
115 limitedIo.afterOp();
116 }
117
118 void
119 clientHandleOpen(websocketpp::connection_hdl hdl)
120 {
121 clientHandle = hdl;
122 limitedIo.afterOp();
123 }
124
125 void
126 clientHandleMessage(websocketpp::connection_hdl, websocket::Client::message_ptr msg)
127 {
128 clientReceivedMessages.push_back(msg->get_payload());
129 limitedIo.afterOp();
130 }
131
132 bool
133 clientHandlePing(websocketpp::connection_hdl, std::string)
134 {
135 auto now = time::steady_clock::now();
136 if (m_prevPingRecvTime != time::steady_clock::TimePoint()) {
137 measuredPingInterval = now - m_prevPingRecvTime;
138 }
139 m_prevPingRecvTime = now;
140
141 limitedIo.afterOp();
142 return clientShouldPong;
143 }
144
145protected:
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700146 std::vector<Interest> faceReceivedInterests;
147
148 websocket::Client client;
149 websocketpp::connection_hdl clientHandle;
150 std::vector<std::string> clientReceivedMessages;
Davide Pesavento9a00fab2016-09-27 11:22:46 +0200151
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700152 time::steady_clock::Duration measuredPingInterval;
Davide Pesavento00335782018-02-10 22:31:33 -0500153 // set clientShouldPong to false to disable the pong response,
154 // which will eventually cause a timeout in listenerChannel
155 bool clientShouldPong = true;
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700156
157private:
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700158 time::steady_clock::TimePoint m_prevPingRecvTime;
159};
160
Davide Pesavento9a00fab2016-09-27 11:22:46 +0200161BOOST_AUTO_TEST_SUITE(Face)
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700162BOOST_FIXTURE_TEST_SUITE(TestWebSocketChannel, WebSocketChannelFixture)
163
164BOOST_AUTO_TEST_CASE(Uri)
165{
166 websocket::Endpoint ep(ip::address_v4::loopback(), 20070);
167 auto channel = makeChannel(ep.address(), ep.port());
Davide Pesaventoeee53aa2016-04-11 17:20:21 +0200168 BOOST_CHECK_EQUAL(channel->getUri(), FaceUri(ep, "ws"));
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700169}
170
171BOOST_AUTO_TEST_CASE(Listen)
172{
173 auto channel = makeChannel(ip::address_v4());
174 BOOST_CHECK_EQUAL(channel->isListening(), false);
175
176 channel->listen(nullptr);
177 BOOST_CHECK_EQUAL(channel->isListening(), true);
178
179 // listen() is idempotent
180 BOOST_CHECK_NO_THROW(channel->listen(nullptr));
181 BOOST_CHECK_EQUAL(channel->isListening(), true);
182}
183
184BOOST_AUTO_TEST_CASE(MultipleAccepts)
185{
Davide Pesaventob8d1fc72017-10-08 02:05:05 -0400186 auto address = getTestIp(AddressFamily::V4, AddressScope::Loopback);
Davide Pesaventoeee53aa2016-04-11 17:20:21 +0200187 SKIP_IF_IP_UNAVAILABLE(address);
188 this->listen(address);
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700189
190 BOOST_CHECK_EQUAL(listenerChannel->isListening(), true);
191 BOOST_CHECK_EQUAL(listenerChannel->size(), 0);
192
193 websocket::Client client1;
Davide Pesaventoeee53aa2016-04-11 17:20:21 +0200194 this->clientConnect(client1);
195
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700196 BOOST_CHECK_EQUAL(limitedIo.run(2, // listenerOnFaceCreated, clientHandleOpen
Davide Pesavento00335782018-02-10 22:31:33 -0500197 1_s), LimitedIo::EXCEED_OPS);
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700198 BOOST_CHECK_EQUAL(listenerChannel->size(), 1);
199
200 websocket::Client client2;
201 websocket::Client client3;
Davide Pesaventoeee53aa2016-04-11 17:20:21 +0200202 this->clientConnect(client2);
203 this->clientConnect(client3);
204
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700205 BOOST_CHECK_EQUAL(limitedIo.run(4, // 2 listenerOnFaceCreated, 2 clientHandleOpen
Davide Pesavento00335782018-02-10 22:31:33 -0500206 2_s), LimitedIo::EXCEED_OPS);
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700207 BOOST_CHECK_EQUAL(listenerChannel->size(), 3);
208
209 // check face persistency
210 for (const auto& face : listenerFaces) {
211 BOOST_CHECK_EQUAL(face->getPersistency(), ndn::nfd::FACE_PERSISTENCY_ON_DEMAND);
212 }
213}
214
215BOOST_AUTO_TEST_CASE(Send)
216{
Davide Pesaventob8d1fc72017-10-08 02:05:05 -0400217 auto address = getTestIp(AddressFamily::V4, AddressScope::Loopback);
Davide Pesaventoeee53aa2016-04-11 17:20:21 +0200218 SKIP_IF_IP_UNAVAILABLE(address);
219 this->initialize(address);
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700220 auto transport = listenerFaces.front()->getTransport();
221
222 Block pkt1 = ndn::encoding::makeStringBlock(300, "hello");
Davide Pesavento00335782018-02-10 22:31:33 -0500223 transport->send(Transport::Packet(Block(pkt1)));
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700224 BOOST_CHECK_EQUAL(limitedIo.run(1, // clientHandleMessage
Davide Pesavento00335782018-02-10 22:31:33 -0500225 1_s), LimitedIo::EXCEED_OPS);
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700226
227 Block pkt2 = ndn::encoding::makeStringBlock(301, "world!");
Davide Pesavento00335782018-02-10 22:31:33 -0500228 transport->send(Transport::Packet(Block(pkt2)));
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700229 BOOST_CHECK_EQUAL(limitedIo.run(1, // clientHandleMessage
Davide Pesavento00335782018-02-10 22:31:33 -0500230 1_s), LimitedIo::EXCEED_OPS);
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700231
232 BOOST_REQUIRE_EQUAL(clientReceivedMessages.size(), 2);
233 BOOST_CHECK_EQUAL_COLLECTIONS(
234 reinterpret_cast<const uint8_t*>(clientReceivedMessages[0].data()),
235 reinterpret_cast<const uint8_t*>(clientReceivedMessages[0].data()) + clientReceivedMessages[0].size(),
236 pkt1.begin(), pkt1.end());
237 BOOST_CHECK_EQUAL_COLLECTIONS(
238 reinterpret_cast<const uint8_t*>(clientReceivedMessages[1].data()),
239 reinterpret_cast<const uint8_t*>(clientReceivedMessages[1].data()) + clientReceivedMessages[1].size(),
240 pkt2.begin(), pkt2.end());
241}
242
243BOOST_AUTO_TEST_CASE(Receive)
244{
Davide Pesaventob8d1fc72017-10-08 02:05:05 -0400245 auto address = getTestIp(AddressFamily::V4, AddressScope::Loopback);
Davide Pesaventoeee53aa2016-04-11 17:20:21 +0200246 SKIP_IF_IP_UNAVAILABLE(address);
247 this->initialize(address);
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700248
249 // use network-layer packets here, otherwise GenericLinkService
250 // won't recognize the packet type and will discard it
251 auto interest1 = makeInterest("ndn:/TpnzGvW9R");
252 auto interest2 = makeInterest("ndn:/QWiIMfj5sL");
253
254 clientSendInterest(*interest1);
255 BOOST_CHECK_EQUAL(limitedIo.run(1, // faceAfterReceiveInterest
Davide Pesavento00335782018-02-10 22:31:33 -0500256 1_s), LimitedIo::EXCEED_OPS);
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700257
258 clientSendInterest(*interest2);
259 BOOST_CHECK_EQUAL(limitedIo.run(1, // faceAfterReceiveInterest
Davide Pesavento00335782018-02-10 22:31:33 -0500260 1_s), LimitedIo::EXCEED_OPS);
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700261
262 BOOST_REQUIRE_EQUAL(faceReceivedInterests.size(), 2);
263 BOOST_CHECK_EQUAL(faceReceivedInterests[0].getName(), interest1->getName());
264 BOOST_CHECK_EQUAL(faceReceivedInterests[1].getName(), interest2->getName());
265}
266
267BOOST_AUTO_TEST_CASE(FaceClosure)
268{
Davide Pesaventob8d1fc72017-10-08 02:05:05 -0400269 auto address = getTestIp(AddressFamily::V4, AddressScope::Loopback);
Davide Pesaventoeee53aa2016-04-11 17:20:21 +0200270 SKIP_IF_IP_UNAVAILABLE(address);
271 this->initialize(address);
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700272
273 listenerFaces.front()->close();
274 BOOST_CHECK_EQUAL(listenerChannel->size(), 0);
275}
276
277BOOST_AUTO_TEST_CASE(RemoteClose)
278{
Davide Pesaventob8d1fc72017-10-08 02:05:05 -0400279 auto address = getTestIp(AddressFamily::V4, AddressScope::Loopback);
Davide Pesaventoeee53aa2016-04-11 17:20:21 +0200280 SKIP_IF_IP_UNAVAILABLE(address);
281 this->initialize(address);
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700282
283 client.close(clientHandle, websocketpp::close::status::going_away, "");
284 BOOST_CHECK_EQUAL(limitedIo.run(1, // faceClosedSignal
Davide Pesavento00335782018-02-10 22:31:33 -0500285 1_s), LimitedIo::EXCEED_OPS);
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700286 BOOST_CHECK_EQUAL(listenerChannel->size(), 0);
287}
288
289BOOST_AUTO_TEST_CASE(SetPingInterval)
290{
Davide Pesaventob8d1fc72017-10-08 02:05:05 -0400291 auto address = getTestIp(AddressFamily::V4, AddressScope::Loopback);
Davide Pesaventoeee53aa2016-04-11 17:20:21 +0200292 SKIP_IF_IP_UNAVAILABLE(address);
Davide Pesavento00335782018-02-10 22:31:33 -0500293 const auto pingInterval = 1200_ms;
294 this->initialize(address, pingInterval);
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700295
296 BOOST_CHECK_EQUAL(limitedIo.run(2, // clientHandlePing
Davide Pesavento00335782018-02-10 22:31:33 -0500297 pingInterval * 3), LimitedIo::EXCEED_OPS);
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700298 BOOST_CHECK_LE(measuredPingInterval, pingInterval * 1.1);
299 BOOST_CHECK_GE(measuredPingInterval, pingInterval * 0.9);
300}
301
302BOOST_AUTO_TEST_CASE(SetPongTimeOut)
303{
Davide Pesaventob8d1fc72017-10-08 02:05:05 -0400304 auto address = getTestIp(AddressFamily::V4, AddressScope::Loopback);
Davide Pesaventoeee53aa2016-04-11 17:20:21 +0200305 SKIP_IF_IP_UNAVAILABLE(address);
Davide Pesavento00335782018-02-10 22:31:33 -0500306 this->initialize(address, 600_ms, 300_ms);
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700307 clientShouldPong = false;
Davide Pesaventoeee53aa2016-04-11 17:20:21 +0200308
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700309 BOOST_CHECK_EQUAL(limitedIo.run(2, // clientHandlePing, faceClosedSignal
Davide Pesavento00335782018-02-10 22:31:33 -0500310 2_s), LimitedIo::EXCEED_OPS);
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700311 BOOST_CHECK_EQUAL(listenerChannel->size(), 0);
312
Davide Pesavento00335782018-02-10 22:31:33 -0500313 auto transport = static_cast<WebSocketTransport*>(listenerFaces.front()->getTransport());
314 BOOST_CHECK(transport->getState() == TransportState::FAILED ||
315 transport->getState() == TransportState::CLOSED);
316 BOOST_CHECK_GE(transport->getCounters().nOutPings, 1);
317 BOOST_CHECK_LE(transport->getCounters().nOutPings, 2);
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700318 BOOST_CHECK_EQUAL(transport->getCounters().nInPongs, 0);
319}
Junxiao Shicde37ad2015-12-24 01:02:05 -0700320
321BOOST_AUTO_TEST_SUITE_END() // TestWebSocketChannel
322BOOST_AUTO_TEST_SUITE_END() // Face
323
324} // namespace tests
Davide Pesavento8fd15e62017-04-06 19:58:54 -0400325} // namespace face
Eric Newberrya98bf932015-09-21 00:58:47 -0700326} // namespace nfd