mirror of
https://github.com/matrix-org/sliding-sync.git
synced 2025-03-10 13:37:11 +00:00
feat: add rate limiting
The server will wait 1s if clients: - repeat the same request (same `?pos=`) - repeatedly hit `/sync` without a `?pos=`. Both of these failure modes have been seen in the wild. Fixes #93.
This commit is contained in:
parent
8c1ef09d65
commit
afaea53064
@ -5,11 +5,17 @@ import (
|
||||
"fmt"
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/matrix-org/sliding-sync/internal"
|
||||
"github.com/matrix-org/sliding-sync/sync3/caches"
|
||||
)
|
||||
|
||||
// The amount of time to artificially wait if the server detects spamming clients. This time will
|
||||
// be added to responses when the server detects the same request being sent over and over e.g
|
||||
// /sync?pos=5 then /sync?pos=5 over and over. Likewise /sync without a ?pos=.
|
||||
var SpamProtectionInterval = time.Second
|
||||
|
||||
type ConnID struct {
|
||||
UserID string
|
||||
DeviceID string
|
||||
@ -176,7 +182,10 @@ func (c *Conn) OnIncomingRequest(ctx context.Context, req *Request) (resp *Respo
|
||||
if isSameRequest {
|
||||
// this is the 2nd+ time we've seen this request, meaning the client likely retried this
|
||||
// request. Send the response we sent before.
|
||||
logger.Trace().Int64("pos", req.pos).Msg("returning cached response for pos")
|
||||
logger.Trace().Int64("pos", req.pos).Msg("returning cached response for pos, with delay")
|
||||
// apply a small artificial wait to protect the proxy in case this is caused by a buggy
|
||||
// client sending the same request over and over
|
||||
time.Sleep(SpamProtectionInterval)
|
||||
return nextUnACKedResponse, nil
|
||||
} else {
|
||||
logger.Info().Int64("pos", req.pos).Msg("client has resent this pos with different request data")
|
||||
|
@ -77,7 +77,14 @@ func (m *ConnMap) CreateConn(cid ConnID, newConnHandler func() ConnHandler) (*Co
|
||||
conn := m.Conn(cid)
|
||||
if conn != nil {
|
||||
// tear down this connection and fallthrough
|
||||
logger.Trace().Str("conn", cid.String()).Msg("closing connection due to CreateConn called again")
|
||||
isSpamming := conn.lastPos <= 1
|
||||
if isSpamming {
|
||||
// the existing connection has only just been used for one response, and now they are asking
|
||||
// for a new connection. Apply an artificial delay here to stop buggy clients from spamming
|
||||
// /sync without a `?pos=` value.
|
||||
time.Sleep(SpamProtectionInterval)
|
||||
}
|
||||
logger.Trace().Str("conn", cid.String()).Bool("spamming", isSpamming).Msg("closing connection due to CreateConn called again")
|
||||
m.closeConn(conn)
|
||||
}
|
||||
h := newConnHandler()
|
||||
|
@ -363,6 +363,9 @@ func runTestServer(t testutils.TestBenchInterface, v2Server *testV2Server, postg
|
||||
if postgresConnectionString == "" {
|
||||
postgresConnectionString = testutils.PrepareDBConnectionString()
|
||||
}
|
||||
//tests often repeat requests. To ensure tests remain fast, reduce the spam protection limits.
|
||||
sync3.SpamProtectionInterval = time.Millisecond
|
||||
|
||||
metricsEnabled := false
|
||||
maxPendingEventUpdates := 200
|
||||
if len(opts) > 0 {
|
||||
|
Loading…
x
Reference in New Issue
Block a user