mirror of
https://github.com/apache/thrift.git
synced 2026-10-28 16:35:10 +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>
230 lines
7.3 KiB
Lua
230 lines
7.3 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 'Thrift'
|
|
|
|
TProtocolException = TException:new {
|
|
UNKNOWN = 0,
|
|
INVALID_DATA = 1,
|
|
NEGATIVE_SIZE = 2,
|
|
SIZE_LIMIT = 3,
|
|
BAD_VERSION = 4,
|
|
INVALID_PROTOCOL = 5,
|
|
DEPTH_LIMIT = 6,
|
|
errorCode = 0,
|
|
__type = 'TProtocolException'
|
|
}
|
|
function TProtocolException:__errorCodeToString()
|
|
if self.errorCode == self.INVALID_DATA then
|
|
return 'Invalid data'
|
|
elseif self.errorCode == self.NEGATIVE_SIZE then
|
|
return 'Negative size'
|
|
elseif self.errorCode == self.SIZE_LIMIT then
|
|
return 'Size limit'
|
|
elseif self.errorCode == self.BAD_VERSION then
|
|
return 'Bad version'
|
|
elseif self.errorCode == self.INVALID_PROTOCOL then
|
|
return 'Invalid protocol'
|
|
elseif self.errorCode == self.DEPTH_LIMIT then
|
|
return 'Exceeded size limit'
|
|
else
|
|
return 'Default (unknown)'
|
|
end
|
|
end
|
|
|
|
DEFAULT_RECURSION_DEPTH = 64
|
|
|
|
-- The longest string or binary field a protocol will read by default. Same
|
|
-- value the frame limit uses; override per protocol instance.
|
|
DEFAULT_MAX_STRING_SIZE = 16384000
|
|
|
|
-- The most elements a list, set or map may declare by default. Same value
|
|
-- again: every element takes at least one byte on the wire, so no container in
|
|
-- a message that fits into one frame of the default size exceeds it. Override
|
|
-- per protocol instance with maxContainerSize; zero or less switches it off.
|
|
DEFAULT_MAX_CONTAINER_SIZE = 16384000
|
|
|
|
TProtocolBase = __TObject:new{
|
|
__type = 'TProtocolBase',
|
|
trans
|
|
}
|
|
|
|
function TProtocolBase:new(obj)
|
|
if ttype(obj) ~= 'table' then
|
|
error(ttype(self) .. 'must be initialized with a table')
|
|
end
|
|
|
|
-- Ensure a transport is provided
|
|
if not obj.trans then
|
|
error('You must provide ' .. ttype(self) .. ' with a trans')
|
|
end
|
|
|
|
obj.recursionDepth = 0
|
|
return __TObject.new(self, obj)
|
|
end
|
|
|
|
-- Refuses a declared string or binary length before anything is read for it.
|
|
function TProtocolBase:checkStringSize(size)
|
|
local limit = self.maxStringSize or DEFAULT_MAX_STRING_SIZE
|
|
if size > limit then
|
|
terror(TProtocolException:new{
|
|
message = 'String size ' .. tostring(size) .. ' exceeds maximum ' .. tostring(limit),
|
|
errorCode = TProtocolException.SIZE_LIMIT
|
|
})
|
|
end
|
|
end
|
|
|
|
-- Refuses a declared list, set or map size before any element is read.
|
|
function TProtocolBase:checkContainerSize(size)
|
|
local limit = self.maxContainerSize or DEFAULT_MAX_CONTAINER_SIZE
|
|
if limit > 0 and size > limit then
|
|
terror(TProtocolException:new{
|
|
message = 'Container size ' .. tostring(size) .. ' exceeds maximum ' .. tostring(limit),
|
|
errorCode = TProtocolException.SIZE_LIMIT
|
|
})
|
|
end
|
|
end
|
|
|
|
function TProtocolBase:incrementRecursionDepth()
|
|
self.recursionDepth = self.recursionDepth + 1
|
|
if self.recursionDepth > DEFAULT_RECURSION_DEPTH then
|
|
self.recursionDepth = self.recursionDepth - 1
|
|
terror(TProtocolException:new{
|
|
message = 'Maximum recursion depth exceeded',
|
|
errorCode = TProtocolException.DEPTH_LIMIT
|
|
})
|
|
end
|
|
end
|
|
|
|
function TProtocolBase:decrementRecursionDepth()
|
|
self.recursionDepth = self.recursionDepth - 1
|
|
end
|
|
|
|
function TProtocolBase:writeMessageBegin(name, ttype, seqid) end
|
|
function TProtocolBase:writeMessageEnd() end
|
|
function TProtocolBase:writeStructBegin(name) end
|
|
function TProtocolBase:writeStructEnd() end
|
|
function TProtocolBase:writeFieldBegin(name, ttype, id) end
|
|
function TProtocolBase:writeFieldEnd() end
|
|
function TProtocolBase:writeFieldStop() end
|
|
function TProtocolBase:writeMapBegin(ktype, vtype, size) end
|
|
function TProtocolBase:writeMapEnd() end
|
|
function TProtocolBase:writeListBegin(ttype, size) end
|
|
function TProtocolBase:writeListEnd() end
|
|
function TProtocolBase:writeSetBegin(ttype, size) end
|
|
function TProtocolBase:writeSetEnd() end
|
|
function TProtocolBase:writeBool(bool) end
|
|
function TProtocolBase:writeByte(byte) end
|
|
function TProtocolBase:writeI16(i16) end
|
|
function TProtocolBase:writeI32(i32) end
|
|
function TProtocolBase:writeI64(i64) end
|
|
function TProtocolBase:writeDouble(dub) end
|
|
function TProtocolBase:writeString(str) end
|
|
function TProtocolBase:writeUuid(uuid) end
|
|
function TProtocolBase:readMessageBegin() end
|
|
function TProtocolBase:readMessageEnd() end
|
|
function TProtocolBase:readStructBegin() end
|
|
function TProtocolBase:readStructEnd() end
|
|
function TProtocolBase:readFieldBegin() end
|
|
function TProtocolBase:readFieldEnd() end
|
|
function TProtocolBase:readMapBegin() end
|
|
function TProtocolBase:readMapEnd() end
|
|
function TProtocolBase:readListBegin() end
|
|
function TProtocolBase:readListEnd() end
|
|
function TProtocolBase:readSetBegin() end
|
|
function TProtocolBase:readSetEnd() end
|
|
function TProtocolBase:readBool() end
|
|
function TProtocolBase:readByte() end
|
|
function TProtocolBase:readI16() end
|
|
function TProtocolBase:readI32() end
|
|
function TProtocolBase:readI64() end
|
|
function TProtocolBase:readDouble() end
|
|
function TProtocolBase:readString() end
|
|
function TProtocolBase:readUuid() end
|
|
|
|
-- Skipping descends through nested structs and containers the same way reading
|
|
-- them does, so it draws on the same depth budget.
|
|
function TProtocolBase:skip(ttype)
|
|
self:incrementRecursionDepth()
|
|
local ok, err = pcall(self.skipType, self, ttype)
|
|
self:decrementRecursionDepth()
|
|
if not ok then
|
|
error(err, 0)
|
|
end
|
|
end
|
|
|
|
function TProtocolBase:skipType(ttype)
|
|
if ttype == TType.BOOL then
|
|
self:readBool()
|
|
elseif ttype == TType.BYTE then
|
|
self:readByte()
|
|
elseif ttype == TType.I16 then
|
|
self:readI16()
|
|
elseif ttype == TType.I32 then
|
|
self:readI32()
|
|
elseif ttype == TType.I64 then
|
|
self:readI64()
|
|
elseif ttype == TType.DOUBLE then
|
|
self:readDouble()
|
|
elseif ttype == TType.STRING then
|
|
self:readString()
|
|
elseif ttype == TType.STRUCT then
|
|
local name = self:readStructBegin()
|
|
while true do
|
|
local name, ttype, id = self:readFieldBegin()
|
|
if ttype == TType.STOP then
|
|
break
|
|
end
|
|
self:skip(ttype)
|
|
self:readFieldEnd()
|
|
end
|
|
self:readStructEnd()
|
|
elseif ttype == TType.MAP then
|
|
local kttype, vttype, size = self:readMapBegin()
|
|
for i = 1, size, 1 do
|
|
self:skip(kttype)
|
|
self:skip(vttype)
|
|
end
|
|
self:readMapEnd()
|
|
elseif ttype == TType.SET then
|
|
local ettype, size = self:readSetBegin()
|
|
for i = 1, size, 1 do
|
|
self:skip(ettype)
|
|
end
|
|
self:readSetEnd()
|
|
elseif ttype == TType.LIST then
|
|
local ettype, size = self:readListBegin()
|
|
for i = 1, size, 1 do
|
|
self:skip(ettype)
|
|
end
|
|
self:readListEnd()
|
|
elseif ttype == TType.UUID then
|
|
self:readUuid()
|
|
else
|
|
terror(TProtocolException:new{
|
|
message = 'Invalid data'
|
|
})
|
|
end
|
|
end
|
|
|
|
TProtocolFactory = __TObject:new{
|
|
__type = 'TProtocolFactory',
|
|
}
|
|
function TProtocolFactory:getProtocol(trans) end
|