blob: 616ca621a7b90bd33153f243bf1e5d0443cd42cb [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));
35 m_acceptor->bind(m_localEndpoint);
36 m_acceptor->listen(backlog);
37
38 shared_ptr<ip::tcp::socket> clientSocket =
Junxiao Shi61e3cc52014-03-03 20:40:28 -070039 make_shared<ip::tcp::socket>(boost::ref(getGlobalIoService()));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080040 m_acceptor->async_accept(*clientSocket,
Alexander Afanasyeva16abd22014-01-31 18:12:29 -080041 bind(&TcpChannel::handleSuccessfulAccept, this, _1,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080042 clientSocket,
Alexander Afanasyeva16abd22014-01-31 18:12:29 -080043 onFaceCreated, onAcceptFailed));
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080044}
45
46void
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080047TcpChannel::connect(const tcp::Endpoint& remoteEndpoint,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080048 const TcpChannel::FaceCreatedCallback& onFaceCreated,
49 const TcpChannel::ConnectFailedCallback& onConnectFailed,
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080050 const time::Duration& timeout/* = time::seconds(4)*/)
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080051{
Alexander Afanasyev334b7142014-01-28 12:59:39 -080052 ChannelFaceMap::iterator i = m_channelFaces.find(remoteEndpoint);
53 if (i != m_channelFaces.end()) {
54 onFaceCreated(i->second);
55 return;
56 }
57
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080058 shared_ptr<ip::tcp::socket> clientSocket =
Junxiao Shi61e3cc52014-03-03 20:40:28 -070059 make_shared<ip::tcp::socket>(boost::ref(getGlobalIoService()));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080060
61 shared_ptr<monotonic_deadline_timer> connectTimeoutTimer =
Junxiao Shi61e3cc52014-03-03 20:40:28 -070062 make_shared<monotonic_deadline_timer>(boost::ref(getGlobalIoService()));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080063
Alexander Afanasyev3958b012014-01-31 15:06:13 -080064 clientSocket->open(m_localEndpoint.protocol());
65
66 // The following does not work and CCNx does not bind the local
67 // socket to a fixed port number for TCP connections
68
69 // clientSocket->set_option(ip::tcp::socket::reuse_address(true));
70 // clientSocket->bind(m_localEndpoint);
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080071
72 clientSocket->async_connect(remoteEndpoint,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080073 bind(&TcpChannel::handleSuccessfulConnect, this, _1,
74 clientSocket, connectTimeoutTimer,
75 onFaceCreated, onConnectFailed));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080076
77 connectTimeoutTimer->expires_from_now(timeout);
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080078 connectTimeoutTimer->async_wait(bind(&TcpChannel::handleFailedConnect, this, _1,
79 clientSocket, connectTimeoutTimer,
80 onConnectFailed));
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080081}
82
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080083void
84TcpChannel::connect(const std::string& remoteHost, const std::string& remotePort,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080085 const TcpChannel::FaceCreatedCallback& onFaceCreated,
86 const TcpChannel::ConnectFailedCallback& onConnectFailed,
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080087 const time::Duration& timeout/* = time::seconds(4)*/)
88{
89 shared_ptr<ip::tcp::socket> clientSocket =
Junxiao Shi61e3cc52014-03-03 20:40:28 -070090 make_shared<ip::tcp::socket>(boost::ref(getGlobalIoService()));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080091
92 shared_ptr<monotonic_deadline_timer> connectTimeoutTimer =
Junxiao Shi61e3cc52014-03-03 20:40:28 -070093 make_shared<monotonic_deadline_timer>(boost::ref(getGlobalIoService()));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080094
Alexander Afanasyev3958b012014-01-31 15:06:13 -080095 clientSocket->open(m_localEndpoint.protocol());
96
97 // The following does not work and CCNx does not bind the local
98 // socket to a fixed port number for TCP connections
99
100 // clientSocket->set_option(ip::tcp::socket::reuse_address(true));
101 // clientSocket->bind(m_localEndpoint);
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800102
103 ip::tcp::resolver::query query(remoteHost, remotePort);
104 shared_ptr<ip::tcp::resolver> resolver =
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700105 make_shared<ip::tcp::resolver>(boost::ref(getGlobalIoService()));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800106
107 resolver->async_resolve(query,
Davide Pesavento855bf292014-02-27 03:58:01 +0100108 bind(&TcpChannel::handleEndpointResolution, this, _1, _2,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800109 clientSocket, connectTimeoutTimer,
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800110 onFaceCreated, onConnectFailed,
111 resolver));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800112
113 connectTimeoutTimer->expires_from_now(timeout);
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800114 connectTimeoutTimer->async_wait(bind(&TcpChannel::handleFailedConnect, this, _1,
115 clientSocket, connectTimeoutTimer,
116 onConnectFailed));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800117}
118
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -0800119size_t
120TcpChannel::size() const
121{
122 return m_channelFaces.size();
123}
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800124
125void
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800126TcpChannel::createFace(const shared_ptr<ip::tcp::socket>& socket,
127 const FaceCreatedCallback& onFaceCreated)
128{
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800129 tcp::Endpoint remoteEndpoint = socket->remote_endpoint();
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -0800130
Alexander Afanasyevbd220a02014-02-20 00:29:56 -0800131 shared_ptr<Face> face;
Alexander Afanasyevbfd31de2014-02-26 13:20:08 -0800132 if (socket->local_endpoint().address().is_loopback())
Alexander Afanasyevbd220a02014-02-20 00:29:56 -0800133 face = make_shared<TcpLocalFace>(boost::cref(socket));
134 else
135 face = make_shared<TcpFace>(boost::cref(socket));
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700136
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -0800137 face->onFail += bind(&TcpChannel::afterFaceFailed, this, remoteEndpoint);
138
139 onFaceCreated(face);
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800140 m_channelFaces[remoteEndpoint] = face;
141}
142
143void
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -0800144TcpChannel::afterFaceFailed(tcp::Endpoint &remoteEndpoint)
145{
146 NFD_LOG_DEBUG("afterFaceFailed: " << remoteEndpoint);
147 m_channelFaces.erase(remoteEndpoint);
148}
149
150void
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800151TcpChannel::handleSuccessfulAccept(const boost::system::error_code& error,
152 const shared_ptr<boost::asio::ip::tcp::socket>& socket,
153 const FaceCreatedCallback& onFaceCreated,
154 const ConnectFailedCallback& onAcceptFailed)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800155{
156 if (error) {
157 if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
158 return;
159
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800160 NFD_LOG_DEBUG("Connect to remote endpoint failed: "
161 << error.category().message(error.value()));
Alexander Afanasyev5f1ec252014-02-28 10:59:17 -0800162
163 if (static_cast<bool>(onAcceptFailed))
164 onAcceptFailed("Connect to remote endpoint failed: " +
165 error.category().message(error.value()));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800166 return;
167 }
168
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800169 // prepare accepting the next connection
170 shared_ptr<ip::tcp::socket> clientSocket =
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700171 make_shared<ip::tcp::socket>(boost::ref(getGlobalIoService()));
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800172 m_acceptor->async_accept(*clientSocket,
173 bind(&TcpChannel::handleSuccessfulAccept, this, _1,
174 clientSocket,
175 onFaceCreated, onAcceptFailed));
Alexander Afanasyev334b7142014-01-28 12:59:39 -0800176
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800177 NFD_LOG_DEBUG("[" << m_localEndpoint << "] "
178 "<< Connection from " << socket->remote_endpoint());
179 createFace(socket, onFaceCreated);
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800180}
181
182void
183TcpChannel::handleSuccessfulConnect(const boost::system::error_code& error,
184 const shared_ptr<ip::tcp::socket>& socket,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800185 const shared_ptr<monotonic_deadline_timer>& timer,
186 const FaceCreatedCallback& onFaceCreated,
187 const ConnectFailedCallback& onConnectFailed)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800188{
189 timer->cancel();
190
191 if (error) {
192 if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
193 return;
194
195 socket->close();
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700196
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800197 NFD_LOG_DEBUG("Connect to remote endpoint failed: "
198 << error.category().message(error.value()));
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700199
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800200 onConnectFailed("Connect to remote endpoint failed: " +
201 error.category().message(error.value()));
202 return;
203 }
204
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800205 NFD_LOG_DEBUG("[" << m_localEndpoint << "] "
206 ">> Connection to " << socket->remote_endpoint());
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700207
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800208 createFace(socket, onFaceCreated);
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800209}
210
211void
212TcpChannel::handleFailedConnect(const boost::system::error_code& error,
213 const shared_ptr<ip::tcp::socket>& socket,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800214 const shared_ptr<monotonic_deadline_timer>& timer,
215 const ConnectFailedCallback& onConnectFailed)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800216{
217 if (error) { // e.g., cancelled
218 return;
219 }
220
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800221 NFD_LOG_DEBUG("Connect to remote endpoint timed out: "
222 << error.category().message(error.value()));
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700223
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800224 onConnectFailed("Connect to remote endpoint timed out: " +
225 error.category().message(error.value()));
226 socket->close(); // abort the connection
227}
228
229void
Davide Pesavento855bf292014-02-27 03:58:01 +0100230TcpChannel::handleEndpointResolution(const boost::system::error_code& error,
231 ip::tcp::resolver::iterator remoteEndpoint,
232 const shared_ptr<boost::asio::ip::tcp::socket>& socket,
233 const shared_ptr<boost::asio::monotonic_deadline_timer>& timer,
234 const FaceCreatedCallback& onFaceCreated,
235 const ConnectFailedCallback& onConnectFailed,
236 const shared_ptr<ip::tcp::resolver>& resolver)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800237{
238 if (error ||
239 remoteEndpoint == ip::tcp::resolver::iterator())
240 {
241 if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
242 return;
243
244 socket->close();
245 timer->cancel();
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800246
247 NFD_LOG_DEBUG("Remote endpoint hostname or port cannot be resolved: "
248 << error.category().message(error.value()));
Junxiao Shi61e3cc52014-03-03 20:40:28 -0700249
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800250 onConnectFailed("Remote endpoint hostname or port cannot be resolved: " +
251 error.category().message(error.value()));
252 return;
253 }
254
255 // got endpoint, now trying to connect (only try the first resolution option)
256 socket->async_connect(*remoteEndpoint,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800257 bind(&TcpChannel::handleSuccessfulConnect, this, _1,
258 socket, timer,
259 onFaceCreated, onConnectFailed));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800260}
261
Alexander Afanasyev18bbf812014-01-29 01:40:23 -0800262} // namespace nfd