Skip to content

Commit

Permalink
services/horizon/internal/httpx: Exempt SSE requests from rate limiti…
Browse files Browse the repository at this point in the history
…ng middleware (#4163)

Rate limiting is applied on horizon http requests via a middleware. However, for streaming requests, rate limiting is also applied on each execution of the event handler.
  • Loading branch information
tamirms authored Jan 6, 2022
1 parent 3f6217e commit bae2749
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 1 deletion.
14 changes: 13 additions & 1 deletion services/horizon/internal/httpx/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/stellar/go/services/horizon/internal/db2/history"
"github.com/stellar/go/services/horizon/internal/ledger"
"github.com/stellar/go/services/horizon/internal/paths"
"github.com/stellar/go/services/horizon/internal/render"
"github.com/stellar/go/services/horizon/internal/render/sse"
"github.com/stellar/go/services/horizon/internal/txsub"
"github.com/stellar/go/support/db"
Expand Down Expand Up @@ -97,7 +98,18 @@ func (r *Router) addMiddleware(config *RouterConfig,
r.Use(c.Handler)

if rateLimitter != nil {
r.Use(rateLimitter.RateLimit)
r.Use(func(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Exempt streaming requests from rate limits via the HTTP middleware
// because rate limiting for streaming requests are already implemented in
// StreamHandler.ServeStream().
if render.Negotiate(r) == render.MimeEventStream {
handler.ServeHTTP(w, r)
return
}
rateLimitter.RateLimit(handler).ServeHTTP(w, r)
})
})
}

if config.PrimaryDBSession != nil {
Expand Down
9 changes: 9 additions & 0 deletions services/horizon/internal/middleware_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,16 +82,25 @@ func (suite *RateLimitMiddlewareTestSuite) TestRateLimit_LimitHeaders() {

// Sets X-RateLimit-Remaining headers correctly.
func (suite *RateLimitMiddlewareTestSuite) TestRateLimit_RemainingHeaders() {
// test that SSE requests are ignored
for i := 0; i < 10; i++ {
w := suite.rh.Get("/", test.RequestHelperStreaming)
assert.Equal(suite.T(), "", w.Header().Get("X-RateLimit-Remaining"))
assert.NotEqual(suite.T(), http.StatusTooManyRequests, w.Code)
}

for i := 0; i < 10; i++ {
w := suite.rh.Get("/")
expected := 10 - (i + 1)
assert.Equal(suite.T(), strconv.Itoa(expected), w.Header().Get("X-RateLimit-Remaining"))
assert.NotEqual(suite.T(), http.StatusTooManyRequests, w.Code)
}

// confirm remaining stays at 0
for i := 0; i < 10; i++ {
w := suite.rh.Get("/")
assert.Equal(suite.T(), "0", w.Header().Get("X-RateLimit-Remaining"))
assert.Equal(suite.T(), http.StatusTooManyRequests, w.Code)
}
}

Expand Down

0 comments on commit bae2749

Please sign in to comment.