Skip to content

Commit a400489

Browse files
committed
add curl support
1 parent 4e55b07 commit a400489

File tree

4 files changed

+96
-53
lines changed

4 files changed

+96
-53
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "pastemp"
3-
version = "0.2.0"
3+
version = "0.4.0"
44
edition = "2021"
55
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
66

config.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
max_age = 172800
22
time_to_delete = 1800
3-
base_url = "http://localhost:8000"
3+
base_url = ""

src/config.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ pub struct Config {
1616
pub max_age: Duration,
1717
#[serde_as(as = "DurationSeconds<i64>")]
1818
pub time_to_delete: Duration,
19+
pub base_url: String,
1920
}
2021

2122
impl Config {

src/simple.rs

Lines changed: 93 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,26 @@ use std::{borrow::Borrow, fmt::Display, str};
22

33
use actix_multipart::Multipart;
44
use actix_web::{
5-
cookie::Cookie,
5+
cookie::{Cookie, CookieJar},
66
dev::HttpServiceFactory,
77
error::ErrorInternalServerError,
88
get,
9+
guard::GuardContext,
910
http::{
1011
header::{self, ContentDisposition, DispositionParam, DispositionType},
1112
StatusCode,
1213
},
13-
post,
14-
web::Data,
14+
post, routes,
15+
web::{Bytes, Data, Payload},
1516
HttpResponse, Responder, ResponseError, Result,
16-
routes
1717
};
1818
use actix_web_lab::extract::Path;
1919
use askama::Template;
2020
use askama_actix::TemplateToResponse;
2121
use chrono::{Duration, Utc};
2222
use futures::{future::ready, StreamExt, TryStreamExt};
23-
use mime_guess::mime::{APPLICATION_OCTET_STREAM, IMAGE};
23+
use futures_util::Stream;
24+
use mime_guess::mime::{self, APPLICATION_OCTET_STREAM, IMAGE};
2425
use rand::distributions::{Alphanumeric, DistString};
2526
use serde::Deserialize;
2627
use syntect::{html::ClassedHTMLGenerator, parsing::SyntaxSet, util::LinesWithEndings};
@@ -44,6 +45,7 @@ pub fn scope() -> impl HttpServiceFactory {
4445
download,
4546
get_ext,
4647
post_form,
48+
post_raw,
4749
index,
4850
redir_down,
4951
)
@@ -358,16 +360,12 @@ impl ResponseError for UploadError {
358360
}
359361
}
360362

361-
#[post("/")]
362-
async fn post_form(
363-
payload: Multipart,
364-
database: Data<DB>,
365-
Cookies(mut cookies): Cookies,
366-
config: Data<Config>,
367-
) -> Result<impl Responder> {
368-
let mut multipart = payload;
369-
let mut extension = None;
370-
363+
async fn create_file<E: ResponseError + 'static>(
364+
mut data: impl Stream<Item = Result<Bytes, E>> + Unpin,
365+
database: &Data<DB>,
366+
cookies: &mut CookieJar,
367+
config: &Data<Config>,
368+
) -> Result<String> {
371369
let owner = if let Some(owner) = cookies.get(OWNER_COOKIE) {
372370
owner.value().to_owned()
373371
} else {
@@ -381,9 +379,80 @@ async fn post_form(
381379
.await
382380
.map_err(ErrorInternalServerError)?;
383381

382+
const FILE_LIMIT: usize = 10_000_000;
383+
let mut limit: usize = FILE_LIMIT;
384+
while let Some(data) = data.try_next().await? {
385+
if let Some(l) = limit.checked_sub(data.len()) {
386+
limit = l;
387+
} else {
388+
return Err(UploadError::FieldTooBig("file", FILE_LIMIT).into());
389+
}
390+
file.append(&data).await.map_err(ErrorInternalServerError)?;
391+
}
392+
393+
if file
394+
.contents()
395+
.await
396+
.map_err(ErrorInternalServerError)?
397+
.is_empty()
398+
{
399+
file.delete().await.map_err(ErrorInternalServerError)?;
400+
return Err(UploadError::NoData.into());
401+
}
402+
403+
Ok(file.name().to_string())
404+
}
405+
406+
fn response(
407+
name: String,
408+
cookies: CookieJar,
409+
extension: Option<String>,
410+
config: &Data<Config>,
411+
) -> impl Responder {
412+
let name = name.clone() + &extension.map(|e| format!(".{e}")).unwrap_or_default();
413+
HttpResponse::Found()
414+
.append_header((header::LOCATION, name.clone()))
415+
.cookie_delta(&cookies)
416+
.body(format!(
417+
"{}{name}",
418+
if !config.base_url.is_empty() && !config.base_url.ends_with('/') {
419+
format!("{}{}", config.base_url, "/")
420+
} else {
421+
format!("{}", config.base_url)
422+
}
423+
))
424+
}
425+
426+
#[post("/")]
427+
async fn post_raw(
428+
payload: Payload,
429+
database: Data<DB>,
430+
Cookies(mut cookies): Cookies,
431+
config: Data<Config>,
432+
) -> Result<impl Responder> {
433+
create_file(payload, &database, &mut cookies, &config)
434+
.await
435+
.map(|it| response(it, cookies, None, &config))
436+
}
437+
438+
fn is_form(it: &GuardContext) -> bool {
439+
it.header::<header::ContentType>().map_or(false, |it| {
440+
it.0.type_() == mime::MULTIPART && it.0.subtype() == mime::FORM_DATA
441+
})
442+
}
443+
444+
#[post("/", guard = "is_form")]
445+
async fn post_form(
446+
payload: Multipart,
447+
database: Data<DB>,
448+
Cookies(mut cookies): Cookies,
449+
config: Data<Config>,
450+
) -> Result<impl Responder> {
451+
let mut multipart = payload;
452+
let mut extension = None;
453+
let mut file = None;
454+
384455
while let Some(mut field) = multipart.try_next().await? {
385-
const FILE_LIMIT: usize = 10_000_000;
386-
let mut limit: usize = FILE_LIMIT;
387456
match field.name() {
388457
"data" => {
389458
if let Some(file_name) = field.content_disposition().get_filename() {
@@ -398,15 +467,7 @@ async fn post_form(
398467
}
399468
}
400469
}
401-
while let Some(data) = field.try_next().await? {
402-
if let Some(l) = limit.checked_sub(data.len()) {
403-
limit = l;
404-
} else {
405-
field.for_each(|_| ready(())).await;
406-
return Err(UploadError::FieldTooBig("file", FILE_LIMIT).into());
407-
}
408-
file.append(&data).await.map_err(ErrorInternalServerError)?;
409-
}
470+
file = Some(create_file(field, &database, &mut cookies, &config).await?);
410471
}
411472
"extension" => {
412473
let mut buf = String::new();
@@ -424,34 +485,15 @@ async fn post_form(
424485
}
425486
name => {
426487
let name = name.to_string();
427-
field.for_each(|_| ready(())).await;
428-
multipart
429-
.for_each(|field| async {
430-
if let Ok(field) = field {
431-
field.for_each(|_| ready(())).await;
432-
}
433-
})
434-
.await;
435488
return Err(UploadError::InvalidField(name).into());
436489
}
437490
}
438491
}
439492

440-
if file
441-
.contents()
442-
.await
443-
.map_err(ErrorInternalServerError)?
444-
.is_empty()
445-
{
446-
file.delete().await.map_err(ErrorInternalServerError)?;
447-
return Err(UploadError::NoData.into());
448-
}
449-
450-
Ok(HttpResponse::Found()
451-
.append_header((
452-
header::LOCATION,
453-
file.name().to_string() + &extension.map(|e| format!(".{e}")).unwrap_or_default(),
454-
))
455-
.cookie_delta(&cookies)
456-
.finish())
493+
Ok(response(
494+
file.ok_or_else(|| UploadError::NoData)?,
495+
cookies,
496+
extension,
497+
&config
498+
))
457499
}

0 commit comments

Comments
 (0)