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.
- checksums.yaml +4 -4
- data/README.md +107 -4
- data/ext/binary_protocol_accelerated.c +52 -17
- data/ext/compact_protocol.c +135 -70
- data/ext/constants.h +3 -0
- data/ext/extconf.rb +4 -4
- data/ext/macros.h +11 -0
- data/ext/memory_buffer.c +35 -17
- data/ext/struct.c +185 -64
- data/ext/thrift_native.c +6 -0
- data/lib/thrift/bytes.rb +2 -2
- data/lib/thrift/client.rb +31 -22
- data/lib/thrift/exceptions.rb +10 -8
- data/lib/thrift/multiplexed_processor.rb +5 -6
- data/lib/thrift/processor.rb +24 -3
- data/lib/thrift/protocol/base_protocol.rb +53 -27
- data/lib/thrift/protocol/binary_protocol.rb +54 -40
- data/lib/thrift/protocol/binary_protocol_accelerated.rb +6 -2
- data/lib/thrift/protocol/compact_protocol.rb +193 -88
- data/lib/thrift/protocol/header_protocol.rb +14 -3
- data/lib/thrift/protocol/json_protocol.rb +146 -104
- data/lib/thrift/protocol/multiplexed_protocol.rb +2 -3
- data/lib/thrift/protocol/protocol_decorator.rb +5 -2
- data/lib/thrift/serializer/serializer.rb +6 -0
- data/lib/thrift/server/base_server.rb +10 -2
- data/lib/thrift/server/nonblocking_server.rb +49 -26
- data/lib/thrift/server/rack_application.rb +69 -0
- data/lib/thrift/server/simple_server.rb +27 -23
- data/lib/thrift/server/thin_http_server.rb +20 -44
- data/lib/thrift/server/thread_pool_server.rb +20 -17
- data/lib/thrift/server/threaded_server.rb +22 -26
- data/lib/thrift/struct.rb +33 -18
- data/lib/thrift/struct_union.rb +36 -30
- data/lib/thrift/transport/base_transport.rb +23 -5
- data/lib/thrift/transport/buffered_transport.rb +4 -4
- data/lib/thrift/transport/framed_transport.rb +32 -7
- data/lib/thrift/transport/header_transport.rb +130 -50
- data/lib/thrift/transport/http_client_transport.rb +27 -14
- data/lib/thrift/transport/io_stream_transport.rb +2 -1
- data/lib/thrift/transport/memory_buffer_transport.rb +8 -10
- data/lib/thrift/transport/server_socket.rb +4 -5
- data/lib/thrift/transport/socket.rb +20 -22
- data/lib/thrift/transport/ssl_server_socket.rb +64 -4
- data/lib/thrift/transport/ssl_socket.rb +32 -5
- data/lib/thrift/transport/unix_server_socket.rb +1 -1
- data/lib/thrift/transport/unix_socket.rb +4 -6
- data/lib/thrift/types.rb +22 -22
- data/lib/thrift/union.rb +20 -11
- data/lib/thrift/uuid.rb +8 -8
- data/lib/thrift.rb +39 -39
- metadata +5 -230
- data/benchmark/Benchmark.thrift +0 -24
- data/benchmark/benchmark.rb +0 -286
- data/benchmark/client.rb +0 -118
- data/benchmark/server.rb +0 -121
- data/benchmark/thin_server.rb +0 -46
- data/lib/thrift/server/mongrel_http_server.rb +0 -61
- data/spec/BaseService.thrift +0 -27
- data/spec/ExtendedService.thrift +0 -25
- data/spec/Referenced.thrift +0 -44
- data/spec/ThriftNamespacedSpec.thrift +0 -53
- data/spec/ThriftSpec.thrift +0 -203
- data/spec/base_protocol_spec.rb +0 -244
- data/spec/base_transport_spec.rb +0 -428
- data/spec/binary_protocol_accelerated_spec.rb +0 -47
- data/spec/binary_protocol_spec.rb +0 -73
- data/spec/binary_protocol_spec_shared.rb +0 -509
- data/spec/bytes_spec.rb +0 -117
- data/spec/client_spec.rb +0 -167
- data/spec/compact_protocol_spec.rb +0 -295
- data/spec/constants_demo_spec.rb +0 -102
- data/spec/exception_spec.rb +0 -141
- data/spec/flat_spec.rb +0 -63
- data/spec/header_protocol_spec.rb +0 -476
- data/spec/header_transport_spec.rb +0 -431
- data/spec/http_client_spec.rb +0 -160
- data/spec/json_protocol_spec.rb +0 -574
- data/spec/multiplexed_processor_spec.rb +0 -75
- data/spec/namespaced_spec.rb +0 -67
- data/spec/nonblocking_server_spec.rb +0 -429
- data/spec/processor_spec.rb +0 -80
- data/spec/recursion_depth_spec.rb +0 -223
- data/spec/serializer_spec.rb +0 -67
- data/spec/server_socket_spec.rb +0 -121
- data/spec/server_spec.rb +0 -238
- data/spec/socket_spec.rb +0 -174
- data/spec/socket_spec_shared.rb +0 -168
- data/spec/spec_helper.rb +0 -70
- data/spec/ssl_server_socket_spec.rb +0 -85
- data/spec/ssl_socket_spec.rb +0 -248
- data/spec/struct_nested_containers_spec.rb +0 -191
- data/spec/struct_spec.rb +0 -398
- data/spec/support/header_protocol_helper.rb +0 -55
- data/spec/thin_http_server_spec.rb +0 -127
- data/spec/types_spec.rb +0 -118
- data/spec/union_spec.rb +0 -273
- data/spec/unix_socket_spec.rb +0 -131
- data/spec/uuid_validation_spec.rb +0 -239
- data/test/fuzz/Makefile +0 -779
- data/test/fuzz/Makefile.am +0 -173
- data/test/fuzz/Makefile.in +0 -775
- data/test/fuzz/README.md +0 -149
- data/test/fuzz/fuzz_common.rb +0 -96
- data/test/fuzz/fuzz_parse_binary_protocol.rb +0 -23
- data/test/fuzz/fuzz_parse_binary_protocol_accelerated.rb +0 -23
- data/test/fuzz/fuzz_parse_binary_protocol_accelerated_harness.rb +0 -23
- data/test/fuzz/fuzz_parse_binary_protocol_harness.rb +0 -23
- data/test/fuzz/fuzz_parse_compact_protocol.rb +0 -23
- data/test/fuzz/fuzz_parse_compact_protocol_harness.rb +0 -23
- data/test/fuzz/fuzz_parse_json_protocol.rb +0 -23
- data/test/fuzz/fuzz_parse_json_protocol_harness.rb +0 -23
- data/test/fuzz/fuzz_roundtrip_binary_protocol.rb +0 -23
- data/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated.rb +0 -23
- data/test/fuzz/fuzz_roundtrip_binary_protocol_accelerated_harness.rb +0 -23
- data/test/fuzz/fuzz_roundtrip_binary_protocol_harness.rb +0 -23
- data/test/fuzz/fuzz_roundtrip_compact_protocol.rb +0 -23
- data/test/fuzz/fuzz_roundtrip_compact_protocol_harness.rb +0 -23
- data/test/fuzz/fuzz_roundtrip_json_protocol.rb +0 -23
- data/test/fuzz/fuzz_roundtrip_json_protocol_harness.rb +0 -23
- data/test/fuzz/fuzz_tracer.rb +0 -29
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
# under the License.
|
|
20
20
|
#
|
|
21
21
|
|
|
22
|
-
require
|
|
23
|
-
require
|
|
22
|
+
require "stringio"
|
|
23
|
+
require "zlib"
|
|
24
24
|
|
|
25
25
|
module Thrift
|
|
26
26
|
# Client type constants for Header protocol
|
|
@@ -71,15 +71,18 @@ module Thrift
|
|
|
71
71
|
# Header magic value (first 2 bytes of header)
|
|
72
72
|
HEADER_MAGIC = 0x0FFF
|
|
73
73
|
|
|
74
|
-
#
|
|
74
|
+
# The largest frame the length field can express (~1GB). This is the
|
|
75
|
+
# protocol's ceiling, not a sensible default -- see DEFAULT_MAX_FRAME_SIZE.
|
|
75
76
|
MAX_FRAME_SIZE = 0x3FFFFFFF
|
|
76
77
|
|
|
78
|
+
# The largest frame accepted by default (~15.6 MB, matches other Thrift
|
|
79
|
+
# bindings). Defaulting to MAX_FRAME_SIZE let a four-byte header ask for a
|
|
80
|
+
# gigabyte, which is the ceiling rather than a limit.
|
|
81
|
+
DEFAULT_MAX_FRAME_SIZE = 16_384_000
|
|
82
|
+
|
|
77
83
|
# Default decompressed-size cap for ZLIB transform (~15.6 MB, matches other Thrift bindings)
|
|
78
84
|
DEFAULT_MAX_DECOMPRESSED_SIZE = 16_384_000
|
|
79
85
|
|
|
80
|
-
# Chunk size for streaming inflate loop
|
|
81
|
-
ZLIB_INFLATE_CHUNK_SIZE = 16_384
|
|
82
|
-
|
|
83
86
|
# Binary protocol version mask and version 1
|
|
84
87
|
BINARY_VERSION_MASK = 0xffff0000
|
|
85
88
|
BINARY_VERSION_1 = 0x80010000
|
|
@@ -89,6 +92,9 @@ module Thrift
|
|
|
89
92
|
COMPACT_VERSION_MASK = 0x1f
|
|
90
93
|
COMPACT_VERSION = 0x01
|
|
91
94
|
|
|
95
|
+
MAX_VARINT32_BYTES = 5
|
|
96
|
+
MAX_VARINT32_LAST_BYTE = 0x0f
|
|
97
|
+
|
|
92
98
|
attr_reader :protocol_id, :sequence_id, :flags
|
|
93
99
|
|
|
94
100
|
# Creates a new HeaderTransport wrapping the given transport.
|
|
@@ -106,7 +112,7 @@ module Thrift
|
|
|
106
112
|
HeaderClientType::FRAMED_BINARY,
|
|
107
113
|
HeaderClientType::UNFRAMED_BINARY,
|
|
108
114
|
HeaderClientType::FRAMED_COMPACT,
|
|
109
|
-
HeaderClientType::UNFRAMED_COMPACT
|
|
115
|
+
HeaderClientType::UNFRAMED_COMPACT,
|
|
110
116
|
]
|
|
111
117
|
|
|
112
118
|
@read_buffer = StringIO.new(Bytes.empty_byte_buffer)
|
|
@@ -118,8 +124,9 @@ module Thrift
|
|
|
118
124
|
|
|
119
125
|
@sequence_id = 0
|
|
120
126
|
@flags = 0
|
|
121
|
-
@max_frame_size =
|
|
127
|
+
@max_frame_size = DEFAULT_MAX_FRAME_SIZE
|
|
122
128
|
@max_decompressed_size = DEFAULT_MAX_DECOMPRESSED_SIZE
|
|
129
|
+
@unframed_bytes_read = 0
|
|
123
130
|
end
|
|
124
131
|
|
|
125
132
|
def sequence_id=(sequence_id)
|
|
@@ -130,6 +137,14 @@ module Thrift
|
|
|
130
137
|
@sequence_id = sequence_id
|
|
131
138
|
end
|
|
132
139
|
|
|
140
|
+
def protocol_id=(protocol_id)
|
|
141
|
+
unless [HeaderSubprotocolID::BINARY, HeaderSubprotocolID::COMPACT].include?(protocol_id)
|
|
142
|
+
raise ArgumentError, "Unknown protocol ID: #{protocol_id}"
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
@protocol_id = protocol_id
|
|
146
|
+
end
|
|
147
|
+
|
|
133
148
|
def open?
|
|
134
149
|
@transport.open?
|
|
135
150
|
end
|
|
@@ -172,6 +187,9 @@ module Thrift
|
|
|
172
187
|
@write_transforms << transform_id unless @write_transforms.include?(transform_id)
|
|
173
188
|
end
|
|
174
189
|
|
|
190
|
+
# The maximum frame size currently in force
|
|
191
|
+
attr_reader :max_frame_size
|
|
192
|
+
|
|
175
193
|
# Sets the maximum allowed frame size
|
|
176
194
|
def set_max_frame_size(size)
|
|
177
195
|
if size <= 0 || size > MAX_FRAME_SIZE
|
|
@@ -199,7 +217,7 @@ module Thrift
|
|
|
199
217
|
# Handle unframed passthrough - read directly from underlying transport
|
|
200
218
|
if @client_type == HeaderClientType::UNFRAMED_BINARY ||
|
|
201
219
|
@client_type == HeaderClientType::UNFRAMED_COMPACT
|
|
202
|
-
return data +
|
|
220
|
+
return data + read_unframed(bytes_left)
|
|
203
221
|
end
|
|
204
222
|
|
|
205
223
|
# Need to read the next frame
|
|
@@ -217,16 +235,15 @@ module Thrift
|
|
|
217
235
|
@write_buffer = StringIO.new(Bytes.empty_byte_buffer)
|
|
218
236
|
|
|
219
237
|
return if payload.empty?
|
|
220
|
-
if payload.bytesize > @max_frame_size
|
|
221
|
-
raise TransportException.new(TransportException::UNKNOWN, "Attempting to send frame that is too large")
|
|
222
|
-
end
|
|
223
238
|
|
|
224
239
|
case @client_type
|
|
225
240
|
when HeaderClientType::HEADERS
|
|
226
241
|
flush_header_format(payload)
|
|
227
242
|
when HeaderClientType::FRAMED_BINARY, HeaderClientType::FRAMED_COMPACT
|
|
243
|
+
validate_frame_size!(payload.bytesize)
|
|
228
244
|
flush_framed(payload)
|
|
229
245
|
when HeaderClientType::UNFRAMED_BINARY, HeaderClientType::UNFRAMED_COMPACT
|
|
246
|
+
validate_frame_size!(payload.bytesize)
|
|
230
247
|
@transport.write(payload)
|
|
231
248
|
@transport.flush
|
|
232
249
|
else
|
|
@@ -235,7 +252,7 @@ module Thrift
|
|
|
235
252
|
end
|
|
236
253
|
|
|
237
254
|
def to_s
|
|
238
|
-
"header(#{@transport
|
|
255
|
+
"header(#{@transport})"
|
|
239
256
|
end
|
|
240
257
|
|
|
241
258
|
# Reads the next frame to detect protocol/client type before decoding.
|
|
@@ -245,6 +262,17 @@ module Thrift
|
|
|
245
262
|
read_frame(0)
|
|
246
263
|
end
|
|
247
264
|
|
|
265
|
+
def message_boundaries?
|
|
266
|
+
true
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
# Starts a new protocol message without forcing client-type detection.
|
|
270
|
+
def reset_message_size
|
|
271
|
+
return unless @read_buffer.nil? || @read_buffer.eof?
|
|
272
|
+
|
|
273
|
+
@unframed_bytes_read = 0
|
|
274
|
+
end
|
|
275
|
+
|
|
248
276
|
private
|
|
249
277
|
|
|
250
278
|
# Sets the client type after validation
|
|
@@ -257,9 +285,16 @@ module Thrift
|
|
|
257
285
|
|
|
258
286
|
# Reads the next frame, detecting client type on first read
|
|
259
287
|
def read_frame(req_sz)
|
|
288
|
+
@read_headers = {}
|
|
289
|
+
@unframed_bytes_read = 0
|
|
290
|
+
|
|
260
291
|
# Read first 4 bytes - could be frame length or protocol magic
|
|
261
|
-
|
|
262
|
-
|
|
292
|
+
begin
|
|
293
|
+
first_word = @transport.read_all(4)
|
|
294
|
+
rescue EOFError
|
|
295
|
+
raise TransportException.new(TransportException::END_OF_FILE, "Unexpected EOF reading frame size")
|
|
296
|
+
end
|
|
297
|
+
frame_size = first_word.unpack1("N")
|
|
263
298
|
|
|
264
299
|
# Check for unframed binary protocol
|
|
265
300
|
if (frame_size & BINARY_VERSION_MASK) == BINARY_VERSION_1
|
|
@@ -282,16 +317,23 @@ module Thrift
|
|
|
282
317
|
if frame_size > @max_frame_size
|
|
283
318
|
raise TransportException.new(TransportException::UNKNOWN, "Frame size #{frame_size} exceeds maximum #{@max_frame_size}")
|
|
284
319
|
end
|
|
320
|
+
if frame_size < 4
|
|
321
|
+
raise TransportException.new(TransportException::UNKNOWN, "Frame size #{frame_size} is too small")
|
|
322
|
+
end
|
|
285
323
|
|
|
286
324
|
# Read the complete frame
|
|
287
|
-
|
|
325
|
+
begin
|
|
326
|
+
frame_data = @transport.read_all(frame_size)
|
|
327
|
+
rescue EOFError
|
|
328
|
+
raise TransportException.new(TransportException::END_OF_FILE, "Unexpected EOF reading frame")
|
|
329
|
+
end
|
|
288
330
|
frame_buf = StringIO.new(frame_data)
|
|
289
331
|
|
|
290
332
|
# Check the second word for protocol type
|
|
291
333
|
second_word = frame_buf.read(4)
|
|
292
334
|
frame_buf.rewind
|
|
293
335
|
|
|
294
|
-
magic = second_word.
|
|
336
|
+
magic = second_word.unpack1("n")
|
|
295
337
|
|
|
296
338
|
if magic == HEADER_MAGIC
|
|
297
339
|
if frame_size < 10
|
|
@@ -299,7 +341,7 @@ module Thrift
|
|
|
299
341
|
end
|
|
300
342
|
set_client_type(HeaderClientType::HEADERS)
|
|
301
343
|
@read_buffer = parse_header_format(frame_buf)
|
|
302
|
-
elsif (second_word.
|
|
344
|
+
elsif (second_word.unpack1("N") & BINARY_VERSION_MASK) == BINARY_VERSION_1
|
|
303
345
|
set_client_type(HeaderClientType::FRAMED_BINARY)
|
|
304
346
|
@protocol_id = HeaderSubprotocolID::BINARY
|
|
305
347
|
@read_buffer = frame_buf
|
|
@@ -315,26 +357,45 @@ module Thrift
|
|
|
315
357
|
|
|
316
358
|
# Handles unframed protocol - puts first_word back in buffer
|
|
317
359
|
def handle_unframed(first_word, req_sz)
|
|
360
|
+
@unframed_bytes_read = first_word.bytesize
|
|
361
|
+
raise_unframed_size_limit if @unframed_bytes_read > @max_frame_size
|
|
362
|
+
|
|
318
363
|
bytes_left = req_sz - 4
|
|
319
|
-
if bytes_left > 0
|
|
320
|
-
rest =
|
|
364
|
+
if bytes_left > 0 && @unframed_bytes_read < @max_frame_size
|
|
365
|
+
rest = read_unframed(bytes_left)
|
|
321
366
|
@read_buffer = StringIO.new(first_word + rest)
|
|
322
367
|
else
|
|
323
368
|
@read_buffer = StringIO.new(first_word)
|
|
324
369
|
end
|
|
325
370
|
end
|
|
326
371
|
|
|
372
|
+
def read_unframed(size)
|
|
373
|
+
remaining = @max_frame_size - @unframed_bytes_read
|
|
374
|
+
raise_unframed_size_limit if remaining <= 0
|
|
375
|
+
|
|
376
|
+
data = @transport.read([size, remaining].min)
|
|
377
|
+
@unframed_bytes_read += data.bytesize
|
|
378
|
+
data
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
def raise_unframed_size_limit
|
|
382
|
+
raise TransportException.new(
|
|
383
|
+
TransportException::SIZE_LIMIT,
|
|
384
|
+
"Unframed message size exceeds maximum #{@max_frame_size}",
|
|
385
|
+
)
|
|
386
|
+
end
|
|
387
|
+
|
|
327
388
|
# Parses a Header format frame
|
|
328
389
|
def parse_header_format(buf)
|
|
329
390
|
# Skip magic (already identified)
|
|
330
391
|
buf.read(2)
|
|
331
392
|
|
|
332
393
|
# Read flags and sequence ID
|
|
333
|
-
@flags = buf.read(2).
|
|
334
|
-
@sequence_id = signed_int32(buf.read(4).
|
|
394
|
+
@flags = buf.read(2).unpack1("n")
|
|
395
|
+
@sequence_id = signed_int32(buf.read(4).unpack1("N"))
|
|
335
396
|
|
|
336
397
|
# Read header length (in 32-bit words)
|
|
337
|
-
header_words = buf.read(2).
|
|
398
|
+
header_words = buf.read(2).unpack1("n")
|
|
338
399
|
if header_words >= 16_384
|
|
339
400
|
raise TransportException.new(TransportException::UNKNOWN, "Header size is unreasonable")
|
|
340
401
|
end
|
|
@@ -359,7 +420,6 @@ module Thrift
|
|
|
359
420
|
transforms << transform_id
|
|
360
421
|
end
|
|
361
422
|
# Read info headers
|
|
362
|
-
@read_headers = {}
|
|
363
423
|
while buf.pos < end_of_headers
|
|
364
424
|
info_type = read_varint32(buf, end_of_headers)
|
|
365
425
|
if info_type == 0
|
|
@@ -397,26 +457,22 @@ module Thrift
|
|
|
397
457
|
def bounded_inflate(compressed)
|
|
398
458
|
inflater = Zlib::Inflate.new
|
|
399
459
|
buffer = Bytes.empty_byte_buffer
|
|
400
|
-
|
|
401
|
-
|
|
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
|
|
460
|
+
append_chunk = lambda do |chunk|
|
|
461
|
+
if buffer.bytesize + chunk.bytesize > @max_decompressed_size
|
|
414
462
|
raise TransportException.new(
|
|
415
463
|
TransportException::SIZE_LIMIT,
|
|
416
|
-
"Decompressed size exceeds limit of #{@max_decompressed_size}"
|
|
464
|
+
"Decompressed size exceeds limit of #{@max_decompressed_size}",
|
|
417
465
|
)
|
|
418
466
|
end
|
|
467
|
+
|
|
468
|
+
buffer << chunk
|
|
469
|
+
end
|
|
470
|
+
begin
|
|
471
|
+
inflater.inflate(compressed, &append_chunk)
|
|
472
|
+
inflater.finish(&append_chunk)
|
|
419
473
|
buffer
|
|
474
|
+
rescue Zlib::DataError, Zlib::BufError
|
|
475
|
+
raise TransportException.new(TransportException::UNKNOWN, "Invalid ZLIB payload")
|
|
420
476
|
ensure
|
|
421
477
|
inflater.close rescue nil
|
|
422
478
|
end
|
|
@@ -449,7 +505,6 @@ module Thrift
|
|
|
449
505
|
write_varstring(header_buf, key)
|
|
450
506
|
write_varstring(header_buf, value)
|
|
451
507
|
end
|
|
452
|
-
@write_headers = {}
|
|
453
508
|
end
|
|
454
509
|
|
|
455
510
|
# Pad header to 4-byte boundary
|
|
@@ -460,14 +515,16 @@ module Thrift
|
|
|
460
515
|
# Calculate total frame size (excludes the 4-byte length field itself)
|
|
461
516
|
# Frame = magic(2) + flags(2) + seqid(4) + header_len(2) + header_data + payload
|
|
462
517
|
frame_size = 2 + 2 + 4 + 2 + header_data.bytesize + payload.bytesize
|
|
518
|
+
validate_frame_size!(frame_size)
|
|
519
|
+
@write_headers = {}
|
|
463
520
|
|
|
464
521
|
# Write complete frame
|
|
465
522
|
frame = Bytes.empty_byte_buffer
|
|
466
|
-
frame << [frame_size].pack(
|
|
467
|
-
frame << [HEADER_MAGIC].pack(
|
|
468
|
-
frame << [@flags].pack(
|
|
469
|
-
frame << [unsigned_int32(@sequence_id)].pack(
|
|
470
|
-
frame << [header_data.bytesize / 4].pack(
|
|
523
|
+
frame << [frame_size].pack("N") # Length
|
|
524
|
+
frame << [HEADER_MAGIC].pack("n") # Magic
|
|
525
|
+
frame << [@flags].pack("n") # Flags
|
|
526
|
+
frame << [unsigned_int32(@sequence_id)].pack("N") # Sequence ID
|
|
527
|
+
frame << [header_data.bytesize / 4].pack("n") # Header length (in 32-bit words)
|
|
471
528
|
frame << header_data # Header data
|
|
472
529
|
frame << payload # Payload
|
|
473
530
|
|
|
@@ -475,9 +532,18 @@ module Thrift
|
|
|
475
532
|
@transport.flush
|
|
476
533
|
end
|
|
477
534
|
|
|
535
|
+
def validate_frame_size!(frame_size)
|
|
536
|
+
return if frame_size <= @max_frame_size
|
|
537
|
+
|
|
538
|
+
raise TransportException.new(
|
|
539
|
+
TransportException::UNKNOWN,
|
|
540
|
+
"Frame size #{frame_size} exceeds maximum #{@max_frame_size}",
|
|
541
|
+
)
|
|
542
|
+
end
|
|
543
|
+
|
|
478
544
|
# Flushes data in simple framed format (for legacy compatibility)
|
|
479
545
|
def flush_framed(payload)
|
|
480
|
-
frame = [payload.bytesize].pack(
|
|
546
|
+
frame = [payload.bytesize].pack("N") + payload
|
|
481
547
|
@transport.write(frame)
|
|
482
548
|
@transport.flush
|
|
483
549
|
end
|
|
@@ -492,21 +558,35 @@ module Thrift
|
|
|
492
558
|
end
|
|
493
559
|
byte = io.getbyte
|
|
494
560
|
raise TransportException.new(TransportException::END_OF_FILE, "Unexpected EOF reading varint") if byte.nil?
|
|
561
|
+
|
|
562
|
+
if shift == (MAX_VARINT32_BYTES - 1) * 7
|
|
563
|
+
if (byte & 0x80) != 0
|
|
564
|
+
raise TransportException.new(TransportException::UNKNOWN, "Variable-length int over 5 bytes.")
|
|
565
|
+
end
|
|
566
|
+
if (byte & ~MAX_VARINT32_LAST_BYTE) != 0
|
|
567
|
+
raise TransportException.new(TransportException::UNKNOWN, "Variable-length int overflows uint32.")
|
|
568
|
+
end
|
|
569
|
+
end
|
|
570
|
+
|
|
495
571
|
result |= (byte & 0x7f) << shift
|
|
496
|
-
|
|
572
|
+
return result if (byte & 0x80) == 0
|
|
497
573
|
shift += 7
|
|
498
574
|
end
|
|
499
|
-
result
|
|
500
575
|
end
|
|
501
576
|
|
|
502
577
|
# Writes a varint32 to the given IO
|
|
503
578
|
def write_varint32(io, n)
|
|
579
|
+
# Encode the low 32 bits: Ruby's >> is arithmetic, so a negative value
|
|
580
|
+
# would keep its sign bit set and never satisfy the (n & ~0x7F) == 0 exit
|
|
581
|
+
# condition below. Masking yields the same unsigned varint read_varint32
|
|
582
|
+
# produces and accepts.
|
|
583
|
+
n &= 0xFFFFFFFF
|
|
504
584
|
loop do
|
|
505
585
|
if (n & ~0x7F) == 0
|
|
506
|
-
io.write([n].pack(
|
|
586
|
+
io.write([n].pack("C"))
|
|
507
587
|
break
|
|
508
588
|
else
|
|
509
|
-
io.write([(n & 0x7F) | 0x80].pack(
|
|
589
|
+
io.write([(n & 0x7F) | 0x80].pack("C"))
|
|
510
590
|
n >>= 7
|
|
511
591
|
end
|
|
512
592
|
end
|
|
@@ -19,24 +19,31 @@
|
|
|
19
19
|
# under the License.
|
|
20
20
|
#
|
|
21
21
|
|
|
22
|
-
require
|
|
23
|
-
require
|
|
24
|
-
require
|
|
25
|
-
require
|
|
26
|
-
require
|
|
22
|
+
require "net/http"
|
|
23
|
+
require "net/https"
|
|
24
|
+
require "openssl"
|
|
25
|
+
require "uri"
|
|
26
|
+
require "stringio"
|
|
27
27
|
|
|
28
28
|
module Thrift
|
|
29
29
|
class HTTPClientTransport < BaseTransport
|
|
30
|
-
|
|
31
30
|
def initialize(url, opts = {})
|
|
32
31
|
@url = URI url
|
|
33
|
-
@headers = {
|
|
32
|
+
@headers = {"Content-Type" => "application/x-thrift"}
|
|
34
33
|
@outbuf = Bytes.empty_byte_buffer
|
|
35
34
|
@ssl_verify_mode = opts.fetch(:ssl_verify_mode, OpenSSL::SSL::VERIFY_PEER)
|
|
35
|
+
@ssl_ca_file = opts[:ssl_ca_file]
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
def open?; true end
|
|
39
|
-
|
|
39
|
+
|
|
40
|
+
def read(sz)
|
|
41
|
+
data = @inbuf.read sz
|
|
42
|
+
return data unless data.nil?
|
|
43
|
+
|
|
44
|
+
raise TransportException.new(TransportException::END_OF_FILE, "#{self.class.name} reached EOF reading response from #{self}, HTTP status code #{@response_code}")
|
|
45
|
+
end
|
|
46
|
+
|
|
40
47
|
def write(buf); @outbuf << Bytes.force_binary_encoding(buf) end
|
|
41
48
|
|
|
42
49
|
def add_headers(headers)
|
|
@@ -45,20 +52,26 @@ module Thrift
|
|
|
45
52
|
|
|
46
53
|
def flush
|
|
47
54
|
http = Net::HTTP.new @url.host, @url.port
|
|
48
|
-
http.use_ssl = @url.scheme ==
|
|
49
|
-
|
|
55
|
+
http.use_ssl = @url.scheme == "https"
|
|
56
|
+
if @url.scheme == "https"
|
|
57
|
+
http.verify_mode = @ssl_verify_mode
|
|
58
|
+
http.ca_file = @ssl_ca_file if @ssl_ca_file
|
|
59
|
+
end
|
|
50
60
|
resp = http.post(@url.request_uri, @outbuf, @headers)
|
|
51
|
-
|
|
61
|
+
response_code = resp.code.to_i
|
|
62
|
+
raise TransportException.new(TransportException::UNKNOWN, "#{self.class.name} Could not connect to #{self}, HTTP status code #{response_code}") unless (200..299).cover?(response_code)
|
|
52
63
|
|
|
53
|
-
|
|
54
|
-
data = Bytes.force_binary_encoding(
|
|
64
|
+
@response_code = response_code
|
|
65
|
+
data = Bytes.force_binary_encoding(resp.body || Bytes.empty_byte_buffer)
|
|
55
66
|
@inbuf = StringIO.new data
|
|
56
67
|
ensure
|
|
57
68
|
@outbuf = Bytes.empty_byte_buffer
|
|
58
69
|
end
|
|
59
70
|
|
|
60
71
|
def to_s
|
|
61
|
-
|
|
72
|
+
path = @url.path.to_s
|
|
73
|
+
path = "/" if path.empty?
|
|
74
|
+
"#{@url.scheme}(#{@url.host}:#{@url.port}#{path})"
|
|
62
75
|
end
|
|
63
76
|
end
|
|
64
77
|
end
|
|
@@ -36,8 +36,9 @@ module Thrift
|
|
|
36
36
|
def write(buf); @output.write(Bytes.force_binary_encoding(buf)) end
|
|
37
37
|
def close; @input.close; @output.close end
|
|
38
38
|
def to_io; @input end # we're assuming this is used in a IO.select for reading
|
|
39
|
+
|
|
39
40
|
def to_s
|
|
40
|
-
"iostream(input=#{@input
|
|
41
|
+
"iostream(input=#{@input},output=#{@output})"
|
|
41
42
|
end
|
|
42
43
|
end
|
|
43
44
|
end
|
|
@@ -21,20 +21,16 @@
|
|
|
21
21
|
|
|
22
22
|
module Thrift
|
|
23
23
|
class MemoryBufferTransport < BaseTransport
|
|
24
|
-
GARBAGE_BUFFER_SIZE = 4*(2**10) # 4kB
|
|
24
|
+
GARBAGE_BUFFER_SIZE = 4 * (2**10) # 4kB
|
|
25
25
|
|
|
26
|
-
#
|
|
27
|
-
# unless you want it to be modified by #read and #write
|
|
28
|
-
#--
|
|
29
|
-
# this behavior is no longer required. If you wish to change it
|
|
30
|
-
# go ahead, just make sure the specs pass
|
|
26
|
+
# The transport copies the input buffer and keeps its own mutable storage.
|
|
31
27
|
def initialize(buffer = nil)
|
|
32
|
-
@buf = buffer ? Bytes.force_binary_encoding(buffer) : Bytes.empty_byte_buffer
|
|
28
|
+
@buf = buffer ? Bytes.force_binary_encoding(buffer.dup) : Bytes.empty_byte_buffer
|
|
33
29
|
@index = 0
|
|
34
30
|
end
|
|
35
31
|
|
|
36
32
|
def open?
|
|
37
|
-
|
|
33
|
+
true
|
|
38
34
|
end
|
|
39
35
|
|
|
40
36
|
def open
|
|
@@ -48,7 +44,7 @@ module Thrift
|
|
|
48
44
|
end
|
|
49
45
|
|
|
50
46
|
# this method does not use the passed object directly but copies it
|
|
51
|
-
def reset_buffer(new_buf =
|
|
47
|
+
def reset_buffer(new_buf = "")
|
|
52
48
|
@buf.replace Bytes.force_binary_encoding(new_buf)
|
|
53
49
|
@index = 0
|
|
54
50
|
end
|
|
@@ -58,6 +54,8 @@ module Thrift
|
|
|
58
54
|
end
|
|
59
55
|
|
|
60
56
|
def read(len)
|
|
57
|
+
raise TransportException.new(TransportException::NEGATIVE_SIZE, "Negative size") unless len >= 0
|
|
58
|
+
|
|
61
59
|
data = @buf.slice(@index, len)
|
|
62
60
|
@index += len
|
|
63
61
|
@index = @buf.size if @index > @buf.size
|
|
@@ -72,7 +70,7 @@ module Thrift
|
|
|
72
70
|
end
|
|
73
71
|
|
|
74
72
|
def read_all(size)
|
|
75
|
-
raise TransportException.new(TransportException::NEGATIVE_SIZE,
|
|
73
|
+
raise TransportException.new(TransportException::NEGATIVE_SIZE, "Negative size") unless size >= 0
|
|
76
74
|
|
|
77
75
|
read(size)
|
|
78
76
|
end
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
# under the License.
|
|
20
20
|
#
|
|
21
21
|
|
|
22
|
-
require
|
|
22
|
+
require "socket"
|
|
23
23
|
|
|
24
24
|
module Thrift
|
|
25
25
|
class ServerSocket < BaseServerTransport
|
|
@@ -54,8 +54,8 @@ module Thrift
|
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
def close
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
@handle.close unless @handle.nil? or @handle.closed?
|
|
58
|
+
@handle = nil
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
def closed?
|
|
@@ -63,12 +63,11 @@ module Thrift
|
|
|
63
63
|
end
|
|
64
64
|
|
|
65
65
|
def to_io
|
|
66
|
-
@handle&.to_io || raise(IOError,
|
|
66
|
+
@handle&.to_io || raise(IOError, "closed stream")
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
def to_s
|
|
70
70
|
"socket(#{@host}:#{@port})"
|
|
71
71
|
end
|
|
72
|
-
|
|
73
72
|
end
|
|
74
73
|
end
|
|
@@ -19,11 +19,11 @@
|
|
|
19
19
|
# under the License.
|
|
20
20
|
#
|
|
21
21
|
|
|
22
|
-
require
|
|
22
|
+
require "socket"
|
|
23
23
|
|
|
24
24
|
module Thrift
|
|
25
25
|
class Socket < BaseTransport
|
|
26
|
-
def initialize(host =
|
|
26
|
+
def initialize(host = "localhost", port = 9090, timeout = nil)
|
|
27
27
|
@host = host
|
|
28
28
|
@port = port
|
|
29
29
|
@timeout = timeout
|
|
@@ -34,6 +34,8 @@ module Thrift
|
|
|
34
34
|
attr_accessor :handle, :timeout
|
|
35
35
|
|
|
36
36
|
def open
|
|
37
|
+
raise TransportException.new(TransportException::ALREADY_OPEN, "Socket already open") if open?
|
|
38
|
+
|
|
37
39
|
deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + @timeout unless @timeout.nil? || @timeout == 0
|
|
38
40
|
@handle = connect_socket(deadline)
|
|
39
41
|
end
|
|
@@ -54,7 +56,7 @@ module Thrift
|
|
|
54
56
|
|
|
55
57
|
while len < str.length
|
|
56
58
|
begin
|
|
57
|
-
len += @handle.write_nonblock(str[len
|
|
59
|
+
len += @handle.write_nonblock(str[len..])
|
|
58
60
|
rescue IO::WaitWritable
|
|
59
61
|
wait_for(:write, deadline, str.length)
|
|
60
62
|
rescue IO::WaitReadable
|
|
@@ -64,12 +66,11 @@ module Thrift
|
|
|
64
66
|
|
|
65
67
|
len
|
|
66
68
|
end
|
|
67
|
-
rescue TransportException
|
|
68
|
-
|
|
69
|
-
raise
|
|
69
|
+
rescue TransportException
|
|
70
|
+
close
|
|
71
|
+
raise
|
|
70
72
|
rescue StandardError => e
|
|
71
|
-
|
|
72
|
-
@handle = nil
|
|
73
|
+
close
|
|
73
74
|
raise TransportException.new(TransportException::NOT_OPEN, e.message)
|
|
74
75
|
end
|
|
75
76
|
end
|
|
@@ -84,24 +85,21 @@ module Thrift
|
|
|
84
85
|
deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + @timeout
|
|
85
86
|
|
|
86
87
|
data = loop do
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
wait_for(:write, deadline, sz)
|
|
93
|
-
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)
|
|
94
93
|
end
|
|
95
94
|
end
|
|
96
|
-
rescue TransportException
|
|
97
|
-
|
|
98
|
-
raise
|
|
95
|
+
rescue TransportException
|
|
96
|
+
close
|
|
97
|
+
raise
|
|
99
98
|
rescue StandardError => e
|
|
100
|
-
|
|
101
|
-
@handle = nil
|
|
99
|
+
close
|
|
102
100
|
raise TransportException.new(TransportException::NOT_OPEN, e.message)
|
|
103
101
|
end
|
|
104
|
-
if
|
|
102
|
+
if data.nil? || data.length == 0
|
|
105
103
|
raise TransportException.new(TransportException::UNKNOWN, "Socket: Could not read #{sz} bytes from #{@desc}")
|
|
106
104
|
end
|
|
107
105
|
data
|
|
@@ -113,7 +111,7 @@ module Thrift
|
|
|
113
111
|
end
|
|
114
112
|
|
|
115
113
|
def to_io
|
|
116
|
-
@handle&.to_io || raise(IOError,
|
|
114
|
+
@handle&.to_io || raise(IOError, "closed stream")
|
|
117
115
|
end
|
|
118
116
|
|
|
119
117
|
def to_s
|