thrift 0.24.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 +107 -4
  3. data/ext/binary_protocol_accelerated.c +52 -17
  4. data/ext/compact_protocol.c +135 -70
  5. data/ext/constants.h +3 -0
  6. data/ext/extconf.rb +4 -4
  7. data/ext/macros.h +11 -0
  8. data/ext/memory_buffer.c +35 -17
  9. data/ext/struct.c +185 -64
  10. data/ext/thrift_native.c +6 -0
  11. data/lib/thrift/bytes.rb +2 -2
  12. data/lib/thrift/client.rb +31 -22
  13. data/lib/thrift/exceptions.rb +10 -8
  14. data/lib/thrift/multiplexed_processor.rb +5 -6
  15. data/lib/thrift/processor.rb +24 -3
  16. data/lib/thrift/protocol/base_protocol.rb +53 -27
  17. data/lib/thrift/protocol/binary_protocol.rb +54 -40
  18. data/lib/thrift/protocol/binary_protocol_accelerated.rb +6 -2
  19. data/lib/thrift/protocol/compact_protocol.rb +193 -88
  20. data/lib/thrift/protocol/header_protocol.rb +14 -3
  21. data/lib/thrift/protocol/json_protocol.rb +146 -104
  22. data/lib/thrift/protocol/multiplexed_protocol.rb +2 -3
  23. data/lib/thrift/protocol/protocol_decorator.rb +5 -2
  24. data/lib/thrift/serializer/serializer.rb +6 -0
  25. data/lib/thrift/server/base_server.rb +10 -2
  26. data/lib/thrift/server/nonblocking_server.rb +49 -26
  27. data/lib/thrift/server/rack_application.rb +69 -0
  28. data/lib/thrift/server/simple_server.rb +27 -23
  29. data/lib/thrift/server/thin_http_server.rb +20 -44
  30. data/lib/thrift/server/thread_pool_server.rb +20 -17
  31. data/lib/thrift/server/threaded_server.rb +22 -26
  32. data/lib/thrift/struct.rb +33 -18
  33. data/lib/thrift/struct_union.rb +36 -30
  34. data/lib/thrift/transport/base_transport.rb +23 -5
  35. data/lib/thrift/transport/buffered_transport.rb +4 -4
  36. data/lib/thrift/transport/framed_transport.rb +32 -7
  37. data/lib/thrift/transport/header_transport.rb +130 -50
  38. data/lib/thrift/transport/http_client_transport.rb +27 -14
  39. data/lib/thrift/transport/io_stream_transport.rb +2 -1
  40. data/lib/thrift/transport/memory_buffer_transport.rb +8 -10
  41. data/lib/thrift/transport/server_socket.rb +4 -5
  42. data/lib/thrift/transport/socket.rb +20 -22
  43. data/lib/thrift/transport/ssl_server_socket.rb +64 -4
  44. data/lib/thrift/transport/ssl_socket.rb +32 -5
  45. data/lib/thrift/transport/unix_server_socket.rb +1 -1
  46. data/lib/thrift/transport/unix_socket.rb +4 -6
  47. data/lib/thrift/types.rb +22 -22
  48. data/lib/thrift/union.rb +20 -11
  49. data/lib/thrift/uuid.rb +8 -8
  50. data/lib/thrift.rb +39 -39
  51. metadata +5 -230
  52. data/benchmark/Benchmark.thrift +0 -24
  53. data/benchmark/benchmark.rb +0 -286
  54. data/benchmark/client.rb +0 -118
  55. data/benchmark/server.rb +0 -121
  56. data/benchmark/thin_server.rb +0 -46
  57. data/lib/thrift/server/mongrel_http_server.rb +0 -61
  58. data/spec/BaseService.thrift +0 -27
  59. data/spec/ExtendedService.thrift +0 -25
  60. data/spec/Referenced.thrift +0 -44
  61. data/spec/ThriftNamespacedSpec.thrift +0 -53
  62. data/spec/ThriftSpec.thrift +0 -203
  63. data/spec/base_protocol_spec.rb +0 -244
  64. data/spec/base_transport_spec.rb +0 -428
  65. data/spec/binary_protocol_accelerated_spec.rb +0 -47
  66. data/spec/binary_protocol_spec.rb +0 -73
  67. data/spec/binary_protocol_spec_shared.rb +0 -509
  68. data/spec/bytes_spec.rb +0 -117
  69. data/spec/client_spec.rb +0 -167
  70. data/spec/compact_protocol_spec.rb +0 -295
  71. data/spec/constants_demo_spec.rb +0 -102
  72. data/spec/exception_spec.rb +0 -141
  73. data/spec/flat_spec.rb +0 -63
  74. data/spec/header_protocol_spec.rb +0 -476
  75. data/spec/header_transport_spec.rb +0 -431
  76. data/spec/http_client_spec.rb +0 -160
  77. data/spec/json_protocol_spec.rb +0 -574
  78. data/spec/multiplexed_processor_spec.rb +0 -75
  79. data/spec/namespaced_spec.rb +0 -67
  80. data/spec/nonblocking_server_spec.rb +0 -429
  81. data/spec/processor_spec.rb +0 -80
  82. data/spec/recursion_depth_spec.rb +0 -223
  83. data/spec/serializer_spec.rb +0 -67
  84. data/spec/server_socket_spec.rb +0 -121
  85. data/spec/server_spec.rb +0 -238
  86. data/spec/socket_spec.rb +0 -174
  87. data/spec/socket_spec_shared.rb +0 -168
  88. data/spec/spec_helper.rb +0 -70
  89. data/spec/ssl_server_socket_spec.rb +0 -85
  90. data/spec/ssl_socket_spec.rb +0 -248
  91. data/spec/struct_nested_containers_spec.rb +0 -191
  92. data/spec/struct_spec.rb +0 -398
  93. data/spec/support/header_protocol_helper.rb +0 -55
  94. data/spec/thin_http_server_spec.rb +0 -127
  95. data/spec/types_spec.rb +0 -118
  96. data/spec/union_spec.rb +0 -273
  97. data/spec/unix_socket_spec.rb +0 -131
  98. data/spec/uuid_validation_spec.rb +0 -239
  99. data/test/fuzz/Makefile +0 -779
  100. data/test/fuzz/Makefile.am +0 -173
  101. data/test/fuzz/Makefile.in +0 -775
  102. data/test/fuzz/README.md +0 -149
  103. data/test/fuzz/fuzz_common.rb +0 -96
  104. data/test/fuzz/fuzz_parse_binary_protocol.rb +0 -23
  105. data/test/fuzz/fuzz_parse_binary_protocol_accelerated.rb +0 -23
  106. data/test/fuzz/fuzz_parse_binary_protocol_accelerated_harness.rb +0 -23
  107. data/test/fuzz/fuzz_parse_binary_protocol_harness.rb +0 -23
  108. data/test/fuzz/fuzz_parse_compact_protocol.rb +0 -23
  109. data/test/fuzz/fuzz_parse_compact_protocol_harness.rb +0 -23
  110. data/test/fuzz/fuzz_parse_json_protocol.rb +0 -23
  111. data/test/fuzz/fuzz_parse_json_protocol_harness.rb +0 -23
  112. data/test/fuzz/fuzz_roundtrip_binary_protocol.rb +0 -23
  113. data/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated.rb +0 -23
  114. data/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated_harness.rb +0 -23
  115. data/test/fuzz/fuzz_roundtrip_binary_protocol_harness.rb +0 -23
  116. data/test/fuzz/fuzz_roundtrip_compact_protocol.rb +0 -23
  117. data/test/fuzz/fuzz_roundtrip_compact_protocol_harness.rb +0 -23
  118. data/test/fuzz/fuzz_roundtrip_json_protocol.rb +0 -23
  119. data/test/fuzz/fuzz_roundtrip_json_protocol_harness.rb +0 -23
  120. data/test/fuzz/fuzz_tracer.rb +0 -29
@@ -17,7 +17,9 @@
17
17
  # specific language governing permissions and limitations
18
18
  # under the License.
19
19
  #
20
- require 'set'
20
+ require "set"
21
+ require "thrift/exceptions"
22
+ require "thrift/types"
21
23
 
22
24
  module Thrift
23
25
  module Struct_Union
@@ -49,16 +51,17 @@ module Thrift
49
51
  end
50
52
  end
51
53
 
52
- def read_field(iprot, field = {})
54
+ def read_field(iprot, field = {}, remaining_depth = DEFAULT_RECURSION_DEPTH)
53
55
  case field[:type]
54
56
  when Types::STRUCT
57
+ remaining_depth -= 1
55
58
  value = field[:class].new
56
- value.read(iprot)
59
+ value.read(iprot, remaining_depth)
57
60
  when Types::MAP
58
61
  key_type, val_type, size = iprot.read_map_begin
59
- raise ProtocolException.new(ProtocolException::NEGATIVE_SIZE, 'Negative size') unless size >= 0
62
+ iprot.validate_container_size(size)
60
63
  # Skip the map contents if the declared key or value types don't match the expected ones.
61
- if (size != 0 && (key_type != field[:key][:type] || val_type != field[:value][:type]))
64
+ if size != 0 && (key_type != field[:key][:type] || val_type != field[:value][:type])
62
65
  size.times do
63
66
  iprot.skip(key_type)
64
67
  iprot.skip(val_type)
@@ -67,15 +70,15 @@ module Thrift
67
70
  else
68
71
  value = {}
69
72
  size.times do
70
- k = read_field(iprot, field_info(field[:key]))
71
- v = read_field(iprot, field_info(field[:value]))
73
+ k = read_field(iprot, field_info(field[:key]), remaining_depth)
74
+ v = read_field(iprot, field_info(field[:value]), remaining_depth)
72
75
  value[k] = v
73
76
  end
74
77
  end
75
78
  iprot.read_map_end
76
79
  when Types::LIST
77
80
  e_type, size = iprot.read_list_begin
78
- raise ProtocolException.new(ProtocolException::NEGATIVE_SIZE, 'Negative size') unless size >= 0
81
+ iprot.validate_container_size(size)
79
82
  # Skip the list contents if the declared element type doesn't match the expected one.
80
83
  if (e_type != field[:element][:type])
81
84
  size.times do
@@ -85,13 +88,13 @@ module Thrift
85
88
  else
86
89
  value = []
87
90
  size.times do
88
- value << read_field(iprot, field_info(field[:element]))
91
+ value << read_field(iprot, field_info(field[:element]), remaining_depth)
89
92
  end
90
93
  end
91
94
  iprot.read_list_end
92
95
  when Types::SET
93
96
  e_type, size = iprot.read_set_begin
94
- raise ProtocolException.new(ProtocolException::NEGATIVE_SIZE, 'Negative size') unless size >= 0
97
+ iprot.validate_container_size(size)
95
98
  # Skip the set contents if the declared element type doesn't match the expected one.
96
99
  if (e_type != field[:element][:type])
97
100
  size.times do
@@ -100,7 +103,7 @@ module Thrift
100
103
  else
101
104
  value = Set.new
102
105
  size.times do
103
- element = read_field(iprot, field_info(field[:element]))
106
+ element = read_field(iprot, field_info(field[:element]), remaining_depth)
104
107
  value << element
105
108
  end
106
109
  end
@@ -111,33 +114,36 @@ module Thrift
111
114
  value
112
115
  end
113
116
 
114
- def write_data(oprot, value, field)
117
+ def write_data(oprot, value, field, remaining_depth = DEFAULT_RECURSION_DEPTH)
115
118
  if is_container? field[:type]
116
- write_container(oprot, value, field)
119
+ write_container(oprot, value, field, remaining_depth)
120
+ elsif field[:type] == Types::STRUCT
121
+ remaining_depth -= 1
122
+ value.write(oprot, remaining_depth)
117
123
  else
118
124
  oprot.write_type(field, value)
119
125
  end
120
126
  end
121
127
 
122
- def write_container(oprot, value, field = {})
128
+ def write_container(oprot, value, field = {}, remaining_depth = DEFAULT_RECURSION_DEPTH)
123
129
  case field[:type]
124
130
  when Types::MAP
125
131
  oprot.write_map_begin(field[:key][:type], field[:value][:type], value.size)
126
132
  value.each do |k, v|
127
- write_data(oprot, k, field[:key])
128
- write_data(oprot, v, field[:value])
133
+ write_data(oprot, k, field[:key], remaining_depth)
134
+ write_data(oprot, v, field[:value], remaining_depth)
129
135
  end
130
136
  oprot.write_map_end
131
137
  when Types::LIST
132
138
  oprot.write_list_begin(field[:element][:type], value.size)
133
139
  value.each do |elem|
134
- write_data(oprot, elem, field[:element])
140
+ write_data(oprot, elem, field[:element], remaining_depth)
135
141
  end
136
142
  oprot.write_list_end
137
143
  when Types::SET
138
144
  oprot.write_set_begin(field[:element][:type], value.size)
139
145
  value.each do |v,| # the , is to preserve compatibility with the old Hash-style sets
140
- write_data(oprot, v, field[:element])
146
+ write_data(oprot, v, field[:element], remaining_depth)
141
147
  end
142
148
  oprot.write_set_end
143
149
  else
@@ -154,11 +160,13 @@ module Thrift
154
160
  end
155
161
 
156
162
  def field_info(field)
157
- { :type => field[:type],
158
- :class => field[:class],
159
- :key => field[:key],
160
- :value => field[:value],
161
- :element => field[:element] }
163
+ {
164
+ type: field[:type],
165
+ class: field[:class],
166
+ key: field[:key],
167
+ value: field[:value],
168
+ element: field[:element],
169
+ }
162
170
  end
163
171
 
164
172
  def inspect_field(value, field_info)
@@ -166,9 +174,8 @@ module Thrift
166
174
  "#{enum_class.const_get(:VALUE_MAP)[value]} (#{value})"
167
175
  elsif value.is_a? Hash
168
176
  if field_info[:type] == Types::MAP
169
- map_buf = []
170
- value.each do |k, v|
171
- map_buf << inspect_field(k, field_info[:key]) + ": " + inspect_field(v, field_info[:value])
177
+ map_buf = value.map do |k, v|
178
+ inspect_field(k, field_info[:key]) + ": " + inspect_field(v, field_info[:value])
172
179
  end
173
180
  "{" + map_buf.join(", ") + "}"
174
181
  else
@@ -180,16 +187,15 @@ module Thrift
180
187
  elsif value.is_a? Set
181
188
  inspect_collection(value, field_info)
182
189
  elsif value.is_a?(String) && field_info[:binary]
183
- value.unpack("H*").first
190
+ value.unpack1("H*")
184
191
  else
185
192
  value.inspect
186
193
  end
187
194
  end
188
195
 
189
196
  def inspect_collection(collection, field_info)
190
- buf = []
191
- collection.each do |k|
192
- buf << inspect_field(k, field_info[:element])
197
+ buf = collection.map do |element|
198
+ inspect_field(element, field_info[:element])
193
199
  end
194
200
  "[" + buf.join(", ") + "]"
195
201
  end
@@ -56,6 +56,11 @@ module Thrift
56
56
 
57
57
  def close; end
58
58
 
59
+ # Returns true when the transport needs protocol message boundary notifications.
60
+ def message_boundaries?
61
+ false
62
+ end
63
+
59
64
  # Reads a number of bytes from the transports. The String returned will have a BINARY (aka ASCII-8BIT) encoding.
60
65
  #
61
66
  # sz - The number of bytes to read from the transport.
@@ -68,7 +73,7 @@ module Thrift
68
73
  # Returns an unsigned byte as a Integer in the range (0..255).
69
74
  def read_byte
70
75
  buf = read_all(1)
71
- return Bytes.get_string_byte(buf, 0)
76
+ Bytes.get_string_byte(buf, 0)
72
77
  end
73
78
 
74
79
  # Reads size bytes and copies them into buffer[0..size].
@@ -83,12 +88,25 @@ module Thrift
83
88
  end
84
89
 
85
90
  def read_all(size)
86
- raise TransportException.new(TransportException::NEGATIVE_SIZE, 'Negative size') unless size >= 0
91
+ raise TransportException.new(TransportException::NEGATIVE_SIZE, "Negative size") unless size >= 0
87
92
  return Bytes.empty_byte_buffer if size == 0
88
- buf = Bytes.force_binary_encoding(read(size))
93
+
94
+ buf = read(size)
95
+ if buf.nil? || buf.empty?
96
+ raise TransportException.new(TransportException::END_OF_FILE, "No more data available")
97
+ end
98
+
99
+ buf = Bytes.force_binary_encoding(buf)
100
+ return buf if buf.length >= size
101
+
102
+ buf = buf.dup if buf.frozen?
89
103
  while (buf.length < size)
90
104
  chunk = read(size - buf.length)
91
- buf << chunk
105
+ if chunk.nil? || chunk.empty?
106
+ raise TransportException.new(TransportException::END_OF_FILE, "No more data available")
107
+ end
108
+
109
+ buf << Bytes.force_binary_encoding(chunk)
92
110
  end
93
111
 
94
112
  buf
@@ -111,7 +129,7 @@ module Thrift
111
129
 
112
130
  class BaseTransportFactory
113
131
  def get_transport(trans)
114
- return trans
132
+ trans
115
133
  end
116
134
 
117
135
  def to_s
@@ -31,7 +31,7 @@ module Thrift
31
31
  end
32
32
 
33
33
  def open?
34
- return @transport.open?
34
+ @transport.open?
35
35
  end
36
36
 
37
37
  def open
@@ -66,7 +66,7 @@ module Thrift
66
66
  # The read buffer has some data now, read a single byte. Using get_string_byte() avoids
67
67
  # allocating a temp string of size 1 unnecessarily.
68
68
  @index += 1
69
- return Bytes.get_string_byte(@rbuf, @index - 1)
69
+ Bytes.get_string_byte(@rbuf, @index - 1)
70
70
  end
71
71
 
72
72
  # Reads a number of bytes from the transport into the buffer passed.
@@ -107,13 +107,13 @@ module Thrift
107
107
  end
108
108
 
109
109
  def to_s
110
- "buffered(#{@transport.to_s})"
110
+ "buffered(#{@transport})"
111
111
  end
112
112
  end
113
113
 
114
114
  class BufferedTransportFactory < BaseTransportFactory
115
115
  def get_transport(transport)
116
- return BufferedTransport.new(transport)
116
+ BufferedTransport.new(transport)
117
117
  end
118
118
 
119
119
  def to_s
@@ -21,15 +21,30 @@
21
21
 
22
22
  module Thrift
23
23
  class FramedTransport < BaseTransport
24
- def initialize(transport, read = true, write = true)
24
+ # The largest frame accepted by default. The value the Java, netstd, C++
25
+ # and Python framed transports use.
26
+ DEFAULT_MAX_FRAME_SIZE = 16_384_000
27
+
28
+ # The frame length field is 30 bits, so no frame can usefully be larger
29
+ # than this whatever a caller configures.
30
+ HARD_MAX_FRAME_SIZE = 0x3FFFFFFF
31
+
32
+ def initialize(transport, read = true, write = true, max_frame_size: DEFAULT_MAX_FRAME_SIZE)
33
+ unless max_frame_size > 0 && max_frame_size <= HARD_MAX_FRAME_SIZE
34
+ raise ArgumentError, "max_frame_size must be > 0 and <= #{HARD_MAX_FRAME_SIZE}"
35
+ end
36
+
25
37
  @transport = transport
26
38
  @rbuf = Bytes.empty_byte_buffer
27
39
  @wbuf = Bytes.empty_byte_buffer
28
40
  @read = read
29
41
  @write = write
30
42
  @index = 0
43
+ @max_frame_size = max_frame_size
31
44
  end
32
45
 
46
+ attr_reader :max_frame_size
47
+
33
48
  def open?
34
49
  @transport.open?
35
50
  end
@@ -54,14 +69,14 @@ module Thrift
54
69
  end
55
70
 
56
71
  def read_byte
57
- return @transport.read_byte() unless @read
72
+ return @transport.read_byte unless @read
58
73
 
59
74
  read_frame if @index >= @rbuf.length
60
75
 
61
76
  # The read buffer has some data now, read a single byte. Using get_string_byte() avoids
62
77
  # allocating a temp string of size 1 unnecessarily.
63
78
  @index += 1
64
- return Bytes.get_string_byte(@rbuf, @index - 1)
79
+ Bytes.get_string_byte(@rbuf, @index - 1)
65
80
  end
66
81
 
67
82
  def read_into_buffer(buffer, size)
@@ -92,7 +107,7 @@ module Thrift
92
107
  def flush
93
108
  return @transport.flush unless @write
94
109
 
95
- out = [@wbuf.length].pack('N')
110
+ out = [@wbuf.length].pack("N")
96
111
  # Array#pack should return a BINARY encoded String, so it shouldn't be necessary to force encoding
97
112
  out << @wbuf
98
113
  @transport.write(out)
@@ -101,13 +116,23 @@ module Thrift
101
116
  end
102
117
 
103
118
  def to_s
104
- "framed(#{@transport.to_s})"
119
+ "framed(#{@transport})"
105
120
  end
106
121
 
107
122
  private
108
123
 
109
124
  def read_frame
110
- sz = @transport.read_all(4).unpack('N').first
125
+ sz = @transport.read_all(4).unpack1("N")
126
+ raise TransportException.new(TransportException::END_OF_FILE, "Cannot read from a zero-length frame") if sz == 0
127
+
128
+ # Checked before the read, not after: read_all accumulates whatever the
129
+ # four bytes declare while the peer decides how much of it to send.
130
+ if sz > @max_frame_size
131
+ raise TransportException.new(
132
+ TransportException::UNKNOWN,
133
+ "Frame size #{sz} exceeds maximum #{@max_frame_size}",
134
+ )
135
+ end
111
136
 
112
137
  @index = 0
113
138
  @rbuf = @transport.read_all(sz)
@@ -116,7 +141,7 @@ module Thrift
116
141
 
117
142
  class FramedTransportFactory < BaseTransportFactory
118
143
  def get_transport(transport)
119
- return FramedTransport.new(transport)
144
+ FramedTransport.new(transport)
120
145
  end
121
146
 
122
147
  def to_s