starpc 0.14.0 → 0.14.1

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.
@@ -1,11 +1,9 @@
1
1
  import type { TsProtoRpc } from './ts-proto-rpc.js';
2
2
  import type { OpenStreamFunc } from './stream.js';
3
3
  export declare class Client implements TsProtoRpc {
4
- private openStreamFn;
5
- private _openStreamFn?;
4
+ private openStreamCtr;
6
5
  constructor(openStreamFn?: OpenStreamFunc);
7
- setOpenStreamFn(openStreamFn?: OpenStreamFunc): Promise<OpenStreamFunc>;
8
- private initOpenStreamFn;
6
+ setOpenStreamFn(openStreamFn?: OpenStreamFunc): void;
9
7
  request(service: string, method: string, data: Uint8Array): Promise<Uint8Array>;
10
8
  clientStreamingRequest(service: string, method: string, data: AsyncIterable<Uint8Array>): Promise<Uint8Array>;
11
9
  serverStreamingRequest(service: string, method: string, data: Uint8Array): AsyncIterable<Uint8Array>;
@@ -4,43 +4,15 @@ import { ClientRPC } from './client-rpc.js';
4
4
  import { writeToPushable } from './pushable.js';
5
5
  import { decodePacketSource, encodePacketSource, parseLengthPrefixTransform, prependLengthPrefixTransform, } from './packet.js';
6
6
  import { combineUint8ArrayListTransform } from './array-list.js';
7
+ import { OpenStreamCtr } from './open-stream-ctr.js';
7
8
  // Client implements the ts-proto Rpc interface with the drpcproto protocol.
8
9
  export class Client {
9
10
  constructor(openStreamFn) {
10
- this.openStreamFn = this.setOpenStreamFn(openStreamFn);
11
+ this.openStreamCtr = new OpenStreamCtr(openStreamFn || undefined);
11
12
  }
12
13
  // setOpenStreamFn updates the openStreamFn for the Client.
13
14
  setOpenStreamFn(openStreamFn) {
14
- if (this._openStreamFn) {
15
- if (openStreamFn) {
16
- this._openStreamFn(openStreamFn);
17
- this._openStreamFn = undefined;
18
- }
19
- }
20
- else {
21
- if (openStreamFn) {
22
- this.openStreamFn = Promise.resolve(openStreamFn);
23
- }
24
- else {
25
- this.initOpenStreamFn();
26
- }
27
- }
28
- return this.openStreamFn;
29
- }
30
- // initOpenStreamFn creates the empty Promise for openStreamFn.
31
- initOpenStreamFn() {
32
- const openPromise = new Promise((resolve, reject) => {
33
- this._openStreamFn = (conn, err) => {
34
- if (err) {
35
- reject(err);
36
- }
37
- else if (conn) {
38
- resolve(conn);
39
- }
40
- };
41
- });
42
- this.openStreamFn = openPromise;
43
- return this.openStreamFn;
15
+ this.openStreamCtr.set(openStreamFn || undefined);
44
16
  }
45
17
  // request starts a non-streaming request.
46
18
  async request(service, method, data) {
@@ -98,7 +70,7 @@ export class Client {
98
70
  // throws any error starting the rpc call
99
71
  // if data == null and data.length == 0, sends a separate data packet.
100
72
  async startRpc(rpcService, rpcMethod, data) {
101
- const openStreamFn = await this.openStreamFn;
73
+ const openStreamFn = await this.openStreamCtr.wait();
102
74
  const conn = await openStreamFn();
103
75
  const call = new ClientRPC(rpcService, rpcMethod);
104
76
  pipe(conn, parseLengthPrefixTransform(), combineUint8ArrayListTransform(), decodePacketSource, call, encodePacketSource, prependLengthPrefixTransform(), conn);
@@ -7,4 +7,6 @@ export { Packet, CallStart, CallData } from './rpcproto.pb.js';
7
7
  export { Mux, StaticMux, createMux } from './mux.js';
8
8
  export { BroadcastChannelDuplex, newBroadcastChannelDuplex, BroadcastChannelConn, } from './broadcast-channel.js';
9
9
  export { MessagePortIterable, newMessagePortIterable, MessagePortConn, } from './message-port.js';
10
+ export { ValueCtr } from './value-ctr.js';
11
+ export { OpenStreamCtr } from './open-stream-ctr.js';
10
12
  export { writeToPushable } from './pushable';
@@ -6,4 +6,6 @@ export { Packet, CallStart, CallData } from './rpcproto.pb.js';
6
6
  export { StaticMux, createMux } from './mux.js';
7
7
  export { BroadcastChannelDuplex, newBroadcastChannelDuplex, BroadcastChannelConn, } from './broadcast-channel.js';
8
8
  export { MessagePortIterable, newMessagePortIterable, MessagePortConn, } from './message-port.js';
9
+ export { ValueCtr } from './value-ctr.js';
10
+ export { OpenStreamCtr } from './open-stream-ctr.js';
9
11
  export { writeToPushable } from './pushable';
@@ -0,0 +1,6 @@
1
+ import { OpenStreamFunc } from './stream.js';
2
+ import { ValueCtr } from './value-ctr.js';
3
+ export declare class OpenStreamCtr extends ValueCtr<OpenStreamFunc> {
4
+ constructor(openStreamFn?: OpenStreamFunc);
5
+ get openStreamFunc(): OpenStreamFunc;
6
+ }
@@ -0,0 +1,17 @@
1
+ import { ValueCtr } from './value-ctr.js';
2
+ // OpenStreamCtr contains an OpenStream func which can be awaited.
3
+ export class OpenStreamCtr extends ValueCtr {
4
+ constructor(openStreamFn) {
5
+ super(openStreamFn);
6
+ }
7
+ // openStreamFunc returns an OpenStreamFunc which waits for the underlying OpenStreamFunc.
8
+ get openStreamFunc() {
9
+ return async () => {
10
+ let openFn = this.value;
11
+ if (!openFn) {
12
+ openFn = await this.wait();
13
+ }
14
+ return openFn();
15
+ };
16
+ }
17
+ }
@@ -0,0 +1,9 @@
1
+ export declare class ValueCtr<T> {
2
+ private _value;
3
+ private _waiters;
4
+ constructor(initialValue?: T);
5
+ get value(): T | undefined;
6
+ wait(): Promise<T>;
7
+ waitWithCb(cb: (val: T) => void): void;
8
+ set(val: T | undefined): void;
9
+ }
@@ -0,0 +1,44 @@
1
+ // ValueCtr contains a value that can be set asynchronously.
2
+ export class ValueCtr {
3
+ constructor(initialValue) {
4
+ this._value = initialValue || undefined;
5
+ this._waiters = [];
6
+ }
7
+ // value returns the current value.
8
+ get value() {
9
+ return this._value;
10
+ }
11
+ // wait waits for the value to not be undefined.
12
+ async wait() {
13
+ const currVal = this._value;
14
+ if (currVal !== undefined) {
15
+ return currVal;
16
+ }
17
+ return new Promise((resolve) => {
18
+ this.waitWithCb((val) => {
19
+ resolve(val);
20
+ });
21
+ });
22
+ }
23
+ // waitWithCb adds a callback to be called when the value is not undefined.
24
+ waitWithCb(cb) {
25
+ if (cb) {
26
+ this._waiters.push(cb);
27
+ }
28
+ }
29
+ // set sets the value and calls the callbacks.
30
+ set(val) {
31
+ this._value = val;
32
+ if (val === undefined) {
33
+ return;
34
+ }
35
+ const waiters = this._waiters;
36
+ if (waiters.length === 0) {
37
+ return;
38
+ }
39
+ this._waiters = [];
40
+ for (const waiter of waiters) {
41
+ waiter(val);
42
+ }
43
+ }
44
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "starpc",
3
- "version": "0.14.0",
3
+ "version": "0.14.1",
4
4
  "description": "Streaming protobuf RPC service protocol over any two-way channel.",
5
5
  "license": "MIT",
6
6
  "author": {
package/srpc/client.ts CHANGED
@@ -12,50 +12,20 @@ import {
12
12
  prependLengthPrefixTransform,
13
13
  } from './packet.js'
14
14
  import { combineUint8ArrayListTransform } from './array-list.js'
15
+ import { OpenStreamCtr } from './open-stream-ctr.js'
15
16
 
16
17
  // Client implements the ts-proto Rpc interface with the drpcproto protocol.
17
18
  export class Client implements TsProtoRpc {
18
- // openStreamFn is a promise which contains the OpenStreamFunc.
19
- private openStreamFn: Promise<OpenStreamFunc>
20
- // _openStreamFn resolves openStreamFn.
21
- private _openStreamFn?: (conn?: OpenStreamFunc, err?: Error) => void
19
+ // openStreamCtr contains the OpenStreamFunc.
20
+ private openStreamCtr: OpenStreamCtr
22
21
 
23
22
  constructor(openStreamFn?: OpenStreamFunc) {
24
- this.openStreamFn = this.setOpenStreamFn(openStreamFn)
23
+ this.openStreamCtr = new OpenStreamCtr(openStreamFn || undefined)
25
24
  }
26
25
 
27
26
  // setOpenStreamFn updates the openStreamFn for the Client.
28
- public setOpenStreamFn(
29
- openStreamFn?: OpenStreamFunc
30
- ): Promise<OpenStreamFunc> {
31
- if (this._openStreamFn) {
32
- if (openStreamFn) {
33
- this._openStreamFn(openStreamFn)
34
- this._openStreamFn = undefined
35
- }
36
- } else {
37
- if (openStreamFn) {
38
- this.openStreamFn = Promise.resolve(openStreamFn)
39
- } else {
40
- this.initOpenStreamFn()
41
- }
42
- }
43
- return this.openStreamFn
44
- }
45
-
46
- // initOpenStreamFn creates the empty Promise for openStreamFn.
47
- private initOpenStreamFn(): Promise<OpenStreamFunc> {
48
- const openPromise = new Promise<OpenStreamFunc>((resolve, reject) => {
49
- this._openStreamFn = (conn?: OpenStreamFunc, err?: Error) => {
50
- if (err) {
51
- reject(err)
52
- } else if (conn) {
53
- resolve(conn)
54
- }
55
- }
56
- })
57
- this.openStreamFn = openPromise
58
- return this.openStreamFn
27
+ public setOpenStreamFn(openStreamFn?: OpenStreamFunc) {
28
+ this.openStreamCtr.set(openStreamFn || undefined)
59
29
  }
60
30
 
61
31
  // request starts a non-streaming request.
@@ -137,7 +107,7 @@ export class Client implements TsProtoRpc {
137
107
  rpcMethod: string,
138
108
  data: Uint8Array | null
139
109
  ): Promise<ClientRPC> {
140
- const openStreamFn = await this.openStreamFn
110
+ const openStreamFn = await this.openStreamCtr.wait()
141
111
  const conn = await openStreamFn()
142
112
  const call = new ClientRPC(rpcService, rpcMethod)
143
113
  pipe(
package/srpc/index.ts CHANGED
@@ -15,4 +15,6 @@ export {
15
15
  newMessagePortIterable,
16
16
  MessagePortConn,
17
17
  } from './message-port.js'
18
+ export { ValueCtr } from './value-ctr.js'
19
+ export { OpenStreamCtr } from './open-stream-ctr.js'
18
20
  export { writeToPushable } from './pushable'
@@ -15,6 +15,7 @@ func NewYamuxConfig() *yamux.Config {
15
15
  // Configuration options from go-libp2p-yamux:
16
16
  config := *ymuxer.DefaultTransport.Config()
17
17
  config.AcceptBacklog = 512
18
+ config.EnableKeepAlive = false
18
19
  return &config
19
20
  }
20
21
 
@@ -0,0 +1,20 @@
1
+ import { OpenStreamFunc, Stream } from './stream.js'
2
+ import { ValueCtr } from './value-ctr.js'
3
+
4
+ // OpenStreamCtr contains an OpenStream func which can be awaited.
5
+ export class OpenStreamCtr extends ValueCtr<OpenStreamFunc> {
6
+ constructor(openStreamFn?: OpenStreamFunc) {
7
+ super(openStreamFn)
8
+ }
9
+
10
+ // openStreamFunc returns an OpenStreamFunc which waits for the underlying OpenStreamFunc.
11
+ get openStreamFunc(): OpenStreamFunc {
12
+ return async (): Promise<Stream> => {
13
+ let openFn = this.value
14
+ if (!openFn) {
15
+ openFn = await this.wait()
16
+ }
17
+ return openFn()
18
+ }
19
+ }
20
+ }
package/srpc/packet-rw.go CHANGED
@@ -94,7 +94,7 @@ func (r *PacketReaderWriter) ReadToHandler(cb PacketHandler) error {
94
94
 
95
95
  // parse the length prefix if not done already
96
96
  if currLen == 0 {
97
- currLen = r.readLengthPrefix(r.buf.Bytes())
97
+ currLen = r.readLengthPrefix(r.buf.Bytes()[:4])
98
98
  if currLen == 0 {
99
99
  return errors.New("unexpected zero len prefix")
100
100
  }
@@ -0,0 +1,54 @@
1
+ // ValueCtr contains a value that can be set asynchronously.
2
+ export class ValueCtr<T> {
3
+ // _value contains the current value.
4
+ private _value: T | undefined
5
+ // _waiters contains the list of waiters.
6
+ // called when the value is set to any value other than undefined.
7
+ private _waiters: ((fn: T) => void)[]
8
+
9
+ constructor(initialValue?: T) {
10
+ this._value = initialValue || undefined
11
+ this._waiters = []
12
+ }
13
+
14
+ // value returns the current value.
15
+ get value(): T | undefined {
16
+ return this._value
17
+ }
18
+
19
+ // wait waits for the value to not be undefined.
20
+ public async wait(): Promise<T> {
21
+ const currVal = this._value
22
+ if (currVal !== undefined) {
23
+ return currVal
24
+ }
25
+ return new Promise<T>((resolve) => {
26
+ this.waitWithCb((val: T) => {
27
+ resolve(val)
28
+ })
29
+ })
30
+ }
31
+
32
+ // waitWithCb adds a callback to be called when the value is not undefined.
33
+ public waitWithCb(cb: (val: T) => void) {
34
+ if (cb) {
35
+ this._waiters.push(cb)
36
+ }
37
+ }
38
+
39
+ // set sets the value and calls the callbacks.
40
+ public set(val: T | undefined) {
41
+ this._value = val
42
+ if (val === undefined) {
43
+ return
44
+ }
45
+ const waiters = this._waiters
46
+ if (waiters.length === 0) {
47
+ return
48
+ }
49
+ this._waiters = []
50
+ for (const waiter of waiters) {
51
+ waiter(val)
52
+ }
53
+ }
54
+ }
package/srpc/websocket.go CHANGED
@@ -2,65 +2,20 @@ package srpc
2
2
 
3
3
  import (
4
4
  "context"
5
- "io"
6
5
 
7
6
  "github.com/libp2p/go-libp2p/core/network"
8
7
  "github.com/libp2p/go-yamux/v4"
9
8
  "nhooyr.io/websocket"
10
9
  )
11
10
 
12
- // WebSocketConn implements the p2p multiplexer over a WebSocket.
13
- type WebSocketConn struct {
14
- // conn is the websocket conn
15
- conn *websocket.Conn
16
- // mconn is the muxed conn
17
- mconn network.MuxedConn
18
- }
19
-
20
- // NewWebSocketConn constructs a new WebSocket connection.
21
- //
11
+ // NewWebSocketConn wraps a websocket into a MuxedConn.
22
12
  // if yamuxConf is unset, uses the defaults.
23
- func NewWebSocketConn(ctx context.Context, conn *websocket.Conn, isServer bool, yamuxConf *yamux.Config) (*WebSocketConn, error) {
13
+ func NewWebSocketConn(
14
+ ctx context.Context,
15
+ conn *websocket.Conn,
16
+ isServer bool,
17
+ yamuxConf *yamux.Config,
18
+ ) (network.MuxedConn, error) {
24
19
  nc := websocket.NetConn(ctx, conn, websocket.MessageBinary)
25
- muxedConn, err := NewMuxedConn(nc, !isServer, yamuxConf)
26
- if err != nil {
27
- return nil, err
28
- }
29
- return &WebSocketConn{conn: conn, mconn: muxedConn}, nil
30
- }
31
-
32
- // GetWebSocket returns the web socket conn.
33
- func (w *WebSocketConn) GetWebSocket() *websocket.Conn {
34
- return w.conn
35
- }
36
-
37
- // GetOpenStreamFunc returns the OpenStream func.
38
- func (w *WebSocketConn) GetOpenStreamFunc() OpenStreamFunc {
39
- return w.OpenStream
40
- }
41
-
42
- // AcceptStream accepts an incoming stream.
43
- func (w *WebSocketConn) AcceptStream() (io.ReadWriteCloser, error) {
44
- strm, err := w.mconn.AcceptStream()
45
- if err != nil {
46
- return nil, err
47
- }
48
- return strm, nil
49
- }
50
-
51
- // OpenStream tries to open a stream with the remote.
52
- func (w *WebSocketConn) OpenStream(ctx context.Context, msgHandler PacketHandler, closeHandler CloseHandler) (Writer, error) {
53
- muxedStream, err := w.mconn.OpenStream(ctx)
54
- if err != nil {
55
- return nil, err
56
- }
57
-
58
- rw := NewPacketReadWriter(muxedStream)
59
- go rw.ReadPump(msgHandler, closeHandler)
60
- return rw, nil
61
- }
62
-
63
- // Close closes the writer.
64
- func (w *WebSocketConn) Close() error {
65
- return w.conn.Close(websocket.StatusGoingAway, "conn closed")
20
+ return NewMuxedConn(nc, !isServer, yamuxConf)
66
21
  }