An open API service indexing awesome lists of open source software.

https://github.com/smartcontractkit/wsrpc

A bi-directional Websockets RPC library
https://github.com/smartcontractkit/wsrpc

Last synced: about 2 months ago
JSON representation

A bi-directional Websockets RPC library

Awesome Lists containing this project

README

        

# Websockets RPC

Establishes a persistent bi-directional communication channel using mTLS and websockets.

## Set up

In order to generate a service definition you will need the wsrpc protoc plugin.

Build the protoc plugin

```
cd cmd/protoc-gen-go-wsrpc
go build
```

Place the resulting binary in your GOPATH.

In the directory containing your protobuf service definition, run:

```
protoc --go_out=. --go_opt=paths=source_relative \
--go-wsrpc_out=. \
--go-wsrpc_opt=paths=source_relative yourproto.proto
```

This will generate the service definitions in *_wsrpc.pb.go

## Usage

### Client to Server RPC

Implement handlers for the server
```go
type pingServer struct {}

func (s *pingServer) Ping(ctx context.Context, req *pb.PingRequest) (*pb.PingResponse, error) {
// Extracts the connection client's public key.
// You can use this to identify the client
p, ok := peer.FromContext(ctx)
if !ok {
return nil, errors.New("could not extract public key")
}
pubKey := p.PublicKey

fmt.Println(pubKey)

return &pb.PingResponse{
Body: "Pong",
}, nil
}
```

Initialize a server with the server's private key and a slice of allowable public keys.

```go
lis, err := net.Listen("tcp", "127.0.0.1:1337")
if err != nil {
log.Fatalf("[MAIN] failed to listen: %v", err)
}
s := wsrpc.NewServer(wsrpc.Creds(privKey, pubKeys))
// Register the ping server implementation with the wsrpc server
pb.RegisterPingServer(s, &pingServer{})

s.Serve(lis)
```

Initialize a client with the client's private key and the server's public key

```go
conn, err := wsrpc.Dial("127.0.0.1:1338", wsrpc.WithTransportCreds(privKey, serverPubKey))
if err != nil {
log.Fatalln(err)
}
defer conn.Close()

// Initialize a new wsrpc client caller
// This is used to called RPC methods on the server
c := pb.NewPingClient(conn)

c.Ping(context.Background(), &pb.Ping{Body: "Ping"})
```

### Server to Client RPC

Implement handlers for the client

```go
type pingClient struct{}

func (c *pingClient) Ping(ctx context.Context, req *pb.PingRequest) (*pb.PingResponse, error) {
return &pb.PingResponse{
Body: "Pong",
}, nil
}
```

Initialize a server with the server's private key and a slice of allowable public keys.

```go
lis, err := net.Listen("tcp", "127.0.0.1:1337")
if err != nil {
log.Fatalf("[MAIN] failed to listen: %v", err)
}
s := wsrpc.NewServer(wsrpc.Creds(privKey, pubKeys))
c := pb.NewPingClient(s)

s.Serve(lis)

// Call the RPC method with the pub key so we know which connection to send it to
// otherwise it will error.
ctx := peer.NewCallContext(context.Background(), pubKey)
c.Ping(ctx, &pb.PingRequest{Body: "Ping"})
```

Initialize a client with the client's private key and the server's public key

```go
conn, err := wsrpc.Dial("127.0.0.1:1337", wsrpc.WithTransportCreds(privKey, serverPubKey))
if err != nil {
log.Fatalln(err)
}
defer conn.Close()

// Initialize RPC call handlers on the client connection
pb.RegisterPingServer(conn, &pingClient{})
```

## Example

You can run a simple example where both the client and server implement a Ping service, and perform RPC calls to each other every 5 seconds.

1. Run the server in `examples/simple/server` with `go run main.go`
2. Run a client (Alice) in `examples/simple/server` with `go run main.go 0`
3. Run a client (Bob) in `examples/simple/server` with `go run main.go 1`
4. Run a invalid client (Charlie) in `examples/simple/server` with `go run main.go 2`. The server will reject this connection.

While the client's are connected, kill the server and see the client's enter a backoff retry loop. Start the server again and they will reconnect.

## TODO

- [ ] Improve Tests
- [ ] Return a response status
- [x] Add a Blocking DialOption

## Release Process

The release process for this package is based off [smartcontractkit/releng-go-lib](https://github.com/smartcontractkit/releng-go-lib). This release process leverages [changesets](https://github.com/changesets/changesets).

### Setup

- Install `pnpm` ([pnpm.io/installation](https://pnpm.io/installation))
- Run `pnpm install`

### General usage

During a regular change, include a `changeset` file in the PR by running `pnpm changeset`. It will prompt you for a description of the change, and whether the change is a major/minor/patch version bump.

This will create a file in the `.changeset` directory. When a release is created this file will be "consumed", applying the version bump, and including the change's description to the release's notes.

### Creating a release

When `changeset` files are present on the main branch, there will be a persistent pull request open. This pull request "consumes" the changeset files, and bumps the version in `package.json`.
When this PR is merged, the automated workflow running against `main` will create a tag and release.