diff --git a/tui/consts.go b/tui/consts.go index e2a45cd..4ac60bb 100644 --- a/tui/consts.go +++ b/tui/consts.go @@ -4,6 +4,7 @@ const ( MY_SIGNAL_EXIT = iota MY_SIGNAL_MESSAGE MY_SIGNAL_CONNECT + MY_SIGNAL_CLOSE ) const ( diff --git a/tui/funcs.go b/tui/funcs.go index 8d0c01b..d2c13bc 100644 --- a/tui/funcs.go +++ b/tui/funcs.go @@ -1,12 +1,14 @@ package tui import ( + "context" "crypto/tls" "crypto/x509" "fmt" "log" "os" "strconv" + "sync" "git.qowevisa.me/Qowevisa/gotell/env" "git.qowevisa.me/Qowevisa/gotell/errors" @@ -74,17 +76,28 @@ func FE_ConnectTLS(t *TUI, data dataT) error { if err != nil { return errors.WrapErr("tls.Dial", err) } - // log.Printf("Set connection to %#v\n", conn) t.tlsConnection = conn if t.stateChannel == nil { return errors.WrapErr("t.stateChannel", errors.NOT_INIT) } t.stateChannel <- "TLS Connected" t.isConnected = true - go t.launchReadingMessagesFromConnection() + ctx, cancel := context.WithCancel(context.Background()) + wg := &sync.WaitGroup{} + wg.Add(1) + t.tlsConnCloseData = closeData{ + wg: wg, + cancel: cancel, + } + go t.launchReadingMessagesFromConnection(ctx, wg) return nil } +func CloseConnection(wg *sync.WaitGroup, cancel context.CancelFunc) { + cancel() + wg.Wait() +} + func AddToStorageEasy(key string, val *[]rune) (dataProcessHandler, dataT) { // that's why I create wrapper around it. // try to understand that, dear viewer! diff --git a/tui/reading.go b/tui/reading.go index b13c667..8b59fe7 100644 --- a/tui/reading.go +++ b/tui/reading.go @@ -1,26 +1,46 @@ package tui import ( + "context" "io" + "net" + "sync" + "time" "git.qowevisa.me/Qowevisa/gotell/errors" ) // NOTE: should be launched as goroutine -func (t *TUI) launchReadingMessagesFromConnection() { +func (t *TUI) launchReadingMessagesFromConnection(ctx context.Context, wg *sync.WaitGroup) { + defer wg.Done() // Mark this goroutine as done when it exits + if t.messageChannel == nil { t.errors <- errors.WrapErr("t.messageChannel", errors.NOT_INIT) return } buf := make([]byte, CONST_MESSAGE_LEN) for { - n, err := t.tlsConnection.Read(buf) - if err != nil { - if err != io.EOF { - t.errors <- errors.WrapErr("t.tlsConnection.Read", err) + select { + case <-ctx.Done(): // Check if context cancellation has been requested + return + default: + timeoutDuration := 5 * time.Second + err := t.tlsConnection.SetReadDeadline(time.Now().Add(timeoutDuration)) + if err != nil { + t.errors <- errors.WrapErr("SetReadDeadline", err) + return } - break + n, err := t.tlsConnection.Read(buf) + if err != nil { + if err != io.EOF { + if netErr, ok := err.(net.Error); ok && netErr.Timeout() { + continue + } + t.errors <- errors.WrapErr("t.tlsConnection.Read", err) + } + return + } + t.messageChannel <- buf[:n] } - t.messageChannel <- buf[:n] } } diff --git a/tui/types.go b/tui/types.go index 43c1d35..2442a15 100644 --- a/tui/types.go +++ b/tui/types.go @@ -2,6 +2,7 @@ package tui import ( "bufio" + "context" "crypto/tls" "os" "sync" @@ -83,6 +84,11 @@ type widgetConfig struct { FinaleData dataT } +type closeData struct { + wg *sync.WaitGroup + cancel context.CancelFunc +} + type TUI struct { width int height int @@ -108,4 +114,5 @@ type TUI struct { selectedNotifier *notifier storage map[string]string tlsConnection *tls.Conn + tlsConnCloseData closeData } diff --git a/tui/ui.go b/tui/ui.go index de5d438..ce68a5d 100644 --- a/tui/ui.go +++ b/tui/ui.go @@ -138,6 +138,15 @@ func (t *TUI) Run() error { t.errors <- errors.WrapErr("t.drawSelectedWidget", err) } } + + case MY_SIGNAL_CLOSE: + if t.isConnected { + CloseConnection(t.tlsConnCloseData.wg, t.tlsConnCloseData.cancel) + t.isConnected = false + t.errors <- t.tlsConnection.Close() + t.stateChannel <- "Disconnected" + } + default: } } // @@ -239,12 +248,18 @@ func (t *TUI) setRoutines() error { Type: MY_SIGNAL_EXIT, } case 'c': - t.mySignals <- mySignal{ - Type: MY_SIGNAL_CONNECT, + if t.isConnected { + t.mySignals <- mySignal{ + Type: MY_SIGNAL_CLOSE, + } + } else { + t.mySignals <- mySignal{ + Type: MY_SIGNAL_CONNECT, + } + readInputMu.Lock() + readInput = true + readInputMu.Unlock() } - readInputMu.Lock() - readInput = true - readInputMu.Unlock() case 'm': t.mySignals <- mySignal{ Type: MY_SIGNAL_MESSAGE,