diff options
Diffstat (limited to 'src/media.rs')
-rw-r--r-- | src/media.rs | 137 |
1 files changed, 73 insertions, 64 deletions
diff --git a/src/media.rs b/src/media.rs index 89d70e7..47eb53e 100644 --- a/src/media.rs +++ b/src/media.rs @@ -1,6 +1,6 @@ -use actix_web::error::{ErrorBadRequest, ErrorNotFound, ErrorInternalServerError}; +use actix_web::error::{ErrorBadRequest, ErrorInternalServerError, ErrorNotFound}; use actix_web::http::header; -use actix_web::{web, Error, HttpRequest, HttpResponse}; +use actix_web::{get, head, web, Error, HttpRequest, HttpResponse}; use image::imageops::FilterType; use image::GenericImageView; @@ -9,57 +9,63 @@ use image::ImageFormat; use futures::TryFutureExt; use tokio::io::AsyncReadExt; -use rusoto_s3::{HeadObjectRequest, GetObjectRequest, S3Client, S3}; +use rusoto_s3::{GetObjectRequest, HeadObjectRequest, S3Client, S3}; use crate::SiteConfig; /// Build an HttpResponse for an AWS response macro_rules! response_for { - ($resp:expr) => { - { - let mut client_resp = HttpResponse::Ok(); - - // This will be the default cache-control header if the object doesn't have its own. - client_resp.set(header::CacheControl(vec![header::CacheDirective::MaxAge( - 31557600u32, - )])); - - // Allow CORS - client_resp.set_header(header::ACCESS_CONTROL_ALLOW_ORIGIN, "*"); - - // Copy all of the relevant S3 headers. - $resp.cache_control.map(|v| client_resp.set_header(header::CACHE_CONTROL, v)); - $resp.content_disposition.map(|v| client_resp.set_header(header::CONTENT_DISPOSITION, v)); - $resp.content_encoding.map(|v| client_resp.set_header(header::CONTENT_ENCODING, v)); - $resp.content_language.map(|v| client_resp.set_header(header::CONTENT_LANGUAGE, v)); - $resp.content_type.map(|v| client_resp.set_header(header::CONTENT_TYPE, v)); - $resp.e_tag.map(|v| client_resp.set_header(header::ETAG, v)); - $resp.last_modified.map(|v| client_resp.set_header(header::LAST_MODIFIED, v)); - - client_resp - } - }; + ($resp:expr) => {{ + let mut client_resp = HttpResponse::Ok(); + + // This will be the default cache-control header if the object doesn't have its own. + client_resp.insert_header(header::CacheControl(vec![header::CacheDirective::MaxAge( + 31557600u32, + )])); + + // Allow CORS + client_resp.insert_header((header::ACCESS_CONTROL_ALLOW_ORIGIN, "*")); + + // Copy all of the relevant S3 headers. + $resp + .cache_control + .map(|v| client_resp.insert_header((header::CACHE_CONTROL, v))); + $resp + .content_disposition + .map(|v| client_resp.insert_header((header::CONTENT_DISPOSITION, v))); + $resp + .content_encoding + .map(|v| client_resp.insert_header((header::CONTENT_ENCODING, v))); + $resp + .content_language + .map(|v| client_resp.insert_header((header::CONTENT_LANGUAGE, v))); + $resp + .content_type + .map(|v| client_resp.insert_header((header::CONTENT_TYPE, v))); + $resp + .e_tag + .map(|v| client_resp.insert_header((header::ETAG, v))); + $resp + .last_modified + .map(|v| client_resp.insert_header((header::LAST_MODIFIED, v))); + + client_resp + }}; } pub fn configure(cfg: &mut web::ServiceConfig) { - cfg.service( - web::resource("/media/photo/{width:\\d+}x{height:\\d+}/{filename}") - .route(web::get().to(serve_photo)), - ); - cfg.service( - web::resource("/media/{type}/{filename:.+}") - .route(web::get().to(serve_file)) - .route(web::head().to(head_file)), - ); + cfg.service(serve_photo) + .service(serve_file) + .service(head_file); } +#[head("/media/{type}/{filename:.+}")] async fn head_file( req: HttpRequest, config: web::Data<SiteConfig>, s3_client: web::Data<S3Client>, ) -> Result<HttpResponse, Error> { - - // Get the path paramaters + // Get the path parameters let media_type = req .match_info() .get("type") @@ -71,27 +77,27 @@ async fn head_file( // Construct an S3 key let key = format!("{}/{}", media_type, filename); - let resp = s3_client.head_object(HeadObjectRequest { - bucket: config.s3_bucket().to_owned(), - key, - ..Default::default() - }) - .map_err(|e| ErrorInternalServerError(e)) - .await?; + let resp = s3_client + .head_object(HeadObjectRequest { + bucket: config.s3_bucket().to_owned(), + key, + ..Default::default() + }) + .map_err(|e| ErrorInternalServerError(e)) + .await?; let mut client_resp = response_for!(resp); // TODO: trick actix into returning the content-length. Ok(client_resp.finish()) } - +#[get("/media/{type}/{filename:.+}")] async fn serve_file( req: HttpRequest, config: web::Data<SiteConfig>, s3_client: web::Data<S3Client>, ) -> Result<HttpResponse, Error> { - - // Get the path paramaters + // Get the path parameters let media_type = req .match_info() .get("type") @@ -103,13 +109,14 @@ async fn serve_file( // Construct an S3 key let key = format!("{}/{}", media_type, filename); - let resp = s3_client.get_object(GetObjectRequest { - bucket: config.s3_bucket().to_owned(), - key, - ..Default::default() - }) - .map_err(|e| ErrorInternalServerError(e)) - .await?; + let resp = s3_client + .get_object(GetObjectRequest { + bucket: config.s3_bucket().to_owned(), + key, + ..Default::default() + }) + .map_err(|e| ErrorInternalServerError(e)) + .await?; // If there is no payload, return a 404. let data = resp.body.ok_or(ErrorNotFound("Not found"))?; @@ -118,6 +125,7 @@ async fn serve_file( Ok(client_resp.streaming(data)) } +#[get("/media/photo/{width:\\d+}x{height:\\d+}/{filename}")] async fn serve_photo( req: HttpRequest, config: web::Data<SiteConfig>, @@ -139,13 +147,14 @@ async fn serve_photo( .ok_or(ErrorBadRequest("Bad URI"))?; let key = format!("photo/{}", filename); - let resp = s3_client.get_object(GetObjectRequest { - bucket: config.s3_bucket().to_owned(), - key, - ..Default::default() - }) - .map_err(|e| ErrorInternalServerError(e)) - .await?; + let resp = s3_client + .get_object(GetObjectRequest { + bucket: config.s3_bucket().to_owned(), + key, + ..Default::default() + }) + .map_err(|e| ErrorInternalServerError(e)) + .await?; let mut data = Vec::new(); resp.body @@ -156,12 +165,12 @@ async fn serve_photo( // Resize the image let (mime, new_data) = web::block(move || scale_image(data.as_ref(), width, height)) - .await + .await? .map_err(|e| ErrorInternalServerError(e))?; // Send the new image to the client. let mut client_resp = response_for!(resp); - client_resp.set_header(header::CONTENT_TYPE, mime); + client_resp.insert_header((header::CONTENT_TYPE, mime)); Ok(client_resp.body(new_data)) } |