face: Implementing close operation and many related fixes in TcpFace and TcpChannel
Change-Id: Ib6b751e80454e149bf94f3867663d5e705cbf4a0
refs: #1250, #1248
diff --git a/daemon/face/stream-face.hpp b/daemon/face/stream-face.hpp
index 529e3ef..65ca82e 100644
--- a/daemon/face/stream-face.hpp
+++ b/daemon/face/stream-face.hpp
@@ -17,8 +17,24 @@
public:
typedef T protocol;
+ /**
+ * \brief Create instance of StreamFace
+ */
StreamFace(const shared_ptr<typename protocol::socket>& socket);
+ virtual
+ ~StreamFace();
+
+ // from Face
+ virtual void
+ sendInterest(const Interest& interest);
+
+ virtual void
+ sendData(const Data& data);
+
+ virtual void
+ close();
+
protected:
void
handleSend(const boost::system::error_code& error,
@@ -28,17 +44,25 @@
handleReceive(const boost::system::error_code& error,
std::size_t bytes_recvd);
+ void
+ keepFaceAliveUntilAllHandlersExecuted(const shared_ptr<Face>& face);
+
protected:
shared_ptr<typename protocol::socket> m_socket;
-
+
private:
uint8_t m_inputBuffer[MAX_NDN_PACKET_SIZE];
std::size_t m_inputBufferSize;
+#ifdef _DEBUG
+ typename protocol::endpoint m_localEndpoint;
+#endif
+
NFD_LOG_INCLASS_DECLARE();
};
-NFD_LOG_INCLASS_TEMPLATE_DEFINE(StreamFace, "StreamFace");
+// All inherited classes must use
+// NFD_LOG_INCLASS_TEMPLATE_SPECIALIZATION_DEFINE(StreamFace, <specialization-parameter>, "Name");
template <class T>
inline
@@ -49,6 +73,58 @@
bind(&StreamFace<T>::handleReceive, this, _1, _2));
}
+template <class T>
+inline
+StreamFace<T>::~StreamFace()
+{
+}
+
+
+template <class T>
+inline void
+StreamFace<T>::sendInterest(const Interest& interest)
+{
+ m_socket->async_send(boost::asio::buffer(interest.wireEncode().wire(),
+ interest.wireEncode().size()),
+ bind(&StreamFace<T>::handleSend, this, _1, interest.wireEncode()));
+
+ // anything else should be done here?
+}
+
+template <class T>
+inline void
+StreamFace<T>::sendData(const Data& data)
+{
+ m_socket->async_send(boost::asio::buffer(data.wireEncode().wire(),
+ data.wireEncode().size()),
+ bind(&StreamFace<T>::handleSend, this, _1, data.wireEncode()));
+
+ // anything else should be done here?
+}
+
+template <class T>
+inline void
+StreamFace<T>::close()
+{
+ if (!m_socket->is_open())
+ return;
+
+ NFD_LOG_INFO("[id:" << this->getId()
+ << ",endpoint:" << m_socket->local_endpoint()
+ << "] Close connection");
+
+
+ boost::asio::io_service& io = m_socket->get_io_service();
+ m_socket->close();
+ // after this, handleSend/handleReceive will be called with set error code
+
+ // ensure that if that Face object is alive at least until all pending
+ // methods are dispatched
+ io.post(bind(&StreamFace<T>::keepFaceAliveUntilAllHandlersExecuted,
+ this, this->shared_from_this()));
+
+ onFail("Close connection");
+}
template <class T>
inline void
@@ -59,14 +135,43 @@
if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
return;
- NFD_LOG_WARN("[id:" << this->getId()
- << ",endpoint:" << m_socket->local_endpoint()
- << "] Send operation failed, closing socket: "
- << error.category().message(error.value()));
+ if (!m_socket->is_open())
+ {
+ onFail("Connection closed");
+ return;
+ }
- onFail("Send operation failed, closing socket: " +
- error.category().message(error.value()));
+ if (error == boost::asio::error::eof)
+ {
+ NFD_LOG_INFO("[id:" << this->getId()
+ << ",endpoint:" << m_socket->local_endpoint()
+ << "] Connection closed");
+ }
+ else
+ {
+ NFD_LOG_WARN("[id:" << this->getId()
+ << ",endpoint:" << m_socket->local_endpoint()
+ << "] Send operation failed, closing socket: "
+ << error.category().message(error.value()));
+ }
+
+ boost::asio::io_service& io = m_socket->get_io_service();
m_socket->close();
+
+ // ensure that if that Face object is alive at least until all pending
+ // methods are dispatched
+ io.post(bind(&StreamFace<T>::keepFaceAliveUntilAllHandlersExecuted,
+ this, this->shared_from_this()));
+
+ if (error == boost::asio::error::eof)
+ {
+ onFail("Connection closed");
+ }
+ else
+ {
+ onFail("Send operation failed, closing socket: " +
+ error.category().message(error.value()));
+ }
return;
}
@@ -81,25 +186,55 @@
StreamFace<T>::handleReceive(const boost::system::error_code& error,
std::size_t bytes_recvd)
{
- NFD_LOG_TRACE("[id:" << this->getId()
- << ",endpoint:" << m_socket->local_endpoint()
- << "] Received: " << bytes_recvd << " bytes");
-
if (error || bytes_recvd == 0) {
if (error == boost::system::errc::operation_canceled) // when socket is closed by someone
return;
- NFD_LOG_WARN("[id:" << this->getId()
- << ",endpoint:" << m_socket->local_endpoint()
- << "] Receive operation failed: "
- << error.category().message(error.value()));
+ // this should be unnecessary, but just in case
+ if (!m_socket->is_open())
+ {
+ onFail("Connection closed");
+ return;
+ }
+
+ if (error == boost::asio::error::eof)
+ {
+ NFD_LOG_INFO("[id:" << this->getId()
+ << ",endpoint:" << m_socket->local_endpoint()
+ << "] Connection closed");
+ }
+ else
+ {
+ NFD_LOG_WARN("[id:" << this->getId()
+ << ",endpoint:" << m_socket->local_endpoint()
+ << "] Receive operation failed: "
+ << error.category().message(error.value()));
+ }
- onFail("Receive operation failed, closing socket: " +
- error.category().message(error.value()));
+ boost::asio::io_service& io = m_socket->get_io_service();
m_socket->close();
+
+ // ensure that if that Face object is alive at least until all pending
+ // methods are dispatched
+ io.post(bind(&StreamFace<T>::keepFaceAliveUntilAllHandlersExecuted,
+ this, this->shared_from_this()));
+
+ if (error == boost::asio::error::eof)
+ {
+ onFail("Connection closed");
+ }
+ else
+ {
+ onFail("Receive operation failed, closing socket: " +
+ error.category().message(error.value()));
+ }
return;
}
+ NFD_LOG_TRACE("[id:" << this->getId()
+ << ",endpoint:" << m_socket->local_endpoint()
+ << "] Received: " << bytes_recvd << " bytes");
+
m_inputBufferSize += bytes_recvd;
// do magic
@@ -150,8 +285,15 @@
<< "] Received input is invalid or too large to process, "
<< "closing down the face");
- onFail("Received input is invalid or too large to process, closing down the face");
+ boost::asio::io_service& io = m_socket->get_io_service();
m_socket->close();
+
+ // ensure that if that Face object is alive at least until all pending
+ // methods are dispatched
+ io.post(bind(&StreamFace<T>::keepFaceAliveUntilAllHandlersExecuted,
+ this, this->shared_from_this()));
+
+ onFail("Received input is invalid or too large to process, closing down the face");
return;
}
}
@@ -175,6 +317,12 @@
bind(&StreamFace<T>::handleReceive, this, _1, _2));
}
+template <class T>
+inline void
+StreamFace<T>::keepFaceAliveUntilAllHandlersExecuted(const shared_ptr<Face>& face)
+{
+}
+
} // namespace nfd