From: Steven Barth Date: Fri, 22 Aug 2008 20:04:49 +0000 (+0000) Subject: RPC part #3 X-Git-Tag: 0.8.0~320 X-Git-Url: http://git.archive.openwrt.org/?a=commitdiff_plain;h=03c0b0ad120c83f80d94e1e4233a6b4bd6eaf3e9;p=project%2Fluci.git RPC part #3 --- diff --git a/libs/json/Makefile b/libs/json/Makefile new file mode 100644 index 000000000..f7fac7740 --- /dev/null +++ b/libs/json/Makefile @@ -0,0 +1,2 @@ +include ../../build/config.mk +include ../../build/module.mk diff --git a/libs/json/luasrc/json.lua b/libs/json/luasrc/json.lua new file mode 100644 index 000000000..85b85e1e8 --- /dev/null +++ b/libs/json/luasrc/json.lua @@ -0,0 +1,519 @@ +--[[ + + JSON Encoder and Parser for Lua 5.1 + + Copyright � 2007 Shaun Brown (http://www.chipmunkav.com). + All Rights Reserved. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + If you find this software useful please give www.chipmunkav.com a mention. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Usage: + + -- Lua script: + local t = { + ["name1"] = "value1", + ["name2"] = {1, false, true, 23.54, "a \021 string"}, + name3 = Json.Null() + } + + local json = Json.Encode (t) + print (json) + --> {"name1":"value1","name3":null,"name2":[1,false,true,23.54,"a \u0015 string"]} + + local t = Json.Decode(json) + print(t.name2[4]) + --> 23.54 + + Notes: + 1) Encodable Lua types: string, number, boolean, table, nil + 2) Use Json.Null() to insert a null value into a Json object + 3) All control chars are encoded to \uXXXX format eg "\021" encodes to "\u0015" + 4) All Json \uXXXX chars are decoded to chars (0-255 byte range only) + 5) Json single line // and /* */ block comments are discarded during decoding + 6) Numerically indexed Lua arrays are encoded to Json Lists eg [1,2,3] + 7) Lua dictionary tables are converted to Json objects eg {"one":1,"two":2} + 8) Json nulls are decoded to Lua nil and treated by Lua in the normal way + +--]] + +local string = string +local math = math +local table = table +local error = error +local tonumber = tonumber +local tostring = tostring +local type = type +local setmetatable = setmetatable +local pairs = pairs +local ipairs = ipairs +local assert = assert +local Chipmunk = Chipmunk + +module("luci.json") + +local StringBuilder = { + buffer = {} +} + +function StringBuilder:New() + local o = {} + setmetatable(o, self) + self.__index = self + o.buffer = {} + return o +end + +function StringBuilder:Append(s) + self.buffer[#self.buffer+1] = s +end + +function StringBuilder:ToString() + return table.concat(self.buffer) +end + +local JsonWriter = { + backslashes = { + ['\b'] = "\\b", + ['\t'] = "\\t", + ['\n'] = "\\n", + ['\f'] = "\\f", + ['\r'] = "\\r", + ['"'] = "\\\"", + ['\\'] = "\\\\", + ['/'] = "\\/" + } +} + +function JsonWriter:New() + local o = {} + o.writer = StringBuilder:New() + setmetatable(o, self) + self.__index = self + return o +end + +function JsonWriter:Append(s) + self.writer:Append(s) +end + +function JsonWriter:ToString() + return self.writer:ToString() +end + +function JsonWriter:Write(o) + local t = type(o) + if t == "nil" then + self:WriteNil() + elseif t == "boolean" then + self:WriteString(o) + elseif t == "number" then + self:WriteString(o) + elseif t == "string" then + self:ParseString(o) + elseif t == "table" then + self:WriteTable(o) + elseif t == "function" then + self:WriteFunction(o) + elseif t == "thread" then + self:WriteError(o) + elseif t == "userdata" then + self:WriteError(o) + end +end + +function JsonWriter:WriteNil() + self:Append("null") +end + +function JsonWriter:WriteString(o) + self:Append(tostring(o)) +end + +function JsonWriter:ParseString(s) + self:Append('"') + self:Append(string.gsub(s, "[%z%c\\\"/]", function(n) + local c = self.backslashes[n] + if c then return c end + return string.format("\\u%.4X", string.byte(n)) + end)) + self:Append('"') +end + +function JsonWriter:IsArray(t) + local count = 0 + local isindex = function(k) + if type(k) == "number" and k > 0 then + if math.floor(k) == k then + return true + end + end + return false + end + for k,v in pairs(t) do + if not isindex(k) then + return false, '{', '}' + else + count = math.max(count, k) + end + end + return true, '[', ']', count +end + +function JsonWriter:WriteTable(t) + local ba, st, et, n = self:IsArray(t) + self:Append(st) + if ba then + for i = 1, n do + self:Write(t[i]) + if i < n then + self:Append(',') + end + end + else + local first = true; + for k, v in pairs(t) do + if not first then + self:Append(',') + end + first = false; + self:ParseString(k) + self:Append(':') + self:Write(v) + end + end + self:Append(et) +end + +function JsonWriter:WriteError(o) + error(string.format( + "Encoding of %s unsupported", + tostring(o))) +end + +function JsonWriter:WriteFunction(o) + if o == Null then + self:WriteNil() + else + self:WriteError(o) + end +end + +local StringReader = { + s = "", + i = 0 +} + +function StringReader:New(s) + local o = {} + setmetatable(o, self) + self.__index = self + o.s = s or o.s + return o +end + +function StringReader:Peek() + local i = self.i + 1 + if i <= #self.s then + return string.sub(self.s, i, i) + end + return nil +end + +function StringReader:Next() + self.i = self.i+1 + if self.i <= #self.s then + return string.sub(self.s, self.i, self.i) + end + return nil +end + +function StringReader:All() + return self.s +end + +local JsonReader = { + escapes = { + ['t'] = '\t', + ['n'] = '\n', + ['f'] = '\f', + ['r'] = '\r', + ['b'] = '\b', + } +} + +function JsonReader:New(s) + local o = {} + o.reader = StringReader:New(s) + setmetatable(o, self) + self.__index = self + return o; +end + +function JsonReader:Read() + self:SkipWhiteSpace() + local peek = self:Peek() + if peek == nil then + error(string.format( + "Nil string: '%s'", + self:All())) + elseif peek == '{' then + return self:ReadObject() + elseif peek == '[' then + return self:ReadArray() + elseif peek == '"' then + return self:ReadString() + elseif string.find(peek, "[%+%-%d]") then + return self:ReadNumber() + elseif peek == 't' then + return self:ReadTrue() + elseif peek == 'f' then + return self:ReadFalse() + elseif peek == 'n' then + return self:ReadNull() + elseif peek == '/' then + self:ReadComment() + return self:Read() + else + error(string.format( + "Invalid input: '%s'", + self:All())) + end +end + +function JsonReader:ReadTrue() + self:TestReservedWord{'t','r','u','e'} + return true +end + +function JsonReader:ReadFalse() + self:TestReservedWord{'f','a','l','s','e'} + return false +end + +function JsonReader:ReadNull() + self:TestReservedWord{'n','u','l','l'} + return nil +end + +function JsonReader:TestReservedWord(t) + for i, v in ipairs(t) do + if self:Next() ~= v then + error(string.format( + "Error reading '%s': %s", + table.concat(t), + self:All())) + end + end +end + +function JsonReader:ReadNumber() + local result = self:Next() + local peek = self:Peek() + while peek ~= nil and string.find( + peek, + "[%+%-%d%.eE]") do + result = result .. self:Next() + peek = self:Peek() + end + result = tonumber(result) + if result == nil then + error(string.format( + "Invalid number: '%s'", + result)) + else + return result + end +end + +function JsonReader:ReadString() + local result = "" + assert(self:Next() == '"') + while self:Peek() ~= '"' do + local ch = self:Next() + if ch == '\\' then + ch = self:Next() + if self.escapes[ch] then + ch = self.escapes[ch] + end + end + result = result .. ch + end + assert(self:Next() == '"') + local fromunicode = function(m) + return string.char(tonumber(m, 16)) + end + return string.gsub( + result, + "u%x%x(%x%x)", + fromunicode) +end + +function JsonReader:ReadComment() + assert(self:Next() == '/') + local second = self:Next() + if second == '/' then + self:ReadSingleLineComment() + elseif second == '*' then + self:ReadBlockComment() + else + error(string.format( + "Invalid comment: %s", + self:All())) + end +end + +function JsonReader:ReadBlockComment() + local done = false + while not done do + local ch = self:Next() + if ch == '*' and self:Peek() == '/' then + done = true + end + if not done and + ch == '/' and + self:Peek() == "*" then + error(string.format( + "Invalid comment: %s, '/*' illegal.", + self:All())) + end + end + self:Next() +end + +function JsonReader:ReadSingleLineComment() + local ch = self:Next() + while ch ~= '\r' and ch ~= '\n' do + ch = self:Next() + end +end + +function JsonReader:ReadArray() + local result = {} + assert(self:Next() == '[') + self:SkipWhiteSpace() + local done = false + if self:Peek() == ']' then + done = true; + end + while not done do + local item = self:Read() + result[#result+1] = item + self:SkipWhiteSpace() + if self:Peek() == ']' then + done = true + end + if not done then + local ch = self:Next() + if ch ~= ',' then + error(string.format( + "Invalid array: '%s' due to: '%s'", + self:All(), ch)) + end + end + end + assert(']' == self:Next()) + return result +end + +function JsonReader:ReadObject() + local result = {} + assert(self:Next() == '{') + self:SkipWhiteSpace() + local done = false + if self:Peek() == '}' then + done = true + end + while not done do + local key = self:Read() + if type(key) ~= "string" then + error(string.format( + "Invalid non-string object key: %s", + key)) + end + self:SkipWhiteSpace() + local ch = self:Next() + if ch ~= ':' then + error(string.format( + "Invalid object: '%s' due to: '%s'", + self:All(), + ch)) + end + self:SkipWhiteSpace() + local val = self:Read() + result[key] = val + self:SkipWhiteSpace() + if self:Peek() == '}' then + done = true + end + if not done then + ch = self:Next() + if ch ~= ',' then + error(string.format( + "Invalid array: '%s' near: '%s'", + self:All(), + ch)) + end + end + end + assert(self:Next() == "}") + return result +end + +function JsonReader:SkipWhiteSpace() + local p = self:Peek() + while p ~= nil and string.find(p, "[%s/]") do + if p == '/' then + self:ReadComment() + else + self:Next() + end + p = self:Peek() + end +end + +function JsonReader:Peek() + return self.reader:Peek() +end + +function JsonReader:Next() + return self.reader:Next() +end + +function JsonReader:All() + return self.reader:All() +end + +function Encode(o) + local writer = JsonWriter:New() + writer:Write(o) + return writer:ToString() +end + +function Decode(s) + local reader = JsonReader:New(s) + local object = reader:Read() + reader:SkipWhiteSpace() + assert(reader:Peek() == nil, "Invalid characters after JSON body") + return object +end + +function Null() + return Null +end diff --git a/libs/rpc/Makefile b/libs/rpc/Makefile deleted file mode 100644 index f7fac7740..000000000 --- a/libs/rpc/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -include ../../build/config.mk -include ../../build/module.mk diff --git a/libs/rpc/luasrc/Json.lua b/libs/rpc/luasrc/Json.lua deleted file mode 100644 index 1dbc65c65..000000000 --- a/libs/rpc/luasrc/Json.lua +++ /dev/null @@ -1,519 +0,0 @@ ---[[ - - JSON Encoder and Parser for Lua 5.1 - - Copyright © 2007 Shaun Brown (http://www.chipmunkav.com). - All Rights Reserved. - - Permission is hereby granted, free of charge, to any person - obtaining a copy of this software to deal in the Software without - restriction, including without limitation the rights to use, - copy, modify, merge, publish, distribute, sublicense, and/or - sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be - included in all copies or substantial portions of the Software. - If you find this software useful please give www.chipmunkav.com a mention. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF - CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - Usage: - - -- Lua script: - local t = { - ["name1"] = "value1", - ["name2"] = {1, false, true, 23.54, "a \021 string"}, - name3 = Json.Null() - } - - local json = Json.Encode (t) - print (json) - --> {"name1":"value1","name3":null,"name2":[1,false,true,23.54,"a \u0015 string"]} - - local t = Json.Decode(json) - print(t.name2[4]) - --> 23.54 - - Notes: - 1) Encodable Lua types: string, number, boolean, table, nil - 2) Use Json.Null() to insert a null value into a Json object - 3) All control chars are encoded to \uXXXX format eg "\021" encodes to "\u0015" - 4) All Json \uXXXX chars are decoded to chars (0-255 byte range only) - 5) Json single line // and /* */ block comments are discarded during decoding - 6) Numerically indexed Lua arrays are encoded to Json Lists eg [1,2,3] - 7) Lua dictionary tables are converted to Json objects eg {"one":1,"two":2} - 8) Json nulls are decoded to Lua nil and treated by Lua in the normal way - ---]] - -local string = string -local math = math -local table = table -local error = error -local tonumber = tonumber -local tostring = tostring -local type = type -local setmetatable = setmetatable -local pairs = pairs -local ipairs = ipairs -local assert = assert -local Chipmunk = Chipmunk - -module("Json") - -local StringBuilder = { - buffer = {} -} - -function StringBuilder:New() - local o = {} - setmetatable(o, self) - self.__index = self - o.buffer = {} - return o -end - -function StringBuilder:Append(s) - self.buffer[#self.buffer+1] = s -end - -function StringBuilder:ToString() - return table.concat(self.buffer) -end - -local JsonWriter = { - backslashes = { - ['\b'] = "\\b", - ['\t'] = "\\t", - ['\n'] = "\\n", - ['\f'] = "\\f", - ['\r'] = "\\r", - ['"'] = "\\\"", - ['\\'] = "\\\\", - ['/'] = "\\/" - } -} - -function JsonWriter:New() - local o = {} - o.writer = StringBuilder:New() - setmetatable(o, self) - self.__index = self - return o -end - -function JsonWriter:Append(s) - self.writer:Append(s) -end - -function JsonWriter:ToString() - return self.writer:ToString() -end - -function JsonWriter:Write(o) - local t = type(o) - if t == "nil" then - self:WriteNil() - elseif t == "boolean" then - self:WriteString(o) - elseif t == "number" then - self:WriteString(o) - elseif t == "string" then - self:ParseString(o) - elseif t == "table" then - self:WriteTable(o) - elseif t == "function" then - self:WriteFunction(o) - elseif t == "thread" then - self:WriteError(o) - elseif t == "userdata" then - self:WriteError(o) - end -end - -function JsonWriter:WriteNil() - self:Append("null") -end - -function JsonWriter:WriteString(o) - self:Append(tostring(o)) -end - -function JsonWriter:ParseString(s) - self:Append('"') - self:Append(string.gsub(s, "[%z%c\\\"/]", function(n) - local c = self.backslashes[n] - if c then return c end - return string.format("\\u%.4X", string.byte(n)) - end)) - self:Append('"') -end - -function JsonWriter:IsArray(t) - local count = 0 - local isindex = function(k) - if type(k) == "number" and k > 0 then - if math.floor(k) == k then - return true - end - end - return false - end - for k,v in pairs(t) do - if not isindex(k) then - return false, '{', '}' - else - count = math.max(count, k) - end - end - return true, '[', ']', count -end - -function JsonWriter:WriteTable(t) - local ba, st, et, n = self:IsArray(t) - self:Append(st) - if ba then - for i = 1, n do - self:Write(t[i]) - if i < n then - self:Append(',') - end - end - else - local first = true; - for k, v in pairs(t) do - if not first then - self:Append(',') - end - first = false; - self:ParseString(k) - self:Append(':') - self:Write(v) - end - end - self:Append(et) -end - -function JsonWriter:WriteError(o) - error(string.format( - "Encoding of %s unsupported", - tostring(o))) -end - -function JsonWriter:WriteFunction(o) - if o == Null then - self:WriteNil() - else - self:WriteError(o) - end -end - -local StringReader = { - s = "", - i = 0 -} - -function StringReader:New(s) - local o = {} - setmetatable(o, self) - self.__index = self - o.s = s or o.s - return o -end - -function StringReader:Peek() - local i = self.i + 1 - if i <= #self.s then - return string.sub(self.s, i, i) - end - return nil -end - -function StringReader:Next() - self.i = self.i+1 - if self.i <= #self.s then - return string.sub(self.s, self.i, self.i) - end - return nil -end - -function StringReader:All() - return self.s -end - -local JsonReader = { - escapes = { - ['t'] = '\t', - ['n'] = '\n', - ['f'] = '\f', - ['r'] = '\r', - ['b'] = '\b', - } -} - -function JsonReader:New(s) - local o = {} - o.reader = StringReader:New(s) - setmetatable(o, self) - self.__index = self - return o; -end - -function JsonReader:Read() - self:SkipWhiteSpace() - local peek = self:Peek() - if peek == nil then - error(string.format( - "Nil string: '%s'", - self:All())) - elseif peek == '{' then - return self:ReadObject() - elseif peek == '[' then - return self:ReadArray() - elseif peek == '"' then - return self:ReadString() - elseif string.find(peek, "[%+%-%d]") then - return self:ReadNumber() - elseif peek == 't' then - return self:ReadTrue() - elseif peek == 'f' then - return self:ReadFalse() - elseif peek == 'n' then - return self:ReadNull() - elseif peek == '/' then - self:ReadComment() - return self:Read() - else - error(string.format( - "Invalid input: '%s'", - self:All())) - end -end - -function JsonReader:ReadTrue() - self:TestReservedWord{'t','r','u','e'} - return true -end - -function JsonReader:ReadFalse() - self:TestReservedWord{'f','a','l','s','e'} - return false -end - -function JsonReader:ReadNull() - self:TestReservedWord{'n','u','l','l'} - return nil -end - -function JsonReader:TestReservedWord(t) - for i, v in ipairs(t) do - if self:Next() ~= v then - error(string.format( - "Error reading '%s': %s", - table.concat(t), - self:All())) - end - end -end - -function JsonReader:ReadNumber() - local result = self:Next() - local peek = self:Peek() - while peek ~= nil and string.find( - peek, - "[%+%-%d%.eE]") do - result = result .. self:Next() - peek = self:Peek() - end - result = tonumber(result) - if result == nil then - error(string.format( - "Invalid number: '%s'", - result)) - else - return result - end -end - -function JsonReader:ReadString() - local result = "" - assert(self:Next() == '"') - while self:Peek() ~= '"' do - local ch = self:Next() - if ch == '\\' then - ch = self:Next() - if self.escapes[ch] then - ch = self.escapes[ch] - end - end - result = result .. ch - end - assert(self:Next() == '"') - local fromunicode = function(m) - return string.char(tonumber(m, 16)) - end - return string.gsub( - result, - "u%x%x(%x%x)", - fromunicode) -end - -function JsonReader:ReadComment() - assert(self:Next() == '/') - local second = self:Next() - if second == '/' then - self:ReadSingleLineComment() - elseif second == '*' then - self:ReadBlockComment() - else - error(string.format( - "Invalid comment: %s", - self:All())) - end -end - -function JsonReader:ReadBlockComment() - local done = false - while not done do - local ch = self:Next() - if ch == '*' and self:Peek() == '/' then - done = true - end - if not done and - ch == '/' and - self:Peek() == "*" then - error(string.format( - "Invalid comment: %s, '/*' illegal.", - self:All())) - end - end - self:Next() -end - -function JsonReader:ReadSingleLineComment() - local ch = self:Next() - while ch ~= '\r' and ch ~= '\n' do - ch = self:Next() - end -end - -function JsonReader:ReadArray() - local result = {} - assert(self:Next() == '[') - self:SkipWhiteSpace() - local done = false - if self:Peek() == ']' then - done = true; - end - while not done do - local item = self:Read() - result[#result+1] = item - self:SkipWhiteSpace() - if self:Peek() == ']' then - done = true - end - if not done then - local ch = self:Next() - if ch ~= ',' then - error(string.format( - "Invalid array: '%s' due to: '%s'", - self:All(), ch)) - end - end - end - assert(']' == self:Next()) - return result -end - -function JsonReader:ReadObject() - local result = {} - assert(self:Next() == '{') - self:SkipWhiteSpace() - local done = false - if self:Peek() == '}' then - done = true - end - while not done do - local key = self:Read() - if type(key) ~= "string" then - error(string.format( - "Invalid non-string object key: %s", - key)) - end - self:SkipWhiteSpace() - local ch = self:Next() - if ch ~= ':' then - error(string.format( - "Invalid object: '%s' due to: '%s'", - self:All(), - ch)) - end - self:SkipWhiteSpace() - local val = self:Read() - result[key] = val - self:SkipWhiteSpace() - if self:Peek() == '}' then - done = true - end - if not done then - ch = self:Next() - if ch ~= ',' then - error(string.format( - "Invalid array: '%s' near: '%s'", - self:All(), - ch)) - end - end - end - assert(self:Next() == "}") - return result -end - -function JsonReader:SkipWhiteSpace() - local p = self:Peek() - while p ~= nil and string.find(p, "[%s/]") do - if p == '/' then - self:ReadComment() - else - self:Next() - end - p = self:Peek() - end -end - -function JsonReader:Peek() - return self.reader:Peek() -end - -function JsonReader:Next() - return self.reader:Next() -end - -function JsonReader:All() - return self.reader:All() -end - -function Encode(o) - local writer = JsonWriter:New() - writer:Write(o) - return writer:ToString() -end - -function Decode(s) - local reader = JsonReader:New(s) - local object = reader:Read() - reader:SkipWhiteSpace() - assert(reader:Peek() == nil, "Invalid characters after JSON body") - return object -end - -function Null() - return Null -end diff --git a/libs/web/luasrc/http.lua b/libs/web/luasrc/http.lua index 5b922d994..17b27482e 100644 --- a/libs/web/luasrc/http.lua +++ b/libs/web/luasrc/http.lua @@ -81,6 +81,14 @@ function Request.formvaluetable(self, prefix) return vals end +function Request.content(self) + if not self.parsed_input then + self:_parse_input() + end + + return self.message.content, self.message.content_length +end + function Request.getcookie(self, name) local c = string.gsub(";" .. (self:getenv("HTTP_COOKIE") or "") .. ";", "%s*;%s*", ";") local p = ";" .. name .. "=(.-);" @@ -122,6 +130,13 @@ function close() end end +--- Return the request content if the request was of unknown type. +-- @return HTTP request body +-- @return HTTP request body length +function content() + return context.request:content() +end + --- Get a certain HTTP input value or a table of all input values. -- @param name Name of the GET or POST variable to fetch -- @param noparse Don't parse POST data before getting the value