mirror of
https://github.com/amigan/rustypaste-pretty.git
synced 2025-01-31 13:02:43 -05:00
parent
51d6fdd0d8
commit
c48e45d68c
7 changed files with 439 additions and 849 deletions
1186
Cargo.lock
generated
1186
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -13,9 +13,10 @@ categories = ["web-programming::http-server"]
|
||||||
include = ["src/**/*", "Cargo.*", "LICENSE", "README.md", "CHANGELOG.md"]
|
include = ["src/**/*", "Cargo.*", "LICENSE", "README.md", "CHANGELOG.md"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-web = { version = "3.3.3", features = ["rustls"] }
|
actix-web = { version = "4.0.1", features = ["rustls"] }
|
||||||
actix-multipart = "0.3.0"
|
actix-multipart = "0.4.0"
|
||||||
actix-files = "0.5.0"
|
actix-files = "0.6.0"
|
||||||
|
awc = { version = "3.0.0", features = ["rustls"] }
|
||||||
env_logger = "0.9.0"
|
env_logger = "0.9.0"
|
||||||
log = "0.4.14"
|
log = "0.4.14"
|
||||||
serde = "1.0.136"
|
serde = "1.0.136"
|
||||||
|
@ -48,7 +49,7 @@ version = "0.7.0"
|
||||||
default-features = false
|
default-features = false
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = "1.1.1"
|
actix-rt = "2.7.0"
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
opt-level = 0
|
opt-level = 0
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use actix_web::http::header::AUTHORIZATION;
|
use actix_web::http::header::{HeaderMap, AUTHORIZATION};
|
||||||
use actix_web::http::HeaderMap;
|
|
||||||
use actix_web::{error, Error};
|
use actix_web::{error, Error};
|
||||||
|
|
||||||
/// Checks the authorization header for the specified token.
|
/// Checks the authorization header for the specified token.
|
||||||
|
@ -28,7 +27,7 @@ pub fn check(host: &str, headers: &HeaderMap, token: Option<String>) -> Result<(
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use actix_web::http::HeaderValue;
|
use actix_web::http::header::HeaderValue;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_check_auth() -> Result<(), Error> {
|
fn test_check_auth() -> Result<(), Error> {
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
use crate::util;
|
use crate::util;
|
||||||
use actix_web::http::header::{
|
use actix_web::http::header::{
|
||||||
ContentDisposition as ActixContentDisposition, DispositionParam, DispositionType,
|
ContentDisposition as ActixContentDisposition, DispositionParam, DispositionType, HeaderMap,
|
||||||
};
|
};
|
||||||
use actix_web::http::HeaderMap;
|
|
||||||
use actix_web::{error, Error as ActixError};
|
use actix_web::{error, Error as ActixError};
|
||||||
use std::convert::TryFrom;
|
|
||||||
|
|
||||||
/// Custom HTTP header for expiry dates.
|
/// Custom HTTP header for expiry dates.
|
||||||
pub const EXPIRE: &str = "expire";
|
pub const EXPIRE: &str = "expire";
|
||||||
|
@ -30,12 +28,10 @@ pub struct ContentDisposition {
|
||||||
inner: ActixContentDisposition,
|
inner: ActixContentDisposition,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<Option<ActixContentDisposition>> for ContentDisposition {
|
impl From<ActixContentDisposition> for ContentDisposition {
|
||||||
type Error = ActixError;
|
fn from(content_disposition: ActixContentDisposition) -> Self {
|
||||||
fn try_from(content_disposition: Option<ActixContentDisposition>) -> Result<Self, Self::Error> {
|
Self {
|
||||||
match content_disposition {
|
inner: content_disposition,
|
||||||
Some(inner) => Ok(Self { inner }),
|
|
||||||
None => Err(error::ErrorBadRequest("content disposition does not exist")),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,31 +62,29 @@ impl ContentDisposition {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use actix_web::http::{HeaderName, HeaderValue};
|
use actix_web::http::header::{HeaderName, HeaderValue};
|
||||||
use std::thread;
|
use std::thread;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_content_disposition() -> Result<(), ActixError> {
|
fn test_content_disposition() -> Result<(), ActixError> {
|
||||||
assert!(ContentDisposition::try_from(None).is_err());
|
let actix_content_disposition = ActixContentDisposition {
|
||||||
|
|
||||||
let actix_content_disposition = Some(ActixContentDisposition {
|
|
||||||
disposition: DispositionType::FormData,
|
disposition: DispositionType::FormData,
|
||||||
parameters: vec![
|
parameters: vec![
|
||||||
DispositionParam::Name(String::from("file")),
|
DispositionParam::Name(String::from("file")),
|
||||||
DispositionParam::Filename(String::from("x.txt")),
|
DispositionParam::Filename(String::from("x.txt")),
|
||||||
],
|
],
|
||||||
});
|
};
|
||||||
let content_disposition = ContentDisposition::try_from(actix_content_disposition)?;
|
let content_disposition = ContentDisposition::from(actix_content_disposition);
|
||||||
assert!(content_disposition.has_form_field("file"));
|
assert!(content_disposition.has_form_field("file"));
|
||||||
assert!(!content_disposition.has_form_field("test"));
|
assert!(!content_disposition.has_form_field("test"));
|
||||||
assert_eq!("x.txt", content_disposition.get_file_name()?);
|
assert_eq!("x.txt", content_disposition.get_file_name()?);
|
||||||
|
|
||||||
let actix_content_disposition = Some(ActixContentDisposition {
|
let actix_content_disposition = ActixContentDisposition {
|
||||||
disposition: DispositionType::Attachment,
|
disposition: DispositionType::Attachment,
|
||||||
parameters: vec![DispositionParam::Name(String::from("file"))],
|
parameters: vec![DispositionParam::Name(String::from("file"))],
|
||||||
});
|
};
|
||||||
let content_disposition = ContentDisposition::try_from(actix_content_disposition)?;
|
let content_disposition = ContentDisposition::from(actix_content_disposition);
|
||||||
assert!(!content_disposition.has_form_field("file"));
|
assert!(!content_disposition.has_form_field("file"));
|
||||||
assert!(content_disposition.get_file_name().is_err());
|
assert!(content_disposition.get_file_name().is_err());
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
15
src/main.rs
15
src/main.rs
|
@ -1,6 +1,7 @@
|
||||||
use actix_web::client::ClientBuilder;
|
|
||||||
use actix_web::middleware::Logger;
|
use actix_web::middleware::Logger;
|
||||||
|
use actix_web::web::Data;
|
||||||
use actix_web::{App, HttpServer};
|
use actix_web::{App, HttpServer};
|
||||||
|
use awc::ClientBuilder;
|
||||||
use hotwatch::{Event, Hotwatch};
|
use hotwatch::{Event, Hotwatch};
|
||||||
use rustypaste::config::Config;
|
use rustypaste::config::Config;
|
||||||
use rustypaste::paste::PasteType;
|
use rustypaste::paste::PasteType;
|
||||||
|
@ -9,7 +10,7 @@ use std::env;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::io::Result as IoResult;
|
use std::io::Result as IoResult;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::RwLock;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
/// Environment variable for setting the configuration file path.
|
/// Environment variable for setting the configuration file path.
|
||||||
|
@ -49,8 +50,8 @@ async fn main() -> IoResult<()> {
|
||||||
.expect("failed to initialize configuration file watcher");
|
.expect("failed to initialize configuration file watcher");
|
||||||
|
|
||||||
// Hot-reload the configuration file.
|
// Hot-reload the configuration file.
|
||||||
let config = Arc::new(RwLock::new(config));
|
let config = Data::new(RwLock::new(config));
|
||||||
let cloned_config = Arc::clone(&config);
|
let cloned_config = Data::clone(&config);
|
||||||
let config_watcher = move |event: Event| {
|
let config_watcher = move |event: Event| {
|
||||||
if let Event::Write(path) = event {
|
if let Event::Write(path) = event {
|
||||||
match Config::parse(&path) {
|
match Config::parse(&path) {
|
||||||
|
@ -75,7 +76,7 @@ async fn main() -> IoResult<()> {
|
||||||
|
|
||||||
// Create a HTTP server.
|
// Create a HTTP server.
|
||||||
let mut http_server = HttpServer::new(move || {
|
let mut http_server = HttpServer::new(move || {
|
||||||
let http_client = ClientBuilder::default()
|
let http_client = ClientBuilder::new()
|
||||||
.timeout(
|
.timeout(
|
||||||
server_config
|
server_config
|
||||||
.timeout
|
.timeout
|
||||||
|
@ -84,8 +85,8 @@ async fn main() -> IoResult<()> {
|
||||||
.disable_redirects()
|
.disable_redirects()
|
||||||
.finish();
|
.finish();
|
||||||
App::new()
|
App::new()
|
||||||
.data(Arc::clone(&config))
|
.app_data(Data::clone(&config))
|
||||||
.data(http_client)
|
.app_data(Data::new(http_client))
|
||||||
.wrap(Logger::default())
|
.wrap(Logger::default())
|
||||||
.configure(server::configure_routes)
|
.configure(server::configure_routes)
|
||||||
})
|
})
|
||||||
|
|
17
src/paste.rs
17
src/paste.rs
|
@ -2,8 +2,8 @@ use crate::config::Config;
|
||||||
use crate::file::Directory;
|
use crate::file::Directory;
|
||||||
use crate::header::ContentDisposition;
|
use crate::header::ContentDisposition;
|
||||||
use crate::util;
|
use crate::util;
|
||||||
use actix_web::client::Client;
|
|
||||||
use actix_web::{error, Error};
|
use actix_web::{error, Error};
|
||||||
|
use awc::Client;
|
||||||
use std::convert::{TryFrom, TryInto};
|
use std::convert::{TryFrom, TryInto};
|
||||||
use std::fs::{self, File};
|
use std::fs::{self, File};
|
||||||
use std::io::{Error as IoError, ErrorKind as IoErrorKind, Result as IoResult, Write};
|
use std::io::{Error as IoError, ErrorKind as IoErrorKind, Result as IoResult, Write};
|
||||||
|
@ -166,14 +166,23 @@ impl Paste {
|
||||||
.and_then(|segments| segments.last())
|
.and_then(|segments| segments.last())
|
||||||
.and_then(|name| if name.is_empty() { None } else { Some(name) })
|
.and_then(|name| if name.is_empty() { None } else { Some(name) })
|
||||||
.unwrap_or("file");
|
.unwrap_or("file");
|
||||||
let mut response = client.get(url.as_str()).send().await?;
|
let mut response = client
|
||||||
|
.get(url.as_str())
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.map_err(error::ErrorInternalServerError)?;
|
||||||
let payload_limit = config
|
let payload_limit = config
|
||||||
.server
|
.server
|
||||||
.max_content_length
|
.max_content_length
|
||||||
.get_bytes()
|
.get_bytes()
|
||||||
.try_into()
|
.try_into()
|
||||||
.map_err(error::ErrorInternalServerError)?;
|
.map_err(error::ErrorInternalServerError)?;
|
||||||
let bytes = response.body().limit(payload_limit).await?.to_vec();
|
let bytes = response
|
||||||
|
.body()
|
||||||
|
.limit(payload_limit)
|
||||||
|
.await
|
||||||
|
.map_err(error::ErrorInternalServerError)?
|
||||||
|
.to_vec();
|
||||||
let bytes_checksum = util::sha256_digest(&*bytes)?;
|
let bytes_checksum = util::sha256_digest(&*bytes)?;
|
||||||
self.data = bytes;
|
self.data = bytes;
|
||||||
if !config.paste.duplicate_files.unwrap_or(true) && expiry_date.is_none() {
|
if !config.paste.duplicate_files.unwrap_or(true) && expiry_date.is_none() {
|
||||||
|
@ -222,8 +231,8 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::random::{RandomURLConfig, RandomURLType};
|
use crate::random::{RandomURLConfig, RandomURLType};
|
||||||
use crate::util;
|
use crate::util;
|
||||||
use actix_web::client::Client;
|
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
|
use awc::Client;
|
||||||
use byte_unit::Byte;
|
use byte_unit::Byte;
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|
||||||
|
|
|
@ -7,20 +7,20 @@ use crate::paste::{Paste, PasteType};
|
||||||
use crate::util;
|
use crate::util;
|
||||||
use actix_files::NamedFile;
|
use actix_files::NamedFile;
|
||||||
use actix_multipart::Multipart;
|
use actix_multipart::Multipart;
|
||||||
use actix_web::client::Client;
|
|
||||||
use actix_web::{error, get, post, web, Error, HttpRequest, HttpResponse, Responder};
|
use actix_web::{error, get, post, web, Error, HttpRequest, HttpResponse, Responder};
|
||||||
|
use awc::Client;
|
||||||
use byte_unit::Byte;
|
use byte_unit::Byte;
|
||||||
use futures_util::stream::StreamExt;
|
use futures_util::stream::StreamExt;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::RwLock;
|
||||||
|
|
||||||
/// Shows the landing page.
|
/// Shows the landing page.
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
async fn index() -> impl Responder {
|
async fn index() -> impl Responder {
|
||||||
HttpResponse::Found()
|
HttpResponse::Found()
|
||||||
.header("Location", env!("CARGO_PKG_HOMEPAGE"))
|
.append_header(("Location", env!("CARGO_PKG_HOMEPAGE")))
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ async fn index() -> impl Responder {
|
||||||
async fn serve(
|
async fn serve(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
file: web::Path<String>,
|
file: web::Path<String>,
|
||||||
config: web::Data<Arc<RwLock<Config>>>,
|
config: web::Data<RwLock<Config>>,
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
let config = config
|
let config = config
|
||||||
.read()
|
.read()
|
||||||
|
@ -62,7 +62,7 @@ async fn serve(
|
||||||
.map_err(error::ErrorInternalServerError)?,
|
.map_err(error::ErrorInternalServerError)?,
|
||||||
)
|
)
|
||||||
.prefer_utf8(true)
|
.prefer_utf8(true)
|
||||||
.into_response(&request)?;
|
.into_response(&request);
|
||||||
if paste_type.is_oneshot() {
|
if paste_type.is_oneshot() {
|
||||||
fs::rename(
|
fs::rename(
|
||||||
&path,
|
&path,
|
||||||
|
@ -76,7 +76,7 @@ async fn serve(
|
||||||
Ok(response)
|
Ok(response)
|
||||||
}
|
}
|
||||||
PasteType::Url => Ok(HttpResponse::Found()
|
PasteType::Url => Ok(HttpResponse::Found()
|
||||||
.header("Location", fs::read_to_string(&path)?)
|
.append_header(("Location", fs::read_to_string(&path)?))
|
||||||
.finish()),
|
.finish()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,10 +87,10 @@ async fn upload(
|
||||||
request: HttpRequest,
|
request: HttpRequest,
|
||||||
mut payload: Multipart,
|
mut payload: Multipart,
|
||||||
client: web::Data<Client>,
|
client: web::Data<Client>,
|
||||||
config: web::Data<Arc<RwLock<Config>>>,
|
config: web::Data<RwLock<Config>>,
|
||||||
) -> Result<HttpResponse, Error> {
|
) -> Result<HttpResponse, Error> {
|
||||||
let connection = request.connection_info().clone();
|
let connection = request.connection_info().clone();
|
||||||
let host = connection.remote_addr().unwrap_or("unknown host");
|
let host = connection.peer_addr().unwrap_or("unknown host");
|
||||||
auth::check(
|
auth::check(
|
||||||
host,
|
host,
|
||||||
request.headers(),
|
request.headers(),
|
||||||
|
@ -106,7 +106,7 @@ async fn upload(
|
||||||
let mut urls: Vec<String> = Vec::new();
|
let mut urls: Vec<String> = Vec::new();
|
||||||
while let Some(item) = payload.next().await {
|
while let Some(item) = payload.next().await {
|
||||||
let mut field = item?;
|
let mut field = item?;
|
||||||
let content = ContentDisposition::try_from(field.content_disposition())?;
|
let content = ContentDisposition::from(field.content_disposition().clone());
|
||||||
if let Ok(paste_type) = PasteType::try_from(&content) {
|
if let Ok(paste_type) = PasteType::try_from(&content) {
|
||||||
let mut bytes = Vec::<u8>::new();
|
let mut bytes = Vec::<u8>::new();
|
||||||
while let Some(chunk) = field.next().await {
|
while let Some(chunk) = field.next().await {
|
||||||
|
@ -212,16 +212,18 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use actix_web::{http, test, App};
|
use actix_web::{http, test, App};
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_web::test]
|
||||||
async fn test_index() {
|
async fn test_index() {
|
||||||
let mut app = test::init_service(App::new().service(index)).await;
|
let mut app = test::init_service(App::new().service(index)).await;
|
||||||
let req = test::TestRequest::with_header("content-type", "text/plain").to_request();
|
let req = test::TestRequest::default()
|
||||||
|
.insert_header(("content-type", "text/plain"))
|
||||||
|
.to_request();
|
||||||
let resp = test::call_service(&mut app, req).await;
|
let resp = test::call_service(&mut app, req).await;
|
||||||
assert!(resp.status().is_redirection());
|
assert!(resp.status().is_redirection());
|
||||||
assert_eq!(http::StatusCode::FOUND, resp.status());
|
assert_eq!(http::StatusCode::FOUND, resp.status());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_web::test]
|
||||||
async fn test_serve() {
|
async fn test_serve() {
|
||||||
let mut app = test::init_service(App::new().service(serve)).await;
|
let mut app = test::init_service(App::new().service(serve)).await;
|
||||||
let req = test::TestRequest::default().to_request();
|
let req = test::TestRequest::default().to_request();
|
||||||
|
|
Loading…
Reference in a new issue