thrift 0.23.0 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +93 -33
- data/benchmark/benchmark.rb +2 -1
- data/benchmark/client.rb +1 -0
- data/benchmark/server.rb +1 -0
- data/benchmark/thin_server.rb +1 -0
- data/ext/binary_protocol_accelerated.c +78 -23
- data/ext/bytes.c +14 -0
- data/ext/compact_protocol.c +59 -31
- data/ext/extconf.rb +8 -0
- data/ext/memory_buffer.c +37 -0
- data/ext/struct.c +14 -14
- data/ext/thrift_native.c +1 -0
- data/lib/thrift/bytes.rb +4 -1
- data/lib/thrift/client.rb +1 -12
- data/lib/thrift/exceptions.rb +1 -0
- data/lib/thrift/multiplexed_processor.rb +14 -4
- data/lib/thrift/processor.rb +1 -0
- data/lib/thrift/protocol/base_protocol.rb +15 -6
- data/lib/thrift/protocol/binary_protocol.rb +41 -12
- data/lib/thrift/protocol/binary_protocol_accelerated.rb +1 -0
- data/lib/thrift/protocol/compact_protocol.rb +15 -4
- data/lib/thrift/protocol/header_protocol.rb +1 -0
- data/lib/thrift/protocol/json_protocol.rb +18 -11
- data/lib/thrift/protocol/multiplexed_protocol.rb +1 -0
- data/lib/thrift/protocol/protocol_decorator.rb +1 -0
- data/lib/thrift/serializer/deserializer.rb +1 -0
- data/lib/thrift/serializer/serializer.rb +1 -0
- data/lib/thrift/server/base_server.rb +1 -0
- data/lib/thrift/server/mongrel_http_server.rb +1 -0
- data/lib/thrift/server/nonblocking_server.rb +28 -2
- data/lib/thrift/server/simple_server.rb +9 -1
- data/lib/thrift/server/thin_http_server.rb +1 -0
- data/lib/thrift/server/thread_pool_server.rb +1 -0
- data/lib/thrift/server/threaded_server.rb +9 -1
- data/lib/thrift/struct.rb +1 -0
- data/lib/thrift/struct_union.rb +4 -9
- data/lib/thrift/thrift_native.rb +2 -1
- data/lib/thrift/transport/base_server_transport.rb +3 -0
- data/lib/thrift/transport/base_transport.rb +6 -2
- data/lib/thrift/transport/buffered_transport.rb +1 -0
- data/lib/thrift/transport/framed_transport.rb +1 -0
- data/lib/thrift/transport/header_transport.rb +47 -1
- data/lib/thrift/transport/http_client_transport.rb +1 -0
- data/lib/thrift/transport/io_stream_transport.rb +1 -0
- data/lib/thrift/transport/memory_buffer_transport.rb +7 -0
- data/lib/thrift/transport/server_socket.rb +6 -3
- data/lib/thrift/transport/socket.rb +68 -29
- data/lib/thrift/transport/ssl_server_socket.rb +3 -2
- data/lib/thrift/transport/ssl_socket.rb +43 -11
- data/lib/thrift/transport/unix_server_socket.rb +5 -1
- data/lib/thrift/transport/unix_socket.rb +1 -0
- data/lib/thrift/types.rb +1 -0
- data/lib/thrift/union.rb +1 -0
- data/lib/thrift/uuid.rb +1 -0
- data/lib/thrift.rb +1 -0
- data/spec/ThriftSpec.thrift +16 -0
- data/spec/base_protocol_spec.rb +20 -0
- data/spec/base_transport_spec.rb +42 -1
- data/spec/binary_protocol_accelerated_spec.rb +1 -0
- data/spec/binary_protocol_spec.rb +1 -0
- data/spec/binary_protocol_spec_shared.rb +79 -15
- data/spec/bytes_spec.rb +19 -1
- data/spec/client_spec.rb +1 -0
- data/spec/compact_protocol_spec.rb +99 -0
- data/spec/constants_demo_spec.rb +1 -0
- data/spec/exception_spec.rb +1 -0
- data/spec/flat_spec.rb +1 -0
- data/spec/header_protocol_spec.rb +1 -0
- data/spec/header_transport_spec.rb +45 -0
- data/spec/http_client_spec.rb +1 -0
- data/spec/json_protocol_spec.rb +26 -4
- data/spec/multiplexed_processor_spec.rb +75 -0
- data/spec/namespaced_spec.rb +1 -0
- data/spec/nonblocking_server_spec.rb +86 -18
- data/spec/processor_spec.rb +1 -0
- data/spec/recursion_depth_spec.rb +223 -0
- data/spec/serializer_spec.rb +1 -0
- data/spec/server_socket_spec.rb +37 -0
- data/spec/server_spec.rb +52 -0
- data/spec/socket_spec.rb +119 -12
- data/spec/socket_spec_shared.rb +1 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/ssl_server_socket_spec.rb +40 -0
- data/spec/ssl_socket_spec.rb +171 -10
- data/spec/struct_nested_containers_spec.rb +1 -0
- data/spec/struct_spec.rb +1 -0
- data/spec/support/header_protocol_helper.rb +2 -1
- data/spec/thin_http_server_spec.rb +1 -0
- data/spec/types_spec.rb +1 -0
- data/spec/union_spec.rb +1 -0
- data/spec/unix_socket_spec.rb +16 -0
- data/spec/uuid_validation_spec.rb +1 -0
- data/test/fuzz/Makefile +779 -0
- data/test/fuzz/Makefile.in +775 -0
- data/test/fuzz/fuzz_common.rb +1 -0
- data/test/fuzz/fuzz_parse_binary_protocol.rb +1 -0
- data/test/fuzz/fuzz_parse_binary_protocol_accelerated.rb +1 -0
- data/test/fuzz/fuzz_parse_binary_protocol_accelerated_harness.rb +1 -0
- data/test/fuzz/fuzz_parse_binary_protocol_harness.rb +1 -0
- data/test/fuzz/fuzz_parse_compact_protocol.rb +1 -0
- data/test/fuzz/fuzz_parse_compact_protocol_harness.rb +1 -0
- data/test/fuzz/fuzz_parse_json_protocol.rb +1 -0
- data/test/fuzz/fuzz_parse_json_protocol_harness.rb +1 -0
- data/test/fuzz/fuzz_roundtrip_binary_protocol.rb +1 -0
- data/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated.rb +1 -0
- data/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated_harness.rb +1 -0
- data/test/fuzz/fuzz_roundtrip_binary_protocol_harness.rb +1 -0
- data/test/fuzz/fuzz_roundtrip_compact_protocol.rb +1 -0
- data/test/fuzz/fuzz_roundtrip_compact_protocol_harness.rb +1 -0
- data/test/fuzz/fuzz_roundtrip_json_protocol.rb +1 -0
- data/test/fuzz/fuzz_roundtrip_json_protocol_harness.rb +1 -0
- data/test/fuzz/fuzz_tracer.rb +1 -0
- metadata +12 -46
data/spec/socket_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
|
|
@@ -24,44 +25,150 @@ describe 'Socket' do
|
|
|
24
25
|
describe Thrift::Socket do
|
|
25
26
|
before(:each) do
|
|
26
27
|
@socket = Thrift::Socket.new
|
|
28
|
+
@addrinfo = double("Addrinfo")
|
|
27
29
|
@handle = double("Handle", :closed? => false)
|
|
28
30
|
allow(@handle).to receive(:close)
|
|
29
|
-
allow(@handle).to receive(:connect_nonblock)
|
|
30
31
|
allow(@handle).to receive(:setsockopt)
|
|
31
|
-
allow(
|
|
32
|
+
allow(@addrinfo).to receive(:connect).and_return(@handle)
|
|
33
|
+
allow(Addrinfo).to receive(:foreach).and_yield(@addrinfo)
|
|
32
34
|
end
|
|
33
35
|
|
|
34
36
|
it_should_behave_like "a socket"
|
|
35
37
|
|
|
36
38
|
it "should raise a TransportException when it cannot open a socket" do
|
|
37
|
-
|
|
38
|
-
expect { @socket.open }.to raise_error(Thrift::TransportException)
|
|
39
|
+
allow(Addrinfo).to receive(:foreach).and_raise(SocketError.new("lookup failed"))
|
|
40
|
+
expect { @socket.open }.to raise_error(Thrift::TransportException) do |e|
|
|
41
|
+
expect(e.type).to eq(Thrift::TransportException::NOT_OPEN)
|
|
42
|
+
expect(e.message).to eq("Could not connect to localhost:9090")
|
|
43
|
+
expect(e.cause).to be_a(SocketError)
|
|
44
|
+
end
|
|
39
45
|
end
|
|
40
46
|
|
|
41
47
|
it "should open a ::Socket with default args" do
|
|
42
|
-
expect(
|
|
43
|
-
expect(
|
|
44
|
-
expect(
|
|
48
|
+
expect(Addrinfo).to receive(:foreach).with("localhost", 9090, nil, :STREAM).and_yield(@addrinfo)
|
|
49
|
+
expect(@addrinfo).to receive(:connect).with(no_args).and_return(@handle)
|
|
50
|
+
expect(@handle).to receive(:setsockopt).with(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
|
|
45
51
|
@socket.to_s == "socket(localhost:9090)"
|
|
46
52
|
@socket.open
|
|
47
53
|
end
|
|
48
54
|
|
|
49
55
|
it "should accept host/port options" do
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
expect(
|
|
56
|
+
handle = double("Handle", :closed? => false)
|
|
57
|
+
allow(handle).to receive(:close)
|
|
58
|
+
expect(handle).to receive(:setsockopt).with(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
|
|
59
|
+
expect(Addrinfo).to receive(:foreach).with("my.domain", 1234, nil, :STREAM).and_yield(@addrinfo)
|
|
60
|
+
expect(@addrinfo).to receive(:connect).with(no_args).and_return(handle)
|
|
53
61
|
@socket = Thrift::Socket.new('my.domain', 1234).open
|
|
54
62
|
@socket.to_s == "socket(my.domain:1234)"
|
|
55
63
|
end
|
|
56
64
|
|
|
57
65
|
it "should accept an optional timeout" do
|
|
58
|
-
allow(::Socket).to receive(:new)
|
|
59
66
|
expect(Thrift::Socket.new('localhost', 8080, 5).timeout).to eq(5)
|
|
60
67
|
end
|
|
61
68
|
|
|
62
69
|
it "should provide a reasonable to_s" do
|
|
63
|
-
allow(::Socket).to receive(:new)
|
|
64
70
|
expect(Thrift::Socket.new('myhost', 8090).to_s).to eq("socket(myhost:8090)")
|
|
65
71
|
end
|
|
72
|
+
|
|
73
|
+
it "should pass the remaining timeout to each address attempt" do
|
|
74
|
+
@socket.timeout = 5
|
|
75
|
+
second_addrinfo = double("Addrinfo")
|
|
76
|
+
|
|
77
|
+
expect(Process).to receive(:clock_gettime).with(Process::CLOCK_MONOTONIC).and_return(100.0, 101.0, 103.0)
|
|
78
|
+
expect(Addrinfo).to receive(:foreach).with("localhost", 9090, nil, :STREAM).and_yield(@addrinfo).and_yield(second_addrinfo)
|
|
79
|
+
expect(@addrinfo).to receive(:connect).with(timeout: 4.0).and_raise(Errno::ECONNREFUSED)
|
|
80
|
+
expect(second_addrinfo).to receive(:connect).with(timeout: 2.0).and_return(@handle)
|
|
81
|
+
expect(@handle).to receive(:setsockopt).with(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
|
|
82
|
+
|
|
83
|
+
expect(@socket.open).to eq(@handle)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
it "should continue to the next address after a connect timeout while time remains" do
|
|
87
|
+
@socket.timeout = 5
|
|
88
|
+
second_addrinfo = double("Addrinfo")
|
|
89
|
+
|
|
90
|
+
expect(Process).to receive(:clock_gettime).with(Process::CLOCK_MONOTONIC).and_return(100.0, 101.0, 103.0)
|
|
91
|
+
expect(Addrinfo).to receive(:foreach).with("localhost", 9090, nil, :STREAM).and_yield(@addrinfo).and_yield(second_addrinfo)
|
|
92
|
+
expect(@addrinfo).to receive(:connect).with(timeout: 4.0).and_raise(Errno::ETIMEDOUT)
|
|
93
|
+
expect(second_addrinfo).to receive(:connect).with(timeout: 2.0).and_return(@handle)
|
|
94
|
+
expect(@handle).to receive(:setsockopt).with(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
|
|
95
|
+
|
|
96
|
+
expect(@socket.open).to eq(@handle)
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
it "should raise TIMED_OUT when an address attempt times out" do
|
|
100
|
+
@socket.timeout = 5
|
|
101
|
+
|
|
102
|
+
expect(Process).to receive(:clock_gettime).with(Process::CLOCK_MONOTONIC).and_return(100.0, 101.0)
|
|
103
|
+
expect(@addrinfo).to receive(:connect).with(timeout: 4.0).and_raise(Errno::ETIMEDOUT)
|
|
104
|
+
|
|
105
|
+
expect { @socket.open }.to raise_error(Thrift::TransportException) do |e|
|
|
106
|
+
expect(e.type).to eq(Thrift::TransportException::TIMED_OUT)
|
|
107
|
+
expect(e.message).to eq("Socket: Timed out opening connection to localhost:9090")
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
it "should raise TIMED_OUT when the deadline expires before the next address attempt" do
|
|
112
|
+
@socket.timeout = 5
|
|
113
|
+
second_addrinfo = double("Addrinfo")
|
|
114
|
+
|
|
115
|
+
expect(Process).to receive(:clock_gettime).with(Process::CLOCK_MONOTONIC).and_return(100.0, 101.0, 105.0)
|
|
116
|
+
expect(Addrinfo).to receive(:foreach).with("localhost", 9090, nil, :STREAM).and_yield(@addrinfo).and_yield(second_addrinfo)
|
|
117
|
+
expect(@addrinfo).to receive(:connect).with(timeout: 4.0).and_raise(Errno::ECONNREFUSED)
|
|
118
|
+
expect(second_addrinfo).not_to receive(:connect)
|
|
119
|
+
|
|
120
|
+
expect { @socket.open }.to raise_error(Thrift::TransportException) do |e|
|
|
121
|
+
expect(e.type).to eq(Thrift::TransportException::TIMED_OUT)
|
|
122
|
+
expect(e.message).to eq("Socket: Timed out opening connection to localhost:9090")
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
it "should treat zero timeout as blocking for open" do
|
|
127
|
+
@socket.timeout = 0
|
|
128
|
+
|
|
129
|
+
expect(@addrinfo).to receive(:connect).with(no_args).and_return(@handle)
|
|
130
|
+
expect(@handle).to receive(:setsockopt).with(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
|
|
131
|
+
|
|
132
|
+
expect(@socket.open).to eq(@handle)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
it "should report the last connection error when all addresses fail" do
|
|
136
|
+
second_addrinfo = double("Addrinfo")
|
|
137
|
+
|
|
138
|
+
expect(Addrinfo).to receive(:foreach).with("localhost", 9090, nil, :STREAM).and_yield(@addrinfo).and_yield(second_addrinfo)
|
|
139
|
+
expect(@addrinfo).to receive(:connect).and_raise(Errno::ECONNREFUSED)
|
|
140
|
+
expect(second_addrinfo).to receive(:connect).and_raise(Errno::EHOSTUNREACH)
|
|
141
|
+
|
|
142
|
+
expect { @socket.open }.to raise_error(Thrift::TransportException) do |e|
|
|
143
|
+
expect(e.type).to eq(Thrift::TransportException::NOT_OPEN)
|
|
144
|
+
expect(e.message).to eq("Could not connect to localhost:9090")
|
|
145
|
+
expect(e.cause).to be_a(Errno::EHOSTUNREACH)
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
it "should close a connected candidate before falling back when socket setup fails" do
|
|
150
|
+
first_addrinfo = @addrinfo
|
|
151
|
+
second_addrinfo = double("Addrinfo")
|
|
152
|
+
first_handle = double("Handle", :closed? => false)
|
|
153
|
+
allow(first_handle).to receive(:close)
|
|
154
|
+
|
|
155
|
+
expect(Addrinfo).to receive(:foreach).with("localhost", 9090, nil, :STREAM).and_yield(first_addrinfo).and_yield(second_addrinfo)
|
|
156
|
+
expect(first_addrinfo).to receive(:connect).with(no_args).and_return(first_handle)
|
|
157
|
+
expect(first_handle).to receive(:setsockopt).with(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1).and_raise(StandardError.new("setsockopt failed"))
|
|
158
|
+
expect(first_handle).to receive(:close)
|
|
159
|
+
expect(second_addrinfo).to receive(:connect).with(no_args).and_return(@handle)
|
|
160
|
+
expect(@handle).to receive(:setsockopt).with(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
|
|
161
|
+
|
|
162
|
+
expect(@socket.open).to eq(@handle)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
it "should avoid a blank error when no addresses are returned" do
|
|
166
|
+
allow(Addrinfo).to receive(:foreach)
|
|
167
|
+
|
|
168
|
+
expect { @socket.open }.to raise_error(Thrift::TransportException) do |e|
|
|
169
|
+
expect(e.type).to eq(Thrift::TransportException::NOT_OPEN)
|
|
170
|
+
expect(e.message).to eq("Could not connect to localhost:9090")
|
|
171
|
+
end
|
|
172
|
+
end
|
|
66
173
|
end
|
|
67
174
|
end
|
data/spec/socket_spec_shared.rb
CHANGED
data/spec/spec_helper.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
|
|
@@ -38,6 +39,45 @@ describe 'SSLServerSocket' do
|
|
|
38
39
|
expect(@socket.to_io).to eq(tcp_server)
|
|
39
40
|
end
|
|
40
41
|
|
|
42
|
+
it "should default accepted sockets to a finite client timeout" do
|
|
43
|
+
expect(@socket.client_timeout).to eq(5)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
it "should preserve the positional ssl context argument" do
|
|
47
|
+
context = double("SSLContext")
|
|
48
|
+
socket = Thrift::SSLServerSocket.new(nil, 1234, context)
|
|
49
|
+
|
|
50
|
+
expect(socket.ssl_context).to eq(context)
|
|
51
|
+
expect(socket.client_timeout).to eq(5)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it "should accept a custom client timeout" do
|
|
55
|
+
context = double("SSLContext")
|
|
56
|
+
socket = Thrift::SSLServerSocket.new(nil, 1234, context, client_timeout: 2.5)
|
|
57
|
+
|
|
58
|
+
expect(socket.ssl_context).to eq(context)
|
|
59
|
+
expect(socket.client_timeout).to eq(2.5)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it "should apply the client timeout to accepted sockets" do
|
|
63
|
+
tcp_server = double("TCPServer")
|
|
64
|
+
ssl_server = double("SSLServer")
|
|
65
|
+
sock = double("SSLSocket")
|
|
66
|
+
|
|
67
|
+
allow(TCPServer).to receive(:new).with(nil, 1234).and_return(tcp_server)
|
|
68
|
+
allow(OpenSSL::SSL::SSLServer).to receive(:new).with(tcp_server, nil).and_return(ssl_server)
|
|
69
|
+
expect(ssl_server).to receive(:accept).and_return(sock)
|
|
70
|
+
expect(sock).to receive(:setsockopt).with(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
|
71
|
+
|
|
72
|
+
trans = double("Socket")
|
|
73
|
+
expect(Thrift::Socket).to receive(:new).and_return(trans)
|
|
74
|
+
expect(trans).to receive(:timeout=).with(Thrift::BaseServerTransport::DEFAULT_CLIENT_TIMEOUT)
|
|
75
|
+
expect(trans).to receive(:handle=).with(sock)
|
|
76
|
+
|
|
77
|
+
@socket.listen
|
|
78
|
+
expect(@socket.accept).to eq(trans)
|
|
79
|
+
end
|
|
80
|
+
|
|
41
81
|
it "should provide a reasonable to_s" do
|
|
42
82
|
expect(@socket.to_s).to eq("ssl(socket(:1234))")
|
|
43
83
|
end
|
data/spec/ssl_socket_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
|
|
@@ -25,40 +26,56 @@ describe 'SSLSocket' do
|
|
|
25
26
|
before(:each) do
|
|
26
27
|
@context = OpenSSL::SSL::SSLContext.new
|
|
27
28
|
@socket = Thrift::SSLSocket.new
|
|
29
|
+
@addrinfo = double("Addrinfo")
|
|
28
30
|
@simple_socket_handle = double("Handle", :closed? => false)
|
|
29
31
|
allow(@simple_socket_handle).to receive(:close)
|
|
30
|
-
allow(@simple_socket_handle).to receive(:connect_nonblock)
|
|
31
32
|
allow(@simple_socket_handle).to receive(:setsockopt)
|
|
33
|
+
allow(@simple_socket_handle).to receive(:wait_readable)
|
|
34
|
+
allow(@simple_socket_handle).to receive(:wait_writable)
|
|
32
35
|
|
|
33
|
-
@handle = double(
|
|
34
|
-
allow(@handle).to receive(:
|
|
36
|
+
@handle = double("SSLHandle", :closed? => false)
|
|
37
|
+
allow(@handle).to receive(:connect).and_return(true)
|
|
38
|
+
allow(@handle).to receive(:connect_nonblock).and_return(@handle)
|
|
35
39
|
allow(@handle).to receive(:close)
|
|
36
40
|
allow(@handle).to receive(:post_connection_check)
|
|
41
|
+
allow(@handle).to receive(:sync_close=)
|
|
37
42
|
allow(@handle).to receive(:to_io).and_return(@simple_socket_handle)
|
|
38
43
|
|
|
39
|
-
allow(
|
|
44
|
+
allow(@addrinfo).to receive(:connect).and_return(@simple_socket_handle)
|
|
45
|
+
allow(Addrinfo).to receive(:foreach).and_yield(@addrinfo)
|
|
40
46
|
allow(OpenSSL::SSL::SSLSocket).to receive(:new).and_return(@handle)
|
|
41
47
|
end
|
|
42
48
|
|
|
43
49
|
it_should_behave_like "a socket"
|
|
44
50
|
|
|
45
51
|
it "should raise a TransportException when it cannot open a ssl socket" do
|
|
46
|
-
|
|
47
|
-
expect { @socket.open }.to raise_error(Thrift::TransportException)
|
|
52
|
+
allow(Addrinfo).to receive(:foreach).and_raise(SocketError.new("lookup failed"))
|
|
53
|
+
expect { @socket.open }.to raise_error(Thrift::TransportException) do |e|
|
|
54
|
+
expect(e.type).to eq(Thrift::TransportException::NOT_OPEN)
|
|
55
|
+
expect(e.message).to eq("Could not connect to localhost:9090")
|
|
56
|
+
expect(e.cause).to be_a(SocketError)
|
|
57
|
+
end
|
|
48
58
|
end
|
|
49
59
|
|
|
50
60
|
it "should open a ::Socket with default args" do
|
|
61
|
+
expect(@simple_socket_handle).to receive(:setsockopt).with(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
|
|
51
62
|
expect(OpenSSL::SSL::SSLSocket).to receive(:new).with(@simple_socket_handle, nil).and_return(@handle)
|
|
63
|
+
expect(@handle).to receive(:sync_close=).with(true)
|
|
64
|
+
expect(@handle).to receive(:connect).and_return(true)
|
|
52
65
|
expect(@handle).to receive(:post_connection_check).with('localhost')
|
|
53
66
|
@socket.open
|
|
54
67
|
end
|
|
55
68
|
|
|
56
69
|
it "should accept host/port options" do
|
|
57
|
-
handle = double("Handle", :
|
|
58
|
-
allow(
|
|
59
|
-
expect(
|
|
60
|
-
expect(
|
|
70
|
+
handle = double("Handle", :closed? => false)
|
|
71
|
+
allow(handle).to receive(:close)
|
|
72
|
+
expect(Process).to receive(:clock_gettime).with(Process::CLOCK_MONOTONIC).and_return(100.0, 100.0)
|
|
73
|
+
expect(handle).to receive(:setsockopt).with(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
|
|
74
|
+
expect(Addrinfo).to receive(:foreach).with("my.domain", 1234, nil, :STREAM).and_yield(@addrinfo)
|
|
75
|
+
expect(@addrinfo).to receive(:connect).with(timeout: 6000).and_return(handle)
|
|
61
76
|
expect(OpenSSL::SSL::SSLSocket).to receive(:new).with(handle, nil).and_return(@handle)
|
|
77
|
+
expect(@handle).to receive(:sync_close=).with(true)
|
|
78
|
+
expect(@handle).to receive(:connect_nonblock).with(exception: false).and_return(@handle)
|
|
62
79
|
expect(@handle).to receive(:post_connection_check).with('my.domain')
|
|
63
80
|
Thrift::SSLSocket.new('my.domain', 1234, 6000, nil).open
|
|
64
81
|
end
|
|
@@ -71,6 +88,150 @@ describe 'SSLSocket' do
|
|
|
71
88
|
expect(Thrift::SSLSocket.new('localhost', 8080, 5, @context).ssl_context).to eq(@context)
|
|
72
89
|
end
|
|
73
90
|
|
|
91
|
+
it "should treat zero timeout as blocking for open and handshake" do
|
|
92
|
+
@socket.timeout = 0
|
|
93
|
+
|
|
94
|
+
expect(@addrinfo).to receive(:connect).with(no_args).and_return(@simple_socket_handle)
|
|
95
|
+
expect(@simple_socket_handle).to receive(:setsockopt).with(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
|
|
96
|
+
expect(@handle).to receive(:sync_close=).with(true)
|
|
97
|
+
expect(@handle).to receive(:connect).and_return(true)
|
|
98
|
+
expect(@handle).not_to receive(:connect_nonblock)
|
|
99
|
+
expect(@handle).to receive(:post_connection_check).with('localhost')
|
|
100
|
+
|
|
101
|
+
expect(@socket.open).to eq(@handle)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
it "should use the remaining timeout across ssl wait states" do
|
|
105
|
+
@socket.timeout = 5
|
|
106
|
+
|
|
107
|
+
expect(Process).to receive(:clock_gettime).with(Process::CLOCK_MONOTONIC).and_return(100.0, 101.0, 102.0, 104.0)
|
|
108
|
+
expect(@addrinfo).to receive(:connect).with(timeout: 4.0).and_return(@simple_socket_handle)
|
|
109
|
+
expect(@simple_socket_handle).to receive(:setsockopt).with(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
|
|
110
|
+
expect(@handle).to receive(:sync_close=).with(true)
|
|
111
|
+
expect(@handle).to receive(:connect_nonblock).with(exception: false).ordered.and_return(:wait_readable)
|
|
112
|
+
expect(@simple_socket_handle).to receive(:wait_readable).with(3.0).ordered.and_return(true)
|
|
113
|
+
expect(@handle).to receive(:connect_nonblock).with(exception: false).ordered.and_return(:wait_writable)
|
|
114
|
+
expect(@simple_socket_handle).to receive(:wait_writable).with(1.0).ordered.and_return(true)
|
|
115
|
+
expect(@handle).to receive(:connect_nonblock).with(exception: false).ordered.and_return(@handle)
|
|
116
|
+
expect(@handle).to receive(:post_connection_check).with('localhost')
|
|
117
|
+
|
|
118
|
+
expect(@socket.open).to eq(@handle)
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
it "should share one timeout budget across tcp fallback and the ssl handshake" do
|
|
122
|
+
@socket.timeout = 5
|
|
123
|
+
second_addrinfo = double("Addrinfo")
|
|
124
|
+
|
|
125
|
+
expect(Process).to receive(:clock_gettime).with(Process::CLOCK_MONOTONIC).and_return(100.0, 101.0, 102.0, 103.0)
|
|
126
|
+
expect(Addrinfo).to receive(:foreach).with("localhost", 9090, nil, :STREAM).and_yield(@addrinfo).and_yield(second_addrinfo)
|
|
127
|
+
expect(@addrinfo).to receive(:connect).with(timeout: 4.0).and_raise(Errno::ECONNREFUSED)
|
|
128
|
+
expect(second_addrinfo).to receive(:connect).with(timeout: 3.0).and_return(@simple_socket_handle)
|
|
129
|
+
expect(@simple_socket_handle).to receive(:setsockopt).with(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
|
|
130
|
+
expect(@handle).to receive(:sync_close=).with(true)
|
|
131
|
+
expect(@handle).to receive(:connect_nonblock).with(exception: false).ordered.and_return(:wait_readable)
|
|
132
|
+
expect(@simple_socket_handle).to receive(:wait_readable).with(2.0).ordered.and_return(true)
|
|
133
|
+
expect(@handle).to receive(:connect_nonblock).with(exception: false).ordered.and_return(@handle)
|
|
134
|
+
expect(@handle).to receive(:post_connection_check).with('localhost')
|
|
135
|
+
|
|
136
|
+
expect(@socket.open).to eq(@handle)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
it "should continue to the next address after a tcp connect timeout during ssl open" do
|
|
140
|
+
@socket.timeout = 5
|
|
141
|
+
second_addrinfo = double("Addrinfo")
|
|
142
|
+
|
|
143
|
+
expect(Process).to receive(:clock_gettime).with(Process::CLOCK_MONOTONIC).and_return(100.0, 101.0, 103.0)
|
|
144
|
+
expect(Addrinfo).to receive(:foreach).with("localhost", 9090, nil, :STREAM).and_yield(@addrinfo).and_yield(second_addrinfo)
|
|
145
|
+
expect(@addrinfo).to receive(:connect).with(timeout: 4.0).and_raise(Errno::ETIMEDOUT)
|
|
146
|
+
expect(second_addrinfo).to receive(:connect).with(timeout: 2.0).and_return(@simple_socket_handle)
|
|
147
|
+
expect(@simple_socket_handle).to receive(:setsockopt).with(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
|
|
148
|
+
expect(@handle).to receive(:sync_close=).with(true)
|
|
149
|
+
expect(@handle).to receive(:connect_nonblock).with(exception: false).and_return(@handle)
|
|
150
|
+
expect(@handle).to receive(:post_connection_check).with('localhost')
|
|
151
|
+
|
|
152
|
+
expect(@socket.open).to eq(@handle)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
it "should raise TIMED_OUT when ssl handshake wait times out" do
|
|
156
|
+
@socket.timeout = 5
|
|
157
|
+
|
|
158
|
+
expect(Process).to receive(:clock_gettime).with(Process::CLOCK_MONOTONIC).and_return(100.0, 101.0, 102.0)
|
|
159
|
+
expect(@addrinfo).to receive(:connect).with(timeout: 4.0).and_return(@simple_socket_handle)
|
|
160
|
+
expect(@simple_socket_handle).to receive(:setsockopt).with(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
|
|
161
|
+
expect(@handle).to receive(:sync_close=).with(true)
|
|
162
|
+
expect(@handle).to receive(:connect_nonblock).with(exception: false).and_return(:wait_readable)
|
|
163
|
+
expect(@simple_socket_handle).to receive(:wait_readable).with(3.0).and_return(nil)
|
|
164
|
+
expect(@handle).to receive(:close)
|
|
165
|
+
|
|
166
|
+
expect { @socket.open }.to raise_error(Thrift::TransportException) do |e|
|
|
167
|
+
expect(e.type).to eq(Thrift::TransportException::TIMED_OUT)
|
|
168
|
+
expect(e.message).to eq("SSL socket: Timed out establishing session with localhost:9090")
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
it "should surface tcp open timeout before starting ssl" do
|
|
173
|
+
@socket.timeout = 5
|
|
174
|
+
|
|
175
|
+
expect(Process).to receive(:clock_gettime).with(Process::CLOCK_MONOTONIC).and_return(100.0, 101.0)
|
|
176
|
+
expect(@addrinfo).to receive(:connect).with(timeout: 4.0).and_raise(Errno::ETIMEDOUT)
|
|
177
|
+
expect(OpenSSL::SSL::SSLSocket).not_to receive(:new)
|
|
178
|
+
|
|
179
|
+
expect { @socket.open }.to raise_error(Thrift::TransportException) do |e|
|
|
180
|
+
expect(e.type).to eq(Thrift::TransportException::TIMED_OUT)
|
|
181
|
+
expect(e.message).to eq("Socket: Timed out opening connection to localhost:9090")
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
it "should close the raw socket if ssl wrapper creation fails" do
|
|
186
|
+
@socket.timeout = 5
|
|
187
|
+
|
|
188
|
+
expect(Process).to receive(:clock_gettime).with(Process::CLOCK_MONOTONIC).and_return(100.0, 101.0)
|
|
189
|
+
expect(@addrinfo).to receive(:connect).with(timeout: 4.0).and_return(@simple_socket_handle)
|
|
190
|
+
expect(@simple_socket_handle).to receive(:setsockopt).with(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
|
|
191
|
+
expect(OpenSSL::SSL::SSLSocket).to receive(:new).with(@simple_socket_handle, nil).and_raise(StandardError.new("ssl init failed"))
|
|
192
|
+
expect(@simple_socket_handle).to receive(:close)
|
|
193
|
+
|
|
194
|
+
expect { @socket.open }.to raise_error(Thrift::TransportException) do |e|
|
|
195
|
+
expect(e.type).to eq(Thrift::TransportException::NOT_OPEN)
|
|
196
|
+
expect(e.message).to eq("Could not connect to localhost:9090")
|
|
197
|
+
expect(e.cause.message).to eq("ssl init failed")
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
it "should close the ssl socket when post connection check fails" do
|
|
202
|
+
@socket.timeout = 5
|
|
203
|
+
|
|
204
|
+
expect(Process).to receive(:clock_gettime).with(Process::CLOCK_MONOTONIC).and_return(100.0, 101.0)
|
|
205
|
+
expect(@addrinfo).to receive(:connect).with(timeout: 4.0).and_return(@simple_socket_handle)
|
|
206
|
+
expect(@simple_socket_handle).to receive(:setsockopt).with(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
|
|
207
|
+
expect(@handle).to receive(:sync_close=).with(true)
|
|
208
|
+
expect(@handle).to receive(:connect_nonblock).with(exception: false).and_return(@handle)
|
|
209
|
+
expect(@handle).to receive(:post_connection_check).with('localhost').and_raise(StandardError.new("hostname mismatch"))
|
|
210
|
+
expect(@handle).to receive(:close)
|
|
211
|
+
|
|
212
|
+
expect { @socket.open }.to raise_error(Thrift::TransportException) do |e|
|
|
213
|
+
expect(e.type).to eq(Thrift::TransportException::NOT_OPEN)
|
|
214
|
+
expect(e.message).to eq("Could not connect to localhost:9090")
|
|
215
|
+
expect(e.cause.message).to eq("hostname mismatch")
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
it "should close the ssl socket on an unexpected nonblocking handshake result" do
|
|
220
|
+
@socket.timeout = 5
|
|
221
|
+
|
|
222
|
+
expect(Process).to receive(:clock_gettime).with(Process::CLOCK_MONOTONIC).and_return(100.0, 101.0)
|
|
223
|
+
expect(@addrinfo).to receive(:connect).with(timeout: 4.0).and_return(@simple_socket_handle)
|
|
224
|
+
expect(@simple_socket_handle).to receive(:setsockopt).with(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
|
|
225
|
+
expect(@handle).to receive(:sync_close=).with(true)
|
|
226
|
+
expect(@handle).to receive(:connect_nonblock).with(exception: false).and_return(:unexpected)
|
|
227
|
+
expect(@handle).to receive(:close)
|
|
228
|
+
|
|
229
|
+
expect { @socket.open }.to raise_error(Thrift::TransportException) do |e|
|
|
230
|
+
expect(e.type).to eq(Thrift::TransportException::NOT_OPEN)
|
|
231
|
+
expect(e.message).to include("unexpected SSL connect result")
|
|
232
|
+
end
|
|
233
|
+
end
|
|
234
|
+
|
|
74
235
|
it "should delegate to_io to the underlying SSL socket handle" do
|
|
75
236
|
@socket.open
|
|
76
237
|
expect(@socket.to_io).to eq(@simple_socket_handle)
|
data/spec/struct_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
|
|
@@ -33,7 +34,7 @@ module HeaderProtocolHelper
|
|
|
33
34
|
end
|
|
34
35
|
|
|
35
36
|
def build_header_frame(header_data, payload = Thrift::Bytes.empty_byte_buffer, header_words: nil)
|
|
36
|
-
header_data =
|
|
37
|
+
header_data = header_data.b
|
|
37
38
|
if header_words.nil?
|
|
38
39
|
padding = (4 - (header_data.bytesize % 4)) % 4
|
|
39
40
|
header_data += "\x00" * padding
|
data/spec/types_spec.rb
CHANGED
data/spec/union_spec.rb
CHANGED
data/spec/unix_socket_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
|
|
@@ -67,10 +68,25 @@ describe 'UNIXSocket' do
|
|
|
67
68
|
expect(handle).to receive(:accept).and_return(sock)
|
|
68
69
|
trans = double("UNIXSocket")
|
|
69
70
|
expect(Thrift::UNIXSocket).to receive(:new).and_return(trans)
|
|
71
|
+
expect(trans).to receive(:timeout=).with(Thrift::BaseServerTransport::DEFAULT_CLIENT_TIMEOUT)
|
|
70
72
|
expect(trans).to receive(:handle=).with(sock)
|
|
71
73
|
expect(@socket.accept).to eq(trans)
|
|
72
74
|
end
|
|
73
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
|
+
|
|
74
90
|
it "should close the handle when closed" do
|
|
75
91
|
handle = double("UNIXServer", :closed? => false)
|
|
76
92
|
expect(UNIXServer).to receive(:new).with(@path).and_return(handle)
|