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
@@ -0,0 +1,139 @@
1
+ //go:build deps_only
2
+
3
+ /*
4
+ * C++ pipe integration server for cross-language testing: it serves one RPC
5
+ * session over length-prefixed packets on stdin and stdout.
6
+ */
7
+
8
+ #include "echo/echo_srpc.pb.hpp"
9
+ #include "srpc/rpcproto.pb.h"
10
+ #include "srpc/server-rpc.hpp"
11
+
12
+ #include <array>
13
+ #include <csignal>
14
+ #include <iostream>
15
+ #include <mutex>
16
+
17
+ #ifdef _WIN32
18
+ #include <fcntl.h>
19
+ #include <io.h>
20
+ #endif
21
+
22
+ namespace {
23
+
24
+ /*
25
+ * PipeWriter writes length-prefixed packets to stdout. Close stops further
26
+ * writes; the process exits when stdin ends, so no destructor is needed.
27
+ */
28
+ class PipeWriter final : public starpc::PacketWriter {
29
+ public:
30
+ starpc::Error WritePacket(const srpc::Packet &packet) override
31
+ {
32
+ std::lock_guard lock(write_mutex);
33
+ if (closed)
34
+ return starpc::Error::Canceled;
35
+
36
+ const auto data = packet.SerializeAsString();
37
+ const auto size = static_cast<uint32_t>(data.size());
38
+ const std::array<char, 4> header{static_cast<char>(size),
39
+ static_cast<char>(size >> 8),
40
+ static_cast<char>(size >> 16),
41
+ static_cast<char>(size >> 24)};
42
+ std::cout.write(header.data(), header.size());
43
+ std::cout.write(data.data(), data.size());
44
+ std::cout.flush();
45
+ return std::cout ? starpc::Error::OK : starpc::Error::Canceled;
46
+ }
47
+ starpc::Error Close() override
48
+ {
49
+ closed = true;
50
+ return starpc::Error::OK;
51
+ }
52
+
53
+ private:
54
+ std::mutex write_mutex;
55
+ std::atomic<bool> closed{false};
56
+ };
57
+
58
+ /* PipeEcho implements the echo service under test. */
59
+ class PipeEcho final : public echo::SRPCEchoerServer {
60
+ public:
61
+ starpc::Error Echo(const echo::EchoMsg &request,
62
+ echo::EchoMsg *response) override
63
+ {
64
+ *response = request;
65
+ return starpc::Error::OK;
66
+ }
67
+ starpc::Error EchoServerStream(
68
+ const echo::EchoMsg &request,
69
+ echo::SRPCEchoer_EchoServerStreamStream *stream) override
70
+ {
71
+ return stream->Send(request);
72
+ }
73
+ starpc::Error
74
+ EchoClientStream(echo::SRPCEchoer_EchoClientStreamStream *stream,
75
+ echo::EchoMsg *response) override
76
+ {
77
+ return stream->Recv(response);
78
+ }
79
+ starpc::Error
80
+ EchoBidiStream(echo::SRPCEchoer_EchoBidiStreamStream *stream) override
81
+ {
82
+ echo::EchoMsg message;
83
+ while (true) {
84
+ const auto read = stream->Recv(&message);
85
+ if (read == starpc::Error::EOF_)
86
+ return starpc::Error::OK;
87
+ if (read != starpc::Error::OK)
88
+ return read;
89
+ const auto sent = stream->Send(message);
90
+ if (sent != starpc::Error::OK)
91
+ return sent;
92
+ }
93
+ }
94
+ starpc::Error RpcStream(echo::SRPCEchoer_RpcStreamStream *) override
95
+ {
96
+ return starpc::Error::Unimplemented;
97
+ }
98
+ starpc::Error DoNothing(const google::protobuf::Empty &,
99
+ google::protobuf::Empty *) override
100
+ {
101
+ return starpc::Error::OK;
102
+ }
103
+ };
104
+
105
+ } // namespace
106
+
107
+ int main()
108
+ {
109
+ #ifdef _WIN32
110
+ _setmode(_fileno(stdin), _O_BINARY);
111
+ _setmode(_fileno(stdout), _O_BINARY);
112
+ #else
113
+ std::signal(SIGPIPE, SIG_IGN);
114
+ #endif
115
+ PipeEcho service;
116
+ const auto handler = echo::NewSRPCEchoerHandler(&service);
117
+ PipeWriter writer;
118
+ starpc::ServerRPC rpc(handler.get(), &writer);
119
+ while (true) {
120
+ std::array<unsigned char, 4> header{};
121
+ std::cin.read(reinterpret_cast<char *>(header.data()),
122
+ header.size());
123
+ if (!std::cin)
124
+ break;
125
+ const uint32_t size = static_cast<uint32_t>(header[0]) |
126
+ static_cast<uint32_t>(header[1]) << 8 |
127
+ static_cast<uint32_t>(header[2]) << 16 |
128
+ static_cast<uint32_t>(header[3]) << 24;
129
+ if (size == 0 || size > 10'000'000)
130
+ return 2;
131
+ std::string packet(size, '\0');
132
+ std::cin.read(packet.data(), packet.size());
133
+ if (!std::cin)
134
+ return 2;
135
+ if (rpc.HandlePacketData(packet) != starpc::Error::OK)
136
+ return 2;
137
+ }
138
+ rpc.HandleStreamClose(starpc::Error::EOF_);
139
+ }
@@ -0,0 +1,114 @@
1
+ package integration
2
+
3
+ import (
4
+ "context"
5
+ "io"
6
+ "os"
7
+ "os/exec"
8
+ "testing"
9
+ "time"
10
+
11
+ "github.com/aperturerobotics/starpc/echo"
12
+ "github.com/aperturerobotics/starpc/srpc"
13
+ )
14
+
15
+ // processPipe joins the separate stdin/stdout handles used by the native process.
16
+ type processPipe struct {
17
+ // input carries packets to the child.
18
+ input io.WriteCloser
19
+ // output carries replies from the child.
20
+ output io.ReadCloser
21
+ }
22
+
23
+ // Read receives framed data from the child.
24
+ func (p *processPipe) Read(data []byte) (int, error) { return p.output.Read(data) }
25
+
26
+ // Write sends framed data to the child.
27
+ func (p *processPipe) Write(data []byte) (int, error) { return p.input.Write(data) }
28
+
29
+ // Close interrupts both directions of the child connection.
30
+ func (p *processPipe) Close() error {
31
+ inputErr := p.input.Close()
32
+ outputErr := p.output.Close()
33
+ if inputErr != nil {
34
+ return inputErr
35
+ }
36
+ return outputErr
37
+ }
38
+
39
+ func TestCppProcessPipe(t *testing.T) {
40
+ binary := os.Getenv("STARPC_CPP_PIPE_SERVER")
41
+ if binary == "" {
42
+ t.Skip("set STARPC_CPP_PIPE_SERVER to the compiled C++ pipe server")
43
+ }
44
+ for _, abrupt := range []bool{false, true} {
45
+ t.Run(map[bool]string{false: "complete", true: "disconnect"}[abrupt], func(t *testing.T) {
46
+ // Start a real C++ process with the same pipe framing used by Go.
47
+ ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
48
+ t.Cleanup(cancel)
49
+ cmd := exec.CommandContext(ctx, binary) //nolint:gosec // the caller names the server binary
50
+ input, err := cmd.StdinPipe()
51
+ if err != nil {
52
+ t.Fatal(err)
53
+ }
54
+ output, err := cmd.StdoutPipe()
55
+ if err != nil {
56
+ t.Fatal(err)
57
+ }
58
+ cmd.Stderr = os.Stderr
59
+ if err := cmd.Start(); err != nil {
60
+ t.Fatal(err)
61
+ }
62
+ connection := &processPipe{input: input, output: output}
63
+ transport := srpc.NewPacketReadWriter(connection)
64
+ readerDone := make(chan struct{})
65
+ client := srpc.NewClient(func(_ context.Context, handler srpc.PacketDataHandler, closed srpc.CloseHandler) (srpc.PacketWriter, error) {
66
+ go func() {
67
+ defer close(readerDone)
68
+ transport.ReadPump(handler, closed)
69
+ }()
70
+ return transport, nil
71
+ })
72
+ t.Cleanup(func() {
73
+ _ = connection.Close()
74
+ <-readerDone
75
+ if err := cmd.Wait(); err != nil {
76
+ t.Errorf("C++ process shutdown: %v", err)
77
+ }
78
+ })
79
+
80
+ // Successive requests use the same running process and generated API.
81
+ stream, err := echo.NewSRPCEchoerClient(client).EchoBidiStream(t.Context())
82
+ if err != nil {
83
+ t.Fatal(err)
84
+ }
85
+ for _, body := range []string{"", "initial setup", "apply another activity"} {
86
+ if err := stream.MsgSend(&echo.EchoMsg{Body: body}); err != nil {
87
+ t.Fatal(err)
88
+ }
89
+ reply, err := stream.Recv()
90
+ if err != nil {
91
+ t.Fatal(err)
92
+ }
93
+ if reply.Body != body {
94
+ t.Fatalf("reply %q, wanted %q", reply.Body, body)
95
+ }
96
+ }
97
+
98
+ // An explicit half-close completes normally; a dropped pipe cancels the
99
+ // blocked C++ handler and process destruction joins it.
100
+ if abrupt {
101
+ _ = connection.Close()
102
+ return
103
+ }
104
+ if err := stream.CloseSend(); err != nil {
105
+ t.Fatal(err)
106
+ }
107
+ if _, err := stream.Recv(); err != io.EOF {
108
+ t.Fatalf("completion returned %v", err)
109
+ }
110
+ })
111
+ }
112
+ }
113
+
114
+ var _ io.ReadWriteCloser = (*processPipe)(nil)