com.jamesward.zio_http_guard

Members list

Type members

Classlikes

object BadActor

Attributes

Companion
class
Supertypes
trait Product
trait Mirror
class Object
trait Matchable
class Any
Self type
BadActor.type
final case class BadActor(banWindow: Duration, banOnRequestCount: Int, store: ConcurrentMap[IP, Ref[Chunk[Instant]]])

Per-IP "suspect-request" sliding-window detector.

Per-IP "suspect-request" sliding-window detector.

Each IP has a small ring of timestamps (capped at BadActor.banOnRequestCount). A request marked suspect = true appends a timestamp; a request marked suspect = false is always Status.Allowed and never modifies the ring.

An IP is Status.Banned when the ring is full and the time between its oldest and newest timestamps fits inside BadActor.banWindow — i.e. the IP made banOnRequestCount suspect requests within banWindow.

The detector is purely a ZIO service over a ConcurrentMap. There is no background eviction; entries persist for the lifetime of the layer. For long-running servers this is fine: each entry is at most banOnRequestCount Instants plus a Ref wrapper.

Attributes

Companion
object
Supertypes
trait Serializable
trait Product
trait Equals
class Object
trait Matchable
class Any
Show all

zio-http middleware that pairs the BadActor detector with a request pipeline:

zio-http middleware that pairs the BadActor detector with a request pipeline:

  1. Extract a client IP from the request (default: last value of X-Forwarded-For, falling back to request.remoteAddress).
  2. Decide whether the request looks "suspect" (default: WordPress / PHP probing patterns in the path).
  3. Call BadActor.checkReq. If the IP is now banned, fail the request with a slow random-byte stream (a tarpit) so the attacker burns time reading garbage.

Apply with routes @@ BadActorMiddleware().

Attributes

Supertypes
class Object
trait Matchable
class Any
Self type

Attributes

Companion
class
Supertypes
trait Product
trait Mirror
class Object
trait Matchable
class Any
Self type
final case class CrawlerLimiter[K](active: ConcurrentMap[String, Slot[K]])

Per-crawler "one active resource at a time" limiter.

Per-crawler "one active resource at a time" limiter.

Each known crawler User-Agent gets a single slot. While a slot is held, requests from that crawler for the same resource key (refresh the slot) and requests from that crawler for paths with no key (homepage, static assets, etc.) are allowed; requests for a different resource key are denied with 429 Too Many Requests until the slot's last access is older than hold.

The "resource key" is whatever you compute from the request — typically a coarse grouping that maps to an expensive backend operation. Examples:

  • a Maven groupId/artifactId/version triple, when each new triple triggers a fresh archive download + extraction
  • a tenant ID, when each new tenant warms an isolated cache
  • a date bucket, when each bucket spans a separate database shard

Crawlers that don't legitimately need to walk the whole keyspace in parallel (Googlebot et al.) will simply move on to a different page from the same key while their slot is held; well-behaved crawlers absorb the limit invisibly.

Value parameters

active

map from crawler User-Agent token to its currently held slot.

Attributes

Companion
object
Supertypes
trait Serializable
trait Product
trait Equals
class Object
trait Matchable
class Any
Show all