blob: d4bbbac61d6322410e8a6b846cd63abe0048ffc2 [file] [log] [blame]
Yukai Tu2d6d5632015-10-26 11:06:02 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Yanbiao Li32dab972016-11-27 12:26:09 +08003 * Copyright (c) 2014-2017, 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 "websocket-transport.hpp"
27
28namespace nfd {
29namespace face {
30
31NFD_LOG_INIT("WebSocketTransport");
32
Eric Newberry4a4ccfe2016-07-21 22:51:04 -070033static bool
34isLoopback(const boost::asio::ip::address& addr)
35{
36 if (addr.is_loopback()) {
37 return true;
38 }
39 // Workaround for loopback IPv4-mapped IPv6 addresses
40 // see https://svn.boost.org/trac/boost/ticket/9084
41 else if (addr.is_v6()) {
42 auto addr6 = addr.to_v6();
43 if (addr6.is_v4_mapped()) {
44 return addr6.to_v4().is_loopback();
45 }
46 }
47
48 return false;
49}
50
Yukai Tu2d6d5632015-10-26 11:06:02 -070051WebSocketTransport::WebSocketTransport(websocketpp::connection_hdl hdl,
52 websocket::Server& server,
53 time::milliseconds pingInterval)
54 : m_handle(hdl)
55 , m_server(server)
56 , m_pingInterval(pingInterval)
57{
58 const auto& sock = m_server.get_con_from_hdl(hdl)->get_socket();
59 this->setLocalUri(FaceUri(sock.local_endpoint(), "ws"));
60 this->setRemoteUri(FaceUri(sock.remote_endpoint(), "wsclient"));
61
Eric Newberry4a4ccfe2016-07-21 22:51:04 -070062 if (isLoopback(sock.local_endpoint().address()) &&
63 isLoopback(sock.remote_endpoint().address())) {
Yukai Tu2d6d5632015-10-26 11:06:02 -070064 this->setScope(ndn::nfd::FACE_SCOPE_LOCAL);
Eric Newberry4a4ccfe2016-07-21 22:51:04 -070065 }
66 else {
Yukai Tu2d6d5632015-10-26 11:06:02 -070067 this->setScope(ndn::nfd::FACE_SCOPE_NON_LOCAL);
Eric Newberry4a4ccfe2016-07-21 22:51:04 -070068 }
Yukai Tu2d6d5632015-10-26 11:06:02 -070069
70 this->setPersistency(ndn::nfd::FACE_PERSISTENCY_ON_DEMAND);
71 this->setLinkType(ndn::nfd::LINK_TYPE_POINT_TO_POINT);
72 this->setMtu(MTU_UNLIMITED);
73
74 this->schedulePing();
75
76 NFD_LOG_FACE_INFO("Creating transport");
77}
78
Davide Pesavento8728a252015-11-06 04:01:22 +010079void
Yukai Tu2d6d5632015-10-26 11:06:02 -070080WebSocketTransport::doSend(Transport::Packet&& packet)
81{
82 NFD_LOG_FACE_TRACE(__func__);
83
84 websocketpp::lib::error_code error;
85 m_server.send(m_handle, packet.packet.wire(), packet.packet.size(),
86 websocketpp::frame::opcode::binary, error);
87 if (error)
88 return processErrorCode(error);
89
90 NFD_LOG_FACE_TRACE("Successfully sent: " << packet.packet.size() << " bytes");
91}
92
93void
94WebSocketTransport::receiveMessage(const std::string& msg)
95{
96 NFD_LOG_FACE_TRACE("Received: " << msg.size() << " bytes");
97
98 bool isOk = false;
99 Block element;
100 std::tie(isOk, element) = Block::fromBuffer(reinterpret_cast<const uint8_t*>(msg.c_str()), msg.size());
101 if (!isOk) {
102 NFD_LOG_FACE_WARN("Failed to parse message payload");
103 return;
104 }
105
106 this->receive(Transport::Packet(std::move(element)));
107}
108
109void
110WebSocketTransport::schedulePing()
111{
112 m_pingEventId = scheduler::schedule(m_pingInterval, bind(&WebSocketTransport::sendPing, this));
113}
114
115void
116WebSocketTransport::sendPing()
117{
118 NFD_LOG_FACE_TRACE(__func__);
119
120 websocketpp::lib::error_code error;
121 m_server.ping(m_handle, "NFD-WebSocket", error);
122 if (error)
123 return processErrorCode(error);
124
Davide Pesavento1816d4b2017-07-02 12:20:48 -0400125 ++this->nOutPings;
126
Yukai Tu2d6d5632015-10-26 11:06:02 -0700127 this->schedulePing();
128}
129
130void
131WebSocketTransport::handlePong()
132{
133 NFD_LOG_FACE_TRACE(__func__);
Junxiao Shi57df2882015-11-11 06:12:35 -0700134
135 ++this->nInPongs;
Yukai Tu2d6d5632015-10-26 11:06:02 -0700136}
137
138void
139WebSocketTransport::handlePongTimeout()
140{
Davide Pesavento1816d4b2017-07-02 12:20:48 -0400141 NFD_LOG_FACE_ERROR("Pong timeout");
Yukai Tu2d6d5632015-10-26 11:06:02 -0700142 this->setState(TransportState::FAILED);
143 doClose();
144}
145
146void
147WebSocketTransport::processErrorCode(const websocketpp::lib::error_code& error)
148{
149 NFD_LOG_FACE_TRACE(__func__);
150
151 if (getState() == TransportState::CLOSING ||
152 getState() == TransportState::FAILED ||
153 getState() == TransportState::CLOSED)
154 // transport is shutting down, ignore any errors
155 return;
156
Davide Pesavento1816d4b2017-07-02 12:20:48 -0400157 NFD_LOG_FACE_ERROR("Send or ping operation failed: " << error.message());
Yukai Tu2d6d5632015-10-26 11:06:02 -0700158 this->setState(TransportState::FAILED);
159 doClose();
160}
161
162void
163WebSocketTransport::doClose()
164{
165 NFD_LOG_FACE_TRACE(__func__);
166
167 m_pingEventId.cancel();
168
169 // use the non-throwing variant and ignore errors, if any
170 websocketpp::lib::error_code error;
171 m_server.close(m_handle, websocketpp::close::status::normal, "closed by NFD", error);
172
173 this->setState(TransportState::CLOSED);
174}
175
176} // namespace face
177} // namespace nfd