mirror of
https://github.com/amigan/rustypaste-pretty.git
synced 2025-01-31 04:52:37 -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"]
|
||||
|
||||
[dependencies]
|
||||
actix-web = { version = "3.3.3", features = ["rustls"] }
|
||||
actix-multipart = "0.3.0"
|
||||
actix-files = "0.5.0"
|
||||
actix-web = { version = "4.0.1", features = ["rustls"] }
|
||||
actix-multipart = "0.4.0"
|
||||
actix-files = "0.6.0"
|
||||
awc = { version = "3.0.0", features = ["rustls"] }
|
||||
env_logger = "0.9.0"
|
||||
log = "0.4.14"
|
||||
serde = "1.0.136"
|
||||
|
@ -48,7 +49,7 @@ version = "0.7.0"
|
|||
default-features = false
|
||||
|
||||
[dev-dependencies]
|
||||
actix-rt = "1.1.1"
|
||||
actix-rt = "2.7.0"
|
||||
|
||||
[profile.dev]
|
||||
opt-level = 0
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use actix_web::http::header::AUTHORIZATION;
|
||||
use actix_web::http::HeaderMap;
|
||||
use actix_web::http::header::{HeaderMap, AUTHORIZATION};
|
||||
use actix_web::{error, Error};
|
||||
|
||||
/// 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)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use actix_web::http::HeaderValue;
|
||||
use actix_web::http::header::HeaderValue;
|
||||
|
||||
#[test]
|
||||
fn test_check_auth() -> Result<(), Error> {
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
use crate::util;
|
||||
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 std::convert::TryFrom;
|
||||
|
||||
/// Custom HTTP header for expiry dates.
|
||||
pub const EXPIRE: &str = "expire";
|
||||
|
@ -30,12 +28,10 @@ pub struct ContentDisposition {
|
|||
inner: ActixContentDisposition,
|
||||
}
|
||||
|
||||
impl TryFrom<Option<ActixContentDisposition>> for ContentDisposition {
|
||||
type Error = ActixError;
|
||||
fn try_from(content_disposition: Option<ActixContentDisposition>) -> Result<Self, Self::Error> {
|
||||
match content_disposition {
|
||||
Some(inner) => Ok(Self { inner }),
|
||||
None => Err(error::ErrorBadRequest("content disposition does not exist")),
|
||||
impl From<ActixContentDisposition> for ContentDisposition {
|
||||
fn from(content_disposition: ActixContentDisposition) -> Self {
|
||||
Self {
|
||||
inner: content_disposition,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -66,31 +62,29 @@ impl ContentDisposition {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use actix_web::http::{HeaderName, HeaderValue};
|
||||
use actix_web::http::header::{HeaderName, HeaderValue};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
#[test]
|
||||
fn test_content_disposition() -> Result<(), ActixError> {
|
||||
assert!(ContentDisposition::try_from(None).is_err());
|
||||
|
||||
let actix_content_disposition = Some(ActixContentDisposition {
|
||||
let actix_content_disposition = ActixContentDisposition {
|
||||
disposition: DispositionType::FormData,
|
||||
parameters: vec![
|
||||
DispositionParam::Name(String::from("file")),
|
||||
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("test"));
|
||||
assert_eq!("x.txt", content_disposition.get_file_name()?);
|
||||
|
||||
let actix_content_disposition = Some(ActixContentDisposition {
|
||||
let actix_content_disposition = ActixContentDisposition {
|
||||
disposition: DispositionType::Attachment,
|
||||
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.get_file_name().is_err());
|
||||
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::web::Data;
|
||||
use actix_web::{App, HttpServer};
|
||||
use awc::ClientBuilder;
|
||||
use hotwatch::{Event, Hotwatch};
|
||||
use rustypaste::config::Config;
|
||||
use rustypaste::paste::PasteType;
|
||||
|
@ -9,7 +10,7 @@ use std::env;
|
|||
use std::fs;
|
||||
use std::io::Result as IoResult;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::sync::RwLock;
|
||||
use std::time::Duration;
|
||||
|
||||
/// Environment variable for setting the configuration file path.
|
||||
|
@ -49,8 +50,8 @@ async fn main() -> IoResult<()> {
|
|||
.expect("failed to initialize configuration file watcher");
|
||||
|
||||
// Hot-reload the configuration file.
|
||||
let config = Arc::new(RwLock::new(config));
|
||||
let cloned_config = Arc::clone(&config);
|
||||
let config = Data::new(RwLock::new(config));
|
||||
let cloned_config = Data::clone(&config);
|
||||
let config_watcher = move |event: Event| {
|
||||
if let Event::Write(path) = event {
|
||||
match Config::parse(&path) {
|
||||
|
@ -75,7 +76,7 @@ async fn main() -> IoResult<()> {
|
|||
|
||||
// Create a HTTP server.
|
||||
let mut http_server = HttpServer::new(move || {
|
||||
let http_client = ClientBuilder::default()
|
||||
let http_client = ClientBuilder::new()
|
||||
.timeout(
|
||||
server_config
|
||||
.timeout
|
||||
|
@ -84,8 +85,8 @@ async fn main() -> IoResult<()> {
|
|||
.disable_redirects()
|
||||
.finish();
|
||||
App::new()
|
||||
.data(Arc::clone(&config))
|
||||
.data(http_client)
|
||||
.app_data(Data::clone(&config))
|
||||
.app_data(Data::new(http_client))
|
||||
.wrap(Logger::default())
|
||||
.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::header::ContentDisposition;
|
||||
use crate::util;
|
||||
use actix_web::client::Client;
|
||||
use actix_web::{error, Error};
|
||||
use awc::Client;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::fs::{self, File};
|
||||
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(|name| if name.is_empty() { None } else { Some(name) })
|
||||
.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
|
||||
.server
|
||||
.max_content_length
|
||||
.get_bytes()
|
||||
.try_into()
|
||||
.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)?;
|
||||
self.data = bytes;
|
||||
if !config.paste.duplicate_files.unwrap_or(true) && expiry_date.is_none() {
|
||||
|
@ -222,8 +231,8 @@ mod tests {
|
|||
use super::*;
|
||||
use crate::random::{RandomURLConfig, RandomURLType};
|
||||
use crate::util;
|
||||
use actix_web::client::Client;
|
||||
use actix_web::web::Data;
|
||||
use awc::Client;
|
||||
use byte_unit::Byte;
|
||||
use std::env;
|
||||
|
||||
|
|
|
@ -7,20 +7,20 @@ use crate::paste::{Paste, PasteType};
|
|||
use crate::util;
|
||||
use actix_files::NamedFile;
|
||||
use actix_multipart::Multipart;
|
||||
use actix_web::client::Client;
|
||||
use actix_web::{error, get, post, web, Error, HttpRequest, HttpResponse, Responder};
|
||||
use awc::Client;
|
||||
use byte_unit::Byte;
|
||||
use futures_util::stream::StreamExt;
|
||||
use std::convert::TryFrom;
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::sync::RwLock;
|
||||
|
||||
/// Shows the landing page.
|
||||
#[get("/")]
|
||||
async fn index() -> impl Responder {
|
||||
HttpResponse::Found()
|
||||
.header("Location", env!("CARGO_PKG_HOMEPAGE"))
|
||||
.append_header(("Location", env!("CARGO_PKG_HOMEPAGE")))
|
||||
.finish()
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,7 @@ async fn index() -> impl Responder {
|
|||
async fn serve(
|
||||
request: HttpRequest,
|
||||
file: web::Path<String>,
|
||||
config: web::Data<Arc<RwLock<Config>>>,
|
||||
config: web::Data<RwLock<Config>>,
|
||||
) -> Result<HttpResponse, Error> {
|
||||
let config = config
|
||||
.read()
|
||||
|
@ -62,7 +62,7 @@ async fn serve(
|
|||
.map_err(error::ErrorInternalServerError)?,
|
||||
)
|
||||
.prefer_utf8(true)
|
||||
.into_response(&request)?;
|
||||
.into_response(&request);
|
||||
if paste_type.is_oneshot() {
|
||||
fs::rename(
|
||||
&path,
|
||||
|
@ -76,7 +76,7 @@ async fn serve(
|
|||
Ok(response)
|
||||
}
|
||||
PasteType::Url => Ok(HttpResponse::Found()
|
||||
.header("Location", fs::read_to_string(&path)?)
|
||||
.append_header(("Location", fs::read_to_string(&path)?))
|
||||
.finish()),
|
||||
}
|
||||
}
|
||||
|
@ -87,10 +87,10 @@ async fn upload(
|
|||
request: HttpRequest,
|
||||
mut payload: Multipart,
|
||||
client: web::Data<Client>,
|
||||
config: web::Data<Arc<RwLock<Config>>>,
|
||||
config: web::Data<RwLock<Config>>,
|
||||
) -> Result<HttpResponse, Error> {
|
||||
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(
|
||||
host,
|
||||
request.headers(),
|
||||
|
@ -106,7 +106,7 @@ async fn upload(
|
|||
let mut urls: Vec<String> = Vec::new();
|
||||
while let Some(item) = payload.next().await {
|
||||
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) {
|
||||
let mut bytes = Vec::<u8>::new();
|
||||
while let Some(chunk) = field.next().await {
|
||||
|
@ -212,16 +212,18 @@ mod tests {
|
|||
use super::*;
|
||||
use actix_web::{http, test, App};
|
||||
|
||||
#[actix_rt::test]
|
||||
#[actix_web::test]
|
||||
async fn test_index() {
|
||||
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;
|
||||
assert!(resp.status().is_redirection());
|
||||
assert_eq!(http::StatusCode::FOUND, resp.status());
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
#[actix_web::test]
|
||||
async fn test_serve() {
|
||||
let mut app = test::init_service(App::new().service(serve)).await;
|
||||
let req = test::TestRequest::default().to_request();
|
||||
|
|
Loading…
Reference in a new issue