blob: b46e92cc154326953ba16fd1f9e35254bd8f60c7 [file] [log] [blame]
Alexander Afanasyeva9034b02014-01-26 18:32:02 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Junxiao Shi1e46be32015-01-08 20:18:05 -07003 * 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.
Alexander Afanasyev9bcbc7c2014-04-06 19:37:37 -070010 *
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/>.
Junxiao Shia1937bf2014-11-06 11:43:40 -070024 */
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080025
26#include "tcp-channel.hpp"
Davide Pesavento6ad890a2015-03-09 03:43:17 +010027#include "tcp-face.hpp"
Junxiao Shi61e3cc52014-03-03 20:40:28 -070028#include "core/global-io.hpp"
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080029
Alexander Afanasyev18bbf812014-01-29 01:40:23 -080030namespace nfd {
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080031
Alexander Afanasyev3958b012014-01-31 15:06:13 -080032NFD_LOG_INIT("TcpChannel");
33
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080034using namespace boost::asio;
35
Junxiao Shi61e3cc52014-03-03 20:40:28 -070036TcpChannel::TcpChannel(const tcp::Endpoint& localEndpoint)
Davide Pesavento292e5e12015-03-13 02:08:33 +010037 : m_localEndpoint(localEndpoint)
38 , m_acceptor(getGlobalIoService())
39 , m_acceptSocket(getGlobalIoService())
Junxiao Shi61e3cc52014-03-03 20:40:28 -070040{
Davide Pesavento6ad890a2015-03-09 03:43:17 +010041 setUri(FaceUri(m_localEndpoint));
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080042}
43
44void
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080045TcpChannel::listen(const FaceCreatedCallback& onFaceCreated,
46 const ConnectFailedCallback& onAcceptFailed,
47 int backlog/* = tcp::acceptor::max_connections*/)
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080048{
Davide Pesavento6ad890a2015-03-09 03:43:17 +010049 if (isListening()) {
50 NFD_LOG_WARN("[" << m_localEndpoint << "] Already listening");
51 return;
52 }
53
54 m_acceptor.open(m_localEndpoint.protocol());
55 m_acceptor.set_option(ip::tcp::acceptor::reuse_address(true));
Alexander Afanasyeveee71fd2014-03-13 17:40:33 -070056 if (m_localEndpoint.address().is_v6())
Davide Pesavento6ad890a2015-03-09 03:43:17 +010057 m_acceptor.set_option(ip::v6_only(true));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080058
Davide Pesavento6ad890a2015-03-09 03:43:17 +010059 m_acceptor.bind(m_localEndpoint);
60 m_acceptor.listen(backlog);
Alexander Afanasyev53a6fd32014-03-23 00:00:04 -070061
Davide Pesavento6ad890a2015-03-09 03:43:17 +010062 // start accepting connections
63 accept(onFaceCreated, onAcceptFailed);
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080064}
65
66void
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080067TcpChannel::connect(const tcp::Endpoint& remoteEndpoint,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080068 const TcpChannel::FaceCreatedCallback& onFaceCreated,
69 const TcpChannel::ConnectFailedCallback& onConnectFailed,
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -070070 const time::seconds& timeout/* = time::seconds(4)*/)
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080071{
Davide Pesavento6ad890a2015-03-09 03:43:17 +010072 auto it = m_channelFaces.find(remoteEndpoint);
73 if (it != m_channelFaces.end()) {
74 onFaceCreated(it->second);
Alexander Afanasyev334b7142014-01-28 12:59:39 -080075 return;
76 }
77
Davide Pesavento292e5e12015-03-13 02:08:33 +010078 auto clientSocket = make_shared<ip::tcp::socket>(ref(getGlobalIoService()));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080079
Junxiao Shi1e46be32015-01-08 20:18:05 -070080 scheduler::EventId connectTimeoutEvent = scheduler::schedule(timeout,
Davide Pesavento292e5e12015-03-13 02:08:33 +010081 bind(&TcpChannel::handleConnectTimeout, this, clientSocket, onConnectFailed));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080082
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080083 clientSocket->async_connect(remoteEndpoint,
Davide Pesavento6ad890a2015-03-09 03:43:17 +010084 bind(&TcpChannel::handleConnect, this,
85 boost::asio::placeholders::error,
Alexander Afanasyev18861802014-06-13 17:49:03 -070086 clientSocket, connectTimeoutEvent,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080087 onFaceCreated, onConnectFailed));
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080088}
89
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -080090size_t
91TcpChannel::size() const
92{
93 return m_channelFaces.size();
94}
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080095
96void
Davide Pesavento292e5e12015-03-13 02:08:33 +010097TcpChannel::createFace(ip::tcp::socket socket,
Alexander Afanasyev355c0662014-03-20 18:08:17 -070098 const FaceCreatedCallback& onFaceCreated,
99 bool isOnDemand)
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800100{
Alexander Afanasyevbd220a02014-02-20 00:29:56 -0800101 shared_ptr<Face> face;
Davide Pesavento292e5e12015-03-13 02:08:33 +0100102 tcp::Endpoint remoteEndpoint = socket.remote_endpoint();
Steve DiBenedetto2aa4eca2014-06-20 10:47:40 -0600103
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100104 auto it = m_channelFaces.find(remoteEndpoint);
Davide Pesavento292e5e12015-03-13 02:08:33 +0100105 if (it == m_channelFaces.end()) {
106 tcp::Endpoint localEndpoint = socket.local_endpoint();
Steve DiBenedetto2aa4eca2014-06-20 10:47:40 -0600107
Davide Pesavento292e5e12015-03-13 02:08:33 +0100108 if (localEndpoint.address().is_loopback() &&
109 remoteEndpoint.address().is_loopback())
110 face = make_shared<TcpLocalFace>(FaceUri(remoteEndpoint), FaceUri(localEndpoint),
111 std::move(socket), isOnDemand);
112 else
113 face = make_shared<TcpFace>(FaceUri(remoteEndpoint), FaceUri(localEndpoint),
114 std::move(socket), isOnDemand);
Steve DiBenedetto2aa4eca2014-06-20 10:47:40 -0600115
Davide Pesavento292e5e12015-03-13 02:08:33 +0100116 face->onFail.connectSingleShot([this, remoteEndpoint] (const std::string&) {
117 NFD_LOG_TRACE("Erasing " << remoteEndpoint << " from channel face map");
118 m_channelFaces.erase(remoteEndpoint);
119 });
120 m_channelFaces[remoteEndpoint] = face;
121 }
122 else {
123 // we already have a face for this endpoint, just reuse it
124 face = it->second;
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700125
Davide Pesavento292e5e12015-03-13 02:08:33 +0100126 boost::system::error_code error;
127 socket.shutdown(ip::tcp::socket::shutdown_both, error);
128 socket.close(error);
129 }
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -0800130
Steve DiBenedetto2aa4eca2014-06-20 10:47:40 -0600131 // Need to invoke the callback regardless of whether or not we have already created
132 // the face so that control responses and such can be sent.
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -0800133 onFaceCreated(face);
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800134}
135
136void
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100137TcpChannel::accept(const FaceCreatedCallback& onFaceCreated,
138 const ConnectFailedCallback& onAcceptFailed)
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -0800139{
Davide Pesavento292e5e12015-03-13 02:08:33 +0100140 m_acceptor.async_accept(m_acceptSocket, bind(&TcpChannel::handleAccept, this,
141 boost::asio::placeholders::error,
142 onFaceCreated, onAcceptFailed));
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -0800143}
144
145void
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100146TcpChannel::handleAccept(const boost::system::error_code& error,
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100147 const FaceCreatedCallback& onFaceCreated,
148 const ConnectFailedCallback& onAcceptFailed)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800149{
150 if (error) {
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100151 if (error == boost::asio::error::operation_aborted) // when the socket is closed by someone
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800152 return;
153
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100154 NFD_LOG_DEBUG("[" << m_localEndpoint << "] Accept failed: " << error.message());
155 if (onAcceptFailed)
156 onAcceptFailed(error.message());
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800157 return;
158 }
159
Davide Pesavento292e5e12015-03-13 02:08:33 +0100160 NFD_LOG_DEBUG("[" << m_localEndpoint << "] Connection from " << m_acceptSocket.remote_endpoint());
161
162 createFace(std::move(m_acceptSocket), onFaceCreated, true);
Alexander Afanasyev334b7142014-01-28 12:59:39 -0800163
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100164 // prepare accepting the next connection
165 accept(onFaceCreated, onAcceptFailed);
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800166}
167
168void
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100169TcpChannel::handleConnect(const boost::system::error_code& error,
170 const shared_ptr<ip::tcp::socket>& socket,
171 const scheduler::EventId& connectTimeoutEvent,
172 const FaceCreatedCallback& onFaceCreated,
173 const ConnectFailedCallback& onConnectFailed)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800174{
Alexander Afanasyev18861802014-06-13 17:49:03 -0700175 scheduler::cancel(connectTimeoutEvent);
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800176
Alexander Afanasyev7465d5f2014-07-07 10:19:42 -0700177#if (BOOST_VERSION == 105400)
178 // To handle regression in Boost 1.54
179 // https://svn.boost.org/trac/boost/ticket/8795
180 boost::system::error_code anotherErrorCode;
181 socket->remote_endpoint(anotherErrorCode);
182 if (error || anotherErrorCode) {
183#else
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800184 if (error) {
Alexander Afanasyev7465d5f2014-07-07 10:19:42 -0700185#endif
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100186 if (error == boost::asio::error::operation_aborted) // when the socket is closed by someone
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800187 return;
188
Davide Pesavento292e5e12015-03-13 02:08:33 +0100189 NFD_LOG_WARN("[" << m_localEndpoint << "] Connect failed: " << error.message());
190
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800191 socket->close();
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700192
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100193 if (onConnectFailed)
194 onConnectFailed(error.message());
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800195 return;
196 }
197
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100198 NFD_LOG_DEBUG("[" << m_localEndpoint << "] Connected to " << socket->remote_endpoint());
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700199
Davide Pesavento292e5e12015-03-13 02:08:33 +0100200 createFace(std::move(*socket), onFaceCreated, false);
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800201}
202
203void
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100204TcpChannel::handleConnectTimeout(const shared_ptr<ip::tcp::socket>& socket,
205 const ConnectFailedCallback& onConnectFailed)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800206{
Alexander Afanasyev18861802014-06-13 17:49:03 -0700207 NFD_LOG_DEBUG("Connect to remote endpoint timed out");
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100208
209 // abort the connection attempt
210 boost::system::error_code error;
211 socket->close(error);
212
213 if (onConnectFailed)
214 onConnectFailed("Connect to remote endpoint timed out");
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800215}
216
Alexander Afanasyev18bbf812014-01-29 01:40:23 -0800217} // namespace nfd