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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
local subprocess = require 'bee.subprocess'
local util = require 'utility'
local task = require 'task'
local pub = require 'pub'
local jsonrpc = require 'jsonrpc'
local ErrorCodes = require 'define.ErrorCodes'
local reqCounter = util.counter()
local m = {}
m.ability = {}
m.waiting = {}
function m.getMethodName(proto)
if proto.method:sub(1, 2) == '$/' then
return proto.method:sub(3), true
else
return proto.method, false
end
end
function m.on(method, callback)
m.ability[method] = callback
end
function m.response(id, res)
-- res 可能是nil,为了转成json时保留nil,使用 container 容器
local data = util.container()
data.id = id
data.result = res
local buf = jsonrpc.encode(data)
io.stdout:write(buf)
end
function m.responseErr(id, code, message)
local buf = jsonrpc.encode {
id = id,
error = {
code = code,
message = message,
}
}
io.stdout:write(buf)
end
function m.notify(name, params)
local buf = jsonrpc.encode {
method = name,
params = params,
}
io.stdout:write(buf)
end
function m.request(name, params)
local id = reqCounter()
local buf = jsonrpc.encode {
id = id,
method = name,
params = params,
}
io.stdout:write(buf)
return task.wait(function (waker)
m.waiting[id] = waker
end)
end
function m.doMethod(proto)
local method, optional = m.getMethodName(proto)
local abil = m.ability[method]
if not abil then
if not optional then
log.warn('Recieved unknown proto: ' .. method)
end
if proto.id then
m.responseErr(proto.id, ErrorCodes.MethodNotFound, method)
end
return
end
task.create(function ()
local clock = os.clock()
local ok, res = xpcall(abil, log.error, proto.params)
local passed = os.clock() - clock
if passed > 0.2 then
log.debug(('Method [%s] takes [%.3f]sec.'):format(method, passed))
end
if ok then
m.response(proto.id, res)
else
m.responseErr(proto.id, ErrorCodes.InternalError, res)
end
end)
end
function m.doResponse(proto)
local id = proto.id
local waker = m.waiting[id]
if not waker then
log.warn('Response id not found: ' .. util.dump(proto))
return
end
m.waiting[id] = nil
if proto.error then
log.warn(('Response error [%d]: %s'):format(proto.error.code, proto.error.message))
return
end
waker(proto.result)
end
function m.listen()
subprocess.filemode(io.stdin, 'b')
subprocess.filemode(io.stdout, 'b')
io.stdin:setvbuf 'no'
io.stdout:setvbuf 'no'
task.create(function ()
while true do
local proto = pub.task('loadProto')
if proto.method then
m.doMethod(proto)
else
m.doResponse(proto)
end
end
end)
end
return m
|