thrift 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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