starpc 0.50.0 → 0.52.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/cmd/protoc-gen-es-starpc/typescript.ts +37 -0
- package/dist/cmd/protoc-gen-es-starpc/typescript.js +24 -0
- package/dist/echo/echo_srpc.pb.d.ts +44 -1
- package/dist/echo/server.d.ts +9 -8
- package/dist/echo/server.js +6 -6
- package/dist/integration/cross-language/ts-client.js +5 -79
- package/dist/integration/cross-language/ts-server.js +4 -100
- package/dist/mock/mock_srpc.pb.d.ts +14 -1
- package/dist/srpc/channel.js +6 -3
- package/dist/srpc/channel.test.js +20 -1
- package/dist/srpc/client.d.ts +1 -3
- package/dist/srpc/client.js +2 -22
- package/dist/srpc/common-rpc.d.ts +1 -9
- package/dist/srpc/common-rpc.js +4 -62
- package/dist/srpc/handler.d.ts +12 -4
- package/dist/srpc/index.d.ts +2 -4
- package/dist/srpc/index.js +1 -3
- package/dist/srpc/invoker.d.ts +2 -2
- package/dist/srpc/invoker.js +2 -2
- package/dist/srpc/rpcproto.pb.d.ts +0 -44
- package/dist/srpc/rpcproto.pb.js +0 -53
- package/dist/srpc/server-context.d.ts +11 -0
- package/dist/srpc/server-context.js +28 -0
- package/dist/srpc/server-rpc.js +3 -3
- package/dist/srpc/server.test.js +65 -2
- package/dist/srpc/watchdog.test.js +1 -0
- package/echo/echo.go +8 -8
- package/echo/echo.pb.go +12 -36
- package/echo/echo_srpc.pb.cpp +1 -1
- package/echo/echo_srpc.pb.go +1 -1
- package/echo/echo_srpc.pb.hpp +1 -1
- package/echo/echo_srpc.pb.rs +1 -1
- package/echo/echo_srpc.pb.ts +74 -0
- package/echo/server.go +1 -1
- package/echo/server.ts +24 -5
- package/go.mod +1 -1
- package/go.sum +2 -0
- package/integration/cross-language/go-client/main.go +4 -142
- package/integration/cross-language/go-server/main.go +2 -170
- package/integration/cross-language/run.bash +5 -105
- package/integration/cross-language/ts-client.ts +5 -90
- package/integration/cross-language/ts-server.ts +4 -112
- package/mock/mock.go +1 -1
- package/mock/mock.pb.go +12 -36
- package/mock/mock_srpc.pb.cpp +1 -1
- package/mock/mock_srpc.pb.go +1 -1
- package/mock/mock_srpc.pb.hpp +1 -1
- package/mock/mock_srpc.pb.rs +1 -1
- package/mock/mock_srpc.pb.ts +19 -1
- package/package.json +2 -2
- package/srpc/channel.test.ts +21 -1
- package/srpc/channel.ts +7 -3
- package/srpc/client-prefix.go +1 -1
- package/srpc/client-set.go +1 -1
- package/srpc/client.go +1 -1
- package/srpc/client.ts +4 -31
- package/srpc/common-rpc.go +15 -69
- package/srpc/common-rpc.ts +4 -77
- package/srpc/common-rpc_test.go +81 -0
- package/srpc/errors.go +26 -1
- package/srpc/handler.ts +54 -5
- package/srpc/index.ts +7 -4
- package/srpc/invoker-prefix.go +1 -1
- package/srpc/invoker.go +2 -2
- package/srpc/invoker.ts +20 -7
- package/srpc/message.go +1 -1
- package/srpc/msg-stream.go +1 -9
- package/srpc/mux-verbose.go +1 -1
- package/srpc/mux.go +1 -1
- package/srpc/rpcproto.pb.cc +4 -15
- package/srpc/rpcproto.pb.go +102 -419
- package/srpc/rpcproto.pb.h +0 -59
- package/srpc/rpcproto.pb.rs +0 -45
- package/srpc/rpcproto.pb.ts +0 -62
- package/srpc/rpcproto.proto +0 -16
- package/srpc/server-context.ts +55 -0
- package/srpc/server-rpc.go +1 -1
- package/srpc/server-rpc.ts +4 -6
- package/srpc/server.test.ts +94 -1
- package/srpc/stream-pipe.go +1 -1
- package/srpc/stream-rwc.go +1 -1
- package/srpc/stream.go +0 -20
- package/srpc/watchdog.test.ts +1 -0
- package/dist/rpcstream/receipt.test.d.ts +0 -1
- package/dist/rpcstream/receipt.test.js +0 -41
- package/dist/srpc/call-receipt.d.ts +0 -17
- package/dist/srpc/call-receipt.js +0 -106
- package/dist/srpc/call-receipt.test.d.ts +0 -1
- package/dist/srpc/call-receipt.test.js +0 -375
- package/dist/srpc/server-invocation.d.ts +0 -17
- package/dist/srpc/server-invocation.js +0 -37
- package/integration/cross-language/go-server/fixture-owner_test.go +0 -127
- package/srpc/call-receipt-e2e_test.go +0 -111
- package/srpc/call-receipt.go +0 -112
- package/srpc/call-receipt.test.ts +0 -441
- package/srpc/call-receipt.ts +0 -131
- package/srpc/call-receipt_test.go +0 -536
- package/srpc/schema-ownership_test.go +0 -115
- package/srpc/server-invocation.go +0 -40
- package/srpc/server-invocation.ts +0 -76
package/srpc/call-receipt.ts
DELETED
|
@@ -1,131 +0,0 @@
|
|
|
1
|
-
/// <reference lib="es2024.promise" />
|
|
2
|
-
import { ERR_RPC_ABORT } from './errors.js'
|
|
3
|
-
import { ClientRPC } from './client-rpc.js'
|
|
4
|
-
import { TerminalKind } from './rpcproto.pb.js'
|
|
5
|
-
|
|
6
|
-
// ReceiptRpc exposes held unary calls without widening ProtoRpc.
|
|
7
|
-
export interface ReceiptRpc {
|
|
8
|
-
requestWithReceipt(
|
|
9
|
-
service: string,
|
|
10
|
-
method: string,
|
|
11
|
-
data: Uint8Array,
|
|
12
|
-
abortSignal?: AbortSignal,
|
|
13
|
-
): Promise<HeldCall>
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// HeldCall contains the first response and its held terminal receipt.
|
|
17
|
-
export interface HeldCall {
|
|
18
|
-
readonly response: Uint8Array
|
|
19
|
-
readonly receipt: CallReceipt
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// CallReceipt holds a unary call until it is committed or aborted.
|
|
23
|
-
export class CallReceipt {
|
|
24
|
-
#call: ClientRPC
|
|
25
|
-
#iterator: AsyncIterator<Uint8Array>
|
|
26
|
-
#terminalPromise: Promise<IteratorResult<Uint8Array>>
|
|
27
|
-
#terminal?: 'committed' | 'aborted'
|
|
28
|
-
#done: Promise<void>
|
|
29
|
-
#requestCommitted = false
|
|
30
|
-
#resolveDone!: () => void
|
|
31
|
-
#rejectDone!: (reason?: unknown) => void
|
|
32
|
-
|
|
33
|
-
public constructor(call: ClientRPC, iterator: AsyncIterator<Uint8Array>) {
|
|
34
|
-
this.#call = call
|
|
35
|
-
this.#iterator = iterator
|
|
36
|
-
const { promise, resolve, reject } = Promise.withResolvers<void>()
|
|
37
|
-
this.#done = promise
|
|
38
|
-
this.#resolveDone = resolve
|
|
39
|
-
this.#rejectDone = reject
|
|
40
|
-
this.#done.catch(() => undefined)
|
|
41
|
-
this.#terminalPromise = this.#observeTerminal()
|
|
42
|
-
this.#terminalPromise.catch(() => undefined)
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
async #observeTerminal(): Promise<IteratorResult<Uint8Array>> {
|
|
46
|
-
try {
|
|
47
|
-
const result = await this.#iterator.next()
|
|
48
|
-
if (!result.done) {
|
|
49
|
-
throw new Error('unexpected trailing response data')
|
|
50
|
-
}
|
|
51
|
-
const terminal = this.#call.getTerminalKind()
|
|
52
|
-
if (
|
|
53
|
-
terminal !== TerminalKind.COMMITTED ||
|
|
54
|
-
!this.#requestCommitted ||
|
|
55
|
-
this.#terminal !== 'committed'
|
|
56
|
-
) {
|
|
57
|
-
throw new Error('receipt closed before commit')
|
|
58
|
-
}
|
|
59
|
-
return result
|
|
60
|
-
} catch (err) {
|
|
61
|
-
this.#rejectDone(err)
|
|
62
|
-
const error =
|
|
63
|
-
err instanceof Error ? err : new Error('receipt terminal failed')
|
|
64
|
-
try {
|
|
65
|
-
await this.#call.close(error)
|
|
66
|
-
} catch {
|
|
67
|
-
// The primary terminal error is already recorded on the receipt.
|
|
68
|
-
}
|
|
69
|
-
throw err
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// done resolves after committed close and rejects on terminal failure.
|
|
74
|
-
public get done(): Promise<void> {
|
|
75
|
-
return this.#done
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// settled reports whether a terminal transition has been claimed.
|
|
79
|
-
public get settled(): boolean {
|
|
80
|
-
return this.#terminal !== undefined
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
// commit sends request completion and waits for server finalization.
|
|
84
|
-
public async commit(): Promise<void> {
|
|
85
|
-
if (this.#terminal === 'aborted') {
|
|
86
|
-
throw new Error(ERR_RPC_ABORT)
|
|
87
|
-
}
|
|
88
|
-
if (this.#terminal === 'committed') {
|
|
89
|
-
return this.#done
|
|
90
|
-
}
|
|
91
|
-
this.#terminal = 'committed'
|
|
92
|
-
this.#requestCommitted = true
|
|
93
|
-
try {
|
|
94
|
-
await this.#call.writeCallData(undefined, true)
|
|
95
|
-
await this.#terminalPromise
|
|
96
|
-
await this.#call.close()
|
|
97
|
-
this.#resolveDone()
|
|
98
|
-
} catch (err) {
|
|
99
|
-
await this.#call.close(
|
|
100
|
-
err instanceof Error ? err : new Error('receipt commit failed'),
|
|
101
|
-
)
|
|
102
|
-
this.#rejectDone(err)
|
|
103
|
-
throw err
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
// abort sends request cancellation and never rejects.
|
|
108
|
-
public async abort(reason?: Error): Promise<void> {
|
|
109
|
-
if (this.#terminal !== undefined) {
|
|
110
|
-
return
|
|
111
|
-
}
|
|
112
|
-
this.#terminal = 'aborted'
|
|
113
|
-
let terminalError: unknown = reason
|
|
114
|
-
try {
|
|
115
|
-
await this.#call.writeCallCancel(true)
|
|
116
|
-
} catch (err) {
|
|
117
|
-
terminalError ??= err
|
|
118
|
-
}
|
|
119
|
-
try {
|
|
120
|
-
await this.#call.close()
|
|
121
|
-
} catch (err) {
|
|
122
|
-
terminalError ??= err
|
|
123
|
-
}
|
|
124
|
-
this.#rejectDone(terminalError ?? new Error(ERR_RPC_ABORT))
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// asyncDispose aborts a receipt that has not reached a terminal.
|
|
128
|
-
public async [Symbol.asyncDispose](): Promise<void> {
|
|
129
|
-
await this.abort()
|
|
130
|
-
}
|
|
131
|
-
}
|
|
@@ -1,536 +0,0 @@
|
|
|
1
|
-
package srpc
|
|
2
|
-
|
|
3
|
-
import (
|
|
4
|
-
"context"
|
|
5
|
-
"errors"
|
|
6
|
-
"io"
|
|
7
|
-
"sync"
|
|
8
|
-
"sync/atomic"
|
|
9
|
-
"testing"
|
|
10
|
-
"time"
|
|
11
|
-
|
|
12
|
-
"github.com/sirupsen/logrus"
|
|
13
|
-
)
|
|
14
|
-
|
|
15
|
-
type receiptTestStream struct {
|
|
16
|
-
ctx context.Context
|
|
17
|
-
recvCount atomic.Int32
|
|
18
|
-
ack <-chan error
|
|
19
|
-
ackErr error
|
|
20
|
-
terminal TerminalKind
|
|
21
|
-
terminalOkay bool
|
|
22
|
-
closeSend atomic.Int32
|
|
23
|
-
closeCount atomic.Int32
|
|
24
|
-
closeErr error
|
|
25
|
-
serviceSeen string
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
func (s *receiptTestStream) Context() context.Context {
|
|
29
|
-
return s.ctx
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
func (s *receiptTestStream) MsgSend(Message) error {
|
|
33
|
-
return nil
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
func (s *receiptTestStream) MsgRecv(msg Message) error {
|
|
37
|
-
if s.recvCount.Add(1) == 1 {
|
|
38
|
-
return msg.UnmarshalVT([]byte("response"))
|
|
39
|
-
}
|
|
40
|
-
if s.ack != nil {
|
|
41
|
-
return <-s.ack
|
|
42
|
-
}
|
|
43
|
-
return s.ackErr
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
func (s *receiptTestStream) CloseSend() error {
|
|
47
|
-
s.closeSend.Add(1)
|
|
48
|
-
return nil
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
func (s *receiptTestStream) Close() error {
|
|
52
|
-
s.closeCount.Add(1)
|
|
53
|
-
return s.closeErr
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
func (s *receiptTestStream) receiptTerminalKind() (TerminalKind, bool) {
|
|
57
|
-
return s.terminal, s.terminalOkay
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
type receiptTestClient struct {
|
|
61
|
-
stream *receiptTestStream
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
func (c *receiptTestClient) ExecCall(context.Context, string, string, Message, Message) error {
|
|
65
|
-
return nil
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
func (c *receiptTestClient) NewStream(
|
|
69
|
-
_ context.Context,
|
|
70
|
-
service, _ string,
|
|
71
|
-
_ Message,
|
|
72
|
-
) (Stream, error) {
|
|
73
|
-
c.stream.serviceSeen = service
|
|
74
|
-
return c.stream, nil
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
func TestCallReceiptCommitWaitsForServerAcknowledgment(t *testing.T) {
|
|
78
|
-
ack := make(chan error, 1)
|
|
79
|
-
stream := &receiptTestStream{ctx: context.Background(), ack: ack}
|
|
80
|
-
client := &receiptTestClient{stream: stream}
|
|
81
|
-
out := NewRawMessage(nil, true)
|
|
82
|
-
receipt, err := ExecCallReceipt(
|
|
83
|
-
context.Background(), client, "service", "method", NewRawMessage(nil, true), out,
|
|
84
|
-
)
|
|
85
|
-
if err != nil {
|
|
86
|
-
t.Fatalf("exec receipt: %v", err)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
commitDone := make(chan error, 1)
|
|
90
|
-
go func() { commitDone <- receipt.Commit() }()
|
|
91
|
-
select {
|
|
92
|
-
case err := <-commitDone:
|
|
93
|
-
t.Fatalf("commit returned before acknowledgment: %v", err)
|
|
94
|
-
case <-time.After(time.Millisecond):
|
|
95
|
-
}
|
|
96
|
-
if got := stream.closeSend.Load(); got != 1 {
|
|
97
|
-
t.Fatalf("close send count = %d, want 1", got)
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
stream.terminal = TerminalKind_TERMINAL_KIND_COMMITTED
|
|
101
|
-
stream.terminalOkay = true
|
|
102
|
-
ack <- io.EOF
|
|
103
|
-
select {
|
|
104
|
-
case err := <-commitDone:
|
|
105
|
-
if err != nil {
|
|
106
|
-
t.Fatalf("commit: %v", err)
|
|
107
|
-
}
|
|
108
|
-
case <-time.After(time.Second):
|
|
109
|
-
t.Fatal("commit did not return after acknowledgment")
|
|
110
|
-
}
|
|
111
|
-
if got := stream.closeCount.Load(); got != 1 {
|
|
112
|
-
t.Fatalf("cleanup close count = %d, want 1", got)
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
func TestCallReceiptRejectsBareCloseAfterCloseSend(t *testing.T) {
|
|
117
|
-
stream := &receiptTestStream{
|
|
118
|
-
ctx: context.Background(),
|
|
119
|
-
ackErr: io.EOF,
|
|
120
|
-
terminal: TerminalKind_TERMINAL_KIND_CLOSED,
|
|
121
|
-
terminalOkay: true,
|
|
122
|
-
}
|
|
123
|
-
receipt, err := ExecCallReceipt(
|
|
124
|
-
context.Background(), &receiptTestClient{stream: stream},
|
|
125
|
-
"service", "method", NewRawMessage(nil, true), NewRawMessage(nil, true),
|
|
126
|
-
)
|
|
127
|
-
if err != nil {
|
|
128
|
-
t.Fatalf("exec receipt: %v", err)
|
|
129
|
-
}
|
|
130
|
-
if err := receipt.Commit(); err == nil {
|
|
131
|
-
t.Fatal("commit accepted bare close after CloseSend")
|
|
132
|
-
}
|
|
133
|
-
if got := stream.closeSend.Load(); got != 1 {
|
|
134
|
-
t.Fatalf("close send count = %d, want 1", got)
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
func TestCallReceiptCommitIgnoresCleanupError(t *testing.T) {
|
|
139
|
-
stream := &receiptTestStream{
|
|
140
|
-
ctx: context.Background(),
|
|
141
|
-
ackErr: io.EOF,
|
|
142
|
-
terminal: TerminalKind_TERMINAL_KIND_COMMITTED,
|
|
143
|
-
terminalOkay: true,
|
|
144
|
-
closeErr: ErrCompleted,
|
|
145
|
-
}
|
|
146
|
-
receipt, err := ExecCallReceipt(
|
|
147
|
-
context.Background(), &receiptTestClient{stream: stream},
|
|
148
|
-
"service", "method", NewRawMessage(nil, true), NewRawMessage(nil, true),
|
|
149
|
-
)
|
|
150
|
-
if err != nil {
|
|
151
|
-
t.Fatalf("exec receipt: %v", err)
|
|
152
|
-
}
|
|
153
|
-
if err := receipt.Commit(); err != nil {
|
|
154
|
-
t.Fatalf("commit = %v, want nil", err)
|
|
155
|
-
}
|
|
156
|
-
if got := stream.closeSend.Load(); got != 1 {
|
|
157
|
-
t.Fatalf("close send count = %d, want 1", got)
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
func TestCallReceiptCommitRejectsTrailingResponse(t *testing.T) {
|
|
162
|
-
stream := &receiptTestStream{ctx: context.Background()}
|
|
163
|
-
receipt, err := ExecCallReceipt(
|
|
164
|
-
context.Background(), &receiptTestClient{stream: stream},
|
|
165
|
-
"service", "method", NewRawMessage(nil, true), NewRawMessage(nil, true),
|
|
166
|
-
)
|
|
167
|
-
if err != nil {
|
|
168
|
-
t.Fatalf("exec receipt: %v", err)
|
|
169
|
-
}
|
|
170
|
-
if err := receipt.Commit(); err == nil {
|
|
171
|
-
t.Fatal("commit accepted trailing response data")
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
func TestCallReceiptCommitReturnsAcknowledgmentError(t *testing.T) {
|
|
176
|
-
ackErr := errors.New("ack transport loss")
|
|
177
|
-
stream := &receiptTestStream{
|
|
178
|
-
ctx: context.Background(),
|
|
179
|
-
ackErr: ackErr,
|
|
180
|
-
}
|
|
181
|
-
receipt, err := ExecCallReceipt(
|
|
182
|
-
context.Background(), &receiptTestClient{stream: stream},
|
|
183
|
-
"service", "method", NewRawMessage(nil, true), NewRawMessage(nil, true),
|
|
184
|
-
)
|
|
185
|
-
if err != nil {
|
|
186
|
-
t.Fatalf("exec receipt: %v", err)
|
|
187
|
-
}
|
|
188
|
-
if err := receipt.Commit(); !errors.Is(err, ackErr) {
|
|
189
|
-
t.Fatalf("commit error = %v, want %v", err, ackErr)
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
func TestCallReceiptCommitAbortExclusion(t *testing.T) {
|
|
194
|
-
for range 100 {
|
|
195
|
-
stream := &receiptTestStream{
|
|
196
|
-
ctx: context.Background(),
|
|
197
|
-
ackErr: io.EOF,
|
|
198
|
-
terminal: TerminalKind_TERMINAL_KIND_COMMITTED,
|
|
199
|
-
terminalOkay: true,
|
|
200
|
-
}
|
|
201
|
-
receipt, err := ExecCallReceipt(
|
|
202
|
-
context.Background(), &receiptTestClient{stream: stream},
|
|
203
|
-
"service", "method", NewRawMessage(nil, true), NewRawMessage(nil, true),
|
|
204
|
-
)
|
|
205
|
-
if err != nil {
|
|
206
|
-
t.Fatalf("exec receipt: %v", err)
|
|
207
|
-
}
|
|
208
|
-
var wg sync.WaitGroup
|
|
209
|
-
wg.Add(2)
|
|
210
|
-
go func() {
|
|
211
|
-
defer wg.Done()
|
|
212
|
-
_ = receipt.Commit()
|
|
213
|
-
}()
|
|
214
|
-
go func() {
|
|
215
|
-
defer wg.Done()
|
|
216
|
-
_ = receipt.Abort()
|
|
217
|
-
}()
|
|
218
|
-
wg.Wait()
|
|
219
|
-
if got := stream.closeCount.Load(); got != 1 {
|
|
220
|
-
t.Fatalf("cleanup close count = %d, want 1", got)
|
|
221
|
-
}
|
|
222
|
-
if got := stream.closeSend.Load(); got > 1 {
|
|
223
|
-
t.Fatalf("close send count = %d, want at most 1", got)
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
func TestExecCallReceiptPreservesNewStreamWrappers(t *testing.T) {
|
|
229
|
-
log := logrus.New()
|
|
230
|
-
cases := []struct {
|
|
231
|
-
name string
|
|
232
|
-
client Client
|
|
233
|
-
service string
|
|
234
|
-
expected string
|
|
235
|
-
}{
|
|
236
|
-
{
|
|
237
|
-
name: "prefix",
|
|
238
|
-
client: NewPrefixClient(newReceiptClient(), []string{"prefix/"}),
|
|
239
|
-
service: "prefix/service",
|
|
240
|
-
expected: "service",
|
|
241
|
-
},
|
|
242
|
-
{
|
|
243
|
-
name: "set",
|
|
244
|
-
client: NewClientSet([]Client{newReceiptClient()}),
|
|
245
|
-
service: "service",
|
|
246
|
-
expected: "service",
|
|
247
|
-
},
|
|
248
|
-
{
|
|
249
|
-
name: "verbose",
|
|
250
|
-
client: NewVClient(newReceiptClient(), logrus.NewEntry(log)),
|
|
251
|
-
service: "service",
|
|
252
|
-
expected: "service",
|
|
253
|
-
},
|
|
254
|
-
}
|
|
255
|
-
for _, tc := range cases {
|
|
256
|
-
t.Run(tc.name, func(t *testing.T) {
|
|
257
|
-
out := NewRawMessage(nil, true)
|
|
258
|
-
receipt, err := ExecCallReceipt(
|
|
259
|
-
context.Background(), tc.client, tc.service, "method",
|
|
260
|
-
NewRawMessage(nil, true), out,
|
|
261
|
-
)
|
|
262
|
-
if err != nil {
|
|
263
|
-
t.Fatalf("exec receipt: %v", err)
|
|
264
|
-
}
|
|
265
|
-
if err := receipt.Abort(); err != nil {
|
|
266
|
-
t.Fatalf("abort: %v", err)
|
|
267
|
-
}
|
|
268
|
-
var stream *receiptTestStream
|
|
269
|
-
switch client := tc.client.(type) {
|
|
270
|
-
case *PrefixClient:
|
|
271
|
-
stream = client.client.(*receiptTestClient).stream
|
|
272
|
-
case *ClientSet:
|
|
273
|
-
stream = client.clients[0].(*receiptTestClient).stream
|
|
274
|
-
case *VClient:
|
|
275
|
-
stream = client.client.(*receiptTestClient).stream
|
|
276
|
-
}
|
|
277
|
-
if stream.serviceSeen != tc.expected {
|
|
278
|
-
t.Fatalf("service = %q, want %q", stream.serviceSeen, tc.expected)
|
|
279
|
-
}
|
|
280
|
-
})
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
func newReceiptClient() *receiptTestClient {
|
|
285
|
-
return &receiptTestClient{
|
|
286
|
-
stream: &receiptTestStream{
|
|
287
|
-
ctx: context.Background(),
|
|
288
|
-
ackErr: io.EOF,
|
|
289
|
-
terminal: TerminalKind_TERMINAL_KIND_COMMITTED,
|
|
290
|
-
terminalOkay: true,
|
|
291
|
-
},
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
func TestServerInvocationTerminalClassification(t *testing.T) {
|
|
296
|
-
cases := []struct {
|
|
297
|
-
name string
|
|
298
|
-
act func(*ServerRPC)
|
|
299
|
-
want TerminalKind
|
|
300
|
-
}{
|
|
301
|
-
{
|
|
302
|
-
name: "explicit complete",
|
|
303
|
-
act: func(rpc *ServerRPC) {
|
|
304
|
-
if err := rpc.HandleCallData(NewCallDataPacket(nil, false, true, nil).GetCallData()); err != nil {
|
|
305
|
-
t.Fatalf("handle complete: %v", err)
|
|
306
|
-
}
|
|
307
|
-
},
|
|
308
|
-
want: TerminalKind_TERMINAL_KIND_COMMITTED,
|
|
309
|
-
},
|
|
310
|
-
{
|
|
311
|
-
name: "cancel",
|
|
312
|
-
act: func(rpc *ServerRPC) {
|
|
313
|
-
if err := rpc.HandleCallCancel(); err != nil {
|
|
314
|
-
t.Fatalf("handle cancel: %v", err)
|
|
315
|
-
}
|
|
316
|
-
},
|
|
317
|
-
want: TerminalKind_TERMINAL_KIND_CANCELED,
|
|
318
|
-
},
|
|
319
|
-
{
|
|
320
|
-
name: "loss",
|
|
321
|
-
act: func(rpc *ServerRPC) {
|
|
322
|
-
rpc.HandleStreamClose(errors.New("transport loss"))
|
|
323
|
-
},
|
|
324
|
-
want: TerminalKind_TERMINAL_KIND_TRANSPORT_LOST,
|
|
325
|
-
},
|
|
326
|
-
|
|
327
|
-
{
|
|
328
|
-
name: "context canceled transport close",
|
|
329
|
-
act: func(rpc *ServerRPC) {
|
|
330
|
-
rpc.HandleStreamClose(context.Canceled)
|
|
331
|
-
},
|
|
332
|
-
want: TerminalKind_TERMINAL_KIND_TRANSPORT_LOST,
|
|
333
|
-
},
|
|
334
|
-
{
|
|
335
|
-
name: "remote error packet",
|
|
336
|
-
act: func(rpc *ServerRPC) {
|
|
337
|
-
if err := rpc.HandleCallData(&CallData{Error: "remote error"}); err != nil {
|
|
338
|
-
t.Fatalf("handle remote error: %v", err)
|
|
339
|
-
}
|
|
340
|
-
},
|
|
341
|
-
want: TerminalKind_TERMINAL_KIND_TRANSPORT_LOST,
|
|
342
|
-
},
|
|
343
|
-
{
|
|
344
|
-
name: "remote error completion packet",
|
|
345
|
-
act: func(rpc *ServerRPC) {
|
|
346
|
-
if err := rpc.HandleCallData(&CallData{
|
|
347
|
-
Complete: true,
|
|
348
|
-
Error: "remote error",
|
|
349
|
-
}); err != nil {
|
|
350
|
-
t.Fatalf("handle remote error completion: %v", err)
|
|
351
|
-
}
|
|
352
|
-
},
|
|
353
|
-
want: TerminalKind_TERMINAL_KIND_TRANSPORT_LOST,
|
|
354
|
-
},
|
|
355
|
-
|
|
356
|
-
{
|
|
357
|
-
name: "bare close",
|
|
358
|
-
act: func(rpc *ServerRPC) {
|
|
359
|
-
rpc.HandleStreamClose(nil)
|
|
360
|
-
},
|
|
361
|
-
want: TerminalKind_TERMINAL_KIND_CLOSED,
|
|
362
|
-
},
|
|
363
|
-
}
|
|
364
|
-
for _, tc := range cases {
|
|
365
|
-
t.Run(tc.name, func(t *testing.T) {
|
|
366
|
-
rpc := NewServerRPC(context.Background(), nil, &closeCountingPacketWriter{})
|
|
367
|
-
result := make(chan TerminalKind, 1)
|
|
368
|
-
go func() {
|
|
369
|
-
kind, err := rpc.WaitTerminal(context.Background())
|
|
370
|
-
if err != nil {
|
|
371
|
-
t.Errorf("wait terminal: %v", err)
|
|
372
|
-
}
|
|
373
|
-
result <- kind
|
|
374
|
-
}()
|
|
375
|
-
tc.act(rpc)
|
|
376
|
-
select {
|
|
377
|
-
case got := <-result:
|
|
378
|
-
if got != tc.want {
|
|
379
|
-
t.Fatalf("terminal = %v, want %v", got, tc.want)
|
|
380
|
-
}
|
|
381
|
-
case <-time.After(time.Second):
|
|
382
|
-
t.Fatal("wait terminal did not return")
|
|
383
|
-
}
|
|
384
|
-
if tc.name == "bare close" && rpc.remoteCompleted {
|
|
385
|
-
t.Fatal("bare close marked remote completion")
|
|
386
|
-
}
|
|
387
|
-
if tc.name == "explicit complete" && !rpc.remoteCompleted {
|
|
388
|
-
t.Fatal("explicit completion did not mark remote completion")
|
|
389
|
-
}
|
|
390
|
-
if tc.name == "remote error packet" && rpc.remoteCompleted {
|
|
391
|
-
t.Fatal("remote error packet marked remote completion")
|
|
392
|
-
}
|
|
393
|
-
if tc.name == "remote error completion packet" && rpc.remoteCompleted {
|
|
394
|
-
t.Fatal("remote error completion packet marked remote completion")
|
|
395
|
-
}
|
|
396
|
-
})
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
func TestServerInvocationTerminalIsMonotonic(t *testing.T) {
|
|
401
|
-
cases := []struct {
|
|
402
|
-
name string
|
|
403
|
-
act func(*ServerRPC)
|
|
404
|
-
}{
|
|
405
|
-
{
|
|
406
|
-
name: "completion then transport loss",
|
|
407
|
-
act: func(rpc *ServerRPC) {
|
|
408
|
-
_ = rpc.HandleCallData(
|
|
409
|
-
NewCallDataPacket(nil, false, true, nil).GetCallData(),
|
|
410
|
-
)
|
|
411
|
-
rpc.HandleStreamClose(errors.New("transport loss"))
|
|
412
|
-
},
|
|
413
|
-
},
|
|
414
|
-
{
|
|
415
|
-
name: "completion then cancel",
|
|
416
|
-
act: func(rpc *ServerRPC) {
|
|
417
|
-
_ = rpc.HandleCallData(
|
|
418
|
-
NewCallDataPacket(nil, false, true, nil).GetCallData(),
|
|
419
|
-
)
|
|
420
|
-
_ = rpc.HandleCallCancel()
|
|
421
|
-
},
|
|
422
|
-
},
|
|
423
|
-
}
|
|
424
|
-
for _, tc := range cases {
|
|
425
|
-
t.Run(tc.name, func(t *testing.T) {
|
|
426
|
-
rpc := NewServerRPC(context.Background(), nil, &closeCountingPacketWriter{})
|
|
427
|
-
tc.act(rpc)
|
|
428
|
-
kind, err := rpc.WaitTerminal(context.Background())
|
|
429
|
-
if err != nil {
|
|
430
|
-
t.Fatalf("wait terminal: %v", err)
|
|
431
|
-
}
|
|
432
|
-
if kind != TerminalKind_TERMINAL_KIND_COMMITTED {
|
|
433
|
-
t.Fatalf("terminal = %v, want committed", kind)
|
|
434
|
-
}
|
|
435
|
-
})
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
func TestServerInvocationGenuineAbandonment(t *testing.T) {
|
|
440
|
-
rpc := NewServerRPC(context.Background(), nil, &closeCountingPacketWriter{})
|
|
441
|
-
ownerCtx, ownerCancel := context.WithCancel(context.Background())
|
|
442
|
-
result := make(chan TerminalKind, 1)
|
|
443
|
-
go func() {
|
|
444
|
-
kind, err := rpc.WaitTerminal(ownerCtx)
|
|
445
|
-
if !errors.Is(err, context.Canceled) {
|
|
446
|
-
return
|
|
447
|
-
}
|
|
448
|
-
result <- kind
|
|
449
|
-
}()
|
|
450
|
-
ownerCancel()
|
|
451
|
-
select {
|
|
452
|
-
case got := <-result:
|
|
453
|
-
if got != TerminalKind_TERMINAL_KIND_ABANDONED {
|
|
454
|
-
t.Fatalf("terminal = %v, want %v", got, TerminalKind_TERMINAL_KIND_ABANDONED)
|
|
455
|
-
}
|
|
456
|
-
case <-time.After(time.Second):
|
|
457
|
-
t.Fatal("wait terminal did not return on owner cancellation")
|
|
458
|
-
}
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
func TestServerInvocationTerminalPrecedesOwnerCancellation(t *testing.T) {
|
|
462
|
-
cases := []struct {
|
|
463
|
-
name string
|
|
464
|
-
act func(*ServerRPC)
|
|
465
|
-
want TerminalKind
|
|
466
|
-
}{
|
|
467
|
-
{
|
|
468
|
-
name: "explicit complete",
|
|
469
|
-
act: func(rpc *ServerRPC) {
|
|
470
|
-
if err := rpc.HandleCallData(NewCallDataPacket(nil, false, true, nil).GetCallData()); err != nil {
|
|
471
|
-
t.Fatalf("handle complete: %v", err)
|
|
472
|
-
}
|
|
473
|
-
},
|
|
474
|
-
want: TerminalKind_TERMINAL_KIND_COMMITTED,
|
|
475
|
-
},
|
|
476
|
-
{
|
|
477
|
-
name: "cancel",
|
|
478
|
-
act: func(rpc *ServerRPC) {
|
|
479
|
-
if err := rpc.HandleCallCancel(); err != nil {
|
|
480
|
-
t.Fatalf("handle cancel: %v", err)
|
|
481
|
-
}
|
|
482
|
-
},
|
|
483
|
-
want: TerminalKind_TERMINAL_KIND_CANCELED,
|
|
484
|
-
},
|
|
485
|
-
{
|
|
486
|
-
name: "loss",
|
|
487
|
-
act: func(rpc *ServerRPC) {
|
|
488
|
-
rpc.HandleStreamClose(errors.New("transport loss"))
|
|
489
|
-
},
|
|
490
|
-
want: TerminalKind_TERMINAL_KIND_TRANSPORT_LOST,
|
|
491
|
-
},
|
|
492
|
-
{
|
|
493
|
-
name: "bare close",
|
|
494
|
-
act: func(rpc *ServerRPC) {
|
|
495
|
-
rpc.HandleStreamClose(nil)
|
|
496
|
-
},
|
|
497
|
-
want: TerminalKind_TERMINAL_KIND_CLOSED,
|
|
498
|
-
},
|
|
499
|
-
}
|
|
500
|
-
for _, tc := range cases {
|
|
501
|
-
t.Run(tc.name, func(t *testing.T) {
|
|
502
|
-
rpc := NewServerRPC(context.Background(), nil, &closeCountingPacketWriter{})
|
|
503
|
-
ownerCtx, ownerCancel := context.WithCancel(context.Background())
|
|
504
|
-
tc.act(rpc)
|
|
505
|
-
ownerCancel()
|
|
506
|
-
|
|
507
|
-
kind, err := rpc.WaitTerminal(ownerCtx)
|
|
508
|
-
if err != nil {
|
|
509
|
-
t.Fatalf("wait terminal: %v", err)
|
|
510
|
-
}
|
|
511
|
-
if kind != tc.want {
|
|
512
|
-
t.Fatalf("terminal = %v, want %v", kind, tc.want)
|
|
513
|
-
}
|
|
514
|
-
})
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
func TestServerInvocationAccessor(t *testing.T) {
|
|
519
|
-
found := make(chan bool, 1)
|
|
520
|
-
rpc := NewServerRPC(context.Background(), InvokerFunc(func(_, _ string, strm Stream) (bool, error) {
|
|
521
|
-
_, ok := GetServerInvocation(strm.Context())
|
|
522
|
-
found <- ok
|
|
523
|
-
return true, nil
|
|
524
|
-
}), &closeCountingPacketWriter{})
|
|
525
|
-
if err := rpc.HandleCallStart(NewCallStartPacket("service", "method", nil, false).GetCallStart()); err != nil {
|
|
526
|
-
t.Fatalf("handle start: %v", err)
|
|
527
|
-
}
|
|
528
|
-
select {
|
|
529
|
-
case ok := <-found:
|
|
530
|
-
if !ok {
|
|
531
|
-
t.Fatal("server invocation missing from stream context")
|
|
532
|
-
}
|
|
533
|
-
case <-time.After(time.Second):
|
|
534
|
-
t.Fatal("invoker did not run")
|
|
535
|
-
}
|
|
536
|
-
}
|