func (t *Transport) Close() error {
	t.t.Kill(nil)
	return t.t.Wait()
}
Close implements interfaces.CallCloser.
package websocket
import (
	"io"
	"context"
	"net"
	"time"
	"github.com/gorilla/websocket"
	"github.com/pkg/errors"
	"github.com/sourcegraph/jsonrpc2"
	tomb "gopkg.in/tomb.v2"
)
const (
	DefaultHandshakeTimeout      = 30 * time.Second
	DefaultWriteTimeout          = 10 * time.Second
	DefaultReadTimeout           = 20 * time.Second
	DefaultAutoReconnectMaxDelay = 1 * time.Minute
	InitialAutoReconnectDelay       = 1 * time.Second
	AutoReconnectBackoffCoefficient = 1.5
)
var netDialer net.Dialer
type Transport struct {
	urls         []string
	nextURLIndex int
	currentURL   string
	handshakeTimeout time.Duration
	readTimeout      time.Duration
	writeTimeout     time.Duration
	autoReconnectEnabled  bool
	autoReconnectMaxDelay time.Duration
	monitorChan chan<- interface{}
	connCh chan chan *jsonrpc2.Conn
	errCh  chan error
	t *tomb.Tomb
}
type Option func(*Transport)
func SetDialTimeout(timeout time.Duration) Option {
	return SetHandshakeTimeout(timeout)
}
func SetHandshakeTimeout(timeout time.Duration) Option {
	return func(t *Transport) {
		t.handshakeTimeout = timeout
	}
}
func SetReadTimeout(timeout time.Duration) Option {
	return func(t *Transport) {
		t.readTimeout = timeout
	}
}
func SetWriteTimeout(timeout time.Duration) Option {
	return func(t *Transport) {
		t.writeTimeout = timeout
	}
}
func SetReadWriteTimeout(timeout time.Duration) Option {
	return func(t *Transport) {
		t.readTimeout = timeout
		t.writeTimeout = timeout
	}
}
func SetAutoReconnectEnabled(enabled bool) Option {
	return func(t *Transport) {
		t.autoReconnectEnabled = enabled
	}
}
func SetAutoReconnectMaxDelay(delay time.Duration) Option {
	return func(t *Transport) {
		t.autoReconnectMaxDelay = delay
	}
}
func SetMonitor(monitorChan chan<- interface{}) Option {
	return func(t *Transport) {
		t.monitorChan = monitorChan
	}
}
func NewTransport(urls []string, options ...Option) (*Transport, error) {
	t := &Transport{
		urls:                  urls,
		handshakeTimeout:      DefaultHandshakeTimeout,
		readTimeout:           DefaultReadTimeout,
		writeTimeout:          DefaultWriteTimeout,
		autoReconnectMaxDelay: DefaultAutoReconnectMaxDelay,
		connCh:                make(chan chan *jsonrpc2.Conn),
		errCh:                 make(chan error),
		t:                     &tomb.Tomb{},
	}
	for _, opt := range options {
		opt(t)
	}
	t.t.Go(t.dialer)
	return t, nil
}
func (t *Transport) Call(method string, params, result interface{}) error {
	ctx := t.t.Context(nil)
Loop:
	for {
		connCh := make(chan *jsonrpc2.Conn, 1)
		select {
		case t.connCh <- connCh:
		case <-ctx.Done():
			return errors.Wrap(ctx.Err(), "context closed")
		}
		conn := <-connCh
		err := conn.Call(ctx, method, params, result)
		if err == nil {
			return nil
		}
		if err := ctx.Err(); err != nil {
			return errors.Wrap(err, "context closed")
		}
		if !t.autoReconnectEnabled {
			return errors.Wrap(err, "call failed")
		}
		err = errors.Cause(err)
		if _, ok := err.(*websocket.CloseError); ok || err == io.ErrUnexpectedEOF {
			select {
			case t.errCh <- errors.Wrap(err, "WebSocket closed"):
				continue Loop
			case <-ctx.Done():
				return errors.Wrap(ctx.Err(), "context closed")
			}
		}
		return errors.Wrap(err, "call failed")
	}
}
func (t *Transport) dialer() error {
	ctx := t.t.Context(nil)
	var conn *jsonrpc2.Conn
	defer func() {
		if conn != nil {
			conn.Close()
			err := errors.Wrap(ctx.Err(), "context closed")
			t.emit(&DisconnectedEvent{t.currentURL, err})
		}
	}()
	connect := func() {
		delay := InitialAutoReconnectDelay
		for {
			var err error
			conn, err = t.dial(ctx)
			if err == nil {
				break
			}
			select {
			case <-time.After(delay):
				delay = time.Duration(float64(delay) * AutoReconnectBackoffCoefficient)
				if delay > t.autoReconnectMaxDelay {
					delay = t.autoReconnectMaxDelay
				}
			case <-ctx.Done():
				return
			}
		}
	}
	connect()
	for {
		select {
		case connCh := <-t.connCh:
			connCh <- conn
		case err := <-t.errCh:
			conn.Close()
			t.emit(&DisconnectedEvent{t.currentURL, err})
			connect()
		case <-ctx.Done():
			return nil
		}
	}
}
func (t *Transport) dial(ctx context.Context) (*jsonrpc2.Conn, error) {
	dialer := websocket.Dialer{
		NetDial: func(network, addr string) (net.Conn, error) {
			return netDialer.DialContext(ctx, network, addr)
		},
		HandshakeTimeout: t.handshakeTimeout,
	}
	u := t.urls[t.nextURLIndex]
	t.nextURLIndex = (t.nextURLIndex + 1) % len(t.urls)
	t.currentURL = u
	t.emit(&ConnectingEvent{u})
	ws, _, err := dialer.Dial(u, nil)
	if err != nil {
		err = errors.Wrapf(err, "failed to dial %v", u)
		t.emit(&DisconnectedEvent{u, err})
		return nil, err
	}
	t.emit(&ConnectedEvent{u})
	stream := NewObjectStream(ws, t.writeTimeout, t.readTimeout)
	return jsonrpc2.NewConn(ctx, stream, nil), nil
}
func (t *Transport) emit(v interface{}) {
	if t.monitorChan != nil {
		select {
		case t.monitorChan <- v:
		default:
		}
	}
}