fix(web): make the SSE streams' 200/min rate limit actually apply (#691)

app.py called limiter.limit("200 per minute")(stream_x) after the routes
were registered and discarded the result. flask-limiter 3.x enforces a
decorated limit in the wrapper limit() returns, and marks the original
function so the before_request middleware skips it, so the streams had
no limit at all -- not even the 1000/min default. Register the wrapper
as the view instead.

The new test (skipped without flask-limiter) reconnects to each stream
201 times and expects the last to get a 429; it fails on the old code.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Chuck
2026-09-30 09:12:23 -04:00
committed by GitHub
co-authored by Claude Opus 5.5
parent c8a0ddcf7b
commit 15c61def67
2 changed files with 58 additions and 3 deletions
+7 -3
View File
@@ -925,10 +925,14 @@ def stream_logs():
# Each SSE stream is one long-lived request, so only a (re)connect counts
# against a limit. The streams get their own 200 per minute, tighter than the
# 1000 per minute default, which bounds a client stuck reconnecting.
# flask-limiter enforces a decorated limit in the wrapper limit() returns, and
# marks the original function exempt from the default, so the wrapper has to
# replace the registered view: discarding it leaves the streams unlimited.
if limiter:
limiter.limit("200 per minute")(stream_stats)
limiter.limit("200 per minute")(stream_display)
limiter.limit("200 per minute")(stream_logs)
for _endpoint in ('stream_stats', 'stream_display', 'stream_logs'):
app.view_functions[_endpoint] = limiter.limit("200 per minute")(
app.view_functions[_endpoint]
)
@app.route('/favicon.ico')
def favicon():