-
Notifications
You must be signed in to change notification settings - Fork 7
/
stdio-jrpc.js
111 lines (98 loc) · 2.47 KB
/
stdio-jrpc.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
// Helper functions
const buildRPCError = (id, errObj) => {
let obj = {
jsonrpc: '2.0',
id: id,
error: errObj
}
return JSON.stringify(obj)
}
const buildRPCResult = (id, result) => {
return JSON.stringify({
jsonrpc: '2.0',
id: id,
result: result,
})
}
const RPCErrors = {
ParseError (data = undefined) {
return { code: -32700, data: data, message: 'Error during parsing.' }
},
InvalidRequest (data = undefined) {
return { code: -32600, data: data, message: 'Invalid Request.' }
},
MethodNotFound (data = undefined) {
return { code: -32601, data: data, message: 'Method Not Found.' }
},
InvalidParams (data = undefined) {
return { code: -32602, data: data, message: 'Invalid Params.' }
},
InternalError (data = undefined) {
return { code: -32603, data: data, message: 'Internal Error.' }
},
}
class RPCConnection {
constructor () {
this._methodTable = new Map()
this._currentHandlingId = null
process.stdin.setEncoding('utf-8')
process.stdin.on('readable', () => {
let data = ''
let chunk
while (true) {
chunk = process.stdin.read()
if (chunk === null) {
break
} else {
data += chunk
}
}
this._receiveRaw(data)
})
}
_sendRaw (rawMsg) {
process.stdout.write(rawMsg)
}
_receiveRaw (rawMsg) {
let json
try {
json = JSON.parse(rawMsg)
} catch (e) {
console.error(e)
this.sendError(RPCErrors.ParseError())
}
if (json != null) {
let { jsonrpc, id, method, params } = json
this._currentHandlingId = id
if (jsonrpc !== '2.0') {
this.sendError(RPCErrors.InvalidRequest())
} else if (!this._methodTable.has(method)) {
this.sendError(RPCErrors.MethodNotFound())
} else {
let m = this._methodTable.get(method)
m(params)
}
}
// Reset id
this._currentHandlingId = null
}
// Export functions
sendResult (result) {
this._sendRaw(buildRPCResult(this._currentHandlingId, result))
}
sendError (errObj) {
this._sendRaw(buildRPCError(this._currentHandlingId, errObj))
}
handle (methodName, callback) {
this._methodTable.set(methodName, callback)
}
static open () {
if (RPCConnection._singleton == null) {
RPCConnection._singleton = new RPCConnection()
}
return RPCConnection._singleton
}
}
// Export
module.exports.RPCConnection = RPCConnection
module.exports.RPCErrors = RPCErrors