§Customization

The classes in the User Quotas classes can be created programmatically, but for convenience you are able to define the classes in configuration. The classes are then bound using Guice and automatically wired into your application.

The main API classes are introduced in the API tour in the Overview page. Here is a quick refresher:

QuotaAction A Play action that checks the user's quota.
QuotaFilter A Play filter that checks the user’s quota.
RequestJudger The internal logic for checking a request against a quota — used by the action and filter above.
UserExtractor Looks at an HTTP request and decides which User it corresponds to.
ResultFormatter Reformats the HTTP response to present quota information, e.g. rate limit headers.
CorrelationIdExtractor Produces a debugging correlation identifier that can be associated with an operation.
Judge Stores usage information and handles quota requests.
UserQuotas Can be queried to get the quota for a User. Used by the Judge.

In your Play application you need to configure a QuotaAction or QuotaFilter to handle the HTTP requests that come into your application. To get these classes, you’ll need to configure a Judge, UserQuotas, UserExtractor, and ResultFormatter.

§Named and child bindings

You can create named bindings to Quota objects or nested bindings.

A named binding is a top-level configuration with its own name. It has the form:

quota.<class alias>.<binding name> { <configuration> }

Quota will then configure an instance of the class and create a Guice binding with that name. You can then inject the instance into your class by using the javax.inject.Named annotation.

quota.exampleClass.foo { <configuration> }
class MyController @Inject() (
  @Named("foo") fooExample: ExampleClass) ...

The special binding name “default” can also be accessed without a @Named annotation.

quota.<class alias>.default { <configuration> }

For example:

quota.exampleClass.default { <configuration> }
class MyController @Inject() (fooExample: ExampleClass) ...

A child binding is defined inside the configuration for another object. It doesn’t have a public name, so you can’t inject it directly into one of your classes. It is only available to the object it is nested inside. A child binding has the general form shown below.

<path to parent object> {
  <nested property> { < configuration> }
}

Here’s an example of a configuration with two child bindings inside it. In this example, “b” is scoped inside “a”, and “c” is scoped inside “b”. Both “b” and “c” will be hidden outside their parent objects. Only “a” can be accessed by its name (“foo”).

quota.a.foo {
  <configuration for a>
  b {
    <configuration for b>
    c {
      <configuration for c>
    }
  }
}

As you can see, named bindings are useful for the top level objects that you’ll be using from with your application code. Named bindings can also be useful when you want to create an object once and then share it between more than one object. See named references below for a description of how to do this.

Child bindings are useful when you want to create objects that are scoped to their parent object only. It’s better to use child bindings when you don’t need to share an object with a global name. The limited scope of child bindings can help keep your configuration simpler and easier to understand.

§Configuration types and properties

When you configure an object, User Quotas will read in all the object’s configured properties and then create an appropriate binding. You can use several properties to direct how the configuration is processed.

The most important field is the type field. The type field says how the configuration should be processed.

<path to configuration> {
  type = <type>
  <configuration for type>
}

§Built-in types

Each Quota class has its own set of types. These types are documented under each class’s configuration section below. Here is an example of types for a MemoryJudge and an IpAddressExtractor.

<path to Judge configuration> {
  type = memory # Create a local in memory judge
  userQuotas { ... }
}

<path to user extractor configuration> {
  type = ipAddress # use IP addresses as users
}

§Named reference types

One common built-in type is name. This type says that a binding should be configured to point to another binding with a certain name. The main benefit of using the named type is that you can define a named binding in one place, then refer to it from multiple places. This allows an object to be configured once and then shared.

# Define an Apple named "yummy"
quota.apple.yummy { <configuration> }

# Define two trees named "big" and "little", both using the Apple "yummy" instance.
quota.tree.big {
  apple {
    type = named
    name = yummy
  }
}

quota.tree.little {
  apple {
    type = named
    name = yummy
  }
}

As a shortcut, you can omit the name if it is “default”.

# Define the default Apple
quota.apple.default { <configuration> }

quota.tree.little {
  # Bind this child Apple to the default Apple
  apple.type = default
}

§Custom types

You can instantiate your own custom class by setting the type property to the fully-qualified name of that class. This allows you to define your own instances of the classes.

<path to Judge configuration> {
  type = com.example.MyJudge
}

These classes will be instantiated with Guice injection.

If you want to read some configuration before you create your binding you’ll need to create a Configurator. A configurator can resolve the dependencies behind a type. A configurator subclasses com.lightbend.binding.config.BindingConfigurator, and reads in the properties set.

For example, when an apple’s definition involves a class type, it defines a class field, which is the public interface. The configurator is attached to the default type definition through the typeDefs field.

apple {
  class = com.example.Apple
  typeDefs = {
    default = red
    red = com.example.RedApple
    com.example.RedApple {
      configurator = "com.example.RedAppleConfigurator"
      stem = null
    }
    green = com.example.GreenApple
    com.example.GreenApple {
      configurator = "com.example.GreenAppleConfigurator"
      seeds = null
    }  
  }
}

Here, the interface is com.example.Apple, and com.example.GreenApple is a class which implements Apple. There are two different types of apple: “red” and “green”. The “red” apple is assigned to the “RedApple” and has a RedAppleConfigurator has a “stem” property. The “green” type is assigned to the GreenApple class, and has a GreenAppleConfigurator that looks for the “seeds” property.

Without a specific type, the default type is used. In this case, using a red apple, the stem property must be defined:

quota.apple.yummy {
  stem = "short"
}

or marked explicitly:

quota.apple.yummy {
  type = red
  stem = "short"
}

while the type and seeds must be specified if a green apple is desired:

quota.apple.yummy {
  type = green
  seeds = 6
}

Now that we’ve outlined the common types, let’s look at the configuration types for each of the User Quotas classes.

§QuotaAction & QuotaFilter configuration

The core Play Quota logic is held together either a QuotaAction, which is an extension of a Play Action) or a QuotaFilter, an extension of a Play Filter.

To define a QuotaAction or a QuotaFilter, add an entry like the following to your application.conf file:

play.quota.(action|filter).<binding name> {
  requestCost = <number of tokens per request>
  judge { <Judge configuration> }
  userExtractor { <UserExtractor configuration> }
  resultFormatter { <ResultFormatter configuration> }
}

So, for example, to configure a QuotaAction with the binding name “custom”, you would specify:

play.quota.action.custom {
  requestCost = <number of tokens per request>
  judge { <Judge configuration> }
  userExtractor { <UserExtractor configuration> }
  resultFormatter { <ResultFormatter configuration> }
}

The QuotaAction bound by this configuration can be accessed by using the @Named annotation.

class MyController @Inject() (
  @Named("custom") quotaAction: QuotaAction) ...

Remember that the special binding name “default” can also be accessed through Guice without needing a @Named annotation.

play.quota.action.default { ... }
class MyController @Inject() (
  quotaAction: QuotaAction) …

You can define multiple QuotaActions, each with different names.

play.quota.action.default {
  tokenCost = 1
  …
}
play.quota.action.api {
  tokenCost = 1
  …
}
play.quota.action.expensive {
  tokenCost = 5
  …
}

Here’s a complete example of a QuotaAction defined in configuration.

play.quota.action.default {
 requestCost = 1
 
 judge {
   # Use a local in-memory judge
   type = memory
   # We set a rate of 50 requests every 5 minutes. This should
   # block robots but not normal users.
   userQuotas {
     type = fixed
     quota {       
       maxBalance = 50
       refillAmount = 50
       tickSize = 5 minutes
     }
   }
 }

 # Use IP addresses as users
 userExtractor {
   type = ipAddress
 }
 
 # Format like Twitter
 resultFormatter {
   type = rest
 }
}

Here is what each configuration property means.

requestCost How many tokens to deduct for each request. Defaults to 1 token.
judge Configuration for the Judge to use. You can use any configuration described in Judge configuration. Here are several examples.

# Configure a new Judge for use
# by only by this QuotaAction.
judge {
  type = memory
  ...
}

# Reference the default Judge. This allows
# a named Judge to be defined elsewhere and shared by
# several QuotaAction or other objects.
judge.type = named

# Reference the @Named("custom") Judge. Again this
# allows the Judge to be shared.
judge {
  type = named
  name = custom
}
userExtractor Configuration for a UserExtractor. You can use any configuration described in UserExtractor configuration. Here are some examples.

# Configure a new IpAddressExtractor for use
# by only by this QuotaAction.
userExtractor.type = ipAddress

# Configure a new SessionExtractor for use
# by only by this QuotaAction.
userExtractor {
  type = session
  sessionKey = userName
}

# Ask Guice to create a new MyUserExtractor instance
# for use by this QuotaAction.
userExtractor.type = com.mycompany.MyUserExtractor

# Reference the default UserExtractor.
userExtractor.type = named

# Reference the @Named("custom") UserExtractor
userExtractor {
  type = named
  name = custom
}
resultFormatter Configuration for a ResultFormatter. The configuration options are described in ResultFormatter configuration.

§Judge configuration

The judge manages rate limiting by allocating a number of requests to each user, and using a set number for each request, using a token bucket algorithm. The underlying type of judge is com.lightbend.quota.Judge: tokens can be deposited or removed using a positive or negative tokenDelta.

Scala API:

package com.lightbend.quota.sapi.judge
trait Judge {
  def petition(user: User, tokenDelta: Int)(implicit correlationId: CorrelationId): Future[Judgement]
}

Java API:

package com.lightbend.quota.japi.judge
public interface Judge {
  CompletionStage<Judgement> petition(User user, int tokenDelta, CorrelationId correlationId);
}

You can bind named Judge instances by adding configuration to your application.conf file.

quota.judge.default { <Judge configuration> }

You can also define a Judge instance inline within the configuration for some other objects. For example, you can define a Judge within a QuotaAction using the judge property.

play.quota.action.foo {
  judge { <Judge configuration> }
  ...
}

§Judge “memory” type

The “memory” type configures an in memory judge that is not shared between JVM instances. This is a good configuration when running on a single JVM, but may lead to inconsistent results when multiple instances are run behind a load balancer. The underlying type is com.lightbend.quota.sapi.judge.memory.MemoryJudge.

MemoryJudge configuration looks like this:

<path to Judge configuration> {
  type = memory
  userQuotas { <userQuotas configuration> }
}
userQuotas A binding to a UserQuotas object. For example:

userQuotas {
  type = fixed
  …
}

§Judge “actorAdaptor” type

The “actorAdapter” type is a judge that sends messages to a JudgeActor. This makes it possible to have a Judge in a different location through an actor implementation. The defined configuration type is “actorAdapter” and the underlying type is akka.quota.sapi.ActorJudge.

judge {
  type = actorAdapter
  judgeActor { <JudgeActorRef configuration> }
  transactTimeout = <how long to wait for quota transactions>
}
judgeActor A reference to a JudgeActorRef.
transactTimeout The period of time after which the lack of a response shall be considered a failure.

§Judge “circuitBreaker” type

The “circuitBreaker” type is a judge that wraps a circuit breaker around another judge, using akka.pattern.CircuitBreaker. Please see the Akka documentation for details. The underlying type is akka.quota.sapi.CircuitBreakerJudge.

judge {
  type = "circuitBreaker"
  allowWhenOpen = true
  maxFailures = 3
  callTimeout = 1 second
  resetTimeout = 5 seconds
  judge {
    ...
  }
}
allowWhenOpen Immediately accepts all judgements when the circuit breaker is open.
maxFailures The number of failures before the circuit breaker opens.
callTimeout The period of time after which the lack of a response shall be considered a failure.
resetTimeout The period of time that must elapse before an open circuit breaker resets.
judge The judge to which the circuit breaker judge delegates.

§JudgeActor configuration

A JudgeActor is an Akka actor that provides a similar interface to a Judge, except it uses message passing rather than futures. It receives Petition objects and replies with Judgement objects. A JudgeActorRef is a reference to a JudgeActor.

JudgeActorRefs can be configured within Quotas in a similar way to other objects. Sometimes binding a JudgeActorRef will involve creating a new actor, sometimes it will just be a reference to another actor and sometimes it will involve creating a router.

You can configure a JudgeActorRef either as a named binding or as a child binding within the ActorJudge type or within the ConsistentHashingJudgeActor type. Named bindings look like this:

quota.judgeActor.<binding name> { <configuration> }

There are two built-in JudgeActor types defined: “judgeAdapter” and “consistentHashing”. The “judgeAdapter” is the default.

§JudgeActor “judgeAdapter” type

The “judgeAdapter” type creates a new actor that wraps a Judge. Any messages sent to the actor will be relayed to the Judge. A typical use case for this is to wrap a local in-memory Judge and make it available via message passing. The underlying type is akka.quota.sapi.JudgeActor.

<path to JudgeActor configuration> {
  type = judgeAdapter
  actorCreator = { < actor creator configuration> }
  actorName = <path to this actor>
  judge { <Judge configuration> }
}
actorCreator A binding to an `ActorCreationConfig` class, which creates an actor using an `com.lightbend.binding.akka.creator.ActorCreator` -- you can think of this as a simplified `akka.actor.ActorRefFactory`. The default is an empty binding.

actorCreator = {
  props = {}
  name = "actorName"
}
actorName Name of the judge actor in Akka.

actorName = "someJudgeActor"

§JudgeActor “consistentHashing” type

The “consistentHashing” type creates an Akka consistent hashing router that dispatches to actors throughout the cluster. This provides a fair way of sharing work throughout the cluster. This router does not provide any redundancy over an in-memory judge actor and can involve a network hop if the actor is not local. In the event of partition, the consistent hashing judge actor may give inconsistent results until the partition has been resolved and the incorrect actor has been drained. There is no special class that implements this kind of JudgeActor because it is just a wrapper around Akka’s ConsistentHashingRouter. The ConsistentHashingJudgeActorConfigurator provides the logic for binding and configuring the router and wrapping it in a JudgeActorRef.

The Akka documentation on cluster aware routers with groups may be useful. The consistent hashing router itself is only relevant when extending behavior.

<path to JudgeActorRef configuration> {
  type = consistentHashing  
  actorCreator { }
  actorName = name of the group
  routeeJudgeActor {}
  allowLocalRoutees = true 
  useRole = "role"   
  numberOfInstances = 5 
}
actorCreator Properties of the consistent hashing group router actor.
actorName The name of the consistent hashing group router actor.
routeeJudgeActor The bindings to a `JudgeActorRef` for the routee judge actor.

routeeJudgeActor {
 type = judgeAdapter
 actorName = memory
 judge {
   type = memory
   userQuotas {
     type = fixed
     quota {
       maxBalance = 50
       refillAmount = 50
       tickSize = 5 minutes
     }
   }
 }
}
allowLocalRoutees Whether or not to allow local group if the node is on current JVM. Based off `akka.cluster.routing.ClusterRouterGroupSettings`.

allowLocalRoutees = true
useRole The role of the cluster aware group, based off `akka.cluster.routing.ClusterRouterGroupSettings`.

useRole = "role"
numberOfInstances Number of instances of the group to create, based off `akka.cluster.routing.ClusterRouterGroupSettings`.

numberOfInstances = 100

§UserQuotas configuration

The UserQuotas configuration is used by the MemoryJudge to assign quotas to Users. The “fixed” type is the default.

Scala API:

package com.lightbend.quota.sapi
trait UserQuotas {   
  def quotaFor(now: Instant, user: User)(implicit correlationId: CorrelationId): Future[Quota]
}

Java API:

package com.lightbend.quota.japi;
public interface UserQuotas {
    CompletionStage<Quota> quotaFor(Instant now, User user, CorrelationId correlationId);
}

§UserQuotas “fixed” type

The “fixed” type sets up a fixed rate user quota. All users will get the same quota. The underlying type is com.lightbend.quota.sapi.FixedUserQuotas.

userQuotas {
  type = fixed
  quota {}  
}
quota The quota object.

§UserQuotaRule configuration

A UserQuotaRule determines how many tokens are available to a User, how often the User’s tokens are refilled, and by how much.

Scala API:

package com.lightbend.quota.sapi.rule
trait UserQuotaRule {
  def userQuotaFor(now: Instant, user: User)(implicit correlationId: CorrelationId): Future[Quota]
}

Java API:

package com.lightbend.quota.japi.rule;
public interface UserQuotaRule {
    CompletionStage<Optional<Quota>> userQuotaFor(Instant now, User user, CorrelationId correlationId);
}

There are two types of matcher rule: matchUserQuotaRule and ruleTable. The default is matchUserQuotaRule.

matcherRule {
  ...
}

User Quota Rules are not enabled by a judge out of the box. They are provided for custom Judge implementations.

§UserQuotaRule “matchUserQuotaRule” type

The “matchUserQuotaRule” type matches a User with a Quota. The underlying type is com.lightbend.quota.sapi.rule.MatchUserQuotaRule.

matcherRule {
  type = "matchUserQuotaRule"
  matcher = { <user matcher configuration> }
  quota =  { <quota configuration> }
}
matcher A UserMatcher.
quota A Quota.

§UserQuotaRule “ruleTable” type

A “ruleTable” type will go through a list of rules until a match is found, and then return the corresponding quota. The underlying type is com.lightbend.quota.sapi.rule.UserQuotaRuleTable.

matcherRule {
  type = "ruleTable"
  rules = [
     <UserQuotaRule>, <UserQuotaRule>
  ]
}
rules The rules underlying the rule table. This is a sequence of `UserQuotaRule` instances.


 rules = [
   {
    matcher { regex = "always" }
    quota = { type = unlimited }
   },
   {
    matcher { regex = "never" }
    quota = { type = zero }
   }
 ]
}

§UserMatcher configuration

A user matcher is used to match an individual user against a rule.

Scala API:

package com.lightbend.quota.sapi.rule
trait UserMatcher {
  def matches(now: Instant, user: User)(implicit correlationId: CorrelationId): Future[Boolean]
}

Java API:

package com.lightbend.quota.japi.rule
public interface UserMatcher {
    public CompletionStage<Boolean> matches(Instant now, User user, CorrelationId correlationId);
}

§UserMatcher “all” type

The “all” type always matches, regardless of input. The underlying type is com.lightbend.quota.sapi.rule.AllUserMatcher.

matcher {
  type = "all"
}

§UserMatcher “regex” type

The “regex” type treats the input user as a string, and uses a Scala regular expression to match. The underlying type is com.lightbend.quota.sapi.rule.RegexUserMatcher.

matcher {
  type = "regex"
  regex = "steve"
}
regex A regular expression.

§Quota configuration

The quota configuration itself. The types are “zero”, “unlimited” and “rateLimited”. The underlying type is com.lightbend.quota.sapi.Quota, which is a sealed trait.

§Quota “zero” type

A “zero” Quota always rejects.

quota {
  type = "zero"
}

§Quota “unlimited” type

A quota with a type of “unlimited” has no limit in place and always accepts.

quota {
  type = "unlimited"
}

§Quota “rateLimited” type

quota {
  type = "rateLimited"
  maxBalance = <max number of tokens>
  refillAmount = <how many tokens to refill each tick>
  tickSize = <how often to refill the bucket>
  tickZero = <when the first tick started, in millis since 1970>
}
maxBalance The maximum size of the token bucket. The bucket will never be refilled above this level.

maxBalance = 50
refillAmount How many tokens to refill each tick. This can be different from the maxBalance, either the same or lower.

refillAmount = 5
refillAmount = 100
tickSize How long each refill "tick" should last. This is a FiniteDuration.

tickSize = 10 seconds
tickSize = 15 minutes
tickSize = 1 hour
tickZero When the first tick started. This defaults to zero millis in the Java epoch. You usually don't need to change this value unless you want to align refill ticks to a time in your own timezone.

tickZero = 0

§CorrelationIdExtractor configuration

The correlation id extractor creates a correlation identifier when a HTTP request comes in, which is useful for monitoring and debugging. The correlation id is made available to the rest of the system as an implicit parameter in methods – this provides a convenient way to trace the passage of a single request across multiple threads and actors.

Note that correlation ids should never play a part as meaningful input in a system, i.e. a correlation id should not be used as primary key, UUID replacement, or a cryptographic nonce. This is because correlation ids are often predictable, may leak internal information such as the generator’s MAC address, and make no guarantees of cryptographic randomness.

The correlation id extractor can use any information in the request to create a correlation id, but the correlation id should be unique throughout the entire user quotas system. This means that any custom information that may be duplicated between instances (i.e. the HTTP request id) should be combined with unique values to form a good correlation id. A CorrelationIdExtractor can also extract custom headers from the HTTP request if a pre-existing correlation id needs to be passed through.

The underlying interface is play.quota.sapi.correlation.CorrelationIdExtractor:

trait CorrelationIdExtractor {
  def correlationIdForRequest(rh: RequestHeader): CorrelationId
}

Note that CorrelationIdExtractor returns CorrelationId immediately rather than through Future[CorrelationId], and so is a blocking interface. This is because correlation id generation should be fast and unique, and should not rely on any external blocking operation.

The correlation id type is com.lightbend.correlation.CorrelationId which is a marker trait with no implementation. The toString() method is used to indicate the value used as the correlation id.

There are three concrete implementations of CorrelationId:

com.lightbend.correlation.StringCorrelationId Wraps a String. Implements `java.io.Serializable` so it can be used with Java Serialization.

To create a `StringCorrelationId` instance, call `new StringCorrelationId(s:String)`.

The `toString()` returns the string itself.

com.lightbend.correlation.Base62CorrelationId Wraps an array of Byte. Implements `java.io.Serializable` so it can be used with Java Serialization.

To create a `Base62CorrelationId` instance by hand, use `new Base62CorrelationId(bytes:Array[Byte])`.

The `toString()` method returns a String in base62 format representing the contents of the array.

com.lightbend.correlation.NoCorrelationId A sentinel that indicates no correlation id is in use. Implements `java.io.Serializable` so it can be used with Java Serialization.

To reference the NoCorrelationId instance, use the `CorrelationId.none` method.

The `toString()` method returns "NoCorrelationId".

To filter out a correlation id in Scala which could be `NoCorrelationId`, use the `CorrelationId.unapply` method:

``` scala Option[CorrelationId] maybeCorrelationId = CorrelationId.unapply(possiblyNoCorrelationId) ```

In Java, the corresponding method for filtering out `NoCorrelationId` is `CorrelationId.of()`:

``` java Optional optCorrelationId = CorrelationId.of(correlationId) ```

`NoCorrelationId` can be provided implicitly by adding the following:

import com.lightbend.correlation.Implicits._
    

You can also create your own custom subclass of CorrelationId. The equals() and hashCode methods should be overridden so that underlying fields are matched, and the toString() method should return the output value, as correlation id should be suitable in logging messages.

§CorrelationIdExtractor noId type

The “noId” type always returns com.lightbend.correlation.NoCorrelationId. This should be used if a correlation id is not desired in user quota operation.

This is the default correlation id extractor if a specific type is not selected.

<path to correlationIdExtractor configuration> {
  type = noId  
}

§CorrelationIdExtractor randomId type

The “randomId” type returns a concatenation of System.nanoTime and Random.nextInt as a StringCorrelationId.

<path to correlationIdExtractor configuration> {
  type = randomId  
}

§CorrelationIdExtractor headerId type

The “headerId” type takes a “headerName” parameter and queries the incoming request for the value of that header. This is useful in situations where a correlation id was produced outside the system and must be propagated from the request through the result. “X-Correlation-Id” is the default header name, but other common header names for correlation ids are “X-Trace-Id” and “X-Request-Id”.

<path to correlationIdExtractor configuration> {
  type = headerId
  headerName = "X-Correlation-Id"
}
headerName The name of the header to use as the correlation id.

headerName = "X-Correlation-Id"

§CorrelationIdExtractor flakeId type

The “flakeId” type uses a flake id generator to provide a decentralized 128-bit (16 bytes) k-ordered id, meaning that ids are unique across nodes without co-ordination. Flake ids are intentionally predictable, are lexically time ordered, and fast to generate. Internally, flake ids consist of a timestamp of milliseconds since epoch, a worker id, and an incremented sequence number.

The underlying type is play.quota.sapi.correlation.FlakeIdExtractor.

<path to correlationIdExtractor configuration> {
  type = "flakeId"
}

Note that the output to User Quotas is a Base62CorrelationId and there is no direct connection between com.lightbend.flake.FlakeId and Base62CorrelationId, although in both classes, the toString method will write out the contents of the byte array as a base62 encoded string for convenience. To create a Base62CorrelationId from a FlakeId by hand, use new Base62CorrelationId(flakeId.bytes).

The flake id is generated through an underlying com.lightbend.flake.FlakeIdGenerator, which is configured as a singleton located at quota.flakeIdGenerator.default to prevent the possibility of id collisions between duplicate id generators.

In most cases, configuration will not be needed, but the worker id of the FlakeIdGenerator can be set manually. If the workerId is not set, the default is the host’s MAC address, using com.lightbend.flake.WorkerId.preferredInetAddress.

quota.flakeIdGenerator.default {
  workerId = 0x1234ABCD
}

§UserExtractor configuration

The user extractor determines how an individual user is selected when an HTTP request comes in. The user picker can use any information in the request to pick an user identifier.

Scala API:

package play.quota.sapi.user
trait UserExtractor {
  def userForRequest(rh: RequestHeader)(implicit correlationId: CorrelationId): Future[Option[User]]
}

Java API:

public interface UserExtractor {
    CompletionStage<Optional<User>> userForRequest(Http.RequestHeader rh, CorrelationId correlationId);
}

You can configure a UserExtractor either as a named binding or as a child binding within the QuotaAction configuration. Named bindings look like this:

quota.userExtractor.<binding name> { <configuration> }

It is very common to define your own user extractor type. User Quotas comes with two example types, but you should feel free to define your own custom type.

§UserExtractor “ipAddress” type

The “ipAddress” type uses the IP address from each request as the User to apply a quota to. The IP address is converted to its canonical form before being used. The underlying type is play.quota.sapi.user.IpAddressExtractor.

<path to UserExtractor configuration> {
  type = ipAddress
}

This type takes no extra configuration, so the following shortcut can be used.

<path to UserExtractor configuration>.type = ipAddress

§UserExtractor “session” type

The “session” type uses a value in the Play session as the user. The underlying type is play.quota.sapi.user.SessionExtractor

<path to UserExtractor configuration> {
  type = session
  sessionName = <the key in the Play session>
}
sessionName The name of the key to use in the Play session cookie.

sessionName = "userName"

§ResultFormatter configuration

A ResultFormatter formats outgoing HTTP responses after they have been processed by User Quotas. It is responsible for setting headers and status values.

Scala API:

package play.quota.sapi.format
trait ResultFormatter {
  def formatGranted(now: Instant, user: User, requestCost: Int, balance: Option[Int], quota: Quota, result: Result)(implicit correlationId: CorrelationId): Future[Result]
  def formatDenied(now: Instant, user: User, requestCost: Int, balance: Option[Int], quota: Quota)(implicit correlationId: CorrelationId): Future[Result]
}

Java API:

package play.quota.japi.format;
public interface ResultFormatter {
    CompletionStage<Result> formatGranted(Instant now, User user, int requestCost, OptionalInt balance, Quota quota, Result result, CorrelationId correlationId);
    CompletionStage<Result> formatDenied(Instant now, User user, int requestCost, OptionalInt balance, Quota quota, CorrelationId correlationId);
}

You can configure a ResultFormatter either as a named binding or as a child binding within the QuotaAction configuration. Named bindings look like this:

quota.resultFormatter.<binding name> { <configuration> }

User Quotas comes with two types of built-in formatter. You can also create your own custom types.

§ResultFormatter “rest” type

The “rest” result formatter follows the Twitter rate limit convention defined in Twitter API Rate Limits documentation. You will get Twitter conventions with the default options.

<path to ResultFormatter configuration> {
  type = rest
}

You can also change it to use Github headers by varying a couple of properties.

<path to ResultFormatter configuration> {
  type = rest
  limitHeaderName = "X-RateLimit-Limit"
  remainingHeaderName = "X-RateLimit-Remaining"
  resetHeaderName = "X-RateLimit-Reset"
}

Most of the header and status values are configurable.

zeroBlockedStatus Status to return when an user is blocked due to a zero rate limit. The default is 403.

zeroBlockedStatus = 403
limitedBlockedStatus Status to return when an user is blocked due to being temporarily rate limited. The default is 429.

limitedBlockedStatus = 429
limitHeaderName The header to use for reporting the maximum rate limit. The default is shown below.

limitHeaderName = "X-Rate-Limit-Limit"
remainingHeaderName The header to use for reporting the remaining tokens. The default is shown below.

remainingHeaderName = "X-Rate-Limit-Remaining"
resetHeaderName The header to use for reporting the next reset time (in seconds since the epoch). The default is shown below.

resetHeaderName = "X-Rate-Limit-Reset"
correlationIdHeaderName The header to use for reporting the correlation id. The default is shown below. If the correlation id type is `NoCorrelationId` or the correlationIdHeaderName is set to null, then the header is not included.

correlationIdHeaderName = "X-Correlation-Id"

§ResultFormatter “minimal” type

The “minimal” result formatter does nothing until a resource is blocked, at which point it returns a 429 Too Many Requests.

<path to ResultFormatter configuration> {
  type = minimal
}

The minimal type takes no extra configuration so the following shortcut can be used.

<path to ResultFormatter configuration>.type = minimal