blob: 78a4a12f96a7c1282d5347510832c60d11f44573 [file] [log] [blame]
Giulio Grassi624f6c62014-02-18 19:42:14 +01001/* -*- 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#ifndef NFD_FACE_DATAGRAM_FACE_HPP
8#define NFD_FACE_DATAGRAM_FACE_HPP
9
10#include "face.hpp"
11
12namespace nfd {
13
14template <class T>
15class DatagramFace : public Face
16{
17public:
18 typedef T protocol;
19
20 explicit
Alexander Afanasyeva39b90b2014-03-05 15:31:00 +000021 DatagramFace(const FaceUri& uri,
22 const shared_ptr<typename protocol::socket>& socket);
Giulio Grassi624f6c62014-02-18 19:42:14 +010023
24 virtual
25 ~DatagramFace();
26
27 // from Face
28 virtual void
29 sendInterest(const Interest& interest);
30
31 virtual void
32 sendData(const Data& data);
33
34 virtual void
35 close();
36
37 void
38 handleSend(const boost::system::error_code& error,
39 const Block& wire);
40
41 void
42 handleReceive(const boost::system::error_code& error,
43 size_t nBytesReceived);
44
45protected:
46
47 void
48 receiveDatagram(const uint8_t* buffer,
49 size_t nBytesReceived,
50 const boost::system::error_code& error);
51
52 void
53 keepFaceAliveUntilAllHandlersExecuted(const shared_ptr<Face>& face);
54
55 void
56 closeSocket();
57
58protected:
59 shared_ptr<typename protocol::socket> m_socket;
60 uint8_t m_inputBuffer[MAX_NDN_PACKET_SIZE];
61
62 NFD_LOG_INCLASS_DECLARE();
63
64};
65
66template <class T>
67inline
Alexander Afanasyeva39b90b2014-03-05 15:31:00 +000068DatagramFace<T>::DatagramFace(const FaceUri& uri,
69 const shared_ptr<typename DatagramFace::protocol::socket>& socket)
70 : Face(uri)
71 , m_socket(socket)
Giulio Grassi624f6c62014-02-18 19:42:14 +010072{
73 m_socket->async_receive(boost::asio::buffer(m_inputBuffer, MAX_NDN_PACKET_SIZE), 0,
74 bind(&DatagramFace<T>::handleReceive, this, _1, _2));
75}
76
77template <class T>
78inline
79DatagramFace<T>::~DatagramFace()
80{
81}
82
83template <class T>
84inline void
85DatagramFace<T>::sendInterest(const Interest& interest)
86{
87 m_socket->async_send(boost::asio::buffer(interest.wireEncode().wire(),
88 interest.wireEncode().size()),
89 bind(&DatagramFace<T>::handleSend, this, _1, interest.wireEncode()));
90
91 // anything else should be done here?
92}
93
94template <class T>
95inline void
96DatagramFace<T>::sendData(const Data& data)
97{
98 m_socket->async_send(boost::asio::buffer(data.wireEncode().wire(),
99 data.wireEncode().size()),
100 bind(&DatagramFace<T>::handleSend, this, _1, data.wireEncode()));
101
102 // anything else should be done here?
103}
104
105template <class T>
106inline void
107DatagramFace<T>::handleSend(const boost::system::error_code& error,
108 const Block& wire)
109{
110 if (error != 0) {
111 if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
112 return;
113
114 if (!m_socket->is_open())
115 {
116 onFail("Tunnel closed");
117 return;
118 }
119
120 NFD_LOG_WARN("[id:" << this->getId()
121 << ",endpoint:" << m_socket->local_endpoint()
122 << "] Send operation failed, closing socket: "
123 << error.category().message(error.value()));
124
125 closeSocket();
126
127 if (error == boost::asio::error::eof)
128 {
129 onFail("Tunnel closed");
130 }
131 else
132 {
133 onFail("Send operation failed, closing socket: " +
134 error.category().message(error.value()));
135 }
136 return;
137 }
138
139 NFD_LOG_TRACE("[id:" << this->getId()
140 << ",endpoint:" << m_socket->local_endpoint()
141 << "] Successfully sent: " << wire.size() << " bytes");
142 // do nothing (needed to retain validity of wire memory block
143}
144
145template <class T>
146inline void
147DatagramFace<T>::close()
148{
149 if (!m_socket->is_open())
150 return;
151
152 NFD_LOG_INFO("[id:" << this->getId()
153 << ",endpoint:" << m_socket->local_endpoint()
154 << "] Close tunnel");
155
156 closeSocket();
157 onFail("Close tunnel");
158}
159
160template <class T>
161inline void
162DatagramFace<T>::handleReceive(const boost::system::error_code& error,
163 size_t nBytesReceived)
164{
165 receiveDatagram(m_inputBuffer, nBytesReceived, error);
166 m_socket->async_receive(boost::asio::buffer(m_inputBuffer, MAX_NDN_PACKET_SIZE), 0,
167 bind(&DatagramFace<T>::handleReceive, this, _1, _2));
168}
169
170template <class T>
171inline void
172DatagramFace<T>::receiveDatagram(const uint8_t* buffer,
173 size_t nBytesReceived,
174 const boost::system::error_code& error)
175{
176 if (error != 0 || nBytesReceived == 0) {
177 if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
178 return;
179
180 // this should be unnecessary, but just in case
181 if (!m_socket->is_open())
182 {
183 onFail("Tunnel closed");
184 return;
185 }
186
187 NFD_LOG_WARN("[id:" << this->getId()
188 << ",endpoint:" << m_socket->local_endpoint()
189 << "] Receive operation failed: "
190 << error.category().message(error.value()));
191
192 closeSocket();
193
194 if (error == boost::asio::error::eof)
195 {
196 onFail("Tunnel closed");
197 }
198 else
199 {
200 onFail("Receive operation failed, closing socket: " +
201 error.category().message(error.value()));
202 }
203 return;
204 }
205
206 NFD_LOG_TRACE("[id:" << this->getId()
207 << ",endpoint:" << m_socket->local_endpoint()
208 << "] Received: " << nBytesReceived << " bytes");
209
210 /// @todo Eliminate reliance on exceptions in this path
211 try {
212 Block element(buffer, nBytesReceived);
213
214 if (element.size() != nBytesReceived)
215 {
216 NFD_LOG_WARN("[id:" << this->getId()
217 << ",endpoint:" << m_socket->local_endpoint()
218 << "] Received datagram size and decoded "
219 << "element size don't match");
220 /// @todo this message should not extend the face lifetime
221 return;
222 }
223 if (!this->decodeAndDispatchInput(element))
224 {
225 NFD_LOG_WARN("[id:" << this->getId()
226 << ",endpoint:" << m_socket->local_endpoint()
227 << "] Received unrecognized block of type ["
228 << element.type() << "]");
229 // ignore unknown packet and proceed
230 /// @todo this message should not extend the face lifetime
231 return;
232 }
233 }
234 catch(const tlv::Error& e) {
235 NFD_LOG_WARN("[id:" << this->getId()
236 << ",endpoint:" << m_socket->local_endpoint()
237 << "] Received input is invalid");
238 /// @todo this message should not extend the face lifetime
239 return;
240 }
241}
242
243
244template <class T>
245inline void
246DatagramFace<T>::keepFaceAliveUntilAllHandlersExecuted(const shared_ptr<Face>& face)
247{
248}
249
250template <class T>
251inline void
252DatagramFace<T>::closeSocket()
253{
254 boost::asio::io_service& io = m_socket->get_io_service();
255
256 // use the non-throwing variants and ignore errors, if any
257 boost::system::error_code error;
258 m_socket->shutdown(protocol::socket::shutdown_both, error);
259 m_socket->close(error);
260 // after this, handlers will be called with an error code
261
262 // ensure that the Face object is alive at least until all pending
263 // handlers are dispatched
264 io.post(bind(&DatagramFace<T>::keepFaceAliveUntilAllHandlersExecuted,
265 this, this->shared_from_this()));
266}
267
268} // namespace nfd
269
270#endif // NFD_FACE_DATAGRAM_FACE_HPP