blob: 9edac1dd3c06e374ce0d3847830b97ac388933ad [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
Dmytro Shteflyuk9ffc3372026-03-11 21:02:28 -0400105 it "should propagate seqid to the outer header frame" do
106 @protocol.write_message_begin("test_method", Thrift::MessageTypes::CALL, 123)
107 @protocol.write_message_end
108 @protocol.trans.flush
109
110 data = @buffer.read(@buffer.available)
111 expect(data[8, 4].unpack('N').first).to eq(123)
112 end
113
Dmytro Shteflyuk67bfb292026-01-28 11:23:50 -0500114 it "should write and read structs" do
115 @protocol.write_message_begin("test", Thrift::MessageTypes::CALL, 1)
116 @protocol.write_struct_begin("TestStruct")
117 @protocol.write_field_begin("field1", Thrift::Types::I32, 1)
118 @protocol.write_i32(42)
119 @protocol.write_field_end
120 @protocol.write_field_stop
121 @protocol.write_struct_end
122 @protocol.write_message_end
123 @protocol.trans.flush
124
125 data = @buffer.read(@buffer.available)
126 read_buffer = Thrift::MemoryBufferTransport.new(data)
127 read_protocol = Thrift::HeaderProtocol.new(read_buffer)
128
129 read_protocol.read_message_begin
130 read_protocol.read_struct_begin
131 name, type, id = read_protocol.read_field_begin
132 expect(type).to eq(Thrift::Types::I32)
133 expect(id).to eq(1)
134 value = read_protocol.read_i32
135 expect(value).to eq(42)
136 end
137
138 it "should write and read all primitive types" do
139 @protocol.write_message_begin("test", Thrift::MessageTypes::CALL, 1)
140 @protocol.write_struct_begin("TestStruct")
141
142 @protocol.write_field_begin("bool", Thrift::Types::BOOL, 1)
143 @protocol.write_bool(true)
144 @protocol.write_field_end
145
146 @protocol.write_field_begin("byte", Thrift::Types::BYTE, 2)
147 @protocol.write_byte(127)
148 @protocol.write_field_end
149
150 @protocol.write_field_begin("i16", Thrift::Types::I16, 3)
151 @protocol.write_i16(32767)
152 @protocol.write_field_end
153
154 @protocol.write_field_begin("i32", Thrift::Types::I32, 4)
155 @protocol.write_i32(2147483647)
156 @protocol.write_field_end
157
158 @protocol.write_field_begin("i64", Thrift::Types::I64, 5)
159 @protocol.write_i64(9223372036854775807)
160 @protocol.write_field_end
161
162 @protocol.write_field_begin("double", Thrift::Types::DOUBLE, 6)
163 @protocol.write_double(3.14159)
164 @protocol.write_field_end
165
166 @protocol.write_field_begin("string", Thrift::Types::STRING, 7)
167 @protocol.write_string("hello")
168 @protocol.write_field_end
169
170 @protocol.write_field_stop
171 @protocol.write_struct_end
172 @protocol.write_message_end
173 @protocol.trans.flush
174
175 data = @buffer.read(@buffer.available)
176 read_buffer = Thrift::MemoryBufferTransport.new(data)
177 read_protocol = Thrift::HeaderProtocol.new(read_buffer)
178
179 read_protocol.read_message_begin
180 read_protocol.read_struct_begin
181
182 # bool
183 _, type, _ = read_protocol.read_field_begin
184 expect(type).to eq(Thrift::Types::BOOL)
185 expect(read_protocol.read_bool).to eq(true)
186 read_protocol.read_field_end
187
188 # byte
189 _, type, _ = read_protocol.read_field_begin
190 expect(type).to eq(Thrift::Types::BYTE)
191 expect(read_protocol.read_byte).to eq(127)
192 read_protocol.read_field_end
193
194 # i16
195 _, type, _ = read_protocol.read_field_begin
196 expect(type).to eq(Thrift::Types::I16)
197 expect(read_protocol.read_i16).to eq(32767)
198 read_protocol.read_field_end
199
200 # i32
201 _, type, _ = read_protocol.read_field_begin
202 expect(type).to eq(Thrift::Types::I32)
203 expect(read_protocol.read_i32).to eq(2147483647)
204 read_protocol.read_field_end
205
206 # i64
207 _, type, _ = read_protocol.read_field_begin
208 expect(type).to eq(Thrift::Types::I64)
209 expect(read_protocol.read_i64).to eq(9223372036854775807)
210 read_protocol.read_field_end
211
212 # double
213 _, type, _ = read_protocol.read_field_begin
214 expect(type).to eq(Thrift::Types::DOUBLE)
215 expect(read_protocol.read_double).to be_within(0.00001).of(3.14159)
216 read_protocol.read_field_end
217
218 # string
219 _, type, _ = read_protocol.read_field_begin
220 expect(type).to eq(Thrift::Types::STRING)
221 expect(read_protocol.read_string).to eq("hello")
222 read_protocol.read_field_end
223 end
224 end
225
226 describe "protocol delegation with Compact protocol" do
227 before(:each) do
228 @buffer = Thrift::MemoryBufferTransport.new
229 @protocol = Thrift::HeaderProtocol.new(
230 @buffer,
231 nil,
232 Thrift::HeaderSubprotocolID::COMPACT
233 )
234 end
235
236 it "should use Compact protocol" do
237 expect(@protocol.to_s).to match(/header\(compact/)
238 end
239
240 it "should write and read with Compact protocol" do
241 @protocol.write_message_begin("test", Thrift::MessageTypes::CALL, 1)
242 @protocol.write_struct_begin("Test")
243 @protocol.write_field_begin("field", Thrift::Types::I32, 1)
244 @protocol.write_i32(999)
245 @protocol.write_field_end
246 @protocol.write_field_stop
247 @protocol.write_struct_end
248 @protocol.write_message_end
249 @protocol.trans.flush
250
251 data = @buffer.read(@buffer.available)
252 read_buffer = Thrift::MemoryBufferTransport.new(data)
253 read_protocol = Thrift::HeaderProtocol.new(read_buffer, nil, Thrift::HeaderSubprotocolID::COMPACT)
254
255 read_protocol.read_message_begin
256 read_protocol.read_struct_begin
257 _, type, _ = read_protocol.read_field_begin
258 expect(type).to eq(Thrift::Types::I32)
259 expect(read_protocol.read_i32).to eq(999)
260 end
261 end
262
263 describe "unknown protocol handling" do
264 it "should write an exception response on unknown protocol id" do
265 header_data = +""
266 header_data << varint32(0x10)
267 header_data << varint32(0)
268 frame = build_header_frame(header_data)
269
270 buffer = Thrift::MemoryBufferTransport.new(frame)
271 protocol = Thrift::HeaderProtocol.new(buffer)
272
273 expect { protocol.read_message_begin }.to raise_error(Thrift::ProtocolException)
274
275 response = buffer.read(buffer.available)
276 expect(response.bytesize).to be > 0
277 magic = response[4, 2].unpack('n').first
278 expect(magic).to eq(Thrift::HeaderTransport::HEADER_MAGIC)
279 end
280 end
281
282 describe "protocol auto-detection with legacy frames" do
283 it "should detect framed compact messages" do
284 write_buffer = Thrift::MemoryBufferTransport.new
285 write_protocol = Thrift::CompactProtocol.new(write_buffer)
286
287 write_protocol.write_message_begin("legacy_framed", Thrift::MessageTypes::CALL, 7)
288 write_protocol.write_struct_begin("Args")
289 write_protocol.write_field_stop
290 write_protocol.write_struct_end
291 write_protocol.write_message_end
292
293 payload = write_buffer.read(write_buffer.available)
294 framed = [payload.bytesize].pack('N') + payload
295
296 read_buffer = Thrift::MemoryBufferTransport.new(framed)
297 protocol = Thrift::HeaderProtocol.new(read_buffer)
298
299 name, type, seqid = protocol.read_message_begin
300 expect(name).to eq("legacy_framed")
301 expect(type).to eq(Thrift::MessageTypes::CALL)
302 expect(seqid).to eq(7)
303
304 protocol.read_struct_begin
305 _, field_type, _ = protocol.read_field_begin
306 expect(field_type).to eq(Thrift::Types::STOP)
307 protocol.read_struct_end
308 protocol.read_message_end
309 end
310
311 it "should detect unframed compact messages" do
312 write_buffer = Thrift::MemoryBufferTransport.new
313 write_protocol = Thrift::CompactProtocol.new(write_buffer)
314
315 write_protocol.write_message_begin("legacy_unframed", Thrift::MessageTypes::CALL, 9)
316 write_protocol.write_struct_begin("Args")
317 write_protocol.write_field_stop
318 write_protocol.write_struct_end
319 write_protocol.write_message_end
320
321 payload = write_buffer.read(write_buffer.available)
322
323 read_buffer = Thrift::MemoryBufferTransport.new(payload)
324 protocol = Thrift::HeaderProtocol.new(read_buffer)
325
326 name, type, seqid = protocol.read_message_begin
327 expect(name).to eq("legacy_unframed")
328 expect(type).to eq(Thrift::MessageTypes::CALL)
329 expect(seqid).to eq(9)
330
331 protocol.read_struct_begin
332 _, field_type, _ = protocol.read_field_begin
333 expect(field_type).to eq(Thrift::Types::STOP)
334 protocol.read_struct_end
335 protocol.read_message_end
336 end
337 end
338
339 describe "with compression" do
340 it "should work with ZLIB transform" do
341 @protocol.add_transform(Thrift::HeaderTransformID::ZLIB)
342
343 @protocol.write_message_begin("compressed_test", Thrift::MessageTypes::CALL, 42)
344 @protocol.write_struct_begin("Args")
345 @protocol.write_field_begin("data", Thrift::Types::STRING, 1)
346 @protocol.write_string("a" * 100) # Compressible data
347 @protocol.write_field_end
348 @protocol.write_field_stop
349 @protocol.write_struct_end
350 @protocol.write_message_end
351 @protocol.trans.flush
352
353 data = @buffer.read(@buffer.available)
354 read_buffer = Thrift::MemoryBufferTransport.new(data)
355 read_protocol = Thrift::HeaderProtocol.new(read_buffer)
356
357 name, type, seqid = read_protocol.read_message_begin
358 expect(name).to eq("compressed_test")
359 expect(seqid).to eq(42)
360
361 read_protocol.read_struct_begin
362 _, _, _ = read_protocol.read_field_begin
363 result = read_protocol.read_string
364 expect(result).to eq("a" * 100)
365 end
366 end
367
368 describe "containers" do
369 it "should write and read lists" do
370 @protocol.write_message_begin("test", Thrift::MessageTypes::CALL, 1)
371 @protocol.write_struct_begin("Test")
372 @protocol.write_field_begin("list", Thrift::Types::LIST, 1)
373 @protocol.write_list_begin(Thrift::Types::I32, 3)
374 @protocol.write_i32(1)
375 @protocol.write_i32(2)
376 @protocol.write_i32(3)
377 @protocol.write_list_end
378 @protocol.write_field_end
379 @protocol.write_field_stop
380 @protocol.write_struct_end
381 @protocol.write_message_end
382 @protocol.trans.flush
383
384 data = @buffer.read(@buffer.available)
385 read_buffer = Thrift::MemoryBufferTransport.new(data)
386 read_protocol = Thrift::HeaderProtocol.new(read_buffer)
387
388 read_protocol.read_message_begin
389 read_protocol.read_struct_begin
390 _, _, _ = read_protocol.read_field_begin
391 etype, size = read_protocol.read_list_begin
392 expect(etype).to eq(Thrift::Types::I32)
393 expect(size).to eq(3)
394 expect(read_protocol.read_i32).to eq(1)
395 expect(read_protocol.read_i32).to eq(2)
396 expect(read_protocol.read_i32).to eq(3)
397 end
398
399 it "should write and read maps" do
400 @protocol.write_message_begin("test", Thrift::MessageTypes::CALL, 1)
401 @protocol.write_struct_begin("Test")
402 @protocol.write_field_begin("map", Thrift::Types::MAP, 1)
403 @protocol.write_map_begin(Thrift::Types::STRING, Thrift::Types::I32, 2)
404 @protocol.write_string("a")
405 @protocol.write_i32(1)
406 @protocol.write_string("b")
407 @protocol.write_i32(2)
408 @protocol.write_map_end
409 @protocol.write_field_end
410 @protocol.write_field_stop
411 @protocol.write_struct_end
412 @protocol.write_message_end
413 @protocol.trans.flush
414
415 data = @buffer.read(@buffer.available)
416 read_buffer = Thrift::MemoryBufferTransport.new(data)
417 read_protocol = Thrift::HeaderProtocol.new(read_buffer)
418
419 read_protocol.read_message_begin
420 read_protocol.read_struct_begin
421 _, _, _ = read_protocol.read_field_begin
422 ktype, vtype, size = read_protocol.read_map_begin
423 expect(ktype).to eq(Thrift::Types::STRING)
424 expect(vtype).to eq(Thrift::Types::I32)
425 expect(size).to eq(2)
426 end
427
428 it "should write and read sets" do
429 @protocol.write_message_begin("test", Thrift::MessageTypes::CALL, 1)
430 @protocol.write_struct_begin("Test")
431 @protocol.write_field_begin("set", Thrift::Types::SET, 1)
432 @protocol.write_set_begin(Thrift::Types::STRING, 2)
433 @protocol.write_string("x")
434 @protocol.write_string("y")
435 @protocol.write_set_end
436 @protocol.write_field_end
437 @protocol.write_field_stop
438 @protocol.write_struct_end
439 @protocol.write_message_end
440 @protocol.trans.flush
441
442 data = @buffer.read(@buffer.available)
443 read_buffer = Thrift::MemoryBufferTransport.new(data)
444 read_protocol = Thrift::HeaderProtocol.new(read_buffer)
445
446 read_protocol.read_message_begin
447 read_protocol.read_struct_begin
448 _, _, _ = read_protocol.read_field_begin
449 etype, size = read_protocol.read_set_begin
450 expect(etype).to eq(Thrift::Types::STRING)
451 expect(size).to eq(2)
452 end
453 end
454 end
455
456 describe Thrift::HeaderProtocolFactory do
457 it "should create HeaderProtocol" do
458 factory = Thrift::HeaderProtocolFactory.new
459 buffer = Thrift::MemoryBufferTransport.new
460 protocol = factory.get_protocol(buffer)
461 expect(protocol).to be_a(Thrift::HeaderProtocol)
462 end
463
464 it "should provide a reasonable to_s" do
465 expect(Thrift::HeaderProtocolFactory.new.to_s).to eq("header")
466 end
467
468 it "should pass configuration to protocol" do
469 factory = Thrift::HeaderProtocolFactory.new(nil, Thrift::HeaderSubprotocolID::COMPACT)
470 buffer = Thrift::MemoryBufferTransport.new
471 protocol = factory.get_protocol(buffer)
472 expect(protocol.to_s).to match(/compact/)
473 end
474 end
475end