blob: edee799faa07027aa736f79b366bd7ce93322f17 [file] [log] [blame]
Yukai Tu2d6d5632015-10-26 11:06:02 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (c) 2014-2015, Regents of the University of California,
4 * 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"
Eric Newberry8717e872015-11-23 12:41:50 -070029#include "transport-test-common.hpp"
Yukai Tu2d6d5632015-10-26 11:06:02 -070030
Yukai Tu2d6d5632015-10-26 11:06:02 -070031#include "tests/limited-io.hpp"
32
33namespace nfd {
34namespace face {
35namespace tests {
36
37using namespace nfd::tests;
38namespace ip = boost::asio::ip;
39
40BOOST_AUTO_TEST_SUITE(Face)
41
Junxiao Shicde37ad2015-12-24 01:02:05 -070042using nfd::Face;
43
Yukai Tu2d6d5632015-10-26 11:06:02 -070044/** \brief a fixture that accepts a single WebSocket connection from a client
45 */
46class SingleWebSocketFixture : public BaseFixture
47{
48public:
49 SingleWebSocketFixture()
50 : transport(nullptr)
51 , serverReceivedPackets(nullptr)
52 , clientShouldPong(true)
53 {
54 }
55
56 /** \brief initialize server and start listening
57 */
58 void
59 serverListen(const ip::tcp::endpoint& ep,
60 const time::milliseconds& pongTimeout = time::milliseconds(1000))
61 {
62 server.clear_access_channels(websocketpp::log::alevel::all);
63 server.clear_error_channels(websocketpp::log::elevel::all);
64
65 server.init_asio(&g_io);
66 server.set_open_handler(bind(&SingleWebSocketFixture::serverHandleOpen, this, _1));
67 server.set_close_handler(bind(&SingleWebSocketFixture::serverHandleClose, this));
68 server.set_message_handler(bind(&SingleWebSocketFixture::serverHandleMessage, this, _2));
69 server.set_pong_handler(bind(&SingleWebSocketFixture::serverHandlePong, this));
70 server.set_pong_timeout_handler(bind(&SingleWebSocketFixture::serverHandlePongTimeout, this));
71 server.set_pong_timeout(pongTimeout.count());
72
73 server.set_reuse_addr(true);
74
75 server.listen(ep);
76 server.start_accept();
77 }
78
79 /** \brief initialize client and connect to server
80 */
81 void
82 clientConnect(const std::string& uri)
83 {
84 client.clear_access_channels(websocketpp::log::alevel::all);
85 client.clear_error_channels(websocketpp::log::elevel::all);
86
87 client.init_asio(&g_io);
88 client.set_open_handler(bind(&SingleWebSocketFixture::clientHandleOpen, this, _1));
89 client.set_message_handler(bind(&SingleWebSocketFixture::clientHandleMessage, this, _2));
90 client.set_ping_handler(bind(&SingleWebSocketFixture::clientHandlePing, this));
91
92 websocketpp::lib::error_code ec;
93 websocket::Client::connection_ptr con = client.get_connection("ws://127.0.0.1:20070", ec);
94 BOOST_REQUIRE(!ec);
95
96 client.connect(con);
97 }
98
99 void
100 makeFace(const time::milliseconds& pingInterval = time::milliseconds(10000))
101 {
Junxiao Shicde37ad2015-12-24 01:02:05 -0700102 face = make_unique<Face>(
Yukai Tu2d6d5632015-10-26 11:06:02 -0700103 make_unique<DummyReceiveLinkService>(),
104 make_unique<WebSocketTransport>(serverHdl, ref(server), pingInterval));
105 transport = static_cast<WebSocketTransport*>(face->getTransport());
106 serverReceivedPackets = &static_cast<DummyReceiveLinkService*>(face->getLinkService())->receivedPackets;
107 }
108
109 /** \brief initialize both server and client, and have each other connected, create Transport
110 */
111 void
112 endToEndInitialize(const ip::tcp::endpoint& ep,
113 const time::milliseconds& pingInterval = time::milliseconds(10000),
114 const time::milliseconds& pongTimeout = time::milliseconds(1000))
115 {
116 this->serverListen(ep, pongTimeout);
117 std::string uri = "ws://" + ep.address().to_string() + ":" + to_string(ep.port());
118 this->clientConnect(uri);
119 BOOST_REQUIRE_EQUAL(limitedIo.run(2, // serverHandleOpen, clientHandleOpen
120 time::seconds(1)), LimitedIo::EXCEED_OPS);
121 this->makeFace(pingInterval);
122 }
123
124private:
125 void
126 serverHandleOpen(websocketpp::connection_hdl hdl)
127 {
128 serverHdl = hdl;
129 limitedIo.afterOp();
130 }
131
132 void
133 serverHandleClose()
134 {
135 if (transport == nullptr) {
136 return;
137 }
138
139 transport->close();
140 limitedIo.afterOp();
141 }
142
143 void
144 serverHandleMessage(websocket::Server::message_ptr msg)
145 {
146 if (transport == nullptr) {
147 return;
148 }
149
150 transport->receiveMessage(msg->get_payload());
151 limitedIo.afterOp();
152 }
153
154 void
155 serverHandlePong()
156 {
157 if (transport == nullptr) {
158 return;
159 }
160
161 transport->handlePong();
162 limitedIo.afterOp();
163 }
164
165 void
166 serverHandlePongTimeout()
167 {
168 if (transport == nullptr) {
169 return;
170 }
171
172 transport->handlePongTimeout();
173 limitedIo.afterOp();
174 }
175
176 void
177 clientHandleOpen(websocketpp::connection_hdl hdl)
178 {
179 clientHdl = hdl;
180 limitedIo.afterOp();
181 }
182
183 void
184 clientHandleMessage(websocket::Client::message_ptr msg)
185 {
186 clientReceivedMessages.push_back(msg->get_payload());
187 limitedIo.afterOp();
188 }
189
190 bool
191 clientHandlePing()
192 {
193 limitedIo.afterOp();
194 return clientShouldPong;
195 }
196
197public:
198 LimitedIo limitedIo;
199
200 websocket::Server server;
201 websocketpp::connection_hdl serverHdl;
Junxiao Shicde37ad2015-12-24 01:02:05 -0700202 unique_ptr<Face> face;
Yukai Tu2d6d5632015-10-26 11:06:02 -0700203 WebSocketTransport* transport;
204 std::vector<Transport::Packet>* serverReceivedPackets;
205
206 websocket::Client client;
207 websocketpp::connection_hdl clientHdl;
208 bool clientShouldPong;
209 std::vector<std::string> clientReceivedMessages;
210};
211
212BOOST_FIXTURE_TEST_SUITE(TestWebSocketTransport, SingleWebSocketFixture)
213
214BOOST_AUTO_TEST_CASE(StaticProperties)
215{
216 ip::tcp::endpoint ep(ip::address_v4::loopback(), 20070);
217 this->endToEndInitialize(ep);
218 checkStaticPropertiesInitialized(*transport);
219
220 BOOST_CHECK_EQUAL(transport->getLocalUri(), FaceUri("ws://127.0.0.1:20070"));
221 BOOST_CHECK_EQUAL(transport->getRemoteUri().getScheme(), "wsclient");
222 BOOST_CHECK_EQUAL(transport->getRemoteUri().getHost(), "127.0.0.1");
223 BOOST_CHECK_EQUAL(transport->getRemoteUri().getPath(), "");
224 BOOST_CHECK_EQUAL(transport->getScope(), ndn::nfd::FACE_SCOPE_LOCAL);
225 BOOST_CHECK_EQUAL(transport->getPersistency(), ndn::nfd::FACE_PERSISTENCY_ON_DEMAND);
226 BOOST_CHECK_EQUAL(transport->getLinkType(), ndn::nfd::LINK_TYPE_POINT_TO_POINT);
227 BOOST_CHECK_EQUAL(transport->getMtu(), MTU_UNLIMITED);
228}
229
230BOOST_AUTO_TEST_CASE(PingPong)
231{
232 ip::tcp::endpoint ep(ip::address_v4::loopback(), 20070);
233 this->endToEndInitialize(ep, time::milliseconds(500), time::milliseconds(300));
234
235 BOOST_CHECK_EQUAL(limitedIo.run(2, // clientHandlePing, serverHandlePong
236 time::milliseconds(1500)), LimitedIo::EXCEED_OPS);
237 BOOST_CHECK_EQUAL(transport->getState(), TransportState::UP);
Junxiao Shi57df2882015-11-11 06:12:35 -0700238 BOOST_CHECK_EQUAL(transport->getCounters().nOutPings, 1);
239 BOOST_CHECK_EQUAL(transport->getCounters().nInPongs, 1);
Yukai Tu2d6d5632015-10-26 11:06:02 -0700240
241 this->clientShouldPong = false;
242 BOOST_CHECK_EQUAL(limitedIo.run(2, // clientHandlePing, serverHandlePongTimeout
243 time::milliseconds(2000)), LimitedIo::EXCEED_OPS);
244 BOOST_CHECK_MESSAGE(transport->getState() == TransportState::FAILED ||
245 transport->getState() == TransportState::CLOSED,
246 "expect FAILED or CLOSED state, actual state=" << transport->getState());
Junxiao Shi57df2882015-11-11 06:12:35 -0700247 BOOST_CHECK_EQUAL(transport->getCounters().nOutPings, 2);
248 BOOST_CHECK_EQUAL(transport->getCounters().nInPongs, 1);
Yukai Tu2d6d5632015-10-26 11:06:02 -0700249}
250
251BOOST_AUTO_TEST_CASE(Send)
252{
253 ip::tcp::endpoint ep(ip::address_v4::loopback(), 20070);
254 this->endToEndInitialize(ep);
255
256 Block pkt1 = ndn::encoding::makeStringBlock(300, "hello");
257 transport->send(Transport::Packet(Block(pkt1)));
258 BOOST_CHECK_EQUAL(limitedIo.run(1, // clientHandleMessage
259 time::milliseconds(1000)), LimitedIo::EXCEED_OPS);
260
261 Block pkt2 = ndn::encoding::makeStringBlock(301, "world!");
262 transport->send(Transport::Packet(Block(pkt2)));
263 BOOST_CHECK_EQUAL(limitedIo.run(1, // clientHandleMessage
264 time::milliseconds(1000)), LimitedIo::EXCEED_OPS);
265
266 BOOST_REQUIRE_EQUAL(clientReceivedMessages.size(), 2);
267 BOOST_CHECK_EQUAL_COLLECTIONS(
268 reinterpret_cast<const uint8_t*>(clientReceivedMessages[0].data()),
269 reinterpret_cast<const uint8_t*>(clientReceivedMessages[0].data()) + clientReceivedMessages[0].size(),
270 pkt1.begin(), pkt1.end());
271 BOOST_CHECK_EQUAL_COLLECTIONS(
272 reinterpret_cast<const uint8_t*>(clientReceivedMessages[1].data()),
273 reinterpret_cast<const uint8_t*>(clientReceivedMessages[1].data()) + clientReceivedMessages[1].size(),
274 pkt2.begin(), pkt2.end());
275}
276
277BOOST_AUTO_TEST_CASE(Receive)
278{
279 ip::tcp::endpoint ep(ip::address_v4::loopback(), 20070);
280 this->endToEndInitialize(ep);
281
282 Block pkt1 = ndn::encoding::makeStringBlock(300, "hello");
283 client.send(clientHdl, pkt1.wire(), pkt1.size(), websocketpp::frame::opcode::binary);
284 BOOST_CHECK_EQUAL(limitedIo.run(1, // serverHandleMessage
285 time::milliseconds(1000)), LimitedIo::EXCEED_OPS);
286
287 Block pkt2 = ndn::encoding::makeStringBlock(301, "world!");
288 client.send(clientHdl, pkt2.wire(), pkt2.size(), websocketpp::frame::opcode::binary);
289 BOOST_CHECK_EQUAL(limitedIo.run(1, // serverHandleMessage
290 time::milliseconds(1000)), LimitedIo::EXCEED_OPS);
291
292 BOOST_REQUIRE_EQUAL(serverReceivedPackets->size(), 2);
293 BOOST_CHECK(serverReceivedPackets->at(0).packet == pkt1);
294 BOOST_CHECK(serverReceivedPackets->at(1).packet == pkt2);
295 BOOST_CHECK_EQUAL(serverReceivedPackets->at(0).remoteEndpoint, serverReceivedPackets->at(1).remoteEndpoint);
296}
297
298BOOST_AUTO_TEST_CASE(ReceiveMalformed)
299{
300 ip::tcp::endpoint ep(ip::address_v4::loopback(), 20070);
301 this->endToEndInitialize(ep);
302
303 Block pkt1 = ndn::encoding::makeStringBlock(300, "hello");
304 client.send(clientHdl, pkt1.wire(), pkt1.size() - 1, // truncated
305 websocketpp::frame::opcode::binary);
306 BOOST_CHECK_EQUAL(limitedIo.run(1, // serverHandleMessage
307 time::milliseconds(1000)), LimitedIo::EXCEED_OPS);
308
309 // bad packet is dropped
310 BOOST_CHECK_EQUAL(transport->getState(), TransportState::UP);
311 BOOST_CHECK_EQUAL(serverReceivedPackets->size(), 0);
312
313 Block pkt2 = ndn::encoding::makeStringBlock(301, "world!");
314 client.send(clientHdl, pkt2.wire(), pkt2.size(), websocketpp::frame::opcode::binary);
315 BOOST_CHECK_EQUAL(limitedIo.run(1, // serverHandleMessage
316 time::milliseconds(1000)), LimitedIo::EXCEED_OPS);
317
318 // next valid packet is still received normally
319 BOOST_REQUIRE_EQUAL(serverReceivedPackets->size(), 1);
320 BOOST_CHECK(serverReceivedPackets->at(0).packet == pkt2);
321}
322
323BOOST_AUTO_TEST_SUITE_END() // TestWebSocketTransport
324BOOST_AUTO_TEST_SUITE_END() // Face
325
326} // namespace tests
327} // namespace face
328} // namespace nfd