use std::io; use proto::{DecodeError, MutPacket, Packet, PacketReadError, ProtoDecode, ProtoDecoder, ProtoEncode, ProtoEncoder, ReadFromPacket, WriteToPacket}; use proto::peer::constants::*; /*=========* * MESSAGE * *=========*/ /// This enum contains all the possible messages peers can exchange. #[derive(Clone, Debug, Eq, PartialEq)] pub enum Message { PierceFirewall(u32), PeerInit(PeerInit), Unknown(u32), } impl ReadFromPacket for Message { fn read_from_packet(packet: &mut Packet) -> Result { let code: u32 = try!(packet.read_value()); let message = match code { CODE_PIERCE_FIREWALL => Message::PierceFirewall(try!(packet.read_value())), CODE_PEER_INIT => Message::PeerInit(try!(packet.read_value())), code => Message::Unknown(code), }; let bytes_remaining = packet.bytes_remaining(); if bytes_remaining > 0 { warn!( "Peer message with code {} contains {} extra bytes", code, bytes_remaining ) } Ok(message) } } impl ProtoDecode for Message { fn decode(decoder: &mut ProtoDecoder) -> Result { let code = decoder.decode_u32()?; let message = match code { CODE_PIERCE_FIREWALL => { let val = decoder.decode_u32()?; Message::PierceFirewall(val) } CODE_PEER_INIT => { let peer_init = PeerInit::decode(decoder)?; Message::PeerInit(peer_init) } _ => { return Err(DecodeError::UnknownCodeError(code)); } }; Ok(message) } } impl ProtoEncode for Message { fn encode(&self, encoder: &mut ProtoEncoder) -> io::Result<()> { match *self { Message::PierceFirewall(token) => { encoder.encode_u32(CODE_PIERCE_FIREWALL)?; encoder.encode_u32(token)?; } Message::PeerInit(ref request) => { encoder.encode_u32(CODE_PEER_INIT)?; request.encode(encoder)?; } Message::Unknown(_) => unreachable!(), } Ok(()) } } impl WriteToPacket for Message { fn write_to_packet(&self, packet: &mut MutPacket) -> io::Result<()> { match *self { Message::PierceFirewall(ref token) => { try!(packet.write_value(&CODE_PIERCE_FIREWALL)); try!(packet.write_value(token)); } Message::PeerInit(ref request) => { try!(packet.write_value(&CODE_PEER_INIT)); try!(packet.write_value(request)); } Message::Unknown(_) => unreachable!(), } Ok(()) } } #[derive(Clone, Debug, Eq, PartialEq)] pub struct PeerInit { pub user_name: String, pub connection_type: String, pub token: u32, } impl ReadFromPacket for PeerInit { fn read_from_packet(packet: &mut Packet) -> Result { let user_name = try!(packet.read_value()); let connection_type = try!(packet.read_value()); let token = try!(packet.read_value()); Ok(PeerInit { user_name, connection_type, token, }) } } impl WriteToPacket for PeerInit { fn write_to_packet(&self, packet: &mut MutPacket) -> io::Result<()> { try!(packet.write_value(&self.user_name)); try!(packet.write_value(&self.connection_type)); try!(packet.write_value(&self.token)); Ok(()) } } impl ProtoEncode for PeerInit { fn encode(&self, encoder: &mut ProtoEncoder) -> io::Result<()> { encoder.encode_string(&self.user_name)?; encoder.encode_string(&self.connection_type)?; encoder.encode_u32(self.token)?; Ok(()) } } impl ProtoDecode for PeerInit { fn decode(decoder: &mut ProtoDecoder) -> Result { let user_name = decoder.decode_string()?; let connection_type = decoder.decode_string()?; let token = decoder.decode_u32()?; Ok(PeerInit { user_name, connection_type, token, }) } } #[cfg(test)] mod tests { use std::fmt::Debug; use std::io; use bytes::BytesMut; use proto::{DecodeError, ProtoDecode, ProtoDecoder, ProtoEncode, ProtoEncoder}; use proto::codec::tests::roundtrip; use super::*; #[test] fn invalid_code() { let mut bytes = BytesMut::new(); ProtoEncoder::new(&mut bytes).encode_u32(1337).unwrap(); let mut cursor = io::Cursor::new(bytes); match Message::decode(&mut ProtoDecoder::new(&mut cursor)) { Err(DecodeError::UnknownCodeError(1337)) => {} result => panic!(result), } } #[test] fn roundtrip_pierce_firewall() { roundtrip(Message::PierceFirewall(1337)) } #[test] fn roundtrip_peer_init() { roundtrip(Message::PeerInit(PeerInit { user_name: "alice".to_string(), connection_type: "P".to_string(), token: 1337, })); } }