mirror of
				https://github.com/fluffle/goirc
				synced 2025-11-04 03:58:03 +00:00 
			
		
		
		
	Port sp0rkle's panic recovery back into goirc.
This commit is contained in:
		
							parent
							
								
									5f2665dde8
								
							
						
					
					
						commit
						ac9d05efa2
					
				
					 3 changed files with 44 additions and 6 deletions
				
			
		| 
						 | 
					@ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue