restructre server network files

This commit is contained in:
CodeSteak 2019-04-25 23:37:16 +02:00
parent efdf28a52a
commit 8cbee2fcc5
6 changed files with 245 additions and 247 deletions

View File

@ -1,106 +1,10 @@
use crate::model::*;
use super::*;
use serde::{Deserialize, Serialize};
// TODO: put conversion and these enums in own file.
#[derive(Eq, PartialEq, Clone, Debug)]
pub enum Package {
Json(JsonValue),
Command(Command),
Binary(Vec<u8>),
Error(u16, String),
}
#[derive(Eq, PartialEq, Clone, Debug)]
pub enum JsonValue {
Null,
True,
False,
Texture(Texture),
TextureArray(Vec<Texture>),
}
#[derive(Serialize, Deserialize, Eq, PartialEq, Clone, Debug)]
pub enum Command {
#[serde(rename = "ping")]
Ping {},
#[serde(rename = "pong")]
Pong {},
#[serde(rename = "query")]
Query { query: Vec<String> },
#[serde(rename = "get_texture")]
GetTexture {
id: Option<String>,
name: Option<String>,
},
#[serde(rename = "get_texture_file")]
GetTextureData { texture_hash: Sha256 },
#[serde(rename = "get_texture_preview")]
GetTexturePreview {
texture_hash: Sha256,
desired_format: TextureFormat,
},
#[serde(rename = "replace_texture")]
ReplaceTexture {
old: Option<Texture>,
new: Option<Texture>,
},
}
use super::results::*;
impl From<ProtocolError> for Package {
fn from(item: ProtocolError) -> Self {
match item {
ProtocolError::BadRequest(msg) => Package::Error(400, msg),
ProtocolError::FileNotFound(msg) => Package::Error(404, msg),
ProtocolError::Conflict(msg) => Package::Error(409, msg),
ProtocolError::InternalServerError(_err) => {
Package::Error(500, "Internal Server Error.".to_string())
}
ProtocolError::NotImplemented => Package::Error(
501,
"Well, I'm sorry, \
but this feature is still a Todo :/"
.to_string(),
),
}
}
}
impl From<ProtocolResult<Vec<Texture>>> for Package {
fn from(item: ProtocolResult<Vec<Texture>>) -> Self {
match item {
Ok(textures) => Package::Json(JsonValue::TextureArray(textures)),
Err(err) => Package::from(err),
}
}
}
impl From<ProtocolResult<Option<Texture>>> for Package {
fn from(item: ProtocolResult<Option<Texture>>) -> Self {
match item {
Ok(Some(texture)) => Package::Json(JsonValue::Texture(texture)),
Ok(None) => Package::Json(JsonValue::Null),
Err(err) => Package::from(err),
}
}
}
impl From<ProtocolResult<Vec<u8>>> for Package {
fn from(item: ProtocolResult<Vec<u8>>) -> Self {
match item {
Ok(bin) => Package::Binary(bin),
Err(err) => Package::from(err),
}
}
}
use crate::protocol::results::ProtocolResult;
use std::io::*;
use std::net::*;
use serde::{Deserialize, Serialize};
pub struct Connection<R: Read + Sized, W: Write + Sized> {
reader: R,
writer: W,

View File

@ -0,0 +1,137 @@
use std::io::*;
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)?;
'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(&query[..])))?;
}
Package::Command(Command::GetTexture { id, name }) => match (id, name) {
(Some(id), None) => {
connection.send(&Package::from(handler.get_texture_by_id(&id)))?;
}
(None, Some(name)) => {
connection.send(&Package::from(handler.get_texture_by_name(&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(texture_hash)))?;
}
Package::Command(Command::GetTexturePreview {
texture_hash,
desired_format,
}) => {
connection.send(&Package::from(
handler.get_texture_preview(texture_hash, desired_format),
))?;
}
// TODO: use less nesting.
Package::Command(Command::ReplaceTexture { old, new }) => {
match handler.replace_texture(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(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(())
}

View File

@ -1,144 +1,10 @@
use super::*;
use std::io::*;
use std::net::*;
mod connection;
use self::connection::*;
use std::thread;
use std::time::Duration;
mod listen_forever;
pub use self::listen_forever::*;
use crate::model::*;
mod protocol_connection;
use self::protocol_connection::*;
use super::results::*;
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)?;
'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(&query[..])))?;
}
Package::Command(Command::GetTexture { id, name }) => match (id, name) {
(Some(id), None) => {
connection.send(&Package::from(handler.get_texture_by_id(&id)))?;
}
(None, Some(name)) => {
connection.send(&Package::from(handler.get_texture_by_name(&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(texture_hash)))?;
}
Package::Command(Command::GetTexturePreview {
texture_hash,
desired_format,
}) => {
connection.send(&Package::from(
handler.get_texture_preview(texture_hash, desired_format),
))?;
}
// TODO: use less nesting.
Package::Command(Command::ReplaceTexture { old, new }) => {
match handler.replace_texture(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(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(())
}
mod package;
use self::package::*;

View File

@ -0,0 +1,96 @@
use crate::model::*;
use serde::{Deserialize, Serialize};
#[derive(Eq, PartialEq, Clone, Debug)]
pub enum Package {
Json(JsonValue),
Command(Command),
Binary(Vec<u8>),
Error(u16, String),
}
#[derive(Eq, PartialEq, Clone, Debug)]
pub enum JsonValue {
Null,
True,
False,
Texture(Texture),
TextureArray(Vec<Texture>),
}
#[derive(Serialize, Deserialize, Eq, PartialEq, Clone, Debug)]
pub enum Command {
#[serde(rename = "ping")]
Ping {},
#[serde(rename = "pong")]
Pong {},
#[serde(rename = "query")]
Query { query: Vec<String> },
#[serde(rename = "get_texture")]
GetTexture {
id: Option<String>,
name: Option<String>,
},
#[serde(rename = "get_texture_file")]
GetTextureData { texture_hash: Sha256 },
#[serde(rename = "get_texture_preview")]
GetTexturePreview {
texture_hash: Sha256,
desired_format: TextureFormat,
},
#[serde(rename = "replace_texture")]
ReplaceTexture {
old: Option<Texture>,
new: Option<Texture>,
},
}
use super::error::*;
impl From<ProtocolError> for Package {
fn from(item: ProtocolError) -> Self {
match item {
ProtocolError::BadRequest(msg) => Package::Error(400, msg),
ProtocolError::FileNotFound(msg) => Package::Error(404, msg),
ProtocolError::Conflict(msg) => Package::Error(409, msg),
ProtocolError::InternalServerError(_err) => {
Package::Error(500, "Internal Server Error.".to_string())
}
ProtocolError::NotImplemented => Package::Error(
501,
"Well, I'm sorry, \
but this feature is still a Todo :/"
.to_string(),
),
}
}
}
impl From<ProtocolResult<Vec<Texture>>> for Package {
fn from(item: ProtocolResult<Vec<Texture>>) -> Self {
match item {
Ok(textures) => Package::Json(JsonValue::TextureArray(textures)),
Err(err) => Package::from(err),
}
}
}
impl From<ProtocolResult<Option<Texture>>> for Package {
fn from(item: ProtocolResult<Option<Texture>>) -> Self {
match item {
Ok(Some(texture)) => Package::Json(JsonValue::Texture(texture)),
Ok(None) => Package::Json(JsonValue::Null),
Err(err) => Package::from(err),
}
}
}
impl From<ProtocolResult<Vec<u8>>> for Package {
fn from(item: ProtocolResult<Vec<u8>>) -> Self {
match item {
Ok(bin) => Package::Binary(bin),
Err(err) => Package::from(err),
}
}
}

View File

@ -1,13 +1,8 @@
// TODO: remove on implementation
#![allow(unused_imports)]
#![allow(unused_variables)]
#![allow(dead_code)]
mod results;
pub use self::results::*;
mod error;
pub use self::error::*;
mod implementation;
pub use self::implementation::listen_forever;
pub use self::implementation::*;
use crate::model::*;