milestone 17: real deadlines, validator holes, upstream editor, trusted proxies, contract samples, badvers
This commit is contained in:
@@ -15,7 +15,9 @@
|
||||
//!
|
||||
//! The route is rate-limit exempt (a long-lived stream must not drain its
|
||||
//! address's token bucket) but pays the per-address SSE connection cap, which
|
||||
//! binds loopback too: hub slots are a fixed resource.
|
||||
//! binds loopback too: hub slots are a fixed resource. The address it keys on is
|
||||
//! `client_addr`, so behind a trusted proxy each remote client holds its own
|
||||
//! budget rather than all of them sharing the proxy's.
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
@@ -79,12 +81,15 @@ pub fn stream(
|
||||
const hub = state.hub orelse
|
||||
return http_util.respondError(request, .service_unavailable, "live stream unavailable");
|
||||
|
||||
const peer = address.NetAddress.fromIp(request.peer);
|
||||
// The effective client, not the socket peer: behind a trusted proxy every
|
||||
// stream would otherwise share one address's budget. Acquire and release
|
||||
// read the same value, so a release can never miss the slot it took.
|
||||
const client = address.NetAddress.fromIp(request.client_addr);
|
||||
if (state.limiter) |limiter| {
|
||||
if (!limiter.tryAcquireSse(io, std.Io.Clock.awake.now(io), peer))
|
||||
if (!limiter.tryAcquireSse(io, std.Io.Clock.awake.now(io), client))
|
||||
return http_util.respondError(request, .too_many_requests, "too many live streams from this address");
|
||||
}
|
||||
defer if (state.limiter) |limiter| limiter.releaseSse(io, peer);
|
||||
defer if (state.limiter) |limiter| limiter.releaseSse(io, client);
|
||||
|
||||
const id = hub.subscribe(io) orelse
|
||||
return http_util.respondError(request, .service_unavailable, "live stream is full");
|
||||
|
||||
Reference in New Issue
Block a user