blob: 3feb9b681075e760a38c135fe169c5929bf59543 [file] [log] [blame]
Dmytro Shteflyuk67bfb292026-01-28 11:23:50 -05001#
2# Licensed to the Apache Software Foundation (ASF) under one
3# or more contributor license agreements. See the NOTICE file
4# distributed with this work for additional information
5# regarding copyright ownership. The ASF licenses this file
6# to you under the Apache License, Version 2.0 (the
7# "License"); you may not use this file except in compliance
8# with the License. You may obtain a copy of the License at
9#
10# http://www.apache.org/licenses/LICENSE-2.0
11#
12# Unless required by applicable law or agreed to in writing,
13# software distributed under the License is distributed on an
14# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15# KIND, either express or implied. See the License for the
16# specific language governing permissions and limitations
17# under the License.
18#
19
20require 'spec_helper'
21require_relative 'support/header_protocol_helper'
22
23describe 'HeaderProtocol' do
24 include HeaderProtocolHelper
25
26 describe Thrift::HeaderProtocol do
27 before(:each) do
28 @buffer = Thrift::MemoryBufferTransport.new
29 @protocol = Thrift::HeaderProtocol.new(@buffer)
30 end
31
32 it "should provide a to_s" do
33 expect(@protocol.to_s).to match(/header\(compact/)
34 end
35
36 it "should wrap transport in HeaderTransport" do
37 expect(@protocol.trans).to be_a(Thrift::HeaderTransport)
38 end
39
40 it "should use existing HeaderTransport if passed" do
41 header_trans = Thrift::HeaderTransport.new(@buffer)
42 protocol = Thrift::HeaderProtocol.new(header_trans)
43 expect(protocol.trans).to equal(header_trans)
44 end
45
46 describe "header management delegation" do
47 it "should delegate get_headers" do
48 # Write with headers and read back to populate read headers
49 @protocol.set_header("key", "value")
50 @protocol.write_message_begin("test", Thrift::MessageTypes::CALL, 1)
51 @protocol.write_struct_begin("args")
52 @protocol.write_field_stop
53 @protocol.write_struct_end
54 @protocol.write_message_end
55 @protocol.trans.flush
56
57 # Read back
58 data = @buffer.read(@buffer.available)
59 read_buffer = Thrift::MemoryBufferTransport.new(data)
60 read_protocol = Thrift::HeaderProtocol.new(read_buffer)
61
62 read_protocol.read_message_begin
63 headers = read_protocol.get_headers
64 expect(headers["key"]).to eq("value")
65 end
66
67 it "should delegate set_header" do
68 expect(@protocol.trans).to receive(:set_header).with("key", "value")
69 @protocol.set_header("key", "value")
70 end
71
72 it "should delegate clear_headers" do
73 expect(@protocol.trans).to receive(:clear_headers)
74 @protocol.clear_headers
75 end
76
77 it "should delegate add_transform" do
78 expect(@protocol.trans).to receive(:add_transform).with(Thrift::HeaderTransformID::ZLIB)
79 @protocol.add_transform(Thrift::HeaderTransformID::ZLIB)
80 end
81 end
82
83 describe "protocol delegation with Binary protocol" do
84 before(:each) do
85 @buffer = Thrift::MemoryBufferTransport.new
86 @protocol = Thrift::HeaderProtocol.new(@buffer, nil, Thrift::HeaderSubprotocolID::BINARY)
87 end
88
89 it "should write message begin" do
90 @protocol.write_message_begin("test_method", Thrift::MessageTypes::CALL, 123)
91 @protocol.write_message_end
92 @protocol.trans.flush
93
94 # Verify we can read it back
95 data = @buffer.read(@buffer.available)
96 read_buffer = Thrift::MemoryBufferTransport.new(data)
97 read_protocol = Thrift::HeaderProtocol.new(read_buffer)
98
99 name, type, seqid = read_protocol.read_message_begin
100 expect(name).to eq("test_method")
101 expect(type).to eq(Thrift::MessageTypes::CALL)
102 expect(seqid).to eq(123)
103 end
104
105 it "should write and read structs" do
106 @protocol.write_message_begin("test", Thrift::MessageTypes::CALL, 1)
107 @protocol.write_struct_begin("TestStruct")
108 @protocol.write_field_begin("field1", Thrift::Types::I32, 1)
109 @protocol.write_i32(42)
110 @protocol.write_field_end
111 @protocol.write_field_stop
112 @protocol.write_struct_end
113 @protocol.write_message_end
114 @protocol.trans.flush
115
116 data = @buffer.read(@buffer.available)
117 read_buffer = Thrift::MemoryBufferTransport.new(data)
118 read_protocol = Thrift::HeaderProtocol.new(read_buffer)
119
120 read_protocol.read_message_begin
121 read_protocol.read_struct_begin
122 name, type, id = read_protocol.read_field_begin
123 expect(type).to eq(Thrift::Types::I32)
124 expect(id).to eq(1)
125 value = read_protocol.read_i32
126 expect(value).to eq(42)
127 end
128
129 it "should write and read all primitive types" do
130 @protocol.write_message_begin("test", Thrift::MessageTypes::CALL, 1)
131 @protocol.write_struct_begin("TestStruct")
132
133 @protocol.write_field_begin("bool", Thrift::Types::BOOL, 1)
134 @protocol.write_bool(true)
135 @protocol.write_field_end
136
137 @protocol.write_field_begin("byte", Thrift::Types::BYTE, 2)
138 @protocol.write_byte(127)
139 @protocol.write_field_end
140
141 @protocol.write_field_begin("i16", Thrift::Types::I16, 3)
142 @protocol.write_i16(32767)
143 @protocol.write_field_end
144
145 @protocol.write_field_begin("i32", Thrift::Types::I32, 4)
146 @protocol.write_i32(2147483647)
147 @protocol.write_field_end
148
149 @protocol.write_field_begin("i64", Thrift::Types::I64, 5)
150 @protocol.write_i64(9223372036854775807)
151 @protocol.write_field_end
152
153 @protocol.write_field_begin("double", Thrift::Types::DOUBLE, 6)
154 @protocol.write_double(3.14159)
155 @protocol.write_field_end
156
157 @protocol.write_field_begin("string", Thrift::Types::STRING, 7)
158 @protocol.write_string("hello")
159 @protocol.write_field_end
160
161 @protocol.write_field_stop
162 @protocol.write_struct_end
163 @protocol.write_message_end
164 @protocol.trans.flush
165
166 data = @buffer.read(@buffer.available)
167 read_buffer = Thrift::MemoryBufferTransport.new(data)
168 read_protocol = Thrift::HeaderProtocol.new(read_buffer)
169
170 read_protocol.read_message_begin
171 read_protocol.read_struct_begin
172
173 # bool
174 _, type, _ = read_protocol.read_field_begin
175 expect(type).to eq(Thrift::Types::BOOL)
176 expect(read_protocol.read_bool).to eq(true)
177 read_protocol.read_field_end
178
179 # byte
180 _, type, _ = read_protocol.read_field_begin
181 expect(type).to eq(Thrift::Types::BYTE)
182 expect(read_protocol.read_byte).to eq(127)
183 read_protocol.read_field_end
184
185 # i16
186 _, type, _ = read_protocol.read_field_begin
187 expect(type).to eq(Thrift::Types::I16)
188 expect(read_protocol.read_i16).to eq(32767)
189 read_protocol.read_field_end
190
191 # i32
192 _, type, _ = read_protocol.read_field_begin
193 expect(type).to eq(Thrift::Types::I32)
194 expect(read_protocol.read_i32).to eq(2147483647)
195 read_protocol.read_field_end
196
197 # i64
198 _, type, _ = read_protocol.read_field_begin
199 expect(type).to eq(Thrift::Types::I64)
200 expect(read_protocol.read_i64).to eq(9223372036854775807)
201 read_protocol.read_field_end
202
203 # double
204 _, type, _ = read_protocol.read_field_begin
205 expect(type).to eq(Thrift::Types::DOUBLE)
206 expect(read_protocol.read_double).to be_within(0.00001).of(3.14159)
207 read_protocol.read_field_end
208
209 # string
210 _, type, _ = read_protocol.read_field_begin
211 expect(type).to eq(Thrift::Types::STRING)
212 expect(read_protocol.read_string).to eq("hello")
213 read_protocol.read_field_end
214 end
215 end
216
217 describe "protocol delegation with Compact protocol" do
218 before(:each) do
219 @buffer = Thrift::MemoryBufferTransport.new
220 @protocol = Thrift::HeaderProtocol.new(
221 @buffer,
222 nil,
223 Thrift::HeaderSubprotocolID::COMPACT
224 )
225 end
226
227 it "should use Compact protocol" do
228 expect(@protocol.to_s).to match(/header\(compact/)
229 end
230
231 it "should write and read with Compact protocol" do
232 @protocol.write_message_begin("test", Thrift::MessageTypes::CALL, 1)
233 @protocol.write_struct_begin("Test")
234 @protocol.write_field_begin("field", Thrift::Types::I32, 1)
235 @protocol.write_i32(999)
236 @protocol.write_field_end
237 @protocol.write_field_stop
238 @protocol.write_struct_end
239 @protocol.write_message_end
240 @protocol.trans.flush
241
242 data = @buffer.read(@buffer.available)
243 read_buffer = Thrift::MemoryBufferTransport.new(data)
244 read_protocol = Thrift::HeaderProtocol.new(read_buffer, nil, Thrift::HeaderSubprotocolID::COMPACT)
245
246 read_protocol.read_message_begin
247 read_protocol.read_struct_begin
248 _, type, _ = read_protocol.read_field_begin
249 expect(type).to eq(Thrift::Types::I32)
250 expect(read_protocol.read_i32).to eq(999)
251 end
252 end
253
254 describe "unknown protocol handling" do
255 it "should write an exception response on unknown protocol id" do
256 header_data = +""
257 header_data << varint32(0x10)
258 header_data << varint32(0)
259 frame = build_header_frame(header_data)
260
261 buffer = Thrift::MemoryBufferTransport.new(frame)
262 protocol = Thrift::HeaderProtocol.new(buffer)
263
264 expect { protocol.read_message_begin }.to raise_error(Thrift::ProtocolException)
265
266 response = buffer.read(buffer.available)
267 expect(response.bytesize).to be > 0
268 magic = response[4, 2].unpack('n').first
269 expect(magic).to eq(Thrift::HeaderTransport::HEADER_MAGIC)
270 end
271 end
272
273 describe "protocol auto-detection with legacy frames" do
274 it "should detect framed compact messages" do
275 write_buffer = Thrift::MemoryBufferTransport.new
276 write_protocol = Thrift::CompactProtocol.new(write_buffer)
277
278 write_protocol.write_message_begin("legacy_framed", Thrift::MessageTypes::CALL, 7)
279 write_protocol.write_struct_begin("Args")
280 write_protocol.write_field_stop
281 write_protocol.write_struct_end
282 write_protocol.write_message_end
283
284 payload = write_buffer.read(write_buffer.available)
285 framed = [payload.bytesize].pack('N') + payload
286
287 read_buffer = Thrift::MemoryBufferTransport.new(framed)
288 protocol = Thrift::HeaderProtocol.new(read_buffer)
289
290 name, type, seqid = protocol.read_message_begin
291 expect(name).to eq("legacy_framed")
292 expect(type).to eq(Thrift::MessageTypes::CALL)
293 expect(seqid).to eq(7)
294
295 protocol.read_struct_begin
296 _, field_type, _ = protocol.read_field_begin
297 expect(field_type).to eq(Thrift::Types::STOP)
298 protocol.read_struct_end
299 protocol.read_message_end
300 end
301
302 it "should detect unframed compact messages" do
303 write_buffer = Thrift::MemoryBufferTransport.new
304 write_protocol = Thrift::CompactProtocol.new(write_buffer)
305
306 write_protocol.write_message_begin("legacy_unframed", Thrift::MessageTypes::CALL, 9)
307 write_protocol.write_struct_begin("Args")
308 write_protocol.write_field_stop
309 write_protocol.write_struct_end
310 write_protocol.write_message_end
311
312 payload = write_buffer.read(write_buffer.available)
313
314 read_buffer = Thrift::MemoryBufferTransport.new(payload)
315 protocol = Thrift::HeaderProtocol.new(read_buffer)
316
317 name, type, seqid = protocol.read_message_begin
318 expect(name).to eq("legacy_unframed")
319 expect(type).to eq(Thrift::MessageTypes::CALL)
320 expect(seqid).to eq(9)
321
322 protocol.read_struct_begin
323 _, field_type, _ = protocol.read_field_begin
324 expect(field_type).to eq(Thrift::Types::STOP)
325 protocol.read_struct_end
326 protocol.read_message_end
327 end
328 end
329
330 describe "with compression" do
331 it "should work with ZLIB transform" do
332 @protocol.add_transform(Thrift::HeaderTransformID::ZLIB)
333
334 @protocol.write_message_begin("compressed_test", Thrift::MessageTypes::CALL, 42)
335 @protocol.write_struct_begin("Args")
336 @protocol.write_field_begin("data", Thrift::Types::STRING, 1)
337 @protocol.write_string("a" * 100) # Compressible data
338 @protocol.write_field_end
339 @protocol.write_field_stop
340 @protocol.write_struct_end
341 @protocol.write_message_end
342 @protocol.trans.flush
343
344 data = @buffer.read(@buffer.available)
345 read_buffer = Thrift::MemoryBufferTransport.new(data)
346 read_protocol = Thrift::HeaderProtocol.new(read_buffer)
347
348 name, type, seqid = read_protocol.read_message_begin
349 expect(name).to eq("compressed_test")
350 expect(seqid).to eq(42)
351
352 read_protocol.read_struct_begin
353 _, _, _ = read_protocol.read_field_begin
354 result = read_protocol.read_string
355 expect(result).to eq("a" * 100)
356 end
357 end
358
359 describe "containers" do
360 it "should write and read lists" do
361 @protocol.write_message_begin("test", Thrift::MessageTypes::CALL, 1)
362 @protocol.write_struct_begin("Test")
363 @protocol.write_field_begin("list", Thrift::Types::LIST, 1)
364 @protocol.write_list_begin(Thrift::Types::I32, 3)
365 @protocol.write_i32(1)
366 @protocol.write_i32(2)
367 @protocol.write_i32(3)
368 @protocol.write_list_end
369 @protocol.write_field_end
370 @protocol.write_field_stop
371 @protocol.write_struct_end
372 @protocol.write_message_end
373 @protocol.trans.flush
374
375 data = @buffer.read(@buffer.available)
376 read_buffer = Thrift::MemoryBufferTransport.new(data)
377 read_protocol = Thrift::HeaderProtocol.new(read_buffer)
378
379 read_protocol.read_message_begin
380 read_protocol.read_struct_begin
381 _, _, _ = read_protocol.read_field_begin
382 etype, size = read_protocol.read_list_begin
383 expect(etype).to eq(Thrift::Types::I32)
384 expect(size).to eq(3)
385 expect(read_protocol.read_i32).to eq(1)
386 expect(read_protocol.read_i32).to eq(2)
387 expect(read_protocol.read_i32).to eq(3)
388 end
389
390 it "should write and read maps" do
391 @protocol.write_message_begin("test", Thrift::MessageTypes::CALL, 1)
392 @protocol.write_struct_begin("Test")
393 @protocol.write_field_begin("map", Thrift::Types::MAP, 1)
394 @protocol.write_map_begin(Thrift::Types::STRING, Thrift::Types::I32, 2)
395 @protocol.write_string("a")
396 @protocol.write_i32(1)
397 @protocol.write_string("b")
398 @protocol.write_i32(2)
399 @protocol.write_map_end
400 @protocol.write_field_end
401 @protocol.write_field_stop
402 @protocol.write_struct_end
403 @protocol.write_message_end
404 @protocol.trans.flush
405
406 data = @buffer.read(@buffer.available)
407 read_buffer = Thrift::MemoryBufferTransport.new(data)
408 read_protocol = Thrift::HeaderProtocol.new(read_buffer)
409
410 read_protocol.read_message_begin
411 read_protocol.read_struct_begin
412 _, _, _ = read_protocol.read_field_begin
413 ktype, vtype, size = read_protocol.read_map_begin
414 expect(ktype).to eq(Thrift::Types::STRING)
415 expect(vtype).to eq(Thrift::Types::I32)
416 expect(size).to eq(2)
417 end
418
419 it "should write and read sets" do
420 @protocol.write_message_begin("test", Thrift::MessageTypes::CALL, 1)
421 @protocol.write_struct_begin("Test")
422 @protocol.write_field_begin("set", Thrift::Types::SET, 1)
423 @protocol.write_set_begin(Thrift::Types::STRING, 2)
424 @protocol.write_string("x")
425 @protocol.write_string("y")
426 @protocol.write_set_end
427 @protocol.write_field_end
428 @protocol.write_field_stop
429 @protocol.write_struct_end
430 @protocol.write_message_end
431 @protocol.trans.flush
432
433 data = @buffer.read(@buffer.available)
434 read_buffer = Thrift::MemoryBufferTransport.new(data)
435 read_protocol = Thrift::HeaderProtocol.new(read_buffer)
436
437 read_protocol.read_message_begin
438 read_protocol.read_struct_begin
439 _, _, _ = read_protocol.read_field_begin
440 etype, size = read_protocol.read_set_begin
441 expect(etype).to eq(Thrift::Types::STRING)
442 expect(size).to eq(2)
443 end
444 end
445 end
446
447 describe Thrift::HeaderProtocolFactory do
448 it "should create HeaderProtocol" do
449 factory = Thrift::HeaderProtocolFactory.new
450 buffer = Thrift::MemoryBufferTransport.new
451 protocol = factory.get_protocol(buffer)
452 expect(protocol).to be_a(Thrift::HeaderProtocol)
453 end
454
455 it "should provide a reasonable to_s" do
456 expect(Thrift::HeaderProtocolFactory.new.to_s).to eq("header")
457 end
458
459 it "should pass configuration to protocol" do
460 factory = Thrift::HeaderProtocolFactory.new(nil, Thrift::HeaderSubprotocolID::COMPACT)
461 buffer = Thrift::MemoryBufferTransport.new
462 protocol = factory.get_protocol(buffer)
463 expect(protocol.to_s).to match(/compact/)
464 end
465 end
466end