starpc 0.52.0 → 0.52.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/README.md +37 -8
- package/dist/echo/client-test.d.ts +1 -1
- package/dist/echo/client-test.js +110 -2
- package/dist/integration/cross-language/tcp-packet-stream.d.ts +3 -0
- package/dist/integration/cross-language/tcp-packet-stream.js +112 -0
- package/dist/integration/cross-language/tcp-packet-stream.test.d.ts +1 -0
- package/dist/integration/cross-language/tcp-packet-stream.test.js +121 -0
- package/dist/integration/cross-language/ts-client.js +50 -37
- package/dist/integration/cross-language/ts-server.js +1 -36
- package/dist/rpcstream/rpcstream.d.ts +5 -1
- package/dist/rpcstream/rpcstream.js +75 -28
- package/dist/rpcstream/rpcstream.test.d.ts +1 -0
- package/dist/rpcstream/rpcstream.test.js +92 -0
- package/dist/srpc/client-rpc.d.ts +1 -0
- package/dist/srpc/client-rpc.js +9 -0
- package/dist/srpc/client-rpc.test.d.ts +1 -0
- package/dist/srpc/client-rpc.test.js +50 -0
- package/dist/srpc/client.js +18 -4
- package/dist/srpc/common-rpc.d.ts +5 -0
- package/dist/srpc/common-rpc.js +37 -8
- package/dist/srpc/common-rpc.test.js +2 -0
- package/dist/srpc/errors.d.ts +2 -0
- package/dist/srpc/errors.js +14 -0
- package/dist/srpc/index.d.ts +1 -1
- package/dist/srpc/index.js +1 -1
- package/dist/srpc/packet-codec.test.d.ts +1 -0
- package/dist/srpc/packet-codec.test.js +75 -0
- package/dist/srpc/packet.d.ts +1 -1
- package/dist/srpc/packet.js +11 -1
- package/dist/srpc/server.js +19 -6
- package/dist/srpc/server.test.js +43 -0
- package/dist/srpc/stream.d.ts +4 -1
- package/dist/srpc/stream.js +62 -3
- package/dist/srpc/stream.test.js +110 -1
- package/dist/srpc/termination.d.ts +27 -0
- package/dist/srpc/termination.js +56 -0
- package/dist/srpc/termination.test.d.ts +1 -0
- package/dist/srpc/termination.test.js +24 -0
- package/dist/testdata/packet-codec-vectors.json +64 -0
- package/echo/Cargo.toml +8 -0
- package/echo/client-test.ts +124 -2
- package/echo/echo_e2e_test.cpp +542 -985
- package/echo/echo_pb2.py +40 -0
- package/echo/echo_pb2.pyi +13 -0
- package/echo/echo_srpc.pb.cpp +3 -5
- package/echo/echo_srpc.pb.go +6 -9
- package/echo/echo_srpc.pb.hpp +23 -3
- package/echo/echo_srpc.pb.rs +61 -26
- package/echo/echo_srpc.py +309 -0
- package/echo/echo_srpc.pyi +85 -0
- package/echo/half_close_test.rs +178 -0
- package/echo/integration_server.rs +14 -21
- package/echo/main.rs +28 -48
- package/go.mod +3 -3
- package/go.sum +6 -8
- package/integration/cross-language/cpp-client.cpp +446 -402
- package/integration/cross-language/cpp-pipe-server.cpp +139 -0
- package/integration/cross-language/cpp-pipe_test.go +114 -0
- package/integration/cross-language/cpp-server.cpp +222 -186
- package/integration/cross-language/go-client/main.go +79 -3
- package/integration/cross-language/python-client.py +146 -0
- package/integration/cross-language/python-server.py +140 -0
- package/integration/cross-language/run.bash +190 -65
- package/integration/cross-language/tcp-packet-stream.test.ts +154 -0
- package/integration/cross-language/tcp-packet-stream.ts +121 -0
- package/integration/cross-language/ts-client.ts +62 -40
- package/integration/cross-language/ts-server.ts +1 -45
- package/mock/mock_pb2.py +38 -0
- package/mock/mock_pb2.pyi +11 -0
- package/mock/mock_srpc.pb.cpp +1 -1
- package/mock/mock_srpc.pb.hpp +1 -1
- package/mock/mock_srpc.pb.rs +10 -9
- package/mock/mock_srpc.py +71 -0
- package/mock/mock_srpc.pyi +27 -0
- package/package.json +19 -5
- package/srpc/CMakeLists.txt +3 -58
- package/srpc/README-cpp.md +69 -0
- package/srpc/__init__.py +0 -0
- package/srpc/accept.go +10 -4
- package/srpc/accept_test.go +25 -0
- package/srpc/build.rs +129 -34
- package/srpc/client-half-close_test.go +152 -0
- package/srpc/client-invoker.go +32 -42
- package/srpc/client-invoker_test.go +53 -0
- package/srpc/client-rpc.cpp +65 -80
- package/srpc/client-rpc.go +32 -33
- package/srpc/client-rpc.hpp +52 -38
- package/srpc/client-rpc.test.ts +67 -0
- package/srpc/client-rpc.ts +10 -0
- package/srpc/client-rpc_test.go +76 -0
- package/srpc/client.cpp +99 -87
- package/srpc/client.hpp +46 -35
- package/srpc/client.rs +78 -38
- package/srpc/client.ts +20 -4
- package/srpc/client_half_close_test.cc +331 -0
- package/srpc/client_invoker.rs +75 -0
- package/srpc/codec.rs +6 -0
- package/srpc/common-rpc.cpp +186 -146
- package/srpc/common-rpc.go +5 -4
- package/srpc/common-rpc.hpp +119 -89
- package/srpc/common-rpc.test.ts +2 -0
- package/srpc/common-rpc.ts +43 -8
- package/srpc/common-rpc_test.go +23 -0
- package/srpc/error.rs +19 -2
- package/srpc/errors.hpp +80 -50
- package/srpc/errors.ts +16 -0
- package/srpc/handler.hpp +9 -7
- package/srpc/handler.rs +7 -45
- package/srpc/index.ts +2 -0
- package/srpc/invoker.hpp +82 -55
- package/srpc/invoker.rs +14 -52
- package/srpc/lib.rs +2 -0
- package/srpc/message.hpp +17 -12
- package/srpc/msg-stream.go +1 -0
- package/srpc/msg-stream.hpp +108 -61
- package/srpc/mux.cpp +84 -84
- package/srpc/mux.hpp +48 -38
- package/srpc/mux.rs +4 -4
- package/srpc/net.go +12 -3
- package/srpc/packet-codec-vectors_test.go +195 -0
- package/srpc/packet-codec.test.ts +139 -0
- package/srpc/packet-rw.go +9 -2
- package/srpc/packet.cpp +60 -58
- package/srpc/packet.hpp +35 -20
- package/srpc/packet.rs +8 -27
- package/srpc/packet.ts +15 -2
- package/srpc/py.typed +0 -0
- package/srpc/rpc.rs +332 -115
- package/srpc/rpc_lifecycle_test.cc +333 -0
- package/srpc/rpcproto_pb2.py +40 -0
- package/srpc/rpcproto_pb2.pyi +40 -0
- package/srpc/server-cancel_test.go +39 -0
- package/srpc/server-rpc.cpp +83 -73
- package/srpc/server-rpc.go +21 -19
- package/srpc/server-rpc.hpp +56 -37
- package/srpc/server.rs +76 -131
- package/srpc/server.test.ts +50 -0
- package/srpc/server.ts +22 -6
- package/srpc/starpc.hpp +4 -2
- package/srpc/stream.go +4 -0
- package/srpc/stream.hpp +95 -34
- package/srpc/stream.rs +7 -2
- package/srpc/stream.test.ts +132 -1
- package/srpc/stream.ts +65 -9
- package/srpc/termination.test.ts +30 -0
- package/srpc/termination.ts +70 -0
- package/srpc/testing.rs +11 -66
- package/srpc/transport.rs +21 -71
- package/srpc/websocket.rs +106 -57
- package/srpc/writer.hpp +46 -32
- package/srpc/yamux.rs +82 -26
- package/srpc/tests/integration_test.rs +0 -495
package/srpc/common-rpc.ts
CHANGED
|
@@ -22,8 +22,10 @@ export class CommonRPC {
|
|
|
22
22
|
objectMode: true,
|
|
23
23
|
})
|
|
24
24
|
|
|
25
|
-
// _rpcDataSource
|
|
26
|
-
|
|
25
|
+
// _rpcDataSource queues incoming rpc messages and the terminal error.
|
|
26
|
+
// The error is queued as an item because ending a pushable with an error
|
|
27
|
+
// discards the messages it buffered.
|
|
28
|
+
private readonly _rpcDataSource = pushable<Uint8Array | Error>({
|
|
27
29
|
objectMode: true,
|
|
28
30
|
})
|
|
29
31
|
|
|
@@ -36,6 +38,8 @@ export class CommonRPC {
|
|
|
36
38
|
private closed?: true | Error
|
|
37
39
|
// remoteError records a remote error or transport failure.
|
|
38
40
|
private remoteError?: Error
|
|
41
|
+
// remoteCompleted records that the remote sent a completion or an error.
|
|
42
|
+
private remoteCompleted = false
|
|
39
43
|
// invocationController cancels the server invocation when the RPC closes.
|
|
40
44
|
private readonly invocationController = new AbortController()
|
|
41
45
|
|
|
@@ -45,7 +49,7 @@ export class CommonRPC {
|
|
|
45
49
|
constructor() {
|
|
46
50
|
this.sink = this._createSink()
|
|
47
51
|
this.source = this._source
|
|
48
|
-
this.rpcDataSource = this.
|
|
52
|
+
this.rpcDataSource = this.readRpcData()
|
|
49
53
|
}
|
|
50
54
|
|
|
51
55
|
// isClosed returns one of: true (closed w/o error), Error (closed w/ error), or false (not closed).
|
|
@@ -53,6 +57,11 @@ export class CommonRPC {
|
|
|
53
57
|
return this.closed ?? false
|
|
54
58
|
}
|
|
55
59
|
|
|
60
|
+
// isRemoteCompleted reports whether the remote sent a completion or an error.
|
|
61
|
+
protected get isRemoteCompleted(): boolean {
|
|
62
|
+
return this.remoteCompleted
|
|
63
|
+
}
|
|
64
|
+
|
|
56
65
|
// invocationSignal is canceled when the RPC closes.
|
|
57
66
|
protected get invocationSignal(): AbortSignal {
|
|
58
67
|
return this.invocationController.signal
|
|
@@ -214,12 +223,12 @@ export class CommonRPC {
|
|
|
214
223
|
this.remoteError ??= remoteError
|
|
215
224
|
this.invocationController.abort()
|
|
216
225
|
}
|
|
217
|
-
if (packet.complete
|
|
218
|
-
this.
|
|
219
|
-
|
|
220
|
-
this._rpcDataSource.end(remoteError)
|
|
226
|
+
if (packet.complete || remoteError) {
|
|
227
|
+
this.remoteCompleted = true
|
|
228
|
+
this.endRpcData(remoteError)
|
|
221
229
|
}
|
|
222
230
|
}
|
|
231
|
+
|
|
223
232
|
// handleCallCancel aborts the invocation and closes the call.
|
|
224
233
|
public async handleCallCancel() {
|
|
225
234
|
await this.close(new Error(ERR_RPC_ABORT))
|
|
@@ -244,7 +253,32 @@ export class CommonRPC {
|
|
|
244
253
|
}
|
|
245
254
|
this.writeDrainAbort.abort()
|
|
246
255
|
this._source.end()
|
|
247
|
-
this.
|
|
256
|
+
this.endRpcData(err)
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// endRpcData ends the rpc message source after its buffered messages,
|
|
260
|
+
// optionally with an error.
|
|
261
|
+
private endRpcData(err?: Error) {
|
|
262
|
+
if (err) {
|
|
263
|
+
this._rpcDataSource.push(err)
|
|
264
|
+
}
|
|
265
|
+
this._rpcDataSource.end()
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// readRpcData yields the buffered rpc messages, then throws the terminal
|
|
269
|
+
// error, if any.
|
|
270
|
+
private async *readRpcData(): AsyncGenerator<Uint8Array> {
|
|
271
|
+
for await (const item of this._rpcDataSource) {
|
|
272
|
+
if (item instanceof Error) {
|
|
273
|
+
throw item
|
|
274
|
+
}
|
|
275
|
+
yield item
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// handleSourceEnd handles the end of the incoming packet source.
|
|
280
|
+
protected async handleSourceEnd() {
|
|
281
|
+
// no-op
|
|
248
282
|
}
|
|
249
283
|
|
|
250
284
|
private _createSink(): Sink<Source<Packet>> {
|
|
@@ -259,6 +293,7 @@ export class CommonRPC {
|
|
|
259
293
|
await this.handlePacket(msg)
|
|
260
294
|
}
|
|
261
295
|
}
|
|
296
|
+
await this.handleSourceEnd()
|
|
262
297
|
} catch (err) {
|
|
263
298
|
this.close(err as Error)
|
|
264
299
|
}
|
package/srpc/common-rpc_test.go
CHANGED
|
@@ -504,3 +504,26 @@ func TestClientRPCCloseReportsALocalCancellation(t *testing.T) {
|
|
|
504
504
|
t.Fatalf("a close this side performed was blamed on the remote: %v", err)
|
|
505
505
|
}
|
|
506
506
|
}
|
|
507
|
+
|
|
508
|
+
func TestClientRPCCloseAfterRemoteCompletionKeepsTheResult(t *testing.T) {
|
|
509
|
+
writer := &closeCountingPacketWriter{}
|
|
510
|
+
rpc := NewClientRPC(context.Background(), "service", "method")
|
|
511
|
+
if err := rpc.Start(writer, false, nil); err != nil {
|
|
512
|
+
t.Fatalf("start: %v", err)
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// The remote answers and completes before this side closes the call, as
|
|
516
|
+
// when the RPC's own completion cancels a Start still writing CallStart.
|
|
517
|
+
if err := rpc.HandleCallData(&CallData{Data: []byte("ok"), Complete: true}); err != nil {
|
|
518
|
+
t.Fatalf("handle completion: %v", err)
|
|
519
|
+
}
|
|
520
|
+
rpc.Close()
|
|
521
|
+
|
|
522
|
+
data, err := rpc.ReadOne()
|
|
523
|
+
if err != nil || string(data) != "ok" {
|
|
524
|
+
t.Fatalf("read after a close of a completed call = %q, %v", data, err)
|
|
525
|
+
}
|
|
526
|
+
if _, err := rpc.ReadOne(); !errors.Is(err, io.EOF) {
|
|
527
|
+
t.Fatalf("read after the response reported %v, want the end of the stream", err)
|
|
528
|
+
}
|
|
529
|
+
}
|
package/srpc/error.rs
CHANGED
|
@@ -52,6 +52,13 @@ pub enum Error {
|
|
|
52
52
|
#[error("stream closed")]
|
|
53
53
|
StreamClosed,
|
|
54
54
|
|
|
55
|
+
/// The stream closed without the remote sending a completion or an error.
|
|
56
|
+
///
|
|
57
|
+
/// The call has no verdict: the handler may have finished with its answer
|
|
58
|
+
/// lost in the transport, or it may never have run.
|
|
59
|
+
#[error("stream closed before the remote reported completion")]
|
|
60
|
+
ClosedBeforeCompletion,
|
|
61
|
+
|
|
55
62
|
/// The RPC was aborted.
|
|
56
63
|
#[error("rpc aborted")]
|
|
57
64
|
Aborted,
|
|
@@ -153,9 +160,19 @@ mod tests {
|
|
|
153
160
|
#[test]
|
|
154
161
|
fn test_error_display() {
|
|
155
162
|
assert_eq!(Error::Unimplemented.to_string(), "method not implemented");
|
|
156
|
-
assert_eq!(
|
|
163
|
+
assert_eq!(
|
|
164
|
+
Error::Completed.to_string(),
|
|
165
|
+
"unexpected packet after rpc was completed"
|
|
166
|
+
);
|
|
157
167
|
assert_eq!(Error::EmptyMethodId.to_string(), "method id empty");
|
|
158
|
-
assert_eq!(
|
|
168
|
+
assert_eq!(
|
|
169
|
+
Error::ClosedBeforeCompletion.to_string(),
|
|
170
|
+
"stream closed before the remote reported completion"
|
|
171
|
+
);
|
|
172
|
+
assert_eq!(
|
|
173
|
+
Error::Remote("test error".into()).to_string(),
|
|
174
|
+
"remote error: test error"
|
|
175
|
+
);
|
|
159
176
|
}
|
|
160
177
|
|
|
161
178
|
#[test]
|
package/srpc/errors.hpp
CHANGED
|
@@ -5,67 +5,97 @@
|
|
|
5
5
|
|
|
6
6
|
namespace starpc {
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
/*
|
|
9
|
+
* Error is the house error value. Every action returns one; OK is success.
|
|
10
|
+
* The codes mirror the Go starpc errors in errors.go; ErrorString holds each
|
|
11
|
+
* diagnostic text.
|
|
12
|
+
*/
|
|
9
13
|
enum class Error {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
OK = 0,
|
|
15
|
+
Unimplemented, /* the RPC method was not implemented */
|
|
16
|
+
Completed, /* unexpected packet after the RPC completed */
|
|
17
|
+
UnrecognizedPacket,
|
|
18
|
+
EmptyPacket,
|
|
19
|
+
InvalidMessage, /* the message failed to parse */
|
|
20
|
+
EmptyMethodID,
|
|
21
|
+
EmptyServiceID,
|
|
22
|
+
NoAvailableClients,
|
|
23
|
+
NilWriter,
|
|
24
|
+
Canceled, /* context.Canceled equivalent */
|
|
25
|
+
EOF_, /* io.EOF equivalent; named EOF_ to avoid the macro */
|
|
26
|
+
ClosedBeforeCompletion, /* the transport ended without a verdict */
|
|
27
|
+
ResourceExhausted, /* the receive queue or thread capacity was hit */
|
|
28
|
+
RemoteError, /* RemoteErrorMessage retains the peer's diagnostic */
|
|
22
29
|
};
|
|
23
30
|
|
|
24
|
-
|
|
25
|
-
inline const char *ErrorString(Error err)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
31
|
+
/* ErrorString returns the stable diagnostic text for err. */
|
|
32
|
+
inline const char *ErrorString(Error err)
|
|
33
|
+
{
|
|
34
|
+
switch (err) {
|
|
35
|
+
case Error::OK:
|
|
36
|
+
return "ok";
|
|
37
|
+
case Error::Unimplemented:
|
|
38
|
+
return "unimplemented";
|
|
39
|
+
case Error::Completed:
|
|
40
|
+
return "unexpected packet after rpc was completed";
|
|
41
|
+
case Error::UnrecognizedPacket:
|
|
42
|
+
return "unrecognized packet type";
|
|
43
|
+
case Error::EmptyPacket:
|
|
44
|
+
return "invalid empty packet";
|
|
45
|
+
case Error::InvalidMessage:
|
|
46
|
+
return "invalid message";
|
|
47
|
+
case Error::EmptyMethodID:
|
|
48
|
+
return "method id empty";
|
|
49
|
+
case Error::EmptyServiceID:
|
|
50
|
+
return "service id empty";
|
|
51
|
+
case Error::NoAvailableClients:
|
|
52
|
+
return "no available rpc clients";
|
|
53
|
+
case Error::NilWriter:
|
|
54
|
+
return "writer cannot be nil";
|
|
55
|
+
case Error::Canceled:
|
|
56
|
+
return "canceled";
|
|
57
|
+
case Error::EOF_:
|
|
58
|
+
return "EOF";
|
|
59
|
+
case Error::ClosedBeforeCompletion:
|
|
60
|
+
return "stream closed before the remote reported completion";
|
|
61
|
+
case Error::ResourceExhausted:
|
|
62
|
+
return "RPC resource limit exceeded";
|
|
63
|
+
case Error::RemoteError:
|
|
64
|
+
return "remote RPC error";
|
|
65
|
+
default:
|
|
66
|
+
return "unknown error";
|
|
67
|
+
}
|
|
54
68
|
}
|
|
55
69
|
|
|
56
|
-
|
|
70
|
+
/*
|
|
71
|
+
* StarpcError carries an Error across a throwing boundary. Exceptions are
|
|
72
|
+
* disabled in the house build; this exists only for embedders that enable
|
|
73
|
+
* them.
|
|
74
|
+
*/
|
|
57
75
|
class StarpcError : public std::runtime_error {
|
|
58
76
|
public:
|
|
59
|
-
|
|
60
|
-
|
|
77
|
+
explicit StarpcError(Error code)
|
|
78
|
+
: std::runtime_error(ErrorString(code)),
|
|
79
|
+
code_(code)
|
|
80
|
+
{
|
|
81
|
+
}
|
|
61
82
|
|
|
62
|
-
|
|
63
|
-
|
|
83
|
+
/* StarpcError reports message as the diagnostic for code. */
|
|
84
|
+
StarpcError(Error code, const std::string &message)
|
|
85
|
+
: std::runtime_error(message),
|
|
86
|
+
code_(code)
|
|
87
|
+
{
|
|
88
|
+
}
|
|
64
89
|
|
|
65
|
-
|
|
90
|
+
/* code returns the Error this exception carries. */
|
|
91
|
+
Error code() const noexcept
|
|
92
|
+
{
|
|
93
|
+
return code_;
|
|
94
|
+
}
|
|
66
95
|
|
|
67
96
|
private:
|
|
68
|
-
|
|
97
|
+
/* code_ keeps the Error; the getter takes the API name code(). */
|
|
98
|
+
Error code_;
|
|
69
99
|
};
|
|
70
100
|
|
|
71
101
|
} // namespace starpc
|
package/srpc/errors.ts
CHANGED
|
@@ -10,6 +10,22 @@ export function isAbortError(err: unknown): boolean {
|
|
|
10
10
|
return message === ERR_RPC_ABORT
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
// ERR_CLOSED_BEFORE_COMPLETION is returned if the stream closed before the
|
|
14
|
+
// remote sent a completion or an error. The call has no verdict: the handler
|
|
15
|
+
// may have finished with its answer lost in the transport, or it may never
|
|
16
|
+
// have run.
|
|
17
|
+
export const ERR_CLOSED_BEFORE_COMPLETION = 'ERR_CLOSED_BEFORE_COMPLETION'
|
|
18
|
+
|
|
19
|
+
// isClosedBeforeCompletionError checks if the error object is
|
|
20
|
+
// ERR_CLOSED_BEFORE_COMPLETION.
|
|
21
|
+
export function isClosedBeforeCompletionError(err: unknown): boolean {
|
|
22
|
+
if (typeof err !== 'object') {
|
|
23
|
+
return false
|
|
24
|
+
}
|
|
25
|
+
const message = (err as Error).message
|
|
26
|
+
return message === ERR_CLOSED_BEFORE_COMPLETION
|
|
27
|
+
}
|
|
28
|
+
|
|
13
29
|
// ERR_STREAM_IDLE is returned if the stream idle timeout was exceeded.
|
|
14
30
|
export const ERR_STREAM_IDLE = 'ERR_STREAM_IDLE'
|
|
15
31
|
|
package/srpc/handler.hpp
CHANGED
|
@@ -7,17 +7,19 @@
|
|
|
7
7
|
|
|
8
8
|
namespace starpc {
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
/*
|
|
11
|
+
* Handler is one registered SRPC service: an Invoker that publishes its
|
|
12
|
+
* service ID and method list. Matches the Go Handler interface in handler.go.
|
|
13
|
+
*/
|
|
12
14
|
class Handler : public Invoker {
|
|
13
15
|
public:
|
|
14
|
-
|
|
16
|
+
~Handler() override = default;
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
/* GetServiceID returns the service this handler registers. */
|
|
19
|
+
virtual const std::string &GetServiceID() const = 0;
|
|
18
20
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
/* GetMethodIDs returns every method the service answers. */
|
|
22
|
+
virtual std::vector<std::string> GetMethodIDs() const = 0;
|
|
21
23
|
};
|
|
22
24
|
|
|
23
25
|
} // namespace starpc
|
package/srpc/handler.rs
CHANGED
|
@@ -8,55 +8,17 @@ use std::sync::Arc;
|
|
|
8
8
|
|
|
9
9
|
use crate::invoker::Invoker;
|
|
10
10
|
|
|
11
|
-
///
|
|
11
|
+
/// Describes the service an Invoker implements, so the Mux can register it.
|
|
12
12
|
///
|
|
13
|
-
/// A Handler extends Invoker with metadata methods
|
|
14
|
-
///
|
|
15
|
-
/// typically implements.
|
|
16
|
-
///
|
|
17
|
-
/// # Example
|
|
18
|
-
///
|
|
19
|
-
/// ```rust,ignore
|
|
20
|
-
/// struct MyServiceHandler {
|
|
21
|
-
/// // Handler state
|
|
22
|
-
/// }
|
|
23
|
-
///
|
|
24
|
-
/// #[async_trait]
|
|
25
|
-
/// impl Invoker for MyServiceHandler {
|
|
26
|
-
/// async fn invoke_method(
|
|
27
|
-
/// &self,
|
|
28
|
-
/// service_id: &str,
|
|
29
|
-
/// method_id: &str,
|
|
30
|
-
/// stream: Box<dyn Stream>,
|
|
31
|
-
/// ) -> (bool, Result<()>) {
|
|
32
|
-
/// match method_id {
|
|
33
|
-
/// "Method1" => (true, self.method1(stream).await),
|
|
34
|
-
/// "Method2" => (true, self.method2(stream).await),
|
|
35
|
-
/// _ => (false, Err(Error::Unimplemented)),
|
|
36
|
-
/// }
|
|
37
|
-
/// }
|
|
38
|
-
/// }
|
|
39
|
-
///
|
|
40
|
-
/// impl Handler for MyServiceHandler {
|
|
41
|
-
/// fn service_id(&self) -> &'static str {
|
|
42
|
-
/// "my.package.MyService"
|
|
43
|
-
/// }
|
|
44
|
-
///
|
|
45
|
-
/// fn method_ids(&self) -> &'static [&'static str] {
|
|
46
|
-
/// &["Method1", "Method2"]
|
|
47
|
-
/// }
|
|
48
|
-
/// }
|
|
49
|
-
/// ```
|
|
13
|
+
/// A Handler extends Invoker with metadata methods. This is the trait that
|
|
14
|
+
/// generated service code implements.
|
|
50
15
|
pub trait Handler: Invoker {
|
|
51
|
-
/// Returns the service
|
|
52
|
-
///
|
|
53
|
-
/// The service ID is typically the fully-qualified protobuf service name,
|
|
54
|
-
/// e.g., "echo.Echoer" or "my.package.MyService".
|
|
16
|
+
/// Returns the fully-qualified protobuf service name this handler
|
|
17
|
+
/// implements, such as `echo.Echoer`.
|
|
55
18
|
fn service_id(&self) -> &'static str;
|
|
56
19
|
|
|
57
|
-
/// Returns the
|
|
58
|
-
///
|
|
59
|
-
/// These are the method names as defined in the protobuf service definition.
|
|
20
|
+
/// Returns the method names, as defined in the protobuf service
|
|
21
|
+
/// definition, that this handler implements.
|
|
60
22
|
fn method_ids(&self) -> &'static [&'static str];
|
|
61
23
|
}
|
|
62
24
|
|
package/srpc/index.ts
CHANGED
package/srpc/invoker.hpp
CHANGED
|
@@ -9,83 +9,110 @@
|
|
|
9
9
|
|
|
10
10
|
namespace starpc {
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
/*
|
|
13
|
+
* Invoker dispatches one SRPC method call. Matches the Go Invoker interface
|
|
14
|
+
* in invoker.go.
|
|
15
|
+
*/
|
|
14
16
|
class Invoker {
|
|
15
17
|
public:
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
virtual ~Invoker() = default;
|
|
19
|
+
|
|
20
|
+
/*
|
|
21
|
+
* InvokeMethod runs the handler for service_id and method_id and
|
|
22
|
+
* reports whether it found one. An empty service_id matches any
|
|
23
|
+
* service. found is false only when no handler applies; strm carries
|
|
24
|
+
* the call.
|
|
25
|
+
*/
|
|
26
|
+
virtual std::pair<bool, Error>
|
|
27
|
+
InvokeMethod(const std::string &service_id,
|
|
28
|
+
const std::string &method_id, Stream *strm) = 0;
|
|
24
29
|
};
|
|
25
30
|
|
|
26
|
-
|
|
27
|
-
|
|
31
|
+
/*
|
|
32
|
+
* QueryableInvoker answers whether it implements a service or method without
|
|
33
|
+
* running one. Matches the Go QueryableInvoker interface in invoker.go.
|
|
34
|
+
*/
|
|
28
35
|
class QueryableInvoker {
|
|
29
36
|
public:
|
|
30
|
-
|
|
37
|
+
virtual ~QueryableInvoker() = default;
|
|
31
38
|
|
|
32
|
-
|
|
33
|
-
|
|
39
|
+
/* HasService reports whether service_id is registered. */
|
|
40
|
+
virtual bool HasService(const std::string &service_id) const = 0;
|
|
34
41
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
42
|
+
/* HasServiceMethod reports whether the service serves method_id. */
|
|
43
|
+
virtual bool HasServiceMethod(const std::string &service_id,
|
|
44
|
+
const std::string &method_id) const = 0;
|
|
38
45
|
};
|
|
39
46
|
|
|
40
|
-
|
|
41
|
-
|
|
47
|
+
/*
|
|
48
|
+
* InvokerSlice tries each invoker in order and stops at the first that
|
|
49
|
+
* handles the call or fails. The invokers are borrowed and must outlive the
|
|
50
|
+
* slice. Matches Go InvokerSlice in invoker.go.
|
|
51
|
+
*/
|
|
42
52
|
class InvokerSlice : public Invoker {
|
|
43
53
|
public:
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
54
|
+
InvokerSlice() = default;
|
|
55
|
+
explicit InvokerSlice(std::vector<Invoker *> invokers)
|
|
56
|
+
: invokers(std::move(invokers))
|
|
57
|
+
{
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/* Add appends an invoker to try after the ones already present. */
|
|
61
|
+
void Add(Invoker *invoker)
|
|
62
|
+
{
|
|
63
|
+
invokers.push_back(invoker);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
std::pair<bool, Error> InvokeMethod(const std::string &service_id,
|
|
67
|
+
const std::string &method_id,
|
|
68
|
+
Stream *strm) override
|
|
69
|
+
{
|
|
70
|
+
for (auto *invoker : invokers) {
|
|
71
|
+
if (invoker == nullptr)
|
|
72
|
+
continue;
|
|
73
|
+
auto [found, err] = invoker->InvokeMethod(
|
|
74
|
+
service_id, method_id, strm);
|
|
75
|
+
if (found || err != Error::OK)
|
|
76
|
+
return {true, err};
|
|
77
|
+
}
|
|
78
|
+
return {false, Error::OK};
|
|
79
|
+
}
|
|
63
80
|
|
|
64
81
|
private:
|
|
65
|
-
|
|
82
|
+
/* invokers are borrowed; the caller owns them. */
|
|
83
|
+
std::vector<Invoker *> invokers;
|
|
66
84
|
};
|
|
67
85
|
|
|
68
|
-
|
|
69
|
-
|
|
86
|
+
/*
|
|
87
|
+
* InvokerFunc is the callable form of Invoker::InvokeMethod. Matches Go
|
|
88
|
+
* InvokerFunc in invoker.go.
|
|
89
|
+
*/
|
|
70
90
|
using InvokerFunc = std::function<std::pair<bool, Error>(
|
|
71
|
-
|
|
91
|
+
const std::string &service_id, const std::string &method_id,
|
|
92
|
+
Stream *strm)>;
|
|
72
93
|
|
|
73
|
-
|
|
94
|
+
/*
|
|
95
|
+
* InvokerFuncWrapper adapts an InvokerFunc to the Invoker interface. An empty
|
|
96
|
+
* function reports that no handler applies.
|
|
97
|
+
*/
|
|
74
98
|
class InvokerFuncWrapper : public Invoker {
|
|
75
99
|
public:
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
100
|
+
explicit InvokerFuncWrapper(InvokerFunc fn)
|
|
101
|
+
: fn(std::move(fn))
|
|
102
|
+
{
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
std::pair<bool, Error> InvokeMethod(const std::string &service_id,
|
|
106
|
+
const std::string &method_id,
|
|
107
|
+
Stream *strm) override
|
|
108
|
+
{
|
|
109
|
+
if (!fn)
|
|
110
|
+
return {false, Error::OK};
|
|
111
|
+
return fn(service_id, method_id, strm);
|
|
112
|
+
}
|
|
86
113
|
|
|
87
114
|
private:
|
|
88
|
-
|
|
115
|
+
InvokerFunc fn;
|
|
89
116
|
};
|
|
90
117
|
|
|
91
118
|
} // namespace starpc
|