mirror of
https://github.com/ergochat/ergo.git
synced 2024-11-22 11:59:40 +01:00
Merge pull request #1993 from slingamn/fakelag_budget.3
exempt a configurable number of MARKREAD commands from fakelag
This commit is contained in:
commit
0e8f447326
@ -849,6 +849,14 @@ fakelag:
|
|||||||
# sending any commands:
|
# sending any commands:
|
||||||
cooldown: 2s
|
cooldown: 2s
|
||||||
|
|
||||||
|
# exempt a certain number of command invocations per session from fakelag;
|
||||||
|
# this is to speed up "resynchronization" of client state during reattach
|
||||||
|
command-budgets:
|
||||||
|
"CHATHISTORY": 16
|
||||||
|
"MARKREAD": 16
|
||||||
|
"MONITOR": 1
|
||||||
|
"WHO": 4
|
||||||
|
|
||||||
# the roleplay commands are semi-standardized extensions to IRC that allow
|
# the roleplay commands are semi-standardized extensions to IRC that allow
|
||||||
# sending and receiving messages from pseudo-nicknames. this can be used either
|
# sending and receiving messages from pseudo-nicknames. this can be used either
|
||||||
# for actual roleplaying, or for bridging IRC with other protocols.
|
# for actual roleplaying, or for bridging IRC with other protocols.
|
||||||
|
@ -668,12 +668,21 @@ func (client *Client) run(session *Session) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
msg, err := ircmsg.ParseLineStrict(line, true, MaxLineLen)
|
||||||
|
// XXX defer processing of command error parsing until after fakelag
|
||||||
|
|
||||||
if client.registered {
|
if client.registered {
|
||||||
touches := session.deferredFakelagCount + 1
|
// apply deferred fakelag
|
||||||
session.deferredFakelagCount = 0
|
for i := 0; i < session.deferredFakelagCount; i++ {
|
||||||
for i := 0; i < touches; i++ {
|
session.fakelag.Touch("")
|
||||||
session.fakelag.Touch()
|
|
||||||
}
|
}
|
||||||
|
session.deferredFakelagCount = 0
|
||||||
|
// touch for the current command
|
||||||
|
var command string
|
||||||
|
if err == nil {
|
||||||
|
command = msg.Command
|
||||||
|
}
|
||||||
|
session.fakelag.Touch(command)
|
||||||
} else {
|
} else {
|
||||||
// DoS hardening, #505
|
// DoS hardening, #505
|
||||||
session.registrationMessages++
|
session.registrationMessages++
|
||||||
@ -683,7 +692,6 @@ func (client *Client) run(session *Session) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
msg, err := ircmsg.ParseLineStrict(line, true, MaxLineLen)
|
|
||||||
if err == ircmsg.ErrorLineIsEmpty {
|
if err == ircmsg.ErrorLineIsEmpty {
|
||||||
continue
|
continue
|
||||||
} else if err == ircmsg.ErrorTagsTooLong {
|
} else if err == ircmsg.ErrorTagsTooLong {
|
||||||
|
@ -524,6 +524,7 @@ type FakelagConfig struct {
|
|||||||
BurstLimit uint `yaml:"burst-limit"`
|
BurstLimit uint `yaml:"burst-limit"`
|
||||||
MessagesPerWindow uint `yaml:"messages-per-window"`
|
MessagesPerWindow uint `yaml:"messages-per-window"`
|
||||||
Cooldown time.Duration
|
Cooldown time.Duration
|
||||||
|
CommandBudgets map[string]int `yaml:"command-budgets"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type TorListenersConfig struct {
|
type TorListenersConfig struct {
|
||||||
@ -1428,6 +1429,17 @@ func LoadConfig(filename string) (config *Config, err error) {
|
|||||||
}
|
}
|
||||||
config.Server.capValues[caps.Languages] = config.languageManager.CapValue()
|
config.Server.capValues[caps.Languages] = config.languageManager.CapValue()
|
||||||
|
|
||||||
|
if len(config.Fakelag.CommandBudgets) != 0 {
|
||||||
|
// normalize command names to uppercase:
|
||||||
|
commandBudgets := make(map[string]int, len(config.Fakelag.CommandBudgets))
|
||||||
|
for command, budget := range config.Fakelag.CommandBudgets {
|
||||||
|
commandBudgets[strings.ToUpper(command)] = budget
|
||||||
|
}
|
||||||
|
config.Fakelag.CommandBudgets = commandBudgets
|
||||||
|
} else {
|
||||||
|
config.Fakelag.CommandBudgets = nil
|
||||||
|
}
|
||||||
|
|
||||||
if config.Server.Relaymsg.Enabled {
|
if config.Server.Relaymsg.Enabled {
|
||||||
for _, char := range protocolBreakingNameCharacters {
|
for _, char := range protocolBreakingNameCharacters {
|
||||||
if strings.ContainsRune(config.Server.Relaymsg.Separators, char) {
|
if strings.ContainsRune(config.Server.Relaymsg.Separators, char) {
|
||||||
|
@ -5,6 +5,8 @@ package irc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ergochat/ergo/irc/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
// fakelag is a system for artificially delaying commands when a user issues
|
// fakelag is a system for artificially delaying commands when a user issues
|
||||||
@ -36,6 +38,10 @@ type Fakelag struct {
|
|||||||
|
|
||||||
func (fl *Fakelag) Initialize(config FakelagConfig) {
|
func (fl *Fakelag) Initialize(config FakelagConfig) {
|
||||||
fl.config = config
|
fl.config = config
|
||||||
|
// XXX don't share mutable member CommandBudgets:
|
||||||
|
if config.CommandBudgets != nil {
|
||||||
|
fl.config.CommandBudgets = utils.CopyMap(config.CommandBudgets)
|
||||||
|
}
|
||||||
fl.nowFunc = time.Now
|
fl.nowFunc = time.Now
|
||||||
fl.sleepFunc = time.Sleep
|
fl.sleepFunc = time.Sleep
|
||||||
fl.state = FakelagBursting
|
fl.state = FakelagBursting
|
||||||
@ -58,11 +64,16 @@ func (fl *Fakelag) Unsuspend() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// register a new command, sleep if necessary to delay it
|
// register a new command, sleep if necessary to delay it
|
||||||
func (fl *Fakelag) Touch() {
|
func (fl *Fakelag) Touch(command string) {
|
||||||
if !fl.config.Enabled {
|
if !fl.config.Enabled {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if budget, ok := fl.config.CommandBudgets[command]; ok && budget > 0 {
|
||||||
|
fl.config.CommandBudgets[command] = budget - 1
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
now := fl.nowFunc()
|
now := fl.nowFunc()
|
||||||
// XXX if lastTouch.IsZero(), treat it as "very far in the past", which is fine
|
// XXX if lastTouch.IsZero(), treat it as "very far in the past", which is fine
|
||||||
elapsed := now.Sub(fl.lastTouch)
|
elapsed := now.Sub(fl.lastTouch)
|
||||||
|
@ -60,7 +60,7 @@ func TestFakelag(t *testing.T) {
|
|||||||
window, _ := time.ParseDuration("1s")
|
window, _ := time.ParseDuration("1s")
|
||||||
fl, mt := newFakelagForTesting(window, 3, 2, window)
|
fl, mt := newFakelagForTesting(window, 3, 2, window)
|
||||||
|
|
||||||
fl.Touch()
|
fl.Touch("")
|
||||||
slept, _ := mt.lastSleep()
|
slept, _ := mt.lastSleep()
|
||||||
if slept {
|
if slept {
|
||||||
t.Fatalf("should not have slept")
|
t.Fatalf("should not have slept")
|
||||||
@ -69,7 +69,7 @@ func TestFakelag(t *testing.T) {
|
|||||||
interval, _ := time.ParseDuration("100ms")
|
interval, _ := time.ParseDuration("100ms")
|
||||||
for i := 0; i < 2; i++ {
|
for i := 0; i < 2; i++ {
|
||||||
mt.pause(interval)
|
mt.pause(interval)
|
||||||
fl.Touch()
|
fl.Touch("")
|
||||||
slept, _ := mt.lastSleep()
|
slept, _ := mt.lastSleep()
|
||||||
if slept {
|
if slept {
|
||||||
t.Fatalf("should not have slept")
|
t.Fatalf("should not have slept")
|
||||||
@ -77,7 +77,7 @@ func TestFakelag(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mt.pause(interval)
|
mt.pause(interval)
|
||||||
fl.Touch()
|
fl.Touch("")
|
||||||
if fl.state != FakelagThrottled {
|
if fl.state != FakelagThrottled {
|
||||||
t.Fatalf("should be throttled")
|
t.Fatalf("should be throttled")
|
||||||
}
|
}
|
||||||
@ -91,7 +91,7 @@ func TestFakelag(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// send another message without a pause; we should have to sleep for 500 msec
|
// send another message without a pause; we should have to sleep for 500 msec
|
||||||
fl.Touch()
|
fl.Touch("")
|
||||||
if fl.state != FakelagThrottled {
|
if fl.state != FakelagThrottled {
|
||||||
t.Fatalf("should be throttled")
|
t.Fatalf("should be throttled")
|
||||||
}
|
}
|
||||||
@ -102,7 +102,7 @@ func TestFakelag(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mt.pause(interval * 6)
|
mt.pause(interval * 6)
|
||||||
fl.Touch()
|
fl.Touch("")
|
||||||
if fl.state != FakelagThrottled {
|
if fl.state != FakelagThrottled {
|
||||||
t.Fatalf("should still be throttled")
|
t.Fatalf("should still be throttled")
|
||||||
}
|
}
|
||||||
@ -112,7 +112,7 @@ func TestFakelag(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
mt.pause(window * 2)
|
mt.pause(window * 2)
|
||||||
fl.Touch()
|
fl.Touch("")
|
||||||
if fl.state != FakelagBursting {
|
if fl.state != FakelagBursting {
|
||||||
t.Fatalf("should be bursting again")
|
t.Fatalf("should be bursting again")
|
||||||
}
|
}
|
||||||
|
@ -821,6 +821,14 @@ fakelag:
|
|||||||
# sending any commands:
|
# sending any commands:
|
||||||
cooldown: 2s
|
cooldown: 2s
|
||||||
|
|
||||||
|
# exempt a certain number of command invocations per session from fakelag;
|
||||||
|
# this is to speed up "resynchronization" of client state during reattach
|
||||||
|
command-budgets:
|
||||||
|
"CHATHISTORY": 16
|
||||||
|
"MARKREAD": 16
|
||||||
|
"MONITOR": 1
|
||||||
|
"WHO": 4
|
||||||
|
|
||||||
# the roleplay commands are semi-standardized extensions to IRC that allow
|
# the roleplay commands are semi-standardized extensions to IRC that allow
|
||||||
# sending and receiving messages from pseudo-nicknames. this can be used either
|
# sending and receiving messages from pseudo-nicknames. this can be used either
|
||||||
# for actual roleplaying, or for bridging IRC with other protocols.
|
# for actual roleplaying, or for bridging IRC with other protocols.
|
||||||
|
Loading…
Reference in New Issue
Block a user