diff --git a/src/main.rs b/src/main.rs index 80a3b6123f817a7e91ce3aff296596f445c6208a..b05a51bf48a5110c010348f297fb62b0cc0e5214 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,6 +13,7 @@ use log::LevelFilter; use crate::errors::RustusResult; use crate::info_storages::InfoStorage; use crate::notifiers::models::notification_manager::NotificationManager; +use crate::state::State; use config::RustusConf; use crate::storages::Storage; @@ -23,6 +24,7 @@ mod info_storages; mod notifiers; mod protocol; mod routes; +mod state; mod storages; mod utils; @@ -64,35 +66,22 @@ fn greeting(app_conf: &RustusConf) { /// This function may throw an error /// if the server can't be bound to the /// given address. -pub fn create_server( - storage: Box<dyn Storage + Send + Sync>, - info_storage: Box<dyn InfoStorage + Send + Sync>, - app_conf: RustusConf, - notification_manager: NotificationManager, -) -> Result<Server, std::io::Error> { - let host = app_conf.host.clone(); - let port = app_conf.port; - let workers = app_conf.workers; - let app_conf_data = web::Data::new(app_conf.clone()); - let info_storage_data: web::Data<Box<dyn InfoStorage + Send + Sync>> = - web::Data::from(Arc::new(info_storage)); - let storage_data: web::Data<Box<dyn Storage + Send + Sync>> = - web::Data::from(Arc::new(storage)); - let manager_data: web::Data<Box<NotificationManager>> = - web::Data::from(Arc::new(Box::new(notification_manager))); +pub fn create_server(state: State) -> Result<Server, std::io::Error> { + let host = state.config.host.clone(); + let port = state.config.port; + let config = state.config.clone(); + let workers = state.config.workers; + let state_data: web::Data<State> = web::Data::from(Arc::new(state)); let mut server = HttpServer::new(move || { App::new() - .app_data(app_conf_data.clone()) - .app_data(storage_data.clone()) - .app_data(manager_data.clone()) - .app_data(info_storage_data.clone()) + .app_data(state_data.clone()) // Adds all routes. - .configure(protocol::setup(app_conf.clone())) + .configure(protocol::setup(config.clone())) // Main middleware that appends TUS headers. .wrap( middleware::DefaultHeaders::new() .add(("Tus-Resumable", "1.0.0")) - .add(("Tus-Max-Size", app_conf.max_body_size.to_string())) + .add(("Tus-Max-Size", config.max_body_size.to_string())) .add(("Tus-Version", "1.0.0")), ) .wrap(middleware::Logger::new("\"%r\" \"-\" \"%s\" \"%a\" \"%D\"")) @@ -176,6 +165,11 @@ async fn main() -> std::io::Result<()> { let notification_manager = NotificationManager::new(&app_conf).await?; // Creating actual server and running it. - let server = create_server(storage, info_storage, app_conf, notification_manager)?; + let server = create_server(State::new( + app_conf.clone(), + storage, + info_storage, + notification_manager, + ))?; server.await } diff --git a/src/protocol/core/routes.rs b/src/protocol/core/routes.rs index f9f282db5896bcede50652a7e037a8b785bffc73..72fb39ecaaa06b966b2d31dbb25af8986655819b 100644 --- a/src/protocol/core/routes.rs +++ b/src/protocol/core/routes.rs @@ -4,7 +4,7 @@ use crate::errors::RustusError; use crate::notifiers::Hook; use crate::protocol::extensions::Extensions; use crate::utils::headers::{check_header, parse_header}; -use crate::{InfoStorage, NotificationManager, RustusConf, Storage}; +use crate::{RustusConf, State}; #[allow(clippy::needless_pass_by_value)] pub fn server_info(app_conf: web::Data<RustusConf>) -> HttpResponse { @@ -20,8 +20,7 @@ pub fn server_info(app_conf: web::Data<RustusConf>) -> HttpResponse { } pub async fn get_file_info( - info_storage: web::Data<Box<dyn InfoStorage + Send + Sync>>, - storage: web::Data<Box<dyn Storage + Send + Sync>>, + state: web::Data<State>, request: HttpRequest, ) -> actix_web::Result<HttpResponse> { // Getting file id from URL. @@ -31,8 +30,8 @@ pub async fn get_file_info( let file_id = request.match_info().get("file_id").unwrap(); // Getting file info from info_storage. - let file_info = info_storage.get_info(file_id).await?; - if file_info.storage != storage.to_string() { + let file_info = state.info_storage.get_info(file_id).await?; + if file_info.storage != state.data_storage.to_string() { return Ok(HttpResponse::NotFound().body("")); } let mut builder = HttpResponse::Ok(); @@ -54,10 +53,7 @@ pub async fn get_file_info( pub async fn write_bytes( request: HttpRequest, bytes: Bytes, - storage: web::Data<Box<dyn Storage + Send + Sync>>, - info_storage: web::Data<Box<dyn InfoStorage + Send + Sync>>, - notification_manager: web::Data<Box<NotificationManager>>, - app_conf: web::Data<RustusConf>, + state: web::Data<State>, ) -> actix_web::Result<HttpResponse> { // Checking if request has required headers. if !check_header(&request, "Content-Type", "application/offset+octet-stream") { @@ -76,7 +72,8 @@ pub async fn write_bytes( // New upload length. // Parses header `Upload-Length` only if the creation-defer-length extension is enabled. - let updated_len = if app_conf + let updated_len = if state + .config .extensions_vec() .contains(&Extensions::CreationDeferLength) { @@ -87,10 +84,10 @@ pub async fn write_bytes( let file_id = request.match_info().get("file_id").unwrap(); // Getting file info. - let mut file_info = info_storage.get_info(file_id).await?; + let mut file_info = state.info_storage.get_info(file_id).await?; // Checking if file was stored in the same storage. - if file_info.storage != storage.to_string() { + if file_info.storage != state.data_storage.to_string() { return Ok(HttpResponse::NotFound().body("")); } // Checking if offset from request is the same as the real offset. @@ -125,24 +122,29 @@ pub async fn write_bytes( } // Appending bytes to file. - storage.add_bytes(&file_info, bytes.as_ref()).await?; + state + .data_storage + .add_bytes(&file_info, bytes.as_ref()) + .await?; // Updating offset. file_info.offset += bytes.len(); // Saving info to info storage. - info_storage.set_info(&file_info, false).await?; + state.info_storage.set_info(&file_info, false).await?; let mut hook = Hook::PostReceive; if file_info.length == Some(file_info.offset) { hook = Hook::PostFinish; } - if app_conf.hook_is_active(hook) { - let message = app_conf + if state.config.hook_is_active(hook) { + let message = state + .config .notification_opts .hooks_format .format(&request, &file_info)?; let headers = request.headers().clone(); tokio::spawn(async move { - notification_manager + state + .notification_manager .send_message(message, hook, &headers) .await }); diff --git a/src/protocol/creation/routes.rs b/src/protocol/creation/routes.rs index 61bbdca8f54e698b888d1683da499d7c82075176..0e8a30a40ec0f8590c0ca81536a641cfdb4d844c 100644 --- a/src/protocol/creation/routes.rs +++ b/src/protocol/creation/routes.rs @@ -7,7 +7,7 @@ use crate::info_storages::FileInfo; use crate::notifiers::Hook; use crate::protocol::extensions::Extensions; use crate::utils::headers::{check_header, parse_header}; -use crate::{InfoStorage, NotificationManager, RustusConf, Storage}; +use crate::State; /// Get metadata info from request. /// @@ -58,10 +58,7 @@ fn get_metadata(request: &HttpRequest) -> Option<HashMap<String, String>> { /// you can upload first bytes if creation-with-upload /// extension is enabled. pub async fn create_file( - storage: web::Data<Box<dyn Storage + Send + Sync>>, - info_storage: web::Data<Box<dyn InfoStorage + Send + Sync>>, - notification_manager: web::Data<Box<NotificationManager>>, - app_conf: web::Data<RustusConf>, + state: web::Data<State>, request: HttpRequest, bytes: Bytes, ) -> actix_web::Result<HttpResponse> { @@ -71,7 +68,8 @@ pub async fn create_file( let defer_size = check_header(&request, "Upload-Defer-Length", "1"); // Indicator that creation-defer-length is enabled. - let defer_ext = app_conf + let defer_ext = state + .config .extensions_vec() .contains(&Extensions::CreationDeferLength); @@ -89,29 +87,32 @@ pub async fn create_file( file_id.as_str(), length, None, - storage.to_string(), + state.data_storage.to_string(), meta.clone(), ); - if app_conf.hook_is_active(Hook::PreCreate) { - let message = app_conf + if state.config.hook_is_active(Hook::PreCreate) { + let message = state + .config .notification_opts .hooks_format .format(&request, &file_info)?; let headers = request.headers(); - notification_manager + state + .notification_manager .send_message(message, Hook::PreCreate, headers) .await?; } // Create file and get the it's path. - file_info.path = Some(storage.create_file(&file_info).await?); + file_info.path = Some(state.data_storage.create_file(&file_info).await?); // Create upload URL for this file. let upload_url = request.url_for("core:write_bytes", &[file_info.id.clone()])?; // Checking if creation-with-upload extension is enabled. - let with_upload = app_conf + let with_upload = state + .config .extensions_vec() .contains(&Extensions::CreationWithUpload); if with_upload && !bytes.is_empty() { @@ -119,14 +120,18 @@ pub async fn create_file( return Ok(HttpResponse::BadRequest().body("")); } // Writing first bytes. - storage.add_bytes(&file_info, bytes.as_ref()).await?; + state + .data_storage + .add_bytes(&file_info, bytes.as_ref()) + .await?; file_info.offset += bytes.len(); } - info_storage.set_info(&file_info, true).await?; + state.info_storage.set_info(&file_info, true).await?; - if app_conf.hook_is_active(Hook::PostCreate) { - let message = app_conf + if state.config.hook_is_active(Hook::PostCreate) { + let message = state + .config .notification_opts .hooks_format .format(&request, &file_info)?; @@ -134,7 +139,8 @@ pub async fn create_file( // Adding send_message task to tokio reactor. // Thin function would be executed in background. tokio::spawn(async move { - notification_manager + state + .notification_manager .send_message(message, Hook::PostCreate, &headers) .await }); diff --git a/src/protocol/getting/routes.rs b/src/protocol/getting/routes.rs index 44186c81c6bbb64727aaad6a82444edeb550c741..97af31a183dba4a927d2b064c5abb9098d75aad8 100644 --- a/src/protocol/getting/routes.rs +++ b/src/protocol/getting/routes.rs @@ -1,23 +1,19 @@ use actix_web::{web, HttpRequest, Responder}; use crate::errors::RustusError; -use crate::{InfoStorage, Storage}; +use crate::State; /// Retrieve actual file. /// /// This method allows you to download files directly from storage. -pub async fn get_file( - request: HttpRequest, - storage: web::Data<Box<dyn Storage + Send + Sync>>, - info_storage: web::Data<Box<dyn InfoStorage + Send + Sync>>, -) -> impl Responder { +pub async fn get_file(request: HttpRequest, state: web::Data<State>) -> impl Responder { let file_id_opt = request.match_info().get("file_id").map(String::from); if let Some(file_id) = file_id_opt { - let file_info = info_storage.get_info(file_id.as_str()).await?; - if file_info.storage != storage.to_string() { + let file_info = state.info_storage.get_info(file_id.as_str()).await?; + if file_info.storage != state.data_storage.to_string() { return Err(RustusError::FileNotFound); } - storage.get_contents(&file_info).await + state.data_storage.get_contents(&file_info).await } else { Err(RustusError::FileNotFound) } diff --git a/src/protocol/termination/routes.rs b/src/protocol/termination/routes.rs index 16d809406a955c404a8ae0d19383b5b1fb171114..dd302e8d05012d4bfb629473dc21ba0fd5fb8424 100644 --- a/src/protocol/termination/routes.rs +++ b/src/protocol/termination/routes.rs @@ -2,35 +2,34 @@ use actix_web::{web, HttpRequest, HttpResponse}; use crate::errors::RustusResult; use crate::notifiers::Hook; -use crate::{InfoStorage, NotificationManager, RustusConf, Storage}; +use crate::State; /// Terminate uploading. /// /// This method will remove all data by id. /// It removes info and actual data. pub async fn terminate( - storage: web::Data<Box<dyn Storage + Send + Sync>>, - info_storage: web::Data<Box<dyn InfoStorage + Send + Sync>>, request: HttpRequest, - notification_manager: web::Data<Box<NotificationManager>>, - app_conf: web::Data<RustusConf>, + state: web::Data<State>, ) -> RustusResult<HttpResponse> { let file_id_opt = request.match_info().get("file_id").map(String::from); if let Some(file_id) = file_id_opt { - let file_info = info_storage.get_info(file_id.as_str()).await?; - if file_info.storage != storage.to_string() { + let file_info = state.info_storage.get_info(file_id.as_str()).await?; + if file_info.storage != state.data_storage.to_string() { return Ok(HttpResponse::NotFound().body("")); } - info_storage.remove_info(file_id.as_str()).await?; - storage.remove_file(&file_info).await?; - if app_conf.hook_is_active(Hook::PostTerminate) { - let message = app_conf + state.info_storage.remove_info(file_id.as_str()).await?; + state.data_storage.remove_file(&file_info).await?; + if state.config.hook_is_active(Hook::PostTerminate) { + let message = state + .config .notification_opts .hooks_format .format(&request, &file_info)?; let headers = request.headers().clone(); tokio::spawn(async move { - notification_manager + state + .notification_manager .send_message(message, Hook::PostTerminate, &headers) .await }); diff --git a/src/state.rs b/src/state.rs new file mode 100644 index 0000000000000000000000000000000000000000..16bc4f24ab2bab10450226a85b8e6c2d8c7faef8 --- /dev/null +++ b/src/state.rs @@ -0,0 +1,24 @@ +use crate::{InfoStorage, NotificationManager, RustusConf, Storage}; + +pub struct State { + pub config: RustusConf, + pub data_storage: Box<dyn Storage + Send + Sync>, + pub info_storage: Box<dyn InfoStorage + Send + Sync>, + pub notification_manager: NotificationManager, +} + +impl State { + pub fn new( + config: RustusConf, + data_storage: Box<dyn Storage + Send + Sync>, + info_storage: Box<dyn InfoStorage + Send + Sync>, + notification_manager: NotificationManager, + ) -> Self { + Self { + config, + data_storage, + info_storage, + notification_manager, + } + } +}