starpc 0.7.1 → 0.8.0

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.
@@ -34,4 +34,6 @@ export async function runRpcStreamTest(client) {
34
34
  console.log('Calling Echo via RPC stream...');
35
35
  const resp = await proxiedService.Echo({ body: 'hello world via proxy' });
36
36
  console.log('rpc stream test: succeeded: response: ' + resp.body);
37
+ console.log('Running client test over RPC stream...');
38
+ await runClientTest(proxiedClient);
37
39
  }
@@ -40,7 +40,11 @@ export async function runRpcStreamTest(client: Client) {
40
40
  )
41
41
  const proxiedClient = new Client(openStreamFn)
42
42
  const proxiedService = new EchoerClientImpl(proxiedClient)
43
+
43
44
  console.log('Calling Echo via RPC stream...')
44
45
  const resp = await proxiedService.Echo({ body: 'hello world via proxy' })
45
46
  console.log('rpc stream test: succeeded: response: ' + resp.body)
47
+
48
+ console.log('Running client test over RPC stream...')
49
+ await runClientTest(proxiedClient)
46
50
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "starpc",
3
- "version": "0.7.1",
3
+ "version": "0.8.0",
4
4
  "description": "Streaming protobuf RPC service protocol over any two-way channel.",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -4,6 +4,7 @@ import (
4
4
  "context"
5
5
 
6
6
  "github.com/pkg/errors"
7
+ "github.com/sirupsen/logrus"
7
8
  )
8
9
 
9
10
  // ClientRPC represents the client side of an on-going RPC call message stream.
@@ -101,6 +102,16 @@ func (r *ClientRPC) HandlePacketData(data []byte) error {
101
102
  return r.HandlePacket(pkt)
102
103
  }
103
104
 
105
+ // HandleStreamClose handles the incoming stream closing w/ optional error.
106
+ func (r *ClientRPC) HandleStreamClose(closeErr error) {
107
+ if closeErr != nil {
108
+ if r.serverErr == nil {
109
+ r.serverErr = closeErr
110
+ }
111
+ r.Close()
112
+ }
113
+ }
114
+
104
115
  // HandlePacket handles an incoming parsed message packet.
105
116
  // Not concurrency safe: use a mutex if calling concurrently.
106
117
  func (r *ClientRPC) HandlePacket(msg *Packet) error {
@@ -130,6 +141,7 @@ func (r *ClientRPC) HandleCallData(pkt *CallData) error {
130
141
  return ErrCompleted
131
142
  }
132
143
 
144
+ logrus.Infof("DEBUG: handling call data for %s/%s: %s", r.service, r.method, pkt)
133
145
  if data := pkt.GetData(); len(data) != 0 || pkt.GetDataIsZero() {
134
146
  select {
135
147
  case <-r.ctx.Done():
package/srpc/client.go CHANGED
@@ -19,7 +19,11 @@ type Client interface {
19
19
 
20
20
  // OpenStreamFunc opens a stream with a remote.
21
21
  // msgHandler must not be called concurrently.
22
- type OpenStreamFunc = func(ctx context.Context, msgHandler PacketHandler) (Writer, error)
22
+ type OpenStreamFunc = func(
23
+ ctx context.Context,
24
+ msgHandler PacketHandler,
25
+ closeHandler CloseHandler,
26
+ ) (Writer, error)
23
27
 
24
28
  // client implements Client with a transport.
25
29
  type client struct {
@@ -44,7 +48,7 @@ func (c *client) Invoke(rctx context.Context, service, method string, in, out Me
44
48
  return err
45
49
  }
46
50
  clientRPC := NewClientRPC(ctx, service, method)
47
- writer, err := c.openStream(ctx, clientRPC.HandlePacket)
51
+ writer, err := c.openStream(ctx, clientRPC.HandlePacket, clientRPC.HandleStreamClose)
48
52
  if err != nil {
49
53
  return err
50
54
  }
@@ -81,7 +85,7 @@ func (c *client) NewStream(ctx context.Context, service, method string, firstMsg
81
85
  }
82
86
 
83
87
  clientRPC := NewClientRPC(ctx, service, method)
84
- writer, err := c.openStream(ctx, clientRPC.HandlePacket)
88
+ writer, err := c.openStream(ctx, clientRPC.HandlePacket, clientRPC.HandleStreamClose)
85
89
  if err != nil {
86
90
  return nil, err
87
91
  }
@@ -14,18 +14,13 @@ func NewClientWithMuxedConn(conn network.MuxedConn) Client {
14
14
 
15
15
  // NewOpenStreamWithMuxedConn constructs a OpenStream func with a MuxedConn.
16
16
  func NewOpenStreamWithMuxedConn(conn network.MuxedConn) OpenStreamFunc {
17
- return func(ctx context.Context, msgHandler PacketHandler) (Writer, error) {
17
+ return func(ctx context.Context, msgHandler PacketHandler, closeHandler CloseHandler) (Writer, error) {
18
18
  mstrm, err := conn.OpenStream(ctx)
19
19
  if err != nil {
20
20
  return nil, err
21
21
  }
22
22
  rw := NewPacketReadWriter(mstrm)
23
- go func() {
24
- err := rw.ReadPump(msgHandler)
25
- if err != nil {
26
- _ = rw.Close()
27
- }
28
- }()
23
+ go rw.ReadPump(msgHandler, closeHandler)
29
24
  return rw, nil
30
25
  }
31
26
  }
package/srpc/packet-rw.go CHANGED
@@ -39,10 +39,23 @@ func (r *PacketReaderWriter) WritePacket(p *Packet) error {
39
39
  }
40
40
 
41
41
  // ReadPump executes the read pump in a goroutine.
42
- func (r *PacketReaderWriter) ReadPump(cb PacketHandler) error {
42
+ //
43
+ // calls the handler when closed or returning an error
44
+ func (r *PacketReaderWriter) ReadPump(cb PacketHandler, closed CloseHandler) {
45
+ err := r.ReadToHandler(cb)
46
+ // signal that the stream is now closed.
47
+ if closed != nil {
48
+ closed(err)
49
+ }
50
+ }
51
+
52
+ // ReadToHandler reads data to the given handler.
53
+ // Does not handle closing the stream, use ReadPump instead.
54
+ func (r *PacketReaderWriter) ReadToHandler(cb PacketHandler) error {
43
55
  var currLen uint32
44
56
  buf := make([]byte, 2048)
45
57
  for {
58
+ // read some data into the buffer
46
59
  n, err := r.rw.Read(buf)
47
60
  if err != nil {
48
61
  if err == io.EOF {
@@ -50,17 +63,20 @@ func (r *PacketReaderWriter) ReadPump(cb PacketHandler) error {
50
63
  }
51
64
  return err
52
65
  }
66
+ // push the data to r.buf
53
67
  _, err = r.buf.Write(buf[:n])
54
68
  if err != nil {
55
69
  return err
56
70
  }
57
71
 
58
- // check if we have enough for a length prefix
72
+ // check if we have enough data for a length prefix
59
73
  bufLen := r.buf.Len()
74
+ if bufLen < 4 {
75
+ continue
76
+ }
77
+
78
+ // parse the length prefix if not done already
60
79
  if currLen == 0 {
61
- if bufLen < 4 {
62
- continue
63
- }
64
80
  currLen = r.readLengthPrefix(r.buf.Bytes())
65
81
  if currLen == 0 {
66
82
  return errors.New("unexpected zero len prefix")
@@ -69,6 +85,8 @@ func (r *PacketReaderWriter) ReadPump(cb PacketHandler) error {
69
85
  return errors.Errorf("message size %v greater than maximum %v", currLen, maxMessageSize)
70
86
  }
71
87
  }
88
+
89
+ // emit the packet if fully buffered
72
90
  if currLen != 0 && bufLen >= int(currLen)+4 {
73
91
  pkt := r.buf.Next(int(currLen + 4))[4:]
74
92
  currLen = 0
package/srpc/packet.go CHANGED
@@ -1,8 +1,14 @@
1
1
  package srpc
2
2
 
3
3
  // PacketHandler handles a packet.
4
+ //
5
+ // pkt is optional (can be nil)
6
+ // if closeErr is set, the stream is closed after pkt.
4
7
  type PacketHandler = func(pkt *Packet) error
5
8
 
9
+ // CloseHandler handles the stream closing with an optional error.
10
+ type CloseHandler = func(closeErr error)
11
+
6
12
  // Validate performs cursory validation of the packet.
7
13
  func (p *Packet) Validate() error {
8
14
  switch b := p.GetBody().(type) {
@@ -9,18 +9,13 @@ import (
9
9
  // Stream with the given Server. Starts read pumps for both. Starts the
10
10
  // HandleStream function on the server in a separate goroutine.
11
11
  func NewServerPipe(server *Server) OpenStreamFunc {
12
- return func(ctx context.Context, msgHandler PacketHandler) (Writer, error) {
12
+ return func(ctx context.Context, msgHandler PacketHandler, closeHandler CloseHandler) (Writer, error) {
13
13
  srvPipe, clientPipe := net.Pipe()
14
14
  go func() {
15
15
  _ = server.HandleStream(ctx, srvPipe)
16
16
  }()
17
17
  clientPrw := NewPacketReadWriter(clientPipe)
18
- go func() {
19
- err := clientPrw.ReadPump(msgHandler)
20
- if err != nil {
21
- _ = clientPrw.Close()
22
- }
23
- }()
18
+ go clientPrw.ReadPump(msgHandler, closeHandler)
24
19
  return clientPrw, nil
25
20
  }
26
21
  }
@@ -29,7 +29,7 @@ type ServerRPC struct {
29
29
  dataChClosed bool
30
30
  // clientErr is an error set by the client.
31
31
  // before dataCh is closed, managed by HandlePacket.
32
- // immutable after dataCh is closed.
32
+ // immutable after dataCh is closed or ctxCancel
33
33
  clientErr error
34
34
  }
35
35
 
@@ -54,9 +54,32 @@ func (r *ServerRPC) Context() context.Context {
54
54
  return r.ctx
55
55
  }
56
56
 
57
+ // Wait waits for the RPC to finish.
58
+ func (r *ServerRPC) Wait(ctx context.Context) error {
59
+ select {
60
+ case <-ctx.Done():
61
+ return context.Canceled
62
+ case <-r.ctx.Done():
63
+ }
64
+ return r.clientErr
65
+ }
66
+
67
+ // HandleStreamClose handles the incoming stream closing w/ optional error.
68
+ func (r *ServerRPC) HandleStreamClose(closeErr error) {
69
+ if closeErr != nil {
70
+ if r.clientErr == nil {
71
+ r.clientErr = closeErr
72
+ }
73
+ r.Close()
74
+ }
75
+ }
76
+
57
77
  // HandlePacket handles an incoming parsed message packet.
58
78
  // Not concurrency safe: use a mutex if calling concurrently.
59
79
  func (r *ServerRPC) HandlePacket(msg *Packet) error {
80
+ if msg == nil {
81
+ return nil
82
+ }
60
83
  if err := msg.Validate(); err != nil {
61
84
  return err
62
85
  }
@@ -147,6 +170,9 @@ func (r *ServerRPC) invokeRPC() {
147
170
  // Close releases any resources held by the ServerRPC.
148
171
  // not concurrency safe with HandlePacket.
149
172
  func (r *ServerRPC) Close() {
173
+ if r.clientErr == nil {
174
+ r.clientErr = context.Canceled
175
+ }
150
176
  r.ctxCancel()
151
177
  if r.service == "" {
152
178
  // invokeRPC has not been called, otherwise it would call Close()
package/srpc/server.go CHANGED
@@ -32,9 +32,8 @@ func (s *Server) HandleStream(ctx context.Context, rwc io.ReadWriteCloser) error
32
32
  serverRPC := NewServerRPC(subCtx, s.mux)
33
33
  prw := NewPacketReadWriter(rwc)
34
34
  serverRPC.SetWriter(prw)
35
- err := prw.ReadPump(serverRPC.HandlePacket)
36
- _ = rwc.Close()
37
- return err
35
+ go prw.ReadPump(serverRPC.HandlePacket, serverRPC.HandleStreamClose)
36
+ return serverRPC.Wait(ctx)
38
37
  }
39
38
 
40
39
  // AcceptMuxedConn runs a loop which calls Accept on a muxer to handle streams.
package/srpc/websocket.go CHANGED
@@ -43,19 +43,14 @@ func (w *WebSocketConn) AcceptStream() (io.ReadWriteCloser, error) {
43
43
  }
44
44
 
45
45
  // OpenStream tries to open a stream with the remote.
46
- func (w *WebSocketConn) OpenStream(ctx context.Context, msgHandler PacketHandler) (Writer, error) {
46
+ func (w *WebSocketConn) OpenStream(ctx context.Context, msgHandler PacketHandler, closeHandler CloseHandler) (Writer, error) {
47
47
  muxedStream, err := w.mconn.OpenStream(ctx)
48
48
  if err != nil {
49
49
  return nil, err
50
50
  }
51
51
 
52
52
  rw := NewPacketReadWriter(muxedStream)
53
- go func() {
54
- err := rw.ReadPump(msgHandler)
55
- if err != nil {
56
- _ = rw.Close()
57
- }
58
- }()
53
+ go rw.ReadPump(msgHandler, closeHandler)
59
54
  return rw, nil
60
55
  }
61
56