blob: 99dc510c7c3cb594bc06027a382f3ff9b5638320 [file] [log] [blame]
Alexander Afanasyevc169a812014-05-20 20:37:29 -04001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Alexander Afanasyev5964fb72014-02-18 12:42:45 -08002/**
Junxiao Shi02a4bf32015-02-21 21:07:46 -07003 * Copyright (c) 2013-2015 Regents of the University of California.
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -07004 *
5 * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
Alexander Afanasyevdfa52c42014-04-24 21:10:11 -07006 *
Alexander Afanasyevc169a812014-05-20 20:37:29 -04007 * ndn-cxx library is free software: you can redistribute it and/or modify it under the
8 * terms of the GNU Lesser General Public License as published by the Free Software
9 * Foundation, either version 3 of the License, or (at your option) any later version.
10 *
11 * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
12 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13 * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
14 *
15 * You should have received copies of the GNU General Public License and GNU Lesser
16 * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
17 * <http://www.gnu.org/licenses/>.
18 *
19 * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
Alexander Afanasyev5964fb72014-02-18 12:42:45 -080020 */
21
22#ifndef NDN_TRANSPORT_STREAM_TRANSPORT_HPP
23#define NDN_TRANSPORT_STREAM_TRANSPORT_HPP
24
Junxiao Shi71355d52014-12-11 09:20:44 -070025#include "transport.hpp"
Alexander Afanasyev5964fb72014-02-18 12:42:45 -080026
Alexander Afanasyev258ec2b2014-05-14 16:15:37 -070027#include <list>
28
Alexander Afanasyev5964fb72014-02-18 12:42:45 -080029namespace ndn {
30
Alexander Afanasyev5964fb72014-02-18 12:42:45 -080031template<class BaseTransport, class Protocol>
32class StreamTransportImpl
33{
34public:
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -070035 typedef StreamTransportImpl<BaseTransport,Protocol> Impl;
Alexander Afanasyev5964fb72014-02-18 12:42:45 -080036
Alexander Afanasyev6a05b4b2014-07-18 17:23:00 -070037 typedef std::list<Block> BlockSequence;
38 typedef std::list<BlockSequence> TransmissionQueue;
39
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -070040 StreamTransportImpl(BaseTransport& transport, boost::asio::io_service& ioService)
Alexander Afanasyev5964fb72014-02-18 12:42:45 -080041 : m_transport(transport)
42 , m_socket(ioService)
Alexander Afanasyev937aa782014-03-21 13:17:57 -070043 , m_inputBufferSize(0)
Alexander Afanasyev5964fb72014-02-18 12:42:45 -080044 , m_connectionInProgress(false)
45 , m_connectTimer(ioService)
46 {
47 }
Alexander Afanasyev937aa782014-03-21 13:17:57 -070048
Alexander Afanasyev5964fb72014-02-18 12:42:45 -080049 void
50 connectHandler(const boost::system::error_code& error)
51 {
52 m_connectionInProgress = false;
53 m_connectTimer.cancel();
54
55 if (!error)
56 {
Alexander Afanasyev52afb3f2014-03-07 09:05:35 +000057 resume();
Alexander Afanasyev5964fb72014-02-18 12:42:45 -080058 m_transport.m_isConnected = true;
59
Alexander Afanasyev6a05b4b2014-07-18 17:23:00 -070060 if (!m_transmissionQueue.empty()) {
61 boost::asio::async_write(m_socket, *m_transmissionQueue.begin(),
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -070062 bind(&Impl::handleAsyncWrite, this, _1,
Alexander Afanasyev6a05b4b2014-07-18 17:23:00 -070063 m_transmissionQueue.begin()));
64 }
Alexander Afanasyev5964fb72014-02-18 12:42:45 -080065 }
66 else
67 {
68 // may need to throw exception
69 m_transport.m_isConnected = false;
70 m_transport.close();
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -070071 BOOST_THROW_EXCEPTION(Transport::Error(error, "error while connecting to the forwarder"));
Alexander Afanasyev5964fb72014-02-18 12:42:45 -080072 }
73 }
74
75 void
76 connectTimeoutHandler(const boost::system::error_code& error)
77 {
78 if (error) // e.g., cancelled timer
79 return;
80
Alexander Afanasyevbc5830a2014-07-11 15:02:38 -070081 m_transport.close();
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -070082 BOOST_THROW_EXCEPTION(Transport::Error(error, "error while connecting to the forwarder"));
Alexander Afanasyev5964fb72014-02-18 12:42:45 -080083 }
84
85 void
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -070086 connect(const typename Protocol::endpoint& endpoint)
Alexander Afanasyev5964fb72014-02-18 12:42:45 -080087 {
88 if (!m_connectionInProgress) {
89 m_connectionInProgress = true;
90
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -070091 // Wait at most 4 seconds to connect
Alexander Afanasyev5964fb72014-02-18 12:42:45 -080092 /// @todo Decide whether this number should be configurable
93 m_connectTimer.expires_from_now(boost::posix_time::seconds(4));
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -070094 m_connectTimer.async_wait(bind(&Impl::connectTimeoutHandler, this, _1));
Alexander Afanasyev5964fb72014-02-18 12:42:45 -080095
96 m_socket.open();
97 m_socket.async_connect(endpoint,
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -070098 bind(&Impl::connectHandler, this, _1));
Alexander Afanasyev5964fb72014-02-18 12:42:45 -080099 }
100 }
Alexander Afanasyev937aa782014-03-21 13:17:57 -0700101
102 void
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800103 close()
104 {
Alexander Afanasyevbc5830a2014-07-11 15:02:38 -0700105 m_connectionInProgress = false;
106
Alexander Afanasyev6507fb12014-04-28 23:18:56 -0700107 boost::system::error_code error; // to silently ignore all errors
108 m_connectTimer.cancel(error);
109 m_socket.cancel(error);
110 m_socket.close(error);
111
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800112 m_transport.m_isConnected = false;
Alexander Afanasyev6e0c5a52014-03-18 16:18:58 -0700113 m_transport.m_isExpectingData = false;
Alexander Afanasyev6a05b4b2014-07-18 17:23:00 -0700114 m_transmissionQueue.clear();
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800115 }
116
117 void
Alexander Afanasyev52afb3f2014-03-07 09:05:35 +0000118 pause()
119 {
Alexander Afanasyev6507fb12014-04-28 23:18:56 -0700120 if (m_connectionInProgress)
121 return;
122
Alexander Afanasyev52afb3f2014-03-07 09:05:35 +0000123 if (m_transport.m_isExpectingData)
124 {
125 m_transport.m_isExpectingData = false;
126 m_socket.cancel();
127 }
128 }
129
Alexander Afanasyev9d158f02015-02-17 21:30:19 -0800130 /**
131 * @warning Must not be called directly or indirectly from within handleAsyncReceive invocation
132 */
Alexander Afanasyev52afb3f2014-03-07 09:05:35 +0000133 void
134 resume()
135 {
Alexander Afanasyev6507fb12014-04-28 23:18:56 -0700136 if (m_connectionInProgress)
137 return;
138
Alexander Afanasyev52afb3f2014-03-07 09:05:35 +0000139 if (!m_transport.m_isExpectingData)
140 {
141 m_transport.m_isExpectingData = true;
Alexander Afanasyev937aa782014-03-21 13:17:57 -0700142 m_inputBufferSize = 0;
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -0700143 m_socket.async_receive(boost::asio::buffer(m_inputBuffer, MAX_NDN_PACKET_SIZE), 0,
144 bind(&Impl::handleAsyncReceive, this, _1, _2));
Alexander Afanasyev52afb3f2014-03-07 09:05:35 +0000145 }
146 }
147
148 void
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800149 send(const Block& wire)
150 {
Alexander Afanasyev6a05b4b2014-07-18 17:23:00 -0700151 BlockSequence sequence;
152 sequence.push_back(wire);
153 m_transmissionQueue.push_back(sequence);
154
155 if (m_transport.m_isConnected && m_transmissionQueue.size() == 1) {
156 boost::asio::async_write(m_socket, *m_transmissionQueue.begin(),
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -0700157 bind(&Impl::handleAsyncWrite, this, _1,
Alexander Afanasyev6a05b4b2014-07-18 17:23:00 -0700158 m_transmissionQueue.begin()));
159 }
160
161 // if not connected or there is transmission in progress (m_transmissionQueue.size() > 1),
162 // next write will be scheduled either in connectHandler or in asyncWriteHandler
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800163 }
164
165 void
166 send(const Block& header, const Block& payload)
167 {
Alexander Afanasyev6a05b4b2014-07-18 17:23:00 -0700168 BlockSequence sequence;
169 sequence.push_back(header);
170 sequence.push_back(payload);
171 m_transmissionQueue.push_back(sequence);
Alexander Afanasyev937aa782014-03-21 13:17:57 -0700172
Alexander Afanasyev6a05b4b2014-07-18 17:23:00 -0700173 if (m_transport.m_isConnected && m_transmissionQueue.size() == 1) {
174 boost::asio::async_write(m_socket, *m_transmissionQueue.begin(),
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -0700175 bind(&Impl::handleAsyncWrite, this, _1,
Alexander Afanasyev6a05b4b2014-07-18 17:23:00 -0700176 m_transmissionQueue.begin()));
177 }
178
179 // if not connected or there is transmission in progress (m_transmissionQueue.size() > 1),
180 // next write will be scheduled either in connectHandler or in asyncWriteHandler
181 }
182
183 void
184 handleAsyncWrite(const boost::system::error_code& error,
185 TransmissionQueue::iterator queueItem)
186 {
187 if (error)
188 {
189 if (error == boost::system::errc::operation_canceled) {
190 // async receive has been explicitly cancelled (e.g., socket close)
191 return;
192 }
193
194 m_transport.close();
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700195 BOOST_THROW_EXCEPTION(Transport::Error(error, "error while sending data to socket"));
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800196 }
Alexander Afanasyev6a05b4b2014-07-18 17:23:00 -0700197
198 m_transmissionQueue.erase(queueItem);
199
200 if (!m_transmissionQueue.empty()) {
201 boost::asio::async_write(m_socket, *m_transmissionQueue.begin(),
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -0700202 bind(&Impl::handleAsyncWrite, this, _1,
Alexander Afanasyev6a05b4b2014-07-18 17:23:00 -0700203 m_transmissionQueue.begin()));
204 }
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800205 }
Alexander Afanasyev937aa782014-03-21 13:17:57 -0700206
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -0700207 bool
208 processAll(uint8_t* buffer, size_t& offset, size_t nBytesAvailable)
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800209 {
Junxiao Shi02a4bf32015-02-21 21:07:46 -0700210 while (offset < nBytesAvailable) {
211 bool isOk = false;
212 Block element;
213 std::tie(isOk, element) = Block::fromBuffer(buffer + offset, nBytesAvailable - offset);
214 if (!isOk)
215 return false;
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800216
Junxiao Shi02a4bf32015-02-21 21:07:46 -0700217 m_transport.receive(element);
218 offset += element.size();
219 }
Alexander Afanasyev937aa782014-03-21 13:17:57 -0700220 return true;
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800221 }
Alexander Afanasyev937aa782014-03-21 13:17:57 -0700222
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800223 void
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -0700224 handleAsyncReceive(const boost::system::error_code& error, std::size_t nBytesRecvd)
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800225 {
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800226 if (error)
227 {
228 if (error == boost::system::errc::operation_canceled) {
229 // async receive has been explicitly cancelled (e.g., socket close)
230 return;
231 }
Alexander Afanasyev937aa782014-03-21 13:17:57 -0700232
Alexander Afanasyevbc5830a2014-07-11 15:02:38 -0700233 m_transport.close();
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700234 BOOST_THROW_EXCEPTION(Transport::Error(error, "error while receiving data from socket"));
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800235 }
Alexander Afanasyev937aa782014-03-21 13:17:57 -0700236
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -0700237 m_inputBufferSize += nBytesRecvd;
Alexander Afanasyev937aa782014-03-21 13:17:57 -0700238 // do magic
239
240 std::size_t offset = 0;
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -0700241 bool hasProcessedSome = processAll(m_inputBuffer, offset, m_inputBufferSize);
242 if (!hasProcessedSome && m_inputBufferSize == MAX_NDN_PACKET_SIZE && offset == 0)
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800243 {
Alexander Afanasyevbc5830a2014-07-11 15:02:38 -0700244 m_transport.close();
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700245 BOOST_THROW_EXCEPTION(Transport::Error(boost::system::error_code(),
246 "input buffer full, but a valid TLV cannot be "
247 "decoded"));
Alexander Afanasyev937aa782014-03-21 13:17:57 -0700248 }
249
250 if (offset > 0)
251 {
252 if (offset != m_inputBufferSize)
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800253 {
Alexander Afanasyev937aa782014-03-21 13:17:57 -0700254 std::copy(m_inputBuffer + offset, m_inputBuffer + m_inputBufferSize,
255 m_inputBuffer);
256 m_inputBufferSize -= offset;
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800257 }
258 else
259 {
Alexander Afanasyev937aa782014-03-21 13:17:57 -0700260 m_inputBufferSize = 0;
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800261 }
262 }
263
Alexander Afanasyev937aa782014-03-21 13:17:57 -0700264 m_socket.async_receive(boost::asio::buffer(m_inputBuffer + m_inputBufferSize,
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -0700265 MAX_NDN_PACKET_SIZE - m_inputBufferSize), 0,
266 bind(&Impl::handleAsyncReceive, this, _1, _2));
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800267 }
268
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800269protected:
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -0700270 BaseTransport& m_transport;
Alexander Afanasyev937aa782014-03-21 13:17:57 -0700271
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -0700272 typename Protocol::socket m_socket;
273 uint8_t m_inputBuffer[MAX_NDN_PACKET_SIZE];
Alexander Afanasyev937aa782014-03-21 13:17:57 -0700274 size_t m_inputBufferSize;
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800275
Alexander Afanasyev6a05b4b2014-07-18 17:23:00 -0700276 TransmissionQueue m_transmissionQueue;
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800277 bool m_connectionInProgress;
278
279 boost::asio::deadline_timer m_connectTimer;
280};
281
282
283template<class BaseTransport, class Protocol>
284class StreamTransportWithResolverImpl : public StreamTransportImpl<BaseTransport, Protocol>
285{
286public:
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -0700287 typedef StreamTransportWithResolverImpl<BaseTransport,Protocol> Impl;
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800288
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -0700289 StreamTransportWithResolverImpl(BaseTransport& transport, boost::asio::io_service& ioService)
290 : StreamTransportImpl<BaseTransport, Protocol>(transport, ioService)
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800291 {
292 }
Alexander Afanasyev937aa782014-03-21 13:17:57 -0700293
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800294 void
295 resolveHandler(const boost::system::error_code& error,
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -0700296 typename Protocol::resolver::iterator endpoint,
297 const shared_ptr<typename Protocol::resolver>&)
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800298 {
299 if (error)
300 {
301 if (error == boost::system::errc::operation_canceled)
302 return;
Alexander Afanasyev937aa782014-03-21 13:17:57 -0700303
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700304 BOOST_THROW_EXCEPTION(Transport::Error(error, "Error during resolution of host or port"));
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800305 }
Alexander Afanasyev937aa782014-03-21 13:17:57 -0700306
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -0700307 typename Protocol::resolver::iterator end;
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800308 if (endpoint == end)
309 {
Alexander Afanasyevbc5830a2014-07-11 15:02:38 -0700310 this->m_transport.close();
Spyridon Mastorakis0d2ed2e2015-07-27 19:09:12 -0700311 BOOST_THROW_EXCEPTION(Transport::Error(error, "Unable to resolve because host or port"));
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800312 }
313
314 this->m_socket.async_connect(*endpoint,
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -0700315 bind(&Impl::connectHandler, this, _1));
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800316 }
Alexander Afanasyev937aa782014-03-21 13:17:57 -0700317
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800318 void
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -0700319 connect(const typename Protocol::resolver::query& query)
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800320 {
321 if (!this->m_connectionInProgress) {
322 this->m_connectionInProgress = true;
323
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -0700324 // Wait at most 4 seconds to connect
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800325 /// @todo Decide whether this number should be configurable
326 this->m_connectTimer.expires_from_now(boost::posix_time::seconds(4));
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -0700327 this->m_connectTimer.async_wait(bind(&Impl::connectTimeoutHandler, this, _1));
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800328
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -0700329 // typename boost::asio::ip::basic_resolver< Protocol > resolver;
330 shared_ptr<typename Protocol::resolver> resolver =
331 make_shared<typename Protocol::resolver>(ref(this->m_socket.get_io_service()));
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800332
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -0700333 resolver->async_resolve(query, bind(&Impl::resolveHandler, this, _1, _2, resolver));
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800334 }
335 }
336};
337
338
339} // namespace ndn
340
341#endif // NDN_TRANSPORT_STREAM_TRANSPORT_HPP