From 24f1c4413a6e26aa05c642a462ce77d70d478e92 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C5=99emysl=20Janouch?= Date: Mon, 30 Jul 2018 10:04:05 +0200 Subject: [PATCH] hid: use time.Time and time.Duration It improves the code significantly over explicit int64 conversions. Despite carrying unnecessary timezone information, time.Time also carries a monotonic reading of time, which allows for more precise measurement of time differences. --- hid/main.go | 87 ++++++++++++++++++++++++++--------------------------- 1 file changed, 42 insertions(+), 45 deletions(-) diff --git a/hid/main.go b/hid/main.go index ff6a84a..1c72fca 100644 --- a/hid/main.go +++ b/hid/main.go @@ -46,11 +46,6 @@ const ( projectVersion = "0" ) -// TODO: Consider using time.Time directly instead of storing Unix epoch -// timestamps with nanosecond precision. Despite carrying unnecessary timezone -// information, it also carries a monotonic reading of the time, which allows -// for more precise measurement of time differences. - // --- Utilities --------------------------------------------------------------- // Split a string by a set of UTF-8 delimiters, optionally ignoring empty items. @@ -207,23 +202,23 @@ func readConfigFile(name string, output interface{}) error { // --- Rate limiter ------------------------------------------------------------ type floodDetector struct { - interval uint // interval for the limit in seconds - limit uint // maximum number of events allowed - timestamps []int64 // timestamps of last events - pos uint // index of the oldest event + interval time.Duration // interval for the limit in seconds + limit uint // maximum number of events allowed + timestamps []time.Time // timestamps of last events + pos uint // index of the oldest event } -func newFloodDetector(interval, limit uint) *floodDetector { +func newFloodDetector(interval time.Duration, limit uint) *floodDetector { return &floodDetector{ interval: interval, limit: limit, - timestamps: make([]int64, limit+1), + timestamps: make([]time.Time, limit+1), pos: 0, } } func (fd *floodDetector) check() bool { - now := time.Now().UnixNano() + now := time.Now() fd.timestamps[fd.pos] = now fd.pos++ @@ -232,9 +227,9 @@ func (fd *floodDetector) check() bool { } var count uint - begin := now - int64(time.Second)*int64(fd.interval) + begin := now.Add(-fd.interval) for _, ts := range fd.timestamps { - if ts >= begin { + if ts.After(begin) { count++ } } @@ -402,11 +397,11 @@ type client struct { closing bool // whether we're closing the connection killTimer *time.Timer // hard kill timeout - opened int64 // when the connection was opened - nSentMessages uint // number of sent messages total - sentBytes int // number of sent bytes total - nReceivedMessages uint // number of received messages total - receivedBytes int // number of received bytes total + opened time.Time // when the connection was opened + nSentMessages uint // number of sent messages total + sentBytes int // number of sent bytes total + nReceivedMessages uint // number of received messages total + receivedBytes int // number of received bytes total hostname string // hostname or IP shown to the network port string // port of the peer as a string @@ -427,9 +422,9 @@ type client struct { mode uint // user's mode awayMessage string // away message - lastActive int64 // last PRIVMSG, to get idle time + lastActive time.Time // last PRIVMSG, to get idle time invites map[string]bool // channel invitations by operators - antiflood floodDetector // flood detector + antiflood *floodDetector // flood detector } // --- Channels ---------------------------------------------------------------- @@ -450,15 +445,15 @@ const ( ) type channel struct { - name string // channel name - modes uint // channel modes - key string // channel key - userLimit int // user limit or -1 - created int64 // creation time + name string // channel name + modes uint // channel modes + key string // channel key + userLimit int // user limit or -1 + created time.Time // creation time - topic string // channel topic - topicWho string // who set the topic - topicTime int64 // when the topic was set + topic string // channel topic + topicWho string // who set the topic + topicTime time.Time // when the topic was set userModes map[*client]uint // modes for all channel users @@ -557,7 +552,7 @@ type writeEvent struct { // XXX: Beware that maps with identifier keys need to be indexed correctly. // We might want to enforce accessor functions for users and channels. var ( - started int64 // when has the server been started + started time.Time // when has the server been started users map[string]*client // maps nicknames to clients channels map[string]*channel // maps channel names to data @@ -616,7 +611,7 @@ func initiateQuit() { func ircChannelCreate(name string) *channel { ch := &channel{ name: name, - created: time.Now().UnixNano(), + created: time.Now(), userLimit: -1, } channels[ircToCanon(name)] = ch @@ -941,7 +936,7 @@ func (c *client) tryFinishRegistration() { c.sendReply(RPL_YOURHOST, serverName, projectVersion) // The purpose of this message eludes me. - c.sendReply(RPL_CREATED, time.Unix(started, 0).Format("Mon, 02 Jan 2006")) + c.sendReply(RPL_CREATED, started.Format("Mon, 02 Jan 2006")) c.sendReply(RPL_MYINFO, serverName, projectVersion, ircSupportedUserModes, ircSupportedChanModes) @@ -1691,8 +1686,7 @@ func ircHandleMODE(msg *message, c *client) { if len(msg.params) < 2 { _, present := ch.userModes[c] c.sendReply(RPL_CHANNELMODEIS, target, ch.getMode(present)) - c.sendReply(RPL_CREATIONTIME, - target, ch.created/int64(time.Second)) + c.sendReply(RPL_CREATIONTIME, target, ch.created.Unix()) } else { ircHandleChanModeChange(c, ch, msg.params[1:]) } @@ -1752,8 +1746,7 @@ func ircHandleUserMessage(msg *message, c *client, func ircHandlePRIVMSG(msg *message, c *client) { ircHandleUserMessage(msg, c, "PRIVMSG", true /* allowAwayReply */) - // Let's not care too much about success or failure. - c.lastActive = time.Now().UnixNano() + c.lastActive = time.Now() } func ircHandleNOTICE(msg *message, c *client) { @@ -1980,7 +1973,7 @@ func ircSendWHOISReply(c, target *client) { c.sendReply(RPL_WHOISOPERATOR, nick) } c.sendReply(RPL_WHOISIDLE, nick, - (time.Now().UnixNano()-target.lastActive)/int64(time.Second)) + time.Now().Sub(target.lastActive)/time.Second) if target.awayMessage != "" { c.sendReply(RPL_AWAY, nick, target.awayMessage) } @@ -2073,7 +2066,7 @@ func ircSendRPLTOPIC(c *client, ch *channel) { } else { c.sendReply(RPL_TOPIC, ch.name, ch.topic) c.sendReply(RPL_TOPICWHOTIME, - ch.name, ch.topicWho, ch.topicTime/int64(time.Second)) + ch.name, ch.topicWho, ch.topicTime.Unix()) } } @@ -2109,7 +2102,7 @@ func ircHandleTOPIC(msg *message, c *client) { ch.topic = msg.params[1] ch.topicWho = fmt.Sprintf("%s@%s@%s", c.nickname, c.username, c.hostname) - ch.topicTime = time.Now().UnixNano() + ch.topicTime = time.Now() message := fmt.Sprintf(":%s!%s@%s TOPIC %s :%s", c.nickname, c.username, c.hostname, target, ch.topic) @@ -2418,7 +2411,7 @@ func ircHandleStatsLinks(c *client, msg *message) { len(client.sendQ), // sendq client.nSentMessages, client.sentBytes/1024, client.nReceivedMessages, client.receivedBytes/1024, - (time.Now().UnixNano()-client.opened)/int64(time.Second)) + time.Now().Sub(client.opened)/time.Second) } } @@ -2440,7 +2433,7 @@ func init() { } func ircHandleStatsUptime(c *client) { - uptime := (time.Now().UnixNano() - started) / int64(time.Second) + uptime := time.Now().Sub(started) / time.Second days := uptime / 60 / 60 / 24 hours := (uptime % (60 * 60 * 24)) / 60 / 60 @@ -2811,10 +2804,13 @@ func processOneEvent() { } c := &client{ - transport: conn, - address: address, - hostname: host, - port: port, + transport: conn, + address: address, + hostname: host, + port: port, + capVersion: 301, + // TODO: Make this configurable and more fine-grained. + antiflood: newFloodDetector(10*time.Second, 20), } clients[c] = true go prepare(c) @@ -2875,6 +2871,7 @@ func main() { log.Fatalln(err) } + started = time.Now() go accept(listener) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)