blob: 8dbd7175b3dfbb6d6d549cebeb47be2005156a96 [file] [log] [blame]
Yukai Tu2d6d5632015-10-26 11:06:02 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Weiwei Liu93606232016-02-26 16:32:11 -07003 * Copyright (c) 2014-2016, Regents of the University of California,
Yukai Tu2d6d5632015-10-26 11:06:02 -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
26#include "face/websocket-transport.hpp"
Junxiao Shicde37ad2015-12-24 01:02:05 -070027#include "face/face.hpp"
Yukai Tu2d6d5632015-10-26 11:06:02 -070028
Davide Pesaventoeee53aa2016-04-11 17:20:21 +020029#include "dummy-receive-link-service.hpp"
30#include "test-ip.hpp"
31#include "transport-test-common.hpp"
Yukai Tu2d6d5632015-10-26 11:06:02 -070032#include "tests/limited-io.hpp"
33
34namespace nfd {
35namespace face {
36namespace tests {
37
38using namespace nfd::tests;
39namespace ip = boost::asio::ip;
40
41BOOST_AUTO_TEST_SUITE(Face)
42
Junxiao Shicde37ad2015-12-24 01:02:05 -070043using nfd::Face;
44
Yukai Tu2d6d5632015-10-26 11:06:02 -070045/** \brief a fixture that accepts a single WebSocket connection from a client
46 */
47class SingleWebSocketFixture : public BaseFixture
48{
49public:
50 SingleWebSocketFixture()
51 : transport(nullptr)
52 , serverReceivedPackets(nullptr)
53 , clientShouldPong(true)
54 {
55 }
56
57 /** \brief initialize server and start listening
58 */
59 void
60 serverListen(const ip::tcp::endpoint& ep,
Weiwei Liu93606232016-02-26 16:32:11 -070061 const time::milliseconds& pongTimeout = time::seconds(1))
Yukai Tu2d6d5632015-10-26 11:06:02 -070062 {
63 server.clear_access_channels(websocketpp::log::alevel::all);
64 server.clear_error_channels(websocketpp::log::elevel::all);
65
66 server.init_asio(&g_io);
67 server.set_open_handler(bind(&SingleWebSocketFixture::serverHandleOpen, this, _1));
68 server.set_close_handler(bind(&SingleWebSocketFixture::serverHandleClose, this));
69 server.set_message_handler(bind(&SingleWebSocketFixture::serverHandleMessage, this, _2));
70 server.set_pong_handler(bind(&SingleWebSocketFixture::serverHandlePong, this));
71 server.set_pong_timeout_handler(bind(&SingleWebSocketFixture::serverHandlePongTimeout, this));
72 server.set_pong_timeout(pongTimeout.count());
73
74 server.set_reuse_addr(true);
75
76 server.listen(ep);
77 server.start_accept();
78 }
79
80 /** \brief initialize client and connect to server
81 */
82 void
83 clientConnect(const std::string& uri)
84 {
85 client.clear_access_channels(websocketpp::log::alevel::all);
86 client.clear_error_channels(websocketpp::log::elevel::all);
87
88 client.init_asio(&g_io);
89 client.set_open_handler(bind(&SingleWebSocketFixture::clientHandleOpen, this, _1));
90 client.set_message_handler(bind(&SingleWebSocketFixture::clientHandleMessage, this, _2));
91 client.set_ping_handler(bind(&SingleWebSocketFixture::clientHandlePing, this));
92
93 websocketpp::lib::error_code ec;
Weiwei Liu93606232016-02-26 16:32:11 -070094 websocket::Client::connection_ptr con = client.get_connection(uri, ec);
Davide Pesaventoeee53aa2016-04-11 17:20:21 +020095 BOOST_REQUIRE_EQUAL(ec, websocketpp::lib::error_code());
Yukai Tu2d6d5632015-10-26 11:06:02 -070096
97 client.connect(con);
98 }
99
100 void
Weiwei Liu93606232016-02-26 16:32:11 -0700101 makeFace(const time::milliseconds& pingInterval = time::seconds(10))
Yukai Tu2d6d5632015-10-26 11:06:02 -0700102 {
Junxiao Shicde37ad2015-12-24 01:02:05 -0700103 face = make_unique<Face>(
Yukai Tu2d6d5632015-10-26 11:06:02 -0700104 make_unique<DummyReceiveLinkService>(),
105 make_unique<WebSocketTransport>(serverHdl, ref(server), pingInterval));
106 transport = static_cast<WebSocketTransport*>(face->getTransport());
107 serverReceivedPackets = &static_cast<DummyReceiveLinkService*>(face->getLinkService())->receivedPackets;
108 }
109
110 /** \brief initialize both server and client, and have each other connected, create Transport
111 */
112 void
113 endToEndInitialize(const ip::tcp::endpoint& ep,
Weiwei Liu93606232016-02-26 16:32:11 -0700114 const time::milliseconds& pingInterval = time::seconds(10),
115 const time::milliseconds& pongTimeout = time::seconds(1))
Yukai Tu2d6d5632015-10-26 11:06:02 -0700116 {
117 this->serverListen(ep, pongTimeout);
118 std::string uri = "ws://" + ep.address().to_string() + ":" + to_string(ep.port());
119 this->clientConnect(uri);
120 BOOST_REQUIRE_EQUAL(limitedIo.run(2, // serverHandleOpen, clientHandleOpen
121 time::seconds(1)), LimitedIo::EXCEED_OPS);
122 this->makeFace(pingInterval);
123 }
124
125private:
126 void
127 serverHandleOpen(websocketpp::connection_hdl hdl)
128 {
129 serverHdl = hdl;
130 limitedIo.afterOp();
131 }
132
133 void
134 serverHandleClose()
135 {
136 if (transport == nullptr) {
137 return;
138 }
139
140 transport->close();
141 limitedIo.afterOp();
142 }
143
144 void
145 serverHandleMessage(websocket::Server::message_ptr msg)
146 {
147 if (transport == nullptr) {
148 return;
149 }
150
151 transport->receiveMessage(msg->get_payload());
152 limitedIo.afterOp();
153 }
154
155 void
156 serverHandlePong()
157 {
158 if (transport == nullptr) {
159 return;
160 }
161
162 transport->handlePong();
163 limitedIo.afterOp();
164 }
165
166 void
167 serverHandlePongTimeout()
168 {
169 if (transport == nullptr) {
170 return;
171 }
172
173 transport->handlePongTimeout();
174 limitedIo.afterOp();
175 }
176
177 void
178 clientHandleOpen(websocketpp::connection_hdl hdl)
179 {
180 clientHdl = hdl;
181 limitedIo.afterOp();
182 }
183
184 void
185 clientHandleMessage(websocket::Client::message_ptr msg)
186 {
187 clientReceivedMessages.push_back(msg->get_payload());
188 limitedIo.afterOp();
189 }
190
191 bool
192 clientHandlePing()
193 {
194 limitedIo.afterOp();
195 return clientShouldPong;
196 }
197
198public:
199 LimitedIo limitedIo;
200
201 websocket::Server server;
202 websocketpp::connection_hdl serverHdl;
Junxiao Shicde37ad2015-12-24 01:02:05 -0700203 unique_ptr<Face> face;
Yukai Tu2d6d5632015-10-26 11:06:02 -0700204 WebSocketTransport* transport;
205 std::vector<Transport::Packet>* serverReceivedPackets;
206
207 websocket::Client client;
208 websocketpp::connection_hdl clientHdl;
209 bool clientShouldPong;
210 std::vector<std::string> clientReceivedMessages;
211};
212
213BOOST_FIXTURE_TEST_SUITE(TestWebSocketTransport, SingleWebSocketFixture)
214
Weiwei Liu93606232016-02-26 16:32:11 -0700215BOOST_AUTO_TEST_CASE(StaticPropertiesLocalIpv4)
Yukai Tu2d6d5632015-10-26 11:06:02 -0700216{
Davide Pesaventoeee53aa2016-04-11 17:20:21 +0200217 auto address = getTestIp<ip::address_v4>(LoopbackAddress::Yes);
218 SKIP_IF_IP_UNAVAILABLE(address);
219 this->endToEndInitialize(ip::tcp::endpoint(address, 20070));
220
Yukai Tu2d6d5632015-10-26 11:06:02 -0700221 checkStaticPropertiesInitialized(*transport);
222
223 BOOST_CHECK_EQUAL(transport->getLocalUri(), FaceUri("ws://127.0.0.1:20070"));
224 BOOST_CHECK_EQUAL(transport->getRemoteUri().getScheme(), "wsclient");
225 BOOST_CHECK_EQUAL(transport->getRemoteUri().getHost(), "127.0.0.1");
226 BOOST_CHECK_EQUAL(transport->getRemoteUri().getPath(), "");
227 BOOST_CHECK_EQUAL(transport->getScope(), ndn::nfd::FACE_SCOPE_LOCAL);
228 BOOST_CHECK_EQUAL(transport->getPersistency(), ndn::nfd::FACE_PERSISTENCY_ON_DEMAND);
229 BOOST_CHECK_EQUAL(transport->getLinkType(), ndn::nfd::LINK_TYPE_POINT_TO_POINT);
230 BOOST_CHECK_EQUAL(transport->getMtu(), MTU_UNLIMITED);
231}
232
Weiwei Liu93606232016-02-26 16:32:11 -0700233BOOST_AUTO_TEST_CASE(StaticPropertiesNonLocalIpv4)
234{
Davide Pesaventoeee53aa2016-04-11 17:20:21 +0200235 auto address = getTestIp<ip::address_v4>(LoopbackAddress::No);
Weiwei Liu93606232016-02-26 16:32:11 -0700236 SKIP_IF_IP_UNAVAILABLE(address);
Davide Pesaventoeee53aa2016-04-11 17:20:21 +0200237 this->endToEndInitialize(ip::tcp::endpoint(address, 20070));
Weiwei Liu93606232016-02-26 16:32:11 -0700238
Weiwei Liu93606232016-02-26 16:32:11 -0700239 checkStaticPropertiesInitialized(*transport);
240
241 BOOST_CHECK_EQUAL(transport->getLocalUri(), FaceUri("ws://" + address.to_string() + ":20070"));
242 BOOST_CHECK_EQUAL(transport->getRemoteUri().getScheme(), "wsclient");
243 BOOST_CHECK_EQUAL(transport->getRemoteUri().getHost(), address.to_string());
244 BOOST_CHECK_EQUAL(transport->getRemoteUri().getPath(), "");
245 BOOST_CHECK_EQUAL(transport->getScope(), ndn::nfd::FACE_SCOPE_NON_LOCAL);
246 BOOST_CHECK_EQUAL(transport->getPersistency(), ndn::nfd::FACE_PERSISTENCY_ON_DEMAND);
247 BOOST_CHECK_EQUAL(transport->getLinkType(), ndn::nfd::LINK_TYPE_POINT_TO_POINT);
248 BOOST_CHECK_EQUAL(transport->getMtu(), MTU_UNLIMITED);
249}
250
Yukai Tu2d6d5632015-10-26 11:06:02 -0700251BOOST_AUTO_TEST_CASE(PingPong)
252{
Davide Pesaventoeee53aa2016-04-11 17:20:21 +0200253 auto address = getTestIp<ip::address_v4>();
254 SKIP_IF_IP_UNAVAILABLE(address);
255 this->endToEndInitialize(ip::tcp::endpoint(address, 20070),
256 time::milliseconds(500), time::milliseconds(300));
Yukai Tu2d6d5632015-10-26 11:06:02 -0700257
258 BOOST_CHECK_EQUAL(limitedIo.run(2, // clientHandlePing, serverHandlePong
259 time::milliseconds(1500)), LimitedIo::EXCEED_OPS);
Weiwei Liu93606232016-02-26 16:32:11 -0700260
Yukai Tu2d6d5632015-10-26 11:06:02 -0700261 BOOST_CHECK_EQUAL(transport->getState(), TransportState::UP);
Junxiao Shi57df2882015-11-11 06:12:35 -0700262 BOOST_CHECK_EQUAL(transport->getCounters().nOutPings, 1);
263 BOOST_CHECK_EQUAL(transport->getCounters().nInPongs, 1);
Yukai Tu2d6d5632015-10-26 11:06:02 -0700264
265 this->clientShouldPong = false;
266 BOOST_CHECK_EQUAL(limitedIo.run(2, // clientHandlePing, serverHandlePongTimeout
Weiwei Liu93606232016-02-26 16:32:11 -0700267 time::seconds(2)), LimitedIo::EXCEED_OPS);
268
Yukai Tu2d6d5632015-10-26 11:06:02 -0700269 BOOST_CHECK_MESSAGE(transport->getState() == TransportState::FAILED ||
270 transport->getState() == TransportState::CLOSED,
271 "expect FAILED or CLOSED state, actual state=" << transport->getState());
Junxiao Shi57df2882015-11-11 06:12:35 -0700272 BOOST_CHECK_EQUAL(transport->getCounters().nOutPings, 2);
273 BOOST_CHECK_EQUAL(transport->getCounters().nInPongs, 1);
Yukai Tu2d6d5632015-10-26 11:06:02 -0700274}
275
276BOOST_AUTO_TEST_CASE(Send)
277{
Davide Pesaventoeee53aa2016-04-11 17:20:21 +0200278 auto address = getTestIp<ip::address_v4>();
279 SKIP_IF_IP_UNAVAILABLE(address);
280 this->endToEndInitialize(ip::tcp::endpoint(address, 20070));
Yukai Tu2d6d5632015-10-26 11:06:02 -0700281
282 Block pkt1 = ndn::encoding::makeStringBlock(300, "hello");
283 transport->send(Transport::Packet(Block(pkt1)));
284 BOOST_CHECK_EQUAL(limitedIo.run(1, // clientHandleMessage
Weiwei Liu93606232016-02-26 16:32:11 -0700285 time::seconds(1)), LimitedIo::EXCEED_OPS);
Yukai Tu2d6d5632015-10-26 11:06:02 -0700286
287 Block pkt2 = ndn::encoding::makeStringBlock(301, "world!");
288 transport->send(Transport::Packet(Block(pkt2)));
289 BOOST_CHECK_EQUAL(limitedIo.run(1, // clientHandleMessage
Weiwei Liu93606232016-02-26 16:32:11 -0700290 time::seconds(1)), LimitedIo::EXCEED_OPS);
Yukai Tu2d6d5632015-10-26 11:06:02 -0700291
292 BOOST_REQUIRE_EQUAL(clientReceivedMessages.size(), 2);
293 BOOST_CHECK_EQUAL_COLLECTIONS(
294 reinterpret_cast<const uint8_t*>(clientReceivedMessages[0].data()),
295 reinterpret_cast<const uint8_t*>(clientReceivedMessages[0].data()) + clientReceivedMessages[0].size(),
296 pkt1.begin(), pkt1.end());
297 BOOST_CHECK_EQUAL_COLLECTIONS(
298 reinterpret_cast<const uint8_t*>(clientReceivedMessages[1].data()),
299 reinterpret_cast<const uint8_t*>(clientReceivedMessages[1].data()) + clientReceivedMessages[1].size(),
300 pkt2.begin(), pkt2.end());
301}
302
Weiwei Liu93606232016-02-26 16:32:11 -0700303BOOST_AUTO_TEST_CASE(ReceiveNormal)
Yukai Tu2d6d5632015-10-26 11:06:02 -0700304{
Davide Pesaventoeee53aa2016-04-11 17:20:21 +0200305 auto address = getTestIp<ip::address_v4>();
306 SKIP_IF_IP_UNAVAILABLE(address);
307 this->endToEndInitialize(ip::tcp::endpoint(address, 20070));
Yukai Tu2d6d5632015-10-26 11:06:02 -0700308
309 Block pkt1 = ndn::encoding::makeStringBlock(300, "hello");
310 client.send(clientHdl, pkt1.wire(), pkt1.size(), websocketpp::frame::opcode::binary);
311 BOOST_CHECK_EQUAL(limitedIo.run(1, // serverHandleMessage
Weiwei Liu93606232016-02-26 16:32:11 -0700312 time::seconds(1)), LimitedIo::EXCEED_OPS);
Yukai Tu2d6d5632015-10-26 11:06:02 -0700313
314 Block pkt2 = ndn::encoding::makeStringBlock(301, "world!");
315 client.send(clientHdl, pkt2.wire(), pkt2.size(), websocketpp::frame::opcode::binary);
316 BOOST_CHECK_EQUAL(limitedIo.run(1, // serverHandleMessage
Weiwei Liu93606232016-02-26 16:32:11 -0700317 time::seconds(1)), LimitedIo::EXCEED_OPS);
Yukai Tu2d6d5632015-10-26 11:06:02 -0700318
319 BOOST_REQUIRE_EQUAL(serverReceivedPackets->size(), 2);
320 BOOST_CHECK(serverReceivedPackets->at(0).packet == pkt1);
321 BOOST_CHECK(serverReceivedPackets->at(1).packet == pkt2);
322 BOOST_CHECK_EQUAL(serverReceivedPackets->at(0).remoteEndpoint, serverReceivedPackets->at(1).remoteEndpoint);
323}
324
325BOOST_AUTO_TEST_CASE(ReceiveMalformed)
326{
Davide Pesaventoeee53aa2016-04-11 17:20:21 +0200327 auto address = getTestIp<ip::address_v4>();
328 SKIP_IF_IP_UNAVAILABLE(address);
329 this->endToEndInitialize(ip::tcp::endpoint(address, 20070));
Yukai Tu2d6d5632015-10-26 11:06:02 -0700330
331 Block pkt1 = ndn::encoding::makeStringBlock(300, "hello");
332 client.send(clientHdl, pkt1.wire(), pkt1.size() - 1, // truncated
333 websocketpp::frame::opcode::binary);
334 BOOST_CHECK_EQUAL(limitedIo.run(1, // serverHandleMessage
Weiwei Liu93606232016-02-26 16:32:11 -0700335 time::seconds(1)), LimitedIo::EXCEED_OPS);
Yukai Tu2d6d5632015-10-26 11:06:02 -0700336
337 // bad packet is dropped
338 BOOST_CHECK_EQUAL(transport->getState(), TransportState::UP);
339 BOOST_CHECK_EQUAL(serverReceivedPackets->size(), 0);
340
341 Block pkt2 = ndn::encoding::makeStringBlock(301, "world!");
342 client.send(clientHdl, pkt2.wire(), pkt2.size(), websocketpp::frame::opcode::binary);
343 BOOST_CHECK_EQUAL(limitedIo.run(1, // serverHandleMessage
Weiwei Liu93606232016-02-26 16:32:11 -0700344 time::seconds(1)), LimitedIo::EXCEED_OPS);
Yukai Tu2d6d5632015-10-26 11:06:02 -0700345
346 // next valid packet is still received normally
347 BOOST_REQUIRE_EQUAL(serverReceivedPackets->size(), 1);
348 BOOST_CHECK(serverReceivedPackets->at(0).packet == pkt2);
349}
350
Weiwei Liu93606232016-02-26 16:32:11 -0700351BOOST_AUTO_TEST_CASE(Close)
352{
Davide Pesaventoeee53aa2016-04-11 17:20:21 +0200353 auto address = getTestIp<ip::address_v4>();
354 SKIP_IF_IP_UNAVAILABLE(address);
355 this->endToEndInitialize(ip::tcp::endpoint(address, 20070));
Weiwei Liu93606232016-02-26 16:32:11 -0700356
357 int nStateChanges = 0;
358 transport->afterStateChange.connect(
359 [&nStateChanges] (TransportState oldState, TransportState newState) {
360 switch (nStateChanges) {
361 case 0:
362 BOOST_CHECK_EQUAL(oldState, TransportState::UP);
363 BOOST_CHECK_EQUAL(newState, TransportState::CLOSING);
364 break;
365 case 1:
366 BOOST_CHECK_EQUAL(oldState, TransportState::CLOSING);
367 BOOST_CHECK_EQUAL(newState, TransportState::CLOSED);
368 break;
369 default:
370 BOOST_CHECK(false);
371 }
372 nStateChanges++;
373 });
374
375 transport->close();
376 BOOST_CHECK_EQUAL(nStateChanges, 2);
377}
378
379BOOST_AUTO_TEST_CASE(RemoteClose)
380{
Davide Pesaventoeee53aa2016-04-11 17:20:21 +0200381 auto address = getTestIp<ip::address_v4>();
382 SKIP_IF_IP_UNAVAILABLE(address);
383 this->endToEndInitialize(ip::tcp::endpoint(address, 20070));
Weiwei Liu93606232016-02-26 16:32:11 -0700384
385 int nStateChanges = 0;
386 transport->afterStateChange.connect(
387 [&nStateChanges] (TransportState oldState, TransportState newState) {
388 switch (nStateChanges) {
389 case 0:
390 BOOST_CHECK_EQUAL(oldState, TransportState::UP);
391 BOOST_CHECK_EQUAL(newState, TransportState::CLOSING);
392 break;
393 case 1:
394 BOOST_CHECK_EQUAL(oldState, TransportState::CLOSING);
395 BOOST_CHECK_EQUAL(newState, TransportState::CLOSED);
396 break;
397 default:
398 BOOST_CHECK(false);
399 }
400 nStateChanges++;
401 });
402
403 client.close(clientHdl, websocketpp::close::status::going_away, "");
404 BOOST_CHECK_EQUAL(limitedIo.run(1, // serverHandleClose
405 time::seconds(1)), LimitedIo::EXCEED_OPS);
406
407 BOOST_CHECK_EQUAL(nStateChanges, 2);
408}
409
Yukai Tu2d6d5632015-10-26 11:06:02 -0700410BOOST_AUTO_TEST_SUITE_END() // TestWebSocketTransport
411BOOST_AUTO_TEST_SUITE_END() // Face
412
413} // namespace tests
414} // namespace face
415} // namespace nfd