blob: c27f1df30ec3ceed3045159b29c218fee43df5f7 [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)
19{
20 this->setUri(FaceUri(localEndpoint));
21}
22
23TcpChannel::~TcpChannel()
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080024{
25}
26
27void
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080028TcpChannel::listen(const FaceCreatedCallback& onFaceCreated,
29 const ConnectFailedCallback& onAcceptFailed,
30 int backlog/* = tcp::acceptor::max_connections*/)
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080031{
Junxiao Shi61e3cc52014-03-03 20:40:28 -070032 m_acceptor = make_shared<ip::tcp::acceptor>(boost::ref(getGlobalIoService()));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080033 m_acceptor->open(m_localEndpoint.protocol());
34 m_acceptor->set_option(ip::tcp::acceptor::reuse_address(true));
Alexander Afanasyeveee71fd2014-03-13 17:40:33 -070035 if (m_localEndpoint.address().is_v6())
36 {
37 m_acceptor->set_option(ip::v6_only(true));
38 }
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080039 m_acceptor->bind(m_localEndpoint);
40 m_acceptor->listen(backlog);
41
42 shared_ptr<ip::tcp::socket> clientSocket =
Junxiao Shi61e3cc52014-03-03 20:40:28 -070043 make_shared<ip::tcp::socket>(boost::ref(getGlobalIoService()));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080044 m_acceptor->async_accept(*clientSocket,
Alexander Afanasyeva16abd22014-01-31 18:12:29 -080045 bind(&TcpChannel::handleSuccessfulAccept, this, _1,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080046 clientSocket,
Alexander Afanasyeva16abd22014-01-31 18:12:29 -080047 onFaceCreated, onAcceptFailed));
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080048}
49
50void
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080051TcpChannel::connect(const tcp::Endpoint& remoteEndpoint,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080052 const TcpChannel::FaceCreatedCallback& onFaceCreated,
53 const TcpChannel::ConnectFailedCallback& onConnectFailed,
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -070054 const time::seconds& timeout/* = time::seconds(4)*/)
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080055{
Alexander Afanasyev334b7142014-01-28 12:59:39 -080056 ChannelFaceMap::iterator i = m_channelFaces.find(remoteEndpoint);
57 if (i != m_channelFaces.end()) {
58 onFaceCreated(i->second);
59 return;
60 }
61
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080062 shared_ptr<ip::tcp::socket> clientSocket =
Junxiao Shi61e3cc52014-03-03 20:40:28 -070063 make_shared<ip::tcp::socket>(boost::ref(getGlobalIoService()));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080064
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -070065 shared_ptr<ndn::monotonic_deadline_timer> connectTimeoutTimer =
66 make_shared<ndn::monotonic_deadline_timer>(boost::ref(getGlobalIoService()));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080067
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080068 clientSocket->async_connect(remoteEndpoint,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080069 bind(&TcpChannel::handleSuccessfulConnect, this, _1,
70 clientSocket, connectTimeoutTimer,
71 onFaceCreated, onConnectFailed));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080072
73 connectTimeoutTimer->expires_from_now(timeout);
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080074 connectTimeoutTimer->async_wait(bind(&TcpChannel::handleFailedConnect, this, _1,
75 clientSocket, connectTimeoutTimer,
76 onConnectFailed));
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080077}
78
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080079void
80TcpChannel::connect(const std::string& remoteHost, const std::string& remotePort,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080081 const TcpChannel::FaceCreatedCallback& onFaceCreated,
82 const TcpChannel::ConnectFailedCallback& onConnectFailed,
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -070083 const time::seconds& timeout/* = time::seconds(4)*/)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080084{
85 shared_ptr<ip::tcp::socket> clientSocket =
Junxiao Shi61e3cc52014-03-03 20:40:28 -070086 make_shared<ip::tcp::socket>(boost::ref(getGlobalIoService()));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080087
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -070088 shared_ptr<ndn::monotonic_deadline_timer> connectTimeoutTimer =
89 make_shared<ndn::monotonic_deadline_timer>(boost::ref(getGlobalIoService()));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080090
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080091 ip::tcp::resolver::query query(remoteHost, remotePort);
92 shared_ptr<ip::tcp::resolver> resolver =
Junxiao Shi61e3cc52014-03-03 20:40:28 -070093 make_shared<ip::tcp::resolver>(boost::ref(getGlobalIoService()));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080094
95 resolver->async_resolve(query,
Davide Pesavento855bf292014-02-27 03:58:01 +010096 bind(&TcpChannel::handleEndpointResolution, this, _1, _2,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080097 clientSocket, connectTimeoutTimer,
Alexander Afanasyev3958b012014-01-31 15:06:13 -080098 onFaceCreated, onConnectFailed,
99 resolver));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800100
101 connectTimeoutTimer->expires_from_now(timeout);
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800102 connectTimeoutTimer->async_wait(bind(&TcpChannel::handleFailedConnect, this, _1,
103 clientSocket, connectTimeoutTimer,
104 onConnectFailed));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800105}
106
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -0800107size_t
108TcpChannel::size() const
109{
110 return m_channelFaces.size();
111}
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800112
113void
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800114TcpChannel::createFace(const shared_ptr<ip::tcp::socket>& socket,
115 const FaceCreatedCallback& onFaceCreated)
116{
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800117 tcp::Endpoint remoteEndpoint = socket->remote_endpoint();
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -0800118
Alexander Afanasyevbd220a02014-02-20 00:29:56 -0800119 shared_ptr<Face> face;
Alexander Afanasyevbfd31de2014-02-26 13:20:08 -0800120 if (socket->local_endpoint().address().is_loopback())
Alexander Afanasyevbd220a02014-02-20 00:29:56 -0800121 face = make_shared<TcpLocalFace>(boost::cref(socket));
122 else
123 face = make_shared<TcpFace>(boost::cref(socket));
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700124
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -0800125 face->onFail += bind(&TcpChannel::afterFaceFailed, this, remoteEndpoint);
126
127 onFaceCreated(face);
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800128 m_channelFaces[remoteEndpoint] = face;
129}
130
131void
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -0800132TcpChannel::afterFaceFailed(tcp::Endpoint &remoteEndpoint)
133{
134 NFD_LOG_DEBUG("afterFaceFailed: " << remoteEndpoint);
135 m_channelFaces.erase(remoteEndpoint);
136}
137
138void
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800139TcpChannel::handleSuccessfulAccept(const boost::system::error_code& error,
140 const shared_ptr<boost::asio::ip::tcp::socket>& socket,
141 const FaceCreatedCallback& onFaceCreated,
142 const ConnectFailedCallback& onAcceptFailed)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800143{
144 if (error) {
145 if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
146 return;
147
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800148 NFD_LOG_DEBUG("Connect to remote endpoint failed: "
149 << error.category().message(error.value()));
Alexander Afanasyev5f1ec252014-02-28 10:59:17 -0800150
151 if (static_cast<bool>(onAcceptFailed))
152 onAcceptFailed("Connect to remote endpoint failed: " +
153 error.category().message(error.value()));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800154 return;
155 }
156
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800157 // prepare accepting the next connection
158 shared_ptr<ip::tcp::socket> clientSocket =
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700159 make_shared<ip::tcp::socket>(boost::ref(getGlobalIoService()));
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800160 m_acceptor->async_accept(*clientSocket,
161 bind(&TcpChannel::handleSuccessfulAccept, this, _1,
162 clientSocket,
163 onFaceCreated, onAcceptFailed));
Alexander Afanasyev334b7142014-01-28 12:59:39 -0800164
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800165 NFD_LOG_DEBUG("[" << m_localEndpoint << "] "
166 "<< Connection from " << socket->remote_endpoint());
167 createFace(socket, onFaceCreated);
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800168}
169
170void
171TcpChannel::handleSuccessfulConnect(const boost::system::error_code& error,
172 const shared_ptr<ip::tcp::socket>& socket,
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -0700173 const shared_ptr<ndn::monotonic_deadline_timer>& timer,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800174 const FaceCreatedCallback& onFaceCreated,
175 const ConnectFailedCallback& onConnectFailed)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800176{
177 timer->cancel();
178
179 if (error) {
180 if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
181 return;
182
183 socket->close();
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700184
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800185 NFD_LOG_DEBUG("Connect to remote endpoint failed: "
186 << error.category().message(error.value()));
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700187
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800188 onConnectFailed("Connect to remote endpoint failed: " +
189 error.category().message(error.value()));
190 return;
191 }
192
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800193 NFD_LOG_DEBUG("[" << m_localEndpoint << "] "
194 ">> Connection to " << socket->remote_endpoint());
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700195
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800196 createFace(socket, onFaceCreated);
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800197}
198
199void
200TcpChannel::handleFailedConnect(const boost::system::error_code& error,
201 const shared_ptr<ip::tcp::socket>& socket,
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -0700202 const shared_ptr<ndn::monotonic_deadline_timer>& timer,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800203 const ConnectFailedCallback& onConnectFailed)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800204{
205 if (error) { // e.g., cancelled
206 return;
207 }
208
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800209 NFD_LOG_DEBUG("Connect to remote endpoint timed out: "
210 << error.category().message(error.value()));
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700211
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800212 onConnectFailed("Connect to remote endpoint timed out: " +
213 error.category().message(error.value()));
214 socket->close(); // abort the connection
215}
216
217void
Davide Pesavento855bf292014-02-27 03:58:01 +0100218TcpChannel::handleEndpointResolution(const boost::system::error_code& error,
219 ip::tcp::resolver::iterator remoteEndpoint,
220 const shared_ptr<boost::asio::ip::tcp::socket>& socket,
Alexander Afanasyeveb3197f2014-03-17 19:28:18 -0700221 const shared_ptr<ndn::monotonic_deadline_timer>& timer,
Davide Pesavento855bf292014-02-27 03:58:01 +0100222 const FaceCreatedCallback& onFaceCreated,
223 const ConnectFailedCallback& onConnectFailed,
224 const shared_ptr<ip::tcp::resolver>& resolver)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800225{
226 if (error ||
227 remoteEndpoint == ip::tcp::resolver::iterator())
228 {
229 if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
230 return;
231
232 socket->close();
233 timer->cancel();
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800234
235 NFD_LOG_DEBUG("Remote endpoint hostname or port cannot be resolved: "
236 << error.category().message(error.value()));
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700237
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800238 onConnectFailed("Remote endpoint hostname or port cannot be resolved: " +
239 error.category().message(error.value()));
240 return;
241 }
242
243 // got endpoint, now trying to connect (only try the first resolution option)
244 socket->async_connect(*remoteEndpoint,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800245 bind(&TcpChannel::handleSuccessfulConnect, this, _1,
246 socket, timer,
247 onFaceCreated, onConnectFailed));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800248}
249
Alexander Afanasyev18bbf812014-01-29 01:40:23 -0800250} // namespace nfd