starpc 0.4.8 → 0.5.1
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/Makefile +1 -0
- package/README.md +20 -10
- package/dist/echo/client-test.d.ts +1 -0
- package/dist/echo/client-test.js +20 -18
- package/dist/echo/echo.pb.d.ts +165 -12
- package/dist/echo/echo.pb.js +61 -17
- package/dist/echo/server.d.ts +8 -4
- package/dist/echo/server.js +29 -37
- package/dist/rpcstream/rpcstream.d.ts +6 -6
- package/dist/rpcstream/rpcstream.js +92 -51
- package/dist/rpcstream/rpcstream.pb.d.ts +46 -1
- package/dist/rpcstream/rpcstream.pb.js +157 -9
- package/dist/srpc/broadcast-channel.d.ts +2 -2
- package/dist/srpc/broadcast-channel.js +6 -6
- package/dist/srpc/client.d.ts +3 -4
- package/dist/srpc/client.js +12 -46
- package/dist/srpc/common-rpc.d.ts +3 -2
- package/dist/srpc/common-rpc.js +12 -0
- package/dist/srpc/definition.d.ts +3 -3
- package/dist/srpc/handler.d.ts +2 -3
- package/dist/srpc/handler.js +5 -22
- package/dist/srpc/index.d.ts +2 -2
- package/dist/srpc/index.js +2 -2
- package/dist/srpc/packet.js +0 -32
- package/dist/srpc/pushable.d.ts +2 -0
- package/dist/srpc/pushable.js +13 -0
- package/dist/srpc/rpcproto.pb.d.ts +13 -6
- package/dist/srpc/rpcproto.pb.js +95 -10
- package/dist/srpc/server.d.ts +1 -0
- package/dist/srpc/server.js +7 -0
- package/dist/srpc/ts-proto-rpc.d.ts +3 -4
- package/e2e/e2e.ts +4 -3
- package/e2e/e2e_test.go +24 -1
- package/echo/client-test.ts +23 -18
- package/echo/echo.pb.go +33 -20
- package/echo/echo.pb.ts +90 -34
- package/echo/echo.proto +4 -0
- package/echo/echo_srpc.pb.go +77 -0
- package/echo/server.go +18 -0
- package/echo/server.ts +47 -41
- package/integration/integration.go +1 -2
- package/integration/integration.ts +5 -1
- package/integration/integration_srpc.pb.go +139 -0
- package/package.json +13 -9
- package/patches/ts-proto+1.116.0.patch +14 -0
- package/srpc/broadcast-channel.ts +8 -8
- package/srpc/client.ts +16 -50
- package/srpc/common-rpc.ts +14 -2
- package/srpc/definition.ts +3 -3
- package/srpc/handler.ts +17 -34
- package/srpc/index.ts +3 -3
- package/srpc/muxed-conn.go +2 -2
- package/srpc/packet-rw.go +4 -6
- package/srpc/packet.ts +0 -33
- package/srpc/pushable.ts +17 -0
- package/srpc/rpcproto.pb.ts +122 -12
- package/srpc/server-pipe.go +2 -2
- package/srpc/server.go +2 -2
- package/srpc/server.ts +8 -0
- package/srpc/ts-proto-rpc.ts +4 -6
- package/srpc/websocket.go +2 -2
- package/dist/echo/sever.d.ts +0 -0
- package/dist/echo/sever.js +0 -1
- package/dist/srpc/observable-source.d.ts +0 -9
- package/dist/srpc/observable-source.js +0 -25
- package/echo/sever.ts +0 -0
- package/srpc/observable-source.ts +0 -40
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { WebSocketConn } from '../srpc/websocket.js'
|
|
2
|
-
import { runClientTest } from '../echo/client-test.js'
|
|
2
|
+
import { runClientTest, runRpcStreamTest } from '../echo/client-test.js'
|
|
3
3
|
import WebSocket from 'isomorphic-ws'
|
|
4
4
|
|
|
5
5
|
async function runRPC() {
|
|
@@ -9,7 +9,11 @@ async function runRPC() {
|
|
|
9
9
|
const channel = new WebSocketConn(ws)
|
|
10
10
|
const client = channel.buildClient()
|
|
11
11
|
|
|
12
|
+
console.log('Running client test via WebSocket..')
|
|
12
13
|
await runClientTest(client)
|
|
14
|
+
|
|
15
|
+
console.log('Running RpcStream test via WebSocket..')
|
|
16
|
+
await runRpcStreamTest(client)
|
|
13
17
|
}
|
|
14
18
|
|
|
15
19
|
runRPC()
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// Code generated by protoc-gen-srpc. DO NOT EDIT.
|
|
2
|
+
// protoc-gen-srpc version: v0.0.0-20220611014014-aa9dc5523865
|
|
3
|
+
// source: github.com/aperturerobotics/starpc/integration/integration.proto
|
|
4
|
+
|
|
5
|
+
package main
|
|
6
|
+
|
|
7
|
+
import (
|
|
8
|
+
context "context"
|
|
9
|
+
|
|
10
|
+
rpcstream "github.com/aperturerobotics/starpc/rpcstream"
|
|
11
|
+
srpc "github.com/aperturerobotics/starpc/srpc"
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
type SRPCIntegrationServiceClient interface {
|
|
15
|
+
SRPCClient() srpc.Client
|
|
16
|
+
|
|
17
|
+
RpcStream(ctx context.Context) (SRPCIntegrationService_RpcStreamClient, error)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
type srpcIntegrationServiceClient struct {
|
|
21
|
+
cc srpc.Client
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
func NewSRPCIntegrationServiceClient(cc srpc.Client) SRPCIntegrationServiceClient {
|
|
25
|
+
return &srpcIntegrationServiceClient{cc}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
func (c *srpcIntegrationServiceClient) SRPCClient() srpc.Client { return c.cc }
|
|
29
|
+
|
|
30
|
+
func (c *srpcIntegrationServiceClient) RpcStream(ctx context.Context) (SRPCIntegrationService_RpcStreamClient, error) {
|
|
31
|
+
stream, err := c.cc.NewStream(ctx, "main.IntegrationService", "RpcStream", nil)
|
|
32
|
+
if err != nil {
|
|
33
|
+
return nil, err
|
|
34
|
+
}
|
|
35
|
+
strm := &srpcIntegrationService_RpcStreamClient{stream}
|
|
36
|
+
return strm, nil
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
type SRPCIntegrationService_RpcStreamClient interface {
|
|
40
|
+
srpc.Stream
|
|
41
|
+
Send(*rpcstream.RpcStreamPacket) error
|
|
42
|
+
Recv() (*rpcstream.RpcStreamPacket, error)
|
|
43
|
+
RecvTo(*rpcstream.RpcStreamPacket) error
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
type srpcIntegrationService_RpcStreamClient struct {
|
|
47
|
+
srpc.Stream
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
func (x *srpcIntegrationService_RpcStreamClient) Send(m *rpcstream.RpcStreamPacket) error {
|
|
51
|
+
return x.MsgSend(m)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
func (x *srpcIntegrationService_RpcStreamClient) Recv() (*rpcstream.RpcStreamPacket, error) {
|
|
55
|
+
m := new(rpcstream.RpcStreamPacket)
|
|
56
|
+
if err := x.MsgRecv(m); err != nil {
|
|
57
|
+
return nil, err
|
|
58
|
+
}
|
|
59
|
+
return m, nil
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
func (x *srpcIntegrationService_RpcStreamClient) RecvTo(m *rpcstream.RpcStreamPacket) error {
|
|
63
|
+
return x.MsgRecv(m)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
type SRPCIntegrationServiceServer interface {
|
|
67
|
+
RpcStream(SRPCIntegrationService_RpcStreamStream) error
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
type SRPCIntegrationServiceUnimplementedServer struct{}
|
|
71
|
+
|
|
72
|
+
func (s *SRPCIntegrationServiceUnimplementedServer) RpcStream(SRPCIntegrationService_RpcStreamStream) error {
|
|
73
|
+
return srpc.ErrUnimplemented
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const SRPCIntegrationServiceServiceID = "main.IntegrationService"
|
|
77
|
+
|
|
78
|
+
type SRPCIntegrationServiceHandler struct {
|
|
79
|
+
impl SRPCIntegrationServiceServer
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
func (SRPCIntegrationServiceHandler) GetServiceID() string { return SRPCIntegrationServiceServiceID }
|
|
83
|
+
|
|
84
|
+
func (SRPCIntegrationServiceHandler) GetMethodIDs() []string {
|
|
85
|
+
return []string{
|
|
86
|
+
"RpcStream",
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
func (d *SRPCIntegrationServiceHandler) InvokeMethod(
|
|
91
|
+
serviceID, methodID string,
|
|
92
|
+
strm srpc.Stream,
|
|
93
|
+
) (bool, error) {
|
|
94
|
+
if serviceID != "" && serviceID != d.GetServiceID() {
|
|
95
|
+
return false, nil
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
switch methodID {
|
|
99
|
+
case "RpcStream":
|
|
100
|
+
return true, d.InvokeMethod_RpcStream(d.impl, strm)
|
|
101
|
+
default:
|
|
102
|
+
return false, nil
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
func (SRPCIntegrationServiceHandler) InvokeMethod_RpcStream(impl SRPCIntegrationServiceServer, strm srpc.Stream) error {
|
|
107
|
+
clientStrm := &srpcIntegrationService_RpcStreamStream{strm}
|
|
108
|
+
return impl.RpcStream(clientStrm)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
func SRPCRegisterIntegrationService(mux srpc.Mux, impl SRPCIntegrationServiceServer) error {
|
|
112
|
+
return mux.Register(&SRPCIntegrationServiceHandler{impl: impl})
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
type SRPCIntegrationService_RpcStreamStream interface {
|
|
116
|
+
srpc.Stream
|
|
117
|
+
Send(*rpcstream.RpcStreamPacket) error
|
|
118
|
+
Recv() (*rpcstream.RpcStreamPacket, error)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
type srpcIntegrationService_RpcStreamStream struct {
|
|
122
|
+
srpc.Stream
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
func (x *srpcIntegrationService_RpcStreamStream) Send(m *rpcstream.RpcStreamPacket) error {
|
|
126
|
+
return x.MsgSend(m)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
func (x *srpcIntegrationService_RpcStreamStream) Recv() (*rpcstream.RpcStreamPacket, error) {
|
|
130
|
+
m := new(rpcstream.RpcStreamPacket)
|
|
131
|
+
if err := x.MsgRecv(m); err != nil {
|
|
132
|
+
return nil, err
|
|
133
|
+
}
|
|
134
|
+
return m, nil
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
func (x *srpcIntegrationService_RpcStreamStream) RecvTo(m *rpcstream.RpcStreamPacket) error {
|
|
138
|
+
return x.MsgRecv(m)
|
|
139
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "starpc",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Streaming protobuf RPC service protocol over any two-way channel.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -35,14 +35,16 @@
|
|
|
35
35
|
"scripts": {
|
|
36
36
|
"build": "rimraf ./dist && tsc --project tsconfig.build.json --outDir ./dist/",
|
|
37
37
|
"check": "tsc",
|
|
38
|
-
"deps": "depcheck",
|
|
38
|
+
"deps": "depcheck --ignores 'bufferutil,utf-8-validate'",
|
|
39
39
|
"codegen": "npm run gen",
|
|
40
40
|
"ci": "npm run build && npm run lint:js && npm run lint:go",
|
|
41
41
|
"format": "prettier --write './{srpc,echo,e2e,integration,rpcstream}/**/(*.ts|*.tsx|*.html|*.css)'",
|
|
42
42
|
"gen": "make genproto",
|
|
43
43
|
"test": "npm run test:js && npm run test:go",
|
|
44
44
|
"test:go": "make test",
|
|
45
|
-
"
|
|
45
|
+
"build:e2e": "npm run build && cd e2e && esbuild e2e.ts --sourcemap --outfile=e2e.js --bundle --platform=node",
|
|
46
|
+
"test:js": "npm run build:e2e && cd e2e && node ./e2e.js",
|
|
47
|
+
"debug:js": "npm run build:e2e && cd e2e && node --inspect --inspect-brk ./e2e.js",
|
|
46
48
|
"test:integration": "make integration",
|
|
47
49
|
"integration": "npm run test:integration",
|
|
48
50
|
"lint": "npm run lint:go && npm run lint:js",
|
|
@@ -56,16 +58,18 @@
|
|
|
56
58
|
"singleQuote": true
|
|
57
59
|
},
|
|
58
60
|
"devDependencies": {
|
|
59
|
-
"@typescript-eslint/eslint-plugin": "^5.30.
|
|
60
|
-
"@typescript-eslint/parser": "^5.30.
|
|
61
|
+
"@typescript-eslint/eslint-plugin": "^5.30.3",
|
|
62
|
+
"@typescript-eslint/parser": "^5.30.3",
|
|
63
|
+
"bufferutil": "^4.0.6",
|
|
61
64
|
"depcheck": "^1.4.3",
|
|
62
|
-
"esbuild": "^0.14.
|
|
65
|
+
"esbuild": "^0.14.48",
|
|
63
66
|
"eslint": "^8.18.0",
|
|
64
67
|
"eslint-config-prettier": "^8.5.0",
|
|
65
68
|
"prettier": "^2.7.1",
|
|
66
69
|
"rimraf": "^3.0.2",
|
|
67
|
-
"ts-proto": "^1.
|
|
68
|
-
"typescript": "^4.7.4"
|
|
70
|
+
"ts-proto": "^1.116.0",
|
|
71
|
+
"typescript": "^4.7.4",
|
|
72
|
+
"utf-8-validate": "^5.0.9"
|
|
69
73
|
},
|
|
70
74
|
"dependencies": {
|
|
71
75
|
"@libp2p/interface-connection": "^2.1.1",
|
|
@@ -73,6 +77,7 @@
|
|
|
73
77
|
"@libp2p/mplex": "^4.0.0",
|
|
74
78
|
"event-iterator": "^2.0.0",
|
|
75
79
|
"isomorphic-ws": "^5.0.0",
|
|
80
|
+
"it-first": "^1.0.7",
|
|
76
81
|
"it-length-prefixed": "^7.0.1",
|
|
77
82
|
"it-pipe": "^2.0.3",
|
|
78
83
|
"it-pushable": "^3.0.0",
|
|
@@ -81,7 +86,6 @@
|
|
|
81
86
|
"long": "^5.2.0",
|
|
82
87
|
"patch-package": "^6.4.7",
|
|
83
88
|
"protobufjs": "^6.11.3",
|
|
84
|
-
"rxjs": "^7.5.5",
|
|
85
89
|
"ws": "^8.8.0"
|
|
86
90
|
}
|
|
87
91
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
diff --git a/node_modules/ts-proto/build/utils.js b/node_modules/ts-proto/build/utils.js
|
|
2
|
+
index 2148e97..e19b35f 100644
|
|
3
|
+
--- a/node_modules/ts-proto/build/utils.js
|
|
4
|
+
+++ b/node_modules/ts-proto/build/utils.js
|
|
5
|
+
@@ -170,7 +170,8 @@ function getPropertyAccessor(objectName, propertyName, optional = false) {
|
|
6
|
+
}
|
|
7
|
+
exports.getPropertyAccessor = getPropertyAccessor;
|
|
8
|
+
function impProto(options, module, type) {
|
|
9
|
+
- const importString = `${type}@./${module}${options.fileSuffix}`;
|
|
10
|
+
+ // NOTE: TypeScript resolves importing foo.js to importing foo.ts.
|
|
11
|
+
+ const importString = `${type}@./${module}${options.fileSuffix}.js`;
|
|
12
|
+
if (options.onlyTypes) {
|
|
13
|
+
return (0, ts_poet_1.imp)('t:' + importString);
|
|
14
|
+
}
|
|
@@ -5,8 +5,8 @@ import { ConnParams } from './conn.js'
|
|
|
5
5
|
import { Server } from './server.js'
|
|
6
6
|
import { DuplexConn } from './conn-duplex.js'
|
|
7
7
|
|
|
8
|
-
//
|
|
9
|
-
export class
|
|
8
|
+
// BroadcastChannelDuplex is a AsyncIterable wrapper for BroadcastChannel.
|
|
9
|
+
export class BroadcastChannelDuplex<T> implements Duplex<T> {
|
|
10
10
|
// readChannel is the incoming broadcast channel
|
|
11
11
|
public readonly readChannel: BroadcastChannel
|
|
12
12
|
// writeChannel is the outgoing broadcast channel
|
|
@@ -55,12 +55,12 @@ export class BroadcastChannelIterable<T> implements Duplex<T> {
|
|
|
55
55
|
}
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
-
//
|
|
59
|
-
export function
|
|
58
|
+
// newBroadcastChannelDuplex constructs a BroadcastChannelDuplex with a channel name.
|
|
59
|
+
export function newBroadcastChannelDuplex<T>(
|
|
60
60
|
readName: string,
|
|
61
61
|
writeName: string
|
|
62
|
-
):
|
|
63
|
-
return new
|
|
62
|
+
): BroadcastChannelDuplex<T> {
|
|
63
|
+
return new BroadcastChannelDuplex<T>(
|
|
64
64
|
new BroadcastChannel(readName),
|
|
65
65
|
new BroadcastChannel(writeName)
|
|
66
66
|
)
|
|
@@ -71,7 +71,7 @@ export function newBroadcastChannelIterable<T>(
|
|
|
71
71
|
// expects Uint8Array objects over the BroadcastChannel.
|
|
72
72
|
export class BroadcastChannelConn extends DuplexConn {
|
|
73
73
|
// broadcastChannel is the broadcast channel iterable
|
|
74
|
-
private broadcastChannel:
|
|
74
|
+
private broadcastChannel: BroadcastChannelDuplex<Uint8Array>
|
|
75
75
|
|
|
76
76
|
constructor(
|
|
77
77
|
readChannel: BroadcastChannel,
|
|
@@ -79,7 +79,7 @@ export class BroadcastChannelConn extends DuplexConn {
|
|
|
79
79
|
server?: Server,
|
|
80
80
|
connParams?: ConnParams
|
|
81
81
|
) {
|
|
82
|
-
const broadcastChannel = new
|
|
82
|
+
const broadcastChannel = new BroadcastChannelDuplex<Uint8Array>(
|
|
83
83
|
readChannel,
|
|
84
84
|
writeChannel
|
|
85
85
|
)
|
package/srpc/client.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { pipe } from 'it-pipe'
|
|
2
2
|
import { pushable, Pushable } from 'it-pushable'
|
|
3
|
-
import { Observable, from as observableFrom } from 'rxjs'
|
|
4
3
|
|
|
5
4
|
import type { TsProtoRpc } from './ts-proto-rpc.js'
|
|
6
5
|
import type { OpenStreamFunc } from './stream.js'
|
|
7
6
|
import { ClientRPC } from './client-rpc.js'
|
|
7
|
+
import { writeToPushable } from './pushable.js'
|
|
8
8
|
import {
|
|
9
9
|
decodePacketSource,
|
|
10
10
|
encodePacketSource,
|
|
@@ -12,21 +12,6 @@ import {
|
|
|
12
12
|
prependLengthPrefixTransform,
|
|
13
13
|
} from './packet.js'
|
|
14
14
|
|
|
15
|
-
// writeClientStream registers the subscriber to write the client data stream.
|
|
16
|
-
function writeClientStream(call: ClientRPC, data: Observable<Uint8Array>) {
|
|
17
|
-
data.subscribe({
|
|
18
|
-
next(value) {
|
|
19
|
-
call.writeCallData(value)
|
|
20
|
-
},
|
|
21
|
-
error(err) {
|
|
22
|
-
call.close(err)
|
|
23
|
-
},
|
|
24
|
-
complete() {
|
|
25
|
-
call.writeCallData(undefined, true)
|
|
26
|
-
},
|
|
27
|
-
})
|
|
28
|
-
}
|
|
29
|
-
|
|
30
15
|
// Client implements the ts-proto Rpc interface with the drpcproto protocol.
|
|
31
16
|
export class Client implements TsProtoRpc {
|
|
32
17
|
// openStreamFn is a promise which contains the OpenStreamFunc.
|
|
@@ -92,10 +77,10 @@ export class Client implements TsProtoRpc {
|
|
|
92
77
|
public async clientStreamingRequest(
|
|
93
78
|
service: string,
|
|
94
79
|
method: string,
|
|
95
|
-
data:
|
|
80
|
+
data: AsyncIterable<Uint8Array>
|
|
96
81
|
): Promise<Uint8Array> {
|
|
97
82
|
const call = await this.startRpc(service, method, null)
|
|
98
|
-
|
|
83
|
+
call.writeCallDataFromSource(data)
|
|
99
84
|
for await (const data of call.rpcDataSource) {
|
|
100
85
|
call.close()
|
|
101
86
|
return data
|
|
@@ -110,21 +95,13 @@ export class Client implements TsProtoRpc {
|
|
|
110
95
|
service: string,
|
|
111
96
|
method: string,
|
|
112
97
|
data: Uint8Array
|
|
113
|
-
):
|
|
114
|
-
const
|
|
115
|
-
const serverData = observableFrom(pushServerData)
|
|
98
|
+
): AsyncIterable<Uint8Array> {
|
|
99
|
+
const serverData: Pushable<Uint8Array> = pushable({ objectMode: true })
|
|
116
100
|
this.startRpc(service, method, data)
|
|
117
101
|
.then(async (call) => {
|
|
118
|
-
|
|
119
|
-
for await (const data of call.rpcDataSource) {
|
|
120
|
-
pushServerData.push(data)
|
|
121
|
-
}
|
|
122
|
-
} catch (err) {
|
|
123
|
-
pushServerData.throw(err as Error)
|
|
124
|
-
}
|
|
125
|
-
pushServerData.end()
|
|
102
|
+
return writeToPushable(call.rpcDataSource, serverData)
|
|
126
103
|
})
|
|
127
|
-
.catch(
|
|
104
|
+
.catch(serverData.throw.bind(serverData))
|
|
128
105
|
return serverData
|
|
129
106
|
}
|
|
130
107
|
|
|
@@ -132,33 +109,22 @@ export class Client implements TsProtoRpc {
|
|
|
132
109
|
public bidirectionalStreamingRequest(
|
|
133
110
|
service: string,
|
|
134
111
|
method: string,
|
|
135
|
-
data:
|
|
136
|
-
):
|
|
137
|
-
const
|
|
138
|
-
const serverData = observableFrom(pushServerData)
|
|
112
|
+
data: AsyncIterable<Uint8Array>
|
|
113
|
+
): AsyncIterable<Uint8Array> {
|
|
114
|
+
const serverData: Pushable<Uint8Array> = pushable({ objectMode: true })
|
|
139
115
|
this.startRpc(service, method, null)
|
|
140
116
|
.then(async (call) => {
|
|
117
|
+
call.writeCallDataFromSource(data)
|
|
141
118
|
try {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
call.writeCallData(value)
|
|
145
|
-
},
|
|
146
|
-
error(err) {
|
|
147
|
-
call.close(err)
|
|
148
|
-
},
|
|
149
|
-
complete() {
|
|
150
|
-
call.close()
|
|
151
|
-
},
|
|
152
|
-
})
|
|
153
|
-
for await (const data of call.rpcDataSource) {
|
|
154
|
-
pushServerData.push(data)
|
|
119
|
+
for await (const message of call.rpcDataSource) {
|
|
120
|
+
serverData.push(message)
|
|
155
121
|
}
|
|
156
122
|
} catch (err) {
|
|
157
|
-
|
|
123
|
+
serverData.throw(err as Error)
|
|
158
124
|
}
|
|
159
|
-
|
|
125
|
+
serverData.end()
|
|
160
126
|
})
|
|
161
|
-
.catch(
|
|
127
|
+
.catch(serverData.throw.bind(serverData))
|
|
162
128
|
return serverData
|
|
163
129
|
}
|
|
164
130
|
|
package/srpc/common-rpc.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Sink } from 'it-stream-types'
|
|
2
2
|
import { pushable } from 'it-pushable'
|
|
3
3
|
|
|
4
4
|
import type { CallData, CallStart } from './rpcproto.pb.js'
|
|
@@ -16,7 +16,7 @@ export class CommonRPC {
|
|
|
16
16
|
end: (err?: Error) => void
|
|
17
17
|
}
|
|
18
18
|
// rpcDataSource emits incoming client RPC messages to the caller.
|
|
19
|
-
public readonly rpcDataSource:
|
|
19
|
+
public readonly rpcDataSource: AsyncIterable<Uint8Array>
|
|
20
20
|
// _rpcDataSource is used to write to the rpc message source.
|
|
21
21
|
private readonly _rpcDataSource: {
|
|
22
22
|
push: (val: Uint8Array) => void
|
|
@@ -60,6 +60,18 @@ export class CommonRPC {
|
|
|
60
60
|
})
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
+
// writeCallDataFromSource writes all call data from the iterable.
|
|
64
|
+
public async writeCallDataFromSource(dataSource: AsyncIterable<Uint8Array>) {
|
|
65
|
+
try {
|
|
66
|
+
for await (const data of dataSource) {
|
|
67
|
+
this.writeCallData(data)
|
|
68
|
+
}
|
|
69
|
+
this.writeCallData(undefined, true)
|
|
70
|
+
} catch (err) {
|
|
71
|
+
this.close(err as Error)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
63
75
|
// writePacket writes a packet to the stream.
|
|
64
76
|
protected async writePacket(packet: Packet) {
|
|
65
77
|
this._source.push(packet)
|
package/srpc/definition.ts
CHANGED
|
@@ -15,16 +15,16 @@ export interface Definition {
|
|
|
15
15
|
|
|
16
16
|
// MethodDefinition describes the method definitions generated by ts-proto.
|
|
17
17
|
// use --ts_proto_opt=outputServices=default,outputServices=generic-definitions
|
|
18
|
-
export interface MethodDefinition<
|
|
18
|
+
export interface MethodDefinition<RequestT, ResponseT> {
|
|
19
19
|
// name is the name and function name of the method.
|
|
20
20
|
// e.x.: Echo
|
|
21
21
|
name: string
|
|
22
22
|
// requestType is the object used for the request.
|
|
23
|
-
requestType: MessageDefinition<
|
|
23
|
+
requestType: MessageDefinition<RequestT>
|
|
24
24
|
// requestStream indicates the request is a stream.
|
|
25
25
|
requestStream: boolean
|
|
26
26
|
// responseType is the object used for the response.
|
|
27
|
-
responseType: MessageDefinition<
|
|
27
|
+
responseType: MessageDefinition<ResponseT>
|
|
28
28
|
// responseStream indicates the response is a stream.
|
|
29
29
|
responseStream: boolean
|
|
30
30
|
}
|
package/srpc/handler.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import type { Sink, Source } from 'it-stream-types'
|
|
2
2
|
import { pipe } from 'it-pipe'
|
|
3
3
|
import { pushable } from 'it-pushable'
|
|
4
|
-
import { Observable, from as observableFrom } from 'rxjs'
|
|
5
4
|
|
|
6
5
|
import { Definition, MethodDefinition } from './definition.js'
|
|
7
6
|
import {
|
|
8
7
|
buildDecodeMessageTransform,
|
|
9
8
|
buildEncodeMessageTransform,
|
|
10
9
|
} from './message.js'
|
|
10
|
+
import { writeToPushable } from './pushable.js'
|
|
11
11
|
|
|
12
12
|
// InvokeFn describes an SRPC call method invoke function.
|
|
13
13
|
export type InvokeFn = (
|
|
@@ -65,21 +65,21 @@ export class StaticHandler implements Handler {
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
// MethodProto is a function which matches one of the RPC signatures.
|
|
68
|
-
type MethodProto =
|
|
69
|
-
| ((request:
|
|
70
|
-
| ((request:
|
|
71
|
-
| ((request:
|
|
72
|
-
| ((request:
|
|
68
|
+
type MethodProto<R, O> =
|
|
69
|
+
| ((request: R) => Promise<O>)
|
|
70
|
+
| ((request: R) => AsyncIterable<O>)
|
|
71
|
+
| ((request: AsyncIterable<R>) => Promise<O>)
|
|
72
|
+
| ((request: AsyncIterable<R>) => AsyncIterable<O>)
|
|
73
73
|
|
|
74
74
|
// createInvokeFn builds an InvokeFn from a method definition and a function prototype.
|
|
75
|
-
export function createInvokeFn(
|
|
76
|
-
methodInfo: MethodDefinition<
|
|
77
|
-
methodProto: MethodProto
|
|
75
|
+
export function createInvokeFn<R, O>(
|
|
76
|
+
methodInfo: MethodDefinition<R, O>,
|
|
77
|
+
methodProto: MethodProto<R, O>
|
|
78
78
|
): InvokeFn {
|
|
79
|
-
const requestDecode = buildDecodeMessageTransform(methodInfo.requestType)
|
|
79
|
+
const requestDecode = buildDecodeMessageTransform<R>(methodInfo.requestType)
|
|
80
80
|
return async (dataSource: Source<Uint8Array>, dataSink: Sink<Uint8Array>) => {
|
|
81
81
|
// responseSink is a Sink for response messages.
|
|
82
|
-
const responseSink = pushable<
|
|
82
|
+
const responseSink = pushable<O>({
|
|
83
83
|
objectMode: true,
|
|
84
84
|
})
|
|
85
85
|
|
|
@@ -96,8 +96,8 @@ export function createInvokeFn(
|
|
|
96
96
|
// build the request argument.
|
|
97
97
|
let requestArg: any
|
|
98
98
|
if (methodInfo.requestStream) {
|
|
99
|
-
//
|
|
100
|
-
requestArg =
|
|
99
|
+
// use the request source as the argument.
|
|
100
|
+
requestArg = requestSource
|
|
101
101
|
} else {
|
|
102
102
|
// receive a single message for the argument.
|
|
103
103
|
for await (const msg of requestSource) {
|
|
@@ -119,27 +119,10 @@ export function createInvokeFn(
|
|
|
119
119
|
throw new Error('return value was undefined')
|
|
120
120
|
}
|
|
121
121
|
if (methodInfo.responseStream) {
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
throw new Error('expected return value to be an Observable')
|
|
125
|
-
}
|
|
126
|
-
return new Promise<void>((resolve, reject) => {
|
|
127
|
-
responseObs.subscribe({
|
|
128
|
-
next(value) {
|
|
129
|
-
responseSink.push(value)
|
|
130
|
-
},
|
|
131
|
-
error: (err: any) => {
|
|
132
|
-
responseSink.throw(err)
|
|
133
|
-
reject(err)
|
|
134
|
-
},
|
|
135
|
-
complete: () => {
|
|
136
|
-
responseSink.end()
|
|
137
|
-
resolve()
|
|
138
|
-
},
|
|
139
|
-
})
|
|
140
|
-
})
|
|
122
|
+
const response = responseObj as AsyncIterable<O>
|
|
123
|
+
return writeToPushable(response, responseSink)
|
|
141
124
|
} else {
|
|
142
|
-
const responsePromise = responseObj as Promise<
|
|
125
|
+
const responsePromise = responseObj as Promise<O>
|
|
143
126
|
if (!responsePromise.then) {
|
|
144
127
|
throw new Error('expected return value to be a Promise')
|
|
145
128
|
}
|
|
@@ -166,7 +149,7 @@ export function createHandler(definition: Definition, impl: any): Handler {
|
|
|
166
149
|
const methodMap: MethodMap = {}
|
|
167
150
|
for (const methodInfo of Object.values(definition.methods)) {
|
|
168
151
|
const methodName = methodInfo.name
|
|
169
|
-
let methodProto: MethodProto = impl[methodName]
|
|
152
|
+
let methodProto: MethodProto<unknown, unknown> = impl[methodName]
|
|
170
153
|
if (!methodProto) {
|
|
171
154
|
continue
|
|
172
155
|
}
|
package/srpc/index.ts
CHANGED
|
@@ -6,8 +6,8 @@ export { Handler, InvokeFn, createHandler, createInvokeFn } from './handler.js'
|
|
|
6
6
|
export { Packet, CallStart, CallData } from './rpcproto.pb.js'
|
|
7
7
|
export { Mux, createMux } from './mux.js'
|
|
8
8
|
export {
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
BroadcastChannelDuplex,
|
|
10
|
+
newBroadcastChannelDuplex,
|
|
11
11
|
BroadcastChannelConn,
|
|
12
12
|
} from './broadcast-channel.js'
|
|
13
13
|
export {
|
|
@@ -15,4 +15,4 @@ export {
|
|
|
15
15
|
newMessagePortIterable,
|
|
16
16
|
MessagePortConn,
|
|
17
17
|
} from './message-port.js'
|
|
18
|
-
export {
|
|
18
|
+
export { writeToPushable } from './pushable'
|
package/srpc/muxed-conn.go
CHANGED
|
@@ -19,9 +19,9 @@ func NewOpenStreamWithMuxedConn(conn network.MuxedConn) OpenStreamFunc {
|
|
|
19
19
|
if err != nil {
|
|
20
20
|
return nil, err
|
|
21
21
|
}
|
|
22
|
-
rw := NewPacketReadWriter(mstrm
|
|
22
|
+
rw := NewPacketReadWriter(mstrm)
|
|
23
23
|
go func() {
|
|
24
|
-
err := rw.ReadPump()
|
|
24
|
+
err := rw.ReadPump(msgHandler)
|
|
25
25
|
if err != nil {
|
|
26
26
|
_ = rw.Close()
|
|
27
27
|
}
|
package/srpc/packet-rw.go
CHANGED
|
@@ -16,15 +16,13 @@ var maxMessageSize = 1e7
|
|
|
16
16
|
type PacketReaderWriter struct {
|
|
17
17
|
// rw is the io.ReadWriterCloser
|
|
18
18
|
rw io.ReadWriteCloser
|
|
19
|
-
// cb is the callback
|
|
20
|
-
cb PacketHandler
|
|
21
19
|
// buf is the buffered data
|
|
22
20
|
buf bytes.Buffer
|
|
23
21
|
}
|
|
24
22
|
|
|
25
23
|
// NewPacketReadWriter constructs a new read/writer.
|
|
26
|
-
func NewPacketReadWriter(rw io.ReadWriteCloser
|
|
27
|
-
return &PacketReaderWriter{rw: rw
|
|
24
|
+
func NewPacketReadWriter(rw io.ReadWriteCloser) *PacketReaderWriter {
|
|
25
|
+
return &PacketReaderWriter{rw: rw}
|
|
28
26
|
}
|
|
29
27
|
|
|
30
28
|
// WritePacket writes a packet to the writer.
|
|
@@ -41,7 +39,7 @@ func (r *PacketReaderWriter) WritePacket(p *Packet) error {
|
|
|
41
39
|
}
|
|
42
40
|
|
|
43
41
|
// ReadPump executes the read pump in a goroutine.
|
|
44
|
-
func (r *PacketReaderWriter) ReadPump() error {
|
|
42
|
+
func (r *PacketReaderWriter) ReadPump(cb PacketHandler) error {
|
|
45
43
|
var currLen uint32
|
|
46
44
|
buf := make([]byte, 2048)
|
|
47
45
|
for {
|
|
@@ -78,7 +76,7 @@ func (r *PacketReaderWriter) ReadPump() error {
|
|
|
78
76
|
if err := npkt.UnmarshalVT(pkt); err != nil {
|
|
79
77
|
return err
|
|
80
78
|
}
|
|
81
|
-
if err :=
|
|
79
|
+
if err := cb(npkt); err != nil {
|
|
82
80
|
return err
|
|
83
81
|
}
|
|
84
82
|
}
|
package/srpc/packet.ts
CHANGED
|
@@ -88,36 +88,3 @@ export function prependPacketLen(msgData: Uint8Array): Uint8Array {
|
|
|
88
88
|
merged.set(msgData, msgLenData.length)
|
|
89
89
|
return merged
|
|
90
90
|
}
|
|
91
|
-
|
|
92
|
-
/*
|
|
93
|
-
// buildCallDataPacket builds a CallData packet.
|
|
94
|
-
function buildCallDataPacket(data: Uint8Array): Packet {
|
|
95
|
-
const callData: CallData = {
|
|
96
|
-
data: p,
|
|
97
|
-
complete: false,
|
|
98
|
-
error: '',
|
|
99
|
-
}
|
|
100
|
-
const pkt: Packet = {
|
|
101
|
-
body: {
|
|
102
|
-
$case: 'callData',
|
|
103
|
-
callData: callData,
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
return pkt
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// wrapCallDataTransform is a transformer that wraps call data into a Packet.
|
|
110
|
-
export async function* wrapCallDataTransform(
|
|
111
|
-
source: Source<Uint8Array | Uint8Array>
|
|
112
|
-
): AsyncIterable<Packet> {
|
|
113
|
-
for await (const pkt of source) {
|
|
114
|
-
if (Array.isArray(pkt)) {
|
|
115
|
-
for (const p of pkt) {
|
|
116
|
-
yield* [buildCallDataPacket(p)]
|
|
117
|
-
}
|
|
118
|
-
} else {
|
|
119
|
-
yield* [buildCallDataPacket(pkt)]
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
*/
|
package/srpc/pushable.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Pushable } from 'it-pushable'
|
|
2
|
+
|
|
3
|
+
// writeToPushable writes the incoming server data to the pushable.
|
|
4
|
+
export async function writeToPushable<T>(
|
|
5
|
+
dataSource: AsyncIterable<T>,
|
|
6
|
+
out: Pushable<T>
|
|
7
|
+
) {
|
|
8
|
+
try {
|
|
9
|
+
for await (const data of dataSource) {
|
|
10
|
+
out.push(data)
|
|
11
|
+
}
|
|
12
|
+
out.end()
|
|
13
|
+
} catch (err) {
|
|
14
|
+
out.end(err as Error)
|
|
15
|
+
throw err
|
|
16
|
+
}
|
|
17
|
+
}
|