starpc 0.3.6 → 0.4.2
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 +22 -13
- package/README.md +4 -0
- package/dist/echo/client-test.js +1 -1
- package/dist/echo/{echo.d.ts → echo.pb.d.ts} +0 -0
- package/dist/echo/{echo.js → echo.pb.js} +0 -0
- package/dist/echo/index.d.ts +1 -1
- package/dist/echo/index.js +1 -1
- package/dist/echo/server.d.ts +1 -1
- package/dist/rpcstream/rpcstream.d.ts +19 -0
- package/dist/rpcstream/rpcstream.js +94 -0
- package/dist/rpcstream/rpcstream.pb.d.ts +85 -0
- package/dist/rpcstream/rpcstream.pb.js +160 -0
- package/dist/srpc/client-rpc.d.ts +1 -1
- package/dist/srpc/client.d.ts +5 -5
- package/dist/srpc/client.js +19 -19
- package/dist/srpc/common-rpc.d.ts +2 -2
- package/dist/srpc/common-rpc.js +1 -1
- package/dist/srpc/conn.d.ts +1 -1
- package/dist/srpc/index.d.ts +2 -1
- package/dist/srpc/index.js +1 -0
- package/dist/srpc/observable-source.d.ts +9 -0
- package/dist/srpc/observable-source.js +25 -0
- package/dist/srpc/packet.d.ts +1 -1
- package/dist/srpc/packet.js +1 -1
- package/dist/srpc/{rpcproto.d.ts → rpcproto.pb.d.ts} +0 -0
- package/dist/srpc/{rpcproto.js → rpcproto.pb.js} +0 -0
- package/dist/srpc/server-rpc.d.ts +1 -1
- package/dist/srpc/server.d.ts +4 -4
- package/dist/srpc/server.js +6 -5
- package/dist/srpc/stream.d.ts +1 -1
- package/echo/client-test.ts +1 -1
- package/echo/{echo.ts → echo.pb.ts} +0 -0
- package/echo/index.ts +1 -1
- package/echo/server.ts +1 -1
- package/integration/integration.ts +2 -2
- package/package.json +4 -3
- package/srpc/broadcast-channel.ts +1 -1
- package/srpc/client-rpc.ts +1 -1
- package/srpc/client.go +2 -2
- package/srpc/client.ts +25 -23
- package/srpc/common-rpc.ts +2 -2
- package/srpc/conn.ts +1 -1
- package/srpc/index.ts +2 -2
- package/srpc/{rpc-stream.go → msg-stream.go} +11 -11
- package/srpc/muxed-conn.go +1 -1
- package/srpc/observable-source.ts +40 -0
- package/srpc/packet.ts +1 -1
- package/srpc/{rpcproto.ts → rpcproto.pb.ts} +0 -0
- package/srpc/server-pipe.go +1 -1
- package/srpc/server-rpc.go +1 -1
- package/srpc/server-rpc.ts +1 -1
- package/srpc/server.go +5 -0
- package/srpc/server.ts +8 -7
- package/srpc/stream.ts +1 -1
- package/srpc/websocket.go +1 -1
- package/e2e/debug.test +0 -0
package/Makefile
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
|
+
# https://github.com/aperturerobotics/protobuf-project
|
|
2
|
+
|
|
1
3
|
PROTOWRAP=hack/bin/protowrap
|
|
2
4
|
PROTOC_GEN_GO=hack/bin/protoc-gen-go
|
|
5
|
+
PROTOC_GEN_GO_STARPC=hack/bin/protoc-gen-go-starpc
|
|
3
6
|
PROTOC_GEN_VTPROTO=hack/bin/protoc-gen-go-vtproto
|
|
4
|
-
PROTOC_GEN_STARPC=hack/bin/protoc-gen-go-starpc
|
|
5
7
|
GOIMPORTS=hack/bin/goimports
|
|
6
8
|
GOLANGCI_LINT=hack/bin/golangci-lint
|
|
7
9
|
GO_MOD_OUTDATED=hack/bin/go-mod-outdated
|
|
8
|
-
export GO111MODULE=on
|
|
9
10
|
GOLIST=go list -f "{{ .Dir }}" -m
|
|
10
11
|
|
|
12
|
+
export GO111MODULE=on
|
|
13
|
+
undefine GOARCH
|
|
14
|
+
undefine GOOS
|
|
15
|
+
|
|
11
16
|
all:
|
|
12
17
|
|
|
13
18
|
vendor:
|
|
@@ -17,19 +22,19 @@ $(PROTOC_GEN_GO):
|
|
|
17
22
|
cd ./hack; \
|
|
18
23
|
go build -v \
|
|
19
24
|
-o ./bin/protoc-gen-go \
|
|
20
|
-
|
|
25
|
+
google.golang.org/protobuf/cmd/protoc-gen-go
|
|
21
26
|
|
|
22
|
-
$(
|
|
27
|
+
$(PROTOC_GEN_GO_STARPC):
|
|
23
28
|
cd ./hack; \
|
|
24
29
|
go build -v \
|
|
25
|
-
-o ./bin/protoc-gen-go-
|
|
26
|
-
github.com/
|
|
30
|
+
-o ./bin/protoc-gen-go-starpc \
|
|
31
|
+
github.com/aperturerobotics/starpc/cmd/protoc-gen-go-starpc
|
|
27
32
|
|
|
28
|
-
$(
|
|
33
|
+
$(PROTOC_GEN_VTPROTO):
|
|
29
34
|
cd ./hack; \
|
|
30
35
|
go build -v \
|
|
31
|
-
-o ./bin/protoc-gen-go-
|
|
32
|
-
github.com/
|
|
36
|
+
-o ./bin/protoc-gen-go-vtproto \
|
|
37
|
+
github.com/planetscale/vtprotobuf/cmd/protoc-gen-go-vtproto
|
|
33
38
|
|
|
34
39
|
$(GOIMPORTS):
|
|
35
40
|
cd ./hack; \
|
|
@@ -56,7 +61,7 @@ $(GO_MOD_OUTDATED):
|
|
|
56
61
|
github.com/psampaz/go-mod-outdated
|
|
57
62
|
|
|
58
63
|
.PHONY: gengo
|
|
59
|
-
gengo: $(GOIMPORTS) $(PROTOWRAP) $(PROTOC_GEN_GO) $(
|
|
64
|
+
gengo: $(GOIMPORTS) $(PROTOWRAP) $(PROTOC_GEN_GO) $(PROTOC_GEN_GO_STARPC) $(PROTOC_GEN_VTPROTO)
|
|
60
65
|
go mod vendor
|
|
61
66
|
shopt -s globstar; \
|
|
62
67
|
set -eo pipefail; \
|
|
@@ -99,9 +104,10 @@ gents: $(PROTOWRAP) node_modules
|
|
|
99
104
|
-I $$(pwd)/vendor \
|
|
100
105
|
--plugin=./node_modules/.bin/protoc-gen-ts_proto \
|
|
101
106
|
--ts_proto_out=$$(pwd)/vendor \
|
|
107
|
+
--ts_proto_opt=esModuleInterop=true \
|
|
108
|
+
--ts_proto_opt=fileSuffix=.pb \
|
|
102
109
|
--ts_proto_opt=forceLong=long \
|
|
103
110
|
--ts_proto_opt=oneof=unions \
|
|
104
|
-
--ts_proto_opt=esModuleInterop=true \
|
|
105
111
|
--ts_proto_opt=outputServices=default,outputServices=generic-definitions \
|
|
106
112
|
--proto_path $$(pwd)/vendor \
|
|
107
113
|
--print_structure \
|
|
@@ -119,15 +125,19 @@ genproto: gengo gents
|
|
|
119
125
|
.PHONY: gen
|
|
120
126
|
gen: genproto
|
|
121
127
|
|
|
128
|
+
.PHONY: outdated
|
|
122
129
|
outdated: $(GO_MOD_OUTDATED)
|
|
123
130
|
go list -mod=mod -u -m -json all | $(GO_MOD_OUTDATED) -update -direct
|
|
124
131
|
|
|
132
|
+
.PHONY: list
|
|
125
133
|
list: $(GO_MOD_OUTDATED)
|
|
126
134
|
go list -mod=mod -u -m -json all | $(GO_MOD_OUTDATED)
|
|
127
135
|
|
|
136
|
+
.PHONY: lint
|
|
128
137
|
lint: $(GOLANGCI_LINT)
|
|
129
138
|
$(GOLANGCI_LINT) run
|
|
130
139
|
|
|
140
|
+
.PHONY: fix
|
|
131
141
|
fix: $(GOLANGCI_LINT)
|
|
132
142
|
$(GOLANGCI_LINT) run --fix
|
|
133
143
|
|
|
@@ -137,5 +147,4 @@ test:
|
|
|
137
147
|
|
|
138
148
|
.PHONY: integration
|
|
139
149
|
integration: node_modules vendor
|
|
140
|
-
cd ./integration &&
|
|
141
|
-
bash ./integration.bash
|
|
150
|
+
cd ./integration && bash ./integration.bash
|
package/README.md
CHANGED
|
@@ -13,6 +13,10 @@ 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
|
+
[rpcstream] supports sub-streams for per-component sub-services.
|
|
17
|
+
|
|
18
|
+
[rpcstream]: ./rpcstream
|
|
19
|
+
|
|
16
20
|
# Usage
|
|
17
21
|
|
|
18
22
|
Starting with the [protobuf-project] repository on the "starpc" branch.
|
package/dist/echo/client-test.js
CHANGED
|
File without changes
|
|
File without changes
|
package/dist/echo/index.d.ts
CHANGED
package/dist/echo/index.js
CHANGED
package/dist/echo/server.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Observable } from 'rxjs';
|
|
2
|
-
import { Echoer, EchoMsg } from './echo';
|
|
2
|
+
import { Echoer, EchoMsg } from './echo.pb.js';
|
|
3
3
|
export declare class EchoerServer implements Echoer {
|
|
4
4
|
Echo(request: EchoMsg): Promise<EchoMsg>;
|
|
5
5
|
EchoServerStream(request: EchoMsg): Observable<EchoMsg>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Observable } from 'rxjs';
|
|
2
|
+
import { Packet } from './rpcstream.pb.js';
|
|
3
|
+
import { Server } from '../srpc/server.js';
|
|
4
|
+
import { OpenStreamFunc, Stream } from '../srpc/stream.js';
|
|
5
|
+
import { Pushable } from 'it-pushable';
|
|
6
|
+
import { Source, Sink } from 'it-stream-types';
|
|
7
|
+
export declare type RpcStreamCaller = (request: Observable<Packet>) => Observable<Packet>;
|
|
8
|
+
export declare function buildRpcStreamOpenStream(componentId: string, caller: RpcStreamCaller): OpenStreamFunc;
|
|
9
|
+
export declare type RpcStreamGetter = (componentId: string) => Promise<Server>;
|
|
10
|
+
export declare function handleRpcStream(stream: Observable<Packet>, getter: RpcStreamGetter): AsyncIterable<Packet>;
|
|
11
|
+
export declare class RpcStream implements Stream {
|
|
12
|
+
readonly source: Source<Uint8Array>;
|
|
13
|
+
readonly sink: Sink<Uint8Array>;
|
|
14
|
+
private readonly _packetSink;
|
|
15
|
+
private readonly _source;
|
|
16
|
+
constructor(packetSink: Pushable<Packet>, packetSource: Observable<Packet>);
|
|
17
|
+
private _createSink;
|
|
18
|
+
private _subscribePacketSource;
|
|
19
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { from as obsFrom } from 'rxjs';
|
|
2
|
+
import { pushable } from 'it-pushable';
|
|
3
|
+
// buildRpcStreamOpenStream builds a OpenStream func with a RpcStream.
|
|
4
|
+
export function buildRpcStreamOpenStream(componentId, caller) {
|
|
5
|
+
return async () => {
|
|
6
|
+
const packetSink = pushable({ objectMode: true });
|
|
7
|
+
const packetObs = obsFrom(packetSink);
|
|
8
|
+
const packetSource = caller(packetObs);
|
|
9
|
+
// write the component id
|
|
10
|
+
packetSink.push({
|
|
11
|
+
body: {
|
|
12
|
+
$case: 'init',
|
|
13
|
+
init: { componentId },
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
// build & return the stream
|
|
17
|
+
return new RpcStream(packetSink, packetSource);
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
// handleRpcStream handles an incoming RPC stream (remote is the initiator).
|
|
21
|
+
export async function* handleRpcStream(stream, getter) {
|
|
22
|
+
// read the component id
|
|
23
|
+
const initPromise = new Promise((resolve, reject) => {
|
|
24
|
+
const subscription = stream.subscribe({
|
|
25
|
+
next(value) {
|
|
26
|
+
resolve(value);
|
|
27
|
+
subscription.unsubscribe();
|
|
28
|
+
},
|
|
29
|
+
error(err) {
|
|
30
|
+
reject(err);
|
|
31
|
+
},
|
|
32
|
+
complete() {
|
|
33
|
+
reject(new Error('no packet received'));
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
// read the init packet
|
|
38
|
+
const initPacket = await initPromise;
|
|
39
|
+
if (initPacket?.body?.$case !== 'init') {
|
|
40
|
+
throw new Error('expected init packet');
|
|
41
|
+
}
|
|
42
|
+
// lookup the server for the component id.
|
|
43
|
+
const server = await getter(initPacket.body.init.componentId);
|
|
44
|
+
// build the outgoing packet sink & the packet source
|
|
45
|
+
const packetSink = pushable({ objectMode: true });
|
|
46
|
+
// handle the stream
|
|
47
|
+
const rpcStream = new RpcStream(packetSink, stream);
|
|
48
|
+
server.handleDuplex(rpcStream);
|
|
49
|
+
// return the outgoing packet sink
|
|
50
|
+
return packetSink;
|
|
51
|
+
}
|
|
52
|
+
// RpcStream implements the Stream on top of a RPC call.
|
|
53
|
+
export class RpcStream {
|
|
54
|
+
constructor(packetSink, packetSource) {
|
|
55
|
+
this._packetSink = packetSink;
|
|
56
|
+
this.sink = this._createSink();
|
|
57
|
+
const source = pushable({ objectMode: true });
|
|
58
|
+
this.source = source;
|
|
59
|
+
this._source = source;
|
|
60
|
+
this._subscribePacketSource(packetSource);
|
|
61
|
+
}
|
|
62
|
+
// _createSink initializes the sink field.
|
|
63
|
+
_createSink() {
|
|
64
|
+
return async (source) => {
|
|
65
|
+
try {
|
|
66
|
+
for await (const msg of source) {
|
|
67
|
+
this._packetSink.push({
|
|
68
|
+
body: { $case: 'data', data: msg }
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
this._packetSink.end();
|
|
72
|
+
}
|
|
73
|
+
catch (err) {
|
|
74
|
+
this._packetSink.end(err);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
// _subscribePacketSource starts the subscription to the response data.
|
|
79
|
+
_subscribePacketSource(packetSource) {
|
|
80
|
+
packetSource.subscribe({
|
|
81
|
+
next: (value) => {
|
|
82
|
+
if (value?.body?.$case === 'data') {
|
|
83
|
+
this._source.push(value.body.data);
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
error: (err) => {
|
|
87
|
+
this._source.end(err);
|
|
88
|
+
},
|
|
89
|
+
complete: () => {
|
|
90
|
+
this._source.end();
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import Long from 'long';
|
|
2
|
+
import * as _m0 from 'protobufjs/minimal';
|
|
3
|
+
export declare const protobufPackage = "rpcstream";
|
|
4
|
+
/** Packet is a packet encapsulating data for a RPC stream. */
|
|
5
|
+
export interface Packet {
|
|
6
|
+
body?: {
|
|
7
|
+
$case: 'init';
|
|
8
|
+
init: RpcStreamInit;
|
|
9
|
+
} | {
|
|
10
|
+
$case: 'data';
|
|
11
|
+
data: Uint8Array;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
/** RpcStreamInit is the first message in a RPC stream. */
|
|
15
|
+
export interface RpcStreamInit {
|
|
16
|
+
/** ComponentId is the identifier of the component making the request. */
|
|
17
|
+
componentId: string;
|
|
18
|
+
}
|
|
19
|
+
export declare const Packet: {
|
|
20
|
+
encode(message: Packet, writer?: _m0.Writer): _m0.Writer;
|
|
21
|
+
decode(input: _m0.Reader | Uint8Array, length?: number): Packet;
|
|
22
|
+
fromJSON(object: any): Packet;
|
|
23
|
+
toJSON(message: Packet): unknown;
|
|
24
|
+
fromPartial<I extends {
|
|
25
|
+
body?: ({
|
|
26
|
+
init?: {
|
|
27
|
+
componentId?: string | undefined;
|
|
28
|
+
} | undefined;
|
|
29
|
+
} & {
|
|
30
|
+
$case: "init";
|
|
31
|
+
}) | ({
|
|
32
|
+
data?: Uint8Array | undefined;
|
|
33
|
+
} & {
|
|
34
|
+
$case: "data";
|
|
35
|
+
}) | undefined;
|
|
36
|
+
} & {
|
|
37
|
+
body?: ({
|
|
38
|
+
init?: {
|
|
39
|
+
componentId?: string | undefined;
|
|
40
|
+
} | undefined;
|
|
41
|
+
} & {
|
|
42
|
+
$case: "init";
|
|
43
|
+
} & {
|
|
44
|
+
init?: ({
|
|
45
|
+
componentId?: string | undefined;
|
|
46
|
+
} & {
|
|
47
|
+
componentId?: string | undefined;
|
|
48
|
+
} & Record<Exclude<keyof I["body"]["init"], "componentId">, never>) | undefined;
|
|
49
|
+
$case: "init";
|
|
50
|
+
} & Record<Exclude<keyof I["body"], "$case" | "init">, never>) | ({
|
|
51
|
+
data?: Uint8Array | undefined;
|
|
52
|
+
} & {
|
|
53
|
+
$case: "data";
|
|
54
|
+
} & {
|
|
55
|
+
data?: Uint8Array | undefined;
|
|
56
|
+
$case: "data";
|
|
57
|
+
} & Record<Exclude<keyof I["body"], "$case" | "data">, never>) | undefined;
|
|
58
|
+
} & Record<Exclude<keyof I, "body">, never>>(object: I): Packet;
|
|
59
|
+
};
|
|
60
|
+
export declare const RpcStreamInit: {
|
|
61
|
+
encode(message: RpcStreamInit, writer?: _m0.Writer): _m0.Writer;
|
|
62
|
+
decode(input: _m0.Reader | Uint8Array, length?: number): RpcStreamInit;
|
|
63
|
+
fromJSON(object: any): RpcStreamInit;
|
|
64
|
+
toJSON(message: RpcStreamInit): unknown;
|
|
65
|
+
fromPartial<I extends {
|
|
66
|
+
componentId?: string | undefined;
|
|
67
|
+
} & {
|
|
68
|
+
componentId?: string | undefined;
|
|
69
|
+
} & Record<Exclude<keyof I, "componentId">, never>>(object: I): RpcStreamInit;
|
|
70
|
+
};
|
|
71
|
+
declare type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined;
|
|
72
|
+
export declare type DeepPartial<T> = T extends Builtin ? T : T extends Long ? string | number | Long : T extends Array<infer U> ? Array<DeepPartial<U>> : T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>> : T extends {
|
|
73
|
+
$case: string;
|
|
74
|
+
} ? {
|
|
75
|
+
[K in keyof Omit<T, '$case'>]?: DeepPartial<T[K]>;
|
|
76
|
+
} & {
|
|
77
|
+
$case: T['$case'];
|
|
78
|
+
} : T extends {} ? {
|
|
79
|
+
[K in keyof T]?: DeepPartial<T[K]>;
|
|
80
|
+
} : Partial<T>;
|
|
81
|
+
declare type KeysOfUnion<T> = T extends T ? keyof T : never;
|
|
82
|
+
export declare type Exact<P, I extends P> = P extends Builtin ? P : P & {
|
|
83
|
+
[K in keyof P]: Exact<P[K], I[K]>;
|
|
84
|
+
} & Record<Exclude<keyof I, KeysOfUnion<P>>, never>;
|
|
85
|
+
export {};
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
2
|
+
import Long from 'long';
|
|
3
|
+
import * as _m0 from 'protobufjs/minimal';
|
|
4
|
+
export const protobufPackage = 'rpcstream';
|
|
5
|
+
function createBasePacket() {
|
|
6
|
+
return { body: undefined };
|
|
7
|
+
}
|
|
8
|
+
export const Packet = {
|
|
9
|
+
encode(message, writer = _m0.Writer.create()) {
|
|
10
|
+
if (message.body?.$case === 'init') {
|
|
11
|
+
RpcStreamInit.encode(message.body.init, writer.uint32(10).fork()).ldelim();
|
|
12
|
+
}
|
|
13
|
+
if (message.body?.$case === 'data') {
|
|
14
|
+
writer.uint32(18).bytes(message.body.data);
|
|
15
|
+
}
|
|
16
|
+
return writer;
|
|
17
|
+
},
|
|
18
|
+
decode(input, length) {
|
|
19
|
+
const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input);
|
|
20
|
+
let end = length === undefined ? reader.len : reader.pos + length;
|
|
21
|
+
const message = createBasePacket();
|
|
22
|
+
while (reader.pos < end) {
|
|
23
|
+
const tag = reader.uint32();
|
|
24
|
+
switch (tag >>> 3) {
|
|
25
|
+
case 1:
|
|
26
|
+
message.body = {
|
|
27
|
+
$case: 'init',
|
|
28
|
+
init: RpcStreamInit.decode(reader, reader.uint32()),
|
|
29
|
+
};
|
|
30
|
+
break;
|
|
31
|
+
case 2:
|
|
32
|
+
message.body = { $case: 'data', data: reader.bytes() };
|
|
33
|
+
break;
|
|
34
|
+
default:
|
|
35
|
+
reader.skipType(tag & 7);
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return message;
|
|
40
|
+
},
|
|
41
|
+
fromJSON(object) {
|
|
42
|
+
return {
|
|
43
|
+
body: isSet(object.init)
|
|
44
|
+
? { $case: 'init', init: RpcStreamInit.fromJSON(object.init) }
|
|
45
|
+
: isSet(object.data)
|
|
46
|
+
? { $case: 'data', data: bytesFromBase64(object.data) }
|
|
47
|
+
: undefined,
|
|
48
|
+
};
|
|
49
|
+
},
|
|
50
|
+
toJSON(message) {
|
|
51
|
+
const obj = {};
|
|
52
|
+
message.body?.$case === 'init' &&
|
|
53
|
+
(obj.init = message.body?.init
|
|
54
|
+
? RpcStreamInit.toJSON(message.body?.init)
|
|
55
|
+
: undefined);
|
|
56
|
+
message.body?.$case === 'data' &&
|
|
57
|
+
(obj.data =
|
|
58
|
+
message.body?.data !== undefined
|
|
59
|
+
? base64FromBytes(message.body?.data)
|
|
60
|
+
: undefined);
|
|
61
|
+
return obj;
|
|
62
|
+
},
|
|
63
|
+
fromPartial(object) {
|
|
64
|
+
const message = createBasePacket();
|
|
65
|
+
if (object.body?.$case === 'init' &&
|
|
66
|
+
object.body?.init !== undefined &&
|
|
67
|
+
object.body?.init !== null) {
|
|
68
|
+
message.body = {
|
|
69
|
+
$case: 'init',
|
|
70
|
+
init: RpcStreamInit.fromPartial(object.body.init),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
if (object.body?.$case === 'data' &&
|
|
74
|
+
object.body?.data !== undefined &&
|
|
75
|
+
object.body?.data !== null) {
|
|
76
|
+
message.body = { $case: 'data', data: object.body.data };
|
|
77
|
+
}
|
|
78
|
+
return message;
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
function createBaseRpcStreamInit() {
|
|
82
|
+
return { componentId: '' };
|
|
83
|
+
}
|
|
84
|
+
export const RpcStreamInit = {
|
|
85
|
+
encode(message, writer = _m0.Writer.create()) {
|
|
86
|
+
if (message.componentId !== '') {
|
|
87
|
+
writer.uint32(10).string(message.componentId);
|
|
88
|
+
}
|
|
89
|
+
return writer;
|
|
90
|
+
},
|
|
91
|
+
decode(input, length) {
|
|
92
|
+
const reader = input instanceof _m0.Reader ? input : new _m0.Reader(input);
|
|
93
|
+
let end = length === undefined ? reader.len : reader.pos + length;
|
|
94
|
+
const message = createBaseRpcStreamInit();
|
|
95
|
+
while (reader.pos < end) {
|
|
96
|
+
const tag = reader.uint32();
|
|
97
|
+
switch (tag >>> 3) {
|
|
98
|
+
case 1:
|
|
99
|
+
message.componentId = reader.string();
|
|
100
|
+
break;
|
|
101
|
+
default:
|
|
102
|
+
reader.skipType(tag & 7);
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return message;
|
|
107
|
+
},
|
|
108
|
+
fromJSON(object) {
|
|
109
|
+
return {
|
|
110
|
+
componentId: isSet(object.componentId) ? String(object.componentId) : '',
|
|
111
|
+
};
|
|
112
|
+
},
|
|
113
|
+
toJSON(message) {
|
|
114
|
+
const obj = {};
|
|
115
|
+
message.componentId !== undefined && (obj.componentId = message.componentId);
|
|
116
|
+
return obj;
|
|
117
|
+
},
|
|
118
|
+
fromPartial(object) {
|
|
119
|
+
const message = createBaseRpcStreamInit();
|
|
120
|
+
message.componentId = object.componentId ?? '';
|
|
121
|
+
return message;
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
var globalThis = (() => {
|
|
125
|
+
if (typeof globalThis !== 'undefined')
|
|
126
|
+
return globalThis;
|
|
127
|
+
if (typeof self !== 'undefined')
|
|
128
|
+
return self;
|
|
129
|
+
if (typeof window !== 'undefined')
|
|
130
|
+
return window;
|
|
131
|
+
if (typeof global !== 'undefined')
|
|
132
|
+
return global;
|
|
133
|
+
throw 'Unable to locate global object';
|
|
134
|
+
})();
|
|
135
|
+
const atob = globalThis.atob ||
|
|
136
|
+
((b64) => globalThis.Buffer.from(b64, 'base64').toString('binary'));
|
|
137
|
+
function bytesFromBase64(b64) {
|
|
138
|
+
const bin = atob(b64);
|
|
139
|
+
const arr = new Uint8Array(bin.length);
|
|
140
|
+
for (let i = 0; i < bin.length; ++i) {
|
|
141
|
+
arr[i] = bin.charCodeAt(i);
|
|
142
|
+
}
|
|
143
|
+
return arr;
|
|
144
|
+
}
|
|
145
|
+
const btoa = globalThis.btoa ||
|
|
146
|
+
((bin) => globalThis.Buffer.from(bin, 'binary').toString('base64'));
|
|
147
|
+
function base64FromBytes(arr) {
|
|
148
|
+
const bin = [];
|
|
149
|
+
arr.forEach((byte) => {
|
|
150
|
+
bin.push(String.fromCharCode(byte));
|
|
151
|
+
});
|
|
152
|
+
return btoa(bin.join(''));
|
|
153
|
+
}
|
|
154
|
+
if (_m0.util.Long !== Long) {
|
|
155
|
+
_m0.util.Long = Long;
|
|
156
|
+
_m0.configure();
|
|
157
|
+
}
|
|
158
|
+
function isSet(value) {
|
|
159
|
+
return value !== null && value !== undefined;
|
|
160
|
+
}
|
package/dist/srpc/client.d.ts
CHANGED
|
@@ -2,11 +2,11 @@ import { Observable } from 'rxjs';
|
|
|
2
2
|
import type { TsProtoRpc } from './ts-proto-rpc.js';
|
|
3
3
|
import type { OpenStreamFunc } from './stream.js';
|
|
4
4
|
export declare class Client implements TsProtoRpc {
|
|
5
|
-
private
|
|
6
|
-
private
|
|
7
|
-
constructor(
|
|
8
|
-
|
|
9
|
-
private
|
|
5
|
+
private openStreamFn;
|
|
6
|
+
private _openStreamFn?;
|
|
7
|
+
constructor(openStreamFn?: OpenStreamFunc);
|
|
8
|
+
setOpenStreamFn(openStreamFn?: OpenStreamFunc): Promise<OpenStreamFunc>;
|
|
9
|
+
private initOpenStreamFn;
|
|
10
10
|
request(service: string, method: string, data: Uint8Array): Promise<Uint8Array>;
|
|
11
11
|
clientStreamingRequest(service: string, method: string, data: Observable<Uint8Array>): Promise<Uint8Array>;
|
|
12
12
|
serverStreamingRequest(service: string, method: string, data: Uint8Array): Observable<Uint8Array>;
|
package/dist/srpc/client.js
CHANGED
|
@@ -19,31 +19,31 @@ function writeClientStream(call, data) {
|
|
|
19
19
|
}
|
|
20
20
|
// Client implements the ts-proto Rpc interface with the drpcproto protocol.
|
|
21
21
|
export class Client {
|
|
22
|
-
constructor(
|
|
23
|
-
this.
|
|
22
|
+
constructor(openStreamFn) {
|
|
23
|
+
this.openStreamFn = this.setOpenStreamFn(openStreamFn);
|
|
24
24
|
}
|
|
25
|
-
//
|
|
26
|
-
|
|
27
|
-
if (this.
|
|
28
|
-
if (
|
|
29
|
-
this.
|
|
30
|
-
this.
|
|
25
|
+
// setOpenStreamFn updates the openStreamFn for the Client.
|
|
26
|
+
setOpenStreamFn(openStreamFn) {
|
|
27
|
+
if (this._openStreamFn) {
|
|
28
|
+
if (openStreamFn) {
|
|
29
|
+
this._openStreamFn(openStreamFn);
|
|
30
|
+
this._openStreamFn = undefined;
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
else {
|
|
34
|
-
if (
|
|
35
|
-
this.
|
|
34
|
+
if (openStreamFn) {
|
|
35
|
+
this.openStreamFn = Promise.resolve(openStreamFn);
|
|
36
36
|
}
|
|
37
37
|
else {
|
|
38
|
-
this.
|
|
38
|
+
this.initOpenStreamFn();
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
|
-
return this.
|
|
41
|
+
return this.openStreamFn;
|
|
42
42
|
}
|
|
43
|
-
//
|
|
44
|
-
|
|
43
|
+
// initOpenStreamFn creates the empty Promise for openStreamFn.
|
|
44
|
+
initOpenStreamFn() {
|
|
45
45
|
const openPromise = new Promise((resolve, reject) => {
|
|
46
|
-
this.
|
|
46
|
+
this._openStreamFn = (conn, err) => {
|
|
47
47
|
if (err) {
|
|
48
48
|
reject(err);
|
|
49
49
|
}
|
|
@@ -52,8 +52,8 @@ export class Client {
|
|
|
52
52
|
}
|
|
53
53
|
};
|
|
54
54
|
});
|
|
55
|
-
this.
|
|
56
|
-
return this.
|
|
55
|
+
this.openStreamFn = openPromise;
|
|
56
|
+
return this.openStreamFn;
|
|
57
57
|
}
|
|
58
58
|
// request starts a non-streaming request.
|
|
59
59
|
async request(service, method, data) {
|
|
@@ -131,8 +131,8 @@ export class Client {
|
|
|
131
131
|
// throws any error starting the rpc call
|
|
132
132
|
// if data == null and data.length == 0, sends a separate data packet.
|
|
133
133
|
async startRpc(rpcService, rpcMethod, data) {
|
|
134
|
-
const
|
|
135
|
-
const conn = await
|
|
134
|
+
const openStreamFn = await this.openStreamFn;
|
|
135
|
+
const conn = await openStreamFn();
|
|
136
136
|
const call = new ClientRPC(rpcService, rpcMethod);
|
|
137
137
|
pipe(conn, parseLengthPrefixTransform(), decodePacketSource, call, encodePacketSource, prependLengthPrefixTransform(), conn);
|
|
138
138
|
await call.writeCallStart(data || undefined);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Source, Sink } from 'it-stream-types';
|
|
2
|
-
import type { CallData, CallStart } from './rpcproto.js';
|
|
3
|
-
import { Packet } from './rpcproto.js';
|
|
2
|
+
import type { CallData, CallStart } from './rpcproto.pb.js';
|
|
3
|
+
import { Packet } from './rpcproto.pb.js';
|
|
4
4
|
export declare class CommonRPC {
|
|
5
5
|
sink: Sink<Packet>;
|
|
6
6
|
source: AsyncIterable<Packet>;
|
package/dist/srpc/common-rpc.js
CHANGED
package/dist/srpc/conn.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export interface ConnParams {
|
|
|
7
7
|
muxerFactory?: StreamMuxerFactory;
|
|
8
8
|
}
|
|
9
9
|
export interface StreamHandler {
|
|
10
|
-
handleStream(strm: Duplex<Uint8Array>):
|
|
10
|
+
handleStream(strm: Duplex<Uint8Array>): void;
|
|
11
11
|
}
|
|
12
12
|
export declare class Conn implements Duplex<Uint8Array> {
|
|
13
13
|
private muxer;
|
package/dist/srpc/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { OpenStreamFunc } from './stream.js';
|
|
1
|
+
export type { PacketHandler, Stream, OpenStreamFunc } from './stream.js';
|
|
2
2
|
export { Client } from './client.js';
|
|
3
3
|
export { Server } from './server.js';
|
|
4
4
|
export { Conn, ConnParams } from './conn.js';
|
|
@@ -6,3 +6,4 @@ export { Handler, InvokeFn, createHandler, createInvokeFn } from './handler.js';
|
|
|
6
6
|
export { Mux, createMux } from './mux.js';
|
|
7
7
|
export { BroadcastChannelIterable, newBroadcastChannelIterable, BroadcastChannelConn, } from './broadcast-channel.js';
|
|
8
8
|
export { MessagePortIterable, newMessagePortIterable, MessagePortConn, } from './message-port.js';
|
|
9
|
+
export { ObservableSource } from './observable-source.js';
|
package/dist/srpc/index.js
CHANGED
|
@@ -5,3 +5,4 @@ export { createHandler, createInvokeFn } from './handler.js';
|
|
|
5
5
|
export { createMux } from './mux.js';
|
|
6
6
|
export { BroadcastChannelIterable, newBroadcastChannelIterable, BroadcastChannelConn, } from './broadcast-channel.js';
|
|
7
7
|
export { MessagePortIterable, newMessagePortIterable, MessagePortConn, } from './message-port.js';
|
|
8
|
+
export { ObservableSource } from './observable-source.js';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { Source } from 'it-stream-types';
|
|
2
|
+
import { Observable } from 'rxjs';
|
|
3
|
+
export declare class ObservableSource<T> {
|
|
4
|
+
readonly source: Source<T>;
|
|
5
|
+
private readonly _source;
|
|
6
|
+
private readonly subscription;
|
|
7
|
+
constructor(observable: Observable<T>);
|
|
8
|
+
close(err?: Error): void;
|
|
9
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { pushable } from 'it-pushable';
|
|
2
|
+
// ObservableSource wraps an Observable into a Source.
|
|
3
|
+
export class ObservableSource {
|
|
4
|
+
constructor(observable) {
|
|
5
|
+
const source = pushable({ objectMode: true });
|
|
6
|
+
this.source = source;
|
|
7
|
+
this._source = source;
|
|
8
|
+
this.subscription = observable.subscribe({
|
|
9
|
+
next: (value) => {
|
|
10
|
+
this._source.push(value);
|
|
11
|
+
},
|
|
12
|
+
error: (err) => {
|
|
13
|
+
this._source.end(err);
|
|
14
|
+
},
|
|
15
|
+
complete: () => {
|
|
16
|
+
this._source.end();
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
// close closes the subscription.
|
|
21
|
+
close(err) {
|
|
22
|
+
this._source.end(err);
|
|
23
|
+
this.subscription.unsubscribe();
|
|
24
|
+
}
|
|
25
|
+
}
|
package/dist/srpc/packet.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Transform } from 'it-stream-types';
|
|
2
|
-
import { Packet } from './rpcproto.js';
|
|
2
|
+
import { Packet } from './rpcproto.pb.js';
|
|
3
3
|
export declare const decodePacketSource: (source: import("it-stream-types").Source<Uint8Array | Uint8Array[]>) => AsyncIterable<Packet>;
|
|
4
4
|
export declare const encodePacketSource: (source: import("it-stream-types").Source<Packet | Packet[]>) => AsyncIterable<Uint8Array>;
|
|
5
5
|
export declare function prependLengthPrefixTransform(): Transform<Uint8Array>;
|
package/dist/srpc/packet.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { encode as lengthPrefixEncode, decode as lengthPrefixDecode, } from 'it-length-prefixed';
|
|
2
|
-
import { Packet } from './rpcproto.js';
|
|
2
|
+
import { Packet } from './rpcproto.pb.js';
|
|
3
3
|
import { buildDecodeMessageTransform, buildEncodeMessageTransform, } from './message.js';
|
|
4
4
|
// decodePacketSource decodes packets from a binary data stream.
|
|
5
5
|
export const decodePacketSource = buildDecodeMessageTransform(Packet);
|
|
File without changes
|
|
File without changes
|
package/dist/srpc/server.d.ts
CHANGED
|
@@ -2,13 +2,13 @@ import { Stream } from '@libp2p/interface-connection';
|
|
|
2
2
|
import { Duplex } from 'it-stream-types';
|
|
3
3
|
import { Mux } from './mux.js';
|
|
4
4
|
import { ServerRPC } from './server-rpc.js';
|
|
5
|
-
import { Packet } from './rpcproto.js';
|
|
5
|
+
import { Packet } from './rpcproto.pb.js';
|
|
6
6
|
import { StreamHandler } from './conn.js';
|
|
7
7
|
export declare class Server implements StreamHandler {
|
|
8
8
|
private mux;
|
|
9
9
|
constructor(mux: Mux);
|
|
10
10
|
startRpc(): ServerRPC;
|
|
11
|
-
handleStream(stream: Stream):
|
|
12
|
-
handleDuplex(stream: Duplex<Uint8Array>):
|
|
13
|
-
handlePacketStream(stream: Duplex<Packet>):
|
|
11
|
+
handleStream(stream: Stream): ServerRPC;
|
|
12
|
+
handleDuplex(stream: Duplex<Uint8Array>): ServerRPC;
|
|
13
|
+
handlePacketStream(stream: Duplex<Packet>): ServerRPC;
|
|
14
14
|
}
|
package/dist/srpc/server.js
CHANGED
|
@@ -12,18 +12,19 @@ export class Server {
|
|
|
12
12
|
return new ServerRPC(this.mux);
|
|
13
13
|
}
|
|
14
14
|
// handleStream handles an incoming Uint8Array message duplex.
|
|
15
|
-
// closes the stream when the rpc completes.
|
|
16
15
|
handleStream(stream) {
|
|
17
16
|
return this.handleDuplex(stream);
|
|
18
17
|
}
|
|
19
18
|
// handleDuplex handles an incoming message duplex.
|
|
20
|
-
|
|
19
|
+
handleDuplex(stream) {
|
|
21
20
|
const rpc = this.startRpc();
|
|
22
|
-
|
|
21
|
+
pipe(stream, parseLengthPrefixTransform(), decodePacketSource, rpc, encodePacketSource, prependLengthPrefixTransform(), stream);
|
|
22
|
+
return rpc;
|
|
23
23
|
}
|
|
24
24
|
// handlePacketStream handles an incoming Packet duplex.
|
|
25
|
-
|
|
25
|
+
handlePacketStream(stream) {
|
|
26
26
|
const rpc = this.startRpc();
|
|
27
|
-
|
|
27
|
+
pipe(stream, rpc, stream);
|
|
28
|
+
return rpc;
|
|
28
29
|
}
|
|
29
30
|
}
|
package/dist/srpc/stream.d.ts
CHANGED
package/echo/client-test.ts
CHANGED
|
File without changes
|
package/echo/index.ts
CHANGED
package/echo/server.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { WebSocketConn } from '../srpc/websocket'
|
|
2
|
-
import { runClientTest } from '../echo'
|
|
1
|
+
import { WebSocketConn } from '../srpc/websocket.js'
|
|
2
|
+
import { runClientTest } from '../echo/client-test.js'
|
|
3
3
|
import WebSocket from 'isomorphic-ws'
|
|
4
4
|
|
|
5
5
|
async function runRPC() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "starpc",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "Streaming protobuf RPC service protocol over any two-way channel.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
"Makefile",
|
|
23
23
|
"dist/echo",
|
|
24
24
|
"dist/srpc",
|
|
25
|
+
"dist/rpcstream",
|
|
25
26
|
"e2e",
|
|
26
27
|
"echo",
|
|
27
28
|
"go.mod",
|
|
@@ -39,7 +40,7 @@
|
|
|
39
40
|
"deps": "depcheck",
|
|
40
41
|
"codegen": "npm run gen",
|
|
41
42
|
"ci": "npm run build && npm run lint:js && npm run lint:go",
|
|
42
|
-
"format": "prettier --write './{srpc,echo,e2e,integration}/**/(*.ts|*.tsx|*.html|*.css)'",
|
|
43
|
+
"format": "prettier --write './{srpc,echo,e2e,integration,rpcstream}/**/(*.ts|*.tsx|*.html|*.css)'",
|
|
43
44
|
"gen": "make genproto",
|
|
44
45
|
"test": "npm run test:js && npm run test:go",
|
|
45
46
|
"test:go": "make test",
|
|
@@ -48,7 +49,7 @@
|
|
|
48
49
|
"integration": "npm run test:integration",
|
|
49
50
|
"lint": "npm run lint:go && npm run lint:js",
|
|
50
51
|
"lint:go": "make lint",
|
|
51
|
-
"lint:js": "eslint -c .eslintrc.js --ext .ts ./{srpc,echo}/**/*.ts",
|
|
52
|
+
"lint:js": "eslint -c .eslintrc.js --ext .ts ./{srpc,echo,rpcstream}/**/*.ts",
|
|
52
53
|
"prepare": "patch-package",
|
|
53
54
|
"precommit": "npm run format"
|
|
54
55
|
},
|
|
@@ -46,8 +46,8 @@ export class BroadcastChannelIterable<T> implements Duplex<T> {
|
|
|
46
46
|
queue.push(ev.data)
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
|
-
this.readChannel.addEventListener('message', messageListener)
|
|
50
49
|
|
|
50
|
+
this.readChannel.addEventListener('message', messageListener)
|
|
51
51
|
return () => {
|
|
52
52
|
this.readChannel.removeEventListener('message', messageListener)
|
|
53
53
|
}
|
package/srpc/client-rpc.ts
CHANGED
package/srpc/client.go
CHANGED
|
@@ -19,7 +19,7 @@ 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
|
|
22
|
+
type OpenStreamFunc = func(ctx context.Context, msgHandler PacketHandler) (Writer, error)
|
|
23
23
|
|
|
24
24
|
// client implements Client with a transport.
|
|
25
25
|
type client struct {
|
|
@@ -89,7 +89,7 @@ func (c *client) NewStream(ctx context.Context, service, method string, firstMsg
|
|
|
89
89
|
return nil, err
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
return
|
|
92
|
+
return NewMsgStream(ctx, clientRPC.writer, clientRPC.dataCh), nil
|
|
93
93
|
}
|
|
94
94
|
|
|
95
95
|
// _ is a type assertion
|
package/srpc/client.ts
CHANGED
|
@@ -29,36 +29,38 @@ function writeClientStream(call: ClientRPC, data: Observable<Uint8Array>) {
|
|
|
29
29
|
|
|
30
30
|
// Client implements the ts-proto Rpc interface with the drpcproto protocol.
|
|
31
31
|
export class Client implements TsProtoRpc {
|
|
32
|
-
//
|
|
33
|
-
private
|
|
34
|
-
//
|
|
35
|
-
private
|
|
32
|
+
// openStreamFn is a promise which contains the OpenStreamFunc.
|
|
33
|
+
private openStreamFn: Promise<OpenStreamFunc>
|
|
34
|
+
// _openStreamFn resolves openStreamFn.
|
|
35
|
+
private _openStreamFn?: (conn?: OpenStreamFunc, err?: Error) => void
|
|
36
36
|
|
|
37
|
-
constructor(
|
|
38
|
-
this.
|
|
37
|
+
constructor(openStreamFn?: OpenStreamFunc) {
|
|
38
|
+
this.openStreamFn = this.setOpenStreamFn(openStreamFn)
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
//
|
|
42
|
-
public
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
41
|
+
// setOpenStreamFn updates the openStreamFn for the Client.
|
|
42
|
+
public setOpenStreamFn(
|
|
43
|
+
openStreamFn?: OpenStreamFunc
|
|
44
|
+
): Promise<OpenStreamFunc> {
|
|
45
|
+
if (this._openStreamFn) {
|
|
46
|
+
if (openStreamFn) {
|
|
47
|
+
this._openStreamFn(openStreamFn)
|
|
48
|
+
this._openStreamFn = undefined
|
|
47
49
|
}
|
|
48
50
|
} else {
|
|
49
|
-
if (
|
|
50
|
-
this.
|
|
51
|
+
if (openStreamFn) {
|
|
52
|
+
this.openStreamFn = Promise.resolve(openStreamFn)
|
|
51
53
|
} else {
|
|
52
|
-
this.
|
|
54
|
+
this.initOpenStreamFn()
|
|
53
55
|
}
|
|
54
56
|
}
|
|
55
|
-
return this.
|
|
57
|
+
return this.openStreamFn
|
|
56
58
|
}
|
|
57
59
|
|
|
58
|
-
//
|
|
59
|
-
private
|
|
60
|
+
// initOpenStreamFn creates the empty Promise for openStreamFn.
|
|
61
|
+
private initOpenStreamFn(): Promise<OpenStreamFunc> {
|
|
60
62
|
const openPromise = new Promise<OpenStreamFunc>((resolve, reject) => {
|
|
61
|
-
this.
|
|
63
|
+
this._openStreamFn = (conn?: OpenStreamFunc, err?: Error) => {
|
|
62
64
|
if (err) {
|
|
63
65
|
reject(err)
|
|
64
66
|
} else if (conn) {
|
|
@@ -66,8 +68,8 @@ export class Client implements TsProtoRpc {
|
|
|
66
68
|
}
|
|
67
69
|
}
|
|
68
70
|
})
|
|
69
|
-
this.
|
|
70
|
-
return this.
|
|
71
|
+
this.openStreamFn = openPromise
|
|
72
|
+
return this.openStreamFn
|
|
71
73
|
}
|
|
72
74
|
|
|
73
75
|
// request starts a non-streaming request.
|
|
@@ -168,8 +170,8 @@ export class Client implements TsProtoRpc {
|
|
|
168
170
|
rpcMethod: string,
|
|
169
171
|
data: Uint8Array | null
|
|
170
172
|
): Promise<ClientRPC> {
|
|
171
|
-
const
|
|
172
|
-
const conn = await
|
|
173
|
+
const openStreamFn = await this.openStreamFn
|
|
174
|
+
const conn = await openStreamFn()
|
|
173
175
|
const call = new ClientRPC(rpcService, rpcMethod)
|
|
174
176
|
pipe(
|
|
175
177
|
conn,
|
package/srpc/common-rpc.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Source, Sink } from 'it-stream-types'
|
|
2
2
|
import { pushable } from 'it-pushable'
|
|
3
3
|
|
|
4
|
-
import type { CallData, CallStart } from './rpcproto.js'
|
|
5
|
-
import { Packet } from './rpcproto.js'
|
|
4
|
+
import type { CallData, CallStart } from './rpcproto.pb.js'
|
|
5
|
+
import { Packet } from './rpcproto.pb.js'
|
|
6
6
|
|
|
7
7
|
// CommonRPC is common logic between server and client RPCs.
|
|
8
8
|
export class CommonRPC {
|
package/srpc/conn.ts
CHANGED
|
@@ -19,7 +19,7 @@ export interface ConnParams {
|
|
|
19
19
|
// Implemented by Server.
|
|
20
20
|
export interface StreamHandler {
|
|
21
21
|
// handleStream handles an incoming stream.
|
|
22
|
-
handleStream(strm: Duplex<Uint8Array>):
|
|
22
|
+
handleStream(strm: Duplex<Uint8Array>): void
|
|
23
23
|
}
|
|
24
24
|
|
|
25
25
|
// Conn implements a generic connection with a two-way stream.
|
package/srpc/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type { OpenStreamFunc } from './stream.js'
|
|
1
|
+
export type { PacketHandler, Stream, OpenStreamFunc } from './stream.js'
|
|
2
2
|
export { Client } from './client.js'
|
|
3
3
|
export { Server } from './server.js'
|
|
4
4
|
export { Conn, ConnParams } from './conn.js'
|
|
@@ -9,9 +9,9 @@ export {
|
|
|
9
9
|
newBroadcastChannelIterable,
|
|
10
10
|
BroadcastChannelConn,
|
|
11
11
|
} from './broadcast-channel.js'
|
|
12
|
-
|
|
13
12
|
export {
|
|
14
13
|
MessagePortIterable,
|
|
15
14
|
newMessagePortIterable,
|
|
16
15
|
MessagePortConn,
|
|
17
16
|
} from './message-port.js'
|
|
17
|
+
export { ObservableSource } from './observable-source.js'
|
|
@@ -5,8 +5,8 @@ import (
|
|
|
5
5
|
"io"
|
|
6
6
|
)
|
|
7
7
|
|
|
8
|
-
//
|
|
9
|
-
type
|
|
8
|
+
// MsgStream implements the stream interface passed to implementations.
|
|
9
|
+
type MsgStream struct {
|
|
10
10
|
// ctx is the stream context
|
|
11
11
|
ctx context.Context
|
|
12
12
|
// writer is the stream writer
|
|
@@ -15,10 +15,10 @@ type RPCStream struct {
|
|
|
15
15
|
dataCh chan []byte
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
//
|
|
18
|
+
// NewMsgStream constructs a new Stream with a ClientRPC.
|
|
19
19
|
// dataCh should be closed when no more messages will arrive.
|
|
20
|
-
func
|
|
21
|
-
return &
|
|
20
|
+
func NewMsgStream(ctx context.Context, writer Writer, dataCh chan []byte) *MsgStream {
|
|
21
|
+
return &MsgStream{
|
|
22
22
|
ctx: ctx,
|
|
23
23
|
writer: writer,
|
|
24
24
|
dataCh: dataCh,
|
|
@@ -26,12 +26,12 @@ func NewRPCStream(ctx context.Context, writer Writer, dataCh chan []byte) *RPCSt
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
// Context is canceled when the Stream is no longer valid.
|
|
29
|
-
func (r *
|
|
29
|
+
func (r *MsgStream) Context() context.Context {
|
|
30
30
|
return r.ctx
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
// MsgSend sends the message to the remote.
|
|
34
|
-
func (r *
|
|
34
|
+
func (r *MsgStream) MsgSend(msg Message) error {
|
|
35
35
|
select {
|
|
36
36
|
case <-r.ctx.Done():
|
|
37
37
|
return context.Canceled
|
|
@@ -48,7 +48,7 @@ func (r *RPCStream) MsgSend(msg Message) error {
|
|
|
48
48
|
|
|
49
49
|
// MsgRecv receives an incoming message from the remote.
|
|
50
50
|
// Parses the message into the object at msg.
|
|
51
|
-
func (r *
|
|
51
|
+
func (r *MsgStream) MsgRecv(msg Message) error {
|
|
52
52
|
select {
|
|
53
53
|
case <-r.Context().Done():
|
|
54
54
|
return context.Canceled
|
|
@@ -61,16 +61,16 @@ func (r *RPCStream) MsgRecv(msg Message) error {
|
|
|
61
61
|
}
|
|
62
62
|
|
|
63
63
|
// CloseSend signals to the remote that we will no longer send any messages.
|
|
64
|
-
func (r *
|
|
64
|
+
func (r *MsgStream) CloseSend() error {
|
|
65
65
|
outPkt := NewCallDataPacket(nil, false, true, nil)
|
|
66
66
|
return r.writer.WritePacket(outPkt)
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
// Close closes the stream.
|
|
70
|
-
func (r *
|
|
70
|
+
func (r *MsgStream) Close() error {
|
|
71
71
|
_ = r.writer.Close()
|
|
72
72
|
return nil
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
// _ is a type assertion
|
|
76
|
-
var _ Stream = ((*
|
|
76
|
+
var _ Stream = ((*MsgStream)(nil))
|
package/srpc/muxed-conn.go
CHANGED
|
@@ -14,7 +14,7 @@ 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
|
|
17
|
+
return func(ctx context.Context, msgHandler PacketHandler) (Writer, error) {
|
|
18
18
|
mstrm, err := conn.OpenStream(ctx)
|
|
19
19
|
if err != nil {
|
|
20
20
|
return nil, err
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Source } from 'it-stream-types'
|
|
2
|
+
import { pushable, Pushable } from 'it-pushable'
|
|
3
|
+
import { Observable, Subscription } from 'rxjs'
|
|
4
|
+
|
|
5
|
+
// ObservableSource wraps an Observable into a Source.
|
|
6
|
+
export class ObservableSource<T> {
|
|
7
|
+
// source is the source for observable objects.
|
|
8
|
+
public readonly source: Source<T>
|
|
9
|
+
// _source emits incoming data to the source.
|
|
10
|
+
private readonly _source: {
|
|
11
|
+
push: (val: T) => void
|
|
12
|
+
end: (err?: Error) => void
|
|
13
|
+
}
|
|
14
|
+
// subscription is the observable subscription
|
|
15
|
+
private readonly subscription: Subscription
|
|
16
|
+
|
|
17
|
+
constructor(observable: Observable<T>) {
|
|
18
|
+
const source: Pushable<T> = pushable({ objectMode: true })
|
|
19
|
+
this.source = source
|
|
20
|
+
this._source = source
|
|
21
|
+
|
|
22
|
+
this.subscription = observable.subscribe({
|
|
23
|
+
next: (value: T) => {
|
|
24
|
+
this._source.push(value)
|
|
25
|
+
},
|
|
26
|
+
error: (err) => {
|
|
27
|
+
this._source.end(err)
|
|
28
|
+
},
|
|
29
|
+
complete: () => {
|
|
30
|
+
this._source.end()
|
|
31
|
+
},
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// close closes the subscription.
|
|
36
|
+
public close(err?: Error) {
|
|
37
|
+
this._source.end(err)
|
|
38
|
+
this.subscription.unsubscribe()
|
|
39
|
+
}
|
|
40
|
+
}
|
package/srpc/packet.ts
CHANGED
|
File without changes
|
package/srpc/server-pipe.go
CHANGED
|
@@ -9,7 +9,7 @@ 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
|
|
12
|
+
return func(ctx context.Context, msgHandler PacketHandler) (Writer, error) {
|
|
13
13
|
srvPipe, clientPipe := net.Pipe()
|
|
14
14
|
go func() {
|
|
15
15
|
_ = server.HandleStream(ctx, srvPipe)
|
package/srpc/server-rpc.go
CHANGED
|
@@ -133,7 +133,7 @@ func (r *ServerRPC) HandleCallData(pkt *CallData) error {
|
|
|
133
133
|
func (r *ServerRPC) invokeRPC() {
|
|
134
134
|
// ctx := r.ctx
|
|
135
135
|
serviceID, methodID := r.service, r.method
|
|
136
|
-
strm :=
|
|
136
|
+
strm := NewMsgStream(r.ctx, r.writer, r.dataCh)
|
|
137
137
|
ok, err := r.mux.InvokeMethod(serviceID, methodID, strm)
|
|
138
138
|
if err == nil && !ok {
|
|
139
139
|
err = ErrUnimplemented
|
package/srpc/server-rpc.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Sink } from 'it-stream-types'
|
|
2
2
|
|
|
3
|
-
import type { CallData, CallStart } from './rpcproto.js'
|
|
3
|
+
import type { CallData, CallStart } from './rpcproto.pb.js'
|
|
4
4
|
import { CommonRPC } from './common-rpc.js'
|
|
5
5
|
import { InvokeFn } from './handler.js'
|
|
6
6
|
import { Mux } from './mux.js'
|
package/srpc/server.go
CHANGED
|
@@ -20,6 +20,11 @@ func NewServer(mux Mux) *Server {
|
|
|
20
20
|
}
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
+
// GetMux returns the mux.
|
|
24
|
+
func (s *Server) GetMux() Mux {
|
|
25
|
+
return s.mux
|
|
26
|
+
}
|
|
27
|
+
|
|
23
28
|
// HandleStream handles an incoming ReadWriteCloser stream.
|
|
24
29
|
func (s *Server) HandleStream(ctx context.Context, rwc io.ReadWriteCloser) error {
|
|
25
30
|
subCtx, subCtxCancel := context.WithCancel(ctx)
|
package/srpc/server.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { pipe } from 'it-pipe'
|
|
|
4
4
|
|
|
5
5
|
import { Mux } from './mux.js'
|
|
6
6
|
import { ServerRPC } from './server-rpc.js'
|
|
7
|
-
import { Packet } from './rpcproto.js'
|
|
7
|
+
import { Packet } from './rpcproto.pb.js'
|
|
8
8
|
import {
|
|
9
9
|
parseLengthPrefixTransform,
|
|
10
10
|
prependLengthPrefixTransform,
|
|
@@ -29,15 +29,14 @@ export class Server implements StreamHandler {
|
|
|
29
29
|
}
|
|
30
30
|
|
|
31
31
|
// handleStream handles an incoming Uint8Array message duplex.
|
|
32
|
-
|
|
33
|
-
public handleStream(stream: Stream): Promise<void> {
|
|
32
|
+
public handleStream(stream: Stream): ServerRPC {
|
|
34
33
|
return this.handleDuplex(stream)
|
|
35
34
|
}
|
|
36
35
|
|
|
37
36
|
// handleDuplex handles an incoming message duplex.
|
|
38
|
-
public
|
|
37
|
+
public handleDuplex(stream: Duplex<Uint8Array>): ServerRPC {
|
|
39
38
|
const rpc = this.startRpc()
|
|
40
|
-
|
|
39
|
+
pipe(
|
|
41
40
|
stream,
|
|
42
41
|
parseLengthPrefixTransform(),
|
|
43
42
|
decodePacketSource,
|
|
@@ -46,11 +45,13 @@ export class Server implements StreamHandler {
|
|
|
46
45
|
prependLengthPrefixTransform(),
|
|
47
46
|
stream
|
|
48
47
|
)
|
|
48
|
+
return rpc
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
// handlePacketStream handles an incoming Packet duplex.
|
|
52
|
-
public
|
|
52
|
+
public handlePacketStream(stream: Duplex<Packet>): ServerRPC {
|
|
53
53
|
const rpc = this.startRpc()
|
|
54
|
-
|
|
54
|
+
pipe(stream, rpc, stream)
|
|
55
|
+
return rpc
|
|
55
56
|
}
|
|
56
57
|
}
|
package/srpc/stream.ts
CHANGED
package/srpc/websocket.go
CHANGED
|
@@ -43,7 +43,7 @@ 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
|
|
46
|
+
func (w *WebSocketConn) OpenStream(ctx context.Context, msgHandler PacketHandler) (Writer, error) {
|
|
47
47
|
muxedStream, err := w.mconn.OpenStream(ctx)
|
|
48
48
|
if err != nil {
|
|
49
49
|
return nil, err
|
package/e2e/debug.test
DELETED
|
Binary file
|