From db601928b4ea5f7c1685c22429df58096fb311f8 Mon Sep 17 00:00:00 2001 From: dank_meme01 <42031238+dankmeme01@users.noreply.github.com> Date: Thu, 7 Dec 2023 22:20:25 +0100 Subject: [PATCH] request cancellation --- server/game/src/server.rs | 12 +++++------- src/net/http/client.cpp | 19 ++++++++++++++++++- src/net/http/request.cpp | 5 +++++ src/net/http/request.hpp | 8 +++++++- src/ui/menu/main/globed_menu_layer.cpp | 2 +- .../server_switcher/server_test_popup.cpp | 2 +- 6 files changed, 37 insertions(+), 11 deletions(-) diff --git a/server/game/src/server.rs b/server/game/src/server.rs index 0f92256f..e5900ca6 100644 --- a/server/game/src/server.rs +++ b/server/game/src/server.rs @@ -1,5 +1,5 @@ use std::{ - net::{Ipv4Addr, SocketAddr, SocketAddrV4}, + net::{SocketAddr, SocketAddrV4}, sync::{atomic::Ordering, Arc}, time::Duration, }; @@ -36,7 +36,7 @@ pub struct GameServer { pub state: ServerState, pub socket: UdpSocket, pub threads: SyncMutex>>, - rate_limiters: SyncMutex>, + rate_limiters: SyncMutex>, pub secret_key: SecretKey, pub central_conf: SyncMutex, pub config: GameServerConfiguration, @@ -210,12 +210,10 @@ impl GameServer { SocketAddr::V6(_) => bail!("rejecting request from ipv6 host"), }; - let ip_addr = peer.ip(); - // block packets if the client is sending too many of them - if self.is_rate_limited(*ip_addr) { + if self.is_rate_limited(peer) { if cfg!(debug_assertions) { - bail!("{ip_addr} is ratelimited"); + bail!("{peer} is ratelimited"); } // silently drop the packet in release mode @@ -303,7 +301,7 @@ impl GameServer { } } - fn is_rate_limited(&'static self, addr: Ipv4Addr) -> bool { + fn is_rate_limited(&'static self, addr: SocketAddrV4) -> bool { let mut limiters = self.rate_limiters.lock(); if let Some(limiter) = limiters.get_mut(&addr) { !limiter.try_tick() diff --git a/src/net/http/client.cpp b/src/net/http/client.cpp index 9732085b..15335d22 100644 --- a/src/net/http/client.cpp +++ b/src/net/http/client.cpp @@ -46,6 +46,15 @@ static size_t writeCallback(void* contents, size_t size, size_t nmemb, std::ostr return totalSize; } +static int progressCallback(GHTTPRequest* request, curl_off_t dltotal, curl_off_t dlnow, curl_off_t ultotal, curl_off_t ulnow) { + // if cancelled, abort the request + if (request->_cancelled) { + return 1; + } + + return 0; +} + GHTTPResponse GHTTPClient::performRequest(GHTTPRequestHandle handle) { GHTTPResponse response; @@ -68,10 +77,12 @@ GHTTPResponse GHTTPClient::performRequest(GHTTPRequestHandle handle) { break; } + // post fields if (!req.rData.empty()) { curl_easy_setopt(curl, CURLOPT_POSTFIELDS, req.rData.c_str()); } + // generic stuff curl_easy_setopt(curl, CURLOPT_USERAGENT, req.rUserAgent.c_str()); curl_easy_setopt(curl, CURLOPT_URL, req.rUrl.c_str()); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, req.rFollowRedirects); @@ -91,12 +102,18 @@ GHTTPResponse GHTTPClient::performRequest(GHTTPRequestHandle handle) { curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerList); } + // data writing std::ostringstream ss; - curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writeCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ss); + // cancellation + curl_easy_setopt(curl, CURLOPT_XFERINFOFUNCTION, progressCallback); + curl_easy_setopt(curl, CURLOPT_XFERINFODATA, (void*)handle.handle.get()); + curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 0); + response.resCode = curl_easy_perform(curl); + geode::log::debug("req finished"); response.failed = response.resCode != CURLE_OK; if (response.failed) { diff --git a/src/net/http/request.cpp b/src/net/http/request.cpp index 4b0ab0a2..a9e0c5c9 100644 --- a/src/net/http/request.cpp +++ b/src/net/http/request.cpp @@ -6,6 +6,11 @@ void GHTTPRequestHandle::discardResult() const { handle->_discard = true; } +void GHTTPRequestHandle::cancel() const { + handle->_cancelled = true; + this->discardResult(); +} + void GHTTPRequestHandle::maybeCallback(const GHTTPResponse& response) const { if (!handle->_discard) { handle->callcb(response); diff --git a/src/net/http/request.hpp b/src/net/http/request.hpp index ab31a951..32760d33 100644 --- a/src/net/http/request.hpp +++ b/src/net/http/request.hpp @@ -13,8 +13,12 @@ class GHTTPRequest; class GHTTPRequestHandle { public: GHTTPRequestHandle(std::shared_ptr handle) : handle(handle) {} + // waits for the request to complete (or for timeout to exceed), but doesn't call the callback. void discardResult() const; - // calls the callback if the `discardResult` hasn't been called earlier + // cancels the request outright, the callbacks are guaranteed to not be called after `cancel` has been called at least once. + // note that it is not possible to cancel a request immediately, there may be up to a one second delay until the actual cancellation happens. + void cancel() const; + // calls the callback if the `discardResult` or `cancel` hasn't been called earlier void maybeCallback(const GHTTPResponse& response) const; std::shared_ptr handle; @@ -61,6 +65,8 @@ class GHTTPRequest { GHTTPRequestHandle send(GHTTPClient& client); GHTTPRequestHandle send(); + util::sync::AtomicBool _cancelled = false; + protected: friend class GHTTPClient; friend class GHTTPRequestHandle; diff --git a/src/ui/menu/main/globed_menu_layer.cpp b/src/ui/menu/main/globed_menu_layer.cpp index 81021231..5de5173f 100644 --- a/src/ui/menu/main/globed_menu_layer.cpp +++ b/src/ui/menu/main/globed_menu_layer.cpp @@ -262,7 +262,7 @@ void GlobedMenuLayer::requestServerList() { void GlobedMenuLayer::cancelWebRequest() { if (serverRequestHandle.has_value()) { - serverRequestHandle->discardResult(); + serverRequestHandle->cancel(); serverRequestHandle = std::nullopt; return; } diff --git a/src/ui/menu/server_switcher/server_test_popup.cpp b/src/ui/menu/server_switcher/server_test_popup.cpp index eb213a48..a9b7d586 100644 --- a/src/ui/menu/server_switcher/server_test_popup.cpp +++ b/src/ui/menu/server_switcher/server_test_popup.cpp @@ -88,7 +88,7 @@ void ServerTestPopup::onTimeout() { void ServerTestPopup::cancelRequest() { if (sentRequestHandle.has_value()) { - sentRequestHandle->discardResult(); + sentRequestHandle->cancel(); sentRequestHandle = std::nullopt; } }