GSoC: Add RPC client
[project/luci.git] / libs / rpcc / luasrc / rpcc.lua
1 --[[
2 LuCIRPCc
3 (c) 2009 Steven Barth <steven@midlink.org>
4
5 Licensed under the Apache License, Version 2.0 (the "License");
6 you may not use this file except in compliance with the License.
7 You may obtain a copy of the License at
8
9         http://www.apache.org/licenses/LICENSE-2.0
10
11 $Id$
12 ]]--
13
14 local util = require "luci.util"
15 local json = require "luci.json"
16 local ltn12 = require "luci.ltn12"
17 local nixio = require "nixio", require "nixio.util"
18
19 local tostring, assert, setmetatable = tostring, assert, setmetatable
20 local error = error
21
22 --- LuCI RPC Client.
23 -- @cstyle instance
24 module "luci.rpcc"
25
26 RQLIMIT = 32 * nixio.const.buffersize
27
28 --- Create a new JSON-RPC stream client.
29 -- @class function
30 -- @param fd File descriptor
31 -- @param v1 Use protocol version 1.0
32 -- @return RPC Client
33 Client = util.class()
34
35 function Client.__init__(self, fd, v1)
36         self.fd = fd
37         self.uniqueid = tostring(self):match("0x([a-f0-9]+)")
38         self.msgid = 1
39         self.v1 = v1
40 end
41
42 --- Request an RP call and get the response.
43 -- @param method Remote method
44 -- @param params Parameters
45 -- @param notification Notification only?
46 -- @return response 
47 function Client.request(self, method, params, notification)
48         local oldchunk = self.decoder and self.decoder.chunk
49         self.decoder = json.ActiveDecoder(self.fd:blocksource(nil, RQLIMIT))
50         self.decoder.chunk = oldchunk
51         
52         local reqid = self.msgid .. self.uniqueid
53         local reqdata = json.Encoder({
54                 id = (not notification) and (self.msgid .. self.uniqueid) or nil,
55                 jsonrpc = (not self.v1) and "2.0" or nil,
56                 method = method,
57                 params = params
58         })
59         ltn12.pump.all(reqdata:source(), self.fd:sink())
60         if not notification then
61                 self.msgid = self.msgid + 1
62                 local response = self.decoder:get()
63                 assert(response.id == reqid, "Invalid response id")
64                 if response.error then
65                         error(response.error.message or response.error)
66                 end
67                 return response.result
68         end
69 end
70
71 --- Create a transparent RPC proxy.
72 -- @param prefix Method prefix
73 -- @return RPC Proxy object
74 function Client.proxy(self, prefix)
75         prefix = prefix or ""
76         return setmetatable({}, {
77                 __call = function(proxy, ...)
78                         return self:request(prefix, {...})
79                 end,
80                 __index = function(proxy, name)
81                         return self:proxy(prefix .. name .. ".")
82                 end
83         })
84 end