2018-08-02 17:58:19 +00:00
|
|
|
|
//! XML stream parser for XMPP
|
|
|
|
|
|
2022-03-22 22:29:25 +00:00
|
|
|
|
use crate::Error;
|
2018-12-18 18:04:31 +00:00
|
|
|
|
use bytes::{BufMut, BytesMut};
|
2022-03-22 22:29:25 +00:00
|
|
|
|
use log::debug;
|
|
|
|
|
use minidom::tree_builder::TreeBuilder;
|
2024-03-16 16:39:55 +00:00
|
|
|
|
use rxml::{Parse, RawParser};
|
2018-12-18 18:04:31 +00:00
|
|
|
|
use std::collections::HashMap;
|
|
|
|
|
use std::fmt::Write;
|
|
|
|
|
use std::io;
|
2023-06-18 12:21:49 +00:00
|
|
|
|
#[cfg(feature = "syntax-highlighting")]
|
|
|
|
|
use std::sync::OnceLock;
|
2020-03-05 00:25:24 +00:00
|
|
|
|
use tokio_util::codec::{Decoder, Encoder};
|
2019-10-22 23:32:41 +00:00
|
|
|
|
use xmpp_parsers::Element;
|
2017-06-01 22:42:57 +00:00
|
|
|
|
|
2023-06-18 12:21:49 +00:00
|
|
|
|
#[cfg(feature = "syntax-highlighting")]
|
|
|
|
|
static PS: OnceLock<syntect::parsing::SyntaxSet> = OnceLock::new();
|
|
|
|
|
#[cfg(feature = "syntax-highlighting")]
|
|
|
|
|
static SYNTAX: OnceLock<syntect::parsing::SyntaxReference> = OnceLock::new();
|
|
|
|
|
#[cfg(feature = "syntax-highlighting")]
|
|
|
|
|
static THEME: OnceLock<syntect::highlighting::Theme> = OnceLock::new();
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "syntax-highlighting")]
|
|
|
|
|
fn init_syntect() {
|
|
|
|
|
let ps = syntect::parsing::SyntaxSet::load_defaults_newlines();
|
|
|
|
|
let syntax = ps.find_syntax_by_extension("xml").unwrap();
|
|
|
|
|
let ts = syntect::highlighting::ThemeSet::load_defaults();
|
|
|
|
|
let theme = ts.themes["Solarized (dark)"].clone();
|
|
|
|
|
|
|
|
|
|
SYNTAX.set(syntax.clone()).unwrap();
|
|
|
|
|
PS.set(ps).unwrap();
|
|
|
|
|
THEME.set(theme).unwrap();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(feature = "syntax-highlighting")]
|
|
|
|
|
fn highlight_xml(xml: &str) -> String {
|
|
|
|
|
let mut h = syntect::easy::HighlightLines::new(SYNTAX.get().unwrap(), THEME.get().unwrap());
|
|
|
|
|
let ranges: Vec<_> = h.highlight_line(&xml, PS.get().unwrap()).unwrap();
|
|
|
|
|
let escaped = syntect::util::as_24_bit_terminal_escaped(&ranges[..], false);
|
|
|
|
|
format!("{}\x1b[0m", escaped)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[cfg(not(feature = "syntax-highlighting"))]
|
|
|
|
|
fn highlight_xml(xml: &str) -> &str {
|
|
|
|
|
xml
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-02 17:58:19 +00:00
|
|
|
|
/// Anything that can be sent or received on an XMPP/XML stream
|
2018-12-20 19:39:01 +00:00
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
2017-07-17 18:53:00 +00:00
|
|
|
|
pub enum Packet {
|
2018-08-02 17:58:19 +00:00
|
|
|
|
/// `<stream:stream>` start tag
|
2017-07-17 18:53:00 +00:00
|
|
|
|
StreamStart(HashMap<String, String>),
|
2018-08-02 17:58:19 +00:00
|
|
|
|
/// A complete stanza or nonza
|
2017-07-17 18:53:00 +00:00
|
|
|
|
Stanza(Element),
|
2018-08-02 17:58:19 +00:00
|
|
|
|
/// Plain text (think whitespace keep-alive)
|
2017-07-17 18:53:00 +00:00
|
|
|
|
Text(String),
|
2018-08-02 17:58:19 +00:00
|
|
|
|
/// `</stream:stream>` closing tag
|
2017-07-17 18:53:00 +00:00
|
|
|
|
StreamEnd,
|
|
|
|
|
}
|
2017-06-01 22:42:57 +00:00
|
|
|
|
|
2018-08-02 17:58:19 +00:00
|
|
|
|
/// Stateful encoder/decoder for a bytestream from/to XMPP `Packet`
|
2024-06-15 13:21:20 +00:00
|
|
|
|
pub struct XmppCodec {
|
2017-07-18 18:12:17 +00:00
|
|
|
|
/// Outgoing
|
|
|
|
|
ns: Option<String>,
|
|
|
|
|
/// Incoming
|
2024-03-16 16:39:55 +00:00
|
|
|
|
driver: RawParser,
|
2022-03-22 22:29:25 +00:00
|
|
|
|
stanza_builder: TreeBuilder,
|
2017-06-01 22:42:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
2024-06-15 13:21:20 +00:00
|
|
|
|
impl XmppCodec {
|
2018-08-02 17:58:19 +00:00
|
|
|
|
/// Constructor
|
2017-06-01 22:42:57 +00:00
|
|
|
|
pub fn new() -> Self {
|
2022-03-22 22:29:25 +00:00
|
|
|
|
let stanza_builder = TreeBuilder::new();
|
2024-03-16 16:39:55 +00:00
|
|
|
|
let driver = RawParser::new();
|
2023-06-18 12:21:49 +00:00
|
|
|
|
#[cfg(feature = "syntax-highlighting")]
|
|
|
|
|
if log::log_enabled!(log::Level::Debug) && PS.get().is_none() {
|
|
|
|
|
init_syntect();
|
|
|
|
|
}
|
2024-06-15 13:21:20 +00:00
|
|
|
|
XmppCodec {
|
2017-07-18 18:12:17 +00:00
|
|
|
|
ns: None,
|
2022-03-22 22:29:25 +00:00
|
|
|
|
driver,
|
|
|
|
|
stanza_builder,
|
2017-06-01 22:42:57 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-15 13:21:20 +00:00
|
|
|
|
impl Default for XmppCodec {
|
2017-07-20 22:19:08 +00:00
|
|
|
|
fn default() -> Self {
|
|
|
|
|
Self::new()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-15 13:21:20 +00:00
|
|
|
|
impl Decoder for XmppCodec {
|
2017-06-04 00:05:08 +00:00
|
|
|
|
type Item = Packet;
|
2022-03-22 22:29:25 +00:00
|
|
|
|
type Error = Error;
|
2017-06-01 22:42:57 +00:00
|
|
|
|
|
2017-06-04 00:05:08 +00:00
|
|
|
|
fn decode(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
2022-03-22 22:29:25 +00:00
|
|
|
|
loop {
|
2024-05-23 16:12:09 +00:00
|
|
|
|
let token = match self.driver.parse_buf(buf, false) {
|
2022-03-22 22:29:25 +00:00
|
|
|
|
Ok(Some(token)) => token,
|
|
|
|
|
Ok(None) => break,
|
|
|
|
|
Err(rxml::Error::IO(e)) if e.kind() == std::io::ErrorKind::WouldBlock => break,
|
|
|
|
|
Err(e) => return Err(minidom::Error::from(e).into()),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let had_stream_root = self.stanza_builder.depth() > 0;
|
|
|
|
|
self.stanza_builder.process_event(token)?;
|
|
|
|
|
let has_stream_root = self.stanza_builder.depth() > 0;
|
|
|
|
|
|
|
|
|
|
if !had_stream_root && has_stream_root {
|
|
|
|
|
let root = self.stanza_builder.top().unwrap();
|
|
|
|
|
let attrs =
|
|
|
|
|
root.attrs()
|
|
|
|
|
.map(|(name, value)| (name.to_owned(), value.to_owned()))
|
|
|
|
|
.chain(root.prefixes.declared_prefixes().iter().map(
|
|
|
|
|
|(prefix, namespace)| {
|
|
|
|
|
(
|
|
|
|
|
prefix
|
|
|
|
|
.as_ref()
|
|
|
|
|
.map(|prefix| format!("xmlns:{}", prefix))
|
|
|
|
|
.unwrap_or_else(|| "xmlns".to_owned()),
|
|
|
|
|
namespace.clone(),
|
|
|
|
|
)
|
|
|
|
|
},
|
|
|
|
|
))
|
|
|
|
|
.collect();
|
2023-06-18 12:21:49 +00:00
|
|
|
|
debug!("<< {}", highlight_xml(&String::from(root)));
|
2022-03-22 22:29:25 +00:00
|
|
|
|
return Ok(Some(Packet::StreamStart(attrs)));
|
|
|
|
|
} else if self.stanza_builder.depth() == 1 {
|
|
|
|
|
self.driver.release_temporaries();
|
|
|
|
|
|
|
|
|
|
if let Some(stanza) = self.stanza_builder.unshift_child() {
|
2023-06-18 12:21:49 +00:00
|
|
|
|
debug!("<< {}", highlight_xml(&String::from(&stanza)));
|
2022-03-22 22:29:25 +00:00
|
|
|
|
return Ok(Some(Packet::Stanza(stanza)));
|
2017-06-19 00:34:16 +00:00
|
|
|
|
}
|
2022-03-22 22:29:25 +00:00
|
|
|
|
} else if let Some(_) = self.stanza_builder.root.take() {
|
|
|
|
|
self.driver.release_temporaries();
|
2017-07-13 23:58:25 +00:00
|
|
|
|
|
2023-06-18 12:21:49 +00:00
|
|
|
|
debug!("<< {}", highlight_xml("</stream:stream>"));
|
2022-03-22 22:29:25 +00:00
|
|
|
|
return Ok(Some(Packet::StreamEnd));
|
2018-12-18 18:04:31 +00:00
|
|
|
|
}
|
2017-06-01 22:42:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-03-22 22:29:25 +00:00
|
|
|
|
Ok(None)
|
2017-06-01 22:42:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-07-17 18:53:00 +00:00
|
|
|
|
fn decode_eof(&mut self, buf: &mut BytesMut) -> Result<Option<Self::Item>, Self::Error> {
|
2017-06-04 00:05:08 +00:00
|
|
|
|
self.decode(buf)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-15 13:21:20 +00:00
|
|
|
|
impl Encoder<Packet> for XmppCodec {
|
2023-05-30 15:37:52 +00:00
|
|
|
|
type Error = Error;
|
2017-06-04 00:05:08 +00:00
|
|
|
|
|
2020-06-21 23:32:01 +00:00
|
|
|
|
fn encode(&mut self, item: Packet, dst: &mut BytesMut) -> Result<(), Self::Error> {
|
2017-07-18 20:12:00 +00:00
|
|
|
|
let remaining = dst.capacity() - dst.len();
|
|
|
|
|
let max_stanza_size: usize = 2usize.pow(16);
|
|
|
|
|
if remaining < max_stanza_size {
|
|
|
|
|
dst.reserve(max_stanza_size - remaining);
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-29 00:50:55 +00:00
|
|
|
|
fn to_io_err<E: Into<Box<dyn std::error::Error + Send + Sync>>>(e: E) -> io::Error {
|
|
|
|
|
io::Error::new(io::ErrorKind::InvalidInput, e)
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-04 00:05:08 +00:00
|
|
|
|
match item {
|
2017-06-05 00:50:22 +00:00
|
|
|
|
Packet::StreamStart(start_attrs) => {
|
|
|
|
|
let mut buf = String::new();
|
2019-10-22 23:32:41 +00:00
|
|
|
|
write!(buf, "<stream:stream").map_err(to_io_err)?;
|
2017-07-20 22:19:08 +00:00
|
|
|
|
for (name, value) in start_attrs {
|
2019-10-22 23:32:41 +00:00
|
|
|
|
write!(buf, " {}=\"{}\"", escape(&name), escape(&value)).map_err(to_io_err)?;
|
2017-07-18 18:12:17 +00:00
|
|
|
|
if name == "xmlns" {
|
|
|
|
|
self.ns = Some(value);
|
|
|
|
|
}
|
2017-06-05 00:50:22 +00:00
|
|
|
|
}
|
2023-06-21 10:02:30 +00:00
|
|
|
|
write!(buf, ">").map_err(to_io_err)?;
|
2017-06-05 00:50:22 +00:00
|
|
|
|
|
2023-06-21 10:02:11 +00:00
|
|
|
|
write!(dst, "{}", buf)?;
|
2023-05-30 15:37:52 +00:00
|
|
|
|
let utf8 = std::str::from_utf8(dst)?;
|
2023-06-21 10:02:11 +00:00
|
|
|
|
debug!(">> {}", highlight_xml(utf8))
|
2023-05-30 15:37:52 +00:00
|
|
|
|
}
|
|
|
|
|
Packet::Stanza(stanza) => {
|
|
|
|
|
let _ = stanza
|
|
|
|
|
.write_to(&mut WriteBytes::new(dst))
|
|
|
|
|
.map_err(|e| to_io_err(format!("{}", e)))?;
|
|
|
|
|
let utf8 = std::str::from_utf8(dst)?;
|
2023-06-18 12:21:49 +00:00
|
|
|
|
debug!(">> {}", highlight_xml(utf8));
|
2023-05-30 15:37:52 +00:00
|
|
|
|
}
|
|
|
|
|
Packet::Text(text) => {
|
|
|
|
|
let _ = write_text(&text, dst).map_err(to_io_err)?;
|
|
|
|
|
let utf8 = std::str::from_utf8(dst)?;
|
2023-06-18 12:21:49 +00:00
|
|
|
|
debug!(">> {}", highlight_xml(utf8));
|
2023-05-30 15:37:52 +00:00
|
|
|
|
}
|
|
|
|
|
Packet::StreamEnd => {
|
|
|
|
|
let _ = write!(dst, "</stream:stream>\n").map_err(to_io_err);
|
2023-06-18 12:21:49 +00:00
|
|
|
|
debug!(">> {}", highlight_xml("</stream:stream>"));
|
2018-12-18 18:04:31 +00:00
|
|
|
|
}
|
2017-06-01 22:42:57 +00:00
|
|
|
|
}
|
2023-05-30 15:37:52 +00:00
|
|
|
|
|
|
|
|
|
Ok(())
|
2017-06-01 22:42:57 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2017-07-13 23:58:25 +00:00
|
|
|
|
|
2018-08-02 17:58:19 +00:00
|
|
|
|
/// Write XML-escaped text string
|
2017-07-18 18:12:17 +00:00
|
|
|
|
pub fn write_text<W: Write>(text: &str, writer: &mut W) -> Result<(), std::fmt::Error> {
|
2017-08-14 01:56:08 +00:00
|
|
|
|
write!(writer, "{}", escape(text))
|
2017-07-18 18:12:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-07-20 22:19:08 +00:00
|
|
|
|
/// Copied from `RustyXML` for now
|
2017-07-17 18:53:00 +00:00
|
|
|
|
pub fn escape(input: &str) -> String {
|
|
|
|
|
let mut result = String::with_capacity(input.len());
|
|
|
|
|
|
|
|
|
|
for c in input.chars() {
|
|
|
|
|
match c {
|
|
|
|
|
'&' => result.push_str("&"),
|
|
|
|
|
'<' => result.push_str("<"),
|
|
|
|
|
'>' => result.push_str(">"),
|
|
|
|
|
'\'' => result.push_str("'"),
|
|
|
|
|
'"' => result.push_str("""),
|
2018-12-18 18:04:31 +00:00
|
|
|
|
o => result.push(o),
|
2017-07-17 18:53:00 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
result
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-14 01:56:08 +00:00
|
|
|
|
/// BytesMut impl only std::fmt::Write but not std::io::Write. The
|
|
|
|
|
/// latter trait is required for minidom's
|
|
|
|
|
/// `Element::write_to_inner()`.
|
|
|
|
|
struct WriteBytes<'a> {
|
|
|
|
|
dst: &'a mut BytesMut,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a> WriteBytes<'a> {
|
|
|
|
|
fn new(dst: &'a mut BytesMut) -> Self {
|
|
|
|
|
WriteBytes { dst }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<'a> std::io::Write for WriteBytes<'a> {
|
|
|
|
|
fn write(&mut self, buf: &[u8]) -> std::result::Result<usize, std::io::Error> {
|
|
|
|
|
self.dst.put_slice(buf);
|
|
|
|
|
Ok(buf.len())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn flush(&mut self) -> std::result::Result<(), std::io::Error> {
|
|
|
|
|
Ok(())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-13 23:58:25 +00:00
|
|
|
|
#[cfg(test)]
|
|
|
|
|
mod tests {
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_stream_start() {
|
2024-06-15 13:21:20 +00:00
|
|
|
|
let mut c = XmppCodec::new();
|
2017-07-13 23:58:25 +00:00
|
|
|
|
let mut b = BytesMut::with_capacity(1024);
|
2020-03-05 00:25:24 +00:00
|
|
|
|
b.put_slice(b"<?xml version='1.0'?><stream:stream xmlns:stream='http://etherx.jabber.org/streams' version='1.0' xmlns='jabber:client'>");
|
2017-07-13 23:58:25 +00:00
|
|
|
|
let r = c.decode(&mut b);
|
|
|
|
|
assert!(match r {
|
|
|
|
|
Ok(Some(Packet::StreamStart(_))) => true,
|
|
|
|
|
_ => false,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-26 20:07:15 +00:00
|
|
|
|
#[test]
|
|
|
|
|
fn test_stream_end() {
|
2024-06-15 13:21:20 +00:00
|
|
|
|
let mut c = XmppCodec::new();
|
2019-01-26 20:07:15 +00:00
|
|
|
|
let mut b = BytesMut::with_capacity(1024);
|
2020-03-05 00:25:24 +00:00
|
|
|
|
b.put_slice(b"<?xml version='1.0'?><stream:stream xmlns:stream='http://etherx.jabber.org/streams' version='1.0' xmlns='jabber:client'>");
|
2019-01-26 20:07:15 +00:00
|
|
|
|
let r = c.decode(&mut b);
|
|
|
|
|
assert!(match r {
|
|
|
|
|
Ok(Some(Packet::StreamStart(_))) => true,
|
|
|
|
|
_ => false,
|
|
|
|
|
});
|
2020-03-05 00:25:24 +00:00
|
|
|
|
b.put_slice(b"</stream:stream>");
|
2019-01-26 20:07:15 +00:00
|
|
|
|
let r = c.decode(&mut b);
|
|
|
|
|
assert!(match r {
|
|
|
|
|
Ok(Some(Packet::StreamEnd)) => true,
|
|
|
|
|
_ => false,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2017-07-13 23:58:25 +00:00
|
|
|
|
#[test]
|
|
|
|
|
fn test_truncated_stanza() {
|
2024-06-15 13:21:20 +00:00
|
|
|
|
let mut c = XmppCodec::new();
|
2017-07-13 23:58:25 +00:00
|
|
|
|
let mut b = BytesMut::with_capacity(1024);
|
2020-03-05 00:25:24 +00:00
|
|
|
|
b.put_slice(b"<?xml version='1.0'?><stream:stream xmlns:stream='http://etherx.jabber.org/streams' version='1.0' xmlns='jabber:client'>");
|
2017-07-13 23:58:25 +00:00
|
|
|
|
let r = c.decode(&mut b);
|
|
|
|
|
assert!(match r {
|
|
|
|
|
Ok(Some(Packet::StreamStart(_))) => true,
|
|
|
|
|
_ => false,
|
|
|
|
|
});
|
|
|
|
|
|
2020-03-05 00:25:24 +00:00
|
|
|
|
b.put_slice("<test>ß</test".as_bytes());
|
2017-07-13 23:58:25 +00:00
|
|
|
|
let r = c.decode(&mut b);
|
|
|
|
|
assert!(match r {
|
|
|
|
|
Ok(None) => true,
|
|
|
|
|
_ => false,
|
|
|
|
|
});
|
|
|
|
|
|
2020-03-05 00:25:24 +00:00
|
|
|
|
b.put_slice(b">");
|
2017-07-13 23:58:25 +00:00
|
|
|
|
let r = c.decode(&mut b);
|
|
|
|
|
assert!(match r {
|
2018-12-18 18:04:31 +00:00
|
|
|
|
Ok(Some(Packet::Stanza(ref el))) if el.name() == "test" && el.text() == "ß" => true,
|
2017-07-13 23:58:25 +00:00
|
|
|
|
_ => false,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_truncated_utf8() {
|
2024-06-15 13:21:20 +00:00
|
|
|
|
let mut c = XmppCodec::new();
|
2017-07-13 23:58:25 +00:00
|
|
|
|
let mut b = BytesMut::with_capacity(1024);
|
2020-03-05 00:25:24 +00:00
|
|
|
|
b.put_slice(b"<?xml version='1.0'?><stream:stream xmlns:stream='http://etherx.jabber.org/streams' version='1.0' xmlns='jabber:client'>");
|
2017-07-13 23:58:25 +00:00
|
|
|
|
let r = c.decode(&mut b);
|
|
|
|
|
assert!(match r {
|
|
|
|
|
Ok(Some(Packet::StreamStart(_))) => true,
|
|
|
|
|
_ => false,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
b.put(&b"<test>\xc3"[..]);
|
|
|
|
|
let r = c.decode(&mut b);
|
|
|
|
|
assert!(match r {
|
|
|
|
|
Ok(None) => true,
|
|
|
|
|
_ => false,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
b.put(&b"\x9f</test>"[..]);
|
|
|
|
|
let r = c.decode(&mut b);
|
|
|
|
|
assert!(match r {
|
2018-12-18 18:04:31 +00:00
|
|
|
|
Ok(Some(Packet::Stanza(ref el))) if el.name() == "test" && el.text() == "ß" => true,
|
2017-07-13 23:58:25 +00:00
|
|
|
|
_ => false,
|
|
|
|
|
});
|
|
|
|
|
}
|
2017-07-18 20:12:00 +00:00
|
|
|
|
|
2019-01-08 10:41:39 +00:00
|
|
|
|
/// test case for https://gitlab.com/xmpp-rs/tokio-xmpp/issues/3
|
|
|
|
|
#[test]
|
|
|
|
|
fn test_atrribute_prefix() {
|
2024-06-15 13:21:20 +00:00
|
|
|
|
let mut c = XmppCodec::new();
|
2019-01-08 10:41:39 +00:00
|
|
|
|
let mut b = BytesMut::with_capacity(1024);
|
2020-03-05 00:25:24 +00:00
|
|
|
|
b.put_slice(b"<?xml version='1.0'?><stream:stream xmlns:stream='http://etherx.jabber.org/streams' version='1.0' xmlns='jabber:client'>");
|
2019-01-08 10:41:39 +00:00
|
|
|
|
let r = c.decode(&mut b);
|
|
|
|
|
assert!(match r {
|
|
|
|
|
Ok(Some(Packet::StreamStart(_))) => true,
|
|
|
|
|
_ => false,
|
|
|
|
|
});
|
|
|
|
|
|
2020-03-05 00:25:24 +00:00
|
|
|
|
b.put_slice(b"<status xml:lang='en'>Test status</status>");
|
2019-01-08 10:41:39 +00:00
|
|
|
|
let r = c.decode(&mut b);
|
|
|
|
|
assert!(match r {
|
2019-10-22 23:32:41 +00:00
|
|
|
|
Ok(Some(Packet::Stanza(ref el)))
|
|
|
|
|
if el.name() == "status"
|
|
|
|
|
&& el.text() == "Test status"
|
|
|
|
|
&& el.attr("xml:lang").map_or(false, |a| a == "en") =>
|
|
|
|
|
true,
|
2019-01-08 10:41:39 +00:00
|
|
|
|
_ => false,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-13 20:54:26 +00:00
|
|
|
|
/// By default, encode() only gets a BytesMut that has 8 KiB space reserved.
|
2017-07-18 20:12:00 +00:00
|
|
|
|
#[test]
|
|
|
|
|
fn test_large_stanza() {
|
2020-03-05 00:25:24 +00:00
|
|
|
|
use futures::{executor::block_on, sink::SinkExt};
|
2018-12-18 18:04:31 +00:00
|
|
|
|
use std::io::Cursor;
|
2020-03-05 00:25:24 +00:00
|
|
|
|
use tokio_util::codec::FramedWrite;
|
2024-06-15 13:21:20 +00:00
|
|
|
|
let mut framed = FramedWrite::new(Cursor::new(vec![]), XmppCodec::new());
|
2017-07-18 20:12:00 +00:00
|
|
|
|
let mut text = "".to_owned();
|
|
|
|
|
for _ in 0..2usize.pow(15) {
|
|
|
|
|
text = text + "A";
|
|
|
|
|
}
|
2020-04-04 21:40:30 +00:00
|
|
|
|
let stanza = Element::builder("message", "jabber:client")
|
|
|
|
|
.append(
|
|
|
|
|
Element::builder("body", "jabber:client")
|
|
|
|
|
.append(text.as_ref())
|
|
|
|
|
.build(),
|
|
|
|
|
)
|
2017-07-18 20:12:00 +00:00
|
|
|
|
.build();
|
2020-03-05 00:25:24 +00:00
|
|
|
|
block_on(framed.send(Packet::Stanza(stanza))).expect("send");
|
2018-12-18 18:04:31 +00:00
|
|
|
|
assert_eq!(
|
|
|
|
|
framed.get_ref().get_ref(),
|
2022-03-22 22:29:25 +00:00
|
|
|
|
&format!(
|
2022-04-23 13:29:03 +00:00
|
|
|
|
"<message xmlns='jabber:client'><body>{}</body></message>",
|
2022-03-22 22:29:25 +00:00
|
|
|
|
text
|
|
|
|
|
)
|
|
|
|
|
.as_bytes()
|
2018-12-18 18:04:31 +00:00
|
|
|
|
);
|
2017-07-18 20:12:00 +00:00
|
|
|
|
}
|
2019-09-08 13:05:57 +00:00
|
|
|
|
|
|
|
|
|
#[test]
|
2020-01-21 17:01:41 +00:00
|
|
|
|
fn test_cut_out_stanza() {
|
2024-06-15 13:21:20 +00:00
|
|
|
|
let mut c = XmppCodec::new();
|
2019-09-08 13:05:57 +00:00
|
|
|
|
let mut b = BytesMut::with_capacity(1024);
|
2020-03-05 00:25:24 +00:00
|
|
|
|
b.put_slice(b"<?xml version='1.0'?><stream:stream xmlns:stream='http://etherx.jabber.org/streams' version='1.0' xmlns='jabber:client'>");
|
2019-09-08 13:05:57 +00:00
|
|
|
|
let r = c.decode(&mut b);
|
|
|
|
|
assert!(match r {
|
|
|
|
|
Ok(Some(Packet::StreamStart(_))) => true,
|
|
|
|
|
_ => false,
|
|
|
|
|
});
|
|
|
|
|
|
2020-03-05 00:25:24 +00:00
|
|
|
|
b.put_slice(b"<message ");
|
|
|
|
|
b.put_slice(b"type='chat'><body>Foo</body></message>");
|
2019-09-08 13:05:57 +00:00
|
|
|
|
let r = c.decode(&mut b);
|
|
|
|
|
assert!(match r {
|
2020-01-21 17:01:41 +00:00
|
|
|
|
Ok(Some(Packet::Stanza(_))) => true,
|
2019-09-08 13:05:57 +00:00
|
|
|
|
_ => false,
|
|
|
|
|
});
|
|
|
|
|
}
|
2017-07-13 23:58:25 +00:00
|
|
|
|
}
|