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
|
@@ -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
|
|
@@ -106,7 +107,7 @@ module Thrift
|
|
|
106
107
|
@num_threads = num
|
|
107
108
|
@logger = logger
|
|
108
109
|
@connections = []
|
|
109
|
-
@buffers = Hash.new { |h, k| h[k] =
|
|
110
|
+
@buffers = Hash.new { |h, k| h[k] = Bytes.empty_byte_buffer }
|
|
110
111
|
@signal_queue = Queue.new
|
|
111
112
|
@signal_pipes = IO.pipe
|
|
112
113
|
@signal_pipes[1].sync = true
|
|
@@ -138,7 +139,12 @@ module Thrift
|
|
|
138
139
|
|
|
139
140
|
def ensure_closed
|
|
140
141
|
kill_worker_threads if @worker_threads
|
|
141
|
-
@iom_thread
|
|
142
|
+
if @iom_thread&.alive?
|
|
143
|
+
@iom_thread.kill
|
|
144
|
+
@iom_thread.join
|
|
145
|
+
end
|
|
146
|
+
close_connections
|
|
147
|
+
close_signal_pipes
|
|
142
148
|
end
|
|
143
149
|
|
|
144
150
|
private
|
|
@@ -246,6 +252,26 @@ module Thrift
|
|
|
246
252
|
@worker_threads.clear
|
|
247
253
|
end
|
|
248
254
|
|
|
255
|
+
def close_connections
|
|
256
|
+
@connections.each do |fd|
|
|
257
|
+
begin
|
|
258
|
+
fd.close
|
|
259
|
+
rescue IOError, SystemCallError, TransportException
|
|
260
|
+
end
|
|
261
|
+
end
|
|
262
|
+
@connections.clear
|
|
263
|
+
@buffers.clear
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def close_signal_pipes
|
|
267
|
+
@signal_pipes.each do |pipe|
|
|
268
|
+
begin
|
|
269
|
+
pipe.close unless pipe.closed?
|
|
270
|
+
rescue IOError
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
|
|
249
275
|
def slice_frame!(buf)
|
|
250
276
|
if buf.length >= 4
|
|
251
277
|
size = buf.unpack('N').first
|
|
@@ -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
|
|
@@ -23,7 +24,14 @@ module Thrift
|
|
|
23
24
|
begin
|
|
24
25
|
@server_transport.listen
|
|
25
26
|
loop do
|
|
26
|
-
|
|
27
|
+
begin
|
|
28
|
+
client = @server_transport.accept
|
|
29
|
+
rescue Errno::ECONNRESET, Errno::EPIPE
|
|
30
|
+
next
|
|
31
|
+
rescue => e
|
|
32
|
+
next if defined?(OpenSSL::SSL::SSLError) && e.is_a?(OpenSSL::SSL::SSLError)
|
|
33
|
+
raise
|
|
34
|
+
end
|
|
27
35
|
trans = @transport_factory.get_transport(client)
|
|
28
36
|
prot = @protocol_factory.get_protocol(trans)
|
|
29
37
|
begin
|
|
@@ -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,7 +26,14 @@ module Thrift
|
|
|
25
26
|
begin
|
|
26
27
|
@server_transport.listen
|
|
27
28
|
loop do
|
|
28
|
-
|
|
29
|
+
begin
|
|
30
|
+
client = @server_transport.accept
|
|
31
|
+
rescue Errno::ECONNRESET, Errno::EPIPE
|
|
32
|
+
next
|
|
33
|
+
rescue => e
|
|
34
|
+
next if defined?(OpenSSL::SSL::SSLError) && e.is_a?(OpenSSL::SSL::SSLError)
|
|
35
|
+
raise
|
|
36
|
+
end
|
|
29
37
|
trans = @transport_factory.get_transport(client)
|
|
30
38
|
prot = @protocol_factory.get_protocol(trans)
|
|
31
39
|
Thread.new(prot, trans) do |p, t|
|
data/lib/thrift/struct.rb
CHANGED
data/lib/thrift/struct_union.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,12 +21,6 @@ require 'set'
|
|
|
20
21
|
|
|
21
22
|
module Thrift
|
|
22
23
|
module Struct_Union
|
|
23
|
-
def validate_container_size(size)
|
|
24
|
-
return size unless size < 0
|
|
25
|
-
|
|
26
|
-
raise ProtocolException.new(ProtocolException::NEGATIVE_SIZE, 'Negative size')
|
|
27
|
-
end
|
|
28
|
-
|
|
29
24
|
def name_to_id(name)
|
|
30
25
|
names_to_ids = self.class.instance_variable_get(:@names_to_ids)
|
|
31
26
|
unless names_to_ids
|
|
@@ -61,7 +56,7 @@ module Thrift
|
|
|
61
56
|
value.read(iprot)
|
|
62
57
|
when Types::MAP
|
|
63
58
|
key_type, val_type, size = iprot.read_map_begin
|
|
64
|
-
|
|
59
|
+
raise ProtocolException.new(ProtocolException::NEGATIVE_SIZE, 'Negative size') unless size >= 0
|
|
65
60
|
# Skip the map contents if the declared key or value types don't match the expected ones.
|
|
66
61
|
if (size != 0 && (key_type != field[:key][:type] || val_type != field[:value][:type]))
|
|
67
62
|
size.times do
|
|
@@ -80,7 +75,7 @@ module Thrift
|
|
|
80
75
|
iprot.read_map_end
|
|
81
76
|
when Types::LIST
|
|
82
77
|
e_type, size = iprot.read_list_begin
|
|
83
|
-
|
|
78
|
+
raise ProtocolException.new(ProtocolException::NEGATIVE_SIZE, 'Negative size') unless size >= 0
|
|
84
79
|
# Skip the list contents if the declared element type doesn't match the expected one.
|
|
85
80
|
if (e_type != field[:element][:type])
|
|
86
81
|
size.times do
|
|
@@ -96,7 +91,7 @@ module Thrift
|
|
|
96
91
|
iprot.read_list_end
|
|
97
92
|
when Types::SET
|
|
98
93
|
e_type, size = iprot.read_set_begin
|
|
99
|
-
|
|
94
|
+
raise ProtocolException.new(ProtocolException::NEGATIVE_SIZE, 'Negative size') unless size >= 0
|
|
100
95
|
# Skip the set contents if the declared element type doesn't match the expected one.
|
|
101
96
|
if (e_type != field[:element][:type])
|
|
102
97
|
size.times do
|
data/lib/thrift/thrift_native.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,5 +21,5 @@
|
|
|
20
21
|
begin
|
|
21
22
|
require "thrift_native"
|
|
22
23
|
rescue LoadError
|
|
23
|
-
|
|
24
|
+
warn "Unable to load thrift_native extension. Defaulting to pure Ruby libraries."
|
|
24
25
|
end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# encoding: ascii-8bit
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
#
|
|
3
4
|
# Licensed to the Apache Software Foundation (ASF) under one
|
|
4
5
|
# or more contributor license agreements. See the NOTICE file
|
|
@@ -20,6 +21,8 @@
|
|
|
20
21
|
|
|
21
22
|
module Thrift
|
|
22
23
|
class BaseServerTransport
|
|
24
|
+
DEFAULT_CLIENT_TIMEOUT = 5
|
|
25
|
+
|
|
23
26
|
def listen
|
|
24
27
|
raise NotImplementedError
|
|
25
28
|
end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# encoding: ascii-8bit
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
#
|
|
3
4
|
# Licensed to the Apache Software Foundation (ASF) under one
|
|
4
5
|
# or more contributor license agreements. See the NOTICE file
|
|
@@ -25,6 +26,8 @@ module Thrift
|
|
|
25
26
|
ALREADY_OPEN = 2
|
|
26
27
|
TIMED_OUT = 3
|
|
27
28
|
END_OF_FILE = 4
|
|
29
|
+
NEGATIVE_SIZE = 5
|
|
30
|
+
SIZE_LIMIT = 6
|
|
28
31
|
|
|
29
32
|
attr_reader :type
|
|
30
33
|
|
|
@@ -80,8 +83,9 @@ module Thrift
|
|
|
80
83
|
end
|
|
81
84
|
|
|
82
85
|
def read_all(size)
|
|
83
|
-
|
|
84
|
-
|
|
86
|
+
raise TransportException.new(TransportException::NEGATIVE_SIZE, 'Negative size') unless size >= 0
|
|
87
|
+
return Bytes.empty_byte_buffer if size == 0
|
|
88
|
+
buf = Bytes.force_binary_encoding(read(size))
|
|
85
89
|
while (buf.length < size)
|
|
86
90
|
chunk = read(size - buf.length)
|
|
87
91
|
buf << chunk
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# encoding: ascii-8bit
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
#
|
|
3
4
|
# Licensed to the Apache Software Foundation (ASF) under one
|
|
4
5
|
# or more contributor license agreements. See the NOTICE file
|
|
@@ -73,6 +74,12 @@ module Thrift
|
|
|
73
74
|
# Maximum frame size (~1GB)
|
|
74
75
|
MAX_FRAME_SIZE = 0x3FFFFFFF
|
|
75
76
|
|
|
77
|
+
# Default decompressed-size cap for ZLIB transform (~15.6 MB, matches other Thrift bindings)
|
|
78
|
+
DEFAULT_MAX_DECOMPRESSED_SIZE = 16_384_000
|
|
79
|
+
|
|
80
|
+
# Chunk size for streaming inflate loop
|
|
81
|
+
ZLIB_INFLATE_CHUNK_SIZE = 16_384
|
|
82
|
+
|
|
76
83
|
# Binary protocol version mask and version 1
|
|
77
84
|
BINARY_VERSION_MASK = 0xffff0000
|
|
78
85
|
BINARY_VERSION_1 = 0x80010000
|
|
@@ -112,6 +119,7 @@ module Thrift
|
|
|
112
119
|
@sequence_id = 0
|
|
113
120
|
@flags = 0
|
|
114
121
|
@max_frame_size = MAX_FRAME_SIZE
|
|
122
|
+
@max_decompressed_size = DEFAULT_MAX_DECOMPRESSED_SIZE
|
|
115
123
|
end
|
|
116
124
|
|
|
117
125
|
def sequence_id=(sequence_id)
|
|
@@ -172,6 +180,14 @@ module Thrift
|
|
|
172
180
|
@max_frame_size = size
|
|
173
181
|
end
|
|
174
182
|
|
|
183
|
+
# Sets the maximum allowed decompressed size for ZLIB transforms
|
|
184
|
+
def set_max_decompressed_size(size)
|
|
185
|
+
if size <= 0 || size > MAX_FRAME_SIZE
|
|
186
|
+
raise ArgumentError, "max_decompressed_size must be > 0 and <= #{MAX_FRAME_SIZE}"
|
|
187
|
+
end
|
|
188
|
+
@max_decompressed_size = size
|
|
189
|
+
end
|
|
190
|
+
|
|
175
191
|
def read(sz)
|
|
176
192
|
# Try reading from existing buffer
|
|
177
193
|
data = @read_buffer.read(sz)
|
|
@@ -369,13 +385,43 @@ module Thrift
|
|
|
369
385
|
payload = buf.read
|
|
370
386
|
transforms.each do |transform_id|
|
|
371
387
|
if transform_id == HeaderTransformID::ZLIB
|
|
372
|
-
payload =
|
|
388
|
+
payload = bounded_inflate(payload)
|
|
373
389
|
end
|
|
374
390
|
end
|
|
375
391
|
|
|
376
392
|
StringIO.new(payload)
|
|
377
393
|
end
|
|
378
394
|
|
|
395
|
+
# Inflates +compressed+ with a running byte-count check against @max_decompressed_size.
|
|
396
|
+
# Raises TransportException::SIZE_LIMIT if the decompressed output would exceed the limit.
|
|
397
|
+
def bounded_inflate(compressed)
|
|
398
|
+
inflater = Zlib::Inflate.new
|
|
399
|
+
buffer = Bytes.empty_byte_buffer
|
|
400
|
+
offset = 0
|
|
401
|
+
begin
|
|
402
|
+
while offset < compressed.bytesize
|
|
403
|
+
buffer << inflater.inflate(compressed.byteslice(offset, ZLIB_INFLATE_CHUNK_SIZE))
|
|
404
|
+
if buffer.bytesize > @max_decompressed_size
|
|
405
|
+
raise TransportException.new(
|
|
406
|
+
TransportException::SIZE_LIMIT,
|
|
407
|
+
"Decompressed size exceeds limit of #{@max_decompressed_size}"
|
|
408
|
+
)
|
|
409
|
+
end
|
|
410
|
+
offset += ZLIB_INFLATE_CHUNK_SIZE
|
|
411
|
+
end
|
|
412
|
+
buffer << inflater.finish
|
|
413
|
+
if buffer.bytesize > @max_decompressed_size
|
|
414
|
+
raise TransportException.new(
|
|
415
|
+
TransportException::SIZE_LIMIT,
|
|
416
|
+
"Decompressed size exceeds limit of #{@max_decompressed_size}"
|
|
417
|
+
)
|
|
418
|
+
end
|
|
419
|
+
buffer
|
|
420
|
+
ensure
|
|
421
|
+
inflater.close rescue nil
|
|
422
|
+
end
|
|
423
|
+
end
|
|
424
|
+
|
|
379
425
|
# Flushes data in Header format
|
|
380
426
|
def flush_header_format(payload)
|
|
381
427
|
# Apply transforms
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# encoding: ascii-8bit
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
#
|
|
3
4
|
# Licensed to the Apache Software Foundation (ASF) under one
|
|
4
5
|
# or more contributor license agreements. See the NOTICE file
|
|
@@ -70,6 +71,12 @@ module Thrift
|
|
|
70
71
|
data
|
|
71
72
|
end
|
|
72
73
|
|
|
74
|
+
def read_all(size)
|
|
75
|
+
raise TransportException.new(TransportException::NEGATIVE_SIZE, 'Negative size') unless size >= 0
|
|
76
|
+
|
|
77
|
+
read(size)
|
|
78
|
+
end
|
|
79
|
+
|
|
73
80
|
def read_byte
|
|
74
81
|
raise EOFError.new("Not enough bytes remain in buffer") if @index >= @buf.size
|
|
75
82
|
val = Bytes.get_string_byte(@buf, @index)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# encoding: ascii-8bit
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
#
|
|
3
4
|
# Licensed to the Apache Software Foundation (ASF) under one
|
|
4
5
|
# or more contributor license agreements. See the NOTICE file
|
|
@@ -22,8 +23,8 @@ require 'socket'
|
|
|
22
23
|
|
|
23
24
|
module Thrift
|
|
24
25
|
class ServerSocket < BaseServerTransport
|
|
25
|
-
# call-seq: initialize(host = nil, port)
|
|
26
|
-
def initialize(host_or_port, port = nil)
|
|
26
|
+
# call-seq: initialize(host = nil, port, client_timeout: DEFAULT_CLIENT_TIMEOUT)
|
|
27
|
+
def initialize(host_or_port, port = nil, client_timeout: DEFAULT_CLIENT_TIMEOUT)
|
|
27
28
|
if port
|
|
28
29
|
@host = host_or_port
|
|
29
30
|
@port = port
|
|
@@ -31,10 +32,11 @@ module Thrift
|
|
|
31
32
|
@host = nil
|
|
32
33
|
@port = host_or_port
|
|
33
34
|
end
|
|
35
|
+
@client_timeout = client_timeout
|
|
34
36
|
@handle = nil
|
|
35
37
|
end
|
|
36
38
|
|
|
37
|
-
attr_reader :handle
|
|
39
|
+
attr_reader :handle, :client_timeout
|
|
38
40
|
|
|
39
41
|
def listen
|
|
40
42
|
@handle = TCPServer.new(@host, @port)
|
|
@@ -45,6 +47,7 @@ module Thrift
|
|
|
45
47
|
sock = @handle.accept
|
|
46
48
|
sock.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
|
|
47
49
|
trans = Socket.new
|
|
50
|
+
trans.timeout = @client_timeout
|
|
48
51
|
trans.handle = sock
|
|
49
52
|
trans
|
|
50
53
|
end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# encoding: ascii-8bit
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
#
|
|
3
4
|
# Licensed to the Apache Software Foundation (ASF) under one
|
|
4
5
|
# or more contributor license agreements. See the NOTICE file
|
|
@@ -33,39 +34,19 @@ module Thrift
|
|
|
33
34
|
attr_accessor :handle, :timeout
|
|
34
35
|
|
|
35
36
|
def open
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
socket = ::Socket.new(addrinfo[4], ::Socket::SOCK_STREAM, 0)
|
|
39
|
-
socket.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
|
|
40
|
-
sockaddr = ::Socket.sockaddr_in(addrinfo[1], addrinfo[3])
|
|
41
|
-
begin
|
|
42
|
-
socket.connect_nonblock(sockaddr)
|
|
43
|
-
rescue Errno::EINPROGRESS
|
|
44
|
-
unless IO.select(nil, [ socket ], nil, @timeout)
|
|
45
|
-
next
|
|
46
|
-
end
|
|
47
|
-
begin
|
|
48
|
-
socket.connect_nonblock(sockaddr)
|
|
49
|
-
rescue Errno::EISCONN
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
return @handle = socket
|
|
53
|
-
rescue StandardError => e
|
|
54
|
-
next
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
raise TransportException.new(TransportException::NOT_OPEN, "Could not connect to #{@desc}: #{e}")
|
|
37
|
+
deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + @timeout unless @timeout.nil? || @timeout == 0
|
|
38
|
+
@handle = connect_socket(deadline)
|
|
58
39
|
end
|
|
59
40
|
|
|
60
41
|
def open?
|
|
61
|
-
!@handle.nil?
|
|
42
|
+
!@handle.nil? && !@handle.closed?
|
|
62
43
|
end
|
|
63
44
|
|
|
64
45
|
def write(str)
|
|
65
46
|
raise TransportException.new(TransportException::NOT_OPEN, "closed stream") unless open?
|
|
66
47
|
str = Bytes.force_binary_encoding(str)
|
|
67
48
|
begin
|
|
68
|
-
if @timeout.nil?
|
|
49
|
+
if @timeout.nil? || @timeout == 0
|
|
69
50
|
@handle.write(str)
|
|
70
51
|
else
|
|
71
52
|
deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + @timeout
|
|
@@ -87,7 +68,7 @@ module Thrift
|
|
|
87
68
|
# pass this on
|
|
88
69
|
raise e
|
|
89
70
|
rescue StandardError => e
|
|
90
|
-
@handle
|
|
71
|
+
close_socket(@handle)
|
|
91
72
|
@handle = nil
|
|
92
73
|
raise TransportException.new(TransportException::NOT_OPEN, e.message)
|
|
93
74
|
end
|
|
@@ -97,7 +78,7 @@ module Thrift
|
|
|
97
78
|
raise TransportException.new(TransportException::NOT_OPEN, "closed stream") unless open?
|
|
98
79
|
|
|
99
80
|
begin
|
|
100
|
-
if @timeout.nil?
|
|
81
|
+
if @timeout.nil? || @timeout == 0
|
|
101
82
|
data = @handle.readpartial(sz)
|
|
102
83
|
else
|
|
103
84
|
deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + @timeout
|
|
@@ -116,18 +97,18 @@ module Thrift
|
|
|
116
97
|
# don't let this get caught by the StandardError handler
|
|
117
98
|
raise e
|
|
118
99
|
rescue StandardError => e
|
|
119
|
-
@handle
|
|
100
|
+
close_socket(@handle)
|
|
120
101
|
@handle = nil
|
|
121
102
|
raise TransportException.new(TransportException::NOT_OPEN, e.message)
|
|
122
103
|
end
|
|
123
|
-
if (data.nil?
|
|
104
|
+
if (data.nil? || data.length == 0)
|
|
124
105
|
raise TransportException.new(TransportException::UNKNOWN, "Socket: Could not read #{sz} bytes from #{@desc}")
|
|
125
106
|
end
|
|
126
107
|
data
|
|
127
108
|
end
|
|
128
109
|
|
|
129
110
|
def close
|
|
130
|
-
@handle
|
|
111
|
+
close_socket(@handle)
|
|
131
112
|
@handle = nil
|
|
132
113
|
end
|
|
133
114
|
|
|
@@ -141,6 +122,64 @@ module Thrift
|
|
|
141
122
|
|
|
142
123
|
private
|
|
143
124
|
|
|
125
|
+
def connect_socket(deadline)
|
|
126
|
+
last_error = nil
|
|
127
|
+
connected_socket = nil
|
|
128
|
+
|
|
129
|
+
Addrinfo.foreach(@host, @port, nil, :STREAM) do |addrinfo|
|
|
130
|
+
socket = nil
|
|
131
|
+
|
|
132
|
+
begin
|
|
133
|
+
socket = if deadline
|
|
134
|
+
remaining = deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
135
|
+
raise TransportException.new(TransportException::TIMED_OUT, "Socket: Timed out opening connection to #{@desc}") if remaining <= 0
|
|
136
|
+
|
|
137
|
+
addrinfo.connect(timeout: remaining)
|
|
138
|
+
else
|
|
139
|
+
addrinfo.connect
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
socket.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
|
|
143
|
+
connected_socket = socket
|
|
144
|
+
break
|
|
145
|
+
rescue Errno::ETIMEDOUT => e
|
|
146
|
+
close_socket(socket)
|
|
147
|
+
last_error = e
|
|
148
|
+
rescue TransportException
|
|
149
|
+
close_socket(socket)
|
|
150
|
+
raise
|
|
151
|
+
rescue StandardError => e
|
|
152
|
+
close_socket(socket)
|
|
153
|
+
last_error = e
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
return connected_socket if connected_socket
|
|
158
|
+
|
|
159
|
+
if last_error.is_a?(Errno::ETIMEDOUT)
|
|
160
|
+
raise TransportException.new(TransportException::TIMED_OUT, "Socket: Timed out opening connection to #{@desc}")
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
if deadline && deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC) <= 0
|
|
164
|
+
raise TransportException.new(TransportException::TIMED_OUT, "Socket: Timed out opening connection to #{@desc}")
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
raise TransportException.new(TransportException::NOT_OPEN, "Could not connect to #{@desc}"), cause: last_error
|
|
168
|
+
rescue TransportException
|
|
169
|
+
raise
|
|
170
|
+
rescue StandardError
|
|
171
|
+
raise TransportException.new(TransportException::NOT_OPEN, "Could not connect to #{@desc}")
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def close_socket(socket)
|
|
175
|
+
return if socket.nil?
|
|
176
|
+
return if socket.respond_to?(:closed?) && socket.closed?
|
|
177
|
+
|
|
178
|
+
socket.close
|
|
179
|
+
rescue StandardError
|
|
180
|
+
nil
|
|
181
|
+
end
|
|
182
|
+
|
|
144
183
|
def wait_for(operation, deadline, sz)
|
|
145
184
|
rd_ary, wr_ary = case operation
|
|
146
185
|
when :read
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# encoding: ascii-8bit
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
#
|
|
3
4
|
# Licensed to the Apache Software Foundation (ASF) under one
|
|
4
5
|
# or more contributor license agreements. See the NOTICE file
|
|
@@ -22,8 +23,8 @@ require 'socket'
|
|
|
22
23
|
|
|
23
24
|
module Thrift
|
|
24
25
|
class SSLServerSocket < ServerSocket
|
|
25
|
-
def initialize(host_or_port, port = nil, ssl_context = nil)
|
|
26
|
-
super(host_or_port, port)
|
|
26
|
+
def initialize(host_or_port, port = nil, ssl_context = nil, client_timeout: DEFAULT_CLIENT_TIMEOUT)
|
|
27
|
+
super(host_or_port, port, client_timeout: client_timeout)
|
|
27
28
|
@ssl_context = ssl_context
|
|
28
29
|
end
|
|
29
30
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# encoding: ascii-8bit
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
#
|
|
3
4
|
# Licensed to the Apache Software Foundation (ASF) under one
|
|
4
5
|
# or more contributor license agreements. See the NOTICE file
|
|
@@ -17,6 +18,8 @@
|
|
|
17
18
|
# specific language governing permissions and limitations
|
|
18
19
|
# under the License.
|
|
19
20
|
|
|
21
|
+
require 'io/wait'
|
|
22
|
+
|
|
20
23
|
module Thrift
|
|
21
24
|
class SSLSocket < Socket
|
|
22
25
|
def initialize(host = 'localhost', port = 9090, timeout = nil, ssl_context = nil)
|
|
@@ -27,20 +30,49 @@ module Thrift
|
|
|
27
30
|
attr_accessor :ssl_context
|
|
28
31
|
|
|
29
32
|
def open
|
|
30
|
-
|
|
31
|
-
|
|
33
|
+
deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + @timeout unless @timeout.nil? || @timeout == 0
|
|
34
|
+
socket = connect_socket(deadline)
|
|
35
|
+
|
|
32
36
|
begin
|
|
33
|
-
@
|
|
37
|
+
ssl_socket = OpenSSL::SSL::SSLSocket.new(socket, @ssl_context)
|
|
38
|
+
ssl_socket.sync_close = true
|
|
39
|
+
@handle = ssl_socket
|
|
40
|
+
|
|
41
|
+
if deadline
|
|
42
|
+
loop do
|
|
43
|
+
result = @handle.connect_nonblock(exception: false)
|
|
44
|
+
|
|
45
|
+
case result
|
|
46
|
+
when @handle
|
|
47
|
+
break
|
|
48
|
+
when :wait_readable
|
|
49
|
+
remaining = deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
50
|
+
if remaining <= 0 || !@handle.to_io.wait_readable(remaining)
|
|
51
|
+
raise TransportException.new(TransportException::TIMED_OUT, "SSL socket: Timed out establishing session with #{@desc}")
|
|
52
|
+
end
|
|
53
|
+
when :wait_writable
|
|
54
|
+
remaining = deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
55
|
+
if remaining <= 0 || !@handle.to_io.wait_writable(remaining)
|
|
56
|
+
raise TransportException.new(TransportException::TIMED_OUT, "SSL socket: Timed out establishing session with #{@desc}")
|
|
57
|
+
end
|
|
58
|
+
else
|
|
59
|
+
raise TransportException.new(TransportException::NOT_OPEN, "Could not connect to #{@desc}: unexpected SSL connect result #{result.inspect}")
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
else
|
|
63
|
+
@handle.connect
|
|
64
|
+
end
|
|
65
|
+
|
|
34
66
|
@handle.post_connection_check(@host)
|
|
35
67
|
@handle
|
|
36
|
-
rescue
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
raise TransportException.new(TransportException::NOT_OPEN, "Could not connect to #{@desc}
|
|
68
|
+
rescue TransportException
|
|
69
|
+
close_socket(@handle)
|
|
70
|
+
@handle = nil
|
|
71
|
+
raise
|
|
72
|
+
rescue StandardError
|
|
73
|
+
close_socket(@handle || socket)
|
|
74
|
+
@handle = nil
|
|
75
|
+
raise TransportException.new(TransportException::NOT_OPEN, "Could not connect to #{@desc}")
|
|
44
76
|
end
|
|
45
77
|
end
|
|
46
78
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
# encoding: ascii-8bit
|
|
2
|
+
# frozen_string_literal: true
|
|
2
3
|
#
|
|
3
4
|
# Licensed to the Apache Software Foundation (ASF) under one
|
|
4
5
|
# or more contributor license agreements. See the NOTICE file
|
|
@@ -22,12 +23,14 @@ require 'socket'
|
|
|
22
23
|
|
|
23
24
|
module Thrift
|
|
24
25
|
class UNIXServerSocket < BaseServerTransport
|
|
25
|
-
def initialize(path)
|
|
26
|
+
def initialize(path, client_timeout: DEFAULT_CLIENT_TIMEOUT)
|
|
26
27
|
@path = path
|
|
28
|
+
@client_timeout = client_timeout
|
|
27
29
|
@handle = nil
|
|
28
30
|
end
|
|
29
31
|
|
|
30
32
|
attr_accessor :handle
|
|
33
|
+
attr_reader :client_timeout
|
|
31
34
|
|
|
32
35
|
def listen
|
|
33
36
|
@handle = ::UNIXServer.new(@path)
|
|
@@ -37,6 +40,7 @@ module Thrift
|
|
|
37
40
|
unless @handle.nil?
|
|
38
41
|
sock = @handle.accept
|
|
39
42
|
trans = UNIXSocket.new(nil)
|
|
43
|
+
trans.timeout = @client_timeout
|
|
40
44
|
trans.handle = sock
|
|
41
45
|
trans
|
|
42
46
|
end
|
data/lib/thrift/types.rb
CHANGED