From 238220c612ffc5aa357505b320f63ad68dac63e4 Mon Sep 17 00:00:00 2001 From: Pavel Kirilin <win10@list.ru> Date: Sat, 18 Dec 2021 03:10:22 +0400 Subject: [PATCH] Added getting extension, formatted code. Signed-off-by: Pavel Kirilin <win10@list.ru> --- Cargo.lock | 8 ++++---- Cargo.toml | 2 +- src/config.rs | 29 ++++++++++++++++------------- src/errors.rs | 10 ++++++---- src/protocol/core/routes.rs | 1 + src/protocol/getting/mod.rs | 21 +++++++++++++++++++++ src/protocol/getting/routes.rs | 16 ++++++++++++++++ src/protocol/mod.rs | 4 ++++ src/routes.rs | 11 +++++------ src/storages/file_storage.rs | 11 +++++++---- src/storages/mod.rs | 2 +- src/storages/sqlite_file_storage.rs | 22 +++++++++++++++++----- 12 files changed, 99 insertions(+), 38 deletions(-) create mode 100644 src/protocol/getting/mod.rs create mode 100644 src/protocol/getting/routes.rs diff --git a/Cargo.lock b/Cargo.lock index 95d8175..20ec430 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1973,18 +1973,18 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.131" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ad69dfbd3e45369132cc64e6748c2d65cdfb001a2b1c232d128b4ad60561c1" +checksum = "8b9875c23cf305cd1fd7eb77234cbb705f21ea6a72c637a5c6db5fe4b8e7f008" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.131" +version = "1.0.132" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b710a83c4e0dff6a3d511946b95274ad9ca9e5d3ae497b63fda866ac955358d2" +checksum = "ecc0db5cb2556c0e558887d9bbdcf6ac4471e83ff66cf696e5419024d1606276" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 5f908aa..d922858 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,4 +23,4 @@ serde_json = "1" log = "^0.4.14" url = "2.2.2" simple-logging = { version = "^2.0.2" } -sqlx = { version = "0.5", features = [ "runtime-async-std-native-tls", "sqlite" ] } \ No newline at end of file +sqlx = { version = "0.5", features = ["runtime-async-std-native-tls", "sqlite"] } \ No newline at end of file diff --git a/src/config.rs b/src/config.rs index 810ab5b..6d03772 100644 --- a/src/config.rs +++ b/src/config.rs @@ -16,10 +16,10 @@ pub struct StorageOptions { /// This directory is used to store files /// for all *file_storage storages. #[structopt( - long, - default_value = "./data", - required_if("storage", "file_storage"), - required_if("storage", "sqlite_file_storage") + long, + default_value = "./data", + required_if("storage", "file_storage"), + required_if("storage", "sqlite_file_storage") )] pub data: PathBuf, @@ -28,9 +28,9 @@ pub struct StorageOptions { /// This file is used to /// store information about uploaded files. #[structopt( - long, - default_value = "data/info.sqlite3", - required_if("storage", "sqlite_file_storage") + long, + default_value = "data/info.sqlite3", + required_if("storage", "sqlite_file_storage") )] pub sqlite_dsn: PathBuf, } @@ -64,9 +64,9 @@ pub struct TuserConf { /// Enabled extensions for TUS protocol. #[structopt( - long, - default_value = "creation,creation-with-upload", - env = "TUSER_EXTENSIONS" + long, + default_value = "creation,creation-with-upload,getting", + env = "TUSER_EXTENSIONS" )] pub extensions: String, @@ -80,6 +80,7 @@ pub enum ProtocolExtensions { CreationWithUpload, Creation, Termination, + Getting, } impl TryFrom<String> for ProtocolExtensions { @@ -93,6 +94,7 @@ impl TryFrom<String> for ProtocolExtensions { "creation" => Ok(ProtocolExtensions::Creation), "creation-with-upload" => Ok(ProtocolExtensions::CreationWithUpload), "termination" => Ok(ProtocolExtensions::Termination), + "getting" => Ok(ProtocolExtensions::Getting), _ => Err(TuserError::UnknownExtension(value.clone())), } } @@ -103,9 +105,10 @@ impl From<ProtocolExtensions> for String { /// original names. fn from(ext: ProtocolExtensions) -> Self { match ext { - ProtocolExtensions::Creation => Self::from("creation"), - ProtocolExtensions::CreationWithUpload => Self::from("creation-with-upload"), - ProtocolExtensions::Termination => Self::from("termination"), + ProtocolExtensions::Creation => "creation".into(), + ProtocolExtensions::CreationWithUpload => "creation-with-upload".into(), + ProtocolExtensions::Termination => "termination".into(), + ProtocolExtensions::Getting => "getting".into(), } } } diff --git a/src/errors.rs b/src/errors.rs index 60851b6..173b3fe 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -8,8 +8,8 @@ pub type TuserResult<T> = Result<T, TuserError>; #[derive(thiserror::Error, Debug)] pub enum TuserError { - #[error("File with id {0} was not found")] - FileNotFound(String), + #[error("Not found")] + FileNotFound, #[error("File with id {0} already exists")] FileAlreadyExists(String), #[error("Given offset is incorrect.")] @@ -40,12 +40,14 @@ impl From<TuserError> for Error { impl ResponseError for TuserError { fn error_response(&self) -> HttpResponse { - HttpResponseBuilder::new(self.status_code()).body(format!("{}", self)) + HttpResponseBuilder::new(self.status_code()) + .set_header("Content-Type", "text/html; charset=utf-8") + .body(format!("{}", self)) } fn status_code(&self) -> StatusCode { match self { - TuserError::FileNotFound(_) => StatusCode::NOT_FOUND, + TuserError::FileNotFound => StatusCode::NOT_FOUND, TuserError::WrongOffset => StatusCode::CONFLICT, _ => StatusCode::INTERNAL_SERVER_ERROR, } diff --git a/src/protocol/core/routes.rs b/src/protocol/core/routes.rs index 87e769b..9b56622 100644 --- a/src/protocol/core/routes.rs +++ b/src/protocol/core/routes.rs @@ -30,6 +30,7 @@ pub async fn get_file_info( HttpResponseBuilder::new(StatusCode::OK) .set_header("Upload-Offset", file_info.offset.to_string()) .set_header("Upload-Length", file_info.length.to_string()) + .set_header("Content-Length", file_info.offset.to_string()) .body("") } else { HttpResponseBuilder::new(StatusCode::NOT_FOUND).body("") diff --git a/src/protocol/getting/mod.rs b/src/protocol/getting/mod.rs new file mode 100644 index 0000000..a63ceb7 --- /dev/null +++ b/src/protocol/getting/mod.rs @@ -0,0 +1,21 @@ +use actix_web::{guard, web}; + +use crate::TuserConf; + +mod routes; + +/// Add getting extension. +/// +/// This extension allows you +/// to get uploaded file. +/// +/// This is unofficial extension. +pub fn add_extension(web_app: &mut web::ServiceConfig, app_conf: &TuserConf) { + web_app.service( + // GET /base/file + web::resource(app_conf.file_url().as_str()) + .name("getting:get") + .guard(guard::Get()) + .to(routes::get_file), + ); +} diff --git a/src/protocol/getting/routes.rs b/src/protocol/getting/routes.rs new file mode 100644 index 0000000..cdcebaf --- /dev/null +++ b/src/protocol/getting/routes.rs @@ -0,0 +1,16 @@ +use actix_web::{HttpRequest, Responder, web}; + +use crate::errors::TuserError; +use crate::Storage; + +pub async fn get_file( + request: HttpRequest, + storage: web::Data<Box<dyn Storage + Send + Sync>>, +) -> impl Responder { + let file_id_opt = request.match_info().get("file_id").map(String::from); + if let Some(file_id) = file_id_opt { + storage.get_contents(file_id.as_str()).await + } else { + Err(TuserError::FileNotFound) + } +} diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs index ae3cd98..64faf84 100644 --- a/src/protocol/mod.rs +++ b/src/protocol/mod.rs @@ -6,6 +6,7 @@ use crate::TuserConf; mod core; mod creation; mod creation_with_upload; +mod getting; mod termination; /// Configure TUS web application. @@ -23,6 +24,9 @@ pub fn setup(app_conf: TuserConf) -> Box<dyn Fn(&mut web::ServiceConfig)> { ProtocolExtensions::Termination => { termination::add_extension(web_app, &app_conf); } + ProtocolExtensions::Getting => { + getting::add_extension(web_app, &app_conf); + } } } core::add_extension(web_app, &app_conf); diff --git a/src/routes.rs b/src/routes.rs index d510bfe..ce07d58 100644 --- a/src/routes.rs +++ b/src/routes.rs @@ -1,10 +1,9 @@ -use actix_web::dev::HttpResponseBuilder; -use actix_web::http::StatusCode; use actix_web::HttpResponse; +use crate::errors::{TuserError, TuserResult}; + /// Default response to all unknown URLs. -pub fn not_found() -> HttpResponse { - HttpResponseBuilder::new(StatusCode::NOT_FOUND) - .set_header("Content-Type", "text/html; charset=utf-8") - .body("Not found") +#[allow(clippy::unused_async)] +pub async fn not_found() -> TuserResult<HttpResponse> { + Err(TuserError::FileNotFound) } diff --git a/src/storages/file_storage.rs b/src/storages/file_storage.rs index 433033f..26d1fc7 100644 --- a/src/storages/file_storage.rs +++ b/src/storages/file_storage.rs @@ -49,7 +49,7 @@ impl Storage for FileStorage { async fn get_file_info(&self, file_id: &str) -> TuserResult<FileInfo> { let info_path = self.info_file_path(file_id); if !info_path.exists() { - return Err(TuserError::FileNotFound(String::from(file_id))); + return Err(TuserError::FileNotFound); } let contents = read_to_string(info_path).await.map_err(|err| { error!("{:?}", err); @@ -83,7 +83,10 @@ impl Storage for FileStorage { } async fn get_contents(&self, file_id: &str) -> TuserResult<NamedFile> { - Err(TuserError::FileNotFound(String::from(file_id))) + NamedFile::open(self.data_file_path(file_id)).map_err(|err| { + error!("{:?}", err); + TuserError::FileNotFound + }) } async fn add_bytes( @@ -162,11 +165,11 @@ impl Storage for FileStorage { async fn remove_file(&self, file_id: &str) -> TuserResult<()> { let info_path = self.info_file_path(file_id); if !info_path.exists() { - return Err(TuserError::FileNotFound(String::from(file_id))); + return Err(TuserError::FileNotFound); } let data_path = self.data_file_path(file_id); if !data_path.exists() { - return Err(TuserError::FileNotFound(String::from(file_id))); + return Err(TuserError::FileNotFound); } remove_file(info_path).await.map_err(|err| { error!("{:?}", err); diff --git a/src/storages/mod.rs b/src/storages/mod.rs index afeb42a..ee35ba3 100644 --- a/src/storages/mod.rs +++ b/src/storages/mod.rs @@ -3,8 +3,8 @@ use std::str::FromStr; use actix_files::NamedFile; use async_trait::async_trait; -use chrono::{DateTime, Utc}; use chrono::serde::ts_seconds; +use chrono::{DateTime, Utc}; use derive_more::{Display, From}; use serde::{Deserialize, Serialize}; use sqlx::FromRow; diff --git a/src/storages/sqlite_file_storage.rs b/src/storages/sqlite_file_storage.rs index d0039dc..fcd401d 100644 --- a/src/storages/sqlite_file_storage.rs +++ b/src/storages/sqlite_file_storage.rs @@ -47,13 +47,23 @@ impl Storage for SQLiteFileStorage { .map_err(|err| TuserError::UnableToPrepareStorage(err.to_string()))?; } if !self.app_conf.storage_opts.sqlite_dsn.exists() { - File::create(self.app_conf.storage_opts.sqlite_dsn.clone()).await.map_err(|err| { - TuserError::UnableToPrepareStorage(err.to_string()) - })?; + File::create(self.app_conf.storage_opts.sqlite_dsn.clone()) + .await + .map_err(|err| TuserError::UnableToPrepareStorage(err.to_string()))?; } let pool = SqlitePoolOptions::new() .max_connections(10) - .connect(format!("sqlite://{}", self.app_conf.storage_opts.sqlite_dsn.as_display().to_string()).as_str()) + .connect( + format!( + "sqlite://{}", + self.app_conf + .storage_opts + .sqlite_dsn + .as_display() + .to_string() + ) + .as_str(), + ) .await .map_err(TuserError::from)?; sqlx::query( @@ -67,7 +77,9 @@ impl Storage for SQLiteFileStorage { deferred_size BOOLEAN, \ metadata TEXT\ );", - ).execute(&pool).await?; + ) + .execute(&pool) + .await?; self.pool = Some(pool); Ok(()) } -- GitLab