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/echo/echo_srpc.pb.ts
CHANGED
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
buildEncodeMessageTransform,
|
|
12
12
|
MessageStream,
|
|
13
13
|
ProtoRpc,
|
|
14
|
+
ServerContext,
|
|
14
15
|
} from 'starpc'
|
|
15
16
|
|
|
16
17
|
/**
|
|
@@ -151,6 +152,79 @@ export interface Echoer {
|
|
|
151
152
|
DoNothing(request: Empty, abortSignal?: AbortSignal): Promise<Empty>
|
|
152
153
|
}
|
|
153
154
|
|
|
155
|
+
/**
|
|
156
|
+
* Echoer service returns the given message.
|
|
157
|
+
*
|
|
158
|
+
* @generated from service echo.Echoer
|
|
159
|
+
*/
|
|
160
|
+
export interface EchoerHandler {
|
|
161
|
+
/**
|
|
162
|
+
* Echo returns the given message.
|
|
163
|
+
*
|
|
164
|
+
* @generated from rpc echo.Echoer.Echo
|
|
165
|
+
*/
|
|
166
|
+
Echo(
|
|
167
|
+
request: EchoMsg,
|
|
168
|
+
abortSignal: AbortSignal,
|
|
169
|
+
context: ServerContext,
|
|
170
|
+
): Promise<EchoMsg>
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* EchoServerStream is an example of a server -> client one-way stream.
|
|
174
|
+
*
|
|
175
|
+
* @generated from rpc echo.Echoer.EchoServerStream
|
|
176
|
+
*/
|
|
177
|
+
EchoServerStream(
|
|
178
|
+
request: EchoMsg,
|
|
179
|
+
abortSignal: AbortSignal,
|
|
180
|
+
context: ServerContext,
|
|
181
|
+
): MessageStream<EchoMsg>
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* EchoClientStream is an example of client->server one-way stream.
|
|
185
|
+
*
|
|
186
|
+
* @generated from rpc echo.Echoer.EchoClientStream
|
|
187
|
+
*/
|
|
188
|
+
EchoClientStream(
|
|
189
|
+
request: MessageStream<EchoMsg>,
|
|
190
|
+
abortSignal: AbortSignal,
|
|
191
|
+
context: ServerContext,
|
|
192
|
+
): Promise<EchoMsg>
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* EchoBidiStream is an example of a two-way stream.
|
|
196
|
+
*
|
|
197
|
+
* @generated from rpc echo.Echoer.EchoBidiStream
|
|
198
|
+
*/
|
|
199
|
+
EchoBidiStream(
|
|
200
|
+
request: MessageStream<EchoMsg>,
|
|
201
|
+
abortSignal: AbortSignal,
|
|
202
|
+
context: ServerContext,
|
|
203
|
+
): MessageStream<EchoMsg>
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* RpcStream opens a nested rpc call stream.
|
|
207
|
+
*
|
|
208
|
+
* @generated from rpc echo.Echoer.RpcStream
|
|
209
|
+
*/
|
|
210
|
+
RpcStream(
|
|
211
|
+
request: MessageStream<RpcStreamPacket>,
|
|
212
|
+
abortSignal: AbortSignal,
|
|
213
|
+
context: ServerContext,
|
|
214
|
+
): MessageStream<RpcStreamPacket>
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* DoNothing does nothing.
|
|
218
|
+
*
|
|
219
|
+
* @generated from rpc echo.Echoer.DoNothing
|
|
220
|
+
*/
|
|
221
|
+
DoNothing(
|
|
222
|
+
request: Empty,
|
|
223
|
+
abortSignal: AbortSignal,
|
|
224
|
+
context: ServerContext,
|
|
225
|
+
): Promise<Empty>
|
|
226
|
+
}
|
|
227
|
+
|
|
154
228
|
export const EchoerServiceName = EchoerDefinition.typeName
|
|
155
229
|
|
|
156
230
|
export class EchoerClient implements Echoer {
|
package/echo/server.go
CHANGED
package/echo/server.ts
CHANGED
|
@@ -3,14 +3,15 @@ import { Message } from '@aptre/protobuf-es-lite'
|
|
|
3
3
|
import { Empty } from '@aptre/protobuf-es-lite/google/protobuf/empty'
|
|
4
4
|
import { EchoMsg } from './echo.pb.js'
|
|
5
5
|
import { Server } from '../srpc/server.js'
|
|
6
|
+
import type { ServerContext } from '../srpc/server-context.js'
|
|
6
7
|
import { messagePushable, writeToPushable } from '../srpc/pushable.js'
|
|
7
8
|
import { RpcStreamPacket } from '../rpcstream/rpcstream.pb.js'
|
|
8
9
|
import { MessageStream } from '../srpc/message.js'
|
|
9
10
|
import { handleRpcStream, RpcStreamHandler } from '../rpcstream/rpcstream.js'
|
|
10
|
-
import {
|
|
11
|
+
import type { EchoerHandler } from './echo_srpc.pb.js'
|
|
11
12
|
|
|
12
13
|
// EchoServer implements the Echoer server.
|
|
13
|
-
export class EchoerServer implements
|
|
14
|
+
export class EchoerServer implements EchoerHandler {
|
|
14
15
|
// proxyServer is the server used for RpcStream requests.
|
|
15
16
|
private proxyServer?: Server
|
|
16
17
|
|
|
@@ -18,11 +19,19 @@ export class EchoerServer implements Echoer {
|
|
|
18
19
|
this.proxyServer = proxyServer
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
public async Echo(
|
|
22
|
+
public async Echo(
|
|
23
|
+
request: EchoMsg,
|
|
24
|
+
_abortSignal: AbortSignal,
|
|
25
|
+
_context: ServerContext,
|
|
26
|
+
): Promise<Message<EchoMsg>> {
|
|
22
27
|
return request
|
|
23
28
|
}
|
|
24
29
|
|
|
25
|
-
public async *EchoServerStream(
|
|
30
|
+
public async *EchoServerStream(
|
|
31
|
+
request: EchoMsg,
|
|
32
|
+
_abortSignal: AbortSignal,
|
|
33
|
+
_context: ServerContext,
|
|
34
|
+
): MessageStream<EchoMsg> {
|
|
26
35
|
for (let i = 0; i < 5; i++) {
|
|
27
36
|
yield request
|
|
28
37
|
await new Promise((resolve) => setTimeout(resolve, 200))
|
|
@@ -31,6 +40,8 @@ export class EchoerServer implements Echoer {
|
|
|
31
40
|
|
|
32
41
|
public async EchoClientStream(
|
|
33
42
|
request: MessageStream<EchoMsg>,
|
|
43
|
+
_abortSignal: AbortSignal,
|
|
44
|
+
_context: ServerContext,
|
|
34
45
|
): Promise<Message<EchoMsg>> {
|
|
35
46
|
// return the first message sent by the client.
|
|
36
47
|
const message = await first(request)
|
|
@@ -42,6 +53,8 @@ export class EchoerServer implements Echoer {
|
|
|
42
53
|
|
|
43
54
|
public EchoBidiStream(
|
|
44
55
|
request: MessageStream<EchoMsg>,
|
|
56
|
+
_abortSignal: AbortSignal,
|
|
57
|
+
_context: ServerContext,
|
|
45
58
|
): MessageStream<EchoMsg> {
|
|
46
59
|
// build result observable
|
|
47
60
|
const result = messagePushable<EchoMsg>()
|
|
@@ -52,6 +65,8 @@ export class EchoerServer implements Echoer {
|
|
|
52
65
|
|
|
53
66
|
public RpcStream(
|
|
54
67
|
request: MessageStream<RpcStreamPacket>,
|
|
68
|
+
_abortSignal: AbortSignal,
|
|
69
|
+
_context: ServerContext,
|
|
55
70
|
): MessageStream<RpcStreamPacket> {
|
|
56
71
|
return handleRpcStream(
|
|
57
72
|
request[Symbol.asyncIterator](),
|
|
@@ -64,7 +79,11 @@ export class EchoerServer implements Echoer {
|
|
|
64
79
|
)
|
|
65
80
|
}
|
|
66
81
|
|
|
67
|
-
public async DoNothing(
|
|
82
|
+
public async DoNothing(
|
|
83
|
+
_request: Empty,
|
|
84
|
+
_abortSignal: AbortSignal,
|
|
85
|
+
_context: ServerContext,
|
|
86
|
+
): Promise<Empty> {
|
|
68
87
|
return {}
|
|
69
88
|
}
|
|
70
89
|
}
|
package/go.mod
CHANGED
|
@@ -3,7 +3,7 @@ module github.com/aperturerobotics/starpc
|
|
|
3
3
|
go 1.25.0
|
|
4
4
|
|
|
5
5
|
require (
|
|
6
|
-
github.com/aperturerobotics/common v0.
|
|
6
|
+
github.com/aperturerobotics/common v0.35.0 // latest
|
|
7
7
|
github.com/aperturerobotics/protobuf-go-lite v0.16.0 // latest
|
|
8
8
|
github.com/aperturerobotics/util v1.34.9 // latest
|
|
9
9
|
)
|
package/go.sum
CHANGED
|
@@ -4,6 +4,8 @@ github.com/aperturerobotics/cli v1.1.0 h1:7a+YRC+EY3npAnTzhHV5gLCiw91KS0Ts3XwLIL
|
|
|
4
4
|
github.com/aperturerobotics/cli v1.1.0/go.mod h1:M7BFP9wow5ytTzMyJQOOO991fGfsUqdTI7gGEsHfTQ8=
|
|
5
5
|
github.com/aperturerobotics/common v0.34.4 h1:RQTOm6LxEnucXkED/kruhKKAOL6tbaPzMgfHPKaES4k=
|
|
6
6
|
github.com/aperturerobotics/common v0.34.4/go.mod h1:xnb1VBRs3x2o1PaKg0OlP7SqDaC9Hmrmt9xH85rOS+A=
|
|
7
|
+
github.com/aperturerobotics/common v0.35.0 h1:VU4OPMXJyqT37lTOtNEMa3QLW5tR8YIBAKbjgxSzuZs=
|
|
8
|
+
github.com/aperturerobotics/common v0.35.0/go.mod h1:xnb1VBRs3x2o1PaKg0OlP7SqDaC9Hmrmt9xH85rOS+A=
|
|
7
9
|
github.com/aperturerobotics/go-protoc-gen-prost v0.0.0-20260705010911-9f53feac967b h1:MMk+AbWPCMfqc2Lm2bJl8lCCsuvlM2+C0CP10fpE3bc=
|
|
8
10
|
github.com/aperturerobotics/go-protoc-gen-prost v0.0.0-20260705010911-9f53feac967b/go.mod h1:OBb/beWmr/pDIZAUfi86j/4tBh2v5ctTxKMqSnh9c/4=
|
|
9
11
|
github.com/aperturerobotics/go-protoc-wasi v0.0.0-20260712054757-d8078c296c17 h1:WT1YmZjJf8fUF1zKbeNNKXFA+t8Wtbr8AVMAmdKcrUU=
|
|
@@ -2,7 +2,6 @@ package main
|
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
4
|
"context"
|
|
5
|
-
"errors"
|
|
6
5
|
"fmt"
|
|
7
6
|
"io"
|
|
8
7
|
"net"
|
|
@@ -15,32 +14,13 @@ import (
|
|
|
15
14
|
const bodyTxt = "hello world via starpc cross-language e2e test"
|
|
16
15
|
|
|
17
16
|
func main() {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
addrIndex := 1
|
|
21
|
-
if receiptMode {
|
|
22
|
-
if len(os.Args) < 3 {
|
|
23
|
-
fmt.Fprintf(os.Stderr, "usage: go-client receipt <case> <addr>\n")
|
|
24
|
-
os.Exit(1)
|
|
25
|
-
}
|
|
26
|
-
receiptCase = os.Args[2]
|
|
27
|
-
addrIndex = 3
|
|
28
|
-
switch receiptCase {
|
|
29
|
-
case "commit", "abort", "loss", "bare-close":
|
|
30
|
-
default:
|
|
31
|
-
fmt.Fprintf(os.Stderr, "unknown receipt case: %s\n", receiptCase)
|
|
32
|
-
os.Exit(1)
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
if len(os.Args) <= addrIndex {
|
|
36
|
-
fmt.Fprintf(os.Stderr, "usage: go-client [receipt <case>] <addr>\n")
|
|
17
|
+
if len(os.Args) <= 1 {
|
|
18
|
+
fmt.Fprintf(os.Stderr, "usage: go-client <addr>\n")
|
|
37
19
|
os.Exit(1)
|
|
38
20
|
}
|
|
39
|
-
addr := os.Args[
|
|
40
|
-
var conn net.Conn
|
|
21
|
+
addr := os.Args[1]
|
|
41
22
|
openStream := func(ctx context.Context, msgHandler srpc.PacketDataHandler, closeHandler srpc.CloseHandler) (srpc.PacketWriter, error) {
|
|
42
|
-
|
|
43
|
-
conn, err = net.Dial("tcp", addr) //nolint:gosec
|
|
23
|
+
conn, err := net.Dial("tcp", addr) //nolint:gosec
|
|
44
24
|
if err != nil {
|
|
45
25
|
return nil, err
|
|
46
26
|
}
|
|
@@ -52,35 +32,22 @@ func main() {
|
|
|
52
32
|
client := srpc.NewClient(openStream)
|
|
53
33
|
echoClient := echo.NewSRPCEchoerClient(client)
|
|
54
34
|
ctx := context.Background()
|
|
55
|
-
if receiptMode {
|
|
56
|
-
if err := testReceipt(ctx, client, &conn, receiptCase); err != nil {
|
|
57
|
-
fmt.Fprintf(os.Stderr, "receipt test failed: %v\n", err)
|
|
58
|
-
os.Exit(1)
|
|
59
|
-
}
|
|
60
|
-
fmt.Println("All tests passed.")
|
|
61
|
-
return
|
|
62
|
-
}
|
|
63
|
-
|
|
64
35
|
if err := testUnary(ctx, echoClient); err != nil {
|
|
65
36
|
fmt.Fprintf(os.Stderr, "unary test failed: %v\n", err)
|
|
66
37
|
os.Exit(1)
|
|
67
38
|
}
|
|
68
|
-
|
|
69
39
|
if err := testServerStream(ctx, echoClient); err != nil {
|
|
70
40
|
fmt.Fprintf(os.Stderr, "server stream test failed: %v\n", err)
|
|
71
41
|
os.Exit(1)
|
|
72
42
|
}
|
|
73
|
-
|
|
74
43
|
if err := testClientStream(ctx, echoClient); err != nil {
|
|
75
44
|
fmt.Fprintf(os.Stderr, "client stream test failed: %v\n", err)
|
|
76
45
|
os.Exit(1)
|
|
77
46
|
}
|
|
78
|
-
|
|
79
47
|
if err := testBidiStream(ctx, echoClient); err != nil {
|
|
80
48
|
fmt.Fprintf(os.Stderr, "bidi stream test failed: %v\n", err)
|
|
81
49
|
os.Exit(1)
|
|
82
50
|
}
|
|
83
|
-
|
|
84
51
|
fmt.Println("All tests passed.")
|
|
85
52
|
}
|
|
86
53
|
|
|
@@ -97,79 +64,6 @@ func testUnary(ctx context.Context, client echo.SRPCEchoerClient) error {
|
|
|
97
64
|
return nil
|
|
98
65
|
}
|
|
99
66
|
|
|
100
|
-
func testReceipt(
|
|
101
|
-
ctx context.Context,
|
|
102
|
-
client srpc.Client,
|
|
103
|
-
conn *net.Conn,
|
|
104
|
-
receiptCase string,
|
|
105
|
-
) error {
|
|
106
|
-
fmt.Printf("Testing held unary receipt (%s)...\n", receiptCase)
|
|
107
|
-
out := new(echo.EchoMsg)
|
|
108
|
-
receipt, err := srpc.ExecCallReceipt(
|
|
109
|
-
ctx, client, echo.SRPCEchoerServiceID, "Echo",
|
|
110
|
-
&echo.EchoMsg{Body: bodyTxt}, out,
|
|
111
|
-
)
|
|
112
|
-
if err != nil {
|
|
113
|
-
return fmt.Errorf("receipt call: %w", err)
|
|
114
|
-
}
|
|
115
|
-
if out.GetBody() != bodyTxt {
|
|
116
|
-
return fmt.Errorf("expected %q got %q", bodyTxt, out.GetBody())
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
switch receiptCase {
|
|
120
|
-
case "commit":
|
|
121
|
-
if err := receipt.Commit(); err != nil {
|
|
122
|
-
return fmt.Errorf("receipt commit: %w", err)
|
|
123
|
-
}
|
|
124
|
-
case "abort":
|
|
125
|
-
if err := receipt.Abort(); err != nil {
|
|
126
|
-
return fmt.Errorf("receipt abort: %w", err)
|
|
127
|
-
}
|
|
128
|
-
case "loss":
|
|
129
|
-
if *conn == nil {
|
|
130
|
-
return errors.New("receipt connection is nil")
|
|
131
|
-
}
|
|
132
|
-
tcpConn, ok := (*conn).(*net.TCPConn)
|
|
133
|
-
if !ok {
|
|
134
|
-
return errors.New("receipt connection is not TCP")
|
|
135
|
-
}
|
|
136
|
-
if err := tcpConn.SetLinger(0); err != nil {
|
|
137
|
-
return fmt.Errorf("set receipt loss linger: %w", err)
|
|
138
|
-
}
|
|
139
|
-
if err := tcpConn.Close(); err != nil {
|
|
140
|
-
return fmt.Errorf("close lost connection: %w", err)
|
|
141
|
-
}
|
|
142
|
-
if err := receipt.Commit(); err == nil {
|
|
143
|
-
return errors.New("loss receipt commit unexpectedly succeeded")
|
|
144
|
-
}
|
|
145
|
-
case "bare-close":
|
|
146
|
-
if *conn == nil {
|
|
147
|
-
return errors.New("receipt connection is nil")
|
|
148
|
-
}
|
|
149
|
-
tcpConn, ok := (*conn).(*net.TCPConn)
|
|
150
|
-
if !ok {
|
|
151
|
-
return errors.New("receipt connection is not TCP")
|
|
152
|
-
}
|
|
153
|
-
if err := tcpConn.CloseWrite(); err != nil {
|
|
154
|
-
return fmt.Errorf("close receipt write side: %w", err)
|
|
155
|
-
}
|
|
156
|
-
if err := receipt.Commit(); err == nil {
|
|
157
|
-
return errors.New("bare-close receipt commit unexpectedly succeeded")
|
|
158
|
-
}
|
|
159
|
-
default:
|
|
160
|
-
return fmt.Errorf("unknown receipt case: %s", receiptCase)
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
if err := emitReceiptEvent(
|
|
164
|
-
fmt.Sprintf("CLIENT_RECEIPT_RESOLVED %s", receiptTerminalName(receiptCase)),
|
|
165
|
-
); err != nil {
|
|
166
|
-
return fmt.Errorf("record receipt resolution: %w", err)
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
fmt.Println(" PASSED")
|
|
170
|
-
return nil
|
|
171
|
-
}
|
|
172
|
-
|
|
173
67
|
func testServerStream(ctx context.Context, client echo.SRPCEchoerClient) error {
|
|
174
68
|
fmt.Println("Testing ServerStream RPC...")
|
|
175
69
|
strm, err := client.EchoServerStream(ctx, &echo.EchoMsg{Body: bodyTxt})
|
|
@@ -249,35 +143,3 @@ func testBidiStream(ctx context.Context, client echo.SRPCEchoerClient) error {
|
|
|
249
143
|
fmt.Println(" PASSED")
|
|
250
144
|
return nil
|
|
251
145
|
}
|
|
252
|
-
|
|
253
|
-
func receiptTerminalName(receiptCase string) string {
|
|
254
|
-
switch receiptCase {
|
|
255
|
-
case "commit":
|
|
256
|
-
return "committed"
|
|
257
|
-
case "abort":
|
|
258
|
-
return "canceled"
|
|
259
|
-
case "loss":
|
|
260
|
-
return "transportLost"
|
|
261
|
-
case "bare-close":
|
|
262
|
-
return "closed"
|
|
263
|
-
default:
|
|
264
|
-
return "unknown"
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
func emitReceiptEvent(line string) error {
|
|
269
|
-
fmt.Println(line)
|
|
270
|
-
fifo := os.Getenv("RECEIPT_EVENT_FIFO")
|
|
271
|
-
if fifo == "" {
|
|
272
|
-
return nil
|
|
273
|
-
}
|
|
274
|
-
file, err := os.OpenFile(fifo, os.O_WRONLY, 0) //nolint:gosec // FIFO path is created by the local integration runner.
|
|
275
|
-
if err != nil {
|
|
276
|
-
return err
|
|
277
|
-
}
|
|
278
|
-
if _, err := file.WriteString(line + "\n"); err != nil {
|
|
279
|
-
_ = file.Close()
|
|
280
|
-
return err
|
|
281
|
-
}
|
|
282
|
-
return file.Close()
|
|
283
|
-
}
|
|
@@ -2,13 +2,10 @@ package main
|
|
|
2
2
|
|
|
3
3
|
import (
|
|
4
4
|
"context"
|
|
5
|
-
"encoding/binary"
|
|
6
5
|
"fmt"
|
|
7
6
|
"net"
|
|
8
7
|
"os"
|
|
9
8
|
"os/signal"
|
|
10
|
-
"sync"
|
|
11
|
-
"sync/atomic"
|
|
12
9
|
|
|
13
10
|
"github.com/aperturerobotics/starpc/echo"
|
|
14
11
|
"github.com/aperturerobotics/starpc/srpc"
|
|
@@ -18,74 +15,13 @@ func main() {
|
|
|
18
15
|
ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt)
|
|
19
16
|
defer cancel()
|
|
20
17
|
|
|
21
|
-
receiptMode := len(os.Args) > 1 && os.Args[1] == "receipt"
|
|
22
|
-
receiptCase := ""
|
|
23
|
-
if receiptMode {
|
|
24
|
-
if len(os.Args) < 3 {
|
|
25
|
-
fmt.Fprintln(os.Stderr, "usage: go-server receipt <case>")
|
|
26
|
-
os.Exit(1)
|
|
27
|
-
}
|
|
28
|
-
receiptCase = os.Args[2]
|
|
29
|
-
switch receiptCase {
|
|
30
|
-
case "commit", "abort", "loss", "bare-close":
|
|
31
|
-
default:
|
|
32
|
-
fmt.Fprintf(os.Stderr, "unknown receipt case: %s\n", receiptCase)
|
|
33
|
-
os.Exit(1)
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
18
|
mux := srpc.NewMux()
|
|
37
19
|
echoServer := echo.NewEchoServer(mux)
|
|
38
20
|
if err := echo.SRPCRegisterEchoer(mux, echoServer); err != nil {
|
|
39
21
|
fmt.Fprintf(os.Stderr, "register error: %v\n", err)
|
|
40
22
|
os.Exit(1)
|
|
41
23
|
}
|
|
42
|
-
|
|
43
|
-
var finishReceipt func()
|
|
44
|
-
var receiptCommitted atomic.Bool
|
|
45
|
-
if receiptMode {
|
|
46
|
-
done := make(chan struct{})
|
|
47
|
-
var doneOnce sync.Once
|
|
48
|
-
receiptDone = done
|
|
49
|
-
finishReceipt = func() {
|
|
50
|
-
doneOnce.Do(func() {
|
|
51
|
-
close(done)
|
|
52
|
-
})
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
var invoker srpc.Invoker = mux
|
|
56
|
-
if receiptMode {
|
|
57
|
-
invoker = srpc.InvokerFunc(func(
|
|
58
|
-
serviceID, methodID string,
|
|
59
|
-
strm srpc.Stream,
|
|
60
|
-
) (bool, error) {
|
|
61
|
-
handled, err := mux.InvokeMethod(serviceID, methodID, strm)
|
|
62
|
-
if err != nil || !handled {
|
|
63
|
-
return handled, err
|
|
64
|
-
}
|
|
65
|
-
invocation, ok := srpc.GetServerInvocation(strm.Context())
|
|
66
|
-
if !ok {
|
|
67
|
-
return true, context.Canceled
|
|
68
|
-
}
|
|
69
|
-
kind, waitErr := invocation.WaitTerminal(context.Background())
|
|
70
|
-
markerErr := emitReceiptEvent(
|
|
71
|
-
fmt.Sprintf("SERVER_RECEIPT_TERMINAL %s", terminalName(kind)),
|
|
72
|
-
)
|
|
73
|
-
if waitErr != nil {
|
|
74
|
-
return true, waitErr
|
|
75
|
-
}
|
|
76
|
-
if markerErr != nil {
|
|
77
|
-
return true, markerErr
|
|
78
|
-
}
|
|
79
|
-
if kind == srpc.TerminalKind_TERMINAL_KIND_COMMITTED {
|
|
80
|
-
receiptCommitted.Store(true)
|
|
81
|
-
}
|
|
82
|
-
if kind != srpc.TerminalKind_TERMINAL_KIND_COMMITTED {
|
|
83
|
-
finishReceipt()
|
|
84
|
-
}
|
|
85
|
-
return true, nil
|
|
86
|
-
})
|
|
87
|
-
}
|
|
88
|
-
server := srpc.NewServer(invoker)
|
|
24
|
+
server := srpc.NewServer(mux)
|
|
89
25
|
ln, err := net.Listen("tcp", "127.0.0.1:0")
|
|
90
26
|
if err != nil {
|
|
91
27
|
fmt.Fprintf(os.Stderr, "listen error: %v\n", err)
|
|
@@ -93,120 +29,16 @@ func main() {
|
|
|
93
29
|
}
|
|
94
30
|
defer ln.Close()
|
|
95
31
|
|
|
96
|
-
if receiptMode {
|
|
97
|
-
go func() {
|
|
98
|
-
<-receiptDone
|
|
99
|
-
_ = ln.Close()
|
|
100
|
-
}()
|
|
101
|
-
}
|
|
102
|
-
|
|
103
32
|
fmt.Printf("LISTENING %s\n", ln.Addr().String())
|
|
104
|
-
|
|
105
33
|
go func() {
|
|
106
34
|
<-ctx.Done()
|
|
107
35
|
_ = ln.Close()
|
|
108
36
|
}()
|
|
109
|
-
|
|
110
37
|
for {
|
|
111
38
|
conn, err := ln.Accept()
|
|
112
39
|
if err != nil {
|
|
113
40
|
return
|
|
114
41
|
}
|
|
115
|
-
|
|
116
|
-
stream = conn
|
|
117
|
-
if receiptMode {
|
|
118
|
-
stream = &receiptConn{
|
|
119
|
-
Conn: conn,
|
|
120
|
-
finishReceipt: finishReceipt,
|
|
121
|
-
committed: &receiptCommitted,
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
go server.HandleStream(ctx, stream)
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
type receiptConn struct {
|
|
129
|
-
net.Conn
|
|
130
|
-
finishReceipt func()
|
|
131
|
-
committed *atomic.Bool
|
|
132
|
-
inspect []byte
|
|
133
|
-
ackPending bool
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
func (c *receiptConn) Write(p []byte) (int, error) {
|
|
137
|
-
if !c.ackPending {
|
|
138
|
-
if err := c.observeReceiptPackets(p); err != nil {
|
|
139
|
-
return 0, err
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
n, err := c.Conn.Write(p)
|
|
143
|
-
if err != nil {
|
|
144
|
-
return n, err
|
|
145
|
-
}
|
|
146
|
-
if c.ackPending && n == len(p) {
|
|
147
|
-
c.finishReceipt()
|
|
148
|
-
c.ackPending = false
|
|
149
|
-
}
|
|
150
|
-
return n, nil
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
func (c *receiptConn) observeReceiptPackets(p []byte) error {
|
|
154
|
-
c.inspect = append(c.inspect, p...)
|
|
155
|
-
for len(c.inspect) >= 4 {
|
|
156
|
-
size := int(binary.LittleEndian.Uint32(c.inspect[:4]))
|
|
157
|
-
if len(c.inspect) < 4+size {
|
|
158
|
-
break
|
|
159
|
-
}
|
|
160
|
-
pkt := &srpc.Packet{}
|
|
161
|
-
if err := pkt.UnmarshalVT(c.inspect[4 : 4+size]); err != nil {
|
|
162
|
-
return err
|
|
163
|
-
}
|
|
164
|
-
c.inspect = c.inspect[4+size:]
|
|
165
|
-
data := pkt.GetCallData()
|
|
166
|
-
if !c.committed.Load() {
|
|
167
|
-
continue
|
|
168
|
-
}
|
|
169
|
-
if data == nil || !data.GetComplete() || data.GetError() != "" {
|
|
170
|
-
continue
|
|
171
|
-
}
|
|
172
|
-
if err := emitReceiptEvent("SERVER_RECEIPT_ACK_WRITE committed"); err != nil {
|
|
173
|
-
return err
|
|
174
|
-
}
|
|
175
|
-
c.ackPending = true
|
|
176
|
-
}
|
|
177
|
-
return nil
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
func terminalName(kind srpc.TerminalKind) string {
|
|
181
|
-
switch kind {
|
|
182
|
-
case srpc.TerminalKind_TERMINAL_KIND_COMMITTED:
|
|
183
|
-
return "committed"
|
|
184
|
-
case srpc.TerminalKind_TERMINAL_KIND_CANCELED:
|
|
185
|
-
return "canceled"
|
|
186
|
-
case srpc.TerminalKind_TERMINAL_KIND_TRANSPORT_LOST:
|
|
187
|
-
return "transportLost"
|
|
188
|
-
case srpc.TerminalKind_TERMINAL_KIND_CLOSED:
|
|
189
|
-
return "closed"
|
|
190
|
-
case srpc.TerminalKind_TERMINAL_KIND_ABANDONED:
|
|
191
|
-
return "abandoned"
|
|
192
|
-
default:
|
|
193
|
-
return "unknown"
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
func emitReceiptEvent(line string) error {
|
|
198
|
-
fmt.Println(line)
|
|
199
|
-
fifo := os.Getenv("RECEIPT_EVENT_FIFO")
|
|
200
|
-
if fifo == "" {
|
|
201
|
-
return nil
|
|
202
|
-
}
|
|
203
|
-
file, err := os.OpenFile(fifo, os.O_WRONLY, 0) //nolint:gosec // FIFO path is created by the local integration runner.
|
|
204
|
-
if err != nil {
|
|
205
|
-
return err
|
|
206
|
-
}
|
|
207
|
-
if _, err := file.WriteString(line + "\n"); err != nil {
|
|
208
|
-
_ = file.Close()
|
|
209
|
-
return err
|
|
42
|
+
go server.HandleStream(ctx, conn)
|
|
210
43
|
}
|
|
211
|
-
return file.Close()
|
|
212
44
|
}
|