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 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 major RPC libraries.
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
- # Usage
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
- # Examples
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
- ## Protobuf
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
- ## Go
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
- ## TypeScript
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
- ## WebSocket
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
- # Support
200
+ ## Support
190
201
 
191
202
  Starpc is built & supported by Aperture Robotics, LLC.
192
203
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "starpc",
3
- "version": "0.10.7",
3
+ "version": "0.10.8",
4
4
  "description": "Streaming protobuf RPC service protocol over any two-way channel.",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -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 ReadWriteCloser.
14
- func NewMuxedConn(conn io.ReadWriteCloser, outbound bool) (network.MuxedConn, error) {
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
@@ -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))