tiny_thrift 1.0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (90) hide show
  1. checksums.yaml +7 -0
  2. data/README +43 -0
  3. data/benchmark/Benchmark.thrift +24 -0
  4. data/benchmark/benchmark.rb +271 -0
  5. data/benchmark/client.rb +74 -0
  6. data/benchmark/server.rb +82 -0
  7. data/benchmark/thin_server.rb +44 -0
  8. data/ext/binary_protocol_accelerated.c +460 -0
  9. data/ext/binary_protocol_accelerated.h +20 -0
  10. data/ext/bytes.c +36 -0
  11. data/ext/bytes.h +31 -0
  12. data/ext/compact_protocol.c +635 -0
  13. data/ext/compact_protocol.h +20 -0
  14. data/ext/constants.h +96 -0
  15. data/ext/extconf.rb +32 -0
  16. data/ext/macros.h +41 -0
  17. data/ext/memory_buffer.c +134 -0
  18. data/ext/memory_buffer.h +20 -0
  19. data/ext/protocol.c +0 -0
  20. data/ext/protocol.h +0 -0
  21. data/ext/strlcpy.c +41 -0
  22. data/ext/strlcpy.h +34 -0
  23. data/ext/struct.c +688 -0
  24. data/ext/struct.h +25 -0
  25. data/ext/thrift_native.c +195 -0
  26. data/lib/thrift.rb +66 -0
  27. data/lib/thrift/bytes.rb +131 -0
  28. data/lib/thrift/client.rb +62 -0
  29. data/lib/thrift/core_ext.rb +23 -0
  30. data/lib/thrift/core_ext/fixnum.rb +29 -0
  31. data/lib/thrift/exceptions.rb +87 -0
  32. data/lib/thrift/processor.rb +57 -0
  33. data/lib/thrift/protocol/base_protocol.rb +377 -0
  34. data/lib/thrift/protocol/binary_protocol.rb +237 -0
  35. data/lib/thrift/protocol/binary_protocol_accelerated.rb +39 -0
  36. data/lib/thrift/protocol/compact_protocol.rb +434 -0
  37. data/lib/thrift/protocol/json_protocol.rb +769 -0
  38. data/lib/thrift/serializer/deserializer.rb +33 -0
  39. data/lib/thrift/serializer/serializer.rb +34 -0
  40. data/lib/thrift/server/base_server.rb +31 -0
  41. data/lib/thrift/server/mongrel_http_server.rb +60 -0
  42. data/lib/thrift/server/nonblocking_server.rb +305 -0
  43. data/lib/thrift/server/simple_server.rb +43 -0
  44. data/lib/thrift/server/thin_http_server.rb +91 -0
  45. data/lib/thrift/server/thread_pool_server.rb +75 -0
  46. data/lib/thrift/server/threaded_server.rb +47 -0
  47. data/lib/thrift/struct.rb +237 -0
  48. data/lib/thrift/struct_union.rb +192 -0
  49. data/lib/thrift/thrift_native.rb +24 -0
  50. data/lib/thrift/transport/base_server_transport.rb +37 -0
  51. data/lib/thrift/transport/base_transport.rb +109 -0
  52. data/lib/thrift/transport/buffered_transport.rb +114 -0
  53. data/lib/thrift/transport/framed_transport.rb +117 -0
  54. data/lib/thrift/transport/http_client_transport.rb +56 -0
  55. data/lib/thrift/transport/io_stream_transport.rb +39 -0
  56. data/lib/thrift/transport/memory_buffer_transport.rb +125 -0
  57. data/lib/thrift/transport/server_socket.rb +63 -0
  58. data/lib/thrift/transport/socket.rb +139 -0
  59. data/lib/thrift/transport/unix_server_socket.rb +60 -0
  60. data/lib/thrift/transport/unix_socket.rb +40 -0
  61. data/lib/thrift/types.rb +101 -0
  62. data/lib/thrift/union.rb +179 -0
  63. data/lib/tiny_thrift.rb +1 -0
  64. data/spec/ThriftSpec.thrift +183 -0
  65. data/spec/base_protocol_spec.rb +217 -0
  66. data/spec/base_transport_spec.rb +350 -0
  67. data/spec/binary_protocol_accelerated_spec.rb +42 -0
  68. data/spec/binary_protocol_spec.rb +66 -0
  69. data/spec/binary_protocol_spec_shared.rb +455 -0
  70. data/spec/bytes_spec.rb +160 -0
  71. data/spec/client_spec.rb +99 -0
  72. data/spec/compact_protocol_spec.rb +143 -0
  73. data/spec/exception_spec.rb +141 -0
  74. data/spec/http_client_spec.rb +120 -0
  75. data/spec/json_protocol_spec.rb +513 -0
  76. data/spec/nonblocking_server_spec.rb +263 -0
  77. data/spec/processor_spec.rb +80 -0
  78. data/spec/serializer_spec.rb +67 -0
  79. data/spec/server_socket_spec.rb +79 -0
  80. data/spec/server_spec.rb +147 -0
  81. data/spec/socket_spec.rb +61 -0
  82. data/spec/socket_spec_shared.rb +104 -0
  83. data/spec/spec_helper.rb +61 -0
  84. data/spec/struct_nested_containers_spec.rb +191 -0
  85. data/spec/struct_spec.rb +293 -0
  86. data/spec/thin_http_server_spec.rb +141 -0
  87. data/spec/types_spec.rb +115 -0
  88. data/spec/union_spec.rb +203 -0
  89. data/spec/unix_socket_spec.rb +107 -0
  90. metadata +313 -0
@@ -0,0 +1,769 @@
1
+ # encoding: UTF-8
2
+ #
3
+ # Licensed to the Apache Software Foundation (ASF) under one
4
+ # or more contributor license agreements. See the NOTICE file
5
+ # distributed with this work for additional information
6
+ # regarding copyright ownership. The ASF licenses this file
7
+ # to you under the Apache License, Version 2.0 (the
8
+ # "License"); you may not use this file except in compliance
9
+ # with the License. You may obtain a copy of the License at
10
+ #
11
+ # http://www.apache.org/licenses/LICENSE-2.0
12
+ #
13
+ # Unless required by applicable law or agreed to in writing,
14
+ # software distributed under the License is distributed on an
15
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16
+ # KIND, either express or implied. See the License for the
17
+ # specific language governing permissions and limitations
18
+ # under the License.
19
+ #
20
+
21
+
22
+ module Thrift
23
+ class LookaheadReader
24
+ def initialize(trans)
25
+ @trans = trans
26
+ @hasData = false
27
+ @data = nil
28
+ end
29
+
30
+ def read
31
+ if @hasData
32
+ @hasData = false
33
+ else
34
+ @data = @trans.read(1)
35
+ end
36
+
37
+ return @data
38
+ end
39
+
40
+ def peek
41
+ if !@hasData
42
+ @data = @trans.read(1)
43
+ end
44
+ @hasData = true
45
+ return @data
46
+ end
47
+ end
48
+
49
+ #
50
+ # Class to serve as base JSON context and as base class for other context
51
+ # implementations
52
+ #
53
+ class JSONContext
54
+ @@kJSONElemSeparator = ','
55
+ #
56
+ # Write context data to the trans. Default is to do nothing.
57
+ #
58
+ def write(trans)
59
+ end
60
+
61
+ #
62
+ # Read context data from the trans. Default is to do nothing.
63
+ #
64
+ def read(reader)
65
+ end
66
+
67
+ #
68
+ # Return true if numbers need to be escaped as strings in this context.
69
+ # Default behavior is to return false.
70
+ #
71
+ def escapeNum
72
+ return false
73
+ end
74
+ end
75
+
76
+ # Context class for object member key-value pairs
77
+ class JSONPairContext < JSONContext
78
+ @@kJSONPairSeparator = ':'
79
+
80
+ def initialize
81
+ @first = true
82
+ @colon = true
83
+ end
84
+
85
+ def write(trans)
86
+ if (@first)
87
+ @first = false
88
+ @colon = true
89
+ else
90
+ trans.write(@colon ? @@kJSONPairSeparator : @@kJSONElemSeparator)
91
+ @colon = !@colon
92
+ end
93
+ end
94
+
95
+ def read(reader)
96
+ if (@first)
97
+ @first = false
98
+ @colon = true
99
+ else
100
+ ch = (@colon ? @@kJSONPairSeparator : @@kJSONElemSeparator)
101
+ @colon = !@colon
102
+ JsonProtocol::read_syntax_char(reader, ch)
103
+ end
104
+ end
105
+
106
+ # Numbers must be turned into strings if they are the key part of a pair
107
+ def escapeNum
108
+ return @colon
109
+ end
110
+ end
111
+
112
+ # Context class for lists
113
+ class JSONListContext < JSONContext
114
+
115
+ def initialize
116
+ @first = true
117
+ end
118
+
119
+ def write(trans)
120
+ if (@first)
121
+ @first = false
122
+ else
123
+ trans.write(@@kJSONElemSeparator)
124
+ end
125
+ end
126
+
127
+ def read(reader)
128
+ if (@first)
129
+ @first = false
130
+ else
131
+ JsonProtocol::read_syntax_char(reader, @@kJSONElemSeparator)
132
+ end
133
+ end
134
+ end
135
+
136
+ class JsonProtocol < BaseProtocol
137
+
138
+ @@kJSONObjectStart = '{'
139
+ @@kJSONObjectEnd = '}'
140
+ @@kJSONArrayStart = '['
141
+ @@kJSONArrayEnd = ']'
142
+ @@kJSONNewline = '\n'
143
+ @@kJSONBackslash = '\\'
144
+ @@kJSONStringDelimiter = '"'
145
+
146
+ @@kThriftVersion1 = 1
147
+
148
+ @@kThriftNan = "NaN"
149
+ @@kThriftInfinity = "Infinity"
150
+ @@kThriftNegativeInfinity = "-Infinity"
151
+
152
+ def initialize(trans)
153
+ super(trans)
154
+ @context = JSONContext.new
155
+ @contexts = Array.new
156
+ @reader = LookaheadReader.new(trans)
157
+ end
158
+
159
+ def get_type_name_for_type_id(id)
160
+ case id
161
+ when Types::BOOL
162
+ "tf"
163
+ when Types::BYTE
164
+ "i8"
165
+ when Types::I16
166
+ "i16"
167
+ when Types::I32
168
+ "i32"
169
+ when Types::I64
170
+ "i64"
171
+ when Types::DOUBLE
172
+ "dbl"
173
+ when Types::STRING
174
+ "str"
175
+ when Types::STRUCT
176
+ "rec"
177
+ when Types::MAP
178
+ "map"
179
+ when Types::SET
180
+ "set"
181
+ when Types::LIST
182
+ "lst"
183
+ else
184
+ raise NotImplementedError
185
+ end
186
+ end
187
+
188
+ def get_type_id_for_type_name(name)
189
+ if (name == "tf")
190
+ result = Types::BOOL
191
+ elsif (name == "i8")
192
+ result = Types::BYTE
193
+ elsif (name == "i16")
194
+ result = Types::I16
195
+ elsif (name == "i32")
196
+ result = Types::I32
197
+ elsif (name == "i64")
198
+ result = Types::I64
199
+ elsif (name == "dbl")
200
+ result = Types::DOUBLE
201
+ elsif (name == "str")
202
+ result = Types::STRING
203
+ elsif (name == "rec")
204
+ result = Types::STRUCT
205
+ elsif (name == "map")
206
+ result = Types::MAP
207
+ elsif (name == "set")
208
+ result = Types::SET
209
+ elsif (name == "lst")
210
+ result = Types::LIST
211
+ else
212
+ result = Types::STOP
213
+ end
214
+ if (result == Types::STOP)
215
+ raise NotImplementedError
216
+ end
217
+ return result
218
+ end
219
+
220
+ # Static helper functions
221
+
222
+ # Read 1 character from the trans and verify that it is the expected character ch.
223
+ # Throw a protocol exception if it is not.
224
+ def self.read_syntax_char(reader, ch)
225
+ ch2 = reader.read
226
+ if (ch2 != ch)
227
+ raise ProtocolException.new(ProtocolException::INVALID_DATA, "Expected \'#{ch}\' got \'#{ch2}\'.")
228
+ end
229
+ end
230
+
231
+ # Return true if the character ch is in [-+0-9.Ee]; false otherwise
232
+ def is_json_numeric(ch)
233
+ case ch
234
+ when '+', '-', '.', '0' .. '9', 'E', "e"
235
+ return true
236
+ else
237
+ return false
238
+ end
239
+ end
240
+
241
+ def push_context(context)
242
+ @contexts.push(@context)
243
+ @context = context
244
+ end
245
+
246
+ def pop_context
247
+ @context = @contexts.pop
248
+ end
249
+
250
+ # Write the character ch as a JSON escape sequence ("\u00xx")
251
+ def write_json_escape_char(ch)
252
+ trans.write('\\u')
253
+ ch_value = ch[0]
254
+ if (ch_value.kind_of? String)
255
+ ch_value = ch.bytes.first
256
+ end
257
+ trans.write(ch_value.to_s(16).rjust(4,'0'))
258
+ end
259
+
260
+ # Write the character ch as part of a JSON string, escaping as appropriate.
261
+ def write_json_char(ch)
262
+ # This table describes the handling for the first 0x30 characters
263
+ # 0 : escape using "\u00xx" notation
264
+ # 1 : just output index
265
+ # <other> : escape using "\<other>" notation
266
+ kJSONCharTable = [
267
+ # 0 1 2 3 4 5 6 7 8 9 A B C D E F
268
+ 0, 0, 0, 0, 0, 0, 0, 0,'b','t','n', 0,'f','r', 0, 0, # 0
269
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, # 1
270
+ 1, 1,'"', 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, # 2
271
+ ]
272
+
273
+ ch_value = ch[0]
274
+ if (ch_value.kind_of? String)
275
+ ch_value = ch.bytes.first
276
+ end
277
+ if (ch_value >= 0x30)
278
+ if (ch == @@kJSONBackslash) # Only special character >= 0x30 is '\'
279
+ trans.write(@@kJSONBackslash)
280
+ trans.write(@@kJSONBackslash)
281
+ else
282
+ trans.write(ch)
283
+ end
284
+ else
285
+ outCh = kJSONCharTable[ch_value];
286
+ # Check if regular character, backslash escaped, or JSON escaped
287
+ if outCh.kind_of? String
288
+ trans.write(@@kJSONBackslash)
289
+ trans.write(outCh)
290
+ elsif outCh == 1
291
+ trans.write(ch)
292
+ else
293
+ write_json_escape_char(ch)
294
+ end
295
+ end
296
+ end
297
+
298
+ # Write out the contents of the string str as a JSON string, escaping characters as appropriate.
299
+ def write_json_string(str)
300
+ @context.write(trans)
301
+ trans.write(@@kJSONStringDelimiter)
302
+ str.split('').each do |ch|
303
+ write_json_char(ch)
304
+ end
305
+ trans.write(@@kJSONStringDelimiter)
306
+ end
307
+
308
+ # Write out the contents of the string as JSON string, base64-encoding
309
+ # the string's contents, and escaping as appropriate
310
+ def write_json_base64(str)
311
+ @context.write(trans)
312
+ trans.write(@@kJSONStringDelimiter)
313
+ write_json_string([str].pack("m"))
314
+ trans.write(@@kJSONStringDelimiter)
315
+ end
316
+
317
+ # Convert the given integer type to a JSON number, or a string
318
+ # if the context requires it (eg: key in a map pair).
319
+ def write_json_integer(num)
320
+ @context.write(trans)
321
+ escapeNum = @context.escapeNum
322
+ if (escapeNum)
323
+ trans.write(@@kJSONStringDelimiter)
324
+ end
325
+ trans.write(num.to_s);
326
+ if (escapeNum)
327
+ trans.write(@@kJSONStringDelimiter)
328
+ end
329
+ end
330
+
331
+ # Convert the given double to a JSON string, which is either the number,
332
+ # "NaN" or "Infinity" or "-Infinity".
333
+ def write_json_double(num)
334
+ @context.write(trans)
335
+ # Normalize output of boost::lexical_cast for NaNs and Infinities
336
+ special = false;
337
+ if (num.nan?)
338
+ special = true;
339
+ val = @@kThriftNan;
340
+ elsif (num.infinite?)
341
+ special = true;
342
+ val = @@kThriftInfinity;
343
+ if (num < 0.0)
344
+ val = @@kThriftNegativeInfinity;
345
+ end
346
+ else
347
+ val = num.to_s
348
+ end
349
+
350
+ escapeNum = special || @context.escapeNum
351
+ if (escapeNum)
352
+ trans.write(@@kJSONStringDelimiter)
353
+ end
354
+ trans.write(val)
355
+ if (escapeNum)
356
+ trans.write(@@kJSONStringDelimiter)
357
+ end
358
+ end
359
+
360
+ def write_json_object_start
361
+ @context.write(trans)
362
+ trans.write(@@kJSONObjectStart)
363
+ push_context(JSONPairContext.new);
364
+ end
365
+
366
+ def write_json_object_end
367
+ pop_context
368
+ trans.write(@@kJSONObjectEnd)
369
+ end
370
+
371
+ def write_json_array_start
372
+ @context.write(trans)
373
+ trans.write(@@kJSONArrayStart)
374
+ push_context(JSONListContext.new);
375
+ end
376
+
377
+ def write_json_array_end
378
+ pop_context
379
+ trans.write(@@kJSONArrayEnd)
380
+ end
381
+
382
+ def write_message_begin(name, type, seqid)
383
+ write_json_array_start
384
+ write_json_integer(@@kThriftVersion1)
385
+ write_json_string(name)
386
+ write_json_integer(type)
387
+ write_json_integer(seqid)
388
+ end
389
+
390
+ def write_message_end
391
+ write_json_array_end
392
+ end
393
+
394
+ def write_struct_begin(name)
395
+ write_json_object_start
396
+ end
397
+
398
+ def write_struct_end
399
+ write_json_object_end
400
+ end
401
+
402
+ def write_field_begin(name, type, id)
403
+ write_json_integer(id)
404
+ write_json_object_start
405
+ write_json_string(get_type_name_for_type_id(type))
406
+ end
407
+
408
+ def write_field_end
409
+ write_json_object_end
410
+ end
411
+
412
+ def write_field_stop; nil; end
413
+
414
+ def write_map_begin(ktype, vtype, size)
415
+ write_json_array_start
416
+ write_json_string(get_type_name_for_type_id(ktype))
417
+ write_json_string(get_type_name_for_type_id(vtype))
418
+ write_json_integer(size)
419
+ write_json_object_start
420
+ end
421
+
422
+ def write_map_end
423
+ write_json_object_end
424
+ write_json_array_end
425
+ end
426
+
427
+ def write_list_begin(etype, size)
428
+ write_json_array_start
429
+ write_json_string(get_type_name_for_type_id(etype))
430
+ write_json_integer(size)
431
+ end
432
+
433
+ def write_list_end
434
+ write_json_array_end
435
+ end
436
+
437
+ def write_set_begin(etype, size)
438
+ write_json_array_start
439
+ write_json_string(get_type_name_for_type_id(etype))
440
+ write_json_integer(size)
441
+ end
442
+
443
+ def write_set_end
444
+ write_json_array_end
445
+ end
446
+
447
+ def write_bool(bool)
448
+ write_json_integer(bool ? 1 : 0)
449
+ end
450
+
451
+ def write_byte(byte)
452
+ write_json_integer(byte)
453
+ end
454
+
455
+ def write_i16(i16)
456
+ write_json_integer(i16)
457
+ end
458
+
459
+ def write_i32(i32)
460
+ write_json_integer(i32)
461
+ end
462
+
463
+ def write_i64(i64)
464
+ write_json_integer(i64)
465
+ end
466
+
467
+ def write_double(dub)
468
+ write_json_double(dub)
469
+ end
470
+
471
+ def write_string(str)
472
+ write_json_string(str)
473
+ end
474
+
475
+ def write_binary(str)
476
+ write_json_base64(str)
477
+ end
478
+
479
+ ##
480
+ # Reading functions
481
+ ##
482
+
483
+ # Reads 1 byte and verifies that it matches ch.
484
+ def read_json_syntax_char(ch)
485
+ JsonProtocol::read_syntax_char(@reader, ch)
486
+ end
487
+
488
+ # Decodes the four hex parts of a JSON escaped string character and returns
489
+ # the character via out.
490
+ #
491
+ # Note - this only supports Unicode characters in the BMP (U+0000 to U+FFFF);
492
+ # characters above the BMP are encoded as two escape sequences (surrogate pairs),
493
+ # which is not yet implemented
494
+ def read_json_escape_char
495
+ str = @reader.read
496
+ str += @reader.read
497
+ str += @reader.read
498
+ str += @reader.read
499
+ if RUBY_VERSION >= '1.9'
500
+ str.hex.chr(Encoding::UTF_8)
501
+ else
502
+ str.hex.chr
503
+ end
504
+ end
505
+
506
+ # Decodes a JSON string, including unescaping, and returns the string via str
507
+ def read_json_string(skipContext = false)
508
+ # This string's characters must match up with the elements in escape_char_vals.
509
+ # I don't have '/' on this list even though it appears on www.json.org --
510
+ # it is not in the RFC -> it is. See RFC 4627
511
+ escape_chars = "\"\\/bfnrt"
512
+
513
+ # The elements of this array must match up with the sequence of characters in
514
+ # escape_chars
515
+ escape_char_vals = [
516
+ '"', '\\', '/', '\b', '\f', '\n', '\r', '\t',
517
+ ]
518
+
519
+ if !skipContext
520
+ @context.read(@reader)
521
+ end
522
+ read_json_syntax_char(@@kJSONStringDelimiter)
523
+ ch = ""
524
+ str = ""
525
+ while (true)
526
+ ch = @reader.read
527
+ if (ch == @@kJSONStringDelimiter)
528
+ break
529
+ end
530
+ if (ch == @@kJSONBackslash)
531
+ ch = @reader.read
532
+ if (ch == 'u')
533
+ ch = read_json_escape_char
534
+ else
535
+ pos = escape_chars.index(ch);
536
+ if (pos.nil?) # not found
537
+ raise ProtocolException.new(ProtocolException::INVALID_DATA, "Expected control char, got \'#{ch}\'.")
538
+ end
539
+ ch = escape_char_vals[pos]
540
+ end
541
+ end
542
+ str += ch
543
+ end
544
+ return str
545
+ end
546
+
547
+ # Reads a block of base64 characters, decoding it, and returns via str
548
+ def read_json_base64
549
+ read_json_string.unpack("m")[0]
550
+ end
551
+
552
+ # Reads a sequence of characters, stopping at the first one that is not
553
+ # a valid JSON numeric character.
554
+ def read_json_numeric_chars
555
+ str = ""
556
+ while (true)
557
+ ch = @reader.peek
558
+ if (!is_json_numeric(ch))
559
+ break;
560
+ end
561
+ ch = @reader.read
562
+ str += ch
563
+ end
564
+ return str
565
+ end
566
+
567
+ # Reads a sequence of characters and assembles them into a number,
568
+ # returning them via num
569
+ def read_json_integer
570
+ @context.read(@reader)
571
+ if (@context.escapeNum)
572
+ read_json_syntax_char(@@kJSONStringDelimiter)
573
+ end
574
+ str = read_json_numeric_chars
575
+
576
+ begin
577
+ num = Integer(str);
578
+ rescue
579
+ raise ProtocolException.new(ProtocolException::INVALID_DATA, "Expected numeric value; got \"#{str}\"")
580
+ end
581
+
582
+ if (@context.escapeNum)
583
+ read_json_syntax_char(@@kJSONStringDelimiter)
584
+ end
585
+
586
+ return num
587
+ end
588
+
589
+ # Reads a JSON number or string and interprets it as a double.
590
+ def read_json_double
591
+ @context.read(@reader)
592
+ num = 0
593
+ if (@reader.peek == @@kJSONStringDelimiter)
594
+ str = read_json_string(true)
595
+ # Check for NaN, Infinity and -Infinity
596
+ if (str == @@kThriftNan)
597
+ num = (+1.0/0.0)/(+1.0/0.0)
598
+ elsif (str == @@kThriftInfinity)
599
+ num = +1.0/0.0
600
+ elsif (str == @@kThriftNegativeInfinity)
601
+ num = -1.0/0.0
602
+ else
603
+ if (!@context.escapeNum)
604
+ # Raise exception -- we should not be in a string in this case
605
+ raise ProtocolException.new(ProtocolException::INVALID_DATA, "Numeric data unexpectedly quoted")
606
+ end
607
+ begin
608
+ num = Float(str)
609
+ rescue
610
+ raise ProtocolException.new(ProtocolException::INVALID_DATA, "Expected numeric value; got \"#{str}\"")
611
+ end
612
+ end
613
+ else
614
+ if (@context.escapeNum)
615
+ # This will throw - we should have had a quote if escapeNum == true
616
+ read_json_syntax_char(@@kJSONStringDelimiter)
617
+ end
618
+ str = read_json_numeric_chars
619
+ begin
620
+ num = Float(str)
621
+ rescue
622
+ raise ProtocolException.new(ProtocolException::INVALID_DATA, "Expected numeric value; got \"#{str}\"")
623
+ end
624
+ end
625
+ return num
626
+ end
627
+
628
+ def read_json_object_start
629
+ @context.read(@reader)
630
+ read_json_syntax_char(@@kJSONObjectStart)
631
+ push_context(JSONPairContext.new)
632
+ nil
633
+ end
634
+
635
+ def read_json_object_end
636
+ read_json_syntax_char(@@kJSONObjectEnd)
637
+ pop_context
638
+ nil
639
+ end
640
+
641
+ def read_json_array_start
642
+ @context.read(@reader)
643
+ read_json_syntax_char(@@kJSONArrayStart)
644
+ push_context(JSONListContext.new)
645
+ nil
646
+ end
647
+
648
+ def read_json_array_end
649
+ read_json_syntax_char(@@kJSONArrayEnd)
650
+ pop_context
651
+ nil
652
+ end
653
+
654
+ def read_message_begin
655
+ read_json_array_start
656
+ version = read_json_integer
657
+ if (version != @@kThriftVersion1)
658
+ raise ProtocolException.new(ProtocolException::BAD_VERSION, 'Message contained bad version.')
659
+ end
660
+ name = read_json_string
661
+ message_type = read_json_integer
662
+ seqid = read_json_integer
663
+ [name, message_type, seqid]
664
+ end
665
+
666
+ def read_message_end
667
+ read_json_array_end
668
+ nil
669
+ end
670
+
671
+ def read_struct_begin
672
+ read_json_object_start
673
+ nil
674
+ end
675
+
676
+ def read_struct_end
677
+ read_json_object_end
678
+ nil
679
+ end
680
+
681
+ def read_field_begin
682
+ # Check if we hit the end of the list
683
+ ch = @reader.peek
684
+ if (ch == @@kJSONObjectEnd)
685
+ field_type = Types::STOP
686
+ else
687
+ field_id = read_json_integer
688
+ read_json_object_start
689
+ field_type = get_type_id_for_type_name(read_json_string)
690
+ end
691
+ [nil, field_type, field_id]
692
+ end
693
+
694
+ def read_field_end
695
+ read_json_object_end
696
+ end
697
+
698
+ def read_map_begin
699
+ read_json_array_start
700
+ key_type = get_type_id_for_type_name(read_json_string)
701
+ val_type = get_type_id_for_type_name(read_json_string)
702
+ size = read_json_integer
703
+ read_json_object_start
704
+ [key_type, val_type, size]
705
+ end
706
+
707
+ def read_map_end
708
+ read_json_object_end
709
+ read_json_array_end
710
+ end
711
+
712
+ def read_list_begin
713
+ read_json_array_start
714
+ [get_type_id_for_type_name(read_json_string), read_json_integer]
715
+ end
716
+
717
+ def read_list_end
718
+ read_json_array_end
719
+ end
720
+
721
+ def read_set_begin
722
+ read_json_array_start
723
+ [get_type_id_for_type_name(read_json_string), read_json_integer]
724
+ end
725
+
726
+ def read_set_end
727
+ read_json_array_end
728
+ end
729
+
730
+ def read_bool
731
+ byte = read_byte
732
+ byte != 0
733
+ end
734
+
735
+ def read_byte
736
+ read_json_integer
737
+ end
738
+
739
+ def read_i16
740
+ read_json_integer
741
+ end
742
+
743
+ def read_i32
744
+ read_json_integer
745
+ end
746
+
747
+ def read_i64
748
+ read_json_integer
749
+ end
750
+
751
+ def read_double
752
+ read_json_double
753
+ end
754
+
755
+ def read_string
756
+ read_json_string
757
+ end
758
+
759
+ def read_binary
760
+ read_json_base64
761
+ end
762
+ end
763
+
764
+ class JsonProtocolFactory < BaseProtocolFactory
765
+ def get_protocol(trans)
766
+ return Thrift::JsonProtocol.new(trans)
767
+ end
768
+ end
769
+ end