Browse Source

exempt a configurable number of MARKREAD commands from fakelag

tags/v2.11.0-rc1
Shivaram Lingamneni 1 year ago
parent
commit
7ad31497c2
6 changed files with 58 additions and 11 deletions
  1. 8
    0
      default.yaml
  2. 12
    4
      irc/client.go
  3. 12
    0
      irc/config.go
  4. 12
    1
      irc/fakelag.go
  5. 6
    6
      irc/fakelag_test.go
  6. 8
    0
      traditional.yaml

+ 8
- 0
default.yaml View File

@@ -849,6 +849,14 @@ fakelag:
849 849
     # sending any commands:
850 850
     cooldown: 2s
851 851
 
852
+    # exempt a certain number of command invocations per session from fakelag;
853
+    # this is to speed up "resynchronization" of client state during reattach
854
+    command-budgets:
855
+        "CHATHISTORY": 16
856
+        "MARKREAD":    16
857
+        "MONITOR":     1
858
+        "WHO":         4
859
+
852 860
 # the roleplay commands are semi-standardized extensions to IRC that allow
853 861
 # sending and receiving messages from pseudo-nicknames. this can be used either
854 862
 # for actual roleplaying, or for bridging IRC with other protocols.

+ 12
- 4
irc/client.go View File

@@ -668,12 +668,21 @@ func (client *Client) run(session *Session) {
668 668
 			}
669 669
 		}
670 670
 
671
+		msg, err := ircmsg.ParseLineStrict(line, true, MaxLineLen)
672
+		// XXX defer processing of command error parsing until after fakelag
673
+
671 674
 		if client.registered {
672
-			touches := session.deferredFakelagCount + 1
675
+			// apply deferred fakelag
676
+			for i := 0; i < session.deferredFakelagCount; i++ {
677
+				session.fakelag.Touch("")
678
+			}
673 679
 			session.deferredFakelagCount = 0
674
-			for i := 0; i < touches; i++ {
675
-				session.fakelag.Touch()
680
+			// touch for the current command
681
+			var command string
682
+			if err == nil {
683
+				command = msg.Command
676 684
 			}
685
+			session.fakelag.Touch(command)
677 686
 		} else {
678 687
 			// DoS hardening, #505
679 688
 			session.registrationMessages++
@@ -683,7 +692,6 @@ func (client *Client) run(session *Session) {
683 692
 			}
684 693
 		}
685 694
 
686
-		msg, err := ircmsg.ParseLineStrict(line, true, MaxLineLen)
687 695
 		if err == ircmsg.ErrorLineIsEmpty {
688 696
 			continue
689 697
 		} else if err == ircmsg.ErrorTagsTooLong {

+ 12
- 0
irc/config.go View File

@@ -524,6 +524,7 @@ type FakelagConfig struct {
524 524
 	BurstLimit        uint `yaml:"burst-limit"`
525 525
 	MessagesPerWindow uint `yaml:"messages-per-window"`
526 526
 	Cooldown          time.Duration
527
+	CommandBudgets    map[string]int `yaml:"command-budgets"`
527 528
 }
528 529
 
529 530
 type TorListenersConfig struct {
@@ -1428,6 +1429,17 @@ func LoadConfig(filename string) (config *Config, err error) {
1428 1429
 	}
1429 1430
 	config.Server.capValues[caps.Languages] = config.languageManager.CapValue()
1430 1431
 
1432
+	if len(config.Fakelag.CommandBudgets) != 0 {
1433
+		// normalize command names to uppercase:
1434
+		commandBudgets := make(map[string]int, len(config.Fakelag.CommandBudgets))
1435
+		for command, budget := range config.Fakelag.CommandBudgets {
1436
+			commandBudgets[strings.ToUpper(command)] = budget
1437
+		}
1438
+		config.Fakelag.CommandBudgets = commandBudgets
1439
+	} else {
1440
+		config.Fakelag.CommandBudgets = nil
1441
+	}
1442
+
1431 1443
 	if config.Server.Relaymsg.Enabled {
1432 1444
 		for _, char := range protocolBreakingNameCharacters {
1433 1445
 			if strings.ContainsRune(config.Server.Relaymsg.Separators, char) {

+ 12
- 1
irc/fakelag.go View File

@@ -5,6 +5,8 @@ package irc
5 5
 
6 6
 import (
7 7
 	"time"
8
+
9
+	"github.com/ergochat/ergo/irc/utils"
8 10
 )
9 11
 
10 12
 // fakelag is a system for artificially delaying commands when a user issues
@@ -36,6 +38,10 @@ type Fakelag struct {
36 38
 
37 39
 func (fl *Fakelag) Initialize(config FakelagConfig) {
38 40
 	fl.config = config
41
+	// XXX don't share mutable member CommandBudgets:
42
+	if config.CommandBudgets != nil {
43
+		fl.config.CommandBudgets = utils.CopyMap(config.CommandBudgets)
44
+	}
39 45
 	fl.nowFunc = time.Now
40 46
 	fl.sleepFunc = time.Sleep
41 47
 	fl.state = FakelagBursting
@@ -58,11 +64,16 @@ func (fl *Fakelag) Unsuspend() {
58 64
 }
59 65
 
60 66
 // register a new command, sleep if necessary to delay it
61
-func (fl *Fakelag) Touch() {
67
+func (fl *Fakelag) Touch(command string) {
62 68
 	if !fl.config.Enabled {
63 69
 		return
64 70
 	}
65 71
 
72
+	if budget, ok := fl.config.CommandBudgets[command]; ok && budget > 0 {
73
+		fl.config.CommandBudgets[command] = budget - 1
74
+		return
75
+	}
76
+
66 77
 	now := fl.nowFunc()
67 78
 	// XXX if lastTouch.IsZero(), treat it as "very far in the past", which is fine
68 79
 	elapsed := now.Sub(fl.lastTouch)

+ 6
- 6
irc/fakelag_test.go View File

@@ -60,7 +60,7 @@ func TestFakelag(t *testing.T) {
60 60
 	window, _ := time.ParseDuration("1s")
61 61
 	fl, mt := newFakelagForTesting(window, 3, 2, window)
62 62
 
63
-	fl.Touch()
63
+	fl.Touch("")
64 64
 	slept, _ := mt.lastSleep()
65 65
 	if slept {
66 66
 		t.Fatalf("should not have slept")
@@ -69,7 +69,7 @@ func TestFakelag(t *testing.T) {
69 69
 	interval, _ := time.ParseDuration("100ms")
70 70
 	for i := 0; i < 2; i++ {
71 71
 		mt.pause(interval)
72
-		fl.Touch()
72
+		fl.Touch("")
73 73
 		slept, _ := mt.lastSleep()
74 74
 		if slept {
75 75
 			t.Fatalf("should not have slept")
@@ -77,7 +77,7 @@ func TestFakelag(t *testing.T) {
77 77
 	}
78 78
 
79 79
 	mt.pause(interval)
80
-	fl.Touch()
80
+	fl.Touch("")
81 81
 	if fl.state != FakelagThrottled {
82 82
 		t.Fatalf("should be throttled")
83 83
 	}
@@ -91,7 +91,7 @@ func TestFakelag(t *testing.T) {
91 91
 	}
92 92
 
93 93
 	// send another message without a pause; we should have to sleep for 500 msec
94
-	fl.Touch()
94
+	fl.Touch("")
95 95
 	if fl.state != FakelagThrottled {
96 96
 		t.Fatalf("should be throttled")
97 97
 	}
@@ -102,7 +102,7 @@ func TestFakelag(t *testing.T) {
102 102
 	}
103 103
 
104 104
 	mt.pause(interval * 6)
105
-	fl.Touch()
105
+	fl.Touch("")
106 106
 	if fl.state != FakelagThrottled {
107 107
 		t.Fatalf("should still be throttled")
108 108
 	}
@@ -112,7 +112,7 @@ func TestFakelag(t *testing.T) {
112 112
 	}
113 113
 
114 114
 	mt.pause(window * 2)
115
-	fl.Touch()
115
+	fl.Touch("")
116 116
 	if fl.state != FakelagBursting {
117 117
 		t.Fatalf("should be bursting again")
118 118
 	}

+ 8
- 0
traditional.yaml View File

@@ -821,6 +821,14 @@ fakelag:
821 821
     # sending any commands:
822 822
     cooldown: 2s
823 823
 
824
+    # exempt a certain number of command invocations per session from fakelag;
825
+    # this is to speed up "resynchronization" of client state during reattach
826
+    command-budgets:
827
+        "CHATHISTORY": 16
828
+        "MARKREAD":    16
829
+        "MONITOR":     1
830
+        "WHO":         4
831
+
824 832
 # the roleplay commands are semi-standardized extensions to IRC that allow
825 833
 # sending and receiving messages from pseudo-nicknames. this can be used either
826 834
 # for actual roleplaying, or for bridging IRC with other protocols.

Loading…
Cancel
Save