1use crate::api::produce::RecordBatchHeader;
2use crate::codec::{DecodeLimits, Decoder, Encoder};
3use crate::error::{Error, Result};
4use crate::header::RequestHeader;
5use crate::record_batch::{decompress_record_batch_records_with_limit, RecordBatchCompression};
6
7pub const API_KEY: i16 = 1;
8
9#[derive(Debug, Clone, PartialEq, Eq)]
10pub struct FetchRequestV2 {
11 pub correlation_id: i32,
12 pub client_id: Option<String>,
13 pub replica_id: i32,
14 pub max_wait_ms: i32,
15 pub min_bytes: i32,
16 pub topics: Vec<FetchTopicV2>,
17}
18
19#[derive(Debug, Clone, PartialEq, Eq)]
20pub struct FetchRequestV4 {
21 pub correlation_id: i32,
22 pub client_id: Option<String>,
23 pub replica_id: i32,
24 pub max_wait_ms: i32,
25 pub min_bytes: i32,
26 pub max_bytes: i32,
27 pub isolation_level: i8,
28 pub topics: Vec<FetchTopicV2>,
29}
30
31#[derive(Debug, Clone, PartialEq, Eq)]
36pub struct FetchRequestV11 {
37 pub correlation_id: i32,
38 pub client_id: Option<String>,
39 pub replica_id: i32,
40 pub max_wait_ms: i32,
41 pub min_bytes: i32,
42 pub max_bytes: i32,
43 pub isolation_level: i8,
44 pub session_id: i32,
45 pub session_epoch: i32,
46 pub topics: Vec<FetchTopicV11>,
47 pub forgotten_topics: Vec<FetchForgottenTopicV11>,
48 pub rack_id: String,
49}
50
51#[derive(Debug, Clone, PartialEq, Eq)]
53pub struct FetchRequestV12 {
54 pub correlation_id: i32,
55 pub client_id: Option<String>,
56 pub replica_id: i32,
57 pub max_wait_ms: i32,
58 pub min_bytes: i32,
59 pub max_bytes: i32,
60 pub isolation_level: i8,
61 pub session_id: i32,
62 pub session_epoch: i32,
63 pub topics: Vec<FetchTopicV12>,
64 pub forgotten_topics: Vec<FetchForgottenTopicV12>,
65 pub rack_id: String,
66}
67
68impl FetchRequestV12 {
69 pub fn encode(&self) -> Result<Vec<u8>> {
70 let mut encoder = Encoder::new();
71 RequestHeader {
72 api_key: API_KEY,
73 api_version: 12,
74 correlation_id: self.correlation_id,
75 client_id: self.client_id.clone(),
76 }
77 .encode_v2(&mut encoder)?;
78 encoder.write_i32(self.replica_id);
79 encoder.write_i32(self.max_wait_ms);
80 encoder.write_i32(self.min_bytes);
81 encoder.write_i32(self.max_bytes);
82 encoder.write_i8(self.isolation_level);
83 encoder.write_i32(self.session_id);
84 encoder.write_i32(self.session_epoch);
85 encoder.write_compact_array(Some(self.topics.as_slice()), |encoder, topic| {
86 topic.encode(encoder)
87 })?;
88 encoder.write_compact_array(Some(self.forgotten_topics.as_slice()), |encoder, topic| {
89 topic.encode(encoder)
90 })?;
91 encoder.write_compact_string(&self.rack_id)?;
92 encoder.write_empty_tagged_fields();
93 Ok(encoder.into_bytes())
94 }
95}
96
97#[derive(Debug, Clone, PartialEq, Eq)]
98pub struct FetchTopicV12 {
99 pub name: String,
100 pub partitions: Vec<FetchPartitionV12>,
101}
102
103impl FetchTopicV12 {
104 fn encode(&self, encoder: &mut Encoder) -> Result<()> {
105 encoder.write_compact_string(&self.name)?;
106 encoder.write_compact_array(Some(self.partitions.as_slice()), |encoder, partition| {
107 partition.encode(encoder)
108 })?;
109 encoder.write_empty_tagged_fields();
110 Ok(())
111 }
112}
113
114#[derive(Debug, Clone, PartialEq, Eq)]
115pub struct FetchPartitionV12 {
116 pub partition_index: i32,
117 pub current_leader_epoch: i32,
118 pub fetch_offset: i64,
119 pub last_fetched_epoch: i32,
120 pub log_start_offset: i64,
121 pub max_bytes: i32,
122}
123
124impl FetchPartitionV12 {
125 fn encode(&self, encoder: &mut Encoder) -> Result<()> {
126 encoder.write_i32(self.partition_index);
127 encoder.write_i32(self.current_leader_epoch);
128 encoder.write_i64(self.fetch_offset);
129 encoder.write_i32(self.last_fetched_epoch);
130 encoder.write_i64(self.log_start_offset);
131 encoder.write_i32(self.max_bytes);
132 encoder.write_empty_tagged_fields();
133 Ok(())
134 }
135}
136
137#[derive(Debug, Clone, PartialEq, Eq)]
138pub struct FetchForgottenTopicV12 {
139 pub name: String,
140 pub partitions: Vec<i32>,
141}
142
143impl FetchForgottenTopicV12 {
144 fn encode(&self, encoder: &mut Encoder) -> Result<()> {
145 encoder.write_compact_string(&self.name)?;
146 encoder.write_compact_array(Some(self.partitions.as_slice()), |encoder, partition| {
147 encoder.write_i32(*partition);
148 Ok(())
149 })?;
150 encoder.write_empty_tagged_fields();
151 Ok(())
152 }
153}
154
155impl FetchRequestV11 {
156 pub fn encode(&self) -> Result<Vec<u8>> {
157 let mut encoder = Encoder::new();
158 RequestHeader {
159 api_key: API_KEY,
160 api_version: 11,
161 correlation_id: self.correlation_id,
162 client_id: self.client_id.clone(),
163 }
164 .encode_v1(&mut encoder)?;
165 encoder.write_i32(self.replica_id);
166 encoder.write_i32(self.max_wait_ms);
167 encoder.write_i32(self.min_bytes);
168 encoder.write_i32(self.max_bytes);
169 encoder.write_i8(self.isolation_level);
170 encoder.write_i32(self.session_id);
171 encoder.write_i32(self.session_epoch);
172 encoder.write_array(Some(self.topics.as_slice()), |encoder, topic| {
173 topic.encode(encoder)
174 })?;
175 encoder.write_array(Some(self.forgotten_topics.as_slice()), |encoder, topic| {
176 topic.encode(encoder)
177 })?;
178 encoder.write_string(&self.rack_id)?;
179 Ok(encoder.into_bytes())
180 }
181}
182
183#[derive(Debug, Clone, PartialEq, Eq)]
184pub struct FetchTopicV11 {
185 pub name: String,
186 pub partitions: Vec<FetchPartitionV11>,
187}
188
189impl FetchTopicV11 {
190 fn encode(&self, encoder: &mut Encoder) -> Result<()> {
191 encoder.write_string(&self.name)?;
192 encoder.write_array(Some(self.partitions.as_slice()), |encoder, partition| {
193 partition.encode(encoder)
194 })
195 }
196}
197
198#[derive(Debug, Clone, PartialEq, Eq)]
199pub struct FetchPartitionV11 {
200 pub partition_index: i32,
201 pub current_leader_epoch: i32,
202 pub fetch_offset: i64,
203 pub log_start_offset: i64,
204 pub max_bytes: i32,
205}
206
207impl FetchPartitionV11 {
208 fn encode(&self, encoder: &mut Encoder) -> Result<()> {
209 encoder.write_i32(self.partition_index);
210 encoder.write_i32(self.current_leader_epoch);
211 encoder.write_i64(self.fetch_offset);
212 encoder.write_i64(self.log_start_offset);
213 encoder.write_i32(self.max_bytes);
214 Ok(())
215 }
216}
217
218#[derive(Debug, Clone, PartialEq, Eq)]
219pub struct FetchForgottenTopicV11 {
220 pub name: String,
221 pub partitions: Vec<i32>,
222}
223
224impl FetchForgottenTopicV11 {
225 fn encode(&self, encoder: &mut Encoder) -> Result<()> {
226 encoder.write_string(&self.name)?;
227 encoder.write_array(Some(self.partitions.as_slice()), |encoder, partition| {
228 encoder.write_i32(*partition);
229 Ok(())
230 })
231 }
232}
233
234impl FetchRequestV4 {
235 pub fn encode(&self) -> Result<Vec<u8>> {
236 let mut encoder = Encoder::new();
237 RequestHeader {
238 api_key: API_KEY,
239 api_version: 4,
240 correlation_id: self.correlation_id,
241 client_id: self.client_id.clone(),
242 }
243 .encode_v1(&mut encoder)?;
244 encoder.write_i32(self.replica_id);
245 encoder.write_i32(self.max_wait_ms);
246 encoder.write_i32(self.min_bytes);
247 encoder.write_i32(self.max_bytes);
248 encoder.write_i8(self.isolation_level);
249 encoder.write_array(Some(self.topics.as_slice()), |encoder, topic| {
250 topic.encode(encoder)
251 })?;
252 Ok(encoder.into_bytes())
253 }
254}
255
256impl FetchRequestV2 {
257 pub fn encode(&self) -> Result<Vec<u8>> {
258 let mut encoder = Encoder::new();
259 RequestHeader {
260 api_key: API_KEY,
261 api_version: 2,
262 correlation_id: self.correlation_id,
263 client_id: self.client_id.clone(),
264 }
265 .encode_v1(&mut encoder)?;
266 encoder.write_i32(self.replica_id);
267 encoder.write_i32(self.max_wait_ms);
268 encoder.write_i32(self.min_bytes);
269 encoder.write_array(Some(self.topics.as_slice()), |encoder, topic| {
270 topic.encode(encoder)
271 })?;
272 Ok(encoder.into_bytes())
273 }
274}
275
276#[derive(Debug, Clone, PartialEq, Eq)]
277pub struct FetchTopicV2 {
278 pub name: String,
279 pub partitions: Vec<FetchPartitionV2>,
280}
281
282impl FetchTopicV2 {
283 fn encode(&self, encoder: &mut Encoder) -> Result<()> {
284 encoder.write_string(&self.name)?;
285 encoder.write_array(Some(self.partitions.as_slice()), |encoder, partition| {
286 partition.encode(encoder)
287 })
288 }
289}
290
291#[derive(Debug, Clone, PartialEq, Eq)]
292pub struct FetchPartitionV2 {
293 pub partition_index: i32,
294 pub fetch_offset: i64,
295 pub max_bytes: i32,
296}
297
298impl FetchPartitionV2 {
299 fn encode(&self, encoder: &mut Encoder) -> Result<()> {
300 encoder.write_i32(self.partition_index);
301 encoder.write_i64(self.fetch_offset);
302 encoder.write_i32(self.max_bytes);
303 Ok(())
304 }
305}
306
307#[derive(Debug, Clone, PartialEq, Eq)]
308pub struct FetchResponseV2 {
309 pub throttle_time_ms: i32,
310 pub responses: Vec<FetchTopicResponseV2>,
311}
312
313#[derive(Debug, Clone, PartialEq, Eq)]
314pub struct FetchResponseV4 {
315 pub throttle_time_ms: i32,
316 pub responses: Vec<FetchTopicResponseV4>,
317}
318
319#[derive(Debug, Clone, PartialEq, Eq)]
321pub struct FetchResponseV11 {
322 pub throttle_time_ms: i32,
323 pub error_code: i16,
324 pub session_id: i32,
325 pub responses: Vec<FetchTopicResponseV11>,
326}
327
328#[derive(Debug, Clone, PartialEq, Eq)]
330pub struct FetchResponseV12 {
331 pub throttle_time_ms: i32,
332 pub error_code: i16,
333 pub session_id: i32,
334 pub responses: Vec<FetchTopicResponseV12>,
335}
336
337impl FetchResponseV12 {
338 pub fn decode_body(decoder: &mut Decoder<'_>) -> Result<Self> {
339 Ok(Self {
340 throttle_time_ms: decoder.read_i32()?,
341 error_code: decoder.read_i16()?,
342 session_id: decoder.read_i32()?,
343 responses: decoder
344 .read_compact_array("fetch responses", FetchTopicResponseV12::decode)?
345 .unwrap_or_default(),
346 })
347 .and_then(|response| {
348 decoder.read_tagged_fields()?;
349 Ok(response)
350 })
351 }
352}
353
354#[derive(Debug, Clone, PartialEq, Eq)]
355pub struct FetchTopicResponseV12 {
356 pub name: String,
357 pub partitions: Vec<FetchPartitionResponseV12>,
358}
359
360impl FetchTopicResponseV12 {
361 fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
362 let name = decoder.read_compact_string()?;
363 let partitions = decoder
364 .read_compact_array(
365 "fetch partition responses",
366 FetchPartitionResponseV12::decode,
367 )?
368 .unwrap_or_default();
369 decoder.read_tagged_fields()?;
370 Ok(Self { name, partitions })
371 }
372}
373
374#[derive(Debug, Clone, PartialEq, Eq)]
375pub struct FetchPartitionResponseV12 {
376 pub partition_index: i32,
377 pub error_code: i16,
378 pub high_watermark: i64,
379 pub last_stable_offset: i64,
380 pub log_start_offset: i64,
381 pub aborted_transactions: Vec<AbortedTransactionV12>,
382 pub preferred_read_replica: i32,
383 pub records: Vec<MessageSetRecord>,
384}
385
386impl FetchPartitionResponseV12 {
387 fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
388 let limits = decoder.limits();
389 let partition_index = decoder.read_i32()?;
390 let error_code = decoder.read_i16()?;
391 let high_watermark = decoder.read_i64()?;
392 let last_stable_offset = decoder.read_i64()?;
393 let log_start_offset = decoder.read_i64()?;
394 let aborted_transactions = decoder
395 .read_compact_array("aborted transactions", AbortedTransactionV12::decode)?
396 .unwrap_or_default();
397 let preferred_read_replica = decoder.read_i32()?;
398 let records = decoder
399 .read_compact_nullable_bytes()?
400 .map(|bytes| decode_message_set(&bytes, limits))
401 .transpose()?
402 .unwrap_or_default();
403 decoder.read_tagged_fields()?;
404 Ok(Self {
405 partition_index,
406 error_code,
407 high_watermark,
408 last_stable_offset,
409 log_start_offset,
410 aborted_transactions,
411 preferred_read_replica,
412 records,
413 })
414 }
415}
416
417#[derive(Debug, Clone, PartialEq, Eq)]
418pub struct AbortedTransactionV12 {
419 pub producer_id: i64,
420 pub first_offset: i64,
421}
422
423impl AbortedTransactionV12 {
424 fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
425 let producer_id = decoder.read_i64()?;
426 let first_offset = decoder.read_i64()?;
427 decoder.read_tagged_fields()?;
428 Ok(Self {
429 producer_id,
430 first_offset,
431 })
432 }
433}
434
435impl FetchResponseV11 {
436 pub fn decode_body(decoder: &mut Decoder<'_>) -> Result<Self> {
437 Ok(Self {
438 throttle_time_ms: decoder.read_i32()?,
439 error_code: decoder.read_i16()?,
440 session_id: decoder.read_i32()?,
441 responses: decoder
442 .read_array("fetch responses", FetchTopicResponseV11::decode)?
443 .unwrap_or_default(),
444 })
445 }
446}
447
448#[derive(Debug, Clone, PartialEq, Eq)]
449pub struct FetchTopicResponseV11 {
450 pub name: String,
451 pub partitions: Vec<FetchPartitionResponseV11>,
452}
453
454impl FetchTopicResponseV11 {
455 fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
456 Ok(Self {
457 name: decoder.read_string()?,
458 partitions: decoder
459 .read_array(
460 "fetch partition responses",
461 FetchPartitionResponseV11::decode,
462 )?
463 .unwrap_or_default(),
464 })
465 }
466}
467
468#[derive(Debug, Clone, PartialEq, Eq)]
469pub struct FetchPartitionResponseV11 {
470 pub partition_index: i32,
471 pub error_code: i16,
472 pub high_watermark: i64,
473 pub last_stable_offset: i64,
474 pub log_start_offset: i64,
475 pub aborted_transactions: Vec<AbortedTransactionV4>,
476 pub preferred_read_replica: i32,
477 pub records: Vec<MessageSetRecord>,
478}
479
480impl FetchPartitionResponseV11 {
481 fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
482 let limits = decoder.limits();
483 Ok(Self {
484 partition_index: decoder.read_i32()?,
485 error_code: decoder.read_i16()?,
486 high_watermark: decoder.read_i64()?,
487 last_stable_offset: decoder.read_i64()?,
488 log_start_offset: decoder.read_i64()?,
489 aborted_transactions: decoder
490 .read_array("aborted transactions", AbortedTransactionV4::decode)?
491 .unwrap_or_default(),
492 preferred_read_replica: decoder.read_i32()?,
493 records: decode_message_set(&decoder.read_bytes()?, limits)?,
494 })
495 }
496}
497
498impl FetchResponseV4 {
499 pub fn decode_body(decoder: &mut Decoder<'_>) -> Result<Self> {
500 Ok(Self {
501 throttle_time_ms: decoder.read_i32()?,
502 responses: decoder
503 .read_array("fetch responses", FetchTopicResponseV4::decode)?
504 .unwrap_or_default(),
505 })
506 }
507}
508
509#[derive(Debug, Clone, PartialEq, Eq)]
510pub struct FetchTopicResponseV4 {
511 pub name: String,
512 pub partitions: Vec<FetchPartitionResponseV4>,
513}
514
515impl FetchTopicResponseV4 {
516 fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
517 Ok(Self {
518 name: decoder.read_string()?,
519 partitions: decoder
520 .read_array(
521 "fetch partition responses",
522 FetchPartitionResponseV4::decode,
523 )?
524 .unwrap_or_default(),
525 })
526 }
527}
528
529#[derive(Debug, Clone, PartialEq, Eq)]
530pub struct FetchPartitionResponseV4 {
531 pub partition_index: i32,
532 pub error_code: i16,
533 pub high_watermark: i64,
534 pub last_stable_offset: i64,
535 pub aborted_transactions: Vec<AbortedTransactionV4>,
536 pub records: Vec<MessageSetRecord>,
537}
538
539impl FetchPartitionResponseV4 {
540 fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
541 let limits = decoder.limits();
542 Ok(Self {
543 partition_index: decoder.read_i32()?,
544 error_code: decoder.read_i16()?,
545 high_watermark: decoder.read_i64()?,
546 last_stable_offset: decoder.read_i64()?,
547 aborted_transactions: decoder
548 .read_array("aborted transactions", AbortedTransactionV4::decode)?
549 .unwrap_or_default(),
550 records: decode_message_set(&decoder.read_bytes()?, limits)?,
551 })
552 }
553}
554
555#[derive(Debug, Clone, PartialEq, Eq)]
556pub struct AbortedTransactionV4 {
557 pub producer_id: i64,
558 pub first_offset: i64,
559}
560
561impl AbortedTransactionV4 {
562 fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
563 Ok(Self {
564 producer_id: decoder.read_i64()?,
565 first_offset: decoder.read_i64()?,
566 })
567 }
568}
569
570impl FetchResponseV2 {
571 pub fn decode_body(decoder: &mut Decoder<'_>) -> Result<Self> {
572 Ok(Self {
573 throttle_time_ms: decoder.read_i32()?,
574 responses: decoder
575 .read_array("fetch responses", FetchTopicResponseV2::decode)?
576 .unwrap_or_default(),
577 })
578 }
579}
580
581#[derive(Debug, Clone, PartialEq, Eq)]
582pub struct FetchTopicResponseV2 {
583 pub name: String,
584 pub partitions: Vec<FetchPartitionResponseV2>,
585}
586
587impl FetchTopicResponseV2 {
588 fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
589 Ok(Self {
590 name: decoder.read_string()?,
591 partitions: decoder
592 .read_array(
593 "fetch partition responses",
594 FetchPartitionResponseV2::decode,
595 )?
596 .unwrap_or_default(),
597 })
598 }
599}
600
601#[derive(Debug, Clone, PartialEq, Eq)]
602pub struct FetchPartitionResponseV2 {
603 pub partition_index: i32,
604 pub error_code: i16,
605 pub high_watermark: i64,
606 pub records: Vec<MessageSetRecord>,
607}
608
609impl FetchPartitionResponseV2 {
610 fn decode(decoder: &mut Decoder<'_>) -> Result<Self> {
611 let limits = decoder.limits();
612 Ok(Self {
613 partition_index: decoder.read_i32()?,
614 error_code: decoder.read_i16()?,
615 high_watermark: decoder.read_i64()?,
616 records: decode_message_set(&decoder.read_bytes()?, limits)?,
617 })
618 }
619}
620
621#[derive(Debug, Clone, PartialEq, Eq)]
622pub struct MessageSetRecord {
623 pub offset: i64,
624 pub leader_epoch: i32,
627 pub timestamp_ms: i64,
628 pub key: Option<Vec<u8>>,
629 pub value: Option<Vec<u8>>,
630 pub headers: Vec<RecordBatchHeader>,
631 pub producer_id: Option<i64>,
632 pub transactional: bool,
633 pub control: bool,
634}
635
636fn decode_message_set(bytes: &[u8], limits: DecodeLimits) -> Result<Vec<MessageSetRecord>> {
637 let mut decoder = Decoder::with_limits(bytes, limits);
638 let mut records = Vec::new();
639
640 while decoder.remaining() >= 12 {
641 let offset = decoder.read_i64()?;
642 let message_size = decoder.read_i32()?;
643 if message_size < 0 {
644 return Err(Error::NegativeLength {
645 kind: "message",
646 length: message_size,
647 });
648 }
649 let message_size =
650 usize::try_from(message_size).map_err(|_| Error::LengthOverflow("message"))?;
651 if decoder.remaining() < message_size {
653 break;
654 }
655 let message = decoder.read_exact(message_size)?;
656 let decoded = decode_message_or_batch(offset, message, limits)?;
657 let total = records
658 .len()
659 .checked_add(decoded.len())
660 .ok_or(Error::LengthOverflow("fetch records"))?;
661 decoder.ensure_collection_length("fetch records", total)?;
662 records.extend(decoded);
663 }
664
665 Ok(records)
666}
667
668fn decode_message_or_batch(
669 offset: i64,
670 bytes: &[u8],
671 limits: DecodeLimits,
672) -> Result<Vec<MessageSetRecord>> {
673 match bytes.get(4).copied() {
674 Some(2) => decode_record_batch(offset, bytes, limits),
675 _ => Ok(vec![decode_message(offset, bytes, limits)?]),
676 }
677}
678
679fn decode_message(offset: i64, bytes: &[u8], limits: DecodeLimits) -> Result<MessageSetRecord> {
680 let mut decoder = Decoder::with_limits(bytes, limits);
681 let _crc = decoder.read_i32()?;
682 let magic = decoder.read_i8()?;
683 let _attributes = decoder.read_i8()?;
684 let timestamp_ms = match magic {
685 0 => -1,
686 1 => decoder.read_i64()?,
687 _ => {
688 return Err(Error::UnsupportedVersion {
689 kind: "message magic",
690 version: i16::from(magic),
691 })
692 }
693 };
694 let key = decoder.read_nullable_bytes()?;
695 let value = decoder.read_nullable_bytes()?;
696
697 Ok(MessageSetRecord {
698 offset,
699 leader_epoch: -1,
700 timestamp_ms,
701 key,
702 value,
703 headers: Vec::new(),
704 producer_id: None,
705 transactional: false,
706 control: false,
707 })
708}
709
710fn decode_record_batch(
711 base_offset: i64,
712 bytes: &[u8],
713 limits: DecodeLimits,
714) -> Result<Vec<MessageSetRecord>> {
715 let mut decoder = Decoder::with_limits(bytes, limits);
716 let partition_leader_epoch = decoder.read_i32()?;
717 let magic = decoder.read_i8()?;
718 if magic != 2 {
719 return Err(Error::UnsupportedVersion {
720 kind: "record batch magic",
721 version: i16::from(magic),
722 });
723 }
724 let _crc = decoder.read_i32()?;
725 let attributes = decoder.read_i16()?;
726 let compression = RecordBatchCompression::from_attributes(attributes)?;
727 let _last_offset_delta = decoder.read_i32()?;
728 let base_timestamp = decoder.read_i64()?;
729 let _max_timestamp = decoder.read_i64()?;
730 let producer_id = decoder.read_i64()?;
731 let _producer_epoch = decoder.read_i16()?;
732 let _base_sequence = decoder.read_i32()?;
733 let record_count = decoder.read_i32()?;
734 if record_count < 0 {
735 return Err(Error::NegativeLength {
736 kind: "record batch records",
737 length: record_count,
738 });
739 }
740
741 let record_count =
742 usize::try_from(record_count).map_err(|_| Error::LengthOverflow("record batch records"))?;
743 decoder.ensure_collection_length("record batch records", record_count)?;
744 let record_bytes = if compression.is_compressed() {
745 let compressed = decoder.read_exact(decoder.remaining())?;
746 decompress_record_batch_records_with_limit(
747 compression,
748 compressed,
749 limits.max_decompressed_record_bytes(),
750 )?
751 } else {
752 if decoder.remaining() > limits.max_decompressed_record_bytes() {
753 return Err(Error::LimitExceeded {
754 kind: "decompressed record batch bytes",
755 actual: decoder.remaining(),
756 max: limits.max_decompressed_record_bytes(),
757 });
758 }
759 decoder.read_exact(decoder.remaining())?.to_vec()
760 };
761 let mut record_decoder = Decoder::with_limits(&record_bytes, limits);
762 let mut records = Vec::with_capacity(record_count);
763 for _ in 0..record_count {
764 let record_length = record_decoder.read_varint()?;
765 if record_length < 0 {
766 return Err(Error::NegativeLength {
767 kind: "record",
768 length: record_length,
769 });
770 }
771 let record_length =
772 usize::try_from(record_length).map_err(|_| Error::LengthOverflow("record"))?;
773 let record_bytes = record_decoder.read_exact(record_length)?;
774 records.push(decode_record(
775 base_offset,
776 partition_leader_epoch,
777 base_timestamp,
778 producer_id,
779 attributes,
780 record_bytes,
781 limits,
782 )?);
783 }
784
785 Ok(records)
786}
787
788fn decode_record(
789 base_offset: i64,
790 partition_leader_epoch: i32,
791 base_timestamp: i64,
792 producer_id: i64,
793 batch_attributes: i16,
794 bytes: &[u8],
795 limits: DecodeLimits,
796) -> Result<MessageSetRecord> {
797 let mut decoder = Decoder::with_limits(bytes, limits);
798 let _attributes = decoder.read_i8()?;
799 let timestamp_delta = decoder.read_varlong()?;
800 let offset_delta = decoder.read_varint()?;
801 let key = decoder.read_varint_nullable_bytes()?;
802 let value = decoder.read_varint_nullable_bytes()?;
803 let header_count = decoder.read_varint()?;
804 if header_count < 0 {
805 return Err(Error::NegativeLength {
806 kind: "record headers",
807 length: header_count,
808 });
809 }
810 let header_count =
811 usize::try_from(header_count).map_err(|_| Error::LengthOverflow("record headers"))?;
812 decoder.ensure_collection_length("record headers", header_count)?;
813 let mut headers = Vec::with_capacity(header_count);
814 for _ in 0..header_count {
815 let header_key =
816 String::from_utf8(decoder.read_varint_bytes()?).map_err(|_| Error::InvalidUtf8)?;
817 let header_value = decoder.read_varint_nullable_bytes()?;
818 headers.push(RecordBatchHeader::new(header_key, header_value));
819 }
820
821 Ok(MessageSetRecord {
822 offset: base_offset.saturating_add(i64::from(offset_delta)),
823 leader_epoch: partition_leader_epoch,
824 timestamp_ms: base_timestamp.saturating_add(timestamp_delta),
825 key,
826 value,
827 headers,
828 producer_id: (producer_id >= 0).then_some(producer_id),
829 transactional: batch_attributes & 0x10 != 0,
830 control: batch_attributes & 0x20 != 0,
831 })
832}
833
834#[cfg(test)]
835#[allow(clippy::unwrap_used)]
836mod tests {
837 use super::{
838 FetchPartitionV11, FetchPartitionV12, FetchPartitionV2, FetchRequestV11, FetchRequestV12,
839 FetchRequestV2, FetchRequestV4, FetchResponseV11, FetchResponseV12, FetchResponseV2,
840 FetchResponseV4, FetchTopicV11, FetchTopicV12, FetchTopicV2, MessageSetRecord,
841 RecordBatchHeader,
842 };
843 use crate::codec::{Decoder, Encoder};
844
845 #[test]
846 fn encodes_fetch_request_v2() {
847 let request = FetchRequestV2 {
848 correlation_id: 7,
849 client_id: Some("kafrust".to_owned()),
850 replica_id: -1,
851 max_wait_ms: 500,
852 min_bytes: 1,
853 topics: vec![FetchTopicV2 {
854 name: "orders".to_owned(),
855 partitions: vec![FetchPartitionV2 {
856 partition_index: 0,
857 fetch_offset: 42,
858 max_bytes: 1_048_576,
859 }],
860 }],
861 };
862
863 let bytes = request.encode().unwrap();
864 assert_eq!(&bytes[0..4], &[0, 1, 0, 2]);
865 assert!(bytes.len() > 40);
866 }
867
868 #[test]
869 fn encodes_fetch_request_v4() {
870 let request = FetchRequestV4 {
871 correlation_id: 8,
872 client_id: Some("kafrust".to_owned()),
873 replica_id: -1,
874 max_wait_ms: 500,
875 min_bytes: 1,
876 max_bytes: 1_048_576,
877 isolation_level: 0,
878 topics: vec![FetchTopicV2 {
879 name: "orders".to_owned(),
880 partitions: vec![FetchPartitionV2 {
881 partition_index: 0,
882 fetch_offset: 42,
883 max_bytes: 1_048_576,
884 }],
885 }],
886 };
887
888 let bytes = request.encode().unwrap();
889 assert_eq!(&bytes[0..4], &[0, 1, 0, 4]);
890 assert_eq!(&bytes[4..8], &[0, 0, 0, 8]);
891 assert!(bytes.len() > 45);
892 }
893
894 #[test]
895 fn encodes_fetch_request_v11_with_rack_and_fetch_session_fields() {
896 let request = FetchRequestV11 {
897 correlation_id: 9,
898 client_id: Some("kafrust".to_owned()),
899 replica_id: -1,
900 max_wait_ms: 500,
901 min_bytes: 1,
902 max_bytes: 1_048_576,
903 isolation_level: 1,
904 session_id: 0,
905 session_epoch: 0,
906 topics: vec![FetchTopicV11 {
907 name: "orders".to_owned(),
908 partitions: vec![FetchPartitionV11 {
909 partition_index: 0,
910 current_leader_epoch: -1,
911 fetch_offset: 42,
912 log_start_offset: -1,
913 max_bytes: 1_048_576,
914 }],
915 }],
916 forgotten_topics: Vec::new(),
917 rack_id: "rack-a".to_owned(),
918 };
919
920 let bytes = request.encode().unwrap();
921 let mut decoder = Decoder::new(&bytes);
922 assert_eq!(decoder.read_i16().unwrap(), 1);
923 assert_eq!(decoder.read_i16().unwrap(), 11);
924 assert_eq!(decoder.read_i32().unwrap(), 9);
925 assert_eq!(
926 decoder.read_nullable_string().unwrap().as_deref(),
927 Some("kafrust")
928 );
929 assert_eq!(decoder.read_i32().unwrap(), -1);
930 assert_eq!(decoder.read_i32().unwrap(), 500);
931 assert_eq!(decoder.read_i32().unwrap(), 1);
932 assert_eq!(decoder.read_i32().unwrap(), 1_048_576);
933 assert_eq!(decoder.read_i8().unwrap(), 1);
934 assert_eq!(decoder.read_i32().unwrap(), 0);
935 assert_eq!(decoder.read_i32().unwrap(), 0);
936 assert_eq!(decoder.read_i32().unwrap(), 1);
937 assert_eq!(decoder.read_string().unwrap(), "orders");
938 assert_eq!(decoder.read_i32().unwrap(), 1);
939 assert_eq!(decoder.read_i32().unwrap(), 0);
940 assert_eq!(decoder.read_i32().unwrap(), -1);
941 assert_eq!(decoder.read_i64().unwrap(), 42);
942 assert_eq!(decoder.read_i64().unwrap(), -1);
943 assert_eq!(decoder.read_i32().unwrap(), 1_048_576);
944 assert_eq!(decoder.read_i32().unwrap(), 0);
945 assert_eq!(decoder.read_string().unwrap(), "rack-a");
946 assert!(decoder.is_empty());
947 }
948
949 #[test]
950 fn decodes_fetch_response_v11_with_preferred_read_replica() {
951 let mut bytes = Encoder::new();
952 bytes.write_i32(3);
953 bytes.write_i16(0);
954 bytes.write_i32(17);
955 bytes.write_i32(1);
956 bytes.write_string("orders").unwrap();
957 bytes.write_i32(1);
958 bytes.write_i32(0);
959 bytes.write_i16(0);
960 bytes.write_i64(43);
961 bytes.write_i64(42);
962 bytes.write_i64(40);
963 bytes.write_i32(0);
964 bytes.write_i32(2);
965 bytes.write_bytes(&[]).unwrap();
966
967 let bytes = bytes.into_bytes();
968 let mut decoder = Decoder::new(&bytes);
969 let response = FetchResponseV11::decode_body(&mut decoder).unwrap();
970 let partition = &response.responses[0].partitions[0];
971
972 assert_eq!(response.throttle_time_ms, 3);
973 assert_eq!(response.session_id, 17);
974 assert_eq!(partition.log_start_offset, 40);
975 assert_eq!(partition.preferred_read_replica, 2);
976 assert!(partition.records.is_empty());
977 assert!(decoder.is_empty());
978 }
979
980 #[test]
981 fn encodes_fetch_request_v12_with_flexible_rack_fields() {
982 let request = FetchRequestV12 {
983 correlation_id: 10,
984 client_id: Some("kafrust".to_owned()),
985 replica_id: -1,
986 max_wait_ms: 500,
987 min_bytes: 1,
988 max_bytes: 1_048_576,
989 isolation_level: 1,
990 session_id: 0,
991 session_epoch: 0,
992 topics: vec![FetchTopicV12 {
993 name: "orders".to_owned(),
994 partitions: vec![FetchPartitionV12 {
995 partition_index: 0,
996 current_leader_epoch: -1,
997 fetch_offset: 42,
998 last_fetched_epoch: -1,
999 log_start_offset: -1,
1000 max_bytes: 1_048_576,
1001 }],
1002 }],
1003 forgotten_topics: Vec::new(),
1004 rack_id: "rack-a".to_owned(),
1005 };
1006
1007 let bytes = request.encode().unwrap();
1008 let mut decoder = Decoder::new(&bytes);
1009 assert_eq!(decoder.read_i16().unwrap(), 1);
1010 assert_eq!(decoder.read_i16().unwrap(), 12);
1011 assert_eq!(decoder.read_i32().unwrap(), 10);
1012 assert_eq!(
1013 decoder.read_nullable_string().unwrap().as_deref(),
1014 Some("kafrust")
1015 );
1016 assert_eq!(decoder.read_unsigned_varint().unwrap(), 0);
1017 assert_eq!(decoder.read_i32().unwrap(), -1);
1018 assert_eq!(decoder.read_i32().unwrap(), 500);
1019 assert_eq!(decoder.read_i32().unwrap(), 1);
1020 assert_eq!(decoder.read_i32().unwrap(), 1_048_576);
1021 assert_eq!(decoder.read_i8().unwrap(), 1);
1022 assert_eq!(decoder.read_i32().unwrap(), 0);
1023 assert_eq!(decoder.read_i32().unwrap(), 0);
1024 assert_eq!(decoder.read_unsigned_varint().unwrap(), 2);
1025 assert_eq!(decoder.read_compact_string().unwrap(), "orders");
1026 assert_eq!(decoder.read_unsigned_varint().unwrap(), 2);
1027 assert_eq!(decoder.read_i32().unwrap(), 0);
1028 assert_eq!(decoder.read_i32().unwrap(), -1);
1029 assert_eq!(decoder.read_i64().unwrap(), 42);
1030 assert_eq!(decoder.read_i32().unwrap(), -1);
1031 assert_eq!(decoder.read_i64().unwrap(), -1);
1032 assert_eq!(decoder.read_i32().unwrap(), 1_048_576);
1033 assert_eq!(decoder.read_unsigned_varint().unwrap(), 0);
1034 assert_eq!(decoder.read_unsigned_varint().unwrap(), 0);
1035 assert_eq!(decoder.read_unsigned_varint().unwrap(), 1);
1036 assert_eq!(decoder.read_compact_string().unwrap(), "rack-a");
1037 assert_eq!(decoder.read_unsigned_varint().unwrap(), 0);
1038 assert!(decoder.is_empty());
1039 }
1040
1041 #[test]
1042 fn decodes_fetch_response_v12_with_preferred_read_replica() {
1043 let mut bytes = Encoder::new();
1044 bytes.write_i32(3);
1045 bytes.write_i16(0);
1046 bytes.write_i32(17);
1047 bytes.write_unsigned_varint(2);
1048 bytes.write_compact_string("orders").unwrap();
1049 bytes.write_unsigned_varint(2);
1050 bytes.write_i32(0);
1051 bytes.write_i16(0);
1052 bytes.write_i64(43);
1053 bytes.write_i64(42);
1054 bytes.write_i64(40);
1055 bytes.write_unsigned_varint(2);
1056 bytes.write_i64(7);
1057 bytes.write_i64(40);
1058 bytes.write_unsigned_varint(0);
1059 bytes.write_i32(2);
1060 bytes.write_compact_nullable_bytes(Some(&[])).unwrap();
1061 bytes.write_unsigned_varint(0);
1062 bytes.write_unsigned_varint(0);
1063 bytes.write_unsigned_varint(0);
1064
1065 let bytes = bytes.into_bytes();
1066 let mut decoder = Decoder::new(&bytes);
1067 let response = FetchResponseV12::decode_body(&mut decoder).unwrap();
1068 let partition = &response.responses[0].partitions[0];
1069
1070 assert_eq!(response.throttle_time_ms, 3);
1071 assert_eq!(response.session_id, 17);
1072 assert_eq!(partition.log_start_offset, 40);
1073 assert_eq!(partition.preferred_read_replica, 2);
1074 assert_eq!(partition.aborted_transactions[0].producer_id, 7);
1075 assert!(partition.records.is_empty());
1076 assert!(decoder.is_empty());
1077 }
1078
1079 #[test]
1080 fn decodes_fetch_response_v4_with_aborted_transaction() {
1081 let mut bytes = Encoder::new();
1082 bytes.write_i32(0);
1083 bytes.write_i32(1);
1084 bytes.write_string("orders").unwrap();
1085 bytes.write_i32(1);
1086 bytes.write_i32(0);
1087 bytes.write_i16(0);
1088 bytes.write_i64(43);
1089 bytes.write_i64(42);
1090 bytes.write_i32(1);
1091 bytes.write_i64(7);
1092 bytes.write_i64(40);
1093 bytes.write_bytes(&[]).unwrap();
1094 let bytes = bytes.into_bytes();
1095
1096 let mut decoder = Decoder::new(&bytes);
1097 let response = FetchResponseV4::decode_body(&mut decoder).unwrap();
1098 let partition = &response.responses[0].partitions[0];
1099
1100 assert_eq!(partition.high_watermark, 43);
1101 assert_eq!(partition.last_stable_offset, 42);
1102 assert_eq!(partition.aborted_transactions[0].producer_id, 7);
1103 assert_eq!(partition.aborted_transactions[0].first_offset, 40);
1104 assert!(partition.records.is_empty());
1105 assert!(decoder.is_empty());
1106 }
1107
1108 #[test]
1109 fn decodes_fetch_response_v2_with_message_set() {
1110 let mut message = Encoder::new();
1111 message.write_i32(0);
1112 message.write_i8(1);
1113 message.write_i8(0);
1114 message.write_i64(123);
1115 message.write_nullable_bytes(Some(b"order-1")).unwrap();
1116 message.write_nullable_bytes(Some(b"created")).unwrap();
1117 let message = message.into_bytes();
1118
1119 let mut set = Encoder::new();
1120 set.write_i64(42);
1121 set.write_i32(i32::try_from(message.len()).unwrap());
1122 set.write_raw(&message);
1123 let set = set.into_bytes();
1124
1125 let mut bytes = Encoder::new();
1126 bytes.write_i32(0);
1127 bytes.write_i32(1);
1128 bytes.write_string("orders").unwrap();
1129 bytes.write_i32(1);
1130 bytes.write_i32(0);
1131 bytes.write_i16(0);
1132 bytes.write_i64(43);
1133 bytes.write_bytes(&set).unwrap();
1134 let bytes = bytes.into_bytes();
1135
1136 let mut decoder = Decoder::new(&bytes);
1137 let response = FetchResponseV2::decode_body(&mut decoder).unwrap();
1138 let record = MessageSetRecord {
1139 offset: 42,
1140 leader_epoch: -1,
1141 timestamp_ms: 123,
1142 key: Some(b"order-1".to_vec()),
1143 value: Some(b"created".to_vec()),
1144 headers: Vec::new(),
1145 producer_id: None,
1146 transactional: false,
1147 control: false,
1148 };
1149
1150 assert_eq!(response.throttle_time_ms, 0);
1151 assert_eq!(response.responses[0].partitions[0].high_watermark, 43);
1152 assert_eq!(response.responses[0].partitions[0].records, vec![record]);
1153 assert!(decoder.is_empty());
1154 }
1155
1156 #[test]
1157 fn decodes_fetch_response_v2_ignores_partial_trailing_message_set_entry() {
1158 let mut message = Encoder::new();
1159 message.write_i32(0);
1160 message.write_i8(1);
1161 message.write_i8(0);
1162 message.write_i64(123);
1163 message.write_nullable_bytes(Some(b"order-1")).unwrap();
1164 message.write_nullable_bytes(Some(b"created")).unwrap();
1165 let message = message.into_bytes();
1166
1167 let mut set = Encoder::new();
1168 set.write_i64(42);
1169 set.write_i32(i32::try_from(message.len()).unwrap());
1170 set.write_raw(&message);
1171 set.write_i64(-1);
1172 set.write_i32(61);
1173 set.write_raw(&[0; 22]);
1174 let set = set.into_bytes();
1175
1176 let mut bytes = Encoder::new();
1177 bytes.write_i32(0);
1178 bytes.write_i32(1);
1179 bytes.write_string("orders").unwrap();
1180 bytes.write_i32(1);
1181 bytes.write_i32(0);
1182 bytes.write_i16(0);
1183 bytes.write_i64(43);
1184 bytes.write_bytes(&set).unwrap();
1185 let bytes = bytes.into_bytes();
1186
1187 let mut decoder = Decoder::new(&bytes);
1188 let response = FetchResponseV2::decode_body(&mut decoder).unwrap();
1189
1190 assert_eq!(response.responses[0].partitions[0].records.len(), 1);
1191 assert_eq!(response.responses[0].partitions[0].records[0].offset, 42);
1192 assert!(decoder.is_empty());
1193 }
1194
1195 #[test]
1196 fn decodes_fetch_response_v2_with_record_batch() {
1197 let mut record = Vec::new();
1198 record.push(0);
1199 write_varlong(&mut record, 5);
1200 write_varint(&mut record, 0);
1201 write_varint(&mut record, 7);
1202 record.extend_from_slice(b"order-1");
1203 write_varint(&mut record, 7);
1204 record.extend_from_slice(b"created");
1205 write_varint(&mut record, 2);
1206 write_varint(&mut record, 6);
1207 record.extend_from_slice(b"source");
1208 write_varint(&mut record, 8);
1209 record.extend_from_slice(b"checkout");
1210 write_varint(&mut record, 9);
1211 record.extend_from_slice(b"tombstone");
1212 write_varint(&mut record, -1);
1213
1214 let mut batch = Encoder::new();
1215 batch.write_i32(0);
1216 batch.write_i8(2);
1217 batch.write_i32(0);
1218 batch.write_i16(0x10);
1219 batch.write_i32(0);
1220 batch.write_i64(1_000);
1221 batch.write_i64(1_005);
1222 batch.write_i64(7);
1223 batch.write_i16(-1);
1224 batch.write_i32(-1);
1225 batch.write_i32(1);
1226 let mut encoded_record = Vec::new();
1227 write_varint(&mut encoded_record, i32::try_from(record.len()).unwrap());
1228 encoded_record.extend_from_slice(&record);
1229 batch.write_raw(&encoded_record);
1230 let batch = batch.into_bytes();
1231
1232 let mut set = Encoder::new();
1233 set.write_i64(42);
1234 set.write_i32(i32::try_from(batch.len()).unwrap());
1235 set.write_raw(&batch);
1236 let set = set.into_bytes();
1237
1238 let mut bytes = Encoder::new();
1239 bytes.write_i32(0);
1240 bytes.write_i32(1);
1241 bytes.write_string("orders").unwrap();
1242 bytes.write_i32(1);
1243 bytes.write_i32(0);
1244 bytes.write_i16(0);
1245 bytes.write_i64(43);
1246 bytes.write_bytes(&set).unwrap();
1247 let bytes = bytes.into_bytes();
1248
1249 let mut decoder = Decoder::new(&bytes);
1250 let response = FetchResponseV2::decode_body(&mut decoder).unwrap();
1251 let record = MessageSetRecord {
1252 offset: 42,
1253 leader_epoch: 0,
1254 timestamp_ms: 1_005,
1255 key: Some(b"order-1".to_vec()),
1256 value: Some(b"created".to_vec()),
1257 headers: vec![
1258 RecordBatchHeader::new("source", Some(b"checkout".to_vec())),
1259 RecordBatchHeader::new("tombstone", None),
1260 ],
1261 producer_id: Some(7),
1262 transactional: true,
1263 control: false,
1264 };
1265
1266 assert_eq!(response.responses[0].partitions[0].records, vec![record]);
1267 assert!(decoder.is_empty());
1268 }
1269
1270 fn write_varint(output: &mut Vec<u8>, value: i32) {
1271 write_unsigned_varint(output, u64::from(((value << 1) ^ (value >> 31)) as u32));
1272 }
1273
1274 fn write_varlong(output: &mut Vec<u8>, value: i64) {
1275 write_unsigned_varint(output, ((value << 1) ^ (value >> 63)) as u64);
1276 }
1277
1278 fn write_unsigned_varint(output: &mut Vec<u8>, mut value: u64) {
1279 loop {
1280 let mut byte = (value & 0x7f) as u8;
1281 value >>= 7;
1282 if value != 0 {
1283 byte |= 0x80;
1284 }
1285 output.push(byte);
1286 if value == 0 {
1287 break;
1288 }
1289 }
1290 }
1291}