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
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
@@ -21,140 +22,96 @@
21
22
  require 'spec_helper'
22
23
 
23
24
  describe Thrift::Bytes do
24
- if RUBY_VERSION >= '1.9'
25
- describe '.empty_byte_buffer' do
26
- it 'should create an empty buffer' do
27
- b = Thrift::Bytes.empty_byte_buffer
28
- expect(b.length).to eq(0)
29
- expect(b.encoding).to eq(Encoding::BINARY)
30
- end
31
-
32
- it 'should create an empty buffer of given size' do
33
- b = Thrift::Bytes.empty_byte_buffer 2
34
- expect(b.length).to eq(2)
35
- expect(b.getbyte(0)).to eq(0)
36
- expect(b.getbyte(1)).to eq(0)
37
- expect(b.encoding).to eq(Encoding::BINARY)
38
- end
25
+ describe '.empty_byte_buffer' do
26
+ it 'should create an empty buffer' do
27
+ b = Thrift::Bytes.empty_byte_buffer
28
+ expect(b.length).to eq(0)
29
+ expect(b.encoding).to eq(Encoding::BINARY)
39
30
  end
40
31
 
41
- describe '.force_binary_encoding' do
42
- it 'should change encoding' do
43
- e = 'STRING'.encode('UTF-8')
44
- expect(e.encoding).not_to eq(Encoding::BINARY)
45
- a = Thrift::Bytes.force_binary_encoding e
46
- expect(a.encoding).to eq(Encoding::BINARY)
47
- end
32
+ it 'should create an empty buffer of given size' do
33
+ b = Thrift::Bytes.empty_byte_buffer 2
34
+ expect(b.length).to eq(2)
35
+ expect(b.getbyte(0)).to eq(0)
36
+ expect(b.getbyte(1)).to eq(0)
37
+ expect(b.encoding).to eq(Encoding::BINARY)
48
38
  end
39
+ end
49
40
 
50
- describe '.get_string_byte' do
51
- it 'should get the byte at index' do
52
- s = "\x41\x42"
53
- expect(Thrift::Bytes.get_string_byte(s, 0)).to eq(0x41)
54
- expect(Thrift::Bytes.get_string_byte(s, 1)).to eq(0x42)
55
- end
41
+ describe '.force_binary_encoding' do
42
+ it 'should change encoding' do
43
+ e = 'STRING'.encode('UTF-8')
44
+ expect(e.encoding).not_to eq(Encoding::BINARY)
45
+ a = Thrift::Bytes.force_binary_encoding e
46
+ expect(a.encoding).to eq(Encoding::BINARY)
56
47
  end
57
48
 
58
- describe '.set_string_byte' do
59
- it 'should set byte value at index' do
60
- s = "\x41\x42"
61
- Thrift::Bytes.set_string_byte(s, 0, 0x43)
62
- expect(s.getbyte(0)).to eq(0x43)
63
- expect(s).to eq('CB')
64
- end
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
65
55
  end
66
56
 
67
- describe '.convert_to_utf8_byte_buffer' do
68
- it 'should convert UTF-8 String to byte buffer' do
69
- e = "\u20AC".encode('UTF-8') # a string with euro sign character U+20AC
70
- expect(e.length).to eq(1)
71
-
72
- a = Thrift::Bytes.convert_to_utf8_byte_buffer e
73
- expect(a.encoding).to eq(Encoding::BINARY)
74
- expect(a.length).to eq(3)
75
- expect(a.unpack('C*')).to eq([0xE2, 0x82, 0xAC])
76
- end
77
-
78
- it 'should convert ISO-8859-15 String to UTF-8 byte buffer' do
79
- # Assumptions
80
- e = "\u20AC".encode('ISO-8859-15') # a string with euro sign character U+20AC, then converted to ISO-8859-15
81
- expect(e.length).to eq(1)
82
- expect(e.unpack('C*')).to eq([0xA4]) # euro sign is a different code point in ISO-8859-15
83
-
84
- a = Thrift::Bytes.convert_to_utf8_byte_buffer e
85
- expect(a.encoding).to eq(Encoding::BINARY)
86
- expect(a.length).to eq(3)
87
- expect(a.unpack('C*')).to eq([0xE2, 0x82, 0xAC])
88
- end
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
89
64
  end
65
+ end
90
66
 
91
- describe '.convert_to_string' do
92
- it 'should convert UTF-8 byte buffer to a UTF-8 String' do
93
- e = [0xE2, 0x82, 0xAC].pack("C*")
94
- expect(e.encoding).to eq(Encoding::BINARY)
95
- a = Thrift::Bytes.convert_to_string e
96
- expect(a.encoding).to eq(Encoding::UTF_8)
97
- expect(a).to eq("\u20AC")
98
- end
67
+ describe '.get_string_byte' do
68
+ it 'should get the byte at index' do
69
+ s = "\x41\x42"
70
+ expect(Thrift::Bytes.get_string_byte(s, 0)).to eq(0x41)
71
+ expect(Thrift::Bytes.get_string_byte(s, 1)).to eq(0x42)
99
72
  end
73
+ end
100
74
 
101
- else # RUBY_VERSION
102
- describe '.empty_byte_buffer' do
103
- it 'should create an empty buffer' do
104
- b = Thrift::Bytes.empty_byte_buffer
105
- expect(b.length).to eq(0)
106
- end
107
-
108
- it 'should create an empty buffer of given size' do
109
- b = Thrift::Bytes.empty_byte_buffer 2
110
- expect(b.length).to eq(2)
111
- expect(b[0]).to eq(0)
112
- expect(b[1]).to eq(0)
113
- end
75
+ describe '.set_string_byte' do
76
+ it 'should set byte value at index' do
77
+ s = "\x41\x42".b
78
+ Thrift::Bytes.set_string_byte(s, 0, 0x43)
79
+ expect(s.getbyte(0)).to eq(0x43)
80
+ expect(s).to eq('CB')
114
81
  end
82
+ end
115
83
 
116
- describe '.force_binary_encoding' do
117
- it 'should be a no-op' do
118
- e = 'STRING'
119
- a = Thrift::Bytes.force_binary_encoding e
120
- expect(a).to eq(e)
121
- expect(a).to be(e)
122
- end
123
- end
84
+ describe '.convert_to_utf8_byte_buffer' do
85
+ it 'should convert UTF-8 String to byte buffer' do
86
+ e = "\u20AC".encode('UTF-8') # a string with euro sign character U+20AC
87
+ expect(e.length).to eq(1)
124
88
 
125
- describe '.get_string_byte' do
126
- it 'should get the byte at index' do
127
- s = "\x41\x42"
128
- expect(Thrift::Bytes.get_string_byte(s, 0)).to eq(0x41)
129
- expect(Thrift::Bytes.get_string_byte(s, 1)).to eq(0x42)
130
- end
89
+ a = Thrift::Bytes.convert_to_utf8_byte_buffer e
90
+ expect(a.encoding).to eq(Encoding::BINARY)
91
+ expect(a.length).to eq(3)
92
+ expect(a.unpack('C*')).to eq([0xE2, 0x82, 0xAC])
131
93
  end
132
94
 
133
- describe '.set_string_byte' do
134
- it 'should set byte value at index' do
135
- s = "\x41\x42"
136
- Thrift::Bytes.set_string_byte(s, 0, 0x43)
137
- expect(s[0]).to eq(0x43)
138
- expect(s).to eq('CB')
139
- end
140
- end
95
+ it 'should convert ISO-8859-15 String to UTF-8 byte buffer' do
96
+ # Assumptions
97
+ e = "\u20AC".encode('ISO-8859-15') # a string with euro sign character U+20AC, then converted to ISO-8859-15
98
+ expect(e.length).to eq(1)
99
+ expect(e.unpack('C*')).to eq([0xA4]) # euro sign is a different code point in ISO-8859-15
141
100
 
142
- describe '.convert_to_utf8_byte_buffer' do
143
- it 'should be a no-op' do
144
- e = 'STRING'
145
- a = Thrift::Bytes.convert_to_utf8_byte_buffer e
146
- expect(a).to eq(e)
147
- expect(a).to be(e)
148
- end
101
+ a = Thrift::Bytes.convert_to_utf8_byte_buffer e
102
+ expect(a.encoding).to eq(Encoding::BINARY)
103
+ expect(a.length).to eq(3)
104
+ expect(a.unpack('C*')).to eq([0xE2, 0x82, 0xAC])
149
105
  end
106
+ end
150
107
 
151
- describe '.convert_to_string' do
152
- it 'should be a no-op' do
153
- e = 'STRING'
154
- a = Thrift::Bytes.convert_to_string e
155
- expect(a).to eq(e)
156
- expect(a).to be(e)
157
- end
108
+ describe '.convert_to_string' do
109
+ it 'should convert UTF-8 byte buffer to a UTF-8 String' do
110
+ e = [0xE2, 0x82, 0xAC].pack("C*")
111
+ expect(e.encoding).to eq(Encoding::BINARY)
112
+ a = Thrift::Bytes.convert_to_string e
113
+ expect(a.encoding).to eq(Encoding::UTF_8)
114
+ expect(a).to eq("\u20AC")
158
115
  end
159
116
  end
160
117
  end
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
@@ -20,11 +21,15 @@
20
21
  require 'spec_helper'
21
22
 
22
23
  describe 'Client' do
23
-
24
24
  class ClientSpec
25
25
  include Thrift::Client
26
26
  end
27
27
 
28
+ class EmptyArgs
29
+ def write(_prot)
30
+ end
31
+ end
32
+
28
33
  before(:each) do
29
34
  @prot = double("MockProtocol")
30
35
  @client = ClientSpec.new(@prot)
@@ -53,15 +58,34 @@ describe 'Client' do
53
58
  end
54
59
 
55
60
  it "should increment the sequence id when sending messages" do
56
- pending "it seems sequence ids are completely ignored right now"
57
- @prot.expect(:write_message_begin).with('testMessage', Thrift::MessageTypes::CALL, 0).ordered
58
- @prot.expect(:write_message_begin).with('testMessage2', Thrift::MessageTypes::CALL, 1).ordered
59
- @prot.expect(:write_message_begin).with('testMessage3', Thrift::MessageTypes::CALL, 2).ordered
60
- @prot.stub!(:write_message_end)
61
- @prot.stub!(:trans).and_return double("trans").as_null_object
62
- @client.send_message('testMessage', double("args class").as_null_object)
63
- @client.send_message('testMessage2', double("args class").as_null_object)
64
- @client.send_message('testMessage3', double("args class").as_null_object)
61
+ expect(@prot).to receive(:write_message_begin).with('testMessage', Thrift::MessageTypes::CALL, 0).ordered
62
+ expect(@prot).to receive(:write_message_begin).with('testMessage2', Thrift::MessageTypes::CALL, 1).ordered
63
+ expect(@prot).to receive(:write_message_begin).with('testMessage3', Thrift::MessageTypes::CALL, 2).ordered
64
+ allow(@prot).to receive(:write_message_end)
65
+ allow(@prot).to receive(:trans).and_return(double("trans", :flush => nil))
66
+
67
+ args_class = double("ArgsClass", :new => EmptyArgs.new)
68
+ @client.send_message('testMessage', args_class)
69
+ @client.send_message('testMessage2', args_class)
70
+ @client.send_message('testMessage3', args_class)
71
+ end
72
+
73
+ it "should keep pending reply sequence ids in FIFO order" do
74
+ expect(@prot).to receive(:write_message_begin).with('first', Thrift::MessageTypes::CALL, 0).ordered
75
+ expect(@prot).to receive(:write_message_begin).with('second', Thrift::MessageTypes::CALL, 1).ordered
76
+ allow(@prot).to receive(:write_message_end)
77
+ allow(@prot).to receive(:trans).and_return(double("trans", :flush => nil))
78
+
79
+ args_class = double("ArgsClass", :new => EmptyArgs.new)
80
+ @client.send_message('first', args_class)
81
+ @client.send_message('second', args_class)
82
+
83
+ expect {
84
+ @client.validate_message_begin('first', Thrift::MessageTypes::REPLY, 0, 'first')
85
+ }.not_to raise_error
86
+ expect {
87
+ @client.validate_message_begin('second', Thrift::MessageTypes::REPLY, 1, 'second')
88
+ }.not_to raise_error
65
89
  end
66
90
 
67
91
  it "should receive a test message" do
@@ -73,16 +97,59 @@ describe 'Client' do
73
97
  @client.receive_message(double("MockClass", :new => mock_klass))
74
98
  end
75
99
 
76
- it "should handle received exceptions" do
77
- expect(@prot).to receive(:read_message_begin).and_return [nil, Thrift::MessageTypes::EXCEPTION, 0]
100
+ it "should raise BAD_SEQUENCE_ID for mismatched replies" do
101
+ @client.instance_variable_set(:@pending_seqids, [0])
102
+
103
+ expect {
104
+ @client.validate_message_begin('testMessage', Thrift::MessageTypes::REPLY, 1, 'testMessage')
105
+ }.to raise_error(Thrift::ApplicationException) { |error|
106
+ expect(error.type).to eq(Thrift::ApplicationException::BAD_SEQUENCE_ID)
107
+ }
108
+ end
109
+
110
+ it "should raise WRONG_METHOD_NAME for unexpected replies" do
111
+ @client.instance_variable_set(:@pending_seqids, [0])
112
+
113
+ expect {
114
+ @client.validate_message_begin('otherMessage', Thrift::MessageTypes::REPLY, 0, 'testMessage')
115
+ }.to raise_error(Thrift::ApplicationException) { |error|
116
+ expect(error.type).to eq(Thrift::ApplicationException::WRONG_METHOD_NAME)
117
+ }
118
+ end
119
+
120
+ it "should raise INVALID_MESSAGE_TYPE for non-reply messages" do
121
+ @client.instance_variable_set(:@pending_seqids, [0])
122
+
123
+ expect {
124
+ @client.validate_message_begin('testMessage', Thrift::MessageTypes::CALL, 0, 'testMessage')
125
+ }.to raise_error(Thrift::ApplicationException) { |error|
126
+ expect(error.type).to eq(Thrift::ApplicationException::INVALID_MESSAGE_TYPE)
127
+ }
128
+ end
129
+
130
+ it "should raise received application exceptions" do
78
131
  expect(@prot).to receive(:read_message_end)
79
- expect(Thrift::ApplicationException).to receive(:new) do
80
- StandardError.new.tap do |mock_exc|
81
- expect(mock_exc).to receive(:read).with(@prot)
82
- end
83
- end
84
- fname, mtype, sqeid = @client.receive_message_begin()
85
- expect { @client.handle_exception(mtype) }.to raise_error(StandardError)
132
+ server_exception = Thrift::ApplicationException.new(Thrift::ApplicationException::UNKNOWN, "boom")
133
+ expect(server_exception).to receive(:read).with(@prot)
134
+ expect(Thrift::ApplicationException).to receive(:new).and_return(server_exception)
135
+ @client.instance_variable_set(:@pending_seqids, [0])
136
+
137
+ expect {
138
+ @client.validate_message_begin('testMessage', Thrift::MessageTypes::EXCEPTION, 0, 'testMessage')
139
+ }.to raise_error(Thrift::ApplicationException, "boom")
140
+ expect(@client.instance_variable_get(:@pending_seqids)).to be_empty
141
+ end
142
+
143
+ it "should roll sequence ids across the signed int32 boundary" do
144
+ expect(@prot).to receive(:write_message_begin).with('testMessage', Thrift::MessageTypes::CALL, Thrift::Client::MAX_SEQUENCE_ID).ordered
145
+ expect(@prot).to receive(:write_message_begin).with('testMessage2', Thrift::MessageTypes::CALL, Thrift::Client::MIN_SEQUENCE_ID).ordered
146
+ allow(@prot).to receive(:write_message_end)
147
+ allow(@prot).to receive(:trans).and_return(double("trans", :flush => nil))
148
+
149
+ @client.instance_variable_set(:@seqid, Thrift::Client::MAX_SEQUENCE_ID)
150
+ args_class = double("ArgsClass", :new => EmptyArgs.new)
151
+ @client.send_message('testMessage', args_class)
152
+ @client.send_message('testMessage2', args_class)
86
153
  end
87
154
 
88
155
  it "should close the transport if an error occurs while sending a message" do
@@ -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,24 +22,34 @@
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
- :i16 => (0..14).map {|shift| [1 << shift, -(1 << shift)]}.flatten.sort,
27
- :i32 => (0..30).map {|shift| [1 << shift, -(1 << shift)]}.flatten.sort,
28
- :i64 => (0..62).map {|shift| [1 << shift, -(1 << shift)]}.flatten.sort,
37
+ :i16 => (0..14).map { |shift| [1 << shift, -(1 << shift)] }.flatten.sort,
38
+ :i32 => (0..30).map { |shift| [1 << shift, -(1 << shift)] }.flatten.sort,
39
+ :i64 => (0..62).map { |shift| [1 << shift, -(1 << shift)] }.flatten.sort,
29
40
  :string => ["", "1", "short", "fourteen123456", "fifteen12345678", "unicode characters: \u20AC \u20AD", "1" * 127, "1" * 3000],
30
41
  :binary => ["", "\001", "\001" * 5, "\001" * 14, "\001" * 15, "\001" * 127, "\001" * 3000],
31
42
  :double => [0.0, 1.0, -1.0, 1.1, -1.1, 10000000.1, 1.0/0.0, -1.0/0.0],
32
43
  :bool => [true, false]
33
44
  }
34
-
45
+
35
46
  it "should encode and decode naked primitives correctly" do
36
47
  TESTS.each_pair do |primitive_type, test_values|
37
48
  test_values.each do |value|
38
49
  # puts "testing #{value}" if primitive_type == :i64
39
50
  trans = Thrift::MemoryBufferTransport.new
40
51
  proto = Thrift::CompactProtocol.new(trans)
41
-
52
+
42
53
  proto.send(writer(primitive_type), value)
43
54
  # puts "buf: #{trans.inspect_buffer}" if primitive_type == :i64
44
55
  read_back = proto.send(reader(primitive_type))
@@ -46,7 +57,7 @@ describe Thrift::CompactProtocol do
46
57
  end
47
58
  end
48
59
  end
49
-
60
+
50
61
  it "should encode and decode primitives in fields correctly" do
51
62
  TESTS.each_pair do |primitive_type, test_values|
52
63
  final_primitive_type = primitive_type == :binary ? :string : primitive_type
@@ -71,6 +82,78 @@ 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
+
133
+ it "should write a uuid" do
134
+ trans = Thrift::MemoryBufferTransport.new
135
+ proto = Thrift::CompactProtocol.new(trans)
136
+
137
+ proto.write_uuid("00112233-4455-6677-8899-aabbccddeeff")
138
+ a = trans.read(trans.available)
139
+ expect(a.unpack('C*')).to eq([0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff])
140
+ end
141
+
142
+ it "should read a uuid" do
143
+ trans = Thrift::MemoryBufferTransport.new
144
+ proto = Thrift::CompactProtocol.new(trans)
145
+
146
+ trans.write([0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff].pack('C*'))
147
+ uuid = proto.read_uuid
148
+ expect(uuid).to eq("00112233-4455-6677-8899-aabbccddeeff")
149
+ end
150
+
151
+ it "should error gracefully when trying to write an invalid uuid" do
152
+ trans = Thrift::MemoryBufferTransport.new
153
+ proto = Thrift::CompactProtocol.new(trans)
154
+ expect { proto.write_uuid("invalid") }.to raise_error(Thrift::ProtocolException)
155
+ end
156
+
74
157
  it "should encode and decode a monster struct correctly" do
75
158
  trans = Thrift::MemoryBufferTransport.new
76
159
  proto = Thrift::CompactProtocol.new(trans)
@@ -80,7 +163,7 @@ describe Thrift::CompactProtocol do
80
163
  struct.write(proto)
81
164
 
82
165
  struct2 = Thrift::Test::CompactProtoTestStruct.new
83
- struct2.read(proto)
166
+ struct2.read(proto)
84
167
  expect(struct2).to eq(struct)
85
168
  end
86
169
 
@@ -99,13 +182,27 @@ describe Thrift::CompactProtocol do
99
182
  processor.process(client_out_proto, client_in_proto)
100
183
  expect(client.recv_Janky).to eq(2)
101
184
  end
102
-
185
+
186
+ it "should round-trip wrapped negative seqids in message headers" do
187
+ trans = Thrift::MemoryBufferTransport.new
188
+ writer = Thrift::CompactProtocol.new(trans)
189
+
190
+ writer.write_message_begin("test", Thrift::MessageTypes::CALL, -2147483648)
191
+ writer.write_message_end
192
+
193
+ reader = Thrift::CompactProtocol.new(trans)
194
+ name, type, seqid = reader.read_message_begin
195
+ expect(name).to eq("test")
196
+ expect(type).to eq(Thrift::MessageTypes::CALL)
197
+ expect(seqid).to eq(-2147483648)
198
+ end
199
+
103
200
  it "should deal with fields following fields that have non-delta ids" do
104
201
  brcp = Thrift::Test::BreaksRubyCompactProtocol.new(
105
- :field1 => "blah",
202
+ :field1 => "blah",
106
203
  :field2 => Thrift::Test::BigFieldIdStruct.new(
107
- :field1 => "string1",
108
- :field2 => "string2"),
204
+ :field1 => "string1",
205
+ :field2 => "string2"),
109
206
  :field3 => 3)
110
207
  ser = Thrift::Serializer.new(Thrift::CompactProtocolFactory.new)
111
208
  bytes = ser.serialize(brcp)
@@ -115,7 +212,7 @@ describe Thrift::CompactProtocol do
115
212
  deser.deserialize(brcp2, bytes)
116
213
  expect(brcp2).to eq(brcp)
117
214
  end
118
-
215
+
119
216
  it "should deserialize an empty map to an empty hash" do
120
217
  struct = Thrift::Test::SingleMapTestStruct.new(:i32_map => {})
121
218
  ser = Thrift::Serializer.new(Thrift::CompactProtocolFactory.new)
@@ -126,22 +223,62 @@ describe Thrift::CompactProtocol do
126
223
  deser.deserialize(struct2, bytes)
127
224
  expect(struct).to eq(struct2)
128
225
  end
129
-
226
+
130
227
  it "should provide a reasonable to_s" do
131
228
  trans = Thrift::MemoryBufferTransport.new
132
229
  expect(Thrift::CompactProtocol.new(trans).to_s).to eq("compact(memory)")
133
230
  end
134
-
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
+
135
272
  class JankyHandler
136
273
  def Janky(i32arg)
137
274
  i32arg * 2
138
275
  end
139
276
  end
140
-
277
+
141
278
  def writer(sym)
142
279
  "write_#{sym.to_s}"
143
280
  end
144
-
281
+
145
282
  def reader(sym)
146
283
  "read_#{sym.to_s}"
147
284
  end