1
- package futures
1
+ package websocket
2
2
3
3
import (
4
4
"context"
@@ -13,11 +13,9 @@ import (
13
13
14
14
"github.com/gorilla/websocket"
15
15
"github.com/jpillora/backoff"
16
-
17
- "github.com/adshao/go-binance/v2/common"
18
16
)
19
17
20
- //go:generate mockgen -source client_ws .go -destination mock/client_ws .go -package mock
18
+ //go:generate mockgen -source client .go -destination mock/client .go -package mock
21
19
22
20
const (
23
21
// reconnectMinInterval define reconnect min interval
@@ -33,22 +31,21 @@ var (
33
31
34
32
// ErrorWsIdAlreadySent defines that request with the same id was already sent
35
33
ErrorWsIdAlreadySent = errors .New ("ws error: request with same id already sent" )
34
+
35
+ // KeepAlivePingDeadline defines deadline to send ping frame
36
+ KeepAlivePingDeadline = 10 * time .Second
36
37
)
37
38
38
39
// messageId define id field of request/response
39
40
type messageId struct {
40
41
Id string `json:"id"`
41
42
}
42
43
43
- // ClientWs define API websocket client
44
- type ClientWs struct {
45
- APIKey string
46
- SecretKey string
44
+ // client define API websocket client
45
+ type client struct {
47
46
Debug bool
48
- KeyType string
49
- TimeOffset int64
50
47
logger * log.Logger
51
- conn wsConnection
48
+ conn Connection
52
49
connMu sync.Mutex
53
50
reconnectSignal chan struct {}
54
51
connectionEstablishedSignal chan struct {}
@@ -58,18 +55,15 @@ type ClientWs struct {
58
55
reconnectCount int64
59
56
}
60
57
61
- func (c * ClientWs ) debug (format string , v ... interface {}) {
58
+ func (c * client ) debug (format string , v ... interface {}) {
62
59
if c .Debug {
63
60
c .logger .Println (fmt .Sprintf (format , v ... ))
64
61
}
65
62
}
66
63
67
- // NewClientWs init ClientWs
68
- func NewClientWs (conn wsConnection , apiKey , secretKey string ) (* ClientWs , error ) {
69
- client := & ClientWs {
70
- APIKey : apiKey ,
71
- SecretKey : secretKey ,
72
- KeyType : common .KeyTypeHmac ,
64
+ // NewClient init client
65
+ func NewClient (conn Connection ) (Client , error ) {
66
+ client := & client {
73
67
logger : log .New (os .Stderr , "Binance-golang " , log .LstdFlags ),
74
68
conn : conn ,
75
69
connMu : sync.Mutex {},
@@ -86,21 +80,17 @@ func NewClientWs(conn wsConnection, apiKey, secretKey string) (*ClientWs, error)
86
80
return client , nil
87
81
}
88
82
89
- type wsClient interface {
83
+ type Client interface {
90
84
Write (id string , data []byte ) error
91
85
WriteSync (id string , data []byte , timeout time.Duration ) ([]byte , error )
92
86
GetReadChannel () <- chan []byte
93
87
GetReadErrorChannel () <- chan error
94
- GetApiKey () string
95
- GetSecretKey () string
96
- GetTimeOffset () int64
97
- GetKeyType () string
98
88
GetReconnectCount () int64
99
89
Wait (timeout time.Duration )
100
90
}
101
91
102
92
// Write sends data into websocket connection
103
- func (c * ClientWs ) Write (id string , data []byte ) error {
93
+ func (c * client ) Write (id string , data []byte ) error {
104
94
c .connMu .Lock ()
105
95
defer c .connMu .Unlock ()
106
96
@@ -120,7 +110,7 @@ func (c *ClientWs) Write(id string, data []byte) error {
120
110
121
111
// WriteSync sends data to the websocket connection and waits for a response synchronously
122
112
// Should be used separately from the asynchronous Write method (do not send anything in parallel)
123
- func (c * ClientWs ) WriteSync (id string , data []byte , timeout time.Duration ) ([]byte , error ) {
113
+ func (c * client ) WriteSync (id string , data []byte , timeout time.Duration ) ([]byte , error ) {
124
114
c .connMu .Lock ()
125
115
defer c .connMu .Unlock ()
126
116
@@ -157,36 +147,20 @@ func (c *ClientWs) WriteSync(id string, data []byte, timeout time.Duration) ([]b
157
147
}
158
148
}
159
149
160
- func (c * ClientWs ) GetReadChannel () <- chan []byte {
150
+ func (c * client ) GetReadChannel () <- chan []byte {
161
151
return c .readC
162
152
}
163
153
164
- func (c * ClientWs ) GetReadErrorChannel () <- chan error {
154
+ func (c * client ) GetReadErrorChannel () <- chan error {
165
155
return c .readErrChan
166
156
}
167
157
168
- func (c * ClientWs ) GetApiKey () string {
169
- return c .APIKey
170
- }
171
-
172
- func (c * ClientWs ) GetSecretKey () string {
173
- return c .SecretKey
174
- }
175
-
176
- func (c * ClientWs ) GetTimeOffset () int64 {
177
- return c .TimeOffset
178
- }
179
-
180
- func (c * ClientWs ) GetKeyType () string {
181
- return c .KeyType
182
- }
183
-
184
- func (c * ClientWs ) Wait (timeout time.Duration ) {
158
+ func (c * client ) Wait (timeout time.Duration ) {
185
159
c .wait (timeout )
186
160
}
187
161
188
162
// read data from connection
189
- func (c * ClientWs ) read () {
163
+ func (c * client ) read () {
190
164
defer func () {
191
165
// reading from closed connection 1000 times caused panic
192
166
// prevent panic for any case
@@ -231,7 +205,7 @@ func (c *ClientWs) read() {
231
205
232
206
// wait until all responses received
233
207
// make sure that you are not sending requests
234
- func (c * ClientWs ) wait (timeout time.Duration ) {
208
+ func (c * client ) wait (timeout time.Duration ) {
235
209
doneC := make (chan struct {})
236
210
237
211
ctx , cancel := context .WithCancel (context .Background ())
@@ -260,7 +234,7 @@ func (c *ClientWs) wait(timeout time.Duration) {
260
234
}
261
235
262
236
// handleReconnect waits for reconnect signal and starts reconnect
263
- func (c * ClientWs ) handleReconnect () {
237
+ func (c * client ) handleReconnect () {
264
238
for _ = range c .reconnectSignal {
265
239
c .debug ("reconnect: received signal" )
266
240
@@ -285,10 +259,10 @@ func (c *ClientWs) handleReconnect() {
285
259
}
286
260
287
261
// startReconnect starts reconnect loop with increasing delay
288
- func (c * ClientWs ) startReconnect (b * backoff.Backoff ) * connection {
262
+ func (c * client ) startReconnect (b * backoff.Backoff ) Connection {
289
263
for {
290
264
atomic .AddInt64 (& c .reconnectCount , 1 )
291
- conn , err := newConnection ()
265
+ conn , err := c . conn . RestoreConnection ()
292
266
if err != nil {
293
267
delay := b .Duration ()
294
268
c .debug ("reconnect: error while reconnecting. try in %s" , delay .Round (time .Millisecond ))
@@ -301,7 +275,9 @@ func (c *ClientWs) startReconnect(b *backoff.Backoff) *connection {
301
275
}
302
276
303
277
// GetReconnectCount returns reconnect counter value
304
- func (c * ClientWs ) GetReconnectCount () int64 { return atomic .LoadInt64 (& c .reconnectCount ) }
278
+ func (c * client ) GetReconnectCount () int64 {
279
+ return atomic .LoadInt64 (& c .reconnectCount )
280
+ }
305
281
306
282
// NewRequestList creates request list
307
283
func NewRequestList () RequestList {
@@ -356,37 +332,47 @@ func (l *RequestList) IsAlreadyInList(id string) bool {
356
332
return false
357
333
}
358
334
359
- // constructor for connection
360
- func newConnection () (* connection , error ) {
361
- conn , err := WsApiInitReadWriteConn ()
335
+ // NewConnection constructor for connection
336
+ func NewConnection (
337
+ initUnderlyingWsConnFn func () (* websocket.Conn , error ),
338
+ isKeepAliveNeeded bool ,
339
+ keepaliveTimeout time.Duration ,
340
+ ) (Connection , error ) {
341
+ underlyingWsConn , err := initUnderlyingWsConnFn ()
362
342
if err != nil {
363
343
return nil , err
364
344
}
365
345
366
346
wsConn := & connection {
367
- conn : conn ,
368
- connectionMu : sync.Mutex {},
369
- lastResponseMu : sync.Mutex {},
347
+ conn : underlyingWsConn ,
348
+ connectionMu : sync.Mutex {},
349
+ lastResponseMu : sync.Mutex {},
350
+ initUnderlyingWsConnFn : initUnderlyingWsConnFn ,
351
+ keepaliveTimeout : keepaliveTimeout ,
370
352
}
371
353
372
- if WebsocketKeepalive {
373
- go wsConn .keepAlive (WebsocketTimeoutReadWriteConnection )
354
+ if isKeepAliveNeeded {
355
+ go wsConn .keepAlive (keepaliveTimeout )
374
356
}
375
357
376
358
return wsConn , nil
377
359
}
378
360
379
- // instance of single connection with keepalive handler
361
+ // connection is an instance of single ws connection with keepalive handler
380
362
type connection struct {
381
- conn * websocket.Conn
382
- connectionMu sync.Mutex
383
- lastResponse time.Time
384
- lastResponseMu sync.Mutex
363
+ conn * websocket.Conn
364
+ connectionMu sync.Mutex
365
+ lastResponse time.Time
366
+ lastResponseMu sync.Mutex
367
+ initUnderlyingWsConnFn func () (* websocket.Conn , error )
368
+ keepaliveTimeout time.Duration
369
+ isKeepAliveNeeded bool
385
370
}
386
371
387
- type wsConnection interface {
372
+ type Connection interface {
388
373
WriteMessage (messageType int , data []byte ) error
389
374
ReadMessage () (messageType int , p []byte , err error )
375
+ RestoreConnection () (Connection , error )
390
376
}
391
377
392
378
// WriteMessage is a thread-safe method for conn.WriteMessage
@@ -401,6 +387,11 @@ func (c *connection) ReadMessage() (int, []byte, error) {
401
387
return c .conn .ReadMessage ()
402
388
}
403
389
390
+ // RestoreConnection recreates ws connection with the same underlying connection callback and keepalive timeout
391
+ func (c * connection ) RestoreConnection () (Connection , error ) {
392
+ return NewConnection (c .initUnderlyingWsConnFn , c .isKeepAliveNeeded , c .keepaliveTimeout )
393
+ }
394
+
404
395
// keepAlive handles ping-pong for connection
405
396
func (c * connection ) keepAlive (timeout time.Duration ) {
406
397
ticker := time .NewTicker (timeout )
@@ -455,41 +446,11 @@ func (c *connection) ping() error {
455
446
c .connectionMu .Lock ()
456
447
defer c .connectionMu .Unlock ()
457
448
458
- deadline := time .Now ().Add (10 * time . Second )
449
+ deadline := time .Now ().Add (KeepAlivePingDeadline )
459
450
err := c .conn .WriteControl (websocket .PingMessage , []byte {}, deadline )
460
451
if err != nil {
461
452
return err
462
453
}
463
454
464
455
return nil
465
456
}
466
-
467
- // NewOrderPlaceWsService init OrderPlaceWsService
468
- func NewOrderPlaceWsService (apiKey , secretKey string ) (* OrderPlaceWsService , error ) {
469
- conn , err := newConnection ()
470
- if err != nil {
471
- return nil , err
472
- }
473
-
474
- client , err := NewClientWs (conn , apiKey , secretKey )
475
- if err != nil {
476
- return nil , err
477
- }
478
-
479
- return & OrderPlaceWsService {c : client }, nil
480
- }
481
-
482
- // NewOrderCancelWsService init OrderCancelWsService
483
- func NewOrderCancelWsService (apiKey , secretKey string ) (* OrderCancelWsService , error ) {
484
- conn , err := newConnection ()
485
- if err != nil {
486
- return nil , err
487
- }
488
-
489
- client , err := NewClientWs (conn , apiKey , secretKey )
490
- if err != nil {
491
- return nil , err
492
- }
493
-
494
- return & OrderCancelWsService {c : client }, nil
495
- }
0 commit comments