From b50634841d23978cee27b35956dcb717d9b7da44 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Tue, 22 Feb 2022 23:00:43 +0100 Subject: [PATCH 01/11] more things --- .github/workflows/ci.yml | 4 +- amqp_transport/src/connection.rs | 191 +++++++++++++++++++------------ amqp_transport/src/error.rs | 2 + amqp_transport/src/frame.rs | 118 ++++++++++++++++--- amqp_transport/src/lib.rs | 2 + yarn.lock | 4 - 6 files changed, 228 insertions(+), 93 deletions(-) delete mode 100644 yarn.lock diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f4c72be..c975e90 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,5 +29,5 @@ jobs: run: cargo fmt --verbose --all -- --check - name: Run tests run: cargo test --verbose --all - - name: Run client integration tests - run: cargo xtask test-js + # - name: Run client integration tests + # run: cargo xtask test-js diff --git a/amqp_transport/src/connection.rs b/amqp_transport/src/connection.rs index a69b185..57abbf3 100644 --- a/amqp_transport/src/connection.rs +++ b/amqp_transport/src/connection.rs @@ -17,7 +17,7 @@ use amqp_core::methods::{FieldValue, Method, Table}; use amqp_core::GlobalData; use crate::error::{ConException, ProtocolError, Result}; -use crate::frame::{ContentHeader, Frame, FrameType}; +use crate::frame::{ChannelId, ContentHeader, Frame, FrameType}; use crate::{frame, methods, sasl}; fn ensure_conn(condition: bool) -> Result<()> { @@ -33,10 +33,11 @@ const CHANNEL_MAX: u16 = 0; const FRAME_SIZE_MAX: u32 = 0; const HEARTBEAT_DELAY: u16 = 0; -#[allow(dead_code)] pub struct Channel { - num: u16, - channel_handle: amqp_core::ChannelHandle, + /// A handle to the global channel representation. Used to remove the channel when it's dropped + handle: amqp_core::ChannelHandle, + /// The current status of the channel, whether it has sent a method that expects a body + status: ChannelStatus, } pub struct Connection { @@ -45,18 +46,26 @@ pub struct Connection { max_frame_size: usize, heartbeat_delay: u16, channel_max: u16, + /// When the next heartbeat expires next_timeout: Pin>, - channels: HashMap, - connection_handle: amqp_core::ConnectionHandle, + channels: HashMap, + handle: amqp_core::ConnectionHandle, global_data: GlobalData, } const DEFAULT_TIMEOUT: Duration = Duration::from_secs(30); -enum WaitForBodyStatus { - Method(Method), - Header(Method, ContentHeader, SmallVec<[Bytes; 1]>), - None, +enum ChannelStatus { + Default, + /// ClassId // todo: newtype it + NeedHeader(u16, Box), + NeedsBody(Box, Box, SmallVec<[Bytes; 1]>), +} + +impl ChannelStatus { + fn take(&mut self) -> Self { + std::mem::replace(self, Self::Default) + } } impl Connection { @@ -73,8 +82,8 @@ impl Connection { heartbeat_delay: HEARTBEAT_DELAY, channel_max: CHANNEL_MAX, next_timeout: Box::pin(time::sleep(DEFAULT_TIMEOUT)), - connection_handle, - channels: HashMap::new(), + handle: connection_handle, + channels: HashMap::with_capacity(4), global_data, } } @@ -85,7 +94,7 @@ impl Connection { Err(err) => error!(%err, "Error during processing of connection"), } - let connection_handle = self.connection_handle.lock(); + let connection_handle = self.handle.lock(); connection_handle.close(); } @@ -100,7 +109,7 @@ impl Connection { self.main_loop().await } - async fn send_method(&mut self, channel: u16, method: Method) -> Result<()> { + async fn send_method(&mut self, channel: ChannelId, method: Method) -> Result<()> { let mut payload = Vec::with_capacity(64); methods::write::write_method(method, &mut payload)?; frame::write_frame( @@ -137,7 +146,7 @@ impl Connection { }; debug!(?start_method, "Sending Start method"); - self.send_method(0, start_method).await?; + self.send_method(ChannelId::zero(), start_method).await?; let start_ok = self.recv_method().await?; debug!(?start_ok, "Received Start-Ok"); @@ -168,7 +177,7 @@ impl Connection { }; debug!("Sending Tune method"); - self.send_method(0, tune_method).await?; + self.send_method(ChannelId::zero(), tune_method).await?; let tune_ok = self.recv_method().await?; debug!(?tune_ok, "Received Tune-Ok method"); @@ -197,7 +206,7 @@ impl Connection { } self.send_method( - 0, + ChannelId::zero(), Method::ConnectionOpenOk { reserved_1: "".to_string(), }, @@ -208,54 +217,29 @@ impl Connection { } async fn main_loop(&mut self) -> Result<()> { - // todo: find out how header/body frames can interleave between channels - let mut wait_for_body = WaitForBodyStatus::None; - loop { debug!("Waiting for next frame"); let frame = frame::read_frame(&mut self.stream, self.max_frame_size).await?; self.reset_timeout(); match frame.kind { - FrameType::Method => wait_for_body = self.dispatch_method(frame).await?, - FrameType::Heartbeat => {} - FrameType::Header => match wait_for_body { - WaitForBodyStatus::None => warn!(channel = %frame.channel, "unexpected header"), - WaitForBodyStatus::Method(method) => { - wait_for_body = - WaitForBodyStatus::Header(method, ContentHeader::new(), SmallVec::new()) - } - WaitForBodyStatus::Header(_, _, _) => { - warn!(channel = %frame.channel, "already got header") - } - }, - FrameType::Body => match &mut wait_for_body { - WaitForBodyStatus::None => warn!(channel = %frame.channel, "unexpected body"), - WaitForBodyStatus::Method(_) => { - warn!(channel = %frame.channel, "unexpected body") - } - WaitForBodyStatus::Header(_, header, vec) => { - vec.push(frame.payload); - match vec - .iter() - .map(Bytes::len) - .sum::() - .cmp(&usize::try_from(header.body_size).unwrap()) - { - Ordering::Equal => todo!("process body"), - Ordering::Greater => todo!("too much data!"), - Ordering::Less => {} // wait for next body - } - } - }, + FrameType::Method => self.dispatch_method(frame).await?, + FrameType::Heartbeat => { /* Nothing here, just the `reset_timeout` above */ } + FrameType::Header => self.dispatch_header(frame)?, + FrameType::Body => self.dispatch_body(frame)?, } } } - async fn dispatch_method(&mut self, frame: Frame) -> Result { + async fn dispatch_method(&mut self, frame: Frame) -> Result<()> { let method = methods::parse_method(&frame.payload)?; debug!(?method, "Received method"); + // Sending a method implicitly cancels the content frames that might be ongoing + self.channels + .get_mut(&frame.channel) + .map(|channel| channel.status.take()); + match method { Method::ConnectionClose { reply_code, @@ -264,18 +248,27 @@ impl Connection { method_id, } => { info!(%reply_code, %reply_text, %class_id, %method_id, "Closing connection"); - self.send_method(0, Method::ConnectionCloseOk {}).await?; + self.send_method(ChannelId::zero(), Method::ConnectionCloseOk {}) + .await?; return Err(ProtocolError::GracefulClose.into()); } Method::ChannelOpen { .. } => self.channel_open(frame.channel).await?, Method::ChannelClose { .. } => self.channel_close(frame.channel, method).await?, - Method::BasicPublish { .. } => return Ok(WaitForBodyStatus::Method(method)), + Method::BasicPublish { .. } => { + const BASIC_CLASS_ID: u16 = 60; + match self.channels.get_mut(&frame.channel) { + Some(channel) => { + channel.status = ChannelStatus::NeedHeader(BASIC_CLASS_ID, Box::new(method)) + } + None => return Err(ConException::Todo.into_trans()), + } + } _ => { let channel_handle = self .channels .get(&frame.channel) .ok_or_else(|| ConException::Todo.into_trans())? - .channel_handle + .handle .clone(); tokio::spawn(amqp_messaging::methods::handle_method( @@ -285,27 +278,79 @@ impl Connection { // we don't handle this here, forward it to *somewhere* } } - - Ok(WaitForBodyStatus::None) + Ok(()) } - async fn channel_open(&mut self, num: u16) -> Result<()> { + fn dispatch_header(&mut self, frame: Frame) -> Result<()> { + self.channels + .get_mut(&frame.channel) + .ok_or_else(|| ConException::Todo.into_trans()) + .and_then(|channel| match channel.status.take() { + ChannelStatus::Default => { + warn!(channel = %frame.channel, "unexpected header"); + Err(ConException::UnexpectedFrame.into_trans()) + } + ChannelStatus::NeedHeader(class_id, method) => { + let header = ContentHeader::parse(&frame.payload)?; + ensure_conn(header.class_id == class_id)?; + + channel.status = ChannelStatus::NeedsBody(method, header, SmallVec::new()); + Ok(()) + } + ChannelStatus::NeedsBody(_, _, _) => { + warn!(channel = %frame.channel, "already got header"); + Err(ConException::UnexpectedFrame.into_trans()) + } + }) + } + + fn dispatch_body(&mut self, frame: Frame) -> Result<()> { + self.channels + .get_mut(&frame.channel) + .ok_or_else(|| ConException::Todo.into_trans()) + .and_then(|channel| match channel.status.take() { + ChannelStatus::Default => { + warn!(channel = %frame.channel, "unexpected body"); + Err(ConException::UnexpectedFrame.into_trans()) + } + ChannelStatus::NeedHeader(_, _) => { + warn!(channel = %frame.channel, "unexpected body"); + Err(ConException::UnexpectedFrame.into_trans()) + } + ChannelStatus::NeedsBody(_, header, mut vec) => { + vec.push(frame.payload); + match vec + .iter() + .map(Bytes::len) + .sum::() + .cmp(&usize::try_from(header.body_size).unwrap()) + { + Ordering::Equal => todo!("process body"), + Ordering::Greater => todo!("too much data!"), + Ordering::Less => {} // wait for next body + } + Ok(()) + } + }) + } + + async fn channel_open(&mut self, channel_id: ChannelId) -> Result<()> { let id = Uuid::from_bytes(rand::random()); let channel_handle = amqp_core::Channel::new_handle( id, - num, - self.connection_handle.clone(), + channel_id.num(), + self.handle.clone(), self.global_data.clone(), ); let channel = Channel { - num, - channel_handle: channel_handle.clone(), + handle: channel_handle.clone(), + status: ChannelStatus::Default, }; - let prev = self.channels.insert(num, channel); + let prev = self.channels.insert(channel_id, channel); if let Some(prev) = prev { - self.channels.insert(num, prev); // restore previous state + self.channels.insert(channel_id, prev); // restore previous state return Err(ConException::ChannelError.into_trans()); } @@ -318,13 +363,13 @@ impl Connection { .unwrap() .lock() .channels - .insert(num, channel_handle); + .insert(channel_id.num(), channel_handle); } - info!(%num, "Opened new channel"); + info!(%channel_id, "Opened new channel"); self.send_method( - num, + channel_id, Method::ChannelOpenOk { reserved_1: Vec::new(), }, @@ -334,7 +379,7 @@ impl Connection { Ok(()) } - async fn channel_close(&mut self, num: u16, method: Method) -> Result<()> { + async fn channel_close(&mut self, channel_id: ChannelId, method: Method) -> Result<()> { if let Method::ChannelClose { reply_code: code, reply_text: reason, @@ -343,9 +388,9 @@ impl Connection { { info!(%code, %reason, "Closing channel"); - if let Some(channel) = self.channels.remove(&num) { + if let Some(channel) = self.channels.remove(&channel_id) { drop(channel); - self.send_method(num, Method::ChannelCloseOk).await?; + self.send_method(channel_id, Method::ChannelCloseOk).await?; } else { return Err(ConException::Todo.into_trans()); } @@ -357,7 +402,7 @@ impl Connection { fn reset_timeout(&mut self) { if self.heartbeat_delay != 0 { - let next = Duration::from_secs(u64::from(self.heartbeat_delay)); + let next = Duration::from_secs(u64::from(self.heartbeat_delay / 2)); self.next_timeout = Box::pin(time::sleep(next)); } } @@ -396,13 +441,13 @@ impl Connection { impl Drop for Connection { fn drop(&mut self) { - self.connection_handle.lock().close(); + self.handle.lock().close(); } } impl Drop for Channel { fn drop(&mut self) { - self.channel_handle.lock().close(); + self.handle.lock().close(); } } diff --git a/amqp_transport/src/error.rs b/amqp_transport/src/error.rs index 4b1b436..188db0a 100644 --- a/amqp_transport/src/error.rs +++ b/amqp_transport/src/error.rs @@ -45,6 +45,8 @@ pub enum ConException { SyntaxError(Vec), #[error("504 Channel error")] ChannelError, + #[error("505 Unexpected Frame")] + UnexpectedFrame, #[error("xxx Not decided yet")] Todo, } diff --git a/amqp_transport/src/frame.rs b/amqp_transport/src/frame.rs index cd9c53c..cd16ea6 100644 --- a/amqp_transport/src/frame.rs +++ b/amqp_transport/src/frame.rs @@ -1,13 +1,36 @@ use crate::error::{ConException, ProtocolError, Result}; -use amqp_core::methods::FieldValue; +use amqp_core::methods; use anyhow::Context; use bytes::Bytes; -use smallvec::SmallVec; +use std::fmt::{Display, Formatter}; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tracing::trace; const REQUIRED_FRAME_END: u8 = 0xCE; +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct ChannelId(u16); + +impl ChannelId { + pub fn num(self) -> u16 { + self.0 + } + + pub fn is_zero(self) -> bool { + self.0 == 0 + } + + pub fn zero() -> Self { + Self(0) + } +} + +impl Display for ChannelId { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } +} + mod frame_type { pub const METHOD: u8 = 1; pub const HEADER: u8 = 2; @@ -19,7 +42,7 @@ mod frame_type { pub struct Frame { /// The type of the frame including its parsed metadata. pub kind: FrameType, - pub channel: u16, + pub channel: ChannelId, /// Includes the whole payload, also including the metadata from each type. pub payload: Bytes, } @@ -33,18 +56,84 @@ pub enum FrameType { Heartbeat = 8, } +#[derive(Debug, Clone, PartialEq)] +pub struct BasicProperties { + content_type: Option, + content_encoding: Option, + headers: Option, + delivery_mode: Option, + priority: Option, + correlation_id: Option, + reply_to: Option, + expiration: Option, + message_id: Option, + timestamp: Option, + r#type: Option, + user_id: Option, + app_id: Option, + reserved: Option, +} + #[derive(Debug, Clone, PartialEq)] pub struct ContentHeader { pub class_id: u16, pub weight: u16, pub body_size: u64, - pub property_flags: SmallVec<[u16; 1]>, - pub property_fields: Vec, + pub property_fields: BasicProperties, +} + +mod content_header_parse { + use crate::error::TransError; + use crate::frame::{BasicProperties, ContentHeader}; + use nom::number::complete::{u16, u64}; + use nom::number::Endianness::Big; + + type IResult<'a, T> = nom::IResult<&'a [u8], T, TransError>; + + pub fn basic_properties(_property_flags: u16, _input: &[u8]) -> IResult<'_, BasicProperties> { + todo!() + } + + pub fn header(input: &[u8]) -> IResult<'_, Box> { + let (input, class_id) = u16(Big)(input)?; + let (input, weight) = u16(Big)(input)?; + let (input, body_size) = u64(Big)(input)?; + + // I do not quite understand this here. Apparently, there can be more than 15 flags? + // But the Basic class only specifies 15, so idk. Don't care about this for now + let (input, property_flags) = u16(Big)(input)?; + let (input, property_fields) = basic_properties(property_flags, input)?; + + Ok(( + input, + Box::new(ContentHeader { + class_id, + weight, + body_size, + property_fields, + }), + )) + } } impl ContentHeader { - pub fn new() -> Self { - todo!() + pub fn parse(input: &[u8]) -> Result> { + match content_header_parse::header(input) { + Ok(([], header)) => Ok(header), + Ok((_, _)) => { + Err( + ConException::SyntaxError(vec!["could not consume all input".to_string()]) + .into_trans(), + ) + } + Err(nom::Err::Incomplete(_)) => { + Err( + ConException::SyntaxError(vec!["there was not enough data".to_string()]) + .into_trans(), + ) + } + Err(nom::Err::Failure(err) | nom::Err::Error(err)) => Err(err), + } } } @@ -55,7 +144,7 @@ where trace!(?frame, "Sending frame"); w.write_u8(frame.kind as u8).await?; - w.write_u16(frame.channel).await?; + w.write_u16(frame.channel.num()).await?; w.write_u32(u32::try_from(frame.payload.len()).context("frame size too big")?) .await?; w.write_all(&frame.payload).await?; @@ -70,6 +159,7 @@ where { let kind = r.read_u8().await.context("read type")?; let channel = r.read_u16().await.context("read channel")?; + let channel = ChannelId(channel); let size = r.read_u32().await.context("read size")?; let mut payload = vec![0; size.try_into().unwrap()]; @@ -98,16 +188,16 @@ where Ok(frame) } -fn parse_frame_type(kind: u8, channel: u16) -> Result { +fn parse_frame_type(kind: u8, channel: ChannelId) -> Result { match kind { frame_type::METHOD => Ok(FrameType::Method), frame_type::HEADER => Ok(FrameType::Header), frame_type::BODY => Ok(FrameType::Body), frame_type::HEARTBEAT => { - if channel != 0 { - Err(ProtocolError::ConException(ConException::FrameError).into()) - } else { + if channel.is_zero() { Ok(FrameType::Heartbeat) + } else { + Err(ProtocolError::ConException(ConException::FrameError).into()) } } _ => Err(ConException::FrameError.into_trans()), @@ -116,7 +206,7 @@ fn parse_frame_type(kind: u8, channel: u16) -> Result { #[cfg(test)] mod tests { - use crate::frame::{Frame, FrameType}; + use crate::frame::{ChannelId, Frame, FrameType}; use bytes::Bytes; #[tokio::test] @@ -145,7 +235,7 @@ mod tests { frame, Frame { kind: FrameType::Method, - channel: 0, + channel: ChannelId(0), payload: Bytes::from_static(&[1, 2, 3]), } ); diff --git a/amqp_transport/src/lib.rs b/amqp_transport/src/lib.rs index 2575acc..f28c3fd 100644 --- a/amqp_transport/src/lib.rs +++ b/amqp_transport/src/lib.rs @@ -8,6 +8,8 @@ mod sasl; #[cfg(test)] mod tests; +// TODO: handle big types + use crate::connection::Connection; use amqp_core::GlobalData; use anyhow::Result; diff --git a/yarn.lock b/yarn.lock deleted file mode 100644 index fb57ccd..0000000 --- a/yarn.lock +++ /dev/null @@ -1,4 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - From 99ce586dec7be3ffd1ca01532141bf8dde3c00c3 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Wed, 23 Feb 2022 20:08:03 +0100 Subject: [PATCH 02/11] send message! --- .github/workflows/ci.yml | 4 +- amqp_core/src/lib.rs | 2 +- amqp_core/src/message.rs | 10 +-- amqp_messaging/src/methods.rs | 10 ++- amqp_transport/src/connection.rs | 116 +++++++++++++++++++++--------- amqp_transport/src/frame.rs | 69 ++++++++++++------ amqp_transport/src/methods/mod.rs | 2 +- xtask/src/test_js.rs | 22 +++++- 8 files changed, 169 insertions(+), 66 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c975e90..f4c72be 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -29,5 +29,5 @@ jobs: run: cargo fmt --verbose --all -- --check - name: Run tests run: cargo test --verbose --all - # - name: Run client integration tests - # run: cargo xtask test-js + - name: Run client integration tests + run: cargo xtask test-js diff --git a/amqp_core/src/lib.rs b/amqp_core/src/lib.rs index f92416d..dd3ab6c 100644 --- a/amqp_core/src/lib.rs +++ b/amqp_core/src/lib.rs @@ -1,6 +1,6 @@ #![warn(rust_2018_idioms)] -mod message; +pub mod message; pub mod methods; use parking_lot::Mutex; diff --git a/amqp_core/src/message.rs b/amqp_core/src/message.rs index eba4c8d..2b190f2 100644 --- a/amqp_core/src/message.rs +++ b/amqp_core/src/message.rs @@ -8,13 +8,15 @@ use uuid::Uuid; pub type Message = Arc; +#[derive(Debug)] pub struct RawMessage { - id: Uuid, - properties: methods::Table, - routing: RoutingInformation, - content: SmallVec<[Bytes; 1]>, + pub id: Uuid, + pub properties: methods::Table, + pub routing: RoutingInformation, + pub content: SmallVec<[Bytes; 1]>, } +#[derive(Debug)] pub struct RoutingInformation { pub exchange: String, pub routing_key: String, diff --git a/amqp_messaging/src/methods.rs b/amqp_messaging/src/methods.rs index 700c97d..8d1248f 100644 --- a/amqp_messaging/src/methods.rs +++ b/amqp_messaging/src/methods.rs @@ -1,8 +1,16 @@ +use amqp_core::message::Message; use amqp_core::methods::Method; use amqp_core::ChannelHandle; use std::time::Duration; use tokio::time; -use tracing::debug; +use tracing::{debug, info}; + +pub async fn handle_basic_publish(_channel_handle: ChannelHandle, message: Message) { + info!( + ?message, + "Someone has summoned the almighty Basic.Publish handler" + ); +} pub async fn handle_method(_channel_handle: ChannelHandle, _method: Method) { debug!("handling method or something in that cool new future"); diff --git a/amqp_transport/src/connection.rs b/amqp_transport/src/connection.rs index 57abbf3..3678eb3 100644 --- a/amqp_transport/src/connection.rs +++ b/amqp_transport/src/connection.rs @@ -2,6 +2,7 @@ use std::cmp::Ordering; use std::collections::HashMap; use std::net::SocketAddr; use std::pin::Pin; +use std::sync::Arc; use std::time::Duration; use anyhow::Context; @@ -13,6 +14,7 @@ use tokio::time; use tracing::{debug, error, info, warn}; use uuid::Uuid; +use amqp_core::message::{RawMessage, RoutingInformation}; use amqp_core::methods::{FieldValue, Method, Table}; use amqp_core::GlobalData; @@ -33,6 +35,8 @@ const CHANNEL_MAX: u16 = 0; const FRAME_SIZE_MAX: u32 = 0; const HEARTBEAT_DELAY: u16 = 0; +const BASIC_CLASS_ID: u16 = 60; + pub struct Channel { /// A handle to the global channel representation. Used to remove the channel when it's dropped handle: amqp_core::ChannelHandle, @@ -254,15 +258,12 @@ impl Connection { } Method::ChannelOpen { .. } => self.channel_open(frame.channel).await?, Method::ChannelClose { .. } => self.channel_close(frame.channel, method).await?, - Method::BasicPublish { .. } => { - const BASIC_CLASS_ID: u16 = 60; - match self.channels.get_mut(&frame.channel) { - Some(channel) => { - channel.status = ChannelStatus::NeedHeader(BASIC_CLASS_ID, Box::new(method)) - } - None => return Err(ConException::Todo.into_trans()), + Method::BasicPublish { .. } => match self.channels.get_mut(&frame.channel) { + Some(channel) => { + channel.status = ChannelStatus::NeedHeader(BASIC_CLASS_ID, Box::new(method)) } - } + None => return Err(ConException::Todo.into_trans()), + }, _ => { let channel_handle = self .channels @@ -305,33 +306,84 @@ impl Connection { } fn dispatch_body(&mut self, frame: Frame) -> Result<()> { - self.channels + let channel = self + .channels .get_mut(&frame.channel) - .ok_or_else(|| ConException::Todo.into_trans()) - .and_then(|channel| match channel.status.take() { - ChannelStatus::Default => { - warn!(channel = %frame.channel, "unexpected body"); - Err(ConException::UnexpectedFrame.into_trans()) - } - ChannelStatus::NeedHeader(_, _) => { - warn!(channel = %frame.channel, "unexpected body"); - Err(ConException::UnexpectedFrame.into_trans()) - } - ChannelStatus::NeedsBody(_, header, mut vec) => { - vec.push(frame.payload); - match vec - .iter() - .map(Bytes::len) - .sum::() - .cmp(&usize::try_from(header.body_size).unwrap()) - { - Ordering::Equal => todo!("process body"), - Ordering::Greater => todo!("too much data!"), - Ordering::Less => {} // wait for next body + .ok_or_else(|| ConException::Todo.into_trans())?; + + match channel.status.take() { + ChannelStatus::Default => { + warn!(channel = %frame.channel, "unexpected body"); + Err(ConException::UnexpectedFrame.into_trans()) + } + ChannelStatus::NeedHeader(_, _) => { + warn!(channel = %frame.channel, "unexpected body"); + Err(ConException::UnexpectedFrame.into_trans()) + } + ChannelStatus::NeedsBody(method, header, mut vec) => { + vec.push(frame.payload); + match vec + .iter() + .map(Bytes::len) + .sum::() + .cmp(&usize::try_from(header.body_size).unwrap()) + { + Ordering::Equal => { + self.process_method_with_body(*method, *header, vec, frame.channel) } - Ok(()) + Ordering::Greater => Err(ConException::Todo.into_trans()), + Ordering::Less => Ok(()), // wait for next body } - }) + } + } + } + + fn process_method_with_body( + &mut self, + method: Method, + header: ContentHeader, + payloads: SmallVec<[Bytes; 1]>, + channel: ChannelId, + ) -> Result<()> { + // The only method with content that is sent to the server is Basic.Publish. + ensure_conn(header.class_id == BASIC_CLASS_ID)?; + + if let Method::BasicPublish { + exchange, + routing_key, + mandatory, + immediate, + .. + } = method + { + let message = RawMessage { + id: Uuid::from_bytes(rand::random()), + properties: header.property_fields, + routing: RoutingInformation { + exchange, + routing_key, + mandatory, + immediate, + }, + content: payloads, + }; + let message = Arc::new(message); + + let channel = self + .channels + .get(&channel) + .ok_or_else(|| ConException::Todo.into_trans())?; + + // Spawn the handler for the publish. The connection task goes back to handling + // just the connection. + tokio::spawn(amqp_messaging::methods::handle_basic_publish( + channel.handle.clone(), + message, + )); + Ok(()) + } else { + Err(ConException::Todo.into_trans()) + } } async fn channel_open(&mut self, channel_id: ChannelId) -> Result<()> { diff --git a/amqp_transport/src/frame.rs b/amqp_transport/src/frame.rs index cd16ea6..df81f8b 100644 --- a/amqp_transport/src/frame.rs +++ b/amqp_transport/src/frame.rs @@ -56,42 +56,66 @@ pub enum FrameType { Heartbeat = 8, } -#[derive(Debug, Clone, PartialEq)] -pub struct BasicProperties { - content_type: Option, - content_encoding: Option, - headers: Option, - delivery_mode: Option, - priority: Option, - correlation_id: Option, - reply_to: Option, - expiration: Option, - message_id: Option, - timestamp: Option, - r#type: Option, - user_id: Option, - app_id: Option, - reserved: Option, -} - #[derive(Debug, Clone, PartialEq)] pub struct ContentHeader { pub class_id: u16, pub weight: u16, pub body_size: u64, - pub property_fields: BasicProperties, + pub property_fields: methods::Table, } mod content_header_parse { use crate::error::TransError; - use crate::frame::{BasicProperties, ContentHeader}; + use crate::frame::ContentHeader; + use crate::methods::parse_helper::{octet, shortstr, table, timestamp}; + use amqp_core::methods; + use amqp_core::methods::FieldValue::{FieldTable, ShortShortUInt, ShortString, Timestamp}; use nom::number::complete::{u16, u64}; use nom::number::Endianness::Big; type IResult<'a, T> = nom::IResult<&'a [u8], T, TransError>; - pub fn basic_properties(_property_flags: u16, _input: &[u8]) -> IResult<'_, BasicProperties> { - todo!() + pub fn basic_properties(flags: u16, input: &[u8]) -> IResult<'_, methods::Table> { + macro_rules! parse_property { + (if $flags:ident >> $n:literal, $parser:ident($input:ident)?, $map:ident.insert($name:expr, $ctor:path)) => { + if (($flags >> $n) & 1) == 1 { + let (input, value) = $parser($input)?; + $map.insert(String::from($name), $ctor(value)); + input + } else { + $input + } + }; + } + + let mut map = methods::Table::new(); + + let input = parse_property!(if flags >> 15, shortstr(input)?, map.insert("content-type", ShortString)); + let input = parse_property!(if flags >> 14, shortstr(input)?, map.insert("content-encoding", ShortString)); + let input = + parse_property!(if flags >> 13, table(input)?, map.insert("headers", FieldTable)); + let input = parse_property!(if flags >> 12, octet(input)?, map.insert("delivery-mode", ShortShortUInt)); + let input = + parse_property!(if flags >> 11, octet(input)?, map.insert("priority", ShortShortUInt)); + let input = parse_property!(if flags >> 10, shortstr(input)?, map.insert("correlation-id", ShortString)); + let input = + parse_property!(if flags >> 9, shortstr(input)?, map.insert("reply-to", ShortString)); + let input = + parse_property!(if flags >> 8, shortstr(input)?, map.insert("expiration", ShortString)); + let input = + parse_property!(if flags >> 7, shortstr(input)?, map.insert("message-id", ShortString)); + let input = + parse_property!(if flags >> 6, timestamp(input)?, map.insert("timestamp", Timestamp)); + let input = + parse_property!(if flags >> 5, shortstr(input)?, map.insert("type", ShortString)); + let input = + parse_property!(if flags >> 4, shortstr(input)?, map.insert("user-id", ShortString)); + let input = + parse_property!(if flags >> 3, shortstr(input)?, map.insert("app-id", ShortString)); + let input = + parse_property!(if flags >> 2, shortstr(input)?, map.insert("reserved", ShortString)); + + Ok((input, map)) } pub fn header(input: &[u8]) -> IResult<'_, Box> { @@ -101,6 +125,7 @@ mod content_header_parse { // I do not quite understand this here. Apparently, there can be more than 15 flags? // But the Basic class only specifies 15, so idk. Don't care about this for now + // Todo: But probably later. let (input, property_flags) = u16(Big)(input)?; let (input, property_fields) = basic_properties(property_flags, input)?; diff --git a/amqp_transport/src/methods/mod.rs b/amqp_transport/src/methods/mod.rs index 0118c7c..f413f91 100644 --- a/amqp_transport/src/methods/mod.rs +++ b/amqp_transport/src/methods/mod.rs @@ -4,7 +4,7 @@ use rand::Rng; use std::collections::HashMap; mod generated; -mod parse_helper; +pub mod parse_helper; #[cfg(test)] mod tests; mod write_helper; diff --git a/xtask/src/test_js.rs b/xtask/src/test_js.rs index f61343f..cc94422 100644 --- a/xtask/src/test_js.rs +++ b/xtask/src/test_js.rs @@ -1,9 +1,25 @@ use crate::project_root; use anyhow::{bail, Context, Result}; -use std::process::Command; +use std::path::Path; +use std::process::{Command, Stdio}; pub fn main() -> Result<()> { - let test_js_root = project_root().join("test-js"); + let project_root = project_root(); + let test_js_root = project_root.join("test-js"); + + let mut amqp_server = Command::new("cargo") + .arg("run") + .spawn() + .context("`cargo run` amqp")?; + + let test_result = run_js(&test_js_root); + + amqp_server.kill()?; + + test_result +} + +fn run_js(test_js_root: &Path) -> Result<()> { let status = Command::new("yarn") .current_dir(&test_js_root) .status() @@ -16,9 +32,9 @@ pub fn main() -> Result<()> { .current_dir(&test_js_root) .status() .context("yarn test tests")?; + if !status.success() { bail!("yarn tests failed"); } - Ok(()) } From 89820b06cafdd683b6605d5e8068ab832757ffc5 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Wed, 23 Feb 2022 20:10:53 +0100 Subject: [PATCH 03/11] fix test --- amqp_transport/src/tests.rs | 4 ++-- xtask/src/test_js.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/amqp_transport/src/tests.rs b/amqp_transport/src/tests.rs index b6af887..1920fe1 100644 --- a/amqp_transport/src/tests.rs +++ b/amqp_transport/src/tests.rs @@ -1,4 +1,4 @@ -use crate::frame::FrameType; +use crate::frame::{ChannelId, FrameType}; use crate::{frame, methods}; use amqp_core::methods::{FieldValue, Method}; use std::collections::HashMap; @@ -21,7 +21,7 @@ async fn write_start_ok_frame() { let frame = frame::Frame { kind: FrameType::Method, - channel: 0, + channel: ChannelId::zero(), payload: payload.into(), }; diff --git a/xtask/src/test_js.rs b/xtask/src/test_js.rs index cc94422..c625d48 100644 --- a/xtask/src/test_js.rs +++ b/xtask/src/test_js.rs @@ -1,7 +1,7 @@ use crate::project_root; use anyhow::{bail, Context, Result}; use std::path::Path; -use std::process::{Command, Stdio}; +use std::process::Command; pub fn main() -> Result<()> { let project_root = project_root(); From 606438f301422ec0f5f0561110aea6a77de874dc Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Sat, 26 Feb 2022 13:06:34 +0100 Subject: [PATCH 04/11] things --- Cargo.lock | 1 + amqp_core/Cargo.toml | 1 + amqp_core/src/error.rs | 35 ++++++++++++ amqp_core/src/lib.rs | 5 ++ amqp_core/src/queue.rs | 25 +++++++++ amqp_messaging/src/methods.rs | 52 ++++++++++++++++-- amqp_transport/src/connection.rs | 63 ++++++++++------------ amqp_transport/src/error.rs | 44 +++------------ amqp_transport/src/frame.rs | 11 ++-- amqp_transport/src/methods/mod.rs | 13 ++--- amqp_transport/src/methods/parse_helper.rs | 17 +++--- amqp_transport/src/sasl.rs | 17 +++--- test-js/src/open-channel.js | 2 +- xtask/src/test_js.rs | 1 + 14 files changed, 173 insertions(+), 114 deletions(-) create mode 100644 amqp_core/src/error.rs create mode 100644 amqp_core/src/queue.rs diff --git a/Cargo.lock b/Cargo.lock index b34948d..d24442f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -31,6 +31,7 @@ dependencies = [ "bytes", "parking_lot", "smallvec", + "thiserror", "uuid", ] diff --git a/amqp_core/Cargo.toml b/amqp_core/Cargo.toml index 4a10592..ce92335 100644 --- a/amqp_core/Cargo.toml +++ b/amqp_core/Cargo.toml @@ -9,4 +9,5 @@ edition = "2021" bytes = "1.1.0" parking_lot = "0.12.0" smallvec = { version = "1.8.0", features = ["union"] } +thiserror = "1.0.30" uuid = "0.8.2" diff --git a/amqp_core/src/error.rs b/amqp_core/src/error.rs new file mode 100644 index 0000000..ab42013 --- /dev/null +++ b/amqp_core/src/error.rs @@ -0,0 +1,35 @@ +#[derive(Debug, thiserror::Error)] +pub enum ProtocolError { + #[error("fatal error")] + Fatal, + #[error("{0}")] + ConException(#[from] ConException), + #[error("{0}")] + ChannelException(#[from] ChannelException), + #[error("Connection must be closed")] + CloseNow, + #[error("Graceful connection closing requested")] + GracefulClose, +} + +#[derive(Debug, thiserror::Error)] +pub enum ConException { + #[error("501 Frame error")] + FrameError, + #[error("503 Command invalid")] + CommandInvalid, + #[error("503 Syntax error | {0:?}")] + /// A method was received but there was a syntax error. The string stores where it occurred. + SyntaxError(Vec), + #[error("504 Channel error")] + ChannelError, + #[error("505 Unexpected Frame")] + UnexpectedFrame, + #[error("540 Not implemented")] + NotImplemented, + #[error("xxx Not decided yet")] + Todo, +} + +#[derive(Debug, thiserror::Error)] +pub enum ChannelException {} diff --git a/amqp_core/src/lib.rs b/amqp_core/src/lib.rs index dd3ab6c..fc875f1 100644 --- a/amqp_core/src/lib.rs +++ b/amqp_core/src/lib.rs @@ -1,8 +1,11 @@ #![warn(rust_2018_idioms)] +pub mod error; pub mod message; pub mod methods; +pub mod queue; +use crate::queue::Queue; use parking_lot::Mutex; use std::collections::HashMap; use std::net::SocketAddr; @@ -47,6 +50,7 @@ pub struct Connection { pub peer_addr: SocketAddr, pub global_data: GlobalData, pub channels: HashMap, + pub exclusive_queues: Vec, } impl Connection { @@ -60,6 +64,7 @@ impl Connection { peer_addr, global_data, channels: HashMap::new(), + exclusive_queues: vec![], })) } diff --git a/amqp_core/src/queue.rs b/amqp_core/src/queue.rs new file mode 100644 index 0000000..f8074a8 --- /dev/null +++ b/amqp_core/src/queue.rs @@ -0,0 +1,25 @@ +use crate::message::Message; +use parking_lot::Mutex; +use std::sync::atomic::AtomicUsize; +use std::sync::Arc; +use uuid::Uuid; + +pub type Queue = Arc; + +#[derive(Debug)] +pub struct RawQueue { + pub id: Uuid, + pub name: String, + pub messages: Mutex>, // use a concurrent linked list??? + pub durable: bool, + /// Whether the queue will automatically be deleted when no consumers uses it anymore. + /// The queue can always be manually deleted. + /// If auto-delete is enabled, it keeps track of the consumer count. + pub deletion: QueueDeletion, +} + +#[derive(Debug)] +pub enum QueueDeletion { + Auto(AtomicUsize), + Manual, +} diff --git a/amqp_messaging/src/methods.rs b/amqp_messaging/src/methods.rs index 8d1248f..cbea527 100644 --- a/amqp_messaging/src/methods.rs +++ b/amqp_messaging/src/methods.rs @@ -1,8 +1,7 @@ +use amqp_core::error::{ConException, ProtocolError}; use amqp_core::message::Message; use amqp_core::methods::Method; use amqp_core::ChannelHandle; -use std::time::Duration; -use tokio::time; use tracing::{debug, info}; pub async fn handle_basic_publish(_channel_handle: ChannelHandle, message: Message) { @@ -12,7 +11,50 @@ pub async fn handle_basic_publish(_channel_handle: ChannelHandle, message: Messa ); } -pub async fn handle_method(_channel_handle: ChannelHandle, _method: Method) { - debug!("handling method or something in that cool new future"); - time::sleep(Duration::from_secs(10)).await; +pub async fn handle_method( + _channel_handle: ChannelHandle, + method: Method, +) -> Result<(), ProtocolError> { + match method { + Method::ExchangeDeclare { .. } => Err(ConException::NotImplemented.into()), + Method::ExchangeDeclareOk => Err(ConException::NotImplemented.into()), + Method::ExchangeDelete { .. } => Err(ConException::NotImplemented.into()), + Method::ExchangeDeleteOk => Err(ConException::NotImplemented.into()), + Method::QueueDeclare { .. } => Err(ConException::NotImplemented.into()), + Method::QueueDeclareOk { .. } => Err(ConException::NotImplemented.into()), + Method::QueueBind { .. } => Err(ConException::NotImplemented.into()), + Method::QueueBindOk => Err(ConException::NotImplemented.into()), + Method::QueueUnbind { .. } => Err(ConException::NotImplemented.into()), + Method::QueueUnbindOk => Err(ConException::NotImplemented.into()), + Method::QueuePurge { .. } => Err(ConException::NotImplemented.into()), + Method::QueuePurgeOk { .. } => Err(ConException::NotImplemented.into()), + Method::QueueDelete { .. } => Err(ConException::NotImplemented.into()), + Method::QueueDeleteOk { .. } => Err(ConException::NotImplemented.into()), + Method::BasicQos { .. } => Err(ConException::NotImplemented.into()), + Method::BasicQosOk => Err(ConException::NotImplemented.into()), + Method::BasicConsume { .. } => Err(ConException::NotImplemented.into()), + Method::BasicConsumeOk { .. } => Err(ConException::NotImplemented.into()), + Method::BasicCancel { .. } => Err(ConException::NotImplemented.into()), + Method::BasicCancelOk { .. } => Err(ConException::NotImplemented.into()), + Method::BasicReturn { .. } => Err(ConException::NotImplemented.into()), + Method::BasicDeliver { .. } => Err(ConException::NotImplemented.into()), + Method::BasicGet { .. } => Err(ConException::NotImplemented.into()), + Method::BasicGetOk { .. } => Err(ConException::NotImplemented.into()), + Method::BasicGetEmpty { .. } => Err(ConException::NotImplemented.into()), + Method::BasicAck { .. } => Err(ConException::NotImplemented.into()), + Method::BasicReject { .. } => Err(ConException::NotImplemented.into()), + Method::BasicRecoverAsync { .. } => Err(ConException::NotImplemented.into()), + Method::BasicRecover { .. } => Err(ConException::NotImplemented.into()), + Method::BasicRecoverOk => Err(ConException::NotImplemented.into()), + Method::TxSelect + | Method::TxSelectOk + | Method::TxCommit + | Method::TxCommitOk + | Method::TxRollback + | Method::TxRollbackOk => Err(ConException::NotImplemented.into()), + Method::BasicPublish { .. } => { + unreachable!("Basic.Publish is handled somewhere else because it has a body") + } + _ => unreachable!("Method handled by transport layer"), + } } diff --git a/amqp_transport/src/connection.rs b/amqp_transport/src/connection.rs index 3678eb3..1b5aac4 100644 --- a/amqp_transport/src/connection.rs +++ b/amqp_transport/src/connection.rs @@ -1,32 +1,29 @@ +use crate::error::{ConException, ProtocolError, Result}; +use crate::frame::{ChannelId, ContentHeader, Frame, FrameType}; +use crate::{frame, methods, sasl}; +use amqp_core::message::{RawMessage, RoutingInformation}; +use amqp_core::methods::{FieldValue, Method, Table}; +use amqp_core::GlobalData; +use anyhow::Context; +use bytes::Bytes; +use smallvec::SmallVec; use std::cmp::Ordering; use std::collections::HashMap; use std::net::SocketAddr; use std::pin::Pin; use std::sync::Arc; use std::time::Duration; - -use anyhow::Context; -use bytes::Bytes; -use smallvec::SmallVec; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::net::TcpStream; use tokio::time; use tracing::{debug, error, info, warn}; use uuid::Uuid; -use amqp_core::message::{RawMessage, RoutingInformation}; -use amqp_core::methods::{FieldValue, Method, Table}; -use amqp_core::GlobalData; - -use crate::error::{ConException, ProtocolError, Result}; -use crate::frame::{ChannelId, ContentHeader, Frame, FrameType}; -use crate::{frame, methods, sasl}; - fn ensure_conn(condition: bool) -> Result<()> { if condition { Ok(()) } else { - Err(ConException::Todo.into_trans()) + Err(ConException::Todo.into()) } } @@ -167,7 +164,7 @@ impl Connection { let plain_user = sasl::parse_sasl_plain_response(&response)?; info!(username = %plain_user.authentication_identity, "SASL Authentication successful") } else { - return Err(ConException::Todo.into_trans()); + return Err(ConException::Todo.into()); } Ok(()) @@ -262,21 +259,20 @@ impl Connection { Some(channel) => { channel.status = ChannelStatus::NeedHeader(BASIC_CLASS_ID, Box::new(method)) } - None => return Err(ConException::Todo.into_trans()), + None => return Err(ConException::Todo.into()), }, _ => { let channel_handle = self .channels .get(&frame.channel) - .ok_or_else(|| ConException::Todo.into_trans())? + .ok_or(ConException::Todo)? .handle .clone(); - tokio::spawn(amqp_messaging::methods::handle_method( - channel_handle, - method, - )); - // we don't handle this here, forward it to *somewhere* + // call into amqp_messaging to handle the method + // amqp_messaging then branches and spawns a new task for longer running things, + // so the connection task will only be "blocked" for a short amount of time + amqp_messaging::methods::handle_method(channel_handle, method).await?; } } Ok(()) @@ -285,11 +281,11 @@ impl Connection { fn dispatch_header(&mut self, frame: Frame) -> Result<()> { self.channels .get_mut(&frame.channel) - .ok_or_else(|| ConException::Todo.into_trans()) + .ok_or_else(|| ConException::Todo.into()) .and_then(|channel| match channel.status.take() { ChannelStatus::Default => { warn!(channel = %frame.channel, "unexpected header"); - Err(ConException::UnexpectedFrame.into_trans()) + Err(ConException::UnexpectedFrame.into()) } ChannelStatus::NeedHeader(class_id, method) => { let header = ContentHeader::parse(&frame.payload)?; @@ -300,7 +296,7 @@ impl Connection { } ChannelStatus::NeedsBody(_, _, _) => { warn!(channel = %frame.channel, "already got header"); - Err(ConException::UnexpectedFrame.into_trans()) + Err(ConException::UnexpectedFrame.into()) } }) } @@ -309,16 +305,16 @@ impl Connection { let channel = self .channels .get_mut(&frame.channel) - .ok_or_else(|| ConException::Todo.into_trans())?; + .ok_or(ConException::Todo)?; match channel.status.take() { ChannelStatus::Default => { warn!(channel = %frame.channel, "unexpected body"); - Err(ConException::UnexpectedFrame.into_trans()) + Err(ConException::UnexpectedFrame.into()) } ChannelStatus::NeedHeader(_, _) => { warn!(channel = %frame.channel, "unexpected body"); - Err(ConException::UnexpectedFrame.into_trans()) + Err(ConException::UnexpectedFrame.into()) } ChannelStatus::NeedsBody(method, header, mut vec) => { vec.push(frame.payload); @@ -331,7 +327,7 @@ impl Connection { Ordering::Equal => { self.process_method_with_body(*method, *header, vec, frame.channel) } - Ordering::Greater => Err(ConException::Todo.into_trans()), + Ordering::Greater => Err(ConException::Todo.into()), Ordering::Less => Ok(()), // wait for next body } } @@ -369,10 +365,7 @@ impl Connection { }; let message = Arc::new(message); - let channel = self - .channels - .get(&channel) - .ok_or_else(|| ConException::Todo.into_trans())?; + let channel = self.channels.get(&channel).ok_or(ConException::Todo)?; // Spawn the handler for the publish. The connection task goes back to handling // just the connection. @@ -382,7 +375,7 @@ impl Connection { )); Ok(()) } else { - Err(ConException::Todo.into_trans()) + Err(ConException::Todo.into()) } } @@ -403,7 +396,7 @@ impl Connection { let prev = self.channels.insert(channel_id, channel); if let Some(prev) = prev { self.channels.insert(channel_id, prev); // restore previous state - return Err(ConException::ChannelError.into_trans()); + return Err(ConException::ChannelError.into()); } { @@ -444,7 +437,7 @@ impl Connection { drop(channel); self.send_method(channel_id, Method::ChannelCloseOk).await?; } else { - return Err(ConException::Todo.into_trans()); + return Err(ConException::Todo.into()); } } else { unreachable!() diff --git a/amqp_transport/src/error.rs b/amqp_transport/src/error.rs index 188db0a..419ebf0 100644 --- a/amqp_transport/src/error.rs +++ b/amqp_transport/src/error.rs @@ -2,6 +2,8 @@ use std::io::Error; +pub use amqp_core::error::{ConException, ProtocolError}; + pub type StdResult = std::result::Result; pub type Result = StdResult; @@ -9,7 +11,7 @@ pub type Result = StdResult; #[derive(Debug, thiserror::Error)] pub enum TransError { #[error("{0}")] - Invalid(#[from] ProtocolError), + Protocol(#[from] ProtocolError), #[error("connection error: `{0}`")] Other(#[from] anyhow::Error), } @@ -20,42 +22,8 @@ impl From for TransError { } } -#[derive(Debug, thiserror::Error)] -pub enum ProtocolError { - #[error("fatal error")] - Fatal, - #[error("{0}")] - ConException(#[from] ConException), - #[error("{0}")] - ChannelException(#[from] ChannelException), - #[error("Connection must be closed")] - CloseNow, - #[error("Graceful connection closing requested")] - GracefulClose, -} - -#[derive(Debug, thiserror::Error)] -pub enum ConException { - #[error("501 Frame error")] - FrameError, - #[error("503 Command invalid")] - CommandInvalid, - #[error("503 Syntax error | {0:?}")] - /// A method was received but there was a syntax error. The string stores where it occurred. - SyntaxError(Vec), - #[error("504 Channel error")] - ChannelError, - #[error("505 Unexpected Frame")] - UnexpectedFrame, - #[error("xxx Not decided yet")] - Todo, -} - -impl ConException { - pub fn into_trans(self) -> TransError { - TransError::Invalid(ProtocolError::ConException(self)) +impl From for TransError { + fn from(err: ConException) -> Self { + Self::Protocol(ProtocolError::ConException(err)) } } - -#[derive(Debug, thiserror::Error)] -pub enum ChannelException {} diff --git a/amqp_transport/src/frame.rs b/amqp_transport/src/frame.rs index df81f8b..b4f71ff 100644 --- a/amqp_transport/src/frame.rs +++ b/amqp_transport/src/frame.rs @@ -148,14 +148,11 @@ impl ContentHeader { Ok((_, _)) => { Err( ConException::SyntaxError(vec!["could not consume all input".to_string()]) - .into_trans(), + .into(), ) } Err(nom::Err::Incomplete(_)) => { - Err( - ConException::SyntaxError(vec!["there was not enough data".to_string()]) - .into_trans(), - ) + Err(ConException::SyntaxError(vec!["there was not enough data".to_string()]).into()) } Err(nom::Err::Failure(err) | nom::Err::Error(err)) => Err(err), } @@ -197,7 +194,7 @@ where } if max_frame_size != 0 && payload.len() > max_frame_size { - return Err(ConException::FrameError.into_trans()); + return Err(ConException::FrameError.into()); } let kind = parse_frame_type(kind, channel)?; @@ -225,7 +222,7 @@ fn parse_frame_type(kind: u8, channel: ChannelId) -> Result { Err(ProtocolError::ConException(ConException::FrameError).into()) } } - _ => Err(ConException::FrameError.into_trans()), + _ => Err(ConException::FrameError.into()), } } diff --git a/amqp_transport/src/methods/mod.rs b/amqp_transport/src/methods/mod.rs index f413f91..f2cd587 100644 --- a/amqp_transport/src/methods/mod.rs +++ b/amqp_transport/src/methods/mod.rs @@ -1,4 +1,5 @@ -use crate::error::{ConException, TransError}; +use crate::error::TransError; +use amqp_core::error::ConException; use amqp_core::methods::{FieldValue, Method, Table}; use rand::Rng; use std::collections::HashMap; @@ -18,16 +19,10 @@ pub fn parse_method(payload: &[u8]) -> Result { match nom_result { Ok(([], method)) => Ok(method), Ok((_, _)) => { - Err( - ConException::SyntaxError(vec!["could not consume all input".to_string()]) - .into_trans(), - ) + Err(ConException::SyntaxError(vec!["could not consume all input".to_string()]).into()) } Err(nom::Err::Incomplete(_)) => { - Err( - ConException::SyntaxError(vec!["there was not enough data".to_string()]) - .into_trans(), - ) + Err(ConException::SyntaxError(vec!["there was not enough data".to_string()]).into()) } Err(nom::Err::Failure(err) | nom::Err::Error(err)) => Err(err), } diff --git a/amqp_transport/src/methods/parse_helper.rs b/amqp_transport/src/methods/parse_helper.rs index f59d694..0638d43 100644 --- a/amqp_transport/src/methods/parse_helper.rs +++ b/amqp_transport/src/methods/parse_helper.rs @@ -1,5 +1,6 @@ -use crate::error::{ConException, ProtocolError, TransError}; +use crate::error::TransError; use crate::methods::generated::parse::IResult; +use amqp_core::error::{ConException, ProtocolError}; use amqp_core::methods::{ Bit, FieldValue, Long, Longlong, Longstr, Octet, Short, Shortstr, Table, TableFieldName, Timestamp, @@ -15,7 +16,7 @@ use std::collections::HashMap; impl nom::error::ParseError for TransError { fn from_error_kind(_input: T, _kind: ErrorKind) -> Self { - ConException::SyntaxError(vec![]).into_trans() + ConException::SyntaxError(vec![]).into() } fn append(_input: T, _kind: ErrorKind, other: Self) -> Self { @@ -28,7 +29,7 @@ pub fn fail_err>(msg: S) -> impl FnOnce(Err) -> Err< let msg = msg.into(); let stack = match err { Err::Error(e) | Err::Failure(e) => match e { - TransError::Invalid(ProtocolError::ConException(ConException::SyntaxError( + TransError::Protocol(ProtocolError::ConException(ConException::SyntaxError( mut stack, ))) => { stack.push(msg); @@ -38,20 +39,20 @@ pub fn fail_err>(msg: S) -> impl FnOnce(Err) -> Err< }, _ => vec![msg], }; - Err::Failure(ConException::SyntaxError(stack).into_trans()) + Err::Failure(ConException::SyntaxError(stack).into()) } } pub fn err_other>(msg: S) -> impl FnOnce(E) -> Err { - move |_| Err::Error(ConException::SyntaxError(vec![msg.into()]).into_trans()) + move |_| Err::Error(ConException::SyntaxError(vec![msg.into()]).into()) } #[macro_export] macro_rules! fail { ($cause:expr) => { return Err(nom::Err::Failure( - crate::error::ProtocolError::ConException(crate::error::ConException::SyntaxError( - vec![String::from($cause)], - )) + ::amqp_core::error::ProtocolError::ConException( + ::amqp_core::error::ConException::SyntaxError(vec![String::from($cause)]), + ) .into(), )) }; diff --git a/amqp_transport/src/sasl.rs b/amqp_transport/src/sasl.rs index 970d113..18d6992 100644 --- a/amqp_transport/src/sasl.rs +++ b/amqp_transport/src/sasl.rs @@ -2,7 +2,8 @@ //! //! Currently only supports PLAIN (see [RFC 4616](https://datatracker.ietf.org/doc/html/rfc4616)) -use crate::error::{ConException, Result}; +use crate::error::Result; +use amqp_core::error::ConException; pub struct PlainUser { pub authorization_identity: String, @@ -13,17 +14,11 @@ pub struct PlainUser { pub fn parse_sasl_plain_response(response: &[u8]) -> Result { let mut parts = response .split(|&n| n == 0) - .map(|bytes| String::from_utf8(bytes.into()).map_err(|_| ConException::Todo.into_trans())); + .map(|bytes| String::from_utf8(bytes.into()).map_err(|_| ConException::Todo)); - let authorization_identity = parts - .next() - .ok_or_else(|| ConException::Todo.into_trans())??; - let authentication_identity = parts - .next() - .ok_or_else(|| ConException::Todo.into_trans())??; - let password = parts - .next() - .ok_or_else(|| ConException::Todo.into_trans())??; + let authorization_identity = parts.next().ok_or_else(|| ConException::Todo)??; + let authentication_identity = parts.next().ok_or_else(|| ConException::Todo)??; + let password = parts.next().ok_or_else(|| ConException::Todo)??; Ok(PlainUser { authorization_identity, diff --git a/test-js/src/open-channel.js b/test-js/src/open-channel.js index 3d47839..0e636ec 100644 --- a/test-js/src/open-channel.js +++ b/test-js/src/open-channel.js @@ -1,4 +1,4 @@ -import { connectAmqp, sleep } from './utils/utils.js'; +import { connectAmqp } from './utils/utils.js'; const connection = await connectAmqp(); diff --git a/xtask/src/test_js.rs b/xtask/src/test_js.rs index c625d48..1a1f63e 100644 --- a/xtask/src/test_js.rs +++ b/xtask/src/test_js.rs @@ -9,6 +9,7 @@ pub fn main() -> Result<()> { let mut amqp_server = Command::new("cargo") .arg("run") + .env("RUST_LOG", "trace") .spawn() .context("`cargo run` amqp")?; From 8532d454c3ace2390da08572ead4e719150f4f43 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Sat, 26 Feb 2022 21:55:17 +0100 Subject: [PATCH 05/11] queue declare --- Cargo.lock | 3 + amqp_core/Cargo.toml | 1 + amqp_core/src/lib.rs | 16 +++ amqp_core/src/queue.rs | 1 + amqp_messaging/Cargo.toml | 4 +- amqp_messaging/src/methods.rs | 60 ---------- amqp_messaging/src/methods/consume.rs | 17 +++ amqp_messaging/src/methods/mod.rs | 125 +++++++++++++++++++++ amqp_messaging/src/methods/queue.rs | 80 +++++++++++++ amqp_transport/src/connection.rs | 4 +- amqp_transport/src/lib.rs | 3 +- amqp_transport/src/methods/parse_helper.rs | 6 +- amqp_transport/src/sasl.rs | 6 +- 13 files changed, 255 insertions(+), 71 deletions(-) delete mode 100644 amqp_messaging/src/methods.rs create mode 100644 amqp_messaging/src/methods/consume.rs create mode 100644 amqp_messaging/src/methods/mod.rs create mode 100644 amqp_messaging/src/methods/queue.rs diff --git a/Cargo.lock b/Cargo.lock index d24442f..4c40ca7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -30,6 +30,7 @@ version = "0.1.0" dependencies = [ "bytes", "parking_lot", + "rand", "smallvec", "thiserror", "uuid", @@ -51,8 +52,10 @@ name = "amqp_messaging" version = "0.1.0" dependencies = [ "amqp_core", + "parking_lot", "tokio", "tracing", + "uuid", ] [[package]] diff --git a/amqp_core/Cargo.toml b/amqp_core/Cargo.toml index ce92335..3adb383 100644 --- a/amqp_core/Cargo.toml +++ b/amqp_core/Cargo.toml @@ -8,6 +8,7 @@ edition = "2021" [dependencies] bytes = "1.1.0" parking_lot = "0.12.0" +rand = "0.8.5" smallvec = { version = "1.8.0", features = ["union"] } thiserror = "1.0.30" uuid = "0.8.2" diff --git a/amqp_core/src/lib.rs b/amqp_core/src/lib.rs index fc875f1..1901e88 100644 --- a/amqp_core/src/lib.rs +++ b/amqp_core/src/lib.rs @@ -25,6 +25,8 @@ impl Default for GlobalData { inner: Arc::new(Mutex::new(GlobalDataInner { connections: HashMap::new(), channels: HashMap::new(), + queues: HashMap::new(), + default_exchange: HashMap::new(), })), } } @@ -40,6 +42,9 @@ impl GlobalData { pub struct GlobalDataInner { pub connections: HashMap, pub channels: HashMap, + pub queues: HashMap, + /// Todo: This is just for testing and will be removed later! + pub default_exchange: HashMap, } pub type ConnectionHandle = Handle; @@ -104,3 +109,14 @@ impl Channel { global_data.channels.remove(&self.id); } } + +pub fn gen_uuid() -> Uuid { + Uuid::from_bytes(rand::random()) +} + +#[macro_export] +macro_rules! amqp_todo { + () => { + return Err(::amqp_core::error::ConException::NotImplemented.into()) + }; +} diff --git a/amqp_core/src/queue.rs b/amqp_core/src/queue.rs index f8074a8..ecfa9c9 100644 --- a/amqp_core/src/queue.rs +++ b/amqp_core/src/queue.rs @@ -12,6 +12,7 @@ pub struct RawQueue { pub name: String, pub messages: Mutex>, // use a concurrent linked list??? pub durable: bool, + pub exclusive: Option, /// Whether the queue will automatically be deleted when no consumers uses it anymore. /// The queue can always be manually deleted. /// If auto-delete is enabled, it keeps track of the consumer count. diff --git a/amqp_messaging/Cargo.toml b/amqp_messaging/Cargo.toml index 4e17b35..b5a5eba 100644 --- a/amqp_messaging/Cargo.toml +++ b/amqp_messaging/Cargo.toml @@ -7,5 +7,7 @@ edition = "2021" [dependencies] amqp_core = { path = "../amqp_core" } +parking_lot = "0.12.0" tracing = "0.1.31" -tokio = { version = "1.17.0", features = ["full"] } \ No newline at end of file +tokio = { version = "1.17.0", features = ["full"] } +uuid = "0.8.2" \ No newline at end of file diff --git a/amqp_messaging/src/methods.rs b/amqp_messaging/src/methods.rs deleted file mode 100644 index cbea527..0000000 --- a/amqp_messaging/src/methods.rs +++ /dev/null @@ -1,60 +0,0 @@ -use amqp_core::error::{ConException, ProtocolError}; -use amqp_core::message::Message; -use amqp_core::methods::Method; -use amqp_core::ChannelHandle; -use tracing::{debug, info}; - -pub async fn handle_basic_publish(_channel_handle: ChannelHandle, message: Message) { - info!( - ?message, - "Someone has summoned the almighty Basic.Publish handler" - ); -} - -pub async fn handle_method( - _channel_handle: ChannelHandle, - method: Method, -) -> Result<(), ProtocolError> { - match method { - Method::ExchangeDeclare { .. } => Err(ConException::NotImplemented.into()), - Method::ExchangeDeclareOk => Err(ConException::NotImplemented.into()), - Method::ExchangeDelete { .. } => Err(ConException::NotImplemented.into()), - Method::ExchangeDeleteOk => Err(ConException::NotImplemented.into()), - Method::QueueDeclare { .. } => Err(ConException::NotImplemented.into()), - Method::QueueDeclareOk { .. } => Err(ConException::NotImplemented.into()), - Method::QueueBind { .. } => Err(ConException::NotImplemented.into()), - Method::QueueBindOk => Err(ConException::NotImplemented.into()), - Method::QueueUnbind { .. } => Err(ConException::NotImplemented.into()), - Method::QueueUnbindOk => Err(ConException::NotImplemented.into()), - Method::QueuePurge { .. } => Err(ConException::NotImplemented.into()), - Method::QueuePurgeOk { .. } => Err(ConException::NotImplemented.into()), - Method::QueueDelete { .. } => Err(ConException::NotImplemented.into()), - Method::QueueDeleteOk { .. } => Err(ConException::NotImplemented.into()), - Method::BasicQos { .. } => Err(ConException::NotImplemented.into()), - Method::BasicQosOk => Err(ConException::NotImplemented.into()), - Method::BasicConsume { .. } => Err(ConException::NotImplemented.into()), - Method::BasicConsumeOk { .. } => Err(ConException::NotImplemented.into()), - Method::BasicCancel { .. } => Err(ConException::NotImplemented.into()), - Method::BasicCancelOk { .. } => Err(ConException::NotImplemented.into()), - Method::BasicReturn { .. } => Err(ConException::NotImplemented.into()), - Method::BasicDeliver { .. } => Err(ConException::NotImplemented.into()), - Method::BasicGet { .. } => Err(ConException::NotImplemented.into()), - Method::BasicGetOk { .. } => Err(ConException::NotImplemented.into()), - Method::BasicGetEmpty { .. } => Err(ConException::NotImplemented.into()), - Method::BasicAck { .. } => Err(ConException::NotImplemented.into()), - Method::BasicReject { .. } => Err(ConException::NotImplemented.into()), - Method::BasicRecoverAsync { .. } => Err(ConException::NotImplemented.into()), - Method::BasicRecover { .. } => Err(ConException::NotImplemented.into()), - Method::BasicRecoverOk => Err(ConException::NotImplemented.into()), - Method::TxSelect - | Method::TxSelectOk - | Method::TxCommit - | Method::TxCommitOk - | Method::TxRollback - | Method::TxRollbackOk => Err(ConException::NotImplemented.into()), - Method::BasicPublish { .. } => { - unreachable!("Basic.Publish is handled somewhere else because it has a body") - } - _ => unreachable!("Method handled by transport layer"), - } -} diff --git a/amqp_messaging/src/methods/consume.rs b/amqp_messaging/src/methods/consume.rs new file mode 100644 index 0000000..7148705 --- /dev/null +++ b/amqp_messaging/src/methods/consume.rs @@ -0,0 +1,17 @@ +use amqp_core::error::ProtocolError; +use amqp_core::methods::{Bit, ConsumerTag, NoAck, NoLocal, NoWait, QueueName, Table}; +use amqp_core::ChannelHandle; + +#[allow(clippy::too_many_arguments)] +pub async fn consume( + _channel_handle: ChannelHandle, + _queue: QueueName, + _consumer_tag: ConsumerTag, + _no_local: NoLocal, + _no_ack: NoAck, + _exclusive: Bit, + _no_wait: NoWait, + _arguments: Table, +) -> Result<(), ProtocolError> { + Ok(()) +} diff --git a/amqp_messaging/src/methods/mod.rs b/amqp_messaging/src/methods/mod.rs new file mode 100644 index 0000000..9a4c57d --- /dev/null +++ b/amqp_messaging/src/methods/mod.rs @@ -0,0 +1,125 @@ +mod consume; +mod queue; + +use amqp_core::amqp_todo; +use amqp_core::error::ProtocolError; +use amqp_core::message::Message; +use amqp_core::methods::Method; +use amqp_core::ChannelHandle; +use tracing::info; + +pub async fn handle_basic_publish(_channel_handle: ChannelHandle, message: Message) { + info!( + ?message, + "Someone has summoned the almighty Basic.Publish handler" + ); +} + +pub async fn handle_method( + channel_handle: ChannelHandle, + method: Method, +) -> Result<(), ProtocolError> { + info!(?method, "Handling method"); + + match method { + Method::ExchangeDeclare { .. } => amqp_todo!(), + Method::ExchangeDeclareOk => amqp_todo!(), + Method::ExchangeDelete { .. } => amqp_todo!(), + Method::ExchangeDeleteOk => amqp_todo!(), + Method::QueueDeclare { + queue, + passive, + durable, + exclusive, + auto_delete, + no_wait, + arguments, + .. + } => { + queue::declare( + channel_handle, + queue, + passive, + durable, + exclusive, + auto_delete, + no_wait, + arguments, + ) + .await + } + Method::QueueDeclareOk { .. } => amqp_todo!(), + Method::QueueBind { + queue, + exchange, + routing_key, + no_wait, + arguments, + .. + } => { + queue::bind( + channel_handle, + queue, + exchange, + routing_key, + no_wait, + arguments, + ) + .await + } + Method::QueueBindOk => amqp_todo!(), + Method::QueueUnbind { .. } => amqp_todo!(), + Method::QueueUnbindOk => amqp_todo!(), + Method::QueuePurge { .. } => amqp_todo!(), + Method::QueuePurgeOk { .. } => amqp_todo!(), + Method::QueueDelete { .. } => amqp_todo!(), + Method::QueueDeleteOk { .. } => amqp_todo!(), + Method::BasicQos { .. } => amqp_todo!(), + Method::BasicQosOk => amqp_todo!(), + Method::BasicConsume { + queue, + consumer_tag, + no_local, + no_ack, + exclusive, + no_wait, + arguments, + .. + } => { + consume::consume( + channel_handle, + queue, + consumer_tag, + no_local, + no_ack, + exclusive, + no_wait, + arguments, + ) + .await + } + Method::BasicConsumeOk { .. } => amqp_todo!(), + Method::BasicCancel { .. } => amqp_todo!(), + Method::BasicCancelOk { .. } => amqp_todo!(), + Method::BasicReturn { .. } => amqp_todo!(), + Method::BasicDeliver { .. } => amqp_todo!(), + Method::BasicGet { .. } => amqp_todo!(), + Method::BasicGetOk { .. } => amqp_todo!(), + Method::BasicGetEmpty { .. } => amqp_todo!(), + Method::BasicAck { .. } => amqp_todo!(), + Method::BasicReject { .. } => amqp_todo!(), + Method::BasicRecoverAsync { .. } => amqp_todo!(), + Method::BasicRecover { .. } => amqp_todo!(), + Method::BasicRecoverOk => amqp_todo!(), + Method::TxSelect + | Method::TxSelectOk + | Method::TxCommit + | Method::TxCommitOk + | Method::TxRollback + | Method::TxRollbackOk => amqp_todo!(), + Method::BasicPublish { .. } => { + unreachable!("Basic.Publish is handled somewhere else because it has a body") + } + _ => unreachable!("Method handled by transport layer"), + } +} diff --git a/amqp_messaging/src/methods/queue.rs b/amqp_messaging/src/methods/queue.rs new file mode 100644 index 0000000..e3ff92a --- /dev/null +++ b/amqp_messaging/src/methods/queue.rs @@ -0,0 +1,80 @@ +#![deny(clippy::future_not_send)] + +use amqp_core::error::{ConException, ProtocolError}; +use amqp_core::methods::{Bit, ExchangeName, NoWait, QueueName, Shortstr, Table}; +use amqp_core::queue::{QueueDeletion, RawQueue}; +use amqp_core::ChannelHandle; +use amqp_core::{amqp_todo, GlobalData}; +use parking_lot::Mutex; +use std::sync::atomic::AtomicUsize; +use std::sync::Arc; +use uuid::Uuid; + +#[allow(clippy::too_many_arguments)] +pub async fn declare( + channel_handle: ChannelHandle, + queue_name: QueueName, + passive: Bit, + durable: Bit, + exclusive: Bit, + auto_delete: Bit, + no_wait: NoWait, + arguments: Table, +) -> Result<(), ProtocolError> { + if !arguments.is_empty() { + return Err(ConException::Todo.into()); + } + + let (global_data, id) = { + let channel = channel_handle.lock(); + + if passive || no_wait { + amqp_todo!(); + } + + let id = amqp_core::gen_uuid(); + let queue = Arc::new(RawQueue { + id, + name: queue_name.clone(), + messages: Mutex::default(), + durable, + exclusive: exclusive.then(|| channel.id), + deletion: if auto_delete { + QueueDeletion::Auto(AtomicUsize::default()) + } else { + QueueDeletion::Manual + }, + }); + + let global_data = channel.global_data.clone(); + + { + let mut global_data_lock = global_data.lock(); + global_data_lock.queues.insert(id, queue); + } + + (global_data, id) + }; + + bind_queue(global_data, id, (), queue_name).await +} + +pub async fn bind( + _channel_handle: ChannelHandle, + _queue: QueueName, + _exchange: ExchangeName, + _routing_key: Shortstr, + _no_wait: NoWait, + _arguments: Table, +) -> Result<(), ProtocolError> { + amqp_todo!(); +} + +async fn bind_queue( + _global_data: GlobalData, + _queue: Uuid, + _exchange: (), + _routing_key: String, +) -> Result<(), ProtocolError> { + amqp_todo!(); +} diff --git a/amqp_transport/src/connection.rs b/amqp_transport/src/connection.rs index 1b5aac4..fd20366 100644 --- a/amqp_transport/src/connection.rs +++ b/amqp_transport/src/connection.rs @@ -353,7 +353,7 @@ impl Connection { } = method { let message = RawMessage { - id: Uuid::from_bytes(rand::random()), + id: amqp_core::gen_uuid(), properties: header.property_fields, routing: RoutingInformation { exchange, @@ -380,7 +380,7 @@ impl Connection { } async fn channel_open(&mut self, channel_id: ChannelId) -> Result<()> { - let id = Uuid::from_bytes(rand::random()); + let id = amqp_core::gen_uuid(); let channel_handle = amqp_core::Channel::new_handle( id, channel_id.num(), diff --git a/amqp_transport/src/lib.rs b/amqp_transport/src/lib.rs index f28c3fd..9705bc6 100644 --- a/amqp_transport/src/lib.rs +++ b/amqp_transport/src/lib.rs @@ -15,7 +15,6 @@ use amqp_core::GlobalData; use anyhow::Result; use tokio::net; use tracing::{info, info_span, Instrument}; -use uuid::Uuid; pub async fn do_thing_i_guess(global_data: GlobalData) -> Result<()> { info!("Binding TCP listener..."); @@ -25,7 +24,7 @@ pub async fn do_thing_i_guess(global_data: GlobalData) -> Result<()> { loop { let (stream, peer_addr) = listener.accept().await?; - let id = Uuid::from_bytes(rand::random()); + let id = amqp_core::gen_uuid(); info!(local_addr = ?stream.local_addr(), %id, "Accepted new connection"); let span = info_span!("client-connection", %id); diff --git a/amqp_transport/src/methods/parse_helper.rs b/amqp_transport/src/methods/parse_helper.rs index 0638d43..dfdc3d0 100644 --- a/amqp_transport/src/methods/parse_helper.rs +++ b/amqp_transport/src/methods/parse_helper.rs @@ -42,8 +42,8 @@ pub fn fail_err>(msg: S) -> impl FnOnce(Err) -> Err< Err::Failure(ConException::SyntaxError(stack).into()) } } -pub fn err_other>(msg: S) -> impl FnOnce(E) -> Err { - move |_| Err::Error(ConException::SyntaxError(vec![msg.into()]).into()) +pub fn other_fail>(msg: S) -> impl FnOnce(E) -> Err { + move |_| Err::Failure(ConException::SyntaxError(vec![msg.into()]).into()) } #[macro_export] @@ -105,7 +105,7 @@ pub fn bit(input: &[u8], amount: usize) -> IResult<'_, Vec> { pub fn shortstr(input: &[u8]) -> IResult<'_, Shortstr> { let (input, len) = u8(input)?; let (input, str_data) = take(usize::from(len))(input)?; - let data = String::from_utf8(str_data.into()).map_err(err_other("shortstr"))?; + let data = String::from_utf8(str_data.into()).map_err(other_fail("shortstr"))?; Ok((input, data)) } diff --git a/amqp_transport/src/sasl.rs b/amqp_transport/src/sasl.rs index 18d6992..494c433 100644 --- a/amqp_transport/src/sasl.rs +++ b/amqp_transport/src/sasl.rs @@ -16,9 +16,9 @@ pub fn parse_sasl_plain_response(response: &[u8]) -> Result { .split(|&n| n == 0) .map(|bytes| String::from_utf8(bytes.into()).map_err(|_| ConException::Todo)); - let authorization_identity = parts.next().ok_or_else(|| ConException::Todo)??; - let authentication_identity = parts.next().ok_or_else(|| ConException::Todo)??; - let password = parts.next().ok_or_else(|| ConException::Todo)??; + let authorization_identity = parts.next().ok_or(ConException::Todo)??; + let authentication_identity = parts.next().ok_or(ConException::Todo)??; + let password = parts.next().ok_or(ConException::Todo)??; Ok(PlainUser { authorization_identity, From 6d944e1265b6c548ee0f4ff5cbb3ea8e93ad82b8 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Sat, 26 Feb 2022 22:26:35 +0100 Subject: [PATCH 06/11] some cleanup --- amqp_core/src/connection.rs | 99 +++++++++++++++++++++++++++ amqp_core/src/lib.rs | 88 +++--------------------- amqp_core/src/macros.rs | 32 +++++++++ amqp_core/src/message.rs | 6 +- amqp_core/src/queue.rs | 8 ++- amqp_messaging/src/methods/consume.rs | 2 +- amqp_messaging/src/methods/mod.rs | 2 +- amqp_messaging/src/methods/queue.rs | 9 ++- amqp_transport/src/connection.rs | 52 +++++++------- amqp_transport/src/frame.rs | 35 ++-------- amqp_transport/src/lib.rs | 4 +- amqp_transport/src/tests.rs | 4 +- 12 files changed, 190 insertions(+), 151 deletions(-) create mode 100644 amqp_core/src/connection.rs create mode 100644 amqp_core/src/macros.rs diff --git a/amqp_core/src/connection.rs b/amqp_core/src/connection.rs new file mode 100644 index 0000000..615f670 --- /dev/null +++ b/amqp_core/src/connection.rs @@ -0,0 +1,99 @@ +use crate::{newtype_id, GlobalData, Handle, Queue}; +use parking_lot::Mutex; +use std::collections::HashMap; +use std::fmt::{Display, Formatter}; +use std::net::SocketAddr; +use std::sync::Arc; + +newtype_id!(pub ConnectionId); +newtype_id!(pub ChannelId); + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct ChannelNum(u16); + +impl ChannelNum { + pub fn new(num: u16) -> Self { + Self(num) + } + + pub fn num(self) -> u16 { + self.0 + } + + pub fn is_zero(self) -> bool { + self.0 == 0 + } + + pub fn zero() -> Self { + Self(0) + } +} + +impl Display for ChannelNum { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + self.0.fmt(f) + } +} + +pub type ConnectionHandle = Handle; + +#[derive(Debug)] +pub struct Connection { + pub id: ConnectionId, + pub peer_addr: SocketAddr, + pub global_data: GlobalData, + pub channels: HashMap, + pub exclusive_queues: Vec, +} + +impl Connection { + pub fn new_handle( + id: ConnectionId, + peer_addr: SocketAddr, + global_data: GlobalData, + ) -> ConnectionHandle { + Arc::new(Mutex::new(Self { + id, + peer_addr, + global_data, + channels: HashMap::new(), + exclusive_queues: vec![], + })) + } + + pub fn close(&self) { + let mut global_data = self.global_data.lock(); + global_data.connections.remove(&self.id); + } +} + +pub type ChannelHandle = Handle; + +#[derive(Debug)] +pub struct Channel { + pub id: ChannelId, + pub num: u16, + pub connection: ConnectionHandle, + pub global_data: GlobalData, +} + +impl Channel { + pub fn new_handle( + id: ChannelId, + num: u16, + connection: ConnectionHandle, + global_data: GlobalData, + ) -> ChannelHandle { + Arc::new(Mutex::new(Self { + id, + num, + connection, + global_data, + })) + } + + pub fn close(&self) { + let mut global_data = self.global_data.lock(); + global_data.channels.remove(&self.id); + } +} diff --git a/amqp_core/src/lib.rs b/amqp_core/src/lib.rs index 1901e88..90d1480 100644 --- a/amqp_core/src/lib.rs +++ b/amqp_core/src/lib.rs @@ -1,16 +1,18 @@ #![warn(rust_2018_idioms)] +pub mod connection; pub mod error; +mod macros; pub mod message; pub mod methods; pub mod queue; -use crate::queue::Queue; +use crate::connection::{ChannelHandle, ConnectionHandle}; +use crate::queue::{Queue, QueueId}; +use connection::{ChannelId, ConnectionId}; use parking_lot::Mutex; use std::collections::HashMap; -use std::net::SocketAddr; use std::sync::Arc; -use uuid::Uuid; type Handle = Arc>; @@ -40,83 +42,9 @@ impl GlobalData { #[derive(Debug)] pub struct GlobalDataInner { - pub connections: HashMap, - pub channels: HashMap, - pub queues: HashMap, + pub connections: HashMap, + pub channels: HashMap, + pub queues: HashMap, /// Todo: This is just for testing and will be removed later! pub default_exchange: HashMap, } - -pub type ConnectionHandle = Handle; - -#[derive(Debug)] -pub struct Connection { - pub id: Uuid, - pub peer_addr: SocketAddr, - pub global_data: GlobalData, - pub channels: HashMap, - pub exclusive_queues: Vec, -} - -impl Connection { - pub fn new_handle( - id: Uuid, - peer_addr: SocketAddr, - global_data: GlobalData, - ) -> ConnectionHandle { - Arc::new(Mutex::new(Self { - id, - peer_addr, - global_data, - channels: HashMap::new(), - exclusive_queues: vec![], - })) - } - - pub fn close(&self) { - let mut global_data = self.global_data.lock(); - global_data.connections.remove(&self.id); - } -} - -pub type ChannelHandle = Handle; - -#[derive(Debug)] -pub struct Channel { - pub id: Uuid, - pub num: u16, - pub connection: ConnectionHandle, - pub global_data: GlobalData, -} - -impl Channel { - pub fn new_handle( - id: Uuid, - num: u16, - connection: ConnectionHandle, - global_data: GlobalData, - ) -> ChannelHandle { - Arc::new(Mutex::new(Self { - id, - num, - connection, - global_data, - })) - } - - pub fn close(&self) { - let mut global_data = self.global_data.lock(); - global_data.channels.remove(&self.id); - } -} - -pub fn gen_uuid() -> Uuid { - Uuid::from_bytes(rand::random()) -} - -#[macro_export] -macro_rules! amqp_todo { - () => { - return Err(::amqp_core::error::ConException::NotImplemented.into()) - }; -} diff --git a/amqp_core/src/macros.rs b/amqp_core/src/macros.rs new file mode 100644 index 0000000..2a4c2bd --- /dev/null +++ b/amqp_core/src/macros.rs @@ -0,0 +1,32 @@ +#[macro_export] +macro_rules! newtype_id { + ($vis:vis $name:ident) => { + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] + $vis struct $name(::uuid::Uuid); + + impl $name { + pub fn random() -> Self { + ::rand::random() + } + } + + impl ::std::fmt::Display for $name { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + self.0.fmt(f) + } + } + + impl ::rand::prelude::Distribution<$name> for ::rand::distributions::Standard { + fn sample(&self, rng: &mut R) -> $name { + $name(::uuid::Uuid::from_bytes(rng.gen())) + } + } + }; +} + +#[macro_export] +macro_rules! amqp_todo { + () => { + return Err(::amqp_core::error::ConException::NotImplemented.into()) + }; +} diff --git a/amqp_core/src/message.rs b/amqp_core/src/message.rs index 2b190f2..190328e 100644 --- a/amqp_core/src/message.rs +++ b/amqp_core/src/message.rs @@ -1,16 +1,18 @@ #![allow(dead_code)] use crate::methods; +use crate::newtype_id; use bytes::Bytes; use smallvec::SmallVec; use std::sync::Arc; -use uuid::Uuid; pub type Message = Arc; +newtype_id!(pub MessageId); + #[derive(Debug)] pub struct RawMessage { - pub id: Uuid, + pub id: MessageId, pub properties: methods::Table, pub routing: RoutingInformation, pub content: SmallVec<[Bytes; 1]>, diff --git a/amqp_core/src/queue.rs b/amqp_core/src/queue.rs index ecfa9c9..9b75c13 100644 --- a/amqp_core/src/queue.rs +++ b/amqp_core/src/queue.rs @@ -1,18 +1,20 @@ use crate::message::Message; +use crate::{newtype_id, ChannelId}; use parking_lot::Mutex; use std::sync::atomic::AtomicUsize; use std::sync::Arc; -use uuid::Uuid; pub type Queue = Arc; +newtype_id!(pub QueueId); + #[derive(Debug)] pub struct RawQueue { - pub id: Uuid, + pub id: QueueId, pub name: String, pub messages: Mutex>, // use a concurrent linked list??? pub durable: bool, - pub exclusive: Option, + pub exclusive: Option, /// Whether the queue will automatically be deleted when no consumers uses it anymore. /// The queue can always be manually deleted. /// If auto-delete is enabled, it keeps track of the consumer count. diff --git a/amqp_messaging/src/methods/consume.rs b/amqp_messaging/src/methods/consume.rs index 7148705..c5cd341 100644 --- a/amqp_messaging/src/methods/consume.rs +++ b/amqp_messaging/src/methods/consume.rs @@ -1,6 +1,6 @@ +use amqp_core::connection::ChannelHandle; use amqp_core::error::ProtocolError; use amqp_core::methods::{Bit, ConsumerTag, NoAck, NoLocal, NoWait, QueueName, Table}; -use amqp_core::ChannelHandle; #[allow(clippy::too_many_arguments)] pub async fn consume( diff --git a/amqp_messaging/src/methods/mod.rs b/amqp_messaging/src/methods/mod.rs index 9a4c57d..478b23a 100644 --- a/amqp_messaging/src/methods/mod.rs +++ b/amqp_messaging/src/methods/mod.rs @@ -2,10 +2,10 @@ mod consume; mod queue; use amqp_core::amqp_todo; +use amqp_core::connection::ChannelHandle; use amqp_core::error::ProtocolError; use amqp_core::message::Message; use amqp_core::methods::Method; -use amqp_core::ChannelHandle; use tracing::info; pub async fn handle_basic_publish(_channel_handle: ChannelHandle, message: Message) { diff --git a/amqp_messaging/src/methods/queue.rs b/amqp_messaging/src/methods/queue.rs index e3ff92a..08a5fc4 100644 --- a/amqp_messaging/src/methods/queue.rs +++ b/amqp_messaging/src/methods/queue.rs @@ -1,14 +1,13 @@ #![deny(clippy::future_not_send)] +use amqp_core::connection::ChannelHandle; use amqp_core::error::{ConException, ProtocolError}; use amqp_core::methods::{Bit, ExchangeName, NoWait, QueueName, Shortstr, Table}; -use amqp_core::queue::{QueueDeletion, RawQueue}; -use amqp_core::ChannelHandle; +use amqp_core::queue::{QueueDeletion, QueueId, RawQueue}; use amqp_core::{amqp_todo, GlobalData}; use parking_lot::Mutex; use std::sync::atomic::AtomicUsize; use std::sync::Arc; -use uuid::Uuid; #[allow(clippy::too_many_arguments)] pub async fn declare( @@ -32,7 +31,7 @@ pub async fn declare( amqp_todo!(); } - let id = amqp_core::gen_uuid(); + let id = QueueId::random(); let queue = Arc::new(RawQueue { id, name: queue_name.clone(), @@ -72,7 +71,7 @@ pub async fn bind( async fn bind_queue( _global_data: GlobalData, - _queue: Uuid, + _queue: QueueId, _exchange: (), _routing_key: String, ) -> Result<(), ProtocolError> { diff --git a/amqp_transport/src/connection.rs b/amqp_transport/src/connection.rs index fd20366..4268a25 100644 --- a/amqp_transport/src/connection.rs +++ b/amqp_transport/src/connection.rs @@ -1,7 +1,8 @@ use crate::error::{ConException, ProtocolError, Result}; -use crate::frame::{ChannelId, ContentHeader, Frame, FrameType}; +use crate::frame::{ContentHeader, Frame, FrameType}; use crate::{frame, methods, sasl}; -use amqp_core::message::{RawMessage, RoutingInformation}; +use amqp_core::connection::{ChannelHandle, ChannelNum, ConnectionHandle, ConnectionId}; +use amqp_core::message::{MessageId, RawMessage, RoutingInformation}; use amqp_core::methods::{FieldValue, Method, Table}; use amqp_core::GlobalData; use anyhow::Context; @@ -17,7 +18,6 @@ use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tokio::net::TcpStream; use tokio::time; use tracing::{debug, error, info, warn}; -use uuid::Uuid; fn ensure_conn(condition: bool) -> Result<()> { if condition { @@ -36,21 +36,21 @@ const BASIC_CLASS_ID: u16 = 60; pub struct Channel { /// A handle to the global channel representation. Used to remove the channel when it's dropped - handle: amqp_core::ChannelHandle, + handle: ChannelHandle, /// The current status of the channel, whether it has sent a method that expects a body status: ChannelStatus, } pub struct Connection { - id: Uuid, + id: ConnectionId, stream: TcpStream, max_frame_size: usize, heartbeat_delay: u16, channel_max: u16, /// When the next heartbeat expires next_timeout: Pin>, - channels: HashMap, - handle: amqp_core::ConnectionHandle, + channels: HashMap, + handle: ConnectionHandle, global_data: GlobalData, } @@ -71,9 +71,9 @@ impl ChannelStatus { impl Connection { pub fn new( - id: Uuid, + id: ConnectionId, stream: TcpStream, - connection_handle: amqp_core::ConnectionHandle, + connection_handle: ConnectionHandle, global_data: GlobalData, ) -> Self { Self { @@ -110,7 +110,7 @@ impl Connection { self.main_loop().await } - async fn send_method(&mut self, channel: ChannelId, method: Method) -> Result<()> { + async fn send_method(&mut self, channel: ChannelNum, method: Method) -> Result<()> { let mut payload = Vec::with_capacity(64); methods::write::write_method(method, &mut payload)?; frame::write_frame( @@ -147,7 +147,7 @@ impl Connection { }; debug!(?start_method, "Sending Start method"); - self.send_method(ChannelId::zero(), start_method).await?; + self.send_method(ChannelNum::zero(), start_method).await?; let start_ok = self.recv_method().await?; debug!(?start_ok, "Received Start-Ok"); @@ -178,7 +178,7 @@ impl Connection { }; debug!("Sending Tune method"); - self.send_method(ChannelId::zero(), tune_method).await?; + self.send_method(ChannelNum::zero(), tune_method).await?; let tune_ok = self.recv_method().await?; debug!(?tune_ok, "Received Tune-Ok method"); @@ -207,7 +207,7 @@ impl Connection { } self.send_method( - ChannelId::zero(), + ChannelNum::zero(), Method::ConnectionOpenOk { reserved_1: "".to_string(), }, @@ -249,7 +249,7 @@ impl Connection { method_id, } => { info!(%reply_code, %reply_text, %class_id, %method_id, "Closing connection"); - self.send_method(ChannelId::zero(), Method::ConnectionCloseOk {}) + self.send_method(ChannelNum::zero(), Method::ConnectionCloseOk {}) .await?; return Err(ProtocolError::GracefulClose.into()); } @@ -339,7 +339,7 @@ impl Connection { method: Method, header: ContentHeader, payloads: SmallVec<[Bytes; 1]>, - channel: ChannelId, + channel: ChannelNum, ) -> Result<()> { // The only method with content that is sent to the server is Basic.Publish. ensure_conn(header.class_id == BASIC_CLASS_ID)?; @@ -353,7 +353,7 @@ impl Connection { } = method { let message = RawMessage { - id: amqp_core::gen_uuid(), + id: MessageId::random(), properties: header.property_fields, routing: RoutingInformation { exchange, @@ -379,11 +379,11 @@ impl Connection { } } - async fn channel_open(&mut self, channel_id: ChannelId) -> Result<()> { - let id = amqp_core::gen_uuid(); - let channel_handle = amqp_core::Channel::new_handle( + async fn channel_open(&mut self, channel_num: ChannelNum) -> Result<()> { + let id = rand::random(); + let channel_handle = amqp_core::connection::Channel::new_handle( id, - channel_id.num(), + channel_num.num(), self.handle.clone(), self.global_data.clone(), ); @@ -393,9 +393,9 @@ impl Connection { status: ChannelStatus::Default, }; - let prev = self.channels.insert(channel_id, channel); + let prev = self.channels.insert(channel_num, channel); if let Some(prev) = prev { - self.channels.insert(channel_id, prev); // restore previous state + self.channels.insert(channel_num, prev); // restore previous state return Err(ConException::ChannelError.into()); } @@ -408,13 +408,13 @@ impl Connection { .unwrap() .lock() .channels - .insert(channel_id.num(), channel_handle); + .insert(channel_num.num(), channel_handle); } - info!(%channel_id, "Opened new channel"); + info!(%channel_num, "Opened new channel"); self.send_method( - channel_id, + channel_num, Method::ChannelOpenOk { reserved_1: Vec::new(), }, @@ -424,7 +424,7 @@ impl Connection { Ok(()) } - async fn channel_close(&mut self, channel_id: ChannelId, method: Method) -> Result<()> { + async fn channel_close(&mut self, channel_id: ChannelNum, method: Method) -> Result<()> { if let Method::ChannelClose { reply_code: code, reply_text: reason, diff --git a/amqp_transport/src/frame.rs b/amqp_transport/src/frame.rs index b4f71ff..e957afb 100644 --- a/amqp_transport/src/frame.rs +++ b/amqp_transport/src/frame.rs @@ -1,36 +1,13 @@ use crate::error::{ConException, ProtocolError, Result}; +use amqp_core::connection::ChannelNum; use amqp_core::methods; use anyhow::Context; use bytes::Bytes; -use std::fmt::{Display, Formatter}; use tokio::io::{AsyncReadExt, AsyncWriteExt}; use tracing::trace; const REQUIRED_FRAME_END: u8 = 0xCE; -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct ChannelId(u16); - -impl ChannelId { - pub fn num(self) -> u16 { - self.0 - } - - pub fn is_zero(self) -> bool { - self.0 == 0 - } - - pub fn zero() -> Self { - Self(0) - } -} - -impl Display for ChannelId { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - self.0.fmt(f) - } -} - mod frame_type { pub const METHOD: u8 = 1; pub const HEADER: u8 = 2; @@ -42,7 +19,7 @@ mod frame_type { pub struct Frame { /// The type of the frame including its parsed metadata. pub kind: FrameType, - pub channel: ChannelId, + pub channel: ChannelNum, /// Includes the whole payload, also including the metadata from each type. pub payload: Bytes, } @@ -181,7 +158,7 @@ where { let kind = r.read_u8().await.context("read type")?; let channel = r.read_u16().await.context("read channel")?; - let channel = ChannelId(channel); + let channel = ChannelNum::new(channel); let size = r.read_u32().await.context("read size")?; let mut payload = vec![0; size.try_into().unwrap()]; @@ -210,7 +187,7 @@ where Ok(frame) } -fn parse_frame_type(kind: u8, channel: ChannelId) -> Result { +fn parse_frame_type(kind: u8, channel: ChannelNum) -> Result { match kind { frame_type::METHOD => Ok(FrameType::Method), frame_type::HEADER => Ok(FrameType::Header), @@ -228,7 +205,7 @@ fn parse_frame_type(kind: u8, channel: ChannelId) -> Result { #[cfg(test)] mod tests { - use crate::frame::{ChannelId, Frame, FrameType}; + use crate::frame::{ChannelNum, Frame, FrameType}; use bytes::Bytes; #[tokio::test] @@ -257,7 +234,7 @@ mod tests { frame, Frame { kind: FrameType::Method, - channel: ChannelId(0), + channel: ChannelNum::new(0), payload: Bytes::from_static(&[1, 2, 3]), } ); diff --git a/amqp_transport/src/lib.rs b/amqp_transport/src/lib.rs index 9705bc6..ad498fa 100644 --- a/amqp_transport/src/lib.rs +++ b/amqp_transport/src/lib.rs @@ -24,13 +24,13 @@ pub async fn do_thing_i_guess(global_data: GlobalData) -> Result<()> { loop { let (stream, peer_addr) = listener.accept().await?; - let id = amqp_core::gen_uuid(); + let id = rand::random(); info!(local_addr = ?stream.local_addr(), %id, "Accepted new connection"); let span = info_span!("client-connection", %id); let connection_handle = - amqp_core::Connection::new_handle(id, peer_addr, global_data.clone()); + amqp_core::connection::Connection::new_handle(id, peer_addr, global_data.clone()); let mut global_data_guard = global_data.lock(); global_data_guard diff --git a/amqp_transport/src/tests.rs b/amqp_transport/src/tests.rs index 1920fe1..fd89146 100644 --- a/amqp_transport/src/tests.rs +++ b/amqp_transport/src/tests.rs @@ -1,4 +1,4 @@ -use crate::frame::{ChannelId, FrameType}; +use crate::frame::{ChannelNum, FrameType}; use crate::{frame, methods}; use amqp_core::methods::{FieldValue, Method}; use std::collections::HashMap; @@ -21,7 +21,7 @@ async fn write_start_ok_frame() { let frame = frame::Frame { kind: FrameType::Method, - channel: ChannelId::zero(), + channel: ChannelNum::zero(), payload: payload.into(), }; From de027d9f5a84ec2abe882dde6c4e97f8f2272c44 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Sat, 26 Feb 2022 23:24:08 +0100 Subject: [PATCH 07/11] more cleanup --- amqp_core/src/connection.rs | 6 +++++ amqp_core/src/macros.rs | 29 +++++++++++++++++++++- amqp_messaging/src/methods/queue.rs | 2 -- amqp_transport/src/connection.rs | 4 +-- amqp_transport/src/frame.rs | 4 +-- amqp_transport/src/methods/mod.rs | 5 ++-- amqp_transport/src/methods/parse_helper.rs | 5 ++-- amqp_transport/src/tests.rs | 3 ++- src/main.rs | 2 +- 9 files changed, 46 insertions(+), 14 deletions(-) diff --git a/amqp_core/src/connection.rs b/amqp_core/src/connection.rs index 615f670..79fb1c6 100644 --- a/amqp_core/src/connection.rs +++ b/amqp_core/src/connection.rs @@ -12,18 +12,22 @@ newtype_id!(pub ChannelId); pub struct ChannelNum(u16); impl ChannelNum { + #[must_use] pub fn new(num: u16) -> Self { Self(num) } + #[must_use] pub fn num(self) -> u16 { self.0 } + #[must_use] pub fn is_zero(self) -> bool { self.0 == 0 } + #[must_use] pub fn zero() -> Self { Self(0) } @@ -47,6 +51,7 @@ pub struct Connection { } impl Connection { + #[must_use] pub fn new_handle( id: ConnectionId, peer_addr: SocketAddr, @@ -78,6 +83,7 @@ pub struct Channel { } impl Channel { + #[must_use] pub fn new_handle( id: ChannelId, num: u16, diff --git a/amqp_core/src/macros.rs b/amqp_core/src/macros.rs index 2a4c2bd..c5d791f 100644 --- a/amqp_core/src/macros.rs +++ b/amqp_core/src/macros.rs @@ -3,8 +3,9 @@ macro_rules! newtype_id { ($vis:vis $name:ident) => { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] $vis struct $name(::uuid::Uuid); - + impl $name { + #[must_use] pub fn random() -> Self { ::rand::random() } @@ -24,6 +25,32 @@ macro_rules! newtype_id { }; } +#[macro_export] +macro_rules! newtype { + ($(#[$meta:meta])* $vis:vis $name:ident: $ty:ty) => { + $(#[$meta])* + $vis struct $name($ty); + + impl $name { + pub fn new(inner: $ty) -> Self { + Self(inner) + } + + pub fn into_inner(self) -> $ty { + self.0 + } + } + + impl std::ops::Deref for $name { + type Target = $ty; + + fn deref(&self) -> &Self::Target { + &self.0 + } + } + }; +} + #[macro_export] macro_rules! amqp_todo { () => { diff --git a/amqp_messaging/src/methods/queue.rs b/amqp_messaging/src/methods/queue.rs index 08a5fc4..fb7406f 100644 --- a/amqp_messaging/src/methods/queue.rs +++ b/amqp_messaging/src/methods/queue.rs @@ -1,5 +1,3 @@ -#![deny(clippy::future_not_send)] - use amqp_core::connection::ChannelHandle; use amqp_core::error::{ConException, ProtocolError}; use amqp_core::methods::{Bit, ExchangeName, NoWait, QueueName, Shortstr, Table}; diff --git a/amqp_transport/src/connection.rs b/amqp_transport/src/connection.rs index 4268a25..c485b5d 100644 --- a/amqp_transport/src/connection.rs +++ b/amqp_transport/src/connection.rs @@ -162,7 +162,7 @@ impl Connection { ensure_conn(mechanism == "PLAIN")?; ensure_conn(locale == "en_US")?; let plain_user = sasl::parse_sasl_plain_response(&response)?; - info!(username = %plain_user.authentication_identity, "SASL Authentication successful") + info!(username = %plain_user.authentication_identity, "SASL Authentication successful"); } else { return Err(ConException::Todo.into()); } @@ -257,7 +257,7 @@ impl Connection { Method::ChannelClose { .. } => self.channel_close(frame.channel, method).await?, Method::BasicPublish { .. } => match self.channels.get_mut(&frame.channel) { Some(channel) => { - channel.status = ChannelStatus::NeedHeader(BASIC_CLASS_ID, Box::new(method)) + channel.status = ChannelStatus::NeedHeader(BASIC_CLASS_ID, Box::new(method)); } None => return Err(ConException::Todo.into()), }, diff --git a/amqp_transport/src/frame.rs b/amqp_transport/src/frame.rs index e957afb..cfb9220 100644 --- a/amqp_transport/src/frame.rs +++ b/amqp_transport/src/frame.rs @@ -138,7 +138,7 @@ impl ContentHeader { pub async fn write_frame(frame: &Frame, mut w: W) -> Result<()> where - W: AsyncWriteExt + Unpin, + W: AsyncWriteExt + Unpin + Send, { trace!(?frame, "Sending frame"); @@ -154,7 +154,7 @@ where pub async fn read_frame(r: &mut R, max_frame_size: usize) -> Result where - R: AsyncReadExt + Unpin, + R: AsyncReadExt + Unpin + Send, { let kind = r.read_u8().await.context("read type")?; let channel = r.read_u16().await.context("read channel")?; diff --git a/amqp_transport/src/methods/mod.rs b/amqp_transport/src/methods/mod.rs index f2cd587..617d6e3 100644 --- a/amqp_transport/src/methods/mod.rs +++ b/amqp_transport/src/methods/mod.rs @@ -2,7 +2,6 @@ use crate::error::TransError; use amqp_core::error::ConException; use amqp_core::methods::{FieldValue, Method, Table}; use rand::Rng; -use std::collections::HashMap; mod generated; pub mod parse_helper; @@ -65,7 +64,9 @@ rand_random_method!(bool, u8, i8, u16, i16, u32, i32, u64, i64, f32, f64); impl RandomMethod for Table { fn random(rng: &mut R) -> Self { let len = rng.gen_range(0..3); - HashMap::from_iter((0..len).map(|_| (String::random(rng), FieldValue::random(rng)))) + (0..len) + .map(|_| (String::random(rng), FieldValue::random(rng))) + .collect() } } diff --git a/amqp_transport/src/methods/parse_helper.rs b/amqp_transport/src/methods/parse_helper.rs index dfdc3d0..23a6b2e 100644 --- a/amqp_transport/src/methods/parse_helper.rs +++ b/amqp_transport/src/methods/parse_helper.rs @@ -12,7 +12,6 @@ use nom::multi::{count, many0}; use nom::number::complete::{f32, f64, i16, i32, i64, i8, u16, u32, u64, u8}; use nom::number::Endianness::Big; use nom::Err; -use std::collections::HashMap; impl nom::error::ParseError for TransError { fn from_error_kind(_input: T, _kind: ErrorKind) -> Self { @@ -37,7 +36,7 @@ pub fn fail_err>(msg: S) -> impl FnOnce(Err) -> Err< } _ => vec![msg], }, - _ => vec![msg], + Err::Incomplete(_) => vec![msg], }; Err::Failure(ConException::SyntaxError(stack).into()) } @@ -133,7 +132,7 @@ pub fn table(input: &[u8]) -> IResult<'_, Table> { )); } - let table = HashMap::from_iter(values.into_iter()); + let table = values.into_iter().collect(); Ok((rest_input, table)) } diff --git a/amqp_transport/src/tests.rs b/amqp_transport/src/tests.rs index fd89146..cb61337 100644 --- a/amqp_transport/src/tests.rs +++ b/amqp_transport/src/tests.rs @@ -1,5 +1,6 @@ -use crate::frame::{ChannelNum, FrameType}; +use crate::frame::FrameType; use crate::{frame, methods}; +use amqp_core::connection::ChannelNum; use amqp_core::methods::{FieldValue, Method}; use std::collections::HashMap; diff --git a/src/main.rs b/src/main.rs index 182a8e8..645accd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -29,8 +29,8 @@ async fn main() -> Result<()> { } fn setup_tracing() { - let rust_log = std::env::var("RUST_LOG"); const DEFAULT_LOG: &str = "hyper=info,debug"; + let rust_log = std::env::var("RUST_LOG"); tracing_subscriber::fmt() .with_level(true) From 439696cf3f86086c628cc7d890cfe945fb808eb1 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Sat, 26 Feb 2022 23:26:43 +0100 Subject: [PATCH 08/11] remove unused deps --- .cargo/config.toml | 4 ---- Cargo.lock | 2 -- amqp_messaging/Cargo.toml | 1 - amqp_transport/Cargo.toml | 1 - 4 files changed, 8 deletions(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index 6fb1fb9..35049cb 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,6 +1,2 @@ [alias] xtask = "run --package xtask --" - -[build] -# note: if this doesn't apply, update your global rustflags in "~/.cargo/config.toml" -rustflags = ["--cfg", "tokio_unstable"] \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index 4c40ca7..2ac90bf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -55,7 +55,6 @@ dependencies = [ "parking_lot", "tokio", "tracing", - "uuid", ] [[package]] @@ -75,7 +74,6 @@ dependencies = [ "thiserror", "tokio", "tracing", - "uuid", ] [[package]] diff --git a/amqp_messaging/Cargo.toml b/amqp_messaging/Cargo.toml index b5a5eba..e662ec9 100644 --- a/amqp_messaging/Cargo.toml +++ b/amqp_messaging/Cargo.toml @@ -10,4 +10,3 @@ amqp_core = { path = "../amqp_core" } parking_lot = "0.12.0" tracing = "0.1.31" tokio = { version = "1.17.0", features = ["full"] } -uuid = "0.8.2" \ No newline at end of file diff --git a/amqp_transport/Cargo.toml b/amqp_transport/Cargo.toml index 9fcae77..a842ee9 100644 --- a/amqp_transport/Cargo.toml +++ b/amqp_transport/Cargo.toml @@ -18,7 +18,6 @@ smallvec = { version = "1.8.0", features = ["union"] } thiserror = "1.0.30" tokio = { version = "1.16.1", features = ["full"] } tracing = "0.1.30" -uuid = "0.8.2" [features] From 14ad4e1011b1af4c2f3154cf173c31535d500a2e Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Sat, 26 Feb 2022 23:47:47 +0100 Subject: [PATCH 09/11] more cleanup --- amqp_core/src/methods/generated.rs | 1483 +++++++++++++---------- amqp_messaging/src/methods/consume.rs | 11 +- amqp_messaging/src/methods/mod.rs | 93 +- amqp_messaging/src/methods/queue.rs | 28 +- amqp_transport/src/connection.rs | 54 +- amqp_transport/src/methods/generated.rs | 514 ++++---- amqp_transport/src/tests.rs | 10 +- xtask/src/codegen/mod.rs | 38 +- xtask/src/codegen/parser.rs | 4 +- xtask/src/codegen/random.rs | 4 +- xtask/src/codegen/write.rs | 8 +- 11 files changed, 1199 insertions(+), 1048 deletions(-) diff --git a/amqp_core/src/methods/generated.rs b/amqp_core/src/methods/generated.rs index bc4f27f..04e808a 100644 --- a/amqp_core/src/methods/generated.rs +++ b/amqp_core/src/methods/generated.rs @@ -126,661 +126,830 @@ pub type Table = super::Table; #[derive(Debug, Clone, PartialEq)] pub enum Method { - /// The connection class provides methods for a client to establish a network connection to - /// a server, and for both peers to operate the connection thereafter. - /// This method starts the connection negotiation process by telling the client the - /// protocol version that the server proposes, along with a list of security mechanisms - /// which the client can use for authentication. - ConnectionStart { - /// The major version number can take any value from 0 to 99 as defined in the - /// AMQP specification. - version_major: Octet, - /// The minor version number can take any value from 0 to 99 as defined in the - /// AMQP specification. - version_minor: Octet, - server_properties: PeerProperties, - /// must not be null - /// - /// A list of the security mechanisms that the server supports, delimited by spaces. - mechanisms: Longstr, - /// must not be null - /// - /// A list of the message locales that the server supports, delimited by spaces. The - /// locale defines the language in which the server will send reply texts. - locales: Longstr, - }, - /// The connection class provides methods for a client to establish a network connection to - /// a server, and for both peers to operate the connection thereafter. - /// This method selects a SASL security mechanism. - ConnectionStartOk { - client_properties: PeerProperties, - /// must not be null - /// - /// A single security mechanisms selected by the client, which must be one of those - /// specified by the server. - mechanism: Shortstr, - /// must not be null - /// - /// A block of opaque data passed to the security mechanism. The contents of this - /// data are defined by the SASL security mechanism. - response: Longstr, - /// must not be null - /// - /// A single message locale selected by the client, which must be one of those - /// specified by the server. - locale: Shortstr, - }, - /// The connection class provides methods for a client to establish a network connection to - /// a server, and for both peers to operate the connection thereafter. - /// The SASL protocol works by exchanging challenges and responses until both peers have - /// received sufficient information to authenticate each other. This method challenges - /// the client to provide more information. - ConnectionSecure { - /// Challenge information, a block of opaque binary data passed to the security - /// mechanism. - challenge: Longstr, - }, - /// The connection class provides methods for a client to establish a network connection to - /// a server, and for both peers to operate the connection thereafter. - /// This method attempts to authenticate, passing a block of SASL data for the security - /// mechanism at the server side. - ConnectionSecureOk { - /// must not be null - /// - /// A block of opaque data passed to the security mechanism. The contents of this - /// data are defined by the SASL security mechanism. - response: Longstr, - }, - /// The connection class provides methods for a client to establish a network connection to - /// a server, and for both peers to operate the connection thereafter. - /// This method proposes a set of connection configuration values to the client. The - /// client can accept and/or adjust these. - ConnectionTune { - /// Specifies highest channel number that the server permits. Usable channel numbers - /// are in the range 1..channel-max. Zero indicates no specified limit. - channel_max: Short, - /// The largest frame size that the server proposes for the connection, including - /// frame header and end-byte. The client can negotiate a lower value. Zero means - /// that the server does not impose any specific limit but may reject very large - /// frames if it cannot allocate resources for them. - frame_max: Long, - /// The delay, in seconds, of the connection heartbeat that the server wants. - /// Zero means the server does not want a heartbeat. - heartbeat: Short, - }, - /// The connection class provides methods for a client to establish a network connection to - /// a server, and for both peers to operate the connection thereafter. - /// This method sends the client's connection tuning parameters to the server. - /// Certain fields are negotiated, others provide capability information. - ConnectionTuneOk { - /// must not be null, must be less than the tune field of the method channel-max - /// - /// The maximum total number of channels that the client will use per connection. - channel_max: Short, - /// The largest frame size that the client and server will use for the connection. - /// Zero means that the client does not impose any specific limit but may reject - /// very large frames if it cannot allocate resources for them. Note that the - /// frame-max limit applies principally to content frames, where large contents can - /// be broken into frames of arbitrary size. - frame_max: Long, - /// The delay, in seconds, of the connection heartbeat that the client wants. Zero - /// means the client does not want a heartbeat. - heartbeat: Short, - }, - /// The connection class provides methods for a client to establish a network connection to - /// a server, and for both peers to operate the connection thereafter. - /// This method opens a connection to a virtual host, which is a collection of - /// resources, and acts to separate multiple application domains within a server. - /// The server may apply arbitrary limits per virtual host, such as the number - /// of each type of entity that may be used, per connection and/or in total. - ConnectionOpen { - /// The name of the virtual host to work with. - virtual_host: Path, - reserved_1: Shortstr, - reserved_2: Bit, - }, - /// The connection class provides methods for a client to establish a network connection to - /// a server, and for both peers to operate the connection thereafter. - /// This method signals to the client that the connection is ready for use. - ConnectionOpenOk { reserved_1: Shortstr }, - /// The connection class provides methods for a client to establish a network connection to - /// a server, and for both peers to operate the connection thereafter. - /// This method indicates that the sender wants to close the connection. This may be - /// due to internal conditions (e.g. a forced shut-down) or due to an error handling - /// a specific method, i.e. an exception. When a close is due to an exception, the - /// sender provides the class and method id of the method which caused the exception. - ConnectionClose { - reply_code: ReplyCode, - reply_text: ReplyText, - /// When the close is provoked by a method exception, this is the class of the - /// method. - class_id: ClassId, - /// When the close is provoked by a method exception, this is the ID of the method. - method_id: MethodId, - }, - /// The connection class provides methods for a client to establish a network connection to - /// a server, and for both peers to operate the connection thereafter. - /// This method confirms a Connection.Close method and tells the recipient that it is - /// safe to release resources for the connection and close the socket. - ConnectionCloseOk, - /// The channel class provides methods for a client to establish a channel to a - /// server and for both peers to operate the channel thereafter. - /// This method opens a channel to the server. - ChannelOpen { reserved_1: Shortstr }, - /// The channel class provides methods for a client to establish a channel to a - /// server and for both peers to operate the channel thereafter. - /// This method signals to the client that the channel is ready for use. - ChannelOpenOk { reserved_1: Longstr }, - /// The channel class provides methods for a client to establish a channel to a - /// server and for both peers to operate the channel thereafter. - /// This method asks the peer to pause or restart the flow of content data sent by - /// a consumer. This is a simple flow-control mechanism that a peer can use to avoid - /// overflowing its queues or otherwise finding itself receiving more messages than - /// it can process. Note that this method is not intended for window control. It does - /// not affect contents returned by Basic.Get-Ok methods. - ChannelFlow { - /// If 1, the peer starts sending content frames. If 0, the peer stops sending - /// content frames. - active: Bit, - }, - /// The channel class provides methods for a client to establish a channel to a - /// server and for both peers to operate the channel thereafter. - /// Confirms to the peer that a flow command was received and processed. - ChannelFlowOk { - /// Confirms the setting of the processed flow method: 1 means the peer will start - /// sending or continue to send content frames; 0 means it will not. - active: Bit, - }, - /// The channel class provides methods for a client to establish a channel to a - /// server and for both peers to operate the channel thereafter. - /// This method indicates that the sender wants to close the channel. This may be due to - /// internal conditions (e.g. a forced shut-down) or due to an error handling a specific - /// method, i.e. an exception. When a close is due to an exception, the sender provides - /// the class and method id of the method which caused the exception. - ChannelClose { - reply_code: ReplyCode, - reply_text: ReplyText, - /// When the close is provoked by a method exception, this is the class of the - /// method. - class_id: ClassId, - /// When the close is provoked by a method exception, this is the ID of the method. - method_id: MethodId, - }, - /// The channel class provides methods for a client to establish a channel to a - /// server and for both peers to operate the channel thereafter. - /// This method confirms a Channel.Close method and tells the recipient that it is safe - /// to release resources for the channel. - ChannelCloseOk, - /// Exchanges match and distribute messages across queues. Exchanges can be configured in - /// the server or declared at runtime. - /// This method creates an exchange if it does not already exist, and if the exchange - /// exists, verifies that it is of the correct and expected class. - ExchangeDeclare { - reserved_1: Short, - /// must not be null - exchange: ExchangeName, - /// Each exchange belongs to one of a set of exchange types implemented by the - /// server. The exchange types define the functionality of the exchange - i.e. how - /// messages are routed through it. It is not valid or meaningful to attempt to - /// change the type of an existing exchange. - r#type: Shortstr, - /// If set, the server will reply with Declare-Ok if the exchange already - /// exists with the same name, and raise an error if not. The client can - /// use this to check whether an exchange exists without modifying the - /// server state. When set, all other method fields except name and no-wait - /// are ignored. A declare with both passive and no-wait has no effect. - /// Arguments are compared for semantic equivalence. - passive: Bit, - /// If set when creating a new exchange, the exchange will be marked as durable. - /// Durable exchanges remain active when a server restarts. Non-durable exchanges - /// (transient exchanges) are purged if/when a server restarts. - durable: Bit, - reserved_2: Bit, - reserved_3: Bit, - no_wait: NoWait, - /// A set of arguments for the declaration. The syntax and semantics of these - /// arguments depends on the server implementation. - arguments: Table, - }, - /// Exchanges match and distribute messages across queues. Exchanges can be configured in - /// the server or declared at runtime. - /// This method confirms a Declare method and confirms the name of the exchange, - /// essential for automatically-named exchanges. - ExchangeDeclareOk, - /// Exchanges match and distribute messages across queues. Exchanges can be configured in - /// the server or declared at runtime. - /// This method deletes an exchange. When an exchange is deleted all queue bindings on - /// the exchange are cancelled. - ExchangeDelete { - reserved_1: Short, - /// must not be null - exchange: ExchangeName, - /// If set, the server will only delete the exchange if it has no queue bindings. If - /// the exchange has queue bindings the server does not delete it but raises a - /// channel exception instead. - if_unused: Bit, - no_wait: NoWait, - }, - /// Exchanges match and distribute messages across queues. Exchanges can be configured in - /// the server or declared at runtime. - /// This method confirms the deletion of an exchange. - ExchangeDeleteOk, - /// Queues store and forward messages. Queues can be configured in the server or created at - /// runtime. Queues must be attached to at least one exchange in order to receive messages - /// from publishers. - /// This method creates or checks a queue. When creating a new queue the client can - /// specify various properties that control the durability of the queue and its - /// contents, and the level of sharing for the queue. - QueueDeclare { - reserved_1: Short, - queue: QueueName, - /// If set, the server will reply with Declare-Ok if the queue already - /// exists with the same name, and raise an error if not. The client can - /// use this to check whether a queue exists without modifying the - /// server state. When set, all other method fields except name and no-wait - /// are ignored. A declare with both passive and no-wait has no effect. - /// Arguments are compared for semantic equivalence. - passive: Bit, - /// If set when creating a new queue, the queue will be marked as durable. Durable - /// queues remain active when a server restarts. Non-durable queues (transient - /// queues) are purged if/when a server restarts. Note that durable queues do not - /// necessarily hold persistent messages, although it does not make sense to send - /// persistent messages to a transient queue. - durable: Bit, - /// Exclusive queues may only be accessed by the current connection, and are - /// deleted when that connection closes. Passive declaration of an exclusive - /// queue by other connections are not allowed. - exclusive: Bit, - /// If set, the queue is deleted when all consumers have finished using it. The last - /// consumer can be cancelled either explicitly or because its channel is closed. If - /// there was no consumer ever on the queue, it won't be deleted. Applications can - /// explicitly delete auto-delete queues using the Delete method as normal. - auto_delete: Bit, - no_wait: NoWait, - /// A set of arguments for the declaration. The syntax and semantics of these - /// arguments depends on the server implementation. - arguments: Table, - }, - /// Queues store and forward messages. Queues can be configured in the server or created at - /// runtime. Queues must be attached to at least one exchange in order to receive messages - /// from publishers. - /// This method confirms a Declare method and confirms the name of the queue, essential - /// for automatically-named queues. - QueueDeclareOk { - /// must not be null - /// - /// Reports the name of the queue. If the server generated a queue name, this field - /// contains that name. - queue: QueueName, - message_count: MessageCount, - /// Reports the number of active consumers for the queue. Note that consumers can - /// suspend activity (Channel.Flow) in which case they do not appear in this count. - consumer_count: Long, - }, - /// Queues store and forward messages. Queues can be configured in the server or created at - /// runtime. Queues must be attached to at least one exchange in order to receive messages - /// from publishers. - /// This method binds a queue to an exchange. Until a queue is bound it will not - /// receive any messages. In a classic messaging model, store-and-forward queues - /// are bound to a direct exchange and subscription queues are bound to a topic - /// exchange. - QueueBind { - reserved_1: Short, - /// Specifies the name of the queue to bind. - queue: QueueName, - exchange: ExchangeName, - /// Specifies the routing key for the binding. The routing key is used for routing - /// messages depending on the exchange configuration. Not all exchanges use a - /// routing key - refer to the specific exchange documentation. If the queue name - /// is empty, the server uses the last queue declared on the channel. If the - /// routing key is also empty, the server uses this queue name for the routing - /// key as well. If the queue name is provided but the routing key is empty, the - /// server does the binding with that empty routing key. The meaning of empty - /// routing keys depends on the exchange implementation. - routing_key: Shortstr, - no_wait: NoWait, - /// A set of arguments for the binding. The syntax and semantics of these arguments - /// depends on the exchange class. - arguments: Table, - }, - /// Queues store and forward messages. Queues can be configured in the server or created at - /// runtime. Queues must be attached to at least one exchange in order to receive messages - /// from publishers. - /// This method confirms that the bind was successful. - QueueBindOk, - /// Queues store and forward messages. Queues can be configured in the server or created at - /// runtime. Queues must be attached to at least one exchange in order to receive messages - /// from publishers. - /// This method unbinds a queue from an exchange. - QueueUnbind { - reserved_1: Short, - /// Specifies the name of the queue to unbind. - queue: QueueName, - /// The name of the exchange to unbind from. - exchange: ExchangeName, - /// Specifies the routing key of the binding to unbind. - routing_key: Shortstr, - /// Specifies the arguments of the binding to unbind. - arguments: Table, - }, - /// Queues store and forward messages. Queues can be configured in the server or created at - /// runtime. Queues must be attached to at least one exchange in order to receive messages - /// from publishers. - /// This method confirms that the unbind was successful. - QueueUnbindOk, - /// Queues store and forward messages. Queues can be configured in the server or created at - /// runtime. Queues must be attached to at least one exchange in order to receive messages - /// from publishers. - /// This method removes all messages from a queue which are not awaiting - /// acknowledgment. - QueuePurge { - reserved_1: Short, - /// Specifies the name of the queue to purge. - queue: QueueName, - no_wait: NoWait, - }, - /// Queues store and forward messages. Queues can be configured in the server or created at - /// runtime. Queues must be attached to at least one exchange in order to receive messages - /// from publishers. - /// This method confirms the purge of a queue. - QueuePurgeOk { - /// Reports the number of messages purged. - message_count: MessageCount, - }, - /// Queues store and forward messages. Queues can be configured in the server or created at - /// runtime. Queues must be attached to at least one exchange in order to receive messages - /// from publishers. - /// This method deletes a queue. When a queue is deleted any pending messages are sent - /// to a dead-letter queue if this is defined in the server configuration, and all - /// consumers on the queue are cancelled. - QueueDelete { - reserved_1: Short, - /// Specifies the name of the queue to delete. - queue: QueueName, - /// If set, the server will only delete the queue if it has no consumers. If the - /// queue has consumers the server does does not delete it but raises a channel - /// exception instead. - if_unused: Bit, - /// If set, the server will only delete the queue if it has no messages. - if_empty: Bit, - no_wait: NoWait, - }, - /// Queues store and forward messages. Queues can be configured in the server or created at - /// runtime. Queues must be attached to at least one exchange in order to receive messages - /// from publishers. - /// This method confirms the deletion of a queue. - QueueDeleteOk { - /// Reports the number of messages deleted. - message_count: MessageCount, - }, - /// The Basic class provides methods that support an industry-standard messaging model. - /// This method requests a specific quality of service. The QoS can be specified for the - /// current channel or for all channels on the connection. The particular properties and - /// semantics of a qos method always depend on the content class semantics. Though the - /// qos method could in principle apply to both peers, it is currently meaningful only - /// for the server. - BasicQos { - /// The client can request that messages be sent in advance so that when the client - /// finishes processing a message, the following message is already held locally, - /// rather than needing to be sent down the channel. Prefetching gives a performance - /// improvement. This field specifies the prefetch window size in octets. The server - /// will send a message in advance if it is equal to or smaller in size than the - /// available prefetch size (and also falls into other prefetch limits). May be set - /// to zero, meaning "no specific limit", although other prefetch limits may still - /// apply. The prefetch-size is ignored if the no-ack option is set. - prefetch_size: Long, - /// Specifies a prefetch window in terms of whole messages. This field may be used - /// in combination with the prefetch-size field; a message will only be sent in - /// advance if both prefetch windows (and those at the channel and connection level) - /// allow it. The prefetch-count is ignored if the no-ack option is set. - prefetch_count: Short, - /// By default the QoS settings apply to the current channel only. If this field is - /// set, they are applied to the entire connection. - global: Bit, - }, - /// The Basic class provides methods that support an industry-standard messaging model. - /// This method tells the client that the requested QoS levels could be handled by the - /// server. The requested QoS applies to all active consumers until a new QoS is - /// defined. - BasicQosOk, - /// The Basic class provides methods that support an industry-standard messaging model. - /// This method asks the server to start a "consumer", which is a transient request for - /// messages from a specific queue. Consumers last as long as the channel they were - /// declared on, or until the client cancels them. - BasicConsume { - reserved_1: Short, - /// Specifies the name of the queue to consume from. - queue: QueueName, - /// Specifies the identifier for the consumer. The consumer tag is local to a - /// channel, so two clients can use the same consumer tags. If this field is - /// empty the server will generate a unique tag. - consumer_tag: ConsumerTag, - no_local: NoLocal, - no_ack: NoAck, - /// Request exclusive consumer access, meaning only this consumer can access the - /// queue. - exclusive: Bit, - no_wait: NoWait, - /// A set of arguments for the consume. The syntax and semantics of these - /// arguments depends on the server implementation. - arguments: Table, - }, - /// The Basic class provides methods that support an industry-standard messaging model. - /// The server provides the client with a consumer tag, which is used by the client - /// for methods called on the consumer at a later stage. - BasicConsumeOk { - /// Holds the consumer tag specified by the client or provided by the server. - consumer_tag: ConsumerTag, - }, - /// The Basic class provides methods that support an industry-standard messaging model. - /// This method cancels a consumer. This does not affect already delivered - /// messages, but it does mean the server will not send any more messages for - /// that consumer. The client may receive an arbitrary number of messages in - /// between sending the cancel method and receiving the cancel-ok reply. - BasicCancel { - consumer_tag: ConsumerTag, - no_wait: NoWait, - }, - /// The Basic class provides methods that support an industry-standard messaging model. - /// This method confirms that the cancellation was completed. - BasicCancelOk { consumer_tag: ConsumerTag }, - /// The Basic class provides methods that support an industry-standard messaging model. - /// This method publishes a message to a specific exchange. The message will be routed - /// to queues as defined by the exchange configuration and distributed to any active - /// consumers when the transaction, if any, is committed. - BasicPublish { - reserved_1: Short, - /// Specifies the name of the exchange to publish to. The exchange name can be - /// empty, meaning the default exchange. If the exchange name is specified, and that - /// exchange does not exist, the server will raise a channel exception. - exchange: ExchangeName, - /// Specifies the routing key for the message. The routing key is used for routing - /// messages depending on the exchange configuration. - routing_key: Shortstr, - /// This flag tells the server how to react if the message cannot be routed to a - /// queue. If this flag is set, the server will return an unroutable message with a - /// Return method. If this flag is zero, the server silently drops the message. - mandatory: Bit, - /// This flag tells the server how to react if the message cannot be routed to a - /// queue consumer immediately. If this flag is set, the server will return an - /// undeliverable message with a Return method. If this flag is zero, the server - /// will queue the message, but with no guarantee that it will ever be consumed. - immediate: Bit, - }, - /// The Basic class provides methods that support an industry-standard messaging model. - /// This method returns an undeliverable message that was published with the "immediate" - /// flag set, or an unroutable message published with the "mandatory" flag set. The - /// reply code and text provide information about the reason that the message was - /// undeliverable. - BasicReturn { - reply_code: ReplyCode, - reply_text: ReplyText, - /// Specifies the name of the exchange that the message was originally published - /// to. May be empty, meaning the default exchange. - exchange: ExchangeName, - /// Specifies the routing key name specified when the message was published. - routing_key: Shortstr, - }, - /// The Basic class provides methods that support an industry-standard messaging model. - /// This method delivers a message to the client, via a consumer. In the asynchronous - /// message delivery model, the client starts a consumer using the Consume method, then - /// the server responds with Deliver methods as and when messages arrive for that - /// consumer. - BasicDeliver { - consumer_tag: ConsumerTag, - delivery_tag: DeliveryTag, - redelivered: Redelivered, - /// Specifies the name of the exchange that the message was originally published to. - /// May be empty, indicating the default exchange. - exchange: ExchangeName, - /// Specifies the routing key name specified when the message was published. - routing_key: Shortstr, - }, - /// The Basic class provides methods that support an industry-standard messaging model. - /// This method provides a direct access to the messages in a queue using a synchronous - /// dialogue that is designed for specific types of application where synchronous - /// functionality is more important than performance. - BasicGet { - reserved_1: Short, - /// Specifies the name of the queue to get a message from. - queue: QueueName, - no_ack: NoAck, - }, - /// The Basic class provides methods that support an industry-standard messaging model. - /// This method delivers a message to the client following a get method. A message - /// delivered by 'get-ok' must be acknowledged unless the no-ack option was set in the - /// get method. - BasicGetOk { - delivery_tag: DeliveryTag, - redelivered: Redelivered, - /// Specifies the name of the exchange that the message was originally published to. - /// If empty, the message was published to the default exchange. - exchange: ExchangeName, - /// Specifies the routing key name specified when the message was published. - routing_key: Shortstr, - message_count: MessageCount, - }, - /// The Basic class provides methods that support an industry-standard messaging model. - /// This method tells the client that the queue has no messages available for the - /// client. - BasicGetEmpty { reserved_1: Shortstr }, - /// The Basic class provides methods that support an industry-standard messaging model. - /// This method acknowledges one or more messages delivered via the Deliver or Get-Ok - /// methods. The client can ask to confirm a single message or a set of messages up to - /// and including a specific message. - BasicAck { - delivery_tag: DeliveryTag, - /// If set to 1, the delivery tag is treated as "up to and including", so that the - /// client can acknowledge multiple messages with a single method. If set to zero, - /// the delivery tag refers to a single message. If the multiple field is 1, and the - /// delivery tag is zero, tells the server to acknowledge all outstanding messages. - multiple: Bit, - }, - /// The Basic class provides methods that support an industry-standard messaging model. - /// This method allows a client to reject a message. It can be used to interrupt and - /// cancel large incoming messages, or return untreatable messages to their original - /// queue. - BasicReject { - delivery_tag: DeliveryTag, - /// If requeue is true, the server will attempt to requeue the message. If requeue - /// is false or the requeue attempt fails the messages are discarded or dead-lettered. - requeue: Bit, - }, - /// The Basic class provides methods that support an industry-standard messaging model. - /// This method asks the server to redeliver all unacknowledged messages on a - /// specified channel. Zero or more messages may be redelivered. This method - /// is deprecated in favour of the synchronous Recover/Recover-Ok. - BasicRecoverAsync { - /// If this field is zero, the message will be redelivered to the original - /// recipient. If this bit is 1, the server will attempt to requeue the message, - /// potentially then delivering it to an alternative subscriber. - requeue: Bit, - }, - /// The Basic class provides methods that support an industry-standard messaging model. - /// This method asks the server to redeliver all unacknowledged messages on a - /// specified channel. Zero or more messages may be redelivered. This method - /// replaces the asynchronous Recover. - BasicRecover { - /// If this field is zero, the message will be redelivered to the original - /// recipient. If this bit is 1, the server will attempt to requeue the message, - /// potentially then delivering it to an alternative subscriber. - requeue: Bit, - }, - /// The Basic class provides methods that support an industry-standard messaging model. - /// This method acknowledges a Basic.Recover method. - BasicRecoverOk, - /// The Tx class allows publish and ack operations to be batched into atomic - /// units of work. The intention is that all publish and ack requests issued - /// within a transaction will complete successfully or none of them will. - /// Servers SHOULD implement atomic transactions at least where all publish - /// or ack requests affect a single queue. Transactions that cover multiple - /// queues may be non-atomic, given that queues can be created and destroyed - /// asynchronously, and such events do not form part of any transaction. - /// Further, the behaviour of transactions with respect to the immediate and - /// mandatory flags on Basic.Publish methods is not defined. - /// This method sets the channel to use standard transactions. The client must use this - /// method at least once on a channel before using the Commit or Rollback methods. - TxSelect, - /// The Tx class allows publish and ack operations to be batched into atomic - /// units of work. The intention is that all publish and ack requests issued - /// within a transaction will complete successfully or none of them will. - /// Servers SHOULD implement atomic transactions at least where all publish - /// or ack requests affect a single queue. Transactions that cover multiple - /// queues may be non-atomic, given that queues can be created and destroyed - /// asynchronously, and such events do not form part of any transaction. - /// Further, the behaviour of transactions with respect to the immediate and - /// mandatory flags on Basic.Publish methods is not defined. - /// This method confirms to the client that the channel was successfully set to use - /// standard transactions. - TxSelectOk, - /// The Tx class allows publish and ack operations to be batched into atomic - /// units of work. The intention is that all publish and ack requests issued - /// within a transaction will complete successfully or none of them will. - /// Servers SHOULD implement atomic transactions at least where all publish - /// or ack requests affect a single queue. Transactions that cover multiple - /// queues may be non-atomic, given that queues can be created and destroyed - /// asynchronously, and such events do not form part of any transaction. - /// Further, the behaviour of transactions with respect to the immediate and - /// mandatory flags on Basic.Publish methods is not defined. - /// This method commits all message publications and acknowledgments performed in - /// the current transaction. A new transaction starts immediately after a commit. - TxCommit, - /// The Tx class allows publish and ack operations to be batched into atomic - /// units of work. The intention is that all publish and ack requests issued - /// within a transaction will complete successfully or none of them will. - /// Servers SHOULD implement atomic transactions at least where all publish - /// or ack requests affect a single queue. Transactions that cover multiple - /// queues may be non-atomic, given that queues can be created and destroyed - /// asynchronously, and such events do not form part of any transaction. - /// Further, the behaviour of transactions with respect to the immediate and - /// mandatory flags on Basic.Publish methods is not defined. - /// This method confirms to the client that the commit succeeded. Note that if a commit - /// fails, the server raises a channel exception. - TxCommitOk, - /// The Tx class allows publish and ack operations to be batched into atomic - /// units of work. The intention is that all publish and ack requests issued - /// within a transaction will complete successfully or none of them will. - /// Servers SHOULD implement atomic transactions at least where all publish - /// or ack requests affect a single queue. Transactions that cover multiple - /// queues may be non-atomic, given that queues can be created and destroyed - /// asynchronously, and such events do not form part of any transaction. - /// Further, the behaviour of transactions with respect to the immediate and - /// mandatory flags on Basic.Publish methods is not defined. - /// This method abandons all message publications and acknowledgments performed in - /// the current transaction. A new transaction starts immediately after a rollback. - /// Note that unacked messages will not be automatically redelivered by rollback; - /// if that is required an explicit recover call should be issued. - TxRollback, - /// The Tx class allows publish and ack operations to be batched into atomic - /// units of work. The intention is that all publish and ack requests issued - /// within a transaction will complete successfully or none of them will. - /// Servers SHOULD implement atomic transactions at least where all publish - /// or ack requests affect a single queue. Transactions that cover multiple - /// queues may be non-atomic, given that queues can be created and destroyed - /// asynchronously, and such events do not form part of any transaction. - /// Further, the behaviour of transactions with respect to the immediate and - /// mandatory flags on Basic.Publish methods is not defined. - /// This method confirms to the client that the rollback succeeded. Note that if an - /// rollback fails, the server raises a channel exception. - TxRollbackOk, + ConnectionStart(ConnectionStart), + ConnectionStartOk(ConnectionStartOk), + ConnectionSecure(ConnectionSecure), + ConnectionSecureOk(ConnectionSecureOk), + ConnectionTune(ConnectionTune), + ConnectionTuneOk(ConnectionTuneOk), + ConnectionOpen(ConnectionOpen), + ConnectionOpenOk(ConnectionOpenOk), + ConnectionClose(ConnectionClose), + ConnectionCloseOk(ConnectionCloseOk), + ChannelOpen(ChannelOpen), + ChannelOpenOk(ChannelOpenOk), + ChannelFlow(ChannelFlow), + ChannelFlowOk(ChannelFlowOk), + ChannelClose(ChannelClose), + ChannelCloseOk(ChannelCloseOk), + ExchangeDeclare(ExchangeDeclare), + ExchangeDeclareOk(ExchangeDeclareOk), + ExchangeDelete(ExchangeDelete), + ExchangeDeleteOk(ExchangeDeleteOk), + QueueDeclare(QueueDeclare), + QueueDeclareOk(QueueDeclareOk), + QueueBind(QueueBind), + QueueBindOk(QueueBindOk), + QueueUnbind(QueueUnbind), + QueueUnbindOk(QueueUnbindOk), + QueuePurge(QueuePurge), + QueuePurgeOk(QueuePurgeOk), + QueueDelete(QueueDelete), + QueueDeleteOk(QueueDeleteOk), + BasicQos(BasicQos), + BasicQosOk(BasicQosOk), + BasicConsume(BasicConsume), + BasicConsumeOk(BasicConsumeOk), + BasicCancel(BasicCancel), + BasicCancelOk(BasicCancelOk), + BasicPublish(BasicPublish), + BasicReturn(BasicReturn), + BasicDeliver(BasicDeliver), + BasicGet(BasicGet), + BasicGetOk(BasicGetOk), + BasicGetEmpty(BasicGetEmpty), + BasicAck(BasicAck), + BasicReject(BasicReject), + BasicRecoverAsync(BasicRecoverAsync), + BasicRecover(BasicRecover), + BasicRecoverOk(BasicRecoverOk), + TxSelect(TxSelect), + TxSelectOk(TxSelectOk), + TxCommit(TxCommit), + TxCommitOk(TxCommitOk), + TxRollback(TxRollback), + TxRollbackOk(TxRollbackOk), } + +/// The connection class provides methods for a client to establish a network connection to +/// a server, and for both peers to operate the connection thereafter. +/// This method starts the connection negotiation process by telling the client the +/// protocol version that the server proposes, along with a list of security mechanisms +/// which the client can use for authentication. +#[derive(Debug, Clone, PartialEq)] +pub struct ConnectionStart { + /// The major version number can take any value from 0 to 99 as defined in the + /// AMQP specification. + pub version_major: Octet, + /// The minor version number can take any value from 0 to 99 as defined in the + /// AMQP specification. + pub version_minor: Octet, + pub server_properties: PeerProperties, + /// must not be null + /// + /// A list of the security mechanisms that the server supports, delimited by spaces. + pub mechanisms: Longstr, + /// must not be null + /// + /// A list of the message locales that the server supports, delimited by spaces. The + /// locale defines the language in which the server will send reply texts. + pub locales: Longstr, +} + +/// The connection class provides methods for a client to establish a network connection to +/// a server, and for both peers to operate the connection thereafter. +/// This method selects a SASL security mechanism. +#[derive(Debug, Clone, PartialEq)] +pub struct ConnectionStartOk { + pub client_properties: PeerProperties, + /// must not be null + /// + /// A single security mechanisms selected by the client, which must be one of those + /// specified by the server. + pub mechanism: Shortstr, + /// must not be null + /// + /// A block of opaque data passed to the security mechanism. The contents of this + /// data are defined by the SASL security mechanism. + pub response: Longstr, + /// must not be null + /// + /// A single message locale selected by the client, which must be one of those + /// specified by the server. + pub locale: Shortstr, +} + +/// The connection class provides methods for a client to establish a network connection to +/// a server, and for both peers to operate the connection thereafter. +/// The SASL protocol works by exchanging challenges and responses until both peers have +/// received sufficient information to authenticate each other. This method challenges +/// the client to provide more information. +#[derive(Debug, Clone, PartialEq)] +pub struct ConnectionSecure { + /// Challenge information, a block of opaque binary data passed to the security + /// mechanism. + pub challenge: Longstr, +} + +/// The connection class provides methods for a client to establish a network connection to +/// a server, and for both peers to operate the connection thereafter. +/// This method attempts to authenticate, passing a block of SASL data for the security +/// mechanism at the server side. +#[derive(Debug, Clone, PartialEq)] +pub struct ConnectionSecureOk { + /// must not be null + /// + /// A block of opaque data passed to the security mechanism. The contents of this + /// data are defined by the SASL security mechanism. + pub response: Longstr, +} + +/// The connection class provides methods for a client to establish a network connection to +/// a server, and for both peers to operate the connection thereafter. +/// This method proposes a set of connection configuration values to the client. The +/// client can accept and/or adjust these. +#[derive(Debug, Clone, PartialEq)] +pub struct ConnectionTune { + /// Specifies highest channel number that the server permits. Usable channel numbers + /// are in the range 1..channel-max. Zero indicates no specified limit. + pub channel_max: Short, + /// The largest frame size that the server proposes for the connection, including + /// frame header and end-byte. The client can negotiate a lower value. Zero means + /// that the server does not impose any specific limit but may reject very large + /// frames if it cannot allocate resources for them. + pub frame_max: Long, + /// The delay, in seconds, of the connection heartbeat that the server wants. + /// Zero means the server does not want a heartbeat. + pub heartbeat: Short, +} + +/// The connection class provides methods for a client to establish a network connection to +/// a server, and for both peers to operate the connection thereafter. +/// This method sends the client's connection tuning parameters to the server. +/// Certain fields are negotiated, others provide capability information. +#[derive(Debug, Clone, PartialEq)] +pub struct ConnectionTuneOk { + /// must not be null, must be less than the tune field of the method channel-max + /// + /// The maximum total number of channels that the client will use per connection. + pub channel_max: Short, + /// The largest frame size that the client and server will use for the connection. + /// Zero means that the client does not impose any specific limit but may reject + /// very large frames if it cannot allocate resources for them. Note that the + /// frame-max limit applies principally to content frames, where large contents can + /// be broken into frames of arbitrary size. + pub frame_max: Long, + /// The delay, in seconds, of the connection heartbeat that the client wants. Zero + /// means the client does not want a heartbeat. + pub heartbeat: Short, +} + +/// The connection class provides methods for a client to establish a network connection to +/// a server, and for both peers to operate the connection thereafter. +/// This method opens a connection to a virtual host, which is a collection of +/// resources, and acts to separate multiple application domains within a server. +/// The server may apply arbitrary limits per virtual host, such as the number +/// of each type of entity that may be used, per connection and/or in total. +#[derive(Debug, Clone, PartialEq)] +pub struct ConnectionOpen { + /// The name of the virtual host to work with. + pub virtual_host: Path, + pub reserved_1: Shortstr, + pub reserved_2: Bit, +} + +/// The connection class provides methods for a client to establish a network connection to +/// a server, and for both peers to operate the connection thereafter. +/// This method signals to the client that the connection is ready for use. +#[derive(Debug, Clone, PartialEq)] +pub struct ConnectionOpenOk { + pub reserved_1: Shortstr, +} + +/// The connection class provides methods for a client to establish a network connection to +/// a server, and for both peers to operate the connection thereafter. +/// This method indicates that the sender wants to close the connection. This may be +/// due to internal conditions (e.g. a forced shut-down) or due to an error handling +/// a specific method, i.e. an exception. When a close is due to an exception, the +/// sender provides the class and method id of the method which caused the exception. +#[derive(Debug, Clone, PartialEq)] +pub struct ConnectionClose { + pub reply_code: ReplyCode, + pub reply_text: ReplyText, + /// When the close is provoked by a method exception, this is the class of the + /// method. + pub class_id: ClassId, + /// When the close is provoked by a method exception, this is the ID of the method. + pub method_id: MethodId, +} + +/// The connection class provides methods for a client to establish a network connection to +/// a server, and for both peers to operate the connection thereafter. +/// This method confirms a Connection.Close method and tells the recipient that it is +/// safe to release resources for the connection and close the socket. +#[derive(Debug, Clone, PartialEq)] +pub struct ConnectionCloseOk; + +/// The channel class provides methods for a client to establish a channel to a +/// server and for both peers to operate the channel thereafter. +/// This method opens a channel to the server. +#[derive(Debug, Clone, PartialEq)] +pub struct ChannelOpen { + pub reserved_1: Shortstr, +} + +/// The channel class provides methods for a client to establish a channel to a +/// server and for both peers to operate the channel thereafter. +/// This method signals to the client that the channel is ready for use. +#[derive(Debug, Clone, PartialEq)] +pub struct ChannelOpenOk { + pub reserved_1: Longstr, +} + +/// The channel class provides methods for a client to establish a channel to a +/// server and for both peers to operate the channel thereafter. +/// This method asks the peer to pause or restart the flow of content data sent by +/// a consumer. This is a simple flow-control mechanism that a peer can use to avoid +/// overflowing its queues or otherwise finding itself receiving more messages than +/// it can process. Note that this method is not intended for window control. It does +/// not affect contents returned by Basic.Get-Ok methods. +#[derive(Debug, Clone, PartialEq)] +pub struct ChannelFlow { + /// If 1, the peer starts sending content frames. If 0, the peer stops sending + /// content frames. + pub active: Bit, +} + +/// The channel class provides methods for a client to establish a channel to a +/// server and for both peers to operate the channel thereafter. +/// Confirms to the peer that a flow command was received and processed. +#[derive(Debug, Clone, PartialEq)] +pub struct ChannelFlowOk { + /// Confirms the setting of the processed flow method: 1 means the peer will start + /// sending or continue to send content frames; 0 means it will not. + pub active: Bit, +} + +/// The channel class provides methods for a client to establish a channel to a +/// server and for both peers to operate the channel thereafter. +/// This method indicates that the sender wants to close the channel. This may be due to +/// internal conditions (e.g. a forced shut-down) or due to an error handling a specific +/// method, i.e. an exception. When a close is due to an exception, the sender provides +/// the class and method id of the method which caused the exception. +#[derive(Debug, Clone, PartialEq)] +pub struct ChannelClose { + pub reply_code: ReplyCode, + pub reply_text: ReplyText, + /// When the close is provoked by a method exception, this is the class of the + /// method. + pub class_id: ClassId, + /// When the close is provoked by a method exception, this is the ID of the method. + pub method_id: MethodId, +} + +/// The channel class provides methods for a client to establish a channel to a +/// server and for both peers to operate the channel thereafter. +/// This method confirms a Channel.Close method and tells the recipient that it is safe +/// to release resources for the channel. +#[derive(Debug, Clone, PartialEq)] +pub struct ChannelCloseOk; + +/// Exchanges match and distribute messages across queues. Exchanges can be configured in +/// the server or declared at runtime. +/// This method creates an exchange if it does not already exist, and if the exchange +/// exists, verifies that it is of the correct and expected class. +#[derive(Debug, Clone, PartialEq)] +pub struct ExchangeDeclare { + pub reserved_1: Short, + /// must not be null + pub exchange: ExchangeName, + /// Each exchange belongs to one of a set of exchange types implemented by the + /// server. The exchange types define the functionality of the exchange - i.e. how + /// messages are routed through it. It is not valid or meaningful to attempt to + /// change the type of an existing exchange. + pub r#type: Shortstr, + /// If set, the server will reply with Declare-Ok if the exchange already + /// exists with the same name, and raise an error if not. The client can + /// use this to check whether an exchange exists without modifying the + /// server state. When set, all other method fields except name and no-wait + /// are ignored. A declare with both passive and no-wait has no effect. + /// Arguments are compared for semantic equivalence. + pub passive: Bit, + /// If set when creating a new exchange, the exchange will be marked as durable. + /// Durable exchanges remain active when a server restarts. Non-durable exchanges + /// (transient exchanges) are purged if/when a server restarts. + pub durable: Bit, + pub reserved_2: Bit, + pub reserved_3: Bit, + pub no_wait: NoWait, + /// A set of arguments for the declaration. The syntax and semantics of these + /// arguments depends on the server implementation. + pub arguments: Table, +} + +/// Exchanges match and distribute messages across queues. Exchanges can be configured in +/// the server or declared at runtime. +/// This method confirms a Declare method and confirms the name of the exchange, +/// essential for automatically-named exchanges. +#[derive(Debug, Clone, PartialEq)] +pub struct ExchangeDeclareOk; + +/// Exchanges match and distribute messages across queues. Exchanges can be configured in +/// the server or declared at runtime. +/// This method deletes an exchange. When an exchange is deleted all queue bindings on +/// the exchange are cancelled. +#[derive(Debug, Clone, PartialEq)] +pub struct ExchangeDelete { + pub reserved_1: Short, + /// must not be null + pub exchange: ExchangeName, + /// If set, the server will only delete the exchange if it has no queue bindings. If + /// the exchange has queue bindings the server does not delete it but raises a + /// channel exception instead. + pub if_unused: Bit, + pub no_wait: NoWait, +} + +/// Exchanges match and distribute messages across queues. Exchanges can be configured in +/// the server or declared at runtime. +/// This method confirms the deletion of an exchange. +#[derive(Debug, Clone, PartialEq)] +pub struct ExchangeDeleteOk; + +/// Queues store and forward messages. Queues can be configured in the server or created at +/// runtime. Queues must be attached to at least one exchange in order to receive messages +/// from publishers. +/// This method creates or checks a queue. When creating a new queue the client can +/// specify various properties that control the durability of the queue and its +/// contents, and the level of sharing for the queue. +#[derive(Debug, Clone, PartialEq)] +pub struct QueueDeclare { + pub reserved_1: Short, + pub queue: QueueName, + /// If set, the server will reply with Declare-Ok if the queue already + /// exists with the same name, and raise an error if not. The client can + /// use this to check whether a queue exists without modifying the + /// server state. When set, all other method fields except name and no-wait + /// are ignored. A declare with both passive and no-wait has no effect. + /// Arguments are compared for semantic equivalence. + pub passive: Bit, + /// If set when creating a new queue, the queue will be marked as durable. Durable + /// queues remain active when a server restarts. Non-durable queues (transient + /// queues) are purged if/when a server restarts. Note that durable queues do not + /// necessarily hold persistent messages, although it does not make sense to send + /// persistent messages to a transient queue. + pub durable: Bit, + /// Exclusive queues may only be accessed by the current connection, and are + /// deleted when that connection closes. Passive declaration of an exclusive + /// queue by other connections are not allowed. + pub exclusive: Bit, + /// If set, the queue is deleted when all consumers have finished using it. The last + /// consumer can be cancelled either explicitly or because its channel is closed. If + /// there was no consumer ever on the queue, it won't be deleted. Applications can + /// explicitly delete auto-delete queues using the Delete method as normal. + pub auto_delete: Bit, + pub no_wait: NoWait, + /// A set of arguments for the declaration. The syntax and semantics of these + /// arguments depends on the server implementation. + pub arguments: Table, +} + +/// Queues store and forward messages. Queues can be configured in the server or created at +/// runtime. Queues must be attached to at least one exchange in order to receive messages +/// from publishers. +/// This method confirms a Declare method and confirms the name of the queue, essential +/// for automatically-named queues. +#[derive(Debug, Clone, PartialEq)] +pub struct QueueDeclareOk { + /// must not be null + /// + /// Reports the name of the queue. If the server generated a queue name, this field + /// contains that name. + pub queue: QueueName, + pub message_count: MessageCount, + /// Reports the number of active consumers for the queue. Note that consumers can + /// suspend activity (Channel.Flow) in which case they do not appear in this count. + pub consumer_count: Long, +} + +/// Queues store and forward messages. Queues can be configured in the server or created at +/// runtime. Queues must be attached to at least one exchange in order to receive messages +/// from publishers. +/// This method binds a queue to an exchange. Until a queue is bound it will not +/// receive any messages. In a classic messaging model, store-and-forward queues +/// are bound to a direct exchange and subscription queues are bound to a topic +/// exchange. +#[derive(Debug, Clone, PartialEq)] +pub struct QueueBind { + pub reserved_1: Short, + /// Specifies the name of the queue to bind. + pub queue: QueueName, + pub exchange: ExchangeName, + /// Specifies the routing key for the binding. The routing key is used for routing + /// messages depending on the exchange configuration. Not all exchanges use a + /// routing key - refer to the specific exchange documentation. If the queue name + /// is empty, the server uses the last queue declared on the channel. If the + /// routing key is also empty, the server uses this queue name for the routing + /// key as well. If the queue name is provided but the routing key is empty, the + /// server does the binding with that empty routing key. The meaning of empty + /// routing keys depends on the exchange implementation. + pub routing_key: Shortstr, + pub no_wait: NoWait, + /// A set of arguments for the binding. The syntax and semantics of these arguments + /// depends on the exchange class. + pub arguments: Table, +} + +/// Queues store and forward messages. Queues can be configured in the server or created at +/// runtime. Queues must be attached to at least one exchange in order to receive messages +/// from publishers. +/// This method confirms that the bind was successful. +#[derive(Debug, Clone, PartialEq)] +pub struct QueueBindOk; + +/// Queues store and forward messages. Queues can be configured in the server or created at +/// runtime. Queues must be attached to at least one exchange in order to receive messages +/// from publishers. +/// This method unbinds a queue from an exchange. +#[derive(Debug, Clone, PartialEq)] +pub struct QueueUnbind { + pub reserved_1: Short, + /// Specifies the name of the queue to unbind. + pub queue: QueueName, + /// The name of the exchange to unbind from. + pub exchange: ExchangeName, + /// Specifies the routing key of the binding to unbind. + pub routing_key: Shortstr, + /// Specifies the arguments of the binding to unbind. + pub arguments: Table, +} + +/// Queues store and forward messages. Queues can be configured in the server or created at +/// runtime. Queues must be attached to at least one exchange in order to receive messages +/// from publishers. +/// This method confirms that the unbind was successful. +#[derive(Debug, Clone, PartialEq)] +pub struct QueueUnbindOk; + +/// Queues store and forward messages. Queues can be configured in the server or created at +/// runtime. Queues must be attached to at least one exchange in order to receive messages +/// from publishers. +/// This method removes all messages from a queue which are not awaiting +/// acknowledgment. +#[derive(Debug, Clone, PartialEq)] +pub struct QueuePurge { + pub reserved_1: Short, + /// Specifies the name of the queue to purge. + pub queue: QueueName, + pub no_wait: NoWait, +} + +/// Queues store and forward messages. Queues can be configured in the server or created at +/// runtime. Queues must be attached to at least one exchange in order to receive messages +/// from publishers. +/// This method confirms the purge of a queue. +#[derive(Debug, Clone, PartialEq)] +pub struct QueuePurgeOk { + /// Reports the number of messages purged. + pub message_count: MessageCount, +} + +/// Queues store and forward messages. Queues can be configured in the server or created at +/// runtime. Queues must be attached to at least one exchange in order to receive messages +/// from publishers. +/// This method deletes a queue. When a queue is deleted any pending messages are sent +/// to a dead-letter queue if this is defined in the server configuration, and all +/// consumers on the queue are cancelled. +#[derive(Debug, Clone, PartialEq)] +pub struct QueueDelete { + pub reserved_1: Short, + /// Specifies the name of the queue to delete. + pub queue: QueueName, + /// If set, the server will only delete the queue if it has no consumers. If the + /// queue has consumers the server does does not delete it but raises a channel + /// exception instead. + pub if_unused: Bit, + /// If set, the server will only delete the queue if it has no messages. + pub if_empty: Bit, + pub no_wait: NoWait, +} + +/// Queues store and forward messages. Queues can be configured in the server or created at +/// runtime. Queues must be attached to at least one exchange in order to receive messages +/// from publishers. +/// This method confirms the deletion of a queue. +#[derive(Debug, Clone, PartialEq)] +pub struct QueueDeleteOk { + /// Reports the number of messages deleted. + pub message_count: MessageCount, +} + +/// The Basic class provides methods that support an industry-standard messaging model. +/// This method requests a specific quality of service. The QoS can be specified for the +/// current channel or for all channels on the connection. The particular properties and +/// semantics of a qos method always depend on the content class semantics. Though the +/// qos method could in principle apply to both peers, it is currently meaningful only +/// for the server. +#[derive(Debug, Clone, PartialEq)] +pub struct BasicQos { + /// The client can request that messages be sent in advance so that when the client + /// finishes processing a message, the following message is already held locally, + /// rather than needing to be sent down the channel. Prefetching gives a performance + /// improvement. This field specifies the prefetch window size in octets. The server + /// will send a message in advance if it is equal to or smaller in size than the + /// available prefetch size (and also falls into other prefetch limits). May be set + /// to zero, meaning "no specific limit", although other prefetch limits may still + /// apply. The prefetch-size is ignored if the no-ack option is set. + pub prefetch_size: Long, + /// Specifies a prefetch window in terms of whole messages. This field may be used + /// in combination with the prefetch-size field; a message will only be sent in + /// advance if both prefetch windows (and those at the channel and connection level) + /// allow it. The prefetch-count is ignored if the no-ack option is set. + pub prefetch_count: Short, + /// By default the QoS settings apply to the current channel only. If this field is + /// set, they are applied to the entire connection. + pub global: Bit, +} + +/// The Basic class provides methods that support an industry-standard messaging model. +/// This method tells the client that the requested QoS levels could be handled by the +/// server. The requested QoS applies to all active consumers until a new QoS is +/// defined. +#[derive(Debug, Clone, PartialEq)] +pub struct BasicQosOk; + +/// The Basic class provides methods that support an industry-standard messaging model. +/// This method asks the server to start a "consumer", which is a transient request for +/// messages from a specific queue. Consumers last as long as the channel they were +/// declared on, or until the client cancels them. +#[derive(Debug, Clone, PartialEq)] +pub struct BasicConsume { + pub reserved_1: Short, + /// Specifies the name of the queue to consume from. + pub queue: QueueName, + /// Specifies the identifier for the consumer. The consumer tag is local to a + /// channel, so two clients can use the same consumer tags. If this field is + /// empty the server will generate a unique tag. + pub consumer_tag: ConsumerTag, + pub no_local: NoLocal, + pub no_ack: NoAck, + /// Request exclusive consumer access, meaning only this consumer can access the + /// queue. + pub exclusive: Bit, + pub no_wait: NoWait, + /// A set of arguments for the consume. The syntax and semantics of these + /// arguments depends on the server implementation. + pub arguments: Table, +} + +/// The Basic class provides methods that support an industry-standard messaging model. +/// The server provides the client with a consumer tag, which is used by the client +/// for methods called on the consumer at a later stage. +#[derive(Debug, Clone, PartialEq)] +pub struct BasicConsumeOk { + /// Holds the consumer tag specified by the client or provided by the server. + pub consumer_tag: ConsumerTag, +} + +/// The Basic class provides methods that support an industry-standard messaging model. +/// This method cancels a consumer. This does not affect already delivered +/// messages, but it does mean the server will not send any more messages for +/// that consumer. The client may receive an arbitrary number of messages in +/// between sending the cancel method and receiving the cancel-ok reply. +#[derive(Debug, Clone, PartialEq)] +pub struct BasicCancel { + pub consumer_tag: ConsumerTag, + pub no_wait: NoWait, +} + +/// The Basic class provides methods that support an industry-standard messaging model. +/// This method confirms that the cancellation was completed. +#[derive(Debug, Clone, PartialEq)] +pub struct BasicCancelOk { + pub consumer_tag: ConsumerTag, +} + +/// The Basic class provides methods that support an industry-standard messaging model. +/// This method publishes a message to a specific exchange. The message will be routed +/// to queues as defined by the exchange configuration and distributed to any active +/// consumers when the transaction, if any, is committed. +#[derive(Debug, Clone, PartialEq)] +pub struct BasicPublish { + pub reserved_1: Short, + /// Specifies the name of the exchange to publish to. The exchange name can be + /// empty, meaning the default exchange. If the exchange name is specified, and that + /// exchange does not exist, the server will raise a channel exception. + pub exchange: ExchangeName, + /// Specifies the routing key for the message. The routing key is used for routing + /// messages depending on the exchange configuration. + pub routing_key: Shortstr, + /// This flag tells the server how to react if the message cannot be routed to a + /// queue. If this flag is set, the server will return an unroutable message with a + /// Return method. If this flag is zero, the server silently drops the message. + pub mandatory: Bit, + /// This flag tells the server how to react if the message cannot be routed to a + /// queue consumer immediately. If this flag is set, the server will return an + /// undeliverable message with a Return method. If this flag is zero, the server + /// will queue the message, but with no guarantee that it will ever be consumed. + pub immediate: Bit, +} + +/// The Basic class provides methods that support an industry-standard messaging model. +/// This method returns an undeliverable message that was published with the "immediate" +/// flag set, or an unroutable message published with the "mandatory" flag set. The +/// reply code and text provide information about the reason that the message was +/// undeliverable. +#[derive(Debug, Clone, PartialEq)] +pub struct BasicReturn { + pub reply_code: ReplyCode, + pub reply_text: ReplyText, + /// Specifies the name of the exchange that the message was originally published + /// to. May be empty, meaning the default exchange. + pub exchange: ExchangeName, + /// Specifies the routing key name specified when the message was published. + pub routing_key: Shortstr, +} + +/// The Basic class provides methods that support an industry-standard messaging model. +/// This method delivers a message to the client, via a consumer. In the asynchronous +/// message delivery model, the client starts a consumer using the Consume method, then +/// the server responds with Deliver methods as and when messages arrive for that +/// consumer. +#[derive(Debug, Clone, PartialEq)] +pub struct BasicDeliver { + pub consumer_tag: ConsumerTag, + pub delivery_tag: DeliveryTag, + pub redelivered: Redelivered, + /// Specifies the name of the exchange that the message was originally published to. + /// May be empty, indicating the default exchange. + pub exchange: ExchangeName, + /// Specifies the routing key name specified when the message was published. + pub routing_key: Shortstr, +} + +/// The Basic class provides methods that support an industry-standard messaging model. +/// This method provides a direct access to the messages in a queue using a synchronous +/// dialogue that is designed for specific types of application where synchronous +/// functionality is more important than performance. +#[derive(Debug, Clone, PartialEq)] +pub struct BasicGet { + pub reserved_1: Short, + /// Specifies the name of the queue to get a message from. + pub queue: QueueName, + pub no_ack: NoAck, +} + +/// The Basic class provides methods that support an industry-standard messaging model. +/// This method delivers a message to the client following a get method. A message +/// delivered by 'get-ok' must be acknowledged unless the no-ack option was set in the +/// get method. +#[derive(Debug, Clone, PartialEq)] +pub struct BasicGetOk { + pub delivery_tag: DeliveryTag, + pub redelivered: Redelivered, + /// Specifies the name of the exchange that the message was originally published to. + /// If empty, the message was published to the default exchange. + pub exchange: ExchangeName, + /// Specifies the routing key name specified when the message was published. + pub routing_key: Shortstr, + pub message_count: MessageCount, +} + +/// The Basic class provides methods that support an industry-standard messaging model. +/// This method tells the client that the queue has no messages available for the +/// client. +#[derive(Debug, Clone, PartialEq)] +pub struct BasicGetEmpty { + pub reserved_1: Shortstr, +} + +/// The Basic class provides methods that support an industry-standard messaging model. +/// This method acknowledges one or more messages delivered via the Deliver or Get-Ok +/// methods. The client can ask to confirm a single message or a set of messages up to +/// and including a specific message. +#[derive(Debug, Clone, PartialEq)] +pub struct BasicAck { + pub delivery_tag: DeliveryTag, + /// If set to 1, the delivery tag is treated as "up to and including", so that the + /// client can acknowledge multiple messages with a single method. If set to zero, + /// the delivery tag refers to a single message. If the multiple field is 1, and the + /// delivery tag is zero, tells the server to acknowledge all outstanding messages. + pub multiple: Bit, +} + +/// The Basic class provides methods that support an industry-standard messaging model. +/// This method allows a client to reject a message. It can be used to interrupt and +/// cancel large incoming messages, or return untreatable messages to their original +/// queue. +#[derive(Debug, Clone, PartialEq)] +pub struct BasicReject { + pub delivery_tag: DeliveryTag, + /// If requeue is true, the server will attempt to requeue the message. If requeue + /// is false or the requeue attempt fails the messages are discarded or dead-lettered. + pub requeue: Bit, +} + +/// The Basic class provides methods that support an industry-standard messaging model. +/// This method asks the server to redeliver all unacknowledged messages on a +/// specified channel. Zero or more messages may be redelivered. This method +/// is deprecated in favour of the synchronous Recover/Recover-Ok. +#[derive(Debug, Clone, PartialEq)] +pub struct BasicRecoverAsync { + /// If this field is zero, the message will be redelivered to the original + /// recipient. If this bit is 1, the server will attempt to requeue the message, + /// potentially then delivering it to an alternative subscriber. + pub requeue: Bit, +} + +/// The Basic class provides methods that support an industry-standard messaging model. +/// This method asks the server to redeliver all unacknowledged messages on a +/// specified channel. Zero or more messages may be redelivered. This method +/// replaces the asynchronous Recover. +#[derive(Debug, Clone, PartialEq)] +pub struct BasicRecover { + /// If this field is zero, the message will be redelivered to the original + /// recipient. If this bit is 1, the server will attempt to requeue the message, + /// potentially then delivering it to an alternative subscriber. + pub requeue: Bit, +} + +/// The Basic class provides methods that support an industry-standard messaging model. +/// This method acknowledges a Basic.Recover method. +#[derive(Debug, Clone, PartialEq)] +pub struct BasicRecoverOk; + +/// The Tx class allows publish and ack operations to be batched into atomic +/// units of work. The intention is that all publish and ack requests issued +/// within a transaction will complete successfully or none of them will. +/// Servers SHOULD implement atomic transactions at least where all publish +/// or ack requests affect a single queue. Transactions that cover multiple +/// queues may be non-atomic, given that queues can be created and destroyed +/// asynchronously, and such events do not form part of any transaction. +/// Further, the behaviour of transactions with respect to the immediate and +/// mandatory flags on Basic.Publish methods is not defined. +/// This method sets the channel to use standard transactions. The client must use this +/// method at least once on a channel before using the Commit or Rollback methods. +#[derive(Debug, Clone, PartialEq)] +pub struct TxSelect; + +/// The Tx class allows publish and ack operations to be batched into atomic +/// units of work. The intention is that all publish and ack requests issued +/// within a transaction will complete successfully or none of them will. +/// Servers SHOULD implement atomic transactions at least where all publish +/// or ack requests affect a single queue. Transactions that cover multiple +/// queues may be non-atomic, given that queues can be created and destroyed +/// asynchronously, and such events do not form part of any transaction. +/// Further, the behaviour of transactions with respect to the immediate and +/// mandatory flags on Basic.Publish methods is not defined. +/// This method confirms to the client that the channel was successfully set to use +/// standard transactions. +#[derive(Debug, Clone, PartialEq)] +pub struct TxSelectOk; + +/// The Tx class allows publish and ack operations to be batched into atomic +/// units of work. The intention is that all publish and ack requests issued +/// within a transaction will complete successfully or none of them will. +/// Servers SHOULD implement atomic transactions at least where all publish +/// or ack requests affect a single queue. Transactions that cover multiple +/// queues may be non-atomic, given that queues can be created and destroyed +/// asynchronously, and such events do not form part of any transaction. +/// Further, the behaviour of transactions with respect to the immediate and +/// mandatory flags on Basic.Publish methods is not defined. +/// This method commits all message publications and acknowledgments performed in +/// the current transaction. A new transaction starts immediately after a commit. +#[derive(Debug, Clone, PartialEq)] +pub struct TxCommit; + +/// The Tx class allows publish and ack operations to be batched into atomic +/// units of work. The intention is that all publish and ack requests issued +/// within a transaction will complete successfully or none of them will. +/// Servers SHOULD implement atomic transactions at least where all publish +/// or ack requests affect a single queue. Transactions that cover multiple +/// queues may be non-atomic, given that queues can be created and destroyed +/// asynchronously, and such events do not form part of any transaction. +/// Further, the behaviour of transactions with respect to the immediate and +/// mandatory flags on Basic.Publish methods is not defined. +/// This method confirms to the client that the commit succeeded. Note that if a commit +/// fails, the server raises a channel exception. +#[derive(Debug, Clone, PartialEq)] +pub struct TxCommitOk; + +/// The Tx class allows publish and ack operations to be batched into atomic +/// units of work. The intention is that all publish and ack requests issued +/// within a transaction will complete successfully or none of them will. +/// Servers SHOULD implement atomic transactions at least where all publish +/// or ack requests affect a single queue. Transactions that cover multiple +/// queues may be non-atomic, given that queues can be created and destroyed +/// asynchronously, and such events do not form part of any transaction. +/// Further, the behaviour of transactions with respect to the immediate and +/// mandatory flags on Basic.Publish methods is not defined. +/// This method abandons all message publications and acknowledgments performed in +/// the current transaction. A new transaction starts immediately after a rollback. +/// Note that unacked messages will not be automatically redelivered by rollback; +/// if that is required an explicit recover call should be issued. +#[derive(Debug, Clone, PartialEq)] +pub struct TxRollback; + +/// The Tx class allows publish and ack operations to be batched into atomic +/// units of work. The intention is that all publish and ack requests issued +/// within a transaction will complete successfully or none of them will. +/// Servers SHOULD implement atomic transactions at least where all publish +/// or ack requests affect a single queue. Transactions that cover multiple +/// queues may be non-atomic, given that queues can be created and destroyed +/// asynchronously, and such events do not form part of any transaction. +/// Further, the behaviour of transactions with respect to the immediate and +/// mandatory flags on Basic.Publish methods is not defined. +/// This method confirms to the client that the rollback succeeded. Note that if an +/// rollback fails, the server raises a channel exception. +#[derive(Debug, Clone, PartialEq)] +pub struct TxRollbackOk; diff --git a/amqp_messaging/src/methods/consume.rs b/amqp_messaging/src/methods/consume.rs index c5cd341..f2f0fc5 100644 --- a/amqp_messaging/src/methods/consume.rs +++ b/amqp_messaging/src/methods/consume.rs @@ -1,17 +1,10 @@ use amqp_core::connection::ChannelHandle; use amqp_core::error::ProtocolError; -use amqp_core::methods::{Bit, ConsumerTag, NoAck, NoLocal, NoWait, QueueName, Table}; +use amqp_core::methods::BasicConsume; -#[allow(clippy::too_many_arguments)] pub async fn consume( _channel_handle: ChannelHandle, - _queue: QueueName, - _consumer_tag: ConsumerTag, - _no_local: NoLocal, - _no_ack: NoAck, - _exclusive: Bit, - _no_wait: NoWait, - _arguments: Table, + _basic_consume: BasicConsume, ) -> Result<(), ProtocolError> { Ok(()) } diff --git a/amqp_messaging/src/methods/mod.rs b/amqp_messaging/src/methods/mod.rs index 478b23a..08c8b2e 100644 --- a/amqp_messaging/src/methods/mod.rs +++ b/amqp_messaging/src/methods/mod.rs @@ -22,82 +22,23 @@ pub async fn handle_method( info!(?method, "Handling method"); match method { - Method::ExchangeDeclare { .. } => amqp_todo!(), - Method::ExchangeDeclareOk => amqp_todo!(), - Method::ExchangeDelete { .. } => amqp_todo!(), - Method::ExchangeDeleteOk => amqp_todo!(), - Method::QueueDeclare { - queue, - passive, - durable, - exclusive, - auto_delete, - no_wait, - arguments, - .. - } => { - queue::declare( - channel_handle, - queue, - passive, - durable, - exclusive, - auto_delete, - no_wait, - arguments, - ) - .await - } + Method::ExchangeDeclare(_) => amqp_todo!(), + Method::ExchangeDeclareOk(_) => amqp_todo!(), + Method::ExchangeDelete(_) => amqp_todo!(), + Method::ExchangeDeleteOk(_) => amqp_todo!(), + Method::QueueDeclare(queue_declare) => queue::declare(channel_handle, queue_declare).await, Method::QueueDeclareOk { .. } => amqp_todo!(), - Method::QueueBind { - queue, - exchange, - routing_key, - no_wait, - arguments, - .. - } => { - queue::bind( - channel_handle, - queue, - exchange, - routing_key, - no_wait, - arguments, - ) - .await - } - Method::QueueBindOk => amqp_todo!(), + Method::QueueBind(queue_bind) => queue::bind(channel_handle, queue_bind).await, + Method::QueueBindOk(_) => amqp_todo!(), Method::QueueUnbind { .. } => amqp_todo!(), - Method::QueueUnbindOk => amqp_todo!(), + Method::QueueUnbindOk(_) => amqp_todo!(), Method::QueuePurge { .. } => amqp_todo!(), Method::QueuePurgeOk { .. } => amqp_todo!(), Method::QueueDelete { .. } => amqp_todo!(), Method::QueueDeleteOk { .. } => amqp_todo!(), Method::BasicQos { .. } => amqp_todo!(), - Method::BasicQosOk => amqp_todo!(), - Method::BasicConsume { - queue, - consumer_tag, - no_local, - no_ack, - exclusive, - no_wait, - arguments, - .. - } => { - consume::consume( - channel_handle, - queue, - consumer_tag, - no_local, - no_ack, - exclusive, - no_wait, - arguments, - ) - .await - } + Method::BasicQosOk(_) => amqp_todo!(), + Method::BasicConsume(consume) => consume::consume(channel_handle, consume).await, Method::BasicConsumeOk { .. } => amqp_todo!(), Method::BasicCancel { .. } => amqp_todo!(), Method::BasicCancelOk { .. } => amqp_todo!(), @@ -110,13 +51,13 @@ pub async fn handle_method( Method::BasicReject { .. } => amqp_todo!(), Method::BasicRecoverAsync { .. } => amqp_todo!(), Method::BasicRecover { .. } => amqp_todo!(), - Method::BasicRecoverOk => amqp_todo!(), - Method::TxSelect - | Method::TxSelectOk - | Method::TxCommit - | Method::TxCommitOk - | Method::TxRollback - | Method::TxRollbackOk => amqp_todo!(), + Method::BasicRecoverOk(_) => amqp_todo!(), + Method::TxSelect(_) + | Method::TxSelectOk(_) + | Method::TxCommit(_) + | Method::TxCommitOk(_) + | Method::TxRollback(_) + | Method::TxRollbackOk(_) => amqp_todo!(), Method::BasicPublish { .. } => { unreachable!("Basic.Publish is handled somewhere else because it has a body") } diff --git a/amqp_messaging/src/methods/queue.rs b/amqp_messaging/src/methods/queue.rs index fb7406f..0ca0f0d 100644 --- a/amqp_messaging/src/methods/queue.rs +++ b/amqp_messaging/src/methods/queue.rs @@ -1,23 +1,27 @@ use amqp_core::connection::ChannelHandle; use amqp_core::error::{ConException, ProtocolError}; -use amqp_core::methods::{Bit, ExchangeName, NoWait, QueueName, Shortstr, Table}; +use amqp_core::methods::{QueueBind, QueueDeclare}; use amqp_core::queue::{QueueDeletion, QueueId, RawQueue}; use amqp_core::{amqp_todo, GlobalData}; use parking_lot::Mutex; use std::sync::atomic::AtomicUsize; use std::sync::Arc; -#[allow(clippy::too_many_arguments)] pub async fn declare( channel_handle: ChannelHandle, - queue_name: QueueName, - passive: Bit, - durable: Bit, - exclusive: Bit, - auto_delete: Bit, - no_wait: NoWait, - arguments: Table, + queue_declare: QueueDeclare, ) -> Result<(), ProtocolError> { + let QueueDeclare { + queue: queue_name, + passive, + durable, + exclusive, + auto_delete, + no_wait, + arguments, + .. + } = queue_declare; + if !arguments.is_empty() { return Err(ConException::Todo.into()); } @@ -58,11 +62,7 @@ pub async fn declare( pub async fn bind( _channel_handle: ChannelHandle, - _queue: QueueName, - _exchange: ExchangeName, - _routing_key: Shortstr, - _no_wait: NoWait, - _arguments: Table, + _queue_bind: QueueBind, ) -> Result<(), ProtocolError> { amqp_todo!(); } diff --git a/amqp_transport/src/connection.rs b/amqp_transport/src/connection.rs index c485b5d..874bdec 100644 --- a/amqp_transport/src/connection.rs +++ b/amqp_transport/src/connection.rs @@ -3,7 +3,11 @@ use crate::frame::{ContentHeader, Frame, FrameType}; use crate::{frame, methods, sasl}; use amqp_core::connection::{ChannelHandle, ChannelNum, ConnectionHandle, ConnectionId}; use amqp_core::message::{MessageId, RawMessage, RoutingInformation}; -use amqp_core::methods::{FieldValue, Method, Table}; +use amqp_core::methods::{ + BasicPublish, ChannelClose, ChannelCloseOk, ChannelOpenOk, ConnectionClose, ConnectionCloseOk, + ConnectionOpen, ConnectionOpenOk, ConnectionStart, ConnectionStartOk, ConnectionTune, + ConnectionTuneOk, FieldValue, Method, Table, +}; use amqp_core::GlobalData; use anyhow::Context; use bytes::Bytes; @@ -134,7 +138,7 @@ impl Connection { } async fn start(&mut self) -> Result<()> { - let start_method = Method::ConnectionStart { + let start_method = Method::ConnectionStart(ConnectionStart { version_major: 0, version_minor: 9, server_properties: server_properties( @@ -144,7 +148,7 @@ impl Connection { ), mechanisms: "PLAIN".into(), locales: "en_US".into(), - }; + }); debug!(?start_method, "Sending Start method"); self.send_method(ChannelNum::zero(), start_method).await?; @@ -152,12 +156,12 @@ impl Connection { let start_ok = self.recv_method().await?; debug!(?start_ok, "Received Start-Ok"); - if let Method::ConnectionStartOk { + if let Method::ConnectionStartOk(ConnectionStartOk { mechanism, locale, response, .. - } = start_ok + }) = start_ok { ensure_conn(mechanism == "PLAIN")?; ensure_conn(locale == "en_US")?; @@ -171,11 +175,11 @@ impl Connection { } async fn tune(&mut self) -> Result<()> { - let tune_method = Method::ConnectionTune { + let tune_method = Method::ConnectionTune(ConnectionTune { channel_max: CHANNEL_MAX, frame_max: FRAME_SIZE_MAX, heartbeat: HEARTBEAT_DELAY, - }; + }); debug!("Sending Tune method"); self.send_method(ChannelNum::zero(), tune_method).await?; @@ -183,11 +187,11 @@ impl Connection { let tune_ok = self.recv_method().await?; debug!(?tune_ok, "Received Tune-Ok method"); - if let Method::ConnectionTuneOk { + if let Method::ConnectionTuneOk(ConnectionTuneOk { channel_max, frame_max, heartbeat, - } = tune_ok + }) = tune_ok { self.channel_max = channel_max; self.max_frame_size = usize::try_from(frame_max).unwrap(); @@ -202,15 +206,15 @@ impl Connection { let open = self.recv_method().await?; debug!(?open, "Received Open method"); - if let Method::ConnectionOpen { virtual_host, .. } = open { + if let Method::ConnectionOpen(ConnectionOpen { virtual_host, .. }) = open { ensure_conn(virtual_host == "/")?; } self.send_method( ChannelNum::zero(), - Method::ConnectionOpenOk { + Method::ConnectionOpenOk(ConnectionOpenOk { reserved_1: "".to_string(), - }, + }), ) .await?; @@ -242,15 +246,18 @@ impl Connection { .map(|channel| channel.status.take()); match method { - Method::ConnectionClose { + Method::ConnectionClose(ConnectionClose { reply_code, reply_text, class_id, method_id, - } => { + }) => { info!(%reply_code, %reply_text, %class_id, %method_id, "Closing connection"); - self.send_method(ChannelNum::zero(), Method::ConnectionCloseOk {}) - .await?; + self.send_method( + ChannelNum::zero(), + Method::ConnectionCloseOk(ConnectionCloseOk), + ) + .await?; return Err(ProtocolError::GracefulClose.into()); } Method::ChannelOpen { .. } => self.channel_open(frame.channel).await?, @@ -344,13 +351,13 @@ impl Connection { // The only method with content that is sent to the server is Basic.Publish. ensure_conn(header.class_id == BASIC_CLASS_ID)?; - if let Method::BasicPublish { + if let Method::BasicPublish(BasicPublish { exchange, routing_key, mandatory, immediate, .. - } = method + }) = method { let message = RawMessage { id: MessageId::random(), @@ -415,9 +422,9 @@ impl Connection { self.send_method( channel_num, - Method::ChannelOpenOk { + Method::ChannelOpenOk(ChannelOpenOk { reserved_1: Vec::new(), - }, + }), ) .await?; @@ -425,17 +432,18 @@ impl Connection { } async fn channel_close(&mut self, channel_id: ChannelNum, method: Method) -> Result<()> { - if let Method::ChannelClose { + if let Method::ChannelClose(ChannelClose { reply_code: code, reply_text: reason, .. - } = method + }) = method { info!(%code, %reason, "Closing channel"); if let Some(channel) = self.channels.remove(&channel_id) { drop(channel); - self.send_method(channel_id, Method::ChannelCloseOk).await?; + self.send_method(channel_id, Method::ChannelCloseOk(ChannelCloseOk)) + .await?; } else { return Err(ConException::Todo.into()); } diff --git a/amqp_transport/src/methods/generated.rs b/amqp_transport/src/methods/generated.rs index 789ecfe..7274a53 100644 --- a/amqp_transport/src/methods/generated.rs +++ b/amqp_transport/src/methods/generated.rs @@ -135,13 +135,13 @@ pub mod parse { } Ok(( input, - Method::ConnectionStart { + Method::ConnectionStart(ConnectionStart { version_major, version_minor, server_properties, mechanisms, locales, - }, + }), )) } fn connection_start_ok(input: &[u8]) -> IResult<'_, Method> { @@ -165,19 +165,22 @@ pub mod parse { } Ok(( input, - Method::ConnectionStartOk { + Method::ConnectionStartOk(ConnectionStartOk { client_properties, mechanism, response, locale, - }, + }), )) } fn connection_secure(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(20_u16.to_be_bytes())(input)?; let (input, challenge) = domain_longstr(input).map_err(fail_err("field challenge in method secure"))?; - Ok((input, Method::ConnectionSecure { challenge })) + Ok(( + input, + Method::ConnectionSecure(ConnectionSecure { challenge }), + )) } fn connection_secure_ok(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(21_u16.to_be_bytes())(input)?; @@ -186,7 +189,10 @@ pub mod parse { if response.is_empty() { fail!("string was null for field response") } - Ok((input, Method::ConnectionSecureOk { response })) + Ok(( + input, + Method::ConnectionSecureOk(ConnectionSecureOk { response }), + )) } fn connection_tune(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(30_u16.to_be_bytes())(input)?; @@ -198,11 +204,11 @@ pub mod parse { domain_short(input).map_err(fail_err("field heartbeat in method tune"))?; Ok(( input, - Method::ConnectionTune { + Method::ConnectionTune(ConnectionTune { channel_max, frame_max, heartbeat, - }, + }), )) } fn connection_tune_ok(input: &[u8]) -> IResult<'_, Method> { @@ -215,11 +221,11 @@ pub mod parse { domain_short(input).map_err(fail_err("field heartbeat in method tune-ok"))?; Ok(( input, - Method::ConnectionTuneOk { + Method::ConnectionTuneOk(ConnectionTuneOk { channel_max, frame_max, heartbeat, - }, + }), )) } fn connection_open(input: &[u8]) -> IResult<'_, Method> { @@ -232,18 +238,21 @@ pub mod parse { let reserved_2 = bits[0]; Ok(( input, - Method::ConnectionOpen { + Method::ConnectionOpen(ConnectionOpen { virtual_host, reserved_1, reserved_2, - }, + }), )) } fn connection_open_ok(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(41_u16.to_be_bytes())(input)?; let (input, reserved_1) = domain_shortstr(input).map_err(fail_err("field reserved-1 in method open-ok"))?; - Ok((input, Method::ConnectionOpenOk { reserved_1 })) + Ok(( + input, + Method::ConnectionOpenOk(ConnectionOpenOk { reserved_1 }), + )) } fn connection_close(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(50_u16.to_be_bytes())(input)?; @@ -257,17 +266,17 @@ pub mod parse { domain_method_id(input).map_err(fail_err("field method-id in method close"))?; Ok(( input, - Method::ConnectionClose { + Method::ConnectionClose(ConnectionClose { reply_code, reply_text, class_id, method_id, - }, + }), )) } fn connection_close_ok(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(51_u16.to_be_bytes())(input)?; - Ok((input, Method::ConnectionCloseOk {})) + Ok((input, Method::ConnectionCloseOk(ConnectionCloseOk {}))) } fn channel(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(20_u16.to_be_bytes())(input)?; @@ -285,25 +294,25 @@ pub mod parse { let (input, _) = tag(10_u16.to_be_bytes())(input)?; let (input, reserved_1) = domain_shortstr(input).map_err(fail_err("field reserved-1 in method open"))?; - Ok((input, Method::ChannelOpen { reserved_1 })) + Ok((input, Method::ChannelOpen(ChannelOpen { reserved_1 }))) } fn channel_open_ok(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(11_u16.to_be_bytes())(input)?; let (input, reserved_1) = domain_longstr(input).map_err(fail_err("field reserved-1 in method open-ok"))?; - Ok((input, Method::ChannelOpenOk { reserved_1 })) + Ok((input, Method::ChannelOpenOk(ChannelOpenOk { reserved_1 }))) } fn channel_flow(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(20_u16.to_be_bytes())(input)?; let (input, bits) = bit(input, 1).map_err(fail_err("field active in method flow"))?; let active = bits[0]; - Ok((input, Method::ChannelFlow { active })) + Ok((input, Method::ChannelFlow(ChannelFlow { active }))) } fn channel_flow_ok(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(21_u16.to_be_bytes())(input)?; let (input, bits) = bit(input, 1).map_err(fail_err("field active in method flow-ok"))?; let active = bits[0]; - Ok((input, Method::ChannelFlowOk { active })) + Ok((input, Method::ChannelFlowOk(ChannelFlowOk { active }))) } fn channel_close(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(40_u16.to_be_bytes())(input)?; @@ -317,17 +326,17 @@ pub mod parse { domain_method_id(input).map_err(fail_err("field method-id in method close"))?; Ok(( input, - Method::ChannelClose { + Method::ChannelClose(ChannelClose { reply_code, reply_text, class_id, method_id, - }, + }), )) } fn channel_close_ok(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(41_u16.to_be_bytes())(input)?; - Ok((input, Method::ChannelCloseOk {})) + Ok((input, Method::ChannelCloseOk(ChannelCloseOk {}))) } fn exchange(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(40_u16.to_be_bytes())(input)?; @@ -360,7 +369,7 @@ pub mod parse { domain_table(input).map_err(fail_err("field arguments in method declare"))?; Ok(( input, - Method::ExchangeDeclare { + Method::ExchangeDeclare(ExchangeDeclare { reserved_1, exchange, r#type, @@ -370,12 +379,12 @@ pub mod parse { reserved_3, no_wait, arguments, - }, + }), )) } fn exchange_declare_ok(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(11_u16.to_be_bytes())(input)?; - Ok((input, Method::ExchangeDeclareOk {})) + Ok((input, Method::ExchangeDeclareOk(ExchangeDeclareOk {}))) } fn exchange_delete(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(20_u16.to_be_bytes())(input)?; @@ -391,17 +400,17 @@ pub mod parse { let no_wait = bits[1]; Ok(( input, - Method::ExchangeDelete { + Method::ExchangeDelete(ExchangeDelete { reserved_1, exchange, if_unused, no_wait, - }, + }), )) } fn exchange_delete_ok(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(21_u16.to_be_bytes())(input)?; - Ok((input, Method::ExchangeDeleteOk {})) + Ok((input, Method::ExchangeDeleteOk(ExchangeDeleteOk {}))) } fn queue(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(50_u16.to_be_bytes())(input)?; @@ -435,7 +444,7 @@ pub mod parse { domain_table(input).map_err(fail_err("field arguments in method declare"))?; Ok(( input, - Method::QueueDeclare { + Method::QueueDeclare(QueueDeclare { reserved_1, queue, passive, @@ -444,7 +453,7 @@ pub mod parse { auto_delete, no_wait, arguments, - }, + }), )) } fn queue_declare_ok(input: &[u8]) -> IResult<'_, Method> { @@ -460,11 +469,11 @@ pub mod parse { domain_long(input).map_err(fail_err("field consumer-count in method declare-ok"))?; Ok(( input, - Method::QueueDeclareOk { + Method::QueueDeclareOk(QueueDeclareOk { queue, message_count, consumer_count, - }, + }), )) } fn queue_bind(input: &[u8]) -> IResult<'_, Method> { @@ -483,19 +492,19 @@ pub mod parse { domain_table(input).map_err(fail_err("field arguments in method bind"))?; Ok(( input, - Method::QueueBind { + Method::QueueBind(QueueBind { reserved_1, queue, exchange, routing_key, no_wait, arguments, - }, + }), )) } fn queue_bind_ok(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(21_u16.to_be_bytes())(input)?; - Ok((input, Method::QueueBindOk {})) + Ok((input, Method::QueueBindOk(QueueBindOk {}))) } fn queue_unbind(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(50_u16.to_be_bytes())(input)?; @@ -511,18 +520,18 @@ pub mod parse { domain_table(input).map_err(fail_err("field arguments in method unbind"))?; Ok(( input, - Method::QueueUnbind { + Method::QueueUnbind(QueueUnbind { reserved_1, queue, exchange, routing_key, arguments, - }, + }), )) } fn queue_unbind_ok(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(51_u16.to_be_bytes())(input)?; - Ok((input, Method::QueueUnbindOk {})) + Ok((input, Method::QueueUnbindOk(QueueUnbindOk {}))) } fn queue_purge(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(30_u16.to_be_bytes())(input)?; @@ -534,18 +543,18 @@ pub mod parse { let no_wait = bits[0]; Ok(( input, - Method::QueuePurge { + Method::QueuePurge(QueuePurge { reserved_1, queue, no_wait, - }, + }), )) } fn queue_purge_ok(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(31_u16.to_be_bytes())(input)?; let (input, message_count) = domain_message_count(input) .map_err(fail_err("field message-count in method purge-ok"))?; - Ok((input, Method::QueuePurgeOk { message_count })) + Ok((input, Method::QueuePurgeOk(QueuePurgeOk { message_count }))) } fn queue_delete(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(40_u16.to_be_bytes())(input)?; @@ -559,20 +568,23 @@ pub mod parse { let no_wait = bits[2]; Ok(( input, - Method::QueueDelete { + Method::QueueDelete(QueueDelete { reserved_1, queue, if_unused, if_empty, no_wait, - }, + }), )) } fn queue_delete_ok(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(41_u16.to_be_bytes())(input)?; let (input, message_count) = domain_message_count(input) .map_err(fail_err("field message-count in method delete-ok"))?; - Ok((input, Method::QueueDeleteOk { message_count })) + Ok(( + input, + Method::QueueDeleteOk(QueueDeleteOk { message_count }), + )) } fn basic(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(60_u16.to_be_bytes())(input)?; @@ -607,16 +619,16 @@ pub mod parse { let global = bits[0]; Ok(( input, - Method::BasicQos { + Method::BasicQos(BasicQos { prefetch_size, prefetch_count, global, - }, + }), )) } fn basic_qos_ok(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(11_u16.to_be_bytes())(input)?; - Ok((input, Method::BasicQosOk {})) + Ok((input, Method::BasicQosOk(BasicQosOk {}))) } fn basic_consume(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(20_u16.to_be_bytes())(input)?; @@ -635,7 +647,7 @@ pub mod parse { domain_table(input).map_err(fail_err("field arguments in method consume"))?; Ok(( input, - Method::BasicConsume { + Method::BasicConsume(BasicConsume { reserved_1, queue, consumer_tag, @@ -644,14 +656,17 @@ pub mod parse { exclusive, no_wait, arguments, - }, + }), )) } fn basic_consume_ok(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(21_u16.to_be_bytes())(input)?; let (input, consumer_tag) = domain_consumer_tag(input) .map_err(fail_err("field consumer-tag in method consume-ok"))?; - Ok((input, Method::BasicConsumeOk { consumer_tag })) + Ok(( + input, + Method::BasicConsumeOk(BasicConsumeOk { consumer_tag }), + )) } fn basic_cancel(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(30_u16.to_be_bytes())(input)?; @@ -661,17 +676,17 @@ pub mod parse { let no_wait = bits[0]; Ok(( input, - Method::BasicCancel { + Method::BasicCancel(BasicCancel { consumer_tag, no_wait, - }, + }), )) } fn basic_cancel_ok(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(31_u16.to_be_bytes())(input)?; let (input, consumer_tag) = domain_consumer_tag(input) .map_err(fail_err("field consumer-tag in method cancel-ok"))?; - Ok((input, Method::BasicCancelOk { consumer_tag })) + Ok((input, Method::BasicCancelOk(BasicCancelOk { consumer_tag }))) } fn basic_publish(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(40_u16.to_be_bytes())(input)?; @@ -686,13 +701,13 @@ pub mod parse { let immediate = bits[1]; Ok(( input, - Method::BasicPublish { + Method::BasicPublish(BasicPublish { reserved_1, exchange, routing_key, mandatory, immediate, - }, + }), )) } fn basic_return(input: &[u8]) -> IResult<'_, Method> { @@ -707,12 +722,12 @@ pub mod parse { domain_shortstr(input).map_err(fail_err("field routing-key in method return"))?; Ok(( input, - Method::BasicReturn { + Method::BasicReturn(BasicReturn { reply_code, reply_text, exchange, routing_key, - }, + }), )) } fn basic_deliver(input: &[u8]) -> IResult<'_, Method> { @@ -730,13 +745,13 @@ pub mod parse { domain_shortstr(input).map_err(fail_err("field routing-key in method deliver"))?; Ok(( input, - Method::BasicDeliver { + Method::BasicDeliver(BasicDeliver { consumer_tag, delivery_tag, redelivered, exchange, routing_key, - }, + }), )) } fn basic_get(input: &[u8]) -> IResult<'_, Method> { @@ -749,11 +764,11 @@ pub mod parse { let no_ack = bits[0]; Ok(( input, - Method::BasicGet { + Method::BasicGet(BasicGet { reserved_1, queue, no_ack, - }, + }), )) } fn basic_get_ok(input: &[u8]) -> IResult<'_, Method> { @@ -771,20 +786,20 @@ pub mod parse { .map_err(fail_err("field message-count in method get-ok"))?; Ok(( input, - Method::BasicGetOk { + Method::BasicGetOk(BasicGetOk { delivery_tag, redelivered, exchange, routing_key, message_count, - }, + }), )) } fn basic_get_empty(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(72_u16.to_be_bytes())(input)?; let (input, reserved_1) = domain_shortstr(input).map_err(fail_err("field reserved-1 in method get-empty"))?; - Ok((input, Method::BasicGetEmpty { reserved_1 })) + Ok((input, Method::BasicGetEmpty(BasicGetEmpty { reserved_1 }))) } fn basic_ack(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(80_u16.to_be_bytes())(input)?; @@ -794,10 +809,10 @@ pub mod parse { let multiple = bits[0]; Ok(( input, - Method::BasicAck { + Method::BasicAck(BasicAck { delivery_tag, multiple, - }, + }), )) } fn basic_reject(input: &[u8]) -> IResult<'_, Method> { @@ -808,10 +823,10 @@ pub mod parse { let requeue = bits[0]; Ok(( input, - Method::BasicReject { + Method::BasicReject(BasicReject { delivery_tag, requeue, - }, + }), )) } fn basic_recover_async(input: &[u8]) -> IResult<'_, Method> { @@ -819,17 +834,20 @@ pub mod parse { let (input, bits) = bit(input, 1).map_err(fail_err("field requeue in method recover-async"))?; let requeue = bits[0]; - Ok((input, Method::BasicRecoverAsync { requeue })) + Ok(( + input, + Method::BasicRecoverAsync(BasicRecoverAsync { requeue }), + )) } fn basic_recover(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(110_u16.to_be_bytes())(input)?; let (input, bits) = bit(input, 1).map_err(fail_err("field requeue in method recover"))?; let requeue = bits[0]; - Ok((input, Method::BasicRecover { requeue })) + Ok((input, Method::BasicRecover(BasicRecover { requeue }))) } fn basic_recover_ok(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(111_u16.to_be_bytes())(input)?; - Ok((input, Method::BasicRecoverOk {})) + Ok((input, Method::BasicRecoverOk(BasicRecoverOk {}))) } fn tx(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(90_u16.to_be_bytes())(input)?; @@ -845,27 +863,27 @@ pub mod parse { } fn tx_select(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(10_u16.to_be_bytes())(input)?; - Ok((input, Method::TxSelect {})) + Ok((input, Method::TxSelect(TxSelect {}))) } fn tx_select_ok(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(11_u16.to_be_bytes())(input)?; - Ok((input, Method::TxSelectOk {})) + Ok((input, Method::TxSelectOk(TxSelectOk {}))) } fn tx_commit(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(20_u16.to_be_bytes())(input)?; - Ok((input, Method::TxCommit {})) + Ok((input, Method::TxCommit(TxCommit {}))) } fn tx_commit_ok(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(21_u16.to_be_bytes())(input)?; - Ok((input, Method::TxCommitOk {})) + Ok((input, Method::TxCommitOk(TxCommitOk {}))) } fn tx_rollback(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(30_u16.to_be_bytes())(input)?; - Ok((input, Method::TxRollback {})) + Ok((input, Method::TxRollback(TxRollback {}))) } fn tx_rollback_ok(input: &[u8]) -> IResult<'_, Method> { let (input, _) = tag(31_u16.to_be_bytes())(input)?; - Ok((input, Method::TxRollbackOk {})) + Ok((input, Method::TxRollbackOk(TxRollbackOk {}))) } } pub mod write { @@ -876,13 +894,13 @@ pub mod write { pub fn write_method(method: Method, mut writer: W) -> Result<(), TransError> { match method { - Method::ConnectionStart { + Method::ConnectionStart(ConnectionStart { version_major, version_minor, server_properties, mechanisms, locales, - } => { + }) => { writer.write_all(&[0, 10, 0, 10])?; octet(version_major, &mut writer)?; octet(version_minor, &mut writer)?; @@ -890,107 +908,107 @@ pub mod write { longstr(mechanisms, &mut writer)?; longstr(locales, &mut writer)?; } - Method::ConnectionStartOk { + Method::ConnectionStartOk(ConnectionStartOk { client_properties, mechanism, response, locale, - } => { + }) => { writer.write_all(&[0, 10, 0, 11])?; table(client_properties, &mut writer)?; shortstr(mechanism, &mut writer)?; longstr(response, &mut writer)?; shortstr(locale, &mut writer)?; } - Method::ConnectionSecure { challenge } => { + Method::ConnectionSecure(ConnectionSecure { challenge }) => { writer.write_all(&[0, 10, 0, 20])?; longstr(challenge, &mut writer)?; } - Method::ConnectionSecureOk { response } => { + Method::ConnectionSecureOk(ConnectionSecureOk { response }) => { writer.write_all(&[0, 10, 0, 21])?; longstr(response, &mut writer)?; } - Method::ConnectionTune { + Method::ConnectionTune(ConnectionTune { channel_max, frame_max, heartbeat, - } => { + }) => { writer.write_all(&[0, 10, 0, 30])?; short(channel_max, &mut writer)?; long(frame_max, &mut writer)?; short(heartbeat, &mut writer)?; } - Method::ConnectionTuneOk { + Method::ConnectionTuneOk(ConnectionTuneOk { channel_max, frame_max, heartbeat, - } => { + }) => { writer.write_all(&[0, 10, 0, 31])?; short(channel_max, &mut writer)?; long(frame_max, &mut writer)?; short(heartbeat, &mut writer)?; } - Method::ConnectionOpen { + Method::ConnectionOpen(ConnectionOpen { virtual_host, reserved_1, reserved_2, - } => { + }) => { writer.write_all(&[0, 10, 0, 40])?; shortstr(virtual_host, &mut writer)?; shortstr(reserved_1, &mut writer)?; bit(&[reserved_2], &mut writer)?; } - Method::ConnectionOpenOk { reserved_1 } => { + Method::ConnectionOpenOk(ConnectionOpenOk { reserved_1 }) => { writer.write_all(&[0, 10, 0, 41])?; shortstr(reserved_1, &mut writer)?; } - Method::ConnectionClose { + Method::ConnectionClose(ConnectionClose { reply_code, reply_text, class_id, method_id, - } => { + }) => { writer.write_all(&[0, 10, 0, 50])?; short(reply_code, &mut writer)?; shortstr(reply_text, &mut writer)?; short(class_id, &mut writer)?; short(method_id, &mut writer)?; } - Method::ConnectionCloseOk {} => { + Method::ConnectionCloseOk(ConnectionCloseOk {}) => { writer.write_all(&[0, 10, 0, 51])?; } - Method::ChannelOpen { reserved_1 } => { + Method::ChannelOpen(ChannelOpen { reserved_1 }) => { writer.write_all(&[0, 20, 0, 10])?; shortstr(reserved_1, &mut writer)?; } - Method::ChannelOpenOk { reserved_1 } => { + Method::ChannelOpenOk(ChannelOpenOk { reserved_1 }) => { writer.write_all(&[0, 20, 0, 11])?; longstr(reserved_1, &mut writer)?; } - Method::ChannelFlow { active } => { + Method::ChannelFlow(ChannelFlow { active }) => { writer.write_all(&[0, 20, 0, 20])?; bit(&[active], &mut writer)?; } - Method::ChannelFlowOk { active } => { + Method::ChannelFlowOk(ChannelFlowOk { active }) => { writer.write_all(&[0, 20, 0, 21])?; bit(&[active], &mut writer)?; } - Method::ChannelClose { + Method::ChannelClose(ChannelClose { reply_code, reply_text, class_id, method_id, - } => { + }) => { writer.write_all(&[0, 20, 0, 40])?; short(reply_code, &mut writer)?; shortstr(reply_text, &mut writer)?; short(class_id, &mut writer)?; short(method_id, &mut writer)?; } - Method::ChannelCloseOk {} => { + Method::ChannelCloseOk(ChannelCloseOk {}) => { writer.write_all(&[0, 20, 0, 41])?; } - Method::ExchangeDeclare { + Method::ExchangeDeclare(ExchangeDeclare { reserved_1, exchange, r#type, @@ -1000,7 +1018,7 @@ pub mod write { reserved_3, no_wait, arguments, - } => { + }) => { writer.write_all(&[0, 40, 0, 10])?; short(reserved_1, &mut writer)?; shortstr(exchange, &mut writer)?; @@ -1011,24 +1029,24 @@ pub mod write { )?; table(arguments, &mut writer)?; } - Method::ExchangeDeclareOk {} => { + Method::ExchangeDeclareOk(ExchangeDeclareOk {}) => { writer.write_all(&[0, 40, 0, 11])?; } - Method::ExchangeDelete { + Method::ExchangeDelete(ExchangeDelete { reserved_1, exchange, if_unused, no_wait, - } => { + }) => { writer.write_all(&[0, 40, 0, 20])?; short(reserved_1, &mut writer)?; shortstr(exchange, &mut writer)?; bit(&[if_unused, no_wait], &mut writer)?; } - Method::ExchangeDeleteOk {} => { + Method::ExchangeDeleteOk(ExchangeDeleteOk {}) => { writer.write_all(&[0, 40, 0, 21])?; } - Method::QueueDeclare { + Method::QueueDeclare(QueueDeclare { reserved_1, queue, passive, @@ -1037,7 +1055,7 @@ pub mod write { auto_delete, no_wait, arguments, - } => { + }) => { writer.write_all(&[0, 50, 0, 10])?; short(reserved_1, &mut writer)?; shortstr(queue, &mut writer)?; @@ -1047,24 +1065,24 @@ pub mod write { )?; table(arguments, &mut writer)?; } - Method::QueueDeclareOk { + Method::QueueDeclareOk(QueueDeclareOk { queue, message_count, consumer_count, - } => { + }) => { writer.write_all(&[0, 50, 0, 11])?; shortstr(queue, &mut writer)?; long(message_count, &mut writer)?; long(consumer_count, &mut writer)?; } - Method::QueueBind { + Method::QueueBind(QueueBind { reserved_1, queue, exchange, routing_key, no_wait, arguments, - } => { + }) => { writer.write_all(&[0, 50, 0, 20])?; short(reserved_1, &mut writer)?; shortstr(queue, &mut writer)?; @@ -1073,16 +1091,16 @@ pub mod write { bit(&[no_wait], &mut writer)?; table(arguments, &mut writer)?; } - Method::QueueBindOk {} => { + Method::QueueBindOk(QueueBindOk {}) => { writer.write_all(&[0, 50, 0, 21])?; } - Method::QueueUnbind { + Method::QueueUnbind(QueueUnbind { reserved_1, queue, exchange, routing_key, arguments, - } => { + }) => { writer.write_all(&[0, 50, 0, 50])?; short(reserved_1, &mut writer)?; shortstr(queue, &mut writer)?; @@ -1090,53 +1108,53 @@ pub mod write { shortstr(routing_key, &mut writer)?; table(arguments, &mut writer)?; } - Method::QueueUnbindOk {} => { + Method::QueueUnbindOk(QueueUnbindOk {}) => { writer.write_all(&[0, 50, 0, 51])?; } - Method::QueuePurge { + Method::QueuePurge(QueuePurge { reserved_1, queue, no_wait, - } => { + }) => { writer.write_all(&[0, 50, 0, 30])?; short(reserved_1, &mut writer)?; shortstr(queue, &mut writer)?; bit(&[no_wait], &mut writer)?; } - Method::QueuePurgeOk { message_count } => { + Method::QueuePurgeOk(QueuePurgeOk { message_count }) => { writer.write_all(&[0, 50, 0, 31])?; long(message_count, &mut writer)?; } - Method::QueueDelete { + Method::QueueDelete(QueueDelete { reserved_1, queue, if_unused, if_empty, no_wait, - } => { + }) => { writer.write_all(&[0, 50, 0, 40])?; short(reserved_1, &mut writer)?; shortstr(queue, &mut writer)?; bit(&[if_unused, if_empty, no_wait], &mut writer)?; } - Method::QueueDeleteOk { message_count } => { + Method::QueueDeleteOk(QueueDeleteOk { message_count }) => { writer.write_all(&[0, 50, 0, 41])?; long(message_count, &mut writer)?; } - Method::BasicQos { + Method::BasicQos(BasicQos { prefetch_size, prefetch_count, global, - } => { + }) => { writer.write_all(&[0, 60, 0, 10])?; long(prefetch_size, &mut writer)?; short(prefetch_count, &mut writer)?; bit(&[global], &mut writer)?; } - Method::BasicQosOk {} => { + Method::BasicQosOk(BasicQosOk {}) => { writer.write_all(&[0, 60, 0, 11])?; } - Method::BasicConsume { + Method::BasicConsume(BasicConsume { reserved_1, queue, consumer_tag, @@ -1145,7 +1163,7 @@ pub mod write { exclusive, no_wait, arguments, - } => { + }) => { writer.write_all(&[0, 60, 0, 20])?; short(reserved_1, &mut writer)?; shortstr(queue, &mut writer)?; @@ -1153,54 +1171,54 @@ pub mod write { bit(&[no_local, no_ack, exclusive, no_wait], &mut writer)?; table(arguments, &mut writer)?; } - Method::BasicConsumeOk { consumer_tag } => { + Method::BasicConsumeOk(BasicConsumeOk { consumer_tag }) => { writer.write_all(&[0, 60, 0, 21])?; shortstr(consumer_tag, &mut writer)?; } - Method::BasicCancel { + Method::BasicCancel(BasicCancel { consumer_tag, no_wait, - } => { + }) => { writer.write_all(&[0, 60, 0, 30])?; shortstr(consumer_tag, &mut writer)?; bit(&[no_wait], &mut writer)?; } - Method::BasicCancelOk { consumer_tag } => { + Method::BasicCancelOk(BasicCancelOk { consumer_tag }) => { writer.write_all(&[0, 60, 0, 31])?; shortstr(consumer_tag, &mut writer)?; } - Method::BasicPublish { + Method::BasicPublish(BasicPublish { reserved_1, exchange, routing_key, mandatory, immediate, - } => { + }) => { writer.write_all(&[0, 60, 0, 40])?; short(reserved_1, &mut writer)?; shortstr(exchange, &mut writer)?; shortstr(routing_key, &mut writer)?; bit(&[mandatory, immediate], &mut writer)?; } - Method::BasicReturn { + Method::BasicReturn(BasicReturn { reply_code, reply_text, exchange, routing_key, - } => { + }) => { writer.write_all(&[0, 60, 0, 50])?; short(reply_code, &mut writer)?; shortstr(reply_text, &mut writer)?; shortstr(exchange, &mut writer)?; shortstr(routing_key, &mut writer)?; } - Method::BasicDeliver { + Method::BasicDeliver(BasicDeliver { consumer_tag, delivery_tag, redelivered, exchange, routing_key, - } => { + }) => { writer.write_all(&[0, 60, 0, 60])?; shortstr(consumer_tag, &mut writer)?; longlong(delivery_tag, &mut writer)?; @@ -1208,23 +1226,23 @@ pub mod write { shortstr(exchange, &mut writer)?; shortstr(routing_key, &mut writer)?; } - Method::BasicGet { + Method::BasicGet(BasicGet { reserved_1, queue, no_ack, - } => { + }) => { writer.write_all(&[0, 60, 0, 70])?; short(reserved_1, &mut writer)?; shortstr(queue, &mut writer)?; bit(&[no_ack], &mut writer)?; } - Method::BasicGetOk { + Method::BasicGetOk(BasicGetOk { delivery_tag, redelivered, exchange, routing_key, message_count, - } => { + }) => { writer.write_all(&[0, 60, 0, 71])?; longlong(delivery_tag, &mut writer)?; bit(&[redelivered], &mut writer)?; @@ -1232,53 +1250,53 @@ pub mod write { shortstr(routing_key, &mut writer)?; long(message_count, &mut writer)?; } - Method::BasicGetEmpty { reserved_1 } => { + Method::BasicGetEmpty(BasicGetEmpty { reserved_1 }) => { writer.write_all(&[0, 60, 0, 72])?; shortstr(reserved_1, &mut writer)?; } - Method::BasicAck { + Method::BasicAck(BasicAck { delivery_tag, multiple, - } => { + }) => { writer.write_all(&[0, 60, 0, 80])?; longlong(delivery_tag, &mut writer)?; bit(&[multiple], &mut writer)?; } - Method::BasicReject { + Method::BasicReject(BasicReject { delivery_tag, requeue, - } => { + }) => { writer.write_all(&[0, 60, 0, 90])?; longlong(delivery_tag, &mut writer)?; bit(&[requeue], &mut writer)?; } - Method::BasicRecoverAsync { requeue } => { + Method::BasicRecoverAsync(BasicRecoverAsync { requeue }) => { writer.write_all(&[0, 60, 0, 100])?; bit(&[requeue], &mut writer)?; } - Method::BasicRecover { requeue } => { + Method::BasicRecover(BasicRecover { requeue }) => { writer.write_all(&[0, 60, 0, 110])?; bit(&[requeue], &mut writer)?; } - Method::BasicRecoverOk {} => { + Method::BasicRecoverOk(BasicRecoverOk {}) => { writer.write_all(&[0, 60, 0, 111])?; } - Method::TxSelect {} => { + Method::TxSelect(TxSelect {}) => { writer.write_all(&[0, 90, 0, 10])?; } - Method::TxSelectOk {} => { + Method::TxSelectOk(TxSelectOk {}) => { writer.write_all(&[0, 90, 0, 11])?; } - Method::TxCommit {} => { + Method::TxCommit(TxCommit {}) => { writer.write_all(&[0, 90, 0, 20])?; } - Method::TxCommitOk {} => { + Method::TxCommitOk(TxCommitOk {}) => { writer.write_all(&[0, 90, 0, 21])?; } - Method::TxRollback {} => { + Method::TxRollback(TxRollback {}) => { writer.write_all(&[0, 90, 0, 30])?; } - Method::TxRollbackOk {} => { + Method::TxRollbackOk(TxRollbackOk {}) => { writer.write_all(&[0, 90, 0, 31])?; } } @@ -1296,76 +1314,76 @@ mod random { fn random(rng: &mut R) -> Self { match rng.gen_range(0u32..6) { 0 => match rng.gen_range(0u32..10) { - 0 => Method::ConnectionStart { + 0 => Method::ConnectionStart(ConnectionStart { version_major: RandomMethod::random(rng), version_minor: RandomMethod::random(rng), server_properties: RandomMethod::random(rng), mechanisms: RandomMethod::random(rng), locales: RandomMethod::random(rng), - }, - 1 => Method::ConnectionStartOk { + }), + 1 => Method::ConnectionStartOk(ConnectionStartOk { client_properties: RandomMethod::random(rng), mechanism: RandomMethod::random(rng), response: RandomMethod::random(rng), locale: RandomMethod::random(rng), - }, - 2 => Method::ConnectionSecure { + }), + 2 => Method::ConnectionSecure(ConnectionSecure { challenge: RandomMethod::random(rng), - }, - 3 => Method::ConnectionSecureOk { + }), + 3 => Method::ConnectionSecureOk(ConnectionSecureOk { response: RandomMethod::random(rng), - }, - 4 => Method::ConnectionTune { + }), + 4 => Method::ConnectionTune(ConnectionTune { channel_max: RandomMethod::random(rng), frame_max: RandomMethod::random(rng), heartbeat: RandomMethod::random(rng), - }, - 5 => Method::ConnectionTuneOk { + }), + 5 => Method::ConnectionTuneOk(ConnectionTuneOk { channel_max: RandomMethod::random(rng), frame_max: RandomMethod::random(rng), heartbeat: RandomMethod::random(rng), - }, - 6 => Method::ConnectionOpen { + }), + 6 => Method::ConnectionOpen(ConnectionOpen { virtual_host: RandomMethod::random(rng), reserved_1: RandomMethod::random(rng), reserved_2: RandomMethod::random(rng), - }, - 7 => Method::ConnectionOpenOk { + }), + 7 => Method::ConnectionOpenOk(ConnectionOpenOk { reserved_1: RandomMethod::random(rng), - }, - 8 => Method::ConnectionClose { + }), + 8 => Method::ConnectionClose(ConnectionClose { reply_code: RandomMethod::random(rng), reply_text: RandomMethod::random(rng), class_id: RandomMethod::random(rng), method_id: RandomMethod::random(rng), - }, - 9 => Method::ConnectionCloseOk {}, + }), + 9 => Method::ConnectionCloseOk(ConnectionCloseOk {}), _ => unreachable!(), }, 1 => match rng.gen_range(0u32..6) { - 0 => Method::ChannelOpen { + 0 => Method::ChannelOpen(ChannelOpen { reserved_1: RandomMethod::random(rng), - }, - 1 => Method::ChannelOpenOk { + }), + 1 => Method::ChannelOpenOk(ChannelOpenOk { reserved_1: RandomMethod::random(rng), - }, - 2 => Method::ChannelFlow { + }), + 2 => Method::ChannelFlow(ChannelFlow { active: RandomMethod::random(rng), - }, - 3 => Method::ChannelFlowOk { + }), + 3 => Method::ChannelFlowOk(ChannelFlowOk { active: RandomMethod::random(rng), - }, - 4 => Method::ChannelClose { + }), + 4 => Method::ChannelClose(ChannelClose { reply_code: RandomMethod::random(rng), reply_text: RandomMethod::random(rng), class_id: RandomMethod::random(rng), method_id: RandomMethod::random(rng), - }, - 5 => Method::ChannelCloseOk {}, + }), + 5 => Method::ChannelCloseOk(ChannelCloseOk {}), _ => unreachable!(), }, 2 => match rng.gen_range(0u32..4) { - 0 => Method::ExchangeDeclare { + 0 => Method::ExchangeDeclare(ExchangeDeclare { reserved_1: RandomMethod::random(rng), exchange: RandomMethod::random(rng), r#type: RandomMethod::random(rng), @@ -1375,19 +1393,19 @@ mod random { reserved_3: RandomMethod::random(rng), no_wait: RandomMethod::random(rng), arguments: RandomMethod::random(rng), - }, - 1 => Method::ExchangeDeclareOk {}, - 2 => Method::ExchangeDelete { + }), + 1 => Method::ExchangeDeclareOk(ExchangeDeclareOk {}), + 2 => Method::ExchangeDelete(ExchangeDelete { reserved_1: RandomMethod::random(rng), exchange: RandomMethod::random(rng), if_unused: RandomMethod::random(rng), no_wait: RandomMethod::random(rng), - }, - 3 => Method::ExchangeDeleteOk {}, + }), + 3 => Method::ExchangeDeleteOk(ExchangeDeleteOk {}), _ => unreachable!(), }, 3 => match rng.gen_range(0u32..10) { - 0 => Method::QueueDeclare { + 0 => Method::QueueDeclare(QueueDeclare { reserved_1: RandomMethod::random(rng), queue: RandomMethod::random(rng), passive: RandomMethod::random(rng), @@ -1396,57 +1414,57 @@ mod random { auto_delete: RandomMethod::random(rng), no_wait: RandomMethod::random(rng), arguments: RandomMethod::random(rng), - }, - 1 => Method::QueueDeclareOk { + }), + 1 => Method::QueueDeclareOk(QueueDeclareOk { queue: RandomMethod::random(rng), message_count: RandomMethod::random(rng), consumer_count: RandomMethod::random(rng), - }, - 2 => Method::QueueBind { + }), + 2 => Method::QueueBind(QueueBind { reserved_1: RandomMethod::random(rng), queue: RandomMethod::random(rng), exchange: RandomMethod::random(rng), routing_key: RandomMethod::random(rng), no_wait: RandomMethod::random(rng), arguments: RandomMethod::random(rng), - }, - 3 => Method::QueueBindOk {}, - 4 => Method::QueueUnbind { + }), + 3 => Method::QueueBindOk(QueueBindOk {}), + 4 => Method::QueueUnbind(QueueUnbind { reserved_1: RandomMethod::random(rng), queue: RandomMethod::random(rng), exchange: RandomMethod::random(rng), routing_key: RandomMethod::random(rng), arguments: RandomMethod::random(rng), - }, - 5 => Method::QueueUnbindOk {}, - 6 => Method::QueuePurge { + }), + 5 => Method::QueueUnbindOk(QueueUnbindOk {}), + 6 => Method::QueuePurge(QueuePurge { reserved_1: RandomMethod::random(rng), queue: RandomMethod::random(rng), no_wait: RandomMethod::random(rng), - }, - 7 => Method::QueuePurgeOk { + }), + 7 => Method::QueuePurgeOk(QueuePurgeOk { message_count: RandomMethod::random(rng), - }, - 8 => Method::QueueDelete { + }), + 8 => Method::QueueDelete(QueueDelete { reserved_1: RandomMethod::random(rng), queue: RandomMethod::random(rng), if_unused: RandomMethod::random(rng), if_empty: RandomMethod::random(rng), no_wait: RandomMethod::random(rng), - }, - 9 => Method::QueueDeleteOk { + }), + 9 => Method::QueueDeleteOk(QueueDeleteOk { message_count: RandomMethod::random(rng), - }, + }), _ => unreachable!(), }, 4 => match rng.gen_range(0u32..17) { - 0 => Method::BasicQos { + 0 => Method::BasicQos(BasicQos { prefetch_size: RandomMethod::random(rng), prefetch_count: RandomMethod::random(rng), global: RandomMethod::random(rng), - }, - 1 => Method::BasicQosOk {}, - 2 => Method::BasicConsume { + }), + 1 => Method::BasicQosOk(BasicQosOk {}), + 2 => Method::BasicConsume(BasicConsume { reserved_1: RandomMethod::random(rng), queue: RandomMethod::random(rng), consumer_tag: RandomMethod::random(rng), @@ -1455,76 +1473,76 @@ mod random { exclusive: RandomMethod::random(rng), no_wait: RandomMethod::random(rng), arguments: RandomMethod::random(rng), - }, - 3 => Method::BasicConsumeOk { + }), + 3 => Method::BasicConsumeOk(BasicConsumeOk { consumer_tag: RandomMethod::random(rng), - }, - 4 => Method::BasicCancel { + }), + 4 => Method::BasicCancel(BasicCancel { consumer_tag: RandomMethod::random(rng), no_wait: RandomMethod::random(rng), - }, - 5 => Method::BasicCancelOk { + }), + 5 => Method::BasicCancelOk(BasicCancelOk { consumer_tag: RandomMethod::random(rng), - }, - 6 => Method::BasicPublish { + }), + 6 => Method::BasicPublish(BasicPublish { reserved_1: RandomMethod::random(rng), exchange: RandomMethod::random(rng), routing_key: RandomMethod::random(rng), mandatory: RandomMethod::random(rng), immediate: RandomMethod::random(rng), - }, - 7 => Method::BasicReturn { + }), + 7 => Method::BasicReturn(BasicReturn { reply_code: RandomMethod::random(rng), reply_text: RandomMethod::random(rng), exchange: RandomMethod::random(rng), routing_key: RandomMethod::random(rng), - }, - 8 => Method::BasicDeliver { + }), + 8 => Method::BasicDeliver(BasicDeliver { consumer_tag: RandomMethod::random(rng), delivery_tag: RandomMethod::random(rng), redelivered: RandomMethod::random(rng), exchange: RandomMethod::random(rng), routing_key: RandomMethod::random(rng), - }, - 9 => Method::BasicGet { + }), + 9 => Method::BasicGet(BasicGet { reserved_1: RandomMethod::random(rng), queue: RandomMethod::random(rng), no_ack: RandomMethod::random(rng), - }, - 10 => Method::BasicGetOk { + }), + 10 => Method::BasicGetOk(BasicGetOk { delivery_tag: RandomMethod::random(rng), redelivered: RandomMethod::random(rng), exchange: RandomMethod::random(rng), routing_key: RandomMethod::random(rng), message_count: RandomMethod::random(rng), - }, - 11 => Method::BasicGetEmpty { + }), + 11 => Method::BasicGetEmpty(BasicGetEmpty { reserved_1: RandomMethod::random(rng), - }, - 12 => Method::BasicAck { + }), + 12 => Method::BasicAck(BasicAck { delivery_tag: RandomMethod::random(rng), multiple: RandomMethod::random(rng), - }, - 13 => Method::BasicReject { + }), + 13 => Method::BasicReject(BasicReject { delivery_tag: RandomMethod::random(rng), requeue: RandomMethod::random(rng), - }, - 14 => Method::BasicRecoverAsync { + }), + 14 => Method::BasicRecoverAsync(BasicRecoverAsync { requeue: RandomMethod::random(rng), - }, - 15 => Method::BasicRecover { + }), + 15 => Method::BasicRecover(BasicRecover { requeue: RandomMethod::random(rng), - }, - 16 => Method::BasicRecoverOk {}, + }), + 16 => Method::BasicRecoverOk(BasicRecoverOk {}), _ => unreachable!(), }, 5 => match rng.gen_range(0u32..6) { - 0 => Method::TxSelect {}, - 1 => Method::TxSelectOk {}, - 2 => Method::TxCommit {}, - 3 => Method::TxCommitOk {}, - 4 => Method::TxRollback {}, - 5 => Method::TxRollbackOk {}, + 0 => Method::TxSelect(TxSelect {}), + 1 => Method::TxSelectOk(TxSelectOk {}), + 2 => Method::TxCommit(TxCommit {}), + 3 => Method::TxCommitOk(TxCommitOk {}), + 4 => Method::TxRollback(TxRollback {}), + 5 => Method::TxRollbackOk(TxRollbackOk {}), _ => unreachable!(), }, _ => unreachable!(), diff --git a/amqp_transport/src/tests.rs b/amqp_transport/src/tests.rs index cb61337..f12c4d7 100644 --- a/amqp_transport/src/tests.rs +++ b/amqp_transport/src/tests.rs @@ -1,13 +1,13 @@ use crate::frame::FrameType; use crate::{frame, methods}; use amqp_core::connection::ChannelNum; -use amqp_core::methods::{FieldValue, Method}; +use amqp_core::methods::{ConnectionStart, ConnectionStartOk, FieldValue, Method}; use std::collections::HashMap; #[tokio::test] async fn write_start_ok_frame() { let mut payload = Vec::new(); - let method = Method::ConnectionStart { + let method = Method::ConnectionStart(ConnectionStart { version_major: 0, version_minor: 9, server_properties: HashMap::from([( @@ -16,7 +16,7 @@ async fn write_start_ok_frame() { )]), mechanisms: "PLAIN".into(), locales: "en_US".into(), - }; + }); methods::write::write_method(method, &mut payload).unwrap(); @@ -141,7 +141,7 @@ fn read_start_ok_payload() { assert_eq!( method, - Method::ConnectionStartOk { + Method::ConnectionStartOk(ConnectionStartOk { client_properties: HashMap::from([ ( "product".to_string(), @@ -179,6 +179,6 @@ fn read_start_ok_payload() { mechanism: "PLAIN".to_string(), response: "\x00admin\x00".into(), locale: "en_US".to_string() - } + }) ); } diff --git a/xtask/src/codegen/mod.rs b/xtask/src/codegen/mod.rs index 9600df1..bd66ad2 100644 --- a/xtask/src/codegen/mod.rs +++ b/xtask/src/codegen/mod.rs @@ -211,11 +211,31 @@ impl Codegen { for class in &amqp.classes { let enum_name = class.name.to_upper_camel_case(); + for method in &class.methods { + let method_name = method.name.to_upper_camel_case(); + write!( + self.output, + " {enum_name}{method_name}({enum_name}{method_name})," + ) + .ok(); + } + } + + writeln!(self.output, "}}\n").ok(); + + // now codegen the individual structs + for class in &amqp.classes { + let class_name = class.name.to_upper_camel_case(); for method in &class.methods { let method_name = method.name.to_upper_camel_case(); self.doc_comment(&class.doc, 4); self.doc_comment(&method.doc, 4); - write!(self.output, " {enum_name}{method_name}").ok(); + writeln!( + self.output, + "#[derive(Debug, Clone, PartialEq)] +pub struct {class_name}{method_name}" + ) + .ok(); if !method.fields.is_empty() { writeln!(self.output, " {{").ok(); for field in &method.fields { @@ -225,24 +245,22 @@ impl Codegen { field.asserts.as_ref(), ); if !field_docs.is_empty() { - writeln!(self.output, " /// {field_docs}").ok(); + writeln!(self.output, " /// {field_docs}").ok(); if !field.doc.is_empty() { - writeln!(self.output, " ///").ok(); - self.doc_comment(&field.doc, 8); + writeln!(self.output, " ///").ok(); + self.doc_comment(&field.doc, 4); } } else { - self.doc_comment(&field.doc, 8); + self.doc_comment(&field.doc, 4); } - writeln!(self.output, " {field_name}: {field_type},").ok(); + writeln!(self.output, " pub {field_name}: {field_type},").ok(); } - writeln!(self.output, " }},").ok(); + writeln!(self.output, " }}\n").ok(); } else { - writeln!(self.output, ",").ok(); + writeln!(self.output, ";\n").ok(); } } } - - writeln!(self.output, "}}\n").ok(); } fn amqp_type_to_rust_type(&self, amqp_type: &str) -> &'static str { diff --git a/xtask/src/codegen/parser.rs b/xtask/src/codegen/parser.rs index 1e3ecea..cd68b52 100644 --- a/xtask/src/codegen/parser.rs +++ b/xtask/src/codegen/parser.rs @@ -154,14 +154,14 @@ pub type IResult<'a, T> = nom::IResult<&'a [u8], T, TransError>; let method_name = method.name.to_upper_camel_case(); writeln!( self.output, - " Ok((input, Method::{class_name}{method_name} {{" + " Ok((input, Method::{class_name}{method_name}({class_name}{method_name} {{" ) .ok(); for field in &method.fields { let field_name = self.snake_case(&field.name); writeln!(self.output, " {field_name},").ok(); } - writeln!(self.output, " }}))").ok(); + writeln!(self.output, " }})))").ok(); writeln!(self.output, "}}").ok(); } diff --git a/xtask/src/codegen/random.rs b/xtask/src/codegen/random.rs index 6dbb287..9ab1442 100644 --- a/xtask/src/codegen/random.rs +++ b/xtask/src/codegen/random.rs @@ -43,7 +43,7 @@ use crate::methods::RandomMethod; let method_name = method.name.to_upper_camel_case(); writeln!( self.output, - " {i} => Method::{class_name}{method_name} {{" + " {i} => Method::{class_name}{method_name}( {class_name}{method_name}{{" ) .ok(); for field in &method.fields { @@ -54,7 +54,7 @@ use crate::methods::RandomMethod; ) .ok(); } - writeln!(self.output, " }},").ok(); + writeln!(self.output, " }}),").ok(); } writeln!( self.output, diff --git a/xtask/src/codegen/write.rs b/xtask/src/codegen/write.rs index 104e5df..e725121 100644 --- a/xtask/src/codegen/write.rs +++ b/xtask/src/codegen/write.rs @@ -22,12 +22,16 @@ pub fn write_method(method: Method, mut writer: W) -> Result<(), Trans for method in &class.methods { let method_name = method.name.to_upper_camel_case(); let method_index = method.index; - writeln!(self.output, " Method::{class_name}{method_name} {{").ok(); + writeln!( + self.output, + " Method::{class_name}{method_name}({class_name}{method_name} {{" + ) + .ok(); for field in &method.fields { let field_name = self.snake_case(&field.name); writeln!(self.output, " {field_name},").ok(); } - writeln!(self.output, " }} => {{").ok(); + writeln!(self.output, " }}) => {{").ok(); let [ci0, ci1] = class_index.to_be_bytes(); let [mi0, mi1] = method_index.to_be_bytes(); writeln!( From 4643483d700f4cc4760245d3d51e460235a4b8cb Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Sun, 27 Feb 2022 18:26:02 +0100 Subject: [PATCH 10/11] more queue stuff --- Cargo.lock | 4 +-- amqp_core/src/error.rs | 4 +-- amqp_core/src/lib.rs | 4 +-- amqp_core/src/macros.rs | 13 +++++++- amqp_core/src/queue.rs | 10 ++++-- amqp_dashboard/src/lib.rs | 23 +++++++++++++- amqp_messaging/src/methods/consume.rs | 7 ++-- amqp_messaging/src/methods/mod.rs | 16 ++++++---- amqp_messaging/src/methods/queue.rs | 46 +++++++++++++++++++-------- amqp_transport/src/connection.rs | 9 +++--- amqp_transport/src/error.rs | 4 +-- test-js/src/declare-queue.js | 18 +++++++++++ test-js/src/utils/utils.js | 6 ++++ xtask/src/codegen/parser.rs | 2 +- xtask/src/codegen/write.rs | 2 +- 15 files changed, 126 insertions(+), 42 deletions(-) create mode 100644 test-js/src/declare-queue.js diff --git a/Cargo.lock b/Cargo.lock index 2ac90bf..9aceb38 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -87,9 +87,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.54" +version = "1.0.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a99269dff3bc004caa411f38845c20303f1e393ca2bd6581576fa3a7f59577d" +checksum = "159bb86af3a200e19a068f4224eae4c8bb2d0fa054c7e5d1cacd5cef95e684cd" [[package]] name = "async-trait" diff --git a/amqp_core/src/error.rs b/amqp_core/src/error.rs index ab42013..d482d69 100644 --- a/amqp_core/src/error.rs +++ b/amqp_core/src/error.rs @@ -25,8 +25,8 @@ pub enum ConException { ChannelError, #[error("505 Unexpected Frame")] UnexpectedFrame, - #[error("540 Not implemented")] - NotImplemented, + #[error("540 Not implemented. '{0}'")] + NotImplemented(&'static str), #[error("xxx Not decided yet")] Todo, } diff --git a/amqp_core/src/lib.rs b/amqp_core/src/lib.rs index 90d1480..3ec91d8 100644 --- a/amqp_core/src/lib.rs +++ b/amqp_core/src/lib.rs @@ -8,7 +8,7 @@ pub mod methods; pub mod queue; use crate::connection::{ChannelHandle, ConnectionHandle}; -use crate::queue::{Queue, QueueId}; +use crate::queue::{Queue, QueueName}; use connection::{ChannelId, ConnectionId}; use parking_lot::Mutex; use std::collections::HashMap; @@ -44,7 +44,7 @@ impl GlobalData { pub struct GlobalDataInner { pub connections: HashMap, pub channels: HashMap, - pub queues: HashMap, + pub queues: HashMap, /// Todo: This is just for testing and will be removed later! pub default_exchange: HashMap, } diff --git a/amqp_core/src/macros.rs b/amqp_core/src/macros.rs index c5d791f..045154c 100644 --- a/amqp_core/src/macros.rs +++ b/amqp_core/src/macros.rs @@ -48,12 +48,23 @@ macro_rules! newtype { &self.0 } } + + impl std::convert::From for $name + where + $ty: From, + { + fn from(other: T) -> Self { + Self(other.into()) + } + } }; } #[macro_export] macro_rules! amqp_todo { () => { - return Err(::amqp_core::error::ConException::NotImplemented.into()) + return Err( + ::amqp_core::error::ConException::NotImplemented(concat!(file!(), ":", line!())).into(), + ) }; } diff --git a/amqp_core/src/queue.rs b/amqp_core/src/queue.rs index 9b75c13..4ee4161 100644 --- a/amqp_core/src/queue.rs +++ b/amqp_core/src/queue.rs @@ -1,5 +1,5 @@ use crate::message::Message; -use crate::{newtype_id, ChannelId}; +use crate::{newtype, newtype_id, ChannelId}; use parking_lot::Mutex; use std::sync::atomic::AtomicUsize; use std::sync::Arc; @@ -8,10 +8,16 @@ pub type Queue = Arc; newtype_id!(pub QueueId); +newtype!( + /// The name of a queue. A newtype wrapper around `Arc`, which guarantees cheap clones. + #[derive(Debug, Clone, PartialEq, Eq, Hash)] + pub QueueName: Arc +); + #[derive(Debug)] pub struct RawQueue { pub id: QueueId, - pub name: String, + pub name: QueueName, pub messages: Mutex>, // use a concurrent linked list??? pub durable: bool, pub exclusive: Option, diff --git a/amqp_dashboard/src/lib.rs b/amqp_dashboard/src/lib.rs index 1fef800..929f5ed 100644 --- a/amqp_dashboard/src/lib.rs +++ b/amqp_dashboard/src/lib.rs @@ -51,6 +51,7 @@ async fn get_style_css() -> Response { #[derive(Serialize)] struct Data { connections: Vec, + queues: Vec, } #[derive(Serialize)] @@ -66,6 +67,13 @@ struct Channel { number: u16, } +#[derive(Serialize)] +struct Queue { + id: String, + name: String, + durable: bool, +} + async fn get_data(global_data: GlobalData) -> impl IntoResponse { let global_data = global_data.lock(); @@ -92,7 +100,20 @@ async fn get_data(global_data: GlobalData) -> impl IntoResponse { }) .collect(); - let data = Data { connections }; + let queues = global_data + .queues + .values() + .map(|queue| Queue { + id: queue.id.to_string(), + name: queue.name.to_string(), + durable: queue.durable, + }) + .collect(); + + let data = Data { + connections, + queues, + }; Json(data) } diff --git a/amqp_messaging/src/methods/consume.rs b/amqp_messaging/src/methods/consume.rs index f2f0fc5..b7fb1bc 100644 --- a/amqp_messaging/src/methods/consume.rs +++ b/amqp_messaging/src/methods/consume.rs @@ -1,10 +1,11 @@ +use amqp_core::amqp_todo; use amqp_core::connection::ChannelHandle; use amqp_core::error::ProtocolError; -use amqp_core::methods::BasicConsume; +use amqp_core::methods::{BasicConsume, Method}; pub async fn consume( _channel_handle: ChannelHandle, _basic_consume: BasicConsume, -) -> Result<(), ProtocolError> { - Ok(()) +) -> Result { + amqp_todo!() } diff --git a/amqp_messaging/src/methods/mod.rs b/amqp_messaging/src/methods/mod.rs index 08c8b2e..d75d7e3 100644 --- a/amqp_messaging/src/methods/mod.rs +++ b/amqp_messaging/src/methods/mod.rs @@ -18,17 +18,19 @@ pub async fn handle_basic_publish(_channel_handle: ChannelHandle, message: Messa pub async fn handle_method( channel_handle: ChannelHandle, method: Method, -) -> Result<(), ProtocolError> { +) -> Result { info!(?method, "Handling method"); - match method { + let response = match method { Method::ExchangeDeclare(_) => amqp_todo!(), Method::ExchangeDeclareOk(_) => amqp_todo!(), Method::ExchangeDelete(_) => amqp_todo!(), Method::ExchangeDeleteOk(_) => amqp_todo!(), - Method::QueueDeclare(queue_declare) => queue::declare(channel_handle, queue_declare).await, + Method::QueueDeclare(queue_declare) => { + queue::declare(channel_handle, queue_declare).await? + } Method::QueueDeclareOk { .. } => amqp_todo!(), - Method::QueueBind(queue_bind) => queue::bind(channel_handle, queue_bind).await, + Method::QueueBind(queue_bind) => queue::bind(channel_handle, queue_bind).await?, Method::QueueBindOk(_) => amqp_todo!(), Method::QueueUnbind { .. } => amqp_todo!(), Method::QueueUnbindOk(_) => amqp_todo!(), @@ -38,7 +40,7 @@ pub async fn handle_method( Method::QueueDeleteOk { .. } => amqp_todo!(), Method::BasicQos { .. } => amqp_todo!(), Method::BasicQosOk(_) => amqp_todo!(), - Method::BasicConsume(consume) => consume::consume(channel_handle, consume).await, + Method::BasicConsume(consume) => consume::consume(channel_handle, consume).await?, Method::BasicConsumeOk { .. } => amqp_todo!(), Method::BasicCancel { .. } => amqp_todo!(), Method::BasicCancelOk { .. } => amqp_todo!(), @@ -62,5 +64,7 @@ pub async fn handle_method( unreachable!("Basic.Publish is handled somewhere else because it has a body") } _ => unreachable!("Method handled by transport layer"), - } + }; + + Ok(response) } diff --git a/amqp_messaging/src/methods/queue.rs b/amqp_messaging/src/methods/queue.rs index 0ca0f0d..903a330 100644 --- a/amqp_messaging/src/methods/queue.rs +++ b/amqp_messaging/src/methods/queue.rs @@ -1,7 +1,7 @@ use amqp_core::connection::ChannelHandle; use amqp_core::error::{ConException, ProtocolError}; -use amqp_core::methods::{QueueBind, QueueDeclare}; -use amqp_core::queue::{QueueDeletion, QueueId, RawQueue}; +use amqp_core::methods::{Method, QueueBind, QueueDeclare, QueueDeclareOk}; +use amqp_core::queue::{QueueDeletion, QueueId, QueueName, RawQueue}; use amqp_core::{amqp_todo, GlobalData}; use parking_lot::Mutex; use std::sync::atomic::AtomicUsize; @@ -10,7 +10,7 @@ use std::sync::Arc; pub async fn declare( channel_handle: ChannelHandle, queue_declare: QueueDeclare, -) -> Result<(), ProtocolError> { +) -> Result { let QueueDeclare { queue: queue_name, passive, @@ -22,12 +22,15 @@ pub async fn declare( .. } = queue_declare; + let queue_name = QueueName::new(queue_name.into()); + if !arguments.is_empty() { return Err(ConException::Todo.into()); } - let (global_data, id) = { + let global_data = { let channel = channel_handle.lock(); + let global_data = channel.global_data.clone(); if passive || no_wait { amqp_todo!(); @@ -47,31 +50,46 @@ pub async fn declare( }, }); - let global_data = channel.global_data.clone(); - { let mut global_data_lock = global_data.lock(); - global_data_lock.queues.insert(id, queue); + global_data_lock.queues.insert(queue_name.clone(), queue); } - (global_data, id) + global_data }; - bind_queue(global_data, id, (), queue_name).await + bind_queue(global_data, (), queue_name.clone().into_inner()).await?; + + Ok(Method::QueueDeclareOk(QueueDeclareOk { + queue: queue_name.to_string(), + message_count: 0, + consumer_count: 0, + })) } pub async fn bind( _channel_handle: ChannelHandle, _queue_bind: QueueBind, -) -> Result<(), ProtocolError> { +) -> Result { amqp_todo!(); } async fn bind_queue( - _global_data: GlobalData, - _queue: QueueId, + global_data: GlobalData, _exchange: (), - _routing_key: String, + routing_key: Arc, ) -> Result<(), ProtocolError> { - amqp_todo!(); + let mut global_data = global_data.lock(); + + // todo: don't + let queue = global_data + .queues + .get(&QueueName::new(routing_key.clone())) + .unwrap() + .clone(); + global_data + .default_exchange + .insert(routing_key.to_string(), queue); + + Ok(()) } diff --git a/amqp_transport/src/connection.rs b/amqp_transport/src/connection.rs index 874bdec..54ad139 100644 --- a/amqp_transport/src/connection.rs +++ b/amqp_transport/src/connection.rs @@ -223,7 +223,6 @@ impl Connection { async fn main_loop(&mut self) -> Result<()> { loop { - debug!("Waiting for next frame"); let frame = frame::read_frame(&mut self.stream, self.max_frame_size).await?; self.reset_timeout(); @@ -277,9 +276,11 @@ impl Connection { .clone(); // call into amqp_messaging to handle the method - // amqp_messaging then branches and spawns a new task for longer running things, - // so the connection task will only be "blocked" for a short amount of time - amqp_messaging::methods::handle_method(channel_handle, method).await?; + // it returns the response method that we are supposed to send + // maybe this might become an `Option` in the future + let return_method = + amqp_messaging::methods::handle_method(channel_handle, method).await?; + self.send_method(frame.channel, return_method).await?; } } Ok(()) diff --git a/amqp_transport/src/error.rs b/amqp_transport/src/error.rs index 419ebf0..28b6a0c 100644 --- a/amqp_transport/src/error.rs +++ b/amqp_transport/src/error.rs @@ -1,10 +1,8 @@ -#![allow(dead_code)] - use std::io::Error; pub use amqp_core::error::{ConException, ProtocolError}; -pub type StdResult = std::result::Result; +type StdResult = std::result::Result; pub type Result = StdResult; diff --git a/test-js/src/declare-queue.js b/test-js/src/declare-queue.js new file mode 100644 index 0000000..9a5f979 --- /dev/null +++ b/test-js/src/declare-queue.js @@ -0,0 +1,18 @@ +import { assert, connectAmqp } from './utils/utils.js'; + +const queueName = 'test-queue-124'; + +const connection = await connectAmqp(); + +const channel = await connection.createChannel(); + +const reply = await channel.assertQueue(queueName, { durable: true }); + +assert(reply.messageCount === 0, 'Message found in queue'); +assert(reply.consumerCount === 0, 'Consumer listening on queue'); +assert(reply.queue === queueName, 'Wrong queue name returned'); + +console.log(`created queue '${queueName}'`); + +await channel.close(); +await connection.close(); diff --git a/test-js/src/utils/utils.js b/test-js/src/utils/utils.js index 7cfcaad..a676864 100644 --- a/test-js/src/utils/utils.js +++ b/test-js/src/utils/utils.js @@ -16,3 +16,9 @@ export const connectAmqp = async () => { {} ); }; + +export const assert = (cond, msg) => { + if (!cond) { + throw new Error(`Assertion failed: ${msg}`); + } +}; diff --git a/xtask/src/codegen/parser.rs b/xtask/src/codegen/parser.rs index cd68b52..4234d1e 100644 --- a/xtask/src/codegen/parser.rs +++ b/xtask/src/codegen/parser.rs @@ -21,8 +21,8 @@ impl Codegen { self.output, "pub mod parse {{ use amqp_core::methods::*; -use crate::methods::parse_helper::*; use crate::error::TransError; +use crate::methods::parse_helper::*; use nom::{{branch::alt, bytes::complete::tag}}; use regex::Regex; use once_cell::sync::Lazy; diff --git a/xtask/src/codegen/write.rs b/xtask/src/codegen/write.rs index e725121..46015f0 100644 --- a/xtask/src/codegen/write.rs +++ b/xtask/src/codegen/write.rs @@ -7,8 +7,8 @@ impl Codegen { self.output, "pub mod write {{ use amqp_core::methods::*; -use crate::methods::write_helper::*; use crate::error::TransError; +use crate::methods::write_helper::*; use std::io::Write; pub fn write_method(method: Method, mut writer: W) -> Result<(), TransError> {{ From d90d61504cc80b1c09a1310948d4150e21563d21 Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Sun, 27 Feb 2022 18:41:14 +0100 Subject: [PATCH 11/11] even more queuing around --- amqp_dashboard/assets/index.html | 2 ++ amqp_dashboard/assets/script.js | 13 +++++++++++++ amqp_messaging/src/methods/consume.rs | 4 +++- amqp_messaging/src/methods/queue.rs | 10 +++++----- test-js/src/declare-queue.js | 2 +- 5 files changed, 24 insertions(+), 7 deletions(-) diff --git a/amqp_dashboard/assets/index.html b/amqp_dashboard/assets/index.html index 1b3a744..891ea73 100644 --- a/amqp_dashboard/assets/index.html +++ b/amqp_dashboard/assets/index.html @@ -14,6 +14,8 @@

AMQP Data

Connections

+

Queues

+
diff --git a/amqp_dashboard/assets/script.js b/amqp_dashboard/assets/script.js index 40ce29b..7b96d32 100644 --- a/amqp_dashboard/assets/script.js +++ b/amqp_dashboard/assets/script.js @@ -39,10 +39,23 @@ const renderConnections = (connections) => { wrapper.replaceChildren(table); }; +const renderQueues = (queues) => { + const wrapper = document.getElementById('queue-wrapper'); + + const table = renderTable( + ['Queue ID', 'Name', 'Durable'], + queues.map((queue) => { + return [queue.id, queue.name, queue.durable ? 'Yes' : 'No']; + }) + ); + wrapper.replaceChildren(table); +}; + const refresh = async () => { const fetched = await fetch('api/data'); const data = await fetched.json(); renderConnections(data.connections); + renderQueues(data.queues); }; setInterval(refresh, 1000); diff --git a/amqp_messaging/src/methods/consume.rs b/amqp_messaging/src/methods/consume.rs index b7fb1bc..79b4eaf 100644 --- a/amqp_messaging/src/methods/consume.rs +++ b/amqp_messaging/src/methods/consume.rs @@ -4,8 +4,10 @@ use amqp_core::error::ProtocolError; use amqp_core::methods::{BasicConsume, Method}; pub async fn consume( - _channel_handle: ChannelHandle, + channel_handle: ChannelHandle, _basic_consume: BasicConsume, ) -> Result { + let _channel = channel_handle.lock(); + amqp_todo!() } diff --git a/amqp_messaging/src/methods/queue.rs b/amqp_messaging/src/methods/queue.rs index 903a330..9b75a36 100644 --- a/amqp_messaging/src/methods/queue.rs +++ b/amqp_messaging/src/methods/queue.rs @@ -25,17 +25,17 @@ pub async fn declare( let queue_name = QueueName::new(queue_name.into()); if !arguments.is_empty() { - return Err(ConException::Todo.into()); + amqp_todo!(); + } + + if passive || no_wait || durable { + amqp_todo!(); } let global_data = { let channel = channel_handle.lock(); let global_data = channel.global_data.clone(); - if passive || no_wait { - amqp_todo!(); - } - let id = QueueId::random(); let queue = Arc::new(RawQueue { id, diff --git a/test-js/src/declare-queue.js b/test-js/src/declare-queue.js index 9a5f979..b81d670 100644 --- a/test-js/src/declare-queue.js +++ b/test-js/src/declare-queue.js @@ -6,7 +6,7 @@ const connection = await connectAmqp(); const channel = await connection.createChannel(); -const reply = await channel.assertQueue(queueName, { durable: true }); +const reply = await channel.assertQueue(queueName, { durable: false }); assert(reply.messageCount === 0, 'Message found in queue'); assert(reply.consumerCount === 0, 'Consumer listening on queue');