starpc 0.15.4 → 0.16.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/Makefile CHANGED
@@ -110,8 +110,9 @@ gents: $(PROTOWRAP) node_modules
110
110
  --ts_proto_opt=forceLong=long \
111
111
  --ts_proto_opt=oneof=unions \
112
112
  --ts_proto_opt=outputServices=default,outputServices=generic-definitions \
113
- --ts_proto_opt=useDate=true \
113
+ --ts_proto_opt=useAbortSignal=true \
114
114
  --ts_proto_opt=useAsyncIterable=true \
115
+ --ts_proto_opt=useDate=true \
115
116
  --proto_path $$(pwd)/vendor \
116
117
  --print_structure \
117
118
  --only_specified_files \
@@ -21,7 +21,7 @@ export declare const MockMsg: {
21
21
  /** Mock service mocks some RPCs for the e2e tests. */
22
22
  export interface Mock {
23
23
  /** MockRequest runs a mock unary request. */
24
- MockRequest(request: MockMsg): Promise<MockMsg>;
24
+ MockRequest(request: MockMsg, abortSignal?: AbortSignal): Promise<MockMsg>;
25
25
  }
26
26
  export declare class MockClientImpl implements Mock {
27
27
  private readonly rpc;
@@ -29,7 +29,7 @@ export declare class MockClientImpl implements Mock {
29
29
  constructor(rpc: Rpc, opts?: {
30
30
  service?: string;
31
31
  });
32
- MockRequest(request: MockMsg): Promise<MockMsg>;
32
+ MockRequest(request: MockMsg, abortSignal?: AbortSignal): Promise<MockMsg>;
33
33
  }
34
34
  /** Mock service mocks some RPCs for the e2e tests. */
35
35
  export type MockDefinition = typeof MockDefinition;
@@ -73,7 +73,7 @@ export declare const MockDefinition: {
73
73
  };
74
74
  };
75
75
  interface Rpc {
76
- request(service: string, method: string, data: Uint8Array): Promise<Uint8Array>;
76
+ request(service: string, method: string, data: Uint8Array, abortSignal?: AbortSignal): Promise<Uint8Array>;
77
77
  }
78
78
  type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined;
79
79
  export type DeepPartial<T> = T extends Builtin ? T : T extends Long ? string | number | Long : T extends Array<infer U> ? Array<DeepPartial<U>> : T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>> : T extends {
@@ -77,9 +77,9 @@ export class MockClientImpl {
77
77
  this.rpc = rpc;
78
78
  this.MockRequest = this.MockRequest.bind(this);
79
79
  }
80
- MockRequest(request) {
80
+ MockRequest(request, abortSignal) {
81
81
  const data = MockMsg.encode(request).finish();
82
- const promise = this.rpc.request(this.service, 'MockRequest', data);
82
+ const promise = this.rpc.request(this.service, 'MockRequest', data, abortSignal || undefined);
83
83
  return promise.then((data) => MockMsg.decode(new _m0.Reader(data)));
84
84
  }
85
85
  }
@@ -1,3 +1,4 @@
1
1
  import { Client } from '../srpc/index.js';
2
2
  export declare function runClientTest(client: Client): Promise<void>;
3
+ export declare function runAbortControllerTest(client: Client): Promise<void>;
3
4
  export declare function runRpcStreamTest(client: Client): Promise<void>;
@@ -1,4 +1,4 @@
1
- import { Client } from '../srpc/index.js';
1
+ import { Client, ERR_RPC_ABORT } from '../srpc/index.js';
2
2
  import { EchoerClientImpl } from './echo.pb.js';
3
3
  import { pushable } from 'it-pushable';
4
4
  import { buildRpcStreamOpenStream } from '../rpcstream/rpcstream.js';
@@ -24,6 +24,30 @@ export async function runClientTest(client) {
24
24
  console.log('server: output', msg.body);
25
25
  }
26
26
  }
27
+ // runAbortControllerTest tests aborting a RPC call.
28
+ export async function runAbortControllerTest(client) {
29
+ const demoServiceClient = new EchoerClientImpl(client);
30
+ console.log('Testing EchoClientStream with AbortController...');
31
+ let errorReturned = false;
32
+ const clientAbort = new AbortController();
33
+ const clientNoopStream = pushable({ objectMode: true });
34
+ new Promise((resolve) => setTimeout(resolve, 1000)).then(() => {
35
+ clientAbort.abort();
36
+ });
37
+ try {
38
+ await demoServiceClient.EchoClientStream(clientNoopStream, clientAbort.signal);
39
+ }
40
+ catch (err) {
41
+ const errMsg = err.message;
42
+ errorReturned = true;
43
+ if (errMsg !== ERR_RPC_ABORT) {
44
+ throw new Error('unexpected error: ' + errMsg);
45
+ }
46
+ }
47
+ if (!errorReturned) {
48
+ throw new Error('expected aborted rpc to throw error');
49
+ }
50
+ }
27
51
  // runRpcStreamTest tests a RPCStream.
28
52
  export async function runRpcStreamTest(client) {
29
53
  console.log('Calling RpcStream to open a RPC stream client...');
@@ -22,15 +22,15 @@ export declare const EchoMsg: {
22
22
  /** Echoer service returns the given message. */
23
23
  export interface Echoer {
24
24
  /** Echo returns the given message. */
25
- Echo(request: EchoMsg): Promise<EchoMsg>;
25
+ Echo(request: EchoMsg, abortSignal?: AbortSignal): Promise<EchoMsg>;
26
26
  /** EchoServerStream is an example of a server -> client one-way stream. */
27
- EchoServerStream(request: EchoMsg): AsyncIterable<EchoMsg>;
27
+ EchoServerStream(request: EchoMsg, abortSignal?: AbortSignal): AsyncIterable<EchoMsg>;
28
28
  /** EchoClientStream is an example of client->server one-way stream. */
29
- EchoClientStream(request: AsyncIterable<EchoMsg>): Promise<EchoMsg>;
29
+ EchoClientStream(request: AsyncIterable<EchoMsg>, abortSignal?: AbortSignal): Promise<EchoMsg>;
30
30
  /** EchoBidiStream is an example of a two-way stream. */
31
- EchoBidiStream(request: AsyncIterable<EchoMsg>): AsyncIterable<EchoMsg>;
31
+ EchoBidiStream(request: AsyncIterable<EchoMsg>, abortSignal?: AbortSignal): AsyncIterable<EchoMsg>;
32
32
  /** RpcStream opens a nested rpc call stream. */
33
- RpcStream(request: AsyncIterable<RpcStreamPacket>): AsyncIterable<RpcStreamPacket>;
33
+ RpcStream(request: AsyncIterable<RpcStreamPacket>, abortSignal?: AbortSignal): AsyncIterable<RpcStreamPacket>;
34
34
  }
35
35
  export declare class EchoerClientImpl implements Echoer {
36
36
  private readonly rpc;
@@ -38,11 +38,11 @@ export declare class EchoerClientImpl implements Echoer {
38
38
  constructor(rpc: Rpc, opts?: {
39
39
  service?: string;
40
40
  });
41
- Echo(request: EchoMsg): Promise<EchoMsg>;
42
- EchoServerStream(request: EchoMsg): AsyncIterable<EchoMsg>;
43
- EchoClientStream(request: AsyncIterable<EchoMsg>): Promise<EchoMsg>;
44
- EchoBidiStream(request: AsyncIterable<EchoMsg>): AsyncIterable<EchoMsg>;
45
- RpcStream(request: AsyncIterable<RpcStreamPacket>): AsyncIterable<RpcStreamPacket>;
41
+ Echo(request: EchoMsg, abortSignal?: AbortSignal): Promise<EchoMsg>;
42
+ EchoServerStream(request: EchoMsg, abortSignal?: AbortSignal): AsyncIterable<EchoMsg>;
43
+ EchoClientStream(request: AsyncIterable<EchoMsg>, abortSignal?: AbortSignal): Promise<EchoMsg>;
44
+ EchoBidiStream(request: AsyncIterable<EchoMsg>, abortSignal?: AbortSignal): AsyncIterable<EchoMsg>;
45
+ RpcStream(request: AsyncIterable<RpcStreamPacket>, abortSignal?: AbortSignal): AsyncIterable<RpcStreamPacket>;
46
46
  }
47
47
  /** Echoer service returns the given message. */
48
48
  export type EchoerDefinition = typeof EchoerDefinition;
@@ -316,10 +316,10 @@ export declare const EchoerDefinition: {
316
316
  };
317
317
  };
318
318
  interface Rpc {
319
- request(service: string, method: string, data: Uint8Array): Promise<Uint8Array>;
320
- clientStreamingRequest(service: string, method: string, data: AsyncIterable<Uint8Array>): Promise<Uint8Array>;
321
- serverStreamingRequest(service: string, method: string, data: Uint8Array): AsyncIterable<Uint8Array>;
322
- bidirectionalStreamingRequest(service: string, method: string, data: AsyncIterable<Uint8Array>): AsyncIterable<Uint8Array>;
319
+ request(service: string, method: string, data: Uint8Array, abortSignal?: AbortSignal): Promise<Uint8Array>;
320
+ clientStreamingRequest(service: string, method: string, data: AsyncIterable<Uint8Array>, abortSignal?: AbortSignal): Promise<Uint8Array>;
321
+ serverStreamingRequest(service: string, method: string, data: Uint8Array, abortSignal?: AbortSignal): AsyncIterable<Uint8Array>;
322
+ bidirectionalStreamingRequest(service: string, method: string, data: AsyncIterable<Uint8Array>, abortSignal?: AbortSignal): AsyncIterable<Uint8Array>;
323
323
  }
324
324
  type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined;
325
325
  export type DeepPartial<T> = T extends Builtin ? T : T extends Long ? string | number | Long : T extends Array<infer U> ? Array<DeepPartial<U>> : T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>> : T extends {
@@ -82,29 +82,29 @@ export class EchoerClientImpl {
82
82
  this.EchoBidiStream = this.EchoBidiStream.bind(this);
83
83
  this.RpcStream = this.RpcStream.bind(this);
84
84
  }
85
- Echo(request) {
85
+ Echo(request, abortSignal) {
86
86
  const data = EchoMsg.encode(request).finish();
87
- const promise = this.rpc.request(this.service, 'Echo', data);
87
+ const promise = this.rpc.request(this.service, 'Echo', data, abortSignal || undefined);
88
88
  return promise.then((data) => EchoMsg.decode(new _m0.Reader(data)));
89
89
  }
90
- EchoServerStream(request) {
90
+ EchoServerStream(request, abortSignal) {
91
91
  const data = EchoMsg.encode(request).finish();
92
- const result = this.rpc.serverStreamingRequest(this.service, 'EchoServerStream', data);
92
+ const result = this.rpc.serverStreamingRequest(this.service, 'EchoServerStream', data, abortSignal || undefined);
93
93
  return EchoMsg.decodeTransform(result);
94
94
  }
95
- EchoClientStream(request) {
95
+ EchoClientStream(request, abortSignal) {
96
96
  const data = EchoMsg.encodeTransform(request);
97
- const promise = this.rpc.clientStreamingRequest(this.service, 'EchoClientStream', data);
97
+ const promise = this.rpc.clientStreamingRequest(this.service, 'EchoClientStream', data, abortSignal || undefined);
98
98
  return promise.then((data) => EchoMsg.decode(new _m0.Reader(data)));
99
99
  }
100
- EchoBidiStream(request) {
100
+ EchoBidiStream(request, abortSignal) {
101
101
  const data = EchoMsg.encodeTransform(request);
102
- const result = this.rpc.bidirectionalStreamingRequest(this.service, 'EchoBidiStream', data);
102
+ const result = this.rpc.bidirectionalStreamingRequest(this.service, 'EchoBidiStream', data, abortSignal || undefined);
103
103
  return EchoMsg.decodeTransform(result);
104
104
  }
105
- RpcStream(request) {
105
+ RpcStream(request, abortSignal) {
106
106
  const data = RpcStreamPacket.encodeTransform(request);
107
- const result = this.rpc.bidirectionalStreamingRequest(this.service, 'RpcStream', data);
107
+ const result = this.rpc.bidirectionalStreamingRequest(this.service, 'RpcStream', data, abortSignal || undefined);
108
108
  return RpcStreamPacket.decodeTransform(result);
109
109
  }
110
110
  }
@@ -1,5 +1,5 @@
1
1
  import { WebSocketConn } from '../srpc/websocket.js';
2
- import { runClientTest, runRpcStreamTest } from '../echo/client-test.js';
2
+ import { runClientTest, runRpcStreamTest, runAbortControllerTest, } from '../echo/client-test.js';
3
3
  import WebSocket from 'isomorphic-ws';
4
4
  async function runRPC() {
5
5
  const addr = 'ws://localhost:5000/demo';
@@ -7,16 +7,22 @@ async function runRPC() {
7
7
  const ws = new WebSocket(addr);
8
8
  const channel = new WebSocketConn(ws, 'outbound');
9
9
  const client = channel.buildClient();
10
- console.log('Running client test via WebSocket..');
11
- await runClientTest(client);
12
10
  console.log('Running RpcStream test via WebSocket..');
13
11
  await runRpcStreamTest(client);
12
+ console.log('Running client test via WebSocket..');
13
+ await runClientTest(client);
14
+ console.log('Running abort controller test via WebSocket..');
15
+ await runAbortControllerTest(client);
14
16
  }
17
+ process.on('unhandledRejection', (ev) => {
18
+ console.error('Unhandled rejection', ev);
19
+ throw ev;
20
+ });
15
21
  runRPC()
16
22
  .then(() => {
17
23
  process.exit(0);
18
24
  })
19
25
  .catch((err) => {
20
- console.error(err);
26
+ console.error('runRPC threw error', err);
21
27
  process.exit(1);
22
28
  });
@@ -81,11 +81,9 @@ export async function* handleRpcStream(packetStream, getter) {
81
81
  }
82
82
  // build the outgoing packet sink & the packet source
83
83
  const packetSink = pushable({ objectMode: true });
84
- // handle the stream in the next event queue tick.
84
+ // start the handler
85
85
  const rpcStream = new RpcStream(packetSink, packetStream);
86
- setTimeout(() => {
87
- handler(rpcStream);
88
- }, 1);
86
+ handler(rpcStream);
89
87
  // process packets
90
88
  for await (const packet of packetSink) {
91
89
  yield* [packet];
@@ -4,9 +4,9 @@ export declare class Client implements TsProtoRpc {
4
4
  private openStreamCtr;
5
5
  constructor(openStreamFn?: OpenStreamFunc);
6
6
  setOpenStreamFn(openStreamFn?: OpenStreamFunc): void;
7
- request(service: string, method: string, data: Uint8Array): Promise<Uint8Array>;
8
- clientStreamingRequest(service: string, method: string, data: AsyncIterable<Uint8Array>): Promise<Uint8Array>;
9
- serverStreamingRequest(service: string, method: string, data: Uint8Array): AsyncIterable<Uint8Array>;
10
- bidirectionalStreamingRequest(service: string, method: string, data: AsyncIterable<Uint8Array>): AsyncIterable<Uint8Array>;
7
+ request(service: string, method: string, data: Uint8Array, abortSignal?: AbortSignal): Promise<Uint8Array>;
8
+ clientStreamingRequest(service: string, method: string, data: AsyncIterable<Uint8Array>, abortSignal?: AbortSignal): Promise<Uint8Array>;
9
+ serverStreamingRequest(service: string, method: string, data: Uint8Array, abortSignal?: AbortSignal): AsyncIterable<Uint8Array>;
10
+ bidirectionalStreamingRequest(service: string, method: string, data: AsyncIterable<Uint8Array>, abortSignal?: AbortSignal): AsyncIterable<Uint8Array>;
11
11
  private startRpc;
12
12
  }
@@ -1,5 +1,6 @@
1
1
  import { pipe } from 'it-pipe';
2
2
  import { pushable } from 'it-pushable';
3
+ import { ERR_RPC_ABORT } from './errors.js';
3
4
  import { ClientRPC } from './client-rpc.js';
4
5
  import { writeToPushable } from './pushable.js';
5
6
  import { decodePacketSource, encodePacketSource, parseLengthPrefixTransform, prependLengthPrefixTransform, } from './packet.js';
@@ -15,8 +16,8 @@ export class Client {
15
16
  this.openStreamCtr.set(openStreamFn || undefined);
16
17
  }
17
18
  // request starts a non-streaming request.
18
- async request(service, method, data) {
19
- const call = await this.startRpc(service, method, data);
19
+ async request(service, method, data, abortSignal) {
20
+ const call = await this.startRpc(service, method, data, abortSignal);
20
21
  for await (const data of call.rpcDataSource) {
21
22
  call.close();
22
23
  return data;
@@ -26,8 +27,8 @@ export class Client {
26
27
  throw err;
27
28
  }
28
29
  // clientStreamingRequest starts a client side streaming request.
29
- async clientStreamingRequest(service, method, data) {
30
- const call = await this.startRpc(service, method, null);
30
+ async clientStreamingRequest(service, method, data, abortSignal) {
31
+ const call = await this.startRpc(service, method, null, abortSignal);
31
32
  call.writeCallDataFromSource(data);
32
33
  for await (const data of call.rpcDataSource) {
33
34
  call.close();
@@ -38,9 +39,9 @@ export class Client {
38
39
  throw err;
39
40
  }
40
41
  // serverStreamingRequest starts a server-side streaming request.
41
- serverStreamingRequest(service, method, data) {
42
+ serverStreamingRequest(service, method, data, abortSignal) {
42
43
  const serverData = pushable({ objectMode: true });
43
- this.startRpc(service, method, data)
44
+ this.startRpc(service, method, data, abortSignal)
44
45
  .then(async (call) => {
45
46
  return writeToPushable(call.rpcDataSource, serverData);
46
47
  })
@@ -48,9 +49,9 @@ export class Client {
48
49
  return serverData;
49
50
  }
50
51
  // bidirectionalStreamingRequest starts a two-way streaming request.
51
- bidirectionalStreamingRequest(service, method, data) {
52
+ bidirectionalStreamingRequest(service, method, data, abortSignal) {
52
53
  const serverData = pushable({ objectMode: true });
53
- this.startRpc(service, method, null)
54
+ this.startRpc(service, method, null, abortSignal)
54
55
  .then(async (call) => {
55
56
  call.writeCallDataFromSource(data);
56
57
  try {
@@ -69,10 +70,16 @@ export class Client {
69
70
  // startRpc is a common utility function to begin a rpc call.
70
71
  // throws any error starting the rpc call
71
72
  // if data == null and data.length == 0, sends a separate data packet.
72
- async startRpc(rpcService, rpcMethod, data) {
73
+ async startRpc(rpcService, rpcMethod, data, abortSignal) {
74
+ if (abortSignal?.aborted) {
75
+ throw new Error(ERR_RPC_ABORT);
76
+ }
73
77
  const openStreamFn = await this.openStreamCtr.wait();
74
78
  const conn = await openStreamFn();
75
79
  const call = new ClientRPC(rpcService, rpcMethod);
80
+ abortSignal?.addEventListener('abort', () => {
81
+ call.close(new Error(ERR_RPC_ABORT));
82
+ });
76
83
  pipe(conn, parseLengthPrefixTransform(), combineUint8ArrayListTransform(), decodePacketSource, call, encodePacketSource, prependLengthPrefixTransform(), conn);
77
84
  await call.writeCallStart(data || undefined);
78
85
  return call;
@@ -39,9 +39,9 @@ export class CommonRPC {
39
39
  async writeCallDataFromSource(dataSource) {
40
40
  try {
41
41
  for await (const data of dataSource) {
42
- this.writeCallData(data);
42
+ await this.writeCallData(data);
43
43
  }
44
- this.writeCallData(undefined, true);
44
+ await this.writeCallData(undefined, true);
45
45
  }
46
46
  catch (err) {
47
47
  this.close(err);
@@ -117,7 +117,8 @@ export class CommonRPC {
117
117
  }
118
118
  finally {
119
119
  this._rpcDataSource.end(err);
120
- this._source.end(err);
120
+ // note: don't pass error to _source here.
121
+ this._source.end();
121
122
  }
122
123
  }
123
124
  // _createSink returns a value for the sink field.
@@ -0,0 +1,2 @@
1
+ export declare const ERR_RPC_ABORT = "ERR_RPC_ABORT";
2
+ export declare function isAbortError(err: unknown): boolean;
@@ -0,0 +1,10 @@
1
+ // ERR_RPC_ABORT is returned if the RPC was aborted.
2
+ export const ERR_RPC_ABORT = 'ERR_RPC_ABORT';
3
+ // isAbortError checks if the error object is ERR_RPC_ABORT.
4
+ export function isAbortError(err) {
5
+ if (typeof err !== 'object') {
6
+ return false;
7
+ }
8
+ const message = err.message;
9
+ return message === ERR_RPC_ABORT;
10
+ }
@@ -1,4 +1,5 @@
1
1
  export type { PacketHandler, Stream, OpenStreamFunc } from './stream.js';
2
+ export { ERR_RPC_ABORT, isAbortError } from './errors.js';
2
3
  export { Client } from './client.js';
3
4
  export { Server } from './server.js';
4
5
  export { Conn, ConnParams } from './conn.js';
@@ -1,3 +1,4 @@
1
+ export { ERR_RPC_ABORT, isAbortError } from './errors.js';
1
2
  export { Client } from './client.js';
2
3
  export { Server } from './server.js';
3
4
  export { Conn } from './conn.js';
@@ -1,6 +1,6 @@
1
1
  export interface TsProtoRpc {
2
- request(service: string, method: string, data: Uint8Array): Promise<Uint8Array>;
3
- clientStreamingRequest(service: string, method: string, data: AsyncIterable<Uint8Array>): Promise<Uint8Array>;
4
- serverStreamingRequest(service: string, method: string, data: Uint8Array): AsyncIterable<Uint8Array>;
5
- bidirectionalStreamingRequest(service: string, method: string, data: AsyncIterable<Uint8Array>): AsyncIterable<Uint8Array>;
2
+ request(service: string, method: string, data: Uint8Array, abortSignal?: AbortSignal): Promise<Uint8Array>;
3
+ clientStreamingRequest(service: string, method: string, data: AsyncIterable<Uint8Array>, abortSignal?: AbortSignal): Promise<Uint8Array>;
4
+ serverStreamingRequest(service: string, method: string, data: Uint8Array, abortSignal?: AbortSignal): AsyncIterable<Uint8Array>;
5
+ bidirectionalStreamingRequest(service: string, method: string, data: AsyncIterable<Uint8Array>, abortSignal?: AbortSignal): AsyncIterable<Uint8Array>;
6
6
  }
@@ -96,7 +96,7 @@ export const MockMsg = {
96
96
  /** Mock service mocks some RPCs for the e2e tests. */
97
97
  export interface Mock {
98
98
  /** MockRequest runs a mock unary request. */
99
- MockRequest(request: MockMsg): Promise<MockMsg>
99
+ MockRequest(request: MockMsg, abortSignal?: AbortSignal): Promise<MockMsg>
100
100
  }
101
101
 
102
102
  export class MockClientImpl implements Mock {
@@ -107,9 +107,14 @@ export class MockClientImpl implements Mock {
107
107
  this.rpc = rpc
108
108
  this.MockRequest = this.MockRequest.bind(this)
109
109
  }
110
- MockRequest(request: MockMsg): Promise<MockMsg> {
110
+ MockRequest(request: MockMsg, abortSignal?: AbortSignal): Promise<MockMsg> {
111
111
  const data = MockMsg.encode(request).finish()
112
- const promise = this.rpc.request(this.service, 'MockRequest', data)
112
+ const promise = this.rpc.request(
113
+ this.service,
114
+ 'MockRequest',
115
+ data,
116
+ abortSignal || undefined
117
+ )
113
118
  return promise.then((data) => MockMsg.decode(new _m0.Reader(data)))
114
119
  }
115
120
  }
@@ -136,7 +141,8 @@ interface Rpc {
136
141
  request(
137
142
  service: string,
138
143
  method: string,
139
- data: Uint8Array
144
+ data: Uint8Array,
145
+ abortSignal?: AbortSignal
140
146
  ): Promise<Uint8Array>
141
147
  }
142
148
 
@@ -1,5 +1,5 @@
1
1
  // Code generated by protoc-gen-srpc. DO NOT EDIT.
2
- // protoc-gen-srpc version: v0.15.1
2
+ // protoc-gen-srpc version: v0.15.4
3
3
  // source: github.com/aperturerobotics/starpc/e2e/mock/mock.proto
4
4
 
5
5
  package e2e_mock
@@ -1,4 +1,4 @@
1
- import { Client } from '../srpc/index.js'
1
+ import { Client, ERR_RPC_ABORT } from '../srpc/index.js'
2
2
  import { EchoerClientImpl, EchoMsg } from './echo.pb.js'
3
3
  import { pushable } from 'it-pushable'
4
4
  import { buildRpcStreamOpenStream } from '../rpcstream/rpcstream.js'
@@ -30,6 +30,34 @@ export async function runClientTest(client: Client) {
30
30
  }
31
31
  }
32
32
 
33
+ // runAbortControllerTest tests aborting a RPC call.
34
+ export async function runAbortControllerTest(client: Client) {
35
+ const demoServiceClient = new EchoerClientImpl(client)
36
+
37
+ console.log('Testing EchoClientStream with AbortController...')
38
+ let errorReturned = false
39
+ const clientAbort = new AbortController()
40
+ const clientNoopStream = pushable<EchoMsg>({ objectMode: true })
41
+ new Promise((resolve) => setTimeout(resolve, 1000)).then(() => {
42
+ clientAbort.abort()
43
+ })
44
+ try {
45
+ await demoServiceClient.EchoClientStream(
46
+ clientNoopStream,
47
+ clientAbort.signal
48
+ )
49
+ } catch (err) {
50
+ const errMsg = (err as Error).message
51
+ errorReturned = true
52
+ if (errMsg !== ERR_RPC_ABORT) {
53
+ throw new Error('unexpected error: ' + errMsg)
54
+ }
55
+ }
56
+ if (!errorReturned) {
57
+ throw new Error('expected aborted rpc to throw error')
58
+ }
59
+ }
60
+
33
61
  // runRpcStreamTest tests a RPCStream.
34
62
  export async function runRpcStreamTest(client: Client) {
35
63
  console.log('Calling RpcStream to open a RPC stream client...')
package/echo/echo.pb.ts CHANGED
@@ -97,16 +97,26 @@ export const EchoMsg = {
97
97
  /** Echoer service returns the given message. */
98
98
  export interface Echoer {
99
99
  /** Echo returns the given message. */
100
- Echo(request: EchoMsg): Promise<EchoMsg>
100
+ Echo(request: EchoMsg, abortSignal?: AbortSignal): Promise<EchoMsg>
101
101
  /** EchoServerStream is an example of a server -> client one-way stream. */
102
- EchoServerStream(request: EchoMsg): AsyncIterable<EchoMsg>
102
+ EchoServerStream(
103
+ request: EchoMsg,
104
+ abortSignal?: AbortSignal
105
+ ): AsyncIterable<EchoMsg>
103
106
  /** EchoClientStream is an example of client->server one-way stream. */
104
- EchoClientStream(request: AsyncIterable<EchoMsg>): Promise<EchoMsg>
107
+ EchoClientStream(
108
+ request: AsyncIterable<EchoMsg>,
109
+ abortSignal?: AbortSignal
110
+ ): Promise<EchoMsg>
105
111
  /** EchoBidiStream is an example of a two-way stream. */
106
- EchoBidiStream(request: AsyncIterable<EchoMsg>): AsyncIterable<EchoMsg>
112
+ EchoBidiStream(
113
+ request: AsyncIterable<EchoMsg>,
114
+ abortSignal?: AbortSignal
115
+ ): AsyncIterable<EchoMsg>
107
116
  /** RpcStream opens a nested rpc call stream. */
108
117
  RpcStream(
109
- request: AsyncIterable<RpcStreamPacket>
118
+ request: AsyncIterable<RpcStreamPacket>,
119
+ abortSignal?: AbortSignal
110
120
  ): AsyncIterable<RpcStreamPacket>
111
121
  }
112
122
 
@@ -122,50 +132,69 @@ export class EchoerClientImpl implements Echoer {
122
132
  this.EchoBidiStream = this.EchoBidiStream.bind(this)
123
133
  this.RpcStream = this.RpcStream.bind(this)
124
134
  }
125
- Echo(request: EchoMsg): Promise<EchoMsg> {
135
+ Echo(request: EchoMsg, abortSignal?: AbortSignal): Promise<EchoMsg> {
126
136
  const data = EchoMsg.encode(request).finish()
127
- const promise = this.rpc.request(this.service, 'Echo', data)
137
+ const promise = this.rpc.request(
138
+ this.service,
139
+ 'Echo',
140
+ data,
141
+ abortSignal || undefined
142
+ )
128
143
  return promise.then((data) => EchoMsg.decode(new _m0.Reader(data)))
129
144
  }
130
145
 
131
- EchoServerStream(request: EchoMsg): AsyncIterable<EchoMsg> {
146
+ EchoServerStream(
147
+ request: EchoMsg,
148
+ abortSignal?: AbortSignal
149
+ ): AsyncIterable<EchoMsg> {
132
150
  const data = EchoMsg.encode(request).finish()
133
151
  const result = this.rpc.serverStreamingRequest(
134
152
  this.service,
135
153
  'EchoServerStream',
136
- data
154
+ data,
155
+ abortSignal || undefined
137
156
  )
138
157
  return EchoMsg.decodeTransform(result)
139
158
  }
140
159
 
141
- EchoClientStream(request: AsyncIterable<EchoMsg>): Promise<EchoMsg> {
160
+ EchoClientStream(
161
+ request: AsyncIterable<EchoMsg>,
162
+ abortSignal?: AbortSignal
163
+ ): Promise<EchoMsg> {
142
164
  const data = EchoMsg.encodeTransform(request)
143
165
  const promise = this.rpc.clientStreamingRequest(
144
166
  this.service,
145
167
  'EchoClientStream',
146
- data
168
+ data,
169
+ abortSignal || undefined
147
170
  )
148
171
  return promise.then((data) => EchoMsg.decode(new _m0.Reader(data)))
149
172
  }
150
173
 
151
- EchoBidiStream(request: AsyncIterable<EchoMsg>): AsyncIterable<EchoMsg> {
174
+ EchoBidiStream(
175
+ request: AsyncIterable<EchoMsg>,
176
+ abortSignal?: AbortSignal
177
+ ): AsyncIterable<EchoMsg> {
152
178
  const data = EchoMsg.encodeTransform(request)
153
179
  const result = this.rpc.bidirectionalStreamingRequest(
154
180
  this.service,
155
181
  'EchoBidiStream',
156
- data
182
+ data,
183
+ abortSignal || undefined
157
184
  )
158
185
  return EchoMsg.decodeTransform(result)
159
186
  }
160
187
 
161
188
  RpcStream(
162
- request: AsyncIterable<RpcStreamPacket>
189
+ request: AsyncIterable<RpcStreamPacket>,
190
+ abortSignal?: AbortSignal
163
191
  ): AsyncIterable<RpcStreamPacket> {
164
192
  const data = RpcStreamPacket.encodeTransform(request)
165
193
  const result = this.rpc.bidirectionalStreamingRequest(
166
194
  this.service,
167
195
  'RpcStream',
168
- data
196
+ data,
197
+ abortSignal || undefined
169
198
  )
170
199
  return RpcStreamPacket.decodeTransform(result)
171
200
  }
@@ -229,22 +258,26 @@ interface Rpc {
229
258
  request(
230
259
  service: string,
231
260
  method: string,
232
- data: Uint8Array
261
+ data: Uint8Array,
262
+ abortSignal?: AbortSignal
233
263
  ): Promise<Uint8Array>
234
264
  clientStreamingRequest(
235
265
  service: string,
236
266
  method: string,
237
- data: AsyncIterable<Uint8Array>
267
+ data: AsyncIterable<Uint8Array>,
268
+ abortSignal?: AbortSignal
238
269
  ): Promise<Uint8Array>
239
270
  serverStreamingRequest(
240
271
  service: string,
241
272
  method: string,
242
- data: Uint8Array
273
+ data: Uint8Array,
274
+ abortSignal?: AbortSignal
243
275
  ): AsyncIterable<Uint8Array>
244
276
  bidirectionalStreamingRequest(
245
277
  service: string,
246
278
  method: string,
247
- data: AsyncIterable<Uint8Array>
279
+ data: AsyncIterable<Uint8Array>,
280
+ abortSignal?: AbortSignal
248
281
  ): AsyncIterable<Uint8Array>
249
282
  }
250
283
 
@@ -1,5 +1,5 @@
1
1
  // Code generated by protoc-gen-srpc. DO NOT EDIT.
2
- // protoc-gen-srpc version: v0.15.1
2
+ // protoc-gen-srpc version: v0.15.4
3
3
  // source: github.com/aperturerobotics/starpc/echo/echo.proto
4
4
 
5
5
  package echo
package/go.mod CHANGED
@@ -5,12 +5,12 @@ go 1.18
5
5
  require (
6
6
  github.com/pkg/errors v0.9.1
7
7
  google.golang.org/protobuf v1.28.1
8
- nhooyr.io/websocket v1.8.8-0.20210410000328-8dee580a7f74
8
+ nhooyr.io/websocket v1.8.8-0.20221213223501-14fb98eba64e
9
9
  )
10
10
 
11
11
  require (
12
- github.com/aperturerobotics/util v0.0.0-20221202094321-2fde40039383
13
- github.com/libp2p/go-libp2p v0.24.0-dev.0.20221202071826-2cc4de512664
12
+ github.com/aperturerobotics/util v0.0.0-20221205090205-f776a34d2d0d
13
+ github.com/libp2p/go-libp2p v0.24.1
14
14
  github.com/libp2p/go-yamux/v4 v4.0.1-0.20220919134236-1c09f2ab3ec1
15
15
  github.com/sirupsen/logrus v1.9.0
16
16
  )
@@ -19,23 +19,23 @@ require (
19
19
  github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect
20
20
  github.com/gogo/protobuf v1.3.2 // indirect
21
21
  github.com/ipfs/go-cid v0.3.2 // indirect
22
- github.com/klauspost/compress v1.15.10 // indirect
23
- github.com/klauspost/cpuid/v2 v2.1.1 // indirect
22
+ github.com/klauspost/compress v1.15.12 // indirect
23
+ github.com/klauspost/cpuid/v2 v2.2.1 // indirect
24
24
  github.com/libp2p/go-buffer-pool v0.1.0 // indirect
25
25
  github.com/libp2p/go-openssl v0.1.0 // indirect
26
26
  github.com/mattn/go-pointer v0.0.1 // indirect
27
27
  github.com/minio/sha256-simd v1.0.0 // indirect
28
28
  github.com/mr-tron/base58 v1.2.0 // indirect
29
29
  github.com/multiformats/go-base32 v0.1.0 // indirect
30
- github.com/multiformats/go-base36 v0.1.1-0.20220823151017-f5af2eed4d9c // indirect
30
+ github.com/multiformats/go-base36 v0.2.0 // indirect
31
31
  github.com/multiformats/go-multiaddr v0.8.0 // indirect
32
32
  github.com/multiformats/go-multibase v0.1.2-0.20220823162309-7160a7347ed1 // indirect
33
33
  github.com/multiformats/go-multicodec v0.7.1-0.20221017174837-a2baec7ca709 // indirect
34
34
  github.com/multiformats/go-multihash v0.2.2-0.20221030163302-608669da49b6 // indirect
35
- github.com/multiformats/go-varint v0.0.7-0.20220823162201-881f9a52d5d2 // indirect
35
+ github.com/multiformats/go-varint v0.0.7 // indirect
36
36
  github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect
37
37
  github.com/spaolacci/murmur3 v1.1.1-0.20190317074736-539464a789e9 // indirect
38
- golang.org/x/crypto v0.2.1-0.20221109165004-21d60a152191 // indirect
39
- golang.org/x/sys v0.2.0 // indirect
38
+ golang.org/x/crypto v0.3.0 // indirect
39
+ golang.org/x/sys v0.3.0 // indirect
40
40
  lukechampine.com/blake3 v1.1.8-0.20220321170924-7afca5966e5e // indirect
41
41
  )
package/go.sum CHANGED
@@ -1,5 +1,5 @@
1
- github.com/aperturerobotics/util v0.0.0-20221202094321-2fde40039383 h1:r0TEmQAFN1Ji+1iMqBW1ymiofevQaUb3uOQTje2wp3Q=
2
- github.com/aperturerobotics/util v0.0.0-20221202094321-2fde40039383/go.mod h1:up2AYcp62UgmFVTm7QhM4USXAKGv73gpb5dHraKmzxQ=
1
+ github.com/aperturerobotics/util v0.0.0-20221205090205-f776a34d2d0d h1:nYXEY4LHTW7a7Vf+Lt+7icxoc02wIyLCa12xPbFuIxo=
2
+ github.com/aperturerobotics/util v0.0.0-20221205090205-f776a34d2d0d/go.mod h1:up2AYcp62UgmFVTm7QhM4USXAKGv73gpb5dHraKmzxQ=
3
3
  github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
4
4
  github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
5
5
  github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -42,18 +42,18 @@ github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/u
42
42
  github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
43
43
  github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
44
44
  github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
45
- github.com/klauspost/compress v1.15.10 h1:Ai8UzuomSCDw90e1qNMtb15msBXsNpH6gzkkENQNcJo=
46
- github.com/klauspost/compress v1.15.10/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
45
+ github.com/klauspost/compress v1.15.12 h1:YClS/PImqYbn+UILDnqxQCZ3RehC9N318SU3kElDUEM=
46
+ github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
47
47
  github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
48
48
  github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
49
- github.com/klauspost/cpuid/v2 v2.1.1 h1:t0wUqjowdm8ezddV5k0tLWVklVuvLJpoHeb4WBdydm0=
50
- github.com/klauspost/cpuid/v2 v2.1.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
49
+ github.com/klauspost/cpuid/v2 v2.2.1 h1:U33DW0aiEj633gHYw3LoDNfkDiYnE5Q8M/TKJn2f2jI=
50
+ github.com/klauspost/cpuid/v2 v2.2.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
51
51
  github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
52
52
  github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
53
53
  github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8=
54
54
  github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg=
55
- github.com/libp2p/go-libp2p v0.24.0-dev.0.20221202071826-2cc4de512664 h1:pv+MuIVoXKlPfxZ/dGjK+aDK3fFmfuo7ombAjrBN7z0=
56
- github.com/libp2p/go-libp2p v0.24.0-dev.0.20221202071826-2cc4de512664/go.mod h1:WVV1V9SPcZ0uV/sBB5X3zsKZW/EikukrgTp1ABOcqWk=
55
+ github.com/libp2p/go-libp2p v0.24.1 h1:+lS4fqj7RF9egcPq9Yo3iqdRTcDMApzoBbQMhxtwOVw=
56
+ github.com/libp2p/go-libp2p v0.24.1/go.mod h1:5LJqbrqFsUzWrq70JHCYqjATlX4ey8Klpct3OEe8hSI=
57
57
  github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA=
58
58
  github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo=
59
59
  github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc=
@@ -73,8 +73,8 @@ github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
73
73
  github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc=
74
74
  github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE=
75
75
  github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI=
76
- github.com/multiformats/go-base36 v0.1.1-0.20220823151017-f5af2eed4d9c h1:3eLctj5+2JpWf0E8PICebevkyD0KcZUhye8ggScrnEQ=
77
- github.com/multiformats/go-base36 v0.1.1-0.20220823151017-f5af2eed4d9c/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4=
76
+ github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0=
77
+ github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4=
78
78
  github.com/multiformats/go-multiaddr v0.8.0 h1:aqjksEcqK+iD/Foe1RRFsGZh8+XFiGo7FgUCZlpv3LU=
79
79
  github.com/multiformats/go-multiaddr v0.8.0/go.mod h1:Fs50eBDWvZu+l3/9S6xAE7ZYj6yhxlvaVZjakWN7xRs=
80
80
  github.com/multiformats/go-multibase v0.1.2-0.20220823162309-7160a7347ed1 h1:fts9VGSGzcENj3XnQ3iz9LGMAJAqT46fUGyaUDGFuxQ=
@@ -83,8 +83,8 @@ github.com/multiformats/go-multicodec v0.7.1-0.20221017174837-a2baec7ca709 h1:Xw
83
83
  github.com/multiformats/go-multicodec v0.7.1-0.20221017174837-a2baec7ca709/go.mod h1:GUC8upxSBE4oG+q3kWZRw/+6yC1BqO550bjhWsJbZlw=
84
84
  github.com/multiformats/go-multihash v0.2.2-0.20221030163302-608669da49b6 h1:qLF997Rz0X1WvdcZ2r5CUkLZ2rvdiXwG1JRSrJZEAuE=
85
85
  github.com/multiformats/go-multihash v0.2.2-0.20221030163302-608669da49b6/go.mod h1:kaHxr8TfO1cxIR/tYxgZ7e59HraJq8arEQQR8E/YNvI=
86
- github.com/multiformats/go-varint v0.0.7-0.20220823162201-881f9a52d5d2 h1:zsa4CR/QUzyyNdyaotjJrqFzKbzCsWzhQWcUSamGWr8=
87
- github.com/multiformats/go-varint v0.0.7-0.20220823162201-881f9a52d5d2/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU=
86
+ github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8=
87
+ github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU=
88
88
  github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
89
89
  github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
90
90
  github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@@ -99,7 +99,7 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
99
99
  github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
100
100
  github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
101
101
  github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
102
- github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
102
+ github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
103
103
  github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
104
104
  github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
105
105
  github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
@@ -109,8 +109,8 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
109
109
  golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
110
110
  golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
111
111
  golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
112
- golang.org/x/crypto v0.2.1-0.20221109165004-21d60a152191 h1:mPxyLskqfKMEHYEIa1kKmcNC8ZSiJLYbMaXyUpW+ooY=
113
- golang.org/x/crypto v0.2.1-0.20221109165004-21d60a152191/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
112
+ golang.org/x/crypto v0.3.0 h1:a06MkbcxBrEFc0w0QIZWXrH/9cCX6KJyWbBOIwAn+7A=
113
+ golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
114
114
  golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
115
115
  golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
116
116
  golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@@ -126,8 +126,8 @@ golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7w
126
126
  golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
127
127
  golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
128
128
  golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
129
- golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
130
- golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
129
+ golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
130
+ golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
131
131
  golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
132
132
  golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
133
133
  golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -152,5 +152,5 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
152
152
  gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
153
153
  lukechampine.com/blake3 v1.1.8-0.20220321170924-7afca5966e5e h1:YeQnmA0g9M+7uVBjUXNUpVQRfWrvTu4I5u+DjFudflw=
154
154
  lukechampine.com/blake3 v1.1.8-0.20220321170924-7afca5966e5e/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA=
155
- nhooyr.io/websocket v1.8.8-0.20210410000328-8dee580a7f74 h1:V2XOYY4rGPHLTGQD4TiOMOfVwNd0zAuEPofzzEqiFWk=
156
- nhooyr.io/websocket v1.8.8-0.20210410000328-8dee580a7f74/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
155
+ nhooyr.io/websocket v1.8.8-0.20221213223501-14fb98eba64e h1:Sk+k5z84Elo/gfEvX1xQR83Yhd6ETPmVDJTXUd2BxR4=
156
+ nhooyr.io/websocket v1.8.8-0.20221213223501-14fb98eba64e/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0=
@@ -1,5 +1,9 @@
1
1
  import { WebSocketConn } from '../srpc/websocket.js'
2
- import { runClientTest, runRpcStreamTest } from '../echo/client-test.js'
2
+ import {
3
+ runClientTest,
4
+ runRpcStreamTest,
5
+ runAbortControllerTest,
6
+ } from '../echo/client-test.js'
3
7
  import WebSocket from 'isomorphic-ws'
4
8
 
5
9
  async function runRPC() {
@@ -9,18 +13,26 @@ async function runRPC() {
9
13
  const channel = new WebSocketConn(ws, 'outbound')
10
14
  const client = channel.buildClient()
11
15
 
16
+ console.log('Running RpcStream test via WebSocket..')
17
+ await runRpcStreamTest(client)
18
+
12
19
  console.log('Running client test via WebSocket..')
13
20
  await runClientTest(client)
14
21
 
15
- console.log('Running RpcStream test via WebSocket..')
16
- await runRpcStreamTest(client)
22
+ console.log('Running abort controller test via WebSocket..')
23
+ await runAbortControllerTest(client)
17
24
  }
18
25
 
26
+ process.on('unhandledRejection', (ev) => {
27
+ console.error('Unhandled rejection', ev)
28
+ throw ev
29
+ })
30
+
19
31
  runRPC()
20
32
  .then(() => {
21
33
  process.exit(0)
22
34
  })
23
35
  .catch((err) => {
24
- console.error(err)
36
+ console.error('runRPC threw error', err)
25
37
  process.exit(1)
26
38
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "starpc",
3
- "version": "0.15.4",
3
+ "version": "0.16.0",
4
4
  "description": "Streaming protobuf RPC service protocol over any two-way channel.",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -60,33 +60,33 @@
60
60
  "singleQuote": true
61
61
  },
62
62
  "devDependencies": {
63
- "@aperturerobotics/ts-common": "^0.2.6",
64
- "@typescript-eslint/eslint-plugin": "^5.45.0",
65
- "@typescript-eslint/parser": "^5.45.0",
63
+ "@aperturerobotics/ts-common": "^0.3.1",
64
+ "@typescript-eslint/eslint-plugin": "^5.46.1",
65
+ "@typescript-eslint/parser": "^5.46.1",
66
66
  "bufferutil": "^4.0.7",
67
67
  "depcheck": "^1.4.3",
68
- "esbuild": "^0.15.16",
69
- "eslint": "^8.28.0",
68
+ "esbuild": "^0.16.7",
69
+ "eslint": "^8.29.0",
70
70
  "eslint-config-prettier": "^8.5.0",
71
- "prettier": "^2.8.0",
71
+ "prettier": "^2.8.1",
72
72
  "rimraf": "^3.0.2",
73
- "ts-proto": "^1.135.0",
74
- "typescript": "^4.9.3",
73
+ "ts-proto": "^1.136.0",
74
+ "typescript": "^4.9.4",
75
75
  "utf-8-validate": "^5.0.10"
76
76
  },
77
77
  "dependencies": {
78
78
  "@chainsafe/libp2p-yamux": "^3.0.3",
79
- "@libp2p/interface-connection": "^3.0.3",
80
- "@libp2p/interface-stream-muxer": "^3.0.1",
79
+ "@libp2p/interface-connection": "^3.0.4",
80
+ "@libp2p/interface-stream-muxer": "^3.0.2",
81
81
  "event-iterator": "^2.0.0",
82
82
  "is-promise": "^4.0.0",
83
83
  "isomorphic-ws": "^5.0.0",
84
84
  "it-first": "^2.0.0",
85
85
  "it-length-prefixed": "^8.0.3",
86
- "it-pipe": "^2.0.4",
86
+ "it-pipe": "^2.0.5",
87
87
  "it-pushable": "^3.1.0",
88
88
  "it-stream-types": "^1.0.4",
89
- "it-ws": "^5.0.3",
89
+ "it-ws": "^5.0.6",
90
90
  "long": "^5.2.1",
91
91
  "patch-package": "^6.5.0",
92
92
  "protobufjs": "^7.1.2",
package/srpc/client.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { pipe } from 'it-pipe'
2
2
  import { pushable, Pushable } from 'it-pushable'
3
3
 
4
+ import { ERR_RPC_ABORT } from './errors.js'
4
5
  import type { TsProtoRpc } from './ts-proto-rpc.js'
5
6
  import type { OpenStreamFunc } from './stream.js'
6
7
  import { ClientRPC } from './client-rpc.js'
@@ -32,9 +33,10 @@ export class Client implements TsProtoRpc {
32
33
  public async request(
33
34
  service: string,
34
35
  method: string,
35
- data: Uint8Array
36
+ data: Uint8Array,
37
+ abortSignal?: AbortSignal
36
38
  ): Promise<Uint8Array> {
37
- const call = await this.startRpc(service, method, data)
39
+ const call = await this.startRpc(service, method, data, abortSignal)
38
40
  for await (const data of call.rpcDataSource) {
39
41
  call.close()
40
42
  return data
@@ -48,9 +50,10 @@ export class Client implements TsProtoRpc {
48
50
  public async clientStreamingRequest(
49
51
  service: string,
50
52
  method: string,
51
- data: AsyncIterable<Uint8Array>
53
+ data: AsyncIterable<Uint8Array>,
54
+ abortSignal?: AbortSignal
52
55
  ): Promise<Uint8Array> {
53
- const call = await this.startRpc(service, method, null)
56
+ const call = await this.startRpc(service, method, null, abortSignal)
54
57
  call.writeCallDataFromSource(data)
55
58
  for await (const data of call.rpcDataSource) {
56
59
  call.close()
@@ -65,10 +68,11 @@ export class Client implements TsProtoRpc {
65
68
  public serverStreamingRequest(
66
69
  service: string,
67
70
  method: string,
68
- data: Uint8Array
71
+ data: Uint8Array,
72
+ abortSignal?: AbortSignal
69
73
  ): AsyncIterable<Uint8Array> {
70
74
  const serverData: Pushable<Uint8Array> = pushable({ objectMode: true })
71
- this.startRpc(service, method, data)
75
+ this.startRpc(service, method, data, abortSignal)
72
76
  .then(async (call) => {
73
77
  return writeToPushable(call.rpcDataSource, serverData)
74
78
  })
@@ -80,10 +84,11 @@ export class Client implements TsProtoRpc {
80
84
  public bidirectionalStreamingRequest(
81
85
  service: string,
82
86
  method: string,
83
- data: AsyncIterable<Uint8Array>
87
+ data: AsyncIterable<Uint8Array>,
88
+ abortSignal?: AbortSignal
84
89
  ): AsyncIterable<Uint8Array> {
85
90
  const serverData: Pushable<Uint8Array> = pushable({ objectMode: true })
86
- this.startRpc(service, method, null)
91
+ this.startRpc(service, method, null, abortSignal)
87
92
  .then(async (call) => {
88
93
  call.writeCallDataFromSource(data)
89
94
  try {
@@ -105,11 +110,18 @@ export class Client implements TsProtoRpc {
105
110
  private async startRpc(
106
111
  rpcService: string,
107
112
  rpcMethod: string,
108
- data: Uint8Array | null
113
+ data: Uint8Array | null,
114
+ abortSignal?: AbortSignal
109
115
  ): Promise<ClientRPC> {
116
+ if (abortSignal?.aborted) {
117
+ throw new Error(ERR_RPC_ABORT)
118
+ }
110
119
  const openStreamFn = await this.openStreamCtr.wait()
111
120
  const conn = await openStreamFn()
112
121
  const call = new ClientRPC(rpcService, rpcMethod)
122
+ abortSignal?.addEventListener('abort', () => {
123
+ call.close(new Error(ERR_RPC_ABORT))
124
+ })
113
125
  pipe(
114
126
  conn,
115
127
  parseLengthPrefixTransform(),
@@ -74,9 +74,9 @@ export class CommonRPC {
74
74
  public async writeCallDataFromSource(dataSource: AsyncIterable<Uint8Array>) {
75
75
  try {
76
76
  for await (const data of dataSource) {
77
- this.writeCallData(data)
77
+ await this.writeCallData(data)
78
78
  }
79
- this.writeCallData(undefined, true)
79
+ await this.writeCallData(undefined, true)
80
80
  } catch (err) {
81
81
  this.close(err as Error)
82
82
  }
@@ -160,7 +160,8 @@ export class CommonRPC {
160
160
  await this.writeCallCancel()
161
161
  } finally {
162
162
  this._rpcDataSource.end(err)
163
- this._source.end(err)
163
+ // note: don't pass error to _source here.
164
+ this._source.end()
164
165
  }
165
166
  }
166
167
 
package/srpc/errors.ts ADDED
@@ -0,0 +1,11 @@
1
+ // ERR_RPC_ABORT is returned if the RPC was aborted.
2
+ export const ERR_RPC_ABORT = 'ERR_RPC_ABORT'
3
+
4
+ // isAbortError checks if the error object is ERR_RPC_ABORT.
5
+ export function isAbortError(err: unknown): boolean {
6
+ if (typeof err !== 'object') {
7
+ return false
8
+ }
9
+ const message = (err as Error).message
10
+ return message === ERR_RPC_ABORT
11
+ }
package/srpc/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export type { PacketHandler, Stream, OpenStreamFunc } from './stream.js'
2
+ export { ERR_RPC_ABORT, isAbortError } from './errors.js'
2
3
  export { Client } from './client.js'
3
4
  export { Server } from './server.js'
4
5
  export { Conn, ConnParams } from './conn.js'
@@ -4,24 +4,28 @@ export interface TsProtoRpc {
4
4
  request(
5
5
  service: string,
6
6
  method: string,
7
- data: Uint8Array
7
+ data: Uint8Array,
8
+ abortSignal?: AbortSignal
8
9
  ): Promise<Uint8Array>
9
10
  // clientStreamingRequest fires a one-way client->server streaming request.
10
11
  clientStreamingRequest(
11
12
  service: string,
12
13
  method: string,
13
- data: AsyncIterable<Uint8Array>
14
+ data: AsyncIterable<Uint8Array>,
15
+ abortSignal?: AbortSignal
14
16
  ): Promise<Uint8Array>
15
17
  // serverStreamingRequest fires a one-way server->client streaming request.
16
18
  serverStreamingRequest(
17
19
  service: string,
18
20
  method: string,
19
- data: Uint8Array
21
+ data: Uint8Array,
22
+ abortSignal?: AbortSignal
20
23
  ): AsyncIterable<Uint8Array>
21
24
  // bidirectionalStreamingRequest implements a two-way streaming request.
22
25
  bidirectionalStreamingRequest(
23
26
  service: string,
24
27
  method: string,
25
- data: AsyncIterable<Uint8Array>
28
+ data: AsyncIterable<Uint8Array>,
29
+ abortSignal?: AbortSignal
26
30
  ): AsyncIterable<Uint8Array>
27
31
  }