mirror of
https://github.com/YGGverse/titanit.git
synced 2026-03-31 17:15:30 +00:00
handle errors
This commit is contained in:
parent
08c437920c
commit
04a276116d
2 changed files with 121 additions and 77 deletions
172
src/main.rs
172
src/main.rs
|
|
@ -13,7 +13,6 @@ use std::{
|
||||||
thread,
|
thread,
|
||||||
time::{SystemTime, UNIX_EPOCH},
|
time::{SystemTime, UNIX_EPOCH},
|
||||||
};
|
};
|
||||||
use titanite::response::success::Default;
|
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
let argument = Arc::new(Argument::parse());
|
let argument = Arc::new(Argument::parse());
|
||||||
|
|
@ -50,95 +49,134 @@ fn main() -> Result<()> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle(argument: Arc<Argument>, peer: SocketAddr, mut stream: TlsStream<TcpStream>) {
|
fn handle(argument: Arc<Argument>, peer: SocketAddr, mut stream: TlsStream<TcpStream>) {
|
||||||
use titanite::{request::Titan, response::*};
|
use titanite::*;
|
||||||
println!("[{}] [info] [{peer}] New connection", now());
|
println!("[{}] [info] [{peer}] New connection", now());
|
||||||
|
|
||||||
// read header bytes
|
// read header bytes
|
||||||
let mut input = vec![0; 1024];
|
let mut input = vec![0; 1024];
|
||||||
match stream.read(&mut input) {
|
match stream.read(&mut input) {
|
||||||
Ok(0) => todo!("Canceled"),
|
Ok(0) => println!("[{}] [warning] [{peer}] Connection closed by peer", now()),
|
||||||
Ok(l) => {
|
Ok(l) => match request::Titan::from_bytes(&input[..l]) {
|
||||||
match Titan::from_bytes(&input[..l]) {
|
Ok(titan) => {
|
||||||
Ok(titan) => {
|
// init memory pool
|
||||||
// init memory pool
|
let mut data: Vec<u8> = Vec::with_capacity(titan.size);
|
||||||
let mut data: Vec<u8> = Vec::with_capacity(titan.size);
|
loop {
|
||||||
loop {
|
// read data bytes
|
||||||
// read data bytes
|
let mut input = vec![0; argument.chunk];
|
||||||
let mut input = vec![0; argument.chunk];
|
match stream.read(&mut input) {
|
||||||
match stream.read(&mut input) {
|
Ok(0) => {
|
||||||
Ok(0) => {
|
println!("[{}] [warning] [{peer}] Connection closed by peer", now())
|
||||||
todo!("Canceled")
|
}
|
||||||
}
|
Ok(l) => {
|
||||||
Ok(l) => {
|
data.extend(&input[..l]);
|
||||||
data.extend(&input[..l]);
|
|
||||||
|
|
||||||
let total = data.len();
|
// calculate once
|
||||||
if argument.size.is_some_and(|limit| total > limit) {
|
let total = data.len();
|
||||||
todo!("Allowed max length limit reached")
|
|
||||||
}
|
// validate server-side limits
|
||||||
if titan.size >= total {
|
if argument.size.is_some_and(|limit| total > limit) {
|
||||||
if titan.size > total {
|
const MESSAGE: &str = "Allowed max length limit reached";
|
||||||
println!(
|
return send(
|
||||||
"[{}] [warning] [{peer}] Data size mismatch header declaration",
|
&response::failure::permanent::BadRequest {
|
||||||
now()
|
message: Some(MESSAGE.to_string()),
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// success
|
.into_bytes(),
|
||||||
match storage::Item::create(&argument.directory) {
|
stream,
|
||||||
Ok(mut tmp) => match tmp.file.write(titan.data) {
|
|result| match result {
|
||||||
Ok(_) => match &stream.write_all(
|
Ok(()) => {
|
||||||
&Success::Default(Default {
|
println!("[{}] [warning] [{peer}] {MESSAGE}", now())
|
||||||
mime: "text/gemini".to_string(),
|
}
|
||||||
})
|
Err(e) => println!("[{}] [error] [{peer}] {e}", now()),
|
||||||
.into_bytes(),
|
},
|
||||||
) {
|
);
|
||||||
// @TODO detect/validate/cache mime based on data received
|
}
|
||||||
Ok(()) => match tmp.commit() {
|
|
||||||
Ok(path) => match stream.flush() {
|
// all expected data received
|
||||||
// Close connection gracefully
|
if titan.size >= total {
|
||||||
// https://geminiprotocol.net/docs/protocol-specification.gmi#closing-connections
|
// validate client-side limits
|
||||||
Ok(()) => match stream.shutdown() {
|
if titan.size > total {
|
||||||
Ok(()) => println!(
|
const MESSAGE: &str = "Data size mismatch header declaration";
|
||||||
"[{}] [info] [{peer}] Data saved to {path}",
|
return send(
|
||||||
now()
|
&response::failure::permanent::BadRequest {
|
||||||
),
|
message: Some(MESSAGE.to_string()),
|
||||||
Err(e) => println!("[{}] [warning] [{peer}] {e}", now())
|
}
|
||||||
},
|
.into_bytes(),
|
||||||
Err(e) => println!("[{}] [warning] [{peer}] {e}", now())
|
stream,
|
||||||
}
|
|result| match result {
|
||||||
Err(e) => println!("[{}] [warning] [{peer}] {e}", now())
|
Ok(()) => {
|
||||||
},
|
println!("[{}] [warning] [{peer}] {MESSAGE}", now())
|
||||||
Err(e) => {
|
}
|
||||||
println!("[{}] [error] [{peer}] {e}", now());
|
Err(e) => println!("[{}] [error] [{peer}] {e}", now()),
|
||||||
if let Err(e) = tmp.delete() {
|
},
|
||||||
println!("[{}] [error] [{peer}] {e}", now())
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @TODO detect/validate/cache mime based on data received
|
||||||
|
|
||||||
|
// success
|
||||||
|
match storage::Item::create(&argument.directory) {
|
||||||
|
Ok(mut tmp) => match tmp.file.write(titan.data) {
|
||||||
|
Ok(_) => match tmp.commit() {
|
||||||
|
Ok(pmt) => send(
|
||||||
|
&response::redirect::Permanent {
|
||||||
|
target: pmt.path.to_str().unwrap().to_owned(),
|
||||||
}
|
}
|
||||||
},
|
.into_bytes(),
|
||||||
Err(e) => {
|
stream,
|
||||||
|
|result| match result {
|
||||||
|
Ok(()) => println!(
|
||||||
|
"[{}] [info] [{peer}] Data saved to {}",
|
||||||
|
now(),
|
||||||
|
pmt.path.to_string_lossy()
|
||||||
|
),
|
||||||
|
Err(e) => {
|
||||||
|
println!(
|
||||||
|
"[{}] [warning] [{peer}] {e}",
|
||||||
|
now()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Err((tmp, e)) => {
|
||||||
println!("[{}] [error] [{peer}] {e}", now());
|
println!("[{}] [error] [{peer}] {e}", now());
|
||||||
if let Err(e) = tmp.delete() {
|
if let Err(e) = tmp.delete() {
|
||||||
println!("[{}] [error] [{peer}] {e}", now())
|
println!("[{}] [error] [{peer}] {e}", now());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Err(e) => println!("[{}] [error] [{peer}] {e}", now()),
|
Err(e) => {
|
||||||
}
|
println!("[{}] [error] [{peer}] {e}", now());
|
||||||
break;
|
if let Err(e) = tmp.delete() {
|
||||||
|
println!("[{}] [error] [{peer}] {e}", now());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => println!("[{}] [error] [{peer}] {e}", now()),
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
Err(e) => todo!("{e}"),
|
|
||||||
}
|
}
|
||||||
|
Err(e) => todo!("{e}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(e) => todo!("{e}"),
|
|
||||||
}
|
}
|
||||||
}
|
Err(e) => todo!("{e}"),
|
||||||
|
},
|
||||||
Err(e) => todo!("{e}"),
|
Err(e) => todo!("{e}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn send(data: &[u8], mut stream: TlsStream<TcpStream>, callback: impl FnOnce(Result<()>)) {
|
||||||
|
callback((|| {
|
||||||
|
stream.write_all(data)?;
|
||||||
|
stream.flush()?;
|
||||||
|
// Close connection gracefully
|
||||||
|
// https://geminiprotocol.net/docs/protocol-specification.gmi#closing-connections
|
||||||
|
stream.shutdown()?;
|
||||||
|
Ok(())
|
||||||
|
})());
|
||||||
|
}
|
||||||
|
|
||||||
fn now() -> u128 {
|
fn now() -> u128 {
|
||||||
SystemTime::now()
|
SystemTime::now()
|
||||||
.duration_since(UNIX_EPOCH)
|
.duration_since(UNIX_EPOCH)
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{anyhow, Error, Result};
|
||||||
use std::{
|
use std::{
|
||||||
fs::{create_dir_all, remove_file, rename, File},
|
fs::{create_dir_all, remove_file, rename, File},
|
||||||
path::{PathBuf, MAIN_SEPARATOR},
|
path::{PathBuf, MAIN_SEPARATOR},
|
||||||
|
str::FromStr,
|
||||||
thread,
|
thread,
|
||||||
time::{Duration, SystemTime, UNIX_EPOCH},
|
time::{Duration, SystemTime, UNIX_EPOCH},
|
||||||
};
|
};
|
||||||
|
|
@ -62,21 +63,26 @@ impl Item {
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
|
|
||||||
/// Take object processed, commit its changes
|
/// Commit changes, return permanent Self
|
||||||
pub fn commit(self) -> Result<String> {
|
pub fn commit(mut self) -> Result<Self, (Self, Error)> {
|
||||||
match self.path.to_str() {
|
match self.path.to_str() {
|
||||||
Some(old) => match old.strip_suffix(TMP_SUFFIX) {
|
Some(old) => match old.strip_suffix(TMP_SUFFIX) {
|
||||||
Some(new) => {
|
Some(new) => match rename(old, new) {
|
||||||
rename(old, new)?;
|
Ok(()) => {
|
||||||
Ok(new.to_string())
|
self.path = match PathBuf::from_str(new) {
|
||||||
}
|
Ok(path) => path,
|
||||||
None => bail!("Invalid TMP suffix"), // | panic
|
Err(e) => return Err((self, anyhow!(e))),
|
||||||
|
};
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
Err(e) => Err((self, anyhow!(e))),
|
||||||
|
},
|
||||||
|
None => Err((self, anyhow!("Unexpected suffix"))),
|
||||||
},
|
},
|
||||||
None => bail!("Invalid Item path"), // | panic
|
None => Err((self, anyhow!("Unexpected file path"))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cleanup object relationships
|
|
||||||
pub fn delete(self) -> Result<()> {
|
pub fn delete(self) -> Result<()> {
|
||||||
Ok(remove_file(self.path)?)
|
Ok(remove_file(self.path)?)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue