blob: 0a6ce7850e8203cd49244e2fecd02c5c44d90667 [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"
Junxiao Shi61e3cc52014-03-03 20:40:28 -070027#include "core/global-io.hpp"
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080028
Alexander Afanasyev18bbf812014-01-29 01:40:23 -080029namespace nfd {
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080030
Alexander Afanasyev3958b012014-01-31 15:06:13 -080031NFD_LOG_INIT("TcpChannel");
32
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080033using namespace boost::asio;
34
Junxiao Shi61e3cc52014-03-03 20:40:28 -070035TcpChannel::TcpChannel(const tcp::Endpoint& localEndpoint)
36 : m_localEndpoint(localEndpoint)
Alexander Afanasyev53a6fd32014-03-23 00:00:04 -070037 , m_isListening(false)
Junxiao Shi61e3cc52014-03-03 20:40:28 -070038{
39 this->setUri(FaceUri(localEndpoint));
40}
41
42TcpChannel::~TcpChannel()
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080043{
44}
45
46void
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080047TcpChannel::listen(const FaceCreatedCallback& onFaceCreated,
48 const ConnectFailedCallback& onAcceptFailed,
49 int backlog/* = tcp::acceptor::max_connections*/)
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080050{
Alexander Afanasyevf6980282014-05-13 18:28:40 -070051 m_acceptor = make_shared<ip::tcp::acceptor>(ref(getGlobalIoService()));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080052 m_acceptor->open(m_localEndpoint.protocol());
53 m_acceptor->set_option(ip::tcp::acceptor::reuse_address(true));
Alexander Afanasyeveee71fd2014-03-13 17:40:33 -070054 if (m_localEndpoint.address().is_v6())
55 {
56 m_acceptor->set_option(ip::v6_only(true));
57 }
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080058 m_acceptor->bind(m_localEndpoint);
59 m_acceptor->listen(backlog);
60
61 shared_ptr<ip::tcp::socket> clientSocket =
Alexander Afanasyevf6980282014-05-13 18:28:40 -070062 make_shared<ip::tcp::socket>(ref(getGlobalIoService()));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080063 m_acceptor->async_accept(*clientSocket,
Alexander Afanasyeva16abd22014-01-31 18:12:29 -080064 bind(&TcpChannel::handleSuccessfulAccept, this, _1,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080065 clientSocket,
Alexander Afanasyeva16abd22014-01-31 18:12:29 -080066 onFaceCreated, onAcceptFailed));
Alexander Afanasyev53a6fd32014-03-23 00:00:04 -070067
68 m_isListening = true;
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080069}
70
71void
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080072TcpChannel::connect(const tcp::Endpoint& remoteEndpoint,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080073 const TcpChannel::FaceCreatedCallback& onFaceCreated,
74 const TcpChannel::ConnectFailedCallback& onConnectFailed,
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -070075 const time::seconds& timeout/* = time::seconds(4)*/)
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080076{
Alexander Afanasyev334b7142014-01-28 12:59:39 -080077 ChannelFaceMap::iterator i = m_channelFaces.find(remoteEndpoint);
78 if (i != m_channelFaces.end()) {
79 onFaceCreated(i->second);
80 return;
81 }
82
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080083 shared_ptr<ip::tcp::socket> clientSocket =
Alexander Afanasyevf6980282014-05-13 18:28:40 -070084 make_shared<ip::tcp::socket>(ref(getGlobalIoService()));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080085
Junxiao Shi1e46be32015-01-08 20:18:05 -070086 scheduler::EventId connectTimeoutEvent = scheduler::schedule(timeout,
87 bind(&TcpChannel::handleFailedConnect, this, clientSocket, onConnectFailed));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080088
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080089 clientSocket->async_connect(remoteEndpoint,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080090 bind(&TcpChannel::handleSuccessfulConnect, this, _1,
Alexander Afanasyev18861802014-06-13 17:49:03 -070091 clientSocket, connectTimeoutEvent,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080092 onFaceCreated, onConnectFailed));
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080093}
94
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -080095size_t
96TcpChannel::size() const
97{
98 return m_channelFaces.size();
99}
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800100
101void
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800102TcpChannel::createFace(const shared_ptr<ip::tcp::socket>& socket,
Alexander Afanasyev355c0662014-03-20 18:08:17 -0700103 const FaceCreatedCallback& onFaceCreated,
104 bool isOnDemand)
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800105{
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800106 tcp::Endpoint remoteEndpoint = socket->remote_endpoint();
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -0800107
Alexander Afanasyevbd220a02014-02-20 00:29:56 -0800108 shared_ptr<Face> face;
Steve DiBenedetto2aa4eca2014-06-20 10:47:40 -0600109
110 ChannelFaceMap::iterator faceMapPos = m_channelFaces.find(remoteEndpoint);
111 if (faceMapPos == m_channelFaces.end())
112 {
113 if (socket->local_endpoint().address().is_loopback())
114 face = make_shared<TcpLocalFace>(socket, isOnDemand);
115 else
116 face = make_shared<TcpFace>(socket, isOnDemand);
117
Junxiao Shic099ddb2014-12-25 20:53:20 -0700118 face->onFail.connectSingleShot(bind(&TcpChannel::afterFaceFailed, this, remoteEndpoint));
Steve DiBenedetto2aa4eca2014-06-20 10:47:40 -0600119
120 m_channelFaces[remoteEndpoint] = face;
121 }
Alexander Afanasyevbd220a02014-02-20 00:29:56 -0800122 else
Steve DiBenedetto2aa4eca2014-06-20 10:47:40 -0600123 {
124 // we've already created a a face for this endpoint, just reuse it
125 face = faceMapPos->second;
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700126
Steve DiBenedetto2aa4eca2014-06-20 10:47:40 -0600127 boost::system::error_code error;
128 socket->shutdown(ip::tcp::socket::shutdown_both, error);
129 socket->close(error);
130 }
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -0800131
Steve DiBenedetto2aa4eca2014-06-20 10:47:40 -0600132 // Need to invoke the callback regardless of whether or not we have already created
133 // the face so that control responses and such can be sent.
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -0800134 onFaceCreated(face);
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800135}
136
137void
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -0800138TcpChannel::afterFaceFailed(tcp::Endpoint &remoteEndpoint)
139{
140 NFD_LOG_DEBUG("afterFaceFailed: " << remoteEndpoint);
141 m_channelFaces.erase(remoteEndpoint);
142}
143
144void
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800145TcpChannel::handleSuccessfulAccept(const boost::system::error_code& error,
146 const shared_ptr<boost::asio::ip::tcp::socket>& socket,
147 const FaceCreatedCallback& onFaceCreated,
148 const ConnectFailedCallback& onAcceptFailed)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800149{
150 if (error) {
151 if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
152 return;
153
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800154 NFD_LOG_DEBUG("Connect to remote endpoint failed: "
155 << error.category().message(error.value()));
Alexander Afanasyev5f1ec252014-02-28 10:59:17 -0800156
157 if (static_cast<bool>(onAcceptFailed))
158 onAcceptFailed("Connect to remote endpoint failed: " +
159 error.category().message(error.value()));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800160 return;
161 }
162
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800163 // prepare accepting the next connection
164 shared_ptr<ip::tcp::socket> clientSocket =
Alexander Afanasyevf6980282014-05-13 18:28:40 -0700165 make_shared<ip::tcp::socket>(ref(getGlobalIoService()));
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800166 m_acceptor->async_accept(*clientSocket,
167 bind(&TcpChannel::handleSuccessfulAccept, this, _1,
168 clientSocket,
169 onFaceCreated, onAcceptFailed));
Alexander Afanasyev334b7142014-01-28 12:59:39 -0800170
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800171 NFD_LOG_DEBUG("[" << m_localEndpoint << "] "
172 "<< Connection from " << socket->remote_endpoint());
Alexander Afanasyev355c0662014-03-20 18:08:17 -0700173 createFace(socket, onFaceCreated, true);
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800174}
175
176void
177TcpChannel::handleSuccessfulConnect(const boost::system::error_code& error,
178 const shared_ptr<ip::tcp::socket>& socket,
Junxiao Shi1e46be32015-01-08 20:18:05 -0700179 const scheduler::EventId& connectTimeoutEvent,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800180 const FaceCreatedCallback& onFaceCreated,
181 const ConnectFailedCallback& onConnectFailed)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800182{
Alexander Afanasyev18861802014-06-13 17:49:03 -0700183 scheduler::cancel(connectTimeoutEvent);
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800184
Alexander Afanasyev7465d5f2014-07-07 10:19:42 -0700185#if (BOOST_VERSION == 105400)
186 // To handle regression in Boost 1.54
187 // https://svn.boost.org/trac/boost/ticket/8795
188 boost::system::error_code anotherErrorCode;
189 socket->remote_endpoint(anotherErrorCode);
190 if (error || anotherErrorCode) {
191#else
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800192 if (error) {
Alexander Afanasyev7465d5f2014-07-07 10:19:42 -0700193#endif
194
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800195 if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
196 return;
197
198 socket->close();
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700199
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800200 NFD_LOG_DEBUG("Connect to remote endpoint failed: "
201 << error.category().message(error.value()));
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700202
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800203 onConnectFailed("Connect to remote endpoint failed: " +
204 error.category().message(error.value()));
205 return;
206 }
207
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800208 NFD_LOG_DEBUG("[" << m_localEndpoint << "] "
209 ">> Connection to " << socket->remote_endpoint());
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700210
Alexander Afanasyev355c0662014-03-20 18:08:17 -0700211 createFace(socket, onFaceCreated, false);
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800212}
213
214void
Alexander Afanasyev18861802014-06-13 17:49:03 -0700215TcpChannel::handleFailedConnect(const shared_ptr<ip::tcp::socket>& socket,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800216 const ConnectFailedCallback& onConnectFailed)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800217{
Alexander Afanasyev18861802014-06-13 17:49:03 -0700218 NFD_LOG_DEBUG("Connect to remote endpoint timed out");
219 onConnectFailed("Connect to remote endpoint timed out");
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800220 socket->close(); // abort the connection
221}
222
Alexander Afanasyev18bbf812014-01-29 01:40:23 -0800223} // namespace nfd