Skip to content

Add request throttling, custom stream throttle and 404 guard middleware - #4837

Open
Rachit7168 wants to merge 4 commits into
fossasia:devfrom
Rachit7168:feature/api-throttling
Open

Add request throttling, custom stream throttle and 404 guard middleware#4837
Rachit7168 wants to merge 4 commits into
fossasia:devfrom
Rachit7168:feature/api-throttling

Conversation

@Rachit7168

@Rachit7168 Rachit7168 commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Close: #4602

Description

This PR introduces robust rate‑limiting for the public API and protects the server from abusive 404 scans.

What’s new

  1. DRF throttling defaultsDEFAULT_THROTTLE_CLASSES and DEFAULT_THROTTLE_RATES are now defined in REST_FRAMEWORK.
    • anon: 60 requests/minute (≈ 1 rps)
    • user: 300 requests/minute (≈ 5 rps)
    • public_stream: 10 requests/minute (the front‑end poll endpoint)
  • public_schedule: 30 requests/minute
    - excessive_404: 1 request/minute (used by custom middleware)
  1. Custom throttles (app/eventyay/api/throttles.py)

    • PublicStreamThrottle – applied to GET /rooms/{id}/streams/current/.
    • PublicScheduleThrottle – placeholder for schedule‑related public endpoints.
    • Excessive404Throttle – used by the 404‑guard middleware.
  2. ViewSet update (app/eventyay/api/views/room.py) – the current_stream action now sets self.throttle_classes = [PublicStreamThrottle].

  3. 404‑rate‑limit middleware (app/eventyay/middleware/block_404.py)

    • Counts 404 responses per client IP using the Redis cache.
    • After >30 404s in a minute the client receives a 429 Too Many Requests with a Retry‑After header.
    • Integrated into the middleware stack (eventyay.middleware.block_404.Block404Middleware).
  4. Redis cache already configured – the project uses django.core.cache.backends.redis.RedisCache; the new middleware re‑uses the same cache, so throttling state is shared across all Gunicorn workers.

Why this matters

  • Prevents a single misbehaving or malicious client from exhausting server resources.
  • Provides a graceful back‑off mechanism (Retry‑After) for both legit polling clients and abusive scanners.
  • All limits are configurable from settings.py, no code changes needed to adjust rates.

Verification steps

  1. Run the test suite – all existing tests pass.
  2. Manually test the anonymous stream endpoint (see below) – the 11‑th request gets a 429 with Retry‑After.
  3. Test authenticated users – after ~300 requests a 429 appears.
  4. Hit a non‑existent endpoint repeatedly – after the 30‑th 404 a 429 with Retry‑After is returned.

Summary by Sourcery

Introduce API-wide rate limiting and 404 abuse protection using DRF throttling and custom middleware.

New Features:

  • Add default DRF throttle classes and rates for anonymous, authenticated, and specific public API scopes.
  • Introduce custom throttles for public stream, public schedule, and excessive 404 traffic.
  • Apply a stricter throttle to the public current stream endpoint.
  • Add middleware to detect excessive 404 responses per client IP and return 429 with Retry-After when limits are exceeded.

Copilot AI lite review requested due to automatic review settings August 11, 2026 11:24

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @Rachit7168, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions github-actions Bot added api REST API backend Python/Django server-side code base Shared models, services, and core utilities labels Aug 11, 2026
@sourcery-ai

sourcery-ai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

Adds DRF-wide throttling defaults, introduces custom throttles for public stream/schedule and excessive 404s, wires a stricter throttle into the public stream endpoint, and adds middleware that rate-limits abusive 404 traffic using the shared Redis cache.

Sequence diagram for 404 guard middleware with Excessive404Throttle

sequenceDiagram
    actor Client
    participant Django as DjangoApp
    participant Block404Middleware
    participant Cache as RedisCache
    participant Excessive404Throttle

    Client->>Django: HTTP request to unknown path
    Django->>Client: 404 response
    Client-->>Block404Middleware: process_response(request, response)
    Block404Middleware->>Cache: get(404_counter:ip)
    Cache-->>Block404Middleware: current_count
    Block404Middleware->>Cache: set(404_counter:ip, count, timeout=60)
    alt [count > MAX_404_PER_MINUTE]
        Block404Middleware->>Excessive404Throttle: allow_request(request, view=None)
        alt [allow_request returns False]
            Excessive404Throttle->>Excessive404Throttle: wait()
            Excessive404Throttle-->>Block404Middleware: retry_after_seconds
            Block404Middleware-->>Client: HttpResponseTooManyRequests
        else [allow_request returns True]
            Block404Middleware-->>Client: original 404 response
        end
    else [count <= MAX_404_PER_MINUTE]
        Block404Middleware-->>Client: original 404 response
    end
Loading

File-Level Changes

Change Details Files
Configure global DRF throttling defaults for anonymous and authenticated users plus named scopes for custom throttles.
  • Add DEFAULT_THROTTLE_CLASSES using AnonRateThrottle and UserRateThrottle in REST_FRAMEWORK settings.
  • Define DEFAULT_THROTTLE_RATES for anon, user, public_stream, public_schedule, and excessive_404 scopes.
app/eventyay/config/settings.py
Apply a stricter throttle to the public current_stream endpoint.
  • Import PublicStreamThrottle into the RoomViewSet module.
  • Set self.throttle_classes = [PublicStreamThrottle] in the current_stream action with a clarifying comment.
app/eventyay/api/views/room.py
Introduce custom DRF throttle classes for public stream, public schedule, and excessive 404 traffic.
  • Create PublicStreamThrottle (AnonRateThrottle) with scope 'public_stream'.
  • Create PublicScheduleThrottle (AnonRateThrottle) with scope 'public_schedule'.
  • Create Excessive404Throttle (AnonRateThrottle) with scope 'excessive_404', intended for middleware use.
app/eventyay/api/throttles.py
Add middleware that counts per-IP 404 responses via Redis and returns 429 with Retry-After when a threshold is exceeded.
  • Create Block404Middleware that intercepts 404 responses, increments a per-IP counter in the default cache with a 60s TTL, and enforces a MAX_404_PER_MINUTE threshold.
  • Instantiate Excessive404Throttle inside the middleware to decide if a throttled 429 should be returned and to compute the Retry-After value.
  • Implement client IP extraction honoring X-Forwarded-For headers.
  • Register Block404Middleware in the project middleware stack.
app/eventyay/middleware/block_404.py
app/eventyay/config/settings.py

Assessment against linked issues

Issue Objective Addressed Explanation
#4602 Introduce global DRF API throttling defaults, including anon/user rate limits and specific scopes/rates for public_stream and public_schedule, configurable via settings and using the existing Redis-backed cache.
#4602 Apply a stricter throttle to the streams/current endpoint so that excessive polling is limited and 429 responses include an appropriate Retry-After header via DRF throttling.
#4602 Implement middleware that detects clients generating more than 30 404 responses per minute and rate-limits them using Redis-backed state, returning 429 Too Many Requests responses with a Retry-After header.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown

Thank you for contributing

Please complete the checklist. Screenshots in the PR description and completed AI reviews are checked automatically.

  • Show what changed — a screenshot or short screen recording of the updated functionality
  • Map structural updates — for architectural work, new directories, or file reorganization, include a diagram in the PR description (component, flow, or directory tree). Optional — only when your PR includes those kinds of changes
  • Request AI feedback — request or receive review from GitHub Copilot, Codex, or other automated reviewers you use

🤖 AI reviews

❌ 2 failed or rate-limited AI reviewers

GitHub Copilot (@copilot-pull-request-reviewer[bot])

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Sourcery AI (@sourcery-ai[bot])

| #4602 | Introduce global DRF API throttling defaults, including anon/user rate limits and specific scopes/rates for public_stream and public_schedule, configurable via settings and using the e…


👤 Reviewer feedback

👤 ⚠️ 1 unresolved reviewer comment

@sarafarajnasardi (requested changes) on app/eventyay/middleware/block_404.py:31view comment

The 404 counter is updated with a non-atomic get() + set(). Under concurrent 404 traffic, multiple requests can read the same old value and overwrite each other, so the limit can be bypassed or delayed exactly when this middleware is needed. Since the default cache is Redis, please use an atomic pattern such as cache.add(key, 0, timeout=60) followed by cache.incr(key), preserving the TTL.


Thank you for your contribution feel free to reach out if you have any questions.

@Rachit7168
Rachit7168 force-pushed the feature/api-throttling branch from eea4a6f to 9b155e5 Compare August 11, 2026 11:27
Comment thread app/eventyay/api/views/room.py Outdated
Comment thread app/eventyay/middleware/block_404.py
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api REST API backend Python/Django server-side code base Shared models, services, and core utilities

Projects

Status: Backlog

Development

Successfully merging this pull request may close these issues.

No API rate limiting — unauthenticated clients can poll without restriction

3 participants