blob: 246cdb037fdfb786cd52be4ad80d3677af204793 [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"
8
Alexander Afanasyev18bbf812014-01-29 01:40:23 -08009namespace nfd {
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080010
Alexander Afanasyev3958b012014-01-31 15:06:13 -080011NFD_LOG_INIT("TcpChannel");
12
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080013using namespace boost::asio;
14
15TcpChannel::TcpChannel(io_service& ioService,
16 const tcp::Endpoint& localEndpoint)
17 : m_ioService(ioService)
18 , m_localEndpoint(localEndpoint)
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080019{
20}
21
22void
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080023TcpChannel::listen(const FaceCreatedCallback& onFaceCreated,
24 const ConnectFailedCallback& onAcceptFailed,
25 int backlog/* = tcp::acceptor::max_connections*/)
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080026{
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080027 m_acceptor = make_shared<ip::tcp::acceptor>(boost::ref(m_ioService));
28 m_acceptor->open(m_localEndpoint.protocol());
29 m_acceptor->set_option(ip::tcp::acceptor::reuse_address(true));
30 m_acceptor->bind(m_localEndpoint);
31 m_acceptor->listen(backlog);
32
33 shared_ptr<ip::tcp::socket> clientSocket =
34 make_shared<ip::tcp::socket>(boost::ref(m_ioService));
35 m_acceptor->async_accept(*clientSocket,
Alexander Afanasyeva16abd22014-01-31 18:12:29 -080036 bind(&TcpChannel::handleSuccessfulAccept, this, _1,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080037 clientSocket,
Alexander Afanasyeva16abd22014-01-31 18:12:29 -080038 onFaceCreated, onAcceptFailed));
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080039}
40
41void
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080042TcpChannel::connect(const tcp::Endpoint& remoteEndpoint,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080043 const TcpChannel::FaceCreatedCallback& onFaceCreated,
44 const TcpChannel::ConnectFailedCallback& onConnectFailed,
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080045 const time::Duration& timeout/* = time::seconds(4)*/)
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080046{
Alexander Afanasyev334b7142014-01-28 12:59:39 -080047 ChannelFaceMap::iterator i = m_channelFaces.find(remoteEndpoint);
48 if (i != m_channelFaces.end()) {
49 onFaceCreated(i->second);
50 return;
51 }
52
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080053 shared_ptr<ip::tcp::socket> clientSocket =
54 make_shared<ip::tcp::socket>(boost::ref(m_ioService));
55
56 shared_ptr<monotonic_deadline_timer> connectTimeoutTimer =
57 make_shared<monotonic_deadline_timer>(boost::ref(m_ioService));
58
Alexander Afanasyev3958b012014-01-31 15:06:13 -080059 clientSocket->open(m_localEndpoint.protocol());
60
61 // The following does not work and CCNx does not bind the local
62 // socket to a fixed port number for TCP connections
63
64 // clientSocket->set_option(ip::tcp::socket::reuse_address(true));
65 // clientSocket->bind(m_localEndpoint);
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080066
67 clientSocket->async_connect(remoteEndpoint,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080068 bind(&TcpChannel::handleSuccessfulConnect, this, _1,
69 clientSocket, connectTimeoutTimer,
70 onFaceCreated, onConnectFailed));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080071
72 connectTimeoutTimer->expires_from_now(timeout);
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080073 connectTimeoutTimer->async_wait(bind(&TcpChannel::handleFailedConnect, this, _1,
74 clientSocket, connectTimeoutTimer,
75 onConnectFailed));
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080076}
77
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080078void
79TcpChannel::connect(const std::string& remoteHost, const std::string& remotePort,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080080 const TcpChannel::FaceCreatedCallback& onFaceCreated,
81 const TcpChannel::ConnectFailedCallback& onConnectFailed,
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080082 const time::Duration& timeout/* = time::seconds(4)*/)
83{
84 shared_ptr<ip::tcp::socket> clientSocket =
85 make_shared<ip::tcp::socket>(boost::ref(m_ioService));
86
87 shared_ptr<monotonic_deadline_timer> connectTimeoutTimer =
88 make_shared<monotonic_deadline_timer>(boost::ref(m_ioService));
89
Alexander Afanasyev3958b012014-01-31 15:06:13 -080090 clientSocket->open(m_localEndpoint.protocol());
91
92 // The following does not work and CCNx does not bind the local
93 // socket to a fixed port number for TCP connections
94
95 // clientSocket->set_option(ip::tcp::socket::reuse_address(true));
96 // clientSocket->bind(m_localEndpoint);
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080097
98 ip::tcp::resolver::query query(remoteHost, remotePort);
99 shared_ptr<ip::tcp::resolver> resolver =
100 make_shared<ip::tcp::resolver>(boost::ref(m_ioService));
101
102 resolver->async_resolve(query,
Davide Pesavento855bf292014-02-27 03:58:01 +0100103 bind(&TcpChannel::handleEndpointResolution, this, _1, _2,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800104 clientSocket, connectTimeoutTimer,
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800105 onFaceCreated, onConnectFailed,
106 resolver));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800107
108 connectTimeoutTimer->expires_from_now(timeout);
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800109 connectTimeoutTimer->async_wait(bind(&TcpChannel::handleFailedConnect, this, _1,
110 clientSocket, connectTimeoutTimer,
111 onConnectFailed));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800112}
113
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -0800114size_t
115TcpChannel::size() const
116{
117 return m_channelFaces.size();
118}
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800119
120void
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800121TcpChannel::createFace(const shared_ptr<ip::tcp::socket>& socket,
122 const FaceCreatedCallback& onFaceCreated)
123{
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800124 tcp::Endpoint remoteEndpoint = socket->remote_endpoint();
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -0800125
Alexander Afanasyevbd220a02014-02-20 00:29:56 -0800126 shared_ptr<Face> face;
Alexander Afanasyevbfd31de2014-02-26 13:20:08 -0800127 if (socket->local_endpoint().address().is_loopback())
Alexander Afanasyevbd220a02014-02-20 00:29:56 -0800128 face = make_shared<TcpLocalFace>(boost::cref(socket));
129 else
130 face = make_shared<TcpFace>(boost::cref(socket));
131
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -0800132 face->onFail += bind(&TcpChannel::afterFaceFailed, this, remoteEndpoint);
133
134 onFaceCreated(face);
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800135 m_channelFaces[remoteEndpoint] = face;
136}
137
138void
Alexander Afanasyeva0a10fb2014-02-13 19:56:15 -0800139TcpChannel::afterFaceFailed(tcp::Endpoint &remoteEndpoint)
140{
141 NFD_LOG_DEBUG("afterFaceFailed: " << remoteEndpoint);
142 m_channelFaces.erase(remoteEndpoint);
143}
144
145void
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800146TcpChannel::handleSuccessfulAccept(const boost::system::error_code& error,
147 const shared_ptr<boost::asio::ip::tcp::socket>& socket,
148 const FaceCreatedCallback& onFaceCreated,
149 const ConnectFailedCallback& onAcceptFailed)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800150{
151 if (error) {
152 if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
153 return;
154
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800155 NFD_LOG_DEBUG("Connect to remote endpoint failed: "
156 << error.category().message(error.value()));
Alexander Afanasyev5f1ec252014-02-28 10:59:17 -0800157
158 if (static_cast<bool>(onAcceptFailed))
159 onAcceptFailed("Connect to remote endpoint failed: " +
160 error.category().message(error.value()));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800161 return;
162 }
163
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800164 // prepare accepting the next connection
165 shared_ptr<ip::tcp::socket> clientSocket =
166 make_shared<ip::tcp::socket>(boost::ref(m_ioService));
167 m_acceptor->async_accept(*clientSocket,
168 bind(&TcpChannel::handleSuccessfulAccept, this, _1,
169 clientSocket,
170 onFaceCreated, onAcceptFailed));
Alexander Afanasyev334b7142014-01-28 12:59:39 -0800171
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800172 NFD_LOG_DEBUG("[" << m_localEndpoint << "] "
173 "<< Connection from " << socket->remote_endpoint());
174 createFace(socket, onFaceCreated);
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800175}
176
177void
178TcpChannel::handleSuccessfulConnect(const boost::system::error_code& error,
179 const shared_ptr<ip::tcp::socket>& socket,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800180 const shared_ptr<monotonic_deadline_timer>& timer,
181 const FaceCreatedCallback& onFaceCreated,
182 const ConnectFailedCallback& onConnectFailed)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800183{
184 timer->cancel();
185
186 if (error) {
187 if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
188 return;
189
190 socket->close();
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800191
192 NFD_LOG_DEBUG("Connect to remote endpoint failed: "
193 << error.category().message(error.value()));
194
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800195 onConnectFailed("Connect to remote endpoint failed: " +
196 error.category().message(error.value()));
197 return;
198 }
199
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800200 NFD_LOG_DEBUG("[" << m_localEndpoint << "] "
201 ">> Connection to " << socket->remote_endpoint());
202
203 createFace(socket, onFaceCreated);
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800204}
205
206void
207TcpChannel::handleFailedConnect(const boost::system::error_code& error,
208 const shared_ptr<ip::tcp::socket>& socket,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800209 const shared_ptr<monotonic_deadline_timer>& timer,
210 const ConnectFailedCallback& onConnectFailed)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800211{
212 if (error) { // e.g., cancelled
213 return;
214 }
215
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800216 NFD_LOG_DEBUG("Connect to remote endpoint timed out: "
217 << error.category().message(error.value()));
218
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800219 onConnectFailed("Connect to remote endpoint timed out: " +
220 error.category().message(error.value()));
221 socket->close(); // abort the connection
222}
223
224void
Davide Pesavento855bf292014-02-27 03:58:01 +0100225TcpChannel::handleEndpointResolution(const boost::system::error_code& error,
226 ip::tcp::resolver::iterator remoteEndpoint,
227 const shared_ptr<boost::asio::ip::tcp::socket>& socket,
228 const shared_ptr<boost::asio::monotonic_deadline_timer>& timer,
229 const FaceCreatedCallback& onFaceCreated,
230 const ConnectFailedCallback& onConnectFailed,
231 const shared_ptr<ip::tcp::resolver>& resolver)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800232{
233 if (error ||
234 remoteEndpoint == ip::tcp::resolver::iterator())
235 {
236 if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
237 return;
238
239 socket->close();
240 timer->cancel();
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800241
242 NFD_LOG_DEBUG("Remote endpoint hostname or port cannot be resolved: "
243 << error.category().message(error.value()));
244
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800245 onConnectFailed("Remote endpoint hostname or port cannot be resolved: " +
246 error.category().message(error.value()));
247 return;
248 }
249
250 // got endpoint, now trying to connect (only try the first resolution option)
251 socket->async_connect(*remoteEndpoint,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800252 bind(&TcpChannel::handleSuccessfulConnect, this, _1,
253 socket, timer,
254 onFaceCreated, onConnectFailed));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800255}
256
Alexander Afanasyev18bbf812014-01-29 01:40:23 -0800257} // namespace nfd