diff options
author | Evgeny Zinoviev <me@ch1p.io> | 2021-05-07 02:18:07 +0300 |
---|---|---|
committer | Evgeny Zinoviev <me@ch1p.io> | 2021-05-07 02:18:07 +0300 |
commit | 7e743b73433475df086fcec81be7b10c1d695a42 (patch) | |
tree | 1737c5f9bdad2a40f740e9a655e510641331b9e2 /src/server/server.cc |
initial
Diffstat (limited to 'src/server/server.cc')
-rw-r--r-- | src/server/server.cc | 143 |
1 files changed, 143 insertions, 0 deletions
diff --git a/src/server/server.cc b/src/server/server.cc new file mode 100644 index 0000000..981104d --- /dev/null +++ b/src/server/server.cc @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: BSD-3-Clause + +#include <cstring> +#include <string> +#include <cerrno> +#include <algorithm> +#include <memory> +#include <utility> +#include <arpa/inet.h> +#include <sys/socket.h> +#include <unistd.h> + +#include "../voltronic/exceptions.h" +#include "../p18/exceptions.h" +#include "../voltronic/time.h" +#include "../logging.h" +//#include "hexdump/hexdump.h" +#include "server.h" +#include "connection.h" +#include "signal.h" + +namespace server { + +Server::Server(std::shared_ptr<voltronic::Device> device) + : sock_(0) + , port_(0) + , cacheTimeout_(CACHE_TIMEOUT) + , verbose_(false) + , device_(std::move(device)) { + client_.setDevice(device_); +} + +void Server::setVerbose(bool verbose) { + verbose_ = verbose; + device_->setVerbose(verbose); +} + +void Server::setCacheTimeout(u64 timeout) { + cacheTimeout_ = timeout; +} + +Server::~Server() { + if (sock_ > 0) + close(sock_); +} + +void Server::start(std::string& host, int port) { + host_ = host; + port_ = port; + + sock_ = socket(AF_INET, SOCK_STREAM, 0); + if (sock_ == -1) + throw ServerError("failed to create socket"); + + struct linger sl = {0}; + sl.l_onoff = 1; + sl.l_linger = 0; + if (setsockopt(sock_, SOL_SOCKET, SO_LINGER, &sl, sizeof(sl)) == -1) + throw ServerError("setsockopt(linger): " + std::string(strerror(errno))); + + int flag = 1; + if (setsockopt(sock_, SOL_SOCKET, SO_REUSEADDR, &flag, sizeof(flag)) == -1) + throw ServerError("setsockopt(reuseaddr): " + std::string(strerror(errno))); + + struct sockaddr_in serv_addr = {0}; + serv_addr.sin_family = AF_INET; + serv_addr.sin_addr.s_addr = inet_addr(host_.c_str()); + serv_addr.sin_port = htons(port_); + memset(serv_addr.sin_zero, 0, sizeof(serv_addr.sin_zero)); + + if (bind(sock_, (struct sockaddr*)&serv_addr, sizeof(serv_addr))) + throw ServerError("bind: " + std::string(strerror(errno))); + + if (listen(sock_, 50)) + throw ServerError("start: " + std::string(strerror(errno))); + + while (!shutdownCaught) { + struct sockaddr_in addr = {0}; + socklen_t addr_size = sizeof(addr); + + if (verbose_) + mylog << "waiting for client.."; + + int sock = accept(sock_, (struct sockaddr*)&addr, &addr_size); + if (sock == -1) + continue; + + auto conn = new Connection(sock, addr, this); + addConnection(conn); + } +} + +void Server::addConnection(Connection *conn) { + if (verbose_) + myerr << "adding " << conn->ipv4(); + LockGuard lock(threads_mutex_); + connections_.emplace_back(conn); +} + +void Server::removeConnection(Connection *conn) { + if (verbose_) + myerr << "removing " << conn->ipv4(); + LockGuard lock(threads_mutex_); + connections_.erase(std::remove(connections_.begin(), connections_.end(), conn), connections_.end()); +} + +size_t Server::getConnectionsCount() const { + return connections_.size(); +} + +std::shared_ptr<p18::response_type::BaseResponse> Server::executeCommand(p18::CommandType commandType, std::vector<std::string>& arguments) { + LockGuard lock(client_mutex_); + + auto it = cache_.find(commandType); + if (it != cache_.end()) { + auto cr = it->second; + if (voltronic::timestamp() - cr.time <= cacheTimeout_) { + return cr.response; + } + } + + try { + auto response = client_.execute(commandType, arguments); + CachedResponse cr{voltronic::timestamp(), response}; + cache_[commandType] = cr; + return response; + } + catch (voltronic::DeviceError& e) { + throw std::runtime_error("device error: " + std::string(e.what())); + } + catch (voltronic::TimeoutError& e) { + throw std::runtime_error("timeout: " + std::string(e.what())); + } + catch (voltronic::InvalidDataError& e) { + throw std::runtime_error("data is invalid: " + std::string(e.what())); + } + catch (p18::InvalidResponseError& e) { + throw std::runtime_error("response is invalid: " + std::string(e.what())); + } +} + + +}
\ No newline at end of file |