aboutsummaryrefslogtreecommitdiff
path: root/src/server/server.cc
diff options
context:
space:
mode:
authorEvgeny Zinoviev <me@ch1p.io>2021-05-07 02:18:07 +0300
committerEvgeny Zinoviev <me@ch1p.io>2021-05-07 02:18:07 +0300
commit7e743b73433475df086fcec81be7b10c1d695a42 (patch)
tree1737c5f9bdad2a40f740e9a655e510641331b9e2 /src/server/server.cc
initial
Diffstat (limited to 'src/server/server.cc')
-rw-r--r--src/server/server.cc143
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