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,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)
|