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
package/srpc/client-rpc.hpp
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
|
-
#include <string>
|
|
4
|
-
|
|
5
3
|
#include "common-rpc.hpp"
|
|
6
4
|
#include "errors.hpp"
|
|
7
5
|
#include "msg-stream.hpp"
|
|
8
6
|
#include "packet.hpp"
|
|
9
7
|
#include "writer.hpp"
|
|
10
8
|
|
|
9
|
+
#include <string>
|
|
10
|
+
|
|
11
11
|
namespace srpc {
|
|
12
12
|
class Packet;
|
|
13
13
|
class CallStart;
|
|
@@ -15,54 +15,68 @@ class CallStart;
|
|
|
15
15
|
|
|
16
16
|
namespace starpc {
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
/*
|
|
19
|
+
* ClientRPC is the caller's side of one call. The transport feeds it packets
|
|
20
|
+
* through HandlePacketData and it writes through the PacketWriter given to
|
|
21
|
+
* Start.
|
|
22
|
+
*/
|
|
20
23
|
class ClientRPC : public CommonRPC, public MsgStreamRw {
|
|
21
24
|
public:
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
/* ClientRPC opens a call for service and method. */
|
|
26
|
+
ClientRPC(const std::string &service, const std::string &method);
|
|
27
|
+
~ClientRPC() override;
|
|
25
28
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
/*
|
|
30
|
+
* Start attaches a borrowed writer and writes CallStart once. The
|
|
31
|
+
* writer must outlive this call and its callbacks. The caller closes
|
|
32
|
+
* a rejected writer.
|
|
33
|
+
*/
|
|
34
|
+
Error Start(PacketWriter *writer, bool write_first_msg,
|
|
35
|
+
const std::string &first_msg);
|
|
31
36
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
Error HandlePacketData(const std::string &data);
|
|
37
|
+
/* HandlePacketData parses one serialized packet and handles it. */
|
|
38
|
+
Error HandlePacketData(const std::string &data);
|
|
35
39
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
void HandleStreamClose(Error close_err);
|
|
40
|
+
/* HandleStreamClose records the transport ending with its outcome. */
|
|
41
|
+
void HandleStreamClose(Error close_err);
|
|
39
42
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
Error HandlePacket(const srpc::Packet &pkt);
|
|
43
|
+
/* HandlePacket validates and dispatches one parsed packet. */
|
|
44
|
+
Error HandlePacket(const srpc::Packet &pkt);
|
|
43
45
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
/*
|
|
47
|
+
* HandleCallStart rejects a CallStart: the caller never receives one.
|
|
48
|
+
*/
|
|
49
|
+
Error HandleCallStart(const srpc::CallStart &pkt);
|
|
47
50
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
void Close();
|
|
51
|
+
/* Close cancels the call. */
|
|
52
|
+
void Close();
|
|
51
53
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
54
|
+
/* MsgStreamRw over the shared call state. */
|
|
55
|
+
Error ReadOne(std::string *out) override
|
|
56
|
+
{
|
|
57
|
+
return CommonRPC::ReadOne(out);
|
|
58
|
+
}
|
|
59
|
+
Error WriteCallData(const std::string &data, bool data_is_zero,
|
|
60
|
+
bool complete, Error err) override
|
|
61
|
+
{
|
|
62
|
+
return CommonRPC::WriteCallData(data, data_is_zero, complete,
|
|
63
|
+
err);
|
|
64
|
+
}
|
|
65
|
+
Error WriteCallCancel() override
|
|
66
|
+
{
|
|
67
|
+
return CommonRPC::WriteCallCancel();
|
|
68
|
+
}
|
|
69
|
+
std::string RemoteErrorMessage() const override
|
|
70
|
+
{
|
|
71
|
+
return CommonRPC::RemoteErrorMessage();
|
|
72
|
+
}
|
|
59
73
|
};
|
|
60
74
|
|
|
61
|
-
|
|
62
|
-
// Matches Go NewClientRPC function in client-rpc.go
|
|
75
|
+
/* NewClientRPC constructs a ClientRPC for service and method. */
|
|
63
76
|
inline std::unique_ptr<ClientRPC> NewClientRPC(const std::string &service,
|
|
64
|
-
|
|
65
|
-
|
|
77
|
+
const std::string &method)
|
|
78
|
+
{
|
|
79
|
+
return std::make_unique<ClientRPC>(service, method);
|
|
66
80
|
}
|
|
67
81
|
|
|
68
82
|
} // namespace starpc
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
|
|
3
|
+
import { ClientRPC } from './client-rpc.js'
|
|
4
|
+
import { isClosedBeforeCompletionError } from './errors.js'
|
|
5
|
+
import { Packet } from './rpcproto.pb.js'
|
|
6
|
+
|
|
7
|
+
describe('ClientRPC', () => {
|
|
8
|
+
it('fails a call whose transport ends before the remote completes it', async () => {
|
|
9
|
+
const call = new ClientRPC('svc', 'method')
|
|
10
|
+
await call.sink(packetSource(callDataPacket(new Uint8Array([1]))))
|
|
11
|
+
|
|
12
|
+
const received = new Array<number>()
|
|
13
|
+
const read = async () => {
|
|
14
|
+
for await (const data of call.rpcDataSource) {
|
|
15
|
+
received.push(data[0])
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
const err = await read().then(
|
|
19
|
+
() => undefined,
|
|
20
|
+
(err: unknown) => err,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
expect(received).toEqual([1])
|
|
24
|
+
expect(isClosedBeforeCompletionError(err)).toBe(true)
|
|
25
|
+
expect(isClosedBeforeCompletionError(call.isClosed)).toBe(true)
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('ends a call cleanly when the remote completes it before the transport ends', async () => {
|
|
29
|
+
const call = new ClientRPC('svc', 'method')
|
|
30
|
+
await call.sink(
|
|
31
|
+
packetSource(
|
|
32
|
+
callDataPacket(new Uint8Array([1])),
|
|
33
|
+
callDataPacket(undefined, true),
|
|
34
|
+
),
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
const received = new Array<number>()
|
|
38
|
+
for await (const data of call.rpcDataSource) {
|
|
39
|
+
received.push(data[0])
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
expect(received).toEqual([1])
|
|
43
|
+
expect(call.isClosed).toBe(false)
|
|
44
|
+
})
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
// callDataPacket builds an incoming call-data packet.
|
|
48
|
+
function callDataPacket(data?: Uint8Array, complete = false): Packet {
|
|
49
|
+
return Packet.create({
|
|
50
|
+
body: {
|
|
51
|
+
case: 'callData',
|
|
52
|
+
value: {
|
|
53
|
+
data: data ?? new Uint8Array(0),
|
|
54
|
+
dataIsZero: false,
|
|
55
|
+
complete,
|
|
56
|
+
error: '',
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// packetSource yields packets and then ends like a closed transport.
|
|
63
|
+
async function* packetSource(...packets: Packet[]): AsyncGenerator<Packet> {
|
|
64
|
+
for (const packet of packets) {
|
|
65
|
+
yield packet
|
|
66
|
+
}
|
|
67
|
+
}
|
package/srpc/client-rpc.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { CompleteMessage } from '@aptre/protobuf-es-lite'
|
|
2
2
|
import type { CallStart } from './rpcproto.pb.js'
|
|
3
3
|
import { CommonRPC } from './common-rpc.js'
|
|
4
|
+
import { ERR_CLOSED_BEFORE_COMPLETION } from './errors.js'
|
|
4
5
|
|
|
5
6
|
// ClientRPC is an ongoing RPC from the client side.
|
|
6
7
|
export class ClientRPC extends CommonRPC {
|
|
@@ -37,4 +38,13 @@ export class ClientRPC extends CommonRPC {
|
|
|
37
38
|
`unexpected server to client rpc: ${packet.rpcService || '<empty>'}/${packet.rpcMethod || '<empty>'}`,
|
|
38
39
|
)
|
|
39
40
|
}
|
|
41
|
+
|
|
42
|
+
// handleSourceEnd fails the call when the transport ends before the remote
|
|
43
|
+
// sent a completion or an error, so a truncated stream does not read as a
|
|
44
|
+
// complete one.
|
|
45
|
+
protected override async handleSourceEnd() {
|
|
46
|
+
if (!this.isRemoteCompleted) {
|
|
47
|
+
await this.close(new Error(ERR_CLOSED_BEFORE_COMPLETION))
|
|
48
|
+
}
|
|
49
|
+
}
|
|
40
50
|
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
package srpc
|
|
2
|
+
|
|
3
|
+
import (
|
|
4
|
+
"context"
|
|
5
|
+
"io"
|
|
6
|
+
"testing"
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
// callbackPacketWriter exercises synchronous transport callbacks.
|
|
10
|
+
type callbackPacketWriter struct {
|
|
11
|
+
// write delivers the outgoing packet before returning.
|
|
12
|
+
write func(*Packet) error
|
|
13
|
+
// close interrupts pending writes.
|
|
14
|
+
close func() error
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// WritePacket invokes the test's synchronous transport.
|
|
18
|
+
func (w *callbackPacketWriter) WritePacket(packet *Packet) error { return w.write(packet) }
|
|
19
|
+
|
|
20
|
+
// Close interrupts the test transport.
|
|
21
|
+
func (w *callbackPacketWriter) Close() error { return w.close() }
|
|
22
|
+
|
|
23
|
+
func TestClientRPCSynchronousTransport(t *testing.T) {
|
|
24
|
+
// A transport may deliver its reply before the request write returns.
|
|
25
|
+
rpc := NewClientRPC(t.Context(), "service", "method")
|
|
26
|
+
writer := &callbackPacketWriter{
|
|
27
|
+
write: func(packet *Packet) error {
|
|
28
|
+
return rpc.HandleCallData(&CallData{Data: []byte("reply"), Complete: true})
|
|
29
|
+
},
|
|
30
|
+
close: func() error {
|
|
31
|
+
rpc.HandleStreamClose(io.EOF)
|
|
32
|
+
return nil
|
|
33
|
+
},
|
|
34
|
+
}
|
|
35
|
+
if err := rpc.Start(writer, false, nil); err != nil {
|
|
36
|
+
t.Fatal(err)
|
|
37
|
+
}
|
|
38
|
+
t.Cleanup(rpc.Close)
|
|
39
|
+
|
|
40
|
+
// Both startup and cleanup callbacks must run outside the state lock.
|
|
41
|
+
data, err := rpc.ReadOne()
|
|
42
|
+
if err != nil {
|
|
43
|
+
t.Fatal(err)
|
|
44
|
+
}
|
|
45
|
+
if string(data) != "reply" {
|
|
46
|
+
t.Fatalf("unexpected response %q", data)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
func TestClientRPCCancelBlockedStart(t *testing.T) {
|
|
51
|
+
// Cancel while CallStart is blocked on a peer that never reads.
|
|
52
|
+
ctx, cancel := context.WithCancel(t.Context())
|
|
53
|
+
t.Cleanup(cancel)
|
|
54
|
+
rpc := NewClientRPC(ctx, "service", "method")
|
|
55
|
+
entered, closed := make(chan struct{}), make(chan struct{})
|
|
56
|
+
writer := &callbackPacketWriter{
|
|
57
|
+
write: func(*Packet) error {
|
|
58
|
+
close(entered)
|
|
59
|
+
<-closed
|
|
60
|
+
return io.ErrClosedPipe
|
|
61
|
+
},
|
|
62
|
+
close: func() error {
|
|
63
|
+
close(closed)
|
|
64
|
+
return nil
|
|
65
|
+
},
|
|
66
|
+
}
|
|
67
|
+
done := make(chan error, 1)
|
|
68
|
+
go func() { done <- rpc.Start(writer, false, nil) }()
|
|
69
|
+
<-entered
|
|
70
|
+
cancel()
|
|
71
|
+
if err := <-done; err != io.ErrClosedPipe {
|
|
72
|
+
t.Fatalf("blocked start returned %v", err)
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
var _ PacketWriter = (*callbackPacketWriter)(nil)
|
package/srpc/client.cpp
CHANGED
|
@@ -3,99 +3,111 @@
|
|
|
3
3
|
#include "client.hpp"
|
|
4
4
|
|
|
5
5
|
namespace starpc {
|
|
6
|
+
namespace {
|
|
7
|
+
|
|
8
|
+
/*
|
|
9
|
+
* ClientStream is the Stream over one opened call. The transport and the
|
|
10
|
+
* stream share the ClientRPC; destroying the writer joins the transport's
|
|
11
|
+
* readers, so the call outlives every callback.
|
|
12
|
+
*/
|
|
13
|
+
class ClientStream final : public Stream {
|
|
14
|
+
public:
|
|
15
|
+
ClientStream(std::shared_ptr<ClientRPC> rpc,
|
|
16
|
+
std::unique_ptr<PacketWriter> writer)
|
|
17
|
+
: rpc(std::move(rpc)),
|
|
18
|
+
writer(std::move(writer)),
|
|
19
|
+
messages(
|
|
20
|
+
this->rpc.get(), [this] { this->rpc->Close(); },
|
|
21
|
+
this->rpc->StopToken())
|
|
22
|
+
{
|
|
23
|
+
}
|
|
24
|
+
~ClientStream() override
|
|
25
|
+
{
|
|
26
|
+
Close();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
Error MsgSend(const Message &message) override
|
|
30
|
+
{
|
|
31
|
+
return messages.MsgSend(message);
|
|
32
|
+
}
|
|
33
|
+
Error MsgRecv(Message *message) override
|
|
34
|
+
{
|
|
35
|
+
return messages.MsgRecv(message);
|
|
36
|
+
}
|
|
37
|
+
Error CloseSend() override
|
|
38
|
+
{
|
|
39
|
+
return messages.CloseSend();
|
|
40
|
+
}
|
|
41
|
+
std::stop_token StopToken() const override
|
|
42
|
+
{
|
|
43
|
+
return rpc->StopToken();
|
|
44
|
+
}
|
|
45
|
+
std::string RemoteErrorMessage() const override
|
|
46
|
+
{
|
|
47
|
+
return rpc->RemoteErrorMessage();
|
|
48
|
+
}
|
|
49
|
+
Error Close() override
|
|
50
|
+
{
|
|
51
|
+
rpc->Close();
|
|
52
|
+
return Error::OK;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
private:
|
|
56
|
+
/* rpc is shared with the transport callbacks. */
|
|
57
|
+
std::shared_ptr<ClientRPC> rpc;
|
|
58
|
+
std::unique_ptr<PacketWriter> writer;
|
|
59
|
+
MsgStream messages;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
} // namespace
|
|
6
63
|
|
|
7
64
|
Error ClientImpl::ExecCall(const std::string &service,
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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;
|
|
65
|
+
const std::string &method, const Message &in,
|
|
66
|
+
Message *out)
|
|
67
|
+
{
|
|
68
|
+
auto [stream, err] = NewStream(service, method, &in);
|
|
69
|
+
if (err != Error::OK)
|
|
70
|
+
return err;
|
|
71
|
+
return stream->MsgRecv(out);
|
|
50
72
|
}
|
|
51
73
|
|
|
52
74
|
std::pair<std::unique_ptr<Stream>, Error>
|
|
53
75
|
ClientImpl::NewStream(const std::string &service, const std::string &method,
|
|
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
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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};
|
|
76
|
+
const Message *first_msg)
|
|
77
|
+
{
|
|
78
|
+
if (service.empty())
|
|
79
|
+
return {nullptr, Error::EmptyServiceID};
|
|
80
|
+
if (method.empty())
|
|
81
|
+
return {nullptr, Error::EmptyMethodID};
|
|
82
|
+
|
|
83
|
+
std::string first_data;
|
|
84
|
+
if (first_msg != nullptr && !first_msg->SerializeToString(&first_data))
|
|
85
|
+
return {nullptr, Error::InvalidMessage};
|
|
86
|
+
|
|
87
|
+
/*
|
|
88
|
+
* Transport callbacks share the call, without referring to this stack
|
|
89
|
+
* frame.
|
|
90
|
+
*/
|
|
91
|
+
auto rpc = std::make_shared<ClientRPC>(service, method);
|
|
92
|
+
auto [writer, err] = open_stream(
|
|
93
|
+
[rpc](const std::string &data) {
|
|
94
|
+
return rpc->HandlePacketData(data);
|
|
95
|
+
},
|
|
96
|
+
[rpc](Error closed) { rpc->HandleStreamClose(closed); });
|
|
97
|
+
if (err != Error::OK) {
|
|
98
|
+
if (writer)
|
|
99
|
+
(void)writer->Close();
|
|
100
|
+
return {nullptr, err};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
err = rpc->Start(writer.get(), first_msg != nullptr, first_data);
|
|
104
|
+
if (err != Error::OK) {
|
|
105
|
+
rpc->Close();
|
|
106
|
+
return {nullptr, err};
|
|
107
|
+
}
|
|
108
|
+
return {std::make_unique<ClientStream>(std::move(rpc),
|
|
109
|
+
std::move(writer)),
|
|
110
|
+
Error::OK};
|
|
99
111
|
}
|
|
100
112
|
|
|
101
113
|
} // namespace starpc
|
package/srpc/client.hpp
CHANGED
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
|
-
#include <functional>
|
|
4
|
-
#include <memory>
|
|
5
|
-
#include <string>
|
|
6
|
-
#include <utility>
|
|
7
|
-
|
|
8
3
|
#include "client-rpc.hpp"
|
|
9
4
|
#include "errors.hpp"
|
|
10
5
|
#include "message.hpp"
|
|
@@ -13,54 +8,70 @@
|
|
|
13
8
|
#include "stream.hpp"
|
|
14
9
|
#include "writer.hpp"
|
|
15
10
|
|
|
11
|
+
#include <functional>
|
|
12
|
+
#include <memory>
|
|
13
|
+
#include <string>
|
|
14
|
+
#include <utility>
|
|
15
|
+
|
|
16
16
|
namespace starpc {
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
/*
|
|
19
|
+
* OpenStreamFunc opens one stream over the transport. It returns the writer
|
|
20
|
+
* the caller writes packets through; msg_handler and close_handler must not
|
|
21
|
+
* be called concurrently. Matches Go OpenStreamFunc in client.go.
|
|
22
|
+
*/
|
|
21
23
|
using OpenStreamFunc =
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
std::function<std::pair<std::unique_ptr<PacketWriter>, Error>(
|
|
25
|
+
PacketDataHandler msg_handler, CloseHandler close_handler)>;
|
|
24
26
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
/*
|
|
28
|
+
* Client starts RPC streams over a transport. Matches the Go Client
|
|
29
|
+
* interface in client.go.
|
|
30
|
+
*/
|
|
27
31
|
class Client {
|
|
28
32
|
public:
|
|
29
|
-
|
|
33
|
+
virtual ~Client() = default;
|
|
30
34
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
35
|
+
/* ExecCall runs one request/reply RPC and parses the reply into out. */
|
|
36
|
+
virtual Error ExecCall(const std::string &service,
|
|
37
|
+
const std::string &method, const Message &in,
|
|
38
|
+
Message *out) = 0;
|
|
34
39
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
/*
|
|
41
|
+
* NewStream opens a streaming RPC and returns the stream. first_msg,
|
|
42
|
+
* when set, is sent as the call's first message.
|
|
43
|
+
*/
|
|
44
|
+
virtual std::pair<std::unique_ptr<Stream>, Error>
|
|
45
|
+
NewStream(const std::string &service, const std::string &method,
|
|
46
|
+
const Message *first_msg) = 0;
|
|
40
47
|
};
|
|
41
48
|
|
|
42
|
-
|
|
43
|
-
|
|
49
|
+
/*
|
|
50
|
+
* ClientImpl is the default Client over an OpenStreamFunc transport. Matches
|
|
51
|
+
* the Go client struct in client.go.
|
|
52
|
+
*/
|
|
44
53
|
class ClientImpl : public Client {
|
|
45
54
|
public:
|
|
46
|
-
|
|
47
|
-
|
|
55
|
+
explicit ClientImpl(OpenStreamFunc open_stream)
|
|
56
|
+
: open_stream(std::move(open_stream))
|
|
57
|
+
{
|
|
58
|
+
}
|
|
48
59
|
|
|
49
|
-
|
|
50
|
-
|
|
60
|
+
Error ExecCall(const std::string &service, const std::string &method,
|
|
61
|
+
const Message &in, Message *out) override;
|
|
51
62
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
63
|
+
std::pair<std::unique_ptr<Stream>, Error>
|
|
64
|
+
NewStream(const std::string &service, const std::string &method,
|
|
65
|
+
const Message *first_msg) override;
|
|
55
66
|
|
|
56
67
|
private:
|
|
57
|
-
|
|
68
|
+
OpenStreamFunc open_stream;
|
|
58
69
|
};
|
|
59
70
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
71
|
+
/* NewClient constructs a Client over open_stream. */
|
|
72
|
+
inline std::unique_ptr<Client> NewClient(OpenStreamFunc open_stream)
|
|
73
|
+
{
|
|
74
|
+
return std::make_unique<ClientImpl>(std::move(open_stream));
|
|
64
75
|
}
|
|
65
76
|
|
|
66
77
|
} // namespace starpc
|