Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

socketmode: awaiting of message receiver goroutine in run method to avoid panics #1170

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 36 additions & 3 deletions socketmode/socket_mode_managed_conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,10 @@ func (smc *Client) run(ctx context.Context, connectionCount int) error {
}
}()

// We don't wait on runMessageReceiver because it doesn't block on a select with the context,
// so we'd have to wait for the ReadJSON to time out, which can take a while.
// Need to wait for runMessageReceiver to avoid panic described in https://github.com/slack-go/slack/issues/1125
wg.Add(1)
go func() {
defer wg.Done()
defer cancel()

// The receiver reads WebSocket messages, and enqueues parsed Socket Mode requests to be handled by
Expand Down Expand Up @@ -439,8 +440,40 @@ func (smc *Client) receiveMessagesInto(ctx context.Context, conn *websocket.Conn
smc.Debugf("Starting to receive message")
defer smc.Debugf("Finished to receive message")

var err error

event := json.RawMessage{}
err := conn.ReadJSON(&event)

readJsonErr := make(chan error)
go func() {
select {
case readJsonErr <- conn.ReadJSON(&event):
// if conn.ReadJSON method returns after ctx.Done(), no one will read the unbuffered channel
// so, to avoid goroutines leak we need to check for ctx.Done() here too.
// need to say here, that conn.ReadJSON will really return after ctx.Done(), because in that case
// conn is closed
break
case <-ctx.Done():
// just need to listen ctx.Done, nothing has to be done here
break
}
}()

select {
case err = <-readJsonErr:
// we have awaited response from conn.ReadJSON, so, we can handle error now
break
case <-ctx.Done():
// context cancellation signal got, closing connection and returning
cerr := conn.Close()
if cerr != nil {
smc.Debugf("Failed to close connection: %v", cerr)
}

return ctx.Err()
}

// after select above, the err will be set to result from conn.ReadJSON, handling

// check if the connection was closed.
if websocket.IsUnexpectedCloseError(err) {
Expand Down