summaryrefslogtreecommitdiff
path: root/src/media.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/media.rs')
-rw-r--r--src/media.rs137
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))
}