| /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ |
| /* |
| * Copyright (c) 2014-2022, Regents of the University of California, |
| * Arizona Board of Regents, |
| * Colorado State University, |
| * University Pierre & Marie Curie, Sorbonne University, |
| * Washington University in St. Louis, |
| * Beijing Institute of Technology, |
| * The University of Memphis. |
| * |
| * This file is part of NFD (Named Data Networking Forwarding Daemon). |
| * See AUTHORS.md for complete list of NFD authors and contributors. |
| * |
| * NFD is free software: you can redistribute it and/or modify it under the terms |
| * of the GNU General Public License as published by the Free Software Foundation, |
| * either version 3 of the License, or (at your option) any later version. |
| * |
| * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; |
| * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR |
| * PURPOSE. See the GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License along with |
| * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| #include "websocket-transport.hpp" |
| #include "common/global.hpp" |
| |
| namespace nfd { |
| namespace face { |
| |
| NFD_LOG_INIT(WebSocketTransport); |
| |
| static bool |
| isLoopback(const boost::asio::ip::address& addr) |
| { |
| if (addr.is_loopback()) { |
| return true; |
| } |
| // Workaround for loopback IPv4-mapped IPv6 addresses |
| // see https://svn.boost.org/trac/boost/ticket/9084 |
| else if (addr.is_v6()) { |
| auto addr6 = addr.to_v6(); |
| if (addr6.is_v4_mapped()) { |
| return addr6.to_v4().is_loopback(); |
| } |
| } |
| |
| return false; |
| } |
| |
| WebSocketTransport::WebSocketTransport(websocketpp::connection_hdl hdl, |
| websocket::Server& server, |
| time::milliseconds pingInterval) |
| : m_handle(hdl) |
| , m_server(server) |
| , m_pingInterval(pingInterval) |
| { |
| const auto& sock = m_server.get_con_from_hdl(hdl)->get_socket(); |
| this->setLocalUri(FaceUri(sock.local_endpoint(), "ws")); |
| this->setRemoteUri(FaceUri(sock.remote_endpoint(), "wsclient")); |
| |
| if (isLoopback(sock.local_endpoint().address()) && |
| isLoopback(sock.remote_endpoint().address())) { |
| this->setScope(ndn::nfd::FACE_SCOPE_LOCAL); |
| } |
| else { |
| this->setScope(ndn::nfd::FACE_SCOPE_NON_LOCAL); |
| } |
| |
| this->setPersistency(ndn::nfd::FACE_PERSISTENCY_ON_DEMAND); |
| this->setLinkType(ndn::nfd::LINK_TYPE_POINT_TO_POINT); |
| this->setMtu(MTU_UNLIMITED); |
| |
| this->schedulePing(); |
| |
| NFD_LOG_FACE_DEBUG("Creating transport"); |
| } |
| |
| void |
| WebSocketTransport::doSend(const Block& packet) |
| { |
| NFD_LOG_FACE_TRACE(__func__); |
| |
| websocketpp::lib::error_code error; |
| m_server.send(m_handle, packet.wire(), packet.size(), |
| websocketpp::frame::opcode::binary, error); |
| if (error) |
| return processErrorCode(error); |
| |
| NFD_LOG_FACE_TRACE("Successfully sent: " << packet.size() << " bytes"); |
| } |
| |
| void |
| WebSocketTransport::receiveMessage(const std::string& msg) |
| { |
| NFD_LOG_FACE_TRACE("Received: " << msg.size() << " bytes"); |
| |
| bool isOk = false; |
| Block element; |
| std::tie(isOk, element) = Block::fromBuffer({reinterpret_cast<const uint8_t*>(msg.data()), msg.size()}); |
| if (!isOk) { |
| NFD_LOG_FACE_WARN("Failed to parse message payload"); |
| return; |
| } |
| |
| this->receive(element); |
| } |
| |
| void |
| WebSocketTransport::schedulePing() |
| { |
| m_pingEventId = getScheduler().schedule(m_pingInterval, [this] { sendPing(); }); |
| } |
| |
| void |
| WebSocketTransport::sendPing() |
| { |
| NFD_LOG_FACE_TRACE(__func__); |
| |
| websocketpp::lib::error_code error; |
| m_server.ping(m_handle, "NFD-WebSocket", error); |
| if (error) |
| return processErrorCode(error); |
| |
| ++this->nOutPings; |
| |
| this->schedulePing(); |
| } |
| |
| void |
| WebSocketTransport::handlePong() |
| { |
| NFD_LOG_FACE_TRACE(__func__); |
| |
| ++this->nInPongs; |
| } |
| |
| void |
| WebSocketTransport::handlePongTimeout() |
| { |
| NFD_LOG_FACE_ERROR("Pong timeout"); |
| this->setState(TransportState::FAILED); |
| doClose(); |
| } |
| |
| void |
| WebSocketTransport::processErrorCode(const websocketpp::lib::error_code& error) |
| { |
| NFD_LOG_FACE_TRACE(__func__); |
| |
| if (getState() == TransportState::CLOSING || |
| getState() == TransportState::FAILED || |
| getState() == TransportState::CLOSED) |
| // transport is shutting down, ignore any errors |
| return; |
| |
| NFD_LOG_FACE_ERROR("Send or ping operation failed: " << error.message()); |
| this->setState(TransportState::FAILED); |
| doClose(); |
| } |
| |
| void |
| WebSocketTransport::doClose() |
| { |
| NFD_LOG_FACE_TRACE(__func__); |
| |
| m_pingEventId.cancel(); |
| |
| // use the non-throwing variant and ignore errors, if any |
| websocketpp::lib::error_code error; |
| m_server.close(m_handle, websocketpp::close::status::normal, "closed by NFD", error); |
| |
| this->setState(TransportState::CLOSED); |
| } |
| |
| } // namespace face |
| } // namespace nfd |