restructre server network files
This commit is contained in:
@ -0,0 +1,390 @@
|
||||
use super::*;
|
||||
|
||||
use std::io::*;
|
||||
use std::net::*;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub struct Connection<R: Read + Sized, W: Write + Sized> {
|
||||
reader: R,
|
||||
writer: W,
|
||||
}
|
||||
|
||||
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<BufReader<TcpStream>, TcpStream> {
|
||||
pub fn from_tcp(connection: TcpStream) -> Result<Self> {
|
||||
let reader = BufReader::new(connection.try_clone()?);
|
||||
Ok(Connection {
|
||||
reader,
|
||||
writer: connection,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Read + Sized, W: Write + Sized> Connection<R, W> {
|
||||
#[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<Package> {
|
||||
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::<Option<bool>>(&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::<Texture>(&payload[..]) {
|
||||
Ok(texture) => {
|
||||
return Ok(Package::Json(JsonValue::Texture(texture)));
|
||||
}
|
||||
_ => (), // else try other
|
||||
}
|
||||
|
||||
// try texture vec
|
||||
match serde_json::from_slice::<Vec<Texture>>(&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::<bool>::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<J: 'static + Serialize>(&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,
|
||||
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]));
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user