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
@@ -0,0 +1,55 @@
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
+ module HeaderProtocolHelper
22
+ def varint32(n)
23
+ bytes = []
24
+ loop do
25
+ if (n & ~0x7f) == 0
26
+ bytes << n
27
+ break
28
+ else
29
+ bytes << ((n & 0x7f) | 0x80)
30
+ n >>= 7
31
+ end
32
+ end
33
+ bytes.pack('C*')
34
+ end
35
+
36
+ def build_header_frame(header_data, payload = Thrift::Bytes.empty_byte_buffer, header_words: nil)
37
+ header_data = header_data.b
38
+ if header_words.nil?
39
+ padding = (4 - (header_data.bytesize % 4)) % 4
40
+ header_data += "\x00" * padding
41
+ header_words = header_data.bytesize / 4
42
+ end
43
+
44
+ frame_size = 2 + 2 + 4 + 2 + header_data.bytesize + payload.bytesize
45
+ frame = Thrift::Bytes.empty_byte_buffer
46
+ frame << [frame_size].pack('N')
47
+ frame << [Thrift::HeaderTransport::HEADER_MAGIC].pack('n')
48
+ frame << [0].pack('n')
49
+ frame << [0].pack('N')
50
+ frame << [header_words].pack('n')
51
+ frame << header_data
52
+ frame << payload
53
+ frame
54
+ end
55
+ end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  #
2
3
  # Licensed to the Apache Software Foundation (ASF) under one
3
4
  # or more contributor license agreements. See the NOTICE file
@@ -22,13 +23,10 @@ require 'rack/test'
22
23
  require 'thrift/server/thin_http_server'
23
24
 
24
25
  describe Thrift::ThinHTTPServer do
25
-
26
26
  let(:processor) { double('processor') }
27
27
 
28
28
  describe "#initialize" do
29
-
30
29
  context "when using the defaults" do
31
-
32
30
  it "binds to port 80, with host 0.0.0.0, a path of '/'" do
33
31
  expect(Thin::Server).to receive(:new).with('0.0.0.0', 80, an_instance_of(Rack::Builder))
34
32
  Thrift::ThinHTTPServer.new(processor)
@@ -43,11 +41,9 @@ describe Thrift::ThinHTTPServer do
43
41
  expect(Thrift::BinaryProtocolFactory).to receive(:new)
44
42
  Thrift::ThinHTTPServer.new(processor)
45
43
  end
46
-
47
44
  end
48
45
 
49
46
  context "when using the options" do
50
-
51
47
  it 'accepts :ip, :port, :path' do
52
48
  ip = "192.168.0.1"
53
49
  port = 3000
@@ -64,13 +60,10 @@ describe Thrift::ThinHTTPServer do
64
60
  Thrift::ThinHTTPServer.new(processor,
65
61
  :protocol_factory => Thrift::JsonProtocolFactory.new)
66
62
  end
67
-
68
63
  end
69
-
70
64
  end
71
65
 
72
66
  describe "#serve" do
73
-
74
67
  it 'starts the Thin server' do
75
68
  underlying_thin_server = double('thin server', :start => true)
76
69
  allow(Thin::Server).to receive(:new).and_return(underlying_thin_server)
@@ -81,7 +74,6 @@ describe Thrift::ThinHTTPServer do
81
74
  thin_thrift_server.serve
82
75
  end
83
76
  end
84
-
85
77
  end
86
78
 
87
79
  describe Thrift::ThinHTTPServer::RackApplication do
@@ -95,23 +87,20 @@ describe Thrift::ThinHTTPServer::RackApplication do
95
87
  end
96
88
 
97
89
  context "404 response" do
98
-
99
90
  it 'receives a non-POST' do
100
91
  header('Content-Type', "application/x-thrift")
101
92
  get "/"
102
- expect(last_response.status).to be 404
93
+ expect(last_response.status).to eq 404
103
94
  end
104
95
 
105
96
  it 'receives a header other than application/x-thrift' do
106
97
  header('Content-Type', "application/json")
107
98
  post "/"
108
- expect(last_response.status).to be 404
99
+ expect(last_response.status).to eq 404
109
100
  end
110
-
111
101
  end
112
102
 
113
103
  context "200 response" do
114
-
115
104
  before do
116
105
  allow(protocol_factory).to receive(:get_protocol)
117
106
  allow(processor).to receive(:process)
@@ -132,10 +121,7 @@ describe Thrift::ThinHTTPServer::RackApplication do
132
121
  it 'status code 200' do
133
122
  header('Content-Type', "application/x-thrift")
134
123
  post "/"
135
- expect(last_response.ok?).to be_truthy
124
+ expect(last_response.ok?).to be true
136
125
  end
137
-
138
126
  end
139
-
140
127
  end
141
-
data/spec/types_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,7 +21,6 @@
20
21
  require 'spec_helper'
21
22
 
22
23
  describe Thrift::Types do
23
-
24
24
  before(:each) do
25
25
  Thrift.type_checking = true
26
26
  end
@@ -39,33 +39,33 @@ describe Thrift::Types do
39
39
 
40
40
  it "should check types properly" do
41
41
  # lambda { Thrift.check_type(nil, Thrift::Types::STOP) }.should raise_error(Thrift::TypeError)
42
- expect { Thrift.check_type(3, {:type => Thrift::Types::STOP}, :foo) }.to raise_error(Thrift::TypeError)
43
- expect { Thrift.check_type(nil, {:type => Thrift::Types::VOID}, :foo) }.not_to raise_error
44
- expect { Thrift.check_type(3, {:type => Thrift::Types::VOID}, :foo) }.to raise_error(Thrift::TypeError)
45
- expect { Thrift.check_type(true, {:type => Thrift::Types::BOOL}, :foo) }.not_to raise_error
46
- expect { Thrift.check_type(3, {:type => Thrift::Types::BOOL}, :foo) }.to raise_error(Thrift::TypeError)
47
- expect { Thrift.check_type(42, {:type => Thrift::Types::BYTE}, :foo) }.not_to raise_error
48
- expect { Thrift.check_type(42, {:type => Thrift::Types::I16}, :foo) }.not_to raise_error
49
- expect { Thrift.check_type(42, {:type => Thrift::Types::I32}, :foo) }.not_to raise_error
50
- expect { Thrift.check_type(42, {:type => Thrift::Types::I64}, :foo) }.not_to raise_error
51
- expect { Thrift.check_type(3.14, {:type => Thrift::Types::I32}, :foo) }.to raise_error(Thrift::TypeError)
52
- expect { Thrift.check_type(3.14, {:type => Thrift::Types::DOUBLE}, :foo) }.not_to raise_error
53
- expect { Thrift.check_type(3, {:type => Thrift::Types::DOUBLE}, :foo) }.to raise_error(Thrift::TypeError)
54
- expect { Thrift.check_type("3", {:type => Thrift::Types::STRING}, :foo) }.not_to raise_error
55
- expect { Thrift.check_type(3, {:type => Thrift::Types::STRING}, :foo) }.to raise_error(Thrift::TypeError)
42
+ expect { Thrift.check_type(3, {:type => Thrift::Types::STOP}, :foo) }.to raise_error(Thrift::TypeError)
43
+ expect { Thrift.check_type(nil, {:type => Thrift::Types::VOID}, :foo) }.not_to raise_error
44
+ expect { Thrift.check_type(3, {:type => Thrift::Types::VOID}, :foo) }.to raise_error(Thrift::TypeError)
45
+ expect { Thrift.check_type(true, {:type => Thrift::Types::BOOL}, :foo) }.not_to raise_error
46
+ expect { Thrift.check_type(3, {:type => Thrift::Types::BOOL}, :foo) }.to raise_error(Thrift::TypeError)
47
+ expect { Thrift.check_type(42, {:type => Thrift::Types::BYTE}, :foo) }.not_to raise_error
48
+ expect { Thrift.check_type(42, {:type => Thrift::Types::I16}, :foo) }.not_to raise_error
49
+ expect { Thrift.check_type(42, {:type => Thrift::Types::I32}, :foo) }.not_to raise_error
50
+ expect { Thrift.check_type(42, {:type => Thrift::Types::I64}, :foo) }.not_to raise_error
51
+ expect { Thrift.check_type(3.14, {:type => Thrift::Types::I32}, :foo) }.to raise_error(Thrift::TypeError)
52
+ expect { Thrift.check_type(3.14, {:type => Thrift::Types::DOUBLE}, :foo) }.not_to raise_error
53
+ expect { Thrift.check_type(3, {:type => Thrift::Types::DOUBLE}, :foo) }.to raise_error(Thrift::TypeError)
54
+ expect { Thrift.check_type("3", {:type => Thrift::Types::STRING}, :foo) }.not_to raise_error
55
+ expect { Thrift.check_type(3, {:type => Thrift::Types::STRING}, :foo) }.to raise_error(Thrift::TypeError)
56
56
  hello = SpecNamespace::Hello.new
57
- expect { Thrift.check_type(hello, {:type => Thrift::Types::STRUCT, :class => SpecNamespace::Hello}, :foo) }.not_to raise_error
58
- expect { Thrift.check_type("foo", {:type => Thrift::Types::STRUCT}, :foo) }.to raise_error(Thrift::TypeError)
57
+ expect { Thrift.check_type(hello, {:type => Thrift::Types::STRUCT, :class => SpecNamespace::Hello}, :foo) }.not_to raise_error
58
+ expect { Thrift.check_type("foo", {:type => Thrift::Types::STRUCT}, :foo) }.to raise_error(Thrift::TypeError)
59
59
  field = {:type => Thrift::Types::MAP, :key => {:type => Thrift::Types::I32}, :value => {:type => Thrift::Types::STRING}}
60
- expect { Thrift.check_type({1 => "one"}, field, :foo) }.not_to raise_error
61
- expect { Thrift.check_type([1], field, :foo) }.to raise_error(Thrift::TypeError)
60
+ expect { Thrift.check_type({1 => "one"}, field, :foo) }.not_to raise_error
61
+ expect { Thrift.check_type([1], field, :foo) }.to raise_error(Thrift::TypeError)
62
62
  field = {:type => Thrift::Types::LIST, :element => {:type => Thrift::Types::I32}}
63
- expect { Thrift.check_type([1], field, :foo) }.not_to raise_error
64
- expect { Thrift.check_type({:foo => 1}, field, :foo) }.to raise_error(Thrift::TypeError)
63
+ expect { Thrift.check_type([1], field, :foo) }.not_to raise_error
64
+ expect { Thrift.check_type({:foo => 1}, field, :foo) }.to raise_error(Thrift::TypeError)
65
65
  field = {:type => Thrift::Types::SET, :element => {:type => Thrift::Types::I32}}
66
- expect { Thrift.check_type(Set.new([1,2]), field, :foo) }.not_to raise_error
67
- expect { Thrift.check_type([1,2], field, :foo) }.to raise_error(Thrift::TypeError)
68
- expect { Thrift.check_type({:foo => true}, field, :foo) }.to raise_error(Thrift::TypeError)
66
+ expect { Thrift.check_type(Set.new([1, 2]), field, :foo) }.not_to raise_error
67
+ expect { Thrift.check_type([1, 2], field, :foo) }.to raise_error(Thrift::TypeError)
68
+ expect { Thrift.check_type({:foo => true}, field, :foo) }.to raise_error(Thrift::TypeError)
69
69
  end
70
70
 
71
71
  it "should error out if nil is passed and skip_types is false" do
@@ -100,9 +100,9 @@ describe Thrift::Types do
100
100
  end
101
101
 
102
102
  it "should give the Thrift::TypeError a readable message" do
103
- msg = /Expected Types::STRING, received (Integer|Fixnum) for field foo/
103
+ msg = "Expected Types::STRING, received Integer for field foo"
104
104
  expect { Thrift.check_type(3, {:type => Thrift::Types::STRING}, :foo) }.to raise_error(Thrift::TypeError, msg)
105
- msg = /Expected Types::STRING, received (Integer|Fixnum) for field foo.element/
105
+ msg = "Expected Types::STRING, received Integer for field foo.element"
106
106
  field = {:type => Thrift::Types::LIST, :element => {:type => Thrift::Types::STRING}}
107
107
  expect { Thrift.check_type([3], field, :foo) }.to raise_error(Thrift::TypeError, msg)
108
108
  msg = "Expected Types::I32, received NilClass for field foo.element.key"
data/spec/union_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,7 +21,6 @@
20
21
  require 'spec_helper'
21
22
 
22
23
  describe 'Union' do
23
-
24
24
  describe Thrift::Union do
25
25
  it "should return nil value in unset union" do
26
26
  union = SpecNamespace::My_union.new
@@ -51,7 +51,9 @@ describe 'Union' do
51
51
  it "should raise for wrong set field when hash initialized and type checking is off" do
52
52
  Thrift.type_checking = false
53
53
  union = SpecNamespace::My_union.new({incorrect_field: :incorrect})
54
- expect { Thrift::Serializer.new.serialize(union) }.to raise_error(RuntimeError, "set_field is not valid for this union!")
54
+ expect { Thrift::Serializer.new.serialize(union) }.to raise_error(Thrift::ProtocolException, "set_field is not valid for this union!") { |error|
55
+ expect(error.type).to eq(Thrift::ProtocolException::INVALID_DATA)
56
+ }
55
57
  end
56
58
 
57
59
  it "should not be equal to nil" do
@@ -91,6 +93,24 @@ describe 'Union' do
91
93
  expect(union).not_to eq(other_union)
92
94
  end
93
95
 
96
+ it "should equate two unions with the same UUID value" do
97
+ union = SpecNamespace::My_union.new(:unique_id, '550e8400-e29b-41d4-a716-446655440000')
98
+ other_union = SpecNamespace::My_union.new(:unique_id, '550e8400-e29b-41d4-a716-446655440000')
99
+ expect(union).to eq(other_union)
100
+ end
101
+
102
+ it "should not equate two unions with different UUID values" do
103
+ union = SpecNamespace::My_union.new(:unique_id, '550e8400-e29b-41d4-a716-446655440000')
104
+ other_union = SpecNamespace::My_union.new(:unique_id, '6ba7b810-9dad-11d1-80b4-00c04fd430c8')
105
+ expect(union).not_to eq(other_union)
106
+ end
107
+
108
+ it "should not equate UUID union with different field type" do
109
+ union = SpecNamespace::My_union.new(:unique_id, '550e8400-e29b-41d4-a716-446655440000')
110
+ other_union = SpecNamespace::My_union.new(:some_characters, '550e8400-e29b-41d4-a716-446655440000')
111
+ expect(union).not_to eq(other_union)
112
+ end
113
+
94
114
  it "should inspect properly" do
95
115
  union = SpecNamespace::My_union.new(:integer32, 25)
96
116
  expect(union.inspect).to eq("<SpecNamespace::My_union integer32: 25>")
@@ -130,7 +150,9 @@ describe 'Union' do
130
150
 
131
151
  it "should raise when validating unset union" do
132
152
  union = SpecNamespace::My_union.new
133
- expect { union.validate }.to raise_error(StandardError, "Union fields are not set.")
153
+ expect { union.validate }.to raise_error(Thrift::ProtocolException, "Union fields are not set.") { |error|
154
+ expect(error.type).to eq(Thrift::ProtocolException::INVALID_DATA)
155
+ }
134
156
 
135
157
  other_union = SpecNamespace::My_union.new(:integer32, 1)
136
158
  expect { other_union.validate }.not_to raise_error
@@ -139,7 +161,9 @@ describe 'Union' do
139
161
  it "should validate an enum field properly" do
140
162
  union = SpecNamespace::TestUnion.new(:enum_field, 3)
141
163
  expect(union.get_set_field).to eq(:enum_field)
142
- expect { union.validate }.to raise_error(Thrift::ProtocolException, "Invalid value of field enum_field!")
164
+ expect { union.validate }.to raise_error(Thrift::ProtocolException, "Invalid value of field enum_field!") { |error|
165
+ expect(error.type).to eq(Thrift::ProtocolException::INVALID_DATA)
166
+ }
143
167
 
144
168
  other_union = SpecNamespace::TestUnion.new(:enum_field, 1)
145
169
  expect { other_union.validate }.not_to raise_error
@@ -170,7 +194,7 @@ describe 'Union' do
170
194
  end
171
195
 
172
196
  it "should not throw an error when inspected and unset" do
173
- expect{SpecNamespace::TestUnion.new().inspect}.not_to raise_error
197
+ expect{ SpecNamespace::TestUnion.new().inspect }.not_to raise_error
174
198
  end
175
199
 
176
200
  it "should print enum value name when inspected" do
@@ -192,23 +216,58 @@ describe 'Union' do
192
216
 
193
217
  it "should be comparable" do
194
218
  relationships = [
195
- [0, -1, -1, -1],
196
- [1, 0, -1, -1],
197
- [1, 1, 0, -1],
198
- [1, 1, 1, 0]]
219
+ [0, -1, -1, -1, -1, -1],
220
+ [1, 0, -1, -1, -1, -1],
221
+ [1, 1, 0, -1, -1, -1],
222
+ [1, 1, 1, 0, -1, -1],
223
+ [1, 1, 1, 1, 0, -1],
224
+ [1, 1, 1, 1, 1, 0]]
199
225
 
200
226
  objs = [
201
227
  SpecNamespace::TestUnion.new(:string_field, "blah"),
202
228
  SpecNamespace::TestUnion.new(:string_field, "blahblah"),
203
229
  SpecNamespace::TestUnion.new(:i32_field, 1),
230
+ SpecNamespace::TestUnion.new(:uuid_field, '550e8400-e29b-41d4-a716-446655440000'),
231
+ SpecNamespace::TestUnion.new(:uuid_field, '6ba7b810-9dad-11d1-80b4-00c04fd430c8'),
204
232
  SpecNamespace::TestUnion.new()]
205
233
 
206
- for y in 0..3
207
- for x in 0..3
234
+ objs.size.times do |y|
235
+ objs.size.times do |x|
208
236
  # puts "#{objs[y].inspect} <=> #{objs[x].inspect} should == #{relationships[y][x]}"
209
237
  expect(objs[y] <=> objs[x]).to eq(relationships[y][x])
210
238
  end
211
239
  end
212
240
  end
241
+
242
+ it "should handle UUID as union value" do
243
+ union = SpecNamespace::My_union.new
244
+ union.unique_id = 'ffffffff-ffff-ffff-ffff-ffffffffffff'
245
+
246
+ trans = Thrift::MemoryBufferTransport.new
247
+ prot = Thrift::CompactProtocol.new(trans)
248
+
249
+ union.write(prot)
250
+
251
+ result = SpecNamespace::My_union.new
252
+ result.read(prot)
253
+
254
+ expect(result.unique_id).to eq('ffffffff-ffff-ffff-ffff-ffffffffffff')
255
+ expect(result.get_set_field).to eq(:unique_id)
256
+ end
257
+
258
+ it "should normalize UUID case in union" do
259
+ union = SpecNamespace::My_union.new
260
+ union.unique_id = '550E8400-E29B-41D4-A716-446655440000'
261
+
262
+ trans = Thrift::MemoryBufferTransport.new
263
+ prot = Thrift::BinaryProtocol.new(trans)
264
+
265
+ union.write(prot)
266
+
267
+ result = SpecNamespace::My_union.new
268
+ result.read(prot)
269
+
270
+ expect(result.unique_id).to eq('550e8400-e29b-41d4-a716-446655440000')
271
+ end
213
272
  end
214
273
  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
@@ -21,7 +22,6 @@ require 'spec_helper'
21
22
  require File.expand_path("#{File.dirname(__FILE__)}/socket_spec_shared")
22
23
 
23
24
  describe 'UNIXSocket' do
24
-
25
25
  describe Thrift::UNIXSocket do
26
26
  before(:each) do
27
27
  @path = '/tmp/thrift_spec_socket'
@@ -42,7 +42,7 @@ describe 'UNIXSocket' do
42
42
  allow(::UNIXSocket).to receive(:new)
43
43
  expect(Thrift::UNIXSocket.new(@path, 5).timeout).to eq(5)
44
44
  end
45
-
45
+
46
46
  it "should provide a reasonable to_s" do
47
47
  allow(::UNIXSocket).to receive(:new)
48
48
  expect(Thrift::UNIXSocket.new(@path).to_s).to eq("domain(#{@path})")
@@ -68,10 +68,25 @@ describe 'UNIXSocket' do
68
68
  expect(handle).to receive(:accept).and_return(sock)
69
69
  trans = double("UNIXSocket")
70
70
  expect(Thrift::UNIXSocket).to receive(:new).and_return(trans)
71
+ expect(trans).to receive(:timeout=).with(Thrift::BaseServerTransport::DEFAULT_CLIENT_TIMEOUT)
71
72
  expect(trans).to receive(:handle=).with(sock)
72
73
  expect(@socket.accept).to eq(trans)
73
74
  end
74
75
 
76
+ it "should default accepted sockets to a finite client timeout" do
77
+ expect(@socket.client_timeout).to eq(5)
78
+ end
79
+
80
+ it "should accept a custom client timeout" do
81
+ @socket = Thrift::UNIXServerSocket.new(@path, client_timeout: 2.5)
82
+ expect(@socket.client_timeout).to eq(2.5)
83
+ end
84
+
85
+ it "should allow blocking accepted sockets with nil or zero client timeout" do
86
+ expect(Thrift::UNIXServerSocket.new(@path, client_timeout: nil).client_timeout).to be_nil
87
+ expect(Thrift::UNIXServerSocket.new(@path, client_timeout: 0).client_timeout).to eq(0)
88
+ end
89
+
75
90
  it "should close the handle when closed" do
76
91
  handle = double("UNIXServer", :closed? => false)
77
92
  expect(UNIXServer).to receive(:new).with(@path).and_return(handle)
@@ -0,0 +1,239 @@
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 'UUID Validation' do
24
+ protocols = [
25
+ ['BinaryProtocol', Thrift::BinaryProtocol],
26
+ ]
27
+
28
+ if defined?(Thrift::BinaryProtocolAccelerated)
29
+ protocols << ['BinaryProtocolAccelerated', Thrift::BinaryProtocolAccelerated]
30
+ end
31
+
32
+ protocols << ['CompactProtocol', Thrift::CompactProtocol]
33
+ protocols << ['JsonProtocol', Thrift::JsonProtocol]
34
+
35
+ protocols.each do |protocol_name, protocol_class|
36
+ describe protocol_name do
37
+ before(:each) do
38
+ @trans = Thrift::MemoryBufferTransport.new
39
+ @prot = protocol_class.new(@trans)
40
+ end
41
+
42
+ context 'valid UUIDs' do
43
+ it 'should accept lowercase UUIDs' do
44
+ uuid = '550e8400-e29b-41d4-a716-446655440000'
45
+ expect { @prot.write_uuid(uuid) }.not_to raise_error
46
+ result = @prot.read_uuid
47
+ expect(result).to eq(uuid)
48
+ end
49
+
50
+ it 'should accept uppercase UUIDs' do
51
+ uuid = '550E8400-E29B-41D4-A716-446655440000'
52
+ expect { @prot.write_uuid(uuid) }.not_to raise_error
53
+ result = @prot.read_uuid
54
+ # Result should be lowercase
55
+ expect(result).to eq('550e8400-e29b-41d4-a716-446655440000')
56
+ end
57
+
58
+ it 'should accept mixed case UUIDs' do
59
+ uuid = '550e8400-E29B-41d4-A716-446655440000'
60
+ expect { @prot.write_uuid(uuid) }.not_to raise_error
61
+ result = @prot.read_uuid
62
+ expect(result).to eq('550e8400-e29b-41d4-a716-446655440000')
63
+ end
64
+
65
+ it 'should accept all zeros' do
66
+ uuid = '00000000-0000-0000-0000-000000000000'
67
+ expect { @prot.write_uuid(uuid) }.not_to raise_error
68
+ result = @prot.read_uuid
69
+ expect(result).to eq(uuid)
70
+ end
71
+
72
+ it 'should accept all fs' do
73
+ uuid = 'ffffffff-ffff-ffff-ffff-ffffffffffff'
74
+ expect { @prot.write_uuid(uuid) }.not_to raise_error
75
+ result = @prot.read_uuid
76
+ expect(result).to eq(uuid)
77
+ end
78
+ end
79
+
80
+ context 'invalid UUIDs' do
81
+ def expect_invalid_uuid(value, message)
82
+ expect { @prot.write_uuid(value) }.to raise_error(Thrift::ProtocolException) do |error|
83
+ expect(error.type).to eq(Thrift::ProtocolException::INVALID_DATA)
84
+ expect(error.message).to eq(message)
85
+ end
86
+ end
87
+
88
+ it 'should reject nil' do
89
+ expect_invalid_uuid(nil, 'UUID must be a string')
90
+ end
91
+
92
+ it 'should reject non-string' do
93
+ expect_invalid_uuid(12345, 'UUID must be a string')
94
+ end
95
+
96
+ it 'should reject wrong length' do
97
+ expect_invalid_uuid('550e8400-e29b-41d4-a716', 'Invalid UUID format')
98
+ end
99
+
100
+ it 'should reject missing hyphens' do
101
+ expect_invalid_uuid('550e8400e29b41d4a716446655440000', 'Invalid UUID format')
102
+ end
103
+
104
+ it 'should reject hyphens in wrong positions' do
105
+ expect_invalid_uuid('550e840-0e29b-41d4-a716-446655440000', 'Invalid UUID format')
106
+ end
107
+
108
+ it 'should reject invalid hex characters (g)' do
109
+ expect_invalid_uuid('550e8400-e29b-41d4-a716-44665544000g', 'Invalid UUID format')
110
+ end
111
+
112
+ it 'should reject invalid hex characters (z)' do
113
+ expect_invalid_uuid('z50e8400-e29b-41d4-a716-446655440000', 'Invalid UUID format')
114
+ end
115
+
116
+ it 'should reject invalid hex characters (space)' do
117
+ expect_invalid_uuid('550e8400-e29b-41d4-a716-44665544000 ', 'Invalid UUID format')
118
+ end
119
+
120
+ it 'should reject empty string' do
121
+ expect_invalid_uuid('', 'Invalid UUID format')
122
+ end
123
+
124
+ it 'should reject UUID with extra characters' do
125
+ expect_invalid_uuid('550e8400-e29b-41d4-a716-446655440000x', 'Invalid UUID format')
126
+ end
127
+
128
+ it 'should reject trailing hyphen' do
129
+ expect_invalid_uuid('550e8400-e29b-41d4-a716-44665544000-', 'Invalid UUID format')
130
+ end
131
+
132
+ it 'should reject hyphen inside hex pair' do
133
+ expect_invalid_uuid('550e8400-e29b-41d4-a716-4466-5544000', 'Invalid UUID format')
134
+ end
135
+ end
136
+
137
+ context 'malformed binary data on read' do
138
+ it 'should raise error on truncated data' do
139
+ @trans = Thrift::MemoryBufferTransport.new
140
+ @prot = protocol_class.new(@trans)
141
+
142
+ # Write only 10 bytes instead of 16
143
+ if protocol_class == Thrift::JsonProtocol
144
+ @trans.write('"00000000-0000-0000-0000"')
145
+ else
146
+ @trans.write("\x00" * 10)
147
+ end
148
+
149
+ expect { @prot.read_uuid }.to raise_error(EOFError)
150
+ end
151
+
152
+ it 'should raise error on 15 bytes (one byte short)' do
153
+ @trans = Thrift::MemoryBufferTransport.new
154
+ @prot = protocol_class.new(@trans)
155
+
156
+ if protocol_class == Thrift::JsonProtocol
157
+ @trans.write('"00000000-0000-0000-0000-000000000"')
158
+ else
159
+ @trans.write("\x00" * 15)
160
+ end
161
+
162
+ expect { @prot.read_uuid }.to raise_error(EOFError)
163
+ end
164
+
165
+ it 'should raise error on empty buffer' do
166
+ @trans = Thrift::MemoryBufferTransport.new
167
+ @prot = protocol_class.new(@trans)
168
+
169
+ expect { @prot.read_uuid }.to raise_error(EOFError)
170
+ end
171
+ end
172
+
173
+ context 'multiple UUIDs in sequence' do
174
+ it 'should handle 10 UUIDs in sequence' do
175
+ uuids = 10.times.map { |i| sprintf('%08x-0000-0000-0000-000000000000', i) }
176
+
177
+ @trans = Thrift::MemoryBufferTransport.new
178
+ @prot = protocol_class.new(@trans)
179
+
180
+ uuids.each { |uuid| @prot.write_uuid(uuid) }
181
+
182
+ results = 10.times.map { @prot.read_uuid }
183
+ expect(results).to eq(uuids)
184
+ end
185
+
186
+ it 'should handle UUIDs interleaved with other types' do
187
+ @trans = Thrift::MemoryBufferTransport.new
188
+ @prot = protocol_class.new(@trans)
189
+
190
+ @prot.write_message_begin('testMessage', Thrift::MessageTypes::CALL, 0)
191
+ @prot.write_i32(42)
192
+ @prot.write_uuid('550e8400-e29b-41d4-a716-446655440000')
193
+ @prot.write_string('test')
194
+ @prot.write_uuid('6ba7b810-9dad-11d1-80b4-00c04fd430c8')
195
+ @prot.write_i64(123456789)
196
+ @prot.write_message_end
197
+
198
+ @prot.read_message_begin
199
+ expect(@prot.read_i32).to eq(42)
200
+ expect(@prot.read_uuid).to eq('550e8400-e29b-41d4-a716-446655440000')
201
+ expect(@prot.read_string).to eq('test')
202
+ expect(@prot.read_uuid).to eq('6ba7b810-9dad-11d1-80b4-00c04fd430c8')
203
+ expect(@prot.read_i64).to eq(123456789)
204
+ @prot.read_message_end
205
+ end
206
+
207
+ it 'should handle UUIDs in struct fields context' do
208
+ @trans = Thrift::MemoryBufferTransport.new
209
+ @prot = protocol_class.new(@trans)
210
+
211
+ # Simulate struct field headers
212
+ @prot.write_struct_begin('test')
213
+ @prot.write_field_begin('uuid1', Thrift::Types::UUID, 1)
214
+ @prot.write_uuid('550e8400-e29b-41d4-a716-446655440000')
215
+ @prot.write_field_end
216
+ @prot.write_field_begin('uuid2', Thrift::Types::UUID, 2)
217
+ @prot.write_uuid('6ba7b810-9dad-11d1-80b4-00c04fd430c8')
218
+ @prot.write_field_end
219
+ @prot.write_field_stop
220
+ @prot.write_struct_end
221
+
222
+ @prot.read_struct_begin
223
+ name, type, id = @prot.read_field_begin
224
+ expect(type).to eq(Thrift::Types::UUID)
225
+ expect(@prot.read_uuid).to eq('550e8400-e29b-41d4-a716-446655440000')
226
+ @prot.read_field_end
227
+
228
+ name, type, id = @prot.read_field_begin
229
+ expect(type).to eq(Thrift::Types::UUID)
230
+ expect(@prot.read_uuid).to eq('6ba7b810-9dad-11d1-80b4-00c04fd430c8')
231
+ @prot.read_field_end
232
+
233
+ name, type, id = @prot.read_field_begin
234
+ expect(type).to eq(Thrift::Types::STOP)
235
+ end
236
+ end
237
+ end
238
+ end
239
+ end