thrift 0.23.0 → 0.24.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 (114) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +93 -33
  3. data/benchmark/benchmark.rb +2 -1
  4. data/benchmark/client.rb +1 -0
  5. data/benchmark/server.rb +1 -0
  6. data/benchmark/thin_server.rb +1 -0
  7. data/ext/binary_protocol_accelerated.c +78 -23
  8. data/ext/bytes.c +14 -0
  9. data/ext/compact_protocol.c +59 -31
  10. data/ext/extconf.rb +8 -0
  11. data/ext/memory_buffer.c +37 -0
  12. data/ext/struct.c +14 -14
  13. data/ext/thrift_native.c +1 -0
  14. data/lib/thrift/bytes.rb +4 -1
  15. data/lib/thrift/client.rb +1 -12
  16. data/lib/thrift/exceptions.rb +1 -0
  17. data/lib/thrift/multiplexed_processor.rb +14 -4
  18. data/lib/thrift/processor.rb +1 -0
  19. data/lib/thrift/protocol/base_protocol.rb +15 -6
  20. data/lib/thrift/protocol/binary_protocol.rb +41 -12
  21. data/lib/thrift/protocol/binary_protocol_accelerated.rb +1 -0
  22. data/lib/thrift/protocol/compact_protocol.rb +15 -4
  23. data/lib/thrift/protocol/header_protocol.rb +1 -0
  24. data/lib/thrift/protocol/json_protocol.rb +18 -11
  25. data/lib/thrift/protocol/multiplexed_protocol.rb +1 -0
  26. data/lib/thrift/protocol/protocol_decorator.rb +1 -0
  27. data/lib/thrift/serializer/deserializer.rb +1 -0
  28. data/lib/thrift/serializer/serializer.rb +1 -0
  29. data/lib/thrift/server/base_server.rb +1 -0
  30. data/lib/thrift/server/mongrel_http_server.rb +1 -0
  31. data/lib/thrift/server/nonblocking_server.rb +28 -2
  32. data/lib/thrift/server/simple_server.rb +9 -1
  33. data/lib/thrift/server/thin_http_server.rb +1 -0
  34. data/lib/thrift/server/thread_pool_server.rb +1 -0
  35. data/lib/thrift/server/threaded_server.rb +9 -1
  36. data/lib/thrift/struct.rb +1 -0
  37. data/lib/thrift/struct_union.rb +4 -9
  38. data/lib/thrift/thrift_native.rb +2 -1
  39. data/lib/thrift/transport/base_server_transport.rb +3 -0
  40. data/lib/thrift/transport/base_transport.rb +6 -2
  41. data/lib/thrift/transport/buffered_transport.rb +1 -0
  42. data/lib/thrift/transport/framed_transport.rb +1 -0
  43. data/lib/thrift/transport/header_transport.rb +47 -1
  44. data/lib/thrift/transport/http_client_transport.rb +1 -0
  45. data/lib/thrift/transport/io_stream_transport.rb +1 -0
  46. data/lib/thrift/transport/memory_buffer_transport.rb +7 -0
  47. data/lib/thrift/transport/server_socket.rb +6 -3
  48. data/lib/thrift/transport/socket.rb +68 -29
  49. data/lib/thrift/transport/ssl_server_socket.rb +3 -2
  50. data/lib/thrift/transport/ssl_socket.rb +43 -11
  51. data/lib/thrift/transport/unix_server_socket.rb +5 -1
  52. data/lib/thrift/transport/unix_socket.rb +1 -0
  53. data/lib/thrift/types.rb +1 -0
  54. data/lib/thrift/union.rb +1 -0
  55. data/lib/thrift/uuid.rb +1 -0
  56. data/lib/thrift.rb +1 -0
  57. data/spec/ThriftSpec.thrift +16 -0
  58. data/spec/base_protocol_spec.rb +20 -0
  59. data/spec/base_transport_spec.rb +42 -1
  60. data/spec/binary_protocol_accelerated_spec.rb +1 -0
  61. data/spec/binary_protocol_spec.rb +1 -0
  62. data/spec/binary_protocol_spec_shared.rb +79 -15
  63. data/spec/bytes_spec.rb +19 -1
  64. data/spec/client_spec.rb +1 -0
  65. data/spec/compact_protocol_spec.rb +99 -0
  66. data/spec/constants_demo_spec.rb +1 -0
  67. data/spec/exception_spec.rb +1 -0
  68. data/spec/flat_spec.rb +1 -0
  69. data/spec/header_protocol_spec.rb +1 -0
  70. data/spec/header_transport_spec.rb +45 -0
  71. data/spec/http_client_spec.rb +1 -0
  72. data/spec/json_protocol_spec.rb +26 -4
  73. data/spec/multiplexed_processor_spec.rb +75 -0
  74. data/spec/namespaced_spec.rb +1 -0
  75. data/spec/nonblocking_server_spec.rb +86 -18
  76. data/spec/processor_spec.rb +1 -0
  77. data/spec/recursion_depth_spec.rb +223 -0
  78. data/spec/serializer_spec.rb +1 -0
  79. data/spec/server_socket_spec.rb +37 -0
  80. data/spec/server_spec.rb +52 -0
  81. data/spec/socket_spec.rb +119 -12
  82. data/spec/socket_spec_shared.rb +1 -0
  83. data/spec/spec_helper.rb +1 -0
  84. data/spec/ssl_server_socket_spec.rb +40 -0
  85. data/spec/ssl_socket_spec.rb +171 -10
  86. data/spec/struct_nested_containers_spec.rb +1 -0
  87. data/spec/struct_spec.rb +1 -0
  88. data/spec/support/header_protocol_helper.rb +2 -1
  89. data/spec/thin_http_server_spec.rb +1 -0
  90. data/spec/types_spec.rb +1 -0
  91. data/spec/union_spec.rb +1 -0
  92. data/spec/unix_socket_spec.rb +16 -0
  93. data/spec/uuid_validation_spec.rb +1 -0
  94. data/test/fuzz/Makefile +779 -0
  95. data/test/fuzz/Makefile.in +775 -0
  96. data/test/fuzz/fuzz_common.rb +1 -0
  97. data/test/fuzz/fuzz_parse_binary_protocol.rb +1 -0
  98. data/test/fuzz/fuzz_parse_binary_protocol_accelerated.rb +1 -0
  99. data/test/fuzz/fuzz_parse_binary_protocol_accelerated_harness.rb +1 -0
  100. data/test/fuzz/fuzz_parse_binary_protocol_harness.rb +1 -0
  101. data/test/fuzz/fuzz_parse_compact_protocol.rb +1 -0
  102. data/test/fuzz/fuzz_parse_compact_protocol_harness.rb +1 -0
  103. data/test/fuzz/fuzz_parse_json_protocol.rb +1 -0
  104. data/test/fuzz/fuzz_parse_json_protocol_harness.rb +1 -0
  105. data/test/fuzz/fuzz_roundtrip_binary_protocol.rb +1 -0
  106. data/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated.rb +1 -0
  107. data/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated_harness.rb +1 -0
  108. data/test/fuzz/fuzz_roundtrip_binary_protocol_harness.rb +1 -0
  109. data/test/fuzz/fuzz_roundtrip_compact_protocol.rb +1 -0
  110. data/test/fuzz/fuzz_roundtrip_compact_protocol_harness.rb +1 -0
  111. data/test/fuzz/fuzz_roundtrip_json_protocol.rb +1 -0
  112. data/test/fuzz/fuzz_roundtrip_json_protocol_harness.rb +1 -0
  113. data/test/fuzz/fuzz_tracer.rb +1 -0
  114. metadata +12 -46
data/ext/memory_buffer.c CHANGED
@@ -28,11 +28,15 @@ ID index_ivar_id;
28
28
  ID slice_method_id;
29
29
 
30
30
  int GARBAGE_BUFFER_SIZE;
31
+ static VALUE transport_exception_class;
32
+ static VALUE transport_negative_size;
33
+ static ID new_method_id;
31
34
 
32
35
  #define GET_BUF(self) rb_ivar_get(self, buf_ivar_id)
33
36
 
34
37
  VALUE rb_thrift_memory_buffer_write(VALUE self, VALUE str);
35
38
  VALUE rb_thrift_memory_buffer_read(VALUE self, VALUE length_value);
39
+ VALUE rb_thrift_memory_buffer_read_all(VALUE self, VALUE length_value);
36
40
  VALUE rb_thrift_memory_buffer_read_byte(VALUE self);
37
41
  VALUE rb_thrift_memory_buffer_read_into_buffer(VALUE self, VALUE buffer_value, VALUE size_value);
38
42
 
@@ -69,6 +73,33 @@ VALUE rb_thrift_memory_buffer_read(VALUE self, VALUE length_value) {
69
73
  return data;
70
74
  }
71
75
 
76
+ VALUE rb_thrift_memory_buffer_read_all(VALUE self, VALUE length_value) {
77
+ int length = FIX2INT(length_value);
78
+
79
+ if (RB_UNLIKELY(length < 0)) {
80
+ rb_exc_raise(rb_funcall(transport_exception_class, new_method_id, 2, transport_negative_size, rb_str_new2("Negative size")));
81
+ }
82
+
83
+ VALUE index_value = rb_ivar_get(self, index_ivar_id);
84
+ int index = FIX2INT(index_value);
85
+ VALUE buf = GET_BUF(self);
86
+
87
+ if (RB_UNLIKELY(length > RSTRING_LEN(buf) - index)) {
88
+ rb_raise(rb_eEOFError, "Not enough bytes remain in memory buffer");
89
+ }
90
+
91
+ VALUE data = rb_str_subseq(buf, index, length);
92
+
93
+ index += length;
94
+ if (index >= GARBAGE_BUFFER_SIZE) {
95
+ rb_ivar_set(self, buf_ivar_id, rb_str_subseq(buf, index, RSTRING_LEN(buf) - index));
96
+ index = 0;
97
+ }
98
+ rb_ivar_set(self, index_ivar_id, INT2FIX(index));
99
+
100
+ return data;
101
+ }
102
+
72
103
  VALUE rb_thrift_memory_buffer_read_byte(VALUE self) {
73
104
  VALUE index_value = rb_ivar_get(self, index_ivar_id);
74
105
  int index = FIX2INT(index_value);
@@ -122,6 +153,7 @@ void Init_memory_buffer(void) {
122
153
  VALUE thrift_memory_buffer_class = rb_const_get(thrift_module, rb_intern("MemoryBufferTransport"));
123
154
  rb_define_method(thrift_memory_buffer_class, "write", rb_thrift_memory_buffer_write, 1);
124
155
  rb_define_method(thrift_memory_buffer_class, "read", rb_thrift_memory_buffer_read, 1);
156
+ rb_define_method(thrift_memory_buffer_class, "read_all", rb_thrift_memory_buffer_read_all, 1);
125
157
  rb_define_method(thrift_memory_buffer_class, "read_byte", rb_thrift_memory_buffer_read_byte, 0);
126
158
  rb_define_method(thrift_memory_buffer_class, "read_into_buffer", rb_thrift_memory_buffer_read_into_buffer, 2);
127
159
 
@@ -129,6 +161,11 @@ void Init_memory_buffer(void) {
129
161
  index_ivar_id = rb_intern("@index");
130
162
 
131
163
  slice_method_id = rb_intern("slice");
164
+ new_method_id = rb_intern("new");
132
165
 
133
166
  GARBAGE_BUFFER_SIZE = FIX2INT(rb_const_get(thrift_memory_buffer_class, rb_intern("GARBAGE_BUFFER_SIZE")));
167
+ transport_exception_class = rb_const_get(thrift_module, rb_intern("TransportException"));
168
+ transport_negative_size = rb_const_get(transport_exception_class, rb_intern("NEGATIVE_SIZE"));
169
+ rb_global_variable(&transport_exception_class);
170
+ rb_global_variable(&transport_negative_size);
134
171
  }
data/ext/struct.c CHANGED
@@ -35,8 +35,8 @@ static ID sorted_field_ids_method_id;
35
35
  #define IS_CONTAINER(ttype) ((ttype) == TTYPE_MAP || (ttype) == TTYPE_LIST || (ttype) == TTYPE_SET)
36
36
  #define STRUCT_FIELDS(obj) rb_const_get(CLASS_OF(obj), fields_const_id)
37
37
 
38
- static VALUE new_container_array(int size) {
39
- if (size < 0) {
38
+ static void validate_container_size(int size) {
39
+ if (RB_UNLIKELY(size < 0)) {
40
40
  rb_exc_raise(
41
41
  get_protocol_exception(
42
42
  INT2FIX(PROTOERR_NEGATIVE_SIZE),
@@ -44,7 +44,10 @@ static VALUE new_container_array(int size) {
44
44
  )
45
45
  );
46
46
  }
47
+ }
47
48
 
49
+ static VALUE new_container_array(int size) {
50
+ validate_container_size(size);
48
51
  return rb_ary_new2(size > 1024 ? 1024 : size);
49
52
  }
50
53
 
@@ -252,8 +255,6 @@ static void write_container(int ttype, VALUE field_info, VALUE value, VALUE prot
252
255
 
253
256
  if (ttype == TTYPE_MAP) {
254
257
  VALUE keys;
255
- VALUE key;
256
- VALUE val;
257
258
 
258
259
  Check_Type(value, T_HASH);
259
260
 
@@ -272,8 +273,8 @@ static void write_container(int ttype, VALUE field_info, VALUE value, VALUE prot
272
273
  default_write_map_begin(protocol, keytype_value, valuetype_value, INT2FIX(sz));
273
274
 
274
275
  for (i = 0; i < sz; i++) {
275
- key = rb_ary_entry(keys, i);
276
- val = rb_hash_aref(value, key);
276
+ VALUE key = rb_ary_entry(keys, i);
277
+ VALUE val = rb_hash_aref(value, key);
277
278
 
278
279
  if (IS_CONTAINER(keytype)) {
279
280
  write_container(keytype, key_info, key, protocol);
@@ -489,8 +490,6 @@ static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info) {
489
490
  rb_thrift_struct_read(result, protocol);
490
491
  }
491
492
  } else if (ttype == TTYPE_MAP) {
492
- int i;
493
-
494
493
  VALUE map_header = default_read_map_begin(protocol);
495
494
  int key_ttype = FIX2INT(rb_ary_entry(map_header, 0));
496
495
  int value_ttype = FIX2INT(rb_ary_entry(map_header, 1));
@@ -511,7 +510,7 @@ static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info) {
511
510
  if (num_entries == 0 || (specified_key_type == key_ttype && specified_value_type == value_ttype)) {
512
511
  result = rb_hash_new();
513
512
 
514
- for (i = 0; i < num_entries; ++i) {
513
+ for (int i = 0; i < num_entries; ++i) {
515
514
  VALUE key, val;
516
515
 
517
516
  key = read_anything(protocol, key_ttype, key_info);
@@ -528,8 +527,6 @@ static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info) {
528
527
 
529
528
  default_read_map_end(protocol);
530
529
  } else if (ttype == TTYPE_LIST) {
531
- int i;
532
-
533
530
  VALUE list_header = default_read_list_begin(protocol);
534
531
  int element_ttype = FIX2INT(rb_ary_entry(list_header, 0));
535
532
  int num_elements = FIX2INT(rb_ary_entry(list_header, 1));
@@ -542,20 +539,21 @@ static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info) {
542
539
  if (specified_element_type == element_ttype) {
543
540
  result = new_container_array(num_elements);
544
541
 
545
- for (i = 0; i < num_elements; ++i) {
542
+ for (int i = 0; i < num_elements; ++i) {
546
543
  rb_ary_push(result, read_anything(protocol, element_ttype, rb_hash_aref(field_info, element_sym)));
547
544
  }
548
545
  } else {
546
+ validate_container_size(num_elements);
549
547
  skip_list_or_set_contents(protocol, INT2FIX(element_ttype), num_elements);
550
548
  }
551
549
  } else {
550
+ validate_container_size(num_elements);
552
551
  skip_list_or_set_contents(protocol, INT2FIX(element_ttype), num_elements);
553
552
  }
554
553
 
555
554
  default_read_list_end(protocol);
556
555
  } else if (ttype == TTYPE_SET) {
557
556
  VALUE items;
558
- int i;
559
557
 
560
558
  VALUE set_header = default_read_set_begin(protocol);
561
559
  int element_ttype = FIX2INT(rb_ary_entry(set_header, 0));
@@ -569,15 +567,17 @@ static VALUE read_anything(VALUE protocol, int ttype, VALUE field_info) {
569
567
  if (specified_element_type == element_ttype) {
570
568
  items = new_container_array(num_elements);
571
569
 
572
- for (i = 0; i < num_elements; ++i) {
570
+ for (int i = 0; i < num_elements; ++i) {
573
571
  rb_ary_push(items, read_anything(protocol, element_ttype, rb_hash_aref(field_info, element_sym)));
574
572
  }
575
573
 
576
574
  result = rb_class_new_instance(1, &items, rb_cSet);
577
575
  } else {
576
+ validate_container_size(num_elements);
578
577
  skip_list_or_set_contents(protocol, INT2FIX(element_ttype), num_elements);
579
578
  }
580
579
  } else {
580
+ validate_container_size(num_elements);
581
581
  skip_list_or_set_contents(protocol, INT2FIX(element_ttype), num_elements);
582
582
  }
583
583
 
data/ext/thrift_native.c CHANGED
@@ -121,6 +121,7 @@ int PROTOERR_BAD_VERSION;
121
121
  int PROTOERR_NOT_IMPLEMENTED;
122
122
  int PROTOERR_DEPTH_LIMIT;
123
123
 
124
+ // cppcheck-suppress unusedFunction
124
125
  RUBY_FUNC_EXPORTED void Init_thrift_native(void) {
125
126
  // cached classes
126
127
  thrift_module = rb_const_get(rb_cObject, rb_intern("Thrift"));
data/lib/thrift/bytes.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # encoding: ascii-8bit
2
+ # frozen_string_literal: true
2
3
  #
3
4
  # Licensed to the Apache Software Foundation (ASF) under one
4
5
  # or more contributor license agreements. See the NOTICE file
@@ -36,13 +37,15 @@ module Thrift
36
37
  end
37
38
 
38
39
  # Forces the encoding of the buffer to BINARY. If the buffer
39
- # passed is frozen, then it will be duplicated.
40
+ # passed is frozen and not already BINARY, then it will be duplicated.
40
41
  #
41
42
  # buffer - The String to force the encoding of.
42
43
  #
43
44
  # Returns the String passed with an encoding of BINARY; returned
44
45
  # String may be a duplicate.
45
46
  def self.force_binary_encoding(buffer)
47
+ return buffer if buffer.encoding == Encoding::BINARY
48
+
46
49
  buffer = buffer.dup if buffer.frozen?
47
50
  buffer.force_encoding(Encoding::BINARY)
48
51
  end
data/lib/thrift/client.rb CHANGED
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  #
2
3
  # Licensed to the Apache Software Foundation (ASF) under one
3
4
  # or more contributor license agreements. See the NOTICE file
@@ -61,11 +62,6 @@ module Thrift
61
62
  [fname, mtype, rseqid]
62
63
  end
63
64
 
64
- def reply_seqid(rseqid)
65
- expected_seqid = dequeue_pending_seqid
66
- !expected_seqid.nil? && rseqid == expected_seqid
67
- end
68
-
69
65
  def validate_message_begin(fname, mtype, rseqid, expected_name)
70
66
  expected_seqid = dequeue_pending_seqid
71
67
 
@@ -102,13 +98,6 @@ module Thrift
102
98
  result
103
99
  end
104
100
 
105
- def handle_exception(mtype)
106
- if mtype == MessageTypes::EXCEPTION
107
- dequeue_pending_seqid
108
- raise_application_exception
109
- end
110
- end
111
-
112
101
  private
113
102
 
114
103
  def next_seqid!
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  #
2
3
  # Licensed to the Apache Software Foundation (ASF) under one
3
4
  # or more contributor license agreements. See the NOTICE file
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  #
2
3
  # Licensed to the Apache Software Foundation (ASF) under one
3
4
  # or more contributor license agreements. See the NOTICE file
@@ -23,17 +24,26 @@ module Thrift
23
24
  class MultiplexedProcessor
24
25
  def initialize
25
26
  @actual_processors = {}
27
+ @default_processor = nil
26
28
  end
27
29
 
28
30
  def register_processor(service_name, processor)
29
31
  @actual_processors[service_name] = processor
30
32
  end
31
33
 
34
+ def register_default(processor)
35
+ @default_processor = processor
36
+ end
37
+
32
38
  def process(iprot, oprot)
33
39
  name, type, seqid = iprot.read_message_begin
34
40
  check_type(type)
35
- check_separator(name)
36
- service_name, method = name.split(':')
41
+ if name.count(':') < 1
42
+ check_default_processor(name)
43
+ return @default_processor.process(StoredMessageProtocol.new(iprot, [name, type, seqid]), oprot)
44
+ end
45
+
46
+ service_name, method = name.split(':', 2)
37
47
  processor(service_name).process(StoredMessageProtocol.new(iprot, [method, type, seqid]), oprot)
38
48
  end
39
49
 
@@ -53,8 +63,8 @@ module Thrift
53
63
  end
54
64
  end
55
65
 
56
- def check_separator(name)
57
- if name.count(':') < 1
66
+ def check_default_processor(name)
67
+ unless @default_processor
58
68
  raise Thrift::Exception.new("Service name not found in message name: #{name}. Did you forget to use a Thrift::Protocol::MultiplexedProtocol in your client?")
59
69
  end
60
70
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  #
2
3
  # Licensed to the Apache Software Foundation (ASF) under one
3
4
  # or more contributor license agreements. See the NOTICE file
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  #
2
3
  # Licensed to the Apache Software Foundation (ASF) under one
3
4
  # or more contributor license agreements. See the NOTICE file
@@ -341,7 +342,8 @@ module Thrift
341
342
  end
342
343
  end
343
344
 
344
- def skip(type)
345
+ def skip(type, max_depth = 64)
346
+ raise ProtocolException.new(ProtocolException::DEPTH_LIMIT, 'Maximum skip depth exceeded') if max_depth <= 0
345
347
  case type
346
348
  when Types::BOOL
347
349
  read_bool
@@ -364,27 +366,30 @@ module Thrift
364
366
  while true
365
367
  name, type, id = read_field_begin
366
368
  break if type == Types::STOP
367
- skip(type)
369
+ skip(type, max_depth - 1)
368
370
  read_field_end
369
371
  end
370
372
  read_struct_end
371
373
  when Types::MAP
372
374
  ktype, vtype, size = read_map_begin
375
+ validate_container_size(size)
373
376
  size.times do
374
- skip(ktype)
375
- skip(vtype)
377
+ skip(ktype, max_depth - 1)
378
+ skip(vtype, max_depth - 1)
376
379
  end
377
380
  read_map_end
378
381
  when Types::SET
379
382
  etype, size = read_set_begin
383
+ validate_container_size(size)
380
384
  size.times do
381
- skip(etype)
385
+ skip(etype, max_depth - 1)
382
386
  end
383
387
  read_set_end
384
388
  when Types::LIST
385
389
  etype, size = read_list_begin
390
+ validate_container_size(size)
386
391
  size.times do
387
- skip(etype)
392
+ skip(etype, max_depth - 1)
388
393
  end
389
394
  read_list_end
390
395
  else
@@ -392,6 +397,10 @@ module Thrift
392
397
  end
393
398
  end
394
399
 
400
+ def validate_container_size(size)
401
+ raise ProtocolException.new(ProtocolException::NEGATIVE_SIZE, 'Negative size') unless size >= 0
402
+ end
403
+
395
404
  def to_s
396
405
  "#{trans.to_s}"
397
406
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  #
2
3
  # Licensed to the Apache Software Foundation (ASF) under one
3
4
  # or more contributor license agreements. See the NOTICE file
@@ -22,6 +23,14 @@ module Thrift
22
23
  VERSION_MASK = 0xffff0000
23
24
  VERSION_1 = 0x80010000
24
25
  TYPE_MASK = 0x000000ff
26
+ BYTE_MIN = -2**7
27
+ BYTE_MAX = 2**7 - 1
28
+ I16_MIN = -2**15
29
+ I16_MAX = 2**15 - 1
30
+ I32_MIN = -2**31
31
+ I32_MAX = 2**31 - 1
32
+ I64_MIN = -2**63
33
+ I64_MAX = 2**63 - 1
25
34
 
26
35
  attr_reader :strict_read, :strict_write
27
36
 
@@ -36,11 +45,10 @@ module Thrift
36
45
  end
37
46
 
38
47
  def write_message_begin(name, type, seqid)
39
- # this is necessary because we added (needed) bounds checking to
40
- # write_i32, and 0x80010000 is too big for that.
41
48
  if strict_write
42
- write_i16(VERSION_1 >> 16)
43
- write_i16(type)
49
+ raise ::TypeError, 'integer argument expected' unless type.is_a?(Integer)
50
+ raise RangeError if type < BYTE_MIN || type > BYTE_MAX
51
+ trans.write([VERSION_1 | type].pack('N'))
44
52
  write_string(name)
45
53
  write_i32(seqid)
46
54
  else
@@ -64,17 +72,17 @@ module Thrift
64
72
  def write_map_begin(ktype, vtype, size)
65
73
  write_byte(ktype)
66
74
  write_byte(vtype)
67
- write_i32(size)
75
+ write_i32_size(size)
68
76
  end
69
77
 
70
78
  def write_list_begin(etype, size)
71
79
  write_byte(etype)
72
- write_i32(size)
80
+ write_i32_size(size)
73
81
  end
74
82
 
75
83
  def write_set_begin(etype, size)
76
84
  write_byte(etype)
77
- write_i32(size)
85
+ write_i32_size(size)
78
86
  end
79
87
 
80
88
  def write_bool(bool)
@@ -83,24 +91,29 @@ module Thrift
83
91
 
84
92
  def write_byte(byte)
85
93
  raise 'nil argument not allowed!' if byte.nil?
86
- raise RangeError if byte < -2**31 || byte >= 2**32
94
+ raise ::TypeError, 'integer argument expected' unless byte.is_a?(Integer)
95
+ raise RangeError if byte < BYTE_MIN || byte > BYTE_MAX
87
96
  trans.write([byte].pack('c'))
88
97
  end
89
98
 
90
99
  def write_i16(i16)
91
100
  raise 'nil argument not allowed!' if i16.nil?
101
+ raise ::TypeError, 'integer argument expected' unless i16.is_a?(Integer)
102
+ raise RangeError if i16 < I16_MIN || i16 > I16_MAX
92
103
  trans.write([i16].pack('n'))
93
104
  end
94
105
 
95
106
  def write_i32(i32)
96
107
  raise 'nil argument not allowed!' if i32.nil?
97
- raise RangeError if i32 < -2**31 || i32 >= 2**31
108
+ raise ::TypeError, 'integer argument expected' unless i32.is_a?(Integer)
109
+ raise RangeError if i32 < I32_MIN || i32 > I32_MAX
98
110
  trans.write([i32].pack('N'))
99
111
  end
100
112
 
101
113
  def write_i64(i64)
102
114
  raise 'nil argument not allowed!' if i64.nil?
103
- raise RangeError if i64 < -2**63 || i64 >= 2**64
115
+ raise ::TypeError, 'integer argument expected' unless i64.is_a?(Integer)
116
+ raise RangeError if i64 < I64_MIN || i64 > I64_MAX
104
117
  hi = i64 >> 32
105
118
  lo = i64 & 0xffffffff
106
119
  trans.write([hi, lo].pack('N2'))
@@ -119,7 +132,7 @@ module Thrift
119
132
 
120
133
  def write_binary(buf)
121
134
  raise 'nil argument not allowed!' if buf.nil?
122
- write_i32(buf.bytesize)
135
+ write_i32_size(buf.bytesize)
123
136
  trans.write(buf)
124
137
  end
125
138
 
@@ -165,18 +178,21 @@ module Thrift
165
178
  ktype = read_byte
166
179
  vtype = read_byte
167
180
  size = read_i32
181
+ raise ProtocolException.new(ProtocolException::NEGATIVE_SIZE, 'Negative size') unless size >= 0
168
182
  [ktype, vtype, size]
169
183
  end
170
184
 
171
185
  def read_list_begin
172
186
  etype = read_byte
173
187
  size = read_i32
188
+ raise ProtocolException.new(ProtocolException::NEGATIVE_SIZE, 'Negative size') unless size >= 0
174
189
  [etype, size]
175
190
  end
176
191
 
177
192
  def read_set_begin
178
193
  etype = read_byte
179
194
  size = read_i32
195
+ raise ProtocolException.new(ProtocolException::NEGATIVE_SIZE, 'Negative size') unless size >= 0
180
196
  [etype, size]
181
197
  end
182
198
 
@@ -236,7 +252,11 @@ module Thrift
236
252
 
237
253
  def read_binary
238
254
  size = read_i32
239
- trans.read_all(size)
255
+ if size >= 0
256
+ trans.read_all(size)
257
+ else
258
+ raise ProtocolException.new(ProtocolException::NEGATIVE_SIZE, 'Negative size')
259
+ end
240
260
  end
241
261
 
242
262
  def read_uuid
@@ -246,6 +266,15 @@ module Thrift
246
266
  def to_s
247
267
  "binary(#{super.to_s})"
248
268
  end
269
+
270
+ private
271
+
272
+ def write_i32_size(size)
273
+ raise 'nil argument not allowed!' if size.nil?
274
+ raise ::TypeError, 'integer argument expected' unless size.is_a?(Integer)
275
+ raise RangeError if size < 0 || size > I32_MAX
276
+ trans.write([size].pack('N'))
277
+ end
249
278
  end
250
279
 
251
280
  class BinaryProtocolFactory < BaseProtocolFactory
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  #
2
3
  # Licensed to the Apache Software Foundation (ASF) under one
3
4
  # or more contributor license agreements. See the NOTICE file
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  #
2
3
  # Licensed to the Apache Software Foundation (ASF) under one
3
4
  # or more contributor license agreements. See the NOTICE file
@@ -26,6 +27,8 @@ module Thrift
26
27
  TYPE_MASK = 0xE0
27
28
  TYPE_BITS = 0x07
28
29
  TYPE_SHIFT_AMOUNT = 5
30
+ MAX_VARINT32_BYTES = 5 # ceil(32/7); matches protobuf wire format
31
+ MAX_VARINT_BYTES = 10 # ceil(64/7); matches protobuf wire format
29
32
 
30
33
  TSTOP = [nil, Types::STOP, 0]
31
34
 
@@ -410,19 +413,27 @@ module Thrift
410
413
  end
411
414
 
412
415
  def read_varint32()
413
- read_varint64()
416
+ shift = 0
417
+ result = 0
418
+ MAX_VARINT32_BYTES.times do
419
+ b = read_byte()
420
+ result |= (b & 0x7f) << shift
421
+ return result if (b & 0x80) != 0x80
422
+ shift += 7
423
+ end
424
+ raise ProtocolException.new(ProtocolException::INVALID_DATA, 'Variable-length int over 5 bytes.')
414
425
  end
415
426
 
416
427
  def read_varint64()
417
428
  shift = 0
418
429
  result = 0
419
- while true
430
+ MAX_VARINT_BYTES.times do
420
431
  b = read_byte()
421
432
  result |= (b & 0x7f) << shift
422
- break if (b & 0x80) != 0x80
433
+ return result if (b & 0x80) != 0x80
423
434
  shift += 7
424
435
  end
425
- result
436
+ raise ProtocolException.new(ProtocolException::INVALID_DATA, 'Variable-length int over 10 bytes.')
426
437
  end
427
438
 
428
439
  def int_to_zig_zag(n)
@@ -1,4 +1,5 @@
1
1
  # encoding: ascii-8bit
2
+ # frozen_string_literal: true
2
3
  #
3
4
  # Licensed to the Apache Software Foundation (ASF) under one
4
5
  # or more contributor license agreements. See the NOTICE file
@@ -1,4 +1,5 @@
1
1
  # encoding: UTF-8
2
+ # frozen_string_literal: true
2
3
  #
3
4
  # Licensed to the Apache Software Foundation (ASF) under one
4
5
  # or more contributor license agreements. See the NOTICE file
@@ -500,10 +501,10 @@ module Thrift
500
501
  # characters above the BMP are encoded as two escape sequences (surrogate pairs),
501
502
  # which is not yet implemented
502
503
  def read_json_escape_char
503
- str = @reader.read
504
- str += @reader.read
505
- str += @reader.read
506
- str += @reader.read
504
+ str = +@reader.read
505
+ str << @reader.read
506
+ str << @reader.read
507
+ str << @reader.read
507
508
  str.hex.chr(Encoding::UTF_8)
508
509
  end
509
510
 
@@ -524,8 +525,7 @@ module Thrift
524
525
  @context.read(@reader)
525
526
  end
526
527
  read_json_syntax_char(@@kJSONStringDelimiter)
527
- ch = ""
528
- str = ""
528
+ str = +''
529
529
  while (true)
530
530
  ch = @reader.read
531
531
  if (ch == @@kJSONStringDelimiter)
@@ -543,7 +543,7 @@ module Thrift
543
543
  ch = escape_char_vals[pos]
544
544
  end
545
545
  end
546
- str += ch
546
+ str << ch
547
547
  end
548
548
  return str
549
549
  end
@@ -564,14 +564,14 @@ module Thrift
564
564
  # Reads a sequence of characters, stopping at the first one that is not
565
565
  # a valid JSON numeric character.
566
566
  def read_json_numeric_chars
567
- str = ""
567
+ str = String.new(encoding: Encoding::UTF_8)
568
568
  while (true)
569
569
  ch = @reader.peek
570
570
  if (!is_json_numeric(ch))
571
571
  break;
572
572
  end
573
573
  ch = @reader.read
574
- str += ch
574
+ str << ch
575
575
  end
576
576
  return str
577
577
  end
@@ -712,6 +712,7 @@ module Thrift
712
712
  key_type = get_type_id_for_type_name(read_json_string)
713
713
  val_type = get_type_id_for_type_name(read_json_string)
714
714
  size = read_json_integer
715
+ validate_container_size(size)
715
716
  read_json_object_start
716
717
  [key_type, val_type, size]
717
718
  end
@@ -723,7 +724,10 @@ module Thrift
723
724
 
724
725
  def read_list_begin
725
726
  read_json_array_start
726
- [get_type_id_for_type_name(read_json_string), read_json_integer]
727
+ type = get_type_id_for_type_name(read_json_string)
728
+ size = read_json_integer
729
+ validate_container_size(size)
730
+ [type, size]
727
731
  end
728
732
 
729
733
  def read_list_end
@@ -732,7 +736,10 @@ module Thrift
732
736
 
733
737
  def read_set_begin
734
738
  read_json_array_start
735
- [get_type_id_for_type_name(read_json_string), read_json_integer]
739
+ type = get_type_id_for_type_name(read_json_string)
740
+ size = read_json_integer
741
+ validate_container_size(size)
742
+ [type, size]
736
743
  end
737
744
 
738
745
  def read_set_end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  #
2
3
  # Licensed to the Apache Software Foundation (ASF) under one
3
4
  # or more contributor license agreements. See the NOTICE file
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  #
2
3
  # Licensed to the Apache Software Foundation (ASF) under one
3
4
  # or more contributor license agreements. See the NOTICE file
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  #
2
3
  # Licensed to the Apache Software Foundation (ASF) under one
3
4
  # or more contributor license agreements. See the NOTICE file
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  #
2
3
  # Licensed to the Apache Software Foundation (ASF) under one
3
4
  # or more contributor license agreements. See the NOTICE file
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  #
2
3
  # Licensed to the Apache Software Foundation (ASF) under one
3
4
  # or more contributor license agreements. See the NOTICE file