|
@@ -6,6 +6,7 @@
|
|
|
|
|
|
#include <asiolink/asio_wrapper.h>
|
|
|
#include <asiolink/unix_domain_socket.h>
|
|
|
+#include <boost/enable_shared_from_this.hpp>
|
|
|
#include <iostream>
|
|
|
using namespace boost::asio::local;
|
|
|
|
|
@@ -13,7 +14,7 @@ namespace isc {
|
|
|
namespace asiolink {
|
|
|
|
|
|
/// @brief Implementation of the unix domain socket.
|
|
|
-class UnixDomainSocketImpl {
|
|
|
+class UnixDomainSocketImpl : public boost::enable_shared_from_this<UnixDomainSocketImpl> {
|
|
|
public:
|
|
|
|
|
|
/// @brief Constructor.
|
|
@@ -30,6 +31,120 @@ public:
|
|
|
close();
|
|
|
}
|
|
|
|
|
|
+ /// @brief Asynchronously connects to an endpoint.
|
|
|
+ ///
|
|
|
+ /// This method schedules asynchronous connect and installs the
|
|
|
+ /// @ref UnixDomainSocketImpl::connectHandler as a callback.
|
|
|
+ ///
|
|
|
+ /// @param endpoint Reference to an endpoint to connect to.
|
|
|
+ /// @param handler User supplied handler to be invoked when the connection
|
|
|
+ /// is established or when error is signalled.
|
|
|
+ void asyncConnect(const stream_protocol::endpoint& endpoint,
|
|
|
+ const UnixDomainSocket::ConnectHandler& handler);
|
|
|
+
|
|
|
+ /// @brief Local handler invoked as a result of asynchronous connection.
|
|
|
+ ///
|
|
|
+ /// This is a wrapper around the user supplied callback. It ignores
|
|
|
+ /// EINPROGRESS errors which are observed on some operating systems as
|
|
|
+ /// a result of trying to connect asynchronously. This error code doesn't
|
|
|
+ /// necessarily indicate a problem and the subsequent attempts to read
|
|
|
+ /// and write to the socket will succeed. Therefore, the handler simply
|
|
|
+ /// overrides this error code with success status. The user supplied
|
|
|
+ /// handler don't need to deal with the EINPROGRESS error codes.
|
|
|
+ ///
|
|
|
+ /// @param remote_handler User supplied callback.
|
|
|
+ /// @param ec Error code returned as a result of connection.
|
|
|
+ void connectHandler(const UnixDomainSocket::ConnectHandler& remote_handler,
|
|
|
+ const boost::system::error_code& ec);
|
|
|
+
|
|
|
+ /// @brief Asynchronously sends data over the socket.
|
|
|
+ ///
|
|
|
+ /// This method schedules an asynchronous send and installs the
|
|
|
+ /// @ref UnixDomainSocketImpl::sendHandler as a callback.
|
|
|
+ ///
|
|
|
+ /// @param data Pointer to data to be sent.
|
|
|
+ /// @param length Number of bytes to be sent.
|
|
|
+ /// @param handler Callback to be invoked when data have been sent or an
|
|
|
+ /// sending error is signalled.
|
|
|
+ void asyncSend(const void* data, const size_t length,
|
|
|
+ const UnixDomainSocket::Handler& handler);
|
|
|
+
|
|
|
+ /// @brief Asynchronously sends the data over the socket.
|
|
|
+ ///
|
|
|
+ /// This method is called by the @ref asyncSend and the @ref sendHandler
|
|
|
+ /// if the asynchronous send has to be repeated as a result of receiving
|
|
|
+ /// EAGAIN or EWOULDBLOCK.
|
|
|
+ ///
|
|
|
+ /// @param buffer Buffers holding the data to be sent.
|
|
|
+ /// @param handler User supplied callback to be invoked when data have
|
|
|
+ /// been sent or sending error is signalled.
|
|
|
+ void doSend(const boost::asio::const_buffers_1& buffer,
|
|
|
+ const UnixDomainSocket::Handler& handler);
|
|
|
+
|
|
|
+
|
|
|
+ /// @brief Local handler invoked as a result of asynchronous send.
|
|
|
+ ///
|
|
|
+ /// This handler is invoked as a result of asynchronous send. It is a
|
|
|
+ /// wrapper callback around the user supplied callback. It handles
|
|
|
+ /// EWOULDBLOCK and EAGAIN errors by retrying an asynchronous send.
|
|
|
+ /// These errors are often returned on some operating systems, even
|
|
|
+ /// though one would expect that asynchronous operation would not
|
|
|
+ /// return such errors. Because these errors are handled by the
|
|
|
+ /// wrapper callback, the user supplied callback never receives
|
|
|
+ /// these errors.
|
|
|
+ ///
|
|
|
+ /// @param remote_handler User supplied callback.
|
|
|
+ /// @param buffer Buffers holding the data to be sent.
|
|
|
+ /// @param ec Error code returned as a result of sending the data.
|
|
|
+ /// @param length Length of the data sent.
|
|
|
+ void sendHandler(const UnixDomainSocket::Handler& remote_handler,
|
|
|
+ const boost::asio::const_buffers_1& buffer,
|
|
|
+ const boost::system::error_code& ec,
|
|
|
+ size_t length);
|
|
|
+
|
|
|
+ /// @brief Asynchronously receive data over the socket.
|
|
|
+ ///
|
|
|
+ /// This method schedules asynchronous receive and installs the
|
|
|
+ /// @ref UnixDomainSocket::receiveHandler is a callback.
|
|
|
+ ///
|
|
|
+ /// @param data Pointer to a buffer into which the data should be read.
|
|
|
+ /// @param length Length of the buffer.
|
|
|
+ /// @param handler User supplied callback invoked when data have been
|
|
|
+ /// received or an error is signalled.
|
|
|
+ void asyncReceive(void* data, const size_t length,
|
|
|
+ const UnixDomainSocket::Handler& handler);
|
|
|
+
|
|
|
+ /// @brief Asynchronously receives the data over the socket.
|
|
|
+ ///
|
|
|
+ /// This method is called @ref asyncReceive and @ref receiveHandler when
|
|
|
+ /// EWOULDBLOCK or EAGAIN is returned.
|
|
|
+ ///
|
|
|
+ /// @param buffer A buffer into which the data should be received.
|
|
|
+ /// @param handler User supplied callback invoked when data have been
|
|
|
+ /// received on an error is signalled.
|
|
|
+ void doReceive(const boost::asio::mutable_buffers_1& buffer,
|
|
|
+ const UnixDomainSocket::Handler& handler);
|
|
|
+
|
|
|
+ /// @brief Local handler invoked as a result of asynchronous receive.
|
|
|
+ ///
|
|
|
+ /// This handler is invoked as a result of asynchronous receive. It is a
|
|
|
+ /// wrapper callback around the user supplied callback. It handles
|
|
|
+ /// EWOULDBLOCK and EAGAIN by retrying to asynchronously receive the
|
|
|
+ /// data. These errors are often returned on some operating systems, even
|
|
|
+ /// though one would expect that asynchronous operation would not
|
|
|
+ /// return such errors. Because these errors are handled by the
|
|
|
+ /// wrapper callback, the user supplied callback never receives
|
|
|
+ /// these errors.
|
|
|
+ ///
|
|
|
+ /// @param remote_handler User supplied callback.
|
|
|
+ /// @param buffer Buffer into which the data are received.
|
|
|
+ /// @param ec Error code returned as a result of asynchronous receive.
|
|
|
+ /// @param length Size of the received data.
|
|
|
+ void receiveHandler(const UnixDomainSocket::Handler& remote_handler,
|
|
|
+ const boost::asio::mutable_buffers_1& buffer,
|
|
|
+ const boost::system::error_code& ec,
|
|
|
+ size_t length);
|
|
|
+
|
|
|
/// @brief Closes the socket.
|
|
|
void close();
|
|
|
|
|
@@ -38,6 +153,98 @@ public:
|
|
|
};
|
|
|
|
|
|
void
|
|
|
+UnixDomainSocketImpl::asyncConnect(const stream_protocol::endpoint& endpoint,
|
|
|
+ const UnixDomainSocket::ConnectHandler& handler) {
|
|
|
+ using namespace std::placeholders;
|
|
|
+
|
|
|
+ UnixDomainSocket::ConnectHandler local_handler =
|
|
|
+ std::bind(&UnixDomainSocketImpl::connectHandler, shared_from_this(),
|
|
|
+ handler, _1);
|
|
|
+ socket_.async_connect(endpoint, local_handler);
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+UnixDomainSocketImpl::connectHandler(const UnixDomainSocket::ConnectHandler& remote_handler,
|
|
|
+ const boost::system::error_code& ec) {
|
|
|
+ // It was observed on Debian and Fedora that asynchronous connect may result
|
|
|
+ // in EINPROGRESS error. This doesn't really indicate a problem with a
|
|
|
+ // connection. If we continue transmitting data over the socket it will
|
|
|
+ // succeed. So we suppress this error and return 'success' to the user's
|
|
|
+ // handler.
|
|
|
+ if (ec.value() == boost::asio::error::in_progress) {
|
|
|
+ remote_handler(boost::system::error_code());
|
|
|
+ } else {
|
|
|
+ remote_handler(ec);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+UnixDomainSocketImpl::asyncSend(const void* data, const size_t length,
|
|
|
+ const UnixDomainSocket::Handler& handler) {
|
|
|
+ doSend(boost::asio::buffer(data, length), handler);
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+UnixDomainSocketImpl::doSend(const boost::asio::const_buffers_1& buffer,
|
|
|
+ const UnixDomainSocket::Handler& handler) {
|
|
|
+ using namespace std::placeholders;
|
|
|
+
|
|
|
+ UnixDomainSocket::Handler local_handler =
|
|
|
+ std::bind(&UnixDomainSocketImpl::sendHandler, shared_from_this(),
|
|
|
+ handler, buffer, _1, _2);
|
|
|
+ socket_.async_send(buffer, local_handler);
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+UnixDomainSocketImpl::sendHandler(const UnixDomainSocket::Handler& remote_handler,
|
|
|
+ const boost::asio::const_buffers_1& buffer,
|
|
|
+ const boost::system::error_code& ec,
|
|
|
+ size_t length) {
|
|
|
+ // The asynchronous send may return EWOULDBLOCK or EAGAIN on some
|
|
|
+ // operating systems. In this case, we simply retry hoping that it
|
|
|
+ // will succeed next time. The user's callback never sees these
|
|
|
+ // errors.
|
|
|
+ if ((ec.value() == boost::asio::error::would_block) ||
|
|
|
+ (ec.value() == boost::asio::error::try_again)) {
|
|
|
+ doSend(buffer, remote_handler);
|
|
|
+ }
|
|
|
+ remote_handler(ec, length);
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+UnixDomainSocketImpl::asyncReceive(void* data, const size_t length,
|
|
|
+ const UnixDomainSocket::Handler& handler) {
|
|
|
+ doReceive(boost::asio::buffer(data, length), handler);
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+UnixDomainSocketImpl::doReceive(const boost::asio::mutable_buffers_1& buffer,
|
|
|
+ const UnixDomainSocket::Handler& handler) {
|
|
|
+ using namespace std::placeholders;
|
|
|
+
|
|
|
+ UnixDomainSocket::Handler local_handler =
|
|
|
+ std::bind(&UnixDomainSocketImpl::receiveHandler, shared_from_this(),
|
|
|
+ handler, buffer, _1, _2);
|
|
|
+ socket_.async_receive(buffer, 0, local_handler);
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+UnixDomainSocketImpl::receiveHandler(const UnixDomainSocket::Handler& remote_handler,
|
|
|
+ const boost::asio::mutable_buffers_1& buffer,
|
|
|
+ const boost::system::error_code& ec,
|
|
|
+ size_t length) {
|
|
|
+ // The asynchronous receive may return EWOULDBLOCK or EAGAIN on some
|
|
|
+ // operating systems. In this case, we simply retry hoping that it
|
|
|
+ // will succeed next time. The user's callback never sees these
|
|
|
+ // errors.
|
|
|
+ if ((ec.value() == boost::asio::error::would_block) ||
|
|
|
+ (ec.value() == boost::asio::error::try_again)) {
|
|
|
+ doReceive(buffer, remote_handler);
|
|
|
+ }
|
|
|
+ remote_handler(ec, length);
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
UnixDomainSocketImpl::close() {
|
|
|
static_cast<void>(socket_.close());
|
|
|
}
|
|
@@ -65,6 +272,11 @@ UnixDomainSocket::connect(const std::string& path) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+void
|
|
|
+UnixDomainSocket::asyncConnect(const std::string& path, const ConnectHandler& handler) {
|
|
|
+ impl_->asyncConnect(stream_protocol::endpoint(path.c_str()), handler);
|
|
|
+}
|
|
|
+
|
|
|
size_t
|
|
|
UnixDomainSocket::write(const void* data, size_t length) {
|
|
|
boost::system::error_code ec;
|
|
@@ -78,6 +290,12 @@ UnixDomainSocket::write(const void* data, size_t length) {
|
|
|
return (res);
|
|
|
}
|
|
|
|
|
|
+void
|
|
|
+UnixDomainSocket::asyncSend(const void* data, const size_t length,
|
|
|
+ const Handler& handler) {
|
|
|
+ impl_->asyncSend(data, length, handler);
|
|
|
+}
|
|
|
+
|
|
|
size_t
|
|
|
UnixDomainSocket::receive(void* data, size_t length) {
|
|
|
boost::system::error_code ec;
|
|
@@ -89,9 +307,15 @@ UnixDomainSocket::receive(void* data, size_t length) {
|
|
|
}
|
|
|
|
|
|
void
|
|
|
+UnixDomainSocket::asyncReceive(void* data, const size_t length,
|
|
|
+ const Handler& handler) {
|
|
|
+ impl_->asyncReceive(data, length, handler);
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
UnixDomainSocket::close() {
|
|
|
impl_->close();
|
|
|
}
|
|
|
|
|
|
-}
|
|
|
-}
|
|
|
+} // end of namespace asiolink
|
|
|
+} // end of namespace isc
|