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/lib/thrift/union.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
data/lib/thrift/uuid.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
data/lib/thrift.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
@@ -185,3 +185,19 @@ struct NestedMapInMapKey {
185
185
  struct NestedMapInMapValue {
186
186
  2: map<byte, map<byte, byte>> value
187
187
  }
188
+
189
+ # Recursive types for the struct/union/exception recursion-depth limit specs (THRIFT-6045).
190
+ struct RecTree {
191
+ 1: list<RecTree> children
192
+ 2: i16 item
193
+ }
194
+
195
+ union RecUnion {
196
+ 1: list<RecUnion> children
197
+ 2: i32 leaf
198
+ }
199
+
200
+ exception RecError {
201
+ 1: list<RecError> children
202
+ 2: i32 leaf
203
+ }
@@ -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
@@ -209,6 +210,25 @@ describe 'BaseProtocol' do
209
210
  expect(@prot).to receive(:read_list_end)
210
211
  real_skip.call(Thrift::Types::LIST)
211
212
  end
213
+
214
+ it "should raise DEPTH_LIMIT when max_depth is exhausted" do
215
+ expect { @prot.skip(Thrift::Types::STRUCT, 0) }.to raise_error(Thrift::ProtocolException) do |e|
216
+ expect(e.type).to eq(Thrift::ProtocolException::DEPTH_LIMIT)
217
+ end
218
+ end
219
+
220
+ it "should skip at max_depth=1 without raising" do
221
+ expect(@prot).to receive(:read_bool).once
222
+ expect { @prot.skip(Thrift::Types::BOOL, 1) }.not_to raise_error
223
+ end
224
+
225
+ it "should reject negative container sizes while skipping" do
226
+ expect(@prot).to receive(:read_list_begin).and_return([Thrift::Types::I32, -1])
227
+
228
+ expect { @prot.skip(Thrift::Types::LIST) }.to raise_error(Thrift::ProtocolException, "Negative size") do |e|
229
+ expect(e.type).to eq(Thrift::ProtocolException::NEGATIVE_SIZE)
230
+ end
231
+ end
212
232
  end
213
233
 
214
234
  describe Thrift::BaseProtocolFactory do
@@ -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
@@ -37,6 +38,25 @@ describe 'BaseTransport' do
37
38
  expect(transport.read_all(40)).to eq("10 lettersfifteen lettersmore characters")
38
39
  end
39
40
 
41
+ it "should coerce reads to binary encoding" do
42
+ transport = Thrift::BaseTransport.new
43
+ expect(transport).to receive(:read).with(3).and_return(+'abc')
44
+
45
+ buf = transport.read_all(3)
46
+
47
+ expect(buf).to eq('abc')
48
+ expect(buf.encoding).to eq(Encoding::BINARY)
49
+ end
50
+
51
+ it "should reject negative read sizes" do
52
+ transport = Thrift::BaseTransport.new
53
+ expect(transport).not_to receive(:read)
54
+
55
+ expect { transport.read_all(-1) }.to raise_error(Thrift::TransportException, "Negative size") do |e|
56
+ expect(e.type).to eq(Thrift::TransportException::NEGATIVE_SIZE)
57
+ end
58
+ end
59
+
40
60
  it "should stub out the rest of the methods" do
41
61
  # can't test for stubbiness, so just make sure they're defined
42
62
  [:open?, :open, :close, :read, :write, :flush].each do |sym|
@@ -286,7 +306,7 @@ describe 'BaseTransport' do
286
306
  end
287
307
 
288
308
  it "should accept a buffer on input and use it directly" do
289
- s = "this is a test"
309
+ s = +"this is a test"
290
310
  @buffer = Thrift::MemoryBufferTransport.new(s)
291
311
  expect(@buffer.read(4)).to eq("this")
292
312
  s.slice!(-4..-1)
@@ -338,6 +358,21 @@ describe 'BaseTransport' do
338
358
  expect(@buffer.read(@buffer.available)).to eq("foo bar")
339
359
  end
340
360
 
361
+ it "should force mutable write buffers into binary in place" do
362
+ s = "abc \u20AC".encode("UTF-8")
363
+ @buffer.write(s)
364
+ expect(s.encoding).to eq(Encoding::BINARY)
365
+ expect(@buffer.read(@buffer.available)).to eq("abc \u20AC".encode("UTF-8").b)
366
+ end
367
+
368
+ it "should not mutate frozen write buffers while forcing binary encoding" do
369
+ s = "abc \u20AC".encode("UTF-8").freeze
370
+ @buffer.write(s)
371
+ expect(s.encoding).to eq(Encoding::UTF_8)
372
+ expect(s).to be_frozen
373
+ expect(@buffer.read(@buffer.available)).to eq("abc \u20AC".encode("UTF-8").b)
374
+ end
375
+
341
376
  it "should throw an EOFError when there isn't enough data in the buffer" do
342
377
  @buffer.reset_buffer("")
343
378
  expect{ @buffer.read(1) }.to raise_error(EOFError)
@@ -345,6 +380,12 @@ describe 'BaseTransport' do
345
380
  @buffer.reset_buffer("1234")
346
381
  expect{ @buffer.read(5) }.to raise_error(EOFError)
347
382
  end
383
+
384
+ it "should reject negative read_all sizes" do
385
+ expect { @buffer.read_all(-1) }.to raise_error(Thrift::TransportException, "Negative size") do |e|
386
+ expect(e.type).to eq(Thrift::TransportException::NEGATIVE_SIZE)
387
+ end
388
+ end
348
389
  end
349
390
 
350
391
  describe Thrift::IOStreamTransport do
@@ -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,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
@@ -109,15 +110,15 @@ shared_examples_for 'a binary protocol' do
109
110
  end
110
111
  end
111
112
 
112
- it "should clip numbers out of signed range" do
113
- (128..255).each do |i|
114
- @prot.write_byte(i)
115
- expect(@trans.read(1)).to eq([i].pack('c'))
113
+ it "should reject bytes outside the signed byte range" do
114
+ [-129, 128, 255, 2**65].each do |i|
115
+ expect { @prot.write_byte(i) }.to raise_error(RangeError)
116
116
  end
117
+ expect(@trans.available).to eq(0)
117
118
  end
118
119
 
119
- it "errors out with a Bignum" do
120
- expect { @prot.write_byte(2**65) }.to raise_error(RangeError)
120
+ it "should reject non-integer bytes" do
121
+ expect { @prot.write_byte(1.5) }.to raise_error(TypeError)
121
122
  end
122
123
 
123
124
  it "should error gracefully when trying to write a nil byte" do
@@ -130,13 +131,19 @@ shared_examples_for 'a binary protocol' do
130
131
  [-2**15, -1024, 17, 0, -10000, 1723, 2**15-1].each do |i|
131
132
  @prot.write_i16(i)
132
133
  end
133
- # and try something out of signed range, it should clip
134
- @prot.write_i16(2**15 + 5)
135
134
 
136
- expect(@trans.read(@trans.available)).to eq("\200\000\374\000\000\021\000\000\330\360\006\273\177\377\200\005")
135
+ expect(@trans.read(@trans.available)).to eq("\200\000\374\000\000\021\000\000\330\360\006\273\177\377")
136
+ end
137
137
 
138
- # a Bignum should error
139
- # lambda { @prot.write_i16(2**65) }.should raise_error(RangeError)
138
+ it "should reject i16 values outside the signed i16 range" do
139
+ [-2**15 - 1, 2**15, 2**65].each do |i|
140
+ expect { @prot.write_i16(i) }.to raise_error(RangeError)
141
+ end
142
+ expect(@trans.available).to eq(0)
143
+ end
144
+
145
+ it "should reject non-integer i16 values" do
146
+ expect { @prot.write_i16(1.5) }.to raise_error(TypeError)
140
147
  end
141
148
 
142
149
  it "should error gracefully when trying to write a nil i16" do
@@ -149,13 +156,16 @@ shared_examples_for 'a binary protocol' do
149
156
  [-2**31, -123123, -2532, -3, 0, 2351235, 12331, 2**31-1].each do |i|
150
157
  @prot.write_i32(i)
151
158
  end
152
- # try something out of signed range, it should clip
153
159
  expect(@trans.read(@trans.available)).to eq("\200\000\000\000" + "\377\376\037\r" + "\377\377\366\034" + "\377\377\377\375" + "\000\000\000\000" + "\000#\340\203" + "\000\0000+" + "\177\377\377\377")
154
- [2 ** 31 + 5, 2 ** 65 + 5].each do |i|
160
+ [-2 ** 31 - 1, 2 ** 31, 2 ** 65 + 5].each do |i|
155
161
  expect { @prot.write_i32(i) }.to raise_error(RangeError)
156
162
  end
157
163
  end
158
164
 
165
+ it "should reject non-integer i32 values" do
166
+ expect { @prot.write_i32(1.5) }.to raise_error(TypeError)
167
+ end
168
+
159
169
  it "should error gracefully when trying to write a nil i32" do
160
170
  expect { @prot.write_i32(nil) }.to raise_error(StandardError, 'nil argument not allowed!')
161
171
  end
@@ -166,7 +176,6 @@ shared_examples_for 'a binary protocol' do
166
176
  [-2**63, -12356123612323, -23512351, -234, 0, 1231, 2351236, 12361236213, 2**63-1].each do |i|
167
177
  @prot.write_i64(i)
168
178
  end
169
- # try something out of signed range, it should clip
170
179
  expect(@trans.read(@trans.available)).to eq(["\200\000\000\000\000\000\000\000",
171
180
  "\377\377\364\303\035\244+]",
172
181
  "\377\377\377\377\376\231:\341",
@@ -176,13 +185,30 @@ shared_examples_for 'a binary protocol' do
176
185
  "\000\000\000\000\000#\340\204",
177
186
  "\000\000\000\002\340\311~\365",
178
187
  "\177\377\377\377\377\377\377\377"].join(""))
179
- expect { @prot.write_i64(2 ** 65 + 5) }.to raise_error(RangeError)
188
+ [-2 ** 63 - 1, 2 ** 63, 2 ** 65 + 5].each do |i|
189
+ expect { @prot.write_i64(i) }.to raise_error(RangeError)
190
+ end
191
+ end
192
+
193
+ it "should reject non-integer i64 values" do
194
+ expect { @prot.write_i64(1.5) }.to raise_error(TypeError)
180
195
  end
181
196
 
182
197
  it "should error gracefully when trying to write a nil i64" do
183
198
  expect { @prot.write_i64(nil) }.to raise_error(StandardError, 'nil argument not allowed!')
184
199
  end
185
200
 
201
+ it "should reject out-of-range field ids" do
202
+ expect { @prot.write_field_begin('foo', Thrift::Types::DOUBLE, -2**15 - 1) }.to raise_error(RangeError)
203
+ expect { @prot.write_field_begin('foo', Thrift::Types::DOUBLE, 2**15) }.to raise_error(RangeError)
204
+ end
205
+
206
+ it "should reject out-of-range container sizes" do
207
+ expect { @prot.write_map_begin(Thrift::Types::STRING, Thrift::Types::LIST, -1) }.to raise_error(RangeError)
208
+ expect { @prot.write_list_begin(Thrift::Types::I16, 2**31) }.to raise_error(RangeError)
209
+ expect { @prot.write_set_begin(Thrift::Types::I16, 2**31) }.to raise_error(RangeError)
210
+ end
211
+
186
212
  it "should write a double" do
187
213
  # try a random scattering of values, including min/max
188
214
  values = [Float::MIN, -1231.15325, -123123.23, -23.23515123, 0, 12351.1325, 523.23, Float::MAX]
@@ -229,6 +255,16 @@ shared_examples_for 'a binary protocol' do
229
255
  expect(a.unpack('C*')).to eq([0x00, 0x00, 0x00, 0x04, 0x00, 0x01, 0x02, 0x03])
230
256
  end
231
257
 
258
+ it 'should write a frozen non-binary string without mutating the input' do
259
+ buffer = "abc \u20AC".encode('UTF-8').freeze
260
+ @prot.write_binary(buffer)
261
+ a = @trans.read(@trans.available)
262
+ expect(buffer.encoding).to eq(Encoding::UTF_8)
263
+ expect(buffer).to be_frozen
264
+ expect(a.encoding).to eq(Encoding::BINARY)
265
+ expect(a.unpack('C*')).to eq([0x00, 0x00, 0x00, 0x07, 0x61, 0x62, 0x63, 0x20, 0xE2, 0x82, 0xAC])
266
+ end
267
+
232
268
  it "should error gracefully when trying to write a nil string" do
233
269
  expect { @prot.write_string(nil) }.to raise_error(StandardError, 'nil argument not allowed!')
234
270
  end
@@ -284,6 +320,13 @@ shared_examples_for 'a binary protocol' do
284
320
  expect(@prot.read_map_begin).to eq([Thrift::Types::DOUBLE, Thrift::Types::I64, 42])
285
321
  end
286
322
 
323
+ it "should reject a negative map size" do
324
+ @trans.write([Thrift::Types::DOUBLE, Thrift::Types::I64, 0xffffffff].pack("ccN"))
325
+ expect { @prot.read_map_begin }.to raise_error(Thrift::ProtocolException, "Negative size") do |e|
326
+ expect(e.type).to eq(Thrift::ProtocolException::NEGATIVE_SIZE)
327
+ end
328
+ end
329
+
287
330
  # map footer is a noop
288
331
 
289
332
  it "should read a list header" do
@@ -291,6 +334,13 @@ shared_examples_for 'a binary protocol' do
291
334
  expect(@prot.read_list_begin).to eq([Thrift::Types::STRING, 17])
292
335
  end
293
336
 
337
+ it "should reject a negative list size" do
338
+ @trans.write([Thrift::Types::STRING, 0xffffffff].pack("cN"))
339
+ expect { @prot.read_list_begin }.to raise_error(Thrift::ProtocolException, "Negative size") do |e|
340
+ expect(e.type).to eq(Thrift::ProtocolException::NEGATIVE_SIZE)
341
+ end
342
+ end
343
+
294
344
  # list footer is a noop
295
345
 
296
346
  it "should read a set header" do
@@ -298,6 +348,13 @@ shared_examples_for 'a binary protocol' do
298
348
  expect(@prot.read_set_begin).to eq([Thrift::Types::STRING, 17])
299
349
  end
300
350
 
351
+ it "should reject a negative set size" do
352
+ @trans.write([Thrift::Types::STRING, 0xffffffff].pack("cN"))
353
+ expect { @prot.read_set_begin }.to raise_error(Thrift::ProtocolException, "Negative size") do |e|
354
+ expect(e.type).to eq(Thrift::ProtocolException::NEGATIVE_SIZE)
355
+ end
356
+ end
357
+
301
358
  # set footer is a noop
302
359
 
303
360
  it "should read a bool" do
@@ -371,6 +428,13 @@ shared_examples_for 'a binary protocol' do
371
428
  expect(a.encoding).to eq(Encoding::BINARY)
372
429
  end
373
430
 
431
+ it "should reject a negative binary size" do
432
+ @trans.write([0xff, 0xff, 0xff, 0xff].pack('C*'))
433
+ expect { @prot.read_binary }.to raise_error(Thrift::ProtocolException, "Negative size") do |e|
434
+ expect(e.type).to eq(Thrift::ProtocolException::NEGATIVE_SIZE)
435
+ end
436
+ end
437
+
374
438
  it "should perform a complete rpc with no args or return" do
375
439
  srv_test(
376
440
  proc { |client| client.send_voidMethod() },
data/spec/bytes_spec.rb CHANGED
@@ -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
@@ -44,6 +45,23 @@ describe Thrift::Bytes do
44
45
  a = Thrift::Bytes.force_binary_encoding e
45
46
  expect(a.encoding).to eq(Encoding::BINARY)
46
47
  end
48
+
49
+ it 'should return the same frozen binary string unchanged' do
50
+ e = 'STRING'.b.freeze
51
+ a = Thrift::Bytes.force_binary_encoding(e)
52
+ expect(a).to equal(e)
53
+ expect(a.encoding).to eq(Encoding::BINARY)
54
+ expect(a).to be_frozen
55
+ end
56
+
57
+ it 'should duplicate a frozen non-binary string before changing encoding' do
58
+ e = 'STRING'.encode('UTF-8').freeze
59
+ a = Thrift::Bytes.force_binary_encoding(e)
60
+ expect(a).not_to equal(e)
61
+ expect(a.encoding).to eq(Encoding::BINARY)
62
+ expect(e.encoding).to eq(Encoding::UTF_8)
63
+ expect(e).to be_frozen
64
+ end
47
65
  end
48
66
 
49
67
  describe '.get_string_byte' do
@@ -56,7 +74,7 @@ describe Thrift::Bytes do
56
74
 
57
75
  describe '.set_string_byte' do
58
76
  it 'should set byte value at index' do
59
- s = "\x41\x42"
77
+ s = "\x41\x42".b
60
78
  Thrift::Bytes.set_string_byte(s, 0, 0x43)
61
79
  expect(s.getbyte(0)).to eq(0x43)
62
80
  expect(s).to eq('CB')
data/spec/client_spec.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
@@ -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
@@ -21,6 +22,16 @@
21
22
  require 'spec_helper'
22
23
 
23
24
  describe Thrift::CompactProtocol do
25
+ INTEGER_BOUNDARY_TESTS = {
26
+ :i32 => [-(2**31), (2**31) - 1],
27
+ :i64 => [-(2**63), (2**63) - 1]
28
+ }
29
+
30
+ INTEGER_MINIMUM_ENCODINGS = {
31
+ :i32 => [0xff, 0xff, 0xff, 0xff, 0x0f],
32
+ :i64 => [0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01]
33
+ }
34
+
24
35
  TESTS = {
25
36
  :byte => (-127..127).to_a,
26
37
  :i16 => (0..14).map { |shift| [1 << shift, -(1 << shift)] }.flatten.sort,
@@ -71,6 +82,54 @@ describe Thrift::CompactProtocol do
71
82
  end
72
83
  end
73
84
 
85
+ it "should round-trip signed integer boundaries correctly" do
86
+ INTEGER_BOUNDARY_TESTS.each_pair do |primitive_type, test_values|
87
+ test_values.each do |value|
88
+ trans = Thrift::MemoryBufferTransport.new
89
+ proto = Thrift::CompactProtocol.new(trans)
90
+
91
+ proto.send(writer(primitive_type), value)
92
+ expect(proto.send(reader(primitive_type))).to eq(value)
93
+ end
94
+ end
95
+ end
96
+
97
+ it "should encode signed integer minima with the canonical zigzag varint bytes" do
98
+ INTEGER_MINIMUM_ENCODINGS.each_pair do |primitive_type, expected_bytes|
99
+ trans = Thrift::MemoryBufferTransport.new
100
+ proto = Thrift::CompactProtocol.new(trans)
101
+
102
+ proto.send(writer(primitive_type), INTEGER_BOUNDARY_TESTS.fetch(primitive_type).first)
103
+ expect(trans.read(trans.available).bytes).to eq(expected_bytes)
104
+ end
105
+ end
106
+
107
+ it "should decode i32 minima from direct canonical zigzag bytes" do
108
+ trans = Thrift::MemoryBufferTransport.new
109
+ trans.write(INTEGER_MINIMUM_ENCODINGS[:i32].pack("C*"))
110
+
111
+ proto = Thrift::CompactProtocol.new(trans)
112
+ expect(proto.read_i32).to eq(INTEGER_BOUNDARY_TESTS[:i32].first)
113
+ end
114
+
115
+ it "should decode i64 minima from direct canonical zigzag bytes" do
116
+ trans = Thrift::MemoryBufferTransport.new
117
+ trans.write(INTEGER_MINIMUM_ENCODINGS[:i64].pack("C*"))
118
+
119
+ proto = Thrift::CompactProtocol.new(trans)
120
+ expect(proto.read_i64).to eq(INTEGER_BOUNDARY_TESTS[:i64].first)
121
+ end
122
+
123
+ it "should read binary values with multi-byte varint32 lengths" do
124
+ payload = "x" * 128
125
+ trans = Thrift::MemoryBufferTransport.new
126
+ trans.write([0x80, 0x01].pack("C*"))
127
+ trans.write(payload)
128
+
129
+ proto = Thrift::CompactProtocol.new(trans)
130
+ expect(proto.read_binary).to eq(payload)
131
+ end
132
+
74
133
  it "should write a uuid" do
75
134
  trans = Thrift::MemoryBufferTransport.new
76
135
  proto = Thrift::CompactProtocol.new(trans)
@@ -170,6 +229,46 @@ describe Thrift::CompactProtocol do
170
229
  expect(Thrift::CompactProtocol.new(trans).to_s).to eq("compact(memory)")
171
230
  end
172
231
 
232
+ it "should write a frozen non-binary string without mutating the input" do
233
+ trans = Thrift::MemoryBufferTransport.new
234
+ prot = Thrift::CompactProtocol.new(trans)
235
+ buffer = "abc \u20AC".encode("UTF-8").freeze
236
+
237
+ prot.write_binary(buffer)
238
+
239
+ expect(buffer.encoding).to eq(Encoding::UTF_8)
240
+ expect(buffer).to be_frozen
241
+ expect(trans.read(trans.available).unpack("C*")).to eq([0x07, 0x61, 0x62, 0x63, 0x20, 0xE2, 0x82, 0xAC])
242
+ end
243
+
244
+ it "should reject a varint with more than 10 continuation bytes" do
245
+ trans = Thrift::MemoryBufferTransport.new(([0x80] * 11).pack("C*"))
246
+ proto = Thrift::CompactProtocol.new(trans)
247
+ expect { proto.read_i64 }.to raise_error(Thrift::ProtocolException) do |e|
248
+ expect(e.type).to eq(Thrift::ProtocolException::INVALID_DATA)
249
+ end
250
+ end
251
+
252
+ it "should accept a valid 10-byte varint" do
253
+ trans = Thrift::MemoryBufferTransport.new((([0x80] * 9) + [0x01]).pack("C*"))
254
+ proto = Thrift::CompactProtocol.new(trans)
255
+ expect { proto.read_i64 }.not_to raise_error
256
+ end
257
+
258
+ it "should reject a 32-bit varint with more than 5 continuation bytes" do
259
+ trans = Thrift::MemoryBufferTransport.new(([0x80] * 6).pack("C*"))
260
+ proto = Thrift::CompactProtocol.new(trans)
261
+ expect { proto.read_i32 }.to raise_error(Thrift::ProtocolException) do |e|
262
+ expect(e.type).to eq(Thrift::ProtocolException::INVALID_DATA)
263
+ end
264
+ end
265
+
266
+ it "should accept a valid 5-byte varint for i32" do
267
+ trans = Thrift::MemoryBufferTransport.new((([0x80] * 4) + [0x0f]).pack("C*"))
268
+ proto = Thrift::CompactProtocol.new(trans)
269
+ expect { proto.read_i32 }.not_to raise_error
270
+ end
271
+
173
272
  class JankyHandler
174
273
  def Janky(i32arg)
175
274
  i32arg * 2
@@ -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
@@ -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
data/spec/flat_spec.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
@@ -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
@@ -158,6 +159,50 @@ describe 'HeaderTransport' do
158
159
  end
159
160
  end
160
161
 
162
+ describe "decompressed size limits" do
163
+ it "should accept a valid max_decompressed_size" do
164
+ expect { @trans.set_max_decompressed_size(1024) }.not_to raise_error
165
+ expect { @trans.set_max_decompressed_size(Thrift::HeaderTransport::MAX_FRAME_SIZE) }.not_to raise_error
166
+ end
167
+
168
+ it "should reject invalid max_decompressed_size" do
169
+ expect { @trans.set_max_decompressed_size(0) }.to raise_error(ArgumentError)
170
+ expect { @trans.set_max_decompressed_size(-1) }.to raise_error(ArgumentError)
171
+ expect { @trans.set_max_decompressed_size(Thrift::HeaderTransport::MAX_FRAME_SIZE + 1) }.to raise_error(ArgumentError)
172
+ end
173
+
174
+ it "should decompress ZLIB payload within the limit" do
175
+ write_buf = Thrift::MemoryBufferTransport.new
176
+ writer = Thrift::HeaderTransport.new(write_buf)
177
+ writer.add_transform(Thrift::HeaderTransformID::ZLIB)
178
+ writer.write("A" * 1_000)
179
+ writer.flush
180
+
181
+ written_data = write_buf.read(write_buf.available)
182
+ read_trans = Thrift::HeaderTransport.new(Thrift::MemoryBufferTransport.new(written_data))
183
+ read_trans.set_max_decompressed_size(2_000)
184
+
185
+ expect(read_trans.read(1_000)).to eq("A" * 1_000)
186
+ end
187
+
188
+ it "should raise SIZE_LIMIT TransportException when decompressed output exceeds the limit" do
189
+ write_buf = Thrift::MemoryBufferTransport.new
190
+ writer = Thrift::HeaderTransport.new(write_buf)
191
+ writer.add_transform(Thrift::HeaderTransformID::ZLIB)
192
+ writer.write("A" * 10_000)
193
+ writer.flush
194
+
195
+ written_data = write_buf.read(write_buf.available)
196
+ read_trans = Thrift::HeaderTransport.new(Thrift::MemoryBufferTransport.new(written_data))
197
+ read_trans.set_max_decompressed_size(100)
198
+
199
+ expect { read_trans.read(1) }.to raise_error(Thrift::TransportException) do |e|
200
+ expect(e.type).to eq(Thrift::TransportException::SIZE_LIMIT)
201
+ expect(e.message).to match(/limit/)
202
+ end
203
+ end
204
+ end
205
+
161
206
  describe "read and frame detection" do
162
207
  it "should detect Header format" do
163
208
  # Write a Header frame
@@ -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,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
@@ -223,14 +224,14 @@ describe 'JsonProtocol' do
223
224
  it 'should write string' do
224
225
  @prot.write_string('this is a test string')
225
226
  a = @trans.read(@trans.available)
226
- expect(a).to eq('"this is a test string"'.force_encoding(Encoding::BINARY))
227
+ expect(a).to eq('"this is a test string"'.b)
227
228
  expect(a.encoding).to eq(Encoding::BINARY)
228
229
  end
229
230
 
230
231
  it 'should write string with unicode characters' do
231
232
  @prot.write_string("this is a test string with unicode characters: \u20AC \u20AD")
232
233
  a = @trans.read(@trans.available)
233
- expect(a).to eq("\"this is a test string with unicode characters: \u20AC \u20AD\"".force_encoding(Encoding::BINARY))
234
+ expect(a).to eq("\"this is a test string with unicode characters: \u20AC \u20AD\"".b)
234
235
  expect(a.encoding).to eq(Encoding::BINARY)
235
236
  end
236
237
 
@@ -442,6 +443,13 @@ describe 'JsonProtocol' do
442
443
  expect(@prot.read_map_begin).to eq([12, 15, 2])
443
444
  end
444
445
 
446
+ it "should reject a negative map size" do
447
+ @trans.write("[\"rec\",\"lst\",-1,{")
448
+ expect { @prot.read_map_begin }.to raise_error(Thrift::ProtocolException, "Negative size") do |e|
449
+ expect(e.type).to eq(Thrift::ProtocolException::NEGATIVE_SIZE)
450
+ end
451
+ end
452
+
445
453
  it "should read map end" do
446
454
  @trans.write("}]")
447
455
  expect(@prot.read_map_end).to eq(nil)
@@ -452,6 +460,13 @@ describe 'JsonProtocol' do
452
460
  expect(@prot.read_list_begin).to eq([12, 2])
453
461
  end
454
462
 
463
+ it "should reject a negative list size" do
464
+ @trans.write("[\"rec\",-1\"\"")
465
+ expect { @prot.read_list_begin }.to raise_error(Thrift::ProtocolException, "Negative size") do |e|
466
+ expect(e.type).to eq(Thrift::ProtocolException::NEGATIVE_SIZE)
467
+ end
468
+ end
469
+
455
470
  it "should read list end" do
456
471
  @trans.write("]")
457
472
  expect(@prot.read_list_end).to eq(nil)
@@ -462,6 +477,13 @@ describe 'JsonProtocol' do
462
477
  expect(@prot.read_set_begin).to eq([12, 2])
463
478
  end
464
479
 
480
+ it "should reject a negative set size" do
481
+ @trans.write("[\"rec\",-1\"\"")
482
+ expect { @prot.read_set_begin }.to raise_error(Thrift::ProtocolException, "Negative size") do |e|
483
+ expect(e.type).to eq(Thrift::ProtocolException::NEGATIVE_SIZE)
484
+ end
485
+ end
486
+
465
487
  it "should read set end" do
466
488
  @trans.write("]")
467
489
  expect(@prot.read_set_end).to eq(nil)
@@ -502,14 +524,14 @@ describe 'JsonProtocol' do
502
524
  end
503
525
 
504
526
  it 'should read string' do
505
- @trans.write('"this is a test string"'.force_encoding(Encoding::BINARY))
527
+ @trans.write('"this is a test string"'.b)
506
528
  a = @prot.read_string
507
529
  expect(a).to eq('this is a test string')
508
530
  expect(a.encoding).to eq(Encoding::UTF_8)
509
531
  end
510
532
 
511
533
  it 'should read string with unicode characters' do
512
- @trans.write('"this is a test string with unicode characters: \u20AC \u20AD"'.force_encoding(Encoding::BINARY))
534
+ @trans.write('"this is a test string with unicode characters: \u20AC \u20AD"'.b)
513
535
  a = @prot.read_string
514
536
  expect(a).to eq("this is a test string with unicode characters: \u20AC \u20AD")
515
537
  expect(a.encoding).to eq(Encoding::UTF_8)