From: Sangwan Kwon Date: Thu, 2 Jan 2020 05:57:05 +0000 (+0900) Subject: Detach server-client interface on RMI X-Git-Tag: submit/tizen/20200810.073515~114 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=413d6d70f4becabcf2c43d352db71eee49b1f2f5;p=platform%2Fcore%2Fsecurity%2Fvist.git Detach server-client interface on RMI This is for supporting both general service and on-demand service. Signed-off-by: Sangwan Kwon --- diff --git a/src/vist/rmi/CMakeLists.txt b/src/vist/rmi/CMakeLists.txt index f0c81e0..833d53c 100644 --- a/src/vist/rmi/CMakeLists.txt +++ b/src/vist/rmi/CMakeLists.txt @@ -16,7 +16,7 @@ SET(TARGET vist-rmi) SET(${TARGET}_SRCS gateway.cpp remote.cpp message.cpp - impl/protocol.cpp) + impl/general/protocol.cpp) ADD_LIBRARY(${TARGET} SHARED ${${TARGET}_SRCS}) SET_TARGET_PROPERTIES(${TARGET} PROPERTIES COMPILE_FLAGS "-fvisibility=hidden") @@ -41,5 +41,5 @@ TARGET_LINK_LIBRARIES(${TARGET} ${TARGET_VIST_COMMON_LIB} FILE(GLOB RMI_TESTS "tests/*.cpp") ADD_VIST_TEST(${RMI_TESTS}) -FILE(GLOB RMI_IMPL_TESTS "impl/tests/*.cpp") -ADD_VIST_TEST(${RMI_IMPL_TESTS}) +FILE(GLOB RMI_GENERAL_TESTS "impl/general/tests/*.cpp") +ADD_VIST_TEST(${RMI_GENERAL_TESTS}) diff --git a/src/vist/rmi/gateway.cpp b/src/vist/rmi/gateway.cpp index e9fbdb8..1d8bc44 100644 --- a/src/vist/rmi/gateway.cpp +++ b/src/vist/rmi/gateway.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include @@ -48,7 +49,7 @@ public: return reply; }; - this->server = std::make_unique(path, dispatcher); + this->server = std::make_unique(path, dispatcher); } inline void start() @@ -62,7 +63,7 @@ public: } private: - std::unique_ptr server; + std::unique_ptr server; }; Gateway::Gateway(const std::string& path) : pImpl(std::make_unique(*this, path)) diff --git a/src/vist/rmi/impl/general/client.hpp b/src/vist/rmi/impl/general/client.hpp new file mode 100644 index 0000000..920ca86 --- /dev/null +++ b/src/vist/rmi/impl/general/client.hpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +#pragma once + +#include "protocol.hpp" + +#include +#include + +namespace vist { +namespace rmi { +namespace impl { +namespace general { + +using boost::asio::local::stream_protocol; + +class Client { +public: + Client(const std::string& path) : socket(this->context) + { + try { + this->socket.connect(Protocol::Endpoint(path)); + } catch(const std::exception& e) { + ERROR(VIST) << "Failed to connect socket: " << e.what(); + std::rethrow_exception(std::current_exception()); + } + } + + inline Message request(Message& message) + { + return Protocol::Request(this->socket, message); + } + +private: + Protocol::Context context; + Protocol::Socket socket; +}; + +} // namespace general +} // namespace impl +} // namespace rmi +} // namespace vist diff --git a/src/vist/rmi/impl/general/protocol.cpp b/src/vist/rmi/impl/general/protocol.cpp new file mode 100644 index 0000000..c80394e --- /dev/null +++ b/src/vist/rmi/impl/general/protocol.cpp @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +#include "protocol.hpp" + +#include +#include + +namespace vist { +namespace rmi { +namespace impl { +namespace general { + +Message Protocol::Recv(Socket& socket) +{ + DEBUG(VIST) << "Socket read event occured."; + Message::Header header; + const auto& headerBuffer = boost::asio::buffer(&header, sizeof(Message::Header)); + auto readen = boost::asio::read(socket, headerBuffer); + if (readen != sizeof(Message::Header)) + THROW(ErrCode::ProtocolBroken) << "Failed to receive message header."; + + Message content(header); + const auto& contentBuffer = boost::asio::buffer(content.getBuffer()); + readen = boost::asio::read(socket, contentBuffer); + if (readen != content.size()) + THROW(ErrCode::ProtocolBroken) << "Failed to receive message content."; + + content.disclose(content.signature); + + return content; +} + +void Protocol::Send(Socket& socket, Message& message) +{ + DEBUG(VIST) << "Socket write event occured."; + const auto& headerBuffer = boost::asio::buffer(&message.header, + sizeof(Message::Header)); + auto written = boost::asio::write(socket, headerBuffer); + if (written != sizeof(Message::Header)) + THROW(ErrCode::ProtocolBroken) << "Failed to send message header."; + + const auto& contentBuffer = boost::asio::buffer(message.getBuffer(), message.size()); + written = boost::asio::write(socket, contentBuffer); + if (written != message.size()) + THROW(ErrCode::ProtocolBroken) << "Failed to send message content."; +} + +Message Protocol::Request(Socket& socket, Message& message) +{ + Protocol::Send(socket, message); + return Protocol::Recv(socket); +} + +void Protocol::Async::dispatch(const Task& task) +{ + auto self = shared_from_this(); + const auto& header = boost::asio::buffer(&this->message.header, + sizeof(Message::Header)); + auto handler = [self, task](const auto& error, std::size_t size) { + if (error) { + if (error == boost::asio::error::eof) { + DEBUG(VIST) << "Socket EoF event occured."; + return; + } else { + THROW(ErrCode::RuntimeError) << error.message(); + } + } + + if (size != sizeof(Message::Header)) + THROW(ErrCode::ProtocolBroken) << error.message(); + + /// Resize message buffer to revieved header length. + self->message.resize(self->message.size()); + + const auto& contentBuffer = boost::asio::buffer(self->message.getBuffer()); + auto readen = boost::asio::read(self->socket, contentBuffer); + if (readen != self->message.size()) + THROW(ErrCode::ProtocolBroken) << "Failed to receive message content." + << readen << ", " << self->message.size(); + + self->message.disclose(self->message.signature); + self->process(task); + }; + + boost::asio::async_read(self->socket, header, handler); +} + +void Protocol::Async::process(const Task& task) +{ + bool raised = false; + std::string errMsg; + auto onError = [&raised, &errMsg](const std::string& message) { + ERROR(VIST) << "Failed to process task: " << message; + raised = true; + errMsg = message; + }; + + try { + /// Process dispatched task. + auto result = task(message); + this->message = result; + } catch (const vist::Exception& e) { + onError(e.what()); + } catch (const std::exception& e) { + onError(e.what()); + } + + if (raised) + this->message = Message(Message::Type::Error, errMsg); + + auto self = shared_from_this(); + const auto& headerBuffer = boost::asio::buffer(&this->message.header, + sizeof(Message::Header)); + auto handler = [self, task](const auto& error, std::size_t size) { + if (error || size != sizeof(Message::Header)) + THROW(ErrCode::ProtocolBroken) << "Failed to send message header: " + << error.message(); + + const auto& contentBuffer = boost::asio::buffer(self->message.getBuffer()); + auto written = boost::asio::write(self->socket, contentBuffer); + if (written != self->message.size()) + THROW(ErrCode::ProtocolBroken) << "Failed to send message content."; + + /// Re-dispatch for next request. + self->dispatch(task); + }; + + boost::asio::async_write(self->socket, headerBuffer, handler); +} + +} // namespace general +} // namespace impl +} // namespace rmi +} // namespace vist diff --git a/src/vist/rmi/impl/general/protocol.hpp b/src/vist/rmi/impl/general/protocol.hpp new file mode 100644 index 0000000..613af2c --- /dev/null +++ b/src/vist/rmi/impl/general/protocol.hpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +#pragma once + +#include +#include + +#include + +namespace vist { +namespace rmi { +namespace impl { +namespace general { + +struct Protocol { + using Acceptor = boost::asio::local::stream_protocol::acceptor; + using Context = boost::asio::io_service; + using Endpoint = boost::asio::local::stream_protocol::endpoint; + using Socket = boost::asio::local::stream_protocol::socket; + + static Message Recv(Socket& socket); + static void Send(Socket& socket, Message& message); + + static Message Request(Socket& socket, Message& message); + + /// Passing shared_from_this() to lambda() guarantees + /// that the lambda() always refer to a live object. + class Async : public std::enable_shared_from_this { + public: + explicit Async(Context& context) : socket(context) {} + void dispatch(const Task& task); + void process(const Task& task); + + inline Socket& getSocket() + { + return this->socket; + } + + private: + Message message; + Socket socket; + }; +}; + +} // namespace general +} // namespace impl +} // namespace rmi +} // namespace vist diff --git a/src/vist/rmi/impl/general/server.hpp b/src/vist/rmi/impl/general/server.hpp new file mode 100644 index 0000000..690f9dd --- /dev/null +++ b/src/vist/rmi/impl/general/server.hpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +#pragma once + +#include "protocol.hpp" + +#include +#include + +#include +#include +#include + +#include +#include + +namespace vist { +namespace rmi { +namespace impl { +namespace general { + +class Server : public impl::Server { +public: + Server(const std::string& path, const Task& task) : impl::Server(path, task) + { + errno = 0; + if (::unlink(path.c_str()) == -1 && errno != ENOENT) + THROW(ErrCode::RuntimeError) << "Failed to remove file."; + + this->acceptor = std::make_unique(this->context, + Protocol::Endpoint(path)); + this->accept(task); + } + virtual ~Server() = default; + + Server(const Server&) = delete; + Server& operator=(const Server&) = delete; + + Server(Server&&) = default; + Server& operator=(Server&&) = default; + + void run() override + { + this->context.run(); + } + + void stop() override + { + this->context.stop(); + } + +private: + void accept(const Task& task) override + { + auto asyncSession = std::make_shared(this->context); + auto handler = [this, asyncSession, task](const auto& error) { + DEBUG(VIST) << "New session is accepted."; + + if (error) + THROW(ErrCode::RuntimeError) << error.message(); + + asyncSession->dispatch(task); + + this->accept(task); + }; + this->acceptor->async_accept(asyncSession->getSocket(), handler); + } + + Protocol::Context context; + std::unique_ptr acceptor; +}; + +} // namespace general +} // namespace impl +} // namespace rmi +} // namespace vist diff --git a/src/vist/rmi/impl/general/tests/protocol.cpp b/src/vist/rmi/impl/general/tests/protocol.cpp new file mode 100644 index 0000000..a67d7d5 --- /dev/null +++ b/src/vist/rmi/impl/general/tests/protocol.cpp @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +#include +#include + +#include +#include + +#include +#include + +#include + +using namespace vist::rmi; +using namespace vist::rmi::impl::general; +using boost::asio::local::stream_protocol; + +namespace { + +/// Request variables +std::string requestSignature = "request signature"; +int request1 = 100; +bool request2 = true; +std::string request3 = "request argument"; + +/// Response variables +std::string responseSignature = "response signature"; +int response1 = 300; +bool response2 = false; +std::string response3 = "response argument"; + +} // anonymous namespace + +TEST(ProtocolTests, sync_server_sync_client) +{ + std::string sockPath = "vist-test.sock"; + ::unlink(sockPath.c_str()); + + /// Server configuration + boost::asio::io_service context; + stream_protocol::acceptor acceptor(context, stream_protocol::endpoint(sockPath)); + stream_protocol::socket sock(context); + + auto handler = [&](const auto& error) { + EXPECT_EQ(error, boost::system::errc::success); + + Message request = Protocol::Recv(sock); + EXPECT_EQ(request.signature, requestSignature); + + int recv1; + bool recv2; + std::string recv3; + request.disclose(recv1, recv2, recv3); + EXPECT_EQ(request1, recv1); + EXPECT_EQ(request2, recv2); + EXPECT_EQ(request3, recv3); + + Message reply(Message::Type::Reply, responseSignature); + reply.enclose(response1, response2, response3); + Protocol::Send(sock, reply); + }; + acceptor.async_accept(sock, handler); + + auto serverThread = std::thread([&]() { + context.run(); + }); + + { /// Client configuration + boost::asio::io_service context; + stream_protocol::socket sock(context); + sock.connect(stream_protocol::endpoint(sockPath)); + + Message request(Message::Type::MethodCall, requestSignature); + request.enclose(request1, request2, request3); + + auto reply = Protocol::Request(sock, request); + EXPECT_EQ(reply.signature, responseSignature); + + int recv1; + bool recv2; + std::string recv3; + reply.disclose(recv1, recv2, recv3); + EXPECT_EQ(response1, recv1); + EXPECT_EQ(response2, recv2); + EXPECT_EQ(response3, recv3); + } + + context.stop(); + + if (serverThread.joinable()) + serverThread.join(); +} + +TEST(ProtocolTests, async_server_sync_client) +{ + std::string sockPath = "vist-test.sock"; + ::unlink(sockPath.c_str()); + + /// Server configuration + boost::asio::io_service context; + stream_protocol::acceptor acceptor(context, stream_protocol::endpoint(sockPath)); + + auto async = std::make_shared(context); + auto handler = [&](const auto& error) { + EXPECT_EQ(error, boost::system::errc::success); + auto task = [&](auto& message) -> Message { + EXPECT_EQ(message.signature, requestSignature); + + int recv1; + bool recv2; + std::string recv3; + message.disclose(recv1, recv2, recv3); + EXPECT_EQ(request1, recv1); + EXPECT_EQ(request2, recv2); + EXPECT_EQ(request3, recv3); + + Message reply(Message::Type::Reply, responseSignature); + reply.enclose(response1, response2, response3); + return reply; + }; + + async->dispatch(task); + }; + acceptor.async_accept(async->getSocket(), handler); + + auto serverThread = std::thread([&]() { + context.run(); + }); + + { /// Client configuration + boost::asio::io_service context; + stream_protocol::socket sock(context); + sock.connect(stream_protocol::endpoint(sockPath)); + + Message request(Message::Type::MethodCall, requestSignature); + request.enclose(request1, request2, request3); + + auto requestClosure = [&]() { + auto reply = Protocol::Request(sock, request); + EXPECT_EQ(reply.signature, responseSignature); + + int recv1; + bool recv2; + std::string recv3; + reply.disclose(recv1, recv2, recv3); + EXPECT_EQ(response1, recv1); + EXPECT_EQ(response2, recv2); + EXPECT_EQ(response3, recv3); + }; + + for (int i = 0; i < 3; i++) + requestClosure(); + } + + context.stop(); + + if (serverThread.joinable()) + serverThread.join(); +} diff --git a/src/vist/rmi/impl/general/tests/server-client.cpp b/src/vist/rmi/impl/general/tests/server-client.cpp new file mode 100644 index 0000000..b8e0407 --- /dev/null +++ b/src/vist/rmi/impl/general/tests/server-client.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +#include +#include +#include + +#include +#include + +#include + +#include + +using namespace vist::rmi; +using namespace vist::rmi::impl::general; +using boost::asio::local::stream_protocol; + +namespace { + +/// Request variables +std::string requestSignature = "request signature"; +int request1 = 100; +bool request2 = true; +std::string request3 = "request argument"; + +/// Response variables +std::string responseSignature = "response signature"; +int response1 = 300; +bool response2 = false; +std::string response3 = "response argument"; + +} // anonymous namespace + +TEST(ServerClientTests, server) +{ + std::string sockPath = "vist-test.sock"; + + auto task = [&](Message& message) -> Message { + EXPECT_EQ(message.signature, requestSignature); + + int recv1; + bool recv2; + std::string recv3; + message.disclose(recv1, recv2, recv3); + EXPECT_EQ(request1, recv1); + EXPECT_EQ(request2, recv2); + EXPECT_EQ(request3, recv3); + + Message reply(Message::Type::Reply, responseSignature); + reply.enclose(response1, response2, response3); + return reply; + }; + + Server server(sockPath, task); + auto serverThread = std::thread([&]() { + server.run(); + }); + + { /// Client configuration + auto clientClosure = [&]() { + Client client(sockPath); + + Message message(Message::Type::MethodCall, requestSignature); + message.enclose(request1, request2, request3); + + for (int i = 0; i < 3; i++) { + auto reply = client.request(message); + EXPECT_EQ(reply.signature, responseSignature); + + int recv1; + bool recv2; + std::string recv3; + reply.disclose(recv1, recv2, recv3); + EXPECT_EQ(response1, recv1); + EXPECT_EQ(response2, recv2); + EXPECT_EQ(response3, recv3); + } + }; + + for (int i = 0; i < 3; i++) + clientClosure(); + } + + server.stop(); + + if (serverThread.joinable()) + serverThread.join(); +} diff --git a/src/vist/rmi/impl/protocol.cpp b/src/vist/rmi/impl/protocol.cpp deleted file mode 100644 index 70e1cb4..0000000 --- a/src/vist/rmi/impl/protocol.cpp +++ /dev/null @@ -1,146 +0,0 @@ -/* - * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -#include "protocol.hpp" - -#include -#include - -namespace vist { -namespace rmi { -namespace impl { - -Message Protocol::Recv(Socket& socket) -{ - DEBUG(VIST) << "Socket read event occured."; - Message::Header header; - const auto& headerBuffer = boost::asio::buffer(&header, sizeof(Message::Header)); - auto readen = boost::asio::read(socket, headerBuffer); - if (readen != sizeof(Message::Header)) - THROW(ErrCode::ProtocolBroken) << "Failed to receive message header."; - - Message content(header); - const auto& contentBuffer = boost::asio::buffer(content.getBuffer()); - readen = boost::asio::read(socket, contentBuffer); - if (readen != content.size()) - THROW(ErrCode::ProtocolBroken) << "Failed to receive message content."; - - content.disclose(content.signature); - - return content; -} - -void Protocol::Send(Socket& socket, Message& message) -{ - DEBUG(VIST) << "Socket write event occured."; - const auto& headerBuffer = boost::asio::buffer(&message.header, - sizeof(Message::Header)); - auto written = boost::asio::write(socket, headerBuffer); - if (written != sizeof(Message::Header)) - THROW(ErrCode::ProtocolBroken) << "Failed to send message header."; - - const auto& contentBuffer = boost::asio::buffer(message.getBuffer(), message.size()); - written = boost::asio::write(socket, contentBuffer); - if (written != message.size()) - THROW(ErrCode::ProtocolBroken) << "Failed to send message content."; -} - -Message Protocol::Request(Socket& socket, Message& message) -{ - Protocol::Send(socket, message); - return Protocol::Recv(socket); -} - -void Protocol::Async::dispatch(const Task& task) -{ - auto self = shared_from_this(); - const auto& header = boost::asio::buffer(&this->message.header, - sizeof(Message::Header)); - auto handler = [self, task](const auto& error, std::size_t size) { - if (error) { - if (error == boost::asio::error::eof) { - DEBUG(VIST) << "Socket EoF event occured."; - return; - } else { - THROW(ErrCode::RuntimeError) << error.message(); - } - } - - if (size != sizeof(Message::Header)) - THROW(ErrCode::ProtocolBroken) << error.message(); - - /// Resize message buffer to revieved header length. - self->message.resize(self->message.size()); - - const auto& contentBuffer = boost::asio::buffer(self->message.getBuffer()); - auto readen = boost::asio::read(self->socket, contentBuffer); - if (readen != self->message.size()) - THROW(ErrCode::ProtocolBroken) << "Failed to receive message content." - << readen << ", " << self->message.size(); - - self->message.disclose(self->message.signature); - self->process(task); - }; - - boost::asio::async_read(self->socket, header, handler); -} - -void Protocol::Async::process(const Task& task) -{ - bool raised = false; - std::string errMsg; - auto onError = [&raised, &errMsg](const std::string& message) { - ERROR(VIST) << "Failed to process task: " << message; - raised = true; - errMsg = message; - }; - - try { - /// Process dispatched task. - auto result = task(message); - this->message = result; - } catch (const vist::Exception& e) { - onError(e.what()); - } catch (const std::exception& e) { - onError(e.what()); - } - - if (raised) - this->message = Message(Message::Type::Error, errMsg); - - auto self = shared_from_this(); - const auto& headerBuffer = boost::asio::buffer(&this->message.header, - sizeof(Message::Header)); - auto handler = [self, task](const auto& error, std::size_t size) { - if (error || size != sizeof(Message::Header)) - THROW(ErrCode::ProtocolBroken) << "Failed to send message header: " - << error.message(); - - const auto& contentBuffer = boost::asio::buffer(self->message.getBuffer()); - auto written = boost::asio::write(self->socket, contentBuffer); - if (written != self->message.size()) - THROW(ErrCode::ProtocolBroken) << "Failed to send message content."; - - /// Re-dispatch for next request. - self->dispatch(task); - }; - - boost::asio::async_write(self->socket, headerBuffer, handler); -} - -} // namespace impl -} // namespace rmi -} // namespace vist diff --git a/src/vist/rmi/impl/protocol.hpp b/src/vist/rmi/impl/protocol.hpp deleted file mode 100644 index eff3505..0000000 --- a/src/vist/rmi/impl/protocol.hpp +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -#pragma once - -#include - -#include - -#include - -namespace vist { -namespace rmi { -namespace impl { - -struct Protocol { - using Acceptor = boost::asio::local::stream_protocol::acceptor; - using Context = boost::asio::io_service; - using Endpoint = boost::asio::local::stream_protocol::endpoint; - using Socket = boost::asio::local::stream_protocol::socket; - using Task = std::function; - - static Message Recv(Socket& socket); - static void Send(Socket& socket, Message& message); - - static Message Request(Socket& socket, Message& message); - - /// Passing shared_from_this() to lambda() guarantees - /// that the lambda() always refer to a live object. - class Async : public std::enable_shared_from_this { - public: - explicit Async(Context& context) : socket(context) {} - void dispatch(const Task& task); - void process(const Task& task); - - inline Socket& getSocket() - { - return this->socket; - } - - private: - Message message; - Socket socket; - }; -}; - -} // namespace impl -} // namespace rmi -} // namespace vist diff --git a/src/vist/rmi/impl/server.hpp b/src/vist/rmi/impl/server.hpp index 098e69d..3e5d590 100644 --- a/src/vist/rmi/impl/server.hpp +++ b/src/vist/rmi/impl/server.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved + * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,64 +16,32 @@ #pragma once -#include "protocol.hpp" +#include -#include -#include - -#include -#include -#include - -#include -#include +#include namespace vist { namespace rmi { namespace impl { +using Task = std::function; + class Server { public: - Server(const std::string& path, const Protocol::Task& task) - { - errno = 0; - if (::unlink(path.c_str()) == -1 && errno != ENOENT) - THROW(ErrCode::RuntimeError) << "Failed to remove file."; - - this->acceptor = std::make_unique(this->context, - Protocol::Endpoint(path)); - this->accept(task); - } - - inline void accept(const Protocol::Task& task) - { - auto asyncSession = std::make_shared(this->context); - auto handler = [this, asyncSession, task](const auto& error) { - DEBUG(VIST) << "New session is accepted."; - - if (error) - THROW(ErrCode::RuntimeError) << error.message(); - - asyncSession->dispatch(task); + Server(const std::string&, const Task&) {} + virtual ~Server() = default; - this->accept(task); - }; - this->acceptor->async_accept(asyncSession->getSocket(), handler); - } + Server(const Server&) = delete; + Server& operator=(const Server&) = delete; - inline void run() - { - this->context.run(); - } + Server(Server&&) = default; + Server& operator=(Server&&) = default; - inline void stop() - { - this->context.stop(); - } + virtual void run() = 0; + virtual void stop() = 0; private: - Protocol::Context context; - std::unique_ptr acceptor; + virtual void accept(const Task& task) = 0; }; } // namespace impl diff --git a/src/vist/rmi/impl/tests/protocol.cpp b/src/vist/rmi/impl/tests/protocol.cpp deleted file mode 100644 index 5e03689..0000000 --- a/src/vist/rmi/impl/tests/protocol.cpp +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -#include -#include - -#include -#include - -#include -#include - -#include - -using namespace vist::rmi; -using namespace vist::rmi::impl; -using boost::asio::local::stream_protocol; - -namespace { - -/// Request variables -std::string requestSignature = "request signature"; -int request1 = 100; -bool request2 = true; -std::string request3 = "request argument"; - -/// Response variables -std::string responseSignature = "response signature"; -int response1 = 300; -bool response2 = false; -std::string response3 = "response argument"; - -} // anonymous namespace - -TEST(ProtocolTests, sync_server_sync_client) -{ - std::string sockPath = "vist-test.sock"; - ::unlink(sockPath.c_str()); - - /// Server configuration - boost::asio::io_service context; - stream_protocol::acceptor acceptor(context, stream_protocol::endpoint(sockPath)); - stream_protocol::socket sock(context); - - auto handler = [&](const auto& error) { - EXPECT_EQ(error, boost::system::errc::success); - - Message request = Protocol::Recv(sock); - EXPECT_EQ(request.signature, requestSignature); - - int recv1; - bool recv2; - std::string recv3; - request.disclose(recv1, recv2, recv3); - EXPECT_EQ(request1, recv1); - EXPECT_EQ(request2, recv2); - EXPECT_EQ(request3, recv3); - - Message reply(Message::Type::Reply, responseSignature); - reply.enclose(response1, response2, response3); - Protocol::Send(sock, reply); - }; - acceptor.async_accept(sock, handler); - - auto serverThread = std::thread([&]() { - context.run(); - }); - - { /// Client configuration - boost::asio::io_service context; - stream_protocol::socket sock(context); - sock.connect(stream_protocol::endpoint(sockPath)); - - Message request(Message::Type::MethodCall, requestSignature); - request.enclose(request1, request2, request3); - - auto reply = Protocol::Request(sock, request); - EXPECT_EQ(reply.signature, responseSignature); - - int recv1; - bool recv2; - std::string recv3; - reply.disclose(recv1, recv2, recv3); - EXPECT_EQ(response1, recv1); - EXPECT_EQ(response2, recv2); - EXPECT_EQ(response3, recv3); - } - - context.stop(); - - if (serverThread.joinable()) - serverThread.join(); -} - -TEST(ProtocolTests, async_server_sync_client) -{ - std::string sockPath = "vist-test.sock"; - ::unlink(sockPath.c_str()); - - /// Server configuration - boost::asio::io_service context; - stream_protocol::acceptor acceptor(context, stream_protocol::endpoint(sockPath)); - - auto async = std::make_shared(context); - auto handler = [&](const auto& error) { - EXPECT_EQ(error, boost::system::errc::success); - auto task = [&](auto& message) -> Message { - EXPECT_EQ(message.signature, requestSignature); - - int recv1; - bool recv2; - std::string recv3; - message.disclose(recv1, recv2, recv3); - EXPECT_EQ(request1, recv1); - EXPECT_EQ(request2, recv2); - EXPECT_EQ(request3, recv3); - - Message reply(Message::Type::Reply, responseSignature); - reply.enclose(response1, response2, response3); - return reply; - }; - - async->dispatch(task); - }; - acceptor.async_accept(async->getSocket(), handler); - - auto serverThread = std::thread([&]() { - context.run(); - }); - - { /// Client configuration - boost::asio::io_service context; - stream_protocol::socket sock(context); - sock.connect(stream_protocol::endpoint(sockPath)); - - Message request(Message::Type::MethodCall, requestSignature); - request.enclose(request1, request2, request3); - - auto requestClosure = [&]() { - auto reply = Protocol::Request(sock, request); - EXPECT_EQ(reply.signature, responseSignature); - - int recv1; - bool recv2; - std::string recv3; - reply.disclose(recv1, recv2, recv3); - EXPECT_EQ(response1, recv1); - EXPECT_EQ(response2, recv2); - EXPECT_EQ(response3, recv3); - }; - - for (int i = 0; i < 3; i++) - requestClosure(); - } - - context.stop(); - - if (serverThread.joinable()) - serverThread.join(); -} diff --git a/src/vist/rmi/impl/tests/server-client.cpp b/src/vist/rmi/impl/tests/server-client.cpp deleted file mode 100644 index 17058cb..0000000 --- a/src/vist/rmi/impl/tests/server-client.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (c) 2019 Samsung Electronics Co., Ltd All Rights Reserved - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License - */ - -#include -#include -#include - -#include -#include - -#include - -#include - -using namespace vist::rmi; -using namespace vist::rmi::impl; -using boost::asio::local::stream_protocol; - -namespace { - -/// Request variables -std::string requestSignature = "request signature"; -int request1 = 100; -bool request2 = true; -std::string request3 = "request argument"; - -/// Response variables -std::string responseSignature = "response signature"; -int response1 = 300; -bool response2 = false; -std::string response3 = "response argument"; - -} // anonymous namespace - -TEST(ServerClientTests, server) -{ - std::string sockPath = "vist-test.sock"; - - auto task = [&](Message& message) -> Message { - EXPECT_EQ(message.signature, requestSignature); - - int recv1; - bool recv2; - std::string recv3; - message.disclose(recv1, recv2, recv3); - EXPECT_EQ(request1, recv1); - EXPECT_EQ(request2, recv2); - EXPECT_EQ(request3, recv3); - - Message reply(Message::Type::Reply, responseSignature); - reply.enclose(response1, response2, response3); - return reply; - }; - - Server server(sockPath, task); - auto serverThread = std::thread([&]() { - server.run(); - }); - - { /// Client configuration - auto clientClosure = [&]() { - Client client(sockPath); - - Message message(Message::Type::MethodCall, requestSignature); - message.enclose(request1, request2, request3); - - for (int i = 0; i < 3; i++) { - auto reply = client.request(message); - EXPECT_EQ(reply.signature, responseSignature); - - int recv1; - bool recv2; - std::string recv3; - reply.disclose(recv1, recv2, recv3); - EXPECT_EQ(response1, recv1); - EXPECT_EQ(response2, recv2); - EXPECT_EQ(response3, recv3); - } - }; - - for (int i = 0; i < 3; i++) - clientClosure(); - } - - server.stop(); - - if (serverThread.joinable()) - serverThread.join(); -} diff --git a/src/vist/rmi/remote.cpp b/src/vist/rmi/remote.cpp index d2ff1e6..575a987 100644 --- a/src/vist/rmi/remote.cpp +++ b/src/vist/rmi/remote.cpp @@ -16,7 +16,7 @@ #include "remote.hpp" -#include +#include #include #include @@ -24,7 +24,7 @@ namespace vist { namespace rmi { -using namespace vist::rmi::impl; +using namespace vist::rmi::impl::general; class Remote::Impl { public: