patternMinor
Limit HTTP request rate for spray/akka-http
Viewed 0 times
spraylimitraterequestakkahttpfor
Problem
I want to limit the request rate in a spray/akka-http route. I'm not an expert with both scala and spray/akka but learning. So I came up with the following custom directive:
The custom directive takes an instance of an
I'm not quiet happy with the
I also posted a Gist (feel free to share, use and improve).
What do you think of the directive? Anything that could be improved?
trait RequestRateLimiter[T] {
def check(ctx: RequestContext, realIp: Option[RemoteAddress], proxiedIp: Option[RemoteAddress]) : RequestRateResult[T]
}
sealed trait RequestRateResult[T]
case class Pass[T](result: T) extends RequestRateResult[T]
case class Block[T](rejection: Rejection) extends RequestRateResult[T]
def limit[T](requestRateLimiter: RequestRateLimiter[T]): Directive1[T] = {
def remoteAddress: Directive1[Option[RemoteAddress]] = optionalHeaderValuePF { case `Remote-Address`(address) ⇒ address }
def xForwardedFor: Directive1[Option[RemoteAddress]] = optionalHeaderValuePF { case `X-Forwarded-For`(Seq(address, _*)) ⇒ address }
extract(identity) flatMap { ctx =>
remoteAddress.flatMap { realIp =>
xForwardedFor.flatMap { proxiedIp =>
requestRateLimiter.check(ctx, realIp, proxiedIp) match {
case Pass(result) => provide(result)
case Block(rejection) => reject(rejection)
}
}
}
}
}The custom directive takes an instance of an
RequestRateLimiter which keeps track of the request rate based on the RequestContext and RemoteAddress. The RequestRateLimiter then decides if the request should be passed or blocked.I'm not quiet happy with the
RequestRateLimiter because it's too generic. It can be anything that either provides something or rejects.I also posted a Gist (feel free to share, use and improve).
What do you think of the directive? Anything that could be improved?
Solution
I think you could rewrite your limit function like this :
Here are the two main changes that I've done :
Overall, I find it more readable.
def limit[T](requestRateLimiter: RequestRateLimiter[T]) = {
def remoteAddress = optionalHeaderValuePF { case `Remote-Address`(address) ⇒ address }
def xForwardedFor = optionalHeaderValuePF { case `X-Forwarded-For`(Seq(address, _*)) ⇒ address }
for {
ctx provide(result)
case Block(rejection) => reject(rejection)
}
}Here are the two main changes that I've done :
- Changed your flatMaps for a "for comprehension" (which imho is more readable)
- Removed the types on the declaration of the two variables.
Overall, I find it more readable.
Code Snippets
def limit[T](requestRateLimiter: RequestRateLimiter[T]) = {
def remoteAddress = optionalHeaderValuePF { case `Remote-Address`(address) ⇒ address }
def xForwardedFor = optionalHeaderValuePF { case `X-Forwarded-For`(Seq(address, _*)) ⇒ address }
for {
ctx <- extract(identity)
realIp <- remoteAddress
proxiedIp <- xForwardedFor
} yield requestRateLimiter.check(ctx, realIp, proxiedIp) match {
case Pass(result) => provide(result)
case Block(rejection) => reject(rejection)
}
}Context
StackExchange Code Review Q#120733, answer score: 4
Revisions (0)
No revisions yet.