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.
- package/dist/echo/echo.pb.js +1 -1
- package/dist/mock/mock.pb.js +1 -1
- package/dist/rpcstream/rpcstream.pb.js +1 -1
- package/dist/srpc/rpcproto.pb.js +1 -1
- package/echo/echo.pb.ts +1 -1
- package/echo/echo_e2e_test.cpp +1048 -0
- package/echo/echo_srpc.pb.cpp +125 -0
- package/echo/echo_srpc.pb.hpp +317 -0
- package/go.mod +7 -7
- package/go.sum +14 -14
- package/mock/mock.pb.ts +1 -1
- package/mock/mock_srpc.pb.cpp +45 -0
- package/mock/mock_srpc.pb.hpp +104 -0
- package/package.json +6 -5
- package/srpc/CMakeLists.txt +59 -0
- package/srpc/client-rpc.cpp +106 -0
- package/srpc/client-rpc.hpp +68 -0
- package/srpc/client.cpp +101 -0
- package/srpc/client.hpp +66 -0
- package/srpc/common-rpc.cpp +182 -0
- package/srpc/common-rpc.hpp +109 -0
- package/srpc/errors.hpp +71 -0
- package/srpc/handler.hpp +23 -0
- package/srpc/invoker.hpp +91 -0
- package/srpc/message.hpp +28 -0
- package/srpc/msg-stream.hpp +86 -0
- package/srpc/mux.cpp +109 -0
- package/srpc/mux.hpp +64 -0
- package/srpc/packet.cpp +83 -0
- package/srpc/packet.hpp +56 -0
- package/srpc/rpcproto.pb.ts +1 -1
- package/srpc/server-rpc.cpp +97 -0
- package/srpc/server-rpc.hpp +63 -0
- package/srpc/starpc.hpp +18 -0
- package/srpc/stream.hpp +61 -0
- package/srpc/writer.hpp +60 -0
|
@@ -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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
package/srpc/client.cpp
ADDED
|
@@ -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
|
package/srpc/client.hpp
ADDED
|
@@ -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
|