mirror of
https://github.com/apache/thrift.git
synced 2026-10-29 00:46:21 +08:00
Client: lua The binary, compact and JSON protocols refused only a negative list, set or map size, and the generated code reads one element per declared element. Lua has no MaxMessageSize that would bound the count otherwise, so the new limit gets a finite default instead of the "no limit of its own" default THRIFT-6291 gives MaxContainerSize. - TProtocol.lua: DEFAULT_MAX_CONTAINER_SIZE = 16384000, the value of the string and frame size limits, and TProtocolBase:checkContainerSize(). maxContainerSize on a protocol instance overrides the default; zero or less switches the limit off. - readListBegin, readSetBegin and readMapBegin of TBinaryProtocol, TCompactProtocol and TJSONProtocol call it before any element is read. - doc/specs/thrift-tconfiguration.md records the finite Lua default and the reason. - test/test_container_limits.lua covers the three protocols for list, set and map, at the limit and one over it, the default, the off values and skip. Its pure-Lua stand-in for libluabpack gains the int8_t wrap of 'c' and toVarint32, which the compact protocol needs. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
782 lines
20 KiB
Lua
782 lines
20 KiB
Lua
--
|
|
-- Licensed to the Apache Software Foundation (ASF) under one
|
|
-- or more contributor license agreements. See the NOTICE file
|
|
-- distributed with this work for additional information
|
|
-- regarding copyright ownership. The ASF licenses this file
|
|
-- to you under the Apache License, Version 2.0 (the
|
|
-- "License"), you may not use this file except in compliance
|
|
-- with the License. You may obtain a copy of the License at
|
|
--
|
|
-- http://www.apache.org/licenses/LICENSE-2.0
|
|
--
|
|
-- Unless required by applicable law or agreed to in writing,
|
|
-- software distributed under the License is distributed on an
|
|
-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
-- KIND, either express or implied. See the License for the
|
|
-- specific language governing permissions and limitations
|
|
-- under the License.
|
|
--
|
|
|
|
require 'TProtocol'
|
|
local libluabpack = require 'libluabpack'
|
|
local libluabitwise = require 'libluabitwise'
|
|
local liblualongnumber = require 'liblualongnumber'
|
|
|
|
TJSONProtocol = __TObject.new(TProtocolBase, {
|
|
__type = 'TJSONProtocol',
|
|
THRIFT_JSON_PROTOCOL_VERSION = 1,
|
|
jsonContext = {},
|
|
jsonContextVal = {first = true, colon = true, ttype = 2, null = true},
|
|
jsonContextIndex = 1,
|
|
hasReadByte = ""
|
|
})
|
|
|
|
TTypeToString = {}
|
|
TTypeToString[TType.BOOL] = "tf"
|
|
TTypeToString[TType.BYTE] = "i8"
|
|
TTypeToString[TType.I16] = "i16"
|
|
TTypeToString[TType.I32] = "i32"
|
|
TTypeToString[TType.I64] = "i64"
|
|
TTypeToString[TType.DOUBLE] = "dbl"
|
|
TTypeToString[TType.STRING] = "str"
|
|
TTypeToString[TType.STRUCT] = "rec"
|
|
TTypeToString[TType.LIST] = "lst"
|
|
TTypeToString[TType.SET] = "set"
|
|
TTypeToString[TType.MAP] = "map"
|
|
TTypeToString[TType.UUID] = "uid"
|
|
|
|
StringToTType = {
|
|
tf = TType.BOOL,
|
|
i8 = TType.BYTE,
|
|
i16 = TType.I16,
|
|
i32 = TType.I32,
|
|
i64 = TType.I64,
|
|
dbl = TType.DOUBLE,
|
|
str = TType.STRING,
|
|
rec = TType.STRUCT,
|
|
map = TType.MAP,
|
|
set = TType.SET,
|
|
lst = TType.LIST,
|
|
uid = TType.UUID,
|
|
}
|
|
|
|
JSONNode = {
|
|
ObjectBegin = '{',
|
|
ObjectEnd = '}',
|
|
ArrayBegin = '[',
|
|
ArrayEnd = ']',
|
|
PairSeparator = ':',
|
|
ElemSeparator = ',',
|
|
Backslash = '\\',
|
|
StringDelimiter = '"',
|
|
ZeroChar = '0',
|
|
EscapeChar = 'u',
|
|
Nan = 'NaN',
|
|
Infinity = 'Infinity',
|
|
NegativeInfinity = '-Infinity',
|
|
EscapeChars = "\"\\bfnrt",
|
|
EscapePrefix = "\\u00"
|
|
}
|
|
|
|
EscapeCharVals = {
|
|
'"', '\\', '\b', '\f', '\n', '\r', '\t'
|
|
}
|
|
|
|
JSONCharTable = {
|
|
--0 1 2 3 4 5 6 7 8 9 A B C D E F
|
|
0, 0, 0, 0, 0, 0, 0, 0, 98,116,110, 0,102,114, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
1, 1,34, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
}
|
|
|
|
-- character table string
|
|
local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
|
|
|
|
-- encoding
|
|
function base64_encode(data)
|
|
return ((data:gsub('.', function(x)
|
|
local r,b='',x:byte()
|
|
for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end
|
|
return r;
|
|
end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)
|
|
if (#x < 6) then return '' end
|
|
local c=0
|
|
for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end
|
|
return b:sub(c+1,c+1)
|
|
end)..({ '', '==', '=' })[#data%3+1])
|
|
end
|
|
|
|
-- decoding
|
|
function base64_decode(data)
|
|
data = string.gsub(data, '[^'..b..'=]', '')
|
|
return (data:gsub('.', function(x)
|
|
if (x == '=') then return '' end
|
|
local r,f='',(b:find(x)-1)
|
|
for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end
|
|
return r;
|
|
end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
|
|
if (#x ~= 8) then return '' end
|
|
local c=0
|
|
for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(8-i) or 0) end
|
|
return string.char(c)
|
|
end))
|
|
end
|
|
|
|
-- Collects a value that is read a few bytes at a time. The bytes are gathered
|
|
-- in a table, every 1024 entries of which are joined into one piece as they
|
|
-- come, and the pieces are joined once at the end. The cost stays linear in
|
|
-- the length of the value, and neither table grows long.
|
|
local function newValueBuffer()
|
|
return {pieces = {}, recent = {}, count = 0}
|
|
end
|
|
|
|
local function addToValue(buf, bytes)
|
|
buf.count = buf.count + 1
|
|
buf.recent[buf.count] = bytes
|
|
if buf.count == 1024 then
|
|
buf.pieces[#buf.pieces + 1] = table.concat(buf.recent)
|
|
buf.recent = {}
|
|
buf.count = 0
|
|
end
|
|
end
|
|
|
|
local function valueOf(buf)
|
|
buf.pieces[#buf.pieces + 1] = table.concat(buf.recent)
|
|
return table.concat(buf.pieces)
|
|
end
|
|
|
|
function TJSONProtocol:resetContext()
|
|
self.jsonContext = {}
|
|
self.jsonContextVal = {first = true, colon = true, ttype = 2, null = true}
|
|
self.jsonContextIndex = 1
|
|
end
|
|
|
|
function TJSONProtocol:contextPush(context)
|
|
self.jsonContextIndex = self.jsonContextIndex + 1
|
|
self.jsonContext[self.jsonContextIndex] = self.jsonContextVal
|
|
self.jsonContextVal = context
|
|
end
|
|
|
|
function TJSONProtocol:contextPop()
|
|
self.jsonContextVal = self.jsonContext[self.jsonContextIndex]
|
|
self.jsonContextIndex = self.jsonContextIndex - 1
|
|
end
|
|
|
|
function TJSONProtocol:escapeNum()
|
|
if self.jsonContextVal.ttype == 1 then
|
|
return self.jsonContextVal.colon
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
function TJSONProtocol:writeElemSeparator()
|
|
if self.jsonContextVal.null then
|
|
return
|
|
end
|
|
if self.jsonContextVal.first then
|
|
self.jsonContextVal.first = false
|
|
else
|
|
if self.jsonContextVal.ttype == 1 then
|
|
if self.jsonContextVal.colon then
|
|
self.trans:write(JSONNode.PairSeparator)
|
|
self.jsonContextVal.colon = false
|
|
else
|
|
self.trans:write(JSONNode.ElemSeparator)
|
|
self.jsonContextVal.colon = true
|
|
end
|
|
else
|
|
self.trans:write(JSONNode.ElemSeparator)
|
|
end
|
|
end
|
|
end
|
|
|
|
function TJSONProtocol:hexChar(val)
|
|
val = libluabitwise.band(val, 0x0f)
|
|
if val < 10 then
|
|
return val + 48
|
|
else
|
|
return val + 87
|
|
end
|
|
end
|
|
|
|
function TJSONProtocol:writeJSONEscapeChar(ch)
|
|
self.trans:write(JSONNode.EscapePrefix)
|
|
local outCh = self:hexChar(libluabitwise.shiftr(ch, 4))
|
|
local buff = libluabpack.bpack('c', outCh)
|
|
self.trans:write(buff)
|
|
outCh = self:hexChar(ch)
|
|
buff = libluabpack.bpack('c', outCh)
|
|
self.trans:write(buff)
|
|
end
|
|
|
|
function TJSONProtocol:writeJSONChar(byte)
|
|
local ch = string.byte(byte)
|
|
if ch >= 0x30 then
|
|
if ch == string.byte(JSONNode.Backslash) then
|
|
self.trans:write(JSONNode.Backslash)
|
|
self.trans:write(JSONNode.Backslash)
|
|
else
|
|
self.trans:write(byte)
|
|
end
|
|
else
|
|
local outCh = JSONCharTable[ch+1]
|
|
if outCh == 1 then
|
|
self.trans:write(byte)
|
|
elseif outCh > 1 then
|
|
self.trans:write(JSONNode.Backslash)
|
|
local buff = libluabpack.bpack('c', outCh)
|
|
self.trans:write(buff)
|
|
else
|
|
self:writeJSONEscapeChar(ch)
|
|
end
|
|
end
|
|
end
|
|
|
|
function TJSONProtocol:writeJSONString(str)
|
|
self:writeElemSeparator()
|
|
self.trans:write(JSONNode.StringDelimiter)
|
|
-- TODO escape special characters
|
|
local length = string.len(str)
|
|
local ii = 1
|
|
while ii <= length do
|
|
self:writeJSONChar(string.sub(str, ii, ii))
|
|
ii = ii + 1
|
|
end
|
|
self.trans:write(JSONNode.StringDelimiter)
|
|
end
|
|
|
|
function TJSONProtocol:writeJSONBase64(str)
|
|
self:writeElemSeparator()
|
|
self.trans:write(JSONNode.StringDelimiter)
|
|
local length = string.len(str)
|
|
local offset = 1
|
|
while length >= 3 do
|
|
-- Encode 3 bytes at a time
|
|
local bytes = base64_encode(string.sub(str, offset, offset+2))
|
|
self.trans:write(bytes)
|
|
length = length - 3
|
|
offset = offset + 3
|
|
end
|
|
if length > 0 then
|
|
local bytes = base64_encode(string.sub(str, offset, offset+length-1))
|
|
self.trans:write(bytes)
|
|
end
|
|
self.trans:write(JSONNode.StringDelimiter)
|
|
end
|
|
|
|
function TJSONProtocol:writeJSONInteger(num)
|
|
self:writeElemSeparator()
|
|
if self:escapeNum() then
|
|
self.trans:write(JSONNode.StringDelimiter)
|
|
end
|
|
local numstr = "" .. num
|
|
numstr = string.sub(numstr, string.find(numstr, "^[+-]?%d+"))
|
|
self.trans:write(numstr)
|
|
if self:escapeNum() then
|
|
self.trans:write(JSONNode.StringDelimiter)
|
|
end
|
|
end
|
|
|
|
function TJSONProtocol:writeJSONDouble(dub)
|
|
self:writeElemSeparator()
|
|
local val = "" .. dub
|
|
local prefix = string.sub(val, 1, 1)
|
|
local special = false
|
|
if prefix == 'N' or prefix == 'n' then
|
|
val = JSONNode.Nan
|
|
special = true
|
|
elseif prefix == 'I' or prefix == 'i' then
|
|
val = JSONNode.Infinity
|
|
special = true
|
|
elseif prefix == '-' then
|
|
local secondByte = string.sub(val, 2, 2)
|
|
if secondByte == 'I' or secondByte == 'i' then
|
|
val = JSONNode.NegativeInfinity
|
|
special = true
|
|
end
|
|
end
|
|
|
|
if special or self:escapeNum() then
|
|
self.trans:write(JSONNode.StringDelimiter)
|
|
end
|
|
self.trans:write(val)
|
|
if special or self:escapeNum() then
|
|
self.trans:write(JSONNode.StringDelimiter)
|
|
end
|
|
end
|
|
|
|
function TJSONProtocol:writeJSONObjectBegin()
|
|
self:writeElemSeparator()
|
|
self.trans:write(JSONNode.ObjectBegin)
|
|
self:contextPush({first = true, colon = true, ttype = 1, null = false})
|
|
end
|
|
|
|
function TJSONProtocol:writeJSONObjectEnd()
|
|
self:contextPop()
|
|
self.trans:write(JSONNode.ObjectEnd)
|
|
end
|
|
|
|
function TJSONProtocol:writeJSONArrayBegin()
|
|
self:writeElemSeparator()
|
|
self.trans:write(JSONNode.ArrayBegin)
|
|
self:contextPush({first = true, colon = true, ttype = 2, null = false})
|
|
end
|
|
|
|
function TJSONProtocol:writeJSONArrayEnd()
|
|
self:contextPop()
|
|
self.trans:write(JSONNode.ArrayEnd)
|
|
end
|
|
|
|
function TJSONProtocol:writeMessageBegin(name, ttype, seqid)
|
|
self:resetContext()
|
|
self:writeJSONArrayBegin()
|
|
self:writeJSONInteger(TJSONProtocol.THRIFT_JSON_PROTOCOL_VERSION)
|
|
self:writeJSONString(name)
|
|
self:writeJSONInteger(ttype)
|
|
self:writeJSONInteger(seqid)
|
|
end
|
|
|
|
function TJSONProtocol:writeMessageEnd()
|
|
self:writeJSONArrayEnd()
|
|
end
|
|
|
|
function TJSONProtocol:writeStructBegin(name)
|
|
self:writeJSONObjectBegin()
|
|
end
|
|
|
|
function TJSONProtocol:writeStructEnd()
|
|
self:writeJSONObjectEnd()
|
|
end
|
|
|
|
function TJSONProtocol:writeFieldBegin(name, ttype, id)
|
|
self:writeJSONInteger(id)
|
|
self:writeJSONObjectBegin()
|
|
self:writeJSONString(TTypeToString[ttype])
|
|
end
|
|
|
|
function TJSONProtocol:writeFieldEnd()
|
|
self:writeJSONObjectEnd()
|
|
end
|
|
|
|
function TJSONProtocol:writeFieldStop()
|
|
end
|
|
|
|
function TJSONProtocol:writeMapBegin(ktype, vtype, size)
|
|
self:writeJSONArrayBegin()
|
|
self:writeJSONString(TTypeToString[ktype])
|
|
self:writeJSONString(TTypeToString[vtype])
|
|
self:writeJSONInteger(size)
|
|
return self:writeJSONObjectBegin()
|
|
end
|
|
|
|
function TJSONProtocol:writeMapEnd()
|
|
self:writeJSONObjectEnd()
|
|
self:writeJSONArrayEnd()
|
|
end
|
|
|
|
function TJSONProtocol:writeListBegin(etype, size)
|
|
self:writeJSONArrayBegin()
|
|
self:writeJSONString(TTypeToString[etype])
|
|
self:writeJSONInteger(size)
|
|
end
|
|
|
|
function TJSONProtocol:writeListEnd()
|
|
self:writeJSONArrayEnd()
|
|
end
|
|
|
|
function TJSONProtocol:writeSetBegin(etype, size)
|
|
self:writeJSONArrayBegin()
|
|
self:writeJSONString(TTypeToString[etype])
|
|
self:writeJSONInteger(size)
|
|
end
|
|
|
|
function TJSONProtocol:writeSetEnd()
|
|
self:writeJSONArrayEnd()
|
|
end
|
|
|
|
function TJSONProtocol:writeBool(bool)
|
|
if bool then
|
|
self:writeJSONInteger(1)
|
|
else
|
|
self:writeJSONInteger(0)
|
|
end
|
|
end
|
|
|
|
function TJSONProtocol:writeByte(byte)
|
|
local buff = libluabpack.bpack('c', byte)
|
|
local val = libluabpack.bunpack('c', buff)
|
|
self:writeJSONInteger(val)
|
|
end
|
|
|
|
function TJSONProtocol:writeI16(i16)
|
|
local buff = libluabpack.bpack('s', i16)
|
|
local val = libluabpack.bunpack('s', buff)
|
|
self:writeJSONInteger(val)
|
|
end
|
|
|
|
function TJSONProtocol:writeI32(i32)
|
|
local buff = libluabpack.bpack('i', i32)
|
|
local val = libluabpack.bunpack('i', buff)
|
|
self:writeJSONInteger(val)
|
|
end
|
|
|
|
function TJSONProtocol:writeI64(i64)
|
|
local buff = libluabpack.bpack('l', i64)
|
|
local val = libluabpack.bunpack('l', buff)
|
|
self:writeJSONInteger(tostring(val))
|
|
end
|
|
|
|
function TJSONProtocol:writeDouble(dub)
|
|
self:writeJSONDouble(string.format("%.20f", dub))
|
|
end
|
|
|
|
function TJSONProtocol:writeString(str)
|
|
self:writeJSONString(str)
|
|
end
|
|
|
|
function TJSONProtocol:writeUuid(uuid)
|
|
self:writeJSONString(uuid:getString())
|
|
end
|
|
|
|
function TJSONProtocol:writeBinary(str)
|
|
-- Should be utf-8
|
|
self:writeJSONBase64(str)
|
|
end
|
|
|
|
function TJSONProtocol:readJSONSyntaxChar(ch)
|
|
local ch2 = ""
|
|
if self.hasReadByte ~= "" then
|
|
ch2 = self.hasReadByte
|
|
self.hasReadByte = ""
|
|
else
|
|
ch2 = self.trans:readAll(1)
|
|
end
|
|
if ch2 ~= ch then
|
|
terror(TProtocolException:new{message = "Expected ".. ch .. ", got " .. ch2})
|
|
end
|
|
end
|
|
|
|
function TJSONProtocol:readElemSeparator()
|
|
if self.jsonContextVal.null then
|
|
return
|
|
end
|
|
if self.jsonContextVal.first then
|
|
self.jsonContextVal.first = false
|
|
else
|
|
if self.jsonContextVal.ttype == 1 then
|
|
if self.jsonContextVal.colon then
|
|
self:readJSONSyntaxChar(JSONNode.PairSeparator)
|
|
self.jsonContextVal.colon = false
|
|
else
|
|
self:readJSONSyntaxChar(JSONNode.ElemSeparator)
|
|
self.jsonContextVal.colon = true
|
|
end
|
|
else
|
|
self:readJSONSyntaxChar(JSONNode.ElemSeparator)
|
|
end
|
|
end
|
|
end
|
|
|
|
function TJSONProtocol:hexVal(ch)
|
|
local val = string.byte(ch)
|
|
if val >= 48 and val <= 57 then
|
|
return val - 48
|
|
elseif val >= 97 and val <= 102 then
|
|
return val - 87
|
|
else
|
|
terror(TProtocolException:new{message = "Expected hex val ([0-9a-f]); got " .. ch})
|
|
end
|
|
end
|
|
|
|
function TJSONProtocol:readJSONEscapeChar(ch)
|
|
self:readJSONSyntaxChar(JSONNode.ZeroChar)
|
|
self:readJSONSyntaxChar(JSONNode.ZeroChar)
|
|
local b1 = self.trans:readAll(1)
|
|
local b2 = self.trans:readAll(1)
|
|
return libluabitwise.shiftl(self:hexVal(b1), 4) + self:hexVal(b2)
|
|
end
|
|
|
|
|
|
-- A JSON string carries no length, so the string maximum is applied as the
|
|
-- value grows.
|
|
function TJSONProtocol:readJSONString()
|
|
self:readElemSeparator()
|
|
self:readJSONSyntaxChar(JSONNode.StringDelimiter)
|
|
local result = newValueBuffer()
|
|
local size = 0
|
|
while true do
|
|
local ch = self.trans:readAll(1)
|
|
if ch == JSONNode.StringDelimiter then
|
|
break
|
|
end
|
|
if ch == JSONNode.Backslash then
|
|
ch = self.trans:readAll(1)
|
|
if ch == JSONNode.EscapeChar then
|
|
ch = string.char(self:readJSONEscapeChar(ch))
|
|
else
|
|
local pos, _ = string.find(JSONNode.EscapeChars, ch, 1, true)
|
|
if pos == nil then
|
|
terror(TProtocolException:new{message = "Expected control char, got " .. ch})
|
|
end
|
|
ch = EscapeCharVals[pos]
|
|
end
|
|
end
|
|
size = size + string.len(ch)
|
|
self:checkStringSize(size)
|
|
addToValue(result, ch)
|
|
end
|
|
return valueOf(result)
|
|
end
|
|
|
|
-- The encoded text is read by readJSONString(), which holds it to the string
|
|
-- maximum; the decoded value is shorter than that.
|
|
function TJSONProtocol:readJSONBase64()
|
|
local result = self:readJSONString()
|
|
local length = string.len(result)
|
|
local str = newValueBuffer()
|
|
local offset = 1
|
|
while length >= 4 do
|
|
local bytes = string.sub(result, offset, offset+4)
|
|
addToValue(str, base64_decode(bytes))
|
|
offset = offset + 4
|
|
length = length - 4
|
|
end
|
|
if length >= 0 then
|
|
addToValue(str, base64_decode(string.sub(result, offset, offset + length)))
|
|
end
|
|
return valueOf(str)
|
|
end
|
|
|
|
-- A JSON number carries no length either, and is held to the string maximum
|
|
-- the same way.
|
|
function TJSONProtocol:readJSONNumericChars()
|
|
local result = newValueBuffer()
|
|
local size = 0
|
|
while true do
|
|
local ch = self.trans:readAll(1)
|
|
if string.find(ch, '[-+0-9.Ee]') then
|
|
size = size + 1
|
|
self:checkStringSize(size)
|
|
addToValue(result, ch)
|
|
else
|
|
self.hasReadByte = ch
|
|
break
|
|
end
|
|
end
|
|
return valueOf(result)
|
|
end
|
|
|
|
function TJSONProtocol:readJSONLongInteger()
|
|
self:readElemSeparator()
|
|
if self:escapeNum() then
|
|
self:readJSONSyntaxChar(JSONNode.StringDelimiter)
|
|
end
|
|
local result = self:readJSONNumericChars()
|
|
if self:escapeNum() then
|
|
self:readJSONSyntaxChar(JSONNode.StringDelimiter)
|
|
end
|
|
return result
|
|
end
|
|
|
|
function TJSONProtocol:readJSONInteger()
|
|
return tonumber(self:readJSONLongInteger())
|
|
end
|
|
|
|
function TJSONProtocol:readJSONDouble()
|
|
self:readElemSeparator()
|
|
local delimiter = self.trans:readAll(1)
|
|
local num = 0.0
|
|
if delimiter == JSONNode.StringDelimiter then
|
|
local str = self:readJSONString()
|
|
if str == JSONNode.Nan then
|
|
num = 1.0
|
|
elseif str == JSONNode.Infinity then
|
|
num = math.maxinteger
|
|
elseif str == JSONNode.NegativeInfinity then
|
|
num = math.mininteger
|
|
else
|
|
num = tonumber(str)
|
|
end
|
|
else
|
|
if self:escapeNum() then
|
|
self:readJSONSyntaxChar(JSONNode.StringDelimiter)
|
|
end
|
|
local result = self:readJSONNumericChars()
|
|
num = tonumber(delimiter.. result)
|
|
end
|
|
return num
|
|
end
|
|
|
|
function TJSONProtocol:readJSONObjectBegin()
|
|
self:readElemSeparator()
|
|
self:readJSONSyntaxChar(JSONNode.ObjectBegin)
|
|
self:contextPush({first = true, colon = true, ttype = 1, null = false})
|
|
end
|
|
|
|
function TJSONProtocol:readJSONObjectEnd()
|
|
self:readJSONSyntaxChar(JSONNode.ObjectEnd)
|
|
self:contextPop()
|
|
end
|
|
|
|
function TJSONProtocol:readJSONArrayBegin()
|
|
self:readElemSeparator()
|
|
self:readJSONSyntaxChar(JSONNode.ArrayBegin)
|
|
self:contextPush({first = true, colon = true, ttype = 2, null = false})
|
|
end
|
|
|
|
function TJSONProtocol:readJSONArrayEnd()
|
|
self:readJSONSyntaxChar(JSONNode.ArrayEnd)
|
|
self:contextPop()
|
|
end
|
|
|
|
function TJSONProtocol:readMessageBegin()
|
|
self:resetContext()
|
|
self:readJSONArrayBegin()
|
|
local version = self:readJSONInteger()
|
|
if version ~= self.THRIFT_JSON_PROTOCOL_VERSION then
|
|
terror(TProtocolException:new{message = "Message contained bad version."})
|
|
end
|
|
local name = self:readJSONString()
|
|
local ttype = self:readJSONInteger()
|
|
local seqid = self:readJSONInteger()
|
|
return name, ttype, seqid
|
|
end
|
|
|
|
function TJSONProtocol:readMessageEnd()
|
|
self:readJSONArrayEnd()
|
|
end
|
|
|
|
function TJSONProtocol:readStructBegin()
|
|
self:readJSONObjectBegin()
|
|
return nil
|
|
end
|
|
|
|
function TJSONProtocol:readStructEnd()
|
|
self:readJSONObjectEnd()
|
|
end
|
|
|
|
function TJSONProtocol:readFieldBegin()
|
|
local ttype = TType.STOP
|
|
local id = 0
|
|
local ch = self.trans:readAll(1)
|
|
self.hasReadByte = ch
|
|
if ch ~= JSONNode.ObjectEnd then
|
|
id = self:readJSONInteger()
|
|
self:readJSONObjectBegin()
|
|
local typeName = self:readJSONString()
|
|
ttype = StringToTType[typeName]
|
|
end
|
|
return nil, ttype, id
|
|
end
|
|
|
|
function TJSONProtocol:readFieldEnd()
|
|
self:readJSONObjectEnd()
|
|
end
|
|
|
|
function TJSONProtocol:readMapBegin()
|
|
self:readJSONArrayBegin()
|
|
local typeName = self:readJSONString()
|
|
local ktype = StringToTType[typeName]
|
|
typeName = self:readJSONString()
|
|
local vtype = StringToTType[typeName]
|
|
local size = self:readJSONInteger()
|
|
if size < 0 then
|
|
terror(TProtocolException:new{errorCode = TProtocolException.NEGATIVE_SIZE})
|
|
end
|
|
self:checkContainerSize(size)
|
|
self:readJSONObjectBegin()
|
|
return ktype, vtype, size
|
|
end
|
|
|
|
function TJSONProtocol:readMapEnd()
|
|
self:readJSONObjectEnd()
|
|
self:readJSONArrayEnd()
|
|
end
|
|
|
|
function TJSONProtocol:readListBegin()
|
|
self:readJSONArrayBegin()
|
|
local typeName = self:readJSONString()
|
|
local etype = StringToTType[typeName]
|
|
local size = self:readJSONInteger()
|
|
if size < 0 then
|
|
terror(TProtocolException:new{errorCode = TProtocolException.NEGATIVE_SIZE})
|
|
end
|
|
self:checkContainerSize(size)
|
|
return etype, size
|
|
end
|
|
|
|
function TJSONProtocol:readListEnd()
|
|
return self:readJSONArrayEnd()
|
|
end
|
|
|
|
function TJSONProtocol:readSetBegin()
|
|
return self:readListBegin()
|
|
end
|
|
|
|
function TJSONProtocol:readSetEnd()
|
|
return self:readJSONArrayEnd()
|
|
end
|
|
|
|
function TJSONProtocol:readBool()
|
|
local result = self:readJSONInteger()
|
|
if result == 1 then
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
function TJSONProtocol:readByte()
|
|
local result = self:readJSONInteger()
|
|
if result >= 256 then
|
|
terror(TProtocolException:new{message = "UnExpected Byte " .. result})
|
|
end
|
|
return result
|
|
end
|
|
|
|
function TJSONProtocol:readI16()
|
|
return self:readJSONInteger()
|
|
end
|
|
|
|
function TJSONProtocol:readI32()
|
|
return self:readJSONInteger()
|
|
end
|
|
|
|
function TJSONProtocol:readI64()
|
|
local long = liblualongnumber.new
|
|
return long(self:readJSONLongInteger())
|
|
end
|
|
|
|
function TJSONProtocol:readDouble()
|
|
return self:readJSONDouble()
|
|
end
|
|
|
|
function TJSONProtocol:readString()
|
|
return self:readJSONString()
|
|
end
|
|
|
|
function TJSONProtocol:readUuid()
|
|
return TUUIDfromString(self:readJSONString())
|
|
end
|
|
|
|
function TJSONProtocol:readBinary()
|
|
return self:readJSONBase64()
|
|
end
|
|
|
|
TJSONProtocolFactory = TProtocolFactory:new{
|
|
__type = 'TJSONProtocolFactory',
|
|
}
|
|
|
|
function TJSONProtocolFactory:getProtocol(trans)
|
|
-- TODO Enforce that this must be a transport class (ie not a bool)
|
|
if not trans then
|
|
terror(TProtocolException:new{
|
|
message = 'Must supply a transport to ' .. ttype(self)
|
|
})
|
|
end
|
|
return TJSONProtocol:new{
|
|
trans = trans
|
|
}
|
|
end
|