Skip to content

Commit

Permalink
request cancellation
Browse files Browse the repository at this point in the history
  • Loading branch information
dankmeme01 committed Dec 7, 2023
1 parent e4db41a commit db60192
Show file tree
Hide file tree
Showing 6 changed files with 37 additions and 11 deletions.
12 changes: 5 additions & 7 deletions server/game/src/server.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::{
net::{Ipv4Addr, SocketAddr, SocketAddrV4},
net::{SocketAddr, SocketAddrV4},
sync::{atomic::Ordering, Arc},
time::Duration,
};
Expand Down Expand Up @@ -36,7 +36,7 @@ pub struct GameServer {
pub state: ServerState,
pub socket: UdpSocket,
pub threads: SyncMutex<FxHashMap<SocketAddrV4, Arc<GameServerThread>>>,
rate_limiters: SyncMutex<FxHashMap<Ipv4Addr, SimpleRateLimiter>>,
rate_limiters: SyncMutex<FxHashMap<SocketAddrV4, SimpleRateLimiter>>,
pub secret_key: SecretKey,
pub central_conf: SyncMutex<GameServerBootData>,
pub config: GameServerConfiguration,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand Down
19 changes: 18 additions & 1 deletion src/net/http/client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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);
Expand All @@ -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) {
Expand Down
5 changes: 5 additions & 0 deletions src/net/http/request.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
8 changes: 7 additions & 1 deletion src/net/http/request.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@ class GHTTPRequest;
class GHTTPRequestHandle {
public:
GHTTPRequestHandle(std::shared_ptr<GHTTPRequest> 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<GHTTPRequest> handle;
Expand Down Expand Up @@ -61,6 +65,8 @@ class GHTTPRequest {
GHTTPRequestHandle send(GHTTPClient& client);
GHTTPRequestHandle send();

util::sync::AtomicBool _cancelled = false;

protected:
friend class GHTTPClient;
friend class GHTTPRequestHandle;
Expand Down
2 changes: 1 addition & 1 deletion src/ui/menu/main/globed_menu_layer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ void GlobedMenuLayer::requestServerList() {

void GlobedMenuLayer::cancelWebRequest() {
if (serverRequestHandle.has_value()) {
serverRequestHandle->discardResult();
serverRequestHandle->cancel();
serverRequestHandle = std::nullopt;
return;
}
Expand Down
2 changes: 1 addition & 1 deletion src/ui/menu/server_switcher/server_test_popup.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ void ServerTestPopup::onTimeout() {

void ServerTestPopup::cancelRequest() {
if (sentRequestHandle.has_value()) {
sentRequestHandle->discardResult();
sentRequestHandle->cancel();
sentRequestHandle = std::nullopt;
}
}
Expand Down

0 comments on commit db60192

Please sign in to comment.