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
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
//go:build deps_only
|
|
2
|
+
|
|
3
|
+
#include "echo/echo.pb.h"
|
|
4
|
+
#include "rpcstream/rpcstream.hpp"
|
|
5
|
+
#include "srpc/client.hpp"
|
|
6
|
+
#include "srpc/rpcproto.pb.h"
|
|
7
|
+
#include "srpc/server-rpc.hpp"
|
|
8
|
+
|
|
9
|
+
#include <atomic>
|
|
10
|
+
#include <cstdlib>
|
|
11
|
+
#include <future>
|
|
12
|
+
#include <iostream>
|
|
13
|
+
#include <latch>
|
|
14
|
+
|
|
15
|
+
namespace {
|
|
16
|
+
using starpc::Error;
|
|
17
|
+
|
|
18
|
+
void Check(bool value, const char *message)
|
|
19
|
+
{
|
|
20
|
+
if (value)
|
|
21
|
+
return;
|
|
22
|
+
std::cerr << message << '\n';
|
|
23
|
+
std::exit(1);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/*
|
|
27
|
+
* Writer is a test PacketWriter: optional write and close callbacks, plus a
|
|
28
|
+
* count of closes for assertions.
|
|
29
|
+
*/
|
|
30
|
+
class Writer final : public starpc::PacketWriter {
|
|
31
|
+
public:
|
|
32
|
+
Error WritePacket(const srpc::Packet &packet) override
|
|
33
|
+
{
|
|
34
|
+
if (write)
|
|
35
|
+
return write(packet);
|
|
36
|
+
return Error::OK;
|
|
37
|
+
}
|
|
38
|
+
Error Close() override
|
|
39
|
+
{
|
|
40
|
+
++closes;
|
|
41
|
+
if (close)
|
|
42
|
+
close();
|
|
43
|
+
return Error::OK;
|
|
44
|
+
}
|
|
45
|
+
std::function<Error(const srpc::Packet &)> write;
|
|
46
|
+
std::function<void()> close;
|
|
47
|
+
std::atomic<int> closes = 0;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/* WaitingHandler blocks in MsgRecv until its test releases it. */
|
|
51
|
+
class WaitingHandler final : public starpc::Invoker {
|
|
52
|
+
public:
|
|
53
|
+
std::pair<bool, Error> InvokeMethod(const std::string &,
|
|
54
|
+
const std::string &,
|
|
55
|
+
starpc::Stream *stream) override
|
|
56
|
+
{
|
|
57
|
+
entered.count_down();
|
|
58
|
+
echo::EchoMsg message;
|
|
59
|
+
const auto err = stream->MsgRecv(&message);
|
|
60
|
+
exited = true;
|
|
61
|
+
return {true, err};
|
|
62
|
+
}
|
|
63
|
+
std::latch entered{1};
|
|
64
|
+
std::atomic<bool> exited = false;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
/* DestroyWaitingServer proves destruction joins a blocked handler. */
|
|
68
|
+
void DestroyWaitingServer()
|
|
69
|
+
{
|
|
70
|
+
Writer writer;
|
|
71
|
+
WaitingHandler handler;
|
|
72
|
+
{
|
|
73
|
+
starpc::ServerRPC rpc(&handler, &writer);
|
|
74
|
+
srpc::CallStart start;
|
|
75
|
+
start.set_rpc_service("test");
|
|
76
|
+
start.set_rpc_method("wait");
|
|
77
|
+
Check(rpc.HandleCallStart(start) == Error::OK, "start handler");
|
|
78
|
+
handler.entered.wait();
|
|
79
|
+
}
|
|
80
|
+
Check(handler.exited,
|
|
81
|
+
"server destruction must join the canceled handler");
|
|
82
|
+
Check(writer.closes == 1, "writer closes once");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/* ReentrantTransport proves a synchronous reply cannot deadlock the call. */
|
|
86
|
+
void ReentrantTransport()
|
|
87
|
+
{
|
|
88
|
+
starpc::ClientRPC rpc("test", "call");
|
|
89
|
+
Writer writer;
|
|
90
|
+
writer.close = [&] { rpc.HandleStreamClose(Error::OK); };
|
|
91
|
+
writer.write = [&](const srpc::Packet &) {
|
|
92
|
+
srpc::CallData data;
|
|
93
|
+
data.set_data("reply");
|
|
94
|
+
data.set_complete(true);
|
|
95
|
+
return rpc.HandleCallData(data);
|
|
96
|
+
};
|
|
97
|
+
Check(rpc.Start(&writer, false, "ignored") == Error::OK,
|
|
98
|
+
"synchronous reply must not deadlock");
|
|
99
|
+
std::string reply;
|
|
100
|
+
Check(rpc.ReadOne(&reply) == Error::OK && reply == "reply",
|
|
101
|
+
"deliver synchronous reply");
|
|
102
|
+
Check(rpc.ReadOne(&reply) == Error::EOF_, "explicit completion is EOF");
|
|
103
|
+
rpc.Close();
|
|
104
|
+
rpc.Close();
|
|
105
|
+
Check(writer.closes == 1,
|
|
106
|
+
"reentrant close must not repeat transport cleanup");
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/* CancelBlockedWriter proves cancellation interrupts a blocked write. */
|
|
110
|
+
void CancelBlockedWriter()
|
|
111
|
+
{
|
|
112
|
+
Writer writer;
|
|
113
|
+
starpc::ClientRPC rpc("test", "call");
|
|
114
|
+
Check(rpc.Start(&writer, false, "") == Error::OK,
|
|
115
|
+
"start blocked writer test");
|
|
116
|
+
std::latch writing{1}, closed{1};
|
|
117
|
+
writer.write = [&](const srpc::Packet &) {
|
|
118
|
+
writing.count_down();
|
|
119
|
+
closed.wait();
|
|
120
|
+
return Error::Canceled;
|
|
121
|
+
};
|
|
122
|
+
writer.close = [&] { closed.count_down(); };
|
|
123
|
+
std::jthread sender([&] {
|
|
124
|
+
(void)rpc.WriteCallData("pending", false, false, Error::OK);
|
|
125
|
+
});
|
|
126
|
+
writing.wait();
|
|
127
|
+
rpc.Close();
|
|
128
|
+
sender.join();
|
|
129
|
+
Check(writer.closes == 1,
|
|
130
|
+
"cancellation must interrupt a blocked write");
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/* DisconnectAndErrors pins how disconnects and remote errors surface. */
|
|
134
|
+
void DisconnectAndErrors()
|
|
135
|
+
{
|
|
136
|
+
starpc::ClientRPC rpc("test", "call");
|
|
137
|
+
rpc.HandleStreamClose(Error::EOF_);
|
|
138
|
+
std::string message;
|
|
139
|
+
Check(rpc.ReadOne(&message) == Error::ClosedBeforeCompletion,
|
|
140
|
+
"abrupt EOF is not completion");
|
|
141
|
+
|
|
142
|
+
starpc::ClientRPC failed("test", "call");
|
|
143
|
+
srpc::CallData data;
|
|
144
|
+
data.set_error("specific server failure");
|
|
145
|
+
Check(failed.HandleCallData(data) == Error::OK, "receive server error");
|
|
146
|
+
Check(failed.ReadOne(&message) == Error::RemoteError,
|
|
147
|
+
"preserve remote error kind");
|
|
148
|
+
Check(failed.RemoteErrorMessage() == data.error(),
|
|
149
|
+
"preserve remote diagnostic");
|
|
150
|
+
auto messages = starpc::NewMsgStream(&failed, [&] { failed.Close(); });
|
|
151
|
+
Check(messages->RemoteErrorMessage() == data.error(),
|
|
152
|
+
"stream exposes the remote diagnostic");
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/* StreamDestruction proves each stream releases its writer exactly once. */
|
|
156
|
+
void StreamDestruction()
|
|
157
|
+
{
|
|
158
|
+
int destroyed = 0;
|
|
159
|
+
struct CountedWriter final : starpc::PacketWriter {
|
|
160
|
+
explicit CountedWriter(int &count)
|
|
161
|
+
: count(count)
|
|
162
|
+
{
|
|
163
|
+
}
|
|
164
|
+
~CountedWriter() override
|
|
165
|
+
{
|
|
166
|
+
++count;
|
|
167
|
+
}
|
|
168
|
+
Error WritePacket(const srpc::Packet &) override
|
|
169
|
+
{
|
|
170
|
+
return Error::OK;
|
|
171
|
+
}
|
|
172
|
+
Error Close() override
|
|
173
|
+
{
|
|
174
|
+
return Error::OK;
|
|
175
|
+
}
|
|
176
|
+
int &count;
|
|
177
|
+
};
|
|
178
|
+
auto client = starpc::NewClient([&](auto, auto) {
|
|
179
|
+
return std::make_pair(
|
|
180
|
+
std::make_unique<CountedWriter>(destroyed), Error::OK);
|
|
181
|
+
});
|
|
182
|
+
{
|
|
183
|
+
auto [stream, err] = client->NewStream("test", "call", nullptr);
|
|
184
|
+
Check(err == Error::OK, "open stream");
|
|
185
|
+
stream->Close();
|
|
186
|
+
stream->Close();
|
|
187
|
+
}
|
|
188
|
+
Check(destroyed == 1, "stream must destroy its writer exactly once");
|
|
189
|
+
{
|
|
190
|
+
auto [stream, err] = client->NewStream("test", "call", nullptr);
|
|
191
|
+
Check(err == Error::OK, "open automatically closed stream");
|
|
192
|
+
}
|
|
193
|
+
Check(destroyed == 2, "dropping a stream must release its writer");
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/* BoundReceiveQueue pins the receive queue's byte and message bounds. */
|
|
197
|
+
void BoundReceiveQueue()
|
|
198
|
+
{
|
|
199
|
+
starpc::ClientRPC rpc("test", "call");
|
|
200
|
+
srpc::CallData data;
|
|
201
|
+
data.set_data(std::string(4 * 1024 * 1024 + 1, 'x'));
|
|
202
|
+
Check(rpc.HandleCallData(data) == Error::ResourceExhausted,
|
|
203
|
+
"bound unread message bytes");
|
|
204
|
+
starpc::ClientRPC empty("test", "call");
|
|
205
|
+
data.clear_data();
|
|
206
|
+
data.set_data_is_zero(true);
|
|
207
|
+
for (int i = 0; i < 1024; ++i) {
|
|
208
|
+
Check(empty.HandleCallData(data) == Error::OK,
|
|
209
|
+
"queue empty message");
|
|
210
|
+
}
|
|
211
|
+
Check(empty.HandleCallData(data) == Error::ResourceExhausted,
|
|
212
|
+
"bound empty message count");
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/*
|
|
216
|
+
* NestedReleaseWaitsForHandler proves the Invoker is released only after the
|
|
217
|
+
* nested handler returns.
|
|
218
|
+
*/
|
|
219
|
+
void NestedReleaseWaitsForHandler()
|
|
220
|
+
{
|
|
221
|
+
WaitingHandler handler;
|
|
222
|
+
class DisconnectStream final : public rpcstream::RpcStream {
|
|
223
|
+
public:
|
|
224
|
+
explicit DisconnectStream(WaitingHandler &handler)
|
|
225
|
+
: handler(handler)
|
|
226
|
+
{
|
|
227
|
+
}
|
|
228
|
+
Error Send(const rpcstream::RpcStreamPacket &) override
|
|
229
|
+
{
|
|
230
|
+
return Error::OK;
|
|
231
|
+
}
|
|
232
|
+
Error CloseSend() override
|
|
233
|
+
{
|
|
234
|
+
return Error::OK;
|
|
235
|
+
}
|
|
236
|
+
Error Close() override
|
|
237
|
+
{
|
|
238
|
+
return Error::OK;
|
|
239
|
+
}
|
|
240
|
+
Error Recv(rpcstream::RpcStreamPacket *packet) override
|
|
241
|
+
{
|
|
242
|
+
packet->Clear();
|
|
243
|
+
if (reads++ == 0) {
|
|
244
|
+
packet->mutable_init()->set_component_id(
|
|
245
|
+
"component");
|
|
246
|
+
return Error::OK;
|
|
247
|
+
}
|
|
248
|
+
if (reads == 2) {
|
|
249
|
+
packet->set_data(
|
|
250
|
+
starpc::NewCallStartPacket(
|
|
251
|
+
"test", "wait", "", false)
|
|
252
|
+
->SerializeAsString());
|
|
253
|
+
return Error::OK;
|
|
254
|
+
}
|
|
255
|
+
handler.entered.wait();
|
|
256
|
+
return Error::EOF_;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
private:
|
|
260
|
+
WaitingHandler &handler;
|
|
261
|
+
int reads = 0;
|
|
262
|
+
};
|
|
263
|
+
bool released = false;
|
|
264
|
+
auto stream = std::make_shared<DisconnectStream>(handler);
|
|
265
|
+
const auto err =
|
|
266
|
+
rpcstream::HandleRpcStream(stream, [&](const std::string &) {
|
|
267
|
+
return std::make_tuple(
|
|
268
|
+
&handler,
|
|
269
|
+
[&] {
|
|
270
|
+
Check(handler.exited,
|
|
271
|
+
"nested handler must return "
|
|
272
|
+
"before its service is released");
|
|
273
|
+
released = true;
|
|
274
|
+
},
|
|
275
|
+
Error::OK);
|
|
276
|
+
});
|
|
277
|
+
Check(err == Error::OK && released,
|
|
278
|
+
"nested disconnect must release its service");
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/* ReceiveThreadJoinsOnDestruction proves writer destruction joins the pump. */
|
|
282
|
+
void ReceiveThreadJoinsOnDestruction()
|
|
283
|
+
{
|
|
284
|
+
class WaitingStream final : public rpcstream::RpcStream {
|
|
285
|
+
public:
|
|
286
|
+
Error Send(const rpcstream::RpcStreamPacket &) override
|
|
287
|
+
{
|
|
288
|
+
return Error::OK;
|
|
289
|
+
}
|
|
290
|
+
Error Recv(rpcstream::RpcStreamPacket *) override
|
|
291
|
+
{
|
|
292
|
+
entered.count_down();
|
|
293
|
+
closed.wait();
|
|
294
|
+
return Error::EOF_;
|
|
295
|
+
}
|
|
296
|
+
Error CloseSend() override
|
|
297
|
+
{
|
|
298
|
+
return Close();
|
|
299
|
+
}
|
|
300
|
+
Error Close() override
|
|
301
|
+
{
|
|
302
|
+
if (!stopped.exchange(true))
|
|
303
|
+
closed.count_down();
|
|
304
|
+
return Error::OK;
|
|
305
|
+
}
|
|
306
|
+
std::latch entered{1}, closed{1};
|
|
307
|
+
std::atomic<bool> stopped{false};
|
|
308
|
+
};
|
|
309
|
+
auto stream = std::make_shared<WaitingStream>();
|
|
310
|
+
std::atomic<bool> returned{false};
|
|
311
|
+
auto [writer, started] = rpcstream::StartReadPump(
|
|
312
|
+
stream, [](const std::string &) { return Error::OK; },
|
|
313
|
+
[&](Error) { returned = true; });
|
|
314
|
+
Check(started == Error::OK, "start receive thread");
|
|
315
|
+
stream->entered.wait();
|
|
316
|
+
writer.reset();
|
|
317
|
+
Check(returned, "writer destruction must join its receive callback");
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
} // namespace
|
|
321
|
+
|
|
322
|
+
int main()
|
|
323
|
+
{
|
|
324
|
+
DestroyWaitingServer();
|
|
325
|
+
ReentrantTransport();
|
|
326
|
+
CancelBlockedWriter();
|
|
327
|
+
DisconnectAndErrors();
|
|
328
|
+
StreamDestruction();
|
|
329
|
+
BoundReceiveQueue();
|
|
330
|
+
NestedReleaseWaitsForHandler();
|
|
331
|
+
ReceiveThreadJoinsOnDestruction();
|
|
332
|
+
std::cout << "RPC lifecycle checks passed\n";
|
|
333
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
package srpc
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"context"
|
|
5
|
+
"errors"
|
|
6
|
+
"testing"
|
|
7
|
+
"time"
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
// TestServerCancellationRetainsIdentity checks cancellation across the wire,
|
|
11
|
+
// independently of the client's own context cancellation.
|
|
12
|
+
func TestServerCancellationRetainsIdentity(t *testing.T) {
|
|
13
|
+
// Exercise the real packet codec and pipe with a canceled remote handler.
|
|
14
|
+
ctx, cancel := context.WithTimeout(t.Context(), time.Second)
|
|
15
|
+
defer cancel()
|
|
16
|
+
server := NewServer(InvokerFunc(func(string, string, Stream) (bool, error) {
|
|
17
|
+
return true, context.Canceled
|
|
18
|
+
}))
|
|
19
|
+
rpc := NewClientRPC(ctx, "service", "method")
|
|
20
|
+
defer rpc.Close()
|
|
21
|
+
writer, err := NewServerPipe(server)(ctx, rpc.HandlePacketData, rpc.HandleStreamClose)
|
|
22
|
+
if err != nil {
|
|
23
|
+
t.Fatal(err)
|
|
24
|
+
}
|
|
25
|
+
if err := rpc.Start(writer, false, nil); err != nil {
|
|
26
|
+
t.Fatal(err)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Both receive and completion must retain the remote cancellation sentinel.
|
|
30
|
+
if _, err := rpc.ReadOne(); !errors.Is(err, context.Canceled) {
|
|
31
|
+
t.Fatalf("receive cancellation = %v", err)
|
|
32
|
+
}
|
|
33
|
+
if err := rpc.Wait(ctx); !errors.Is(err, context.Canceled) {
|
|
34
|
+
t.Fatalf("completion cancellation = %v", err)
|
|
35
|
+
}
|
|
36
|
+
if err := ctx.Err(); err != nil {
|
|
37
|
+
t.Fatal("client deadline hid the remote cancellation", err)
|
|
38
|
+
}
|
|
39
|
+
}
|
package/srpc/server-rpc.cpp
CHANGED
|
@@ -5,93 +5,103 @@
|
|
|
5
5
|
#include "packet.hpp"
|
|
6
6
|
#include "rpcproto.pb.h"
|
|
7
7
|
|
|
8
|
+
#include <system_error>
|
|
9
|
+
|
|
8
10
|
namespace starpc {
|
|
9
11
|
|
|
10
12
|
ServerRPC::ServerRPC(Invoker *invoker, PacketWriter *writer)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
: invoker(invoker)
|
|
14
|
+
{
|
|
15
|
+
this->writer = writer;
|
|
14
16
|
}
|
|
15
17
|
|
|
16
|
-
ServerRPC::~ServerRPC()
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
ServerRPC::~ServerRPC()
|
|
19
|
+
{
|
|
20
|
+
Cancel();
|
|
21
|
+
if (invoke_thread.joinable())
|
|
22
|
+
invoke_thread.join();
|
|
20
23
|
}
|
|
21
24
|
|
|
22
|
-
Error ServerRPC::HandlePacketData(const std::string &data)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
Error ServerRPC::HandlePacketData(const std::string &data)
|
|
26
|
+
{
|
|
27
|
+
srpc::Packet packet;
|
|
28
|
+
if (!packet.ParseFromString(data))
|
|
29
|
+
return Error::InvalidMessage;
|
|
30
|
+
return HandlePacket(packet);
|
|
28
31
|
}
|
|
29
32
|
|
|
30
|
-
Error ServerRPC::HandlePacket(const srpc::Packet &
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
33
|
+
Error ServerRPC::HandlePacket(const srpc::Packet &packet)
|
|
34
|
+
{
|
|
35
|
+
const auto err = ValidatePacket(packet);
|
|
36
|
+
if (err != Error::OK)
|
|
37
|
+
return err;
|
|
38
|
+
|
|
39
|
+
switch (packet.body_case()) {
|
|
40
|
+
case srpc::Packet::kCallStart:
|
|
41
|
+
return HandleCallStart(packet.call_start());
|
|
42
|
+
case srpc::Packet::kCallData:
|
|
43
|
+
if (service.empty())
|
|
44
|
+
return Error::UnrecognizedPacket;
|
|
45
|
+
return HandleCallData(packet.call_data());
|
|
46
|
+
case srpc::Packet::kCallCancel:
|
|
47
|
+
return packet.call_cancel() ? HandleCallCancel() : Error::OK;
|
|
48
|
+
default:
|
|
49
|
+
return Error::UnrecognizedPacket;
|
|
50
|
+
}
|
|
49
51
|
}
|
|
50
52
|
|
|
51
|
-
Error ServerRPC::HandleCallStart(const srpc::CallStart &
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
53
|
+
Error ServerRPC::HandleCallStart(const srpc::CallStart &packet)
|
|
54
|
+
{
|
|
55
|
+
const auto valid = ValidateCallStart(packet);
|
|
56
|
+
if (valid != Error::OK)
|
|
57
|
+
return valid;
|
|
58
|
+
|
|
59
|
+
{
|
|
60
|
+
std::lock_guard lock(state_mutex);
|
|
61
|
+
if (writer == nullptr)
|
|
62
|
+
return Error::NilWriter;
|
|
63
|
+
if (invoker == nullptr)
|
|
64
|
+
return Error::Unimplemented;
|
|
65
|
+
if (!service.empty() || canceled || data_closed)
|
|
66
|
+
return Error::Completed;
|
|
67
|
+
service = packet.rpc_service();
|
|
68
|
+
method = packet.rpc_method();
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (!packet.data().empty() || packet.data_is_zero()) {
|
|
72
|
+
srpc::CallData first;
|
|
73
|
+
first.set_data(packet.data());
|
|
74
|
+
first.set_data_is_zero(packet.data_is_zero());
|
|
75
|
+
const auto queued = HandleCallData(first);
|
|
76
|
+
if (queued != Error::OK)
|
|
77
|
+
return queued;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/*
|
|
81
|
+
* Thread creation is the sole throwing runtime boundary in this
|
|
82
|
+
* component; a stop callback cancels the call when the thread ends.
|
|
83
|
+
*/
|
|
84
|
+
try {
|
|
85
|
+
invoke_thread = std::jthread([this](std::stop_token stop) {
|
|
86
|
+
std::stop_callback canceled(stop, [this] { Cancel(); });
|
|
87
|
+
InvokeRPC(service, method);
|
|
88
|
+
});
|
|
89
|
+
} catch (const std::system_error &) {
|
|
90
|
+
Cancel();
|
|
91
|
+
return Error::ResourceExhausted;
|
|
92
|
+
}
|
|
93
|
+
return Error::OK;
|
|
79
94
|
}
|
|
80
95
|
|
|
81
96
|
void ServerRPC::InvokeRPC(const std::string &service_id,
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
auto out_pkt = NewCallDataPacket("", false, true, err);
|
|
92
|
-
writer_->WritePacket(*out_pkt);
|
|
93
|
-
writer_->Close();
|
|
94
|
-
Cancel();
|
|
97
|
+
const std::string &method_id)
|
|
98
|
+
{
|
|
99
|
+
auto stream = NewMsgStream(this, [this] { Cancel(); }, StopToken());
|
|
100
|
+
auto [found, err] =
|
|
101
|
+
invoker->InvokeMethod(service_id, method_id, stream.get());
|
|
102
|
+
if (!found && err == Error::OK)
|
|
103
|
+
err = Error::Unimplemented;
|
|
104
|
+
Finish(err);
|
|
95
105
|
}
|
|
96
106
|
|
|
97
107
|
} // namespace starpc
|
package/srpc/server-rpc.go
CHANGED
|
@@ -6,15 +6,15 @@ import (
|
|
|
6
6
|
"github.com/pkg/errors"
|
|
7
7
|
)
|
|
8
8
|
|
|
9
|
-
// ServerRPC
|
|
9
|
+
// ServerRPC runs one remote method and retains its resources until the handler exits.
|
|
10
10
|
type ServerRPC struct {
|
|
11
11
|
commonRPC
|
|
12
|
-
|
|
12
|
+
|
|
13
|
+
// invoker dispatches the selected service and method.
|
|
13
14
|
invoker Invoker
|
|
14
15
|
}
|
|
15
16
|
|
|
16
|
-
// NewServerRPC
|
|
17
|
-
// note: call SetWriter before handling any incoming messages.
|
|
17
|
+
// NewServerRPC attaches the handler and transport for one incoming call.
|
|
18
18
|
func NewServerRPC(ctx context.Context, invoker Invoker, writer PacketWriter) *ServerRPC {
|
|
19
19
|
rpc := &ServerRPC{invoker: invoker}
|
|
20
20
|
initCommonRPC(ctx, &rpc.commonRPC)
|
|
@@ -33,6 +33,7 @@ func (r *ServerRPC) HandlePacketData(data []byte) error {
|
|
|
33
33
|
|
|
34
34
|
// HandlePacket handles an incoming parsed message packet.
|
|
35
35
|
func (r *ServerRPC) HandlePacket(msg *Packet) error {
|
|
36
|
+
// Ignore an absent packet and reject malformed messages before dispatch.
|
|
36
37
|
if msg == nil {
|
|
37
38
|
return nil
|
|
38
39
|
}
|
|
@@ -40,6 +41,7 @@ func (r *ServerRPC) HandlePacket(msg *Packet) error {
|
|
|
40
41
|
return err
|
|
41
42
|
}
|
|
42
43
|
|
|
44
|
+
// Route each packet through the shared stream state.
|
|
43
45
|
switch b := msg.GetBody().(type) {
|
|
44
46
|
case *Packet_CallStart:
|
|
45
47
|
return r.HandleCallStart(b.CallStart)
|
|
@@ -57,25 +59,20 @@ func (r *ServerRPC) HandlePacket(msg *Packet) error {
|
|
|
57
59
|
|
|
58
60
|
// HandleCallStart handles the call start packet.
|
|
59
61
|
func (r *ServerRPC) HandleCallStart(pkt *CallStart) error {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
+
// Accept one start before any terminal stream transition.
|
|
62
63
|
locked := r.bcast.Lock()
|
|
63
|
-
// process start: method and service
|
|
64
64
|
if r.method != "" || r.service != "" {
|
|
65
|
-
err = errors.New("call start must be sent only once")
|
|
66
65
|
locked.Unlock()
|
|
67
|
-
return
|
|
66
|
+
return errors.New("call start must be sent only once")
|
|
68
67
|
}
|
|
69
68
|
if r.dataClosed {
|
|
70
|
-
err = ErrCompleted
|
|
71
69
|
locked.Unlock()
|
|
72
|
-
return
|
|
70
|
+
return ErrCompleted
|
|
73
71
|
}
|
|
74
72
|
|
|
73
|
+
// Retain the request identity and optional first message for the handler.
|
|
75
74
|
service, method := pkt.GetRpcService(), pkt.GetRpcMethod()
|
|
76
75
|
r.service, r.method = service, method
|
|
77
|
-
|
|
78
|
-
// process first data packet, if included
|
|
79
76
|
if data := pkt.GetData(); len(data) != 0 || pkt.GetDataIsZero() {
|
|
80
77
|
r.dataQueue = append(r.dataQueue, data)
|
|
81
78
|
}
|
|
@@ -84,27 +81,32 @@ func (r *ServerRPC) HandleCallStart(pkt *CallStart) error {
|
|
|
84
81
|
// Mark the method active before scheduling it so cancellation cannot make
|
|
85
82
|
// Wait return and release a mux while invokeRPC is still running user code.
|
|
86
83
|
r.localActive = true
|
|
87
|
-
|
|
88
|
-
// invoke the rpc
|
|
89
84
|
locked.Broadcast()
|
|
90
85
|
startServerRPCInvoke(func() {
|
|
91
86
|
r.invokeRPC(service, method)
|
|
92
87
|
})
|
|
93
88
|
locked.Unlock()
|
|
94
89
|
|
|
95
|
-
return
|
|
90
|
+
return nil
|
|
96
91
|
}
|
|
97
92
|
|
|
98
93
|
// invokeRPC invokes the RPC after CallStart is received.
|
|
99
94
|
func (r *ServerRPC) invokeRPC(serviceID, methodID string) {
|
|
100
|
-
//
|
|
95
|
+
// Run the selected handler before publishing its terminal result.
|
|
101
96
|
strm := NewMsgStream(r.ctx, r, r.cancelContext)
|
|
102
97
|
ok, err := r.invoker.InvokeMethod(serviceID, methodID, strm)
|
|
103
98
|
if err == nil && !ok {
|
|
104
99
|
err = ErrUnimplemented
|
|
105
100
|
}
|
|
106
|
-
|
|
101
|
+
|
|
102
|
+
// Cancellation has its own packet; a text error would lose its identity.
|
|
107
103
|
outPkt := NewCallDataPacket(nil, false, true, err)
|
|
108
|
-
|
|
104
|
+
if errors.Is(err, context.Canceled) {
|
|
105
|
+
outPkt = NewCallCancelPacket()
|
|
106
|
+
}
|
|
107
|
+
r.beginLocalCompletion()
|
|
108
|
+
if err := r.writer.WritePacket(outPkt); err != nil {
|
|
109
|
+
r.HandleStreamClose(err)
|
|
110
|
+
}
|
|
109
111
|
r.finishLocalCompletion()
|
|
110
112
|
}
|