From: Sangwan Kwon Date: Thu, 28 Nov 2019 06:20:13 +0000 (+0900) Subject: Import RMI code for internalize X-Git-Tag: submit/tizen/20200810.073515~146 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=2b03d9470450b86d601ecb6171e64687fe6a927d;p=platform%2Fcore%2Fsecurity%2Fvist.git Import RMI code for internalize Signed-off-by: Sangwan Kwon --- diff --git a/src/vist/CMakeLists.txt b/src/vist/CMakeLists.txt index 63162e1..25ec601 100644 --- a/src/vist/CMakeLists.txt +++ b/src/vist/CMakeLists.txt @@ -33,9 +33,13 @@ ADD_DEFINITIONS(-DDB_PATH="${DB_INSTALL_DIR}/.vist.db" # common ADD_SUBDIRECTORY(common) +ADD_SUBDIRECTORY(event) ADD_SUBDIRECTORY(ipc) +ADD_SUBDIRECTORY(klass) ADD_SUBDIRECTORY(logger) +ADD_SUBDIRECTORY(rmi) ADD_SUBDIRECTORY(sdk) +ADD_SUBDIRECTORY(transport) # policy ADD_SUBDIRECTORY(policy) diff --git a/src/vist/event/CMakeLists.txt b/src/vist/event/CMakeLists.txt new file mode 100644 index 0000000..25dd007 --- /dev/null +++ b/src/vist/event/CMakeLists.txt @@ -0,0 +1,16 @@ +# 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 + +ADD_VIST_COMMON_LIBRARY(vist_event eventfd.cpp + mainloop.cpp) diff --git a/src/vist/event/eventfd.cpp b/src/vist/event/eventfd.cpp new file mode 100644 index 0000000..3e27692 --- /dev/null +++ b/src/vist/event/eventfd.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2018-present 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 + */ +/* + * @file eventfd.cpp + * @author Jaemin Ryu (jm77.ryu@samsung.com) + * @author Sangwan Kwon (sangwan.kwon@samsung.com) + * @brief Implementation of event notification. + */ + +#include "eventfd.hpp" + +#include +#include + +#include +#include + +namespace vist { +namespace event { + +EventFD::EventFD(unsigned int initval, int flags) + : fd(::eventfd(initval, flags)) +{ + if (this->fd == -1) + throw std::runtime_error("Failed to create eventfd."); +} + +EventFD::~EventFD() +{ + ::close(fd); +} + +void EventFD::send(void) +{ + const std::uint64_t val = 1; + if (::write(this->fd, &val, sizeof(val)) == -1) + throw std::runtime_error("Failed to write to eventfd."); +} + +void EventFD::receive(void) +{ + std::uint64_t val; + if (::read(this->fd, &val, sizeof(val)) == -1) + throw std::runtime_error("Failed to read from eventfd."); +} + +int EventFD::getFd(void) const noexcept +{ + return this->fd; +} + +} // namespace event +} // namespace vist diff --git a/src/vist/event/eventfd.hpp b/src/vist/event/eventfd.hpp new file mode 100644 index 0000000..6b85707 --- /dev/null +++ b/src/vist/event/eventfd.hpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2018-present 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 + */ +/* + * @file eventfd.hpp + * @author Jaemin Ryu (jm77.ryu@samsung.com) + * @author Sangwan Kwon (sangwan.kwon@samsung.com) + * @brief A file descriptor for event notification. + */ + +#pragma once + +#include + +namespace vist { +namespace event { + +class EventFD final { +public: + explicit EventFD(unsigned int initval = 0, int flags = EFD_SEMAPHORE | EFD_CLOEXEC); + ~EventFD(); + + EventFD(const EventFD&) = delete; + EventFD& operator=(const EventFD&) = delete; + + EventFD(EventFD&&) = delete; + EventFD& operator=(EventFD&&) = delete; + + void send(void); + void receive(void); + + int getFd(void) const noexcept; + +private: + int fd; +}; + +} // namespace event +} // namespace vist diff --git a/src/vist/event/mainloop.cpp b/src/vist/event/mainloop.cpp new file mode 100644 index 0000000..991ceb8 --- /dev/null +++ b/src/vist/event/mainloop.cpp @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2018-present 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 + */ +/* + * @file mainloop.cpp + * @author Sangwan Kwon (sangwan.kwon@samsung.com) + * @brief Implementation of mainloop. + */ + +#include "mainloop.hpp" + +#include + +#include +#include + +#include + +namespace vist { +namespace event { + +Mainloop::Mainloop() : + epollFd(::epoll_create1(EPOLL_CLOEXEC)), + stopped(false) +{ + if (epollFd == -1) + throw std::runtime_error("Failed to create epoll instance."); +} + +Mainloop::~Mainloop() +{ + ::close(this->epollFd); +} + +void Mainloop::addHandler(const int fd, OnEvent&& onEvent, OnError&& onError) +{ + std::lock_guard lock(mutex); + + if (this->listener.find(fd) != this->listener.end()) + throw std::runtime_error("Event is already registered."); + + ::epoll_event event; + std::memset(&event, 0, sizeof(epoll_event)); + + event.events = EPOLLIN | EPOLLHUP | EPOLLRDHUP; + event.data.fd = fd; + + if (::epoll_ctl(this->epollFd, EPOLL_CTL_ADD, fd, &event) == -1) + throw std::runtime_error("Failed to add event source."); + + auto onEventPtr = std::make_shared(onEvent); + auto onErrorPtr = (onError != nullptr) ? std::make_shared(onError) : nullptr; + + auto handler = std::make_pair(std::move(onEventPtr), std::move(onErrorPtr)); + + this->listener.insert({fd, handler}); +} + +void Mainloop::removeHandler(const int fd) +{ + std::lock_guard lock(mutex); + + auto iter = this->listener.find(fd); + if (iter == this->listener.end()) + return; + + this->listener.erase(iter); + + ::epoll_ctl(epollFd, EPOLL_CTL_DEL, fd, NULL); +} + +bool Mainloop::prepare(void) +{ + auto wakeup = [this]() { + this->wakeupSignal.receive(); + this->removeHandler(wakeupSignal.getFd()); + this->stopped = true; + }; + + this->addHandler(this->wakeupSignal.getFd(), wakeup); +} + +bool Mainloop::dispatch(int timeout) noexcept +{ + int nfds; + ::epoll_event event[MAX_EPOLL_EVENTS]; + + do { + errno = 0; + nfds = ::epoll_wait(epollFd, event, MAX_EPOLL_EVENTS, timeout); + } while ((nfds == -1) && (errno == EINTR)); + + if (nfds <= 0) + return false; + + for (int i = 0; i < nfds; i++) { + std::shared_ptr onEvent; + std::shared_ptr onError; + + { + std::lock_guard lock(mutex); + + auto iter = this->listener.find(event[i].data.fd); + if (iter == this->listener.end()) + continue; + + onEvent = iter->second.first; + onError = iter->second.second; + } + + try { + if ((event[i].events & (EPOLLHUP | EPOLLRDHUP))) { + if (onError != nullptr) + (*onError)(); + } else { + (*onEvent)(); + } + + } catch (std::exception& e) { + ERROR(VIST) << "EXCEPTION ON MAINLOOP: " << e.what(); + } + } + + return true; +} + +void Mainloop::run(int timeout) +{ + bool done = false; + this->stopped = false; + + this->prepare(); + + while (!this->stopped && !done) { + done = !dispatch(timeout); + } +} + +void Mainloop::stop(void) +{ + this->wakeupSignal.send(); +} + +} // namespace event +} // namespace vist diff --git a/src/vist/event/mainloop.hpp b/src/vist/event/mainloop.hpp new file mode 100644 index 0000000..6e2f1ca --- /dev/null +++ b/src/vist/event/mainloop.hpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2018-present 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 + */ +/* + * @file mainloop.hpp + * @author Sangwan Kwon (sangwan.kwon@samsung.com) + * @brief The loop for events. + */ + +#pragma once + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace vist { +namespace event { + +class Mainloop { +public: + using OnEvent = std::function; + using OnError = std::function; + + Mainloop(); + virtual ~Mainloop(); + + Mainloop(const Mainloop&) = delete; + Mainloop& operator=(const Mainloop&) = delete; + + Mainloop(Mainloop&&) = delete; + Mainloop& operator=(Mainloop&&) = delete; + + void addHandler(const int fd, OnEvent&& onEvent, OnError&& = nullptr); + void removeHandler(const int fd); + + void run(int timeout = -1); + void stop(void); + +private: + // recursive_mutex makes additional calls to lock in calling thread. + // And other threads will block (for calls to lock). + // So, addHandler() can be called during dispatch(). + using Mutex = std::recursive_mutex; + using Handler = std::pair, std::shared_ptr>; + using Listener = std::unordered_map; + + bool prepare(void); + + bool dispatch(const int timeout) noexcept; + + Mutex mutex; + Listener listener; + EventFD wakeupSignal; + + int epollFd; + std::atomic stopped; + + const int MAX_EPOLL_EVENTS = 16; +}; + +} // namespace event +} // namespace vist diff --git a/src/vist/klass/CMakeLists.txt b/src/vist/klass/CMakeLists.txt new file mode 100644 index 0000000..fea5e7e --- /dev/null +++ b/src/vist/klass/CMakeLists.txt @@ -0,0 +1,16 @@ +# 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 + +FILE(GLOB KLASS_TESTS "tests/*.cpp") +ADD_VIST_TEST(${KLASS_TESTS}) diff --git a/src/vist/klass/function.hpp b/src/vist/klass/function.hpp new file mode 100644 index 0000000..db6b35b --- /dev/null +++ b/src/vist/klass/function.hpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2018-present 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 + */ +/* + * @file function.hpp + * @author Sangwan Kwon (sangwan.kwon@samsung.com) + * @brief Hold the class member fuction. + */ + +#pragma once + +#include + +namespace vist { +namespace klass { + +template +using remove_cv_t = typename std::remove_cv::type; +template +using remove_ref_t = typename std::remove_reference::type; +template +using remove_cv_ref_t = remove_cv_t>; + +template +class Function { +public: + using Klass = K; + using Return = R; + using Parameters = std::tuple...>; + using Pointer = Return (Klass::*)(Ps...); + + auto get(void) noexcept -> const Pointer&; + +private: + explicit Function(Pointer pointer); + + template + friend Function make_function(RR (KK::* member)(PPs...)); + + Pointer pointer; +}; + +template +Function::Function(Pointer pointer) : pointer(pointer) +{ +} + +template +auto Function::get(void) noexcept -> const Pointer& +{ + return this->pointer; +} + +template +Function make_function(R (K::* member)(Ps...)) +{ + constexpr bool notVoid = !(std::is_same::value); + static_assert(notVoid, "Return type cannot be void."); + + using IsValid = std::is_member_function_pointer; + static_assert(IsValid::value, "Pamameter should be member function type."); + + return Function(member); +} + +} // namespace klass +} // namespace vist diff --git a/src/vist/klass/functor.hpp b/src/vist/klass/functor.hpp new file mode 100644 index 0000000..7a178ad --- /dev/null +++ b/src/vist/klass/functor.hpp @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2018-present 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 + */ +/* + * @file functor.hpp + * @author Sangwan Kwon (sangwan.kwon@samsung.com) + * @brief Functor is callable object which binds instance with member function. + */ + +#pragma once + +#include +#include + +#include +#include +#include +#include + +namespace vist { +namespace klass { + +struct AbstractFunctor { + template + R invoke(Args&&... args); + inline Archive invoke(Archive& archive); + +protected: + virtual Archive dispatch(Archive& archive) = 0; +}; + +using FunctorMap = std::unordered_map>; + +template +class Functor : public AbstractFunctor { +public: + using Klass = K; + using MemFunc = Function; + using Invokable = std::function; + + explicit Functor(std::shared_ptr instance, MemFunc memFunc); + + template + auto operator()(Args&&... args) -> typename MemFunc::Return; + inline auto operator()(Archive& archive) -> typename MemFunc::Return; + +protected: + inline Archive dispatch(Archive& archive) override; + +private: + template + auto operator()(T& tuple, EmptySequence) -> typename MemFunc::Return; + template + auto operator()(T& tuple, IndexSequence) -> typename MemFunc::Return; + + std::shared_ptr instance; + MemFunc memFunc; +}; + +template +R AbstractFunctor::invoke(Args&&... args) +{ + Archive parameters; + parameters.pack(std::forward(args)...); + + auto result = this->dispatch(parameters); + R ret; + result >> ret; + return ret; +} + +Archive AbstractFunctor::invoke(Archive& archive) +{ + return this->dispatch(archive); +} + +template +Functor::Functor(std::shared_ptr instance, MemFunc memFunc) + : instance(instance), memFunc(std::move(memFunc)) +{ +} + +template +template +auto Functor::operator()(Args&&... args) -> typename MemFunc::Return +{ + const Invokable& invokable = this->memFunc.get(); + return invokable(*this->instance, std::forward(args)...); +} + +template +auto Functor::operator()(Archive& archive) -> typename MemFunc::Return +{ + using ParamsTuple = typename MemFunc::Parameters; + constexpr auto size = std::tuple_size::value; + + ParamsTuple params; + archive.transform(params); + + return (*this)(params, make_index_sequence()); +} + +template +Archive Functor::dispatch(Archive& archive) +{ + Archive ret; + return (ret << (*this)(archive)); +} + +template +template +auto Functor::operator()(T& tuple, + EmptySequence) -> typename MemFunc::Return +{ + return (*this)(); +} + +template +template +auto Functor::operator()(T& tuple, + IndexSequence) -> typename MemFunc::Return +{ + return (*this)(std::get(tuple)...); +} + +template +Functor make_functor(std::shared_ptr instance, R (K::* member)(Ps...)) +{ + if (instance == nullptr) + throw std::invalid_argument("Instance can't be nullptr."); + + return Functor(instance, make_function(member)); +} + +template +std::shared_ptr> make_functor_ptr(std::shared_ptr instance, + R (K::* member)(Ps...)) +{ + if (instance == nullptr) + throw std::invalid_argument("Instance can't be nullptr."); + + return std::make_shared>(instance, make_function(member)); +} + +} // namespace klass +} // namespace vist diff --git a/src/vist/klass/tests/functor.cpp b/src/vist/klass/tests/functor.cpp new file mode 100644 index 0000000..91b0360 --- /dev/null +++ b/src/vist/klass/tests/functor.cpp @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2018-present 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 + +using namespace vist; +using namespace vist::klass; + +struct Foo { + bool setName(const std::string& name) + { + this->name = name; + return false; + } + + std::string getName(void) + { + return this->name; + } + + int echo(const std::string& a, const std::string b, std::string& c) + { + std::cout << a << ", " << b << ", " << c << std::endl; + return false; + } + +// void impossible(void) {} + + std::string name; +}; + +TEST(FunctorTests, functor) +{ + auto foo = std::make_shared(); + auto fooSetName = make_functor(foo, &Foo::setName); + auto fooGetName = make_functor(foo, &Foo::getName); + auto fooEcho = make_functor(foo, &Foo::echo); +// auto fooImp = make_functor(foo, &Foo::impossible); + + std::string input = "Foo name"; + bool ret = true; + ret = fooSetName(input); + EXPECT_EQ(ret, false); + + std::string output = fooGetName(); + EXPECT_EQ(output, input); + + std::string a("aaa"), b("bbb"), c("ccc"); + ret = true; + ret = fooEcho(a, b, c); + EXPECT_EQ(ret, false); +} + +TEST(FunctorTests, functor_map) +{ + auto foo = std::make_shared(); + + FunctorMap fooMap; + fooMap["setName"] = make_functor_ptr(foo, &Foo::setName); + fooMap["getName"] = make_functor_ptr(foo, &Foo::getName); + fooMap["echo"] = make_functor_ptr(foo, &Foo::echo); + + auto fooSetNamePtr = fooMap.at("setName"); + auto fooGetNamePtr = fooMap.at("getName"); + auto fooEchoPtr = fooMap.at("echo"); + + std::string input = "Foo name"; + bool ret = true; + ret = fooSetNamePtr->invoke(input); + EXPECT_EQ(ret, false); + + std::string output = fooGetNamePtr->invoke(); + EXPECT_EQ(output, input); + + std::string a("aaaa"), b("bbbb"), c("cccc"); + ret = true; + ret = fooEchoPtr->invoke(a, b, c); + EXPECT_EQ(ret, false); +} + +TEST(FunctorTests, archive) +{ + auto foo = std::make_shared(); + + FunctorMap fooMap; + fooMap["echo"] = make_functor_ptr(foo, &Foo::echo); + + std::string a("aaaa"), b("bbbb"), c("cccc"); + Archive archive; + archive << a << b << c; + + auto fooEchoPtr = fooMap.at("echo"); + auto result = fooEchoPtr->invoke(archive); + bool ret = true; + result >> ret; + EXPECT_EQ(ret, false); +} diff --git a/src/vist/rmi/CMakeLists.txt b/src/vist/rmi/CMakeLists.txt new file mode 100644 index 0000000..167ef87 --- /dev/null +++ b/src/vist/rmi/CMakeLists.txt @@ -0,0 +1,19 @@ +# 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 + +ADD_VIST_COMMON_LIBRARY(vist_rmi client.cpp + server.cpp) + +FILE(GLOB RMI_TESTS "tests/*.cpp") +ADD_VIST_TEST(${RMI_TESTS}) diff --git a/src/vist/rmi/client.cpp b/src/vist/rmi/client.cpp new file mode 100644 index 0000000..1d289f4 --- /dev/null +++ b/src/vist/rmi/client.cpp @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2018-present 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 + */ +/* + * @file client.cpp + * @author Jaemin Ryu (jm77.ryu@samsung.com) + * @author Sangwan Kwon (sangwan.kwon@samsung.com) + * @brief Implementation of client application. + */ + +#include "client.hpp" + +namespace vist { +namespace rmi { + +Client::Client(const std::string& remotePath) : connection(remotePath) +{ +} + +} // namespace rmi +} // namespace vist diff --git a/src/vist/rmi/client.hpp b/src/vist/rmi/client.hpp new file mode 100644 index 0000000..fec6ed6 --- /dev/null +++ b/src/vist/rmi/client.hpp @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2018-present 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 + */ +/* + * @file client.hpp + * @author Jaemin Ryu (jm77.ryu@samsung.com) + * @author Sangwan Kwon (sangwan.kwon@samsung.com) + * @brief Client application for invoking remote function(method). + */ + +#pragma once + +#include +#include + +#include +#include + +using namespace vist::transport; + +namespace vist { +namespace rmi { + +class Client { +public: + explicit Client(const std::string& remotePath); + virtual ~Client() = default; + + Client(const Client&) = delete; + Client& operator=(const Client&) = delete; + + Client(Client&&) = delete; + Client& operator=(Client&&) = delete; + + template + R invoke(const std::string& name, Args&&... args); + +private: + Connection connection; + std::mutex mutex; +}; + +template +R Client::invoke(const std::string& name, Args&&... args) +{ + Message msg(Message::Type::MethodCall, name); + msg.enclose(std::forward(args)...); + + std::lock_guard lock(this->mutex); + + Message reply = this->connection.request(msg); + R ret; + reply.disclose(ret); + + return ret; +} + +} // namespace rmi +} // namespace vist diff --git a/src/vist/rmi/server.cpp b/src/vist/rmi/server.cpp new file mode 100644 index 0000000..269fa9c --- /dev/null +++ b/src/vist/rmi/server.cpp @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2018-present 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 + */ +/* + * @file server.cpp + * @author Jaemin Ryu (jm77.ryu@samsung.com) + * @author Sangwan Kwon (sangwan.kwon@samsung.com) + * @brief Implementation of server application. + */ + +#include "server.hpp" + +#include +#include + +namespace vist { +namespace rmi { + +void Server::start(void) +{ + for (const auto& path : this->socketPaths) { + auto socket = std::make_shared(path); + auto accept = [this, socket]() { + this->onAccept(std::make_shared(socket->accept())); + }; + + this->mainloop.addHandler(socket->getFd(), std::move(accept)); + } + + this->mainloop.run(); +} + +void Server::stop(void) +{ + { + std::lock_guard lock(this->connectionMutex); + + for (auto iter : this->connectionMap) + this->mainloop.removeHandler(iter.first); + } + + this->mainloop.stop(); +} + +void Server::listen(const std::string& socketPath) +{ + this->socketPaths.insert(socketPath); +} + +void Server::onAccept(std::shared_ptr&& connection) +{ + if (connection == nullptr) + throw std::invalid_argument("Wrong connection."); + + auto onRead = [this, connection]() { + std::shared_ptr conn; + + std::lock_guard lock(this->connectionMutex); + + auto iter = this->connectionMap.find(connection->getFd()); + if (iter == this->connectionMap.end()) + throw std::runtime_error("Faild to find connection."); + + conn = iter->second; + + this->dispatch(conn); + }; + + auto onError = [this, connection]() { + ERROR(VIST) << "Connection error occured. fd: " << connection->getFd(); + this->onClose(connection); + }; + + int clientFd = connection->getFd(); + this->mainloop.addHandler(clientFd, std::move(onRead), std::move(onError)); + INFO(VIST) << "Connection is accepted. fd: " << clientFd; + + { + std::lock_guard lock(this->connectionMutex); + + this->dispatch(connection); + this->connectionMap[clientFd] = std::move(connection); + } +} + +void Server::onClose(const std::shared_ptr& connection) +{ + if (connection == nullptr) + throw std::invalid_argument("Wrong connection."); + + { + std::lock_guard lock(this->connectionMutex); + + auto iter = this->connectionMap.find(connection->getFd()); + if (iter == this->connectionMap.end()) + throw std::runtime_error("Faild to find connection."); + + this->mainloop.removeHandler(iter->first); + INFO(VIST) << "Connection is closed. fd: " << iter->first; + this->connectionMap.erase(iter); + } +} + +void Server::dispatch(const std::shared_ptr& connection) +{ + Message request = connection->recv(); + std::string funcName = request.signature; + + { + std::lock_guard lock(this->functorMutex); + + auto iter = this->functorMap.find(funcName); + if (iter == this->functorMap.end()) + throw std::runtime_error("Faild to find function."); + + DEBUG(VIST) << "Remote method invokation: " << funcName; + + auto functor = iter->second; + auto result = functor->invoke(request.buffer); + + Message reply(Message::Type::Reply, funcName); + reply.enclose(result); + + connection->send(reply); + } +} + +} // namespace rmi +} // namespace vist diff --git a/src/vist/rmi/server.hpp b/src/vist/rmi/server.hpp new file mode 100644 index 0000000..d18fa92 --- /dev/null +++ b/src/vist/rmi/server.hpp @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2018-present 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 + */ +/* + * @file server.hpp + * @author Jaemin Ryu (jm77.ryu@samsung.com) + * @author Sangwan Kwon (sangwan.kwon@samsung.com) + * @brief Server application for exposing function(method). + */ + +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +using namespace vist::klass; +using namespace vist::transport; +using namespace vist::event; + +namespace vist { +namespace rmi { + +class Server { +public: + explicit Server() = default; + virtual ~Server() = default; + + Server(const Server&) = delete; + Server& operator=(const Server&) = delete; + + Server(Server&&) = delete; + Server& operator=(Server&&) = delete; + + void start(void); + void stop(void); + + void listen(const std::string& socketPath); + + template + void expose(O&& object, const std::string& name, F&& func); + +private: + using ConnectionMap = std::unordered_map>; + + void onAccept(std::shared_ptr&& connection); + void onClose(const std::shared_ptr& connection); + + void dispatch(const std::shared_ptr& connection); + + Mainloop mainloop; + + std::set socketPaths; + + ConnectionMap connectionMap; + std::mutex connectionMutex; + + FunctorMap functorMap; + std::mutex functorMutex; +}; + +template +void Server::expose(O&& object, const std::string& name, F&& func) +{ + auto functor = make_functor_ptr(std::forward(object), std::forward(func)); + this->functorMap[name] = functor; +} + +} // namespace rmi +} // namespace vist diff --git a/src/vist/rmi/tests/server-client.cpp b/src/vist/rmi/tests/server-client.cpp new file mode 100644 index 0000000..6a18cfa --- /dev/null +++ b/src/vist/rmi/tests/server-client.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2018-present 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 + +#include + +using namespace vist::rmi; +using namespace vist::transport; + +// Server side methods +struct Foo { + bool setName(const std::string& name) + { + this->name = name; + return false; + } + + std::string getName(void) + { + return this->name; + } + + std::string name; +}; + +TEST(RmiTests, server_client) +{ + std::string sockPath = ("/tmp/test-server"); + + // server-side + Server server; + server.listen(sockPath); + + auto foo = std::make_shared(); + server.expose(foo, "Foo::setName", &Foo::setName); + server.expose(foo, "Foo::getName", &Foo::getName); + + auto client = std::thread([&]() { + std::this_thread::sleep_for(std::chrono::seconds(1)); + + // client-side + Client client(sockPath); + + std::string param = "RMI-TEST"; + bool ret = client.invoke("Foo::setName", param); + EXPECT_EQ(ret, false); + + std::string name = client.invoke("Foo::getName"); + EXPECT_EQ(name, param); + + server.stop(); + }); + + server.start(); + + if (client.joinable()) + client.join(); +} diff --git a/src/vist/transport/CMakeLists.txt b/src/vist/transport/CMakeLists.txt new file mode 100644 index 0000000..b3fb7b1 --- /dev/null +++ b/src/vist/transport/CMakeLists.txt @@ -0,0 +1,20 @@ +# 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 + +ADD_VIST_COMMON_LIBRARY(vist_transport connection.cpp + socket.cpp + message.cpp) + +FILE(GLOB TRANSPORT_TESTS "tests/*.cpp") +ADD_VIST_TEST(${TRANSPORT_TESTS}) diff --git a/src/vist/transport/connection.cpp b/src/vist/transport/connection.cpp new file mode 100644 index 0000000..b9fdbe7 --- /dev/null +++ b/src/vist/transport/connection.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2018-present 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 + */ +/* + * @file connection.cpp + * @author Jaemin Ryu (jm77.ryu@samsung.com) + * @author Sangwan Kwon (sangwan.kwon@samsung.com) + * @brief Implementation of the socket communication session. + */ + +#include "connection.hpp" + +#include + +namespace vist { +namespace transport { + +Connection::Connection(transport::Socket&& socket) noexcept : socket(std::move(socket)) +{ +} + +Connection::Connection(const std::string& path) : + socket(transport::Socket::connect(path)) +{ +} + +void Connection::send(Message& message) +{ + std::lock_guard lock(this->sendMutex); + + message.header.id = this->sequence++; + this->socket.send(&message.header); + + this->socket.send(message.buffer.get(), message.header.length); +} + +Message Connection::recv(void) const +{ + std::lock_guard lock(this->recvMutex); + Message::Header header; + this->socket.recv(&header); + + Message message(header); + this->socket.recv(message.buffer.get(), message.size()); + message.disclose(message.signature); + + return message; +} + +Message Connection::request(Message& message) +{ + this->send(message); + return this->recv(); +} + +int Connection::getFd(void) const noexcept +{ + return this->socket.getFd(); +} + +} // namespace transport +} // namespace vist diff --git a/src/vist/transport/connection.hpp b/src/vist/transport/connection.hpp new file mode 100644 index 0000000..4710146 --- /dev/null +++ b/src/vist/transport/connection.hpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2018-present 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 + */ +/* + * @file connection.hpp + * @author Jaemin Ryu (jm77.ryu@samsung.com) + * @author Sangwan Kwon (sangwan.kwon@samsung.com) + * @brief Define the socket communication session. + */ + +#pragma once + +#include +#include + +#include +#include + +namespace vist { +namespace transport { + +class Connection { +public: + explicit Connection(transport::Socket&& socket) noexcept; + explicit Connection(const std::string& path); + virtual ~Connection() = default; + + Connection(const Connection&) = delete; + Connection& operator=(const Connection&) = delete; + + Connection(Connection&&) = default; + Connection& operator=(Connection&&) = default; + + // server-side + void send(Message& message); + Message recv(void) const; + + // client-side + Message request(Message& message); + + int getFd(void) const noexcept; + +private: + transport::Socket socket; + + // SOCK_STREAM are full-duplex byte streams + mutable std::mutex sendMutex; + mutable std::mutex recvMutex; + + unsigned int sequence = 0; +}; + +} // namespace transport +} // namespace vist diff --git a/src/vist/transport/message.cpp b/src/vist/transport/message.cpp new file mode 100644 index 0000000..aca58d6 --- /dev/null +++ b/src/vist/transport/message.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2018-present 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 + */ +/* + * @file message.cpp + * @author Jaemin Ryu (jm77.ryu@samsung.com) + * @author Sangwan Kwon (sangwan.kwon@samsung.com) + * @brief Implementaion of message. + */ + +#include "message.hpp" + +namespace vist { +namespace transport { + +Message::Message(unsigned int type, const std::string& signature) : + header({0, type, signature.size()}), + signature(signature) +{ + this->enclose(signature); +} + +Message::Message(Header header) : header(header) +{ + this->buffer.reserve(this->header.length); +} + +std::size_t Message::size(void) const noexcept +{ + return this->header.length; +} + +} // namespace transport +} // namespace vist diff --git a/src/vist/transport/message.hpp b/src/vist/transport/message.hpp new file mode 100644 index 0000000..f8c832b --- /dev/null +++ b/src/vist/transport/message.hpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2018-present 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 + */ +/* + * @file message.hpp + * @author Jaemin Ryu (jm77.ryu@samsung.com) + * @author Sangwan Kwon (sangwan.kwon@samsung.com) + * @brief The unit of socket communication. + */ + +#pragma once + +#include +#include + +#include + +namespace vist { +namespace transport { + +struct Message final { + enum Type : unsigned int { + Invalid, + MethodCall, + Reply, + Error, + Signal + }; + + struct Header { + unsigned int id; + unsigned int type; + size_t length; + }; + + explicit Message(void) = default; + explicit Message(unsigned int type, const std::string& signature); + explicit Message(Header header); + + ~Message(void) noexcept = default; + + Message(const Message&) = default; + Message(Message&&) = default; + + Message& operator=(const Message&) = default; + Message& operator=(Message&&) = default; + + template + void enclose(Args&&... args); + template + void disclose(Args&... args); + + std::size_t size(void) const noexcept; + + Header header; + std::string signature; + Archive buffer; +}; + +template +void Message::enclose(Args&&... args) +{ + this->buffer.pack(std::forward(args)...); + header.length = this->buffer.size(); +} + +template +void Message::disclose(Args&... args) +{ + this->buffer.unpack(args...); +} + +} // namespace transport +} // namespace vist diff --git a/src/vist/transport/socket.cpp b/src/vist/transport/socket.cpp new file mode 100644 index 0000000..cb7bbc4 --- /dev/null +++ b/src/vist/transport/socket.cpp @@ -0,0 +1,151 @@ +/* + * Copyright (c) 2018-present 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 + */ +/* + * @file socket.cpp + * @author Jaemin Ryu (jm77.ryu@samsung.com) + * @author Sangwan Kwon (sangwan.kwon@samsung.com) + * @brief Implementation of Unix Domain Socket. + */ + +#include "socket.hpp" + +#include +#include +#include +#include + +#include +#include + +namespace vist { +namespace transport { + +namespace { + +void set_cloexec(int fd) +{ + if (::fcntl(fd, F_SETFD, FD_CLOEXEC) == -1) + throw std::runtime_error("Failed to set CLOSEXEC."); +} + +} // anonymous namespace + +Socket::Socket(int fd) noexcept : fd(fd) +{ +} + +Socket::Socket(const std::string& path) +{ + if (path.size() >= sizeof(::sockaddr_un::sun_path)) + throw std::invalid_argument("Socket path size is wrong."); + + int fd = ::socket(AF_UNIX, SOCK_STREAM, 0); + if (fd == -1) + throw std::runtime_error("Failed to create socket."); + + set_cloexec(fd); + + ::sockaddr_un addr; + addr.sun_family = AF_UNIX; + ::strncpy(addr.sun_path, path.c_str(), sizeof(sockaddr_un::sun_path)); + + if (addr.sun_path[0] == '@') + addr.sun_path[0] = '\0'; + + struct stat buf; + if (::stat(path.c_str(), &buf) == 0) + if (::unlink(path.c_str()) == -1) + throw std::runtime_error("Failed to remove exist socket."); + + if (::bind(fd, reinterpret_cast<::sockaddr*>(&addr), sizeof(::sockaddr_un)) == -1) { + ::close(fd); + throw std::runtime_error("Failed to bind."); + } + + if (::listen(fd, MAX_BACKLOG_SIZE) == -1) { + ::close(fd); + throw std::runtime_error("Failed to liten."); + } + + this->fd = fd; +} + +Socket::Socket(Socket&& that) : fd(that.fd) +{ + that.fd = -1; +} + +Socket& Socket::operator=(Socket&& that) +{ + if (this == &that) + return *this; + + this->fd = that.fd; + that.fd = -1; + + return *this; +} + +Socket::~Socket(void) +{ + if (fd != -1) + ::close(fd); +} + +Socket Socket::accept(void) const +{ + int fd = ::accept(this->fd, nullptr, nullptr); + if (fd == -1) + throw std::runtime_error("Failed to accept."); + + set_cloexec(fd); + + return Socket(fd); +} + +Socket Socket::connect(const std::string& path) +{ + if (path.size() >= sizeof(::sockaddr_un::sun_path)) + throw std::invalid_argument("Socket path size is wrong."); + + int fd = ::socket(AF_UNIX, SOCK_STREAM, 0); + if (fd == -1) + throw std::runtime_error("Failed to create socket."); + + set_cloexec(fd); + + ::sockaddr_un addr; + addr.sun_family = AF_UNIX; + ::strncpy(addr.sun_path, path.c_str(), sizeof(::sockaddr_un::sun_path)); + + if (addr.sun_path[0] == '@') + addr.sun_path[0] = '\0'; + + if (::connect(fd, reinterpret_cast<::sockaddr*>(&addr), sizeof(sockaddr_un)) == -1) { + ::close(fd); + throw std::runtime_error("Failed to connect."); + } + + return Socket(fd); +} + +int Socket::getFd(void) const noexcept +{ + return this->fd; +} + +} // namespace transport +} // namespace vist diff --git a/src/vist/transport/socket.hpp b/src/vist/transport/socket.hpp new file mode 100644 index 0000000..69069f4 --- /dev/null +++ b/src/vist/transport/socket.hpp @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2018-present 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 + */ +/* + * @file socket.hpp + * @author Jaemin Ryu (jm77.ryu@samsung.com) + * @author Sangwan Kwon (sangwan.kwon@samsung.com) + * @brief Define Unix Domain Socket. + */ + +#pragma once + +#include +#include + +#include +#include +#include + +namespace vist { +namespace transport { + +class Socket { +public: + explicit Socket(int fd) noexcept; + explicit Socket(const std::string& path); + virtual ~Socket(void); + + Socket(const Socket&) = delete; + Socket& operator=(const Socket&) = delete; + + Socket(Socket&&); + Socket& operator=(Socket&&); + + Socket accept(void) const; + static Socket connect(const std::string& path); + + template + void send(const T* buffer, const std::size_t size = sizeof(T)) const; + + template + void recv(T* buffer, const std::size_t size = sizeof(T)) const; + + int getFd(void) const noexcept; + +private: + const int MAX_BACKLOG_SIZE = 100; + + int fd = -1; +}; + +template +void Socket::send(const T *buffer, const std::size_t size) const +{ + std::size_t written = 0; + while (written < size) { + auto rest = reinterpret_cast(buffer) + written; + auto bytes = ::write(this->fd, rest, size - written); + if (bytes >= 0) + written += bytes; + else if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) + continue; + else + std::runtime_error("Failed to write."); + } +} + +template +void Socket::recv(T *buffer, const std::size_t size) const +{ + std::size_t readen = 0; + while (readen < size) { + auto rest = reinterpret_cast(buffer) + readen; + auto bytes = ::read(this->fd, rest, size - readen); + if (bytes >= 0) + readen += bytes; + else if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) + continue; + else + std::runtime_error("Failed to read."); + } +} + +} // namespace transport +} // namespace vist diff --git a/src/vist/transport/tests/connection.cpp b/src/vist/transport/tests/connection.cpp new file mode 100644 index 0000000..5c34a02 --- /dev/null +++ b/src/vist/transport/tests/connection.cpp @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2018-present 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::transport; +using namespace vist::event; + +TEST(TransportTests, socket_communication) +{ + std::string sockPath = ("/tmp/vist-test.sock"); + + // server-side + Mainloop mainloop; + Socket socket(sockPath); + + std::string requestSignature = "request signature"; + int request1 = 100; + bool request2 = true; + std::string request3 = "request argument"; + + std::string responseSignature = "response signature"; + int response1 = 300; + bool response2 = false; + std::string response3 = "response argument"; + + auto onAccept = [&]() { + Connection conn(socket.accept()); + Message request = conn.recv(); + EXPECT_EQ(requestSignature, request.signature); + + 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); + conn.send(reply); + + mainloop.removeHandler(socket.getFd()); + mainloop.stop(); + }; + + mainloop.addHandler(socket.getFd(), std::move(onAccept)); + auto serverThread = std::thread([&]() { mainloop.run(); }); + + // client-side + Connection conn(sockPath); + Message msg(Message::Type::Signal, requestSignature); + msg.enclose(request1, request2, request3); + + Message reply = conn.request(msg); + 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); + + if (serverThread.joinable()) + serverThread.join(); +} diff --git a/src/vist/transport/tests/socket.cpp b/src/vist/transport/tests/socket.cpp new file mode 100644 index 0000000..db3c456 --- /dev/null +++ b/src/vist/transport/tests/socket.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2018-present 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 + +#include + +using namespace vist::transport; + +TEST(TransportTests, socket_read_write) +{ + std::string sockPath = "/tmp/vist-test.sock"; + Socket socket(sockPath); + + int input = std::numeric_limits::max(); + bool input2 = true; + + int output = 0; + bool output2 = false; + + auto client = std::thread([&]() { + std::this_thread::sleep_for(std::chrono::seconds(1)); + + // Send input to server. + Socket connected = Socket::connect(sockPath); + connected.send(&input); + + // Recv input2 from server. + connected.recv(&output2); + + EXPECT_EQ(input2, output2); + }); + + Socket accepted = socket.accept(); + + // Recv input from client. + accepted.recv(&output); + EXPECT_EQ(input, output); + + // Send input2 to client. + accepted.send(&input2); + + if (client.joinable()) + client.join(); +} + +TEST(TransportTests, socket_abstract) +{ + std::string sockPath = "@vist-sock"; + Socket socket(sockPath); + + int input = std::numeric_limits::max(); + bool input2 = true; + + int output = 0; + bool output2 = false; + + auto client = std::thread([&]() { + std::this_thread::sleep_for(std::chrono::seconds(1)); + + // Send input to server. + Socket connected = Socket::connect(sockPath); + connected.send(&input); + + // Recv input2 from server. + connected.recv(&output2); + + EXPECT_EQ(input2, output2); + }); + + Socket accepted = socket.accept(); + + // Recv input from client. + accepted.recv(&output); + EXPECT_EQ(input, output); + + // Send input2 to client. + accepted.send(&input2); + + if (client.joinable()) + client.join(); +}