bare-rpc 0.2.6 → 0.2.7
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/README.md +16 -14
- package/index.js +5 -2
- package/lib/incoming-request.js +9 -5
- package/lib/messages.js +77 -90
- package/lib/outgoing-request.js +13 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,6 +11,7 @@ npm i bare-rpc
|
|
|
11
11
|
```js
|
|
12
12
|
import RPC from 'bare-rpc'
|
|
13
13
|
|
|
14
|
+
// On one end
|
|
14
15
|
const rpc = new RPC(stream, (req) => {
|
|
15
16
|
if (req.command === 42) {
|
|
16
17
|
console.log(req.data.toString()) // ping
|
|
@@ -30,7 +31,7 @@ console.log(replyBuffer.toString()) // pong
|
|
|
30
31
|
|
|
31
32
|
### `RPC`
|
|
32
33
|
|
|
33
|
-
#### `const rpc = new RPC(stream,
|
|
34
|
+
#### `const rpc = new RPC(stream[, onrequest])`
|
|
34
35
|
|
|
35
36
|
Create an RPC instance using a duplex `stream`. `onrequest` is an optional callback run when a remote request is received. This is where processing and responding to an RPC request happens. `onrequest` receives a `RPCIncomingRequest` as an argument, for example:
|
|
36
37
|
|
|
@@ -64,7 +65,7 @@ A boolean for whether the request has been sent.
|
|
|
64
65
|
|
|
65
66
|
A boolean for whether the request has received a reply.
|
|
66
67
|
|
|
67
|
-
#### `req.send(data,
|
|
68
|
+
#### `req.send(data[, encoding])`
|
|
68
69
|
|
|
69
70
|
Send the request with the provided `data`. `data` can be a buffer or a string which will be encoded using `encoding`.
|
|
70
71
|
|
|
@@ -74,13 +75,13 @@ Send the request with the provided `data`. `data` can be a buffer or a string wh
|
|
|
74
75
|
|
|
75
76
|
Await the reply from the remote end to the request. `encoding` can be defined for decoding the response `data` buffer back into a string.
|
|
76
77
|
|
|
77
|
-
#### `const stream = req.createRequestStream([
|
|
78
|
+
#### `const stream = req.createRequestStream([options])`
|
|
78
79
|
|
|
79
|
-
Create a [`Writable`](https://github.com/mafintosh/streamx
|
|
80
|
+
Create a [`Writable`](https://github.com/mafintosh/streamx#writable-stream) stream for sending data with the request.
|
|
80
81
|
|
|
81
|
-
#### `const stream = req.createResponseStream([
|
|
82
|
+
#### `const stream = req.createResponseStream([options])`
|
|
82
83
|
|
|
83
|
-
Create a [`Readable`](https://github.com/mafintosh/streamx
|
|
84
|
+
Create a [`Readable`](https://github.com/mafintosh/streamx#readable-stream) stream for receiving data in reply to the request.
|
|
84
85
|
|
|
85
86
|
### `RPCIncomingRequest`
|
|
86
87
|
|
|
@@ -104,11 +105,11 @@ A boolean for whether the request has been received as a stream. See [`req.creat
|
|
|
104
105
|
|
|
105
106
|
Reply to the request with the provided `data`. `data` can be a buffer or a string which will be encoded using `encoding`.
|
|
106
107
|
|
|
107
|
-
#### `const stream = req.createRequestStream([
|
|
108
|
+
#### `const stream = req.createRequestStream([options])`
|
|
108
109
|
|
|
109
110
|
Create a `Readable` stream for receiving data from the request.
|
|
110
111
|
|
|
111
|
-
#### `const stream = req.createResponseStream([
|
|
112
|
+
#### `const stream = req.createResponseStream([options])`
|
|
112
113
|
|
|
113
114
|
Create a `Writable` stream for sending data in reply to the request.
|
|
114
115
|
|
|
@@ -118,8 +119,7 @@ An alternative way to define commands and handlers for receiving them.
|
|
|
118
119
|
|
|
119
120
|
#### `const router = new RPC.CommandRouter()`
|
|
120
121
|
|
|
121
|
-
Create a new command router. This router can then be used when creating an
|
|
122
|
-
`rpc`. For example:
|
|
122
|
+
Create a new command router. This router can then be used when creating an `rpc`. For example:
|
|
123
123
|
|
|
124
124
|
```js
|
|
125
125
|
const router = new RPC.CommandRouter()
|
|
@@ -135,16 +135,18 @@ const req = rpc.request(42)
|
|
|
135
135
|
req.send('ping')
|
|
136
136
|
```
|
|
137
137
|
|
|
138
|
-
#### `router.respond(command,
|
|
138
|
+
#### `router.respond(command[, options], async (req, data) => {})`
|
|
139
139
|
|
|
140
140
|
Define a command and the handler for it. The callback for a command receives both the request (`req`) and the `data` buffer and can return a value to respond. If the request is responded to in the callback, the return value is ignored.
|
|
141
141
|
|
|
142
|
-
|
|
142
|
+
Options include:
|
|
143
143
|
|
|
144
144
|
```
|
|
145
145
|
{
|
|
146
|
-
|
|
147
|
-
|
|
146
|
+
// Encoding for incoming request
|
|
147
|
+
requestEncoding: c.raw,
|
|
148
|
+
// Encoding for outgoing response
|
|
149
|
+
responseEncoding: c.raw,
|
|
148
150
|
}
|
|
149
151
|
```
|
|
150
152
|
|
package/index.js
CHANGED
|
@@ -12,7 +12,6 @@ const CommandRouter = require('./lib/command-router')
|
|
|
12
12
|
module.exports = exports = class RPC {
|
|
13
13
|
constructor(stream, onrequest = noop) {
|
|
14
14
|
this._stream = stream
|
|
15
|
-
|
|
16
15
|
this._id = 0
|
|
17
16
|
|
|
18
17
|
this._outgoingRequests = new Map()
|
|
@@ -43,7 +42,11 @@ module.exports = exports = class RPC {
|
|
|
43
42
|
}
|
|
44
43
|
|
|
45
44
|
_sendMessage(message) {
|
|
46
|
-
|
|
45
|
+
const header = c.encode(m.header, message)
|
|
46
|
+
|
|
47
|
+
this._stream.write(header)
|
|
48
|
+
|
|
49
|
+
if (message.data) this._stream.write(message.data)
|
|
47
50
|
}
|
|
48
51
|
|
|
49
52
|
_sendRequest(request, data = null) {
|
package/lib/incoming-request.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const c = require('compact-encoding')
|
|
2
2
|
const errors = require('./errors')
|
|
3
3
|
|
|
4
4
|
module.exports = class RPCIncomingRequest {
|
|
@@ -19,12 +19,16 @@ module.exports = class RPCIncomingRequest {
|
|
|
19
19
|
throw errors.ALREADY_SENT('Response has already been sent')
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
+
encoding =
|
|
23
|
+
encoding && encoding !== 'buffer'
|
|
24
|
+
? c.from(encoding)
|
|
25
|
+
: typeof data === 'string'
|
|
26
|
+
? c.raw.utf8
|
|
27
|
+
: null
|
|
28
|
+
|
|
22
29
|
this.sent = true
|
|
23
30
|
|
|
24
|
-
this.rpc._sendResponse(
|
|
25
|
-
this,
|
|
26
|
-
typeof data === 'string' ? b4a.from(data, encoding) : data
|
|
27
|
-
)
|
|
31
|
+
this.rpc._sendResponse(this, encoding ? c.encode(encoding, data) : data)
|
|
28
32
|
}
|
|
29
33
|
|
|
30
34
|
createResponseStream(opts = {}) {
|
package/lib/messages.js
CHANGED
|
@@ -2,26 +2,6 @@ const c = require('compact-encoding')
|
|
|
2
2
|
const { type: t, stream: s } = require('./constants')
|
|
3
3
|
const errors = require('./errors')
|
|
4
4
|
|
|
5
|
-
const request = {
|
|
6
|
-
preencode(state, m) {
|
|
7
|
-
c.uint.preencode(state, m.command)
|
|
8
|
-
c.uint.preencode(state, m.stream)
|
|
9
|
-
if (m.stream === 0) c.buffer.preencode(state, m.data)
|
|
10
|
-
},
|
|
11
|
-
encode(state, m) {
|
|
12
|
-
c.uint.encode(state, m.command)
|
|
13
|
-
c.uint.encode(state, m.stream)
|
|
14
|
-
if (m.stream === 0) c.buffer.encode(state, m.data)
|
|
15
|
-
},
|
|
16
|
-
decode(state, id) {
|
|
17
|
-
const command = c.uint.decode(state)
|
|
18
|
-
const stream = c.uint.decode(state)
|
|
19
|
-
const data = stream === 0 ? c.buffer.decode(state) : null
|
|
20
|
-
|
|
21
|
-
return { type: t.REQUEST, id, command, stream, data }
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
5
|
const error = {
|
|
26
6
|
preencode(state, m) {
|
|
27
7
|
c.utf8.preencode(state, m.message)
|
|
@@ -41,79 +21,38 @@ const error = {
|
|
|
41
21
|
}
|
|
42
22
|
}
|
|
43
23
|
|
|
44
|
-
|
|
45
|
-
preencode(state, m) {
|
|
46
|
-
c.bool.preencode(state, !!m.error)
|
|
47
|
-
c.uint.preencode(state, m.stream)
|
|
48
|
-
|
|
49
|
-
if (m.error) error.preencode(state, m.error)
|
|
50
|
-
else if (m.stream === 0) c.buffer.preencode(state, m.data)
|
|
51
|
-
},
|
|
52
|
-
encode(state, m) {
|
|
53
|
-
c.bool.encode(state, !!m.error)
|
|
54
|
-
c.uint.encode(state, m.stream)
|
|
55
|
-
|
|
56
|
-
if (m.error) error.encode(state, m.error)
|
|
57
|
-
else if (m.stream === 0) c.buffer.encode(state, m.data)
|
|
58
|
-
},
|
|
59
|
-
decode(state) {
|
|
60
|
-
const err = c.bool.decode(state)
|
|
61
|
-
const stream = c.uint.decode(state)
|
|
62
|
-
|
|
63
|
-
if (err) {
|
|
64
|
-
return { stream, error: error.decode(state), data: null }
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (stream === 0) {
|
|
68
|
-
return { stream, error: null, data: c.buffer.decode(state) }
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
return { stream, error: null, data: null }
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
const stream = {
|
|
76
|
-
preencode(state, m) {
|
|
77
|
-
c.uint.preencode(state, m.stream)
|
|
78
|
-
|
|
79
|
-
if (m.stream & s.ERROR) error.preencode(state, m.error)
|
|
80
|
-
else if (m.stream & s.DATA) c.buffer.preencode(state, m.data)
|
|
81
|
-
},
|
|
82
|
-
encode(state, m) {
|
|
83
|
-
c.uint.encode(state, m.stream)
|
|
84
|
-
|
|
85
|
-
if (m.stream & s.ERROR) error.encode(state, m.error)
|
|
86
|
-
else if (m.stream & s.DATA) c.buffer.encode(state, m.data)
|
|
87
|
-
},
|
|
88
|
-
decode(state) {
|
|
89
|
-
const stream = c.uint.decode(state)
|
|
90
|
-
|
|
91
|
-
if (stream & s.ERROR)
|
|
92
|
-
return { stream, error: error.decode(state), data: null }
|
|
93
|
-
if (stream & s.DATA)
|
|
94
|
-
return { stream, error: null, data: c.buffer.decode(state) }
|
|
95
|
-
|
|
96
|
-
return { stream, error: null, data: null }
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
exports.message = {
|
|
24
|
+
exports.header = {
|
|
101
25
|
preencode(state, m) {
|
|
102
26
|
c.uint32.preencode(state, 0) // Frame
|
|
103
27
|
c.uint.preencode(state, m.type)
|
|
104
28
|
c.uint.preencode(state, m.id)
|
|
105
29
|
|
|
30
|
+
let hasData = false
|
|
31
|
+
|
|
106
32
|
switch (m.type) {
|
|
107
33
|
case t.REQUEST:
|
|
108
|
-
|
|
34
|
+
c.uint.preencode(state, m.command)
|
|
35
|
+
c.uint.preencode(state, m.stream)
|
|
36
|
+
if (m.stream === 0) hasData = true
|
|
109
37
|
break
|
|
38
|
+
|
|
110
39
|
case t.RESPONSE:
|
|
111
|
-
|
|
40
|
+
c.bool.preencode(state, !!m.error)
|
|
41
|
+
c.uint.preencode(state, m.stream)
|
|
42
|
+
|
|
43
|
+
if (m.error) error.preencode(state, m.error)
|
|
44
|
+
else if (m.stream === 0) hasData = true
|
|
112
45
|
break
|
|
46
|
+
|
|
113
47
|
case t.STREAM:
|
|
114
|
-
|
|
48
|
+
c.uint.preencode(state, m.stream)
|
|
49
|
+
|
|
50
|
+
if (m.stream & s.ERROR) error.preencode(state, m.error)
|
|
51
|
+
else if (m.stream & s.DATA) hasData = true
|
|
115
52
|
break
|
|
116
53
|
}
|
|
54
|
+
|
|
55
|
+
if (hasData) c.uint.preencode(state, m.data.byteLength)
|
|
117
56
|
},
|
|
118
57
|
encode(state, m) {
|
|
119
58
|
const frame = state.start
|
|
@@ -125,26 +64,44 @@ exports.message = {
|
|
|
125
64
|
c.uint.encode(state, m.type)
|
|
126
65
|
c.uint.encode(state, m.id)
|
|
127
66
|
|
|
67
|
+
let hasData = false
|
|
68
|
+
|
|
128
69
|
switch (m.type) {
|
|
129
70
|
case t.REQUEST:
|
|
130
|
-
|
|
71
|
+
c.uint.encode(state, m.command)
|
|
72
|
+
c.uint.encode(state, m.stream)
|
|
73
|
+
if (m.stream === 0) hasData = true
|
|
131
74
|
break
|
|
75
|
+
|
|
132
76
|
case t.RESPONSE:
|
|
133
|
-
|
|
77
|
+
c.bool.encode(state, !!m.error)
|
|
78
|
+
c.uint.encode(state, m.stream)
|
|
79
|
+
|
|
80
|
+
if (m.error) error.encode(state, m.error)
|
|
81
|
+
else if (m.stream === 0) hasData = true
|
|
134
82
|
break
|
|
83
|
+
|
|
135
84
|
case t.STREAM:
|
|
136
|
-
|
|
85
|
+
c.uint.encode(state, m.stream)
|
|
86
|
+
|
|
87
|
+
if (m.stream & s.ERROR) error.encode(state, m.error)
|
|
88
|
+
else if (m.stream & s.DATA) hasData = true
|
|
137
89
|
break
|
|
138
90
|
}
|
|
139
91
|
|
|
92
|
+
if (hasData) c.uint.encode(state, m.data.byteLength)
|
|
93
|
+
|
|
140
94
|
const end = state.start
|
|
141
95
|
|
|
142
96
|
state.start = frame
|
|
143
97
|
|
|
144
|
-
c.uint32.encode(state, end - start)
|
|
98
|
+
c.uint32.encode(state, end - start + (hasData ? m.data.byteLength : 0))
|
|
145
99
|
|
|
146
100
|
state.start = end
|
|
147
|
-
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
exports.message = {
|
|
148
105
|
decode(state) {
|
|
149
106
|
const frame = c.uint32.decode(state)
|
|
150
107
|
|
|
@@ -154,12 +111,42 @@ exports.message = {
|
|
|
154
111
|
const id = c.uint.decode(state)
|
|
155
112
|
|
|
156
113
|
switch (type) {
|
|
157
|
-
case t.REQUEST:
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
114
|
+
case t.REQUEST: {
|
|
115
|
+
const command = c.uint.decode(state)
|
|
116
|
+
const stream = c.uint.decode(state)
|
|
117
|
+
const data = stream === 0 ? c.buffer.decode(state) : null
|
|
118
|
+
|
|
119
|
+
return { type, id, command, stream, data }
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
case t.RESPONSE: {
|
|
123
|
+
const err = c.bool.decode(state)
|
|
124
|
+
const stream = c.uint.decode(state)
|
|
125
|
+
|
|
126
|
+
if (err) {
|
|
127
|
+
return { type, id, stream, error: error.decode(state), data: null }
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (stream === 0) {
|
|
131
|
+
return { type, id, stream, error: null, data: c.buffer.decode(state) }
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return { type, id, stream, error: null, data: null }
|
|
135
|
+
}
|
|
136
|
+
|
|
161
137
|
case t.STREAM:
|
|
162
|
-
|
|
138
|
+
const stream = c.uint.decode(state)
|
|
139
|
+
|
|
140
|
+
if (stream & s.ERROR) {
|
|
141
|
+
return { type, id, stream, error: error.decode(state), data: null }
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (stream & s.DATA) {
|
|
145
|
+
return { type, id, stream, error: null, data: c.buffer.decode(state) }
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return { type, id, stream, error: null, data: null }
|
|
149
|
+
|
|
163
150
|
default:
|
|
164
151
|
throw errors.UNKNOWN_MESSAGE(`Unknown message '${type}'`)
|
|
165
152
|
}
|
package/lib/outgoing-request.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
const
|
|
1
|
+
const c = require('compact-encoding')
|
|
2
2
|
const errors = require('./errors')
|
|
3
3
|
|
|
4
4
|
module.exports = class RPCOutgoingRequest {
|
|
@@ -23,12 +23,16 @@ module.exports = class RPCOutgoingRequest {
|
|
|
23
23
|
throw errors.ALREADY_SENT('Request has already been sent')
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
encoding =
|
|
27
|
+
encoding && encoding !== 'buffer'
|
|
28
|
+
? c.from(encoding)
|
|
29
|
+
: typeof data === 'string'
|
|
30
|
+
? c.raw.utf8
|
|
31
|
+
: null
|
|
32
|
+
|
|
26
33
|
this.sent = true
|
|
27
34
|
|
|
28
|
-
this.rpc._sendRequest(
|
|
29
|
-
this,
|
|
30
|
-
typeof data === 'string' ? b4a.from(data, encoding) : data
|
|
31
|
-
)
|
|
35
|
+
this.rpc._sendRequest(this, encoding ? c.encode(encoding, data) : data)
|
|
32
36
|
}
|
|
33
37
|
|
|
34
38
|
reply(encoding) {
|
|
@@ -36,10 +40,12 @@ module.exports = class RPCOutgoingRequest {
|
|
|
36
40
|
throw errors.ALREADY_RECEIVED('Response is already being received')
|
|
37
41
|
}
|
|
38
42
|
|
|
43
|
+
encoding = encoding && encoding !== 'buffer' ? c.from(encoding) : null
|
|
44
|
+
|
|
39
45
|
this.received = true
|
|
40
46
|
|
|
41
|
-
return encoding
|
|
42
|
-
? this._promise.then((data) =>
|
|
47
|
+
return encoding
|
|
48
|
+
? this._promise.then((data) => c.decode(encoding, data))
|
|
43
49
|
: this._promise
|
|
44
50
|
}
|
|
45
51
|
|