use super::*; use std::fmt; use std::io::*; use std::net::*; use serde::Serialize; pub struct Connection { reader: R, writer: W, } impl fmt::Display for Connection { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "") // Todo use some form of marker?? } } impl ClientConnection for Connection {} const KIB: u32 = 1024; const MIB: u32 = 1024 * 1024; const PACKAGE_TYPE_ERROR: u8 = 0; const PACKAGE_TYPE_JSON: u8 = 1; const PACKAGE_TYPE_BIN: u8 = 2; impl Connection, TcpStream> { pub fn from_tcp(connection: TcpStream) -> Result { let reader = BufReader::new(connection.try_clone()?); Ok(Connection { reader, writer: connection, }) } } impl Connection { #[cfg(test)] pub fn new(reader: R, writer: W) -> Self { Connection { reader, writer } } #[cfg(test)] pub fn destruct(self) -> (R, W) { let Connection { reader, writer } = self; (reader, writer) } pub fn receive(&mut self) -> Result { let mut payload_type_buffer = [0u8; 1]; let mut reserved_buffer = [0u8; 3]; let mut payload_length_buffer = [0u8; 4]; self.reader.read_exact(&mut payload_type_buffer)?; self.reader.read_exact(&mut reserved_buffer)?; self.reader.read_exact(&mut payload_length_buffer)?; let payload_type = payload_type_buffer[0]; let payload_length = u32::from_be_bytes(payload_length_buffer); // Check length. match payload_type { PACKAGE_TYPE_ERROR => { if payload_length > 1 * KIB { return Err(Error::new( ErrorKind::InvalidData, "Maximum length of Error Package is 1 KiB.", )); } } PACKAGE_TYPE_JSON => { if payload_length > 16 * MIB { return Err(Error::new( ErrorKind::InvalidData, "Maximum length of JSON Package is 16 MiB.", )); } } PACKAGE_TYPE_BIN => { if payload_length > 512 * MIB { return Err(Error::new( ErrorKind::InvalidData, "Maximum length of Binary Package is 512 MiB.", )); } } _ => { return Err(Error::new(ErrorKind::InvalidData, "Unknown Package Type.")); } } let mut payload = vec![0u8; payload_length as usize]; self.reader.read_exact(&mut payload[..])?; match payload_type { PACKAGE_TYPE_ERROR => { let contents = String::from_utf8(payload) .map_err(|_| Error::new(ErrorKind::InvalidData, "Invalid UTF-8."))?; let mut parts = contents.splitn(2, " "); match (parts.next(), parts.next()) { (Some(code), Some(info)) => { let code: u16 = code.parse().map_err(|_| { Error::new(ErrorKind::InvalidData, "Status code in error expected!") })?; Ok(Package::Error(code, info.to_string())) } _ => Err(Error::new( ErrorKind::InvalidData, "Status code in error expected!", )), } } PACKAGE_TYPE_JSON => { // try special packages first. match serde_json::from_slice::>(&payload[..]) { Ok(Some(true)) => { return Ok(Package::Json(JsonValue::True)); } Ok(Some(false)) => { return Ok(Package::Json(JsonValue::False)); } Ok(None) => { return Ok(Package::Json(JsonValue::Null)); } _ => (), // else try other } // try single texture match serde_json::from_slice::(&payload[..]) { Ok(texture) => { return Ok(Package::Json(JsonValue::Texture(texture))); } _ => (), // else try other } // try texture vec match serde_json::from_slice::>(&payload[..]) { Ok(textures) => { return Ok(Package::Json(JsonValue::TextureArray(textures))); } _ => (), // else try other } let json: Command = serde_json::from_slice(&payload[..]).map_err(|_e| { #[cfg(test)] dbg!(&_e); Error::new(ErrorKind::InvalidData, "Invalid JSON.") })?; Ok(Package::Command(json)) } PACKAGE_TYPE_BIN => Ok(Package::Binary(payload)), _ => { // Covered in the match above. unreachable!(); } } } pub fn send(&mut self, pkg: &Package) -> Result<()> { match pkg { Package::Json(JsonValue::Null) => self.send_json(&Option::::None), Package::Json(JsonValue::True) => self.send_json(&true), Package::Json(JsonValue::False) => self.send_json(&false), Package::Json(JsonValue::Texture(texture)) => self.send_json(texture), Package::Json(JsonValue::TextureArray(textures)) => self.send_json(textures), Package::Command(cmd) => self.send_json(cmd), Package::Binary(bin) => self.send_binary(bin), Package::Error(code, msg) => self.send_error(*code, msg), } } fn send_json(&mut self, msg: &J) -> Result<()> { let w = &mut self.writer; let payload = serde_json::to_vec(msg).unwrap(); w.write_all(&[PACKAGE_TYPE_JSON, 42, 42, 42])?; w.write_all(&(payload.len() as u32).to_be_bytes())?; w.write_all(&payload[..])?; w.flush() } fn send_binary(&mut self, payload: &[u8]) -> Result<()> { let w = &mut self.writer; w.write_all(&[PACKAGE_TYPE_BIN, 42, 42, 42])?; w.write_all(&(payload.len() as u32).to_be_bytes())?; w.write_all(payload)?; w.flush() } fn send_error(&mut self, code: u16, msg: &str) -> Result<()> { let w = &mut self.writer; let payload = format!("{:03} {}", code, msg).into_bytes(); w.write_all(&[PACKAGE_TYPE_ERROR, 42, 42, 42])?; w.write_all(&(payload.len() as u32).to_be_bytes())?; w.write_all(&payload[..])?; w.flush() } } #[cfg(test)] mod test { use super::*; use crate::model::*; // Utility fn fn demo_texture() -> Texture { const HASH_BYTES: [u8; 32] = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, ]; Texture { id: "d32e1f80-a17a-4dd7-8ed7-c3a2de1de1c9".to_string(), name: "texture.png".to_string(), tags: vec!["Wood".to_string(), "Hair".to_string()], format: TextureFormat::PNG, added_on: Date::new(2019, 10, 12), resolution: (512, 512), texture_hash: Sha256(HASH_BYTES), } } fn test_read_back(pkg: &Package) { let dummy_read_data = [255u8]; // generate write let mut c = Connection::new(&dummy_read_data[..], Vec::new()); c.send(&pkg).unwrap(); let (_, written) = c.destruct(); // read back in let mut c = Connection::new(&written[..], Vec::new() /*dummy*/); let read_back_pkg = c.receive().unwrap(); assert_eq!(&read_back_pkg, pkg) } #[test] fn read_error_good() { let mut read_data = Vec::new(); let msg = b"42 TEST"; read_data.extend_from_slice(&[0, 42, 42, 42, 0, 0, 0, msg.len() as u8]); read_data.extend_from_slice(msg); let mut c = Connection::new(&read_data[..], Vec::new()); assert_eq!(c.receive().unwrap(), Package::Error(42, "TEST".to_string())) } #[test] fn read_error_bad() { let mut read_data = Vec::new(); let msg = b"TEST XXXX"; read_data.extend_from_slice(&[0, 42, 42, 42, 0, 0, 0, msg.len() as u8]); read_data.extend_from_slice(msg); let mut c = Connection::new(&read_data[..], Vec::new()); assert_eq!(c.receive().is_err(), true) } #[test] fn read_binary_good() { let mut read_data = Vec::new(); let msg = b"Hello World"; read_data.extend_from_slice(&[2, 42, 42, 42, 0, 0, 0, msg.len() as u8]); read_data.extend_from_slice(msg); let mut c = Connection::new(&read_data[..], Vec::new()); assert_eq!(c.receive().unwrap(), Package::Binary(Vec::from(&msg[..]))); } #[test] fn read_binary_bad() { let mut read_data = Vec::new(); let msg = b"Hello World"; // to large (size). read_data.extend_from_slice(&[2, 42, 42, 42, 255, 0, 0, 0]); read_data.extend_from_slice(msg); let mut c = Connection::new(&read_data[..], Vec::new()); assert_eq!(c.receive().is_err(), true); } // we don't test the actual json parsing here, // since serde_json is well tested. #[test] fn read_json_good() { let mut read_data = Vec::new(); let msg = br#"{ "query" : { "query" : ["Hallo", "Welt!"] } }"#; read_data.extend_from_slice(&[1, 42, 42, 42, 0, 0, 0, msg.len() as u8]); read_data.extend_from_slice(msg); let mut c = Connection::new(&read_data[..], Vec::new()); assert_eq!( c.receive().unwrap(), Package::Command(Command::Query { query: vec!["Hallo".to_string(), "Welt!".to_string()], }) ); } #[test] fn read_json_multiple_special() { let mut read_data = Vec::new(); let msg = br#"null"#; read_data.extend_from_slice(&[1, 42, 42, 42, 0, 0, 0, msg.len() as u8]); read_data.extend_from_slice(msg); let msg = br#"true"#; read_data.extend_from_slice(&[1, 42, 42, 42, 0, 0, 0, msg.len() as u8]); read_data.extend_from_slice(msg); let msg = br#"false"#; read_data.extend_from_slice(&[1, 42, 42, 42, 0, 0, 0, msg.len() as u8]); read_data.extend_from_slice(msg); let mut c = Connection::new(&read_data[..], Vec::new()); assert_eq!(c.receive().unwrap(), Package::Json(JsonValue::Null)); assert_eq!(c.receive().unwrap(), Package::Json(JsonValue::True)); assert_eq!(c.receive().unwrap(), Package::Json(JsonValue::False)); } #[test] fn read_json_bad() { let mut read_data = Vec::new(); let msg = br#"{ "big foot" : { "query" : ["Hallo", "Welt!"] } }"#; read_data.extend_from_slice(&[1, 42, 42, 42, 0, 0, 0, msg.len() as u8]); read_data.extend_from_slice(msg); let mut c = Connection::new(&read_data[..], Vec::new()); assert_eq!(c.receive().is_err(), true); } #[test] fn writes() { test_read_back(&Package::Json(JsonValue::Null)); test_read_back(&Package::Json(JsonValue::True)); test_read_back(&Package::Json(JsonValue::False)); test_read_back(&Package::Json(JsonValue::Texture(demo_texture()))); test_read_back(&Package::Json(JsonValue::TextureArray(vec![ demo_texture(), demo_texture(), ]))); test_read_back(&Package::Command(Command::Pong {})); test_read_back(&Package::Error(500, "I bims 1 Error".to_string())); test_read_back(&Package::Binary(vec![42u8; 50000])); } #[test] fn test_correct_serialization() { let mut buffer = Vec::new(); serde_json::to_writer(&mut buffer, &demo_texture()).unwrap(); let json_value: serde_json::value::Value = serde_json::from_reader(&buffer[..]).unwrap(); let expected_value = serde_json::json!( { "id": "d32e1f80-a17a-4dd7-8ed7-c3a2de1de1c9", "name": "texture.png", "tags": ["Wood", "Hair"], "format": "png", "resolution": [512, 512], "added_on": [2019, 10, 12], "texture_hash": "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F" } ); assert_eq!(json_value, expected_value); } }