starpc 0.10.7 → 0.10.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -8
- package/package.json +1 -1
- package/srpc/muxed-conn.go +7 -2
- package/srpc/rwc-conn.go +229 -0
package/README.md
CHANGED
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
# Stream RPC
|
|
2
2
|
|
|
3
|
+
[![GoDoc Widget]][GoDoc] [![Go Report Card Widget]][Go Report Card]
|
|
4
|
+
|
|
5
|
+
> Protobuf 3 RPC services over any stream multiplexer.
|
|
6
|
+
|
|
7
|
+
[GoDoc]: https://godoc.org/github.com/aperturerobotics/starpc
|
|
8
|
+
[GoDoc Widget]: https://godoc.org/github.com/aperturerobotics/starpc?status.svg
|
|
9
|
+
[Go Report Card Widget]: https://goreportcard.com/badge/github.com/aperturerobotics/starpc
|
|
10
|
+
[Go Report Card]: https://goreportcard.com/report/github.com/aperturerobotics/starpc
|
|
11
|
+
|
|
12
|
+
## Introduction
|
|
13
|
+
|
|
3
14
|
**starpc** implements [Proto3 services] (server & client) in both TypeScript and Go.
|
|
4
15
|
|
|
5
16
|
[Proto3 services]: https://developers.google.com/protocol-buffers/docs/proto3#services
|
|
6
17
|
|
|
7
18
|
Supports **client-to-server streaming** RPCs in the web browser, currently not
|
|
8
|
-
supported by any of the
|
|
19
|
+
supported by any of the other RPC libraries.
|
|
9
20
|
|
|
10
21
|
The [rpcproto](./srpc/rpcproto.proto) file describes the protocol.
|
|
11
22
|
|
|
@@ -17,7 +28,7 @@ Can use any Stream multiplexer: defaults to [libp2p-mplex] over a WebSocket.
|
|
|
17
28
|
|
|
18
29
|
[rpcstream]: ./rpcstream
|
|
19
30
|
|
|
20
|
-
|
|
31
|
+
## Usage
|
|
21
32
|
|
|
22
33
|
Start with the [protobuf-project] template repository on the "starpc" branch.
|
|
23
34
|
|
|
@@ -26,13 +37,13 @@ Start with the [protobuf-project] template repository on the "starpc" branch.
|
|
|
26
37
|
Use "git add" to add your new .proto files, then `yarn gen` to generate the
|
|
27
38
|
TypeScript and Go code.
|
|
28
39
|
|
|
29
|
-
|
|
40
|
+
## Examples
|
|
30
41
|
|
|
31
42
|
The demo/boilerplate project implements the Echo example below.
|
|
32
43
|
|
|
33
44
|
This repository uses protowrap, see the [Makefile](./Makefile).
|
|
34
45
|
|
|
35
|
-
|
|
46
|
+
### Protobuf
|
|
36
47
|
|
|
37
48
|
The following examples use the [echo](./echo/echo.proto) protobuf sample.
|
|
38
49
|
|
|
@@ -58,7 +69,7 @@ message EchoMsg {
|
|
|
58
69
|
}
|
|
59
70
|
```
|
|
60
71
|
|
|
61
|
-
|
|
72
|
+
### Go
|
|
62
73
|
|
|
63
74
|
This example demonstrates both the server and client:
|
|
64
75
|
|
|
@@ -94,7 +105,7 @@ if out.GetBody() != bodyTxt {
|
|
|
94
105
|
|
|
95
106
|
[e2e test]: ./e2e/e2e_test.go
|
|
96
107
|
|
|
97
|
-
|
|
108
|
+
### TypeScript
|
|
98
109
|
|
|
99
110
|
See the ts-proto README to generate the TypeScript for your protobufs.
|
|
100
111
|
|
|
@@ -155,7 +166,7 @@ for await (const msg of serverStream) {
|
|
|
155
166
|
}
|
|
156
167
|
```
|
|
157
168
|
|
|
158
|
-
|
|
169
|
+
### WebSocket
|
|
159
170
|
|
|
160
171
|
One way to integrate Go and TypeScript is over a WebSocket:
|
|
161
172
|
|
|
@@ -186,7 +197,7 @@ Uses [vtprotobuf] to generate Protobuf marshal / unmarshal code.
|
|
|
186
197
|
|
|
187
198
|
[vtprotobuf]: https://github.com/planetscale/vtprotobuf
|
|
188
199
|
|
|
189
|
-
|
|
200
|
+
## Support
|
|
190
201
|
|
|
191
202
|
Starpc is built & supported by Aperture Robotics, LLC.
|
|
192
203
|
|
package/package.json
CHANGED
package/srpc/muxed-conn.go
CHANGED
|
@@ -10,8 +10,8 @@ import (
|
|
|
10
10
|
mp "github.com/libp2p/go-mplex"
|
|
11
11
|
)
|
|
12
12
|
|
|
13
|
-
// NewMuxedConn constructs a new MuxedConn from a
|
|
14
|
-
func NewMuxedConn(conn
|
|
13
|
+
// NewMuxedConn constructs a new MuxedConn from a net.Conn.
|
|
14
|
+
func NewMuxedConn(conn net.Conn, outbound bool) (network.MuxedConn, error) {
|
|
15
15
|
m, err := mp.NewMultiplex(conn, outbound, nil)
|
|
16
16
|
if err != nil {
|
|
17
17
|
return nil, err
|
|
@@ -19,6 +19,11 @@ func NewMuxedConn(conn io.ReadWriteCloser, outbound bool) (network.MuxedConn, er
|
|
|
19
19
|
return mplex.NewMuxedConn(m), nil
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
// NewMuxedConnWithRwc builds a new MuxedConn with a io.ReadWriteCloser.
|
|
23
|
+
func NewMuxedConnWithRwc(ctx context.Context, rwc io.ReadWriteCloser, outbound bool) (network.MuxedConn, error) {
|
|
24
|
+
return NewMuxedConn(NewRwcConn(ctx, rwc, nil, nil, 10), outbound)
|
|
25
|
+
}
|
|
26
|
+
|
|
22
27
|
// NewClientWithConn constructs the muxer and the client.
|
|
23
28
|
//
|
|
24
29
|
// uses libp2p mplex
|
package/srpc/rwc-conn.go
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
package srpc
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"context"
|
|
5
|
+
"io"
|
|
6
|
+
"net"
|
|
7
|
+
"os"
|
|
8
|
+
"sync"
|
|
9
|
+
"time"
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
// connPktSize is the size of the buffers to use for packets for the RwcConn.
|
|
13
|
+
const connPktSize = 2048
|
|
14
|
+
|
|
15
|
+
// RwcConn implements a Conn with a buffered ReadWriteCloser.
|
|
16
|
+
type RwcConn struct {
|
|
17
|
+
// ctx is the context
|
|
18
|
+
ctx context.Context
|
|
19
|
+
// ctxCancel is the context canceler
|
|
20
|
+
ctxCancel context.CancelFunc
|
|
21
|
+
// rwc is the read-write-closer
|
|
22
|
+
rwc io.ReadWriteCloser
|
|
23
|
+
// laddr is the local addr
|
|
24
|
+
laddr net.Addr
|
|
25
|
+
// raddr is the remote addr
|
|
26
|
+
raddr net.Addr
|
|
27
|
+
|
|
28
|
+
ar sync.Pool // packet arena
|
|
29
|
+
rd time.Time // read deadline
|
|
30
|
+
wd time.Time // write deadline
|
|
31
|
+
packetCh chan []byte // packet ch
|
|
32
|
+
closeErr error
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// NewRwcConn constructs a new packet conn and starts the rx pump.
|
|
36
|
+
func NewRwcConn(
|
|
37
|
+
ctx context.Context,
|
|
38
|
+
rwc io.ReadWriteCloser,
|
|
39
|
+
laddr, raddr net.Addr,
|
|
40
|
+
bufferPacketN int,
|
|
41
|
+
) *RwcConn {
|
|
42
|
+
ctx, ctxCancel := context.WithCancel(ctx)
|
|
43
|
+
if bufferPacketN <= 0 {
|
|
44
|
+
bufferPacketN = 10
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
c := &RwcConn{
|
|
48
|
+
ctx: ctx,
|
|
49
|
+
ctxCancel: ctxCancel,
|
|
50
|
+
rwc: rwc,
|
|
51
|
+
laddr: laddr,
|
|
52
|
+
raddr: raddr,
|
|
53
|
+
packetCh: make(chan []byte, bufferPacketN),
|
|
54
|
+
}
|
|
55
|
+
go func() {
|
|
56
|
+
_ = c.rxPump()
|
|
57
|
+
}()
|
|
58
|
+
return c
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// LocalAddr returns the local network address.
|
|
62
|
+
func (p *RwcConn) LocalAddr() net.Addr {
|
|
63
|
+
return p.laddr
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// RemoteAddr returns the bound remote network address.
|
|
67
|
+
func (p *RwcConn) RemoteAddr() net.Addr {
|
|
68
|
+
return p.raddr
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Read reads data from the connection.
|
|
72
|
+
// Read can be made to time out and return an error after a fixed
|
|
73
|
+
// time limit; see SetDeadline and SetReadDeadline.
|
|
74
|
+
func (p *RwcConn) Read(b []byte) (n int, err error) {
|
|
75
|
+
deadline := p.rd
|
|
76
|
+
ctx := p.ctx
|
|
77
|
+
if !deadline.IsZero() {
|
|
78
|
+
var ctxCancel context.CancelFunc
|
|
79
|
+
ctx, ctxCancel = context.WithDeadline(ctx, deadline)
|
|
80
|
+
defer ctxCancel()
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
var pkt []byte
|
|
84
|
+
var ok bool
|
|
85
|
+
select {
|
|
86
|
+
case <-ctx.Done():
|
|
87
|
+
if !deadline.IsZero() {
|
|
88
|
+
return 0, os.ErrDeadlineExceeded
|
|
89
|
+
}
|
|
90
|
+
return 0, context.Canceled
|
|
91
|
+
case pkt, ok = <-p.packetCh:
|
|
92
|
+
if !ok {
|
|
93
|
+
err = p.closeErr
|
|
94
|
+
if err == nil {
|
|
95
|
+
err = io.EOF
|
|
96
|
+
}
|
|
97
|
+
return 0, err
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
pl := len(pkt)
|
|
102
|
+
copy(b, pkt)
|
|
103
|
+
p.ar.Put(&pkt)
|
|
104
|
+
if len(b) < pl {
|
|
105
|
+
return len(b), io.ErrShortBuffer
|
|
106
|
+
}
|
|
107
|
+
return pl, nil
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Write writes data to the connection.
|
|
111
|
+
func (p *RwcConn) Write(pkt []byte) (n int, err error) {
|
|
112
|
+
if len(pkt) == 0 {
|
|
113
|
+
return 0, nil
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
written := 0
|
|
117
|
+
for written < len(pkt) {
|
|
118
|
+
n, err = p.rwc.Write(pkt[written:])
|
|
119
|
+
written += n
|
|
120
|
+
if err != nil {
|
|
121
|
+
return written, err
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return written, nil
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// SetDeadline sets the read and write deadlines associated
|
|
128
|
+
// with the connection. It is equivalent to calling both
|
|
129
|
+
// SetReadDeadline and SetWriteDeadline.
|
|
130
|
+
//
|
|
131
|
+
// A deadline is an absolute time after which I/O operations
|
|
132
|
+
// fail instead of blocking. The deadline applies to all future
|
|
133
|
+
// and pending I/O, not just the immediately following call to
|
|
134
|
+
// Read or Write. After a deadline has been exceeded, the
|
|
135
|
+
// connection can be refreshed by setting a deadline in the future.
|
|
136
|
+
//
|
|
137
|
+
// If the deadline is exceeded a call to Read or Write or to other
|
|
138
|
+
// I/O methods will return an error that wraps os.ErrDeadlineExceeded.
|
|
139
|
+
// This can be tested using errors.Is(err, os.ErrDeadlineExceeded).
|
|
140
|
+
// The error's Timeout method will return true, but note that there
|
|
141
|
+
// are other possible errors for which the Timeout method will
|
|
142
|
+
// return true even if the deadline has not been exceeded.
|
|
143
|
+
//
|
|
144
|
+
// An idle timeout can be implemented by repeatedly extending
|
|
145
|
+
// the deadline after successful ReadFrom or WriteTo calls.
|
|
146
|
+
//
|
|
147
|
+
// A zero value for t means I/O operations will not time out.
|
|
148
|
+
func (p *RwcConn) SetDeadline(t time.Time) error {
|
|
149
|
+
p.rd = t
|
|
150
|
+
p.wd = t
|
|
151
|
+
return nil
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// SetReadDeadline sets the deadline for future ReadFrom calls
|
|
155
|
+
// and any currently-blocked ReadFrom call.
|
|
156
|
+
// A zero value for t means ReadFrom will not time out.
|
|
157
|
+
func (p *RwcConn) SetReadDeadline(t time.Time) error {
|
|
158
|
+
p.rd = t
|
|
159
|
+
return nil
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// SetWriteDeadline sets the deadline for future WriteTo calls
|
|
163
|
+
// and any currently-blocked WriteTo call.
|
|
164
|
+
// Even if write times out, it may return n > 0, indicating that
|
|
165
|
+
// some of the data was successfully written.
|
|
166
|
+
// A zero value for t means WriteTo will not time out.
|
|
167
|
+
func (p *RwcConn) SetWriteDeadline(t time.Time) error {
|
|
168
|
+
p.wd = t
|
|
169
|
+
return nil
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Close closes the connection.
|
|
173
|
+
// Any blocked ReadFrom or WriteTo operations will be unblocked and return errors.
|
|
174
|
+
func (p *RwcConn) Close() error {
|
|
175
|
+
return p.rwc.Close()
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// getArenaBuf returns a buf from the packet arena with at least the given size
|
|
179
|
+
func (p *RwcConn) getArenaBuf(size int) []byte {
|
|
180
|
+
var buf []byte
|
|
181
|
+
bufp := p.ar.Get()
|
|
182
|
+
if bufp != nil {
|
|
183
|
+
buf = *bufp.(*[]byte)
|
|
184
|
+
}
|
|
185
|
+
if size != 0 {
|
|
186
|
+
if cap(buf) < size {
|
|
187
|
+
buf = make([]byte, size)
|
|
188
|
+
} else {
|
|
189
|
+
buf = buf[:size]
|
|
190
|
+
}
|
|
191
|
+
} else {
|
|
192
|
+
buf = buf[:cap(buf)]
|
|
193
|
+
}
|
|
194
|
+
return buf
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// rxPump receives messages from the underlying connection.
|
|
198
|
+
func (p *RwcConn) rxPump() (rerr error) {
|
|
199
|
+
defer func() {
|
|
200
|
+
p.closeErr = rerr
|
|
201
|
+
close(p.packetCh)
|
|
202
|
+
}()
|
|
203
|
+
|
|
204
|
+
for {
|
|
205
|
+
select {
|
|
206
|
+
case <-p.ctx.Done():
|
|
207
|
+
return p.ctx.Err()
|
|
208
|
+
default:
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
pktBuf := p.getArenaBuf(int(connPktSize))
|
|
212
|
+
n, err := p.rwc.Read(pktBuf)
|
|
213
|
+
if n == 0 {
|
|
214
|
+
p.ar.Put(&pktBuf)
|
|
215
|
+
} else {
|
|
216
|
+
select {
|
|
217
|
+
case <-p.ctx.Done():
|
|
218
|
+
return context.Canceled
|
|
219
|
+
case p.packetCh <- pktBuf[:n]:
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
if err != nil {
|
|
223
|
+
return err
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// _ is a type assertion
|
|
229
|
+
var _ net.Conn = ((*RwcConn)(nil))
|