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.
- package/README.md +37 -8
- 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/common-rpc.d.ts +5 -0
- package/dist/srpc/common-rpc.js +37 -8
- 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/echo/Cargo.toml +8 -0
- package/echo/echo_e2e_test.cpp +542 -985
- 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 +4 -1
- package/echo/half_close_test.rs +178 -0
- package/echo/integration_server.rs +14 -21
- package/echo/main.rs +28 -48
- package/go.mod +1 -1
- package/go.sum +2 -18
- 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/mock/mock_srpc.pb.cpp +1 -1
- package/mock/mock_srpc.pb.hpp +1 -1
- package/mock/mock_srpc.pb.rs +10 -9
- package/package.json +1 -1
- package/srpc/CMakeLists.txt +3 -58
- package/srpc/README-cpp.md +69 -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_half_close_test.cc +331 -0
- package/srpc/client_invoker.rs +75 -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.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.cpp +60 -58
- package/srpc/packet.hpp +35 -20
- package/srpc/packet.rs +8 -27
- package/srpc/rpc.rs +332 -115
- package/srpc/rpc_lifecycle_test.cc +333 -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/starpc.hpp +4 -2
- package/srpc/stream.go +4 -0
- package/srpc/stream.hpp +95 -34
- package/srpc/stream.rs +7 -2
- 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/echo/echo_srpc.pb.cpp
CHANGED
|
@@ -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.
|
|
4
|
+
// protoc-gen-starpc-cpp version: v0.52.2-0.20260927183858-4d0f9be5aa9d+dirty
|
|
5
5
|
// source: github.com/aperturerobotics/starpc/echo/echo.proto
|
|
6
6
|
|
|
7
7
|
#include "echo_srpc.pb.hpp"
|
|
@@ -17,10 +17,8 @@ std::pair<std::unique_ptr<SRPCEchoer_EchoServerStreamClient>, starpc::Error> SRP
|
|
|
17
17
|
if (err != starpc::Error::OK) {
|
|
18
18
|
return {nullptr, err};
|
|
19
19
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
return {nullptr, err};
|
|
23
|
-
}
|
|
20
|
+
// A failed half-close means the call ended; MsgRecv reports its outcome.
|
|
21
|
+
(void)strm->CloseSend();
|
|
24
22
|
return {std::make_unique<SRPCEchoer_EchoServerStreamClient>(std::move(strm)), starpc::Error::OK};
|
|
25
23
|
}
|
|
26
24
|
|
package/echo/echo_srpc.pb.go
CHANGED
|
@@ -63,9 +63,8 @@ func (c *srpcEchoerClient) EchoServerStream(ctx context.Context, in *EchoMsg) (S
|
|
|
63
63
|
return nil, err
|
|
64
64
|
}
|
|
65
65
|
strm := &srpcEchoer_EchoServerStreamClient{stream}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
}
|
|
66
|
+
// A failed half-close means the call ended; MsgRecv reports its outcome.
|
|
67
|
+
_ = strm.CloseSend()
|
|
69
68
|
return strm, nil
|
|
70
69
|
}
|
|
71
70
|
|
|
@@ -118,9 +117,8 @@ func (x *srpcEchoer_EchoClientStreamClient) Send(m *EchoMsg) error {
|
|
|
118
117
|
}
|
|
119
118
|
|
|
120
119
|
func (x *srpcEchoer_EchoClientStreamClient) CloseAndRecv() (*EchoMsg, error) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
120
|
+
// A failed half-close means the call ended; MsgRecv reports its outcome.
|
|
121
|
+
_ = x.CloseSend()
|
|
124
122
|
m := new(EchoMsg)
|
|
125
123
|
if err := x.MsgRecv(m); err != nil {
|
|
126
124
|
return nil, err
|
|
@@ -129,9 +127,8 @@ func (x *srpcEchoer_EchoClientStreamClient) CloseAndRecv() (*EchoMsg, error) {
|
|
|
129
127
|
}
|
|
130
128
|
|
|
131
129
|
func (x *srpcEchoer_EchoClientStreamClient) CloseAndMsgRecv(m *EchoMsg) error {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
}
|
|
130
|
+
// A failed half-close means the call ended; MsgRecv reports its outcome.
|
|
131
|
+
_ = x.CloseSend()
|
|
135
132
|
return x.MsgRecv(m)
|
|
136
133
|
}
|
|
137
134
|
|
package/echo/echo_srpc.pb.hpp
CHANGED
|
@@ -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.
|
|
4
|
+
// protoc-gen-starpc-cpp version: v0.52.2-0.20260927183858-4d0f9be5aa9d+dirty
|
|
5
5
|
// source: github.com/aperturerobotics/starpc/echo/echo.proto
|
|
6
6
|
|
|
7
7
|
#pragma once
|
|
@@ -146,6 +146,8 @@ class SRPCEchoer_EchoServerStreamClient {
|
|
|
146
146
|
|
|
147
147
|
starpc::Error CloseSend() { return strm_->CloseSend(); }
|
|
148
148
|
starpc::Error Close() { return strm_->Close(); }
|
|
149
|
+
std::stop_token StopToken() const { return strm_->StopToken(); }
|
|
150
|
+
std::string RemoteErrorMessage() const { return strm_->RemoteErrorMessage(); }
|
|
149
151
|
|
|
150
152
|
private:
|
|
151
153
|
std::unique_ptr<starpc::Stream> strm_;
|
|
@@ -155,6 +157,9 @@ class SRPCEchoer_EchoServerStreamClient {
|
|
|
155
157
|
class SRPCEchoer_EchoServerStreamStream {
|
|
156
158
|
public:
|
|
157
159
|
explicit SRPCEchoer_EchoServerStreamStream(starpc::Stream* strm) : strm_(strm) {}
|
|
160
|
+
std::stop_token StopToken() const { return strm_->StopToken(); }
|
|
161
|
+
starpc::Error Close() { return strm_->Close(); }
|
|
162
|
+
starpc::Error CloseSend() { return strm_->CloseSend(); }
|
|
158
163
|
|
|
159
164
|
starpc::Error Send(const echo::EchoMsg& msg) {
|
|
160
165
|
return strm_->MsgSend(msg);
|
|
@@ -180,13 +185,15 @@ class SRPCEchoer_EchoClientStreamClient {
|
|
|
180
185
|
}
|
|
181
186
|
|
|
182
187
|
starpc::Error CloseAndRecv(echo::EchoMsg* msg) {
|
|
183
|
-
|
|
184
|
-
|
|
188
|
+
// A failed half-close means the call ended; MsgRecv reports its outcome.
|
|
189
|
+
(void)strm_->CloseSend();
|
|
185
190
|
return strm_->MsgRecv(msg);
|
|
186
191
|
}
|
|
187
192
|
|
|
188
193
|
starpc::Error CloseSend() { return strm_->CloseSend(); }
|
|
189
194
|
starpc::Error Close() { return strm_->Close(); }
|
|
195
|
+
std::stop_token StopToken() const { return strm_->StopToken(); }
|
|
196
|
+
std::string RemoteErrorMessage() const { return strm_->RemoteErrorMessage(); }
|
|
190
197
|
|
|
191
198
|
private:
|
|
192
199
|
std::unique_ptr<starpc::Stream> strm_;
|
|
@@ -196,6 +203,9 @@ class SRPCEchoer_EchoClientStreamClient {
|
|
|
196
203
|
class SRPCEchoer_EchoClientStreamStream {
|
|
197
204
|
public:
|
|
198
205
|
explicit SRPCEchoer_EchoClientStreamStream(starpc::Stream* strm) : strm_(strm) {}
|
|
206
|
+
std::stop_token StopToken() const { return strm_->StopToken(); }
|
|
207
|
+
starpc::Error Close() { return strm_->Close(); }
|
|
208
|
+
starpc::Error CloseSend() { return strm_->CloseSend(); }
|
|
199
209
|
|
|
200
210
|
starpc::Error Recv(echo::EchoMsg* msg) {
|
|
201
211
|
return strm_->MsgRecv(msg);
|
|
@@ -220,6 +230,8 @@ class SRPCEchoer_EchoBidiStreamClient {
|
|
|
220
230
|
|
|
221
231
|
starpc::Error CloseSend() { return strm_->CloseSend(); }
|
|
222
232
|
starpc::Error Close() { return strm_->Close(); }
|
|
233
|
+
std::stop_token StopToken() const { return strm_->StopToken(); }
|
|
234
|
+
std::string RemoteErrorMessage() const { return strm_->RemoteErrorMessage(); }
|
|
223
235
|
|
|
224
236
|
private:
|
|
225
237
|
std::unique_ptr<starpc::Stream> strm_;
|
|
@@ -229,6 +241,9 @@ class SRPCEchoer_EchoBidiStreamClient {
|
|
|
229
241
|
class SRPCEchoer_EchoBidiStreamStream {
|
|
230
242
|
public:
|
|
231
243
|
explicit SRPCEchoer_EchoBidiStreamStream(starpc::Stream* strm) : strm_(strm) {}
|
|
244
|
+
std::stop_token StopToken() const { return strm_->StopToken(); }
|
|
245
|
+
starpc::Error Close() { return strm_->Close(); }
|
|
246
|
+
starpc::Error CloseSend() { return strm_->CloseSend(); }
|
|
232
247
|
|
|
233
248
|
starpc::Error Send(const echo::EchoMsg& msg) {
|
|
234
249
|
return strm_->MsgSend(msg);
|
|
@@ -263,6 +278,8 @@ class SRPCEchoer_RpcStreamClient {
|
|
|
263
278
|
|
|
264
279
|
starpc::Error CloseSend() { return strm_->CloseSend(); }
|
|
265
280
|
starpc::Error Close() { return strm_->Close(); }
|
|
281
|
+
std::stop_token StopToken() const { return strm_->StopToken(); }
|
|
282
|
+
std::string RemoteErrorMessage() const { return strm_->RemoteErrorMessage(); }
|
|
266
283
|
|
|
267
284
|
private:
|
|
268
285
|
std::unique_ptr<starpc::Stream> strm_;
|
|
@@ -272,6 +289,9 @@ class SRPCEchoer_RpcStreamClient {
|
|
|
272
289
|
class SRPCEchoer_RpcStreamStream {
|
|
273
290
|
public:
|
|
274
291
|
explicit SRPCEchoer_RpcStreamStream(starpc::Stream* strm) : strm_(strm) {}
|
|
292
|
+
std::stop_token StopToken() const { return strm_->StopToken(); }
|
|
293
|
+
starpc::Error Close() { return strm_->Close(); }
|
|
294
|
+
starpc::Error CloseSend() { return strm_->CloseSend(); }
|
|
275
295
|
|
|
276
296
|
starpc::Error Send(const rpcstream::RpcStreamPacket& msg) {
|
|
277
297
|
return strm_->MsgSend(msg);
|
package/echo/echo_srpc.pb.rs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// Code generated by protoc-gen-starpc-rust. DO NOT EDIT.
|
|
2
|
-
// protoc-gen-starpc-rust version:
|
|
2
|
+
// protoc-gen-starpc-rust version: (devel)
|
|
3
3
|
// source: github.com/aperturerobotics/starpc/echo/echo.proto
|
|
4
4
|
|
|
5
5
|
#[allow(unused_imports)]
|
|
6
|
-
use starpc::StreamExt;
|
|
6
|
+
use starpc::StreamExt as _;
|
|
7
7
|
|
|
8
8
|
/// Service ID for Echoer.
|
|
9
9
|
pub const ECHOER_SERVICE_ID: &str = "echo.Echoer";
|
|
@@ -62,7 +62,10 @@ pub trait EchoerClient: Send + Sync {
|
|
|
62
62
|
/// Echo.
|
|
63
63
|
async fn echo(&self, request: &EchoMsg) -> starpc::Result<EchoMsg>;
|
|
64
64
|
/// EchoServerStream.
|
|
65
|
-
async fn echo_server_stream(
|
|
65
|
+
async fn echo_server_stream(
|
|
66
|
+
&self,
|
|
67
|
+
request: &EchoMsg,
|
|
68
|
+
) -> starpc::Result<Box<dyn EchoerEchoServerStreamStream>>;
|
|
66
69
|
/// EchoClientStream.
|
|
67
70
|
async fn echo_client_stream(&self) -> starpc::Result<Box<dyn EchoerEchoClientStreamStream>>;
|
|
68
71
|
/// EchoBidiStream.
|
|
@@ -90,27 +93,45 @@ impl<C: starpc::Client + 'static> EchoerClient for EchoerClientImpl<C> {
|
|
|
90
93
|
async fn echo(&self, request: &EchoMsg) -> starpc::Result<EchoMsg> {
|
|
91
94
|
self.client.exec_call("echo.Echoer", "Echo", request).await
|
|
92
95
|
}
|
|
93
|
-
async fn echo_server_stream(
|
|
96
|
+
async fn echo_server_stream(
|
|
97
|
+
&self,
|
|
98
|
+
request: &EchoMsg,
|
|
99
|
+
) -> starpc::Result<Box<dyn EchoerEchoServerStreamStream>> {
|
|
94
100
|
use starpc::ProstMessage;
|
|
95
101
|
let data = request.encode_to_vec();
|
|
96
|
-
let stream = self
|
|
97
|
-
|
|
102
|
+
let stream = self
|
|
103
|
+
.client
|
|
104
|
+
.new_stream("echo.Echoer", "EchoServerStream", Some(&data))
|
|
105
|
+
.await?;
|
|
106
|
+
// A failed half-close means the call ended; msg_recv reports its outcome.
|
|
107
|
+
let _ = stream.close_send().await;
|
|
98
108
|
Ok(Box::new(EchoerEchoServerStreamStreamImpl { stream }))
|
|
99
109
|
}
|
|
100
110
|
async fn echo_client_stream(&self) -> starpc::Result<Box<dyn EchoerEchoClientStreamStream>> {
|
|
101
|
-
let stream = self
|
|
111
|
+
let stream = self
|
|
112
|
+
.client
|
|
113
|
+
.new_stream("echo.Echoer", "EchoClientStream", None)
|
|
114
|
+
.await?;
|
|
102
115
|
Ok(Box::new(EchoerEchoClientStreamStreamImpl { stream }))
|
|
103
116
|
}
|
|
104
117
|
async fn echo_bidi_stream(&self) -> starpc::Result<Box<dyn EchoerEchoBidiStreamStream>> {
|
|
105
|
-
let stream = self
|
|
118
|
+
let stream = self
|
|
119
|
+
.client
|
|
120
|
+
.new_stream("echo.Echoer", "EchoBidiStream", None)
|
|
121
|
+
.await?;
|
|
106
122
|
Ok(Box::new(EchoerEchoBidiStreamStreamImpl { stream }))
|
|
107
123
|
}
|
|
108
124
|
async fn rpc_stream(&self) -> starpc::Result<Box<dyn EchoerRpcStreamStream>> {
|
|
109
|
-
let stream = self
|
|
125
|
+
let stream = self
|
|
126
|
+
.client
|
|
127
|
+
.new_stream("echo.Echoer", "RpcStream", None)
|
|
128
|
+
.await?;
|
|
110
129
|
Ok(Box::new(EchoerRpcStreamStreamImpl { stream }))
|
|
111
130
|
}
|
|
112
131
|
async fn do_nothing(&self, request: &Empty) -> starpc::Result<Empty> {
|
|
113
|
-
self.client
|
|
132
|
+
self.client
|
|
133
|
+
.exec_call("echo.Echoer", "DoNothing", request)
|
|
134
|
+
.await
|
|
114
135
|
}
|
|
115
136
|
}
|
|
116
137
|
|
|
@@ -144,7 +165,8 @@ impl EchoerEchoClientStreamStream for EchoerEchoClientStreamStreamImpl {
|
|
|
144
165
|
self.stream.msg_send(msg).await
|
|
145
166
|
}
|
|
146
167
|
async fn close_and_recv(&self) -> starpc::Result<EchoMsg> {
|
|
147
|
-
|
|
168
|
+
// A failed half-close means the call ended; msg_recv reports its outcome.
|
|
169
|
+
let _ = self.stream.close_send().await;
|
|
148
170
|
self.stream.msg_recv().await
|
|
149
171
|
}
|
|
150
172
|
}
|
|
@@ -193,9 +215,13 @@ impl EchoerRpcStreamStream for EchoerRpcStreamStreamImpl {
|
|
|
193
215
|
#[starpc::async_trait]
|
|
194
216
|
pub trait EchoerServer: Send + Sync {
|
|
195
217
|
/// Echo.
|
|
196
|
-
async fn echo(&self, request: EchoMsg) -> starpc::Result<EchoMsg>;
|
|
218
|
+
async fn echo(&self, context: &starpc::Context, request: EchoMsg) -> starpc::Result<EchoMsg>;
|
|
197
219
|
/// EchoServerStream.
|
|
198
|
-
async fn echo_server_stream(
|
|
220
|
+
async fn echo_server_stream(
|
|
221
|
+
&self,
|
|
222
|
+
request: EchoMsg,
|
|
223
|
+
stream: Box<dyn starpc::Stream>,
|
|
224
|
+
) -> starpc::Result<()>;
|
|
199
225
|
/// EchoClientStream.
|
|
200
226
|
async fn echo_client_stream(&self, stream: &dyn starpc::Stream) -> starpc::Result<EchoMsg>;
|
|
201
227
|
/// EchoBidiStream.
|
|
@@ -203,7 +229,7 @@ pub trait EchoerServer: Send + Sync {
|
|
|
203
229
|
/// RpcStream.
|
|
204
230
|
async fn rpc_stream(&self, stream: Box<dyn starpc::Stream>) -> starpc::Result<()>;
|
|
205
231
|
/// DoNothing.
|
|
206
|
-
async fn do_nothing(&self, request: Empty) -> starpc::Result<Empty>;
|
|
232
|
+
async fn do_nothing(&self, context: &starpc::Context, request: Empty) -> starpc::Result<Empty>;
|
|
207
233
|
}
|
|
208
234
|
|
|
209
235
|
const ECHOER_METHOD_IDS: &[&str] = &[
|
|
@@ -223,7 +249,9 @@ pub struct EchoerHandler<S: EchoerServer> {
|
|
|
223
249
|
impl<S: EchoerServer + 'static> EchoerHandler<S> {
|
|
224
250
|
/// Creates a new handler wrapping the server implementation.
|
|
225
251
|
pub fn new(server: S) -> Self {
|
|
226
|
-
Self {
|
|
252
|
+
Self {
|
|
253
|
+
server: std::sync::Arc::new(server),
|
|
254
|
+
}
|
|
227
255
|
}
|
|
228
256
|
|
|
229
257
|
/// Creates a new handler with a shared server.
|
|
@@ -246,7 +274,7 @@ impl<S: EchoerServer + 'static> starpc::Invoker for EchoerHandler<S> {
|
|
|
246
274
|
Ok(r) => r,
|
|
247
275
|
Err(e) => return (true, Err(e)),
|
|
248
276
|
};
|
|
249
|
-
match self.server.
|
|
277
|
+
match <S as EchoerServer>::echo(self.server.as_ref(), stream.context(), request).await {
|
|
250
278
|
Ok(response) => {
|
|
251
279
|
if let Err(e) = stream.msg_send(&response).await {
|
|
252
280
|
return (true, Err(e));
|
|
@@ -261,10 +289,16 @@ impl<S: EchoerServer + 'static> starpc::Invoker for EchoerHandler<S> {
|
|
|
261
289
|
Ok(r) => r,
|
|
262
290
|
Err(e) => return (true, Err(e)),
|
|
263
291
|
};
|
|
264
|
-
(
|
|
292
|
+
(
|
|
293
|
+
true,
|
|
294
|
+
<S as EchoerServer>::echo_server_stream(self.server.as_ref(), request, stream)
|
|
295
|
+
.await,
|
|
296
|
+
)
|
|
265
297
|
}
|
|
266
298
|
"EchoClientStream" => {
|
|
267
|
-
match self.server.
|
|
299
|
+
match <S as EchoerServer>::echo_client_stream(self.server.as_ref(), stream.as_ref())
|
|
300
|
+
.await
|
|
301
|
+
{
|
|
268
302
|
Ok(response) => {
|
|
269
303
|
if let Err(e) = stream.msg_send(&response).await {
|
|
270
304
|
return (true, Err(e));
|
|
@@ -274,18 +308,20 @@ impl<S: EchoerServer + 'static> starpc::Invoker for EchoerHandler<S> {
|
|
|
274
308
|
Err(e) => (true, Err(e)),
|
|
275
309
|
}
|
|
276
310
|
}
|
|
277
|
-
"EchoBidiStream" =>
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
311
|
+
"EchoBidiStream" => (
|
|
312
|
+
true,
|
|
313
|
+
<S as EchoerServer>::echo_bidi_stream(self.server.as_ref(), stream).await,
|
|
314
|
+
),
|
|
315
|
+
"RpcStream" => (
|
|
316
|
+
true,
|
|
317
|
+
<S as EchoerServer>::rpc_stream(self.server.as_ref(), stream).await,
|
|
318
|
+
),
|
|
283
319
|
"DoNothing" => {
|
|
284
320
|
let request: Empty = match stream.msg_recv().await {
|
|
285
321
|
Ok(r) => r,
|
|
286
322
|
Err(e) => return (true, Err(e)),
|
|
287
323
|
};
|
|
288
|
-
match self.server.
|
|
324
|
+
match <S as EchoerServer>::do_nothing(self.server.as_ref(), stream.context(), request).await {
|
|
289
325
|
Ok(response) => {
|
|
290
326
|
if let Err(e) = stream.msg_send(&response).await {
|
|
291
327
|
return (true, Err(e));
|
|
@@ -309,4 +345,3 @@ impl<S: EchoerServer + 'static> starpc::Handler for EchoerHandler<S> {
|
|
|
309
345
|
ECHOER_METHOD_IDS
|
|
310
346
|
}
|
|
311
347
|
}
|
|
312
|
-
|
package/echo/echo_srpc.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import contextlib
|
|
3
4
|
from collections.abc import AsyncIterable, AsyncIterator
|
|
4
5
|
from typing import Protocol
|
|
5
6
|
|
|
@@ -117,7 +118,9 @@ class EchoerClient:
|
|
|
117
118
|
try:
|
|
118
119
|
async for request in requests:
|
|
119
120
|
await call.send(request.SerializeToString(deterministic=True))
|
|
120
|
-
|
|
121
|
+
# A failed half-close means the call ended; receive reports its outcome.
|
|
122
|
+
with contextlib.suppress(OSError):
|
|
123
|
+
await call.finish()
|
|
121
124
|
data = await call.receive()
|
|
122
125
|
if data is None:
|
|
123
126
|
raise CallProtocolError("missing client-stream response")
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
//! Half-close tests for the generated Rust callers.
|
|
2
|
+
//!
|
|
3
|
+
//! The half-close is only a notice once the request is written: a caller that
|
|
4
|
+
//! half-closes after the remote ended the call must still read the buffered
|
|
5
|
+
//! responses and the call's outcome.
|
|
6
|
+
|
|
7
|
+
#[allow(dead_code)]
|
|
8
|
+
mod gen;
|
|
9
|
+
|
|
10
|
+
use std::sync::Arc;
|
|
11
|
+
|
|
12
|
+
use async_trait::async_trait;
|
|
13
|
+
use futures::StreamExt as _;
|
|
14
|
+
use prost::Message;
|
|
15
|
+
use starpc::client::OpenStream;
|
|
16
|
+
use starpc::testing::{create_pipe_default, create_test_pair, SingleInMemoryOpener};
|
|
17
|
+
use starpc::{Client, Error, Mux, PacketCodec, Result, Server, SrpcClient, Stream, StreamExt};
|
|
18
|
+
use tokio::task::JoinHandle;
|
|
19
|
+
use tokio_util::codec::FramedRead;
|
|
20
|
+
|
|
21
|
+
use gen::{EchoMsg, EchoerClient, EchoerClientImpl, EchoerHandler, EchoerServer};
|
|
22
|
+
|
|
23
|
+
const BODY_TXT: &str = "hello world via starpc half-close test";
|
|
24
|
+
|
|
25
|
+
/// Answers each streaming call once and completes it.
|
|
26
|
+
struct OnceEchoServer;
|
|
27
|
+
|
|
28
|
+
#[async_trait]
|
|
29
|
+
impl EchoerServer for OnceEchoServer {
|
|
30
|
+
async fn echo(&self, _context: &starpc::Context, request: EchoMsg) -> Result<EchoMsg> {
|
|
31
|
+
Ok(request)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
async fn echo_server_stream(&self, request: EchoMsg, stream: Box<dyn Stream>) -> Result<()> {
|
|
35
|
+
stream.msg_send(&request).await
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async fn echo_client_stream(&self, stream: &dyn Stream) -> Result<EchoMsg> {
|
|
39
|
+
// Reply to the first message without waiting for the half-close.
|
|
40
|
+
stream.msg_recv().await
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async fn echo_bidi_stream(&self, _stream: Box<dyn Stream>) -> Result<()> {
|
|
44
|
+
Err(Error::Unimplemented)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async fn rpc_stream(&self, _stream: Box<dyn Stream>) -> Result<()> {
|
|
48
|
+
Err(Error::Unimplemented)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async fn do_nothing(
|
|
52
|
+
&self,
|
|
53
|
+
_context: &starpc::Context,
|
|
54
|
+
request: gen::Empty,
|
|
55
|
+
) -> Result<gen::Empty> {
|
|
56
|
+
Ok(request)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/// Returns each new stream only after its transport has closed, so the
|
|
61
|
+
/// generated caller half-closes after the remote already ended the call.
|
|
62
|
+
struct EndedClient<T: OpenStream> {
|
|
63
|
+
client: SrpcClient<T>,
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
#[async_trait]
|
|
67
|
+
impl<T: OpenStream + 'static> Client for EndedClient<T> {
|
|
68
|
+
async fn exec_call<I, O>(&self, service: &str, method: &str, input: &I) -> Result<O>
|
|
69
|
+
where
|
|
70
|
+
I: Message + Send + Sync,
|
|
71
|
+
O: Message + Default,
|
|
72
|
+
{
|
|
73
|
+
self.client.exec_call(service, method, input).await
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async fn new_stream(
|
|
77
|
+
&self,
|
|
78
|
+
service: &str,
|
|
79
|
+
method: &str,
|
|
80
|
+
first_msg: Option<&[u8]>,
|
|
81
|
+
) -> Result<Box<dyn Stream>> {
|
|
82
|
+
let stream = self.client.new_stream(service, method, first_msg).await?;
|
|
83
|
+
// The transport close handler cancels the context after it settles
|
|
84
|
+
// the call's outcome and closes the writer.
|
|
85
|
+
stream.context().cancelled().await;
|
|
86
|
+
Ok(stream)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/// Returns one transport opener and the server task that its caller must join.
|
|
91
|
+
fn serve_once_echo() -> (SingleInMemoryOpener, JoinHandle<Result<()>>) {
|
|
92
|
+
let mux = Arc::new(Mux::new());
|
|
93
|
+
mux.register(Arc::new(EchoerHandler::new(OnceEchoServer)))
|
|
94
|
+
.unwrap();
|
|
95
|
+
let (opener, server_stream) = create_test_pair();
|
|
96
|
+
let server = Server::with_arc(mux);
|
|
97
|
+
let task = tokio::spawn(async move { server.handle_stream(server_stream).await });
|
|
98
|
+
(opener, task)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/// Returns a transport that closes after CallStart and the peer task to join after observation.
|
|
102
|
+
fn drop_after_request() -> (SingleInMemoryOpener, JoinHandle<()>) {
|
|
103
|
+
// Use the actual framing boundary to order peer closure after request admission.
|
|
104
|
+
let (client_stream, server_stream) = create_pipe_default();
|
|
105
|
+
let task = tokio::spawn(async move {
|
|
106
|
+
let mut framed = FramedRead::new(server_stream, PacketCodec::new());
|
|
107
|
+
let _ = framed.next().await;
|
|
108
|
+
});
|
|
109
|
+
(SingleInMemoryOpener::new(client_stream), task)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
#[tokio::test]
|
|
113
|
+
async fn server_stream_completed_before_close_send() {
|
|
114
|
+
// Complete the server stream before the generated caller half-closes.
|
|
115
|
+
let (opener, server) = serve_once_echo();
|
|
116
|
+
let client = EchoerClientImpl::new(EndedClient {
|
|
117
|
+
client: SrpcClient::new(opener),
|
|
118
|
+
});
|
|
119
|
+
let request = EchoMsg {
|
|
120
|
+
body: BODY_TXT.into(),
|
|
121
|
+
};
|
|
122
|
+
let stream = client
|
|
123
|
+
.echo_server_stream(&request)
|
|
124
|
+
.await
|
|
125
|
+
.expect("server stream discarded after remote completion");
|
|
126
|
+
|
|
127
|
+
// Read the buffered response, then the clean completion.
|
|
128
|
+
let msg = stream.recv().await.expect("buffered response lost");
|
|
129
|
+
assert_eq!(msg.body, BODY_TXT);
|
|
130
|
+
let end = stream.recv().await;
|
|
131
|
+
assert!(
|
|
132
|
+
matches!(end, Err(Error::StreamClosed)),
|
|
133
|
+
"expected StreamClosed after completion, got {end:?}"
|
|
134
|
+
);
|
|
135
|
+
server.await.unwrap().unwrap();
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
#[tokio::test]
|
|
139
|
+
async fn client_stream_completed_before_close_send() {
|
|
140
|
+
let (opener, server) = serve_once_echo();
|
|
141
|
+
let client = EchoerClientImpl::new(SrpcClient::new(opener));
|
|
142
|
+
let stream = client.echo_client_stream().await.unwrap();
|
|
143
|
+
|
|
144
|
+
// Send the one request and wait for the call to end.
|
|
145
|
+
let request = EchoMsg {
|
|
146
|
+
body: BODY_TXT.into(),
|
|
147
|
+
};
|
|
148
|
+
stream.send(&request).await.unwrap();
|
|
149
|
+
stream.context().cancelled().await;
|
|
150
|
+
|
|
151
|
+
// The half-close fails, but the reply is still readable.
|
|
152
|
+
let msg = stream.close_and_recv().await.expect("buffered reply lost");
|
|
153
|
+
assert_eq!(msg.body, BODY_TXT);
|
|
154
|
+
server.await.unwrap().unwrap();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
#[tokio::test]
|
|
158
|
+
async fn server_stream_transport_failure_before_close_send() {
|
|
159
|
+
let (opener, server) = drop_after_request();
|
|
160
|
+
let client = EchoerClientImpl::new(EndedClient {
|
|
161
|
+
client: SrpcClient::new(opener),
|
|
162
|
+
});
|
|
163
|
+
let request = EchoMsg {
|
|
164
|
+
body: BODY_TXT.into(),
|
|
165
|
+
};
|
|
166
|
+
let stream = client
|
|
167
|
+
.echo_server_stream(&request)
|
|
168
|
+
.await
|
|
169
|
+
.expect("server stream discarded after transport failure");
|
|
170
|
+
|
|
171
|
+
// Recv reports that the call ended without a verdict.
|
|
172
|
+
let end = stream.recv().await;
|
|
173
|
+
assert!(
|
|
174
|
+
matches!(end, Err(Error::ClosedBeforeCompletion)),
|
|
175
|
+
"expected ClosedBeforeCompletion, got {end:?}"
|
|
176
|
+
);
|
|
177
|
+
server.await.unwrap();
|
|
178
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
//! Echo transport endpoint used by cross-language integration checks.
|
|
2
|
+
|
|
1
3
|
#[allow(dead_code)]
|
|
2
4
|
mod gen;
|
|
3
5
|
|
|
@@ -9,21 +11,16 @@ use tokio::net::TcpListener;
|
|
|
9
11
|
|
|
10
12
|
use gen::{EchoMsg, EchoerHandler, EchoerServer, Empty};
|
|
11
13
|
|
|
14
|
+
/// Implements each echo call shape through the supplied transport stream.
|
|
12
15
|
struct EchoServerImpl;
|
|
13
16
|
|
|
14
17
|
#[async_trait]
|
|
15
18
|
impl EchoerServer for EchoServerImpl {
|
|
16
|
-
async fn echo(&self, request: EchoMsg) -> Result<EchoMsg> {
|
|
17
|
-
Ok(EchoMsg {
|
|
18
|
-
body: request.body,
|
|
19
|
-
})
|
|
19
|
+
async fn echo(&self, _context: &starpc::Context, request: EchoMsg) -> Result<EchoMsg> {
|
|
20
|
+
Ok(EchoMsg { body: request.body })
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
async fn echo_server_stream(
|
|
23
|
-
&self,
|
|
24
|
-
request: EchoMsg,
|
|
25
|
-
stream: Box<dyn Stream>,
|
|
26
|
-
) -> Result<()> {
|
|
23
|
+
async fn echo_server_stream(&self, request: EchoMsg, stream: Box<dyn Stream>) -> Result<()> {
|
|
27
24
|
for _ in 0..5 {
|
|
28
25
|
let response = EchoMsg {
|
|
29
26
|
body: request.body.clone(),
|
|
@@ -34,10 +31,7 @@ impl EchoerServer for EchoServerImpl {
|
|
|
34
31
|
}
|
|
35
32
|
|
|
36
33
|
async fn echo_client_stream(&self, stream: &dyn Stream) -> Result<EchoMsg> {
|
|
37
|
-
|
|
38
|
-
Ok(msg) => Ok(msg),
|
|
39
|
-
Err(e) => Err(e),
|
|
40
|
-
}
|
|
34
|
+
stream.msg_recv().await
|
|
41
35
|
}
|
|
42
36
|
|
|
43
37
|
async fn echo_bidi_stream(&self, stream: Box<dyn Stream>) -> Result<()> {
|
|
@@ -63,11 +57,12 @@ impl EchoerServer for EchoServerImpl {
|
|
|
63
57
|
Err(Error::Unimplemented)
|
|
64
58
|
}
|
|
65
59
|
|
|
66
|
-
async fn do_nothing(&self, _request: Empty) -> Result<Empty> {
|
|
60
|
+
async fn do_nothing(&self, _context: &starpc::Context, _request: Empty) -> Result<Empty> {
|
|
67
61
|
Ok(Empty {})
|
|
68
62
|
}
|
|
69
63
|
}
|
|
70
64
|
|
|
65
|
+
/// Publishes a bound address and keeps accepted connections under the server owner.
|
|
71
66
|
#[tokio::main]
|
|
72
67
|
async fn main() -> Result<()> {
|
|
73
68
|
let listener = TcpListener::bind("127.0.0.1:0").await?;
|
|
@@ -77,11 +72,9 @@ async fn main() -> Result<()> {
|
|
|
77
72
|
let mux = Arc::new(Mux::new());
|
|
78
73
|
mux.register(Arc::new(EchoerHandler::new(EchoServerImpl)))?;
|
|
79
74
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
});
|
|
86
|
-
}
|
|
75
|
+
// The server owns concurrent calls and their cancellation for this listener's lifetime.
|
|
76
|
+
let incoming = futures::stream::unfold(listener, |listener| async move {
|
|
77
|
+
Some((listener.accept().await.map(|(stream, _)| stream), listener))
|
|
78
|
+
});
|
|
79
|
+
Server::with_arc(mux).serve(Box::pin(incoming)).await
|
|
87
80
|
}
|