thrift 0.22.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 (119) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +235 -17
  3. data/benchmark/benchmark.rb +23 -8
  4. data/benchmark/client.rb +50 -6
  5. data/benchmark/server.rb +46 -7
  6. data/benchmark/thin_server.rb +2 -0
  7. data/ext/binary_protocol_accelerated.c +154 -42
  8. data/ext/bytes.c +14 -0
  9. data/ext/compact_protocol.c +138 -45
  10. data/ext/constants.h +12 -0
  11. data/ext/extconf.rb +17 -8
  12. data/ext/memory_buffer.c +44 -7
  13. data/ext/protocol.c +29 -0
  14. data/ext/protocol.h +35 -0
  15. data/ext/struct.c +48 -17
  16. data/ext/thrift_native.c +28 -3
  17. data/lib/thrift/bytes.rb +70 -100
  18. data/lib/thrift/client.rb +54 -13
  19. data/lib/thrift/exceptions.rb +6 -5
  20. data/lib/thrift/multiplexed_processor.rb +20 -10
  21. data/lib/thrift/processor.rb +7 -6
  22. data/lib/thrift/protocol/base_protocol.rb +52 -21
  23. data/lib/thrift/protocol/binary_protocol.rb +65 -20
  24. data/lib/thrift/protocol/binary_protocol_accelerated.rb +6 -5
  25. data/lib/thrift/protocol/compact_protocol.rb +76 -41
  26. data/lib/thrift/protocol/header_protocol.rb +321 -0
  27. data/lib/thrift/protocol/json_protocol.rb +44 -27
  28. data/lib/thrift/protocol/multiplexed_protocol.rb +6 -5
  29. data/lib/thrift/protocol/protocol_decorator.rb +13 -4
  30. data/lib/thrift/serializer/deserializer.rb +6 -5
  31. data/lib/thrift/serializer/serializer.rb +5 -5
  32. data/lib/thrift/server/base_server.rb +5 -4
  33. data/lib/thrift/server/mongrel_http_server.rb +7 -6
  34. data/lib/thrift/server/nonblocking_server.rb +35 -9
  35. data/lib/thrift/server/simple_server.rb +13 -5
  36. data/lib/thrift/server/thin_http_server.rb +4 -3
  37. data/lib/thrift/server/thread_pool_server.rb +7 -6
  38. data/lib/thrift/server/threaded_server.rb +13 -5
  39. data/lib/thrift/struct.rb +12 -11
  40. data/lib/thrift/struct_union.rb +14 -9
  41. data/lib/thrift/thrift_native.rb +3 -2
  42. data/lib/thrift/transport/base_server_transport.rb +8 -5
  43. data/lib/thrift/transport/base_transport.rb +18 -14
  44. data/lib/thrift/transport/buffered_transport.rb +7 -6
  45. data/lib/thrift/transport/framed_transport.rb +8 -7
  46. data/lib/thrift/transport/header_transport.rb +562 -0
  47. data/lib/thrift/transport/http_client_transport.rb +2 -1
  48. data/lib/thrift/transport/io_stream_transport.rb +4 -3
  49. data/lib/thrift/transport/memory_buffer_transport.rb +13 -6
  50. data/lib/thrift/transport/server_socket.rb +14 -8
  51. data/lib/thrift/transport/socket.rb +126 -60
  52. data/lib/thrift/transport/ssl_server_socket.rb +4 -3
  53. data/lib/thrift/transport/ssl_socket.rb +45 -13
  54. data/lib/thrift/transport/unix_server_socket.rb +9 -5
  55. data/lib/thrift/transport/unix_socket.rb +7 -6
  56. data/lib/thrift/types.rb +10 -6
  57. data/lib/thrift/union.rb +15 -8
  58. data/lib/thrift/uuid.rb +50 -0
  59. data/lib/thrift.rb +4 -1
  60. data/spec/ThriftSpec.thrift +21 -1
  61. data/spec/base_protocol_spec.rb +21 -2
  62. data/spec/base_transport_spec.rb +48 -8
  63. data/spec/binary_protocol_accelerated_spec.rb +1 -0
  64. data/spec/binary_protocol_spec.rb +1 -2
  65. data/spec/binary_protocol_spec_shared.rb +206 -155
  66. data/spec/bytes_spec.rb +71 -114
  67. data/spec/client_spec.rb +86 -19
  68. data/spec/compact_protocol_spec.rb +153 -16
  69. data/spec/constants_demo_spec.rb +102 -0
  70. data/spec/exception_spec.rb +1 -1
  71. data/spec/flat_spec.rb +1 -0
  72. data/spec/header_protocol_spec.rb +476 -0
  73. data/spec/header_transport_spec.rb +431 -0
  74. data/spec/http_client_spec.rb +5 -6
  75. data/spec/json_protocol_spec.rb +69 -47
  76. data/spec/multiplexed_processor_spec.rb +75 -0
  77. data/spec/namespaced_spec.rb +1 -1
  78. data/spec/nonblocking_server_spec.rb +174 -8
  79. data/spec/processor_spec.rb +1 -1
  80. data/spec/recursion_depth_spec.rb +223 -0
  81. data/spec/serializer_spec.rb +1 -1
  82. data/spec/server_socket_spec.rb +38 -1
  83. data/spec/server_spec.rb +60 -9
  84. data/spec/socket_spec.rb +119 -13
  85. data/spec/socket_spec_shared.rb +73 -9
  86. data/spec/spec_helper.rb +2 -1
  87. data/spec/ssl_server_socket_spec.rb +52 -1
  88. data/spec/ssl_socket_spec.rb +181 -11
  89. data/spec/struct_nested_containers_spec.rb +2 -2
  90. data/spec/struct_spec.rb +114 -9
  91. data/spec/support/header_protocol_helper.rb +55 -0
  92. data/spec/thin_http_server_spec.rb +4 -18
  93. data/spec/types_spec.rb +26 -26
  94. data/spec/union_spec.rb +70 -11
  95. data/spec/unix_socket_spec.rb +17 -2
  96. data/spec/uuid_validation_spec.rb +239 -0
  97. data/test/fuzz/Makefile +779 -0
  98. data/test/fuzz/Makefile.am +173 -0
  99. data/test/fuzz/Makefile.in +775 -0
  100. data/test/fuzz/README.md +149 -0
  101. data/test/fuzz/fuzz_common.rb +96 -0
  102. data/{lib/thrift/core_ext.rb → test/fuzz/fuzz_parse_binary_protocol.rb} +4 -4
  103. data/{lib/thrift/core_ext/fixnum.rb → test/fuzz/fuzz_parse_binary_protocol_accelerated.rb} +7 -13
  104. data/test/fuzz/fuzz_parse_binary_protocol_accelerated_harness.rb +23 -0
  105. data/test/fuzz/fuzz_parse_binary_protocol_harness.rb +23 -0
  106. data/test/fuzz/fuzz_parse_compact_protocol.rb +23 -0
  107. data/test/fuzz/fuzz_parse_compact_protocol_harness.rb +23 -0
  108. data/test/fuzz/fuzz_parse_json_protocol.rb +23 -0
  109. data/test/fuzz/fuzz_parse_json_protocol_harness.rb +23 -0
  110. data/test/fuzz/fuzz_roundtrip_binary_protocol.rb +23 -0
  111. data/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated.rb +23 -0
  112. data/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated_harness.rb +23 -0
  113. data/test/fuzz/fuzz_roundtrip_binary_protocol_harness.rb +23 -0
  114. data/test/fuzz/fuzz_roundtrip_compact_protocol.rb +23 -0
  115. data/test/fuzz/fuzz_roundtrip_compact_protocol_harness.rb +23 -0
  116. data/test/fuzz/fuzz_roundtrip_json_protocol.rb +23 -0
  117. data/test/fuzz/fuzz_roundtrip_json_protocol_harness.rb +23 -0
  118. data/test/fuzz/fuzz_tracer.rb +29 -0
  119. metadata +105 -70
@@ -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,7 +22,6 @@
21
22
  require 'spec_helper'
22
23
 
23
24
  describe 'JsonProtocol' do
24
-
25
25
  describe Thrift::JsonProtocol do
26
26
  before(:each) do
27
27
  @trans = Thrift::MemoryBufferTransport.new
@@ -221,25 +221,18 @@ describe 'JsonProtocol' do
221
221
  expect(@trans.read(@trans.available)).to eq("\"-Infinity\"")
222
222
  end
223
223
 
224
- if RUBY_VERSION >= '1.9'
225
- it 'should write string' do
226
- @prot.write_string('this is a test string')
227
- a = @trans.read(@trans.available)
228
- expect(a).to eq('"this is a test string"'.force_encoding(Encoding::BINARY))
229
- expect(a.encoding).to eq(Encoding::BINARY)
230
- end
224
+ it 'should write string' do
225
+ @prot.write_string('this is a test string')
226
+ a = @trans.read(@trans.available)
227
+ expect(a).to eq('"this is a test string"'.b)
228
+ expect(a.encoding).to eq(Encoding::BINARY)
229
+ end
231
230
 
232
- it 'should write string with unicode characters' do
233
- @prot.write_string("this is a test string with unicode characters: \u20AC \u20AD")
234
- a = @trans.read(@trans.available)
235
- expect(a).to eq("\"this is a test string with unicode characters: \u20AC \u20AD\"".force_encoding(Encoding::BINARY))
236
- expect(a.encoding).to eq(Encoding::BINARY)
237
- end
238
- else
239
- it 'should write string' do
240
- @prot.write_string('this is a test string')
241
- expect(@trans.read(@trans.available)).to eq('"this is a test string"')
242
- end
231
+ it 'should write string with unicode characters' do
232
+ @prot.write_string("this is a test string with unicode characters: \u20AC \u20AD")
233
+ a = @trans.read(@trans.available)
234
+ expect(a).to eq("\"this is a test string with unicode characters: \u20AC \u20AD\"".b)
235
+ expect(a.encoding).to eq(Encoding::BINARY)
243
236
  end
244
237
 
245
238
  it "should write binary" do
@@ -252,9 +245,14 @@ describe 'JsonProtocol' do
252
245
  expect(@trans.read(@trans.available)).to eq("\"AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w==\"")
253
246
  end
254
247
 
248
+ it "should write a uuid" do
249
+ @prot.write_uuid("00112233-4455-6677-8899-aabbccddeeff")
250
+ expect(@trans.read(@trans.available)).to eq("\"00112233-4455-6677-8899-aabbccddeeff\"")
251
+ end
252
+
255
253
  it "should get type name for type id" do
256
- expect {@prot.get_type_name_for_type_id(Thrift::Types::STOP)}.to raise_error(NotImplementedError)
257
- expect {@prot.get_type_name_for_type_id(Thrift::Types::VOID)}.to raise_error(NotImplementedError)
254
+ expect { @prot.get_type_name_for_type_id(Thrift::Types::STOP) }.to raise_error(NotImplementedError)
255
+ expect { @prot.get_type_name_for_type_id(Thrift::Types::VOID) }.to raise_error(NotImplementedError)
258
256
  expect(@prot.get_type_name_for_type_id(Thrift::Types::BOOL)).to eq("tf")
259
257
  expect(@prot.get_type_name_for_type_id(Thrift::Types::BYTE)).to eq("i8")
260
258
  expect(@prot.get_type_name_for_type_id(Thrift::Types::DOUBLE)).to eq("dbl")
@@ -269,7 +267,7 @@ describe 'JsonProtocol' do
269
267
  end
270
268
 
271
269
  it "should get type id for type name" do
272
- expect {@prot.get_type_id_for_type_name("pp")}.to raise_error(NotImplementedError)
270
+ expect { @prot.get_type_id_for_type_name("pp") }.to raise_error(NotImplementedError)
273
271
  expect(@prot.get_type_id_for_type_name("tf")).to eq(Thrift::Types::BOOL)
274
272
  expect(@prot.get_type_id_for_type_name("i8")).to eq(Thrift::Types::BYTE)
275
273
  expect(@prot.get_type_id_for_type_name("dbl")).to eq(Thrift::Types::DOUBLE)
@@ -285,7 +283,7 @@ describe 'JsonProtocol' do
285
283
 
286
284
  it "should read json syntax char" do
287
285
  @trans.write('F')
288
- expect {@prot.read_json_syntax_char('G')}.to raise_error(Thrift::ProtocolException)
286
+ expect { @prot.read_json_syntax_char('G') }.to raise_error(Thrift::ProtocolException)
289
287
  @trans.write('H')
290
288
  @prot.read_json_syntax_char('H')
291
289
  end
@@ -321,7 +319,7 @@ describe 'JsonProtocol' do
321
319
 
322
320
  it "should read json string" do
323
321
  @trans.write("\"\\P")
324
- expect {@prot.read_json_string(false)}.to raise_error(Thrift::ProtocolException)
322
+ expect { @prot.read_json_string(false) }.to raise_error(Thrift::ProtocolException)
325
323
 
326
324
  @trans.write("\"this is a test string\"")
327
325
  expect(@prot.read_json_string).to eq("this is a test string")
@@ -358,7 +356,7 @@ describe 'JsonProtocol' do
358
356
 
359
357
  it "should read json integer" do
360
358
  @trans.write("1.45\"\"")
361
- expect {@prot.read_json_integer}.to raise_error(Thrift::ProtocolException)
359
+ expect { @prot.read_json_integer }.to raise_error(Thrift::ProtocolException)
362
360
  @prot.read_string
363
361
 
364
362
  @trans.write("1453T")
@@ -367,11 +365,11 @@ describe 'JsonProtocol' do
367
365
 
368
366
  it "should read json double" do
369
367
  @trans.write("1.45e3e01\"\"")
370
- expect {@prot.read_json_double}.to raise_error(Thrift::ProtocolException)
368
+ expect { @prot.read_json_double }.to raise_error(Thrift::ProtocolException)
371
369
  @prot.read_string
372
370
 
373
371
  @trans.write("\"1.453e01\"")
374
- expect {@prot.read_json_double}.to raise_error(Thrift::ProtocolException)
372
+ expect { @prot.read_json_double }.to raise_error(Thrift::ProtocolException)
375
373
 
376
374
  @trans.write("1.453e01\"\"")
377
375
  expect(@prot.read_json_double).to eq(14.53)
@@ -409,7 +407,7 @@ describe 'JsonProtocol' do
409
407
 
410
408
  it "should read_message_begin" do
411
409
  @trans.write("[2,")
412
- expect {@prot.read_message_begin}.to raise_error(Thrift::ProtocolException)
410
+ expect { @prot.read_message_begin }.to raise_error(Thrift::ProtocolException)
413
411
 
414
412
  @trans.write("[1,\"name\",12,32\"\"")
415
413
  expect(@prot.read_message_begin).to eq(["name", 12, 32])
@@ -445,6 +443,13 @@ describe 'JsonProtocol' do
445
443
  expect(@prot.read_map_begin).to eq([12, 15, 2])
446
444
  end
447
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
+
448
453
  it "should read map end" do
449
454
  @trans.write("}]")
450
455
  expect(@prot.read_map_end).to eq(nil)
@@ -455,6 +460,13 @@ describe 'JsonProtocol' do
455
460
  expect(@prot.read_list_begin).to eq([12, 2])
456
461
  end
457
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
+
458
470
  it "should read list end" do
459
471
  @trans.write("]")
460
472
  expect(@prot.read_list_end).to eq(nil)
@@ -465,6 +477,13 @@ describe 'JsonProtocol' do
465
477
  expect(@prot.read_set_begin).to eq([12, 2])
466
478
  end
467
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
+
468
487
  it "should read set end" do
469
488
  @trans.write("]")
470
489
  expect(@prot.read_set_end).to eq(nil)
@@ -504,25 +523,18 @@ describe 'JsonProtocol' do
504
523
  expect(@prot.read_double).to eq(12.23)
505
524
  end
506
525
 
507
- if RUBY_VERSION >= '1.9'
508
- it 'should read string' do
509
- @trans.write('"this is a test string"'.force_encoding(Encoding::BINARY))
510
- a = @prot.read_string
511
- expect(a).to eq('this is a test string')
512
- expect(a.encoding).to eq(Encoding::UTF_8)
513
- end
526
+ it 'should read string' do
527
+ @trans.write('"this is a test string"'.b)
528
+ a = @prot.read_string
529
+ expect(a).to eq('this is a test string')
530
+ expect(a.encoding).to eq(Encoding::UTF_8)
531
+ end
514
532
 
515
- it 'should read string with unicode characters' do
516
- @trans.write('"this is a test string with unicode characters: \u20AC \u20AD"'.force_encoding(Encoding::BINARY))
517
- a = @prot.read_string
518
- expect(a).to eq("this is a test string with unicode characters: \u20AC \u20AD")
519
- expect(a.encoding).to eq(Encoding::UTF_8)
520
- end
521
- else
522
- it 'should read string' do
523
- @trans.write('"this is a test string"')
524
- expect(@prot.read_string).to eq('this is a test string')
525
- end
533
+ it 'should read string with unicode characters' do
534
+ @trans.write('"this is a test string with unicode characters: \u20AC \u20AD"'.b)
535
+ a = @prot.read_string
536
+ expect(a).to eq("this is a test string with unicode characters: \u20AC \u20AD")
537
+ expect(a.encoding).to eq(Encoding::UTF_8)
526
538
  end
527
539
 
528
540
  it "should read binary" do
@@ -534,7 +546,17 @@ describe 'JsonProtocol' do
534
546
  @trans.write("\"AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w==\"")
535
547
  expect(@prot.read_binary.bytes.to_a).to eq((0...256).to_a)
536
548
  end
537
-
549
+
550
+ it "should read a uuid" do
551
+ @trans.write("\"00112233-4455-6677-8899-aabbccddeeff\"")
552
+ expect(@prot.read_uuid).to eq("00112233-4455-6677-8899-aabbccddeeff")
553
+ end
554
+
555
+ it "should normalize uppercase uuid on read" do
556
+ @trans.write("\"00112233-4455-6677-8899-AABBCCDDEEFF\"")
557
+ expect(@prot.read_uuid).to eq("00112233-4455-6677-8899-aabbccddeeff")
558
+ end
559
+
538
560
  it "should provide a reasonable to_s" do
539
561
  expect(@prot.to_s).to eq("json(memory)")
540
562
  end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
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
+ require 'spec_helper'
22
+
23
+ describe Thrift::MultiplexedProcessor do
24
+ before(:each) do
25
+ @processor = Thrift::MultiplexedProcessor.new
26
+ @iprot = double('MockInputProtocol')
27
+ @oprot = double('MockOutputProtocol')
28
+ end
29
+
30
+ it 'dispatches multiplexed calls to the registered service processor' do
31
+ actual_processor = double('ActualProcessor')
32
+ @processor.register_processor('ThriftTest', actual_processor)
33
+
34
+ expect(@iprot).to receive(:read_message_begin).and_return(['ThriftTest:testVoid', Thrift::MessageTypes::CALL, 1])
35
+ expect(actual_processor).to receive(:process) do |stored_protocol, oprot|
36
+ expect(stored_protocol.read_message_begin).to eq(['testVoid', Thrift::MessageTypes::CALL, 1])
37
+ expect(oprot).to eq(@oprot)
38
+ true
39
+ end
40
+
41
+ expect(@processor.process(@iprot, @oprot)).to eq(true)
42
+ end
43
+
44
+ it 'dispatches non-multiplexed calls to the default processor' do
45
+ default_processor = double('DefaultProcessor')
46
+ @processor.register_default(default_processor)
47
+
48
+ expect(@iprot).to receive(:read_message_begin).and_return(['testVoid', Thrift::MessageTypes::CALL, 2])
49
+ expect(default_processor).to receive(:process) do |stored_protocol, oprot|
50
+ expect(stored_protocol.read_message_begin).to eq(['testVoid', Thrift::MessageTypes::CALL, 2])
51
+ expect(oprot).to eq(@oprot)
52
+ true
53
+ end
54
+
55
+ expect(@processor.process(@iprot, @oprot)).to eq(true)
56
+ end
57
+
58
+ it 'raises for non-multiplexed calls when no default processor is registered' do
59
+ expect(@iprot).to receive(:read_message_begin).and_return(['testVoid', Thrift::MessageTypes::CALL, 3])
60
+
61
+ expect { @processor.process(@iprot, @oprot) }.to raise_error(
62
+ Thrift::Exception,
63
+ 'Service name not found in message name: testVoid. Did you forget to use a Thrift::Protocol::MultiplexedProtocol in your client?'
64
+ )
65
+ end
66
+
67
+ it 'raises for unknown multiplexed service names' do
68
+ expect(@iprot).to receive(:read_message_begin).and_return(['Missing:testVoid', Thrift::MessageTypes::CALL, 4])
69
+
70
+ expect { @processor.process(@iprot, @oprot) }.to raise_error(
71
+ Thrift::Exception,
72
+ 'Service name not found: Missing. Did you forget to call Thrift::MultiplexedProcessor#register_processor?'
73
+ )
74
+ end
75
+ 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
@@ -63,5 +64,4 @@ describe 'namespaced generation' do
63
64
  it "extended a service" do
64
65
  require "extended/extended_service"
65
66
  end
66
-
67
67
  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
@@ -18,9 +19,9 @@
18
19
  #
19
20
 
20
21
  require 'spec_helper'
22
+ require 'timeout'
21
23
 
22
24
  describe 'NonblockingServer' do
23
-
24
25
  class Handler
25
26
  def initialize
26
27
  @queue = Queue.new
@@ -76,7 +77,7 @@ describe 'NonblockingServer' do
76
77
  @transport.read(sz)
77
78
  end
78
79
 
79
- def write(buf,sz=nil)
80
+ def write(buf, sz = nil)
80
81
  @transport.write(buf, sz)
81
82
  end
82
83
 
@@ -101,7 +102,7 @@ describe 'NonblockingServer' do
101
102
 
102
103
  describe Thrift::NonblockingServer do
103
104
  before(:each) do
104
- @port = 43251
105
+ @port = available_port
105
106
  handler = Handler.new
106
107
  processor = SpecNamespace::NonblockingService::Processor.new(handler)
107
108
  queue = Queue.new
@@ -121,6 +122,7 @@ describe 'NonblockingServer' do
121
122
  end
122
123
  end
123
124
  queue.pop
125
+ wait_until_listening(@transport, @server_thread)
124
126
 
125
127
  @clients = []
126
128
  @catch_exceptions = false
@@ -128,9 +130,11 @@ describe 'NonblockingServer' do
128
130
 
129
131
  after(:each) do
130
132
  @clients.each { |client, trans| trans.close }
131
- # @server.shutdown(1)
132
- @server_thread.kill
133
- @transport.close
133
+ @server.shutdown(1, false) if @server
134
+ @server_thread.join(2) if @server_thread
135
+ @server_thread.kill if @server_thread && @server_thread.alive?
136
+ @server_thread.join(2) if @server_thread
137
+ @transport.close if @transport
134
138
  end
135
139
 
136
140
  def setup_client(queue = nil)
@@ -166,7 +170,7 @@ describe 'NonblockingServer' do
166
170
  break
167
171
  end
168
172
  end
169
- @clients.each { |c,t| t.close and break if c == client } #close the transport
173
+ @clients.each { |c, t| t.close and break if c == client } # close the transport
170
174
  rescue => e
171
175
  raise e unless @catch_exceptions
172
176
  end
@@ -245,7 +249,7 @@ describe 'NonblockingServer' do
245
249
  it "should kill active messages when they don't expire while shutting down" do
246
250
  result = Queue.new
247
251
  client = setup_client_thread(result)
248
- client << [:sleep, 10]
252
+ client << [:sleep, 10.0]
249
253
  sleep 0.1 # start processing the client's message
250
254
  @server.shutdown(1)
251
255
  @catch_exceptions = true
@@ -260,4 +264,166 @@ describe 'NonblockingServer' do
260
264
  expect(@server_thread.join(2)).not_to be_nil
261
265
  end
262
266
  end
267
+
268
+ describe Thrift::NonblockingServer::IOManager do
269
+ def build_io_manager
270
+ logger = Logger.new(IO::NULL)
271
+ logger.level = Logger::FATAL
272
+ Thrift::NonblockingServer::IOManager.new(
273
+ double('processor'),
274
+ double('server_transport'),
275
+ Thrift::BaseTransportFactory.new,
276
+ Thrift::BinaryProtocolFactory.new,
277
+ 1,
278
+ logger
279
+ )
280
+ end
281
+
282
+ it "closes tracked connections and signal pipes during forced cleanup" do
283
+ io_manager = build_io_manager
284
+ connection = double('connection', :close => nil)
285
+ pipe_a = double('pipe_a', :closed? => false, :close => nil)
286
+ pipe_b = double('pipe_b', :closed? => false, :close => nil)
287
+
288
+ io_manager.instance_variable_set(:@connections, [connection])
289
+ io_manager.instance_variable_set(:@buffers, { connection => 'frame' })
290
+ io_manager.instance_variable_set(:@signal_pipes, [pipe_a, pipe_b])
291
+ io_manager.instance_variable_set(:@worker_threads, [])
292
+
293
+ io_manager.ensure_closed
294
+
295
+ expect(connection).to have_received(:close)
296
+ expect(pipe_a).to have_received(:close)
297
+ expect(pipe_b).to have_received(:close)
298
+ expect(io_manager.instance_variable_get(:@connections)).to be_empty
299
+ expect(io_manager.instance_variable_get(:@buffers)).to be_empty
300
+ end
301
+
302
+ it "continues closing remaining signal pipes when one close raises" do
303
+ io_manager = build_io_manager
304
+ pipe_a = double('pipe_a', :closed? => false)
305
+ pipe_b = double('pipe_b', :closed? => false, :close => nil)
306
+
307
+ allow(pipe_a).to receive(:close).and_raise(IOError)
308
+
309
+ io_manager.instance_variable_set(:@signal_pipes, [pipe_a, pipe_b])
310
+ io_manager.instance_variable_set(:@worker_threads, [])
311
+
312
+ io_manager.send(:close_signal_pipes)
313
+
314
+ expect(pipe_a).to have_received(:close)
315
+ expect(pipe_b).to have_received(:close)
316
+ end
317
+
318
+ it "drops removed connections from bookkeeping" do
319
+ io_manager = build_io_manager
320
+ connection = double('connection', :close => nil)
321
+
322
+ io_manager.instance_variable_set(:@connections, [connection])
323
+ io_manager.instance_variable_set(:@buffers, { connection => 'frame' })
324
+
325
+ io_manager.send(:remove_connection, connection)
326
+
327
+ expect(io_manager.instance_variable_get(:@connections)).to be_empty
328
+ expect(io_manager.instance_variable_get(:@buffers)).to be_empty
329
+ end
330
+ end
331
+
332
+ describe "#{Thrift::NonblockingServer} with TLS transport" do
333
+ before(:each) do
334
+ @port = available_port
335
+ handler = Handler.new
336
+ processor = SpecNamespace::NonblockingService::Processor.new(handler)
337
+ @transport = Thrift::SSLServerSocket.new('localhost', @port, create_server_ssl_context)
338
+ transport_factory = Thrift::FramedTransportFactory.new
339
+ logger = Logger.new(STDERR)
340
+ logger.level = Logger::WARN
341
+ @server = Thrift::NonblockingServer.new(processor, @transport, transport_factory, nil, 5, logger)
342
+ handler.server = @server
343
+
344
+ @server_thread = Thread.new(Thread.current) do |master_thread|
345
+ begin
346
+ @server.serve
347
+ rescue => e
348
+ master_thread.raise e
349
+ end
350
+ end
351
+
352
+ @clients = []
353
+ wait_until_listening(@transport, @server_thread)
354
+ end
355
+
356
+ after(:each) do
357
+ @clients.each(&:close)
358
+ @server.shutdown if @server
359
+ @server_thread.join(2) if @server_thread
360
+ @transport.close if @transport
361
+ end
362
+
363
+ it "should handle requests over TLS" do
364
+ expect(@server_thread).to be_alive
365
+
366
+ client = setup_tls_client
367
+ expect(client.greeting(true)).to eq(SpecNamespace::Hello.new)
368
+
369
+ @server.shutdown
370
+ expect(@server_thread.join(2)).to be_an_instance_of(Thread)
371
+ end
372
+
373
+ def setup_tls_client
374
+ transport = Thrift::FramedTransport.new(
375
+ Thrift::SSLSocket.new('localhost', @port, nil, create_client_ssl_context)
376
+ )
377
+ protocol = Thrift::BinaryProtocol.new(transport)
378
+ client = SpecNamespace::NonblockingService::Client.new(protocol)
379
+ transport.open
380
+ @clients << transport
381
+ client
382
+ end
383
+
384
+ def ssl_keys_dir
385
+ File.expand_path('../../../test/keys', __dir__)
386
+ end
387
+
388
+ def create_server_ssl_context
389
+ OpenSSL::SSL::SSLContext.new.tap do |ctx|
390
+ ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
391
+ if ctx.respond_to?(:min_version=) && OpenSSL::SSL.const_defined?(:TLS1_2_VERSION)
392
+ ctx.min_version = OpenSSL::SSL::TLS1_2_VERSION
393
+ end
394
+ ctx.ca_file = File.join(ssl_keys_dir, 'CA.pem')
395
+ ctx.cert = OpenSSL::X509::Certificate.new(File.read(File.join(ssl_keys_dir, 'server.crt')))
396
+ ctx.cert_store = OpenSSL::X509::Store.new
397
+ ctx.cert_store.add_file(File.join(ssl_keys_dir, 'client.pem'))
398
+ ctx.key = OpenSSL::PKey::RSA.new(File.read(File.join(ssl_keys_dir, 'server.key')))
399
+ end
400
+ end
401
+
402
+ def create_client_ssl_context
403
+ OpenSSL::SSL::SSLContext.new.tap do |ctx|
404
+ ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
405
+ if ctx.respond_to?(:min_version=) && OpenSSL::SSL.const_defined?(:TLS1_2_VERSION)
406
+ ctx.min_version = OpenSSL::SSL::TLS1_2_VERSION
407
+ end
408
+ ctx.ca_file = File.join(ssl_keys_dir, 'CA.pem')
409
+ ctx.cert = OpenSSL::X509::Certificate.new(File.read(File.join(ssl_keys_dir, 'client.crt')))
410
+ ctx.cert_store = OpenSSL::X509::Store.new
411
+ ctx.cert_store.add_file(File.join(ssl_keys_dir, 'server.pem'))
412
+ ctx.key = OpenSSL::PKey::RSA.new(File.read(File.join(ssl_keys_dir, 'client.key')))
413
+ end
414
+ end
415
+ end
416
+
417
+ def wait_until_listening(server_transport, server_thread)
418
+ Timeout.timeout(2) do
419
+ until server_transport.handle
420
+ raise "Server thread exited unexpectedly" unless server_thread.alive?
421
+ sleep 0.01
422
+ end
423
+ end
424
+ end
425
+
426
+ def available_port
427
+ TCPServer.open('localhost', 0) { |server| server.addr[1] }
428
+ end
263
429
  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
@@ -20,7 +21,6 @@
20
21
  require 'spec_helper'
21
22
 
22
23
  describe 'Processor' do
23
-
24
24
  class ProcessorSpec
25
25
  include Thrift::Processor
26
26
  end