1use crate::utils::truncate::truncate_str;
36use anyhow::{anyhow, Result};
37use serde_json::{Map, Value};
38
39pub const PROTOCOL_HEADER_091: [u8; 8] = *b"AMQP\x00\x00\x09\x01";
46
47pub const FRAME_METHOD: u8 = 1;
48pub const FRAME_HEADER: u8 = 2;
49pub const FRAME_BODY: u8 = 3;
50pub const FRAME_HEARTBEAT: u8 = 8;
51pub const FRAME_END: u8 = 0xCE;
52
53pub const FRAME_OVERHEAD: usize = 8;
55
56pub const CLASS_CONNECTION: u16 = 10;
57pub const CLASS_CHANNEL: u16 = 20;
58pub const CLASS_EXCHANGE: u16 = 40;
59pub const CLASS_QUEUE: u16 = 50;
60pub const CLASS_BASIC: u16 = 60;
61
62pub const CONNECTION_START: u16 = 10;
63pub const CONNECTION_START_OK: u16 = 11;
64pub const CONNECTION_TUNE: u16 = 30;
65pub const CONNECTION_TUNE_OK: u16 = 31;
66pub const CONNECTION_OPEN: u16 = 40;
67pub const CONNECTION_OPEN_OK: u16 = 41;
68pub const CONNECTION_CLOSE: u16 = 50;
69pub const CONNECTION_CLOSE_OK: u16 = 51;
70
71pub const CHANNEL_OPEN: u16 = 10;
72pub const CHANNEL_OPEN_OK: u16 = 11;
73pub const CHANNEL_CLOSE: u16 = 40;
74pub const CHANNEL_CLOSE_OK: u16 = 41;
75
76pub const EXCHANGE_DECLARE: u16 = 10;
77pub const EXCHANGE_DECLARE_OK: u16 = 11;
78
79pub const QUEUE_DECLARE: u16 = 10;
80pub const QUEUE_DECLARE_OK: u16 = 11;
81pub const QUEUE_BIND: u16 = 20;
82pub const QUEUE_BIND_OK: u16 = 21;
83
84pub const BASIC_QOS: u16 = 10;
85pub const BASIC_QOS_OK: u16 = 11;
86pub const BASIC_CONSUME: u16 = 20;
87pub const BASIC_CONSUME_OK: u16 = 21;
88pub const BASIC_CANCEL: u16 = 30;
89pub const BASIC_CANCEL_OK: u16 = 31;
90pub const BASIC_PUBLISH: u16 = 40;
91pub const BASIC_RETURN: u16 = 50;
92pub const BASIC_DELIVER: u16 = 60;
93pub const BASIC_ACK: u16 = 80;
94pub const BASIC_REJECT: u16 = 90;
95pub const BASIC_NACK: u16 = 120;
96
97pub const REPLY_NOT_IMPLEMENTED: u16 = 540;
99pub const REPLY_ACCESS_REFUSED: u16 = 403;
101pub const REPLY_RESOURCE_LOCKED: u16 = 405;
103
104pub fn method_name(class_id: u16, method_id: u16) -> String {
106 let name = match (class_id, method_id) {
107 (CLASS_CONNECTION, CONNECTION_START) => "connection.start",
108 (CLASS_CONNECTION, CONNECTION_START_OK) => "connection.start-ok",
109 (CLASS_CONNECTION, CONNECTION_TUNE) => "connection.tune",
110 (CLASS_CONNECTION, CONNECTION_TUNE_OK) => "connection.tune-ok",
111 (CLASS_CONNECTION, CONNECTION_OPEN) => "connection.open",
112 (CLASS_CONNECTION, CONNECTION_OPEN_OK) => "connection.open-ok",
113 (CLASS_CONNECTION, CONNECTION_CLOSE) => "connection.close",
114 (CLASS_CONNECTION, CONNECTION_CLOSE_OK) => "connection.close-ok",
115 (CLASS_CHANNEL, CHANNEL_OPEN) => "channel.open",
116 (CLASS_CHANNEL, CHANNEL_OPEN_OK) => "channel.open-ok",
117 (CLASS_CHANNEL, CHANNEL_CLOSE) => "channel.close",
118 (CLASS_CHANNEL, CHANNEL_CLOSE_OK) => "channel.close-ok",
119 (CLASS_EXCHANGE, EXCHANGE_DECLARE) => "exchange.declare",
120 (CLASS_QUEUE, QUEUE_DECLARE) => "queue.declare",
121 (CLASS_QUEUE, QUEUE_BIND) => "queue.bind",
122 (CLASS_BASIC, BASIC_QOS) => "basic.qos",
123 (CLASS_BASIC, BASIC_CONSUME) => "basic.consume",
124 (CLASS_BASIC, BASIC_CANCEL) => "basic.cancel",
125 (CLASS_BASIC, BASIC_PUBLISH) => "basic.publish",
126 (CLASS_BASIC, BASIC_ACK) => "basic.ack",
127 (CLASS_BASIC, BASIC_REJECT) => "basic.reject",
128 (CLASS_BASIC, BASIC_NACK) => "basic.nack",
129 _ => "",
130 };
131 if name.is_empty() {
132 format!("class {} method {}", class_id, method_id)
133 } else {
134 name.to_string()
135 }
136}
137
138const MAX_FIELD_TABLE_DEPTH: usize = 32;
159
160pub const MAX_SHORT_STRING_LEN: usize = 255;
163
164pub struct Decoder<'a> {
165 buf: &'a [u8],
166 pos: usize,
167}
168
169impl<'a> Decoder<'a> {
170 pub fn new(buf: &'a [u8]) -> Self {
171 Self { buf, pos: 0 }
172 }
173
174 pub fn remaining(&self) -> usize {
175 self.buf.len().saturating_sub(self.pos)
176 }
177
178 fn take(&mut self, n: usize) -> Result<&'a [u8]> {
179 let end = self
180 .pos
181 .checked_add(n)
182 .ok_or_else(|| anyhow!("AMQP field length {} overflows the payload offset", n))?;
183 let slice = self.buf.get(self.pos..end).ok_or_else(|| {
184 anyhow!(
185 "AMQP payload truncated: {} bytes wanted at offset {}, only {} available",
186 n,
187 self.pos,
188 self.remaining()
189 )
190 })?;
191 self.pos = end;
192 Ok(slice)
193 }
194
195 pub fn u8(&mut self) -> Result<u8> {
196 let b: [u8; 1] = self
197 .take(1)?
198 .try_into()
199 .map_err(|_| anyhow!("AMQP octet read failed"))?;
200 Ok(b[0])
201 }
202
203 pub fn u16(&mut self) -> Result<u16> {
204 let b: [u8; 2] = self
205 .take(2)?
206 .try_into()
207 .map_err(|_| anyhow!("AMQP short read failed"))?;
208 Ok(u16::from_be_bytes(b))
209 }
210
211 pub fn u32(&mut self) -> Result<u32> {
212 let b: [u8; 4] = self
213 .take(4)?
214 .try_into()
215 .map_err(|_| anyhow!("AMQP long read failed"))?;
216 Ok(u32::from_be_bytes(b))
217 }
218
219 pub fn u64(&mut self) -> Result<u64> {
220 let b: [u8; 8] = self
221 .take(8)?
222 .try_into()
223 .map_err(|_| anyhow!("AMQP long-long read failed"))?;
224 Ok(u64::from_be_bytes(b))
225 }
226
227 pub fn short_string(&mut self) -> Result<String> {
229 let len = self.u8()? as usize;
230 let bytes = self.take(len)?;
231 Ok(String::from_utf8_lossy(bytes).into_owned())
232 }
233
234 pub fn long_bytes(&mut self) -> Result<&'a [u8]> {
236 let len = self.u32()? as usize;
237 self.take(len)
238 }
239
240 pub fn long_string(&mut self) -> Result<String> {
243 let bytes = self.long_bytes()?;
244 Ok(String::from_utf8_lossy(bytes).into_owned())
245 }
246
247 pub fn bits(&mut self, count: usize) -> Result<Vec<bool>> {
249 let bytes = self.take(count.div_ceil(8))?;
250 let mut out = Vec::with_capacity(count);
251 for i in 0..count {
252 let byte = bytes.get(i / 8).copied().unwrap_or(0);
253 out.push(byte & (1u8 << (i % 8)) != 0);
254 }
255 Ok(out)
256 }
257
258 pub fn field_table(&mut self) -> Result<Value> {
264 self.field_table_at(0)
265 }
266
267 fn field_table_at(&mut self, depth: usize) -> Result<Value> {
268 if depth >= MAX_FIELD_TABLE_DEPTH {
269 return Err(anyhow!(
270 "AMQP field table nested deeper than {} levels",
271 MAX_FIELD_TABLE_DEPTH
272 ));
273 }
274 let body = self.long_bytes()?;
275 let mut inner = Decoder::new(body);
276 let mut map = Map::new();
277 while inner.remaining() > 0 {
278 let Ok(key) = inner.short_string() else { break };
279 match inner.field_value_at(depth + 1) {
280 Ok(value) => {
281 map.insert(key, value);
282 }
283 Err(_) => break,
284 }
285 }
286 Ok(Value::Object(map))
287 }
288
289 fn field_value_at(&mut self, depth: usize) -> Result<Value> {
307 if depth >= MAX_FIELD_TABLE_DEPTH {
308 return Err(anyhow!(
309 "AMQP field table nested deeper than {} levels",
310 MAX_FIELD_TABLE_DEPTH
311 ));
312 }
313 let tag = self.u8()?;
314 let value = match tag {
315 b't' => Value::Bool(self.u8()? != 0),
316 b'b' => Value::from(self.u8()? as i8),
317 b'B' => Value::from(self.u8()?),
318 b's' | b'U' => Value::from(self.u16()? as i16),
319 b'u' => Value::from(self.u16()?),
320 b'I' => Value::from(self.u32()? as i32),
321 b'i' => Value::from(self.u32()?),
322 b'l' | b'L' => Value::from(self.u64()? as i64),
323 b'f' => Value::from(f32::from_bits(self.u32()?)),
324 b'd' => Value::from(f64::from_bits(self.u64()?)),
325 b'D' => {
326 let scale = self.u8()? as u32;
327 let raw = self.u32()? as i32;
328 Value::from(raw as f64 / 10f64.powi(scale as i32))
329 }
330 b'S' => Value::String(self.long_string()?),
331 b'A' => {
332 let body = self.long_bytes()?;
333 let mut inner = Decoder::new(body);
334 let mut items = Vec::new();
335 while inner.remaining() > 0 {
336 match inner.field_value_at(depth + 1) {
337 Ok(v) => items.push(v),
338 Err(_) => break,
339 }
340 }
341 Value::Array(items)
342 }
343 b'T' => Value::from(self.u64()?),
344 b'F' => self.field_table_at(depth + 1)?,
345 b'x' => {
346 let bytes = self.long_bytes()?;
347 Value::String(String::from_utf8_lossy(bytes).into_owned())
348 }
349 b'V' => Value::Null,
350 other => {
351 return Err(anyhow!(
352 "unknown AMQP field-table type id {:?}",
353 other as char
354 ))
355 }
356 };
357 Ok(value)
358 }
359}
360
361#[derive(Default)]
367pub struct Encoder {
368 buf: Vec<u8>,
369}
370
371impl Encoder {
372 pub fn new() -> Self {
373 Self { buf: Vec::new() }
374 }
375
376 pub fn u8(&mut self, v: u8) {
377 self.buf.push(v);
378 }
379
380 pub fn u16(&mut self, v: u16) {
381 self.buf.extend_from_slice(&v.to_be_bytes());
382 }
383
384 pub fn u32(&mut self, v: u32) {
385 self.buf.extend_from_slice(&v.to_be_bytes());
386 }
387
388 pub fn u64(&mut self, v: u64) {
389 self.buf.extend_from_slice(&v.to_be_bytes());
390 }
391
392 pub fn short_string(&mut self, s: &str) -> Result<()> {
406 if s.len() > MAX_SHORT_STRING_LEN {
407 return Err(anyhow!(
408 "AMQP shortstr cannot carry {} bytes; the wire format's length octet stops at \
409 {}. Shorten the name rather than letting it be cut, which would name \
410 something else: {}",
411 s.len(),
412 MAX_SHORT_STRING_LEN,
413 crate::utils::truncate_for_log(s, 48)
414 ));
415 }
416 self.buf.push(s.len() as u8);
417 self.buf.extend_from_slice(s.as_bytes());
418 Ok(())
419 }
420
421 pub fn reply_text(&mut self, text: &str) {
432 let text = truncate_str(text, MAX_SHORT_STRING_LEN);
433 self.buf.push(text.len() as u8);
434 self.buf.extend_from_slice(text.as_bytes());
435 }
436
437 pub fn long_string(&mut self, bytes: &[u8]) {
438 self.u32(bytes.len() as u32);
439 self.buf.extend_from_slice(bytes);
440 }
441
442 pub fn bits(&mut self, bits: &[bool]) {
443 for chunk in bits.chunks(8) {
444 let mut byte = 0u8;
445 for (i, set) in chunk.iter().enumerate() {
446 if *set {
447 byte |= 1u8 << i;
448 }
449 }
450 self.buf.push(byte);
451 }
452 }
453
454 pub fn field_table(&mut self, value: &Value) -> Result<()> {
462 self.field_table_at(value, 0)
463 }
464
465 fn field_table_at(&mut self, value: &Value, depth: usize) -> Result<()> {
466 if depth >= MAX_FIELD_TABLE_DEPTH {
467 return Err(anyhow!(
468 "AMQP field table nested deeper than {} levels",
469 MAX_FIELD_TABLE_DEPTH
470 ));
471 }
472 let mut inner = Encoder::new();
473 if let Some(map) = value.as_object() {
474 for (key, val) in map {
475 inner.short_string(key)?;
476 inner.field_value_at(val, depth + 1)?;
477 }
478 }
479 self.long_string(&inner.buf);
480 Ok(())
481 }
482
483 fn field_value_at(&mut self, value: &Value, depth: usize) -> Result<()> {
499 if depth >= MAX_FIELD_TABLE_DEPTH {
500 return Err(anyhow!(
501 "AMQP field table nested deeper than {} levels",
502 MAX_FIELD_TABLE_DEPTH
503 ));
504 }
505 match value {
506 Value::Null => self.u8(b'V'),
507 Value::Bool(b) => {
508 self.u8(b't');
509 self.u8(u8::from(*b));
510 }
511 Value::Number(n) => {
512 if let Some(i) = n.as_i64() {
513 self.u8(b'l');
514 self.u64(i as u64);
515 } else {
516 self.u8(b'd');
517 self.u64(n.as_f64().unwrap_or(0.0).to_bits());
518 }
519 }
520 Value::String(s) => {
521 self.u8(b'S');
522 self.long_string(s.as_bytes());
523 }
524 Value::Array(items) => {
527 let mut inner = Encoder::new();
528 for item in items {
529 inner.field_value_at(item, depth + 1)?;
530 }
531 self.u8(b'A');
532 self.long_string(&inner.buf);
533 }
534 Value::Object(_) => {
535 let mut inner = Encoder::new();
536 inner.field_table_at(value, depth + 1)?;
537 self.u8(b'F');
538 self.buf.extend_from_slice(&inner.buf);
539 }
540 }
541 Ok(())
542 }
543
544 pub fn into_vec(self) -> Vec<u8> {
545 self.buf
546 }
547
548 pub fn as_slice(&self) -> &[u8] {
549 &self.buf
550 }
551}
552
553pub struct Frame {
559 pub frame_type: u8,
560 pub channel: u16,
561 pub payload: Vec<u8>,
562}
563
564pub fn method_frame(channel: u16, class_id: u16, method_id: u16, args: &[u8]) -> Vec<u8> {
566 let mut payload = Vec::with_capacity(4 + args.len());
567 payload.extend_from_slice(&class_id.to_be_bytes());
568 payload.extend_from_slice(&method_id.to_be_bytes());
569 payload.extend_from_slice(args);
570 raw_frame(FRAME_METHOD, channel, &payload)
571}
572
573pub fn content_header_frame(channel: u16, class_id: u16, body_size: u64, props: &[u8]) -> Vec<u8> {
575 let mut payload = Vec::with_capacity(12 + props.len());
576 payload.extend_from_slice(&class_id.to_be_bytes());
577 payload.extend_from_slice(&0u16.to_be_bytes()); payload.extend_from_slice(&body_size.to_be_bytes());
579 payload.extend_from_slice(props);
580 raw_frame(FRAME_HEADER, channel, &payload)
581}
582
583pub fn body_frames(channel: u16, body: &[u8], max_frame_size: usize) -> Vec<Vec<u8>> {
588 let chunk_size = max_frame_size.saturating_sub(FRAME_OVERHEAD).max(1);
589 body.chunks(chunk_size)
590 .map(|chunk| raw_frame(FRAME_BODY, channel, chunk))
591 .collect()
592}
593
594pub fn heartbeat_frame() -> Vec<u8> {
595 raw_frame(FRAME_HEARTBEAT, 0, &[])
596}
597
598fn raw_frame(frame_type: u8, channel: u16, payload: &[u8]) -> Vec<u8> {
599 let mut out = Vec::with_capacity(FRAME_OVERHEAD + payload.len());
600 out.push(frame_type);
601 out.extend_from_slice(&channel.to_be_bytes());
602 out.extend_from_slice(&(payload.len() as u32).to_be_bytes());
603 out.extend_from_slice(payload);
604 out.push(FRAME_END);
605 out
606}
607
608#[derive(Default, Clone, Debug)]
617pub struct BasicProperties {
618 pub content_type: Option<String>,
619 pub content_encoding: Option<String>,
620 pub headers: Option<Value>,
621 pub delivery_mode: Option<u8>,
622 pub priority: Option<u8>,
623 pub correlation_id: Option<String>,
624 pub reply_to: Option<String>,
625 pub expiration: Option<String>,
626 pub message_id: Option<String>,
627 pub timestamp: Option<u64>,
628 pub kind: Option<String>,
629 pub user_id: Option<String>,
630 pub app_id: Option<String>,
631 pub cluster_id: Option<String>,
632}
633
634impl BasicProperties {
635 pub fn decode(d: &mut Decoder<'_>) -> Result<Self> {
636 let flags = d.u16()?;
640 let mut word = flags;
641 let mut words = 1;
642 while word & 1 != 0 {
643 if words >= 8 {
644 return Err(anyhow!(
645 "AMQP content header declares more than 8 property flag words"
646 ));
647 }
648 word = d.u16()?;
649 words += 1;
650 }
651
652 let has = |bit: u32| flags & (1 << (15 - bit)) != 0;
653 let mut props = BasicProperties::default();
654 if has(0) {
655 props.content_type = Some(d.short_string()?);
656 }
657 if has(1) {
658 props.content_encoding = Some(d.short_string()?);
659 }
660 if has(2) {
661 props.headers = Some(d.field_table()?);
662 }
663 if has(3) {
664 props.delivery_mode = Some(d.u8()?);
665 }
666 if has(4) {
667 props.priority = Some(d.u8()?);
668 }
669 if has(5) {
670 props.correlation_id = Some(d.short_string()?);
671 }
672 if has(6) {
673 props.reply_to = Some(d.short_string()?);
674 }
675 if has(7) {
676 props.expiration = Some(d.short_string()?);
677 }
678 if has(8) {
679 props.message_id = Some(d.short_string()?);
680 }
681 if has(9) {
682 props.timestamp = Some(d.u64()?);
683 }
684 if has(10) {
685 props.kind = Some(d.short_string()?);
686 }
687 if has(11) {
688 props.user_id = Some(d.short_string()?);
689 }
690 if has(12) {
691 props.app_id = Some(d.short_string()?);
692 }
693 if has(13) {
694 props.cluster_id = Some(d.short_string()?);
695 }
696 Ok(props)
697 }
698
699 pub fn encode(&self) -> Result<Vec<u8>> {
708 let present = [
709 self.content_type.is_some(),
710 self.content_encoding.is_some(),
711 self.headers.is_some(),
712 self.delivery_mode.is_some(),
713 self.priority.is_some(),
714 self.correlation_id.is_some(),
715 self.reply_to.is_some(),
716 self.expiration.is_some(),
717 self.message_id.is_some(),
718 self.timestamp.is_some(),
719 self.kind.is_some(),
720 self.user_id.is_some(),
721 self.app_id.is_some(),
722 self.cluster_id.is_some(),
723 ];
724 let mut flags: u16 = 0;
725 for (bit, set) in present.iter().enumerate() {
726 if *set {
727 flags |= 1 << (15 - bit as u16);
728 }
729 }
730
731 let mut e = Encoder::new();
732 e.u16(flags);
733 if let Some(v) = &self.content_type {
734 e.short_string(v)?;
735 }
736 if let Some(v) = &self.content_encoding {
737 e.short_string(v)?;
738 }
739 if let Some(v) = &self.headers {
740 e.field_table(v)?;
741 }
742 if let Some(v) = self.delivery_mode {
743 e.u8(v);
744 }
745 if let Some(v) = self.priority {
746 e.u8(v);
747 }
748 if let Some(v) = &self.correlation_id {
749 e.short_string(v)?;
750 }
751 if let Some(v) = &self.reply_to {
752 e.short_string(v)?;
753 }
754 if let Some(v) = &self.expiration {
755 e.short_string(v)?;
756 }
757 if let Some(v) = &self.message_id {
758 e.short_string(v)?;
759 }
760 if let Some(v) = self.timestamp {
761 e.u64(v);
762 }
763 if let Some(v) = &self.kind {
764 e.short_string(v)?;
765 }
766 if let Some(v) = &self.user_id {
767 e.short_string(v)?;
768 }
769 if let Some(v) = &self.app_id {
770 e.short_string(v)?;
771 }
772 if let Some(v) = &self.cluster_id {
773 e.short_string(v)?;
774 }
775 Ok(e.into_vec())
776 }
777
778 pub fn to_json(&self) -> Value {
780 let mut map = Map::new();
781 if let Some(v) = &self.content_type {
782 map.insert("content_type".into(), Value::String(v.clone()));
783 }
784 if let Some(v) = &self.content_encoding {
785 map.insert("content_encoding".into(), Value::String(v.clone()));
786 }
787 if let Some(v) = &self.headers {
788 map.insert("headers".into(), v.clone());
789 }
790 if let Some(v) = self.delivery_mode {
791 map.insert("delivery_mode".into(), Value::from(v));
792 }
793 if let Some(v) = self.priority {
794 map.insert("priority".into(), Value::from(v));
795 }
796 if let Some(v) = &self.correlation_id {
797 map.insert("correlation_id".into(), Value::String(v.clone()));
798 }
799 if let Some(v) = &self.reply_to {
800 map.insert("reply_to".into(), Value::String(v.clone()));
801 }
802 if let Some(v) = &self.expiration {
803 map.insert("expiration".into(), Value::String(v.clone()));
804 }
805 if let Some(v) = &self.message_id {
806 map.insert("message_id".into(), Value::String(v.clone()));
807 }
808 if let Some(v) = self.timestamp {
809 map.insert("timestamp".into(), Value::from(v));
810 }
811 if let Some(v) = &self.kind {
812 map.insert("type".into(), Value::String(v.clone()));
813 }
814 if let Some(v) = &self.user_id {
815 map.insert("user_id".into(), Value::String(v.clone()));
816 }
817 if let Some(v) = &self.app_id {
818 map.insert("app_id".into(), Value::String(v.clone()));
819 }
820 if let Some(v) = &self.cluster_id {
821 map.insert("cluster_id".into(), Value::String(v.clone()));
822 }
823 Value::Object(map)
824 }
825}
826
827pub fn parse_plain_response(response: &str) -> (Option<String>, bool) {
832 let mut parts = response.split('\0');
833 let _authzid = parts.next();
835 let username = parts.next().filter(|s| !s.is_empty()).map(str::to_string);
836 let has_password = parts.next().is_some_and(|p| !p.is_empty());
837 (username, has_password)
838}