blob: 24743001a81ac3251295eedadd359e0e29e2baa9 [file] [log] [blame]
Eric Newberrya98bf932015-09-21 00:58:47 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Weiwei Liu280d7dd2016-03-02 23:19:26 -07003 * Copyright (c) 2014-2016, 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 Pesaventoeee53aa2016-04-11 17:20:21 +020029#include "test-ip.hpp"
Weiwei Liu280d7dd2016-03-02 23:19:26 -070030#include "tests/limited-io.hpp"
Junxiao Shicde37ad2015-12-24 01:02:05 -070031#include "tests/test-common.hpp"
Eric Newberrya98bf932015-09-21 00:58:47 -070032
33namespace nfd {
Junxiao Shicde37ad2015-12-24 01:02:05 -070034namespace tests {
Eric Newberrya98bf932015-09-21 00:58:47 -070035
Junxiao Shicde37ad2015-12-24 01:02:05 -070036BOOST_AUTO_TEST_SUITE(Face)
Eric Newberrya98bf932015-09-21 00:58:47 -070037
Weiwei Liu280d7dd2016-03-02 23:19:26 -070038using nfd::Face;
39namespace ip = boost::asio::ip;
Junxiao Shicde37ad2015-12-24 01:02:05 -070040
Weiwei Liu280d7dd2016-03-02 23:19:26 -070041class WebSocketChannelFixture : public BaseFixture
42{
43protected:
44 WebSocketChannelFixture()
45 : clientShouldPong(true)
46 , m_nextPort(20070)
47 {
48 }
49
50 unique_ptr<WebSocketChannel>
51 makeChannel(const ip::address& addr, uint16_t port = 0)
52 {
53 if (port == 0)
54 port = m_nextPort++;
55
56 return make_unique<WebSocketChannel>(websocket::Endpoint(addr, port));
57 }
58
59 void
60 listen(const ip::address& addr,
61 const time::milliseconds& pingInterval = time::seconds(10),
62 const time::milliseconds& pongTimeout = time::seconds(1))
63 {
64 listenerEp = websocket::Endpoint(addr, 20030);
65 listenerChannel = makeChannel(addr, 20030);
66 listenerChannel->setPingInterval(pingInterval);
67 listenerChannel->setPongTimeout(pongTimeout);
68 listenerChannel->listen(bind(&WebSocketChannelFixture::listenerOnFaceCreated, this, _1));
69 }
70
71 void
72 clientConnect(websocket::Client& client)
73 {
74 client.clear_access_channels(websocketpp::log::alevel::all);
75 client.clear_error_channels(websocketpp::log::elevel::all);
76
77 client.init_asio(&g_io);
78 client.set_open_handler(bind(&WebSocketChannelFixture::clientHandleOpen, this, _1));
79 client.set_message_handler(bind(&WebSocketChannelFixture::clientHandleMessage, this, _1, _2));
80 client.set_ping_handler(bind(&WebSocketChannelFixture::clientHandlePing, this, _1, _2));
81
82 std::string uri = "ws://" + listenerEp.address().to_string() + ":" + to_string(listenerEp.port());
83 websocketpp::lib::error_code ec;
84 websocket::Client::connection_ptr con = client.get_connection(uri, ec);
85 BOOST_REQUIRE_EQUAL(ec, websocketpp::lib::error_code());
86
87 client.connect(con);
88 }
89
90 void
91 initialize(const ip::address& addr,
92 const time::milliseconds& pingInterval = time::seconds(10),
93 const time::milliseconds& pongTimeout = time::seconds(1))
94 {
95 listen(addr, pingInterval, pongTimeout);
96 clientConnect(client);
97 BOOST_REQUIRE_EQUAL(limitedIo.run(2, // listenerOnFaceCreated, clientHandleOpen
98 time::seconds(1)), LimitedIo::EXCEED_OPS);
99 BOOST_REQUIRE_EQUAL(listenerChannel->size(), 1);
100 }
101
102 void
103 clientSendInterest(const Interest& interest)
104 {
105 const Block& payload = interest.wireEncode();
106 client.send(clientHandle, payload.wire(), payload.size(), websocketpp::frame::opcode::binary);
107 }
108
109private:
110 void
111 listenerOnFaceCreated(const shared_ptr<Face>& newFace)
112 {
113 BOOST_REQUIRE(newFace != nullptr);
114 newFace->afterReceiveInterest.connect(bind(&WebSocketChannelFixture::faceAfterReceiveInterest, this, _1));
115 connectFaceClosedSignal(*newFace, [this] { limitedIo.afterOp(); });
116 listenerFaces.push_back(newFace);
117 limitedIo.afterOp();
118 }
119
120 void
121 faceAfterReceiveInterest(const Interest& interest)
122 {
123 faceReceivedInterests.push_back(interest);
124 limitedIo.afterOp();
125 }
126
127 void
128 clientHandleOpen(websocketpp::connection_hdl hdl)
129 {
130 clientHandle = hdl;
131 limitedIo.afterOp();
132 }
133
134 void
135 clientHandleMessage(websocketpp::connection_hdl, websocket::Client::message_ptr msg)
136 {
137 clientReceivedMessages.push_back(msg->get_payload());
138 limitedIo.afterOp();
139 }
140
141 bool
142 clientHandlePing(websocketpp::connection_hdl, std::string)
143 {
144 auto now = time::steady_clock::now();
145 if (m_prevPingRecvTime != time::steady_clock::TimePoint()) {
146 measuredPingInterval = now - m_prevPingRecvTime;
147 }
148 m_prevPingRecvTime = now;
149
150 limitedIo.afterOp();
151 return clientShouldPong;
152 }
153
154protected:
155 LimitedIo limitedIo;
156
157 websocket::Endpoint listenerEp;
158 unique_ptr<WebSocketChannel> listenerChannel;
159 std::vector<shared_ptr<Face>> listenerFaces;
160 std::vector<Interest> faceReceivedInterests;
161
162 websocket::Client client;
163 websocketpp::connection_hdl clientHandle;
164 std::vector<std::string> clientReceivedMessages;
165 time::steady_clock::Duration measuredPingInterval;
166 bool clientShouldPong; // set clientShouldPong false to disable the pong response,
167 // which will cause timeout in listenerChannel
168
169private:
170 uint16_t m_nextPort;
171 time::steady_clock::TimePoint m_prevPingRecvTime;
172};
173
174BOOST_FIXTURE_TEST_SUITE(TestWebSocketChannel, WebSocketChannelFixture)
175
176BOOST_AUTO_TEST_CASE(Uri)
177{
178 websocket::Endpoint ep(ip::address_v4::loopback(), 20070);
179 auto channel = makeChannel(ep.address(), ep.port());
Davide Pesaventoeee53aa2016-04-11 17:20:21 +0200180 BOOST_CHECK_EQUAL(channel->getUri(), FaceUri(ep, "ws"));
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700181}
182
183BOOST_AUTO_TEST_CASE(Listen)
184{
185 auto channel = makeChannel(ip::address_v4());
186 BOOST_CHECK_EQUAL(channel->isListening(), false);
187
188 channel->listen(nullptr);
189 BOOST_CHECK_EQUAL(channel->isListening(), true);
190
191 // listen() is idempotent
192 BOOST_CHECK_NO_THROW(channel->listen(nullptr));
193 BOOST_CHECK_EQUAL(channel->isListening(), true);
194}
195
196BOOST_AUTO_TEST_CASE(MultipleAccepts)
197{
Davide Pesaventoeee53aa2016-04-11 17:20:21 +0200198 auto address = getTestIp<ip::address_v4>(LoopbackAddress::Yes);
199 SKIP_IF_IP_UNAVAILABLE(address);
200 this->listen(address);
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700201
202 BOOST_CHECK_EQUAL(listenerChannel->isListening(), true);
203 BOOST_CHECK_EQUAL(listenerChannel->size(), 0);
204
205 websocket::Client client1;
Davide Pesaventoeee53aa2016-04-11 17:20:21 +0200206 this->clientConnect(client1);
207
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700208 BOOST_CHECK_EQUAL(limitedIo.run(2, // listenerOnFaceCreated, clientHandleOpen
209 time::seconds(1)), LimitedIo::EXCEED_OPS);
210 BOOST_CHECK_EQUAL(listenerChannel->size(), 1);
211
212 websocket::Client client2;
213 websocket::Client client3;
Davide Pesaventoeee53aa2016-04-11 17:20:21 +0200214 this->clientConnect(client2);
215 this->clientConnect(client3);
216
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700217 BOOST_CHECK_EQUAL(limitedIo.run(4, // 2 listenerOnFaceCreated, 2 clientHandleOpen
218 time::seconds(1)), LimitedIo::EXCEED_OPS);
219 BOOST_CHECK_EQUAL(listenerChannel->size(), 3);
220
221 // check face persistency
222 for (const auto& face : listenerFaces) {
223 BOOST_CHECK_EQUAL(face->getPersistency(), ndn::nfd::FACE_PERSISTENCY_ON_DEMAND);
224 }
225}
226
227BOOST_AUTO_TEST_CASE(Send)
228{
Davide Pesaventoeee53aa2016-04-11 17:20:21 +0200229 auto address = getTestIp<ip::address_v4>(LoopbackAddress::Yes);
230 SKIP_IF_IP_UNAVAILABLE(address);
231 this->initialize(address);
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700232 auto transport = listenerFaces.front()->getTransport();
233
234 Block pkt1 = ndn::encoding::makeStringBlock(300, "hello");
235 transport->send(face::Transport::Packet(Block(pkt1)));
236 BOOST_CHECK_EQUAL(limitedIo.run(1, // clientHandleMessage
237 time::seconds(1)), LimitedIo::EXCEED_OPS);
238
239 Block pkt2 = ndn::encoding::makeStringBlock(301, "world!");
240 transport->send(face::Transport::Packet(Block(pkt2)));
241 BOOST_CHECK_EQUAL(limitedIo.run(1, // clientHandleMessage
242 time::seconds(1)), LimitedIo::EXCEED_OPS);
243
244 BOOST_REQUIRE_EQUAL(clientReceivedMessages.size(), 2);
245 BOOST_CHECK_EQUAL_COLLECTIONS(
246 reinterpret_cast<const uint8_t*>(clientReceivedMessages[0].data()),
247 reinterpret_cast<const uint8_t*>(clientReceivedMessages[0].data()) + clientReceivedMessages[0].size(),
248 pkt1.begin(), pkt1.end());
249 BOOST_CHECK_EQUAL_COLLECTIONS(
250 reinterpret_cast<const uint8_t*>(clientReceivedMessages[1].data()),
251 reinterpret_cast<const uint8_t*>(clientReceivedMessages[1].data()) + clientReceivedMessages[1].size(),
252 pkt2.begin(), pkt2.end());
253}
254
255BOOST_AUTO_TEST_CASE(Receive)
256{
Davide Pesaventoeee53aa2016-04-11 17:20:21 +0200257 auto address = getTestIp<ip::address_v4>(LoopbackAddress::Yes);
258 SKIP_IF_IP_UNAVAILABLE(address);
259 this->initialize(address);
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700260
261 // use network-layer packets here, otherwise GenericLinkService
262 // won't recognize the packet type and will discard it
263 auto interest1 = makeInterest("ndn:/TpnzGvW9R");
264 auto interest2 = makeInterest("ndn:/QWiIMfj5sL");
265
266 clientSendInterest(*interest1);
267 BOOST_CHECK_EQUAL(limitedIo.run(1, // faceAfterReceiveInterest
268 time::seconds(1)), LimitedIo::EXCEED_OPS);
269
270 clientSendInterest(*interest2);
271 BOOST_CHECK_EQUAL(limitedIo.run(1, // faceAfterReceiveInterest
272 time::seconds(1)), LimitedIo::EXCEED_OPS);
273
274 BOOST_REQUIRE_EQUAL(faceReceivedInterests.size(), 2);
275 BOOST_CHECK_EQUAL(faceReceivedInterests[0].getName(), interest1->getName());
276 BOOST_CHECK_EQUAL(faceReceivedInterests[1].getName(), interest2->getName());
277}
278
279BOOST_AUTO_TEST_CASE(FaceClosure)
280{
Davide Pesaventoeee53aa2016-04-11 17:20:21 +0200281 auto address = getTestIp<ip::address_v4>(LoopbackAddress::Yes);
282 SKIP_IF_IP_UNAVAILABLE(address);
283 this->initialize(address);
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700284
285 listenerFaces.front()->close();
286 BOOST_CHECK_EQUAL(listenerChannel->size(), 0);
287}
288
289BOOST_AUTO_TEST_CASE(RemoteClose)
290{
Davide Pesaventoeee53aa2016-04-11 17:20:21 +0200291 auto address = getTestIp<ip::address_v4>(LoopbackAddress::Yes);
292 SKIP_IF_IP_UNAVAILABLE(address);
293 this->initialize(address);
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700294
295 client.close(clientHandle, websocketpp::close::status::going_away, "");
296 BOOST_CHECK_EQUAL(limitedIo.run(1, // faceClosedSignal
297 time::seconds(1)), LimitedIo::EXCEED_OPS);
298 BOOST_CHECK_EQUAL(listenerChannel->size(), 0);
299}
300
301BOOST_AUTO_TEST_CASE(SetPingInterval)
302{
303 auto pingInterval = time::milliseconds(300);
Davide Pesaventoeee53aa2016-04-11 17:20:21 +0200304 auto address = getTestIp<ip::address_v4>(LoopbackAddress::Yes);
305 SKIP_IF_IP_UNAVAILABLE(address);
306 this->initialize(address, pingInterval, time::milliseconds(1000));
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700307
308 BOOST_CHECK_EQUAL(limitedIo.run(2, // clientHandlePing
309 time::seconds(1)), LimitedIo::EXCEED_OPS);
310 BOOST_CHECK_LE(measuredPingInterval, pingInterval * 1.1);
311 BOOST_CHECK_GE(measuredPingInterval, pingInterval * 0.9);
312}
313
314BOOST_AUTO_TEST_CASE(SetPongTimeOut)
315{
Davide Pesaventoeee53aa2016-04-11 17:20:21 +0200316 auto address = getTestIp<ip::address_v4>(LoopbackAddress::Yes);
317 SKIP_IF_IP_UNAVAILABLE(address);
318 this->initialize(address, time::milliseconds(500), time::milliseconds(300));
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700319 clientShouldPong = false;
Davide Pesaventoeee53aa2016-04-11 17:20:21 +0200320
Weiwei Liu280d7dd2016-03-02 23:19:26 -0700321 BOOST_CHECK_EQUAL(limitedIo.run(2, // clientHandlePing, faceClosedSignal
322 time::seconds(2)), LimitedIo::EXCEED_OPS);
323 BOOST_CHECK_EQUAL(listenerChannel->size(), 0);
324
325 auto transport = static_cast<face::WebSocketTransport*>(listenerFaces.front()->getTransport());
326 BOOST_CHECK(transport->getState() == face::TransportState::FAILED ||
327 transport->getState() == face::TransportState::CLOSED);
328 BOOST_CHECK_EQUAL(transport->getCounters().nOutPings, 1);
329 BOOST_CHECK_EQUAL(transport->getCounters().nInPongs, 0);
330}
Junxiao Shicde37ad2015-12-24 01:02:05 -0700331
332BOOST_AUTO_TEST_SUITE_END() // TestWebSocketChannel
333BOOST_AUTO_TEST_SUITE_END() // Face
334
335} // namespace tests
Eric Newberrya98bf932015-09-21 00:58:47 -0700336} // namespace nfd