Fix websocket apis (#127)
Remove ping check and instead use a context that will be canceled if it the client sends a close message or an error occurs on reading. The context will be used to cancel all functions using it.
This commit is contained in:
@@ -24,7 +24,6 @@ import (
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
mc "github.com/minio/mc/cmd"
|
||||
@@ -35,106 +34,48 @@ type watchOptions struct {
|
||||
mc.WatchOptions
|
||||
}
|
||||
|
||||
// startWatch starts by setting a websocket reader that
|
||||
// will check for a heartbeat.
|
||||
//
|
||||
// A WaitGroup is used to handle goroutines and to ensure
|
||||
// all finish in the proper order. If any, sendWatchInfo()
|
||||
// or wsReadCheck() returns, watch should end.
|
||||
func startWatch(conn WSConn, client MCS3Client, options watchOptions) (mError error) {
|
||||
// a WaitGroup waits for a collection of goroutines to finish
|
||||
wg := sync.WaitGroup{}
|
||||
// a cancel context is needed to end all goroutines used
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
// Set number of goroutines to wait. wg.Wait()
|
||||
// waits until counter is zero (all are done)
|
||||
wg.Add(3)
|
||||
// start go routine for reading websocket heartbeat
|
||||
readErr := wsReadCheck(ctx, &wg, conn)
|
||||
// send Stream of watch events to the ws c.connection
|
||||
ch := sendWatchInfo(ctx, &wg, conn, client, options)
|
||||
// If wsReadCheck returns it means that it is not possible to check
|
||||
// ws heartbeat anymore so we stop from doing Watch, cancel context
|
||||
// for all goroutines.
|
||||
go func(wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
if err := <-readErr; err != nil {
|
||||
log.Println("error on wsReadCheck:", err)
|
||||
mError = err
|
||||
}
|
||||
// cancel context for all goroutines.
|
||||
cancel()
|
||||
}(&wg)
|
||||
|
||||
if err := <-ch; err != nil {
|
||||
mError = err
|
||||
func startWatch(ctx context.Context, conn WSConn, wsc MCS3Client, options watchOptions) error {
|
||||
wo, pErr := wsc.watch(options.WatchOptions)
|
||||
if pErr != nil {
|
||||
fmt.Println("error initializing watch:", pErr.Cause)
|
||||
return pErr.Cause
|
||||
}
|
||||
|
||||
// if ch closes for any reason,
|
||||
// cancel context for all goroutines
|
||||
cancel()
|
||||
// wait all goroutines to finish
|
||||
wg.Wait()
|
||||
return mError
|
||||
}
|
||||
|
||||
// sendWatchInfo sends stream of Watch Event to the ws connection
|
||||
func sendWatchInfo(ctx context.Context, wg *sync.WaitGroup, conn WSConn, wsc MCS3Client, options watchOptions) <-chan error {
|
||||
// decrements the WaitGroup counter
|
||||
// by one when the function returns
|
||||
defer wg.Done()
|
||||
ch := make(chan error)
|
||||
go func(ch chan<- error) {
|
||||
defer close(ch)
|
||||
wo, pErr := wsc.watch(options.WatchOptions)
|
||||
if pErr != nil {
|
||||
fmt.Println("error initializing watch:", pErr.Cause)
|
||||
ch <- pErr.Cause
|
||||
return
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
close(wo.DoneChan)
|
||||
return
|
||||
case events, ok := <-wo.Events():
|
||||
// zero value returned because the channel is closed and empty
|
||||
if !ok {
|
||||
return
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
close(wo.DoneChan)
|
||||
return nil
|
||||
case events, ok := <-wo.Events():
|
||||
// zero value returned because the channel is closed and empty
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
for _, event := range events {
|
||||
// Serialize message to be sent
|
||||
bytes, err := json.Marshal(event)
|
||||
if err != nil {
|
||||
log.Println("error on json.Marshal:", err)
|
||||
return err
|
||||
}
|
||||
for _, event := range events {
|
||||
// Serialize message to be sent
|
||||
bytes, err := json.Marshal(event)
|
||||
if err != nil {
|
||||
fmt.Println("error on json.Marshal:", err)
|
||||
ch <- err
|
||||
return
|
||||
}
|
||||
// Send Message through websocket connection
|
||||
err = conn.writeMessage(websocket.TextMessage, bytes)
|
||||
if err != nil {
|
||||
log.Println("error writeMessage:", err)
|
||||
ch <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
case pErr, ok := <-wo.Errors():
|
||||
// zero value returned because the channel is closed and empty
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if pErr != nil {
|
||||
log.Println("error on watch:", pErr.Cause)
|
||||
ch <- pErr.Cause
|
||||
return
|
||||
|
||||
// Send Message through websocket connection
|
||||
err = conn.writeMessage(websocket.TextMessage, bytes)
|
||||
if err != nil {
|
||||
log.Println("error writeMessage:", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
case pErr, ok := <-wo.Errors():
|
||||
// zero value returned because the channel is closed and empty
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
if pErr != nil {
|
||||
log.Println("error on watch:", pErr.Cause)
|
||||
return pErr.Cause
|
||||
|
||||
}
|
||||
}
|
||||
}(ch)
|
||||
return ch
|
||||
}
|
||||
}
|
||||
|
||||
// getOptionsFromReq gets bucket name, events, prefix, suffix from a websocket
|
||||
|
||||
Reference in New Issue
Block a user