blob: 3048197d425e3772f2d251f2982932faae68dc51 [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
Roger Meier0895dfe2012-12-26 22:09:55 +010020from TProtocol import TType, TProtocolBase, TProtocolException
21import base64
22import json
23import math
Roger Meier85fb6de2012-11-02 00:05:42 +000024
Roger Meier0895dfe2012-12-26 22:09:55 +010025__all__ = ['TJSONProtocol',
26 'TJSONProtocolFactory',
27 'TSimpleJSONProtocol',
28 'TSimpleJSONProtocolFactory']
Roger Meier85fb6de2012-11-02 00:05:42 +000029
30VERSION = 1
31
32COMMA = ','
33COLON = ':'
34LBRACE = '{'
35RBRACE = '}'
36LBRACKET = '['
37RBRACKET = ']'
38QUOTE = '"'
39BACKSLASH = '\\'
40ZERO = '0'
41
42ESCSEQ = '\\u00'
43ESCAPE_CHAR = '"\\bfnrt'
44ESCAPE_CHAR_VALS = ['"', '\\', '\b', '\f', '\n', '\r', '\t']
45NUMERIC_CHAR = '+-.0123456789Ee'
46
47CTYPES = {TType.BOOL: 'tf',
48 TType.BYTE: 'i8',
49 TType.I16: 'i16',
50 TType.I32: 'i32',
51 TType.I64: 'i64',
52 TType.DOUBLE: 'dbl',
53 TType.STRING: 'str',
Roger Meier05ab89a2012-11-02 10:36:59 +000054 TType.STRUCT: 'rec',
Roger Meier85fb6de2012-11-02 00:05:42 +000055 TType.LIST: 'lst',
56 TType.SET: 'set',
57 TType.MAP: 'map'}
58
59JTYPES = {}
60for key in CTYPES.keys():
61 JTYPES[CTYPES[key]] = key
62
63
Roger Meierad8154a2012-12-18 21:02:16 +010064class JSONBaseContext(object):
Roger Meier85fb6de2012-11-02 00:05:42 +000065
66 def __init__(self, protocol):
67 self.protocol = protocol
68 self.first = True
69
70 def doIO(self, function):
71 pass
72
73 def write(self):
74 pass
75
76 def read(self):
77 pass
78
79 def escapeNum(self):
80 return False
81
Roger Meier0895dfe2012-12-26 22:09:55 +010082 def __str__(self):
83 return self.__class__.__name__
84
Roger Meier85fb6de2012-11-02 00:05:42 +000085
86class JSONListContext(JSONBaseContext):
87
88 def doIO(self, function):
89 if self.first is True:
90 self.first = False
91 else:
92 function(COMMA)
93
94 def write(self):
95 self.doIO(self.protocol.trans.write)
96
97 def read(self):
98 self.doIO(self.protocol.readJSONSyntaxChar)
99
100
101class JSONPairContext(JSONBaseContext):
Roger Meier0895dfe2012-12-26 22:09:55 +0100102
103 def __init__(self, protocol):
104 super(JSONPairContext, self).__init__(protocol)
105 self.colon = True
Roger Meier85fb6de2012-11-02 00:05:42 +0000106
107 def doIO(self, function):
Roger Meier0895dfe2012-12-26 22:09:55 +0100108 if self.first:
Roger Meier85fb6de2012-11-02 00:05:42 +0000109 self.first = False
110 self.colon = True
111 else:
Roger Meier0895dfe2012-12-26 22:09:55 +0100112 function(COLON if self.colon else COMMA)
Roger Meier85fb6de2012-11-02 00:05:42 +0000113 self.colon = not self.colon
114
115 def write(self):
116 self.doIO(self.protocol.trans.write)
117
118 def read(self):
119 self.doIO(self.protocol.readJSONSyntaxChar)
120
121 def escapeNum(self):
122 return self.colon
123
Roger Meier0895dfe2012-12-26 22:09:55 +0100124 def __str__(self):
125 return '%s, colon=%s' % (self.__class__.__name__, self.colon)
126
Roger Meier85fb6de2012-11-02 00:05:42 +0000127
128class LookaheadReader():
129 hasData = False
130 data = ''
131
132 def __init__(self, protocol):
133 self.protocol = protocol
134
135 def read(self):
136 if self.hasData is True:
137 self.hasData = False
138 else:
139 self.data = self.protocol.trans.read(1)
140 return self.data
141
142 def peek(self):
143 if self.hasData is False:
144 self.data = self.protocol.trans.read(1)
145 self.hasData = True
146 return self.data
147
148class TJSONProtocolBase(TProtocolBase):
149
150 def __init__(self, trans):
151 TProtocolBase.__init__(self, trans)
Roger Meierad8154a2012-12-18 21:02:16 +0100152 self.resetWriteContext()
153 self.resetReadContext()
Roger Meier85fb6de2012-11-02 00:05:42 +0000154
155 def resetWriteContext(self):
Roger Meier0895dfe2012-12-26 22:09:55 +0100156 self.context = JSONBaseContext(self)
157 self.contextStack = [self.context]
Roger Meier85fb6de2012-11-02 00:05:42 +0000158
159 def resetReadContext(self):
160 self.resetWriteContext()
161 self.reader = LookaheadReader(self)
162
163 def pushContext(self, ctx):
164 self.contextStack.append(ctx)
165 self.context = ctx
166
167 def popContext(self):
168 self.contextStack.pop()
Roger Meier0895dfe2012-12-26 22:09:55 +0100169 if self.contextStack:
170 self.context = self.contextStack[-1]
171 else:
172 self.context = JSONBaseContext(self)
Roger Meier85fb6de2012-11-02 00:05:42 +0000173
174 def writeJSONString(self, string):
175 self.context.write()
176 self.trans.write(json.dumps(string))
177
178 def writeJSONNumber(self, number):
179 self.context.write()
180 jsNumber = str(number)
181 if self.context.escapeNum():
182 jsNumber = "%s%s%s" % (QUOTE, jsNumber, QUOTE)
183 self.trans.write(jsNumber)
184
185 def writeJSONBase64(self, binary):
186 self.context.write()
187 self.trans.write(QUOTE)
188 self.trans.write(base64.b64encode(binary))
189 self.trans.write(QUOTE)
190
191 def writeJSONObjectStart(self):
192 self.context.write()
193 self.trans.write(LBRACE)
194 self.pushContext(JSONPairContext(self))
195
196 def writeJSONObjectEnd(self):
197 self.popContext()
198 self.trans.write(RBRACE)
199
200 def writeJSONArrayStart(self):
201 self.context.write()
202 self.trans.write(LBRACKET)
203 self.pushContext(JSONListContext(self))
204
205 def writeJSONArrayEnd(self):
206 self.popContext()
207 self.trans.write(RBRACKET)
208
209 def readJSONSyntaxChar(self, character):
210 current = self.reader.read()
211 if character != current:
212 raise TProtocolException(TProtocolException.INVALID_DATA,
213 "Unexpected character: %s" % current)
214
215 def readJSONString(self, skipContext):
216 string = []
217 if skipContext is False:
218 self.context.read()
219 self.readJSONSyntaxChar(QUOTE)
220 while True:
221 character = self.reader.read()
222 if character == QUOTE:
223 break
224 if character == ESCSEQ[0]:
225 character = self.reader.read()
226 if character == ESCSEQ[1]:
227 self.readJSONSyntaxChar(ZERO)
228 self.readJSONSyntaxChar(ZERO)
229 character = json.JSONDecoder().decode('"\u00%s"' % self.trans.read(2))
230 else:
Roger Meier0895dfe2012-12-26 22:09:55 +0100231 off = ESCAPE_CHAR.find(character)
Roger Meier85fb6de2012-11-02 00:05:42 +0000232 if off == -1:
233 raise TProtocolException(TProtocolException.INVALID_DATA,
234 "Expected control char")
235 character = ESCAPE_CHAR_VALS[off]
236 string.append(character)
237 return ''.join(string)
238
239 def isJSONNumeric(self, character):
240 return (True if NUMERIC_CHAR.find(character) != - 1 else False)
241
242 def readJSONQuotes(self):
243 if (self.context.escapeNum()):
244 self.readJSONSyntaxChar(QUOTE)
245
246 def readJSONNumericChars(self):
247 numeric = []
248 while True:
249 character = self.reader.peek()
250 if self.isJSONNumeric(character) is False:
251 break
252 numeric.append(self.reader.read())
253 return ''.join(numeric)
254
255 def readJSONInteger(self):
256 self.context.read()
257 self.readJSONQuotes()
258 numeric = self.readJSONNumericChars()
259 self.readJSONQuotes()
260 try:
261 return int(numeric)
262 except ValueError:
263 raise TProtocolException(TProtocolException.INVALID_DATA,
264 "Bad data encounted in numeric data")
265
266 def readJSONDouble(self):
267 self.context.read()
268 if self.reader.peek() == QUOTE:
269 string = self.readJSONString(True)
270 try:
271 double = float(string)
Roger Meier0895dfe2012-12-26 22:09:55 +0100272 if (self.context.escapeNum is False and
273 not math.isinf(double) and
274 not math.isnan(double)):
Roger Meier85fb6de2012-11-02 00:05:42 +0000275 raise TProtocolException(TProtocolException.INVALID_DATA,
276 "Numeric data unexpectedly quoted")
277 return double
278 except ValueError:
279 raise TProtocolException(TProtocolException.INVALID_DATA,
280 "Bad data encounted in numeric data")
281 else:
282 if self.context.escapeNum() is True:
283 self.readJSONSyntaxChar(QUOTE)
284 try:
285 return float(self.readJSONNumericChars())
Roger Meierad8154a2012-12-18 21:02:16 +0100286 except ValueError:
Roger Meier85fb6de2012-11-02 00:05:42 +0000287 raise TProtocolException(TProtocolException.INVALID_DATA,
288 "Bad data encounted in numeric data")
289
290 def readJSONBase64(self):
291 string = self.readJSONString(False)
292 return base64.b64decode(string)
293
294 def readJSONObjectStart(self):
295 self.context.read()
296 self.readJSONSyntaxChar(LBRACE)
297 self.pushContext(JSONPairContext(self))
298
299 def readJSONObjectEnd(self):
300 self.readJSONSyntaxChar(RBRACE)
301 self.popContext()
302
303 def readJSONArrayStart(self):
304 self.context.read()
305 self.readJSONSyntaxChar(LBRACKET)
306 self.pushContext(JSONListContext(self))
307
308 def readJSONArrayEnd(self):
309 self.readJSONSyntaxChar(RBRACKET)
310 self.popContext()
311
312
313class TJSONProtocol(TJSONProtocolBase):
314
315 def readMessageBegin(self):
316 self.resetReadContext()
317 self.readJSONArrayStart()
318 if self.readJSONInteger() != VERSION:
319 raise TProtocolException(TProtocolException.BAD_VERSION,
320 "Message contained bad version.")
321 name = self.readJSONString(False)
322 typen = self.readJSONInteger()
323 seqid = self.readJSONInteger()
324 return (name, typen, seqid)
325
326 def readMessageEnd(self):
327 self.readJSONArrayEnd()
328
329 def readStructBegin(self):
330 self.readJSONObjectStart()
331
332 def readStructEnd(self):
333 self.readJSONObjectEnd()
334
335 def readFieldBegin(self):
336 character = self.reader.peek()
Roger Meierad8154a2012-12-18 21:02:16 +0100337 ttype = 0
Roger Meier85fb6de2012-11-02 00:05:42 +0000338 id = 0
339 if character == RBRACE:
Roger Meierad8154a2012-12-18 21:02:16 +0100340 ttype = TType.STOP
Roger Meier85fb6de2012-11-02 00:05:42 +0000341 else:
342 id = self.readJSONInteger()
343 self.readJSONObjectStart()
Roger Meierad8154a2012-12-18 21:02:16 +0100344 ttype = JTYPES[self.readJSONString(False)]
345 return (None, ttype, id)
Roger Meier85fb6de2012-11-02 00:05:42 +0000346
347 def readFieldEnd(self):
348 self.readJSONObjectEnd()
349
350 def readMapBegin(self):
351 self.readJSONArrayStart()
352 keyType = JTYPES[self.readJSONString(False)]
353 valueType = JTYPES[self.readJSONString(False)]
354 size = self.readJSONInteger()
355 self.readJSONObjectStart()
356 return (keyType, valueType, size)
357
358 def readMapEnd(self):
359 self.readJSONObjectEnd()
360 self.readJSONArrayEnd()
361
362 def readCollectionBegin(self):
363 self.readJSONArrayStart()
364 elemType = JTYPES[self.readJSONString(False)]
365 size = self.readJSONInteger()
Roger Meierad8154a2012-12-18 21:02:16 +0100366 return (elemType, size)
Roger Meier85fb6de2012-11-02 00:05:42 +0000367 readListBegin = readCollectionBegin
368 readSetBegin = readCollectionBegin
369
370 def readCollectionEnd(self):
371 self.readJSONArrayEnd()
372 readSetEnd = readCollectionEnd
373 readListEnd = readCollectionEnd
374
375 def readBool(self):
376 return (False if self.readJSONInteger() == 0 else True)
377
378 def readNumber(self):
379 return self.readJSONInteger()
380 readByte = readNumber
381 readI16 = readNumber
382 readI32 = readNumber
383 readI64 = readNumber
384
385 def readDouble(self):
386 return self.readJSONDouble()
387
388 def readString(self):
389 return self.readJSONString(False)
390
391 def readBinary(self):
392 return self.readJSONBase64()
393
394 def writeMessageBegin(self, name, request_type, seqid):
395 self.resetWriteContext()
396 self.writeJSONArrayStart()
397 self.writeJSONNumber(VERSION)
398 self.writeJSONString(name)
399 self.writeJSONNumber(request_type)
400 self.writeJSONNumber(seqid)
401
402 def writeMessageEnd(self):
403 self.writeJSONArrayEnd()
404
405 def writeStructBegin(self, name):
406 self.writeJSONObjectStart()
407
408 def writeStructEnd(self):
409 self.writeJSONObjectEnd()
410
Roger Meierad8154a2012-12-18 21:02:16 +0100411 def writeFieldBegin(self, name, ttype, id):
Roger Meier85fb6de2012-11-02 00:05:42 +0000412 self.writeJSONNumber(id)
413 self.writeJSONObjectStart()
Roger Meierad8154a2012-12-18 21:02:16 +0100414 self.writeJSONString(CTYPES[ttype])
Roger Meier85fb6de2012-11-02 00:05:42 +0000415
416 def writeFieldEnd(self):
417 self.writeJSONObjectEnd()
418
419 def writeFieldStop(self):
420 pass
421
422 def writeMapBegin(self, ktype, vtype, size):
423 self.writeJSONArrayStart()
424 self.writeJSONString(CTYPES[ktype])
425 self.writeJSONString(CTYPES[vtype])
426 self.writeJSONNumber(size)
427 self.writeJSONObjectStart()
428
429 def writeMapEnd(self):
430 self.writeJSONObjectEnd()
431 self.writeJSONArrayEnd()
432
433 def writeListBegin(self, etype, size):
434 self.writeJSONArrayStart()
435 self.writeJSONString(CTYPES[etype])
436 self.writeJSONNumber(size)
437
438 def writeListEnd(self):
439 self.writeJSONArrayEnd()
440
441 def writeSetBegin(self, etype, size):
442 self.writeJSONArrayStart()
443 self.writeJSONString(CTYPES[etype])
444 self.writeJSONNumber(size)
445
446 def writeSetEnd(self):
447 self.writeJSONArrayEnd()
448
449 def writeBool(self, boolean):
450 self.writeJSONNumber(1 if boolean is True else 0)
451
452 def writeInteger(self, integer):
453 self.writeJSONNumber(integer)
454 writeByte = writeInteger
455 writeI16 = writeInteger
456 writeI32 = writeInteger
457 writeI64 = writeInteger
458
459 def writeDouble(self, dbl):
460 self.writeJSONNumber(dbl)
461
462 def writeString(self, string):
463 self.writeJSONString(string)
464
465 def writeBinary(self, binary):
466 self.writeJSONBase64(binary)
467
Roger Meier0895dfe2012-12-26 22:09:55 +0100468
Roger Meier85fb6de2012-11-02 00:05:42 +0000469class TJSONProtocolFactory:
Roger Meier85fb6de2012-11-02 00:05:42 +0000470
471 def getProtocol(self, trans):
472 return TJSONProtocol(trans)
Roger Meier0895dfe2012-12-26 22:09:55 +0100473
474
475class TSimpleJSONProtocol(TJSONProtocolBase):
476 """Simple, readable, write-only JSON protocol.
477
478 Useful for interacting with scripting languages.
479 """
480
481 def readMessageBegin(self):
482 raise NotImplementedError()
483
484 def readMessageEnd(self):
485 raise NotImplementedError()
486
487 def readStructBegin(self):
488 raise NotImplementedError()
489
490 def readStructEnd(self):
491 raise NotImplementedError()
492
493 def writeMessageBegin(self, name, request_type, seqid):
494 self.resetWriteContext()
495
496 def writeMessageEnd(self):
497 pass
498
499 def writeStructBegin(self, name):
500 self.writeJSONObjectStart()
501
502 def writeStructEnd(self):
503 self.writeJSONObjectEnd()
504
505 def writeFieldBegin(self, name, ttype, fid):
506 self.writeJSONString(name)
507
508 def writeFieldEnd(self):
509 pass
510
511 def writeMapBegin(self, ktype, vtype, size):
512 self.writeJSONObjectStart()
513
514 def writeMapEnd(self):
515 self.writeJSONObjectEnd()
516
517 def _writeCollectionBegin(self, etype, size):
518 self.writeJSONArrayStart()
519
520 def _writeCollectionEnd(self):
521 self.writeJSONArrayEnd()
522 writeListBegin = _writeCollectionBegin
523 writeListEnd = _writeCollectionEnd
524 writeSetBegin = _writeCollectionBegin
525 writeSetEnd = _writeCollectionEnd
526
527 def writeInteger(self, integer):
528 self.writeJSONNumber(integer)
529 writeByte = writeInteger
530 writeI16 = writeInteger
531 writeI32 = writeInteger
532 writeI64 = writeInteger
533
534 def writeBool(self, boolean):
535 self.writeJSONNumber(1 if boolean is True else 0)
536
537 def writeDouble(self, dbl):
538 self.writeJSONNumber(dbl)
539
540 def writeString(self, string):
541 self.writeJSONString(string)
542
543 def writeBinary(self, binary):
544 self.writeJSONBase64(binary)
545
546
547class TSimpleJSONProtocolFactory(object):
548
549 def getProtocol(self, trans):
550 return TSimpleJSONProtocol(trans)