starpc 0.43.1 → 0.45.0

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.
@@ -0,0 +1,104 @@
1
+ // Code generated by protoc-gen-starpc-cpp. DO NOT EDIT.
2
+ // protoc-gen-starpc-cpp version: v0.43.2-0.20260131104303-1de77bff6409+dirty
3
+ // source: github.com/aperturerobotics/starpc/mock/mock.proto
4
+
5
+ #pragma once
6
+
7
+ #include <functional>
8
+ #include <memory>
9
+ #include <string>
10
+ #include <utility>
11
+
12
+ #include "srpc/starpc.hpp"
13
+
14
+ #include "mock.pb.h"
15
+
16
+ namespace e2e::mock {
17
+
18
+ // Service ID for Mock
19
+ constexpr const char *kSRPCMockServiceID = "e2e.mock.Mock";
20
+
21
+ // SRPCMockClient is the client API for Mock service.
22
+ class SRPCMockClient {
23
+ public:
24
+ virtual ~SRPCMockClient() = default;
25
+
26
+ // SRPCClient returns the underlying SRPC client.
27
+ virtual starpc::Client *SRPCClient() = 0;
28
+
29
+ // MockRequest
30
+ virtual starpc::Error MockRequest(const e2e::mock::MockMsg &in,
31
+ e2e::mock::MockMsg *out) = 0;
32
+ };
33
+
34
+ // SRPCMockClientImpl implements SRPCMockClient.
35
+ class SRPCMockClientImpl : public SRPCMockClient {
36
+ public:
37
+ explicit SRPCMockClientImpl(starpc::Client *cc,
38
+ const std::string &service_id = "")
39
+ : cc_(cc),
40
+ service_id_(service_id.empty() ? kSRPCMockServiceID : service_id) {}
41
+
42
+ starpc::Client *SRPCClient() override { return cc_; }
43
+
44
+ // MockRequest
45
+ virtual starpc::Error MockRequest(const e2e::mock::MockMsg &in,
46
+ e2e::mock::MockMsg *out) override;
47
+
48
+ private:
49
+ starpc::Client *cc_;
50
+ std::string service_id_;
51
+ };
52
+
53
+ // NewSRPCMockClient creates a new client.
54
+ inline std::unique_ptr<SRPCMockClient> NewSRPCMockClient(starpc::Client *cc) {
55
+ return std::make_unique<SRPCMockClientImpl>(cc);
56
+ }
57
+
58
+ // SRPCMockServer is the server API for Mock service.
59
+ class SRPCMockServer {
60
+ public:
61
+ virtual ~SRPCMockServer() = default;
62
+
63
+ // MockRequest
64
+ virtual starpc::Error MockRequest(const e2e::mock::MockMsg &req,
65
+ e2e::mock::MockMsg *resp) = 0;
66
+ };
67
+
68
+ // SRPCMockHandler implements starpc::Handler for Mock.
69
+ class SRPCMockHandler : public starpc::Handler {
70
+ public:
71
+ SRPCMockHandler(SRPCMockServer *impl, const std::string &service_id = "")
72
+ : impl_(impl),
73
+ service_id_(service_id.empty() ? kSRPCMockServiceID : service_id) {}
74
+
75
+ const std::string &GetServiceID() const override { return service_id_; }
76
+ std::vector<std::string> GetMethodIDs() const override;
77
+ std::pair<bool, starpc::Error> InvokeMethod(const std::string &service_id,
78
+ const std::string &method_id,
79
+ starpc::Stream *strm) override;
80
+
81
+ private:
82
+ SRPCMockServer *impl_;
83
+ std::string service_id_;
84
+ };
85
+
86
+ // NewSRPCMockHandler creates a new handler for the given implementation.
87
+ inline std::unique_ptr<SRPCMockHandler>
88
+ NewSRPCMockHandler(SRPCMockServer *impl) {
89
+ return std::make_unique<SRPCMockHandler>(impl);
90
+ }
91
+
92
+ // SRPCRegisterMock registers the server implementation with the mux.
93
+ // The returned handler must outlive the mux registration.
94
+ inline std::pair<std::unique_ptr<SRPCMockHandler>, starpc::Error>
95
+ SRPCRegisterMock(starpc::Mux *mux, SRPCMockServer *impl) {
96
+ auto handler = NewSRPCMockHandler(impl);
97
+ starpc::Error err = mux->Register(handler.get());
98
+ if (err != starpc::Error::OK) {
99
+ return {nullptr, err};
100
+ }
101
+ return {std::move(handler), starpc::Error::OK};
102
+ }
103
+
104
+ } // namespace e2e::mock
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "starpc",
3
- "version": "0.43.1",
3
+ "version": "0.45.0",
4
4
  "description": "Streaming protobuf RPC service protocol over any two-way channel.",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -76,6 +76,7 @@
76
76
  "gen:force": "bun run go:aptre -- generate --force && bun run format",
77
77
  "test": "bun run test:js && bun run test:go",
78
78
  "test:go": "bun run go:aptre -- test",
79
+ "test:cpp": "mkdir -p build && cd build && cmake .. -DCMAKE_BUILD_TYPE=Release && cmake --build . --parallel && ctest --output-on-failure",
79
80
  "build:e2e": "bun run build && cd e2e && esbuild e2e.ts --sourcemap --outfile=e2e.cjs --bundle --platform=node",
80
81
  "test:js": "vitest run",
81
82
  "test:js:watch": "vitest",
@@ -107,19 +108,19 @@
107
108
  "esbuild": "^0.27.0",
108
109
  "eslint": "^9.39.1",
109
110
  "eslint-config-prettier": "^10.0.2",
110
- "happy-dom": "^20.4.0",
111
+ "happy-dom": "^20.5.0",
111
112
  "husky": "^9.1.7",
112
113
  "lint-staged": "^16.2.7",
113
114
  "prettier": "^3.8.1",
114
115
  "rimraf": "^6.1.2",
115
116
  "tsx": "^4.20.4",
116
117
  "typescript": "^5.8.2",
117
- "@typescript/native-preview": "^7.0.0-dev.20260130.1",
118
+ "@typescript/native-preview": "^7.0.0-dev.20260202.1",
118
119
  "vitest": "^4.0.18"
119
120
  },
120
121
  "dependencies": {
121
122
  "@aptre/it-ws": "^1.1.2",
122
- "@aptre/protobuf-es-lite": "^0.5.2",
123
+ "@aptre/protobuf-es-lite": "^0.5.3",
123
124
  "@chainsafe/libp2p-yamux": "^7.0.1",
124
125
  "@libp2p/interface": "^2.6.1",
125
126
  "event-iterator": "^2.0.0",
@@ -132,6 +133,6 @@
132
133
  "ws": "^8.18.1"
133
134
  },
134
135
  "resolutions": {
135
- "@aptre/protobuf-es-lite": "0.5.2"
136
+ "@aptre/protobuf-es-lite": "0.5.3"
136
137
  }
137
138
  }
@@ -0,0 +1,59 @@
1
+ cmake_minimum_required(VERSION 3.14)
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
+ )
@@ -0,0 +1,106 @@
1
+ // go:build deps_only
2
+
3
+ #include "client-rpc.hpp"
4
+
5
+ #include "rpcproto.pb.h"
6
+
7
+ namespace starpc {
8
+
9
+ ClientRPC::ClientRPC(const std::string &service, const std::string &method) {
10
+ Init();
11
+ service_ = service;
12
+ method_ = method;
13
+ }
14
+
15
+ ClientRPC::~ClientRPC() = default;
16
+
17
+ Error ClientRPC::Start(PacketWriter *writer, bool write_first_msg,
18
+ const std::string &first_msg) {
19
+ if (writer == nullptr) {
20
+ return Error::NilWriter;
21
+ }
22
+
23
+ if (IsCanceled()) {
24
+ Cancel();
25
+ writer->Close();
26
+ return Error::Canceled;
27
+ }
28
+
29
+ bool first_msg_empty = false;
30
+ Error err = Error::OK;
31
+
32
+ {
33
+ std::lock_guard<std::mutex> lock(mtx_);
34
+ writer_ = writer;
35
+
36
+ if (write_first_msg) {
37
+ first_msg_empty = first_msg.empty();
38
+ }
39
+
40
+ auto pkt =
41
+ NewCallStartPacket(service_, method_, first_msg, first_msg_empty);
42
+ err = writer->WritePacket(*pkt);
43
+ if (err != Error::OK) {
44
+ Cancel();
45
+ writer->Close();
46
+ }
47
+ }
48
+
49
+ cv_.notify_all();
50
+ return err;
51
+ }
52
+
53
+ Error ClientRPC::HandlePacketData(const std::string &data) {
54
+ srpc::Packet pkt;
55
+ if (!pkt.ParseFromString(data)) {
56
+ return Error::InvalidMessage;
57
+ }
58
+ return HandlePacket(pkt);
59
+ }
60
+
61
+ void ClientRPC::HandleStreamClose(Error close_err) {
62
+ std::lock_guard<std::mutex> lock(mtx_);
63
+ if (close_err != Error::OK && remote_err_ == Error::OK) {
64
+ remote_err_ = close_err;
65
+ }
66
+ data_closed_ = true;
67
+ Cancel();
68
+ cv_.notify_all();
69
+ }
70
+
71
+ Error ClientRPC::HandlePacket(const srpc::Packet &msg) {
72
+ Error err = ValidatePacket(msg);
73
+ if (err != Error::OK) {
74
+ return err;
75
+ }
76
+
77
+ switch (msg.body_case()) {
78
+ case srpc::Packet::kCallStart:
79
+ return HandleCallStart(msg.call_start());
80
+ case srpc::Packet::kCallData:
81
+ return HandleCallData(msg.call_data());
82
+ case srpc::Packet::kCallCancel:
83
+ if (msg.call_cancel()) {
84
+ return HandleCallCancel();
85
+ }
86
+ return Error::OK;
87
+ default:
88
+ return Error::OK;
89
+ }
90
+ }
91
+
92
+ Error ClientRPC::HandleCallStart(const srpc::CallStart &pkt) {
93
+ // server-to-client calls not supported
94
+ return Error::UnrecognizedPacket;
95
+ }
96
+
97
+ void ClientRPC::Close() {
98
+ std::lock_guard<std::mutex> lock(mtx_);
99
+ // call did not start yet if writer is nil.
100
+ if (writer_ != nullptr) {
101
+ WriteCallCancelLocked();
102
+ CloseLocked();
103
+ }
104
+ }
105
+
106
+ } // namespace starpc
@@ -0,0 +1,68 @@
1
+ #pragma once
2
+
3
+ #include <string>
4
+
5
+ #include "common-rpc.hpp"
6
+ #include "errors.hpp"
7
+ #include "msg-stream.hpp"
8
+ #include "packet.hpp"
9
+ #include "writer.hpp"
10
+
11
+ namespace srpc {
12
+ class Packet;
13
+ class CallStart;
14
+ } // namespace srpc
15
+
16
+ namespace starpc {
17
+
18
+ // ClientRPC represents the client side of an on-going RPC call message stream.
19
+ // Matches Go ClientRPC struct in client-rpc.go
20
+ class ClientRPC : public CommonRPC, public MsgStreamRw {
21
+ public:
22
+ // Constructor matching NewClientRPC in Go
23
+ ClientRPC(const std::string &service, const std::string &method);
24
+ ~ClientRPC() override;
25
+
26
+ // Start sets the writer and writes the CallStart message.
27
+ // Must only be called once!
28
+ // Matches Go Start method in client-rpc.go
29
+ Error Start(PacketWriter *writer, bool write_first_msg,
30
+ const std::string &first_msg);
31
+
32
+ // HandlePacketData handles an incoming unparsed message packet.
33
+ // Matches Go HandlePacketData in client-rpc.go
34
+ Error HandlePacketData(const std::string &data);
35
+
36
+ // HandleStreamClose handles the stream closing optionally with an error.
37
+ // Matches Go HandleStreamClose (overrides CommonRPC)
38
+ void HandleStreamClose(Error close_err);
39
+
40
+ // HandlePacket handles an incoming parsed message packet.
41
+ // Matches Go HandlePacket in client-rpc.go
42
+ Error HandlePacket(const srpc::Packet &pkt);
43
+
44
+ // HandleCallStart handles the call start packet.
45
+ // Matches Go HandleCallStart in client-rpc.go
46
+ Error HandleCallStart(const srpc::CallStart &pkt);
47
+
48
+ // Close releases any resources held by the ClientRPC.
49
+ // Matches Go Close in client-rpc.go
50
+ void Close();
51
+
52
+ // MsgStreamRw interface implementation
53
+ Error ReadOne(std::string *out) override { return CommonRPC::ReadOne(out); }
54
+ Error WriteCallData(const std::string &data, bool data_is_zero, bool complete,
55
+ Error err) override {
56
+ return CommonRPC::WriteCallData(data, data_is_zero, complete, err);
57
+ }
58
+ Error WriteCallCancel() override { return CommonRPC::WriteCallCancel(); }
59
+ };
60
+
61
+ // NewClientRPC constructs a new ClientRPC session.
62
+ // Matches Go NewClientRPC function in client-rpc.go
63
+ inline std::unique_ptr<ClientRPC> NewClientRPC(const std::string &service,
64
+ const std::string &method) {
65
+ return std::make_unique<ClientRPC>(service, method);
66
+ }
67
+
68
+ } // namespace starpc
@@ -0,0 +1,101 @@
1
+ // go:build deps_only
2
+
3
+ #include "client.hpp"
4
+
5
+ namespace starpc {
6
+
7
+ Error ClientImpl::ExecCall(const std::string &service,
8
+ const std::string &method, const Message &in,
9
+ Message *out) {
10
+ std::string first_msg;
11
+ if (!in.SerializeToString(&first_msg)) {
12
+ return Error::InvalidMessage;
13
+ }
14
+
15
+ auto client_rpc = NewClientRPC(service, method);
16
+
17
+ // Open the stream with handlers
18
+ auto [writer, err] = open_stream_(
19
+ [&client_rpc](const std::string &data) -> Error {
20
+ return client_rpc->HandlePacketData(data);
21
+ },
22
+ [&client_rpc](Error close_err) {
23
+ client_rpc->HandleStreamClose(close_err);
24
+ });
25
+
26
+ if (err != Error::OK) {
27
+ return err;
28
+ }
29
+
30
+ err = client_rpc->Start(writer.get(), true, first_msg);
31
+ if (err != Error::OK) {
32
+ client_rpc->Close();
33
+ return err;
34
+ }
35
+
36
+ std::string msg;
37
+ err = client_rpc->ReadOne(&msg);
38
+ if (err != Error::OK) {
39
+ client_rpc->Close();
40
+ return err;
41
+ }
42
+
43
+ if (!out->ParseFromString(msg)) {
44
+ client_rpc->Close();
45
+ return Error::InvalidMessage;
46
+ }
47
+
48
+ client_rpc->Close();
49
+ return Error::OK;
50
+ }
51
+
52
+ std::pair<std::unique_ptr<Stream>, Error>
53
+ ClientImpl::NewStream(const std::string &service, const std::string &method,
54
+ const Message *first_msg) {
55
+ std::string first_msg_data;
56
+ if (first_msg != nullptr) {
57
+ if (!first_msg->SerializeToString(&first_msg_data)) {
58
+ return {nullptr, Error::InvalidMessage};
59
+ }
60
+ }
61
+
62
+ auto client_rpc = std::make_shared<ClientRPC>(service, method);
63
+
64
+ // Open the stream with handlers
65
+ auto [writer, err] = open_stream_(
66
+ [client_rpc](const std::string &data) -> Error {
67
+ return client_rpc->HandlePacketData(data);
68
+ },
69
+ [client_rpc](Error close_err) {
70
+ client_rpc->HandleStreamClose(close_err);
71
+ });
72
+
73
+ if (err != Error::OK) {
74
+ return {nullptr, err};
75
+ }
76
+
77
+ err = client_rpc->Start(writer.get(), first_msg != nullptr, first_msg_data);
78
+ if (err != Error::OK) {
79
+ return {nullptr, err};
80
+ }
81
+
82
+ // Create MsgStream with close callback
83
+ // Note: We need to capture writer to extend its lifetime
84
+ auto writer_ptr = writer.release();
85
+ auto stream =
86
+ std::make_unique<MsgStream>(client_rpc.get(), [client_rpc, writer_ptr]() {
87
+ client_rpc->Cancel();
88
+ if (writer_ptr) {
89
+ writer_ptr->Close();
90
+ delete writer_ptr;
91
+ }
92
+ });
93
+
94
+ // We need to keep client_rpc alive - in a real implementation
95
+ // we'd use a mechanism to tie its lifetime to the stream.
96
+ // For now, the shared_ptr in the lambda captures keeps it alive.
97
+
98
+ return {std::move(stream), Error::OK};
99
+ }
100
+
101
+ } // namespace starpc
@@ -0,0 +1,66 @@
1
+ #pragma once
2
+
3
+ #include <functional>
4
+ #include <memory>
5
+ #include <string>
6
+ #include <utility>
7
+
8
+ #include "client-rpc.hpp"
9
+ #include "errors.hpp"
10
+ #include "message.hpp"
11
+ #include "msg-stream.hpp"
12
+ #include "packet.hpp"
13
+ #include "stream.hpp"
14
+ #include "writer.hpp"
15
+
16
+ namespace starpc {
17
+
18
+ // OpenStreamFunc opens a stream with a remote.
19
+ // msgHandler must not be called concurrently.
20
+ // Matches Go OpenStreamFunc in client.go
21
+ using OpenStreamFunc =
22
+ std::function<std::pair<std::unique_ptr<PacketWriter>, Error>(
23
+ PacketDataHandler msg_handler, CloseHandler close_handler)>;
24
+
25
+ // Client implements a SRPC client which can initiate RPC streams.
26
+ // Matches Go Client interface in client.go
27
+ class Client {
28
+ public:
29
+ virtual ~Client() = default;
30
+
31
+ // ExecCall executes a request/reply RPC with the remote.
32
+ virtual Error ExecCall(const std::string &service, const std::string &method,
33
+ const Message &in, Message *out) = 0;
34
+
35
+ // NewStream starts a streaming RPC with the remote & returns the stream.
36
+ // firstMsg is optional (can be nullptr).
37
+ virtual std::pair<std::unique_ptr<Stream>, Error>
38
+ NewStream(const std::string &service, const std::string &method,
39
+ const Message *first_msg) = 0;
40
+ };
41
+
42
+ // ClientImpl is the default implementation of Client with a transport.
43
+ // Matches Go client struct in client.go
44
+ class ClientImpl : public Client {
45
+ public:
46
+ explicit ClientImpl(OpenStreamFunc open_stream)
47
+ : open_stream_(std::move(open_stream)) {}
48
+
49
+ Error ExecCall(const std::string &service, const std::string &method,
50
+ const Message &in, Message *out) override;
51
+
52
+ std::pair<std::unique_ptr<Stream>, Error>
53
+ NewStream(const std::string &service, const std::string &method,
54
+ const Message *first_msg) override;
55
+
56
+ private:
57
+ OpenStreamFunc open_stream_;
58
+ };
59
+
60
+ // NewClient constructs a client with a OpenStreamFunc.
61
+ // Matches Go NewClient function in client.go
62
+ inline std::unique_ptr<Client> NewClient(OpenStreamFunc open_stream) {
63
+ return std::make_unique<ClientImpl>(std::move(open_stream));
64
+ }
65
+
66
+ } // namespace starpc