速率限制HTTP请求(通过http.HandlerFunc中间件)

eli*_*rar 12 http go

我正在寻找一小部分限速中间件:

  1. 允许我为每个远程IP设置合理的速率(例如,10 req/s)
  2. 可能(但不一定)允许爆发
  3. 丢弃(关闭?)超过速率的连接并返回HTTP 429

然后,我可以围绕身份验证路由或其他可能容易遭受暴力攻击的路由(即使用过期的令牌密码重置URL等).有人粗暴地强制使用16或24字节令牌的可能性非常低,但是进行额外步骤并没有什么坏处.

我查看了https://code.google.com/p/go-wiki/wiki/RateLimiting,但我不确定如何将其与http.Request(s)进行协调.此外,我不确定在任何时间段内我们如何"跟踪"来自给定IP的请求.

理想情况下,我最终得到类似的东西,注意我是在反向代理(nginx)的后面,所以我们正在检查REMOTE_ADDRHTTP头而不是使用r.RemoteAddr:

// Rate-limiting middleware
func rateLimit(h http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {

        remoteIP := r.Header.Get("REMOTE_ADDR")
        for req := range (what here?) {
            // what here?
            // w.WriteHeader(429) and close the request if it exceeds the limit
            // else pass to the next handler in the chain
            h.ServeHTTP(w, r)
        }
}

// Example routes
r.HandleFunc("/login", use(loginForm, rateLimit, csrf)
r.HandleFunc("/form", use(editHandler, rateLimit, csrf)

// Middleware wrapper, for context
func use(h http.HandlerFunc, middleware ...func(http.HandlerFunc) http.HandlerFunc) http.HandlerFunc {
    for _, m := range middleware {
        h = m(h)
    }

    return h
}
Run Code Online (Sandbox Code Playgroud)

我在这里感谢一些指导.

jus*_*nas 11

您链接的速率限制示例是一般的.它使用范围,因为它通过通道获取请求.

这是一个与HTTP请求不同的故事,但这里没有什么真正复杂的.请注意,您不会迭代请求或任何事件的通道 - 分别为每个传入请求调用HandlerFunc .

func rateLimit(h http.HandlerFunc) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        remoteIP := r.Header.Get("REMOTE_ADDR")
        if exceededTheLimit(remoteIP) {
            w.WriteHeader(429)
            // it then returns, not passing the request down the chain
        } else {
            h.ServeHTTP(w, r);
        }
    }       
}
Run Code Online (Sandbox Code Playgroud)

现在,选择存储速率限制计数器的位置取决于您.一种解决方案是简单地使用将IP映射到其请求计数器的全局映射(不要忘记安全的并发访问).但是,您必须知道请求发生了多久以前.

Sergio建议使用Redis.它的键值特性非常适合这种简单的结构,并且您可以免费使用.