thrift 0.23.0 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +203 -40
  3. data/ext/binary_protocol_accelerated.c +130 -40
  4. data/ext/bytes.c +14 -0
  5. data/ext/compact_protocol.c +184 -91
  6. data/ext/constants.h +3 -0
  7. data/ext/extconf.rb +12 -4
  8. data/ext/macros.h +11 -0
  9. data/ext/memory_buffer.c +67 -12
  10. data/ext/struct.c +192 -71
  11. data/ext/thrift_native.c +7 -0
  12. data/lib/thrift/bytes.rb +6 -3
  13. data/lib/thrift/client.rb +31 -33
  14. data/lib/thrift/exceptions.rb +11 -8
  15. data/lib/thrift/multiplexed_processor.rb +17 -8
  16. data/lib/thrift/processor.rb +25 -3
  17. data/lib/thrift/protocol/base_protocol.rb +66 -31
  18. data/lib/thrift/protocol/binary_protocol.rb +80 -37
  19. data/lib/thrift/protocol/binary_protocol_accelerated.rb +7 -2
  20. data/lib/thrift/protocol/compact_protocol.rb +204 -88
  21. data/lib/thrift/protocol/header_protocol.rb +15 -3
  22. data/lib/thrift/protocol/json_protocol.rb +158 -109
  23. data/lib/thrift/protocol/multiplexed_protocol.rb +3 -3
  24. data/lib/thrift/protocol/protocol_decorator.rb +6 -2
  25. data/lib/thrift/serializer/deserializer.rb +1 -0
  26. data/lib/thrift/serializer/serializer.rb +7 -0
  27. data/lib/thrift/server/base_server.rb +11 -2
  28. data/lib/thrift/server/nonblocking_server.rb +69 -20
  29. data/lib/thrift/server/rack_application.rb +69 -0
  30. data/lib/thrift/server/simple_server.rb +27 -15
  31. data/lib/thrift/server/thin_http_server.rb +21 -44
  32. data/lib/thrift/server/thread_pool_server.rb +21 -17
  33. data/lib/thrift/server/threaded_server.rb +22 -18
  34. data/lib/thrift/struct.rb +34 -18
  35. data/lib/thrift/struct_union.rb +37 -36
  36. data/lib/thrift/thrift_native.rb +2 -1
  37. data/lib/thrift/transport/base_server_transport.rb +3 -0
  38. data/lib/thrift/transport/base_transport.rb +26 -4
  39. data/lib/thrift/transport/buffered_transport.rb +5 -4
  40. data/lib/thrift/transport/framed_transport.rb +33 -7
  41. data/lib/thrift/transport/header_transport.rb +159 -33
  42. data/lib/thrift/transport/http_client_transport.rb +28 -14
  43. data/lib/thrift/transport/io_stream_transport.rb +3 -1
  44. data/lib/thrift/transport/memory_buffer_transport.rb +14 -9
  45. data/lib/thrift/transport/server_socket.rb +10 -8
  46. data/lib/thrift/transport/socket.rb +85 -48
  47. data/lib/thrift/transport/ssl_server_socket.rb +67 -6
  48. data/lib/thrift/transport/ssl_socket.rb +73 -14
  49. data/lib/thrift/transport/unix_server_socket.rb +6 -2
  50. data/lib/thrift/transport/unix_socket.rb +5 -6
  51. data/lib/thrift/types.rb +23 -22
  52. data/lib/thrift/union.rb +21 -11
  53. data/lib/thrift/uuid.rb +9 -8
  54. data/lib/thrift.rb +40 -39
  55. metadata +7 -266
  56. data/benchmark/Benchmark.thrift +0 -24
  57. data/benchmark/benchmark.rb +0 -285
  58. data/benchmark/client.rb +0 -117
  59. data/benchmark/server.rb +0 -120
  60. data/benchmark/thin_server.rb +0 -45
  61. data/lib/thrift/server/mongrel_http_server.rb +0 -60
  62. data/spec/BaseService.thrift +0 -27
  63. data/spec/ExtendedService.thrift +0 -25
  64. data/spec/Referenced.thrift +0 -44
  65. data/spec/ThriftNamespacedSpec.thrift +0 -53
  66. data/spec/ThriftSpec.thrift +0 -187
  67. data/spec/base_protocol_spec.rb +0 -224
  68. data/spec/base_transport_spec.rb +0 -387
  69. data/spec/binary_protocol_accelerated_spec.rb +0 -46
  70. data/spec/binary_protocol_spec.rb +0 -72
  71. data/spec/binary_protocol_spec_shared.rb +0 -445
  72. data/spec/bytes_spec.rb +0 -99
  73. data/spec/client_spec.rb +0 -166
  74. data/spec/compact_protocol_spec.rb +0 -196
  75. data/spec/constants_demo_spec.rb +0 -101
  76. data/spec/exception_spec.rb +0 -140
  77. data/spec/flat_spec.rb +0 -62
  78. data/spec/header_protocol_spec.rb +0 -475
  79. data/spec/header_transport_spec.rb +0 -386
  80. data/spec/http_client_spec.rb +0 -159
  81. data/spec/json_protocol_spec.rb +0 -552
  82. data/spec/namespaced_spec.rb +0 -66
  83. data/spec/nonblocking_server_spec.rb +0 -361
  84. data/spec/processor_spec.rb +0 -79
  85. data/spec/serializer_spec.rb +0 -66
  86. data/spec/server_socket_spec.rb +0 -84
  87. data/spec/server_spec.rb +0 -186
  88. data/spec/socket_spec.rb +0 -67
  89. data/spec/socket_spec_shared.rb +0 -167
  90. data/spec/spec_helper.rb +0 -69
  91. data/spec/ssl_server_socket_spec.rb +0 -45
  92. data/spec/ssl_socket_spec.rb +0 -87
  93. data/spec/struct_nested_containers_spec.rb +0 -190
  94. data/spec/struct_spec.rb +0 -397
  95. data/spec/support/header_protocol_helper.rb +0 -54
  96. data/spec/thin_http_server_spec.rb +0 -126
  97. data/spec/types_spec.rb +0 -117
  98. data/spec/union_spec.rb +0 -272
  99. data/spec/unix_socket_spec.rb +0 -115
  100. data/spec/uuid_validation_spec.rb +0 -238
  101. data/test/fuzz/Makefile.am +0 -173
  102. data/test/fuzz/README.md +0 -149
  103. data/test/fuzz/fuzz_common.rb +0 -95
  104. data/test/fuzz/fuzz_parse_binary_protocol.rb +0 -22
  105. data/test/fuzz/fuzz_parse_binary_protocol_accelerated.rb +0 -22
  106. data/test/fuzz/fuzz_parse_binary_protocol_accelerated_harness.rb +0 -22
  107. data/test/fuzz/fuzz_parse_binary_protocol_harness.rb +0 -22
  108. data/test/fuzz/fuzz_parse_compact_protocol.rb +0 -22
  109. data/test/fuzz/fuzz_parse_compact_protocol_harness.rb +0 -22
  110. data/test/fuzz/fuzz_parse_json_protocol.rb +0 -22
  111. data/test/fuzz/fuzz_parse_json_protocol_harness.rb +0 -22
  112. data/test/fuzz/fuzz_roundtrip_binary_protocol.rb +0 -22
  113. data/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated.rb +0 -22
  114. data/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated_harness.rb +0 -22
  115. data/test/fuzz/fuzz_roundtrip_binary_protocol_harness.rb +0 -22
  116. data/test/fuzz/fuzz_roundtrip_compact_protocol.rb +0 -22
  117. data/test/fuzz/fuzz_roundtrip_compact_protocol_harness.rb +0 -22
  118. data/test/fuzz/fuzz_roundtrip_json_protocol.rb +0 -22
  119. data/test/fuzz/fuzz_roundtrip_json_protocol_harness.rb +0 -22
  120. data/test/fuzz/fuzz_tracer.rb +0 -28
@@ -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
@@ -18,11 +19,11 @@
18
19
  # under the License.
19
20
  #
20
21
 
21
- require 'socket'
22
+ require "socket"
22
23
 
23
24
  module Thrift
24
25
  class Socket < BaseTransport
25
- def initialize(host = 'localhost', port = 9090, timeout = nil)
26
+ def initialize(host = "localhost", port = 9090, timeout = nil)
26
27
  @host = host
27
28
  @port = port
28
29
  @timeout = timeout
@@ -33,39 +34,21 @@ module Thrift
33
34
  attr_accessor :handle, :timeout
34
35
 
35
36
  def open
36
- for addrinfo in ::Socket::getaddrinfo(@host, @port, nil, ::Socket::SOCK_STREAM) do
37
- begin
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
+ raise TransportException.new(TransportException::ALREADY_OPEN, "Socket already open") if open?
38
+
39
+ deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + @timeout unless @timeout.nil? || @timeout == 0
40
+ @handle = connect_socket(deadline)
58
41
  end
59
42
 
60
43
  def open?
61
- !@handle.nil? and !@handle.closed?
44
+ !@handle.nil? && !@handle.closed?
62
45
  end
63
46
 
64
47
  def write(str)
65
48
  raise TransportException.new(TransportException::NOT_OPEN, "closed stream") unless open?
66
49
  str = Bytes.force_binary_encoding(str)
67
50
  begin
68
- if @timeout.nil? or @timeout == 0
51
+ if @timeout.nil? || @timeout == 0
69
52
  @handle.write(str)
70
53
  else
71
54
  deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + @timeout
@@ -73,7 +56,7 @@ module Thrift
73
56
 
74
57
  while len < str.length
75
58
  begin
76
- len += @handle.write_nonblock(str[len..-1])
59
+ len += @handle.write_nonblock(str[len..])
77
60
  rescue IO::WaitWritable
78
61
  wait_for(:write, deadline, str.length)
79
62
  rescue IO::WaitReadable
@@ -83,12 +66,11 @@ module Thrift
83
66
 
84
67
  len
85
68
  end
86
- rescue TransportException => e
87
- # pass this on
88
- raise e
69
+ rescue TransportException
70
+ close
71
+ raise
89
72
  rescue StandardError => e
90
- @handle.close
91
- @handle = nil
73
+ close
92
74
  raise TransportException.new(TransportException::NOT_OPEN, e.message)
93
75
  end
94
76
  end
@@ -97,42 +79,39 @@ module Thrift
97
79
  raise TransportException.new(TransportException::NOT_OPEN, "closed stream") unless open?
98
80
 
99
81
  begin
100
- if @timeout.nil? or @timeout == 0
82
+ if @timeout.nil? || @timeout == 0
101
83
  data = @handle.readpartial(sz)
102
84
  else
103
85
  deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + @timeout
104
86
 
105
87
  data = loop do
106
- begin
107
- break @handle.read_nonblock(sz)
108
- rescue IO::WaitReadable
109
- wait_for(:read, deadline, sz)
110
- rescue IO::WaitWritable
111
- wait_for(:write, deadline, sz)
112
- end
88
+ break @handle.read_nonblock(sz)
89
+ rescue IO::WaitReadable
90
+ wait_for(:read, deadline, sz)
91
+ rescue IO::WaitWritable
92
+ wait_for(:write, deadline, sz)
113
93
  end
114
94
  end
115
- rescue TransportException => e
116
- # don't let this get caught by the StandardError handler
117
- raise e
95
+ rescue TransportException
96
+ close
97
+ raise
118
98
  rescue StandardError => e
119
- @handle.close unless @handle.closed?
120
- @handle = nil
99
+ close
121
100
  raise TransportException.new(TransportException::NOT_OPEN, e.message)
122
101
  end
123
- if (data.nil? or data.length == 0)
102
+ if data.nil? || data.length == 0
124
103
  raise TransportException.new(TransportException::UNKNOWN, "Socket: Could not read #{sz} bytes from #{@desc}")
125
104
  end
126
105
  data
127
106
  end
128
107
 
129
108
  def close
130
- @handle.close unless @handle.nil? or @handle.closed?
109
+ close_socket(@handle)
131
110
  @handle = nil
132
111
  end
133
112
 
134
113
  def to_io
135
- @handle&.to_io || raise(IOError, 'closed stream')
114
+ @handle&.to_io || raise(IOError, "closed stream")
136
115
  end
137
116
 
138
117
  def to_s
@@ -141,6 +120,64 @@ module Thrift
141
120
 
142
121
  private
143
122
 
123
+ def connect_socket(deadline)
124
+ last_error = nil
125
+ connected_socket = nil
126
+
127
+ Addrinfo.foreach(@host, @port, nil, :STREAM) do |addrinfo|
128
+ socket = nil
129
+
130
+ begin
131
+ socket = if deadline
132
+ remaining = deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC)
133
+ raise TransportException.new(TransportException::TIMED_OUT, "Socket: Timed out opening connection to #{@desc}") if remaining <= 0
134
+
135
+ addrinfo.connect(timeout: remaining)
136
+ else
137
+ addrinfo.connect
138
+ end
139
+
140
+ socket.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
141
+ connected_socket = socket
142
+ break
143
+ rescue Errno::ETIMEDOUT => e
144
+ close_socket(socket)
145
+ last_error = e
146
+ rescue TransportException
147
+ close_socket(socket)
148
+ raise
149
+ rescue StandardError => e
150
+ close_socket(socket)
151
+ last_error = e
152
+ end
153
+ end
154
+
155
+ return connected_socket if connected_socket
156
+
157
+ if last_error.is_a?(Errno::ETIMEDOUT)
158
+ raise TransportException.new(TransportException::TIMED_OUT, "Socket: Timed out opening connection to #{@desc}")
159
+ end
160
+
161
+ if deadline && deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC) <= 0
162
+ raise TransportException.new(TransportException::TIMED_OUT, "Socket: Timed out opening connection to #{@desc}")
163
+ end
164
+
165
+ raise TransportException.new(TransportException::NOT_OPEN, "Could not connect to #{@desc}"), cause: last_error
166
+ rescue TransportException
167
+ raise
168
+ rescue StandardError
169
+ raise TransportException.new(TransportException::NOT_OPEN, "Could not connect to #{@desc}")
170
+ end
171
+
172
+ def close_socket(socket)
173
+ return if socket.nil?
174
+ return if socket.respond_to?(:closed?) && socket.closed?
175
+
176
+ socket.close
177
+ rescue StandardError
178
+ nil
179
+ end
180
+
144
181
  def wait_for(operation, deadline, sz)
145
182
  rd_ary, wr_ary = case operation
146
183
  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
@@ -18,24 +19,84 @@
18
19
  # under the License.
19
20
  #
20
21
 
21
- require 'socket'
22
+ require "io/wait"
23
+ require "socket"
22
24
 
23
25
  module Thrift
24
26
  class SSLServerSocket < ServerSocket
25
- def initialize(host_or_port, port = nil, ssl_context = nil)
26
- super(host_or_port, port)
27
+ def initialize(host_or_port, port = nil, ssl_context = nil, client_timeout: DEFAULT_CLIENT_TIMEOUT)
28
+ super(host_or_port, port, client_timeout: client_timeout)
27
29
  @ssl_context = ssl_context
28
30
  end
29
31
 
30
32
  attr_accessor :ssl_context
31
33
 
32
34
  def listen
33
- socket = TCPServer.new(@host, @port)
34
- @handle = OpenSSL::SSL::SSLServer.new(socket, @ssl_context)
35
+ tcp_server = TCPServer.new(@host, @port)
36
+ @handle = OpenSSL::SSL::SSLServer.new(tcp_server, @ssl_context).tap do |server|
37
+ server.start_immediately = false
38
+ end
39
+ end
40
+
41
+ def accept
42
+ return if @handle.nil?
43
+
44
+ transport = nil
45
+ ssl_socket = @handle.accept
46
+ deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + @client_timeout unless @client_timeout.nil? || @client_timeout == 0
47
+ ssl_socket.to_io.setsockopt(::Socket::IPPROTO_TCP, ::Socket::TCP_NODELAY, 1)
48
+ accept_ssl_socket(ssl_socket, deadline)
49
+
50
+ transport = Socket.new.tap do |accepted_transport|
51
+ accepted_transport.timeout = @client_timeout
52
+ accepted_transport.handle = ssl_socket
53
+ end
54
+ ensure
55
+ # Thread#kill leaves $! nil, but marks the thread as aborting.
56
+ close_accepted_socket(ssl_socket) if transport.nil? || $! || Thread.current.status == "aborting"
35
57
  end
36
58
 
37
59
  def to_s
38
- "ssl(#{super.to_s})"
60
+ "ssl(#{super})"
61
+ end
62
+
63
+ private
64
+
65
+ def accept_ssl_socket(ssl_socket, deadline)
66
+ return ssl_socket.accept unless deadline
67
+
68
+ loop do
69
+ case ssl_socket.accept_nonblock(exception: false)
70
+ when ssl_socket
71
+ return ssl_socket
72
+ when :wait_readable
73
+ wait_for_handshake(ssl_socket, :read, deadline)
74
+ when :wait_writable
75
+ wait_for_handshake(ssl_socket, :write, deadline)
76
+ else
77
+ raise TransportException.new(TransportException::NOT_OPEN, "SSL server socket: Unexpected TLS handshake result")
78
+ end
79
+ end
80
+ end
81
+
82
+ def wait_for_handshake(ssl_socket, direction, deadline)
83
+ remaining = deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC)
84
+ ready = if remaining > 0
85
+ if direction == :read
86
+ ssl_socket.to_io.wait_readable(remaining)
87
+ else
88
+ ssl_socket.to_io.wait_writable(remaining)
89
+ end
90
+ end
91
+ return if ready
92
+
93
+ raise OpenSSL::SSL::SSLError, "SSL server socket: Timed out accepting TLS connection"
94
+ end
95
+
96
+ def close_accepted_socket(ssl_socket)
97
+ ssl_socket&.close
98
+ rescue StandardError
99
+ nil
39
100
  end
40
101
  end
41
102
  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
@@ -17,35 +18,93 @@
17
18
  # specific language governing permissions and limitations
18
19
  # under the License.
19
20
 
21
+ require "io/wait"
22
+ require "ipaddr"
23
+
20
24
  module Thrift
21
25
  class SSLSocket < Socket
22
- def initialize(host = 'localhost', port = 9090, timeout = nil, ssl_context = nil)
26
+ def initialize(host = "localhost", port = 9090, timeout = nil, ssl_context = nil, server_hostname: host)
23
27
  super(host, port, timeout)
24
28
  @ssl_context = ssl_context
29
+ @server_hostname = server_hostname
25
30
  end
26
31
 
27
32
  attr_accessor :ssl_context
33
+ attr_accessor :server_hostname
28
34
 
29
35
  def open
30
- socket = super
31
- @handle = OpenSSL::SSL::SSLSocket.new(socket, @ssl_context)
36
+ deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + @timeout unless @timeout.nil? || @timeout == 0
37
+ socket = connect_socket(deadline)
38
+
32
39
  begin
33
- @handle.connect_nonblock
34
- @handle.post_connection_check(@host)
40
+ ssl_context = configured_ssl_context
41
+ ssl_socket = OpenSSL::SSL::SSLSocket.new(socket, ssl_context)
42
+ ssl_socket.hostname = @server_hostname if send_server_hostname?
43
+ ssl_socket.sync_close = true
44
+ @handle = ssl_socket
45
+
46
+ if deadline
47
+ loop do
48
+ result = @handle.connect_nonblock(exception: false)
49
+
50
+ case result
51
+ when @handle
52
+ break
53
+ when :wait_readable
54
+ remaining = deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC)
55
+ if remaining <= 0 || !@handle.to_io.wait_readable(remaining)
56
+ raise TransportException.new(TransportException::TIMED_OUT, "SSL socket: Timed out establishing session with #{@desc}")
57
+ end
58
+ when :wait_writable
59
+ remaining = deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC)
60
+ if remaining <= 0 || !@handle.to_io.wait_writable(remaining)
61
+ raise TransportException.new(TransportException::TIMED_OUT, "SSL socket: Timed out establishing session with #{@desc}")
62
+ end
63
+ else
64
+ raise TransportException.new(TransportException::NOT_OPEN, "Could not connect to #{@desc}: unexpected SSL connect result #{result.inspect}")
65
+ end
66
+ end
67
+ else
68
+ @handle.connect
69
+ end
70
+
71
+ @handle.post_connection_check(server_hostname_for_verification)
35
72
  @handle
36
- rescue IO::WaitReadable
37
- IO.select([ @handle ], nil, nil, @timeout)
38
- retry
39
- rescue IO::WaitWritable
40
- IO.select(nil, [ @handle ], nil, @timeout)
41
- retry
42
- rescue StandardError => e
43
- raise TransportException.new(TransportException::NOT_OPEN, "Could not connect to #{@desc}: #{e}")
73
+ rescue TransportException
74
+ close_socket(@handle)
75
+ @handle = nil
76
+ raise
77
+ rescue StandardError
78
+ close_socket(@handle || socket)
79
+ @handle = nil
80
+ raise TransportException.new(TransportException::NOT_OPEN, "Could not connect to #{@desc}")
44
81
  end
45
82
  end
46
83
 
47
84
  def to_s
48
- "ssl(#{super.to_s})"
85
+ "ssl(#{super})"
86
+ end
87
+
88
+ private
89
+
90
+ def configured_ssl_context
91
+ @ssl_context ||= OpenSSL::SSL::SSLContext.new.tap do |context|
92
+ context.verify_mode = OpenSSL::SSL::VERIFY_PEER
93
+ context.cert_store = OpenSSL::X509::Store.new.tap(&:set_default_paths)
94
+ end
95
+ end
96
+
97
+ def send_server_hostname?
98
+ return false if @server_hostname.nil? || @server_hostname.empty?
99
+
100
+ IPAddr.new(@server_hostname)
101
+ false
102
+ rescue IPAddr::InvalidAddressError
103
+ true
104
+ end
105
+
106
+ def server_hostname_for_verification
107
+ @server_hostname.nil? || @server_hostname.empty? ? @host : @server_hostname
49
108
  end
50
109
  end
51
110
  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
@@ -18,16 +19,18 @@
18
19
  # under the License.
19
20
  #
20
21
 
21
- require 'socket'
22
+ 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
@@ -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
@@ -18,7 +19,7 @@
18
19
  # under the License.
19
20
  #
20
21
 
21
- require 'socket'
22
+ require "socket"
22
23
 
23
24
  module Thrift
24
25
  class UNIXSocket < Socket
@@ -30,11 +31,9 @@ module Thrift
30
31
  end
31
32
 
32
33
  def open
33
- begin
34
- @handle = ::UNIXSocket.new(@path)
35
- rescue StandardError
36
- raise TransportException.new(TransportException::NOT_OPEN, "Could not open UNIX socket at #{@path}")
37
- end
34
+ @handle = ::UNIXSocket.new(@path)
35
+ rescue StandardError
36
+ raise TransportException.new(TransportException::NOT_OPEN, "Could not open UNIX socket at #{@path}")
38
37
  end
39
38
 
40
39
  def to_s
data/lib/thrift/types.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
@@ -17,7 +18,7 @@
17
18
  # under the License.
18
19
  #
19
20
 
20
- require 'set'
21
+ require "set"
21
22
 
22
23
  module Thrift
23
24
  module Types
@@ -47,27 +48,27 @@ module Thrift
47
48
  def self.check_type(value, field, name, skip_nil = true)
48
49
  return if value.nil? and skip_nil
49
50
  klasses = case field[:type]
50
- when Types::VOID
51
- NilClass
52
- when Types::BOOL
53
- [TrueClass, FalseClass]
54
- when Types::BYTE, Types::I16, Types::I32, Types::I64
55
- Integer
56
- when Types::DOUBLE
57
- Float
58
- when Types::STRING
59
- String
60
- when Types::UUID
61
- String
62
- when Types::STRUCT
63
- [Struct, Union]
64
- when Types::MAP
65
- Hash
66
- when Types::SET
67
- Set
68
- when Types::LIST
69
- Array
70
- end
51
+ when Types::VOID
52
+ NilClass
53
+ when Types::BOOL
54
+ [TrueClass, FalseClass]
55
+ when Types::BYTE, Types::I16, Types::I32, Types::I64
56
+ Integer
57
+ when Types::DOUBLE
58
+ Float
59
+ when Types::STRING
60
+ String
61
+ when Types::UUID
62
+ String
63
+ when Types::STRUCT
64
+ [Struct, Union]
65
+ when Types::MAP
66
+ Hash
67
+ when Types::SET
68
+ Set
69
+ when Types::LIST
70
+ Array
71
+ end
71
72
  valid = klasses && [*klasses].any? { |klass| klass === value }
72
73
  raise TypeError, "Expected #{type_name(field[:type])}, received #{value.class} for field #{name}" unless valid
73
74
  # check elements now
data/lib/thrift/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
@@ -17,6 +18,8 @@
17
18
  # under the License.
18
19
  #
19
20
 
21
+ require "thrift/struct_union"
22
+
20
23
  module Thrift
21
24
  class Union
22
25
  def initialize(name = nil, value = nil)
@@ -53,13 +56,17 @@ module Thrift
53
56
  end
54
57
  end
55
58
 
56
- def read(iprot)
59
+ def read(iprot, remaining_depth = DEFAULT_RECURSION_DEPTH)
60
+ raise ProtocolException.new(ProtocolException::DEPTH_LIMIT, "Maximum recursion depth exceeded") if remaining_depth <= 0
61
+ @setfield = nil
62
+ @value = nil
63
+
57
64
  iprot.read_struct_begin
58
- fname, ftype, fid = iprot.read_field_begin
59
- handle_message(iprot, fid, ftype)
65
+ _, ftype, fid = iprot.read_field_begin
66
+ handle_message(iprot, fid, ftype, remaining_depth)
60
67
  iprot.read_field_end
61
68
 
62
- fname, ftype, fid = iprot.read_field_begin
69
+ _, ftype, _ = iprot.read_field_begin
63
70
  unless (ftype == Types::STOP)
64
71
  raise ProtocolException.new(ProtocolException::INVALID_DATA, "Too many fields for union")
65
72
  end
@@ -68,11 +75,12 @@ module Thrift
68
75
  validate
69
76
  end
70
77
 
71
- def write(oprot)
78
+ def write(oprot, remaining_depth = DEFAULT_RECURSION_DEPTH)
79
+ raise ProtocolException.new(ProtocolException::DEPTH_LIMIT, "Maximum recursion depth exceeded") if remaining_depth <= 0
72
80
  validate
73
81
  oprot.write_struct_begin(self.class.name)
74
82
 
75
- fid = self.name_to_id(@setfield.to_s)
83
+ fid = name_to_id(@setfield.to_s)
76
84
 
77
85
  field_info = struct_fields[fid]
78
86
  unless field_info
@@ -82,8 +90,10 @@ module Thrift
82
90
  type = field_info[:type]
83
91
  if is_container? type
84
92
  oprot.write_field_begin(@setfield, type, fid)
85
- write_container(oprot, @value, field_info)
93
+ write_container(oprot, @value, field_info, remaining_depth)
86
94
  oprot.write_field_end
95
+ elsif type == Types::STRUCT
96
+ oprot.write_field(field_info, fid, @value, remaining_depth)
87
97
  else
88
98
  oprot.write_field(@setfield, type, fid, @value)
89
99
  end
@@ -93,7 +103,7 @@ module Thrift
93
103
  end
94
104
 
95
105
  def ==(other)
96
- other.equal?(self) || other.instance_of?(self.class) && @setfield == other.get_set_field && @value == other.get_value
106
+ other.equal?(self) || (other.instance_of?(self.class) && @setfield == other.get_set_field && @value == other.get_value)
97
107
  end
98
108
  alias_method :eql?, :==
99
109
 
@@ -106,7 +116,7 @@ module Thrift
106
116
  if field_info[:name].to_sym == @setfield
107
117
  @value
108
118
  else
109
- raise RuntimeError, "#{field_info[:name]} is not union's set field."
119
+ raise "#{field_info[:name]} is not union's set field."
110
120
  end
111
121
  end
112
122
 
@@ -168,10 +178,10 @@ module Thrift
168
178
 
169
179
  protected
170
180
 
171
- def handle_message(iprot, fid, ftype)
181
+ def handle_message(iprot, fid, ftype, remaining_depth)
172
182
  field = struct_fields[fid]
173
183
  if field and field[:type] == ftype
174
- @value = read_field(iprot, field)
184
+ @value = read_field(iprot, field, remaining_depth)
175
185
  name = field[:name].to_sym
176
186
  @setfield = name
177
187
  else
data/lib/thrift/uuid.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
@@ -17,32 +18,32 @@
17
18
  # under the License.
18
19
  #
19
20
 
20
- require 'thrift/protocol/base_protocol'
21
+ require "thrift/protocol/base_protocol"
21
22
 
22
23
  module Thrift
23
24
  module UUID
24
- UUID_REGEX = /\A[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\z/.freeze
25
+ UUID_REGEX = /\A[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\z/
25
26
 
26
27
  def self.validate_uuid!(uuid)
27
28
  unless uuid.is_a?(String)
28
- raise ProtocolException.new(ProtocolException::INVALID_DATA, 'UUID must be a string')
29
+ raise ProtocolException.new(ProtocolException::INVALID_DATA, "UUID must be a string")
29
30
  end
30
31
 
31
- unless uuid =~ UUID_REGEX
32
- raise ProtocolException.new(ProtocolException::INVALID_DATA, 'Invalid UUID format')
32
+ unless UUID_REGEX.match?(uuid)
33
+ raise ProtocolException.new(ProtocolException::INVALID_DATA, "Invalid UUID format")
33
34
  end
34
35
  end
35
36
 
36
37
  def self.uuid_bytes(uuid)
37
- [uuid.delete('-')].pack('H*')
38
+ [uuid.delete("-")].pack("H*")
38
39
  end
39
40
 
40
41
  def self.uuid_from_bytes(bytes)
41
42
  unless bytes.bytesize == 16
42
- raise ProtocolException.new(ProtocolException::INVALID_DATA, 'Invalid UUID data length')
43
+ raise ProtocolException.new(ProtocolException::INVALID_DATA, "Invalid UUID data length")
43
44
  end
44
45
 
45
- hex = bytes.unpack('H*').first
46
+ hex = bytes.unpack1("H*")
46
47
  "#{hex[0, 8]}-#{hex[8, 4]}-#{hex[12, 4]}-#{hex[16, 4]}-#{hex[20, 12]}"
47
48
  end
48
49
  end