blob: 4c438becfa8be098c3c688da3167369e7e8e054e [file] [log] [blame]
Alexander Afanasyeva9034b02014-01-26 18:32:02 -08001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (C) 2014 Named Data Networking Project
4 * See COPYING for copyright and distribution information.
5 */
6
7#include "tcp-channel.hpp"
Junxiao Shi61e3cc52014-03-03 20:40:28 -07008#include "core/global-io.hpp"
9#include "core/face-uri.hpp"
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080010
Alexander Afanasyev18bbf812014-01-29 01:40:23 -080011namespace nfd {
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080012
Alexander Afanasyev3958b012014-01-31 15:06:13 -080013NFD_LOG_INIT("TcpChannel");
14
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080015using namespace boost::asio;
16
Junxiao Shi61e3cc52014-03-03 20:40:28 -070017TcpChannel::TcpChannel(const tcp::Endpoint& localEndpoint)
18 : m_localEndpoint(localEndpoint)
Alexander Afanasyev53a6fd32014-03-23 00:00:04 -070019 , m_isListening(false)
Junxiao Shi61e3cc52014-03-03 20:40:28 -070020{
21 this->setUri(FaceUri(localEndpoint));
22}
23
24TcpChannel::~TcpChannel()
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080025{
26}
27
28void
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080029TcpChannel::listen(const FaceCreatedCallback& onFaceCreated,
30 const ConnectFailedCallback& onAcceptFailed,
31 int backlog/* = tcp::acceptor::max_connections*/)
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080032{
Junxiao Shi61e3cc52014-03-03 20:40:28 -070033 m_acceptor = make_shared<ip::tcp::acceptor>(boost::ref(getGlobalIoService()));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080034 m_acceptor->open(m_localEndpoint.protocol());
35 m_acceptor->set_option(ip::tcp::acceptor::reuse_address(true));
Alexander Afanasyeveee71fd2014-03-13 17:40:33 -070036 if (m_localEndpoint.address().is_v6())
37 {
38 m_acceptor->set_option(ip::v6_only(true));
39 }
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080040 m_acceptor->bind(m_localEndpoint);
41 m_acceptor->listen(backlog);
42
43 shared_ptr<ip::tcp::socket> clientSocket =
Junxiao Shi61e3cc52014-03-03 20:40:28 -070044 make_shared<ip::tcp::socket>(boost::ref(getGlobalIoService()));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080045 m_acceptor->async_accept(*clientSocket,
Alexander Afanasyeva16abd22014-01-31 18:12:29 -080046 bind(&TcpChannel::handleSuccessfulAccept, this, _1,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080047 clientSocket,
Alexander Afanasyeva16abd22014-01-31 18:12:29 -080048 onFaceCreated, onAcceptFailed));
Alexander Afanasyev53a6fd32014-03-23 00:00:04 -070049
50 m_isListening = true;
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080051}
52
53void
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080054TcpChannel::connect(const tcp::Endpoint& remoteEndpoint,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080055 const TcpChannel::FaceCreatedCallback& onFaceCreated,
56 const TcpChannel::ConnectFailedCallback& onConnectFailed,
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -070057 const time::seconds& timeout/* = time::seconds(4)*/)
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080058{
Alexander Afanasyev334b7142014-01-28 12:59:39 -080059 ChannelFaceMap::iterator i = m_channelFaces.find(remoteEndpoint);
60 if (i != m_channelFaces.end()) {
61 onFaceCreated(i->second);
62 return;
63 }
64
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080065 shared_ptr<ip::tcp::socket> clientSocket =
Junxiao Shi61e3cc52014-03-03 20:40:28 -070066 make_shared<ip::tcp::socket>(boost::ref(getGlobalIoService()));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080067
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -070068 shared_ptr<ndn::monotonic_deadline_timer> connectTimeoutTimer =
69 make_shared<ndn::monotonic_deadline_timer>(boost::ref(getGlobalIoService()));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080070
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080071 clientSocket->async_connect(remoteEndpoint,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080072 bind(&TcpChannel::handleSuccessfulConnect, this, _1,
73 clientSocket, connectTimeoutTimer,
74 onFaceCreated, onConnectFailed));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080075
76 connectTimeoutTimer->expires_from_now(timeout);
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080077 connectTimeoutTimer->async_wait(bind(&TcpChannel::handleFailedConnect, this, _1,
78 clientSocket, connectTimeoutTimer,
79 onConnectFailed));
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080080}
81
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080082void
83TcpChannel::connect(const std::string& remoteHost, const std::string& remotePort,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080084 const TcpChannel::FaceCreatedCallback& onFaceCreated,
85 const TcpChannel::ConnectFailedCallback& onConnectFailed,
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -070086 const time::seconds& timeout/* = time::seconds(4)*/)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080087{
88 shared_ptr<ip::tcp::socket> clientSocket =
Junxiao Shi61e3cc52014-03-03 20:40:28 -070089 make_shared<ip::tcp::socket>(boost::ref(getGlobalIoService()));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080090
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -070091 shared_ptr<ndn::monotonic_deadline_timer> connectTimeoutTimer =
92 make_shared<ndn::monotonic_deadline_timer>(boost::ref(getGlobalIoService()));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080093
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080094 ip::tcp::resolver::query query(remoteHost, remotePort);
95 shared_ptr<ip::tcp::resolver> resolver =
Junxiao Shi61e3cc52014-03-03 20:40:28 -070096 make_shared<ip::tcp::resolver>(boost::ref(getGlobalIoService()));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080097
98 resolver->async_resolve(query,
Davide Pesavento855bf292014-02-27 03:58:01 +010099 bind(&TcpChannel::handleEndpointResolution, this, _1, _2,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800100 clientSocket, connectTimeoutTimer,
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800101 onFaceCreated, onConnectFailed,
102 resolver));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800103
104 connectTimeoutTimer->expires_from_now(timeout);
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800105 connectTimeoutTimer->async_wait(bind(&TcpChannel::handleFailedConnect, this, _1,
106 clientSocket, connectTimeoutTimer,
107 onConnectFailed));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800108}
109
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -0800110size_t
111TcpChannel::size() const
112{
113 return m_channelFaces.size();
114}
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800115
116void
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800117TcpChannel::createFace(const shared_ptr<ip::tcp::socket>& socket,
Alexander Afanasyev355c0662014-03-20 18:08:17 -0700118 const FaceCreatedCallback& onFaceCreated,
119 bool isOnDemand)
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800120{
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800121 tcp::Endpoint remoteEndpoint = socket->remote_endpoint();
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -0800122
Alexander Afanasyevbd220a02014-02-20 00:29:56 -0800123 shared_ptr<Face> face;
Alexander Afanasyevbfd31de2014-02-26 13:20:08 -0800124 if (socket->local_endpoint().address().is_loopback())
Alexander Afanasyev355c0662014-03-20 18:08:17 -0700125 face = make_shared<TcpLocalFace>(boost::cref(socket), isOnDemand);
Alexander Afanasyevbd220a02014-02-20 00:29:56 -0800126 else
Alexander Afanasyev355c0662014-03-20 18:08:17 -0700127 face = make_shared<TcpFace>(boost::cref(socket), isOnDemand);
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700128
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -0800129 face->onFail += bind(&TcpChannel::afterFaceFailed, this, remoteEndpoint);
130
131 onFaceCreated(face);
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800132 m_channelFaces[remoteEndpoint] = face;
133}
134
135void
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -0800136TcpChannel::afterFaceFailed(tcp::Endpoint &remoteEndpoint)
137{
138 NFD_LOG_DEBUG("afterFaceFailed: " << remoteEndpoint);
139 m_channelFaces.erase(remoteEndpoint);
140}
141
142void
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800143TcpChannel::handleSuccessfulAccept(const boost::system::error_code& error,
144 const shared_ptr<boost::asio::ip::tcp::socket>& socket,
145 const FaceCreatedCallback& onFaceCreated,
146 const ConnectFailedCallback& onAcceptFailed)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800147{
148 if (error) {
149 if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
150 return;
151
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800152 NFD_LOG_DEBUG("Connect to remote endpoint failed: "
153 << error.category().message(error.value()));
Alexander Afanasyev5f1ec252014-02-28 10:59:17 -0800154
155 if (static_cast<bool>(onAcceptFailed))
156 onAcceptFailed("Connect to remote endpoint failed: " +
157 error.category().message(error.value()));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800158 return;
159 }
160
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800161 // prepare accepting the next connection
162 shared_ptr<ip::tcp::socket> clientSocket =
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700163 make_shared<ip::tcp::socket>(boost::ref(getGlobalIoService()));
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800164 m_acceptor->async_accept(*clientSocket,
165 bind(&TcpChannel::handleSuccessfulAccept, this, _1,
166 clientSocket,
167 onFaceCreated, onAcceptFailed));
Alexander Afanasyev334b7142014-01-28 12:59:39 -0800168
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800169 NFD_LOG_DEBUG("[" << m_localEndpoint << "] "
170 "<< Connection from " << socket->remote_endpoint());
Alexander Afanasyev355c0662014-03-20 18:08:17 -0700171 createFace(socket, onFaceCreated, true);
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800172}
173
174void
175TcpChannel::handleSuccessfulConnect(const boost::system::error_code& error,
176 const shared_ptr<ip::tcp::socket>& socket,
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -0700177 const shared_ptr<ndn::monotonic_deadline_timer>& timer,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800178 const FaceCreatedCallback& onFaceCreated,
179 const ConnectFailedCallback& onConnectFailed)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800180{
181 timer->cancel();
182
183 if (error) {
184 if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
185 return;
186
187 socket->close();
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700188
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800189 NFD_LOG_DEBUG("Connect to remote endpoint failed: "
190 << error.category().message(error.value()));
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700191
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800192 onConnectFailed("Connect to remote endpoint failed: " +
193 error.category().message(error.value()));
194 return;
195 }
196
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800197 NFD_LOG_DEBUG("[" << m_localEndpoint << "] "
198 ">> Connection to " << socket->remote_endpoint());
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700199
Alexander Afanasyev355c0662014-03-20 18:08:17 -0700200 createFace(socket, onFaceCreated, false);
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800201}
202
203void
204TcpChannel::handleFailedConnect(const boost::system::error_code& error,
205 const shared_ptr<ip::tcp::socket>& socket,
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -0700206 const shared_ptr<ndn::monotonic_deadline_timer>& timer,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800207 const ConnectFailedCallback& onConnectFailed)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800208{
209 if (error) { // e.g., cancelled
210 return;
211 }
212
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800213 NFD_LOG_DEBUG("Connect to remote endpoint timed out: "
214 << error.category().message(error.value()));
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700215
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800216 onConnectFailed("Connect to remote endpoint timed out: " +
217 error.category().message(error.value()));
218 socket->close(); // abort the connection
219}
220
221void
Davide Pesavento855bf292014-02-27 03:58:01 +0100222TcpChannel::handleEndpointResolution(const boost::system::error_code& error,
223 ip::tcp::resolver::iterator remoteEndpoint,
224 const shared_ptr<boost::asio::ip::tcp::socket>& socket,
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -0700225 const shared_ptr<ndn::monotonic_deadline_timer>& timer,
Davide Pesavento855bf292014-02-27 03:58:01 +0100226 const FaceCreatedCallback& onFaceCreated,
227 const ConnectFailedCallback& onConnectFailed,
228 const shared_ptr<ip::tcp::resolver>& resolver)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800229{
230 if (error ||
231 remoteEndpoint == ip::tcp::resolver::iterator())
232 {
233 if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
234 return;
235
236 socket->close();
237 timer->cancel();
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800238
239 NFD_LOG_DEBUG("Remote endpoint hostname or port cannot be resolved: "
240 << error.category().message(error.value()));
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700241
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800242 onConnectFailed("Remote endpoint hostname or port cannot be resolved: " +
243 error.category().message(error.value()));
244 return;
245 }
246
247 // got endpoint, now trying to connect (only try the first resolution option)
248 socket->async_connect(*remoteEndpoint,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800249 bind(&TcpChannel::handleSuccessfulConnect, this, _1,
250 socket, timer,
251 onFaceCreated, onConnectFailed));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800252}
253
Alexander Afanasyev18bbf812014-01-29 01:40:23 -0800254} // namespace nfd