blob: cec8e2640aad45c2c26a4fde4e6535d9075f2604 [file] [log] [blame]
Wang Yaofu19a3a272016-02-14 18:15:45 +08001--
2-- Licensed to the Apache Software Foundation (ASF) under one
3-- or more contributor license agreements. See the NOTICE file
4-- distributed with this work for additional information
5-- regarding copyright ownership. The ASF licenses this file
6-- to you under the Apache License, Version 2.0 (the
7-- "License"), you may not use this file except in compliance
8-- with the License. You may obtain a copy of the License at
9--
10-- http://www.apache.org/licenses/LICENSE-2.0
11--
12-- Unless required by applicable law or agreed to in writing,
13-- software distributed under the License is distributed on an
14-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15-- KIND, either express or implied. See the License for the
16-- specific language governing permissions and limitations
17-- under the License.
18--
19
20require 'TProtocol'
Thomaseb684d32024-07-28 15:32:23 +020021local libluabpack = require 'libluabpack'
22local libluabitwise = require 'libluabitwise'
23local liblualongnumber = require 'liblualongnumber'
Wang Yaofu19a3a272016-02-14 18:15:45 +080024
25TJSONProtocol = __TObject.new(TProtocolBase, {
26 __type = 'TJSONProtocol',
27 THRIFT_JSON_PROTOCOL_VERSION = 1,
28 jsonContext = {},
29 jsonContextVal = {first = true, colon = true, ttype = 2, null = true},
30 jsonContextIndex = 1,
31 hasReadByte = ""
32})
33
34TTypeToString = {}
35TTypeToString[TType.BOOL] = "tf"
36TTypeToString[TType.BYTE] = "i8"
37TTypeToString[TType.I16] = "i16"
38TTypeToString[TType.I32] = "i32"
39TTypeToString[TType.I64] = "i64"
40TTypeToString[TType.DOUBLE] = "dbl"
41TTypeToString[TType.STRING] = "str"
42TTypeToString[TType.STRUCT] = "rec"
43TTypeToString[TType.LIST] = "lst"
44TTypeToString[TType.SET] = "set"
45TTypeToString[TType.MAP] = "map"
Thomaseb684d32024-07-28 15:32:23 +020046TTypeToString[TType.UUID] = "uid"
Wang Yaofu19a3a272016-02-14 18:15:45 +080047
48StringToTType = {
49 tf = TType.BOOL,
50 i8 = TType.BYTE,
51 i16 = TType.I16,
52 i32 = TType.I32,
53 i64 = TType.I64,
54 dbl = TType.DOUBLE,
55 str = TType.STRING,
56 rec = TType.STRUCT,
57 map = TType.MAP,
58 set = TType.SET,
Thomaseb684d32024-07-28 15:32:23 +020059 lst = TType.LIST,
60 uid = TType.UUID,
Wang Yaofu19a3a272016-02-14 18:15:45 +080061}
62
63JSONNode = {
64 ObjectBegin = '{',
65 ObjectEnd = '}',
66 ArrayBegin = '[',
67 ArrayEnd = ']',
68 PairSeparator = ':',
69 ElemSeparator = ',',
70 Backslash = '\\',
71 StringDelimiter = '"',
72 ZeroChar = '0',
73 EscapeChar = 'u',
74 Nan = 'NaN',
75 Infinity = 'Infinity',
76 NegativeInfinity = '-Infinity',
77 EscapeChars = "\"\\bfnrt",
78 EscapePrefix = "\\u00"
79}
80
81EscapeCharVals = {
82 '"', '\\', '\b', '\f', '\n', '\r', '\t'
83}
84
85JSONCharTable = {
86 --0 1 2 3 4 5 6 7 8 9 A B C D E F
87 0, 0, 0, 0, 0, 0, 0, 0, 98,116,110, 0,102,114, 0, 0,
88 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
89 1, 1,34, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
90}
91
92-- character table string
93local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
94
95-- encoding
96function base64_encode(data)
97 return ((data:gsub('.', function(x)
98 local r,b='',x:byte()
99 for i=8,1,-1 do r=r..(b%2^i-b%2^(i-1)>0 and '1' or '0') end
100 return r;
101 end)..'0000'):gsub('%d%d%d?%d?%d?%d?', function(x)
102 if (#x < 6) then return '' end
103 local c=0
104 for i=1,6 do c=c+(x:sub(i,i)=='1' and 2^(6-i) or 0) end
105 return b:sub(c+1,c+1)
106 end)..({ '', '==', '=' })[#data%3+1])
107end
108
109-- decoding
110function base64_decode(data)
111 data = string.gsub(data, '[^'..b..'=]', '')
112 return (data:gsub('.', function(x)
113 if (x == '=') then return '' end
114 local r,f='',(b:find(x)-1)
115 for i=6,1,-1 do r=r..(f%2^i-f%2^(i-1)>0 and '1' or '0') end
116 return r;
117 end):gsub('%d%d%d?%d?%d?%d?%d?%d?', function(x)
118 if (#x ~= 8) then return '' end
119 local c=0
120 for i=1,8 do c=c+(x:sub(i,i)=='1' and 2^(8-i) or 0) end
121 return string.char(c)
122 end))
123end
124
125function TJSONProtocol:resetContext()
126 self.jsonContext = {}
127 self.jsonContextVal = {first = true, colon = true, ttype = 2, null = true}
128 self.jsonContextIndex = 1
129end
130
131function TJSONProtocol:contextPush(context)
132 self.jsonContextIndex = self.jsonContextIndex + 1
133 self.jsonContext[self.jsonContextIndex] = self.jsonContextVal
134 self.jsonContextVal = context
135end
136
137function TJSONProtocol:contextPop()
138 self.jsonContextVal = self.jsonContext[self.jsonContextIndex]
139 self.jsonContextIndex = self.jsonContextIndex - 1
140end
141
142function TJSONProtocol:escapeNum()
143 if self.jsonContextVal.ttype == 1 then
144 return self.jsonContextVal.colon
145 else
146 return false
147 end
148end
149
150function TJSONProtocol:writeElemSeparator()
151 if self.jsonContextVal.null then
152 return
153 end
154 if self.jsonContextVal.first then
155 self.jsonContextVal.first = false
156 else
157 if self.jsonContextVal.ttype == 1 then
158 if self.jsonContextVal.colon then
159 self.trans:write(JSONNode.PairSeparator)
160 self.jsonContextVal.colon = false
161 else
162 self.trans:write(JSONNode.ElemSeparator)
163 self.jsonContextVal.colon = true
164 end
165 else
166 self.trans:write(JSONNode.ElemSeparator)
167 end
168 end
169end
170
171function TJSONProtocol:hexChar(val)
172 val = libluabitwise.band(val, 0x0f)
173 if val < 10 then
174 return val + 48
175 else
176 return val + 87
177 end
178end
179
180function TJSONProtocol:writeJSONEscapeChar(ch)
181 self.trans:write(JSONNode.EscapePrefix)
182 local outCh = hexChar(libluabitwise.shiftr(ch, 4))
183 local buff = libluabpack.bpack('c', outCh)
184 self.trans:write(buff)
185 outCh = hexChar(ch)
186 buff = libluabpack.bpack('c', outCh)
187 self.trans:write(buff)
188end
189
190function TJSONProtocol:writeJSONChar(byte)
191 ch = string.byte(byte)
192 if ch >= 0x30 then
193 if ch == JSONNode.Backslash then
194 self.trans:write(JSONNode.Backslash)
195 self.trans:write(JSONNode.Backslash)
196 else
197 self.trans:write(byte)
198 end
199 else
200 local outCh = JSONCharTable[ch+1]
201 if outCh == 1 then
202 self.trans:write(byte)
203 elseif outCh > 1 then
204 self.trans:write(JSONNode.Backslash)
205 local buff = libluabpack.bpack('c', outCh)
206 self.trans:write(buff)
207 else
208 self:writeJSONEscapeChar(ch)
209 end
210 end
211end
212
213function TJSONProtocol:writeJSONString(str)
214 self:writeElemSeparator()
215 self.trans:write(JSONNode.StringDelimiter)
216 -- TODO escape special characters
217 local length = string.len(str)
218 local ii = 1
219 while ii <= length do
220 self:writeJSONChar(string.sub(str, ii, ii))
221 ii = ii + 1
222 end
223 self.trans:write(JSONNode.StringDelimiter)
224end
225
226function TJSONProtocol:writeJSONBase64(str)
227 self:writeElemSeparator()
228 self.trans:write(JSONNode.StringDelimiter)
229 local length = string.len(str)
230 local offset = 1
231 while length >= 3 do
232 -- Encode 3 bytes at a time
233 local bytes = base64_encode(string.sub(str, offset, offset+3))
234 self.trans:write(bytes)
235 length = length - 3
236 offset = offset + 3
237 end
238 if length > 0 then
239 local bytes = base64_encode(string.sub(str, offset, offset+length))
240 self.trans:write(bytes)
241 end
242 self.trans:write(JSONNode.StringDelimiter)
243end
244
245function TJSONProtocol:writeJSONInteger(num)
246 self:writeElemSeparator()
247 if self:escapeNum() then
248 self.trans:write(JSONNode.StringDelimiter)
249 end
250 local numstr = "" .. num
251 numstr = string.sub(numstr, string.find(numstr, "^[+-]?%d+"))
252 self.trans:write(numstr)
253 if self:escapeNum() then
254 self.trans:write(JSONNode.StringDelimiter)
255 end
256end
257
258function TJSONProtocol:writeJSONDouble(dub)
259 self:writeElemSeparator()
260 local val = "" .. dub
261 local prefix = string.sub(val, 1, 1)
262 local special = false
263 if prefix == 'N' or prefix == 'n' then
264 val = JSONNode.Nan
265 special = true
266 elseif prefix == 'I' or prefix == 'i' then
267 val = JSONNode.Infinity
268 special = true
269 elseif prefix == '-' then
270 local secondByte = string.sub(val, 2, 2)
271 if secondByte == 'I' or secondByte == 'i' then
272 val = JSONNode.NegativeInfinity
273 special = true
274 end
275 end
276
277 if special or self:escapeNum() then
278 self.trans:write(JSONNode.StringDelimiter)
279 end
280 self.trans:write(val)
281 if special or self:escapeNum() then
282 self.trans:write(JSONNode.StringDelimiter)
283 end
284end
285
286function TJSONProtocol:writeJSONObjectBegin()
287 self:writeElemSeparator()
288 self.trans:write(JSONNode.ObjectBegin)
289 self:contextPush({first = true, colon = true, ttype = 1, null = false})
290end
291
292function TJSONProtocol:writeJSONObjectEnd()
293 self:contextPop()
294 self.trans:write(JSONNode.ObjectEnd)
295end
296
297function TJSONProtocol:writeJSONArrayBegin()
298 self:writeElemSeparator()
299 self.trans:write(JSONNode.ArrayBegin)
300 self:contextPush({first = true, colon = true, ttype = 2, null = false})
301end
302
303function TJSONProtocol:writeJSONArrayEnd()
304 self:contextPop()
305 self.trans:write(JSONNode.ArrayEnd)
306end
307
308function TJSONProtocol:writeMessageBegin(name, ttype, seqid)
309 self:resetContext()
310 self:writeJSONArrayBegin()
311 self:writeJSONInteger(TJSONProtocol.THRIFT_JSON_PROTOCOL_VERSION)
312 self:writeJSONString(name)
313 self:writeJSONInteger(ttype)
314 self:writeJSONInteger(seqid)
315end
316
317function TJSONProtocol:writeMessageEnd()
318 self:writeJSONArrayEnd()
319end
320
321function TJSONProtocol:writeStructBegin(name)
322 self:writeJSONObjectBegin()
323end
324
325function TJSONProtocol:writeStructEnd()
326 self:writeJSONObjectEnd()
327end
328
329function TJSONProtocol:writeFieldBegin(name, ttype, id)
330 self:writeJSONInteger(id)
331 self:writeJSONObjectBegin()
332 self:writeJSONString(TTypeToString[ttype])
333end
334
335function TJSONProtocol:writeFieldEnd()
336 self:writeJSONObjectEnd()
337end
338
339function TJSONProtocol:writeFieldStop()
340end
341
342function TJSONProtocol:writeMapBegin(ktype, vtype, size)
343 self:writeJSONArrayBegin()
344 self:writeJSONString(TTypeToString[ktype])
345 self:writeJSONString(TTypeToString[vtype])
346 self:writeJSONInteger(size)
347 return self:writeJSONObjectBegin()
348end
349
350function TJSONProtocol:writeMapEnd()
351 self:writeJSONObjectEnd()
352 self:writeJSONArrayEnd()
353end
354
355function TJSONProtocol:writeListBegin(etype, size)
356 self:writeJSONArrayBegin()
357 self:writeJSONString(TTypeToString[etype])
358 self:writeJSONInteger(size)
359end
360
361function TJSONProtocol:writeListEnd()
362 self:writeJSONArrayEnd()
363end
364
365function TJSONProtocol:writeSetBegin(etype, size)
366 self:writeJSONArrayBegin()
367 self:writeJSONString(TTypeToString[etype])
368 self:writeJSONInteger(size)
369end
370
371function TJSONProtocol:writeSetEnd()
372 self:writeJSONArrayEnd()
373end
374
375function TJSONProtocol:writeBool(bool)
376 if bool then
377 self:writeJSONInteger(1)
378 else
379 self:writeJSONInteger(0)
380 end
381end
382
383function TJSONProtocol:writeByte(byte)
384 local buff = libluabpack.bpack('c', byte)
385 local val = libluabpack.bunpack('c', buff)
386 self:writeJSONInteger(val)
387end
388
389function TJSONProtocol:writeI16(i16)
390 local buff = libluabpack.bpack('s', i16)
391 local val = libluabpack.bunpack('s', buff)
392 self:writeJSONInteger(val)
393end
394
395function TJSONProtocol:writeI32(i32)
396 local buff = libluabpack.bpack('i', i32)
397 local val = libluabpack.bunpack('i', buff)
398 self:writeJSONInteger(val)
399end
400
401function TJSONProtocol:writeI64(i64)
402 local buff = libluabpack.bpack('l', i64)
403 local val = libluabpack.bunpack('l', buff)
404 self:writeJSONInteger(tostring(val))
405end
406
407function TJSONProtocol:writeDouble(dub)
Thomaseb684d32024-07-28 15:32:23 +0200408 self:writeJSONDouble(string.format("%.20f", dub))
Wang Yaofu19a3a272016-02-14 18:15:45 +0800409end
410
411function TJSONProtocol:writeString(str)
412 self:writeJSONString(str)
413end
414
Thomaseb684d32024-07-28 15:32:23 +0200415function TJSONProtocol:writeUuid(uuid)
416 self:writeJSONString(uuid:getString())
417end
418
Wang Yaofu19a3a272016-02-14 18:15:45 +0800419function TJSONProtocol:writeBinary(str)
420 -- Should be utf-8
421 self:writeJSONBase64(str)
422end
423
424function TJSONProtocol:readJSONSyntaxChar(ch)
425 local ch2 = ""
426 if self.hasReadByte ~= "" then
427 ch2 = self.hasReadByte
428 self.hasReadByte = ""
429 else
430 ch2 = self.trans:readAll(1)
431 end
432 if ch2 ~= ch then
433 terror(TProtocolException:new{message = "Expected ".. ch .. ", got " .. ch2})
434 end
435end
436
437function TJSONProtocol:readElemSeparator()
438 if self.jsonContextVal.null then
439 return
440 end
441 if self.jsonContextVal.first then
442 self.jsonContextVal.first = false
443 else
444 if self.jsonContextVal.ttype == 1 then
445 if self.jsonContextVal.colon then
446 self:readJSONSyntaxChar(JSONNode.PairSeparator)
447 self.jsonContextVal.colon = false
448 else
449 self:readJSONSyntaxChar(JSONNode.ElemSeparator)
450 self.jsonContextVal.colon = true
451 end
452 else
453 self:readJSONSyntaxChar(JSONNode.ElemSeparator)
454 end
455 end
456end
457
458function TJSONProtocol:hexVal(ch)
459 local val = string.byte(ch)
460 if val >= 48 and val <= 57 then
461 return val - 48
462 elseif val >= 97 and val <= 102 then
463 return val - 87
464 else
465 terror(TProtocolException:new{message = "Expected hex val ([0-9a-f]); got " .. ch})
466 end
467end
468
469function TJSONProtocol:readJSONEscapeChar(ch)
470 self:readJSONSyntaxChar(JSONNode.ZeroChar)
471 self:readJSONSyntaxChar(JSONNode.ZeroChar)
472 local b1 = self.trans:readAll(1)
473 local b2 = self.trans:readAll(1)
474 return libluabitwise.shiftl(self:hexVal(b1), 4) + self:hexVal(b2)
475end
476
477
478function TJSONProtocol:readJSONString()
479 self:readElemSeparator()
480 self:readJSONSyntaxChar(JSONNode.StringDelimiter)
481 local result = ""
482 while true do
483 local ch = self.trans:readAll(1)
484 if ch == JSONNode.StringDelimiter then
485 break
486 end
487 if ch == JSONNode.Backslash then
488 ch = self.trans:readAll(1)
489 if ch == JSONNode.EscapeChar then
490 self:readJSONEscapeChar(ch)
491 else
492 local pos, _ = string.find(JSONNode.EscapeChars, ch)
493 if pos == nil then
494 terror(TProtocolException:new{message = "Expected control char, got " .. ch})
495 end
496 ch = EscapeCharVals[pos]
497 end
498 end
499 result = result .. ch
500 end
501 return result
502end
503
504function TJSONProtocol:readJSONBase64()
505 local result = self:readJSONString()
506 local length = string.len(result)
507 local str = ""
508 local offset = 1
509 while length >= 4 do
510 local bytes = string.sub(result, offset, offset+4)
511 str = str .. base64_decode(bytes)
512 offset = offset + 4
513 length = length - 4
514 end
515 if length >= 0 then
516 str = str .. base64_decode(string.sub(result, offset, offset + length))
517 end
518 return str
519end
520
521function TJSONProtocol:readJSONNumericChars()
522 local result = ""
523 while true do
524 local ch = self.trans:readAll(1)
525 if string.find(ch, '[-+0-9.Ee]') then
526 result = result .. ch
527 else
528 self.hasReadByte = ch
529 break
530 end
531 end
532 return result
533end
534
535function TJSONProtocol:readJSONLongInteger()
536 self:readElemSeparator()
537 if self:escapeNum() then
538 self:readJSONSyntaxChar(JSONNode.StringDelimiter)
539 end
540 local result = self:readJSONNumericChars()
541 if self:escapeNum() then
542 self:readJSONSyntaxChar(JSONNode.StringDelimiter)
543 end
544 return result
545end
546
547function TJSONProtocol:readJSONInteger()
548 return tonumber(self:readJSONLongInteger())
549end
550
551function TJSONProtocol:readJSONDouble()
552 self:readElemSeparator()
553 local delimiter = self.trans:readAll(1)
554 local num = 0.0
555 if delimiter == JSONNode.StringDelimiter then
556 local str = self:readJSONString()
557 if str == JSONNode.Nan then
558 num = 1.0
559 elseif str == JSONNode.Infinity then
560 num = math.maxinteger
561 elseif str == JSONNode.NegativeInfinity then
562 num = math.mininteger
563 else
564 num = tonumber(str)
565 end
566 else
567 if self:escapeNum() then
568 self:readJSONSyntaxChar(JSONNode.StringDelimiter)
569 end
570 local result = self:readJSONNumericChars()
571 num = tonumber(delimiter.. result)
572 end
573 return num
574end
575
576function TJSONProtocol:readJSONObjectBegin()
577 self:readElemSeparator()
578 self:readJSONSyntaxChar(JSONNode.ObjectBegin)
579 self:contextPush({first = true, colon = true, ttype = 1, null = false})
580end
581
582function TJSONProtocol:readJSONObjectEnd()
583 self:readJSONSyntaxChar(JSONNode.ObjectEnd)
584 self:contextPop()
585end
586
587function TJSONProtocol:readJSONArrayBegin()
588 self:readElemSeparator()
589 self:readJSONSyntaxChar(JSONNode.ArrayBegin)
590 self:contextPush({first = true, colon = true, ttype = 2, null = false})
591end
592
593function TJSONProtocol:readJSONArrayEnd()
594 self:readJSONSyntaxChar(JSONNode.ArrayEnd)
595 self:contextPop()
596end
597
598function TJSONProtocol:readMessageBegin()
599 self:resetContext()
600 self:readJSONArrayBegin()
601 local version = self:readJSONInteger()
602 if version ~= self.THRIFT_JSON_PROTOCOL_VERSION then
603 terror(TProtocolException:new{message = "Message contained bad version."})
604 end
605 local name = self:readJSONString()
606 local ttype = self:readJSONInteger()
607 local seqid = self:readJSONInteger()
608 return name, ttype, seqid
609end
610
611function TJSONProtocol:readMessageEnd()
612 self:readJSONArrayEnd()
613end
614
615function TJSONProtocol:readStructBegin()
616 self:readJSONObjectBegin()
617 return nil
618end
619
620function TJSONProtocol:readStructEnd()
621 self:readJSONObjectEnd()
622end
623
624function TJSONProtocol:readFieldBegin()
625 local ttype = TType.STOP
626 local id = 0
627 local ch = self.trans:readAll(1)
628 self.hasReadByte = ch
629 if ch ~= JSONNode.ObjectEnd then
630 id = self:readJSONInteger()
631 self:readJSONObjectBegin()
632 local typeName = self:readJSONString()
633 ttype = StringToTType[typeName]
634 end
635 return nil, ttype, id
636end
637
638function TJSONProtocol:readFieldEnd()
639 self:readJSONObjectEnd()
640end
641
642function TJSONProtocol:readMapBegin()
643 self:readJSONArrayBegin()
644 local typeName = self:readJSONString()
645 local ktype = StringToTType[typeName]
646 typeName = self:readJSONString()
647 local vtype = StringToTType[typeName]
648 local size = self:readJSONInteger()
649 self:readJSONObjectBegin()
650 return ktype, vtype, size
651end
652
653function TJSONProtocol:readMapEnd()
654 self:readJSONObjectEnd()
655 self:readJSONArrayEnd()
656end
657
658function TJSONProtocol:readListBegin()
659 self:readJSONArrayBegin()
660 local typeName = self:readJSONString()
661 local etype = StringToTType[typeName]
662 local size = self:readJSONInteger()
663 return etype, size
664end
665
666function TJSONProtocol:readListEnd()
667 return self:readJSONArrayEnd()
668end
669
670function TJSONProtocol:readSetBegin()
671 return self:readListBegin()
672end
673
674function TJSONProtocol:readSetEnd()
675 return self:readJSONArrayEnd()
676end
677
678function TJSONProtocol:readBool()
679 local result = self:readJSONInteger()
680 if result == 1 then
681 return true
682 else
683 return false
684 end
685end
686
687function TJSONProtocol:readByte()
688 local result = self:readJSONInteger()
689 if result >= 256 then
690 terror(TProtocolException:new{message = "UnExpected Byte " .. result})
691 end
692 return result
693end
694
695function TJSONProtocol:readI16()
696 return self:readJSONInteger()
697end
698
699function TJSONProtocol:readI32()
700 return self:readJSONInteger()
701end
702
703function TJSONProtocol:readI64()
704 local long = liblualongnumber.new
705 return long(self:readJSONLongInteger())
706end
707
708function TJSONProtocol:readDouble()
709 return self:readJSONDouble()
710end
711
712function TJSONProtocol:readString()
713 return self:readJSONString()
714end
715
Thomaseb684d32024-07-28 15:32:23 +0200716function TJSONProtocol:readUuid()
717 return TUUIDfromString(self:readJSONString())
718end
719
Wang Yaofu19a3a272016-02-14 18:15:45 +0800720function TJSONProtocol:readBinary()
721 return self:readJSONBase64()
722end
723
724TJSONProtocolFactory = TProtocolFactory:new{
725 __type = 'TJSONProtocolFactory',
726}
727
728function TJSONProtocolFactory:getProtocol(trans)
729 -- TODO Enforce that this must be a transport class (ie not a bool)
730 if not trans then
731 terror(TProtocolException:new{
732 message = 'Must supply a transport to ' .. ttype(self)
733 })
734 end
735 return TJSONProtocol:new{
736 trans = trans
737 }
738end