blob: cd2fb4f7e258d53197451f959aced6a23bffeef3 [file] [log] [blame]
Alexander Afanasyeva9034b02014-01-26 18:32:02 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
Alexander Afanasyev9bcbc7c2014-04-06 19:37:37 -07003 * Copyright (c) 2014 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 *
10 * This file is part of NFD (Named Data Networking Forwarding Daemon).
11 * See AUTHORS.md for complete list of NFD authors and contributors.
12 *
13 * NFD is free software: you can redistribute it and/or modify it under the terms
14 * of the GNU General Public License as published by the Free Software Foundation,
15 * either version 3 of the License, or (at your option) any later version.
16 *
17 * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
18 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
19 * PURPOSE. See the GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License along with
22 * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
23 **/
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080024
25#include "tcp-channel.hpp"
Junxiao Shi61e3cc52014-03-03 20:40:28 -070026#include "core/global-io.hpp"
27#include "core/face-uri.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
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -070086 shared_ptr<ndn::monotonic_deadline_timer> connectTimeoutTimer =
Alexander Afanasyevf6980282014-05-13 18:28:40 -070087 make_shared<ndn::monotonic_deadline_timer>(ref(getGlobalIoService()));
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,
91 clientSocket, connectTimeoutTimer,
92 onFaceCreated, onConnectFailed));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080093
94 connectTimeoutTimer->expires_from_now(timeout);
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080095 connectTimeoutTimer->async_wait(bind(&TcpChannel::handleFailedConnect, this, _1,
96 clientSocket, connectTimeoutTimer,
97 onConnectFailed));
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080098}
99
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800100void
101TcpChannel::connect(const std::string& remoteHost, const std::string& remotePort,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800102 const TcpChannel::FaceCreatedCallback& onFaceCreated,
103 const TcpChannel::ConnectFailedCallback& onConnectFailed,
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -0700104 const time::seconds& timeout/* = time::seconds(4)*/)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800105{
106 shared_ptr<ip::tcp::socket> clientSocket =
Alexander Afanasyevf6980282014-05-13 18:28:40 -0700107 make_shared<ip::tcp::socket>(ref(getGlobalIoService()));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800108
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -0700109 shared_ptr<ndn::monotonic_deadline_timer> connectTimeoutTimer =
Alexander Afanasyevf6980282014-05-13 18:28:40 -0700110 make_shared<ndn::monotonic_deadline_timer>(ref(getGlobalIoService()));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800111
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800112 ip::tcp::resolver::query query(remoteHost, remotePort);
113 shared_ptr<ip::tcp::resolver> resolver =
Alexander Afanasyevf6980282014-05-13 18:28:40 -0700114 make_shared<ip::tcp::resolver>(ref(getGlobalIoService()));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800115
116 resolver->async_resolve(query,
Davide Pesavento855bf292014-02-27 03:58:01 +0100117 bind(&TcpChannel::handleEndpointResolution, this, _1, _2,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800118 clientSocket, connectTimeoutTimer,
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800119 onFaceCreated, onConnectFailed,
120 resolver));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800121
122 connectTimeoutTimer->expires_from_now(timeout);
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800123 connectTimeoutTimer->async_wait(bind(&TcpChannel::handleFailedConnect, this, _1,
124 clientSocket, connectTimeoutTimer,
125 onConnectFailed));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800126}
127
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -0800128size_t
129TcpChannel::size() const
130{
131 return m_channelFaces.size();
132}
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800133
134void
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800135TcpChannel::createFace(const shared_ptr<ip::tcp::socket>& socket,
Alexander Afanasyev355c0662014-03-20 18:08:17 -0700136 const FaceCreatedCallback& onFaceCreated,
137 bool isOnDemand)
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800138{
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800139 tcp::Endpoint remoteEndpoint = socket->remote_endpoint();
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -0800140
Alexander Afanasyevbd220a02014-02-20 00:29:56 -0800141 shared_ptr<Face> face;
Alexander Afanasyevbfd31de2014-02-26 13:20:08 -0800142 if (socket->local_endpoint().address().is_loopback())
Alexander Afanasyevf6980282014-05-13 18:28:40 -0700143 face = make_shared<TcpLocalFace>(socket, isOnDemand);
Alexander Afanasyevbd220a02014-02-20 00:29:56 -0800144 else
Alexander Afanasyevf6980282014-05-13 18:28:40 -0700145 face = make_shared<TcpFace>(socket, isOnDemand);
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700146
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -0800147 face->onFail += bind(&TcpChannel::afterFaceFailed, this, remoteEndpoint);
148
149 onFaceCreated(face);
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800150 m_channelFaces[remoteEndpoint] = face;
151}
152
153void
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -0800154TcpChannel::afterFaceFailed(tcp::Endpoint &remoteEndpoint)
155{
156 NFD_LOG_DEBUG("afterFaceFailed: " << remoteEndpoint);
157 m_channelFaces.erase(remoteEndpoint);
158}
159
160void
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800161TcpChannel::handleSuccessfulAccept(const boost::system::error_code& error,
162 const shared_ptr<boost::asio::ip::tcp::socket>& socket,
163 const FaceCreatedCallback& onFaceCreated,
164 const ConnectFailedCallback& onAcceptFailed)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800165{
166 if (error) {
167 if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
168 return;
169
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800170 NFD_LOG_DEBUG("Connect to remote endpoint failed: "
171 << error.category().message(error.value()));
Alexander Afanasyev5f1ec252014-02-28 10:59:17 -0800172
173 if (static_cast<bool>(onAcceptFailed))
174 onAcceptFailed("Connect to remote endpoint failed: " +
175 error.category().message(error.value()));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800176 return;
177 }
178
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800179 // prepare accepting the next connection
180 shared_ptr<ip::tcp::socket> clientSocket =
Alexander Afanasyevf6980282014-05-13 18:28:40 -0700181 make_shared<ip::tcp::socket>(ref(getGlobalIoService()));
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800182 m_acceptor->async_accept(*clientSocket,
183 bind(&TcpChannel::handleSuccessfulAccept, this, _1,
184 clientSocket,
185 onFaceCreated, onAcceptFailed));
Alexander Afanasyev334b7142014-01-28 12:59:39 -0800186
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800187 NFD_LOG_DEBUG("[" << m_localEndpoint << "] "
188 "<< Connection from " << socket->remote_endpoint());
Alexander Afanasyev355c0662014-03-20 18:08:17 -0700189 createFace(socket, onFaceCreated, true);
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800190}
191
192void
193TcpChannel::handleSuccessfulConnect(const boost::system::error_code& error,
194 const shared_ptr<ip::tcp::socket>& socket,
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -0700195 const shared_ptr<ndn::monotonic_deadline_timer>& timer,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800196 const FaceCreatedCallback& onFaceCreated,
197 const ConnectFailedCallback& onConnectFailed)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800198{
199 timer->cancel();
200
201 if (error) {
202 if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
203 return;
204
205 socket->close();
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700206
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800207 NFD_LOG_DEBUG("Connect to remote endpoint failed: "
208 << error.category().message(error.value()));
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700209
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800210 onConnectFailed("Connect to remote endpoint failed: " +
211 error.category().message(error.value()));
212 return;
213 }
214
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800215 NFD_LOG_DEBUG("[" << m_localEndpoint << "] "
216 ">> Connection to " << socket->remote_endpoint());
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700217
Alexander Afanasyev355c0662014-03-20 18:08:17 -0700218 createFace(socket, onFaceCreated, false);
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800219}
220
221void
222TcpChannel::handleFailedConnect(const boost::system::error_code& error,
223 const shared_ptr<ip::tcp::socket>& socket,
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -0700224 const shared_ptr<ndn::monotonic_deadline_timer>& timer,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800225 const ConnectFailedCallback& onConnectFailed)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800226{
227 if (error) { // e.g., cancelled
228 return;
229 }
230
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800231 NFD_LOG_DEBUG("Connect to remote endpoint timed out: "
232 << error.category().message(error.value()));
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700233
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800234 onConnectFailed("Connect to remote endpoint timed out: " +
235 error.category().message(error.value()));
236 socket->close(); // abort the connection
237}
238
239void
Davide Pesavento855bf292014-02-27 03:58:01 +0100240TcpChannel::handleEndpointResolution(const boost::system::error_code& error,
241 ip::tcp::resolver::iterator remoteEndpoint,
242 const shared_ptr<boost::asio::ip::tcp::socket>& socket,
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -0700243 const shared_ptr<ndn::monotonic_deadline_timer>& timer,
Davide Pesavento855bf292014-02-27 03:58:01 +0100244 const FaceCreatedCallback& onFaceCreated,
245 const ConnectFailedCallback& onConnectFailed,
246 const shared_ptr<ip::tcp::resolver>& resolver)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800247{
248 if (error ||
249 remoteEndpoint == ip::tcp::resolver::iterator())
250 {
251 if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
252 return;
253
254 socket->close();
255 timer->cancel();
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800256
257 NFD_LOG_DEBUG("Remote endpoint hostname or port cannot be resolved: "
258 << error.category().message(error.value()));
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700259
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800260 onConnectFailed("Remote endpoint hostname or port cannot be resolved: " +
261 error.category().message(error.value()));
262 return;
263 }
264
265 // got endpoint, now trying to connect (only try the first resolution option)
266 socket->async_connect(*remoteEndpoint,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800267 bind(&TcpChannel::handleSuccessfulConnect, this, _1,
268 socket, timer,
269 onFaceCreated, onConnectFailed));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800270}
271
Alexander Afanasyev18bbf812014-01-29 01:40:23 -0800272} // namespace nfd