blob: bb47ab342daa86ad5b73c165aaec660580afac45 [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,
103 bind(&TcpChannel::handleEndpointResoution, 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 Afanasyev232c9872014-02-20 17:33:00 -0800127 if (is_loopback(socket->local_endpoint().address()))
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()));
157
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800158 onAcceptFailed("Connect to remote endpoint failed: " +
159 error.category().message(error.value()));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800160 return;
161 }
162
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800163 // prepare accepting the next connection
164 shared_ptr<ip::tcp::socket> clientSocket =
165 make_shared<ip::tcp::socket>(boost::ref(m_ioService));
166 m_acceptor->async_accept(*clientSocket,
167 bind(&TcpChannel::handleSuccessfulAccept, this, _1,
168 clientSocket,
169 onFaceCreated, onAcceptFailed));
Alexander Afanasyev334b7142014-01-28 12:59:39 -0800170
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800171 NFD_LOG_DEBUG("[" << m_localEndpoint << "] "
172 "<< Connection from " << socket->remote_endpoint());
173 createFace(socket, onFaceCreated);
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800174}
175
176void
177TcpChannel::handleSuccessfulConnect(const boost::system::error_code& error,
178 const shared_ptr<ip::tcp::socket>& socket,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800179 const shared_ptr<monotonic_deadline_timer>& timer,
180 const FaceCreatedCallback& onFaceCreated,
181 const ConnectFailedCallback& onConnectFailed)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800182{
183 timer->cancel();
184
185 if (error) {
186 if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
187 return;
188
189 socket->close();
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800190
191 NFD_LOG_DEBUG("Connect to remote endpoint failed: "
192 << error.category().message(error.value()));
193
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800194 onConnectFailed("Connect to remote endpoint failed: " +
195 error.category().message(error.value()));
196 return;
197 }
198
Alexander Afanasyeva16abd22014-01-31 18:12:29 -0800199 NFD_LOG_DEBUG("[" << m_localEndpoint << "] "
200 ">> Connection to " << socket->remote_endpoint());
201
202 createFace(socket, onFaceCreated);
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800203}
204
205void
206TcpChannel::handleFailedConnect(const boost::system::error_code& error,
207 const shared_ptr<ip::tcp::socket>& socket,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800208 const shared_ptr<monotonic_deadline_timer>& timer,
209 const ConnectFailedCallback& onConnectFailed)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800210{
211 if (error) { // e.g., cancelled
212 return;
213 }
214
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800215 NFD_LOG_DEBUG("Connect to remote endpoint timed out: "
216 << error.category().message(error.value()));
217
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800218 onConnectFailed("Connect to remote endpoint timed out: " +
219 error.category().message(error.value()));
220 socket->close(); // abort the connection
221}
222
223void
224TcpChannel::handleEndpointResoution(const boost::system::error_code& error,
225 ip::tcp::resolver::iterator remoteEndpoint,
226 const shared_ptr<boost::asio::ip::tcp::socket>& socket,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800227 const shared_ptr<boost::asio::monotonic_deadline_timer>& timer,
228 const FaceCreatedCallback& onFaceCreated,
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800229 const ConnectFailedCallback& onConnectFailed,
230 const shared_ptr<ip::tcp::resolver>& resolver)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800231{
232 if (error ||
233 remoteEndpoint == ip::tcp::resolver::iterator())
234 {
235 if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
236 return;
237
238 socket->close();
239 timer->cancel();
Alexander Afanasyev3958b012014-01-31 15:06:13 -0800240
241 NFD_LOG_DEBUG("Remote endpoint hostname or port cannot be resolved: "
242 << error.category().message(error.value()));
243
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800244 onConnectFailed("Remote endpoint hostname or port cannot be resolved: " +
245 error.category().message(error.value()));
246 return;
247 }
248
249 // got endpoint, now trying to connect (only try the first resolution option)
250 socket->async_connect(*remoteEndpoint,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800251 bind(&TcpChannel::handleSuccessfulConnect, this, _1,
252 socket, timer,
253 onFaceCreated, onConnectFailed));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800254}
255
256
Alexander Afanasyev18bbf812014-01-29 01:40:23 -0800257} // namespace nfd