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.
Files changed (152) hide show
  1. package/README.md +37 -8
  2. package/dist/echo/client-test.d.ts +1 -1
  3. package/dist/echo/client-test.js +110 -2
  4. package/dist/integration/cross-language/tcp-packet-stream.d.ts +3 -0
  5. package/dist/integration/cross-language/tcp-packet-stream.js +112 -0
  6. package/dist/integration/cross-language/tcp-packet-stream.test.d.ts +1 -0
  7. package/dist/integration/cross-language/tcp-packet-stream.test.js +121 -0
  8. package/dist/integration/cross-language/ts-client.js +50 -37
  9. package/dist/integration/cross-language/ts-server.js +1 -36
  10. package/dist/rpcstream/rpcstream.d.ts +5 -1
  11. package/dist/rpcstream/rpcstream.js +75 -28
  12. package/dist/rpcstream/rpcstream.test.d.ts +1 -0
  13. package/dist/rpcstream/rpcstream.test.js +92 -0
  14. package/dist/srpc/client-rpc.d.ts +1 -0
  15. package/dist/srpc/client-rpc.js +9 -0
  16. package/dist/srpc/client-rpc.test.d.ts +1 -0
  17. package/dist/srpc/client-rpc.test.js +50 -0
  18. package/dist/srpc/client.js +18 -4
  19. package/dist/srpc/common-rpc.d.ts +5 -0
  20. package/dist/srpc/common-rpc.js +37 -8
  21. package/dist/srpc/common-rpc.test.js +2 -0
  22. package/dist/srpc/errors.d.ts +2 -0
  23. package/dist/srpc/errors.js +14 -0
  24. package/dist/srpc/index.d.ts +1 -1
  25. package/dist/srpc/index.js +1 -1
  26. package/dist/srpc/packet-codec.test.d.ts +1 -0
  27. package/dist/srpc/packet-codec.test.js +75 -0
  28. package/dist/srpc/packet.d.ts +1 -1
  29. package/dist/srpc/packet.js +11 -1
  30. package/dist/srpc/server.js +19 -6
  31. package/dist/srpc/server.test.js +43 -0
  32. package/dist/srpc/stream.d.ts +4 -1
  33. package/dist/srpc/stream.js +62 -3
  34. package/dist/srpc/stream.test.js +110 -1
  35. package/dist/srpc/termination.d.ts +27 -0
  36. package/dist/srpc/termination.js +56 -0
  37. package/dist/srpc/termination.test.d.ts +1 -0
  38. package/dist/srpc/termination.test.js +24 -0
  39. package/dist/testdata/packet-codec-vectors.json +64 -0
  40. package/echo/Cargo.toml +8 -0
  41. package/echo/client-test.ts +124 -2
  42. package/echo/echo_e2e_test.cpp +542 -985
  43. package/echo/echo_pb2.py +40 -0
  44. package/echo/echo_pb2.pyi +13 -0
  45. package/echo/echo_srpc.pb.cpp +3 -5
  46. package/echo/echo_srpc.pb.go +6 -9
  47. package/echo/echo_srpc.pb.hpp +23 -3
  48. package/echo/echo_srpc.pb.rs +61 -26
  49. package/echo/echo_srpc.py +309 -0
  50. package/echo/echo_srpc.pyi +85 -0
  51. package/echo/half_close_test.rs +178 -0
  52. package/echo/integration_server.rs +14 -21
  53. package/echo/main.rs +28 -48
  54. package/go.mod +3 -3
  55. package/go.sum +6 -8
  56. package/integration/cross-language/cpp-client.cpp +446 -402
  57. package/integration/cross-language/cpp-pipe-server.cpp +139 -0
  58. package/integration/cross-language/cpp-pipe_test.go +114 -0
  59. package/integration/cross-language/cpp-server.cpp +222 -186
  60. package/integration/cross-language/go-client/main.go +79 -3
  61. package/integration/cross-language/python-client.py +146 -0
  62. package/integration/cross-language/python-server.py +140 -0
  63. package/integration/cross-language/run.bash +190 -65
  64. package/integration/cross-language/tcp-packet-stream.test.ts +154 -0
  65. package/integration/cross-language/tcp-packet-stream.ts +121 -0
  66. package/integration/cross-language/ts-client.ts +62 -40
  67. package/integration/cross-language/ts-server.ts +1 -45
  68. package/mock/mock_pb2.py +38 -0
  69. package/mock/mock_pb2.pyi +11 -0
  70. package/mock/mock_srpc.pb.cpp +1 -1
  71. package/mock/mock_srpc.pb.hpp +1 -1
  72. package/mock/mock_srpc.pb.rs +10 -9
  73. package/mock/mock_srpc.py +71 -0
  74. package/mock/mock_srpc.pyi +27 -0
  75. package/package.json +19 -5
  76. package/srpc/CMakeLists.txt +3 -58
  77. package/srpc/README-cpp.md +69 -0
  78. package/srpc/__init__.py +0 -0
  79. package/srpc/accept.go +10 -4
  80. package/srpc/accept_test.go +25 -0
  81. package/srpc/build.rs +129 -34
  82. package/srpc/client-half-close_test.go +152 -0
  83. package/srpc/client-invoker.go +32 -42
  84. package/srpc/client-invoker_test.go +53 -0
  85. package/srpc/client-rpc.cpp +65 -80
  86. package/srpc/client-rpc.go +32 -33
  87. package/srpc/client-rpc.hpp +52 -38
  88. package/srpc/client-rpc.test.ts +67 -0
  89. package/srpc/client-rpc.ts +10 -0
  90. package/srpc/client-rpc_test.go +76 -0
  91. package/srpc/client.cpp +99 -87
  92. package/srpc/client.hpp +46 -35
  93. package/srpc/client.rs +78 -38
  94. package/srpc/client.ts +20 -4
  95. package/srpc/client_half_close_test.cc +331 -0
  96. package/srpc/client_invoker.rs +75 -0
  97. package/srpc/codec.rs +6 -0
  98. package/srpc/common-rpc.cpp +186 -146
  99. package/srpc/common-rpc.go +5 -4
  100. package/srpc/common-rpc.hpp +119 -89
  101. package/srpc/common-rpc.test.ts +2 -0
  102. package/srpc/common-rpc.ts +43 -8
  103. package/srpc/common-rpc_test.go +23 -0
  104. package/srpc/error.rs +19 -2
  105. package/srpc/errors.hpp +80 -50
  106. package/srpc/errors.ts +16 -0
  107. package/srpc/handler.hpp +9 -7
  108. package/srpc/handler.rs +7 -45
  109. package/srpc/index.ts +2 -0
  110. package/srpc/invoker.hpp +82 -55
  111. package/srpc/invoker.rs +14 -52
  112. package/srpc/lib.rs +2 -0
  113. package/srpc/message.hpp +17 -12
  114. package/srpc/msg-stream.go +1 -0
  115. package/srpc/msg-stream.hpp +108 -61
  116. package/srpc/mux.cpp +84 -84
  117. package/srpc/mux.hpp +48 -38
  118. package/srpc/mux.rs +4 -4
  119. package/srpc/net.go +12 -3
  120. package/srpc/packet-codec-vectors_test.go +195 -0
  121. package/srpc/packet-codec.test.ts +139 -0
  122. package/srpc/packet-rw.go +9 -2
  123. package/srpc/packet.cpp +60 -58
  124. package/srpc/packet.hpp +35 -20
  125. package/srpc/packet.rs +8 -27
  126. package/srpc/packet.ts +15 -2
  127. package/srpc/py.typed +0 -0
  128. package/srpc/rpc.rs +332 -115
  129. package/srpc/rpc_lifecycle_test.cc +333 -0
  130. package/srpc/rpcproto_pb2.py +40 -0
  131. package/srpc/rpcproto_pb2.pyi +40 -0
  132. package/srpc/server-cancel_test.go +39 -0
  133. package/srpc/server-rpc.cpp +83 -73
  134. package/srpc/server-rpc.go +21 -19
  135. package/srpc/server-rpc.hpp +56 -37
  136. package/srpc/server.rs +76 -131
  137. package/srpc/server.test.ts +50 -0
  138. package/srpc/server.ts +22 -6
  139. package/srpc/starpc.hpp +4 -2
  140. package/srpc/stream.go +4 -0
  141. package/srpc/stream.hpp +95 -34
  142. package/srpc/stream.rs +7 -2
  143. package/srpc/stream.test.ts +132 -1
  144. package/srpc/stream.ts +65 -9
  145. package/srpc/termination.test.ts +30 -0
  146. package/srpc/termination.ts +70 -0
  147. package/srpc/testing.rs +11 -66
  148. package/srpc/transport.rs +21 -71
  149. package/srpc/websocket.rs +106 -57
  150. package/srpc/writer.hpp +46 -32
  151. package/srpc/yamux.rs +82 -26
  152. package/srpc/tests/integration_test.rs +0 -495
package/srpc/invoker.rs CHANGED
@@ -10,59 +10,17 @@ use std::sync::Arc;
10
10
  use crate::error::Result;
11
11
  use crate::stream::Stream;
12
12
 
13
- /// Trait for invoking RPC methods.
13
+ /// Dispatches incoming RPC calls to a handler implementation.
14
14
  ///
15
- /// An Invoker is responsible for dispatching incoming RPC calls to the
16
- /// appropriate handler implementation. The Mux implements this trait to
17
- /// route calls based on service and method IDs.
18
- ///
19
- /// # Return Value
20
- ///
21
- /// The `invoke_method` method returns a tuple of `(found, result)`:
22
- /// - `found`: Whether the method was found and handled
23
- /// - `result`: The result of the invocation, or an error
24
- ///
25
- /// This design allows callers to distinguish between:
26
- /// - Method not found: `(false, Err(Error::Unimplemented))`
27
- /// - Method found but failed: `(true, Err(...))`
28
- /// - Method found and succeeded: `(true, Ok(()))`
29
- ///
30
- /// # Example
31
- ///
32
- /// ```rust,ignore
33
- /// #[async_trait]
34
- /// impl Invoker for MyHandler {
35
- /// async fn invoke_method(
36
- /// &self,
37
- /// service_id: &str,
38
- /// method_id: &str,
39
- /// stream: Box<dyn Stream>,
40
- /// ) -> (bool, Result<()>) {
41
- /// match method_id {
42
- /// "MyMethod" => {
43
- /// // Handle the method
44
- /// (true, self.my_method(stream).await)
45
- /// }
46
- /// _ => (false, Err(Error::Unimplemented)),
47
- /// }
48
- /// }
49
- /// }
50
- /// ```
15
+ /// The Mux implements this trait to route calls by service and method ID.
16
+ /// `invoke_method` returns `(found, result)`, letting a caller distinguish a
17
+ /// method that was not found (`(false, Err(Error::Unimplemented))`) from one
18
+ /// that was found and failed (`(true, Err(...))`).
51
19
  #[async_trait]
52
20
  pub trait Invoker: Send + Sync {
53
- /// Invokes an RPC method.
54
- ///
55
- /// # Arguments
56
- ///
57
- /// * `service_id` - The service identifier (e.g., "echo.Echoer")
58
- /// * `method_id` - The method identifier (e.g., "Echo")
59
- /// * `stream` - The bidirectional stream for this RPC
60
- ///
61
- /// # Returns
62
- ///
63
- /// A tuple of (found, result) where:
64
- /// * `found` - Whether the method was found and handled
65
- /// * `result` - The result of the invocation
21
+ /// Invokes the RPC method, consuming `stream` whether or not the method
22
+ /// is found. Returns whether the method was handled and the invocation's
23
+ /// result.
66
24
  async fn invoke_method(
67
25
  &self,
68
26
  service_id: &str,
@@ -157,7 +115,9 @@ mod tests {
157
115
 
158
116
  #[tokio::test]
159
117
  async fn test_invoker_found() {
160
- let invoker = TestInvoker { should_handle: true };
118
+ let invoker = TestInvoker {
119
+ should_handle: true,
120
+ };
161
121
  let (found, result) = invoker
162
122
  .invoke_method("svc", "method", Box::new(MockStream))
163
123
  .await;
@@ -181,7 +141,9 @@ mod tests {
181
141
 
182
142
  #[tokio::test]
183
143
  async fn test_arc_invoker() {
184
- let invoker: Arc<dyn Invoker> = Arc::new(TestInvoker { should_handle: true });
144
+ let invoker: Arc<dyn Invoker> = Arc::new(TestInvoker {
145
+ should_handle: true,
146
+ });
185
147
  let (found, result) = invoker
186
148
  .invoke_method("svc", "method", Box::new(MockStream))
187
149
  .await;
package/srpc/lib.rs CHANGED
@@ -57,6 +57,7 @@
57
57
  //! This format is compatible with the Go and TypeScript implementations.
58
58
 
59
59
  pub mod client;
60
+ pub mod client_invoker;
60
61
  pub mod codec;
61
62
  pub mod error;
62
63
  pub mod handler;
@@ -82,6 +83,7 @@ pub mod yamux;
82
83
 
83
84
  // Re-exports for convenience.
84
85
  pub use client::{BoxClient, Client, OpenStream, SrpcClient};
86
+ pub use client_invoker::ClientInvoker;
85
87
  pub use codec::{PacketCodec, MAX_MESSAGE_SIZE};
86
88
  pub use error::{Error, Result};
87
89
  pub use handler::{BoxHandler, Handler};
package/srpc/message.hpp CHANGED
@@ -6,23 +6,28 @@
6
6
 
7
7
  namespace starpc {
8
8
 
9
- // Message is the interface for protobuf messages.
10
- // Matches Go Message interface in message.go
11
- // In C++, we use google::protobuf::MessageLite as the base.
9
+ /*
10
+ * Message is the wire-message type handed across the RPC boundary. It aliases
11
+ * protobuf's MessageLite, matching the Go Message interface in message.go.
12
+ */
12
13
  using Message = google::protobuf::MessageLite;
13
14
 
14
- // MarshalVT serializes the message to bytes.
15
- // (VT suffix matches vtprotobuf convention used in Go)
16
- inline bool MarshalVT(const Message &msg, std::string *out) {
17
- return msg.SerializeToString(out);
15
+ /* MarshalVT serializes msg into out and reports parse success. */
16
+ inline bool MarshalVT(const Message &msg, std::string *out)
17
+ {
18
+ return msg.SerializeToString(out);
18
19
  }
19
20
 
20
- // UnmarshalVT deserializes the message from bytes.
21
- inline bool UnmarshalVT(Message *msg, const std::string &data) {
22
- return msg->ParseFromString(data);
21
+ /* UnmarshalVT parses data into msg and reports success. */
22
+ inline bool UnmarshalVT(Message *msg, const std::string &data)
23
+ {
24
+ return msg->ParseFromString(data);
23
25
  }
24
26
 
25
- // SizeVT returns the serialized size of the message.
26
- inline size_t SizeVT(const Message &msg) { return msg.ByteSizeLong(); }
27
+ /* SizeVT returns the serialized size of msg in bytes. */
28
+ inline size_t SizeVT(const Message &msg)
29
+ {
30
+ return msg.ByteSizeLong();
31
+ }
27
32
 
28
33
  } // namespace starpc
@@ -72,6 +72,7 @@ func (r *MsgStream) MsgRecv(msg Message) error {
72
72
  }
73
73
 
74
74
  // CloseSend signals to the remote that we will no longer send any messages.
75
+ // A write error leaves received messages and the call outcome to MsgRecv.
75
76
  func (r *MsgStream) CloseSend() error {
76
77
  return r.rw.WriteCallData(nil, false, true, nil)
77
78
  }
@@ -1,86 +1,133 @@
1
1
  #pragma once
2
2
 
3
- #include <functional>
4
- #include <string>
5
-
6
3
  #include "errors.hpp"
7
4
  #include "message.hpp"
8
5
  #include "stream.hpp"
9
6
 
7
+ #include <atomic>
8
+ #include <functional>
9
+ #include <string>
10
+
10
11
  namespace starpc {
11
12
 
12
- // MsgStreamRw is the read-write interface for MsgStream.
13
- // Matches Go MsgStreamRw interface in msg-stream.go
13
+ /*
14
+ * MsgStreamRw is the call-state side of MsgStream: it reads and writes one
15
+ * call's messages. Matches the Go msgStreamRw interface.
16
+ */
14
17
  class MsgStreamRw {
15
18
  public:
16
- virtual ~MsgStreamRw() = default;
19
+ virtual ~MsgStreamRw() = default;
17
20
 
18
- // ReadOne reads a single message and returns.
19
- // Returns EOF_ if the stream ended.
20
- virtual Error ReadOne(std::string *out) = 0;
21
+ /* RemoteErrorMessage returns the peer's retained diagnostic, if any. */
22
+ virtual std::string RemoteErrorMessage() const
23
+ {
24
+ return {};
25
+ }
21
26
 
22
- // WriteCallData writes a call data packet.
23
- virtual Error WriteCallData(const std::string &data, bool data_is_zero,
24
- bool complete, Error err) = 0;
27
+ /*
28
+ * ReadOne returns the next message in out. Returns EOF_ after the
29
+ * call completes and Canceled after local cancellation.
30
+ */
31
+ virtual Error ReadOne(std::string *out) = 0;
25
32
 
26
- // WriteCallCancel writes a call cancel (close) packet.
27
- virtual Error WriteCallCancel() = 0;
33
+ /*
34
+ * WriteCallData writes one data packet. data_is_zero marks an
35
+ * intentionally empty message; complete ends the sending side; err
36
+ * carries the terminal verdict.
37
+ */
38
+ virtual Error WriteCallData(const std::string &data, bool data_is_zero,
39
+ bool complete, Error err) = 0;
40
+
41
+ /* WriteCallCancel cancels the call on the remote. */
42
+ virtual Error WriteCallCancel() = 0;
28
43
  };
29
44
 
30
- // MsgStream implements the stream interface passed to implementations.
31
- // Matches Go MsgStream struct in msg-stream.go
45
+ /*
46
+ * MsgStream is the Stream handed to handlers: it serializes messages over a
47
+ * MsgStreamRw and closes the call when the handler is done.
48
+ */
32
49
  class MsgStream : public Stream {
33
50
  public:
34
- // Constructor matching NewMsgStream in Go
35
- MsgStream(MsgStreamRw *rw, std::function<void()> close_cb)
36
- : rw_(rw), close_cb_(std::move(close_cb)) {}
37
-
38
- // MsgSend sends the message to the remote.
39
- Error MsgSend(const Message &msg) override {
40
- std::string msg_data;
41
- if (!msg.SerializeToString(&msg_data)) {
42
- return Error::InvalidMessage;
43
- }
44
- return rw_->WriteCallData(msg_data, msg_data.empty(), false, Error::OK);
45
- }
46
-
47
- // MsgRecv receives an incoming message from the remote.
48
- Error MsgRecv(Message *msg) override {
49
- std::string data;
50
- Error err = rw_->ReadOne(&data);
51
- if (err != Error::OK) {
52
- return err;
53
- }
54
- if (!msg->ParseFromString(data)) {
55
- return Error::InvalidMessage;
56
- }
57
- return Error::OK;
58
- }
59
-
60
- // CloseSend signals to the remote that we will no longer send any messages.
61
- Error CloseSend() override {
62
- return rw_->WriteCallData("", false, true, Error::OK);
63
- }
64
-
65
- // Close closes the stream.
66
- Error Close() override {
67
- Error err = rw_->WriteCallCancel();
68
- if (close_cb_) {
69
- close_cb_();
70
- }
71
- return err;
72
- }
51
+ /*
52
+ * MsgStream borrows rw; it must outlive the stream. close_cb, when
53
+ * set, replaces the cancel write on Close; stop carries the call's
54
+ * cancellation to the handler.
55
+ */
56
+ MsgStream(MsgStreamRw *rw, std::function<void()> close_cb,
57
+ std::stop_token stop = {})
58
+ : rw(rw),
59
+ close_cb(std::move(close_cb)),
60
+ stop(stop)
61
+ {
62
+ }
63
+
64
+ std::stop_token StopToken() const override
65
+ {
66
+ return stop;
67
+ }
68
+ std::string RemoteErrorMessage() const override
69
+ {
70
+ return rw->RemoteErrorMessage();
71
+ }
72
+
73
+ Error MsgSend(const Message &msg) override
74
+ {
75
+ std::string msg_data;
76
+ if (!msg.SerializeToString(&msg_data))
77
+ return Error::InvalidMessage;
78
+ return rw->WriteCallData(msg_data, msg_data.empty(), false,
79
+ Error::OK);
80
+ }
81
+
82
+ Error MsgRecv(Message *msg) override
83
+ {
84
+ std::string data;
85
+ Error err = rw->ReadOne(&data);
86
+ if (err != Error::OK)
87
+ return err;
88
+ if (!msg->ParseFromString(data))
89
+ return Error::InvalidMessage;
90
+ return Error::OK;
91
+ }
92
+
93
+ /*
94
+ * CloseSend half-closes the sending side. See Stream::CloseSend for
95
+ * why its failure leaves the outcome to MsgRecv.
96
+ */
97
+ Error CloseSend() override
98
+ {
99
+ return rw->WriteCallData("", false, true, Error::OK);
100
+ }
101
+
102
+ /*
103
+ * Close ends the call once: through close_cb when the call is shared
104
+ * with a transport, otherwise by writing cancel to the remote.
105
+ */
106
+ Error Close() override
107
+ {
108
+ if (closed.exchange(true))
109
+ return Error::OK;
110
+ if (close_cb) {
111
+ close_cb();
112
+ return Error::OK;
113
+ }
114
+ return rw->WriteCallCancel();
115
+ }
73
116
 
74
117
  private:
75
- MsgStreamRw *rw_;
76
- std::function<void()> close_cb_;
118
+ /* rw is borrowed; the caller owns it. */
119
+ MsgStreamRw *rw;
120
+ std::function<void()> close_cb;
121
+ std::stop_token stop;
122
+ std::atomic<bool> closed{false};
77
123
  };
78
124
 
79
- // NewMsgStream constructs a new Stream with a MsgStreamRw.
80
- // Matches Go NewMsgStream function in msg-stream.go
125
+ /* NewMsgStream constructs a Stream over rw. */
81
126
  inline std::unique_ptr<MsgStream> NewMsgStream(MsgStreamRw *rw,
82
- std::function<void()> close_cb) {
83
- return std::make_unique<MsgStream>(rw, std::move(close_cb));
127
+ std::function<void()> close_cb,
128
+ std::stop_token stop = {})
129
+ {
130
+ return std::make_unique<MsgStream>(rw, std::move(close_cb), stop);
84
131
  }
85
132
 
86
133
  } // namespace starpc
package/srpc/mux.cpp CHANGED
@@ -1,109 +1,109 @@
1
1
  //go:build deps_only
2
2
 
3
3
  #include "mux.hpp"
4
+
4
5
  #include <mutex>
6
+ #include <shared_mutex>
5
7
 
6
8
  namespace starpc {
7
9
 
8
10
  Mux::Mux(std::vector<Invoker *> fallback_invokers)
9
- : fallback_(std::move(fallback_invokers)) {}
11
+ : fallback(std::move(fallback_invokers))
12
+ {
13
+ }
10
14
 
11
- Error Mux::Register(Handler *handler) {
12
- const std::string &service_id = handler->GetServiceID();
13
- auto method_ids = handler->GetMethodIDs();
15
+ Error Mux::Register(Handler *handler)
16
+ {
17
+ const std::string &service_id = handler->GetServiceID();
18
+ auto method_ids = handler->GetMethodIDs();
14
19
 
15
- if (service_id.empty()) {
16
- return Error::EmptyServiceID;
17
- }
20
+ if (service_id.empty())
21
+ return Error::EmptyServiceID;
18
22
 
19
- std::unique_lock<std::shared_mutex> lock(mtx_);
23
+ std::unique_lock<std::shared_mutex> lock(mu);
20
24
 
21
- auto &service_methods = services_[service_id];
22
- for (const auto &method_id : method_ids) {
23
- if (!method_id.empty()) {
24
- service_methods[method_id] = handler;
25
- }
26
- }
25
+ auto &service_methods = services[service_id];
26
+ for (const auto &method_id : method_ids) {
27
+ if (!method_id.empty())
28
+ service_methods[method_id] = handler;
29
+ }
27
30
 
28
- return Error::OK;
31
+ return Error::OK;
29
32
  }
30
33
 
31
- bool Mux::HasService(const std::string &service_id) const {
32
- if (service_id.empty()) {
33
- return false;
34
- }
34
+ bool Mux::HasService(const std::string &service_id) const
35
+ {
36
+ if (service_id.empty())
37
+ return false;
35
38
 
36
- std::shared_lock<std::shared_mutex> lock(mtx_);
37
- auto it = services_.find(service_id);
38
- return it != services_.end() && !it->second.empty();
39
+ std::shared_lock<std::shared_mutex> lock(mu);
40
+ auto it = services.find(service_id);
41
+ return it != services.end() && !it->second.empty();
39
42
  }
40
43
 
41
44
  bool Mux::HasServiceMethod(const std::string &service_id,
42
- const std::string &method_id) const {
43
- if (service_id.empty() || method_id.empty()) {
44
- return false;
45
- }
46
-
47
- std::shared_lock<std::shared_mutex> lock(mtx_);
48
- auto svc_it = services_.find(service_id);
49
- if (svc_it == services_.end()) {
50
- return false;
51
- }
52
-
53
- for (const auto &[mid, handler] : svc_it->second) {
54
- for (const auto &handler_method : handler->GetMethodIDs()) {
55
- if (handler_method == method_id) {
56
- return true;
57
- }
58
- }
59
- }
60
-
61
- return false;
45
+ const std::string &method_id) const
46
+ {
47
+ if (service_id.empty() || method_id.empty())
48
+ return false;
49
+
50
+ std::shared_lock<std::shared_mutex> lock(mu);
51
+ auto svc_it = services.find(service_id);
52
+ if (svc_it == services.end())
53
+ return false;
54
+
55
+ for (const auto &[mid, handler] : svc_it->second) {
56
+ for (const auto &handler_method : handler->GetMethodIDs()) {
57
+ if (handler_method == method_id)
58
+ return true;
59
+ }
60
+ }
61
+
62
+ return false;
62
63
  }
63
64
 
64
65
  std::pair<bool, Error> Mux::InvokeMethod(const std::string &service_id,
65
- const std::string &method_id,
66
- Stream *strm) {
67
- Handler *handler = nullptr;
68
-
69
- {
70
- std::shared_lock<std::shared_mutex> lock(mtx_);
71
-
72
- if (service_id.empty()) {
73
- // If service string is empty, search all services
74
- for (const auto &[svc_id, svc_methods] : services_) {
75
- auto it = svc_methods.find(method_id);
76
- if (it != svc_methods.end()) {
77
- handler = it->second;
78
- break;
79
- }
80
- }
81
- } else {
82
- auto svc_it = services_.find(service_id);
83
- if (svc_it != services_.end()) {
84
- auto method_it = svc_it->second.find(method_id);
85
- if (method_it != svc_it->second.end()) {
86
- handler = method_it->second;
87
- }
88
- }
89
- }
90
- }
91
-
92
- if (handler != nullptr) {
93
- return handler->InvokeMethod(service_id, method_id, strm);
94
- }
95
-
96
- // Try fallback invokers
97
- for (auto *invoker : fallback_) {
98
- if (invoker != nullptr) {
99
- auto [handled, err] = invoker->InvokeMethod(service_id, method_id, strm);
100
- if (err != Error::OK || handled) {
101
- return {handled, err};
102
- }
103
- }
104
- }
105
-
106
- return {false, Error::OK};
66
+ const std::string &method_id,
67
+ Stream *strm)
68
+ {
69
+ Handler *handler = nullptr;
70
+
71
+ /* Find the registered handler outside any handler call. */
72
+ {
73
+ std::shared_lock<std::shared_mutex> lock(mu);
74
+
75
+ if (service_id.empty()) {
76
+ for (const auto &[svc_id, svc_methods] : services) {
77
+ auto it = svc_methods.find(method_id);
78
+ if (it != svc_methods.end()) {
79
+ handler = it->second;
80
+ break;
81
+ }
82
+ }
83
+ } else {
84
+ auto svc_it = services.find(service_id);
85
+ if (svc_it != services.end()) {
86
+ auto method_it = svc_it->second.find(method_id);
87
+ if (method_it != svc_it->second.end())
88
+ handler = method_it->second;
89
+ }
90
+ }
91
+ }
92
+
93
+ if (handler != nullptr)
94
+ return handler->InvokeMethod(service_id, method_id, strm);
95
+
96
+ /* Try each fallback invoker in order. */
97
+ for (auto *invoker : fallback) {
98
+ if (invoker == nullptr)
99
+ continue;
100
+ auto [handled, err] =
101
+ invoker->InvokeMethod(service_id, method_id, strm);
102
+ if (err != Error::OK || handled)
103
+ return {handled, err};
104
+ }
105
+
106
+ return {false, Error::OK};
107
107
  }
108
108
 
109
109
  } // namespace starpc
package/srpc/mux.hpp CHANGED
@@ -1,64 +1,74 @@
1
1
  #pragma once
2
2
 
3
+ #include "errors.hpp"
4
+ #include "handler.hpp"
5
+ #include "invoker.hpp"
6
+
3
7
  #include <shared_mutex>
4
8
  #include <string>
5
9
  #include <unordered_map>
6
10
  #include <vector>
7
11
 
8
- #include "errors.hpp"
9
- #include "handler.hpp"
10
- #include "invoker.hpp"
11
-
12
12
  namespace starpc {
13
13
 
14
- // Mux contains a set of <service, method> handlers.
15
- // Matches Go Mux interface in mux.go
14
+ /*
15
+ * Mux dispatches calls to registered handlers and falls back to a list of
16
+ * invokers when no handler matches. Matches the Go Mux in mux.go.
17
+ */
16
18
  class Mux : public Invoker, public QueryableInvoker {
17
19
  public:
18
- // Constructor matching NewMux in Go
19
- explicit Mux(std::vector<Invoker *> fallback_invokers = {});
20
- ~Mux() override = default;
20
+ /*
21
+ * Mux consults fallback_invokers, in order, when no registered
22
+ * handler matches. The invokers are borrowed and must outlive the
23
+ * mux.
24
+ */
25
+ explicit Mux(std::vector<Invoker *> fallback_invokers = {});
26
+ ~Mux() override = default;
21
27
 
22
- // Register registers a new RPC method handler (service).
23
- // Matches Go Register in mux.go
24
- Error Register(Handler *handler);
28
+ /*
29
+ * Register maps the handler's service and method IDs to it. An empty
30
+ * service ID fails; empty method IDs are skipped.
31
+ */
32
+ Error Register(Handler *handler);
25
33
 
26
- // InvokeMethod invokes the method matching the service & method ID.
27
- // Returns {found, error} - found is false if method not found.
28
- // If service string is empty, ignore it.
29
- // Matches Go InvokeMethod in mux.go
30
- std::pair<bool, Error> InvokeMethod(const std::string &service_id,
31
- const std::string &method_id,
32
- Stream *strm) override;
34
+ /*
35
+ * InvokeMethod runs the registered handler for the IDs and reports
36
+ * whether it found one. An empty service_id searches every service.
37
+ * Unhandled calls go to the fallback invokers.
38
+ */
39
+ std::pair<bool, Error> InvokeMethod(const std::string &service_id,
40
+ const std::string &method_id,
41
+ Stream *strm) override;
33
42
 
34
- // HasService checks if the service ID exists in the handlers.
35
- // Matches Go HasService in mux.go
36
- bool HasService(const std::string &service_id) const override;
43
+ /* HasService reports whether any handler registered service_id. */
44
+ bool HasService(const std::string &service_id) const override;
37
45
 
38
- // HasServiceMethod checks if <service-id, method-id> exists in the handlers.
39
- // Matches Go HasServiceMethod in mux.go
40
- bool HasServiceMethod(const std::string &service_id,
41
- const std::string &method_id) const override;
46
+ /*
47
+ * HasServiceMethod reports whether a registered handler for
48
+ * service_id serves method_id.
49
+ */
50
+ bool HasServiceMethod(const std::string &service_id,
51
+ const std::string &method_id) const override;
42
52
 
43
53
  private:
44
- // Mapping from method id to handler
45
- using MuxMethods = std::unordered_map<std::string, Handler *>;
54
+ /* methods maps one method ID to the handler that registered it. */
55
+ using methods = std::unordered_map<std::string, Handler *>;
46
56
 
47
- // Fallback invokers
48
- std::vector<Invoker *> fallback_;
57
+ /* fallback invokers are borrowed; the caller owns them. */
58
+ std::vector<Invoker *> fallback;
49
59
 
50
- // Read-write mutex guards services_
51
- mutable std::shared_mutex mtx_;
60
+ /* mu guards services. */
61
+ mutable std::shared_mutex mu;
52
62
 
53
- // Services contains a mapping from services to handlers
54
- std::unordered_map<std::string, MuxMethods> services_;
63
+ /* services maps each service ID to its method table. */
64
+ std::unordered_map<std::string, methods> services;
55
65
  };
56
66
 
57
- // NewMux constructs a new Mux.
58
- // Matches Go NewMux function in mux.go
67
+ /* NewMux constructs a Mux with the given fallback invokers. */
59
68
  inline std::unique_ptr<Mux>
60
- NewMux(std::vector<Invoker *> fallback_invokers = {}) {
61
- return std::make_unique<Mux>(std::move(fallback_invokers));
69
+ NewMux(std::vector<Invoker *> fallback_invokers = {})
70
+ {
71
+ return std::make_unique<Mux>(std::move(fallback_invokers));
62
72
  }
63
73
 
64
74
  } // namespace starpc