thrift 0.22.0 → 0.23.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 +175 -17
- data/benchmark/benchmark.rb +22 -8
- data/benchmark/client.rb +49 -6
- data/benchmark/server.rb +45 -7
- data/benchmark/thin_server.rb +1 -0
- data/ext/binary_protocol_accelerated.c +76 -19
- data/ext/compact_protocol.c +80 -15
- data/ext/constants.h +12 -0
- data/ext/extconf.rb +10 -9
- data/ext/memory_buffer.c +7 -7
- data/ext/protocol.c +29 -0
- data/ext/protocol.h +35 -0
- data/ext/struct.c +36 -5
- data/ext/thrift_native.c +27 -3
- data/lib/thrift/bytes.rb +68 -101
- data/lib/thrift/client.rb +61 -9
- data/lib/thrift/exceptions.rb +5 -5
- data/lib/thrift/multiplexed_processor.rb +6 -6
- data/lib/thrift/processor.rb +6 -6
- data/lib/thrift/protocol/base_protocol.rb +37 -15
- data/lib/thrift/protocol/binary_protocol.rb +25 -9
- data/lib/thrift/protocol/binary_protocol_accelerated.rb +5 -5
- data/lib/thrift/protocol/compact_protocol.rb +61 -37
- data/lib/thrift/protocol/header_protocol.rb +320 -0
- data/lib/thrift/protocol/json_protocol.rb +26 -16
- data/lib/thrift/protocol/multiplexed_protocol.rb +5 -5
- data/lib/thrift/protocol/protocol_decorator.rb +12 -4
- data/lib/thrift/serializer/deserializer.rb +5 -5
- data/lib/thrift/serializer/serializer.rb +4 -5
- data/lib/thrift/server/base_server.rb +4 -4
- data/lib/thrift/server/mongrel_http_server.rb +6 -6
- data/lib/thrift/server/nonblocking_server.rb +8 -8
- data/lib/thrift/server/simple_server.rb +4 -4
- data/lib/thrift/server/thin_http_server.rb +3 -3
- data/lib/thrift/server/thread_pool_server.rb +6 -6
- data/lib/thrift/server/threaded_server.rb +4 -4
- data/lib/thrift/struct.rb +11 -11
- data/lib/thrift/struct_union.rb +19 -9
- data/lib/thrift/thrift_native.rb +1 -1
- data/lib/thrift/transport/base_server_transport.rb +5 -5
- data/lib/thrift/transport/base_transport.rb +12 -12
- data/lib/thrift/transport/buffered_transport.rb +6 -6
- data/lib/thrift/transport/framed_transport.rb +7 -7
- data/lib/thrift/transport/header_transport.rb +516 -0
- data/lib/thrift/transport/http_client_transport.rb +1 -1
- data/lib/thrift/transport/io_stream_transport.rb +3 -3
- data/lib/thrift/transport/memory_buffer_transport.rb +6 -6
- data/lib/thrift/transport/server_socket.rb +8 -5
- data/lib/thrift/transport/socket.rb +58 -31
- data/lib/thrift/transport/ssl_server_socket.rb +1 -1
- data/lib/thrift/transport/ssl_socket.rb +2 -2
- data/lib/thrift/transport/unix_server_socket.rb +4 -4
- data/lib/thrift/transport/unix_socket.rb +6 -6
- data/lib/thrift/types.rb +9 -6
- data/lib/thrift/union.rb +14 -8
- data/lib/thrift/uuid.rb +49 -0
- data/lib/thrift.rb +3 -1
- data/spec/ThriftSpec.thrift +5 -1
- data/spec/base_protocol_spec.rb +1 -2
- data/spec/base_transport_spec.rb +6 -7
- data/spec/binary_protocol_spec.rb +0 -2
- data/spec/binary_protocol_spec_shared.rb +129 -142
- data/spec/bytes_spec.rb +57 -118
- data/spec/client_spec.rb +85 -19
- data/spec/compact_protocol_spec.rb +54 -16
- data/spec/constants_demo_spec.rb +101 -0
- data/spec/exception_spec.rb +0 -1
- data/spec/header_protocol_spec.rb +475 -0
- data/spec/header_transport_spec.rb +386 -0
- data/spec/http_client_spec.rb +4 -6
- data/spec/json_protocol_spec.rb +47 -47
- data/spec/namespaced_spec.rb +0 -1
- data/spec/nonblocking_server_spec.rb +102 -4
- data/spec/processor_spec.rb +0 -1
- data/spec/serializer_spec.rb +0 -1
- data/spec/server_socket_spec.rb +1 -1
- data/spec/server_spec.rb +8 -9
- data/spec/socket_spec.rb +0 -1
- data/spec/socket_spec_shared.rb +72 -9
- data/spec/spec_helper.rb +1 -1
- data/spec/ssl_server_socket_spec.rb +12 -1
- data/spec/ssl_socket_spec.rb +10 -1
- data/spec/struct_nested_containers_spec.rb +1 -2
- data/spec/struct_spec.rb +113 -9
- data/spec/support/header_protocol_helper.rb +54 -0
- data/spec/thin_http_server_spec.rb +3 -18
- data/spec/types_spec.rb +25 -26
- data/spec/union_spec.rb +69 -11
- data/spec/unix_socket_spec.rb +1 -2
- data/spec/uuid_validation_spec.rb +238 -0
- data/test/fuzz/Makefile.am +173 -0
- data/test/fuzz/README.md +149 -0
- data/test/fuzz/fuzz_common.rb +95 -0
- data/{lib/thrift/core_ext.rb → test/fuzz/fuzz_parse_binary_protocol.rb} +3 -4
- data/{lib/thrift/core_ext/fixnum.rb → test/fuzz/fuzz_parse_binary_protocol_accelerated.rb} +6 -13
- data/test/fuzz/fuzz_parse_binary_protocol_accelerated_harness.rb +22 -0
- data/test/fuzz/fuzz_parse_binary_protocol_harness.rb +22 -0
- data/test/fuzz/fuzz_parse_compact_protocol.rb +22 -0
- data/test/fuzz/fuzz_parse_compact_protocol_harness.rb +22 -0
- data/test/fuzz/fuzz_parse_json_protocol.rb +22 -0
- data/test/fuzz/fuzz_parse_json_protocol_harness.rb +22 -0
- data/test/fuzz/fuzz_roundtrip_binary_protocol.rb +22 -0
- data/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated.rb +22 -0
- data/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated_harness.rb +22 -0
- data/test/fuzz/fuzz_roundtrip_binary_protocol_harness.rb +22 -0
- data/test/fuzz/fuzz_roundtrip_compact_protocol.rb +22 -0
- data/test/fuzz/fuzz_roundtrip_compact_protocol_harness.rb +22 -0
- data/test/fuzz/fuzz_roundtrip_json_protocol.rb +22 -0
- data/test/fuzz/fuzz_roundtrip_json_protocol_harness.rb +22 -0
- data/test/fuzz/fuzz_tracer.rb +28 -0
- metadata +106 -37
|
@@ -18,9 +18,9 @@
|
|
|
18
18
|
#
|
|
19
19
|
|
|
20
20
|
require 'spec_helper'
|
|
21
|
+
require 'timeout'
|
|
21
22
|
|
|
22
23
|
describe 'NonblockingServer' do
|
|
23
|
-
|
|
24
24
|
class Handler
|
|
25
25
|
def initialize
|
|
26
26
|
@queue = Queue.new
|
|
@@ -76,7 +76,7 @@ describe 'NonblockingServer' do
|
|
|
76
76
|
@transport.read(sz)
|
|
77
77
|
end
|
|
78
78
|
|
|
79
|
-
def write(buf,sz=nil)
|
|
79
|
+
def write(buf, sz = nil)
|
|
80
80
|
@transport.write(buf, sz)
|
|
81
81
|
end
|
|
82
82
|
|
|
@@ -166,7 +166,7 @@ describe 'NonblockingServer' do
|
|
|
166
166
|
break
|
|
167
167
|
end
|
|
168
168
|
end
|
|
169
|
-
@clients.each { |c,t| t.close and break if c == client } #close the transport
|
|
169
|
+
@clients.each { |c, t| t.close and break if c == client } # close the transport
|
|
170
170
|
rescue => e
|
|
171
171
|
raise e unless @catch_exceptions
|
|
172
172
|
end
|
|
@@ -245,7 +245,7 @@ describe 'NonblockingServer' do
|
|
|
245
245
|
it "should kill active messages when they don't expire while shutting down" do
|
|
246
246
|
result = Queue.new
|
|
247
247
|
client = setup_client_thread(result)
|
|
248
|
-
client << [:sleep, 10]
|
|
248
|
+
client << [:sleep, 10.0]
|
|
249
249
|
sleep 0.1 # start processing the client's message
|
|
250
250
|
@server.shutdown(1)
|
|
251
251
|
@catch_exceptions = true
|
|
@@ -260,4 +260,102 @@ describe 'NonblockingServer' do
|
|
|
260
260
|
expect(@server_thread.join(2)).not_to be_nil
|
|
261
261
|
end
|
|
262
262
|
end
|
|
263
|
+
|
|
264
|
+
describe "#{Thrift::NonblockingServer} with TLS transport" do
|
|
265
|
+
before(:each) do
|
|
266
|
+
@port = available_port
|
|
267
|
+
handler = Handler.new
|
|
268
|
+
processor = SpecNamespace::NonblockingService::Processor.new(handler)
|
|
269
|
+
@transport = Thrift::SSLServerSocket.new('localhost', @port, create_server_ssl_context)
|
|
270
|
+
transport_factory = Thrift::FramedTransportFactory.new
|
|
271
|
+
logger = Logger.new(STDERR)
|
|
272
|
+
logger.level = Logger::WARN
|
|
273
|
+
@server = Thrift::NonblockingServer.new(processor, @transport, transport_factory, nil, 5, logger)
|
|
274
|
+
handler.server = @server
|
|
275
|
+
|
|
276
|
+
@server_thread = Thread.new(Thread.current) do |master_thread|
|
|
277
|
+
begin
|
|
278
|
+
@server.serve
|
|
279
|
+
rescue => e
|
|
280
|
+
master_thread.raise e
|
|
281
|
+
end
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
@clients = []
|
|
285
|
+
wait_until_listening
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
after(:each) do
|
|
289
|
+
@clients.each(&:close)
|
|
290
|
+
@server.shutdown if @server
|
|
291
|
+
@server_thread.join(2) if @server_thread
|
|
292
|
+
@transport.close if @transport
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
it "should handle requests over TLS" do
|
|
296
|
+
expect(@server_thread).to be_alive
|
|
297
|
+
|
|
298
|
+
client = setup_tls_client
|
|
299
|
+
expect(client.greeting(true)).to eq(SpecNamespace::Hello.new)
|
|
300
|
+
|
|
301
|
+
@server.shutdown
|
|
302
|
+
expect(@server_thread.join(2)).to be_an_instance_of(Thread)
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
def setup_tls_client
|
|
306
|
+
transport = Thrift::FramedTransport.new(
|
|
307
|
+
Thrift::SSLSocket.new('localhost', @port, nil, create_client_ssl_context)
|
|
308
|
+
)
|
|
309
|
+
protocol = Thrift::BinaryProtocol.new(transport)
|
|
310
|
+
client = SpecNamespace::NonblockingService::Client.new(protocol)
|
|
311
|
+
transport.open
|
|
312
|
+
@clients << transport
|
|
313
|
+
client
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
def wait_until_listening
|
|
317
|
+
Timeout.timeout(2) do
|
|
318
|
+
until @transport.handle
|
|
319
|
+
raise "Server thread exited unexpectedly" unless @server_thread.alive?
|
|
320
|
+
sleep 0.01
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
def available_port
|
|
326
|
+
TCPServer.open('localhost', 0) { |server| server.addr[1] }
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def ssl_keys_dir
|
|
330
|
+
File.expand_path('../../../test/keys', __dir__)
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
def create_server_ssl_context
|
|
334
|
+
OpenSSL::SSL::SSLContext.new.tap do |ctx|
|
|
335
|
+
ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
|
336
|
+
if ctx.respond_to?(:min_version=) && OpenSSL::SSL.const_defined?(:TLS1_2_VERSION)
|
|
337
|
+
ctx.min_version = OpenSSL::SSL::TLS1_2_VERSION
|
|
338
|
+
end
|
|
339
|
+
ctx.ca_file = File.join(ssl_keys_dir, 'CA.pem')
|
|
340
|
+
ctx.cert = OpenSSL::X509::Certificate.new(File.read(File.join(ssl_keys_dir, 'server.crt')))
|
|
341
|
+
ctx.cert_store = OpenSSL::X509::Store.new
|
|
342
|
+
ctx.cert_store.add_file(File.join(ssl_keys_dir, 'client.pem'))
|
|
343
|
+
ctx.key = OpenSSL::PKey::RSA.new(File.read(File.join(ssl_keys_dir, 'server.key')))
|
|
344
|
+
end
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
def create_client_ssl_context
|
|
348
|
+
OpenSSL::SSL::SSLContext.new.tap do |ctx|
|
|
349
|
+
ctx.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
|
350
|
+
if ctx.respond_to?(:min_version=) && OpenSSL::SSL.const_defined?(:TLS1_2_VERSION)
|
|
351
|
+
ctx.min_version = OpenSSL::SSL::TLS1_2_VERSION
|
|
352
|
+
end
|
|
353
|
+
ctx.ca_file = File.join(ssl_keys_dir, 'CA.pem')
|
|
354
|
+
ctx.cert = OpenSSL::X509::Certificate.new(File.read(File.join(ssl_keys_dir, 'client.crt')))
|
|
355
|
+
ctx.cert_store = OpenSSL::X509::Store.new
|
|
356
|
+
ctx.cert_store.add_file(File.join(ssl_keys_dir, 'server.pem'))
|
|
357
|
+
ctx.key = OpenSSL::PKey::RSA.new(File.read(File.join(ssl_keys_dir, 'client.key')))
|
|
358
|
+
end
|
|
359
|
+
end
|
|
360
|
+
end
|
|
263
361
|
end
|
data/spec/processor_spec.rb
CHANGED
data/spec/serializer_spec.rb
CHANGED
data/spec/server_socket_spec.rb
CHANGED
|
@@ -21,7 +21,6 @@ require 'spec_helper'
|
|
|
21
21
|
require File.expand_path("#{File.dirname(__FILE__)}/socket_spec_shared")
|
|
22
22
|
|
|
23
23
|
describe 'Thrift::ServerSocket' do
|
|
24
|
-
|
|
25
24
|
describe Thrift::ServerSocket do
|
|
26
25
|
before(:each) do
|
|
27
26
|
@socket = Thrift::ServerSocket.new(1234)
|
|
@@ -44,6 +43,7 @@ describe 'Thrift::ServerSocket' do
|
|
|
44
43
|
expect(TCPServer).to receive(:new).with(nil, 1234).and_return(handle)
|
|
45
44
|
@socket.listen
|
|
46
45
|
sock = double("sock")
|
|
46
|
+
expect(sock).to receive(:setsockopt).with(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
|
47
47
|
expect(handle).to receive(:accept).and_return(sock)
|
|
48
48
|
trans = double("Socket")
|
|
49
49
|
expect(Thrift::Socket).to receive(:new).and_return(trans)
|
data/spec/server_spec.rb
CHANGED
|
@@ -19,7 +19,6 @@
|
|
|
19
19
|
require 'spec_helper'
|
|
20
20
|
|
|
21
21
|
describe 'Server' do
|
|
22
|
-
|
|
23
22
|
describe Thrift::BaseServer do
|
|
24
23
|
before(:each) do
|
|
25
24
|
@processor = double("Processor")
|
|
@@ -36,9 +35,9 @@ describe 'Server' do
|
|
|
36
35
|
end
|
|
37
36
|
|
|
38
37
|
it "should not serve" do
|
|
39
|
-
expect { @server.serve()}.to raise_error(NotImplementedError)
|
|
38
|
+
expect { @server.serve() }.to raise_error(NotImplementedError)
|
|
40
39
|
end
|
|
41
|
-
|
|
40
|
+
|
|
42
41
|
it "should provide a reasonable to_s" do
|
|
43
42
|
expect(@serverTrans).to receive(:to_s).once.and_return("serverTrans")
|
|
44
43
|
expect(@trans).to receive(:to_s).once.and_return("trans")
|
|
@@ -56,14 +55,14 @@ describe 'Server' do
|
|
|
56
55
|
@client = double("Client")
|
|
57
56
|
@server = described_class.new(@processor, @serverTrans, @trans, @prot)
|
|
58
57
|
end
|
|
59
|
-
|
|
58
|
+
|
|
60
59
|
it "should provide a reasonable to_s" do
|
|
61
60
|
expect(@serverTrans).to receive(:to_s).once.and_return("serverTrans")
|
|
62
61
|
expect(@trans).to receive(:to_s).once.and_return("trans")
|
|
63
62
|
expect(@prot).to receive(:to_s).once.and_return("prot")
|
|
64
63
|
expect(@server.to_s).to eq("simple(server(prot(trans(serverTrans))))")
|
|
65
64
|
end
|
|
66
|
-
|
|
65
|
+
|
|
67
66
|
it "should serve in the main thread" do
|
|
68
67
|
expect(@serverTrans).to receive(:listen).ordered
|
|
69
68
|
expect(@serverTrans).to receive(:accept).exactly(3).times.and_return(@client)
|
|
@@ -99,7 +98,7 @@ describe 'Server' do
|
|
|
99
98
|
expect(@prot).to receive(:to_s).once.and_return("prot")
|
|
100
99
|
expect(@server.to_s).to eq("threaded(server(prot(trans(serverTrans))))")
|
|
101
100
|
end
|
|
102
|
-
|
|
101
|
+
|
|
103
102
|
it "should serve using threads" do
|
|
104
103
|
expect(@serverTrans).to receive(:listen).ordered
|
|
105
104
|
expect(@serverTrans).to receive(:accept).exactly(3).times.and_return(@client)
|
|
@@ -137,10 +136,10 @@ describe 'Server' do
|
|
|
137
136
|
expect(@prot).to receive(:to_s).once.and_return("prot")
|
|
138
137
|
expect(@server.to_s).to eq("threadpool(server(prot(trans(server_trans))))")
|
|
139
138
|
end
|
|
140
|
-
|
|
139
|
+
|
|
141
140
|
it "should serve inside a thread" do
|
|
142
141
|
exception_q = @server.instance_variable_get(:@exception_q)
|
|
143
|
-
expect_any_instance_of(described_class).to receive(:serve) do
|
|
142
|
+
expect_any_instance_of(described_class).to receive(:serve) do
|
|
144
143
|
exception_q.push(StandardError.new('ERROR'))
|
|
145
144
|
end
|
|
146
145
|
expect { @server.rescuable_serve }.to(raise_error('ERROR'))
|
|
@@ -149,7 +148,7 @@ describe 'Server' do
|
|
|
149
148
|
|
|
150
149
|
it "should avoid running the server twice when retrying rescuable_serve" do
|
|
151
150
|
exception_q = @server.instance_variable_get(:@exception_q)
|
|
152
|
-
expect_any_instance_of(described_class).to receive(:serve) do
|
|
151
|
+
expect_any_instance_of(described_class).to receive(:serve) do
|
|
153
152
|
exception_q.push(StandardError.new('ERROR1'))
|
|
154
153
|
exception_q.push(StandardError.new('ERROR2'))
|
|
155
154
|
end
|
data/spec/socket_spec.rb
CHANGED
data/spec/socket_spec_shared.rb
CHANGED
|
@@ -67,38 +67,101 @@ shared_examples_for "a socket" do
|
|
|
67
67
|
@socket.open
|
|
68
68
|
allow(@handle).to receive(:closed?).and_return(true)
|
|
69
69
|
expect(@socket).not_to be_open
|
|
70
|
-
expect { @socket.write("fail") }.to raise_error(
|
|
71
|
-
expect { @socket.read(10) }.to raise_error(
|
|
70
|
+
expect { @socket.write("fail") }.to raise_error(Thrift::TransportException, "closed stream") { |e| expect(e.type).to eq(Thrift::TransportException::NOT_OPEN) }
|
|
71
|
+
expect { @socket.read(10) }.to raise_error(Thrift::TransportException, "closed stream") { |e| expect(e.type).to eq(Thrift::TransportException::NOT_OPEN) }
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
it "should support the timeout accessor for read" do
|
|
75
75
|
@socket.timeout = 3
|
|
76
76
|
@socket.open
|
|
77
|
-
expect(
|
|
78
|
-
expect(
|
|
77
|
+
expect(@handle).to receive(:read_nonblock).with(17).and_raise(IO::EAGAINWaitReadable)
|
|
78
|
+
expect(IO).to receive(:select) do |rd, wr, err, timeout|
|
|
79
|
+
expect(rd).to eq([@handle])
|
|
80
|
+
expect(wr).to be_nil
|
|
81
|
+
expect(err).to be_nil
|
|
82
|
+
expect(timeout).to be > 0
|
|
83
|
+
expect(timeout).to be <= 3
|
|
84
|
+
[[@handle], [], []]
|
|
85
|
+
end
|
|
86
|
+
expect(@handle).to receive(:read_nonblock).with(17).and_return("test data")
|
|
79
87
|
expect(@socket.read(17)).to eq("test data")
|
|
80
88
|
end
|
|
81
89
|
|
|
82
90
|
it "should support the timeout accessor for write" do
|
|
83
91
|
@socket.timeout = 3
|
|
84
92
|
@socket.open
|
|
85
|
-
|
|
86
|
-
expect(@handle).to receive(:write_nonblock).
|
|
87
|
-
|
|
93
|
+
write_calls = 0
|
|
94
|
+
expect(@handle).to receive(:write_nonblock).exactly(3).times do |chunk|
|
|
95
|
+
write_calls += 1
|
|
96
|
+
case write_calls
|
|
97
|
+
when 1
|
|
98
|
+
expect(chunk).to eq("test data")
|
|
99
|
+
raise IO::EAGAINWaitWritable
|
|
100
|
+
when 2
|
|
101
|
+
expect(chunk).to eq("test data")
|
|
102
|
+
4
|
|
103
|
+
when 3
|
|
104
|
+
expect(chunk).to eq(" data")
|
|
105
|
+
5
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
expect(IO).to receive(:select) do |rd, wr, err, timeout|
|
|
109
|
+
expect(rd).to be_nil
|
|
110
|
+
expect(wr).to eq([@handle])
|
|
111
|
+
expect(err).to be_nil
|
|
112
|
+
expect(timeout).to be > 0
|
|
113
|
+
expect(timeout).to be <= 3
|
|
114
|
+
[[], [@handle], []]
|
|
115
|
+
end
|
|
88
116
|
expect(@socket.write("test data")).to eq(9)
|
|
89
117
|
end
|
|
90
118
|
|
|
91
119
|
it "should raise an error when read times out" do
|
|
92
120
|
@socket.timeout = 0.5
|
|
93
121
|
@socket.open
|
|
94
|
-
expect(
|
|
122
|
+
expect(@handle).to receive(:read_nonblock).with(17).and_raise(IO::EAGAINWaitReadable)
|
|
123
|
+
expect(IO).to receive(:select).once { sleep(0.6); nil }
|
|
95
124
|
expect { @socket.read(17) }.to raise_error(Thrift::TransportException) { |e| expect(e.type).to eq(Thrift::TransportException::TIMED_OUT) }
|
|
96
125
|
end
|
|
97
126
|
|
|
98
127
|
it "should raise an error when write times out" do
|
|
99
128
|
@socket.timeout = 0.5
|
|
100
129
|
@socket.open
|
|
101
|
-
|
|
130
|
+
expect(@handle).to receive(:write_nonblock).with("test data").and_raise(IO::EAGAINWaitWritable)
|
|
131
|
+
expect(IO).to receive(:select).once { sleep(0.6); nil }
|
|
102
132
|
expect { @socket.write("test data") }.to raise_error(Thrift::TransportException) { |e| expect(e.type).to eq(Thrift::TransportException::TIMED_OUT) }
|
|
103
133
|
end
|
|
134
|
+
|
|
135
|
+
it "should read buffered SSL data without waiting on the raw socket again" do
|
|
136
|
+
@socket.timeout = 1
|
|
137
|
+
@socket.open
|
|
138
|
+
|
|
139
|
+
expect(@handle).to receive(:read_nonblock).with(4).ordered.and_raise(IO::EAGAINWaitReadable)
|
|
140
|
+
expect(IO).to receive(:select).once.ordered do |rd, wr, err, timeout|
|
|
141
|
+
expect(rd).to eq([@handle])
|
|
142
|
+
expect(wr).to be_nil
|
|
143
|
+
expect(err).to be_nil
|
|
144
|
+
expect(timeout).to be > 0
|
|
145
|
+
expect(timeout).to be <= 1
|
|
146
|
+
[[@handle], [], []]
|
|
147
|
+
end
|
|
148
|
+
expect(@handle).to receive(:read_nonblock).with(4).ordered.and_return("ABCD")
|
|
149
|
+
expect(@handle).to receive(:read_nonblock).with(5).ordered.and_return("12345")
|
|
150
|
+
|
|
151
|
+
expect(@socket.read(4)).to eq("ABCD")
|
|
152
|
+
expect(@socket.read(5)).to eq("12345")
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
it "should read without timeout using the blocking path" do
|
|
156
|
+
@socket.timeout = nil
|
|
157
|
+
@socket.open
|
|
158
|
+
|
|
159
|
+
expect(IO).not_to receive(:select)
|
|
160
|
+
expect(@handle).not_to receive(:read_nonblock)
|
|
161
|
+
expect(@handle).to receive(:readpartial).with(4).ordered.and_return("ABCD")
|
|
162
|
+
expect(@handle).to receive(:readpartial).with(5).ordered.and_return("12345")
|
|
163
|
+
|
|
164
|
+
expect(@socket.read(4)).to eq("ABCD")
|
|
165
|
+
expect(@socket.read(5)).to eq("12345")
|
|
166
|
+
end
|
|
104
167
|
end
|
data/spec/spec_helper.rb
CHANGED
|
@@ -55,7 +55,7 @@ require 'nonblocking_service'
|
|
|
55
55
|
|
|
56
56
|
module Fixtures
|
|
57
57
|
COMPACT_PROTOCOL_TEST_STRUCT = Thrift::Test::COMPACT_TEST.dup
|
|
58
|
-
COMPACT_PROTOCOL_TEST_STRUCT.a_binary = [0,1,2,3,4,5,6,7,8].pack('c*')
|
|
58
|
+
COMPACT_PROTOCOL_TEST_STRUCT.a_binary = [0, 1, 2, 3, 4, 5, 6, 7, 8].pack('c*')
|
|
59
59
|
COMPACT_PROTOCOL_TEST_STRUCT.set_byte_map = nil
|
|
60
60
|
COMPACT_PROTOCOL_TEST_STRUCT.map_byte_map = nil
|
|
61
61
|
end
|
|
@@ -21,12 +21,23 @@ require 'spec_helper'
|
|
|
21
21
|
require File.expand_path("#{File.dirname(__FILE__)}/socket_spec_shared")
|
|
22
22
|
|
|
23
23
|
describe 'SSLServerSocket' do
|
|
24
|
-
|
|
25
24
|
describe Thrift::SSLServerSocket do
|
|
26
25
|
before(:each) do
|
|
27
26
|
@socket = Thrift::SSLServerSocket.new(1234)
|
|
28
27
|
end
|
|
29
28
|
|
|
29
|
+
it "should delegate to_io to the underlying SSL server handle" do
|
|
30
|
+
tcp_server = double("TCPServer")
|
|
31
|
+
ssl_server = double("SSLServer")
|
|
32
|
+
|
|
33
|
+
allow(TCPServer).to receive(:new).with(nil, 1234).and_return(tcp_server)
|
|
34
|
+
allow(OpenSSL::SSL::SSLServer).to receive(:new).with(tcp_server, nil).and_return(ssl_server)
|
|
35
|
+
allow(ssl_server).to receive(:to_io).and_return(tcp_server)
|
|
36
|
+
|
|
37
|
+
@socket.listen
|
|
38
|
+
expect(@socket.to_io).to eq(tcp_server)
|
|
39
|
+
end
|
|
40
|
+
|
|
30
41
|
it "should provide a reasonable to_s" do
|
|
31
42
|
expect(@socket.to_s).to eq("ssl(socket(:1234))")
|
|
32
43
|
end
|
data/spec/ssl_socket_spec.rb
CHANGED
|
@@ -21,7 +21,6 @@ require 'spec_helper'
|
|
|
21
21
|
require File.expand_path("#{File.dirname(__FILE__)}/socket_spec_shared")
|
|
22
22
|
|
|
23
23
|
describe 'SSLSocket' do
|
|
24
|
-
|
|
25
24
|
describe Thrift::SSLSocket do
|
|
26
25
|
before(:each) do
|
|
27
26
|
@context = OpenSSL::SSL::SSLContext.new
|
|
@@ -35,6 +34,7 @@ describe 'SSLSocket' do
|
|
|
35
34
|
allow(@handle).to receive(:connect_nonblock)
|
|
36
35
|
allow(@handle).to receive(:close)
|
|
37
36
|
allow(@handle).to receive(:post_connection_check)
|
|
37
|
+
allow(@handle).to receive(:to_io).and_return(@simple_socket_handle)
|
|
38
38
|
|
|
39
39
|
allow(::Socket).to receive(:new).and_return(@simple_socket_handle)
|
|
40
40
|
allow(OpenSSL::SSL::SSLSocket).to receive(:new).and_return(@handle)
|
|
@@ -71,6 +71,15 @@ describe 'SSLSocket' do
|
|
|
71
71
|
expect(Thrift::SSLSocket.new('localhost', 8080, 5, @context).ssl_context).to eq(@context)
|
|
72
72
|
end
|
|
73
73
|
|
|
74
|
+
it "should delegate to_io to the underlying SSL socket handle" do
|
|
75
|
+
@socket.open
|
|
76
|
+
expect(@socket.to_io).to eq(@simple_socket_handle)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it "should raise IOError when to_io is called on a closed stream" do
|
|
80
|
+
expect { @socket.to_io }.to raise_error(IOError, 'closed stream')
|
|
81
|
+
end
|
|
82
|
+
|
|
74
83
|
it "should provide a reasonable to_s" do
|
|
75
84
|
expect(Thrift::SSLSocket.new('myhost', 8090).to_s).to eq("ssl(socket(myhost:8090))")
|
|
76
85
|
end
|
|
@@ -20,7 +20,6 @@
|
|
|
20
20
|
require 'spec_helper'
|
|
21
21
|
|
|
22
22
|
describe 'StructNestedContainers' do
|
|
23
|
-
|
|
24
23
|
def with_type_checking
|
|
25
24
|
saved_type_checking, Thrift.type_checking = Thrift.type_checking, true
|
|
26
25
|
begin
|
|
@@ -166,7 +165,7 @@ describe 'StructNestedContainers' do
|
|
|
166
165
|
with_type_checking do
|
|
167
166
|
a, b = SpecNamespace::NestedMapInMapKey.new, SpecNamespace::NestedMapInMapKey.new
|
|
168
167
|
[a, b].each do |thrift_struct|
|
|
169
|
-
thrift_struct.value = { { 1 => 2, 3 => 4} => 1, {2 => 3, 4 => 5}
|
|
168
|
+
thrift_struct.value = { { 1 => 2, 3 => 4} => 1, {2 => 3, 4 => 5} => 2 }
|
|
170
169
|
thrift_struct.validate
|
|
171
170
|
end
|
|
172
171
|
expect(a).to eq(b)
|
data/spec/struct_spec.rb
CHANGED
|
@@ -20,11 +20,10 @@
|
|
|
20
20
|
require 'spec_helper'
|
|
21
21
|
|
|
22
22
|
describe 'Struct' do
|
|
23
|
-
|
|
24
23
|
describe Thrift::Struct do
|
|
25
24
|
it "should iterate over all fields properly" do
|
|
26
25
|
fields = {}
|
|
27
|
-
SpecNamespace::Foo.new.each_field { |fid,field_info| fields[fid] = field_info }
|
|
26
|
+
SpecNamespace::Foo.new.each_field { |fid, field_info| fields[fid] = field_info }
|
|
28
27
|
expect(fields).to eq(SpecNamespace::Foo::FIELDS)
|
|
29
28
|
end
|
|
30
29
|
|
|
@@ -51,10 +50,10 @@ describe 'Struct' do
|
|
|
51
50
|
begin
|
|
52
51
|
struct = SpecNamespace::Foo.new
|
|
53
52
|
struct.ints << 17
|
|
54
|
-
expect(SpecNamespace::Foo.new.ints).to eq([1,2,2,3])
|
|
53
|
+
expect(SpecNamespace::Foo.new.ints).to eq([1, 2, 2, 3])
|
|
55
54
|
ensure
|
|
56
55
|
# ensure no leakage to other tests
|
|
57
|
-
SpecNamespace::Foo::FIELDS[4][:default] = [1,2,2,3]
|
|
56
|
+
SpecNamespace::Foo::FIELDS[4][:default] = [1, 2, 2, 3]
|
|
58
57
|
end
|
|
59
58
|
end
|
|
60
59
|
|
|
@@ -142,6 +141,34 @@ describe 'Struct' do
|
|
|
142
141
|
expect(struct.shorts).to eq(Set.new([3, 2]))
|
|
143
142
|
end
|
|
144
143
|
|
|
144
|
+
it "rejects negative container sizes while reading" do
|
|
145
|
+
struct = SpecNamespace::Foo.new
|
|
146
|
+
prot = Thrift::BaseProtocol.new(double("transport"))
|
|
147
|
+
|
|
148
|
+
expect(prot).to receive(:read_list_begin).and_return([Thrift::Types::I32, -1])
|
|
149
|
+
|
|
150
|
+
expect {
|
|
151
|
+
struct.send(:read_field, prot, SpecNamespace::Foo::FIELDS[4])
|
|
152
|
+
}.to raise_error(Thrift::ProtocolException, "Negative size") { |error|
|
|
153
|
+
expect(error.type).to eq(Thrift::ProtocolException::NEGATIVE_SIZE)
|
|
154
|
+
}
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
it "does not preallocate arrays from declared list sizes" do
|
|
158
|
+
struct = SpecNamespace::Foo.new
|
|
159
|
+
prot = Thrift::BaseProtocol.new(double("transport"))
|
|
160
|
+
declared_size = 1 << 30
|
|
161
|
+
sentinel = RuntimeError.new("stop after first element")
|
|
162
|
+
|
|
163
|
+
expect(prot).to receive(:read_list_begin).and_return([Thrift::Types::I32, declared_size])
|
|
164
|
+
expect(prot).to receive(:read_i32).and_raise(sentinel)
|
|
165
|
+
expect(Array).not_to receive(:new).with(declared_size)
|
|
166
|
+
|
|
167
|
+
expect {
|
|
168
|
+
struct.send(:read_field, prot, SpecNamespace::Foo::FIELDS[4])
|
|
169
|
+
}.to raise_error(sentinel)
|
|
170
|
+
end
|
|
171
|
+
|
|
145
172
|
it "should serialize false boolean fields correctly" do
|
|
146
173
|
b = SpecNamespace::BoolStruct.new(:yesno => false)
|
|
147
174
|
prot = Thrift::BinaryProtocol.new(Thrift::MemoryBufferTransport.new)
|
|
@@ -181,7 +208,7 @@ describe 'Struct' do
|
|
|
181
208
|
end
|
|
182
209
|
|
|
183
210
|
it "should write itself to the wire" do
|
|
184
|
-
prot = Thrift::BaseProtocol.new(double("transport")) #mock("Protocol")
|
|
211
|
+
prot = Thrift::BaseProtocol.new(double("transport")) # mock("Protocol")
|
|
185
212
|
expect(prot).to receive(:write_struct_begin).with("SpecNamespace::Foo")
|
|
186
213
|
expect(prot).to receive(:write_struct_begin).with("SpecNamespace::Hello")
|
|
187
214
|
expect(prot).to receive(:write_struct_end).twice
|
|
@@ -227,7 +254,7 @@ describe 'Struct' do
|
|
|
227
254
|
it "should support optional type-checking in Thrift::Struct.new" do
|
|
228
255
|
Thrift.type_checking = true
|
|
229
256
|
begin
|
|
230
|
-
expect { SpecNamespace::Hello.new(:greeting => 3) }.to raise_error(Thrift::TypeError,
|
|
257
|
+
expect { SpecNamespace::Hello.new(:greeting => 3) }.to raise_error(Thrift::TypeError, "Expected Types::STRING, received Integer for field greeting")
|
|
231
258
|
ensure
|
|
232
259
|
Thrift.type_checking = false
|
|
233
260
|
end
|
|
@@ -238,7 +265,7 @@ describe 'Struct' do
|
|
|
238
265
|
Thrift.type_checking = true
|
|
239
266
|
begin
|
|
240
267
|
hello = SpecNamespace::Hello.new
|
|
241
|
-
expect { hello.greeting = 3 }.to raise_error(Thrift::TypeError,
|
|
268
|
+
expect { hello.greeting = 3 }.to raise_error(Thrift::TypeError, "Expected Types::STRING, received Integer for field greeting")
|
|
242
269
|
ensure
|
|
243
270
|
Thrift.type_checking = false
|
|
244
271
|
end
|
|
@@ -259,9 +286,9 @@ describe 'Struct' do
|
|
|
259
286
|
prot = Thrift::BaseProtocol.new(double("trans"))
|
|
260
287
|
expect(prot).to receive(:write_struct_begin).with("SpecNamespace::Xception")
|
|
261
288
|
expect(prot).to receive(:write_struct_end)
|
|
262
|
-
expect(prot).to receive(:write_field_begin).with('message', Thrift::Types::STRING, 1)
|
|
289
|
+
expect(prot).to receive(:write_field_begin).with('message', Thrift::Types::STRING, 1)
|
|
263
290
|
expect(prot).to receive(:write_string).with("something happened")
|
|
264
|
-
expect(prot).to receive(:write_field_begin).with('code', Thrift::Types::I32, 2)
|
|
291
|
+
expect(prot).to receive(:write_field_begin).with('code', Thrift::Types::I32, 2)
|
|
265
292
|
expect(prot).to receive(:write_i32).with(1)
|
|
266
293
|
expect(prot).to receive(:write_field_stop)
|
|
267
294
|
expect(prot).to receive(:write_field_end).twice
|
|
@@ -289,5 +316,82 @@ describe 'Struct' do
|
|
|
289
316
|
e.write(prot)
|
|
290
317
|
end
|
|
291
318
|
end
|
|
319
|
+
|
|
320
|
+
it "should handle UUID fields in structs" do
|
|
321
|
+
struct = SpecNamespace::Foo.new(
|
|
322
|
+
simple: 42,
|
|
323
|
+
words: 'test',
|
|
324
|
+
opt_uuid: '550e8400-e29b-41d4-a716-446655440000'
|
|
325
|
+
)
|
|
326
|
+
|
|
327
|
+
trans = Thrift::MemoryBufferTransport.new
|
|
328
|
+
prot = Thrift::BinaryProtocol.new(trans)
|
|
329
|
+
|
|
330
|
+
struct.write(prot)
|
|
331
|
+
|
|
332
|
+
result = SpecNamespace::Foo.new
|
|
333
|
+
result.read(prot)
|
|
334
|
+
|
|
335
|
+
expect(result.simple).to eq(42)
|
|
336
|
+
expect(result.words).to eq('test')
|
|
337
|
+
expect(result.opt_uuid).to eq('550e8400-e29b-41d4-a716-446655440000')
|
|
338
|
+
end
|
|
339
|
+
|
|
340
|
+
it "should handle optional UUID fields when unset" do
|
|
341
|
+
struct = SpecNamespace::Foo.new(simple: 42, words: 'test')
|
|
342
|
+
expect(struct.opt_uuid).to be_nil
|
|
343
|
+
expect(struct.opt_uuid?).to be_falsey
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
it "should handle list of UUIDs in SimpleList" do
|
|
347
|
+
uuids = ['550e8400-e29b-41d4-a716-446655440000', '6ba7b810-9dad-11d1-80b4-00c04fd430c8']
|
|
348
|
+
struct = SpecNamespace::SimpleList.new(uuids: uuids)
|
|
349
|
+
|
|
350
|
+
trans = Thrift::MemoryBufferTransport.new
|
|
351
|
+
prot = Thrift::CompactProtocol.new(trans)
|
|
352
|
+
|
|
353
|
+
struct.write(prot)
|
|
354
|
+
|
|
355
|
+
result = SpecNamespace::SimpleList.new
|
|
356
|
+
result.read(prot)
|
|
357
|
+
|
|
358
|
+
expect(result.uuids).to eq(uuids)
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
it "should normalize UUID case to lowercase" do
|
|
362
|
+
struct = SpecNamespace::Foo.new(opt_uuid: '550E8400-E29B-41D4-A716-446655440000')
|
|
363
|
+
|
|
364
|
+
trans = Thrift::MemoryBufferTransport.new
|
|
365
|
+
prot = Thrift::BinaryProtocol.new(trans)
|
|
366
|
+
|
|
367
|
+
struct.write(prot)
|
|
368
|
+
|
|
369
|
+
result = SpecNamespace::Foo.new
|
|
370
|
+
result.read(prot)
|
|
371
|
+
|
|
372
|
+
expect(result.opt_uuid).to eq('550e8400-e29b-41d4-a716-446655440000')
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
it "should handle UUID alongside other types in SimpleList" do
|
|
376
|
+
struct = SpecNamespace::SimpleList.new(
|
|
377
|
+
bools: [true, false],
|
|
378
|
+
i32s: [1, 2, 3],
|
|
379
|
+
strings: ['hello', 'world'],
|
|
380
|
+
uuids: ['550e8400-e29b-41d4-a716-446655440000', '00000000-0000-0000-0000-000000000000']
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
trans = Thrift::MemoryBufferTransport.new
|
|
384
|
+
prot = Thrift::BinaryProtocol.new(trans)
|
|
385
|
+
|
|
386
|
+
struct.write(prot)
|
|
387
|
+
|
|
388
|
+
result = SpecNamespace::SimpleList.new
|
|
389
|
+
result.read(prot)
|
|
390
|
+
|
|
391
|
+
expect(result.bools).to eq([true, false])
|
|
392
|
+
expect(result.i32s).to eq([1, 2, 3])
|
|
393
|
+
expect(result.strings).to eq(['hello', 'world'])
|
|
394
|
+
expect(result.uuids).to eq(['550e8400-e29b-41d4-a716-446655440000', '00000000-0000-0000-0000-000000000000'])
|
|
395
|
+
end
|
|
292
396
|
end
|
|
293
397
|
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Licensed to the Apache Software Foundation (ASF) under one
|
|
3
|
+
# or more contributor license agreements. See the NOTICE file
|
|
4
|
+
# distributed with this work for additional information
|
|
5
|
+
# regarding copyright ownership. The ASF licenses this file
|
|
6
|
+
# to you under the Apache License, Version 2.0 (the
|
|
7
|
+
# "License"); you may not use this file except in compliance
|
|
8
|
+
# with the License. You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing,
|
|
13
|
+
# software distributed under the License is distributed on an
|
|
14
|
+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
15
|
+
# KIND, either express or implied. See the License for the
|
|
16
|
+
# specific language governing permissions and limitations
|
|
17
|
+
# under the License.
|
|
18
|
+
#
|
|
19
|
+
|
|
20
|
+
module HeaderProtocolHelper
|
|
21
|
+
def varint32(n)
|
|
22
|
+
bytes = []
|
|
23
|
+
loop do
|
|
24
|
+
if (n & ~0x7f) == 0
|
|
25
|
+
bytes << n
|
|
26
|
+
break
|
|
27
|
+
else
|
|
28
|
+
bytes << ((n & 0x7f) | 0x80)
|
|
29
|
+
n >>= 7
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
bytes.pack('C*')
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def build_header_frame(header_data, payload = Thrift::Bytes.empty_byte_buffer, header_words: nil)
|
|
36
|
+
header_data = Thrift::Bytes.force_binary_encoding(header_data)
|
|
37
|
+
if header_words.nil?
|
|
38
|
+
padding = (4 - (header_data.bytesize % 4)) % 4
|
|
39
|
+
header_data += "\x00" * padding
|
|
40
|
+
header_words = header_data.bytesize / 4
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
frame_size = 2 + 2 + 4 + 2 + header_data.bytesize + payload.bytesize
|
|
44
|
+
frame = Thrift::Bytes.empty_byte_buffer
|
|
45
|
+
frame << [frame_size].pack('N')
|
|
46
|
+
frame << [Thrift::HeaderTransport::HEADER_MAGIC].pack('n')
|
|
47
|
+
frame << [0].pack('n')
|
|
48
|
+
frame << [0].pack('N')
|
|
49
|
+
frame << [header_words].pack('n')
|
|
50
|
+
frame << header_data
|
|
51
|
+
frame << payload
|
|
52
|
+
frame
|
|
53
|
+
end
|
|
54
|
+
end
|