1
0
Fork 0
mirror of https://github.com/fluffle/goirc synced 2025-05-13 11:03:19 +00:00

Epic final commit for nick/chan/logging/testing refactor.

* Brings logging changes to client library.
* Brings state tracker to client library.
* Rewrites all tests to use mock logger and mock state tracker.
* Makes state tracking optional, finally.
* Shaves yaks until they are almost completely bald.
This commit is contained in:
Alex Bramley 2011-11-06 04:56:46 +00:00
parent 69d52270b6
commit 85097043cf
7 changed files with 525 additions and 577 deletions

View file

@ -5,6 +5,7 @@ import (
"crypto/tls"
"github.com/fluffle/goirc/event"
"github.com/fluffle/goirc/logging"
"github.com/fluffle/goirc/state"
"fmt"
"net"
"os"
@ -20,15 +21,19 @@ const (
type Conn struct {
// Connection Hostname and Nickname
Host string
Me *Nick
Me *state.Nick
Network string
// Event handler registry and dispatcher
Registry event.EventRegistry
Dispatcher event.EventDispatcher
ER event.EventRegistry
ED event.EventDispatcher
// State tracker for nicks and channels
Tracker StateTracker
ST state.StateTracker
st bool
// Logger for debugging/warning/etc output
l logging.Logger
// Use the State field to store external state that handlers might need.
// Remember ... you might need locking for this ;-)
@ -61,11 +66,20 @@ type Conn struct {
// Creates a new IRC connection object, but doesn't connect to anything so
// that you can add event handlers to it. See AddHandler() for details.
func New(nick, user, name string) *Conn {
reg := event.NewRegistry()
// Dependency injection is a bitch :-/
func New(nick, user, name string, st bool,
r event.EventRegistry, l logging.Logger) *Conn {
if r == nil {
r = event.NewRegistry()
}
if l == nil {
l = logging.NewFromFlags()
}
conn := &Conn{
Registry: reg,
Dispatcher: reg,
ER: r,
ED: r,
l: l,
st: st,
in: make(chan *Line, 32),
out: make(chan string, 32),
cSend: make(chan bool),
@ -77,23 +91,27 @@ func New(nick, user, name string) *Conn {
badness: 0,
lastsent: 0,
}
conn.addIntHandlers()
if st {
conn.ST = state.NewTracker(nick, l)
conn.Me = conn.ST.Me()
conn.addSTHandlers()
} else {
conn.Me = state.NewNick(nick, l)
}
conn.Me.Ident = user
conn.Me.Name = name
conn.initialise()
conn.SetupHandlers()
conn.Me = conn.NewNick(nick, user, name, "")
return conn
}
// Per-connection state initialisation.
func (conn *Conn) initialise() {
conn.nicks = make(map[string]*Nick)
conn.chans = make(map[string]*Channel)
conn.io = nil
conn.sock = nil
// If this is being called because we are reconnecting, conn.Me
// will still have all the old channels referenced -- nuke them!
if conn.Me != nil {
conn.Me = conn.NewNick(conn.Me.Nick, conn.Me.Ident, conn.Me.Name, "")
if conn.st {
conn.ST.Wipe()
}
}
@ -113,7 +131,7 @@ func (conn *Conn) Connect(host string, pass ...string) os.Error {
if !hasPort(host) {
host += ":6697"
}
logging.Info("irc.Connect(): Connecting to %s with SSL.", host)
conn.l.Info("irc.Connect(): Connecting to %s with SSL.", host)
if s, err := tls.Dial("tcp", host, conn.SSLConfig); err == nil {
conn.sock = s
} else {
@ -123,7 +141,7 @@ func (conn *Conn) Connect(host string, pass ...string) os.Error {
if !hasPort(host) {
host += ":6667"
}
logging.Info("irc.Connect(): Connecting to %s without SSL.", host)
conn.l.Info("irc.Connect(): Connecting to %s without SSL.", host)
if s, err := net.Dial("tcp", host); err == nil {
conn.sock = s
} else {
@ -176,16 +194,18 @@ func (conn *Conn) recv() {
for {
s, err := conn.io.ReadString('\n')
if err != nil {
logging.Error("irc.recv(): %s", err.String())
conn.l.Error("irc.recv(): %s", err.String())
conn.shutdown()
return
}
s = strings.Trim(s, "\r\n")
logging.Debug("<- %s", s)
conn.l.Debug("<- %s", s)
if line := parseLine(s); line != nil {
line.Time = time.LocalTime()
conn.in <- line
} else {
conn.l.Warn("irc.recv(): problems parsing line:\n %s", s)
}
}
}
@ -195,7 +215,7 @@ func (conn *Conn) runLoop() {
for {
select {
case line := <-conn.in:
conn.Dispatcher.Dispatch(line.Cmd, conn, line)
conn.ED.Dispatch(line.Cmd, conn, line)
case <-conn.cLoop:
// strobe on control channel, bail out
return
@ -211,12 +231,12 @@ func (conn *Conn) write(line string) {
}
if _, err := conn.io.WriteString(line + "\r\n"); err != nil {
logging.Error("irc.send(): %s", err.String())
conn.l.Error("irc.send(): %s", err.String())
conn.shutdown()
return
}
conn.io.Flush()
logging.Debug("-> %s", line)
conn.l.Debug("-> %s", line)
}
// Implement Hybrid's flood control algorithm to rate-limit outgoing lines.
@ -234,9 +254,9 @@ func (conn *Conn) rateLimit(chars int64) {
// calculation above, then we're at risk of "Excess Flood".
if conn.badness > 10*second && !conn.Flood {
// so sleep for the current line's time value before sending it
logging.Debug("irc.rateLimit(): Flood! Sleeping for %.2f secs.",
conn.l.Debug("irc.rateLimit(): Flood! Sleeping for %.2f secs.",
float64(linetime)/float64(second))
time.Sleep(linetime)
<-time.After(linetime)
}
}
@ -244,8 +264,8 @@ func (conn *Conn) shutdown() {
// Guard against double-call of shutdown() if we get an error in send()
// as calling sock.Close() will cause recv() to recieve EOF in readstring()
if conn.Connected {
logging.Info("irc.shutdown(): Disconnected from server.")
conn.Dispatcher.Dispatch("disconnected", conn, &Line{})
conn.l.Info("irc.shutdown(): Disconnected from server.")
conn.ED.Dispatch("disconnected", conn, &Line{})
conn.Connected = false
conn.sock.Close()
conn.cSend <- true
@ -257,7 +277,7 @@ func (conn *Conn) shutdown() {
}
// Dumps a load of information about the current state of the connection to a
// string for debugging state tracking and other such things.
// string for debugging state tracking and other such things.
func (conn *Conn) String() string {
str := "GoIRC Connection\n"
str += "----------------\n\n"
@ -267,17 +287,8 @@ func (conn *Conn) String() string {
str += "Not currently connected!\n\n"
}
str += conn.Me.String() + "\n"
str += "GoIRC Channels\n"
str += "--------------\n\n"
for _, ch := range conn.chans {
str += ch.String() + "\n"
}
str += "GoIRC NickNames\n"
str += "---------------\n\n"
for _, n := range conn.nicks {
if n != conn.Me {
str += n.String() + "\n"
}
if conn.st {
str += conn.ST.String() + "\n"
}
return str
}