starpc 0.3.4 → 0.3.5
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 +41 -31
- package/package.json +1 -1
- package/srpc/server-http.go +1 -1
- package/srpc/server-pipe.go +2 -2
- package/srpc/server.go +29 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Stream RPC
|
|
2
2
|
|
|
3
|
-
**starpc** implements [Proto3 services] in both TypeScript and Go.
|
|
3
|
+
**starpc** implements [Proto3 services] (server & client) in both TypeScript and Go.
|
|
4
4
|
|
|
5
5
|
[Proto3 services]: https://developers.google.com/protocol-buffers/docs/proto3#services
|
|
6
6
|
|
|
@@ -13,8 +13,6 @@ Can use any Stream multiplexer: defaults to [libp2p-mplex] over a WebSocket.
|
|
|
13
13
|
|
|
14
14
|
[libp2p-mplex]: https://github.com/libp2p/js-libp2p-mplex
|
|
15
15
|
|
|
16
|
-
Note: the server has not yet been implemented in TypeScript.
|
|
17
|
-
|
|
18
16
|
# Usage
|
|
19
17
|
|
|
20
18
|
Starting with the [protobuf-project] repository on the "starpc" branch.
|
|
@@ -30,6 +28,8 @@ The demo/boilerplate project implements the Echo example below.
|
|
|
30
28
|
|
|
31
29
|
[protobuf-project]: https://github.com/aperturerobotics/protobuf-project/tree/starpc
|
|
32
30
|
|
|
31
|
+
This repository uses protowrap, see the [Makefile](./Makefile).
|
|
32
|
+
|
|
33
33
|
## Protobuf
|
|
34
34
|
|
|
35
35
|
The following examples use the [echo](./echo/echo.proto) protobuf sample.
|
|
@@ -56,9 +56,9 @@ message EchoMsg {
|
|
|
56
56
|
}
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
-
## Go
|
|
59
|
+
## Go
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
This example demonstrates both the server and client:
|
|
62
62
|
|
|
63
63
|
```go
|
|
64
64
|
// construct the server
|
|
@@ -96,39 +96,58 @@ if out.GetBody() != bodyTxt {
|
|
|
96
96
|
|
|
97
97
|
See the ts-proto README to generate the TypeScript for your protobufs.
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
For an example of Go <-> TypeScript interop, see the [integration] test. For an
|
|
100
|
+
example of TypeScript <-> TypeScript interop, see the [e2e] test.
|
|
100
101
|
|
|
101
|
-
|
|
102
|
-
|
|
102
|
+
[e2e]: ./e2e/e2e.ts
|
|
103
|
+
[integration]: ./integration/integration.ts
|
|
103
104
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
Supports any AsyncIterable communication channel. `DuplexConn`,
|
|
106
|
+
`MessagePortConn`, and `WebSocketConn` use [js-libp2p-mplex] to multiplex
|
|
107
|
+
streams, but any multiplexer can be used.
|
|
107
108
|
|
|
108
109
|
[js-libp2p-mplex]: https://github.com/libp2p/js-libp2p-mplex
|
|
109
110
|
|
|
110
|
-
|
|
111
|
+
This example demonstrates both the server and client:
|
|
111
112
|
|
|
112
113
|
```typescript
|
|
113
|
-
import {
|
|
114
|
-
import {
|
|
114
|
+
import { pipe } from 'it-pipe'
|
|
115
|
+
import { createHandler, createMux, Server, Client, Conn } from 'srpc'
|
|
116
|
+
import { EchoerDefinition, EchoerServer, runClientTest } from 'srpc/echo'
|
|
115
117
|
|
|
116
118
|
const mux = createMux()
|
|
117
|
-
|
|
119
|
+
const echoer = new EchoerServer()
|
|
120
|
+
mux.register(createHandler(EchoerDefinition, echoer))
|
|
118
121
|
const server = new Server(mux)
|
|
119
122
|
|
|
120
|
-
|
|
121
|
-
const
|
|
122
|
-
|
|
123
|
-
|
|
123
|
+
const clientConn = new Conn()
|
|
124
|
+
const serverConn = new Conn(server)
|
|
125
|
+
pipe(clientConn, serverConn, clientConn)
|
|
126
|
+
const client = new Client(clientConn.buildOpenStreamFunc())
|
|
127
|
+
|
|
128
|
+
console.log('Calling Echo: unary call...')
|
|
129
|
+
let result = await demoServiceClient.Echo({
|
|
130
|
+
body: 'Hello world!',
|
|
131
|
+
})
|
|
132
|
+
console.log('success: output', result.body)
|
|
133
|
+
|
|
134
|
+
const clientRequestStream = new Observable<EchoMsg>(subscriber => {
|
|
135
|
+
subscriber.next({body: 'Hello world from streaming request.'})
|
|
136
|
+
subscriber.complete()
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
console.log('Calling EchoClientStream: client -> server...')
|
|
140
|
+
result = await demoServiceClient.EchoClientStream(clientRequestStream)
|
|
141
|
+
console.log('success: output', result.body)
|
|
124
142
|
```
|
|
125
143
|
|
|
144
|
+
## WebSocket
|
|
126
145
|
|
|
127
|
-
|
|
146
|
+
One way to integrate Go and TypeScript is over a WebSocket:
|
|
128
147
|
|
|
129
148
|
```typescript
|
|
130
|
-
import { WebSocketConn } from '
|
|
131
|
-
import { EchoerClientImpl } from '
|
|
149
|
+
import { WebSocketConn } from 'srpc'
|
|
150
|
+
import { EchoerClientImpl } from 'srpc/echo'
|
|
132
151
|
|
|
133
152
|
const ws = new WebSocket('ws://localhost:5000/demo')
|
|
134
153
|
const channel = new WebSocketConn(ws)
|
|
@@ -139,15 +158,6 @@ const result = await demoServiceClient.Echo({
|
|
|
139
158
|
body: "Hello world!"
|
|
140
159
|
})
|
|
141
160
|
console.log('output', result.body)
|
|
142
|
-
|
|
143
|
-
const clientRequestStream = new Observable<EchoMsg>(subscriber => {
|
|
144
|
-
subscriber.next({body: 'Hello world from streaming request.'})
|
|
145
|
-
subscriber.complete()
|
|
146
|
-
})
|
|
147
|
-
|
|
148
|
-
console.log('Calling EchoClientStream: client -> server...')
|
|
149
|
-
result = await demoServiceClient.EchoClientStream(clientRequestStream)
|
|
150
|
-
console.log('success: output', result.body)
|
|
151
161
|
```
|
|
152
162
|
|
|
153
163
|
# Attribution
|
package/package.json
CHANGED
package/srpc/server-http.go
CHANGED
|
@@ -57,7 +57,7 @@ func (s *HTTPServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
|
57
57
|
return
|
|
58
58
|
}
|
|
59
59
|
go func() {
|
|
60
|
-
err := s.srpc.
|
|
60
|
+
err := s.srpc.HandleStream(ctx, strm)
|
|
61
61
|
_ = err
|
|
62
62
|
// TODO: handle / log error?
|
|
63
63
|
// err != nil && err != io.EOF && err != context.Canceled
|
package/srpc/server-pipe.go
CHANGED
|
@@ -7,12 +7,12 @@ import (
|
|
|
7
7
|
|
|
8
8
|
// NewServerPipe constructs a open stream func which creates an in-memory Pipe
|
|
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
12
|
return func(ctx context.Context, msgHandler func(pkt *Packet) error) (Writer, error) {
|
|
13
13
|
srvPipe, clientPipe := net.Pipe()
|
|
14
14
|
go func() {
|
|
15
|
-
_ = server.
|
|
15
|
+
_ = server.HandleStream(ctx, srvPipe)
|
|
16
16
|
}()
|
|
17
17
|
clientPrw := NewPacketReadWriter(clientPipe, msgHandler)
|
|
18
18
|
go func() {
|
package/srpc/server.go
CHANGED
|
@@ -3,6 +3,8 @@ package srpc
|
|
|
3
3
|
import (
|
|
4
4
|
"context"
|
|
5
5
|
"io"
|
|
6
|
+
|
|
7
|
+
"github.com/libp2p/go-libp2p-core/network"
|
|
6
8
|
)
|
|
7
9
|
|
|
8
10
|
// Server handles incoming RPC streams with a mux.
|
|
@@ -18,8 +20,8 @@ func NewServer(mux Mux) *Server {
|
|
|
18
20
|
}
|
|
19
21
|
}
|
|
20
22
|
|
|
21
|
-
//
|
|
22
|
-
func (s *Server)
|
|
23
|
+
// HandleStream handles an incoming ReadWriteCloser stream.
|
|
24
|
+
func (s *Server) HandleStream(ctx context.Context, rwc io.ReadWriteCloser) error {
|
|
23
25
|
subCtx, subCtxCancel := context.WithCancel(ctx)
|
|
24
26
|
defer subCtxCancel()
|
|
25
27
|
serverRPC := NewServerRPC(subCtx, s.mux)
|
|
@@ -27,3 +29,28 @@ func (s *Server) HandleConn(ctx context.Context, rwc io.ReadWriteCloser) error {
|
|
|
27
29
|
serverRPC.SetWriter(prw)
|
|
28
30
|
return prw.ReadPump()
|
|
29
31
|
}
|
|
32
|
+
|
|
33
|
+
// AcceptMuxedConn runs a loop which calls Accept on a muxer to handle streams.
|
|
34
|
+
//
|
|
35
|
+
// Starts HandleStream in a separate goroutine to handle the stream.
|
|
36
|
+
// Returns context.Canceled or io.EOF when the loop is complete / closed.
|
|
37
|
+
func (s *Server) AcceptMuxedConn(ctx context.Context, mplex network.MuxedConn) error {
|
|
38
|
+
for {
|
|
39
|
+
select {
|
|
40
|
+
case <-ctx.Done():
|
|
41
|
+
return context.Canceled
|
|
42
|
+
default:
|
|
43
|
+
if mplex.IsClosed() {
|
|
44
|
+
return io.EOF
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
muxedStream, err := mplex.AcceptStream()
|
|
49
|
+
if err != nil {
|
|
50
|
+
return err
|
|
51
|
+
}
|
|
52
|
+
go func() {
|
|
53
|
+
_ = s.HandleStream(ctx, muxedStream)
|
|
54
|
+
}()
|
|
55
|
+
}
|
|
56
|
+
}
|