@@ -46,8 +46,10 @@ use crate::webserver::http::RequestContext;
4646use crate :: webserver:: response_writer:: { AsyncResponseWriter , ResponseWriter } ;
4747use crate :: webserver:: ErrorWithStatus ;
4848use crate :: AppState ;
49+ use actix_web:: body:: MessageBody ;
4950use actix_web:: cookie:: time:: format_description:: well_known:: Rfc3339 ;
5051use actix_web:: cookie:: time:: OffsetDateTime ;
52+ use actix_web:: http:: header:: TryIntoHeaderPair ;
5153use actix_web:: http:: { header, StatusCode } ;
5254use actix_web:: { HttpResponse , HttpResponseBuilder } ;
5355use anyhow:: { bail, format_err, Context as AnyhowContext } ;
@@ -116,7 +118,7 @@ impl HeaderContext {
116118 Some ( HeaderComponent :: HttpHeader ) => {
117119 self . add_http_header ( & data) . map ( PageContext :: Header )
118120 }
119- Some ( HeaderComponent :: Redirect ) => self . redirect ( & data) . map ( PageContext :: Close ) ,
121+ Some ( HeaderComponent :: Redirect ) => self . redirect ( & data) ,
120122 Some ( HeaderComponent :: Json ) => self . json ( & data) ,
121123 Some ( HeaderComponent :: Csv ) => self . csv ( & data) . await ,
122124 Some ( HeaderComponent :: Cookie ) => self . add_cookie ( & data) . map ( PageContext :: Header ) ,
@@ -167,9 +169,9 @@ impl HeaderContext {
167169 self . response . status ( StatusCode :: FOUND ) ;
168170 self . has_status = true ;
169171 }
170- let sanitized_value = sanitize_header_value ( value_str) ;
171- self . response
172- . insert_header ( ( name . as_str ( ) , sanitized_value . as_ref ( ) ) ) ;
172+ let header = TryIntoHeaderPair :: try_into_pair ( ( name . as_str ( ) , value_str) )
173+ . map_err ( |e| anyhow :: anyhow! ( "Invalid header: {name}:{value_str}: {e:#?}" ) ) ? ;
174+ self . response . insert_header ( header ) ;
173175 }
174176 Ok ( self )
175177 }
@@ -239,15 +241,13 @@ impl HeaderContext {
239241 Ok ( self )
240242 }
241243
242- fn redirect ( mut self , data : & JsonValue ) -> anyhow:: Result < HttpResponse > {
244+ fn redirect ( mut self , data : & JsonValue ) -> anyhow:: Result < PageContext > {
243245 self . response . status ( StatusCode :: FOUND ) ;
244246 self . has_status = true ;
245247 let link = get_object_str ( data, "link" )
246248 . with_context ( || "The redirect component requires a 'link' property" ) ?;
247- let sanitized_link = sanitize_header_value ( link) ;
248- self . response
249- . insert_header ( ( header:: LOCATION , sanitized_link. as_ref ( ) ) ) ;
250- Ok ( self . into_response_builder ( ) . body ( ( ) ) )
249+ self . response . insert_header ( ( header:: LOCATION , link) ) ;
250+ self . close_with_body ( ( ) )
251251 }
252252
253253 /// Answers to the HTTP request with a single json object
@@ -260,9 +260,7 @@ impl HeaderContext {
260260 } else {
261261 serde_json:: to_vec ( contents) ?
262262 } ;
263- Ok ( PageContext :: Close (
264- self . into_response_builder ( ) . body ( json_response) ,
265- ) )
263+ self . close_with_body ( json_response)
266264 } else {
267265 let body_type = get_object_str ( data, "type" ) ;
268266 let json_renderer = match body_type {
@@ -293,10 +291,9 @@ impl HeaderContext {
293291 get_object_str ( options, "filename" ) . or_else ( || get_object_str ( options, "title" ) )
294292 {
295293 let extension = if filename. contains ( '.' ) { "" } else { ".csv" } ;
296- let sanitized_filename = sanitize_header_value ( filename) ;
297294 self . response . insert_header ( (
298295 header:: CONTENT_DISPOSITION ,
299- format ! ( "attachment; filename={sanitized_filename }{extension}" ) ,
296+ format ! ( "attachment; filename={filename }{extension}" ) ,
300297 ) ) ;
301298 }
302299 let csv_renderer = CsvBodyRenderer :: new ( self . writer , options) . await ?;
@@ -321,15 +318,15 @@ impl HeaderContext {
321318 log:: debug!( "Authentication failed" ) ;
322319 // The authentication failed
323320 if let Some ( link) = get_object_str ( & data, "link" ) {
324- let sanitized_link = sanitize_header_value ( link) ;
325321 self . response
326322 . status ( StatusCode :: FOUND )
327- . insert_header ( ( header:: LOCATION , sanitized_link . as_ref ( ) ) ) ;
323+ . insert_header ( ( header:: LOCATION , link ) ) ;
328324 self . has_status = true ;
329- Ok ( PageContext :: Close ( self . into_response_builder ( ) . body (
325+ let response = self . into_response (
330326 "Sorry, but you are not authorized to access this page. \
331327 Redirecting to the login page...",
332- ) ) )
328+ ) ?;
329+ Ok ( PageContext :: Close ( response) )
333330 } else {
334331 anyhow:: bail!( ErrorWithStatus {
335332 status: StatusCode :: UNAUTHORIZED
@@ -339,10 +336,9 @@ impl HeaderContext {
339336
340337 fn download ( mut self , options : & JsonValue ) -> anyhow:: Result < PageContext > {
341338 if let Some ( filename) = get_object_str ( options, "filename" ) {
342- let sanitized_filename = sanitize_header_value ( filename) ;
343339 self . response . insert_header ( (
344340 header:: CONTENT_DISPOSITION ,
345- format ! ( "attachment; filename=\" {sanitized_filename }\" " ) ,
341+ format ! ( "attachment; filename=\" {filename }\" " ) ,
346342 ) ) ;
347343 }
348344 let data_url = get_object_str ( options, "data_url" )
@@ -365,9 +361,7 @@ impl HeaderContext {
365361 self . response
366362 . insert_header ( ( header:: CONTENT_TYPE , content_type) ) ;
367363 }
368- Ok ( PageContext :: Close (
369- self . into_response_builder ( ) . body ( body_bytes. into_owned ( ) ) ,
370- ) )
364+ self . close_with_body ( body_bytes. into_owned ( ) )
371365 }
372366
373367 fn log ( self , data : & JsonValue ) -> anyhow:: Result < PageContext > {
@@ -381,9 +375,18 @@ impl HeaderContext {
381375 }
382376 }
383377
384- fn into_response_builder ( mut self ) -> HttpResponseBuilder {
378+ fn into_response < B : MessageBody + ' static > ( mut self , body : B ) -> anyhow :: Result < HttpResponse > {
385379 self . add_server_timing_header ( ) ;
386- self . response
380+ match self . response . message_body ( body) {
381+ Ok ( response) => Ok ( response. map_into_boxed_body ( ) ) ,
382+ Err ( e) => Err ( anyhow:: anyhow!(
383+ "An error occured while generating the request headers: {e:#}"
384+ ) ) ,
385+ }
386+ }
387+
388+ fn close_with_body < B : MessageBody + ' static > ( self , body : B ) -> anyhow:: Result < PageContext > {
389+ Ok ( PageContext :: Close ( self . into_response ( body) ?) )
387390 }
388391
389392 async fn start_body ( mut self , data : JsonValue ) -> anyhow:: Result < PageContext > {
@@ -401,7 +404,7 @@ impl HeaderContext {
401404 }
402405
403406 pub fn close ( self ) -> HttpResponse {
404- self . into_response_builder ( ) . finish ( )
407+ self . into_response ( ( ) ) . unwrap ( )
405408 }
406409}
407410
@@ -418,27 +421,6 @@ async fn verify_password_async(
418421 . await ?
419422}
420423
421- fn sanitize_header_value ( value : & str ) -> Cow < ' _ , str > {
422- if value. bytes ( ) . all ( |b| b >= 0x20 && b != 0x7F ) {
423- return Cow :: Borrowed ( value) ;
424- }
425-
426- log:: warn!(
427- "Sanitized header value by removing control characters. Original length: {}" ,
428- value. len( )
429- ) ;
430-
431- Cow :: Owned (
432- value
433- . chars ( )
434- . filter ( |& c| {
435- let byte = c as u32 ;
436- byte >= 0x20 && byte != 0x7F
437- } )
438- . collect ( ) ,
439- )
440- }
441-
442424fn get_object_str < ' a > ( json : & ' a JsonValue , key : & str ) -> Option < & ' a str > {
443425 json. as_object ( )
444426 . and_then ( |obj| obj. get ( key) )
0 commit comments