TextureSync/server/texture-sync-server/src/protocol/implementation/listen_forever.rs

149 lines
5.5 KiB
Rust

use std::net::*;
use std::thread;
use std::time::Duration;
use super::*;
pub fn listen_forever<H>(handler: H, config: &ProtocolConfig) -> io::Result<()>
where
H: 'static + ProtocolHandler + Sized,
{
let listener = TcpListener::bind((config.listen_addr.as_str(), config.port))?;
for mut connection in listener.incoming() {
// If there is an successful connection,
// set timeouts.
// We ignore errors here, so they will be caught in the clients thread.
let _ = connection.as_mut().map(|stream| {
stream.set_read_timeout(Duration::from_secs(config.read_timeout_s).into())?;
stream.set_write_timeout(Duration::from_secs(config.read_timeout_s).into())
});
let handler = handler.clone();
let _ = thread::spawn(move || client_loop(connection?, handler));
}
Ok(())
}
fn client_loop<H>(connection: TcpStream, mut handler: H) -> io::Result<()>
where
H: 'static + ProtocolHandler + Sized,
{
let mut connection = Connection::from_tcp(connection)?;
handler.new_connection(&connection);
'outer: loop {
let package = connection.receive()?;
match package {
Package::Error(_, _) => {
// Just close the connection.
break;
}
Package::Json(_) | Package::Binary(_) => {
connection.send(&Package::Error(400, "Expected Command.".to_string()))?;
break;
}
Package::Command(Command::Ping {}) => {
connection.send(&Package::Command(Command::Pong {}))?;
}
Package::Command(Command::Pong {}) => {
// Ignore
}
Package::Command(Command::Query { query }) => {
connection.send(&Package::from(handler.query(&connection, &query[..])))?;
}
Package::Command(Command::GetTexture { id, name }) => match (id, name) {
(Some(id), None) => {
connection.send(&Package::from(handler.get_texture_by_id(&connection, &id)))?;
}
(None, Some(name)) => {
connection.send(&Package::from(
handler.get_texture_by_name(&connection, &name),
))?;
}
_ => {
connection.send(&Package::from(ProtocolError::BadRequest(
"Either 'id' or 'name' must be set!".to_string(),
)))?;
}
},
Package::Command(Command::GetTextureData { texture_hash }) => {
connection.send(&Package::from(
handler.get_texture_file(&connection, texture_hash),
))?;
}
Package::Command(Command::GetTexturePreview {
texture_hash,
desired_format,
}) => {
connection.send(&Package::from(handler.get_texture_preview(
&connection,
texture_hash,
desired_format,
)))?;
}
// TODO: use less nesting.
Package::Command(Command::ReplaceTexture { old, new }) => {
match handler.replace_texture(&connection, old.clone(), new.clone(), None) {
Ok(ReplaceTextureStatus::Ok) => {
connection.send(&Package::Json(JsonValue::True))?;
}
Ok(ReplaceTextureStatus::NeedTextureData(hash)) => {
connection.send(&Package::Command(Command::GetTextureData {
texture_hash: hash,
}))?;
let pkg = connection.receive()?;
match pkg {
Package::Binary(data) => {
match handler.replace_texture(
&connection,
old.clone(),
new.clone(),
Some(data),
) {
Ok(ReplaceTextureStatus::Ok) => {
connection.send(&Package::Json(JsonValue::True))?;
}
Ok(ReplaceTextureStatus::NeedTextureData(_hash)) => {
panic!("Contract Violation: handler must not return NeedTextureData \
when data is given.");
}
Err(err) => {
connection.send(&Package::from(err))?;
}
}
}
Package::Error(_, _) => {
// Just close the connection.
break 'outer;
}
_ => {
connection.send(&Package::from(ProtocolError::BadRequest(
"Expected Texture Data!".to_string(),
)))?;
}
}
}
Err(err) => {
connection.send(&Package::from(err))?;
}
}
}
}
}
Ok(())
}