From d48141fee20ace6c5685e338b0e06afcca0877ed Mon Sep 17 00:00:00 2001 From: Untone Date: Wed, 23 Oct 2024 13:31:05 +0300 Subject: [PATCH] get-request-parser-fix --- src/handlers/proxy.rs | 258 ++++++++++++++++++++---------------------- src/thumbnail.rs | 50 ++++---- 2 files changed, 156 insertions(+), 152 deletions(-) diff --git a/src/handlers/proxy.rs b/src/handlers/proxy.rs index 8ae21da..0b188ab 100644 --- a/src/handlers/proxy.rs +++ b/src/handlers/proxy.rs @@ -20,146 +20,138 @@ pub async fn proxy_handler( }; // парсим GET запрос - if let Some((base_filename, requested_width, extension)) = parse_image_request(&normalized_path) - { - warn!("base_filename: {}", base_filename); - warn!("requested_width: {}", requested_width); - warn!("extension: {}", extension); - let filekey = format!("{}.{}", base_filename, extension); - let content_type = match extension.as_str() { - "jpg" | "jpeg" => "image/jpeg", - "png" => "image/png", - "webp" => "image/webp", - "gif" => "image/gif", - "mp3" => "audio/mpeg", - "wav" => "audio/x-wav", - "ogg" => "audio/ogg", - "aac" => "audio/aac", - "m4a" => "audio/m4a", - "flac" => "audio/flac", - _ => { - error!("unsupported file format"); - return Err(ErrorInternalServerError("unsupported file format")); - }, - }; + let (base_filename, requested_width, extension) = parse_image_request(&normalized_path); - warn!("content_type: {}", content_type); + let filekey = format!("{}.{}", base_filename, extension); + let content_type = match extension.as_str() { + "jpg" | "jpeg" => "image/jpeg", + "png" => "image/png", + "webp" => "image/webp", + "gif" => "image/gif", + "mp3" => "audio/mpeg", + "wav" => "audio/x-wav", + "ogg" => "audio/ogg", + "aac" => "audio/aac", + "m4a" => "audio/m4a", + "flac" => "audio/flac", + _ => { + error!("unsupported file format"); + return Err(ErrorInternalServerError("unsupported file format")); + }, + }; - return match state.get_path(&filekey).await { - Ok(Some(stored_path)) => { - warn!("stored_path: {}", stored_path); - // we have stored file path in storj - if check_file_exists(&state.storj_client, &state.bucket, &stored_path).await? { - if content_type.starts_with("image") { - return match requested_width == 0 { - true => serve_file(&stored_path, &state).await, - false => { - // find closest thumb width - let closest: u32 = find_closest_width(requested_width as u32); - let thumb_filename = - &format!("{}_{}.{}", base_filename, closest, extension); + warn!("content_type: {}", content_type); - return match check_file_exists( - &state.storj_client, - &state.bucket, - thumb_filename, - ) - .await - { - Ok(true) => { - warn!("serve existed thumb file: {}", thumb_filename); - serve_file(thumb_filename, &state).await - }, - Ok(false) => { - if let Ok(filedata) = load_file_from_s3( - &state.storj_client, - &state.bucket, - &stored_path, + return match state.get_path(&filekey).await { + Ok(Some(stored_path)) => { + warn!("stored_path: {}", stored_path); + // we have stored file path in storj + if check_file_exists(&state.storj_client, &state.bucket, &stored_path).await? { + if content_type.starts_with("image") { + return match requested_width == 0 { + true => serve_file(&stored_path, &state).await, + false => { + // find closest thumb width + let closest: u32 = find_closest_width(requested_width as u32); + let thumb_filename = + &format!("{}_{}.{}", base_filename, closest, extension); + + return match check_file_exists( + &state.storj_client, + &state.bucket, + thumb_filename, + ) + .await + { + Ok(true) => { + warn!("serve existed thumb file: {}", thumb_filename); + serve_file(thumb_filename, &state).await + }, + Ok(false) => { + if let Ok(filedata) = load_file_from_s3( + &state.storj_client, + &state.bucket, + &stored_path, + ) + .await + { + warn!("generate new thumb files: {}", stored_path); + warn!("{} bytes", filedata.len()); + thumbdata_save( + filedata.clone(), + &state, + &filekey, + content_type.to_string(), ) - .await - { - warn!("generate new thumb files: {}", stored_path); - warn!("{} bytes", filedata.len()); - thumbdata_save( - filedata.clone(), - &state, - &filekey, - content_type.to_string(), - ) - .await; - warn!("serve new thumb file: {}", thumb_filename); - serve_file(thumb_filename, &state).await - } else { - Err(ErrorInternalServerError( - "cannot generate thumbnail", - )) - } + .await; + warn!("serve new thumb file: {}", thumb_filename); + serve_file(thumb_filename, &state).await + } else { + Err(ErrorInternalServerError( + "cannot generate thumbnail", + )) } - Err(_) => { - Err(ErrorInternalServerError("failed to load thumbnail")) - } - }; - } - }; - } - // not image passing thumb generation - } - - // we need to download what stored_path keeping in aws - return match load_file_from_s3(&state.aws_client, &state.bucket, &stored_path).await - { - Ok(filedata) => { - warn!("download stored_path from aws: {:?}", stored_path); - if let Err(e) = upload_to_s3( - &state.storj_client, - &state.bucket, - &filekey, - filedata.clone(), - content_type, - ) - .await { - warn!("cannot upload to storj: {}", e); - } else { - warn!("file {} uploaded to storj", filekey); + } + Err(_) => { + Err(ErrorInternalServerError("failed to load thumbnail")) + } + }; } - thumbdata_save(filedata.clone(), &state, &filekey, content_type.to_string()) - .await; - - Ok(HttpResponse::Ok().content_type(content_type).body(filedata)) - } - Err(err) => Err(ErrorInternalServerError(err)), - }; + }; + } + // not image passing thumb generation } - Ok(None) => { - // download from aws to storj - match load_file_from_s3(&state.aws_client, &state.bucket, &requested_res).await { - Ok(filedata) => { - thumbdata_save(filedata.clone(), &state, &filekey, content_type.to_string()) - .await; - if let Err(e) = upload_to_s3( - &state.storj_client, - &state.bucket, - &filekey, - filedata.clone(), - content_type, - ) - .await { - warn!("cannot upload to storj: {}", e); - } else { - warn!("file {} uploaded to storj", filekey); - } - Ok(HttpResponse::Ok().content_type(content_type).body(filedata)) - }, - Err(e) => Err(ErrorInternalServerError(e)), - } - }, - Err(e) => Err(ErrorInternalServerError(e)) - }; - } else { - warn!("cant parse file request"); - } - Err(ErrorInternalServerError("invalid file key")) + // we need to download what stored_path keeping in aws + return match load_file_from_s3(&state.aws_client, &state.bucket, &stored_path).await + { + Ok(filedata) => { + warn!("download stored_path from aws: {:?}", stored_path); + if let Err(e) = upload_to_s3( + &state.storj_client, + &state.bucket, + &filekey, + filedata.clone(), + content_type, + ) + .await { + warn!("cannot upload to storj: {}", e); + } else { + warn!("file {} uploaded to storj", filekey); + } + thumbdata_save(filedata.clone(), &state, &filekey, content_type.to_string()) + .await; + + Ok(HttpResponse::Ok().content_type(content_type).body(filedata)) + } + Err(err) => Err(ErrorInternalServerError(err)), + }; + } + Ok(None) => { + // download from aws to storj + match load_file_from_s3(&state.aws_client, &state.bucket, &requested_res).await { + Ok(filedata) => { + thumbdata_save(filedata.clone(), &state, &filekey, content_type.to_string()) + .await; + if let Err(e) = upload_to_s3( + &state.storj_client, + &state.bucket, + &filekey, + filedata.clone(), + content_type, + ) + .await { + warn!("cannot upload to storj: {}", e); + } else { + warn!("file {} uploaded to storj", filekey); + } + Ok(HttpResponse::Ok().content_type(content_type).body(filedata)) + }, + Err(e) => Err(ErrorInternalServerError(e)), + } + }, + Err(e) => Err(ErrorInternalServerError(e)) + } } async fn thumbdata_save( @@ -171,7 +163,7 @@ async fn thumbdata_save( let state = state.clone(); if content_type.starts_with("image") { warn!("original file name: {}", original_filename); - let (base_filename, _, extension) = parse_image_request(&original_filename).unwrap(); + let (base_filename, _, extension) = parse_image_request(&original_filename); let filename = format!("{}.{}", base_filename, extension); warn!("file extension: {}", extension); diff --git a/src/thumbnail.rs b/src/thumbnail.rs index 4a64332..24c3be2 100644 --- a/src/thumbnail.rs +++ b/src/thumbnail.rs @@ -5,33 +5,45 @@ use std::{cmp::max, collections::HashMap, io::Cursor}; pub const THUMB_WIDTHS: [u32; 7] = [10, 40, 110, 300, 600, 800, 1400]; /// Парсит запрос на миниатюру, извлекая оригинальное имя файла и требуемую ширину. -/// Пример: "filename_150.ext" -> ("filename", 150, "ext") -/// unsafe/1440x/production/image/439efaa0-816f-11ef-b201-439da98539bc.jpg -> ("439efaa0-816f-11ef-b201-439da98539bc", 1440, "jpg") -pub fn parse_image_request(path: &str) -> Option<(String, u32, String)> { +/// Пример: "filename_150.ext" -> ("filename.ext", 150, "ext") +/// unsafe/1440x/production/image/439efaa0-816f-11ef-b201-439da98539bc.jpg -> ("439efaa0-816f-11ef-b201-439da98539bc.jpg", 1440, "jpg") +/// unsafe/production/image/5627e002-0c53-11ee-9565-0242ac110006.png -> ("5627e002-0c53-11ee-9565-0242ac110006.png", 0, "png") +pub fn parse_image_request(path: &str) -> (String, u32, String) { let mut path_parts = path.rsplit('/').collect::>(); - if let Some(filename_part) = path_parts.pop() { - let mut oldwidth = 0; - if path.starts_with("unsafe") { - if let Some(old_width_str) = path_parts.get(1) { + let filename_part = path_parts.pop().unwrap_or(""); + let mut width = 0; + + if path.starts_with("unsafe") { + if let Some(old_width_str) = path_parts.get(1) { let mut old_width_str = old_width_str.to_string(); if old_width_str.ends_with('x') { old_width_str.pop(); - if let Ok(width) = old_width_str.parse::() { - oldwidth = width; - } + if let Ok(w) = old_width_str.parse::() { + width = w; } } } - if let Some((name_part, ext_part)) = filename_part.rsplit_once('.') { - if let Some((base_name, width_str)) = name_part.rsplit_once('_') { - if let Ok(width) = width_str.parse::() { - return Some((base_name.to_string(), max(width, oldwidth), ext_part.to_string())); - } - } - return Some((name_part.to_string(), 0, ext_part.to_string().to_lowercase())); - } } - None + + if let Some((name_part, ext_part)) = filename_part.rsplit_once('.') { + if let Some((base_name, width_str)) = name_part.rsplit_once('_') { + if let Ok(w) = width_str.parse::() { + return ( + format!("{}.{}", base_name, ext_part), + max(w, width), + ext_part.to_string(), + ); + } + } + ( + format!("{}.{}", name_part, ext_part), + width, + ext_part.to_string().to_lowercase(), + ) + } else { + // Если расширение отсутствует, возвращаем имя файла как есть, ширину 0 и пустую строку для расширения + (filename_part.to_string(), width, "".to_string()) + } } /// Выбирает ближайший подходящий размер из предопределённых.