blob: 637cfa3802bce362cbf9170572dd17481aadfc15 [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 Pesavento6ad890a2015-03-09 03:43:17 +010037 : m_acceptor(getGlobalIoService())
38 , m_localEndpoint(localEndpoint)
Junxiao Shi61e3cc52014-03-03 20:40:28 -070039{
Davide Pesavento6ad890a2015-03-09 03:43:17 +010040 setUri(FaceUri(m_localEndpoint));
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080041}
42
43void
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080044TcpChannel::listen(const FaceCreatedCallback& onFaceCreated,
45 const ConnectFailedCallback& onAcceptFailed,
46 int backlog/* = tcp::acceptor::max_connections*/)
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080047{
Davide Pesavento6ad890a2015-03-09 03:43:17 +010048 if (isListening()) {
49 NFD_LOG_WARN("[" << m_localEndpoint << "] Already listening");
50 return;
51 }
52
53 m_acceptor.open(m_localEndpoint.protocol());
54 m_acceptor.set_option(ip::tcp::acceptor::reuse_address(true));
Alexander Afanasyeveee71fd2014-03-13 17:40:33 -070055 if (m_localEndpoint.address().is_v6())
Davide Pesavento6ad890a2015-03-09 03:43:17 +010056 m_acceptor.set_option(ip::v6_only(true));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080057
Davide Pesavento6ad890a2015-03-09 03:43:17 +010058 m_acceptor.bind(m_localEndpoint);
59 m_acceptor.listen(backlog);
Alexander Afanasyev53a6fd32014-03-23 00:00:04 -070060
Davide Pesavento6ad890a2015-03-09 03:43:17 +010061 // start accepting connections
62 accept(onFaceCreated, onAcceptFailed);
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080063}
64
65void
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080066TcpChannel::connect(const tcp::Endpoint& remoteEndpoint,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080067 const TcpChannel::FaceCreatedCallback& onFaceCreated,
68 const TcpChannel::ConnectFailedCallback& onConnectFailed,
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -070069 const time::seconds& timeout/* = time::seconds(4)*/)
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080070{
Davide Pesavento6ad890a2015-03-09 03:43:17 +010071 auto it = m_channelFaces.find(remoteEndpoint);
72 if (it != m_channelFaces.end()) {
73 onFaceCreated(it->second);
Alexander Afanasyev334b7142014-01-28 12:59:39 -080074 return;
75 }
76
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080077 shared_ptr<ip::tcp::socket> clientSocket =
Alexander Afanasyevf6980282014-05-13 18:28:40 -070078 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 Pesavento6ad890a2015-03-09 03:43:17 +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
Alexander Afanasyeva16abd22014-01-31 18:12:29 -080097TcpChannel::createFace(const shared_ptr<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 Afanasyeva16abd22014-01-31 18:12:29 -0800101 tcp::Endpoint remoteEndpoint = socket->remote_endpoint();
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -0800102
Alexander Afanasyevbd220a02014-02-20 00:29:56 -0800103 shared_ptr<Face> face;
Steve DiBenedetto2aa4eca2014-06-20 10:47:40 -0600104
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100105 auto it = m_channelFaces.find(remoteEndpoint);
106 if (it == m_channelFaces.end())
Steve DiBenedetto2aa4eca2014-06-20 10:47:40 -0600107 {
108 if (socket->local_endpoint().address().is_loopback())
109 face = make_shared<TcpLocalFace>(socket, isOnDemand);
110 else
111 face = make_shared<TcpFace>(socket, isOnDemand);
112
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100113 face->onFail.connectSingleShot([this, remoteEndpoint] (const std::string&) {
114 NFD_LOG_TRACE("Erasing " << remoteEndpoint << " from channel face map");
115 m_channelFaces.erase(remoteEndpoint);
116 });
Steve DiBenedetto2aa4eca2014-06-20 10:47:40 -0600117
118 m_channelFaces[remoteEndpoint] = face;
119 }
Alexander Afanasyevbd220a02014-02-20 00:29:56 -0800120 else
Steve DiBenedetto2aa4eca2014-06-20 10:47:40 -0600121 {
122 // we've already created a a face for this endpoint, just reuse it
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100123 face = it->second;
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700124
Steve DiBenedetto2aa4eca2014-06-20 10:47:40 -0600125 boost::system::error_code error;
126 socket->shutdown(ip::tcp::socket::shutdown_both, error);
127 socket->close(error);
128 }
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -0800129
Steve DiBenedetto2aa4eca2014-06-20 10:47:40 -0600130 // Need to invoke the callback regardless of whether or not we have already created
131 // the face so that control responses and such can be sent.
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -0800132 onFaceCreated(face);
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800133}
134
135void
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100136TcpChannel::accept(const FaceCreatedCallback& onFaceCreated,
137 const ConnectFailedCallback& onAcceptFailed)
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -0800138{
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100139 auto socket = make_shared<ip::tcp::socket>(ref(getGlobalIoService()));
140
141 m_acceptor.async_accept(*socket,
142 bind(&TcpChannel::handleAccept, this,
143 boost::asio::placeholders::error,
144 socket, onFaceCreated, onAcceptFailed));
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -0800145}
146
147void
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100148TcpChannel::handleAccept(const boost::system::error_code& error,
149 const shared_ptr<boost::asio::ip::tcp::socket>& socket,
150 const FaceCreatedCallback& onFaceCreated,
151 const ConnectFailedCallback& onAcceptFailed)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800152{
153 if (error) {
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100154 if (error == boost::asio::error::operation_aborted) // when the socket is closed by someone
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800155 return;
156
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100157 NFD_LOG_DEBUG("[" << m_localEndpoint << "] Accept failed: " << error.message());
158 if (onAcceptFailed)
159 onAcceptFailed(error.message());
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800160 return;
161 }
162
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100163 NFD_LOG_DEBUG("[" << m_localEndpoint << "] Connection from " << socket->remote_endpoint());
Alexander Afanasyev334b7142014-01-28 12:59:39 -0800164
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100165 // prepare accepting the next connection
166 accept(onFaceCreated, onAcceptFailed);
167
Alexander Afanasyev355c0662014-03-20 18:08:17 -0700168 createFace(socket, onFaceCreated, true);
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800169}
170
171void
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100172TcpChannel::handleConnect(const boost::system::error_code& error,
173 const shared_ptr<ip::tcp::socket>& socket,
174 const scheduler::EventId& connectTimeoutEvent,
175 const FaceCreatedCallback& onFaceCreated,
176 const ConnectFailedCallback& onConnectFailed)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800177{
Alexander Afanasyev18861802014-06-13 17:49:03 -0700178 scheduler::cancel(connectTimeoutEvent);
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800179
Alexander Afanasyev7465d5f2014-07-07 10:19:42 -0700180#if (BOOST_VERSION == 105400)
181 // To handle regression in Boost 1.54
182 // https://svn.boost.org/trac/boost/ticket/8795
183 boost::system::error_code anotherErrorCode;
184 socket->remote_endpoint(anotherErrorCode);
185 if (error || anotherErrorCode) {
186#else
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800187 if (error) {
Alexander Afanasyev7465d5f2014-07-07 10:19:42 -0700188#endif
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100189 if (error == boost::asio::error::operation_aborted) // when the socket is closed by someone
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800190 return;
191
192 socket->close();
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700193
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100194 NFD_LOG_DEBUG("[" << m_localEndpoint << "] Connect failed: " << error.message());
195 if (onConnectFailed)
196 onConnectFailed(error.message());
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800197 return;
198 }
199
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100200 NFD_LOG_DEBUG("[" << m_localEndpoint << "] Connected to " << socket->remote_endpoint());
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700201
Alexander Afanasyev355c0662014-03-20 18:08:17 -0700202 createFace(socket, onFaceCreated, false);
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800203}
204
205void
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100206TcpChannel::handleConnectTimeout(const shared_ptr<ip::tcp::socket>& socket,
207 const ConnectFailedCallback& onConnectFailed)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800208{
Alexander Afanasyev18861802014-06-13 17:49:03 -0700209 NFD_LOG_DEBUG("Connect to remote endpoint timed out");
Davide Pesavento6ad890a2015-03-09 03:43:17 +0100210
211 // abort the connection attempt
212 boost::system::error_code error;
213 socket->close(error);
214
215 if (onConnectFailed)
216 onConnectFailed("Connect to remote endpoint timed out");
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800217}
218
Alexander Afanasyev18bbf812014-01-29 01:40:23 -0800219} // namespace nfd