blob: d270ce8243258a90cfab5abe1ea1ced0757ec501 [file] [log] [blame]
Roger Meier85fb6de2012-11-02 00:05:42 +00001#
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
Konrad Grochowski93fea152014-10-02 16:29:14 +020020from TProtocol import TType, TProtocolBase, TProtocolException, \
21 checkIntegerLimits
Roger Meier0895dfe2012-12-26 22:09:55 +010022import base64
23import json
24import math
Roger Meier85fb6de2012-11-02 00:05:42 +000025
Roger Meier0895dfe2012-12-26 22:09:55 +010026__all__ = ['TJSONProtocol',
27 'TJSONProtocolFactory',
28 'TSimpleJSONProtocol',
29 'TSimpleJSONProtocolFactory']
Roger Meier85fb6de2012-11-02 00:05:42 +000030
31VERSION = 1
32
33COMMA = ','
34COLON = ':'
35LBRACE = '{'
36RBRACE = '}'
37LBRACKET = '['
38RBRACKET = ']'
39QUOTE = '"'
40BACKSLASH = '\\'
41ZERO = '0'
42
43ESCSEQ = '\\u00'
Jens Geyer15c40042015-09-29 21:33:23 +020044ESCAPE_CHAR = '"\\bfnrt/'
45ESCAPE_CHAR_VALS = ['"', '\\', '\b', '\f', '\n', '\r', '\t', '/']
Roger Meier85fb6de2012-11-02 00:05:42 +000046NUMERIC_CHAR = '+-.0123456789Ee'
47
48CTYPES = {TType.BOOL: 'tf',
49 TType.BYTE: 'i8',
50 TType.I16: 'i16',
51 TType.I32: 'i32',
52 TType.I64: 'i64',
53 TType.DOUBLE: 'dbl',
54 TType.STRING: 'str',
Roger Meier05ab89a2012-11-02 10:36:59 +000055 TType.STRUCT: 'rec',
Roger Meier85fb6de2012-11-02 00:05:42 +000056 TType.LIST: 'lst',
57 TType.SET: 'set',
58 TType.MAP: 'map'}
59
60JTYPES = {}
61for key in CTYPES.keys():
62 JTYPES[CTYPES[key]] = key
63
64
Roger Meierad8154a2012-12-18 21:02:16 +010065class JSONBaseContext(object):
Roger Meier85fb6de2012-11-02 00:05:42 +000066
67 def __init__(self, protocol):
68 self.protocol = protocol
69 self.first = True
70
71 def doIO(self, function):
72 pass
73
74 def write(self):
75 pass
76
77 def read(self):
78 pass
79
80 def escapeNum(self):
81 return False
82
Roger Meier0895dfe2012-12-26 22:09:55 +010083 def __str__(self):
84 return self.__class__.__name__
85
Roger Meier85fb6de2012-11-02 00:05:42 +000086
87class JSONListContext(JSONBaseContext):
88
89 def doIO(self, function):
90 if self.first is True:
91 self.first = False
92 else:
93 function(COMMA)
94
95 def write(self):
96 self.doIO(self.protocol.trans.write)
97
98 def read(self):
99 self.doIO(self.protocol.readJSONSyntaxChar)
100
101
102class JSONPairContext(JSONBaseContext):
Roger Meier0895dfe2012-12-26 22:09:55 +0100103
104 def __init__(self, protocol):
105 super(JSONPairContext, self).__init__(protocol)
106 self.colon = True
Roger Meier85fb6de2012-11-02 00:05:42 +0000107
108 def doIO(self, function):
Roger Meier0895dfe2012-12-26 22:09:55 +0100109 if self.first:
Roger Meier85fb6de2012-11-02 00:05:42 +0000110 self.first = False
111 self.colon = True
112 else:
Roger Meier0895dfe2012-12-26 22:09:55 +0100113 function(COLON if self.colon else COMMA)
Roger Meier85fb6de2012-11-02 00:05:42 +0000114 self.colon = not self.colon
115
116 def write(self):
117 self.doIO(self.protocol.trans.write)
118
119 def read(self):
120 self.doIO(self.protocol.readJSONSyntaxChar)
121
122 def escapeNum(self):
123 return self.colon
124
Roger Meier0895dfe2012-12-26 22:09:55 +0100125 def __str__(self):
126 return '%s, colon=%s' % (self.__class__.__name__, self.colon)
127
Roger Meier85fb6de2012-11-02 00:05:42 +0000128
129class LookaheadReader():
130 hasData = False
131 data = ''
132
133 def __init__(self, protocol):
134 self.protocol = protocol
135
136 def read(self):
137 if self.hasData is True:
138 self.hasData = False
139 else:
140 self.data = self.protocol.trans.read(1)
141 return self.data
142
143 def peek(self):
144 if self.hasData is False:
145 self.data = self.protocol.trans.read(1)
146 self.hasData = True
147 return self.data
148
149class TJSONProtocolBase(TProtocolBase):
150
151 def __init__(self, trans):
152 TProtocolBase.__init__(self, trans)
Roger Meierad8154a2012-12-18 21:02:16 +0100153 self.resetWriteContext()
154 self.resetReadContext()
Roger Meier85fb6de2012-11-02 00:05:42 +0000155
156 def resetWriteContext(self):
Roger Meier0895dfe2012-12-26 22:09:55 +0100157 self.context = JSONBaseContext(self)
158 self.contextStack = [self.context]
Roger Meier85fb6de2012-11-02 00:05:42 +0000159
160 def resetReadContext(self):
161 self.resetWriteContext()
162 self.reader = LookaheadReader(self)
163
164 def pushContext(self, ctx):
165 self.contextStack.append(ctx)
166 self.context = ctx
167
168 def popContext(self):
169 self.contextStack.pop()
Roger Meier0895dfe2012-12-26 22:09:55 +0100170 if self.contextStack:
171 self.context = self.contextStack[-1]
172 else:
173 self.context = JSONBaseContext(self)
Roger Meier85fb6de2012-11-02 00:05:42 +0000174
175 def writeJSONString(self, string):
176 self.context.write()
177 self.trans.write(json.dumps(string))
178
179 def writeJSONNumber(self, number):
180 self.context.write()
181 jsNumber = str(number)
182 if self.context.escapeNum():
183 jsNumber = "%s%s%s" % (QUOTE, jsNumber, QUOTE)
184 self.trans.write(jsNumber)
185
186 def writeJSONBase64(self, binary):
187 self.context.write()
188 self.trans.write(QUOTE)
189 self.trans.write(base64.b64encode(binary))
190 self.trans.write(QUOTE)
191
192 def writeJSONObjectStart(self):
193 self.context.write()
194 self.trans.write(LBRACE)
195 self.pushContext(JSONPairContext(self))
196
197 def writeJSONObjectEnd(self):
198 self.popContext()
199 self.trans.write(RBRACE)
200
201 def writeJSONArrayStart(self):
202 self.context.write()
203 self.trans.write(LBRACKET)
204 self.pushContext(JSONListContext(self))
205
206 def writeJSONArrayEnd(self):
207 self.popContext()
208 self.trans.write(RBRACKET)
209
210 def readJSONSyntaxChar(self, character):
211 current = self.reader.read()
212 if character != current:
213 raise TProtocolException(TProtocolException.INVALID_DATA,
214 "Unexpected character: %s" % current)
215
216 def readJSONString(self, skipContext):
217 string = []
218 if skipContext is False:
219 self.context.read()
220 self.readJSONSyntaxChar(QUOTE)
221 while True:
222 character = self.reader.read()
223 if character == QUOTE:
224 break
225 if character == ESCSEQ[0]:
226 character = self.reader.read()
227 if character == ESCSEQ[1]:
228 self.readJSONSyntaxChar(ZERO)
229 self.readJSONSyntaxChar(ZERO)
230 character = json.JSONDecoder().decode('"\u00%s"' % self.trans.read(2))
231 else:
Roger Meier0895dfe2012-12-26 22:09:55 +0100232 off = ESCAPE_CHAR.find(character)
Roger Meier85fb6de2012-11-02 00:05:42 +0000233 if off == -1:
234 raise TProtocolException(TProtocolException.INVALID_DATA,
235 "Expected control char")
236 character = ESCAPE_CHAR_VALS[off]
237 string.append(character)
238 return ''.join(string)
239
240 def isJSONNumeric(self, character):
241 return (True if NUMERIC_CHAR.find(character) != - 1 else False)
242
243 def readJSONQuotes(self):
244 if (self.context.escapeNum()):
245 self.readJSONSyntaxChar(QUOTE)
246
247 def readJSONNumericChars(self):
248 numeric = []
249 while True:
250 character = self.reader.peek()
251 if self.isJSONNumeric(character) is False:
252 break
253 numeric.append(self.reader.read())
254 return ''.join(numeric)
255
256 def readJSONInteger(self):
257 self.context.read()
258 self.readJSONQuotes()
259 numeric = self.readJSONNumericChars()
260 self.readJSONQuotes()
261 try:
262 return int(numeric)
263 except ValueError:
264 raise TProtocolException(TProtocolException.INVALID_DATA,
265 "Bad data encounted in numeric data")
266
267 def readJSONDouble(self):
268 self.context.read()
269 if self.reader.peek() == QUOTE:
270 string = self.readJSONString(True)
271 try:
272 double = float(string)
Roger Meier0895dfe2012-12-26 22:09:55 +0100273 if (self.context.escapeNum is False and
274 not math.isinf(double) and
275 not math.isnan(double)):
Roger Meier85fb6de2012-11-02 00:05:42 +0000276 raise TProtocolException(TProtocolException.INVALID_DATA,
277 "Numeric data unexpectedly quoted")
278 return double
279 except ValueError:
280 raise TProtocolException(TProtocolException.INVALID_DATA,
281 "Bad data encounted in numeric data")
282 else:
283 if self.context.escapeNum() is True:
284 self.readJSONSyntaxChar(QUOTE)
285 try:
286 return float(self.readJSONNumericChars())
Roger Meierad8154a2012-12-18 21:02:16 +0100287 except ValueError:
Roger Meier85fb6de2012-11-02 00:05:42 +0000288 raise TProtocolException(TProtocolException.INVALID_DATA,
289 "Bad data encounted in numeric data")
290
291 def readJSONBase64(self):
292 string = self.readJSONString(False)
293 return base64.b64decode(string)
294
295 def readJSONObjectStart(self):
296 self.context.read()
297 self.readJSONSyntaxChar(LBRACE)
298 self.pushContext(JSONPairContext(self))
299
300 def readJSONObjectEnd(self):
301 self.readJSONSyntaxChar(RBRACE)
302 self.popContext()
303
304 def readJSONArrayStart(self):
305 self.context.read()
306 self.readJSONSyntaxChar(LBRACKET)
307 self.pushContext(JSONListContext(self))
308
309 def readJSONArrayEnd(self):
310 self.readJSONSyntaxChar(RBRACKET)
311 self.popContext()
312
313
314class TJSONProtocol(TJSONProtocolBase):
315
316 def readMessageBegin(self):
317 self.resetReadContext()
318 self.readJSONArrayStart()
319 if self.readJSONInteger() != VERSION:
320 raise TProtocolException(TProtocolException.BAD_VERSION,
321 "Message contained bad version.")
322 name = self.readJSONString(False)
323 typen = self.readJSONInteger()
324 seqid = self.readJSONInteger()
325 return (name, typen, seqid)
326
327 def readMessageEnd(self):
328 self.readJSONArrayEnd()
329
330 def readStructBegin(self):
331 self.readJSONObjectStart()
332
333 def readStructEnd(self):
334 self.readJSONObjectEnd()
335
336 def readFieldBegin(self):
337 character = self.reader.peek()
Roger Meierad8154a2012-12-18 21:02:16 +0100338 ttype = 0
Roger Meier85fb6de2012-11-02 00:05:42 +0000339 id = 0
340 if character == RBRACE:
Roger Meierad8154a2012-12-18 21:02:16 +0100341 ttype = TType.STOP
Roger Meier85fb6de2012-11-02 00:05:42 +0000342 else:
343 id = self.readJSONInteger()
344 self.readJSONObjectStart()
Roger Meierad8154a2012-12-18 21:02:16 +0100345 ttype = JTYPES[self.readJSONString(False)]
346 return (None, ttype, id)
Roger Meier85fb6de2012-11-02 00:05:42 +0000347
348 def readFieldEnd(self):
349 self.readJSONObjectEnd()
350
351 def readMapBegin(self):
352 self.readJSONArrayStart()
353 keyType = JTYPES[self.readJSONString(False)]
354 valueType = JTYPES[self.readJSONString(False)]
355 size = self.readJSONInteger()
356 self.readJSONObjectStart()
357 return (keyType, valueType, size)
358
359 def readMapEnd(self):
360 self.readJSONObjectEnd()
361 self.readJSONArrayEnd()
362
363 def readCollectionBegin(self):
364 self.readJSONArrayStart()
365 elemType = JTYPES[self.readJSONString(False)]
366 size = self.readJSONInteger()
Roger Meierad8154a2012-12-18 21:02:16 +0100367 return (elemType, size)
Roger Meier85fb6de2012-11-02 00:05:42 +0000368 readListBegin = readCollectionBegin
369 readSetBegin = readCollectionBegin
370
371 def readCollectionEnd(self):
372 self.readJSONArrayEnd()
373 readSetEnd = readCollectionEnd
374 readListEnd = readCollectionEnd
375
376 def readBool(self):
377 return (False if self.readJSONInteger() == 0 else True)
378
379 def readNumber(self):
380 return self.readJSONInteger()
381 readByte = readNumber
382 readI16 = readNumber
383 readI32 = readNumber
384 readI64 = readNumber
385
386 def readDouble(self):
387 return self.readJSONDouble()
388
389 def readString(self):
390 return self.readJSONString(False)
391
392 def readBinary(self):
393 return self.readJSONBase64()
394
395 def writeMessageBegin(self, name, request_type, seqid):
396 self.resetWriteContext()
397 self.writeJSONArrayStart()
398 self.writeJSONNumber(VERSION)
399 self.writeJSONString(name)
400 self.writeJSONNumber(request_type)
401 self.writeJSONNumber(seqid)
402
403 def writeMessageEnd(self):
404 self.writeJSONArrayEnd()
405
406 def writeStructBegin(self, name):
407 self.writeJSONObjectStart()
408
409 def writeStructEnd(self):
410 self.writeJSONObjectEnd()
411
Roger Meierad8154a2012-12-18 21:02:16 +0100412 def writeFieldBegin(self, name, ttype, id):
Roger Meier85fb6de2012-11-02 00:05:42 +0000413 self.writeJSONNumber(id)
414 self.writeJSONObjectStart()
Roger Meierad8154a2012-12-18 21:02:16 +0100415 self.writeJSONString(CTYPES[ttype])
Roger Meier85fb6de2012-11-02 00:05:42 +0000416
417 def writeFieldEnd(self):
418 self.writeJSONObjectEnd()
419
420 def writeFieldStop(self):
421 pass
422
423 def writeMapBegin(self, ktype, vtype, size):
424 self.writeJSONArrayStart()
425 self.writeJSONString(CTYPES[ktype])
426 self.writeJSONString(CTYPES[vtype])
427 self.writeJSONNumber(size)
428 self.writeJSONObjectStart()
429
430 def writeMapEnd(self):
431 self.writeJSONObjectEnd()
432 self.writeJSONArrayEnd()
433
434 def writeListBegin(self, etype, size):
435 self.writeJSONArrayStart()
436 self.writeJSONString(CTYPES[etype])
437 self.writeJSONNumber(size)
438
439 def writeListEnd(self):
440 self.writeJSONArrayEnd()
441
442 def writeSetBegin(self, etype, size):
443 self.writeJSONArrayStart()
444 self.writeJSONString(CTYPES[etype])
445 self.writeJSONNumber(size)
446
447 def writeSetEnd(self):
448 self.writeJSONArrayEnd()
449
450 def writeBool(self, boolean):
451 self.writeJSONNumber(1 if boolean is True else 0)
452
Konrad Grochowski93fea152014-10-02 16:29:14 +0200453 def writeByte(self, byte):
454 checkIntegerLimits(byte, 8)
455 self.writeJSONNumber(byte)
456
457 def writeI16(self, i16):
458 checkIntegerLimits(i16, 16)
459 self.writeJSONNumber(i16)
460
461 def writeI32(self, i32):
462 checkIntegerLimits(i32, 32)
463 self.writeJSONNumber(i32)
464
465 def writeI64(self, i64):
466 checkIntegerLimits(i64, 64)
467 self.writeJSONNumber(i64)
Roger Meier85fb6de2012-11-02 00:05:42 +0000468
469 def writeDouble(self, dbl):
470 self.writeJSONNumber(dbl)
471
472 def writeString(self, string):
473 self.writeJSONString(string)
474
475 def writeBinary(self, binary):
476 self.writeJSONBase64(binary)
477
Roger Meier0895dfe2012-12-26 22:09:55 +0100478
Roger Meier85fb6de2012-11-02 00:05:42 +0000479class TJSONProtocolFactory:
Roger Meier85fb6de2012-11-02 00:05:42 +0000480
481 def getProtocol(self, trans):
482 return TJSONProtocol(trans)
Roger Meier0895dfe2012-12-26 22:09:55 +0100483
484
485class TSimpleJSONProtocol(TJSONProtocolBase):
486 """Simple, readable, write-only JSON protocol.
487
488 Useful for interacting with scripting languages.
489 """
490
491 def readMessageBegin(self):
492 raise NotImplementedError()
493
494 def readMessageEnd(self):
495 raise NotImplementedError()
496
497 def readStructBegin(self):
498 raise NotImplementedError()
499
500 def readStructEnd(self):
501 raise NotImplementedError()
502
503 def writeMessageBegin(self, name, request_type, seqid):
504 self.resetWriteContext()
505
506 def writeMessageEnd(self):
507 pass
508
509 def writeStructBegin(self, name):
510 self.writeJSONObjectStart()
511
512 def writeStructEnd(self):
513 self.writeJSONObjectEnd()
514
515 def writeFieldBegin(self, name, ttype, fid):
516 self.writeJSONString(name)
517
518 def writeFieldEnd(self):
519 pass
520
521 def writeMapBegin(self, ktype, vtype, size):
522 self.writeJSONObjectStart()
523
524 def writeMapEnd(self):
525 self.writeJSONObjectEnd()
526
527 def _writeCollectionBegin(self, etype, size):
528 self.writeJSONArrayStart()
529
530 def _writeCollectionEnd(self):
531 self.writeJSONArrayEnd()
532 writeListBegin = _writeCollectionBegin
533 writeListEnd = _writeCollectionEnd
534 writeSetBegin = _writeCollectionBegin
535 writeSetEnd = _writeCollectionEnd
536
Konrad Grochowski93fea152014-10-02 16:29:14 +0200537 def writeByte(self, byte):
538 checkIntegerLimits(byte, 8)
539 self.writeJSONNumber(byte)
540
541 def writeI16(self, i16):
542 checkIntegerLimits(i16, 16)
543 self.writeJSONNumber(i16)
544
545 def writeI32(self, i32):
546 checkIntegerLimits(i32, 32)
547 self.writeJSONNumber(i32)
548
549 def writeI64(self, i64):
550 checkIntegerLimits(i64, 64)
551 self.writeJSONNumber(i64)
Roger Meier0895dfe2012-12-26 22:09:55 +0100552
553 def writeBool(self, boolean):
554 self.writeJSONNumber(1 if boolean is True else 0)
555
556 def writeDouble(self, dbl):
557 self.writeJSONNumber(dbl)
558
559 def writeString(self, string):
560 self.writeJSONString(string)
561
562 def writeBinary(self, binary):
563 self.writeJSONBase64(binary)
564
565
566class TSimpleJSONProtocolFactory(object):
567
568 def getProtocol(self, trans):
569 return TSimpleJSONProtocol(trans)