blob: f67e9b2df5efbf1223fc003c1731082c5d05adc1 [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 Afanasyev8ad71ba2014-01-27 00:07:14 -080011using namespace boost::asio;
12
13TcpChannel::TcpChannel(io_service& ioService,
14 const tcp::Endpoint& localEndpoint)
15 : m_ioService(ioService)
16 , m_localEndpoint(localEndpoint)
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080017{
18}
19
20void
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080021TcpChannel::listen(const FaceCreatedCallback& onFaceCreated,
22 const ConnectFailedCallback& onAcceptFailed,
23 int backlog/* = tcp::acceptor::max_connections*/)
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080024{
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080025 m_acceptor = make_shared<ip::tcp::acceptor>(boost::ref(m_ioService));
26 m_acceptor->open(m_localEndpoint.protocol());
27 m_acceptor->set_option(ip::tcp::acceptor::reuse_address(true));
28 m_acceptor->bind(m_localEndpoint);
29 m_acceptor->listen(backlog);
30
31 shared_ptr<ip::tcp::socket> clientSocket =
32 make_shared<ip::tcp::socket>(boost::ref(m_ioService));
33 m_acceptor->async_accept(*clientSocket,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080034 bind(&TcpChannel::handleConnection, this, _1,
35 clientSocket,
36 onFaceCreated, onAcceptFailed));
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080037}
38
39void
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080040TcpChannel::connect(const tcp::Endpoint& remoteEndpoint,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080041 const TcpChannel::FaceCreatedCallback& onFaceCreated,
42 const TcpChannel::ConnectFailedCallback& onConnectFailed,
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080043 const time::Duration& timeout/* = time::seconds(4)*/)
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080044{
Alexander Afanasyev334b7142014-01-28 12:59:39 -080045 ChannelFaceMap::iterator i = m_channelFaces.find(remoteEndpoint);
46 if (i != m_channelFaces.end()) {
47 onFaceCreated(i->second);
48 return;
49 }
50
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080051 shared_ptr<ip::tcp::socket> clientSocket =
52 make_shared<ip::tcp::socket>(boost::ref(m_ioService));
53
54 shared_ptr<monotonic_deadline_timer> connectTimeoutTimer =
55 make_shared<monotonic_deadline_timer>(boost::ref(m_ioService));
56
57 // not sure if it works. This will bind to something...
58 // Do we need reuse here too?
59 clientSocket->bind(m_localEndpoint);
60
61 clientSocket->async_connect(remoteEndpoint,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080062 bind(&TcpChannel::handleSuccessfulConnect, this, _1,
63 clientSocket, connectTimeoutTimer,
64 onFaceCreated, onConnectFailed));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080065
66 connectTimeoutTimer->expires_from_now(timeout);
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080067 connectTimeoutTimer->async_wait(bind(&TcpChannel::handleFailedConnect, this, _1,
68 clientSocket, connectTimeoutTimer,
69 onConnectFailed));
Alexander Afanasyeva9034b02014-01-26 18:32:02 -080070}
71
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080072void
73TcpChannel::connect(const std::string& remoteHost, const std::string& remotePort,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080074 const TcpChannel::FaceCreatedCallback& onFaceCreated,
75 const TcpChannel::ConnectFailedCallback& onConnectFailed,
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080076 const time::Duration& timeout/* = time::seconds(4)*/)
77{
78 shared_ptr<ip::tcp::socket> clientSocket =
79 make_shared<ip::tcp::socket>(boost::ref(m_ioService));
80
81 shared_ptr<monotonic_deadline_timer> connectTimeoutTimer =
82 make_shared<monotonic_deadline_timer>(boost::ref(m_ioService));
83
84 // not sure if it works. This will bind to something...
85 // Do we need reuse here too?
86 clientSocket->bind(m_localEndpoint);
87
88 ip::tcp::resolver::query query(remoteHost, remotePort);
89 shared_ptr<ip::tcp::resolver> resolver =
90 make_shared<ip::tcp::resolver>(boost::ref(m_ioService));
91
92 resolver->async_resolve(query,
93 bind(&TcpChannel::handleEndpointResoution, this, _1, _2,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080094 clientSocket, connectTimeoutTimer,
95 onFaceCreated, onConnectFailed));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -080096
97 connectTimeoutTimer->expires_from_now(timeout);
Alexander Afanasyev855a53c2014-01-27 12:03:00 -080098 connectTimeoutTimer->async_wait(bind(&TcpChannel::handleFailedConnect, this, _1,
99 clientSocket, connectTimeoutTimer,
100 onConnectFailed));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800101}
102
103
104void
105TcpChannel::handleConnection(const boost::system::error_code& error,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800106 const shared_ptr<ip::tcp::socket>& socket,
107 const FaceCreatedCallback& onFaceCreated,
108 const ConnectFailedCallback& onConnectFailed)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800109{
110 if (error) {
111 if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
112 return;
113
114 /// \todo Log the error
115 onConnectFailed("Connect to remote endpoint failed: " +
116 error.category().message(error.value()));
117 return;
118 }
119
Junxiao Shi8c8d2182014-01-30 22:33:00 -0700120 shared_ptr<TcpFace> face = make_shared<TcpFace>(boost::cref(socket));
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800121 onFaceCreated(face);
Alexander Afanasyev334b7142014-01-28 12:59:39 -0800122
123 tcp::Endpoint remoteEndpoint = socket->remote_endpoint();
124 m_channelFaces[remoteEndpoint] = face;
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800125}
126
127void
128TcpChannel::handleSuccessfulConnect(const boost::system::error_code& error,
129 const shared_ptr<ip::tcp::socket>& socket,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800130 const shared_ptr<monotonic_deadline_timer>& timer,
131 const FaceCreatedCallback& onFaceCreated,
132 const ConnectFailedCallback& onConnectFailed)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800133{
134 timer->cancel();
135
136 if (error) {
137 if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
138 return;
139
140 socket->close();
141 onConnectFailed("Connect to remote endpoint failed: " +
142 error.category().message(error.value()));
143 return;
144 }
145
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800146 handleConnection(error, socket, onFaceCreated, onConnectFailed);
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800147}
148
149void
150TcpChannel::handleFailedConnect(const boost::system::error_code& error,
151 const shared_ptr<ip::tcp::socket>& socket,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800152 const shared_ptr<monotonic_deadline_timer>& timer,
153 const ConnectFailedCallback& onConnectFailed)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800154{
155 if (error) { // e.g., cancelled
156 return;
157 }
158
159 onConnectFailed("Connect to remote endpoint timed out: " +
160 error.category().message(error.value()));
161 socket->close(); // abort the connection
162}
163
164void
165TcpChannel::handleEndpointResoution(const boost::system::error_code& error,
166 ip::tcp::resolver::iterator remoteEndpoint,
167 const shared_ptr<boost::asio::ip::tcp::socket>& socket,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800168 const shared_ptr<boost::asio::monotonic_deadline_timer>& timer,
169 const FaceCreatedCallback& onFaceCreated,
170 const ConnectFailedCallback& onConnectFailed)
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800171{
172 if (error ||
173 remoteEndpoint == ip::tcp::resolver::iterator())
174 {
175 if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
176 return;
177
178 socket->close();
179 timer->cancel();
180 onConnectFailed("Remote endpoint hostname or port cannot be resolved: " +
181 error.category().message(error.value()));
182 return;
183 }
184
185 // got endpoint, now trying to connect (only try the first resolution option)
186 socket->async_connect(*remoteEndpoint,
Alexander Afanasyev855a53c2014-01-27 12:03:00 -0800187 bind(&TcpChannel::handleSuccessfulConnect, this, _1,
188 socket, timer,
189 onFaceCreated, onConnectFailed));
Alexander Afanasyev8ad71ba2014-01-27 00:07:14 -0800190}
191
192
Alexander Afanasyev18bbf812014-01-29 01:40:23 -0800193} // namespace nfd