mirror of https://github.com/fluffle/goirc
Port sp0rkle's panic recovery back into goirc.
This commit is contained in:
parent
5f2665dde8
commit
ac9d05efa2
|
@ -68,6 +68,9 @@ type Config struct {
|
||||||
|
|
||||||
// Sent as the QUIT message.
|
// Sent as the QUIT message.
|
||||||
QuitMessage string
|
QuitMessage string
|
||||||
|
|
||||||
|
// Configurable panic recovery for all handlers.
|
||||||
|
Recover func(*Conn, *Line)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConfig(nick string, args ...string) *Config {
|
func NewConfig(nick string, args ...string) *Config {
|
||||||
|
@ -75,6 +78,7 @@ func NewConfig(nick string, args ...string) *Config {
|
||||||
Me: state.NewNick(nick),
|
Me: state.NewNick(nick),
|
||||||
PingFreq: 3 * time.Minute,
|
PingFreq: 3 * time.Minute,
|
||||||
NewNick: func(s string) string { return s + "_" },
|
NewNick: func(s string) string { return s + "_" },
|
||||||
|
Recover: (*Conn).LogPanic, // in dispatch.go
|
||||||
}
|
}
|
||||||
cfg.Me.Ident = "goirc"
|
cfg.Me.Ident = "goirc"
|
||||||
if len(args) > 0 && args[0] != "" {
|
if len(args) > 0 && args[0] != "" {
|
||||||
|
|
|
@ -2,6 +2,7 @@ package client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/fluffle/golog/logging"
|
"github.com/fluffle/golog/logging"
|
||||||
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
)
|
)
|
||||||
|
@ -45,8 +46,9 @@ type hNode struct {
|
||||||
handler Handler
|
handler Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
// A hNode implements both Handler...
|
// A hNode implements both Handler (with configurable panic recovery)...
|
||||||
func (hn *hNode) Handle(conn *Conn, line *Line) {
|
func (hn *hNode) Handle(conn *Conn, line *Line) {
|
||||||
|
defer conn.cfg.Recover(conn, line)
|
||||||
hn.handler.Handle(conn, line)
|
hn.handler.Handle(conn, line)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,3 +143,10 @@ func (conn *Conn) HandleFunc(name string, hf HandlerFunc) Remover {
|
||||||
func (conn *Conn) dispatch(line *Line) {
|
func (conn *Conn) dispatch(line *Line) {
|
||||||
conn.handlers.dispatch(conn, line)
|
conn.handlers.dispatch(conn, line)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (conn *Conn) LogPanic(line *Line) {
|
||||||
|
if err := recover(); err != nil {
|
||||||
|
_, f, l, _ := runtime.Caller(2)
|
||||||
|
logging.Error("%s:%d: panic: %v", f, l, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -6,6 +6,11 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestHandlerSet(t *testing.T) {
|
func TestHandlerSet(t *testing.T) {
|
||||||
|
// A Conn is needed here because the previous behaviour of passing nil to
|
||||||
|
// hset.dispatch causes a nil pointer dereference with panic recovery.
|
||||||
|
c, s := setUp(t)
|
||||||
|
defer s.tearDown()
|
||||||
|
|
||||||
hs := handlerSet()
|
hs := handlerSet()
|
||||||
if len(hs.set) != 0 {
|
if len(hs.set) != 0 {
|
||||||
t.Errorf("New set contains things!")
|
t.Errorf("New set contains things!")
|
||||||
|
@ -83,7 +88,7 @@ func TestHandlerSet(t *testing.T) {
|
||||||
if callcount != 0 {
|
if callcount != 0 {
|
||||||
t.Errorf("Something incremented call count before we were expecting it.")
|
t.Errorf("Something incremented call count before we were expecting it.")
|
||||||
}
|
}
|
||||||
hs.dispatch(nil, &Line{Cmd: "One"})
|
hs.dispatch(c, &Line{Cmd: "One"})
|
||||||
<-time.After(time.Millisecond)
|
<-time.After(time.Millisecond)
|
||||||
if callcount != 4 {
|
if callcount != 4 {
|
||||||
t.Errorf("Our handler wasn't called four times :-(")
|
t.Errorf("Our handler wasn't called four times :-(")
|
||||||
|
@ -107,7 +112,7 @@ func TestHandlerSet(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dispatch should result in 3 additions.
|
// Dispatch should result in 3 additions.
|
||||||
hs.dispatch(nil, &Line{Cmd: "One"})
|
hs.dispatch(c, &Line{Cmd: "One"})
|
||||||
<-time.After(time.Millisecond)
|
<-time.After(time.Millisecond)
|
||||||
if callcount != 7 {
|
if callcount != 7 {
|
||||||
t.Errorf("Our handler wasn't called three times :-(")
|
t.Errorf("Our handler wasn't called three times :-(")
|
||||||
|
@ -129,7 +134,7 @@ func TestHandlerSet(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dispatch should result in 2 additions.
|
// Dispatch should result in 2 additions.
|
||||||
hs.dispatch(nil, &Line{Cmd: "One"})
|
hs.dispatch(c, &Line{Cmd: "One"})
|
||||||
<-time.After(time.Millisecond)
|
<-time.After(time.Millisecond)
|
||||||
if callcount != 9 {
|
if callcount != 9 {
|
||||||
t.Errorf("Our handler wasn't called two times :-(")
|
t.Errorf("Our handler wasn't called two times :-(")
|
||||||
|
@ -151,7 +156,7 @@ func TestHandlerSet(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dispatch should result in 1 addition.
|
// Dispatch should result in 1 addition.
|
||||||
hs.dispatch(nil, &Line{Cmd: "One"})
|
hs.dispatch(c, &Line{Cmd: "One"})
|
||||||
<-time.After(time.Millisecond)
|
<-time.After(time.Millisecond)
|
||||||
if callcount != 10 {
|
if callcount != 10 {
|
||||||
t.Errorf("Our handler wasn't called once :-(")
|
t.Errorf("Our handler wasn't called once :-(")
|
||||||
|
@ -170,9 +175,29 @@ func TestHandlerSet(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dispatch should result in NO additions.
|
// Dispatch should result in NO additions.
|
||||||
hs.dispatch(nil, &Line{Cmd: "One"})
|
hs.dispatch(c, &Line{Cmd: "One"})
|
||||||
<-time.After(time.Millisecond)
|
<-time.After(time.Millisecond)
|
||||||
if callcount != 10 {
|
if callcount != 10 {
|
||||||
t.Errorf("Our handler was called?")
|
t.Errorf("Our handler was called?")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPanicRecovery(t *testing.T) {
|
||||||
|
c, s := setUp(t)
|
||||||
|
defer s.tearDown()
|
||||||
|
|
||||||
|
recovered := false
|
||||||
|
c.cfg.Recover = func(conn *Conn, line *Line) {
|
||||||
|
if err, ok := recover().(string); ok && err == "panic!" {
|
||||||
|
recovered = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.HandleFunc(PRIVMSG, func(conn *Conn, line *Line) {
|
||||||
|
panic("panic!")
|
||||||
|
})
|
||||||
|
c.in <- parseLine(":nick!user@host.com PRIVMSG #channel :OH NO PIGEONS")
|
||||||
|
<-time.After(time.Millisecond)
|
||||||
|
if recovered != true {
|
||||||
|
t.Errorf("Failed to recover panic!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue