From 9fdd4e92747579024cc3efde33a25b9e5657c63c Mon Sep 17 00:00:00 2001 From: Liam Stanley Date: Thu, 19 Oct 2017 19:51:57 -0400 Subject: [PATCH] improve ERROR event handling --- client.go | 32 +++++++++++++++++++++++++++++++- conn.go | 4 ++-- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/client.go b/client.go index ddbc89c..1a4d4ac 100644 --- a/client.go +++ b/client.go @@ -266,7 +266,22 @@ func (c *Client) Close() { c.mu.RUnlock() } -func (c *Client) execLoop(ctx context.Context, wg *sync.WaitGroup) { +// ErrEvent is an error returned when the server (or library) sends an ERROR +// message response. The string returned contains the trailing text from the +// message. +type ErrEvent struct { + Event *Event +} + +func (e *ErrEvent) Error() string { + if e.Event == nil { + return "unknown error occurred" + } + + return e.Event.Trailing +} + +func (c *Client) execLoop(ctx context.Context, errs chan error, wg *sync.WaitGroup) { c.debug.Print("starting execLoop") defer c.debug.Print("closing execLoop") @@ -292,6 +307,21 @@ func (c *Client) execLoop(ctx context.Context, wg *sync.WaitGroup) { wg.Done() return case event = <-c.rx: + if event != nil && event.Command == ERROR { + // Handles incoming ERROR responses. These are only ever sent + // by the server (with the exception that this library may use + // them as a lower level way of signalling to disconnect due + // to some other client-choosen error), and should always be + // followed up by the server disconnecting the client. If for + // some reason the server doesn't disconnect the client, or + // if this library is the source of the error, this should + // signal back up to the main connect loop, to disconnect. + errs <- &ErrEvent{Event: event} + + // Make sure to not actually exit, so we can let any handlers + // actually handle the ERROR event. + } + c.RunHandlers(event) } } diff --git a/conn.go b/conn.go index 92693ad..dac794c 100644 --- a/conn.go +++ b/conn.go @@ -273,12 +273,12 @@ func (c *Client) internalConnect(mock net.Conn, dialer Dialer) error { ctx, c.stop = context.WithCancel(context.Background()) c.mu.Unlock() - errs := make(chan error, 3) + errs := make(chan error, 4) var wg sync.WaitGroup // 4 being the number of goroutines we need to finish when this function // returns. wg.Add(4) - go c.execLoop(ctx, &wg) + go c.execLoop(ctx, errs, &wg) go c.readLoop(ctx, errs, &wg) go c.sendLoop(ctx, errs, &wg) go c.pingLoop(ctx, errs, &wg)