github.com/jmigpin/editor@v1.6.0/core/godebug/client.go (about) 1 package godebug 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "net" 8 "sync" 9 "time" 10 11 "github.com/jmigpin/editor/core/godebug/debug" 12 ) 13 14 type Client struct { 15 Conn net.Conn 16 Messages chan interface{} 17 waitg sync.WaitGroup 18 } 19 20 func NewClient(ctx context.Context, network, addr string) (*Client, error) { 21 client := &Client{ 22 Messages: make(chan interface{}, 64), 23 } 24 if err := client.connect(ctx, network, addr); err != nil { 25 return nil, fmt.Errorf("client connect: %w", err) 26 } 27 28 // ensure connection close on ctx cancel 29 go func() { 30 select { 31 case <-ctx.Done(): 32 client.Conn.Close() 33 } 34 }() 35 36 // receive msgs from server and send to channel 37 client.waitg.Add(1) 38 go func() { 39 defer client.waitg.Done() 40 client.receiveLoop() 41 }() 42 43 return client, nil 44 } 45 46 func (client *Client) Wait() { 47 client.waitg.Wait() 48 } 49 50 func (client *Client) Close() error { 51 if client.Conn != nil { 52 return client.Conn.Close() 53 } 54 return nil 55 } 56 57 func (client *Client) connect(ctx context.Context, network, addr string) error { 58 // timeout to get a sucessful connection 59 ctx, cancel := context.WithTimeout(ctx, 8*time.Second) 60 defer cancel() 61 62 retry := 250 * time.Millisecond 63 for { 64 dialer := &net.Dialer{} 65 dialer.Timeout = retry 66 conn, err := dialer.DialContext(ctx, network, addr) 67 if err == nil { 68 client.Conn = conn 69 return nil 70 } 71 select { 72 case <-ctx.Done(): 73 return ctx.Err() 74 default: // non-blocking 75 if retry < 5*time.Second { 76 retry += retry // increase for slower conns 77 } 78 } 79 } 80 } 81 82 func (client *Client) receiveLoop() { 83 defer close(client.Messages) 84 for { 85 msg, err := debug.DecodeMessage(client.Conn) 86 if err != nil { 87 // unable to read (server was probably closed) 88 if operr, ok := err.(*net.OpError); ok { 89 if operr.Op == "read" { 90 break 91 } 92 } 93 // connection ended gracefully by the client 94 if err == io.EOF { 95 break 96 } 97 98 client.Messages <- err 99 continue 100 } 101 102 client.Messages <- msg 103 } 104 }