starpc 0.52.1 → 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.
Files changed (91) hide show
  1. package/README.md +37 -8
  2. package/dist/srpc/client-rpc.d.ts +1 -0
  3. package/dist/srpc/client-rpc.js +9 -0
  4. package/dist/srpc/client-rpc.test.d.ts +1 -0
  5. package/dist/srpc/client-rpc.test.js +50 -0
  6. package/dist/srpc/common-rpc.d.ts +5 -0
  7. package/dist/srpc/common-rpc.js +37 -8
  8. package/dist/srpc/errors.d.ts +2 -0
  9. package/dist/srpc/errors.js +14 -0
  10. package/dist/srpc/index.d.ts +1 -1
  11. package/dist/srpc/index.js +1 -1
  12. package/echo/Cargo.toml +8 -0
  13. package/echo/echo_e2e_test.cpp +542 -985
  14. package/echo/echo_srpc.pb.cpp +3 -5
  15. package/echo/echo_srpc.pb.go +6 -9
  16. package/echo/echo_srpc.pb.hpp +23 -3
  17. package/echo/echo_srpc.pb.rs +61 -26
  18. package/echo/echo_srpc.py +4 -1
  19. package/echo/half_close_test.rs +178 -0
  20. package/echo/integration_server.rs +14 -21
  21. package/echo/main.rs +28 -48
  22. package/go.mod +1 -1
  23. package/go.sum +2 -18
  24. package/integration/cross-language/cpp-client.cpp +446 -402
  25. package/integration/cross-language/cpp-pipe-server.cpp +139 -0
  26. package/integration/cross-language/cpp-pipe_test.go +114 -0
  27. package/integration/cross-language/cpp-server.cpp +222 -186
  28. package/mock/mock_srpc.pb.cpp +1 -1
  29. package/mock/mock_srpc.pb.hpp +1 -1
  30. package/mock/mock_srpc.pb.rs +10 -9
  31. package/package.json +1 -1
  32. package/srpc/CMakeLists.txt +3 -58
  33. package/srpc/README-cpp.md +69 -0
  34. package/srpc/accept.go +10 -4
  35. package/srpc/accept_test.go +25 -0
  36. package/srpc/build.rs +129 -34
  37. package/srpc/client-half-close_test.go +152 -0
  38. package/srpc/client-invoker.go +32 -42
  39. package/srpc/client-invoker_test.go +53 -0
  40. package/srpc/client-rpc.cpp +65 -80
  41. package/srpc/client-rpc.go +32 -33
  42. package/srpc/client-rpc.hpp +52 -38
  43. package/srpc/client-rpc.test.ts +67 -0
  44. package/srpc/client-rpc.ts +10 -0
  45. package/srpc/client-rpc_test.go +76 -0
  46. package/srpc/client.cpp +99 -87
  47. package/srpc/client.hpp +46 -35
  48. package/srpc/client.rs +78 -38
  49. package/srpc/client_half_close_test.cc +331 -0
  50. package/srpc/client_invoker.rs +75 -0
  51. package/srpc/common-rpc.cpp +186 -146
  52. package/srpc/common-rpc.go +5 -4
  53. package/srpc/common-rpc.hpp +119 -89
  54. package/srpc/common-rpc.ts +43 -8
  55. package/srpc/common-rpc_test.go +23 -0
  56. package/srpc/error.rs +19 -2
  57. package/srpc/errors.hpp +80 -50
  58. package/srpc/errors.ts +16 -0
  59. package/srpc/handler.hpp +9 -7
  60. package/srpc/handler.rs +7 -45
  61. package/srpc/index.ts +2 -0
  62. package/srpc/invoker.hpp +82 -55
  63. package/srpc/invoker.rs +14 -52
  64. package/srpc/lib.rs +2 -0
  65. package/srpc/message.hpp +17 -12
  66. package/srpc/msg-stream.go +1 -0
  67. package/srpc/msg-stream.hpp +108 -61
  68. package/srpc/mux.cpp +84 -84
  69. package/srpc/mux.hpp +48 -38
  70. package/srpc/mux.rs +4 -4
  71. package/srpc/net.go +12 -3
  72. package/srpc/packet.cpp +60 -58
  73. package/srpc/packet.hpp +35 -20
  74. package/srpc/packet.rs +8 -27
  75. package/srpc/rpc.rs +332 -115
  76. package/srpc/rpc_lifecycle_test.cc +333 -0
  77. package/srpc/server-cancel_test.go +39 -0
  78. package/srpc/server-rpc.cpp +83 -73
  79. package/srpc/server-rpc.go +21 -19
  80. package/srpc/server-rpc.hpp +56 -37
  81. package/srpc/server.rs +76 -131
  82. package/srpc/starpc.hpp +4 -2
  83. package/srpc/stream.go +4 -0
  84. package/srpc/stream.hpp +95 -34
  85. package/srpc/stream.rs +7 -2
  86. package/srpc/testing.rs +11 -66
  87. package/srpc/transport.rs +21 -71
  88. package/srpc/websocket.rs +106 -57
  89. package/srpc/writer.hpp +46 -32
  90. package/srpc/yamux.rs +82 -26
  91. package/srpc/tests/integration_test.rs +0 -495
@@ -22,8 +22,10 @@ export class CommonRPC {
22
22
  objectMode: true,
23
23
  })
24
24
 
25
- // _rpcDataSource is used to write to the rpc message source.
26
- private readonly _rpcDataSource = pushable<Uint8Array>({
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._rpcDataSource
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 && !remoteError) {
218
- this._rpcDataSource.end(remoteError)
219
- } else if (remoteError) {
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._rpcDataSource.end(err)
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
  }
@@ -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!(Error::Completed.to_string(), "unexpected packet after rpc was completed");
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!(Error::Remote("test error".into()).to_string(), "remote error: test error");
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
- // Error codes matching Go starpc errors (see errors.go)
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
- OK = 0,
11
- Unimplemented, // ErrUnimplemented - RPC method was not implemented
12
- Completed, // ErrCompleted - unexpected packet after rpc was completed
13
- UnrecognizedPacket, // ErrUnrecognizedPacket - unrecognized packet type
14
- EmptyPacket, // ErrEmptyPacket - invalid empty packet
15
- InvalidMessage, // ErrInvalidMessage - message failed to parse
16
- EmptyMethodID, // ErrEmptyMethodID - method id empty
17
- EmptyServiceID, // ErrEmptyServiceID - service id empty
18
- NoAvailableClients, // ErrNoAvailableClients - no available rpc clients
19
- NilWriter, // ErrNilWriter - writer cannot be nil
20
- Canceled, // context.Canceled equivalent
21
- EOF_, // io.EOF equivalent (named EOF_ to avoid macro collision)
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
- // Convert Error to string (matches Go error messages)
25
- inline const char *ErrorString(Error err) {
26
- switch (err) {
27
- case Error::OK:
28
- return "ok";
29
- case Error::Unimplemented:
30
- return "unimplemented";
31
- case Error::Completed:
32
- return "unexpected packet after rpc was completed";
33
- case Error::UnrecognizedPacket:
34
- return "unrecognized packet type";
35
- case Error::EmptyPacket:
36
- return "invalid empty packet";
37
- case Error::InvalidMessage:
38
- return "invalid message";
39
- case Error::EmptyMethodID:
40
- return "method id empty";
41
- case Error::EmptyServiceID:
42
- return "service id empty";
43
- case Error::NoAvailableClients:
44
- return "no available rpc clients";
45
- case Error::NilWriter:
46
- return "writer cannot be nil";
47
- case Error::Canceled:
48
- return "canceled";
49
- case Error::EOF_:
50
- return "EOF";
51
- default:
52
- return "unknown error";
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
- // StarpcError exception for error propagation
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
- explicit StarpcError(Error code)
60
- : std::runtime_error(ErrorString(code)), code_(code) {}
77
+ explicit StarpcError(Error code)
78
+ : std::runtime_error(ErrorString(code)),
79
+ code_(code)
80
+ {
81
+ }
61
82
 
62
- StarpcError(Error code, const std::string &message)
63
- : std::runtime_error(message), code_(code) {}
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
- Error code() const noexcept { return code_; }
90
+ /* code returns the Error this exception carries. */
91
+ Error code() const noexcept
92
+ {
93
+ return code_;
94
+ }
66
95
 
67
96
  private:
68
- Error code_;
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
- // Handler describes a SRPC call handler implementation.
11
- // Matches Go Handler interface in handler.go
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
- ~Handler() override = default;
16
+ ~Handler() override = default;
15
17
 
16
- // GetServiceID returns the ID of the service.
17
- virtual const std::string &GetServiceID() const = 0;
18
+ /* GetServiceID returns the service this handler registers. */
19
+ virtual const std::string &GetServiceID() const = 0;
18
20
 
19
- // GetMethodIDs returns the list of methods for the service.
20
- virtual std::vector<std::string> GetMethodIDs() const = 0;
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
- /// Trait for RPC service handlers.
11
+ /// Describes the service an Invoker implements, so the Mux can register it.
12
12
  ///
13
- /// A Handler extends Invoker with metadata methods that describe the service
14
- /// and methods it implements. This is the trait that generated service code
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 ID that this handler implements.
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 list of method IDs that this handler implements.
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
@@ -1,6 +1,8 @@
1
1
  export {
2
2
  ERR_RPC_ABORT,
3
3
  isAbortError,
4
+ ERR_CLOSED_BEFORE_COMPLETION,
5
+ isClosedBeforeCompletionError,
4
6
  ERR_STREAM_IDLE,
5
7
  isStreamIdleError,
6
8
  castToError,
package/srpc/invoker.hpp CHANGED
@@ -9,83 +9,110 @@
9
9
 
10
10
  namespace starpc {
11
11
 
12
- // Invoker is a function for invoking SRPC service methods.
13
- // Matches Go Invoker interface in invoker.go
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
- virtual ~Invoker() = default;
17
-
18
- // InvokeMethod invokes the method matching the service & method ID.
19
- // Returns {found, error} - found is false if method not found.
20
- // If service string is empty, ignore it.
21
- virtual std::pair<bool, Error> InvokeMethod(const std::string &service_id,
22
- const std::string &method_id,
23
- Stream *strm) = 0;
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
- // QueryableInvoker can be used to check if a service and method is implemented.
27
- // Matches Go QueryableInvoker interface in invoker.go
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
- virtual ~QueryableInvoker() = default;
37
+ virtual ~QueryableInvoker() = default;
31
38
 
32
- // HasService checks if the service ID exists in the handlers.
33
- virtual bool HasService(const std::string &service_id) const = 0;
39
+ /* HasService reports whether service_id is registered. */
40
+ virtual bool HasService(const std::string &service_id) const = 0;
34
41
 
35
- // HasServiceMethod checks if <service-id, method-id> exists in the handlers.
36
- virtual bool HasServiceMethod(const std::string &service_id,
37
- const std::string &method_id) const = 0;
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
- // InvokerSlice is a list of invokers.
41
- // Matches Go InvokerSlice in invoker.go
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
- InvokerSlice() = default;
45
- explicit InvokerSlice(std::vector<Invoker *> invokers)
46
- : invokers_(std::move(invokers)) {}
47
-
48
- void Add(Invoker *invoker) { invokers_.push_back(invoker); }
49
-
50
- std::pair<bool, Error> InvokeMethod(const std::string &service_id,
51
- const std::string &method_id,
52
- Stream *strm) override {
53
- for (auto *invoker : invokers_) {
54
- if (invoker == nullptr)
55
- continue;
56
- auto [found, err] = invoker->InvokeMethod(service_id, method_id, strm);
57
- if (found || err != Error::OK) {
58
- return {true, err};
59
- }
60
- }
61
- return {false, Error::OK};
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
- std::vector<Invoker *> invokers_;
82
+ /* invokers are borrowed; the caller owns them. */
83
+ std::vector<Invoker *> invokers;
66
84
  };
67
85
 
68
- // InvokerFunc is a function implementing InvokeMethod.
69
- // Matches Go InvokerFunc in invoker.go
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
- const std::string &service_id, const std::string &method_id, Stream *strm)>;
91
+ const std::string &service_id, const std::string &method_id,
92
+ Stream *strm)>;
72
93
 
73
- // InvokerFuncWrapper wraps an InvokerFunc as an Invoker.
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
- explicit InvokerFuncWrapper(InvokerFunc fn) : fn_(std::move(fn)) {}
77
-
78
- std::pair<bool, Error> InvokeMethod(const std::string &service_id,
79
- const std::string &method_id,
80
- Stream *strm) override {
81
- if (!fn_) {
82
- return {false, Error::OK};
83
- }
84
- return fn_(service_id, method_id, strm);
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
- InvokerFunc fn_;
115
+ InvokerFunc fn;
89
116
  };
90
117
 
91
118
  } // namespace starpc