decred.org/dcrdex@v1.0.5/tatanka/tcp/client/client.go (about) 1 // This code is available on the terms of the project LICENSE.md file, 2 // also available online at https://blueoakcouncil.org/license/1.0.0. 3 4 package client 5 6 import ( 7 "context" 8 "fmt" 9 "net/url" 10 "sync" 11 "time" 12 13 "decred.org/dcrdex/client/comms" 14 "decred.org/dcrdex/dex" 15 "decred.org/dcrdex/dex/msgjson" 16 "github.com/decred/dcrd/dcrec/secp256k1/v4" 17 ) 18 19 type Client struct { 20 ctx context.Context 21 log dex.Logger 22 url *url.URL 23 cert []byte 24 cl comms.WsConn 25 cm *dex.ConnectionMaster 26 handle func(*msgjson.Message) *msgjson.Error 27 } 28 29 type Config struct { 30 Logger dex.Logger 31 URL string 32 Cert []byte 33 PrivateKey *secp256k1.PrivateKey 34 HandleMessage func(*msgjson.Message) *msgjson.Error 35 } 36 37 func New(cfg *Config) (*Client, error) { 38 u, err := url.Parse(cfg.URL) 39 if err != nil { 40 return nil, fmt.Errorf("error parsing URL: %w", err) 41 } 42 switch u.Scheme { 43 case "ws", "wss": 44 default: 45 return nil, fmt.Errorf("protocol should be 'ws' or 'wss', not %q", u.Scheme) 46 } 47 return &Client{ 48 log: cfg.Logger, 49 url: u, 50 cert: cfg.Cert, 51 handle: cfg.HandleMessage, 52 }, nil 53 } 54 55 func (c *Client) Connect(ctx context.Context) (_ *sync.WaitGroup, err error) { 56 c.ctx = ctx 57 if c.cl, err = comms.NewWsConn(&comms.WsCfg{ 58 URL: c.url.String(), 59 PingWait: 20 * time.Second, 60 Cert: c.cert, 61 ReconnectSync: func() { 62 fmt.Println("## RECONNECTED RECONNECTED RECONNECTED RECONNECTED ") 63 }, 64 ConnectEventFunc: func(status comms.ConnectionStatus) { 65 if status == comms.Disconnected { 66 // Remove it from the map. 67 c.log.Infof("WebSockets client for %s has disconnected", c.url) 68 } 69 }, 70 Logger: c.log.SubLogger("TC"), 71 }); err != nil { 72 return nil, fmt.Errorf("error creating websockets connection: %w", err) 73 } 74 75 var wg sync.WaitGroup 76 77 msgs := c.cl.MessageSource() 78 79 c.cm = dex.NewConnectionMaster(c.cl) 80 if err := c.cm.ConnectOnce(ctx); err != nil { 81 return nil, fmt.Errorf("error connecting to %q: %w", c.url, err) 82 } 83 84 wg.Add(1) 85 go func() { 86 defer wg.Done() 87 for { 88 select { 89 case msg := <-msgs: 90 c.handle(msg) 91 case <-ctx.Done(): 92 return 93 } 94 } 95 }() 96 97 return &wg, nil 98 } 99 100 func (c *Client) Send(msg *msgjson.Message) error { 101 return c.cl.Send(msg) 102 } 103 104 func (c *Client) Request(msg *msgjson.Message, respHandler func(*msgjson.Message)) error { 105 return c.cl.Request(msg, respHandler) 106 }