ibb.rs

  1// Copyright (c) 2017 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
  2//
  3// This Source Code Form is subject to the terms of the Mozilla Public
  4// License, v. 2.0. If a copy of the MPL was not distributed with this
  5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
  6
  7use crate::iq::IqSetPayload;
  8use crate::util::helpers::Base64;
  9
 10generate_id!(
 11    /// An identifier matching a stream.
 12    StreamId
 13);
 14
 15generate_attribute!(
 16/// Which stanza type to use to exchange data.
 17Stanza, "stanza", {
 18    /// `<iq/>` gives a feedback on whether the chunk has been received or not,
 19    /// which is useful in the case the recipient might not receive them in a
 20    /// timely manner, or to do your own throttling based on the results.
 21    Iq => "iq",
 22
 23    /// `<message/>` can be faster, since it doesn’t require any feedback, but in
 24    /// practice it will be throttled by the servers on the way.
 25    Message => "message",
 26}, Default = Iq);
 27
 28generate_element!(
 29/// Starts an In-Band Bytestream session with the given parameters.
 30Open, "open", IBB,
 31attributes: [
 32    /// Maximum size in bytes for each chunk.
 33    block_size: Required<u16> = "block-size",
 34
 35    /// The identifier to be used to create a stream.
 36    sid: Required<StreamId> = "sid",
 37
 38    /// Which stanza type to use to exchange data.
 39    stanza: Default<Stanza> = "stanza",
 40]);
 41
 42impl IqSetPayload for Open {}
 43
 44generate_element!(
 45/// Exchange a chunk of data in an open stream.
 46Data, "data", IBB,
 47    attributes: [
 48        /// Sequence number of this chunk, must wraparound after 65535.
 49        seq: Required<u16> = "seq",
 50
 51        /// The identifier of the stream on which data is being exchanged.
 52        sid: Required<StreamId> = "sid"
 53    ],
 54    text: (
 55        /// Vector of bytes to be exchanged.
 56        data: Base64<Vec<u8>>
 57    )
 58);
 59
 60impl IqSetPayload for Data {}
 61
 62generate_element!(
 63/// Close an open stream.
 64Close, "close", IBB,
 65attributes: [
 66    /// The identifier of the stream to be closed.
 67    sid: Required<StreamId> = "sid",
 68]);
 69
 70impl IqSetPayload for Close {}
 71
 72#[cfg(test)]
 73mod tests {
 74    use super::*;
 75    use crate::util::error::Error;
 76    use crate::Element;
 77    use std::convert::TryFrom;
 78
 79    #[cfg(target_pointer_width = "32")]
 80    #[test]
 81    fn test_size() {
 82        assert_size!(StreamId, 12);
 83        assert_size!(Stanza, 1);
 84        assert_size!(Open, 16);
 85        assert_size!(Data, 28);
 86        assert_size!(Close, 12);
 87    }
 88
 89    #[cfg(target_pointer_width = "64")]
 90    #[test]
 91    fn test_size() {
 92        assert_size!(StreamId, 24);
 93        assert_size!(Stanza, 1);
 94        assert_size!(Open, 32);
 95        assert_size!(Data, 56);
 96        assert_size!(Close, 24);
 97    }
 98
 99    #[test]
100    fn test_simple() {
101        let sid = StreamId(String::from("coucou"));
102
103        let elem: Element =
104            "<open xmlns='http://jabber.org/protocol/ibb' block-size='3' sid='coucou'/>"
105                .parse()
106                .unwrap();
107        let open = Open::try_from(elem).unwrap();
108        assert_eq!(open.block_size, 3);
109        assert_eq!(open.sid, sid);
110        assert_eq!(open.stanza, Stanza::Iq);
111
112        let elem: Element =
113            "<data xmlns='http://jabber.org/protocol/ibb' seq='0' sid='coucou'>AAAA</data>"
114                .parse()
115                .unwrap();
116        let data = Data::try_from(elem).unwrap();
117        assert_eq!(data.seq, 0);
118        assert_eq!(data.sid, sid);
119        assert_eq!(data.data, vec!(0, 0, 0));
120
121        let elem: Element = "<close xmlns='http://jabber.org/protocol/ibb' sid='coucou'/>"
122            .parse()
123            .unwrap();
124        let close = Close::try_from(elem).unwrap();
125        assert_eq!(close.sid, sid);
126    }
127
128    #[test]
129    fn test_invalid() {
130        let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb'/>"
131            .parse()
132            .unwrap();
133        let error = Open::try_from(elem).unwrap_err();
134        let message = match error {
135            Error::ParseError(string) => string,
136            _ => panic!(),
137        };
138        assert_eq!(message, "Required attribute 'block-size' missing.");
139
140        let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb' block-size='-5'/>"
141            .parse()
142            .unwrap();
143        let error = Open::try_from(elem).unwrap_err();
144        let message = match error {
145            Error::ParseIntError(error) => error,
146            _ => panic!(),
147        };
148        assert_eq!(message.to_string(), "invalid digit found in string");
149
150        let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb' block-size='128'/>"
151            .parse()
152            .unwrap();
153        let error = Open::try_from(elem).unwrap_err();
154        let message = match error {
155            Error::ParseError(error) => error,
156            _ => panic!(),
157        };
158        assert_eq!(message, "Required attribute 'sid' missing.");
159    }
160
161    #[test]
162    fn test_invalid_stanza() {
163        let elem: Element = "<open xmlns='http://jabber.org/protocol/ibb' block-size='128' sid='coucou' stanza='fdsq'/>".parse().unwrap();
164        let error = Open::try_from(elem).unwrap_err();
165        let message = match error {
166            Error::ParseError(string) => string,
167            _ => panic!(),
168        };
169        assert_eq!(message, "Unknown value for 'stanza' attribute.");
170    }
171}