summaryrefslogtreecommitdiff
path: root/xS/main.go
diff options
context:
space:
mode:
Diffstat (limited to 'xS/main.go')
-rw-r--r--xS/main.go84
1 files changed, 64 insertions, 20 deletions
diff --git a/xS/main.go b/xS/main.go
index bcf7bc6..c3b2875 100644
--- a/xS/main.go
+++ b/xS/main.go
@@ -761,7 +761,7 @@ var (
config simpleConfig // server configuration
serverName string // our server name
- pingInterval uint // ping interval in seconds
+ pingInterval time.Duration // ping interval
maxConnections int // max connections allowed or 0
motd []string // MOTD (none if empty)
operators = make(map[string]bool) // TLS cert. fingerprints for IRCops
@@ -773,7 +773,7 @@ var (
prepared = make(chan preparedEvent)
reads = make(chan readEvent)
writes = make(chan writeEvent)
- timeouts = make(chan *client)
+ timers = make(chan func())
tlsConf *tls.Config
clients = make(map[*client]bool)
@@ -962,12 +962,7 @@ func (c *client) kill(reason string) {
_ = c.transport.Close()
}
- // Clean up the goroutine, although a spurious event may still be sent.
- // TODO: Other timers if needed.
- if c.killTimer != nil {
- c.killTimer.Stop()
- }
-
+ c.cancelTimers()
delete(clients, c)
}
@@ -994,9 +989,7 @@ func (c *client) closeLink(reason string) {
c.closing = true
c.unregister(reason)
- c.killTimer = time.AfterFunc(3*time.Second, func() {
- timeouts <- c
- })
+ c.setKillTimer()
}
func (c *client) inMaskList(masks []string) bool {
@@ -1025,7 +1018,57 @@ func (c *client) getTLSCertFingerprint() string {
// --- Timers ------------------------------------------------------------------
-// TODO
+// Free the resources of timers that haven't fired yet and for timers that are
+// in between firing and being collected by the event loop, mark that the event
+// should not be acted upon.
+func (c *client) cancelTimers() {
+ for _, timer := range []**time.Timer{
+ &c.killTimer, &c.timeoutTimer, &c.pingTimer,
+ } {
+ if *timer != nil {
+ (*timer).Stop()
+ *timer = nil
+ }
+ }
+}
+
+// Arrange for a function to be called later from the main goroutine.
+func (c *client) setTimer(timer **time.Timer, delay time.Duration, cb func()) {
+ c.cancelTimers()
+
+ var identityCapture *time.Timer
+ identityCapture = time.AfterFunc(delay, func() {
+ timers <- func() {
+ // The timer might have been cancelled or even replaced.
+ // When the client is killed, this will be nil.
+ if *timer == identityCapture {
+ cb()
+ }
+ }
+ })
+
+ *timer = identityCapture
+}
+
+func (c *client) setKillTimer() {
+ c.setTimer(&c.killTimer, pingInterval, func() {
+ c.kill("Timeout")
+ })
+}
+
+func (c *client) setTimeoutTimer() {
+ c.setTimer(&c.timeoutTimer, pingInterval, func() {
+ c.closeLink(fmt.Sprintf("Ping timeout: >%d seconds",
+ pingInterval/time.Second))
+ })
+}
+
+func (c *client) setPingTimer() {
+ c.setTimer(&c.pingTimer, pingInterval, func() {
+ c.sendf("PING :%s", serverName)
+ c.setTimeoutTimer()
+ })
+}
// --- IRC command handling ----------------------------------------------------
@@ -1451,7 +1494,7 @@ func ircHandlePONG(msg *message, c *client) {
}
// Set a new timer to send another PING
- // TODO
+ c.setPingTimer()
}
func ircHandleQUIT(msg *message, c *client) {
@@ -2817,6 +2860,7 @@ func (c *client) onPrepared(host string, isTLS bool) {
// If we tried to send any data before now, we would need to flushSendQ.
go read(c)
c.reading = true
+ c.setPingTimer()
}
// Handle the results from trying to read from the client connection.
@@ -2996,6 +3040,9 @@ func processOneEvent() {
case <-quitTimer:
forceQuit("timeout")
+ case callback := <-timers:
+ callback()
+
case conn := <-conns:
if maxConnections > 0 && len(clients) >= maxConnections {
log.Println("connection limit reached, refusing connection")
@@ -3026,6 +3073,9 @@ func processOneEvent() {
clients[c] = true
go prepare(c)
+ // The TLS autodetection in prepare needs to have a timeout.
+ c.setKillTimer()
+
case ev := <-prepared:
log.Println("client is ready:", ev.host)
if _, ok := clients[ev.client]; ok {
@@ -3043,12 +3093,6 @@ func processOneEvent() {
if _, ok := clients[ev.client]; ok {
ev.client.onWrite(ev.written, ev.err)
}
-
- case c := <-timeouts:
- if _, ok := clients[c]; ok {
- log.Println("client timeouted")
- c.kill("TODO")
- }
}
}
@@ -3147,7 +3191,7 @@ func ircParseConfig() error {
} else if u < 1 {
return "the value is out of range"
} else {
- pingInterval = uint(u)
+ pingInterval = time.Second * time.Duration(u)
}
return ""
})