2022-06-22 23:37:46 -04:00
|
|
|
// clang-format off
|
2022-05-06 18:19:37 -04:00
|
|
|
#include "ReplServer.h"
|
|
|
|
|
|
|
|
#include "common/cross_sockets/XSocket.h"
|
2023-04-22 14:13:57 -04:00
|
|
|
#include "common/versions/versions.h"
|
2022-05-06 18:19:37 -04:00
|
|
|
|
2024-03-05 22:11:52 -05:00
|
|
|
#include "fmt/core.h"
|
2022-05-06 18:19:37 -04:00
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
#define NOMINMAX
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
|
|
#include <Windows.h>
|
|
|
|
#include <WinSock2.h>
|
|
|
|
#include <WS2tcpip.h>
|
|
|
|
#endif
|
2023-06-09 00:02:07 -04:00
|
|
|
#include "common/log/log.h"
|
2022-06-22 23:37:46 -04:00
|
|
|
// clang-format on
|
2022-05-06 18:19:37 -04:00
|
|
|
|
|
|
|
// TODO - The server also needs to eventually return the result of the evaluation
|
|
|
|
|
|
|
|
ReplServer::~ReplServer() {
|
|
|
|
// Close all our client sockets!
|
|
|
|
for (const int& sock : client_sockets) {
|
|
|
|
close_socket(sock);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void ReplServer::post_init() {
|
|
|
|
// Add the listening socket to our set of sockets
|
2024-06-03 00:14:52 -04:00
|
|
|
lg::debug("[nREPL:{}:{}] awaiting connections", tcp_port, listening_socket);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ReplServer::error_response(int socket, const std::string& error) {
|
|
|
|
std::string msg = fmt::format("[ERROR]: {}", error);
|
|
|
|
auto resp = write_to_socket(socket, msg.c_str(), msg.size());
|
|
|
|
if (resp == -1) {
|
|
|
|
lg::warn("[nREPL:{}] Client Disconnected: {}", tcp_port, address_to_string(addr),
|
|
|
|
ntohs(addr.sin_port), socket);
|
|
|
|
close_socket(socket);
|
|
|
|
client_sockets.erase(socket);
|
|
|
|
}
|
2022-05-06 18:19:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void ReplServer::ping_response(int socket) {
|
|
|
|
std::string ping = fmt::format("Connected to OpenGOAL v{}.{} nREPL!",
|
|
|
|
versions::GOAL_VERSION_MAJOR, versions::GOAL_VERSION_MINOR);
|
2022-06-12 21:07:03 -04:00
|
|
|
auto resp = write_to_socket(socket, ping.c_str(), ping.size());
|
|
|
|
if (resp == -1) {
|
2023-08-08 20:53:16 -04:00
|
|
|
lg::warn("[nREPL:{}] Client Disconnected: {}", tcp_port, address_to_string(addr),
|
2023-06-09 00:02:07 -04:00
|
|
|
ntohs(addr.sin_port), socket);
|
2022-06-12 21:07:03 -04:00
|
|
|
close_socket(socket);
|
|
|
|
client_sockets.erase(socket);
|
|
|
|
}
|
2022-05-06 18:19:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<std::string> ReplServer::get_msg() {
|
|
|
|
// Clear the sockets we are listening on
|
|
|
|
FD_ZERO(&read_sockets);
|
|
|
|
|
|
|
|
// Add the server's main listening socket (where we accept clients from)
|
|
|
|
FD_SET(listening_socket, &read_sockets);
|
|
|
|
int max_sd = listening_socket;
|
|
|
|
for (const int& sock : client_sockets) {
|
|
|
|
if (sock > max_sd) {
|
|
|
|
max_sd = sock;
|
|
|
|
}
|
|
|
|
if (sock > 0) {
|
|
|
|
FD_SET(sock, &read_sockets);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait for activity on _something_, with a timeout so we don't get stuck here on exit.
|
2024-06-03 00:14:52 -04:00
|
|
|
struct timeval timeout = {0, 100000};
|
2022-05-06 18:19:37 -04:00
|
|
|
auto activity = select(max_sd + 1, &read_sockets, NULL, NULL, &timeout);
|
2024-06-03 00:14:52 -04:00
|
|
|
if (activity < 0 && errno != EINTR) {
|
|
|
|
lg::error("[nREPL:{}] select error, returned: {}, errno: {}", tcp_port, activity,
|
|
|
|
strerror(errno));
|
2022-05-06 18:19:37 -04:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If something happened on the master socket - it's a new connection
|
|
|
|
if (FD_ISSET(listening_socket, &read_sockets)) {
|
|
|
|
socklen_t addr_len = sizeof(addr);
|
|
|
|
auto new_socket = accept_socket(listening_socket, (sockaddr*)&addr, &addr_len);
|
|
|
|
if (new_socket < 0) {
|
2024-06-03 00:14:52 -04:00
|
|
|
if (new_socket != -1) {
|
|
|
|
lg::error("[nREPL:{}] accept error, returned: {}, errono: {}", tcp_port, new_socket,
|
|
|
|
strerror(errno));
|
|
|
|
}
|
2022-05-06 18:19:37 -04:00
|
|
|
} else {
|
2023-08-08 20:53:16 -04:00
|
|
|
lg::info("[nREPL:{}]: New socket connection: {}:{}:{}", tcp_port, address_to_string(addr),
|
2023-06-09 00:02:07 -04:00
|
|
|
ntohs(addr.sin_port), new_socket);
|
2022-05-06 18:19:37 -04:00
|
|
|
// Say hello
|
|
|
|
ping_response(new_socket);
|
|
|
|
// Track the new socket
|
2022-05-11 22:53:53 -04:00
|
|
|
if ((int)client_sockets.size() < max_clients) {
|
2022-05-06 18:19:37 -04:00
|
|
|
client_sockets.insert(new_socket);
|
|
|
|
} else {
|
2024-06-03 00:14:52 -04:00
|
|
|
// Respond with NO and close the socket
|
|
|
|
lg::warn("[nREPL:{}]: Maximum clients reached. Rejecting connection.", tcp_port);
|
|
|
|
error_response(new_socket, "Maximum clients reached. Rejecting connection.");
|
|
|
|
close_socket(new_socket);
|
2022-05-06 18:19:37 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-03 00:14:52 -04:00
|
|
|
// Check all clients for activity
|
|
|
|
for (auto it = client_sockets.begin(); it != client_sockets.end();) {
|
|
|
|
int sock = *it;
|
2022-05-06 18:19:37 -04:00
|
|
|
if (FD_ISSET(sock, &read_sockets)) {
|
|
|
|
// Attempt to read a header
|
|
|
|
auto req_bytes = read_from_socket(sock, header_buffer.data(), header_buffer.size());
|
2024-06-03 00:14:52 -04:00
|
|
|
if (req_bytes <= 0) {
|
2023-01-07 11:24:02 -05:00
|
|
|
// TODO - add a queue of messages in the REPL::Wrapper so we can print _BEFORE_ the prompt
|
|
|
|
// is output
|
2024-06-03 00:14:52 -04:00
|
|
|
if (req_bytes == 0) {
|
|
|
|
lg::warn("[nREPL:{}] Client Disconnected: {}", tcp_port, address_to_string(addr));
|
|
|
|
} else {
|
|
|
|
lg::warn("[nREPL:{}] Error reading from socket on {}: {}", tcp_port,
|
|
|
|
address_to_string(addr), strerror(errno));
|
|
|
|
}
|
2022-05-06 18:19:37 -04:00
|
|
|
// Cleanup the socket and remove it from our set
|
|
|
|
close_socket(sock);
|
2024-06-03 00:14:52 -04:00
|
|
|
it = client_sockets.erase(it); // Erase and move to the next element
|
|
|
|
continue;
|
2022-05-06 18:19:37 -04:00
|
|
|
} else {
|
|
|
|
// Otherwise, process the message
|
|
|
|
auto* header = (ReplServerHeader*)(header_buffer.data());
|
|
|
|
// get the body of the message
|
|
|
|
int expected_size = header->length;
|
|
|
|
int got = 0;
|
2023-06-09 00:02:07 -04:00
|
|
|
int tries = 0;
|
2024-06-03 00:14:52 -04:00
|
|
|
bool skip_to_next_socket = false;
|
2022-05-06 18:19:37 -04:00
|
|
|
while (got < expected_size) {
|
2024-06-03 00:14:52 -04:00
|
|
|
if (want_exit_callback()) {
|
|
|
|
lg::warn("[nREPL:{}] Terminating nREPL early", tcp_port);
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
2023-06-09 00:02:07 -04:00
|
|
|
tries++;
|
|
|
|
if (tries > 100) {
|
|
|
|
break;
|
|
|
|
}
|
2022-05-11 22:53:53 -04:00
|
|
|
if (got + expected_size > (int)buffer.size()) {
|
2023-06-09 00:02:07 -04:00
|
|
|
lg::error(
|
|
|
|
"[nREPL:{}]: Bad message, aborting the read. Got :{}, Expected: {}, Buffer "
|
|
|
|
"Size: {}",
|
|
|
|
tcp_port, got, expected_size, buffer.size());
|
2022-05-06 18:19:37 -04:00
|
|
|
return std::nullopt;
|
|
|
|
}
|
2024-06-03 00:14:52 -04:00
|
|
|
auto bytes_read = read_from_socket(sock, buffer.data() + got, expected_size - got);
|
|
|
|
if (bytes_read <= 0) {
|
|
|
|
if (bytes_read == 0) {
|
|
|
|
lg::warn("[nREPL:{}] Client Disconnected: {}", tcp_port, address_to_string(addr));
|
|
|
|
} else {
|
|
|
|
lg::warn("[nREPL:{}] Error reading from socket on {}: {}", tcp_port,
|
|
|
|
address_to_string(addr), strerror(errno));
|
|
|
|
}
|
|
|
|
close_socket(sock);
|
|
|
|
it = client_sockets.erase(it); // Erase and move to the next element
|
|
|
|
skip_to_next_socket = true;
|
|
|
|
break;
|
2022-05-06 18:19:37 -04:00
|
|
|
}
|
2024-06-03 00:14:52 -04:00
|
|
|
got += bytes_read;
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (skip_to_next_socket) {
|
|
|
|
continue;
|
2022-05-06 18:19:37 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (header->type) {
|
|
|
|
case ReplServerMessageType::PING:
|
|
|
|
ping_response(sock);
|
|
|
|
return std::nullopt;
|
|
|
|
case ReplServerMessageType::EVAL:
|
|
|
|
std::string msg(buffer.data(), header->length);
|
2023-06-09 00:02:07 -04:00
|
|
|
lg::debug("[nREPL:{}] Received Message: {}", tcp_port, msg);
|
2022-05-06 18:19:37 -04:00
|
|
|
return std::make_optional(msg);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-06-03 00:14:52 -04:00
|
|
|
++it;
|
2022-05-06 18:19:37 -04:00
|
|
|
}
|
|
|
|
return std::nullopt;
|
|
|
|
}
|