2020-01-15 04:56:50 +00:00
|
|
|
// Package wsutil provides abstractions around the Websocket, including rate
|
|
|
|
// limits.
|
2020-01-09 05:24:45 +00:00
|
|
|
package wsutil
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"time"
|
|
|
|
|
2020-01-15 18:32:54 +00:00
|
|
|
"github.com/diamondburned/arikawa/internal/json"
|
2020-01-09 05:24:45 +00:00
|
|
|
"github.com/pkg/errors"
|
|
|
|
"golang.org/x/time/rate"
|
|
|
|
)
|
|
|
|
|
2020-01-19 06:14:46 +00:00
|
|
|
const DefaultTimeout = time.Minute
|
2020-01-09 05:24:45 +00:00
|
|
|
|
|
|
|
type Event struct {
|
|
|
|
Data []byte
|
|
|
|
|
|
|
|
// Error is non-nil if Data is nil.
|
|
|
|
Error error
|
|
|
|
}
|
|
|
|
|
|
|
|
type Websocket struct {
|
2020-01-15 04:43:34 +00:00
|
|
|
Conn Connection
|
|
|
|
Addr string
|
2020-01-09 05:24:45 +00:00
|
|
|
|
2020-01-15 04:43:34 +00:00
|
|
|
SendLimiter *rate.Limiter
|
|
|
|
DialLimiter *rate.Limiter
|
|
|
|
|
|
|
|
listener <-chan Event
|
2020-01-16 03:28:21 +00:00
|
|
|
dialed bool
|
2020-01-09 05:24:45 +00:00
|
|
|
}
|
|
|
|
|
2020-01-15 04:43:34 +00:00
|
|
|
func New(ctx context.Context, addr string) (*Websocket, error) {
|
|
|
|
return NewCustom(ctx, NewConn(json.Default{}), addr)
|
|
|
|
}
|
2020-01-09 05:24:45 +00:00
|
|
|
|
2020-01-15 04:43:34 +00:00
|
|
|
// NewCustom creates a new undialed Websocket.
|
|
|
|
func NewCustom(
|
|
|
|
ctx context.Context, conn Connection, addr string) (*Websocket, error) {
|
2020-01-09 05:24:45 +00:00
|
|
|
|
2020-01-15 04:43:34 +00:00
|
|
|
ws := &Websocket{
|
|
|
|
Conn: conn,
|
|
|
|
Addr: addr,
|
|
|
|
|
|
|
|
SendLimiter: NewSendLimiter(),
|
|
|
|
DialLimiter: NewDialLimiter(),
|
2020-01-09 05:24:45 +00:00
|
|
|
}
|
|
|
|
|
2020-01-15 04:43:34 +00:00
|
|
|
return ws, nil
|
2020-01-09 05:24:45 +00:00
|
|
|
}
|
|
|
|
|
2020-01-20 11:06:20 +00:00
|
|
|
func (ws *Websocket) Dial(ctx context.Context) error {
|
2020-01-15 04:43:34 +00:00
|
|
|
if err := ws.DialLimiter.Wait(ctx); err != nil {
|
|
|
|
// Expired, fatal error
|
|
|
|
return errors.Wrap(err, "Failed to wait")
|
|
|
|
}
|
2020-01-09 05:24:45 +00:00
|
|
|
|
2020-01-15 04:43:34 +00:00
|
|
|
if err := ws.Conn.Dial(ctx, ws.Addr); err != nil {
|
|
|
|
return errors.Wrap(err, "Failed to dial")
|
2020-01-09 05:24:45 +00:00
|
|
|
}
|
2020-01-15 04:43:34 +00:00
|
|
|
|
|
|
|
return nil
|
2020-01-09 05:24:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (ws *Websocket) Listen() <-chan Event {
|
2020-01-15 04:43:34 +00:00
|
|
|
if ws.listener == nil {
|
|
|
|
ws.listener = ws.Conn.Listen()
|
|
|
|
}
|
|
|
|
return ws.listener
|
2020-01-09 05:24:45 +00:00
|
|
|
}
|
|
|
|
|
2020-01-15 04:43:34 +00:00
|
|
|
func (ws *Websocket) Send(ctx context.Context, b []byte) error {
|
2020-01-09 05:24:45 +00:00
|
|
|
if err := ws.SendLimiter.Wait(ctx); err != nil {
|
|
|
|
return errors.Wrap(err, "SendLimiter failed")
|
|
|
|
}
|
|
|
|
|
2020-01-15 04:43:34 +00:00
|
|
|
return ws.Conn.Send(ctx, b)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (ws *Websocket) Close(err error) error {
|
|
|
|
return ws.Conn.Close(err)
|
2020-01-09 05:24:45 +00:00
|
|
|
}
|