restructre server network files
This commit is contained in:
parent
efdf28a52a
commit
8cbee2fcc5
@ -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::io::*;
|
||||||
use std::net::*;
|
use std::net::*;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub struct Connection<R: Read + Sized, W: Write + Sized> {
|
pub struct Connection<R: Read + Sized, W: Write + Sized> {
|
||||||
reader: R,
|
reader: R,
|
||||||
writer: W,
|
writer: W,
|
@ -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(())
|
||||||
|
}
|
@ -1,144 +1,10 @@
|
|||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use std::io::*;
|
mod connection;
|
||||||
use std::net::*;
|
use self::connection::*;
|
||||||
|
|
||||||
use std::thread;
|
mod listen_forever;
|
||||||
use std::time::Duration;
|
pub use self::listen_forever::*;
|
||||||
|
|
||||||
use crate::model::*;
|
mod package;
|
||||||
|
use self::package::*;
|
||||||
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(())
|
|
||||||
}
|
|
||||||
|
@ -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),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +1,8 @@
|
|||||||
// TODO: remove on implementation
|
mod error;
|
||||||
#![allow(unused_imports)]
|
pub use self::error::*;
|
||||||
#![allow(unused_variables)]
|
|
||||||
#![allow(dead_code)]
|
|
||||||
|
|
||||||
mod results;
|
|
||||||
pub use self::results::*;
|
|
||||||
|
|
||||||
mod implementation;
|
mod implementation;
|
||||||
pub use self::implementation::listen_forever;
|
pub use self::implementation::*;
|
||||||
|
|
||||||
use crate::model::*;
|
use crate::model::*;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user