diff --git a/Cargo.toml b/Cargo.toml
index f3cd918386e8398fbc285448c25bc5e3c1f99ff1..c538a529c2d89118b89e6e2702b8cd1b203f4e83 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -2,6 +2,7 @@
 name = "tuser"
 version = "0.1.0"
 edition = "2021"
+description = "TUS protocol implementation written in Rust."
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 [[bin]]
diff --git a/src/config.rs b/src/config.rs
index e1c4102b3d7c02aea285a9461652376ccdc331a0..8e626c8e5cd52df214b5139f9c90792c340033c8 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -2,6 +2,7 @@ use std::path::PathBuf;
 
 use structopt::StructOpt;
 
+use crate::errors::TuserError;
 use crate::storages::AvailableStores;
 
 #[derive(Debug, StructOpt, Clone)]
@@ -38,10 +39,99 @@ pub struct TuserConf {
     /// Number of actix workers default value = number of cpu cores.
     #[structopt(long, short)]
     pub workers: Option<usize>,
+
+    /// Enabled extensions for TUS protocol.
+    #[structopt(long, default_value = "creation,creation-with-upload")]
+    pub extensions: String,
+}
+
+/// Enum of available Protocol Extensions
+#[derive(PartialEq, PartialOrd, Ord, Eq)]
+pub enum ProtocolExtensions {
+    CreationWithUpload,
+    Creation,
+    Termination,
+}
+
+impl TryFrom<String> for ProtocolExtensions {
+    type Error = TuserError;
+
+    /// Parse string to protocol extension.
+    ///
+    /// This function raises an error if unknown protocol was passed.
+    fn try_from(value: String) -> Result<Self, Self::Error> {
+        match value.as_str() {
+            "creation" => Ok(ProtocolExtensions::Creation),
+            "creation-with-upload" => Ok(ProtocolExtensions::CreationWithUpload),
+            "termination" => Ok(ProtocolExtensions::Termination),
+            _ => Err(TuserError::UnknownExtension(value.clone())),
+        }
+    }
+}
+
+impl From<ProtocolExtensions> for String {
+    /// Mapping protocol extensions to their
+    /// 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"),
+        }
+    }
 }
 
 impl TuserConf {
+    /// Function to parse CLI parametes.
+    ///
+    /// This is a workaround for issue mentioned
+    /// [here](https://www.reddit.com/r/rust/comments/8ddd19/confusion_with_splitting_mainrs_into_smaller/).
     pub fn from_args() -> TuserConf {
         <TuserConf as StructOpt>::from_args()
     }
+
+    /// Base API url.
+    pub fn base_url(&self) -> String {
+        format!(
+            "/{}",
+            self.url
+                .strip_prefix('/')
+                .unwrap_or_else(|| self.url.as_str())
+        )
+    }
+
+    /// URL for a particular file.
+    pub fn file_url(&self) -> String {
+        let base_url = self.base_url();
+        format!(
+            "{}/{{file_id}}",
+            base_url
+                .strip_suffix('/')
+                .unwrap_or_else(|| base_url.as_str())
+        )
+    }
+
+    /// List of extensions.
+    ///
+    /// This function will parse list of extensions from CLI
+    /// and sort them.
+    ///
+    /// Protocol extensions must be sorted,
+    /// because Actix doesn't override
+    /// existing methods.
+    pub fn extensions_vec(&self) -> Vec<ProtocolExtensions> {
+        let mut ext = self
+            .extensions
+            .split(',')
+            .flat_map(|ext| ProtocolExtensions::try_from(String::from(ext)))
+            .collect::<Vec<ProtocolExtensions>>();
+        // creation-with-upload
+        if ext.contains(&ProtocolExtensions::CreationWithUpload)
+            && !ext.contains(&ProtocolExtensions::Creation)
+        {
+            ext.push(ProtocolExtensions::Creation);
+        }
+        ext.sort();
+        ext
+    }
 }
diff --git a/src/errors.rs b/src/errors.rs
index f62354fe2fac6df47afc55bace59d15d0298a802..3a70a22f99e631bea61689b3e3d4a82669b93724 100644
--- a/src/errors.rs
+++ b/src/errors.rs
@@ -1,8 +1,8 @@
 use std::io::{Error, ErrorKind};
 
-use actix_web::{HttpResponse, ResponseError};
 use actix_web::dev::HttpResponseBuilder;
 use actix_web::http::StatusCode;
+use actix_web::{HttpResponse, ResponseError};
 use thiserror::Error;
 
 pub type TuserResult<T> = Result<T, TuserError>;
@@ -23,8 +23,12 @@ pub enum TuserError {
     UnableToReadInfo,
     #[error("Unable to write file {0}")]
     UnableToWrite(String),
+    #[error("Unable to remove file {0}")]
+    UnableToRemove(String),
     #[error("Unable to prepare storage. Reason: {0}")]
     UnableToPrepareStorage(String),
+    #[error("Unknown extension: {0}")]
+    UnknownExtension(String),
 }
 
 impl From<TuserError> for Error {
diff --git a/src/main.rs b/src/main.rs
index ffb625402295f48d3a78f9d3ee37e7e3d470a707..bbfd9b5a2868870d0b7e6c45e7a2ed15016fe026 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,10 +1,10 @@
 use std::str::FromStr;
 
+use actix_web::http::Method;
 use actix_web::{
-    App,
-    dev::{Server, Service}, guard, HttpServer, middleware, web,
+    dev::{Server, Service},
+    middleware, web, App, HttpServer,
 };
-use actix_web::http::Method;
 use log::error;
 
 use config::TuserConf;
@@ -13,6 +13,7 @@ use crate::storages::Storage;
 
 mod config;
 mod errors;
+mod protocol;
 mod routes;
 mod storages;
 
@@ -29,70 +30,24 @@ mod storages;
 /// This function may throw an error
 /// if the server can't be bound to the
 /// given address.
-pub fn create_server<T: Storage + 'static + Send>(
-    storage: T,
+pub fn create_server<S: Storage + 'static + Send>(
+    storage: S,
     app_conf: TuserConf,
 ) -> Result<Server, std::io::Error> {
     let host = app_conf.host.clone();
     let port = app_conf.port;
     let workers = app_conf.workers;
-    let base_url = format!(
-        "/{}",
-        app_conf
-            .url
-            .strip_prefix('/')
-            .unwrap_or_else(|| app_conf.url.as_str())
-    );
-    let file_url = format!(
-        "{}/{{file_id}}",
-        base_url
-            .strip_suffix('/')
-            .unwrap_or_else(|| base_url.as_str())
-    );
     let mut server = HttpServer::new(move || {
         App::new()
             .data(app_conf.clone())
             .data(storage.clone())
-            .service(
-                // PATCH /base/{file_id}
-                // Main URL for uploading files.
-                web::resource(base_url.as_str())
-                    .name("server_info")
-                    .guard(guard::Options())
-                    .to(routes::server_info),
-            )
-
-            .service(
-                // PATCH /base/{file_id}
-                // Main URL for uploading files.
-                web::resource(file_url.as_str())
-                    .name("write_bytes")
-                    .guard(guard::Patch())
-                    .to(routes::write_bytes::<T>),
-            )
-            .service(
-                // HEAD /base/{file_id}
-                // Main URL for getting info about files.
-                web::resource(file_url.as_str())
-                    .name("file_info")
-                    .guard(guard::Head())
-                    // Header to prevent the client and/or proxies from caching the response.
-                    .wrap(middleware::DefaultHeaders::new().header("Cache-Control", "no-store"))
-                    .to(routes::get_file_info::<T>),
-            )
-            .service(
-                // Post /base/{file_id}
-                // URL for creating files.
-                web::resource(base_url.as_str())
-                    .name("create_file")
-                    .guard(guard::Post())
-                    .to(routes::create_file::<T>),
-            )
+            // Adds all routes.
+            .configure(protocol::setup::<S>(app_conf.clone()))
             // Main middleware that appends TUS headers.
             .wrap(
                 middleware::DefaultHeaders::new()
                     .header("Tus-Resumable", "1.0.0")
-                    .header("Tus-Version", "1.0.0")
+                    .header("Tus-Version", "1.0.0"),
             )
             .wrap(middleware::Logger::new("\"%r\" \"-\" \"%s\" \"%a\" \"%D\""))
             // Middleware that overrides method of a request if
@@ -111,7 +66,7 @@ pub fn create_server<T: Storage + 'static + Send>(
             // It returns 404 status_code.
             .default_service(web::route().to(routes::not_found))
     })
-        .bind((host, port))?;
+    .bind((host, port))?;
 
     // If custom workers count variable is provided.
     if let Some(workers_count) = workers {
@@ -123,15 +78,14 @@ pub fn create_server<T: Storage + 'static + Send>(
 /// Main program entrypoint.
 #[actix_web::main]
 async fn main() -> std::io::Result<()> {
-    let args = TuserConf::from_args();
-    simple_logging::log_to_stderr(args.log_level);
+    let app_conf = TuserConf::from_args();
+    simple_logging::log_to_stderr(app_conf.log_level);
 
-    let storage_conf = args.clone();
-    let storage = args.storage.get_storage(storage_conf);
+    let storage = app_conf.storage.get_storage(&app_conf);
     if let Err(err) = storage.prepare().await {
         error!("{}", err);
         return Err(err.into());
     }
-    let server = create_server(storage, args)?;
+    let server = create_server(storage, app_conf)?;
     server.await
 }
diff --git a/src/protocol/core/mod.rs b/src/protocol/core/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..b2248f46f17bfd92e83814d8efb0e0947a462697
--- /dev/null
+++ b/src/protocol/core/mod.rs
@@ -0,0 +1,46 @@
+use actix_web::{guard, middleware, web};
+
+use crate::{Storage, TuserConf};
+
+mod routes;
+
+/// Add core TUS protocol endpoints.
+///
+/// This part of a protocol
+/// has several endpoints.
+///
+/// OPTIONS /api    - to get info about the app.
+/// HEAD /api/file  - to get info about the file.
+/// PATCH /api/file - to add bytes to file.
+pub fn add_extension<S: Storage + 'static + Send>(
+    web_app: &mut web::ServiceConfig,
+    app_conf: &TuserConf,
+) {
+    web_app
+        .service(
+            // PATCH /base/{file_id}
+            // Main URL for uploading files.
+            web::resource(app_conf.base_url().as_str())
+                .name("core:server_info")
+                .guard(guard::Options())
+                .to(routes::server_info),
+        )
+        .service(
+            // PATCH /base/{file_id}
+            // Main URL for uploading files.
+            web::resource(app_conf.file_url().as_str())
+                .name("core:write_bytes")
+                .guard(guard::Patch())
+                .to(routes::write_bytes::<S>),
+        )
+        .service(
+            // HEAD /base/{file_id}
+            // Main URL for getting info about files.
+            web::resource(app_conf.file_url().as_str())
+                .name("core:file_info")
+                .guard(guard::Head())
+                // Header to prevent the client and/or proxies from caching the response.
+                .wrap(middleware::DefaultHeaders::new().header("Cache-Control", "no-store"))
+                .to(routes::get_file_info::<S>),
+        );
+}
diff --git a/src/protocol/core/routes.rs b/src/protocol/core/routes.rs
new file mode 100644
index 0000000000000000000000000000000000000000..b43a63b967f6d101179857736d1d8d3f9b7ef218
--- /dev/null
+++ b/src/protocol/core/routes.rs
@@ -0,0 +1,80 @@
+use actix_web::{
+    dev::HttpResponseBuilder,
+    http::StatusCode,
+    web,
+    web::{Buf, Bytes},
+    HttpRequest, HttpResponse,
+};
+
+use crate::{Storage, TuserConf};
+
+#[allow(clippy::needless_pass_by_value)]
+pub fn server_info(app_conf: web::Data<TuserConf>) -> HttpResponse {
+    let ext_str = app_conf
+        .extensions_vec()
+        .into_iter()
+        .map(String::from)
+        .collect::<Vec<String>>()
+        .join(",");
+    HttpResponseBuilder::new(StatusCode::OK)
+        .set_header("Tus-Extension", ext_str.as_str())
+        .body("")
+}
+
+pub async fn get_file_info<T: Storage>(
+    storage: web::Data<T>,
+    request: HttpRequest,
+) -> actix_web::Result<HttpResponse> {
+    let resp = if let Some(file_id) = request.match_info().get("file_id") {
+        let file_info = storage.get_file_info(file_id).await?;
+        HttpResponseBuilder::new(StatusCode::OK)
+            .set_header("Upload-Offset", file_info.offset.to_string())
+            .set_header("Upload-Length", file_info.length.to_string())
+            .body("")
+    } else {
+        HttpResponseBuilder::new(StatusCode::NOT_FOUND).body("")
+    };
+    Ok(resp)
+}
+
+pub async fn write_bytes<T: Storage>(
+    request: HttpRequest,
+    bytes: Bytes,
+    storage: web::Data<T>,
+) -> actix_web::Result<HttpResponse> {
+    let content_type =
+        request
+            .headers()
+            .get("Content-Type")
+            .and_then(|header_val| match header_val.to_str() {
+                Ok(val) => Some(val == "application/offset+octet-stream"),
+                Err(_) => None,
+            });
+    if Some(true) != content_type {
+        return Ok(HttpResponseBuilder::new(StatusCode::UNSUPPORTED_MEDIA_TYPE).body(""));
+    }
+    let offset = request
+        .headers()
+        .get("Upload-Offset")
+        .and_then(|header_val| match header_val.to_str() {
+            Ok(val) => Some(String::from(val)),
+            Err(_) => None,
+        })
+        .and_then(|val| match val.parse::<usize>() {
+            Ok(offset) => Some(offset),
+            Err(_) => None,
+        });
+    if offset.is_none() {
+        return Ok(HttpResponseBuilder::new(StatusCode::UNSUPPORTED_MEDIA_TYPE).body(""));
+    }
+    if let Some(file_id) = request.match_info().get("file_id") {
+        let offset = storage
+            .add_bytes(file_id, offset.unwrap(), bytes.bytes())
+            .await?;
+        Ok(HttpResponseBuilder::new(StatusCode::NO_CONTENT)
+            .set_header("Upload-Offset", offset.to_string())
+            .body(""))
+    } else {
+        Ok(HttpResponseBuilder::new(StatusCode::NOT_FOUND).body(""))
+    }
+}
diff --git a/src/protocol/creation/mod.rs b/src/protocol/creation/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..69214dc27555183200aae0bab7ff92c82302a40d
--- /dev/null
+++ b/src/protocol/creation/mod.rs
@@ -0,0 +1,23 @@
+use actix_web::{guard, web};
+
+use crate::{Storage, TuserConf};
+
+mod routes;
+
+/// Add creation extensions.
+///
+/// This extension allows you
+/// to create file before sending data.
+pub fn add_extension<S: Storage + 'static + Send>(
+    web_app: &mut web::ServiceConfig,
+    app_conf: &TuserConf,
+) {
+    web_app.service(
+        // Post /base
+        // URL for creating files.
+        web::resource(app_conf.base_url().as_str())
+            .name("creation:create_file")
+            .guard(guard::Post())
+            .to(routes::create_file::<S>),
+    );
+}
diff --git a/src/protocol/creation/routes.rs b/src/protocol/creation/routes.rs
new file mode 100644
index 0000000000000000000000000000000000000000..d1177196b60e2c4c793282adcbf8e15c431b72f6
--- /dev/null
+++ b/src/protocol/creation/routes.rs
@@ -0,0 +1,28 @@
+use actix_web::dev::HttpResponseBuilder;
+use actix_web::http::StatusCode;
+use actix_web::{web, HttpRequest, HttpResponse};
+
+use crate::Storage;
+
+pub async fn create_file<T: Storage>(
+    storage: web::Data<T>,
+    request: HttpRequest,
+) -> actix_web::Result<HttpResponse> {
+    let length = request
+        .headers()
+        .get("Upload-Length")
+        .and_then(|value| match value.to_str() {
+            Ok(header_str) => Some(String::from(header_str)),
+            Err(_) => None,
+        })
+        .and_then(|val| match val.parse::<usize>() {
+            Ok(num) => Some(num),
+            Err(_) => None,
+        });
+    let file_id = storage.create_file(length, None).await?;
+    let upload_url = request.url_for("core:write_bytes", &[file_id])?;
+    Ok(HttpResponseBuilder::new(StatusCode::CREATED)
+        .set_header("Location", upload_url.as_str())
+        .set_header("Upload-Offset", "0")
+        .body(""))
+}
diff --git a/src/protocol/creation_with_upload/mod.rs b/src/protocol/creation_with_upload/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..a0b3707c169b7d6556bf283c548ee6050747f3a2
--- /dev/null
+++ b/src/protocol/creation_with_upload/mod.rs
@@ -0,0 +1,19 @@
+use actix_web::{guard, web};
+
+use crate::{Storage, TuserConf};
+
+mod routes;
+
+pub fn add_extension<S: Storage + 'static + Send>(
+    web_app: &mut web::ServiceConfig,
+    app_conf: &TuserConf,
+) {
+    web_app.service(
+        // Post /base
+        // URL for creating files.
+        web::resource(app_conf.base_url().as_str())
+            .name("creation-with-upload:create_file")
+            .guard(guard::Post())
+            .to(routes::create_file::<S>),
+    );
+}
diff --git a/src/protocol/creation_with_upload/routes.rs b/src/protocol/creation_with_upload/routes.rs
new file mode 100644
index 0000000000000000000000000000000000000000..c3b38a00a7e04879ad439a20804187dee3581c8b
--- /dev/null
+++ b/src/protocol/creation_with_upload/routes.rs
@@ -0,0 +1,69 @@
+use actix_web::dev::HttpResponseBuilder;
+use actix_web::http::StatusCode;
+use actix_web::web::{Buf, Bytes};
+use actix_web::{web, HttpRequest, HttpResponse};
+
+use crate::Storage;
+
+/// Creates files with initial bytes.
+///
+/// This function is similar to
+/// creation:create_file,
+/// except that it can write bytes
+/// right after it created a data file.
+pub async fn create_file<T: Storage>(
+    storage: web::Data<T>,
+    request: HttpRequest,
+    bytes: Bytes,
+) -> actix_web::Result<HttpResponse> {
+    let length = request
+        .headers()
+        .get("Upload-Length")
+        .and_then(|value| match value.to_str() {
+            Ok(header_str) => Some(String::from(header_str)),
+            Err(_) => None,
+        })
+        .and_then(|val| match val.parse::<usize>() {
+            Ok(num) => Some(num),
+            Err(_) => None,
+        });
+    let file_id = storage.create_file(length, None).await?;
+    let mut upload_offset = 0;
+    if !bytes.is_empty() {
+        // Checking if content type matches.
+        let content_type = request
+            .headers()
+            .get("Content-Type")
+            .and_then(|header_val| match header_val.to_str() {
+                Ok(val) => Some(val == "application/offset+octet-stream"),
+                Err(_) => None,
+            });
+        if Some(true) != content_type {
+            return Ok(HttpResponseBuilder::new(StatusCode::UNSUPPORTED_MEDIA_TYPE).body(""));
+        }
+        // Checking if
+        let offset = request
+            .headers()
+            .get("Upload-Offset")
+            .and_then(|header_val| match header_val.to_str() {
+                Ok(val) => Some(String::from(val)),
+                Err(_) => None,
+            })
+            .and_then(|val| match val.parse::<usize>() {
+                Ok(offset) => Some(offset),
+                Err(_) => None,
+            });
+        if offset.is_none() {
+            return Ok(HttpResponseBuilder::new(StatusCode::UNSUPPORTED_MEDIA_TYPE).body(""));
+        }
+
+        upload_offset = storage
+            .add_bytes(file_id.as_str(), offset.unwrap(), bytes.bytes())
+            .await?;
+    }
+    let upload_url = request.url_for("core:write_bytes", &[file_id])?;
+    Ok(HttpResponseBuilder::new(StatusCode::CREATED)
+        .set_header("Location", upload_url.as_str())
+        .set_header("Upload-Offset", upload_offset.to_string())
+        .body(""))
+}
diff --git a/src/protocol/mod.rs b/src/protocol/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..b41c1475af3f43fa1e5f8ec852b3971b04ab31cf
--- /dev/null
+++ b/src/protocol/mod.rs
@@ -0,0 +1,32 @@
+use actix_web::web;
+
+use crate::config::ProtocolExtensions;
+use crate::{Storage, TuserConf};
+
+mod core;
+mod creation;
+mod creation_with_upload;
+mod termination;
+
+/// Configure TUS web application.
+///
+/// This function resolves all protocol extensions
+/// provided by CLI into services and adds it to the application.
+pub fn setup<S: Storage + 'static + Send>(
+    app_conf: TuserConf,
+) -> Box<dyn Fn(&mut web::ServiceConfig)> {
+    Box::new(move |web_app| {
+        for extension in app_conf.extensions_vec() {
+            match extension {
+                ProtocolExtensions::Creation => creation::add_extension::<S>(web_app, &app_conf),
+                ProtocolExtensions::CreationWithUpload => {
+                    creation_with_upload::add_extension::<S>(web_app, &app_conf);
+                }
+                ProtocolExtensions::Termination => {
+                    termination::add_extension::<S>(web_app, &app_conf);
+                }
+            }
+        }
+        core::add_extension::<S>(web_app, &app_conf);
+    })
+}
diff --git a/src/protocol/termination/mod.rs b/src/protocol/termination/mod.rs
new file mode 100644
index 0000000000000000000000000000000000000000..7ed341d05ad5a48c9b28866d81199ae171a42545
--- /dev/null
+++ b/src/protocol/termination/mod.rs
@@ -0,0 +1,22 @@
+use actix_web::{guard, web};
+
+use crate::{Storage, TuserConf};
+
+mod routes;
+
+/// Add termination extension.
+///
+/// This extension allows you
+/// to terminate file upload.
+pub fn add_extension<S: Storage + 'static + Send>(
+    web_app: &mut web::ServiceConfig,
+    app_conf: &TuserConf,
+) {
+    web_app.service(
+        // DELETE /base/file
+        web::resource(app_conf.file_url().as_str())
+            .name("termination:terminate")
+            .guard(guard::Delete())
+            .to(routes::terminate::<S>),
+    );
+}
diff --git a/src/protocol/termination/routes.rs b/src/protocol/termination/routes.rs
new file mode 100644
index 0000000000000000000000000000000000000000..0aa971ce962e985810e74013579067879d74b2b7
--- /dev/null
+++ b/src/protocol/termination/routes.rs
@@ -0,0 +1,21 @@
+use actix_web::dev::HttpResponseBuilder;
+use actix_web::http::StatusCode;
+use actix_web::{web, HttpRequest, HttpResponse};
+
+use crate::errors::TuserResult;
+use crate::Storage;
+
+/// Terminate uploading.
+///
+/// This method will remove all
+/// files by id.
+pub async fn terminate<S: Storage>(
+    storage: web::Data<S>,
+    request: HttpRequest,
+) -> TuserResult<HttpResponse> {
+    let file_id_opt = request.match_info().get("file_id").map(String::from);
+    if let Some(file_id) = file_id_opt {
+        storage.remove_file(file_id.as_str()).await?;
+    }
+    Ok(HttpResponseBuilder::new(StatusCode::NO_CONTENT).body(""))
+}
diff --git a/src/routes.rs b/src/routes.rs
index 1413c2d0d29976ebb821c7f16d827d30347176fb..d510bfea02e86dffdb06c5a81b196876b01755e2 100644
--- a/src/routes.rs
+++ b/src/routes.rs
@@ -1,102 +1,10 @@
-use actix_web::{HttpRequest, HttpResponse};
 use actix_web::dev::HttpResponseBuilder;
 use actix_web::http::StatusCode;
-use actix_web::web;
-use actix_web::web::{Buf, Bytes};
-
-use crate::storages::Storage;
+use actix_web::HttpResponse;
 
 /// Default response to all unknown URLs.
-#[allow(clippy::needless_pass_by_value)]
 pub fn not_found() -> HttpResponse {
     HttpResponseBuilder::new(StatusCode::NOT_FOUND)
         .set_header("Content-Type", "text/html; charset=utf-8")
         .body("Not found")
 }
-
-pub fn server_info() -> HttpResponse {
-    HttpResponseBuilder::new(StatusCode::OK)
-        .set_header("Tus-Extension", "creation")
-        .body("")
-}
-
-pub async fn get_file_info<T: Storage>(
-    storage: web::Data<T>,
-    request: HttpRequest,
-) -> actix_web::Result<HttpResponse> {
-    let resp = if let Some(file_id) = request.match_info().get("file_id") {
-        let file_info = storage.get_file_info(file_id).await?;
-        HttpResponseBuilder::new(StatusCode::OK)
-            .set_header("Upload-Offset", file_info.offset.to_string())
-            .set_header("Upload-Length", file_info.length.to_string())
-            .body("")
-    } else {
-        HttpResponseBuilder::new(StatusCode::NOT_FOUND).body("")
-    };
-    Ok(resp)
-}
-
-pub async fn create_file<T: Storage>(
-    storage: web::Data<T>,
-    request: HttpRequest,
-) -> actix_web::Result<HttpResponse> {
-    let length = request
-        .headers()
-        .get("Upload-Length")
-        .and_then(|value| match value.to_str() {
-            Ok(header_str) => Some(String::from(header_str)),
-            Err(_) => None,
-        })
-        .and_then(|val| match val.parse::<usize>() {
-            Ok(num) => Some(num),
-            Err(_) => None,
-        });
-    let file_id = storage.create_file(length, None).await?;
-    let upload_url = request.url_for("write_bytes", &[file_id])?;
-    Ok(HttpResponseBuilder::new(StatusCode::CREATED)
-        .set_header("Location", upload_url.as_str())
-        .body(""))
-}
-
-pub async fn write_bytes<T: Storage>(
-    request: HttpRequest,
-    bytes: Bytes,
-    storage: web::Data<T>,
-) -> actix_web::Result<HttpResponse> {
-    let conflict_response = HttpResponseBuilder::new(StatusCode::UNSUPPORTED_MEDIA_TYPE).body("");
-    let content_type =
-        request
-            .headers()
-            .get("Content-Type")
-            .and_then(|header_val| match header_val.to_str() {
-                Ok(val) => Some(val == "application/offset+octet-stream"),
-                Err(_) => None,
-            });
-    if Some(true) != content_type {
-        return Ok(conflict_response);
-    }
-    let offset =
-        request
-            .headers()
-            .get("Upload-Offset")
-            .and_then(|header_val| match header_val.to_str() {
-                Ok(val) => Some(String::from(val)),
-                Err(_) => None,
-            }).and_then(|val| {
-            match val.parse::<usize>() {
-                Ok(offset) => Some(offset),
-                Err(_) => None
-            }
-        });
-    if offset.is_none() {
-        return Ok(conflict_response);
-    }
-    if let Some(file_id) = request.match_info().get("file_id") {
-        let offset = storage.add_bytes(file_id, offset.unwrap(), bytes.bytes()).await?;
-        Ok(HttpResponseBuilder::new(StatusCode::NO_CONTENT)
-            .set_header("Upload-Offset", offset.to_string())
-            .body(""))
-    } else {
-        Ok(HttpResponseBuilder::new(StatusCode::NOT_FOUND).body(""))
-    }
-}
diff --git a/src/storages/file_storage.rs b/src/storages/file_storage.rs
index 80aee961fea71882f8d5ffbe0fe54e12f2dca82d..1f6abeb63b2a8c7626e77c4c6ce4e303079a81ab 100644
--- a/src/storages/file_storage.rs
+++ b/src/storages/file_storage.rs
@@ -1,7 +1,8 @@
 use std::collections::HashMap;
+use std::path::PathBuf;
 
 use actix_files::NamedFile;
-use async_std::fs::{DirBuilder, OpenOptions, read_to_string};
+use async_std::fs::{DirBuilder, OpenOptions, read_to_string, remove_file};
 use async_std::prelude::*;
 use async_trait::async_trait;
 use log::error;
@@ -20,6 +21,14 @@ impl FileStorage {
     pub fn new(app_conf: TuserConf) -> FileStorage {
         FileStorage { app_conf }
     }
+
+    pub fn info_file_path(&self, file_id: &str) -> PathBuf {
+        self.app_conf.data.join(format!("{}.info", file_id))
+    }
+
+    pub fn data_file_path(&self, file_id: &str) -> PathBuf {
+        self.app_conf.data.join(file_id.to_string())
+    }
 }
 
 #[async_trait]
@@ -35,8 +44,11 @@ impl Storage for FileStorage {
     }
 
     async fn get_file_info(&self, file_id: &str) -> TuserResult<FileInfo> {
-        let info_file_path = self.app_conf.data.join(format!("{}.info", file_id));
-        let contents = read_to_string(info_file_path).await.map_err(|err| {
+        let info_path = self.info_file_path(file_id);
+        if !info_path.exists() {
+            return Err(TuserError::FileNotFound(String::from(file_id)));
+        }
+        let contents = read_to_string(info_path).await.map_err(|err| {
             error!("{:?}", err);
             TuserError::UnableToReadInfo
         })?;
@@ -44,11 +56,10 @@ impl Storage for FileStorage {
     }
 
     async fn set_file_info(&self, file_info: &FileInfo) -> TuserResult<()> {
-        let info_file_path = self.app_conf.data.join(format!("{}.info", file_info.id));
         let mut file = OpenOptions::new()
             .write(true)
             .create(true)
-            .open(info_file_path.as_path())
+            .open(self.info_file_path(file_info.id.as_str()).as_path())
             .await
             .map_err(|err| {
                 error!("{:?}", err);
@@ -58,7 +69,12 @@ impl Storage for FileStorage {
             .await
             .map_err(|err| {
                 error!("{:?}", err);
-                TuserError::UnableToWrite(info_file_path.as_path().display().to_string())
+                TuserError::UnableToWrite(
+                    self.info_file_path(file_info.id.as_str())
+                        .as_path()
+                        .display()
+                        .to_string(),
+                )
             })?;
         Ok(())
     }
@@ -67,8 +83,12 @@ impl Storage for FileStorage {
         Err(TuserError::FileNotFound(String::from(file_id)))
     }
 
-    async fn add_bytes(&self, file_id: &str, request_offset: usize, bytes: &[u8]) -> TuserResult<usize> {
-        let file_path = self.app_conf.data.join(file_id);
+    async fn add_bytes(
+        &self,
+        file_id: &str,
+        request_offset: usize,
+        bytes: &[u8],
+    ) -> TuserResult<usize> {
         let mut info = self.get_file_info(file_id).await?;
         if info.offset != request_offset {
             return Err(TuserError::WrongOffset);
@@ -77,7 +97,7 @@ impl Storage for FileStorage {
             .write(true)
             .append(true)
             .create(false)
-            .open(file_path.as_path())
+            .open(self.data_file_path(file_id))
             .await
             .map_err(|err| {
                 error!("{:?}", err);
@@ -85,7 +105,7 @@ impl Storage for FileStorage {
             })?;
         file.write_all(bytes).await.map_err(|err| {
             error!("{:?}", err);
-            TuserError::UnableToWrite(file_path.as_path().display().to_string())
+            TuserError::UnableToWrite(self.data_file_path(file_id).as_path().display().to_string())
         })?;
         info.offset += bytes.len();
         self.set_file_info(&info).await?;
@@ -98,12 +118,12 @@ impl Storage for FileStorage {
         metadata: Option<HashMap<String, String>>,
     ) -> TuserResult<String> {
         let file_id = Uuid::new_v4().simple().to_string();
-        let file_path = self.app_conf.data.join(file_id.as_str());
+
         let mut file = OpenOptions::new()
             .write(true)
             .create(true)
             .create_new(true)
-            .open(file_path.as_path())
+            .open(self.data_file_path(file_id.as_str()).as_path())
             .await
             .map_err(|err| {
                 error!("{:?}", err);
@@ -113,13 +133,21 @@ impl Storage for FileStorage {
         // We write empty file here.
         file.write_all(b"").await.map_err(|err| {
             error!("{:?}", err);
-            TuserError::UnableToWrite(file_path.as_path().display().to_string())
+            TuserError::UnableToWrite(
+                self.data_file_path(file_id.as_str())
+                    .as_path()
+                    .display()
+                    .to_string(),
+            )
         })?;
 
         let file_info = FileInfo::new(
             file_id.as_str(),
             file_size,
-            file_path.as_path().display().to_string(),
+            self.data_file_path(file_id.as_str())
+                .as_path()
+                .display()
+                .to_string(),
             metadata,
         );
 
@@ -127,4 +155,24 @@ impl Storage for FileStorage {
 
         Ok(file_id)
     }
+
+    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)));
+        }
+        let data_path = self.data_file_path(file_id);
+        if !data_path.exists() {
+            return Err(TuserError::FileNotFound(String::from(file_id)));
+        }
+        remove_file(info_path).await.map_err(|err| {
+            error!("{:?}", err);
+            TuserError::UnableToRemove(String::from(file_id))
+        })?;
+        remove_file(data_path).await.map_err(|err| {
+            error!("{:?}", err);
+            TuserError::UnableToRemove(String::from(file_id))
+        })?;
+        Ok(())
+    }
 }
diff --git a/src/storages/mod.rs b/src/storages/mod.rs
index dd641fa15df6cfe8cb6029c7f3577eece3bb7721..613d77f47a5b07d21f11729cd7d609ff317fbbc9 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};
 
@@ -23,7 +23,7 @@ pub enum AvailableStores {
 impl FromStr for AvailableStores {
     type Err = String;
 
-    /// This function converts string to the AvailableStore item.
+    /// This function converts string to the `AvailableStore` item.
     /// This function is used by structopt to parse CLI parameters.
     ///
     /// # Params
@@ -37,13 +37,14 @@ impl FromStr for AvailableStores {
 }
 
 impl AvailableStores {
-    /// Convert AvailableStores to the Storage.
+    /// Convert `AvailableStores` to the Storage.
     ///
     /// # Params
     /// `config` - Tuser configuration.
     ///
-    pub fn get_storage(&self, config: TuserConf) -> impl Storage {
-        file_storage::FileStorage::new(config)
+    #[allow(clippy::unused_self)]
+    pub fn get_storage(&self, config: &TuserConf) -> impl Storage {
+        file_storage::FileStorage::new(config.clone())
     }
 }
 
@@ -62,7 +63,7 @@ pub struct FileInfo {
 }
 
 impl FileInfo {
-    /// Create new FileInfo
+    /// Creates new `FileInfo`.
     ///
     /// # Params
     ///
@@ -141,11 +142,15 @@ pub trait Storage: Clone {
     /// It returns new offset.
     ///
     /// # Params
-    ///
     /// `file_id` - unique file identifier;
     /// `request_offset` - offset from the client.
     /// `bytes` - bytes to append to the file.
-    async fn add_bytes(&self, file_id: &str, request_offset: usize, bytes: &[u8]) -> TuserResult<usize>;
+    async fn add_bytes(
+        &self,
+        file_id: &str,
+        request_offset: usize,
+        bytes: &[u8],
+    ) -> TuserResult<usize>;
 
     /// Create file in storage.
     ///
@@ -159,4 +164,13 @@ pub trait Storage: Clone {
         file_size: Option<usize>,
         metadata: Option<HashMap<String, String>>,
     ) -> TuserResult<String>;
+
+    /// Remove file from storage
+    ///
+    /// This method removes file and all associated
+    /// object if any.
+    ///
+    /// # Params
+    /// `file_id` - unique file identifier;
+    async fn remove_file(&self, file_id: &str) -> TuserResult<()>;
 }