good morning!!!!

Skip to content
Snippets Groups Projects
reconnecting.go 1.71 KiB
Newer Older
a's avatar
a committed
package client

import (
	"context"
	"sync"

	"gfx.cafe/open/jrpc"
a's avatar
a committed
	"gfx.cafe/open/jrpc/pkg/jsonrpc"
a's avatar
a committed
)

a's avatar
ok  
a committed
var _ jrpc.Conn = (*Reconnecting)(nil)
a's avatar
a committed

a's avatar
a committed
type Reconnecting struct {
a's avatar
ok  
a committed
	dialer     func(ctx context.Context) (jrpc.Conn, error)
	base       jsonrpc.Conn
	alive      bool
a's avatar
a committed
	middleware []jsonrpc.Middleware
a's avatar
a committed

	mu sync.Mutex
}

a's avatar
a committed
func (r *Reconnecting) Notify(ctx context.Context, method string, params any) error {
	return r.base.Notify(ctx, method, params)
}

a's avatar
ok  
a committed
func NewReconnecting(dialer func(ctx context.Context) (jrpc.Conn, error)) *Reconnecting {
a's avatar
a committed
	r := &Reconnecting{
		dialer: dialer,
	}
	return r
}

a's avatar
ok  
a committed
func (r *Reconnecting) getClient(ctx context.Context) (jrpc.Conn, error) {
	reconnect := func() error {
		conn, err := r.dialer(ctx)
		if err != nil {
			return err
		}
		r.base = conn
		r.alive = true
		return nil
	}
	r.mu.Lock()
	defer r.mu.Unlock()
	if r.base == nil {
		err := reconnect()
		if err != nil {
			return nil, err
		}
a's avatar
a committed
	} else {
		select {
		case <-r.base.Closed():
			err := reconnect()
			if err != nil {
				return nil, err
			}
		default:
a's avatar
a committed
	return r.base, nil
}

func (r *Reconnecting) Do(ctx context.Context, result any, method string, params any) error {
	errChan := make(chan error)
	go func() {
		conn, err := r.getClient(ctx)
		if err != nil {
			errChan <- err
			return
		}
		errChan <- conn.Do(ctx, result, method, params)
	}()
	return <-errChan
}

a's avatar
a committed
func (r *Reconnecting) Mount(m jsonrpc.Middleware) {
a's avatar
a committed
	r.middleware = append(r.middleware, m)
}

// why would you want to do this....
func (r *Reconnecting) Close() error {
a's avatar
a committed
	conn, err := r.getClient(context.Background())
	if err != nil {
		return err
	}
	return conn.Close()
a's avatar
a committed
}

// never....
func (r *Reconnecting) Closed() <-chan struct{} {
	return make(<-chan struct{})
}