1#![forbid(unsafe_code)]
2
3use super::*;
17use crate::oson::{decode_oson_with_limits, encode_oson, OsonValue};
18use crate::wire::ProtocolLimits;
19
20#[derive(Clone, Debug, PartialEq, Eq)]
23pub enum AqPayloadKind {
24 Raw,
26 Json,
28 Object,
30}
31
32#[derive(Clone, Debug)]
34pub struct AqQueueDesc {
35 pub name: String,
36 pub kind: AqPayloadKind,
37 pub payload_toid: Vec<u8>,
40}
41
42impl AqQueueDesc {
43 pub fn new(name: String, kind: AqPayloadKind, object_oid: Option<Vec<u8>>) -> Self {
46 let payload_toid = match kind {
47 AqPayloadKind::Raw => raw_payload_toid(),
48 AqPayloadKind::Json => json_payload_toid(),
49 AqPayloadKind::Object => object_oid.unwrap_or_default(),
50 };
51 Self {
52 name,
53 kind,
54 payload_toid,
55 }
56 }
57}
58
59fn raw_payload_toid() -> Vec<u8> {
60 let mut toid = vec![0u8; 15];
61 toid.push(0x17);
62 toid
63}
64
65fn json_payload_toid() -> Vec<u8> {
66 let mut toid = vec![0u8; 15];
67 toid.push(0x47);
68 toid
69}
70
71#[derive(Clone, Debug)]
73pub enum AqPayloadValue {
74 Raw(Vec<u8>),
76 Json(OsonValue),
78 Object { oid: Vec<u8>, image: Vec<u8> },
80}
81
82#[derive(Clone, Debug)]
85pub struct AqMsgProps {
86 pub priority: i32,
87 pub delay: i32,
88 pub expiration: i32,
89 pub correlation: Option<String>,
90 pub exception_queue: Option<String>,
91 pub state: i32,
92 pub enq_txn_id: Option<Vec<u8>>,
93 pub recipients: Option<Vec<String>>,
95 pub payload: Option<AqPayloadValue>,
98}
99
100impl Default for AqMsgProps {
101 fn default() -> Self {
102 Self {
103 priority: 0,
104 delay: 0,
105 expiration: -1,
106 correlation: None,
107 exception_queue: None,
108 state: 0,
109 enq_txn_id: None,
110 recipients: Some(Vec::new()),
114 payload: None,
115 }
116 }
117}
118
119#[derive(Clone, Debug)]
122pub struct AqEnqOptions {
123 pub visibility: u32,
124 pub delivery_mode: u16,
125}
126
127impl Default for AqEnqOptions {
128 fn default() -> Self {
129 Self {
130 visibility: 2,
131 delivery_mode: TNS_AQ_MSG_PERSISTENT,
132 }
133 }
134}
135
136#[derive(Clone, Debug)]
140pub struct AqDeqOptions {
141 pub condition: Option<String>,
142 pub consumer_name: Option<String>,
143 pub correlation: Option<String>,
144 pub delivery_mode: u16,
145 pub mode: i32,
146 pub msgid: Option<Vec<u8>>,
147 pub navigation: i32,
148 pub visibility: i32,
149 pub wait: u32,
150}
151
152impl Default for AqDeqOptions {
153 fn default() -> Self {
154 Self {
155 condition: None,
156 consumer_name: None,
157 correlation: None,
158 delivery_mode: TNS_AQ_MSG_PERSISTENT,
159 mode: 3,
160 msgid: None,
161 navigation: 3,
162 visibility: 2,
163 wait: 0xFFFF_FFFF,
164 }
165 }
166}
167
168#[derive(Clone, Debug, Default)]
171pub struct AqDeqMessage {
172 pub priority: i32,
173 pub delay: i32,
174 pub expiration: i32,
175 pub correlation: Option<String>,
176 pub num_attempts: i32,
177 pub exception_queue: Option<String>,
178 pub state: i32,
179 pub enq_time: Option<QueryValue>,
181 pub delivery_mode: u16,
182 pub msgid: Option<Vec<u8>>,
183 pub payload: Option<AqDeqPayload>,
185}
186
187#[derive(Clone, Debug)]
189pub enum AqDeqPayload {
190 Raw(Vec<u8>),
192 Json(OsonValue),
194 Object(Vec<u8>),
197}
198
199fn write_aq_function_code(
207 writer: &mut TtcWriter,
208 function_code: u8,
209 seq_num: u8,
210 ttc_field_version: u8,
211) {
212 writer.write_function_header(function_code, seq_num, ttc_field_version);
213}
214
215fn write_value_with_length(writer: &mut TtcWriter, value: Option<&[u8]>) -> Result<()> {
216 match value {
217 None => {
218 writer.write_ub4(0);
219 Ok(())
220 }
221 Some(bytes) => writer.write_bytes_with_two_lengths(Some(bytes)),
222 }
223}
224
225fn write_msg_props(
227 writer: &mut TtcWriter,
228 props: &AqMsgProps,
229 ttc_field_version: u8,
230) -> Result<()> {
231 writer.write_ub4(props.priority as u32);
232 writer.write_ub4(props.delay as u32);
233 writer.write_sb4(props.expiration);
234 write_value_with_length(writer, props.correlation.as_deref().map(str::as_bytes))?;
235 writer.write_ub4(0); write_value_with_length(writer, props.exception_queue.as_deref().map(str::as_bytes))?;
237 writer.write_ub4(props.state as u32);
238 writer.write_ub4(0); write_value_with_length(writer, props.enq_txn_id.as_deref())?;
240 writer.write_ub4(4); writer.write_u8(0x0e); writer.write_keyword_value_pair(None, None, TNS_AQ_EXT_KEYWORD_AGENT_NAME)?;
243 writer.write_keyword_value_pair(None, None, TNS_AQ_EXT_KEYWORD_AGENT_ADDRESS)?;
244 writer.write_keyword_value_pair(None, Some(b"\x00"), TNS_AQ_EXT_KEYWORD_AGENT_PROTOCOL)?;
245 writer.write_keyword_value_pair(None, None, TNS_AQ_EXT_KEYWORD_ORIGINAL_MSGID)?;
246 writer.write_ub4(0); writer.write_ub4(0); writer.write_ub4(0); writer.write_ub4(0); if version_gates::carries_aq_shard_id(ttc_field_version) {
251 writer.write_ub4(0xFFFF_FFFF); }
253 Ok(())
254}
255
256fn write_recipients(writer: &mut TtcWriter, recipients: &[String]) -> Result<()> {
258 let mut index: u16 = 0;
259 for recipient in recipients {
260 writer.write_keyword_value_pair(Some(recipient.as_bytes()), None, index)?;
261 writer.write_keyword_value_pair(None, None, index + 1)?;
262 writer.write_keyword_value_pair(None, Some(b"\x00"), index + 2)?;
263 index += 3;
264 }
265 Ok(())
266}
267
268fn write_payload(
270 writer: &mut TtcWriter,
271 payload: &AqPayloadValue,
272 supports_oson_long_fnames: bool,
273) -> Result<()> {
274 match payload {
275 AqPayloadValue::Json(value) => {
276 let image = encode_oson(value, supports_oson_long_fnames)?;
279 crate::vector::write_oson_aq_payload(writer, &image)
280 }
281 AqPayloadValue::Object { oid, image } => write_dbobject_bind(writer, oid, image),
282 AqPayloadValue::Raw(bytes) => {
283 writer.write_raw(bytes);
284 Ok(())
285 }
286 }
287}
288
289pub fn build_aq_enq_payload(
296 queue: &AqQueueDesc,
297 props: &AqMsgProps,
298 enq_options: &AqEnqOptions,
299 seq_num: u8,
300 ttc_field_version: u8,
301 supports_oson_long_fnames: bool,
302) -> Result<Vec<u8>> {
303 let payload = props
304 .payload
305 .as_ref()
306 .ok_or(ProtocolError::TtcDecode("AQ enqueue has no payload"))?;
307 let queue_name = queue.name.as_bytes();
308 let mut writer = TtcWriter::new();
309 write_aq_function_code(&mut writer, TNS_FUNC_AQ_ENQ, seq_num, ttc_field_version);
310 writer.write_u8(1); writer.write_ub4(queue_name.len() as u32);
312 write_msg_props(&mut writer, props, ttc_field_version)?;
313 match props.recipients.as_ref() {
314 None => {
315 writer.write_u8(0); writer.write_ub4(0); }
318 Some(recipients) => {
319 writer.write_u8(1);
320 writer.write_ub4(3 * recipients.len() as u32);
321 }
322 }
323 writer.write_ub4(enq_options.visibility);
324 writer.write_u8(0); writer.write_ub4(0); writer.write_ub4(0); writer.write_u8(1); writer.write_ub4(16); writer.write_ub2(TNS_AQ_MESSAGE_VERSION);
330 match queue.kind {
331 AqPayloadKind::Json => {
332 writer.write_u8(0); writer.write_u8(0); writer.write_ub4(0); }
336 AqPayloadKind::Object => {
337 writer.write_u8(1); writer.write_u8(0); writer.write_ub4(0); }
341 AqPayloadKind::Raw => {
342 let raw_len = match payload {
343 AqPayloadValue::Raw(bytes) => bytes.len() as u32,
344 _ => return Err(ProtocolError::TtcDecode("RAW queue requires RAW payload")),
345 };
346 writer.write_u8(0); writer.write_u8(1); writer.write_ub4(raw_len);
349 }
350 }
351 writer.write_u8(1); writer.write_ub4(TNS_AQ_MESSAGE_ID_LENGTH as u32);
353 let mut enq_flags = 0u32;
354 if enq_options.delivery_mode == TNS_AQ_MSG_BUFFERED {
355 enq_flags |= TNS_KPD_AQ_BUFMSG;
356 }
357 writer.write_ub4(enq_flags); writer.write_u8(0); writer.write_ub4(0); writer.write_u8(0); writer.write_ub4(0); writer.write_u8(0); writer.write_ub4(0); writer.write_u8(0); writer.write_ub4(0); writer.write_u8(0); writer.write_u8(0); writer.write_ub4(0); writer.write_u8(0); writer.write_ub4(0); writer.write_u8(0); writer.write_ub4(0); writer.write_u8(0); writer.write_u8(0); if version_gates::writes_aq_json_payload(ttc_field_version) {
376 writer.write_u8(u8::from(queue.kind == AqPayloadKind::Json));
378 }
379
380 writer.write_bytes_with_length(queue_name)?;
381 if let Some(recipients) = props.recipients.as_ref() {
382 write_recipients(&mut writer, recipients)?;
383 }
384 writer.write_raw(&queue.payload_toid);
385 write_payload(&mut writer, payload, supports_oson_long_fnames)?;
386 Ok(writer.into_bytes())
387}
388
389pub fn parse_aq_enq_response(
392 payload: &[u8],
393 capabilities: ClientCapabilities,
394) -> Result<Option<Vec<u8>>> {
395 parse_aq_enq_response_with_limits(payload, capabilities, ProtocolLimits::DEFAULT)
396}
397
398pub fn parse_aq_enq_response_with_limits(
399 payload: &[u8],
400 capabilities: ClientCapabilities,
401 limits: ProtocolLimits,
402) -> Result<Option<Vec<u8>>> {
403 let mut reader = TtcReader::with_limits(payload, limits)?;
404 let mut msgid: Option<Vec<u8>> = None;
405 while reader.remaining() > 0 {
406 let message_type = reader.read_u8()?;
407 match message_type {
408 0 => {}
409 TNS_MSG_TYPE_PARAMETER => {
410 let id = reader.read_raw(TNS_AQ_MESSAGE_ID_LENGTH)?.to_vec();
411 let _ext_len = reader.read_ub2()?;
412 msgid = Some(id);
413 }
414 TNS_MSG_TYPE_STATUS => {
415 let _call_status = reader.read_ub4()?;
416 let _seq = reader.read_ub2()?;
417 }
418 TNS_MSG_TYPE_SERVER_SIDE_PIGGYBACK => {
419 let _ = skip_server_side_piggyback(&mut reader)?;
420 }
421 TNS_MSG_TYPE_END_OF_RESPONSE => break,
422 TNS_MSG_TYPE_ERROR => {
423 let info = parse_server_error_info(&mut reader, capabilities.ttc_field_version)?;
424 if info.number != 0 {
425 return Err(ProtocolError::ServerErrorInfo(Box::new(
426 info.into_details(),
427 )));
428 }
429 }
430 _ => {
431 return Err(ProtocolError::UnknownMessageType {
432 message_type,
433 position: reader.position().saturating_sub(1),
434 })
435 }
436 }
437 }
438 Ok(msgid)
439}
440
441pub fn build_aq_deq_payload(
448 queue: &AqQueueDesc,
449 deq_options: &AqDeqOptions,
450 seq_num: u8,
451 ttc_field_version: u8,
452) -> Result<Vec<u8>> {
453 let queue_name = queue.name.as_bytes();
454 let mut writer = TtcWriter::new();
455 write_aq_function_code(&mut writer, TNS_FUNC_AQ_DEQ, seq_num, ttc_field_version);
456 writer.write_u8(1); writer.write_ub4(queue_name.len() as u32);
458 writer.write_u8(1); writer.write_u8(1); writer.write_u8(1); writer.write_u8(1); let consumer_name = deq_options
463 .consumer_name
464 .as_ref()
465 .filter(|name| !name.is_empty());
466 match consumer_name {
467 Some(name) => {
468 writer.write_u8(1);
469 writer.write_ub4(name.len() as u32);
470 }
471 None => {
472 writer.write_u8(0);
473 writer.write_ub4(0);
474 }
475 }
476 writer.write_sb4(deq_options.mode);
477 writer.write_sb4(deq_options.navigation);
478 writer.write_sb4(deq_options.visibility);
479 writer.write_sb4(deq_options.wait as i32);
480 let msgid = deq_options.msgid.as_ref().filter(|id| !id.is_empty());
481 match msgid {
482 Some(_) => {
483 writer.write_u8(1);
484 writer.write_ub4(TNS_AQ_MESSAGE_ID_LENGTH as u32);
485 }
486 None => {
487 writer.write_u8(0);
488 writer.write_ub4(0);
489 }
490 }
491 let correlation = deq_options.correlation.as_ref().filter(|c| !c.is_empty());
492 match correlation {
493 Some(c) => {
494 writer.write_u8(1);
495 writer.write_ub4(c.len() as u32);
496 }
497 None => {
498 writer.write_u8(0);
499 writer.write_ub4(0);
500 }
501 }
502 writer.write_u8(1); writer.write_ub4(16); writer.write_ub2(TNS_AQ_MESSAGE_VERSION);
505 writer.write_u8(1); writer.write_u8(1); writer.write_ub4(TNS_AQ_MESSAGE_ID_LENGTH as u32);
508 let mut deq_flags = 0u32;
509 match deq_options.delivery_mode {
510 TNS_AQ_MSG_BUFFERED => deq_flags |= TNS_KPD_AQ_BUFMSG,
511 TNS_AQ_MSG_PERSISTENT_OR_BUFFERED => deq_flags |= TNS_KPD_AQ_EITHER,
512 _ => {}
513 }
514 writer.write_ub4(deq_flags);
515 let condition = deq_options.condition.as_ref().filter(|c| !c.is_empty());
516 match condition {
517 Some(c) => {
518 writer.write_u8(1);
519 writer.write_ub4(c.len() as u32);
520 }
521 None => {
522 writer.write_u8(0);
523 writer.write_ub4(0);
524 }
525 }
526 writer.write_u8(0); writer.write_ub4(0); if version_gates::writes_aq_json_payload(ttc_field_version) {
529 writer.write_u8(0); }
531 if version_gates::carries_aq_shard_id(ttc_field_version) {
532 writer.write_ub4(0xFFFF_FFFF); }
534
535 writer.write_bytes_with_length(queue_name)?;
536 if let Some(name) = consumer_name {
537 writer.write_bytes_with_length(name.as_bytes())?;
538 }
539 if let Some(id) = msgid {
540 let mut id = id.clone();
541 id.truncate(16);
542 if id.len() < 16 {
543 id.resize(16, 0);
544 }
545 writer.write_raw(&id);
546 }
547 if let Some(c) = correlation {
548 writer.write_bytes_with_length(c.as_bytes())?;
549 }
550 writer.write_raw(&queue.payload_toid);
551 if let Some(c) = condition {
552 writer.write_bytes_with_length(c.as_bytes())?;
553 }
554 Ok(writer.into_bytes())
555}
556
557#[derive(Clone, Debug, Default)]
559pub struct AqDeqResult {
560 pub message: Option<AqDeqMessage>,
562}
563
564pub fn parse_aq_deq_response(
567 payload: &[u8],
568 capabilities: ClientCapabilities,
569 kind: &AqPayloadKind,
570) -> Result<AqDeqResult> {
571 parse_aq_deq_response_with_limits(payload, capabilities, kind, ProtocolLimits::DEFAULT)
572}
573
574pub fn parse_aq_deq_response_with_limits(
575 payload: &[u8],
576 capabilities: ClientCapabilities,
577 kind: &AqPayloadKind,
578 limits: ProtocolLimits,
579) -> Result<AqDeqResult> {
580 let mut reader = TtcReader::with_limits(payload, limits)?;
581 let mut result = AqDeqResult::default();
582 let mut no_msg_found = false;
583 while reader.remaining() > 0 {
584 let message_type = reader.read_u8()?;
585 match message_type {
586 0 => {}
587 TNS_MSG_TYPE_PARAMETER => {
588 let num_bytes = reader.read_ub4()?;
589 if num_bytes > 0 {
590 let mut message = AqDeqMessage::default();
591 process_msg_props(&mut reader, &mut message, capabilities.ttc_field_version)?;
592 process_recipients(&mut reader)?;
593 message.payload = process_payload(&mut reader, kind)?;
594 message.msgid = Some(process_msg_id(&mut reader)?);
595 result.message = Some(message);
596 }
597 }
598 TNS_MSG_TYPE_STATUS => {
599 let _call_status = reader.read_ub4()?;
600 let _seq = reader.read_ub2()?;
601 }
602 TNS_MSG_TYPE_SERVER_SIDE_PIGGYBACK => {
603 let _ = skip_server_side_piggyback(&mut reader)?;
604 }
605 TNS_MSG_TYPE_END_OF_RESPONSE => break,
606 TNS_MSG_TYPE_ERROR => {
607 let info = parse_server_error_info(&mut reader, capabilities.ttc_field_version)?;
608 if info.number == TNS_ERR_NO_MESSAGES_FOUND as u32 {
609 no_msg_found = true;
610 } else if info.number != 0 {
611 return Err(ProtocolError::ServerErrorInfo(Box::new(
612 info.into_details(),
613 )));
614 }
615 }
616 _ => {
617 return Err(ProtocolError::UnknownMessageType {
618 message_type,
619 position: reader.position().saturating_sub(1),
620 })
621 }
622 }
623 }
624 if no_msg_found {
625 result.message = None;
626 }
627 Ok(result)
628}
629
630pub fn build_aq_array_enq_payload(
637 queue: &AqQueueDesc,
638 props_list: &[AqMsgProps],
639 enq_options: &AqEnqOptions,
640 seq_num: u8,
641 ttc_field_version: u8,
642 supports_oson_long_fnames: bool,
643) -> Result<Vec<u8>> {
644 let num_iters = props_list.len() as u32;
645 let queue_name = queue.name.as_bytes();
646 let mut writer = TtcWriter::new();
647 write_aq_function_code(&mut writer, TNS_FUNC_AQ_ARRAY, seq_num, ttc_field_version);
648 writer.write_u8(0); writer.write_ub4(0); writer.write_ub4(TNS_AQ_ARRAY_FLAGS_RETURN_MESSAGE_ID);
651 writer.write_u8(1); writer.write_u8(0); writer.write_sb4(TNS_AQ_ARRAY_ENQ);
654 writer.write_u8(1); if version_gates::carries_aq_shard_id(ttc_field_version) {
656 writer.write_ub4(0xFFFF); }
658 writer.write_ub4(num_iters);
659
660 let mut flags = 0u32;
661 if enq_options.delivery_mode == TNS_AQ_MSG_BUFFERED {
662 flags |= TNS_KPD_AQ_BUFMSG;
663 }
664 writer.write_ub4(0); writer.write_u8(TNS_MSG_TYPE_ROW_HEADER);
666 writer.write_bytes_with_two_lengths(Some(queue_name))?;
667 writer.write_raw(&queue.payload_toid);
668 writer.write_ub2(TNS_AQ_MESSAGE_VERSION);
669 writer.write_ub4(flags);
670 for props in props_list {
671 let payload = props
672 .payload
673 .as_ref()
674 .ok_or(ProtocolError::TtcDecode("AQ array enqueue has no payload"))?;
675 writer.write_u8(TNS_MSG_TYPE_ROW_DATA);
676 writer.write_ub4(flags); write_msg_props(&mut writer, props, ttc_field_version)?;
678 match props.recipients.as_ref() {
679 None => writer.write_ub4(0),
680 Some(recipients) => {
681 writer.write_ub4(3 * recipients.len() as u32);
682 write_recipients(&mut writer, recipients)?;
683 }
684 }
685 writer.write_sb4(enq_options.visibility as i32);
686 writer.write_ub4(0); writer.write_sb4(0); if matches!(queue.kind, AqPayloadKind::Raw) {
689 let raw_len = match payload {
690 AqPayloadValue::Raw(bytes) => bytes.len() as u32,
691 _ => return Err(ProtocolError::TtcDecode("RAW queue requires RAW payload")),
692 };
693 writer.write_ub4(raw_len);
694 }
695 write_payload(&mut writer, payload, supports_oson_long_fnames)?;
696 }
697 writer.write_u8(TNS_MSG_TYPE_STATUS);
698 Ok(writer.into_bytes())
699}
700
701pub fn build_aq_array_deq_payload(
704 queue: &AqQueueDesc,
705 deq_options: &AqDeqOptions,
706 num_iters: u32,
707 seq_num: u8,
708 ttc_field_version: u8,
709) -> Result<Vec<u8>> {
710 let queue_name = queue.name.as_bytes();
711 let mut writer = TtcWriter::new();
712 write_aq_function_code(&mut writer, TNS_FUNC_AQ_ARRAY, seq_num, ttc_field_version);
713 writer.write_u8(1); writer.write_ub4(num_iters);
715 writer.write_ub4(TNS_AQ_ARRAY_FLAGS_RETURN_MESSAGE_ID);
716 writer.write_u8(1); writer.write_u8(1); writer.write_sb4(TNS_AQ_ARRAY_DEQ);
719 writer.write_u8(0); if version_gates::carries_aq_shard_id(ttc_field_version) {
721 writer.write_ub4(0xFFFF); }
723
724 let mut flags = 0u32;
725 match deq_options.delivery_mode {
726 TNS_AQ_MSG_BUFFERED => flags |= TNS_KPD_AQ_BUFMSG,
727 TNS_AQ_MSG_PERSISTENT_OR_BUFFERED => flags |= TNS_KPD_AQ_EITHER,
728 _ => {}
729 }
730 let consumer_name = deq_options
731 .consumer_name
732 .as_ref()
733 .filter(|name| !name.is_empty())
734 .map(|name| name.as_bytes());
735 let correlation = deq_options
736 .correlation
737 .as_ref()
738 .filter(|c| !c.is_empty())
739 .map(|c| c.as_bytes());
740 let condition = deq_options
741 .condition
742 .as_ref()
743 .filter(|c| !c.is_empty())
744 .map(|c| c.as_bytes());
745 let props = AqMsgProps::default();
746 for _ in 0..num_iters {
747 writer.write_bytes_with_two_lengths(Some(queue_name))?;
748 write_msg_props(&mut writer, &props, ttc_field_version)?;
749 writer.write_ub4(0); write_value_with_length(&mut writer, consumer_name)?;
751 writer.write_sb4(deq_options.mode);
752 writer.write_sb4(deq_options.navigation);
753 writer.write_sb4(deq_options.visibility);
754 writer.write_sb4(deq_options.wait as i32);
755 write_value_with_length(&mut writer, deq_options.msgid.as_deref())?;
756 write_value_with_length(&mut writer, correlation)?;
757 write_value_with_length(&mut writer, condition)?;
758 writer.write_ub4(0); writer.write_ub4(0); writer.write_sb4(0); writer.write_bytes_with_two_lengths(Some(&queue.payload_toid))?;
762 writer.write_ub2(TNS_AQ_MESSAGE_VERSION);
763 writer.write_ub4(0); writer.write_ub4(0); writer.write_ub4(0);
766 writer.write_ub4(flags);
767 writer.write_ub4(0); writer.write_ub4(0); }
770 Ok(writer.into_bytes())
771}
772
773#[derive(Clone, Debug, Default)]
776pub struct AqArrayResult {
777 pub enq_msgids: Vec<Vec<u8>>,
779 pub deq_messages: Vec<AqDeqMessage>,
781}
782
783pub fn parse_aq_array_response(
789 payload: &[u8],
790 capabilities: ClientCapabilities,
791 operation: i32,
792 props_count: u32,
793 kind: &AqPayloadKind,
794) -> Result<AqArrayResult> {
795 parse_aq_array_response_with_limits(
796 payload,
797 capabilities,
798 operation,
799 props_count,
800 kind,
801 ProtocolLimits::DEFAULT,
802 )
803}
804
805pub fn parse_aq_array_response_with_limits(
806 payload: &[u8],
807 capabilities: ClientCapabilities,
808 operation: i32,
809 props_count: u32,
810 kind: &AqPayloadKind,
811 limits: ProtocolLimits,
812) -> Result<AqArrayResult> {
813 limits.check_batch_rows(props_count as usize)?;
814 let mut reader = TtcReader::with_limits(payload, limits)?;
815 let mut result = AqArrayResult::default();
816 let mut messages: Vec<AqDeqMessage> = Vec::new();
817 let mut enq_msgid_blob: Option<Vec<u8>> = None;
818 let mut response_num_iters: u32 = 0;
819 let mut no_msg_found = false;
820 while reader.remaining() > 0 {
821 let message_type = reader.read_u8()?;
822 match message_type {
823 0 => {}
824 TNS_MSG_TYPE_PARAMETER => {
825 let num_iters = reader.read_ub4()?;
826 reader.limits().check_batch_rows(num_iters as usize)?;
827 response_num_iters = num_iters;
828 for i in 0..num_iters {
829 let mut message = AqDeqMessage::default();
830 let props_len = reader.read_ub2()?;
831 if props_len > 0 {
832 reader.read_u8()?; process_msg_props(
834 &mut reader,
835 &mut message,
836 capabilities.ttc_field_version,
837 )?;
838 }
839 process_recipients(&mut reader)?;
840 let payload_len = reader.read_ub2()?;
841 if payload_len > 0 {
842 message.payload = process_payload(&mut reader, kind)?;
843 }
844 let msgid = reader.read_bytes_with_length()?.unwrap_or_default();
845 if operation == TNS_AQ_ARRAY_ENQ {
846 enq_msgid_blob = Some(msgid);
847 } else {
848 message.msgid = Some(msgid);
849 }
850 let ext_len = reader.read_ub2()?;
851 if ext_len > 0 {
852 return Err(ProtocolError::UnsupportedFeature("AQ array extensions"));
853 }
854 let _output_ack = reader.read_ub2()?;
855 if operation != TNS_AQ_ARRAY_ENQ {
856 let _ = i;
857 messages.push(message);
858 }
859 }
860 if operation == TNS_AQ_ARRAY_ENQ {
861 response_num_iters = reader.read_ub4()?;
862 reader
863 .limits()
864 .check_batch_rows(response_num_iters as usize)?;
865 }
866 }
867 TNS_MSG_TYPE_STATUS => {
868 let _call_status = reader.read_ub4()?;
869 let _seq = reader.read_ub2()?;
870 }
871 TNS_MSG_TYPE_SERVER_SIDE_PIGGYBACK => {
872 let _ = skip_server_side_piggyback(&mut reader)?;
873 }
874 TNS_MSG_TYPE_END_OF_RESPONSE => break,
875 TNS_MSG_TYPE_ERROR => {
876 let info = parse_server_error_info(&mut reader, capabilities.ttc_field_version)?;
877 if info.number == TNS_ERR_NO_MESSAGES_FOUND as u32 {
878 no_msg_found = true;
879 } else if info.number != 0 {
880 return Err(ProtocolError::ServerErrorInfo(Box::new(
881 info.into_details(),
882 )));
883 }
884 }
885 _ => {
886 return Err(ProtocolError::UnknownMessageType {
887 message_type,
888 position: reader.position().saturating_sub(1),
889 })
890 }
891 }
892 }
893 if operation == TNS_AQ_ARRAY_ENQ {
894 if let Some(blob) = enq_msgid_blob {
895 let count = props_count as usize;
896 result.enq_msgids = (0..count)
897 .map(|j| {
898 let start = j * 16;
899 let end = start + 16;
900 blob.get(start..end).map(<[u8]>::to_vec).unwrap_or_default()
901 })
902 .collect();
903 }
904 } else if no_msg_found {
905 result.deq_messages = Vec::new();
906 } else {
907 let keep = response_num_iters as usize;
908 messages.truncate(keep);
909 result.deq_messages = messages;
910 }
911 Ok(result)
912}
913
914fn process_msg_props(
919 reader: &mut TtcReader<'_>,
920 message: &mut AqDeqMessage,
921 ttc_field_version: u8,
922) -> Result<()> {
923 message.priority = reader.read_sb4()?;
924 message.delay = reader.read_sb4()?;
925 message.expiration = reader.read_sb4()?;
926 message.correlation = reader.read_string_with_length()?;
927 message.num_attempts = reader.read_sb4()?;
928 message.exception_queue = reader.read_string_with_length()?;
929 message.state = reader.read_sb4()?;
930 message.enq_time = process_date(reader)?;
931 let _enq_txn_id = reader.read_bytes_with_length()?;
932 process_extensions(reader)?;
933 let user_props = reader.read_ub4()?;
934 if user_props > 0 {
935 return Err(ProtocolError::UnsupportedFeature("AQ user properties"));
936 }
937 let _csn = reader.read_ub4()?;
938 let _dsn = reader.read_ub4()?;
939 let flags = reader.read_ub4()?;
940 message.delivery_mode = if flags == TNS_KPD_AQ_BUFMSG {
941 TNS_AQ_MSG_BUFFERED
942 } else {
943 TNS_AQ_MSG_PERSISTENT
944 };
945 if version_gates::carries_aq_shard_id(ttc_field_version) {
946 let _shard = reader.read_ub4()?;
947 }
948 Ok(())
949}
950
951fn process_date(reader: &mut TtcReader<'_>) -> Result<Option<QueryValue>> {
955 let num_bytes = reader.read_ub4()?;
956 if num_bytes == 0 {
957 return Ok(None);
958 }
959 let len = usize::from(reader.read_u8()?);
960 if len == 0 {
961 return Ok(None);
962 }
963 let bytes = reader.read_raw(len)?;
964 Ok(Some(decode_datetime_value(bytes)?))
965}
966
967fn process_extensions(reader: &mut TtcReader<'_>) -> Result<()> {
968 let num_extensions = reader.read_ub4()?;
969 if num_extensions > 0 {
970 reader.read_u8()?; for _ in 0..num_extensions {
972 let _text = reader.read_bytes_with_length()?;
973 let _binary = reader.read_bytes_with_length()?;
974 let _keyword = reader.read_ub2()?;
975 }
976 }
977 Ok(())
978}
979
980fn process_recipients(reader: &mut TtcReader<'_>) -> Result<()> {
981 let count = reader.read_ub4()?;
982 if count > 0 {
983 return Err(ProtocolError::UnsupportedFeature(
984 "AQ recipients on dequeue",
985 ));
986 }
987 Ok(())
988}
989
990fn process_msg_id(reader: &mut TtcReader<'_>) -> Result<Vec<u8>> {
991 Ok(reader.read_raw(TNS_AQ_MESSAGE_ID_LENGTH)?.to_vec())
992}
993
994fn process_payload(
996 reader: &mut TtcReader<'_>,
997 kind: &AqPayloadKind,
998) -> Result<Option<AqDeqPayload>> {
999 if matches!(kind, AqPayloadKind::Object) {
1000 let _toid = reader.read_bytes_with_length()?;
1004 let _oid = reader.read_bytes_with_length()?;
1005 let _snapshot = reader.read_bytes_with_length()?;
1006 let _version = reader.read_ub2()?;
1007 let image_length = reader.read_ub4()?;
1008 reader
1009 .limits()
1010 .check_response_bytes(image_length as usize)?;
1011 let _flags = reader.read_ub2()?;
1012 if image_length == 0 {
1013 return Ok(None);
1014 }
1015 let image = reader
1016 .read_bytes()?
1017 .ok_or(ProtocolError::TtcDecode("AQ object payload missing"))?;
1018 return Ok(Some(AqDeqPayload::Object(image)));
1019 }
1020 let _toid = reader.read_bytes_with_length()?;
1022 let _oid = reader.read_bytes_with_length()?;
1023 let _snapshot = reader.read_bytes_with_length()?;
1024 let _version = reader.read_ub2()?;
1025 let image_length = reader.read_ub4()? as usize;
1026 reader.limits().check_response_bytes(image_length)?;
1027 let _flags = reader.read_ub2()?;
1028 if image_length > 0 {
1029 let raw = reader
1031 .read_bytes()?
1032 .ok_or(ProtocolError::TtcDecode("AQ payload missing"))?;
1033 if raw.len() < image_length {
1034 return Err(ProtocolError::TtcDecode("AQ payload shorter than declared"));
1035 }
1036 let end = image_length;
1037 let start = 4.min(end);
1038 let payload = raw.get(start..end).unwrap_or_default().to_vec();
1039 if matches!(kind, AqPayloadKind::Json) {
1040 let value = decode_oson_with_limits(&payload, reader.limits())?;
1041 return Ok(Some(AqDeqPayload::Json(value)));
1042 }
1043 return Ok(Some(AqDeqPayload::Raw(payload)));
1044 }
1045 if matches!(kind, AqPayloadKind::Raw) {
1046 return Ok(Some(AqDeqPayload::Raw(Vec::new())));
1047 }
1048 Ok(None)
1049}
1050
1051#[cfg(test)]
1052mod tests {
1053 use super::*;
1054
1055 const FV: u8 = 24;
1058
1059 fn caps() -> ClientCapabilities {
1060 ClientCapabilities {
1061 ttc_field_version: FV,
1062 max_string_size: 32767,
1063 charset_id: 873,
1064 }
1065 }
1066
1067 const GOLDEN_RAW_ENQ: &[u8] = &[
1072 0x03, 0x79, 0x04, 0x00, 0x01, 0x01, 0x0e, 0x01, 0x02, 0x00, 0x81, 0x01, 0x01, 0x05, 0x05,
1073 0x43, 0x4f, 0x52, 0x52, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x0e, 0x00, 0x00,
1074 0x01, 0x40, 0x00, 0x00, 0x01, 0x41, 0x00, 0x01, 0x01, 0x01, 0x00, 0x01, 0x42, 0x00, 0x00,
1075 0x01, 0x45, 0x00, 0x00, 0x00, 0x00, 0x04, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x01, 0x02,
1076 0x00, 0x00, 0x00, 0x01, 0x01, 0x10, 0x01, 0x01, 0x00, 0x01, 0x01, 0x11, 0x01, 0x01, 0x10,
1077 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1078 0x00, 0x00, 0x00, 0x00, 0x0e, 0x54, 0x45, 0x53, 0x54, 0x5f, 0x52, 0x41, 0x57, 0x5f, 0x51,
1079 0x55, 0x45, 0x55, 0x45, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1080 0x00, 0x00, 0x00, 0x00, 0x17, 0x73, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x20, 0x72, 0x61, 0x77,
1081 0x20, 0x64, 0x61, 0x74, 0x61, 0x20, 0x31,
1082 ];
1083
1084 const GOLDEN_RAW_DEQ: &[u8] = &[
1087 0x03, 0x7a, 0x06, 0x00, 0x01, 0x01, 0x0e, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x01, 0x03,
1088 0x01, 0x01, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x10, 0x01, 0x01, 0x01,
1089 0x01, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0xff, 0xff, 0xff, 0xff, 0x0e,
1090 0x54, 0x45, 0x53, 0x54, 0x5f, 0x52, 0x41, 0x57, 0x5f, 0x51, 0x55, 0x45, 0x55, 0x45, 0x00,
1091 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17,
1092 ];
1093
1094 #[test]
1095 fn raw_enqueue_request_matches_golden() {
1096 let queue = AqQueueDesc::new("TEST_RAW_QUEUE".to_string(), AqPayloadKind::Raw, None);
1097 let props = AqMsgProps {
1098 priority: 2,
1099 correlation: Some("CORR1".to_string()),
1100 payload: Some(AqPayloadValue::Raw(b"sample raw data 1".to_vec())),
1101 ..AqMsgProps::default()
1102 };
1103 let bytes = build_aq_enq_payload(&queue, &props, &AqEnqOptions::default(), 4, FV, false)
1104 .expect("build enqueue");
1105 assert_eq!(bytes, GOLDEN_RAW_ENQ);
1106 }
1107
1108 #[test]
1109 fn raw_dequeue_request_matches_golden() {
1110 let queue = AqQueueDesc::new("TEST_RAW_QUEUE".to_string(), AqPayloadKind::Raw, None);
1111 let deq = AqDeqOptions {
1112 wait: 0,
1113 navigation: 1,
1114 ..AqDeqOptions::default()
1115 };
1116 let bytes = build_aq_deq_payload(&queue, &deq, 6, FV).expect("build dequeue");
1117 assert_eq!(bytes, GOLDEN_RAW_DEQ);
1118 }
1119
1120 #[test]
1121 fn empty_queue_dequeue_yields_no_message() {
1122 let caps = caps();
1126 let res = parse_aq_deq_response(&[], caps, &AqPayloadKind::Raw).expect("parse");
1127 assert!(res.message.is_none());
1128 }
1129
1130 fn deq_response_with_raw_image(image_length: u32, raw_image: &[u8]) -> Vec<u8> {
1131 let mut writer = TtcWriter::new();
1132 writer.write_u8(TNS_MSG_TYPE_PARAMETER);
1133 writer.write_ub4(1);
1134 write_msg_props(&mut writer, &AqMsgProps::default(), FV).expect("write message props");
1135 writer.write_ub4(0); writer.write_ub4(0); writer.write_ub4(0); writer.write_ub4(0); writer.write_ub2(0); writer.write_ub4(image_length);
1141 writer.write_ub2(0); writer
1143 .write_bytes_with_length(raw_image)
1144 .expect("write raw image field");
1145 writer.write_raw(&[0u8; TNS_AQ_MESSAGE_ID_LENGTH]);
1146 writer.write_u8(TNS_MSG_TYPE_END_OF_RESPONSE);
1147 writer.into_bytes()
1148 }
1149
1150 #[test]
1151 fn raw_dequeue_rejects_declared_image_length_shortfall() {
1152 let response = deq_response_with_raw_image(8, &[0, 0, 0, 0, b'A', b'B']);
1153 let err = parse_aq_deq_response(&response, caps(), &AqPayloadKind::Raw)
1154 .expect_err("short RAW image must fail");
1155 assert!(matches!(err, ProtocolError::TtcDecode(_)), "got {err:?}");
1156 }
1157
1158 #[test]
1159 fn json_dequeue_rejects_declared_image_length_shortfall() {
1160 let response = deq_response_with_raw_image(8, &[0, 0, 0, 0, b'A', b'B']);
1161 let err = parse_aq_deq_response(&response, caps(), &AqPayloadKind::Json)
1162 .expect_err("short JSON image must fail");
1163 assert!(matches!(err, ProtocolError::TtcDecode(_)), "got {err:?}");
1164 }
1165
1166 #[test]
1167 fn raw_dequeue_accepts_exact_declared_image_length() {
1168 let response = deq_response_with_raw_image(6, &[0, 0, 0, 0, b'A', b'B']);
1169 let res = parse_aq_deq_response(&response, caps(), &AqPayloadKind::Raw)
1170 .expect("exact RAW image should parse");
1171 let message = res.message.expect("message present");
1172 match message.payload {
1173 Some(AqDeqPayload::Raw(payload)) => assert_eq!(payload, vec![b'A', b'B']),
1174 other => panic!("unexpected payload {other:?}"),
1175 }
1176 }
1177
1178 #[test]
1179 fn aq_array_response_respects_protocol_batch_limit() {
1180 let limits = ProtocolLimits {
1181 max_batch_rows: 1,
1182 ..ProtocolLimits::DEFAULT
1183 };
1184 let err = parse_aq_array_response_with_limits(
1185 &[],
1186 caps(),
1187 TNS_AQ_ARRAY_ENQ,
1188 2,
1189 &AqPayloadKind::Raw,
1190 limits,
1191 )
1192 .expect_err("client-side AQ batch count above policy must fail");
1193 assert!(
1194 matches!(
1195 err,
1196 ProtocolError::ResourceLimit {
1197 limit: "batch_rows",
1198 observed: 2,
1199 maximum: 1,
1200 }
1201 ),
1202 "got {err:?}"
1203 );
1204 }
1205
1206 const GOLDEN_JSON_ENQ: &[u8] = &[
1210 0x03, 0x79, 0x04, 0x00, 0x01, 0x01, 0x0f, 0x00, 0x00, 0x81, 0x01, 0x00, 0x00, 0x00, 0x00,
1211 0x00, 0x00, 0x01, 0x04, 0x0e, 0x00, 0x00, 0x01, 0x40, 0x00, 0x00, 0x01, 0x41, 0x00, 0x01,
1212 0x01, 0x01, 0x00, 0x01, 0x42, 0x00, 0x00, 0x01, 0x45, 0x00, 0x00, 0x00, 0x00, 0x04, 0xff,
1213 0xff, 0xff, 0xff, 0x01, 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x01, 0x01, 0x10, 0x01, 0x01,
1214 0x00, 0x00, 0x00, 0x01, 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1215 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x0f, 0x54, 0x45, 0x53, 0x54,
1216 0x5f, 0x4a, 0x53, 0x4f, 0x4e, 0x5f, 0x51, 0x55, 0x45, 0x55, 0x45, 0x00, 0x00, 0x00, 0x00,
1217 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x01, 0x28, 0x00,
1218 0x26, 0x00, 0x04, 0x61, 0x08, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1219 0x00, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1220 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x43, 0xff, 0x4a, 0x5a, 0x01, 0x21,
1221 0x02, 0x03, 0x00, 0x0e, 0x00, 0x1f, 0x00, 0x00, 0x42, 0x9c, 0xe6, 0x00, 0x09, 0x00, 0x05,
1222 0x00, 0x00, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x03, 0x61, 0x67, 0x65, 0x04, 0x63, 0x69, 0x74,
1223 0x79, 0xa4, 0x03, 0x03, 0x02, 0x01, 0x00, 0x00, 0x00, 0x11, 0x00, 0x00, 0x00, 0x17, 0x00,
1224 0x00, 0x00, 0x1b, 0x33, 0x04, 0x4a, 0x6f, 0x68, 0x6e, 0x34, 0x02, 0xc1, 0x1f, 0x33, 0x02,
1225 0x4e, 0x59,
1226 ];
1227
1228 #[test]
1229 fn json_enqueue_request_matches_golden() {
1230 let queue = AqQueueDesc::new("TEST_JSON_QUEUE".to_string(), AqPayloadKind::Json, None);
1231 let value = OsonValue::Object(vec![
1233 ("name".to_string(), OsonValue::String("John".to_string())),
1234 ("age".to_string(), OsonValue::Number("30".to_string())),
1235 ("city".to_string(), OsonValue::String("NY".to_string())),
1236 ]);
1237 let props = AqMsgProps {
1238 payload: Some(AqPayloadValue::Json(value)),
1239 ..AqMsgProps::default()
1240 };
1241 let bytes = build_aq_enq_payload(&queue, &props, &AqEnqOptions::default(), 4, FV, true)
1243 .expect("build json enqueue");
1244 assert_eq!(bytes, GOLDEN_JSON_ENQ);
1245 }
1246
1247 fn assert_single_insertion(lo: &[u8], hi: &[u8], label: &str) {
1259 assert!(
1260 hi.len() > lo.len(),
1261 "{label}: gated field must add bytes at/above the boundary"
1262 );
1263 let prefix = lo.iter().zip(hi).take_while(|(a, b)| a == b).count();
1264 let suffix = lo[prefix..]
1265 .iter()
1266 .rev()
1267 .zip(hi[prefix..].iter().rev())
1268 .take_while(|(a, b)| a == b)
1269 .count();
1270 assert_eq!(
1271 prefix + suffix,
1272 lo.len(),
1273 "{label}: below-boundary bytes must equal above-boundary bytes minus one contiguous inserted block"
1274 );
1275 }
1276
1277 fn caps_fv(fv: u8) -> ClientCapabilities {
1278 ClientCapabilities {
1279 ttc_field_version: fv,
1280 max_string_size: 32767,
1281 charset_id: 873,
1282 }
1283 }
1284
1285 #[test]
1287 fn aq_enq_gates_json_payload_pointer_on_20_1() {
1288 let queue = AqQueueDesc::new("Q".to_string(), AqPayloadKind::Raw, None);
1289 let props = AqMsgProps {
1290 payload: Some(AqPayloadValue::Raw(b"x".to_vec())),
1291 ..AqMsgProps::default()
1292 };
1293 let build = |fv| {
1294 build_aq_enq_payload(&queue, &props, &AqEnqOptions::default(), 1, fv, false)
1295 .expect("enq payload")
1296 };
1297 assert_single_insertion(
1298 &build(TNS_CCAP_FIELD_VERSION_20_1 - 1),
1299 &build(TNS_CCAP_FIELD_VERSION_20_1),
1300 "aq enq JSON-payload pointer (20.1)",
1301 );
1302 }
1303
1304 #[test]
1306 fn aq_deq_gates_json_payload_flag_on_20_1() {
1307 let queue = AqQueueDesc::new("Q".to_string(), AqPayloadKind::Raw, None);
1308 let build = |fv| {
1309 build_aq_deq_payload(&queue, &AqDeqOptions::default(), 1, fv).expect("deq payload")
1310 };
1311 assert_single_insertion(
1312 &build(TNS_CCAP_FIELD_VERSION_20_1 - 1),
1313 &build(TNS_CCAP_FIELD_VERSION_20_1),
1314 "aq deq JSON-payload flag (20.1)",
1315 );
1316 }
1317
1318 #[test]
1321 fn aq_deq_gates_shard_id_on_21_1() {
1322 let queue = AqQueueDesc::new("Q".to_string(), AqPayloadKind::Raw, None);
1323 let build = |fv| {
1324 build_aq_deq_payload(&queue, &AqDeqOptions::default(), 1, fv).expect("deq payload")
1325 };
1326 assert_single_insertion(
1327 &build(TNS_CCAP_FIELD_VERSION_21_1 - 1),
1328 &build(TNS_CCAP_FIELD_VERSION_21_1),
1329 "aq deq shard id (21.1)",
1330 );
1331 }
1332
1333 #[test]
1338 fn aq_array_gates_shard_id_on_21_1() {
1339 let queue = AqQueueDesc::new("Q".to_string(), AqPayloadKind::Raw, None);
1340 let build = |fv| {
1341 build_aq_array_enq_payload(&queue, &[], &AqEnqOptions::default(), 1, fv, false)
1342 .expect("array enq payload")
1343 };
1344 assert_single_insertion(
1345 &build(TNS_CCAP_FIELD_VERSION_21_1 - 1),
1346 &build(TNS_CCAP_FIELD_VERSION_21_1),
1347 "aq array shard id (21.1)",
1348 );
1349 }
1350
1351 #[test]
1353 fn aq_msg_props_gates_shard_id_on_21_1() {
1354 let build = |fv| {
1355 let mut w = TtcWriter::new();
1356 write_msg_props(&mut w, &AqMsgProps::default(), fv).expect("msg props");
1357 w.into_bytes()
1358 };
1359 assert_single_insertion(
1360 &build(TNS_CCAP_FIELD_VERSION_21_1 - 1),
1361 &build(TNS_CCAP_FIELD_VERSION_21_1),
1362 "aq message-props shard id (21.1)",
1363 );
1364 }
1365
1366 #[test]
1371 fn aq_deq_msg_props_read_gates_shard_on_21_1() {
1372 let response_at = |fv: u8| {
1373 let mut writer = TtcWriter::new();
1374 writer.write_u8(TNS_MSG_TYPE_PARAMETER);
1375 writer.write_ub4(1);
1376 write_msg_props(&mut writer, &AqMsgProps::default(), fv).expect("write message props");
1377 writer.write_ub4(0); writer.write_ub4(0); writer.write_ub4(0); writer.write_ub4(0); writer.write_ub2(0); writer.write_ub4(6); writer.write_ub2(0); writer
1385 .write_bytes_with_length(&[0, 0, 0, 0, b'A', b'B'])
1386 .expect("write raw image field");
1387 writer.write_raw(&[0u8; TNS_AQ_MESSAGE_ID_LENGTH]);
1388 writer.write_u8(TNS_MSG_TYPE_END_OF_RESPONSE);
1389 writer.into_bytes()
1390 };
1391 let lo = TNS_CCAP_FIELD_VERSION_21_1 - 1;
1392 let hi = TNS_CCAP_FIELD_VERSION_21_1;
1393
1394 let raw_of = |resp: AqDeqResult| match resp.message.and_then(|m| m.payload) {
1396 Some(AqDeqPayload::Raw(bytes)) => Some(bytes),
1397 _ => None,
1398 };
1399 let matched_hi = raw_of(
1400 parse_aq_deq_response(&response_at(hi), caps_fv(hi), &AqPayloadKind::Raw)
1401 .expect("matched hi parse"),
1402 );
1403 let matched_lo = raw_of(
1404 parse_aq_deq_response(&response_at(lo), caps_fv(lo), &AqPayloadKind::Raw)
1405 .expect("matched lo parse"),
1406 );
1407 assert_eq!(
1408 matched_hi, matched_lo,
1409 "both bands decode the same RAW payload"
1410 );
1411 assert_eq!(matched_hi.as_deref(), Some(&b"AB"[..]));
1412
1413 let mismatched = parse_aq_deq_response(&response_at(hi), caps_fv(lo), &AqPayloadKind::Raw);
1418 let diverged = match mismatched {
1419 Err(_) => true,
1420 Ok(resp) => raw_of(resp) != matched_hi,
1421 };
1422 assert!(
1423 diverged,
1424 "read side must consume the 21.1 shard: skipping it changes the decode"
1425 );
1426 }
1427}