blob: d7f2e49f0e4ba60e8383fbb46f871af8dc37bf1a [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();
71 throw Transport::Error(error, "error while connecting to the forwarder");
72 }
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();
Alexander Afanasyev5964fb72014-02-18 12:42:45 -080082 throw Transport::Error(error, "error while connecting to the forwarder");
83 }
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();
195 throw 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();
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800234 throw Transport::Error(error, "error while receiving data from socket");
235 }
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();
Alexander Afanasyev937aa782014-03-21 13:17:57 -0700245 throw Transport::Error(boost::system::error_code(),
246 "input buffer full, but a valid TLV cannot be decoded");
247 }
248
249 if (offset > 0)
250 {
251 if (offset != m_inputBufferSize)
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800252 {
Alexander Afanasyev937aa782014-03-21 13:17:57 -0700253 std::copy(m_inputBuffer + offset, m_inputBuffer + m_inputBufferSize,
254 m_inputBuffer);
255 m_inputBufferSize -= offset;
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800256 }
257 else
258 {
Alexander Afanasyev937aa782014-03-21 13:17:57 -0700259 m_inputBufferSize = 0;
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800260 }
261 }
262
Alexander Afanasyev937aa782014-03-21 13:17:57 -0700263 m_socket.async_receive(boost::asio::buffer(m_inputBuffer + m_inputBufferSize,
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -0700264 MAX_NDN_PACKET_SIZE - m_inputBufferSize), 0,
265 bind(&Impl::handleAsyncReceive, this, _1, _2));
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800266 }
267
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800268protected:
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -0700269 BaseTransport& m_transport;
Alexander Afanasyev937aa782014-03-21 13:17:57 -0700270
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -0700271 typename Protocol::socket m_socket;
272 uint8_t m_inputBuffer[MAX_NDN_PACKET_SIZE];
Alexander Afanasyev937aa782014-03-21 13:17:57 -0700273 size_t m_inputBufferSize;
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800274
Alexander Afanasyev6a05b4b2014-07-18 17:23:00 -0700275 TransmissionQueue m_transmissionQueue;
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800276 bool m_connectionInProgress;
277
278 boost::asio::deadline_timer m_connectTimer;
279};
280
281
282template<class BaseTransport, class Protocol>
283class StreamTransportWithResolverImpl : public StreamTransportImpl<BaseTransport, Protocol>
284{
285public:
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -0700286 typedef StreamTransportWithResolverImpl<BaseTransport,Protocol> Impl;
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800287
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -0700288 StreamTransportWithResolverImpl(BaseTransport& transport, boost::asio::io_service& ioService)
289 : StreamTransportImpl<BaseTransport, Protocol>(transport, ioService)
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800290 {
291 }
Alexander Afanasyev937aa782014-03-21 13:17:57 -0700292
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800293 void
294 resolveHandler(const boost::system::error_code& error,
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -0700295 typename Protocol::resolver::iterator endpoint,
296 const shared_ptr<typename Protocol::resolver>&)
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800297 {
298 if (error)
299 {
300 if (error == boost::system::errc::operation_canceled)
301 return;
Alexander Afanasyev937aa782014-03-21 13:17:57 -0700302
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800303 throw Transport::Error(error, "Error during resolution of host or port");
304 }
Alexander Afanasyev937aa782014-03-21 13:17:57 -0700305
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -0700306 typename Protocol::resolver::iterator end;
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800307 if (endpoint == end)
308 {
Alexander Afanasyevbc5830a2014-07-11 15:02:38 -0700309 this->m_transport.close();
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800310 throw Transport::Error(error, "Unable to resolve because host or port");
311 }
312
313 this->m_socket.async_connect(*endpoint,
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -0700314 bind(&Impl::connectHandler, this, _1));
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800315 }
Alexander Afanasyev937aa782014-03-21 13:17:57 -0700316
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800317 void
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -0700318 connect(const typename Protocol::resolver::query& query)
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800319 {
320 if (!this->m_connectionInProgress) {
321 this->m_connectionInProgress = true;
322
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -0700323 // Wait at most 4 seconds to connect
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800324 /// @todo Decide whether this number should be configurable
325 this->m_connectTimer.expires_from_now(boost::posix_time::seconds(4));
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -0700326 this->m_connectTimer.async_wait(bind(&Impl::connectTimeoutHandler, this, _1));
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800327
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -0700328 // typename boost::asio::ip::basic_resolver< Protocol > resolver;
329 shared_ptr<typename Protocol::resolver> resolver =
330 make_shared<typename Protocol::resolver>(ref(this->m_socket.get_io_service()));
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800331
Alexander Afanasyev49bb1fb2014-07-21 12:54:01 -0700332 resolver->async_resolve(query, bind(&Impl::resolveHandler, this, _1, _2, resolver));
Alexander Afanasyev5964fb72014-02-18 12:42:45 -0800333 }
334 }
335};
336
337
338} // namespace ndn
339
340#endif // NDN_TRANSPORT_STREAM_TRANSPORT_HPP