blob: 6a028ada67a056f08f3f67029bff253f0c327f1e [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#include "dummy-receive-link-service.hpp"
Weiwei Liu93606232016-02-26 16:32:11 -070029#include "get-available-interface-ip.hpp"
Eric Newberry8717e872015-11-23 12:41:50 -070030#include "transport-test-common.hpp"
Yukai Tu2d6d5632015-10-26 11:06:02 -070031
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);
Yukai Tu2d6d5632015-10-26 11:06:02 -070095 BOOST_REQUIRE(!ec);
96
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{
217 ip::tcp::endpoint ep(ip::address_v4::loopback(), 20070);
218 this->endToEndInitialize(ep);
219 checkStaticPropertiesInitialized(*transport);
220
221 BOOST_CHECK_EQUAL(transport->getLocalUri(), FaceUri("ws://127.0.0.1:20070"));
222 BOOST_CHECK_EQUAL(transport->getRemoteUri().getScheme(), "wsclient");
223 BOOST_CHECK_EQUAL(transport->getRemoteUri().getHost(), "127.0.0.1");
224 BOOST_CHECK_EQUAL(transport->getRemoteUri().getPath(), "");
225 BOOST_CHECK_EQUAL(transport->getScope(), ndn::nfd::FACE_SCOPE_LOCAL);
226 BOOST_CHECK_EQUAL(transport->getPersistency(), ndn::nfd::FACE_PERSISTENCY_ON_DEMAND);
227 BOOST_CHECK_EQUAL(transport->getLinkType(), ndn::nfd::LINK_TYPE_POINT_TO_POINT);
228 BOOST_CHECK_EQUAL(transport->getMtu(), MTU_UNLIMITED);
229}
230
Weiwei Liu93606232016-02-26 16:32:11 -0700231BOOST_AUTO_TEST_CASE(StaticPropertiesNonLocalIpv4)
232{
233 auto address = getAvailableInterfaceIp<ip::address_v4>();
234 SKIP_IF_IP_UNAVAILABLE(address);
235
236 ip::tcp::endpoint ep(address, 20070);
237 this->endToEndInitialize(ep);
238 checkStaticPropertiesInitialized(*transport);
239
240 BOOST_CHECK_EQUAL(transport->getLocalUri(), FaceUri("ws://" + address.to_string() + ":20070"));
241 BOOST_CHECK_EQUAL(transport->getRemoteUri().getScheme(), "wsclient");
242 BOOST_CHECK_EQUAL(transport->getRemoteUri().getHost(), address.to_string());
243 BOOST_CHECK_EQUAL(transport->getRemoteUri().getPath(), "");
244 BOOST_CHECK_EQUAL(transport->getScope(), ndn::nfd::FACE_SCOPE_NON_LOCAL);
245 BOOST_CHECK_EQUAL(transport->getPersistency(), ndn::nfd::FACE_PERSISTENCY_ON_DEMAND);
246 BOOST_CHECK_EQUAL(transport->getLinkType(), ndn::nfd::LINK_TYPE_POINT_TO_POINT);
247 BOOST_CHECK_EQUAL(transport->getMtu(), MTU_UNLIMITED);
248}
249
Yukai Tu2d6d5632015-10-26 11:06:02 -0700250BOOST_AUTO_TEST_CASE(PingPong)
251{
252 ip::tcp::endpoint ep(ip::address_v4::loopback(), 20070);
253 this->endToEndInitialize(ep, time::milliseconds(500), time::milliseconds(300));
254
255 BOOST_CHECK_EQUAL(limitedIo.run(2, // clientHandlePing, serverHandlePong
256 time::milliseconds(1500)), LimitedIo::EXCEED_OPS);
Weiwei Liu93606232016-02-26 16:32:11 -0700257
Yukai Tu2d6d5632015-10-26 11:06:02 -0700258 BOOST_CHECK_EQUAL(transport->getState(), TransportState::UP);
Junxiao Shi57df2882015-11-11 06:12:35 -0700259 BOOST_CHECK_EQUAL(transport->getCounters().nOutPings, 1);
260 BOOST_CHECK_EQUAL(transport->getCounters().nInPongs, 1);
Yukai Tu2d6d5632015-10-26 11:06:02 -0700261
262 this->clientShouldPong = false;
263 BOOST_CHECK_EQUAL(limitedIo.run(2, // clientHandlePing, serverHandlePongTimeout
Weiwei Liu93606232016-02-26 16:32:11 -0700264 time::seconds(2)), LimitedIo::EXCEED_OPS);
265
Yukai Tu2d6d5632015-10-26 11:06:02 -0700266 BOOST_CHECK_MESSAGE(transport->getState() == TransportState::FAILED ||
267 transport->getState() == TransportState::CLOSED,
268 "expect FAILED or CLOSED state, actual state=" << transport->getState());
Junxiao Shi57df2882015-11-11 06:12:35 -0700269 BOOST_CHECK_EQUAL(transport->getCounters().nOutPings, 2);
270 BOOST_CHECK_EQUAL(transport->getCounters().nInPongs, 1);
Yukai Tu2d6d5632015-10-26 11:06:02 -0700271}
272
273BOOST_AUTO_TEST_CASE(Send)
274{
275 ip::tcp::endpoint ep(ip::address_v4::loopback(), 20070);
276 this->endToEndInitialize(ep);
277
278 Block pkt1 = ndn::encoding::makeStringBlock(300, "hello");
279 transport->send(Transport::Packet(Block(pkt1)));
280 BOOST_CHECK_EQUAL(limitedIo.run(1, // clientHandleMessage
Weiwei Liu93606232016-02-26 16:32:11 -0700281 time::seconds(1)), LimitedIo::EXCEED_OPS);
Yukai Tu2d6d5632015-10-26 11:06:02 -0700282
283 Block pkt2 = ndn::encoding::makeStringBlock(301, "world!");
284 transport->send(Transport::Packet(Block(pkt2)));
285 BOOST_CHECK_EQUAL(limitedIo.run(1, // clientHandleMessage
Weiwei Liu93606232016-02-26 16:32:11 -0700286 time::seconds(1)), LimitedIo::EXCEED_OPS);
Yukai Tu2d6d5632015-10-26 11:06:02 -0700287
288 BOOST_REQUIRE_EQUAL(clientReceivedMessages.size(), 2);
289 BOOST_CHECK_EQUAL_COLLECTIONS(
290 reinterpret_cast<const uint8_t*>(clientReceivedMessages[0].data()),
291 reinterpret_cast<const uint8_t*>(clientReceivedMessages[0].data()) + clientReceivedMessages[0].size(),
292 pkt1.begin(), pkt1.end());
293 BOOST_CHECK_EQUAL_COLLECTIONS(
294 reinterpret_cast<const uint8_t*>(clientReceivedMessages[1].data()),
295 reinterpret_cast<const uint8_t*>(clientReceivedMessages[1].data()) + clientReceivedMessages[1].size(),
296 pkt2.begin(), pkt2.end());
297}
298
Weiwei Liu93606232016-02-26 16:32:11 -0700299BOOST_AUTO_TEST_CASE(ReceiveNormal)
Yukai Tu2d6d5632015-10-26 11:06:02 -0700300{
301 ip::tcp::endpoint ep(ip::address_v4::loopback(), 20070);
302 this->endToEndInitialize(ep);
303
304 Block pkt1 = ndn::encoding::makeStringBlock(300, "hello");
305 client.send(clientHdl, pkt1.wire(), pkt1.size(), websocketpp::frame::opcode::binary);
306 BOOST_CHECK_EQUAL(limitedIo.run(1, // serverHandleMessage
Weiwei Liu93606232016-02-26 16:32:11 -0700307 time::seconds(1)), LimitedIo::EXCEED_OPS);
Yukai Tu2d6d5632015-10-26 11:06:02 -0700308
309 Block pkt2 = ndn::encoding::makeStringBlock(301, "world!");
310 client.send(clientHdl, pkt2.wire(), pkt2.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 BOOST_REQUIRE_EQUAL(serverReceivedPackets->size(), 2);
315 BOOST_CHECK(serverReceivedPackets->at(0).packet == pkt1);
316 BOOST_CHECK(serverReceivedPackets->at(1).packet == pkt2);
317 BOOST_CHECK_EQUAL(serverReceivedPackets->at(0).remoteEndpoint, serverReceivedPackets->at(1).remoteEndpoint);
318}
319
320BOOST_AUTO_TEST_CASE(ReceiveMalformed)
321{
322 ip::tcp::endpoint ep(ip::address_v4::loopback(), 20070);
323 this->endToEndInitialize(ep);
324
325 Block pkt1 = ndn::encoding::makeStringBlock(300, "hello");
326 client.send(clientHdl, pkt1.wire(), pkt1.size() - 1, // truncated
327 websocketpp::frame::opcode::binary);
328 BOOST_CHECK_EQUAL(limitedIo.run(1, // serverHandleMessage
Weiwei Liu93606232016-02-26 16:32:11 -0700329 time::seconds(1)), LimitedIo::EXCEED_OPS);
Yukai Tu2d6d5632015-10-26 11:06:02 -0700330
331 // bad packet is dropped
332 BOOST_CHECK_EQUAL(transport->getState(), TransportState::UP);
333 BOOST_CHECK_EQUAL(serverReceivedPackets->size(), 0);
334
335 Block pkt2 = ndn::encoding::makeStringBlock(301, "world!");
336 client.send(clientHdl, pkt2.wire(), pkt2.size(), websocketpp::frame::opcode::binary);
337 BOOST_CHECK_EQUAL(limitedIo.run(1, // serverHandleMessage
Weiwei Liu93606232016-02-26 16:32:11 -0700338 time::seconds(1)), LimitedIo::EXCEED_OPS);
Yukai Tu2d6d5632015-10-26 11:06:02 -0700339
340 // next valid packet is still received normally
341 BOOST_REQUIRE_EQUAL(serverReceivedPackets->size(), 1);
342 BOOST_CHECK(serverReceivedPackets->at(0).packet == pkt2);
343}
344
Weiwei Liu93606232016-02-26 16:32:11 -0700345BOOST_AUTO_TEST_CASE(Close)
346{
347 ip::tcp::endpoint ep(ip::address_v4::loopback(), 20070);
348 this->endToEndInitialize(ep);
349
350 int nStateChanges = 0;
351 transport->afterStateChange.connect(
352 [&nStateChanges] (TransportState oldState, TransportState newState) {
353 switch (nStateChanges) {
354 case 0:
355 BOOST_CHECK_EQUAL(oldState, TransportState::UP);
356 BOOST_CHECK_EQUAL(newState, TransportState::CLOSING);
357 break;
358 case 1:
359 BOOST_CHECK_EQUAL(oldState, TransportState::CLOSING);
360 BOOST_CHECK_EQUAL(newState, TransportState::CLOSED);
361 break;
362 default:
363 BOOST_CHECK(false);
364 }
365 nStateChanges++;
366 });
367
368 transport->close();
369 BOOST_CHECK_EQUAL(nStateChanges, 2);
370}
371
372BOOST_AUTO_TEST_CASE(RemoteClose)
373{
374 ip::tcp::endpoint ep(ip::address_v4::loopback(), 20070);
375 this->endToEndInitialize(ep);
376
377 int nStateChanges = 0;
378 transport->afterStateChange.connect(
379 [&nStateChanges] (TransportState oldState, TransportState newState) {
380 switch (nStateChanges) {
381 case 0:
382 BOOST_CHECK_EQUAL(oldState, TransportState::UP);
383 BOOST_CHECK_EQUAL(newState, TransportState::CLOSING);
384 break;
385 case 1:
386 BOOST_CHECK_EQUAL(oldState, TransportState::CLOSING);
387 BOOST_CHECK_EQUAL(newState, TransportState::CLOSED);
388 break;
389 default:
390 BOOST_CHECK(false);
391 }
392 nStateChanges++;
393 });
394
395 client.close(clientHdl, websocketpp::close::status::going_away, "");
396 BOOST_CHECK_EQUAL(limitedIo.run(1, // serverHandleClose
397 time::seconds(1)), LimitedIo::EXCEED_OPS);
398
399 BOOST_CHECK_EQUAL(nStateChanges, 2);
400}
401
Yukai Tu2d6d5632015-10-26 11:06:02 -0700402BOOST_AUTO_TEST_SUITE_END() // TestWebSocketTransport
403BOOST_AUTO_TEST_SUITE_END() // Face
404
405} // namespace tests
406} // namespace face
407} // namespace nfd