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
@@ -1,7 +1,14 @@
1
1
  //go:build deps_only
2
2
 
3
- // C++ TCP integration server for cross-language testing.
4
- // Listens on TCP, handles one RPC per connection using length-prefixed packets.
3
+ /*
4
+ * C++ TCP integration server for cross-language testing: it listens on TCP
5
+ * and serves one RPC per connection over length-prefixed packets.
6
+ */
7
+
8
+ #include "echo/echo_srpc.pb.hpp"
9
+ #include "rpcstream/rpcstream.hpp"
10
+ #include "srpc/rpcproto.pb.h"
11
+ #include "srpc/starpc.hpp"
5
12
 
6
13
  #include <arpa/inet.h>
7
14
  #include <csignal>
@@ -13,209 +20,238 @@
13
20
  #include <sys/socket.h>
14
21
  #include <thread>
15
22
  #include <unistd.h>
16
-
17
- #include "echo/echo_srpc.pb.hpp"
18
- #include "rpcstream/rpcstream.hpp"
19
- #include "srpc/rpcproto.pb.h"
20
- #include "srpc/starpc.hpp"
23
+ #include <vector>
21
24
 
22
25
  namespace {
23
26
 
24
- // ReadFull reads exactly n bytes from fd.
25
- bool ReadFull(int fd, void *buf, size_t n) {
26
- size_t total = 0;
27
- while (total < n) {
28
- ssize_t r = read(fd, static_cast<char *>(buf) + total, n - total);
29
- if (r <= 0)
30
- return false;
31
- total += r;
32
- }
33
- return true;
27
+ /* ReadFull reads exactly n bytes from fd, reporting short reads as false. */
28
+ bool ReadFull(int fd, void *buf, size_t n)
29
+ {
30
+ size_t total = 0;
31
+ while (total < n) {
32
+ ssize_t r =
33
+ read(fd, static_cast<char *>(buf) + total, n - total);
34
+ if (r <= 0)
35
+ return false;
36
+ total += r;
37
+ }
38
+ return true;
34
39
  }
35
40
 
36
- // WriteFull writes exactly n bytes to fd.
37
- bool WriteFull(int fd, const void *buf, size_t n) {
38
- size_t total = 0;
39
- while (total < n) {
40
- ssize_t w = write(fd, static_cast<const char *>(buf) + total, n - total);
41
- if (w <= 0)
42
- return false;
43
- total += w;
44
- }
45
- return true;
41
+ /* WriteFull writes exactly n bytes to fd, reporting short writes as false. */
42
+ bool WriteFull(int fd, const void *buf, size_t n)
43
+ {
44
+ size_t total = 0;
45
+ while (total < n) {
46
+ ssize_t w = write(fd, static_cast<const char *>(buf) + total,
47
+ n - total);
48
+ if (w <= 0)
49
+ return false;
50
+ total += w;
51
+ }
52
+ return true;
46
53
  }
47
54
 
48
- // TcpPacketWriter writes length-prefixed packets to a TCP socket.
55
+ /*
56
+ * TcpPacketWriter writes length-prefixed packets to a TCP socket. The fd is
57
+ * borrowed and closed by the connection handler, not the writer.
58
+ */
49
59
  class TcpPacketWriter : public starpc::PacketWriter {
50
60
  public:
51
- explicit TcpPacketWriter(int fd) : fd_(fd) {}
52
-
53
- starpc::Error WritePacket(const srpc::Packet &pkt) override {
54
- std::lock_guard<std::mutex> lock(mtx_);
55
- std::string data;
56
- if (!pkt.SerializeToString(&data))
57
- return starpc::Error::InvalidMessage;
58
-
59
- uint32_t len = static_cast<uint32_t>(data.size());
60
- // Write LE uint32 length prefix.
61
- if (!WriteFull(fd_, &len, 4))
62
- return starpc::Error::EOF_;
63
- if (!WriteFull(fd_, data.data(), data.size()))
64
- return starpc::Error::EOF_;
65
- return starpc::Error::OK;
66
- }
67
-
68
- starpc::Error Close() override {
69
- shutdown(fd_, SHUT_WR);
70
- return starpc::Error::OK;
71
- }
61
+ explicit TcpPacketWriter(int fd)
62
+ : fd(fd)
63
+ {
64
+ }
65
+
66
+ starpc::Error WritePacket(const srpc::Packet &pkt) override
67
+ {
68
+ std::lock_guard<std::mutex> lock(mu);
69
+
70
+ std::string data;
71
+ if (!pkt.SerializeToString(&data))
72
+ return starpc::Error::InvalidMessage;
73
+
74
+ /* The length prefix is a little-endian uint32. */
75
+ uint32_t len = static_cast<uint32_t>(data.size());
76
+ if (!WriteFull(fd, &len, 4))
77
+ return starpc::Error::EOF_;
78
+ if (!WriteFull(fd, data.data(), data.size()))
79
+ return starpc::Error::EOF_;
80
+ return starpc::Error::OK;
81
+ }
82
+
83
+ starpc::Error Close() override
84
+ {
85
+ shutdown(fd, SHUT_WR);
86
+ return starpc::Error::OK;
87
+ }
72
88
 
73
89
  private:
74
- int fd_;
75
- std::mutex mtx_;
90
+ int fd;
91
+ std::mutex mu;
76
92
  };
77
93
 
78
- // EchoServerImpl implements the echo service.
94
+ /* EchoServerImpl implements the echo service under test. */
79
95
  class EchoServerImpl : public echo::SRPCEchoerServer {
80
96
  public:
81
- starpc::Error Echo(const echo::EchoMsg &req, echo::EchoMsg *resp) override {
82
- resp->set_body(req.body());
83
- return starpc::Error::OK;
84
- }
85
-
86
- starpc::Error
87
- EchoServerStream(const echo::EchoMsg &req,
88
- echo::SRPCEchoer_EchoServerStreamStream *strm) override {
89
- for (int i = 0; i < 5; i++) {
90
- echo::EchoMsg msg;
91
- msg.set_body(req.body());
92
- starpc::Error err = strm->Send(msg);
93
- if (err != starpc::Error::OK)
94
- return err;
95
- }
96
- return starpc::Error::OK;
97
- }
98
-
99
- starpc::Error EchoClientStream(echo::SRPCEchoer_EchoClientStreamStream *strm,
100
- echo::EchoMsg *resp) override {
101
- echo::EchoMsg msg;
102
- starpc::Error err = strm->Recv(&msg);
103
- if (err != starpc::Error::OK)
104
- return err;
105
- resp->set_body(msg.body());
106
- return starpc::Error::OK;
107
- }
108
-
109
- starpc::Error
110
- EchoBidiStream(echo::SRPCEchoer_EchoBidiStreamStream *strm) override {
111
- // Send initial message (matches Go server behavior).
112
- echo::EchoMsg init;
113
- init.set_body("hello from server");
114
- starpc::Error err = strm->Send(init);
115
- if (err != starpc::Error::OK)
116
- return err;
117
-
118
- while (true) {
119
- echo::EchoMsg msg;
120
- err = strm->Recv(&msg);
121
- if (err == starpc::Error::EOF_)
122
- break;
123
- if (err != starpc::Error::OK)
124
- return err;
125
- err = strm->Send(msg);
126
- if (err != starpc::Error::OK)
127
- return err;
128
- }
129
- return starpc::Error::OK;
130
- }
131
-
132
- starpc::Error RpcStream(echo::SRPCEchoer_RpcStreamStream *) override {
133
- return starpc::Error::Unimplemented;
134
- }
135
-
136
- starpc::Error DoNothing(const google::protobuf::Empty &,
137
- google::protobuf::Empty *) override {
138
- return starpc::Error::OK;
139
- }
97
+ starpc::Error Echo(const echo::EchoMsg &req,
98
+ echo::EchoMsg *resp) override
99
+ {
100
+ resp->set_body(req.body());
101
+ return starpc::Error::OK;
102
+ }
103
+
104
+ starpc::Error
105
+ EchoServerStream(const echo::EchoMsg &req,
106
+ echo::SRPCEchoer_EchoServerStreamStream *strm) override
107
+ {
108
+ for (int i = 0; i < 5; i++) {
109
+ echo::EchoMsg msg;
110
+ msg.set_body(req.body());
111
+ starpc::Error err = strm->Send(msg);
112
+ if (err != starpc::Error::OK)
113
+ return err;
114
+ }
115
+ return starpc::Error::OK;
116
+ }
117
+
118
+ starpc::Error
119
+ EchoClientStream(echo::SRPCEchoer_EchoClientStreamStream *strm,
120
+ echo::EchoMsg *resp) override
121
+ {
122
+ echo::EchoMsg msg;
123
+ starpc::Error err = strm->Recv(&msg);
124
+ if (err != starpc::Error::OK)
125
+ return err;
126
+ resp->set_body(msg.body());
127
+ return starpc::Error::OK;
128
+ }
129
+
130
+ starpc::Error
131
+ EchoBidiStream(echo::SRPCEchoer_EchoBidiStreamStream *strm) override
132
+ {
133
+ /* Send the initial message the other clients expect. */
134
+ echo::EchoMsg init;
135
+ init.set_body("hello from server");
136
+ starpc::Error err = strm->Send(init);
137
+ if (err != starpc::Error::OK)
138
+ return err;
139
+
140
+ while (true) {
141
+ echo::EchoMsg msg;
142
+ err = strm->Recv(&msg);
143
+ if (err == starpc::Error::EOF_)
144
+ break;
145
+ if (err != starpc::Error::OK)
146
+ return err;
147
+ err = strm->Send(msg);
148
+ if (err != starpc::Error::OK)
149
+ return err;
150
+ }
151
+ return starpc::Error::OK;
152
+ }
153
+
154
+ starpc::Error RpcStream(echo::SRPCEchoer_RpcStreamStream *) override
155
+ {
156
+ return starpc::Error::Unimplemented;
157
+ }
158
+
159
+ starpc::Error DoNothing(const google::protobuf::Empty &,
160
+ google::protobuf::Empty *) override
161
+ {
162
+ return starpc::Error::OK;
163
+ }
140
164
  };
141
165
 
142
- // HandleConnection handles one TCP connection (one RPC).
143
- void HandleConnection(int fd, starpc::Mux *mux) {
144
- auto writer = std::make_unique<TcpPacketWriter>(fd);
145
- auto serverRpc = starpc::NewServerRPC(mux, writer.get());
146
-
147
- while (true) {
148
- // Read 4-byte LE uint32 length prefix.
149
- uint32_t len = 0;
150
- if (!ReadFull(fd, &len, 4))
151
- break;
152
-
153
- // Read the packet data.
154
- std::string data(len, '\0');
155
- if (!ReadFull(fd, data.data(), len))
156
- break;
157
-
158
- starpc::Error err = serverRpc->HandlePacketData(data);
159
- if (err != starpc::Error::OK && err != starpc::Error::Completed)
160
- break;
161
- }
162
-
163
- close(fd);
166
+ /*
167
+ * HandleConnection serves one TCP connection: it reads length-prefixed
168
+ * packets until the socket ends, then closes the fd.
169
+ */
170
+ void HandleConnection(int fd, starpc::Mux *mux)
171
+ {
172
+ auto writer = std::make_unique<TcpPacketWriter>(fd);
173
+ auto server_rpc = starpc::NewServerRPC(mux, writer.get());
174
+
175
+ while (true) {
176
+ /* The length prefix is a little-endian uint32. */
177
+ uint32_t len = 0;
178
+ if (!ReadFull(fd, &len, 4))
179
+ break;
180
+
181
+ std::string data(len, '\0');
182
+ if (!ReadFull(fd, data.data(), len))
183
+ break;
184
+
185
+ starpc::Error err = server_rpc->HandlePacketData(data);
186
+ if (err != starpc::Error::OK && err != starpc::Error::Completed)
187
+ break;
188
+ }
189
+
190
+ close(fd);
164
191
  }
165
192
 
166
193
  } // namespace
167
194
 
168
- int main() {
169
- signal(SIGPIPE, SIG_IGN);
170
-
171
- auto mux = starpc::NewMux();
172
- EchoServerImpl server;
173
- auto [handler, err] = echo::SRPCRegisterEchoer(mux.get(), &server);
174
- (void)handler;
175
- if (err != starpc::Error::OK) {
176
- std::cerr << "register error: " << starpc::ErrorString(err) << std::endl;
177
- return 1;
178
- }
179
-
180
- int sockfd = socket(AF_INET, SOCK_STREAM, 0);
181
- if (sockfd < 0) {
182
- std::cerr << "socket error" << std::endl;
183
- return 1;
184
- }
185
-
186
- int opt = 1;
187
- setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
188
-
189
- struct sockaddr_in addr {};
190
- addr.sin_family = AF_INET;
191
- addr.sin_port = 0; // OS-assigned port.
192
- addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
193
-
194
- if (bind(sockfd, reinterpret_cast<struct sockaddr *>(&addr), sizeof(addr)) <
195
- 0) {
196
- std::cerr << "bind error" << std::endl;
197
- close(sockfd);
198
- return 1;
199
- }
200
-
201
- if (listen(sockfd, 10) < 0) {
202
- std::cerr << "listen error" << std::endl;
203
- close(sockfd);
204
- return 1;
205
- }
206
-
207
- // Get the assigned port.
208
- socklen_t addrLen = sizeof(addr);
209
- getsockname(sockfd, reinterpret_cast<struct sockaddr *>(&addr), &addrLen);
210
- std::cout << "LISTENING 127.0.0.1:" << ntohs(addr.sin_port) << std::endl;
211
-
212
- while (true) {
213
- int clientFd = accept(sockfd, nullptr, nullptr);
214
- if (clientFd < 0)
215
- break;
216
- std::thread(HandleConnection, clientFd, mux.get()).detach();
217
- }
218
-
219
- close(sockfd);
220
- return 0;
195
+ int main()
196
+ {
197
+ signal(SIGPIPE, SIG_IGN);
198
+
199
+ auto mux = starpc::NewMux();
200
+ EchoServerImpl server;
201
+ auto [handler, err] = echo::SRPCRegisterEchoer(mux.get(), &server);
202
+ (void)handler;
203
+ if (err != starpc::Error::OK) {
204
+ std::cerr << "register error: " << starpc::ErrorString(err)
205
+ << std::endl;
206
+ return 1;
207
+ }
208
+
209
+ int sockfd = socket(AF_INET, SOCK_STREAM, 0);
210
+ if (sockfd < 0) {
211
+ std::cerr << "socket error" << std::endl;
212
+ return 1;
213
+ }
214
+
215
+ int opt = 1;
216
+ setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
217
+
218
+ struct sockaddr_in addr{};
219
+ addr.sin_family = AF_INET;
220
+ addr.sin_port = 0; /* OS-assigned port. */
221
+ addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
222
+
223
+ if (bind(sockfd, reinterpret_cast<struct sockaddr *>(&addr),
224
+ sizeof(addr)) < 0) {
225
+ std::cerr << "bind error" << std::endl;
226
+ close(sockfd);
227
+ return 1;
228
+ }
229
+
230
+ if (listen(sockfd, 10) < 0) {
231
+ std::cerr << "listen error" << std::endl;
232
+ close(sockfd);
233
+ return 1;
234
+ }
235
+
236
+ /* Report the assigned port so the test driver can connect. */
237
+ socklen_t addr_len = sizeof(addr);
238
+ getsockname(sockfd, reinterpret_cast<struct sockaddr *>(&addr),
239
+ &addr_len);
240
+ std::cout << "LISTENING 127.0.0.1:" << ntohs(addr.sin_port)
241
+ << std::endl;
242
+
243
+ /* Connection threads borrow mux, so join them before it ends. */
244
+ std::vector<std::thread> connections;
245
+ while (true) {
246
+ int client_fd = accept(sockfd, nullptr, nullptr);
247
+ if (client_fd < 0)
248
+ break;
249
+ connections.emplace_back(HandleConnection, client_fd,
250
+ mux.get());
251
+ }
252
+
253
+ close(sockfd);
254
+ for (auto &connection : connections)
255
+ connection.join();
256
+ return 0;
221
257
  }
@@ -1,7 +1,7 @@
1
1
  //go:build deps_only
2
2
 
3
3
  // Code generated by protoc-gen-starpc-cpp. DO NOT EDIT.
4
- // protoc-gen-starpc-cpp version: v0.49.21
4
+ // protoc-gen-starpc-cpp version: v0.52.2-0.20260903015026-8f335488f9dc+dirty
5
5
  // source: github.com/aperturerobotics/starpc/mock/mock.proto
6
6
 
7
7
  #include "mock_srpc.pb.hpp"
@@ -1,7 +1,7 @@
1
1
  //go:build deps_only && cgo
2
2
 
3
3
  // Code generated by protoc-gen-starpc-cpp. DO NOT EDIT.
4
- // protoc-gen-starpc-cpp version: v0.49.21
4
+ // protoc-gen-starpc-cpp version: v0.52.2-0.20260903015026-8f335488f9dc+dirty
5
5
  // source: github.com/aperturerobotics/starpc/mock/mock.proto
6
6
 
7
7
  #pragma once
@@ -1,9 +1,9 @@
1
1
  // Code generated by protoc-gen-starpc-rust. DO NOT EDIT.
2
- // protoc-gen-starpc-rust version: v0.49.21
2
+ // protoc-gen-starpc-rust version: (devel)
3
3
  // source: github.com/aperturerobotics/starpc/mock/mock.proto
4
4
 
5
5
  #[allow(unused_imports)]
6
- use starpc::StreamExt;
6
+ use starpc::StreamExt as _;
7
7
 
8
8
  /// Service ID for Mock.
9
9
  pub const MOCK_SERVICE_ID: &str = "e2e.mock.Mock";
@@ -30,7 +30,9 @@ impl<C: starpc::Client> MockClientImpl<C> {
30
30
  #[starpc::async_trait]
31
31
  impl<C: starpc::Client + 'static> MockClient for MockClientImpl<C> {
32
32
  async fn mock_request(&self, request: &MockMsg) -> starpc::Result<MockMsg> {
33
- self.client.exec_call("e2e.mock.Mock", "MockRequest", request).await
33
+ self.client
34
+ .exec_call("e2e.mock.Mock", "MockRequest", request)
35
+ .await
34
36
  }
35
37
  }
36
38
 
@@ -41,9 +43,7 @@ pub trait MockServer: Send + Sync {
41
43
  async fn mock_request(&self, request: MockMsg) -> starpc::Result<MockMsg>;
42
44
  }
43
45
 
44
- const MOCK_METHOD_IDS: &[&str] = &[
45
- "MockRequest",
46
- ];
46
+ const MOCK_METHOD_IDS: &[&str] = &["MockRequest"];
47
47
 
48
48
  /// Handler for Mock.
49
49
  pub struct MockHandler<S: MockServer> {
@@ -53,7 +53,9 @@ pub struct MockHandler<S: MockServer> {
53
53
  impl<S: MockServer + 'static> MockHandler<S> {
54
54
  /// Creates a new handler wrapping the server implementation.
55
55
  pub fn new(server: S) -> Self {
56
- Self { server: std::sync::Arc::new(server) }
56
+ Self {
57
+ server: std::sync::Arc::new(server),
58
+ }
57
59
  }
58
60
 
59
61
  /// Creates a new handler with a shared server.
@@ -76,7 +78,7 @@ impl<S: MockServer + 'static> starpc::Invoker for MockHandler<S> {
76
78
  Ok(r) => r,
77
79
  Err(e) => return (true, Err(e)),
78
80
  };
79
- match self.server.mock_request(request).await {
81
+ match <S as MockServer>::mock_request(self.server.as_ref(), request).await {
80
82
  Ok(response) => {
81
83
  if let Err(e) = stream.msg_send(&response).await {
82
84
  return (true, Err(e));
@@ -100,4 +102,3 @@ impl<S: MockServer + 'static> starpc::Handler for MockHandler<S> {
100
102
  MOCK_METHOD_IDS
101
103
  }
102
104
  }
103
-
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "starpc",
3
- "version": "0.52.1",
3
+ "version": "0.52.2",
4
4
  "description": "Streaming protobuf RPC service protocol over any two-way channel.",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -1,59 +1,4 @@
1
- cmake_minimum_required(VERSION 3.14)
1
+ cmake_minimum_required(VERSION 3.24)
2
2
 
3
- # C++ starpc library
4
- # This builds the core srpc library
5
-
6
- set(STARPC_SOURCES
7
- packet.cpp
8
- common-rpc.cpp
9
- client-rpc.cpp
10
- server-rpc.cpp
11
- mux.cpp
12
- client.cpp
13
- rpcproto.pb.cc
14
- )
15
-
16
- set(STARPC_HEADERS
17
- starpc.hpp
18
- errors.hpp
19
- message.hpp
20
- writer.hpp
21
- packet.hpp
22
- stream.hpp
23
- msg-stream.hpp
24
- common-rpc.hpp
25
- client-rpc.hpp
26
- server-rpc.hpp
27
- invoker.hpp
28
- handler.hpp
29
- mux.hpp
30
- client.hpp
31
- rpcproto.pb.h
32
- )
33
-
34
- add_library(starpc ${STARPC_SOURCES})
35
-
36
- target_include_directories(starpc PUBLIC
37
- ${CMAKE_CURRENT_SOURCE_DIR}/..
38
- ${CMAKE_CURRENT_SOURCE_DIR}
39
- )
40
-
41
- target_compile_features(starpc PUBLIC cxx_std_17)
42
-
43
- # Link protobuf
44
- find_package(Protobuf REQUIRED)
45
- target_link_libraries(starpc PUBLIC protobuf::libprotobuf)
46
-
47
- # Link threads
48
- find_package(Threads REQUIRED)
49
- target_link_libraries(starpc PUBLIC Threads::Threads)
50
-
51
- # Install targets
52
- install(TARGETS starpc
53
- ARCHIVE DESTINATION lib
54
- LIBRARY DESTINATION lib
55
- )
56
-
57
- install(FILES ${STARPC_HEADERS}
58
- DESTINATION include/srpc
59
- )
3
+ # Keep the historical subdirectory entry point on the same complete target.
4
+ add_subdirectory(${CMAKE_CURRENT_LIST_DIR}/.. ${CMAKE_CURRENT_BINARY_DIR}/starpc)
@@ -0,0 +1,69 @@
1
+ # Starpc C++
2
+
3
+ Starpc provides generated C++ clients and handlers for unary and streaming
4
+ protobuf RPCs. It uses the same packets as the Go, TypeScript, Rust, and Python
5
+ implementations. C++20 and the protobuf runtime matching the generated headers
6
+ are required.
7
+
8
+ ## Build
9
+
10
+ ```sh
11
+ go mod vendor
12
+ cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
13
+ cmake --build build --parallel
14
+ ctest --test-dir build --output-on-failure --timeout 60
15
+ ```
16
+
17
+ A parent project can use `add_subdirectory(path/to/starpc)` and link
18
+ `starpc::starpc`. An existing `protobuf::libprotobuf` target is reused, including
19
+ its include paths and compile definitions. Otherwise, set
20
+ `STARPC_USE_SYSTEM_PROTOBUF=ON` to find an installed protobuf package, or vendor
21
+ Go dependencies before configuring. Tests and examples default to off when
22
+ Starpc is a subdirectory. The library includes the nested RPC messages even
23
+ when examples are disabled.
24
+
25
+ Generate service bindings with `protoc-gen-starpc-cpp`. The generated streaming
26
+ wrappers expose `Send`, `Recv`, `Close`, `CloseSend`, and `StopToken` as applicable.
27
+ See `echo/echo.proto` and its generated bindings for each RPC shape.
28
+
29
+ ## Connect a transport
30
+
31
+ One Starpc call uses one ordered, bidirectional packet stream. A streaming RPC
32
+ can carry successive commands and events over a single process connection.
33
+ Independent concurrent calls require separate streams from the transport.
34
+
35
+ A C++ server lends an `Invoker` and `PacketWriter` to `ServerRPC`, then feeds
36
+ received packets to `HandlePacketData`. Call `HandleStreamClose` when input ends,
37
+ including errors. Serialize those input callbacks. Destruction cancels the call
38
+ and joins its handler before the borrowed objects can be released.
39
+
40
+ For a client, supply an `OpenStreamFunc` to `NewClient`. Its returned writer must
41
+ keep receive callbacks alive until destruction. `Close` must interrupt blocked
42
+ transport reads and writes without waiting for those callbacks; destruction must
43
+ join any receive thread. This permits a callback to close its own transport.
44
+
45
+ Packet writers serialize their writes and allow `Close` concurrently with a
46
+ write. Close is idempotent. A byte-stream transport uses a four-byte unsigned
47
+ little-endian length followed by the serialized `srpc.Packet`, matching Go's
48
+ `PacketReadWriter`. Reject empty frames and frames larger than 10,000,000 bytes
49
+ before allocating their bodies. The process-pipe integration test demonstrates
50
+ this framing with a Go client and a C++ child process.
51
+
52
+ ## Cancellation and completion
53
+
54
+ `CloseSend` ends outgoing messages while allowing incoming replies. Once the
55
+ request is written, the half-close is only a notice: it fails when the call
56
+ already ended, so generated callers ignore its error and read the outcome with
57
+ `MsgRecv`. Explicit remote completion becomes EOF after queued messages are
58
+ read. A disconnected
59
+ transport without completion returns `ClosedBeforeCompletion`. Remote errors
60
+ return `RemoteError`; `CommonRPC::RemoteErrorMessage` retains the peer's text.
61
+
62
+ Handlers performing work outside `Recv` must observe their stream's `StopToken`.
63
+ Destroying a server waits for its handler to return; it cannot forcibly terminate
64
+ application code that ignores cancellation. Dropping a client stream closes its
65
+ transport and releases its receive thread. Nested RPCs release their service
66
+ only after the nested handler returns.
67
+
68
+ An unread C++ call is limited to 4 MiB or 1,024 queued messages, including empty
69
+ messages. Exceeding either bound ends the call with `ResourceExhausted`.