1+ use brotli:: enc:: BrotliEncoderParams ;
2+ use brotli:: BrotliCompress ;
13use std:: collections:: HashMap ;
24use std:: net:: SocketAddr ;
35use std:: path:: Path ;
@@ -134,6 +136,7 @@ impl Server {
134136 async fn handle_fallible_get_async < F , R , S , E > (
135137 & self ,
136138 req : & Request ,
139+ compression : & Option < BrotliEncoderParams > ,
137140 handler : F ,
138141 ) -> Result < Response , ServerError >
139142 where
@@ -152,7 +155,7 @@ impl Server {
152155 . header_typed ( ContentType :: json ( ) )
153156 . header_typed ( CacheControl :: new ( ) . with_no_cache ( ) . with_no_store ( ) ) ;
154157 let body = serde_json:: to_vec ( & result) . unwrap ( ) ;
155- response. body ( hyper :: Body :: from ( body ) ) . unwrap ( )
158+ maybe_compressed_response ( response, body, compression )
156159 }
157160 Err ( err) => http:: Response :: builder ( )
158161 . status ( StatusCode :: INTERNAL_SERVER_ERROR )
@@ -313,6 +316,24 @@ async fn serve_req(server: Server, req: Request) -> Result<Response, ServerError
313316 let path = req. uri ( ) . path ( ) . to_owned ( ) ;
314317 let path = path. as_str ( ) ;
315318
319+ let allow_compression = req
320+ . headers ( )
321+ . get ( hyper:: header:: ACCEPT_ENCODING )
322+ . map_or ( false , |e| e. to_str ( ) . unwrap ( ) . contains ( "br" ) ) ;
323+
324+ let compression = if allow_compression {
325+ let mut brotli = BrotliEncoderParams :: default ( ) ;
326+ // In tests on /perf/graphs and /perf/get, quality = 2 reduces size by 20-40% compared to 0,
327+ // while quality = 4 takes 80% longer but reduces size by less than 5% compared to 2.
328+ // Around 4-5 is sometimes said to be "smaller and faster than gzip".
329+ // [Google's default is 6](https://github.com/google/ngx_brotli#brotli_comp_level),
330+ // higher levels offer only small size savings but are much slower.
331+ brotli. quality = 2 ;
332+ Some ( brotli)
333+ } else {
334+ None
335+ } ;
336+
316337 if let Some ( response) = handle_fs_path ( path) {
317338 return Ok ( response) ;
318339 }
@@ -353,13 +374,17 @@ async fn serve_req(server: Server, req: Request) -> Result<Response, ServerError
353374 "/perf/graph" => {
354375 let query = check ! ( parse_query_string( req. uri( ) ) ) ;
355376 return server
356- . handle_fallible_get_async ( & req, |c| request_handlers:: handle_graph ( query, c) )
377+ . handle_fallible_get_async ( & req, & compression, |c| {
378+ request_handlers:: handle_graph ( query, c)
379+ } )
357380 . await ;
358381 }
359382 "/perf/graphs" => {
360383 let query = check ! ( parse_query_string( req. uri( ) ) ) ;
361384 return server
362- . handle_fallible_get_async ( & req, |c| request_handlers:: handle_graphs ( query, c) )
385+ . handle_fallible_get_async ( & req, & compression, |c| {
386+ request_handlers:: handle_graphs ( query, c)
387+ } )
363388 . await ;
364389 }
365390 "/perf/metrics" => {
@@ -404,6 +429,7 @@ async fn serve_req(server: Server, req: Request) -> Result<Response, ServerError
404429 crate :: comparison:: handle_compare ( check ! ( parse_body( & body) ) , & ctxt)
405430 . await
406431 . map_err ( |e| e. to_string ( ) ) ,
432+ & compression,
407433 ) ) ,
408434 "/perf/collected" => {
409435 if !server. check_auth ( & req) {
@@ -412,7 +438,10 @@ async fn serve_req(server: Server, req: Request) -> Result<Response, ServerError
412438 . body ( hyper:: Body :: empty ( ) )
413439 . unwrap ( ) ) ;
414440 }
415- Ok ( to_response ( request_handlers:: handle_collected ( ) . await ) )
441+ Ok ( to_response (
442+ request_handlers:: handle_collected ( ) . await ,
443+ & compression,
444+ ) )
416445 }
417446 "/perf/github-hook" => {
418447 if !verify_gh ( & ctxt. config , & req, & body) {
@@ -435,6 +464,7 @@ async fn serve_req(server: Server, req: Request) -> Result<Response, ServerError
435464 match event. as_str ( ) {
436465 "issue_comment" => Ok ( to_response (
437466 request_handlers:: handle_github ( check ! ( parse_body( & body) ) , ctxt. clone ( ) ) . await ,
467+ & compression,
438468 ) ) ,
439469 _ => Ok ( http:: Response :: builder ( )
440470 . status ( StatusCode :: OK )
@@ -444,9 +474,11 @@ async fn serve_req(server: Server, req: Request) -> Result<Response, ServerError
444474 }
445475 "/perf/self-profile" => Ok ( to_response (
446476 request_handlers:: handle_self_profile ( check ! ( parse_body( & body) ) , & ctxt) . await ,
477+ & compression,
447478 ) ) ,
448479 "/perf/self-profile-raw" => Ok ( to_response (
449480 request_handlers:: handle_self_profile_raw ( check ! ( parse_body( & body) ) , & ctxt) . await ,
481+ & compression,
450482 ) ) ,
451483 "/perf/bootstrap" => Ok (
452484 match request_handlers:: handle_bootstrap ( check ! ( parse_body( & body) ) , & ctxt) . await {
@@ -594,7 +626,7 @@ fn verify_gh_sig(cfg: &Config, header: &str, body: &[u8]) -> Option<bool> {
594626 Some ( false )
595627}
596628
597- fn to_response < S > ( result : ServerResult < S > ) -> Response
629+ fn to_response < S > ( result : ServerResult < S > , compression : & Option < BrotliEncoderParams > ) -> Response
598630where
599631 S : Serialize ,
600632{
@@ -604,7 +636,7 @@ where
604636 . header_typed ( ContentType :: octet_stream ( ) )
605637 . header_typed ( CacheControl :: new ( ) . with_no_cache ( ) . with_no_store ( ) ) ;
606638 let body = rmp_serde:: to_vec_named ( & result) . unwrap ( ) ;
607- response. body ( hyper :: Body :: from ( body ) ) . unwrap ( )
639+ maybe_compressed_response ( response, body, compression )
608640 }
609641 Err ( err) => http:: Response :: builder ( )
610642 . status ( StatusCode :: INTERNAL_SERVER_ERROR )
@@ -615,6 +647,30 @@ where
615647 }
616648}
617649
650+ fn maybe_compressed_response (
651+ response : http:: response:: Builder ,
652+ body : Vec < u8 > ,
653+ compression : & Option < BrotliEncoderParams > ,
654+ ) -> Response {
655+ match compression {
656+ None => response. body ( hyper:: Body :: from ( body) ) . unwrap ( ) ,
657+ Some ( brotli_params) => {
658+ let compressed = compress_bytes ( & body, brotli_params) ;
659+ let response = response. header (
660+ hyper:: header:: CONTENT_ENCODING ,
661+ hyper:: header:: HeaderValue :: from_static ( "br" ) ,
662+ ) ;
663+ response. body ( hyper:: Body :: from ( compressed) ) . unwrap ( )
664+ }
665+ }
666+ }
667+
668+ fn compress_bytes ( mut bytes : & [ u8 ] , brotli_params : & BrotliEncoderParams ) -> Vec < u8 > {
669+ let mut compressed = Vec :: with_capacity ( bytes. len ( ) ) ;
670+ BrotliCompress ( & mut bytes, & mut compressed, brotli_params) . unwrap ( ) ;
671+ compressed
672+ }
673+
618674fn to_triage_response ( result : ServerResult < api:: triage:: Response > ) -> Response {
619675 match result {
620676 Ok ( result) => {
0 commit comments