2018-08-02 17:58:19 +00:00
|
|
|
//! XML stream parser for XMPP
|
|
|
|
|
2017-06-01 22:42:57 +00:00
|
|
|
use std;
|
2017-07-17 18:53:00 +00:00
|
|
|
use std::default::Default;
|
|
|
|
use std::iter::FromIterator;
|
|
|
|
use std::cell::RefCell;
|
|
|
|
use std::rc::Rc;
|
2017-06-04 00:05:08 +00:00
|
|
|
use std::fmt::Write;
|
2018-09-06 15:46:06 +00:00
|
|
|
use std::str::from_utf8;
|
2018-08-05 19:52:32 +00:00
|
|
|
use std::io;
|
2017-06-01 22:42:57 +00:00
|
|
|
use std::collections::HashMap;
|
2017-07-17 18:53:00 +00:00
|
|
|
use std::collections::vec_deque::VecDeque;
|
2018-07-31 21:34:04 +00:00
|
|
|
use tokio_codec::{Encoder, Decoder};
|
2017-08-14 01:56:08 +00:00
|
|
|
use minidom::Element;
|
2017-07-17 18:53:00 +00:00
|
|
|
use xml5ever::tokenizer::{XmlTokenizer, TokenSink, Token, Tag, TagKind};
|
2017-07-18 18:12:17 +00:00
|
|
|
use xml5ever::interface::Attribute;
|
2017-07-18 20:54:10 +00:00
|
|
|
use bytes::{BytesMut, BufMut};
|
2018-07-31 20:37:04 +00:00
|
|
|
use quick_xml::Writer as EventWriter;
|
2018-09-06 15:46:06 +00:00
|
|
|
use {ParserError, ParseError};
|
2017-06-01 22:42:57 +00:00
|
|
|
|
2018-08-02 17:58:19 +00:00
|
|
|
/// Anything that can be sent or received on an XMPP/XML stream
|
2017-07-17 18:53:00 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
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-05 19:52:32 +00:00
|
|
|
type QueueItem = Result<Packet, ParserError>;
|
|
|
|
|
2018-08-02 17:58:19 +00:00
|
|
|
/// Parser state
|
2017-07-17 18:53:00 +00:00
|
|
|
struct ParserSink {
|
2017-07-18 18:12:17 +00:00
|
|
|
// Ready stanzas, shared with XMPPCodec
|
2018-08-05 19:52:32 +00:00
|
|
|
queue: Rc<RefCell<VecDeque<QueueItem>>>,
|
2017-07-17 18:53:00 +00:00
|
|
|
// Parsing stack
|
|
|
|
stack: Vec<Element>,
|
2017-07-18 18:12:17 +00:00
|
|
|
ns_stack: Vec<HashMap<Option<String>, String>>,
|
2017-06-01 22:42:57 +00:00
|
|
|
}
|
|
|
|
|
2017-07-17 18:53:00 +00:00
|
|
|
impl ParserSink {
|
2018-08-05 19:52:32 +00:00
|
|
|
pub fn new(queue: Rc<RefCell<VecDeque<QueueItem>>>) -> Self {
|
2017-07-17 18:53:00 +00:00
|
|
|
ParserSink {
|
|
|
|
queue,
|
|
|
|
stack: vec![],
|
2017-07-18 18:12:17 +00:00
|
|
|
ns_stack: vec![],
|
2017-06-01 22:42:57 +00:00
|
|
|
}
|
2017-07-17 18:53:00 +00:00
|
|
|
}
|
2017-06-01 22:42:57 +00:00
|
|
|
|
2017-07-17 18:53:00 +00:00
|
|
|
fn push_queue(&self, pkt: Packet) {
|
2018-08-05 19:52:32 +00:00
|
|
|
self.queue.borrow_mut().push_back(Ok(pkt));
|
|
|
|
}
|
|
|
|
|
|
|
|
fn push_queue_error(&self, e: ParserError) {
|
|
|
|
self.queue.borrow_mut().push_back(Err(e));
|
2017-06-01 22:42:57 +00:00
|
|
|
}
|
|
|
|
|
2018-08-02 17:58:19 +00:00
|
|
|
/// Lookup XML namespace declaration for given prefix (or no prefix)
|
2017-07-18 18:12:17 +00:00
|
|
|
fn lookup_ns(&self, prefix: &Option<String>) -> Option<&str> {
|
|
|
|
for nss in self.ns_stack.iter().rev() {
|
2017-07-20 22:19:08 +00:00
|
|
|
if let Some(ns) = nss.get(prefix) {
|
|
|
|
return Some(ns);
|
2017-07-18 18:12:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
None
|
|
|
|
}
|
|
|
|
|
2017-07-17 18:53:00 +00:00
|
|
|
fn handle_start_tag(&mut self, tag: Tag) {
|
2017-07-18 18:12:17 +00:00
|
|
|
let mut nss = HashMap::new();
|
|
|
|
let is_prefix_xmlns = |attr: &Attribute| attr.name.prefix.as_ref()
|
|
|
|
.map(|prefix| prefix.eq_str_ignore_ascii_case("xmlns"))
|
|
|
|
.unwrap_or(false);
|
|
|
|
for attr in &tag.attrs {
|
|
|
|
match attr.name.local.as_ref() {
|
|
|
|
"xmlns" => {
|
|
|
|
nss.insert(None, attr.value.as_ref().to_owned());
|
|
|
|
},
|
|
|
|
prefix if is_prefix_xmlns(attr) => {
|
|
|
|
nss.insert(Some(prefix.to_owned()), attr.value.as_ref().to_owned());
|
|
|
|
},
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self.ns_stack.push(nss);
|
|
|
|
|
|
|
|
let el = {
|
|
|
|
let mut el_builder = Element::builder(tag.name.local.as_ref());
|
2017-07-20 22:19:08 +00:00
|
|
|
if let Some(el_ns) = self.lookup_ns(
|
|
|
|
&tag.name.prefix.map(|prefix|
|
|
|
|
prefix.as_ref().to_owned())
|
|
|
|
) {
|
|
|
|
el_builder = el_builder.ns(el_ns);
|
2017-07-18 18:12:17 +00:00
|
|
|
}
|
|
|
|
for attr in &tag.attrs {
|
|
|
|
match attr.name.local.as_ref() {
|
|
|
|
"xmlns" => (),
|
|
|
|
_ if is_prefix_xmlns(attr) => (),
|
|
|
|
_ => {
|
|
|
|
el_builder = el_builder.attr(
|
|
|
|
attr.name.local.as_ref(),
|
|
|
|
attr.value.as_ref()
|
|
|
|
);
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
el_builder.build()
|
|
|
|
};
|
|
|
|
|
|
|
|
if self.stack.is_empty() {
|
|
|
|
let attrs = HashMap::from_iter(
|
2017-07-18 23:02:45 +00:00
|
|
|
tag.attrs.iter()
|
|
|
|
.map(|attr| (attr.name.local.as_ref().to_owned(), attr.value.as_ref().to_owned()))
|
2017-07-18 18:12:17 +00:00
|
|
|
);
|
|
|
|
self.push_queue(Packet::StreamStart(attrs));
|
|
|
|
}
|
|
|
|
|
2017-07-17 18:53:00 +00:00
|
|
|
self.stack.push(el);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn handle_end_tag(&mut self) {
|
|
|
|
let el = self.stack.pop().unwrap();
|
2017-07-18 18:12:17 +00:00
|
|
|
self.ns_stack.pop();
|
|
|
|
|
2017-07-17 18:53:00 +00:00
|
|
|
match self.stack.len() {
|
|
|
|
// </stream:stream>
|
|
|
|
0 =>
|
|
|
|
self.push_queue(Packet::StreamEnd),
|
|
|
|
// </stanza>
|
|
|
|
1 =>
|
|
|
|
self.push_queue(Packet::Stanza(el)),
|
|
|
|
len => {
|
|
|
|
let parent = &mut self.stack[len - 1];
|
|
|
|
parent.append_child(el);
|
|
|
|
},
|
|
|
|
}
|
2017-06-01 22:42:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-17 18:53:00 +00:00
|
|
|
impl TokenSink for ParserSink {
|
|
|
|
fn process_token(&mut self, token: Token) {
|
|
|
|
match token {
|
|
|
|
Token::TagToken(tag) => match tag.kind {
|
|
|
|
TagKind::StartTag =>
|
|
|
|
self.handle_start_tag(tag),
|
|
|
|
TagKind::EndTag =>
|
|
|
|
self.handle_end_tag(),
|
|
|
|
TagKind::EmptyTag => {
|
|
|
|
self.handle_start_tag(tag);
|
|
|
|
self.handle_end_tag();
|
|
|
|
},
|
|
|
|
TagKind::ShortTag =>
|
2018-08-05 19:52:32 +00:00
|
|
|
self.push_queue_error(ParserError::ShortTag),
|
2017-07-17 18:53:00 +00:00
|
|
|
},
|
|
|
|
Token::CharacterTokens(tendril) =>
|
|
|
|
match self.stack.len() {
|
|
|
|
0 | 1 =>
|
|
|
|
self.push_queue(Packet::Text(tendril.into())),
|
|
|
|
len => {
|
|
|
|
let el = &mut self.stack[len - 1];
|
|
|
|
el.append_text_node(tendril);
|
|
|
|
},
|
|
|
|
},
|
|
|
|
Token::EOFToken =>
|
|
|
|
self.push_queue(Packet::StreamEnd),
|
|
|
|
Token::ParseError(s) => {
|
2018-08-02 23:14:21 +00:00
|
|
|
// println!("ParseError: {:?}", s);
|
2018-09-06 15:46:06 +00:00
|
|
|
self.push_queue_error(ParserError::Parse(ParseError(s)));
|
2017-07-17 18:53:00 +00:00
|
|
|
},
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// fn end(&mut self) {
|
|
|
|
// }
|
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`
|
2017-06-01 22:42:57 +00:00
|
|
|
pub struct XMPPCodec {
|
2017-07-18 18:12:17 +00:00
|
|
|
/// Outgoing
|
|
|
|
ns: Option<String>,
|
|
|
|
/// Incoming
|
2017-07-17 18:53:00 +00:00
|
|
|
parser: XmlTokenizer<ParserSink>,
|
2017-07-18 18:12:17 +00:00
|
|
|
/// For handling incoming truncated utf8
|
|
|
|
// TODO: optimize using tendrils?
|
2017-07-13 23:58:25 +00:00
|
|
|
buf: Vec<u8>,
|
2017-07-18 18:12:17 +00:00
|
|
|
/// Shared with ParserSink
|
2018-08-05 19:52:32 +00:00
|
|
|
queue: Rc<RefCell<VecDeque<QueueItem>>>,
|
2017-06-01 22:42:57 +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 {
|
2017-07-20 22:19:08 +00:00
|
|
|
let queue = Rc::new(RefCell::new(VecDeque::new()));
|
2017-07-17 18:53:00 +00:00
|
|
|
let sink = ParserSink::new(queue.clone());
|
|
|
|
// TODO: configure parser?
|
|
|
|
let parser = XmlTokenizer::new(sink, Default::default());
|
2017-06-01 22:42:57 +00:00
|
|
|
XMPPCodec {
|
2017-07-18 18:12:17 +00:00
|
|
|
ns: None,
|
2017-07-17 18:53:00 +00:00
|
|
|
parser,
|
|
|
|
queue,
|
2017-07-13 23:58:25 +00:00
|
|
|
buf: vec![],
|
2017-06-01 22:42:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-20 22:19:08 +00:00
|
|
|
impl Default for XMPPCodec {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self::new()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-04 00:05:08 +00:00
|
|
|
impl Decoder for XMPPCodec {
|
|
|
|
type Item = Packet;
|
2018-08-05 19:52:32 +00:00
|
|
|
type Error = ParserError;
|
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> {
|
2017-07-13 23:58:25 +00:00
|
|
|
let buf1: Box<AsRef<[u8]>> =
|
2017-07-20 22:19:08 +00:00
|
|
|
if ! self.buf.is_empty() && ! buf.is_empty() {
|
2017-07-13 23:58:25 +00:00
|
|
|
let mut prefix = std::mem::replace(&mut self.buf, vec![]);
|
|
|
|
prefix.extend_from_slice(buf.take().as_ref());
|
|
|
|
Box::new(prefix)
|
|
|
|
} else {
|
|
|
|
Box::new(buf.take())
|
|
|
|
};
|
|
|
|
let buf1 = buf1.as_ref().as_ref();
|
|
|
|
match from_utf8(buf1) {
|
2017-06-19 00:34:16 +00:00
|
|
|
Ok(s) => {
|
2017-07-20 22:19:08 +00:00
|
|
|
if ! s.is_empty() {
|
2018-08-02 23:14:21 +00:00
|
|
|
// println!("<< {}", s);
|
2017-07-17 18:53:00 +00:00
|
|
|
let tendril = FromIterator::from_iter(s.chars());
|
|
|
|
self.parser.feed(tendril);
|
2017-06-19 00:34:16 +00:00
|
|
|
}
|
|
|
|
},
|
2017-07-13 23:58:25 +00:00
|
|
|
// Remedies for truncated utf8
|
|
|
|
Err(e) if e.valid_up_to() >= buf1.len() - 3 => {
|
|
|
|
// Prepare all the valid data
|
|
|
|
let mut b = BytesMut::with_capacity(e.valid_up_to());
|
|
|
|
b.put(&buf1[0..e.valid_up_to()]);
|
|
|
|
|
|
|
|
// Retry
|
|
|
|
let result = self.decode(&mut b);
|
|
|
|
|
|
|
|
// Keep the tail back in
|
|
|
|
self.buf.extend_from_slice(&buf1[e.valid_up_to()..]);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
},
|
|
|
|
Err(e) => {
|
2018-08-02 23:14:21 +00:00
|
|
|
// println!("error {} at {}/{} in {:?}", e, e.valid_up_to(), buf1.len(), buf1);
|
2018-08-05 19:52:32 +00:00
|
|
|
return Err(ParserError::Utf8(e));
|
2017-07-13 23:58:25 +00:00
|
|
|
},
|
2017-06-01 22:42:57 +00:00
|
|
|
}
|
|
|
|
|
2018-08-05 19:52:32 +00:00
|
|
|
match self.queue.borrow_mut().pop_front() {
|
|
|
|
None => Ok(None),
|
|
|
|
Some(result) =>
|
|
|
|
result.map(|pkt| Some(pkt)),
|
|
|
|
}
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Encoder for XMPPCodec {
|
|
|
|
type Item = Packet;
|
2018-08-05 19:52:32 +00:00
|
|
|
type Error = io::Error;
|
2017-06-04 00:05:08 +00:00
|
|
|
|
|
|
|
fn encode(&mut self, item: Self::Item, 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);
|
|
|
|
}
|
|
|
|
|
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();
|
|
|
|
write!(buf, "<stream:stream").unwrap();
|
2017-07-20 22:19:08 +00:00
|
|
|
for (name, value) in start_attrs {
|
2017-07-17 18:53:00 +00:00
|
|
|
write!(buf, " {}=\"{}\"", escape(&name), escape(&value))
|
2017-06-05 00:50:22 +00:00
|
|
|
.unwrap();
|
2017-07-18 18:12:17 +00:00
|
|
|
if name == "xmlns" {
|
|
|
|
self.ns = Some(value);
|
|
|
|
}
|
2017-06-05 00:50:22 +00:00
|
|
|
}
|
|
|
|
write!(buf, ">\n").unwrap();
|
|
|
|
|
2017-06-19 00:34:16 +00:00
|
|
|
print!(">> {}", buf);
|
2017-06-05 00:50:22 +00:00
|
|
|
write!(dst, "{}", buf)
|
2018-08-05 19:52:32 +00:00
|
|
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))
|
2017-06-01 22:42:57 +00:00
|
|
|
},
|
2017-06-19 00:34:16 +00:00
|
|
|
Packet::Stanza(stanza) => {
|
2018-02-22 19:05:41 +00:00
|
|
|
stanza.write_to_inner(&mut EventWriter::new(WriteBytes::new(dst)))
|
2017-07-18 18:12:17 +00:00
|
|
|
.and_then(|_| {
|
2018-08-02 23:14:21 +00:00
|
|
|
// println!(">> {:?}", dst);
|
2017-07-18 18:12:17 +00:00
|
|
|
Ok(())
|
|
|
|
})
|
2018-08-05 19:52:32 +00:00
|
|
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, format!("{}", e)))
|
2017-06-19 00:34:16 +00:00
|
|
|
},
|
|
|
|
Packet::Text(text) => {
|
2017-07-18 18:12:17 +00:00
|
|
|
write_text(&text, dst)
|
|
|
|
.and_then(|_| {
|
2018-08-02 23:14:21 +00:00
|
|
|
// println!(">> {:?}", dst);
|
2017-07-18 18:12:17 +00:00
|
|
|
Ok(())
|
|
|
|
})
|
2018-08-05 19:52:32 +00:00
|
|
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, format!("{}", e)))
|
2017-06-19 00:34:16 +00:00
|
|
|
},
|
2017-06-01 22:42:57 +00:00
|
|
|
// TODO: Implement all
|
|
|
|
_ => Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
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("""),
|
|
|
|
o => result.push(o)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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::*;
|
|
|
|
use bytes::BytesMut;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_stream_start() {
|
|
|
|
let mut c = XMPPCodec::new();
|
|
|
|
let mut b = BytesMut::with_capacity(1024);
|
|
|
|
b.put(r"<?xml version='1.0'?><stream:stream xmlns:stream='http://etherx.jabber.org/streams' version='1.0' xmlns='jabber:client'>");
|
|
|
|
let r = c.decode(&mut b);
|
|
|
|
assert!(match r {
|
|
|
|
Ok(Some(Packet::StreamStart(_))) => true,
|
|
|
|
_ => false,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_truncated_stanza() {
|
|
|
|
let mut c = XMPPCodec::new();
|
|
|
|
let mut b = BytesMut::with_capacity(1024);
|
|
|
|
b.put(r"<?xml version='1.0'?><stream:stream xmlns:stream='http://etherx.jabber.org/streams' version='1.0' xmlns='jabber:client'>");
|
|
|
|
let r = c.decode(&mut b);
|
|
|
|
assert!(match r {
|
|
|
|
Ok(Some(Packet::StreamStart(_))) => true,
|
|
|
|
_ => false,
|
|
|
|
});
|
|
|
|
|
|
|
|
b.clear();
|
|
|
|
b.put(r"<test>ß</test");
|
|
|
|
let r = c.decode(&mut b);
|
|
|
|
assert!(match r {
|
|
|
|
Ok(None) => true,
|
|
|
|
_ => false,
|
|
|
|
});
|
|
|
|
|
|
|
|
b.clear();
|
|
|
|
b.put(r">");
|
|
|
|
let r = c.decode(&mut b);
|
|
|
|
assert!(match r {
|
|
|
|
Ok(Some(Packet::Stanza(ref el)))
|
2017-07-17 18:53:00 +00:00
|
|
|
if el.name() == "test"
|
|
|
|
&& el.text() == "ß"
|
2017-07-13 23:58:25 +00:00
|
|
|
=> true,
|
|
|
|
_ => false,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_truncated_utf8() {
|
|
|
|
let mut c = XMPPCodec::new();
|
|
|
|
let mut b = BytesMut::with_capacity(1024);
|
|
|
|
b.put(r"<?xml version='1.0'?><stream:stream xmlns:stream='http://etherx.jabber.org/streams' version='1.0' xmlns='jabber:client'>");
|
|
|
|
let r = c.decode(&mut b);
|
|
|
|
assert!(match r {
|
|
|
|
Ok(Some(Packet::StreamStart(_))) => true,
|
|
|
|
_ => false,
|
|
|
|
});
|
|
|
|
|
|
|
|
b.clear();
|
|
|
|
b.put(&b"<test>\xc3"[..]);
|
|
|
|
let r = c.decode(&mut b);
|
|
|
|
assert!(match r {
|
|
|
|
Ok(None) => true,
|
|
|
|
_ => false,
|
|
|
|
});
|
|
|
|
|
|
|
|
b.clear();
|
|
|
|
b.put(&b"\x9f</test>"[..]);
|
|
|
|
let r = c.decode(&mut b);
|
|
|
|
assert!(match r {
|
|
|
|
Ok(Some(Packet::Stanza(ref el)))
|
2017-07-17 18:53:00 +00:00
|
|
|
if el.name() == "test"
|
|
|
|
&& el.text() == "ß"
|
2017-07-13 23:58:25 +00:00
|
|
|
=> true,
|
|
|
|
_ => false,
|
|
|
|
});
|
|
|
|
}
|
2017-07-18 20:12:00 +00:00
|
|
|
|
|
|
|
/// By default, encode() only get's a BytesMut that has 8kb space reserved.
|
|
|
|
#[test]
|
|
|
|
fn test_large_stanza() {
|
|
|
|
use std::io::Cursor;
|
|
|
|
use futures::{Future, Sink};
|
2018-07-31 21:34:04 +00:00
|
|
|
use tokio_codec::FramedWrite;
|
2017-07-18 20:12:00 +00:00
|
|
|
let framed = FramedWrite::new(Cursor::new(vec![]), XMPPCodec::new());
|
|
|
|
let mut text = "".to_owned();
|
|
|
|
for _ in 0..2usize.pow(15) {
|
|
|
|
text = text + "A";
|
|
|
|
}
|
|
|
|
let stanza = Element::builder("message")
|
|
|
|
.append(
|
|
|
|
Element::builder("body")
|
|
|
|
.append(&text)
|
|
|
|
.build()
|
|
|
|
)
|
|
|
|
.build();
|
|
|
|
let framed = framed.send(Packet::Stanza(stanza))
|
|
|
|
.wait()
|
|
|
|
.expect("send");
|
|
|
|
assert_eq!(framed.get_ref().get_ref(), &("<message><body>".to_owned() + &text + "</body></message>").as_bytes());
|
|
|
|
}
|
2017-07-13 23:58:25 +00:00
|
|
|
}
|