1+ use std:: path:: Path ;
2+
13use bytes:: Bytes ;
24use image:: ImageFormat ;
35use log:: info;
6+ use regex:: Regex ;
47use tokio:: task;
58use unicase:: Ascii ;
69use warp:: { Filter , http, Rejection } ;
@@ -49,7 +52,7 @@ async fn proxy_upscale_and_forward(
4952 headers : HeaderMap ,
5053 body : Bytes ,
5154) -> Result < Response < Body > , Rejection > {
52- let uri_str = format ! ( "{} {}" , method, uri. as_str( ) ) ;
55+ let uri_str = format ! ( "{} {}{} " , method, uri. as_str( ) , params . as_deref ( ) . map ( |query| "?" . to_string ( ) + query ) . unwrap_or_default ( ) ) ;
5356 // TODO do not request compressed data to avoid decode and re-encode
5457 let response = proxy_to_and_forward_response ( proxy_address, base_path, uri, params, method, headers, body) . await ?;
5558 let status = response. status ( ) ;
@@ -104,17 +107,26 @@ async fn response_to_upscaled_reply(
104107 format : ImageFormat ,
105108) -> Result < Response < Body > , errors:: Error > {
106109 let mime_type = match format {
107- ImageFormat :: Png => { Some ( "image/png" ) }
108- ImageFormat :: Jpeg => { Some ( "image/jpeg" ) }
109- ImageFormat :: WebP => { Some ( "image/webp" ) }
110+ ImageFormat :: Png => { Some ( ( "image/png" , "png" ) ) }
111+ ImageFormat :: Jpeg => { Some ( ( "image/jpeg" , "jpeg" ) ) }
112+ ImageFormat :: WebP => { Some ( ( "image/webp" , "webp" ) ) }
110113 _ => { None }
111114 } ;
112115 let mut builder = http:: Response :: builder ( ) ;
113116 for ( k, v) in headers {
114117 if Ascii :: new ( "Content-Length" ) == k {
115118 builder = builder. header ( "Content-Length" , bytes. len ( ) )
116119 } else if Ascii :: new ( "Content-Type" ) == k && mime_type. is_some ( ) {
117- builder = builder. header ( "Content-Type" , mime_type. unwrap ( ) )
120+ builder = builder. header ( "Content-Type" , mime_type. unwrap ( ) . 0 )
121+ } else if Ascii :: new ( "Content-Disposition" ) == k && mime_type. is_some ( ) {
122+ let new_value: String = v. to_str ( ) . unwrap ( ) . split ( "; " )
123+ . map ( |param| if param. starts_with ( "filename=" ) {
124+ with_new_file_extension ( param, mime_type. unwrap ( ) . 1 )
125+ } else if param. starts_with ( "filename*=" ) {
126+ with_new_file_extension ( param, mime_type. unwrap ( ) . 1 )
127+ } else { param. to_string ( ) } )
128+ . collect :: < Vec < String > > ( ) . join ( "; " ) ;
129+ builder = builder. header ( "Content-Disposition" , new_value)
118130 } else {
119131 builder = builder. header ( k, v) ;
120132 }
@@ -125,6 +137,16 @@ async fn response_to_upscaled_reply(
125137 . map_err ( errors:: Error :: Http )
126138}
127139
140+ fn with_new_file_extension ( name : & str , extension : & str ) -> String {
141+ let regex = Regex :: new ( r"(filename\*=UTF-8''|filename=)(.+\b)" ) . unwrap ( ) ;
142+ let captures = regex. captures ( name) . unwrap ( ) ;
143+ let param_name = captures. get ( 1 ) . unwrap ( ) . as_str ( ) ;
144+ let filename = captures. get ( 2 ) . unwrap ( ) . as_str ( ) ;
145+ let new_filename = Path :: new ( filename) . with_extension ( extension)
146+ . into_os_string ( ) . into_string ( ) . unwrap ( ) ;
147+ format ! ( "{}{}" , param_name, new_filename)
148+ }
149+
128150fn unwrap_encoding_header ( encoding : & HeaderValue ) -> Algorithm {
129151 let encoding = encoding. to_str ( ) . unwrap ( ) ;
130152 match encoding {
0 commit comments