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
|
@@ -111,27 +111,6 @@ stop_server() {
|
|
|
111
111
|
|
|
112
112
|
# run_pair <test_name> <server_args...> -- <client_args...>
|
|
113
113
|
# The client receives $SERVER_ADDR as its last argument.
|
|
114
|
-
# check_receipt_events verifies terminal markers after the receipt server
|
|
115
|
-
# naturally completes, so receipt synchronization is event/process-backed.
|
|
116
|
-
check_receipt_events() {
|
|
117
|
-
local expected="$1"
|
|
118
|
-
local event_log="$2"
|
|
119
|
-
awk -v expected="$expected" '
|
|
120
|
-
$0 == "SERVER_RECEIPT_TERMINAL " expected { terminal = NR; next }
|
|
121
|
-
$0 == "SERVER_RECEIPT_ACK_WRITE " expected { ack_write = NR; next }
|
|
122
|
-
$0 == "CLIENT_RECEIPT_RESOLVED " expected { client = NR; next }
|
|
123
|
-
END {
|
|
124
|
-
if (!terminal || !client) {
|
|
125
|
-
exit 1
|
|
126
|
-
}
|
|
127
|
-
if (expected == "committed" &&
|
|
128
|
-
(!ack_write || terminal >= ack_write || ack_write >= client)) {
|
|
129
|
-
exit 1
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
' "$event_log"
|
|
133
|
-
}
|
|
134
|
-
|
|
135
114
|
run_pair() {
|
|
136
115
|
local test_name="$1"
|
|
137
116
|
shift
|
|
@@ -140,7 +119,6 @@ run_pair() {
|
|
|
140
119
|
return
|
|
141
120
|
fi
|
|
142
121
|
|
|
143
|
-
# Split args on "--".
|
|
144
122
|
local srv_args=()
|
|
145
123
|
local cli_args=()
|
|
146
124
|
local in_client=false
|
|
@@ -156,103 +134,33 @@ run_pair() {
|
|
|
156
134
|
fi
|
|
157
135
|
done
|
|
158
136
|
|
|
159
|
-
local expected_terminal=""
|
|
160
|
-
case "$test_name" in
|
|
161
|
-
*"receipt commit") expected_terminal="committed" ;;
|
|
162
|
-
*"receipt abort") expected_terminal="canceled" ;;
|
|
163
|
-
*"receipt loss") expected_terminal="transportLost" ;;
|
|
164
|
-
*"receipt bare-close") expected_terminal="closed" ;;
|
|
165
|
-
esac
|
|
166
|
-
|
|
167
137
|
echo -n " ${test_name}... "
|
|
168
|
-
|
|
169
|
-
local event_fifo=""
|
|
170
|
-
local event_log=""
|
|
171
|
-
local event_reader=""
|
|
172
|
-
if [ -n "$expected_terminal" ]; then
|
|
173
|
-
event_fifo=$(mktemp)
|
|
174
|
-
rm -f "$event_fifo"
|
|
175
|
-
mkfifo "$event_fifo"
|
|
176
|
-
event_log=$(mktemp)
|
|
177
|
-
exec 3<>"$event_fifo"
|
|
178
|
-
(
|
|
179
|
-
exec 3>&-
|
|
180
|
-
cat "$event_fifo" > "$event_log"
|
|
181
|
-
) &
|
|
182
|
-
event_reader=$!
|
|
183
|
-
export RECEIPT_EVENT_FIFO="$event_fifo"
|
|
184
|
-
else
|
|
185
|
-
unset RECEIPT_EVENT_FIFO
|
|
186
|
-
fi
|
|
187
|
-
|
|
188
138
|
if ! start_server "${srv_args[@]}"; then
|
|
189
139
|
echo "FAILED (server start)"
|
|
190
140
|
FAILED=$((FAILED + 1))
|
|
191
141
|
ERRORS="${ERRORS}\n ${test_name} (server start failed)"
|
|
192
|
-
if [ -n "$event_reader" ]; then
|
|
193
|
-
exec 3>&-
|
|
194
|
-
kill "$event_reader" 2>/dev/null || true
|
|
195
|
-
wait "$event_reader" 2>/dev/null || true
|
|
196
|
-
rm -f "$event_fifo" "$event_log"
|
|
197
|
-
fi
|
|
198
|
-
unset RECEIPT_EVENT_FIFO
|
|
199
142
|
return
|
|
200
143
|
fi
|
|
201
144
|
|
|
202
145
|
local client_out
|
|
203
146
|
client_out=$(mktemp)
|
|
204
147
|
local client_ok=false
|
|
205
|
-
local server_ok=true
|
|
206
148
|
if "${cli_args[@]}" "$SERVER_ADDR" > "$client_out" 2>&1; then
|
|
207
149
|
client_ok=true
|
|
208
150
|
fi
|
|
151
|
+
stop_server
|
|
209
152
|
|
|
210
|
-
if
|
|
211
|
-
if $client_ok; then
|
|
212
|
-
if ! wait "$SERVER_PID"; then
|
|
213
|
-
server_ok=false
|
|
214
|
-
fi
|
|
215
|
-
else
|
|
216
|
-
stop_server
|
|
217
|
-
wait "$SERVER_PID" 2>/dev/null || true
|
|
218
|
-
server_ok=false
|
|
219
|
-
fi
|
|
220
|
-
exec 3>&-
|
|
221
|
-
wait "$event_reader" 2>/dev/null || true
|
|
222
|
-
else
|
|
223
|
-
stop_server
|
|
224
|
-
fi
|
|
225
|
-
unset RECEIPT_EVENT_FIFO
|
|
226
|
-
|
|
227
|
-
local terminal_ok=true
|
|
228
|
-
if [ -n "$expected_terminal" ] &&
|
|
229
|
-
! check_receipt_events "$expected_terminal" "$event_log"; then
|
|
230
|
-
terminal_ok=false
|
|
231
|
-
fi
|
|
232
|
-
|
|
233
|
-
if $client_ok && $server_ok && $terminal_ok; then
|
|
153
|
+
if $client_ok; then
|
|
234
154
|
echo "PASSED"
|
|
235
|
-
if [ -n "$expected_terminal" ]; then
|
|
236
|
-
echo " receipt events:"
|
|
237
|
-
sed 's/^/ /' "$event_log"
|
|
238
|
-
fi
|
|
239
155
|
PASSED=$((PASSED + 1))
|
|
240
156
|
else
|
|
241
157
|
echo "FAILED"
|
|
242
158
|
FAILED=$((FAILED + 1))
|
|
243
159
|
ERRORS="${ERRORS}\n ${test_name}"
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
sed 's/^/ /' "$client_out"
|
|
247
|
-
fi
|
|
248
|
-
if ! $terminal_ok; then
|
|
249
|
-
echo " receipt events:"
|
|
250
|
-
sed 's/^/ /' "$event_log"
|
|
251
|
-
echo " server output:"
|
|
252
|
-
sed 's/^/ /' "$SERVER_LOG"
|
|
253
|
-
fi
|
|
160
|
+
echo " client output:"
|
|
161
|
+
sed 's/^/ /' "$client_out"
|
|
254
162
|
fi
|
|
255
|
-
rm -f "$client_out" "$SERVER_LOG"
|
|
163
|
+
rm -f "$client_out" "$SERVER_LOG"
|
|
256
164
|
SERVER_LOG=""
|
|
257
165
|
}
|
|
258
166
|
|
|
@@ -264,10 +172,6 @@ echo ""
|
|
|
264
172
|
run_pair "go-server + go-client" "$GO_SERVER" -- "$GO_CLIENT"
|
|
265
173
|
run_pair "go-server + rust-client" "$GO_SERVER" -- "$RUST_CLIENT"
|
|
266
174
|
run_pair "go-server + ts-client" "$GO_SERVER" -- node "$TS_CLIENT"
|
|
267
|
-
run_pair "go-server + ts-client receipt commit" "$GO_SERVER" receipt commit -- node "$TS_CLIENT" receipt commit
|
|
268
|
-
run_pair "go-server + ts-client receipt abort" "$GO_SERVER" receipt abort -- node "$TS_CLIENT" receipt abort
|
|
269
|
-
run_pair "go-server + ts-client receipt loss" "$GO_SERVER" receipt loss -- node "$TS_CLIENT" receipt loss
|
|
270
|
-
run_pair "go-server + ts-client receipt bare-close" "$GO_SERVER" receipt bare-close -- node "$TS_CLIENT" receipt bare-close
|
|
271
175
|
run_pair "go-server + cpp-client" "$GO_SERVER" -- "$CPP_CLIENT"
|
|
272
176
|
|
|
273
177
|
# Rust server combinations
|
|
@@ -278,10 +182,6 @@ run_pair "rust-server + cpp-client" "$RUST_SERVER" -- "$CPP_CLIENT"
|
|
|
278
182
|
|
|
279
183
|
# TypeScript server combinations
|
|
280
184
|
run_pair "ts-server + go-client" node "$TS_SERVER" -- "$GO_CLIENT"
|
|
281
|
-
run_pair "ts-server + go-client receipt commit" node "$TS_SERVER" receipt commit -- "$GO_CLIENT" receipt commit
|
|
282
|
-
run_pair "ts-server + go-client receipt abort" node "$TS_SERVER" receipt abort -- "$GO_CLIENT" receipt abort
|
|
283
|
-
run_pair "ts-server + go-client receipt loss" node "$TS_SERVER" receipt loss -- "$GO_CLIENT" receipt loss
|
|
284
|
-
run_pair "ts-server + go-client receipt bare-close" node "$TS_SERVER" receipt bare-close -- "$GO_CLIENT" receipt bare-close
|
|
285
185
|
run_pair "ts-server + rust-client" node "$TS_SERVER" -- "$RUST_CLIENT"
|
|
286
186
|
run_pair "ts-server + ts-client" node "$TS_SERVER" -- node "$TS_CLIENT"
|
|
287
187
|
run_pair "ts-server + cpp-client" node "$TS_SERVER" -- "$CPP_CLIENT"
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import net from 'net'
|
|
2
|
-
import { closeSync, openSync, writeSync } from 'node:fs'
|
|
3
2
|
import { pipe } from 'it-pipe'
|
|
4
3
|
import { pushable } from 'it-pushable'
|
|
5
4
|
import { Client } from '../../srpc/client.js'
|
|
@@ -8,22 +7,9 @@ import {
|
|
|
8
7
|
prependLengthPrefixTransform,
|
|
9
8
|
} from '../../srpc/packet.js'
|
|
10
9
|
import { combineUint8ArrayListTransform } from '../../srpc/array-list.js'
|
|
11
|
-
import {
|
|
10
|
+
import { runClientTest } from '../../echo/index.js'
|
|
12
11
|
import type { OpenStreamFunc, PacketStream } from '../../srpc/stream.js'
|
|
13
12
|
import type { Source } from 'it-stream-types'
|
|
14
|
-
function emitReceiptEvent(line: string): void {
|
|
15
|
-
console.log(line)
|
|
16
|
-
const fifo = process.env.RECEIPT_EVENT_FIFO
|
|
17
|
-
if (!fifo) {
|
|
18
|
-
return
|
|
19
|
-
}
|
|
20
|
-
const fd = openSync(fifo, 'w')
|
|
21
|
-
try {
|
|
22
|
-
writeSync(fd, `${line}\n`)
|
|
23
|
-
} finally {
|
|
24
|
-
closeSync(fd)
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
13
|
|
|
28
14
|
// tcpSocketToPacketStream wraps a Node.js TCP socket into a PacketStream.
|
|
29
15
|
function tcpSocketToPacketStream(socket: net.Socket): PacketStream {
|
|
@@ -60,28 +46,17 @@ function tcpSocketToPacketStream(socket: net.Socket): PacketStream {
|
|
|
60
46
|
}
|
|
61
47
|
|
|
62
48
|
async function main() {
|
|
63
|
-
const
|
|
64
|
-
const receiptCase = receiptMode ? process.argv[3] : undefined
|
|
65
|
-
const addr = receiptMode ? process.argv[4] : process.argv[2]
|
|
49
|
+
const addr = process.argv[2]
|
|
66
50
|
if (!addr) {
|
|
67
|
-
console.error('usage: ts-client
|
|
51
|
+
console.error('usage: ts-client <host:port>')
|
|
68
52
|
process.exit(1)
|
|
69
53
|
}
|
|
70
|
-
if (
|
|
71
|
-
receiptMode &&
|
|
72
|
-
!['commit', 'abort', 'loss', 'bare-close'].includes(receiptCase ?? '')
|
|
73
|
-
) {
|
|
74
|
-
throw new Error(`unknown receipt case: ${receiptCase}`)
|
|
75
|
-
}
|
|
76
54
|
|
|
77
55
|
const [host, portStr] = addr.split(':')
|
|
78
56
|
const port = parseInt(portStr, 10)
|
|
79
|
-
let activeSocket: net.Socket | undefined
|
|
80
|
-
|
|
81
57
|
const openStream: OpenStreamFunc = async (): Promise<PacketStream> => {
|
|
82
58
|
const { promise, resolve, reject } = Promise.withResolvers<PacketStream>()
|
|
83
59
|
const socket = net.connect(port, host, () => {
|
|
84
|
-
activeSocket = socket
|
|
85
60
|
resolve(tcpSocketToPacketStream(socket))
|
|
86
61
|
})
|
|
87
62
|
socket.on('error', reject)
|
|
@@ -89,71 +64,11 @@ async function main() {
|
|
|
89
64
|
}
|
|
90
65
|
|
|
91
66
|
const client = new Client(openStream)
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
console.log(`Running held receipt test via TCP (${receiptCase})...`)
|
|
95
|
-
const request = EchoMsg.create({ body: 'held receipt' })
|
|
96
|
-
const held = await client.requestWithReceipt(
|
|
97
|
-
'echo.Echoer',
|
|
98
|
-
'Echo',
|
|
99
|
-
EchoMsg.toBinary(request),
|
|
100
|
-
)
|
|
101
|
-
const result = EchoMsg.fromBinary(held.response)
|
|
102
|
-
if (result.body !== request.body) {
|
|
103
|
-
throw new Error(`expected ${request.body}, got ${result.body}`)
|
|
104
|
-
}
|
|
105
|
-
switch (receiptCase) {
|
|
106
|
-
case 'commit':
|
|
107
|
-
await held.receipt.commit()
|
|
108
|
-
break
|
|
109
|
-
case 'abort':
|
|
110
|
-
await held.receipt.abort()
|
|
111
|
-
break
|
|
112
|
-
case 'loss':
|
|
113
|
-
activeSocket?.resetAndDestroy()
|
|
114
|
-
await expectReceiptFailure(held.receipt)
|
|
115
|
-
break
|
|
116
|
-
case 'bare-close':
|
|
117
|
-
activeSocket?.end()
|
|
118
|
-
await expectReceiptFailure(held.receipt)
|
|
119
|
-
break
|
|
120
|
-
}
|
|
121
|
-
emitReceiptEvent(
|
|
122
|
-
`CLIENT_RECEIPT_RESOLVED ${receiptTerminalName(receiptCase ?? '')}`,
|
|
123
|
-
)
|
|
124
|
-
} else {
|
|
125
|
-
console.log('Running client test via TCP...')
|
|
126
|
-
await runClientTest(client)
|
|
127
|
-
}
|
|
67
|
+
console.log('Running client test via TCP...')
|
|
68
|
+
await runClientTest(client)
|
|
128
69
|
console.log('All tests passed.')
|
|
129
70
|
}
|
|
130
71
|
|
|
131
|
-
function receiptTerminalName(receiptCase: string): string {
|
|
132
|
-
switch (receiptCase) {
|
|
133
|
-
case 'commit':
|
|
134
|
-
return 'committed'
|
|
135
|
-
case 'abort':
|
|
136
|
-
return 'canceled'
|
|
137
|
-
case 'loss':
|
|
138
|
-
return 'transportLost'
|
|
139
|
-
case 'bare-close':
|
|
140
|
-
return 'closed'
|
|
141
|
-
default:
|
|
142
|
-
return 'unknown'
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
async function expectReceiptFailure(receipt: {
|
|
147
|
-
commit(): Promise<void>
|
|
148
|
-
}): Promise<void> {
|
|
149
|
-
try {
|
|
150
|
-
await receipt.commit()
|
|
151
|
-
} catch {
|
|
152
|
-
return
|
|
153
|
-
}
|
|
154
|
-
throw new Error('receipt commit unexpectedly succeeded')
|
|
155
|
-
}
|
|
156
|
-
|
|
157
72
|
process.on('unhandledRejection', (ev) => {
|
|
158
73
|
console.error('Unhandled rejection', ev)
|
|
159
74
|
process.exit(1)
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import net from 'net'
|
|
2
|
-
import { closeSync, openSync, writeSync } from 'node:fs'
|
|
3
2
|
import { pipe } from 'it-pipe'
|
|
4
3
|
import { pushable } from 'it-pushable'
|
|
5
4
|
import type { Source } from 'it-stream-types'
|
|
@@ -10,25 +9,10 @@ import {
|
|
|
10
9
|
prependLengthPrefixTransform,
|
|
11
10
|
} from '../../srpc/packet.js'
|
|
12
11
|
import { combineUint8ArrayListTransform } from '../../srpc/array-list.js'
|
|
13
|
-
import { EchoerServer
|
|
12
|
+
import { EchoerServer } from '../../echo/index.js'
|
|
14
13
|
import { EchoerDefinition } from '../../echo/echo_srpc.pb.js'
|
|
15
|
-
import { Packet, TerminalKind } from '../../srpc/rpcproto.pb.js'
|
|
16
14
|
import type { PacketStream } from '../../srpc/stream.js'
|
|
17
15
|
|
|
18
|
-
function emitReceiptEvent(line: string): void {
|
|
19
|
-
console.log(line)
|
|
20
|
-
const fifo = process.env.RECEIPT_EVENT_FIFO
|
|
21
|
-
if (!fifo) {
|
|
22
|
-
return
|
|
23
|
-
}
|
|
24
|
-
const fd = openSync(fifo, 'w')
|
|
25
|
-
try {
|
|
26
|
-
writeSync(fd, `${line}\n`)
|
|
27
|
-
} finally {
|
|
28
|
-
closeSync(fd)
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
16
|
// tcpSocketToPacketStream wraps a Node.js TCP socket into a PacketStream.
|
|
33
17
|
// Each Uint8Array in source/sink is one packet (no length prefix).
|
|
34
18
|
function tcpSocketToPacketStream(socket: net.Socket): PacketStream {
|
|
@@ -53,25 +37,12 @@ function tcpSocketToPacketStream(socket: net.Socket): PacketStream {
|
|
|
53
37
|
sink: async (source: Source<Uint8Array>): Promise<void> => {
|
|
54
38
|
for await (const chunk of pipe(source, prependLengthPrefixTransform())) {
|
|
55
39
|
const data = chunk instanceof Uint8Array ? chunk : chunk.subarray()
|
|
56
|
-
|
|
57
|
-
const receiptCompletion =
|
|
58
|
-
receiptMode &&
|
|
59
|
-
packet.body?.case === 'callData' &&
|
|
60
|
-
packet.body.value.complete &&
|
|
61
|
-
!packet.body.value.error
|
|
62
|
-
if (receiptCompletion) {
|
|
63
|
-
emitReceiptEvent('SERVER_RECEIPT_ACK_WRITE committed')
|
|
64
|
-
}
|
|
65
|
-
const writeDone = new Promise<void>((resolve, reject) => {
|
|
40
|
+
await new Promise<void>((resolve, reject) => {
|
|
66
41
|
socket.write(data, (err) => {
|
|
67
42
|
if (err) reject(err)
|
|
68
43
|
else resolve()
|
|
69
44
|
})
|
|
70
45
|
})
|
|
71
|
-
await writeDone
|
|
72
|
-
if (receiptCompletion) {
|
|
73
|
-
finishReceiptServer()
|
|
74
|
-
}
|
|
75
46
|
}
|
|
76
47
|
socket.end()
|
|
77
48
|
},
|
|
@@ -79,82 +50,9 @@ function tcpSocketToPacketStream(socket: net.Socket): PacketStream {
|
|
|
79
50
|
}
|
|
80
51
|
|
|
81
52
|
const mux = createMux()
|
|
82
|
-
const receiptMode = process.argv[2] === 'receipt'
|
|
83
|
-
const receiptCase = receiptMode ? process.argv[3] : undefined
|
|
84
|
-
if (
|
|
85
|
-
receiptMode &&
|
|
86
|
-
!['commit', 'abort', 'loss', 'bare-close'].includes(receiptCase ?? '')
|
|
87
|
-
) {
|
|
88
|
-
console.error(`unknown receipt case: ${receiptCase}`)
|
|
89
|
-
process.exit(1)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const receiptServerDone = Promise.withResolvers<void>()
|
|
93
|
-
let receiptServerFinished = false
|
|
94
|
-
function finishReceiptServer(): void {
|
|
95
|
-
if (!receiptServerFinished) {
|
|
96
|
-
receiptServerFinished = true
|
|
97
|
-
receiptServerDone.resolve()
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
if (receiptMode) {
|
|
102
|
-
mux.registerLookupMethod(async (serviceID, methodID) => {
|
|
103
|
-
if (serviceID !== 'echo.Echoer' || methodID !== 'Echo') {
|
|
104
|
-
return null
|
|
105
|
-
}
|
|
106
|
-
return async (dataSource, dataSink, invocation) => {
|
|
107
|
-
let requestData: Uint8Array | undefined
|
|
108
|
-
for await (const data of dataSource) {
|
|
109
|
-
requestData = data
|
|
110
|
-
break
|
|
111
|
-
}
|
|
112
|
-
if (!requestData) {
|
|
113
|
-
throw new Error('receipt request was empty')
|
|
114
|
-
}
|
|
115
|
-
const requestMsg = EchoMsg.fromBinary(requestData)
|
|
116
|
-
await dataSink(
|
|
117
|
-
(async function* () {
|
|
118
|
-
yield EchoMsg.toBinary(requestMsg)
|
|
119
|
-
if (!invocation) {
|
|
120
|
-
throw new Error('receipt invocation was missing')
|
|
121
|
-
}
|
|
122
|
-
emitReceiptEvent('SERVER_RECEIPT_WAITING')
|
|
123
|
-
const terminal = await invocation.waitTerminal(
|
|
124
|
-
new AbortController().signal,
|
|
125
|
-
)
|
|
126
|
-
emitReceiptEvent(`SERVER_RECEIPT_TERMINAL ${terminalName(terminal)}`)
|
|
127
|
-
if (terminal !== TerminalKind.COMMITTED) {
|
|
128
|
-
finishReceiptServer()
|
|
129
|
-
}
|
|
130
|
-
})(),
|
|
131
|
-
)
|
|
132
|
-
}
|
|
133
|
-
})
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
function terminalName(terminal: TerminalKind): string {
|
|
137
|
-
switch (terminal) {
|
|
138
|
-
case TerminalKind.COMMITTED:
|
|
139
|
-
return 'committed'
|
|
140
|
-
case TerminalKind.CANCELED:
|
|
141
|
-
return 'canceled'
|
|
142
|
-
case TerminalKind.TRANSPORT_LOST:
|
|
143
|
-
return 'transportLost'
|
|
144
|
-
case TerminalKind.CLOSED:
|
|
145
|
-
return 'closed'
|
|
146
|
-
case TerminalKind.ABANDONED:
|
|
147
|
-
return 'abandoned'
|
|
148
|
-
default:
|
|
149
|
-
return 'unknown'
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
|
|
153
53
|
const server = new Server(mux.lookupMethod)
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
mux.register(createHandler(EchoerDefinition, echoer))
|
|
157
|
-
}
|
|
54
|
+
const echoer = new EchoerServer(server)
|
|
55
|
+
mux.register(createHandler(EchoerDefinition, echoer))
|
|
158
56
|
|
|
159
57
|
const tcpServer = net.createServer((socket) => {
|
|
160
58
|
const stream = tcpSocketToPacketStream(socket)
|
|
@@ -166,12 +64,6 @@ tcpServer.listen(0, '127.0.0.1', () => {
|
|
|
166
64
|
console.log(`LISTENING ${addr.address}:${addr.port}`)
|
|
167
65
|
})
|
|
168
66
|
|
|
169
|
-
if (receiptMode) {
|
|
170
|
-
void receiptServerDone.promise.then(() => {
|
|
171
|
-
tcpServer.close()
|
|
172
|
-
})
|
|
173
|
-
}
|
|
174
|
-
|
|
175
67
|
process.on('SIGINT', () => {
|
|
176
68
|
tcpServer.close()
|
|
177
69
|
process.exit(0)
|
package/mock/mock.go
CHANGED
package/mock/mock.pb.go
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Code generated by protoc-gen-go-lite. DO NOT EDIT.
|
|
2
|
-
// protoc-gen-go-lite version: v0.
|
|
2
|
+
// protoc-gen-go-lite version: v0.16.0
|
|
3
3
|
// source: github.com/aperturerobotics/starpc/mock/mock.proto
|
|
4
4
|
|
|
5
5
|
package e2e_mock
|
|
@@ -8,8 +8,6 @@ import (
|
|
|
8
8
|
fmt "fmt"
|
|
9
9
|
io "io"
|
|
10
10
|
slices "slices"
|
|
11
|
-
strconv "strconv"
|
|
12
|
-
strings "strings"
|
|
13
11
|
|
|
14
12
|
protobuf_go_lite "github.com/aperturerobotics/protobuf-go-lite"
|
|
15
13
|
json "github.com/aperturerobotics/protobuf-go-lite/json"
|
|
@@ -139,13 +137,10 @@ func (m *MockMsg) MarshalToSizedBufferVT(dAtA []byte) (int, error) {
|
|
|
139
137
|
var l int
|
|
140
138
|
_ = l
|
|
141
139
|
if m.unknownFields != nil {
|
|
142
|
-
i
|
|
143
|
-
copy(dAtA[i:], m.unknownFields)
|
|
140
|
+
i = protobuf_go_lite.EncodeRawBytes(dAtA, i, m.unknownFields)
|
|
144
141
|
}
|
|
145
142
|
if len(m.Body) > 0 {
|
|
146
|
-
i
|
|
147
|
-
copy(dAtA[i:], m.Body)
|
|
148
|
-
i = protobuf_go_lite.EncodeVarint(dAtA, i, uint64(len(m.Body)))
|
|
143
|
+
i = protobuf_go_lite.EncodeString(dAtA, i, m.Body)
|
|
149
144
|
i--
|
|
150
145
|
dAtA[i] = 0xa
|
|
151
146
|
}
|
|
@@ -158,26 +153,19 @@ func (m *MockMsg) SizeVT() (n int) {
|
|
|
158
153
|
}
|
|
159
154
|
var l int
|
|
160
155
|
_ = l
|
|
161
|
-
|
|
162
|
-
if l > 0 {
|
|
163
|
-
n += 1 + l + protobuf_go_lite.SizeOfVarint(uint64(l))
|
|
164
|
-
}
|
|
156
|
+
n += protobuf_go_lite.SizeStringNonEmpty(1, m.Body)
|
|
165
157
|
n += len(m.unknownFields)
|
|
166
158
|
return n
|
|
167
159
|
}
|
|
168
160
|
|
|
169
161
|
func (x *MockMsg) MarshalProtoText() string {
|
|
170
|
-
var sb
|
|
171
|
-
|
|
162
|
+
var sb protobuf_go_lite.TextBuilder
|
|
163
|
+
initialLen := protobuf_go_lite.TextStartMessage(&sb, "MockMsg")
|
|
172
164
|
if x.Body != "" {
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
}
|
|
176
|
-
sb.WriteString("body: ")
|
|
177
|
-
sb.WriteString(strconv.Quote(x.Body))
|
|
165
|
+
protobuf_go_lite.TextWriteFieldPrefix(&sb, initialLen, "body")
|
|
166
|
+
protobuf_go_lite.TextWriteString(&sb, x.Body)
|
|
178
167
|
}
|
|
179
|
-
|
|
180
|
-
return sb.String()
|
|
168
|
+
return protobuf_go_lite.TextFinishMessage(&sb)
|
|
181
169
|
}
|
|
182
170
|
|
|
183
171
|
func (x *MockMsg) String() string {
|
|
@@ -208,24 +196,12 @@ func (m *MockMsg) UnmarshalVT(dAtA []byte) error {
|
|
|
208
196
|
if wireType != 2 {
|
|
209
197
|
return fmt.Errorf("proto: wrong wireType = %d for field Body", wireType)
|
|
210
198
|
}
|
|
211
|
-
var
|
|
212
|
-
|
|
199
|
+
var v string
|
|
200
|
+
v, iNdEx, err = protobuf_go_lite.DecodeString(dAtA, iNdEx)
|
|
213
201
|
if err != nil {
|
|
214
202
|
return err
|
|
215
203
|
}
|
|
216
|
-
|
|
217
|
-
if intStringLen < 0 {
|
|
218
|
-
return protobuf_go_lite.ErrInvalidLength
|
|
219
|
-
}
|
|
220
|
-
postIndex := iNdEx + intStringLen
|
|
221
|
-
if postIndex < 0 {
|
|
222
|
-
return protobuf_go_lite.ErrInvalidLength
|
|
223
|
-
}
|
|
224
|
-
if postIndex > l {
|
|
225
|
-
return io.ErrUnexpectedEOF
|
|
226
|
-
}
|
|
227
|
-
m.Body = string(dAtA[iNdEx:postIndex])
|
|
228
|
-
iNdEx = postIndex
|
|
204
|
+
m.Body = v
|
|
229
205
|
default:
|
|
230
206
|
iNdEx = preIndex
|
|
231
207
|
skippy, err := protobuf_go_lite.Skip(dAtA[iNdEx:])
|
package/mock/mock_srpc.pb.cpp
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
//go:build deps_only
|
|
2
2
|
|
|
3
3
|
// Code generated by protoc-gen-starpc-cpp. DO NOT EDIT.
|
|
4
|
-
// protoc-gen-starpc-cpp version: v0.49.
|
|
4
|
+
// protoc-gen-starpc-cpp version: v0.49.21
|
|
5
5
|
// source: github.com/aperturerobotics/starpc/mock/mock.proto
|
|
6
6
|
|
|
7
7
|
#include "mock_srpc.pb.hpp"
|
package/mock/mock_srpc.pb.go
CHANGED
package/mock/mock_srpc.pb.hpp
CHANGED
package/mock/mock_srpc.pb.rs
CHANGED
package/mock/mock_srpc.pb.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { MockMsg } from './mock.pb.js'
|
|
6
6
|
import { MethodKind } from '@aptre/protobuf-es-lite'
|
|
7
|
-
import { ProtoRpc } from 'starpc'
|
|
7
|
+
import { ProtoRpc, ServerContext } from 'starpc'
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* Mock service mocks some RPCs for the e2e tests.
|
|
@@ -42,6 +42,24 @@ export interface Mock {
|
|
|
42
42
|
MockRequest(request: MockMsg, abortSignal?: AbortSignal): Promise<MockMsg>
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Mock service mocks some RPCs for the e2e tests.
|
|
47
|
+
*
|
|
48
|
+
* @generated from service e2e.mock.Mock
|
|
49
|
+
*/
|
|
50
|
+
export interface MockHandler {
|
|
51
|
+
/**
|
|
52
|
+
* MockRequest runs a mock unary request.
|
|
53
|
+
*
|
|
54
|
+
* @generated from rpc e2e.mock.Mock.MockRequest
|
|
55
|
+
*/
|
|
56
|
+
MockRequest(
|
|
57
|
+
request: MockMsg,
|
|
58
|
+
abortSignal: AbortSignal,
|
|
59
|
+
context: ServerContext,
|
|
60
|
+
): Promise<MockMsg>
|
|
61
|
+
}
|
|
62
|
+
|
|
45
63
|
export const MockServiceName = MockDefinition.typeName
|
|
46
64
|
|
|
47
65
|
export class MockClient implements Mock {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "starpc",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.52.0",
|
|
4
4
|
"description": "Streaming protobuf RPC service protocol over any two-way channel.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": {
|
|
@@ -114,7 +114,7 @@
|
|
|
114
114
|
"happy-dom": "^20.9.0",
|
|
115
115
|
"husky": "^9.1.7",
|
|
116
116
|
"lint-staged": "^17.0.0",
|
|
117
|
-
"oxfmt": "0.
|
|
117
|
+
"oxfmt": "0.62.0",
|
|
118
118
|
"oxlint": "^1.76.0",
|
|
119
119
|
"rimraf": "^6.1.3",
|
|
120
120
|
"tsx": "^4.20.4",
|
package/srpc/channel.test.ts
CHANGED
|
@@ -1,9 +1,29 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
1
|
+
import { describe, expect, it, vi } from 'vitest'
|
|
2
2
|
import { pushable } from 'it-pushable'
|
|
3
3
|
|
|
4
4
|
import { ChannelStream } from './channel.js'
|
|
5
5
|
|
|
6
6
|
describe('ChannelStream', () => {
|
|
7
|
+
it('recognizes MessagePort implementations from another realm', () => {
|
|
8
|
+
const port = {
|
|
9
|
+
close: vi.fn(),
|
|
10
|
+
onmessage: null,
|
|
11
|
+
postMessage: vi.fn(),
|
|
12
|
+
start: vi.fn(),
|
|
13
|
+
} as unknown as MessagePort
|
|
14
|
+
|
|
15
|
+
const stream = new ChannelStream<Uint8Array>('client', port)
|
|
16
|
+
try {
|
|
17
|
+
expect(port.start).toHaveBeenCalledOnce()
|
|
18
|
+
expect(port.postMessage).toHaveBeenCalledWith({
|
|
19
|
+
ack: true,
|
|
20
|
+
from: 'client',
|
|
21
|
+
})
|
|
22
|
+
} finally {
|
|
23
|
+
stream.close()
|
|
24
|
+
}
|
|
25
|
+
})
|
|
26
|
+
|
|
7
27
|
it('keeps MessagePort peer writes open after local source completes normally', async () => {
|
|
8
28
|
const { port1, port2 } = new MessageChannel()
|
|
9
29
|
const client = new ChannelStream<Uint8Array>('client', port1)
|