diff --git a/Cargo.lock b/Cargo.lock index 3b145cc..76e578c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -303,6 +303,12 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "http-range-header" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bfe8eed0a9285ef776bb792479ea3834e8b94e13d615c2f66d03dd50a435a29" + [[package]] name = "httparse" version = "1.8.0" @@ -379,6 +385,7 @@ dependencies = [ "axum", "magick_rust", "tokio", + "tower-http", ] [[package]] @@ -748,6 +755,24 @@ dependencies = [ "tracing", ] +[[package]] +name = "tower-http" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8bd22a874a2d0b70452d5597b12c537331d49060824a95f49f108994f94aa4c" +dependencies = [ + "bitflags 2.3.3", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-range-header", + "pin-project-lite", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" version = "0.3.2" diff --git a/Cargo.toml b/Cargo.toml index 7455334..de4e3b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,9 +3,8 @@ name = "liquid-rescale-api-v2" version = "0.1.0" edition = "2021" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] axum = { version = "0.6", features = ["multipart", "macros"] } tokio = { version = "1.29.1", features = ["macros", "rt-multi-thread"] } +tower-http = { version = "0.4.1", features = ["limit"] } magick_rust = "0.19.0" diff --git a/src/main.rs b/src/main.rs index 63fa88a..63c7fe6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,18 @@ -use std::{env::var, io, net::SocketAddr}; +use std::{env::var, io, net::SocketAddr, sync::Once}; -use axum::{routing::get, Router}; +use axum::{ + body::{Bytes, Full}, + extract::{DefaultBodyLimit, Multipart}, + http::StatusCode, + response::{IntoResponse, Response}, + routing::get, + Router, +}; + +use magick_rust::{magick_wand_genesis, MagickWand}; +use tower_http::limit::RequestBodyLimitLayer; + +static START: Once = Once::new(); #[tokio::main] async fn main() -> io::Result<()> { @@ -14,6 +26,8 @@ async fn main() -> io::Result<()> { .serve( Router::new() .route("/", get(handle_request)) + .layer(DefaultBodyLimit::disable()) + .layer(RequestBodyLimitLayer::new(10 * 1024 * 1024 /* 10mb */)) .into_make_service(), ) .await @@ -22,6 +36,33 @@ async fn main() -> io::Result<()> { Ok(()) } -async fn handle_request() -> &'static str { - "Hello world!" +async fn handle_request(mut payload: Multipart) -> Result>, Response> { + let mut data: Vec = Vec::new(); + + while let Some(field) = payload.next_field().await.unwrap() { + let content_type = field.content_type().unwrap(); + + match content_type { + "image/jpeg" | "image/png" => data = field.bytes().await.unwrap().to_vec(), + _ => return Err(StatusCode::UNSUPPORTED_MEDIA_TYPE.into_response()), + } + } + + START.call_once(|| magick_wand_genesis()); + + let wand = MagickWand::new(); + + wand.read_image_blob(&data).unwrap(); + wand.fit(640, 640); + wand.liquid_rescale_image(320, 320, 1.0, 0.0).unwrap(); + wand.fit(640, 640); + + let bytes = wand.write_image_blob("JPEG").unwrap(); + let body = Full::from(bytes); + + Ok(Response::builder() + .status(StatusCode::OK) + .header("Content-Type", "image/jpeg") + .body(Full::from(body)) + .unwrap()) }