243 lines
11 KiB
C++
243 lines
11 KiB
C++
#include <deque>
|
|
#include <log/LogUtils.h>
|
|
#include "WebServer.h"
|
|
#include "src/client/web/WebClient.h"
|
|
#include <netinet/tcp.h>
|
|
#include "src/InstanceHandler.h"
|
|
#include "./GlobalNetworkEvents.h"
|
|
|
|
using namespace std;
|
|
using namespace std::chrono;
|
|
using namespace ts;
|
|
using namespace ts::server;
|
|
|
|
#if defined(TCP_CORK) && !defined(TCP_NOPUSH)
|
|
#define TCP_NOPUSH TCP_CORK
|
|
#endif
|
|
|
|
WebControlServer::WebControlServer(const std::shared_ptr<VirtualServer>& handle) : handle(handle) {}
|
|
WebControlServer::~WebControlServer() = default;
|
|
|
|
bool WebControlServer::start(const std::deque<std::shared_ptr<WebControlServer::Binding>>& target_bindings, std::string& error) {
|
|
if(this->running()) {
|
|
error = "server already running";
|
|
return false;
|
|
}
|
|
this->_running = true;
|
|
|
|
/* reserve backup file descriptor in case that the max file descriptors have been reached */
|
|
{
|
|
this->server_reserve_fd = dup(1);
|
|
if(this->server_reserve_fd < 0)
|
|
logWarning(this->handle->getServerId(), "Failed to reserve a backup accept file descriptor. ({} | {})", errno, strerror(errno));
|
|
}
|
|
|
|
{
|
|
for(auto& binding : target_bindings) {
|
|
binding->file_descriptor = socket(binding->address.ss_family, SOCK_STREAM | SOCK_NONBLOCK, 0);
|
|
if(binding->file_descriptor < 0) {
|
|
logError(this->handle->getServerId(), "[Web] Failed to bind server to {}. (Failed to create socket: {} | {})", binding->as_string(), errno, strerror(errno));
|
|
continue;
|
|
}
|
|
|
|
int enable = 1, disabled = 0;
|
|
|
|
if (setsockopt(binding->file_descriptor, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0)
|
|
logWarning(this->handle->getServerId(), "[Web] Failed to activate SO_REUSEADDR for binding {} ({} | {})", binding->as_string(), errno, strerror(errno));
|
|
if(setsockopt(binding->file_descriptor, IPPROTO_TCP, TCP_NOPUSH, &disabled, sizeof disabled) < 0)
|
|
logWarning(this->handle->getServerId(), "[Web] Failed to deactivate TCP_NOPUSH for binding {} ({} | {})", binding->as_string(), errno, strerror(errno));
|
|
if(binding->address.ss_family == AF_INET6) {
|
|
if(setsockopt(binding->file_descriptor, IPPROTO_IPV6, IPV6_V6ONLY, &enable, sizeof(int)) < 0)
|
|
logWarning(this->handle->getServerId(), "[Web] Failed to activate IPV6_V6ONLY for IPv6 binding {} ({} | {})", binding->as_string(), errno, strerror(errno));
|
|
}
|
|
if(fcntl(binding->file_descriptor, F_SETFD, FD_CLOEXEC) < 0)
|
|
logWarning(this->handle->getServerId(), "[Web] Failed to set flag FD_CLOEXEC for binding {} ({} | {})", binding->as_string(), errno, strerror(errno));
|
|
|
|
|
|
if (bind(binding->file_descriptor, (struct sockaddr *) &binding->address, sizeof(binding->address)) < 0) {
|
|
logError(this->handle->getServerId(), "[Web] Failed to bind server to {}. (Failed to bind socket: {} | {})", binding->as_string(), errno, strerror(errno));
|
|
close(binding->file_descriptor);
|
|
continue;
|
|
}
|
|
|
|
if (listen(binding->file_descriptor, SOMAXCONN) < 0) {
|
|
logError(this->handle->getServerId(), "[Web] Failed to bind server to {}. (Failed to listen: {} | {})", binding->as_string(), errno, strerror(errno));
|
|
close(binding->file_descriptor);
|
|
continue;
|
|
}
|
|
|
|
|
|
binding->event_accept = serverInstance->network_event_loop()->allocate_event(binding->file_descriptor, EV_READ | EV_PERSIST, WebControlServer::on_client_receive, this, nullptr);
|
|
if(!binding->event_accept) {
|
|
logError(this->handle->getServerId(), "[Web] Failed to allocate network event for binding {}.", binding->as_string());
|
|
close(binding->file_descriptor);
|
|
continue;
|
|
}
|
|
|
|
event_add(binding->event_accept, nullptr);
|
|
this->bindings.push_back(binding);
|
|
}
|
|
|
|
if(this->bindings.empty()) {
|
|
this->stop();
|
|
error = "failed to bind to any address";
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
#define CLOSE_CONNECTION \
|
|
if(shutdown(file_descriptor, SHUT_RDWR) < 0) { \
|
|
debugMessage(LOG_FT, "[{}] Failed to shutdown socket ({} | {}).", logging_address(remote_address), errno, strerror(errno)); \
|
|
} \
|
|
if(close(file_descriptor) < 0) { \
|
|
debugMessage(LOG_FT, "[{}] Failed to close socket ({} | {}).", logging_address(remote_address), errno, strerror(errno)); \
|
|
}
|
|
|
|
inline std::string logging_address(const sockaddr_storage& address) {
|
|
if(config::server::disable_ip_saving)
|
|
return "X.X.X.X" + to_string(net::port(address));
|
|
return net::to_string(address, true);
|
|
}
|
|
|
|
void WebControlServer::on_client_receive(int server_file_descriptor_, short, void *ptr_server) {
|
|
auto server = (WebControlServer*) ptr_server;
|
|
|
|
sockaddr_storage remote_address{};
|
|
memset(&remote_address, 0, sizeof(remote_address));
|
|
socklen_t address_length = sizeof(remote_address);
|
|
|
|
int file_descriptor = accept(server_file_descriptor_, (struct sockaddr *) &remote_address, &address_length);
|
|
if (file_descriptor < 0) {
|
|
if(errno == EAGAIN) {
|
|
return;
|
|
}
|
|
|
|
if(errno == EMFILE || errno == ENFILE) {
|
|
if(errno == EMFILE) {
|
|
logError(server->handle->getServerId(), "[Web] Server ran out file descriptors. Please increase the process file descriptor limit.");
|
|
} else {
|
|
logError(server->handle->getServerId(), "[Web] Server ran out file descriptors. Please increase the process and system-wide file descriptor limit.");
|
|
}
|
|
|
|
bool tmp_close_success = false;
|
|
{
|
|
lock_guard reserve_fd_lock(server->server_reserve_fd_lock);
|
|
if(server->server_reserve_fd > 0) {
|
|
debugMessage(server->handle->getServerId(), "[Web] Trying to accept client with the reserved file descriptor to close the incoming connection.");
|
|
auto _ = [&]{
|
|
if(close(server->server_reserve_fd) < 0) {
|
|
debugMessage(server->handle->getServerId(), "[Web] Failed to close reserved file descriptor");
|
|
tmp_close_success = false;
|
|
return;
|
|
}
|
|
server->server_reserve_fd = 0;
|
|
|
|
errno = 0;
|
|
file_descriptor = accept(server_file_descriptor_, (struct sockaddr *) &remote_address, &address_length);
|
|
if(file_descriptor < 0) {
|
|
if(errno == EMFILE || errno == ENFILE)
|
|
debugMessage(server->handle->getServerId(), "[Web] [{}] Even with freeing the reserved descriptor accept failed. Attempting to reclaim reserved file descriptor", logging_address(remote_address));
|
|
else if(errno == EAGAIN);
|
|
else {
|
|
debugMessage(server->handle->getServerId(), "[Web] [{}] Failed to accept client with reserved file descriptor. ({} | {})", logging_address(remote_address), errno, strerror(errno));
|
|
}
|
|
server->server_reserve_fd = dup(1);
|
|
if(server->server_reserve_fd < 0)
|
|
debugMessage(server->handle->getServerId(), "[Web] [{}] Failed to reclaim reserved file descriptor. Future clients cant be accepted!", logging_address(remote_address));
|
|
else
|
|
tmp_close_success = true;
|
|
return;
|
|
}
|
|
debugMessage(server->handle->getServerId(), "[Web] [{}] Successfully accepted client via reserved descriptor (fd: {}). Disconnecting client.", logging_address(remote_address), file_descriptor);
|
|
|
|
CLOSE_CONNECTION
|
|
server->server_reserve_fd = dup(1);
|
|
if(server->server_reserve_fd < 0)
|
|
debugMessage(server->handle->getServerId(), "[Web] Failed to reclaim reserved file descriptor. Future clients cant be accepted!");
|
|
else
|
|
tmp_close_success = true;
|
|
logMessage(server->handle->getServerId(), "[Web] [{}] Dropping file transfer connection attempt because of too many open file descriptors.", logging_address(remote_address));
|
|
};
|
|
_();
|
|
}
|
|
}
|
|
|
|
if(!tmp_close_success) {
|
|
debugMessage(server->handle->getServerId(), "[Web] Sleeping two seconds because we're currently having no resources for this user. (Removing the accept event)");
|
|
for(auto& binding : server->bindings)
|
|
event_del_noblock(binding->event_accept);
|
|
server->accept_event_deleted = system_clock::now();
|
|
return;
|
|
}
|
|
return;
|
|
}
|
|
logMessage(server->handle->getServerId(), "[Web] Got an error while accepting a new client. (errno: {}, message: {})", errno, strerror(errno));
|
|
return;
|
|
}
|
|
|
|
|
|
auto client = std::make_shared<WebClient>(server, file_descriptor);
|
|
memcpy(&client->remote_address, &remote_address, sizeof(remote_address));
|
|
client->initialize_weak_reference(client);
|
|
client->initialize();
|
|
|
|
server->clientLock.lock();
|
|
server->clients.push_back(client);
|
|
server->clientLock.unlock();
|
|
event_add(client->readEvent, nullptr);
|
|
logMessage(server->handle->getServerId(), "[Web] Got new client from {}:{}", client->getLoggingPeerIp(), client->getPeerPort());
|
|
}
|
|
|
|
void WebControlServer::stop() {
|
|
//TODO
|
|
this->clientLock.lock();
|
|
auto clList = this->clients;
|
|
this->clientLock.unlock();
|
|
|
|
for(const auto& e : clList) {
|
|
e->close_connection(system_clock::now());
|
|
}
|
|
|
|
|
|
for(auto& binding : this->bindings) {
|
|
if(binding->event_accept) {
|
|
event_del_block(binding->event_accept);
|
|
event_free(binding->event_accept);
|
|
binding->event_accept = nullptr;
|
|
}
|
|
if(binding->file_descriptor > 0) {
|
|
if(shutdown(binding->file_descriptor, SHUT_RDWR) < 0)
|
|
logWarning(this->handle->getServerId(), "[Web] Failed to shutdown socket for binding {} ({} | {}).", binding->as_string(), errno, strerror(errno));
|
|
if(close(binding->file_descriptor) < 0)
|
|
logError(this->handle->getServerId(), "[Web] Failed to close socket for binding {} ({} | {}).", binding->as_string(), errno, strerror(errno));
|
|
binding->file_descriptor = -1;
|
|
}
|
|
}
|
|
this->bindings.clear();
|
|
|
|
if(this->server_reserve_fd > 0) {
|
|
if(close(this->server_reserve_fd) < 0)
|
|
logError(this->handle->getServerId(), "[Web] Failed to close backup file descriptor ({} | {})", errno, strerror(errno));
|
|
}
|
|
this->server_reserve_fd = -1;
|
|
}
|
|
|
|
void WebControlServer::unregisterConnection(const std::shared_ptr<WebClient>& connection) {
|
|
//TODO may checks?
|
|
{
|
|
std::lock_guard lock{this->clientLock};
|
|
auto index = std::find(this->clients.begin(), this->clients.end(), connection);
|
|
if(index == this->clients.end()) {
|
|
return;
|
|
}
|
|
|
|
this->clients.erase(index);
|
|
}
|
|
}
|
|
|
|
std::optional<std::string> WebControlServer::external_binding() {
|
|
/* FIXME: TODO! */
|
|
return std::nullopt;
|
|
} |