starpc 0.49.10 → 0.49.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/srpc/channel.d.ts +6 -0
- package/dist/srpc/channel.js +38 -6
- package/dist/srpc/channel.test.d.ts +1 -0
- package/dist/srpc/channel.test.js +89 -0
- package/dist/srpc/server.test.js +64 -1
- package/dist/srpc/stream.js +9 -5
- package/dist/srpc/stream.test.d.ts +1 -0
- package/dist/srpc/stream.test.js +121 -0
- package/go.mod +2 -2
- package/go.sum +4 -6
- package/package.json +1 -1
- package/srpc/channel.test.ts +108 -0
- package/srpc/channel.ts +46 -6
- package/srpc/client-rpc.go +28 -28
- package/srpc/common-rpc.go +62 -66
- package/srpc/common-rpc_test.go +29 -1
- package/srpc/server-rpc.go +22 -20
- package/srpc/server.test.ts +72 -0
- package/srpc/stream.test.ts +174 -0
- package/srpc/stream.ts +8 -5
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { pipe } from 'it-pipe'
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
ChannelStream,
|
|
6
|
+
combineUint8ArrayListTransform,
|
|
7
|
+
StreamConn,
|
|
8
|
+
type ChannelStreamOpts,
|
|
9
|
+
type PacketStream,
|
|
10
|
+
type StreamHandler,
|
|
11
|
+
} from '../srpc/index.js'
|
|
12
|
+
|
|
13
|
+
describe('StreamConn packet stream', () => {
|
|
14
|
+
it('keeps yamux peer writes open after local packet source completes normally', async () => {
|
|
15
|
+
const request = new TextEncoder().encode('request')
|
|
16
|
+
const response = new TextEncoder().encode('response')
|
|
17
|
+
|
|
18
|
+
let serverError: unknown
|
|
19
|
+
const serverDone = new Promise<void>((resolve, reject) => {
|
|
20
|
+
const { clientConn, cleanup } = connectStreamConns({
|
|
21
|
+
handlePacketStream(stream: PacketStream) {
|
|
22
|
+
void (async () => {
|
|
23
|
+
const packets = stream.source[Symbol.asyncIterator]()
|
|
24
|
+
const first = await nextWithTimeout(
|
|
25
|
+
packets,
|
|
26
|
+
'server request packet',
|
|
27
|
+
)
|
|
28
|
+
expect(first.done).toBe(false)
|
|
29
|
+
expect([...first.value]).toEqual([...request])
|
|
30
|
+
|
|
31
|
+
const done = await nextWithTimeout(packets, 'server request eof')
|
|
32
|
+
expect(done.done).toBe(true)
|
|
33
|
+
|
|
34
|
+
await stream.sink(
|
|
35
|
+
(async function* () {
|
|
36
|
+
yield response
|
|
37
|
+
})(),
|
|
38
|
+
)
|
|
39
|
+
})()
|
|
40
|
+
.then(resolve)
|
|
41
|
+
.catch((err) => {
|
|
42
|
+
serverError = err
|
|
43
|
+
reject(err)
|
|
44
|
+
})
|
|
45
|
+
},
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
void (async () => {
|
|
49
|
+
try {
|
|
50
|
+
const clientStream = await clientConn.openStream()
|
|
51
|
+
await clientStream.sink(
|
|
52
|
+
(async function* () {
|
|
53
|
+
yield request
|
|
54
|
+
})(),
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
const packets = clientStream.source[Symbol.asyncIterator]()
|
|
58
|
+
const first = await nextWithTimeout(packets, 'client response packet')
|
|
59
|
+
expect(first.done).toBe(false)
|
|
60
|
+
expect([...first.value]).toEqual([...response])
|
|
61
|
+
|
|
62
|
+
const done = await nextWithTimeout(packets, 'client response eof')
|
|
63
|
+
expect(done.done).toBe(true)
|
|
64
|
+
} finally {
|
|
65
|
+
cleanup()
|
|
66
|
+
}
|
|
67
|
+
})().catch(reject)
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
await serverDone
|
|
71
|
+
expect(serverError).toBeUndefined()
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
it('aborts the yamux stream when the packet source errors', async () => {
|
|
75
|
+
const request = new TextEncoder().encode('request')
|
|
76
|
+
const sourceError = new Error('source failed')
|
|
77
|
+
let resolveReset: (err: unknown) => void = () => {}
|
|
78
|
+
const resetSeen = new Promise<unknown>((resolve) => {
|
|
79
|
+
resolveReset = resolve
|
|
80
|
+
})
|
|
81
|
+
const { clientConn, cleanup } = connectStreamConns({
|
|
82
|
+
handlePacketStream(stream: PacketStream) {
|
|
83
|
+
void (async () => {
|
|
84
|
+
try {
|
|
85
|
+
for await (const _packet of stream.source) {
|
|
86
|
+
// Drain until the reset arrives.
|
|
87
|
+
}
|
|
88
|
+
resolveReset(new Error('server stream ended without reset'))
|
|
89
|
+
} catch (err) {
|
|
90
|
+
resolveReset(err)
|
|
91
|
+
}
|
|
92
|
+
})()
|
|
93
|
+
},
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
try {
|
|
97
|
+
const clientStream = await clientConn.openStream()
|
|
98
|
+
await expect(
|
|
99
|
+
clientStream.sink(
|
|
100
|
+
(async function* () {
|
|
101
|
+
yield request
|
|
102
|
+
throw sourceError
|
|
103
|
+
})(),
|
|
104
|
+
),
|
|
105
|
+
).rejects.toThrow('source failed')
|
|
106
|
+
|
|
107
|
+
const resetErr = await promiseWithTimeout(resetSeen, 'server reset')
|
|
108
|
+
expect(resetErr).toBeInstanceOf(Error)
|
|
109
|
+
expect((resetErr as Error).message).toBe('stream reset')
|
|
110
|
+
} finally {
|
|
111
|
+
cleanup()
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
function connectStreamConns(server: StreamHandler): {
|
|
117
|
+
clientConn: StreamConn
|
|
118
|
+
cleanup: () => void
|
|
119
|
+
} {
|
|
120
|
+
const clientConn = new StreamConn()
|
|
121
|
+
const serverConn = new StreamConn(server, { direction: 'inbound' })
|
|
122
|
+
|
|
123
|
+
const { port1: clientPort, port2: serverPort } = new MessageChannel()
|
|
124
|
+
const opts: ChannelStreamOpts = {}
|
|
125
|
+
const clientChannelStream = new ChannelStream('client', clientPort, opts)
|
|
126
|
+
const serverChannelStream = new ChannelStream('server', serverPort, opts)
|
|
127
|
+
|
|
128
|
+
pipe(
|
|
129
|
+
clientChannelStream,
|
|
130
|
+
clientConn,
|
|
131
|
+
combineUint8ArrayListTransform(),
|
|
132
|
+
clientChannelStream,
|
|
133
|
+
)
|
|
134
|
+
.catch((err: Error) => clientConn.close(err))
|
|
135
|
+
.then(() => clientConn.close())
|
|
136
|
+
|
|
137
|
+
pipe(
|
|
138
|
+
serverChannelStream,
|
|
139
|
+
serverConn,
|
|
140
|
+
combineUint8ArrayListTransform(),
|
|
141
|
+
serverChannelStream,
|
|
142
|
+
)
|
|
143
|
+
.catch((err: Error) => serverConn.close(err))
|
|
144
|
+
.then(() => serverConn.close())
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
clientConn,
|
|
148
|
+
cleanup() {
|
|
149
|
+
clientConn.close()
|
|
150
|
+
serverConn.close()
|
|
151
|
+
clientChannelStream.close()
|
|
152
|
+
serverChannelStream.close()
|
|
153
|
+
},
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async function nextWithTimeout<T>(
|
|
158
|
+
source: AsyncIterator<T>,
|
|
159
|
+
label: string,
|
|
160
|
+
): Promise<IteratorResult<T>> {
|
|
161
|
+
return promiseWithTimeout(source.next(), label)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async function promiseWithTimeout<T>(
|
|
165
|
+
promise: Promise<T>,
|
|
166
|
+
label: string,
|
|
167
|
+
): Promise<T> {
|
|
168
|
+
return Promise.race([
|
|
169
|
+
promise,
|
|
170
|
+
new Promise<T>((_, reject) => {
|
|
171
|
+
setTimeout(() => reject(new Error(`timed out waiting for ${label}`)), 500)
|
|
172
|
+
}),
|
|
173
|
+
])
|
|
174
|
+
}
|
package/srpc/stream.ts
CHANGED
|
@@ -28,8 +28,6 @@ export type OpenStreamFunc = () => Promise<PacketStream>
|
|
|
28
28
|
export type HandleStreamFunc = (ch: PacketStream) => Promise<void>
|
|
29
29
|
|
|
30
30
|
// streamToPacketStream converts a Stream into a PacketStream using length-prefix framing.
|
|
31
|
-
//
|
|
32
|
-
// The stream is closed when the source writing to the sink ends.
|
|
33
31
|
export function streamToPacketStream(stream: Stream): PacketStream {
|
|
34
32
|
return {
|
|
35
33
|
source: pipe(
|
|
@@ -38,9 +36,14 @@ export function streamToPacketStream(stream: Stream): PacketStream {
|
|
|
38
36
|
combineUint8ArrayListTransform(),
|
|
39
37
|
),
|
|
40
38
|
sink: async (source: Source<Uint8Array>): Promise<void> => {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
try {
|
|
40
|
+
await pipe(source, prependLengthPrefixTransform(), stream)
|
|
41
|
+
await stream.closeWrite()
|
|
42
|
+
} catch (err) {
|
|
43
|
+
const error = err instanceof Error ? err : new Error(String(err))
|
|
44
|
+
stream.abort(error)
|
|
45
|
+
throw error
|
|
46
|
+
}
|
|
44
47
|
},
|
|
45
48
|
}
|
|
46
49
|
}
|