thrift 0.8.0 → 0.9.0

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