http: bound spawn_http concurrency (http_max_concurrent, default 32) like spawn_crypto — it spawned one unbounded OS thread per call, so a pre-auth VERIFY/REGISTER flood could exhaust threads and hammer the accounts backend; at capacity the command now fails with TEMPORARILY_UNAVAILABLE instead
This commit is contained in:
parent
801614605f
commit
bbe4ee4567
2 changed files with 40 additions and 5 deletions
|
|
@ -203,13 +203,21 @@ impl Command for Register {
|
||||||
urlencode(&ip),
|
urlencode(&ip),
|
||||||
port
|
port
|
||||||
);
|
);
|
||||||
s.spawn_http(
|
if !s.spawn_http(
|
||||||
uid,
|
uid,
|
||||||
format!("acctreg:register:{account}"),
|
format!("acctreg:register:{account}"),
|
||||||
url,
|
url,
|
||||||
body,
|
body,
|
||||||
apikey_headers(s),
|
apikey_headers(s),
|
||||||
|
) {
|
||||||
|
s.fail(
|
||||||
|
uid,
|
||||||
|
"REGISTER",
|
||||||
|
"TEMPORARILY_UNAVAILABLE",
|
||||||
|
"The server is busy; please try again in a moment.",
|
||||||
);
|
);
|
||||||
|
return CmdResult::Fail;
|
||||||
|
}
|
||||||
CmdResult::Ok
|
CmdResult::Ok
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -252,13 +260,21 @@ impl Command for Verify {
|
||||||
urlencode(&account),
|
urlencode(&account),
|
||||||
urlencode(¶ms[1])
|
urlencode(¶ms[1])
|
||||||
);
|
);
|
||||||
s.spawn_http(
|
if !s.spawn_http(
|
||||||
uid,
|
uid,
|
||||||
format!("acctreg:verify:{account}"),
|
format!("acctreg:verify:{account}"),
|
||||||
url,
|
url,
|
||||||
body,
|
body,
|
||||||
apikey_headers(s),
|
apikey_headers(s),
|
||||||
|
) {
|
||||||
|
s.fail(
|
||||||
|
uid,
|
||||||
|
"VERIFY",
|
||||||
|
"TEMPORARILY_UNAVAILABLE",
|
||||||
|
"The server is busy; please try again in a moment.",
|
||||||
);
|
);
|
||||||
|
return CmdResult::Fail;
|
||||||
|
}
|
||||||
CmdResult::Ok
|
CmdResult::Ok
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -462,6 +462,10 @@ impl Server {
|
||||||
/// the core as `Event::HttpResult { uid, tag, .. }` — the same self-injection
|
/// the core as `Event::HttpResult { uid, tag, .. }` — the same self-injection
|
||||||
/// pattern as the DNS resolver, so a slow endpoint never blocks the main loop.
|
/// pattern as the DNS resolver, so a slow endpoint never blocks the main loop.
|
||||||
/// `tag` is `"<module>:<detail>"`; the core routes the reply by its prefix.
|
/// `tag` is `"<module>:<detail>"`; the core routes the reply by its prefix.
|
||||||
|
/// Returns `false` when at capacity (`http_max_concurrent`, default 32) so the
|
||||||
|
/// caller can reject instead of spawning an unbounded number of threads — a
|
||||||
|
/// pre-auth flood (e.g. VERIFY) would otherwise exhaust threads and hammer the
|
||||||
|
/// backend. A `Drop` guard keeps the counter correct even if the task panics.
|
||||||
pub fn spawn_http(
|
pub fn spawn_http(
|
||||||
&self,
|
&self,
|
||||||
uid: Uid,
|
uid: Uid,
|
||||||
|
|
@ -469,10 +473,24 @@ impl Server {
|
||||||
url: String,
|
url: String,
|
||||||
body: String,
|
body: String,
|
||||||
headers: Vec<(String, String)>,
|
headers: Vec<(String, String)>,
|
||||||
) {
|
) -> bool {
|
||||||
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
static ACTIVE: AtomicUsize = AtomicUsize::new(0);
|
||||||
|
struct Guard;
|
||||||
|
impl Drop for Guard {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
ACTIVE.fetch_sub(1, Ordering::Relaxed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let max = self.conf_num("http_max_concurrent", 32usize);
|
||||||
|
if ACTIVE.fetch_add(1, Ordering::Relaxed) >= max {
|
||||||
|
ACTIVE.fetch_sub(1, Ordering::Relaxed);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
let tx = self.event_tx.clone();
|
let tx = self.event_tx.clone();
|
||||||
let verify = self.conf_bool("http_tls_verify", true);
|
let verify = self.conf_bool("http_tls_verify", true);
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
|
let _guard = Guard; // decrements even on panic
|
||||||
let (status, body) = crate::http::post(
|
let (status, body) = crate::http::post(
|
||||||
&url,
|
&url,
|
||||||
"application/x-www-form-urlencoded",
|
"application/x-www-form-urlencoded",
|
||||||
|
|
@ -489,6 +507,7 @@ impl Server {
|
||||||
body,
|
body,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run an expensive credential operation (a KDF: bcrypt / pbkdf2) on a worker
|
/// Run an expensive credential operation (a KDF: bcrypt / pbkdf2) on a worker
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue