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.
This commit is contained in:
parent
40370702d4
commit
24f1c4413a
87
hid/main.go
87
hid/main.go
@ -46,11 +46,6 @@ const (
|
|||||||
projectVersion = "0"
|
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 ---------------------------------------------------------------
|
// --- Utilities ---------------------------------------------------------------
|
||||||
|
|
||||||
// Split a string by a set of UTF-8 delimiters, optionally ignoring empty items.
|
// 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 ------------------------------------------------------------
|
// --- Rate limiter ------------------------------------------------------------
|
||||||
|
|
||||||
type floodDetector struct {
|
type floodDetector struct {
|
||||||
interval uint // interval for the limit in seconds
|
interval time.Duration // interval for the limit in seconds
|
||||||
limit uint // maximum number of events allowed
|
limit uint // maximum number of events allowed
|
||||||
timestamps []int64 // timestamps of last events
|
timestamps []time.Time // timestamps of last events
|
||||||
pos uint // index of the oldest event
|
pos uint // index of the oldest event
|
||||||
}
|
}
|
||||||
|
|
||||||
func newFloodDetector(interval, limit uint) *floodDetector {
|
func newFloodDetector(interval time.Duration, limit uint) *floodDetector {
|
||||||
return &floodDetector{
|
return &floodDetector{
|
||||||
interval: interval,
|
interval: interval,
|
||||||
limit: limit,
|
limit: limit,
|
||||||
timestamps: make([]int64, limit+1),
|
timestamps: make([]time.Time, limit+1),
|
||||||
pos: 0,
|
pos: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fd *floodDetector) check() bool {
|
func (fd *floodDetector) check() bool {
|
||||||
now := time.Now().UnixNano()
|
now := time.Now()
|
||||||
fd.timestamps[fd.pos] = now
|
fd.timestamps[fd.pos] = now
|
||||||
|
|
||||||
fd.pos++
|
fd.pos++
|
||||||
@ -232,9 +227,9 @@ func (fd *floodDetector) check() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var count uint
|
var count uint
|
||||||
begin := now - int64(time.Second)*int64(fd.interval)
|
begin := now.Add(-fd.interval)
|
||||||
for _, ts := range fd.timestamps {
|
for _, ts := range fd.timestamps {
|
||||||
if ts >= begin {
|
if ts.After(begin) {
|
||||||
count++
|
count++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -402,11 +397,11 @@ type client struct {
|
|||||||
closing bool // whether we're closing the connection
|
closing bool // whether we're closing the connection
|
||||||
killTimer *time.Timer // hard kill timeout
|
killTimer *time.Timer // hard kill timeout
|
||||||
|
|
||||||
opened int64 // when the connection was opened
|
opened time.Time // when the connection was opened
|
||||||
nSentMessages uint // number of sent messages total
|
nSentMessages uint // number of sent messages total
|
||||||
sentBytes int // number of sent bytes total
|
sentBytes int // number of sent bytes total
|
||||||
nReceivedMessages uint // number of received messages total
|
nReceivedMessages uint // number of received messages total
|
||||||
receivedBytes int // number of received bytes total
|
receivedBytes int // number of received bytes total
|
||||||
|
|
||||||
hostname string // hostname or IP shown to the network
|
hostname string // hostname or IP shown to the network
|
||||||
port string // port of the peer as a string
|
port string // port of the peer as a string
|
||||||
@ -427,9 +422,9 @@ type client struct {
|
|||||||
|
|
||||||
mode uint // user's mode
|
mode uint // user's mode
|
||||||
awayMessage string // away message
|
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
|
invites map[string]bool // channel invitations by operators
|
||||||
antiflood floodDetector // flood detector
|
antiflood *floodDetector // flood detector
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Channels ----------------------------------------------------------------
|
// --- Channels ----------------------------------------------------------------
|
||||||
@ -450,15 +445,15 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type channel struct {
|
type channel struct {
|
||||||
name string // channel name
|
name string // channel name
|
||||||
modes uint // channel modes
|
modes uint // channel modes
|
||||||
key string // channel key
|
key string // channel key
|
||||||
userLimit int // user limit or -1
|
userLimit int // user limit or -1
|
||||||
created int64 // creation time
|
created time.Time // creation time
|
||||||
|
|
||||||
topic string // channel topic
|
topic string // channel topic
|
||||||
topicWho string // who set the topic
|
topicWho string // who set the topic
|
||||||
topicTime int64 // when the topic was set
|
topicTime time.Time // when the topic was set
|
||||||
|
|
||||||
userModes map[*client]uint // modes for all channel users
|
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.
|
// XXX: Beware that maps with identifier keys need to be indexed correctly.
|
||||||
// We might want to enforce accessor functions for users and channels.
|
// We might want to enforce accessor functions for users and channels.
|
||||||
var (
|
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
|
users map[string]*client // maps nicknames to clients
|
||||||
channels map[string]*channel // maps channel names to data
|
channels map[string]*channel // maps channel names to data
|
||||||
@ -616,7 +611,7 @@ func initiateQuit() {
|
|||||||
func ircChannelCreate(name string) *channel {
|
func ircChannelCreate(name string) *channel {
|
||||||
ch := &channel{
|
ch := &channel{
|
||||||
name: name,
|
name: name,
|
||||||
created: time.Now().UnixNano(),
|
created: time.Now(),
|
||||||
userLimit: -1,
|
userLimit: -1,
|
||||||
}
|
}
|
||||||
channels[ircToCanon(name)] = ch
|
channels[ircToCanon(name)] = ch
|
||||||
@ -941,7 +936,7 @@ func (c *client) tryFinishRegistration() {
|
|||||||
|
|
||||||
c.sendReply(RPL_YOURHOST, serverName, projectVersion)
|
c.sendReply(RPL_YOURHOST, serverName, projectVersion)
|
||||||
// The purpose of this message eludes me.
|
// 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,
|
c.sendReply(RPL_MYINFO, serverName, projectVersion,
|
||||||
ircSupportedUserModes, ircSupportedChanModes)
|
ircSupportedUserModes, ircSupportedChanModes)
|
||||||
|
|
||||||
@ -1691,8 +1686,7 @@ func ircHandleMODE(msg *message, c *client) {
|
|||||||
if len(msg.params) < 2 {
|
if len(msg.params) < 2 {
|
||||||
_, present := ch.userModes[c]
|
_, present := ch.userModes[c]
|
||||||
c.sendReply(RPL_CHANNELMODEIS, target, ch.getMode(present))
|
c.sendReply(RPL_CHANNELMODEIS, target, ch.getMode(present))
|
||||||
c.sendReply(RPL_CREATIONTIME,
|
c.sendReply(RPL_CREATIONTIME, target, ch.created.Unix())
|
||||||
target, ch.created/int64(time.Second))
|
|
||||||
} else {
|
} else {
|
||||||
ircHandleChanModeChange(c, ch, msg.params[1:])
|
ircHandleChanModeChange(c, ch, msg.params[1:])
|
||||||
}
|
}
|
||||||
@ -1752,8 +1746,7 @@ func ircHandleUserMessage(msg *message, c *client,
|
|||||||
|
|
||||||
func ircHandlePRIVMSG(msg *message, c *client) {
|
func ircHandlePRIVMSG(msg *message, c *client) {
|
||||||
ircHandleUserMessage(msg, c, "PRIVMSG", true /* allowAwayReply */)
|
ircHandleUserMessage(msg, c, "PRIVMSG", true /* allowAwayReply */)
|
||||||
// Let's not care too much about success or failure.
|
c.lastActive = time.Now()
|
||||||
c.lastActive = time.Now().UnixNano()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func ircHandleNOTICE(msg *message, c *client) {
|
func ircHandleNOTICE(msg *message, c *client) {
|
||||||
@ -1980,7 +1973,7 @@ func ircSendWHOISReply(c, target *client) {
|
|||||||
c.sendReply(RPL_WHOISOPERATOR, nick)
|
c.sendReply(RPL_WHOISOPERATOR, nick)
|
||||||
}
|
}
|
||||||
c.sendReply(RPL_WHOISIDLE, nick,
|
c.sendReply(RPL_WHOISIDLE, nick,
|
||||||
(time.Now().UnixNano()-target.lastActive)/int64(time.Second))
|
time.Now().Sub(target.lastActive)/time.Second)
|
||||||
if target.awayMessage != "" {
|
if target.awayMessage != "" {
|
||||||
c.sendReply(RPL_AWAY, nick, target.awayMessage)
|
c.sendReply(RPL_AWAY, nick, target.awayMessage)
|
||||||
}
|
}
|
||||||
@ -2073,7 +2066,7 @@ func ircSendRPLTOPIC(c *client, ch *channel) {
|
|||||||
} else {
|
} else {
|
||||||
c.sendReply(RPL_TOPIC, ch.name, ch.topic)
|
c.sendReply(RPL_TOPIC, ch.name, ch.topic)
|
||||||
c.sendReply(RPL_TOPICWHOTIME,
|
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.topic = msg.params[1]
|
||||||
ch.topicWho = fmt.Sprintf("%s@%s@%s", c.nickname, c.username, c.hostname)
|
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",
|
message := fmt.Sprintf(":%s!%s@%s TOPIC %s :%s",
|
||||||
c.nickname, c.username, c.hostname, target, ch.topic)
|
c.nickname, c.username, c.hostname, target, ch.topic)
|
||||||
@ -2418,7 +2411,7 @@ func ircHandleStatsLinks(c *client, msg *message) {
|
|||||||
len(client.sendQ), // sendq
|
len(client.sendQ), // sendq
|
||||||
client.nSentMessages, client.sentBytes/1024,
|
client.nSentMessages, client.sentBytes/1024,
|
||||||
client.nReceivedMessages, client.receivedBytes/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) {
|
func ircHandleStatsUptime(c *client) {
|
||||||
uptime := (time.Now().UnixNano() - started) / int64(time.Second)
|
uptime := time.Now().Sub(started) / time.Second
|
||||||
|
|
||||||
days := uptime / 60 / 60 / 24
|
days := uptime / 60 / 60 / 24
|
||||||
hours := (uptime % (60 * 60 * 24)) / 60 / 60
|
hours := (uptime % (60 * 60 * 24)) / 60 / 60
|
||||||
@ -2811,10 +2804,13 @@ func processOneEvent() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
c := &client{
|
c := &client{
|
||||||
transport: conn,
|
transport: conn,
|
||||||
address: address,
|
address: address,
|
||||||
hostname: host,
|
hostname: host,
|
||||||
port: port,
|
port: port,
|
||||||
|
capVersion: 301,
|
||||||
|
// TODO: Make this configurable and more fine-grained.
|
||||||
|
antiflood: newFloodDetector(10*time.Second, 20),
|
||||||
}
|
}
|
||||||
clients[c] = true
|
clients[c] = true
|
||||||
go prepare(c)
|
go prepare(c)
|
||||||
@ -2875,6 +2871,7 @@ func main() {
|
|||||||
log.Fatalln(err)
|
log.Fatalln(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
started = time.Now()
|
||||||
go accept(listener)
|
go accept(listener)
|
||||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user