blob: 63be570e3ede52ac60dca136fa3c7d50e60827a5 [file] [log] [blame]
Yukai Tu2d6d5632015-10-26 11:06:02 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Davide Pesaventoa3148082018-04-12 18:21:54 -04002/*
Davide Pesavento3dade002019-03-19 11:29:56 -06003 * Copyright (c) 2014-2019, 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"
Davide Pesavento3dade002019-03-19 11:29:56 -060027#include "daemon/global.hpp"
Yukai Tu2d6d5632015-10-26 11:06:02 -070028
29namespace nfd {
30namespace face {
31
Davide Pesaventoa3148082018-04-12 18:21:54 -040032NFD_LOG_INIT(WebSocketTransport);
Yukai Tu2d6d5632015-10-26 11:06:02 -070033
Eric Newberry4a4ccfe2016-07-21 22:51:04 -070034static bool
35isLoopback(const boost::asio::ip::address& addr)
36{
37 if (addr.is_loopback()) {
38 return true;
39 }
40 // Workaround for loopback IPv4-mapped IPv6 addresses
41 // see https://svn.boost.org/trac/boost/ticket/9084
42 else if (addr.is_v6()) {
43 auto addr6 = addr.to_v6();
44 if (addr6.is_v4_mapped()) {
45 return addr6.to_v4().is_loopback();
46 }
47 }
48
49 return false;
50}
51
Yukai Tu2d6d5632015-10-26 11:06:02 -070052WebSocketTransport::WebSocketTransport(websocketpp::connection_hdl hdl,
53 websocket::Server& server,
54 time::milliseconds pingInterval)
55 : m_handle(hdl)
56 , m_server(server)
57 , m_pingInterval(pingInterval)
58{
59 const auto& sock = m_server.get_con_from_hdl(hdl)->get_socket();
60 this->setLocalUri(FaceUri(sock.local_endpoint(), "ws"));
61 this->setRemoteUri(FaceUri(sock.remote_endpoint(), "wsclient"));
62
Eric Newberry4a4ccfe2016-07-21 22:51:04 -070063 if (isLoopback(sock.local_endpoint().address()) &&
64 isLoopback(sock.remote_endpoint().address())) {
Yukai Tu2d6d5632015-10-26 11:06:02 -070065 this->setScope(ndn::nfd::FACE_SCOPE_LOCAL);
Eric Newberry4a4ccfe2016-07-21 22:51:04 -070066 }
67 else {
Yukai Tu2d6d5632015-10-26 11:06:02 -070068 this->setScope(ndn::nfd::FACE_SCOPE_NON_LOCAL);
Eric Newberry4a4ccfe2016-07-21 22:51:04 -070069 }
Yukai Tu2d6d5632015-10-26 11:06:02 -070070
71 this->setPersistency(ndn::nfd::FACE_PERSISTENCY_ON_DEMAND);
72 this->setLinkType(ndn::nfd::LINK_TYPE_POINT_TO_POINT);
73 this->setMtu(MTU_UNLIMITED);
74
75 this->schedulePing();
76
Davide Pesaventoa681a242019-03-29 23:48:27 -040077 NFD_LOG_FACE_DEBUG("Creating transport");
Yukai Tu2d6d5632015-10-26 11:06:02 -070078}
79
Davide Pesavento8728a252015-11-06 04:01:22 +010080void
Yukai Tu2d6d5632015-10-26 11:06:02 -070081WebSocketTransport::doSend(Transport::Packet&& packet)
82{
83 NFD_LOG_FACE_TRACE(__func__);
84
85 websocketpp::lib::error_code error;
86 m_server.send(m_handle, packet.packet.wire(), packet.packet.size(),
87 websocketpp::frame::opcode::binary, error);
88 if (error)
89 return processErrorCode(error);
90
91 NFD_LOG_FACE_TRACE("Successfully sent: " << packet.packet.size() << " bytes");
92}
93
94void
95WebSocketTransport::receiveMessage(const std::string& msg)
96{
97 NFD_LOG_FACE_TRACE("Received: " << msg.size() << " bytes");
98
99 bool isOk = false;
100 Block element;
Davide Pesaventoe4b22382018-06-10 14:37:24 -0400101 std::tie(isOk, element) = Block::fromBuffer(reinterpret_cast<const uint8_t*>(msg.data()), msg.size());
Yukai Tu2d6d5632015-10-26 11:06:02 -0700102 if (!isOk) {
103 NFD_LOG_FACE_WARN("Failed to parse message payload");
104 return;
105 }
106
107 this->receive(Transport::Packet(std::move(element)));
108}
109
110void
111WebSocketTransport::schedulePing()
112{
Davide Pesavento3dade002019-03-19 11:29:56 -0600113 m_pingEventId = getScheduler().schedule(m_pingInterval, [this] { sendPing(); });
Yukai Tu2d6d5632015-10-26 11:06:02 -0700114}
115
116void
117WebSocketTransport::sendPing()
118{
119 NFD_LOG_FACE_TRACE(__func__);
120
121 websocketpp::lib::error_code error;
122 m_server.ping(m_handle, "NFD-WebSocket", error);
123 if (error)
124 return processErrorCode(error);
125
Davide Pesavento1816d4b2017-07-02 12:20:48 -0400126 ++this->nOutPings;
127
Yukai Tu2d6d5632015-10-26 11:06:02 -0700128 this->schedulePing();
129}
130
131void
132WebSocketTransport::handlePong()
133{
134 NFD_LOG_FACE_TRACE(__func__);
Junxiao Shi57df2882015-11-11 06:12:35 -0700135
136 ++this->nInPongs;
Yukai Tu2d6d5632015-10-26 11:06:02 -0700137}
138
139void
140WebSocketTransport::handlePongTimeout()
141{
Davide Pesavento1816d4b2017-07-02 12:20:48 -0400142 NFD_LOG_FACE_ERROR("Pong timeout");
Yukai Tu2d6d5632015-10-26 11:06:02 -0700143 this->setState(TransportState::FAILED);
144 doClose();
145}
146
147void
148WebSocketTransport::processErrorCode(const websocketpp::lib::error_code& error)
149{
150 NFD_LOG_FACE_TRACE(__func__);
151
152 if (getState() == TransportState::CLOSING ||
153 getState() == TransportState::FAILED ||
154 getState() == TransportState::CLOSED)
155 // transport is shutting down, ignore any errors
156 return;
157
Davide Pesavento1816d4b2017-07-02 12:20:48 -0400158 NFD_LOG_FACE_ERROR("Send or ping operation failed: " << error.message());
Yukai Tu2d6d5632015-10-26 11:06:02 -0700159 this->setState(TransportState::FAILED);
160 doClose();
161}
162
163void
164WebSocketTransport::doClose()
165{
166 NFD_LOG_FACE_TRACE(__func__);
167
168 m_pingEventId.cancel();
169
170 // use the non-throwing variant and ignore errors, if any
171 websocketpp::lib::error_code error;
172 m_server.close(m_handle, websocketpp::close::status::normal, "closed by NFD", error);
173
174 this->setState(TransportState::CLOSED);
175}
176
177} // namespace face
178} // namespace nfd