1
+ use brotli:: enc:: BrotliEncoderParams ;
2
+ use brotli:: BrotliCompress ;
1
3
use std:: collections:: HashMap ;
2
4
use std:: net:: SocketAddr ;
3
5
use std:: path:: Path ;
@@ -134,6 +136,7 @@ impl Server {
134
136
async fn handle_fallible_get_async < F , R , S , E > (
135
137
& self ,
136
138
req : & Request ,
139
+ compression : & Option < BrotliEncoderParams > ,
137
140
handler : F ,
138
141
) -> Result < Response , ServerError >
139
142
where
@@ -152,7 +155,7 @@ impl Server {
152
155
. header_typed ( ContentType :: json ( ) )
153
156
. header_typed ( CacheControl :: new ( ) . with_no_cache ( ) . with_no_store ( ) ) ;
154
157
let body = serde_json:: to_vec ( & result) . unwrap ( ) ;
155
- response. body ( hyper :: Body :: from ( body ) ) . unwrap ( )
158
+ maybe_compressed_response ( response, body, compression )
156
159
}
157
160
Err ( err) => http:: Response :: builder ( )
158
161
. status ( StatusCode :: INTERNAL_SERVER_ERROR )
@@ -313,6 +316,24 @@ async fn serve_req(server: Server, req: Request) -> Result<Response, ServerError
313
316
let path = req. uri ( ) . path ( ) . to_owned ( ) ;
314
317
let path = path. as_str ( ) ;
315
318
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
+
316
337
if let Some ( response) = handle_fs_path ( path) {
317
338
return Ok ( response) ;
318
339
}
@@ -353,13 +374,17 @@ async fn serve_req(server: Server, req: Request) -> Result<Response, ServerError
353
374
"/perf/graph" => {
354
375
let query = check ! ( parse_query_string( req. uri( ) ) ) ;
355
376
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
+ } )
357
380
. await ;
358
381
}
359
382
"/perf/graphs" => {
360
383
let query = check ! ( parse_query_string( req. uri( ) ) ) ;
361
384
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
+ } )
363
388
. await ;
364
389
}
365
390
"/perf/metrics" => {
@@ -404,6 +429,7 @@ async fn serve_req(server: Server, req: Request) -> Result<Response, ServerError
404
429
crate :: comparison:: handle_compare ( check ! ( parse_body( & body) ) , & ctxt)
405
430
. await
406
431
. map_err ( |e| e. to_string ( ) ) ,
432
+ & compression,
407
433
) ) ,
408
434
"/perf/collected" => {
409
435
if !server. check_auth ( & req) {
@@ -412,7 +438,10 @@ async fn serve_req(server: Server, req: Request) -> Result<Response, ServerError
412
438
. body ( hyper:: Body :: empty ( ) )
413
439
. unwrap ( ) ) ;
414
440
}
415
- Ok ( to_response ( request_handlers:: handle_collected ( ) . await ) )
441
+ Ok ( to_response (
442
+ request_handlers:: handle_collected ( ) . await ,
443
+ & compression,
444
+ ) )
416
445
}
417
446
"/perf/github-hook" => {
418
447
if !verify_gh ( & ctxt. config , & req, & body) {
@@ -435,6 +464,7 @@ async fn serve_req(server: Server, req: Request) -> Result<Response, ServerError
435
464
match event. as_str ( ) {
436
465
"issue_comment" => Ok ( to_response (
437
466
request_handlers:: handle_github ( check ! ( parse_body( & body) ) , ctxt. clone ( ) ) . await ,
467
+ & compression,
438
468
) ) ,
439
469
_ => Ok ( http:: Response :: builder ( )
440
470
. status ( StatusCode :: OK )
@@ -444,9 +474,11 @@ async fn serve_req(server: Server, req: Request) -> Result<Response, ServerError
444
474
}
445
475
"/perf/self-profile" => Ok ( to_response (
446
476
request_handlers:: handle_self_profile ( check ! ( parse_body( & body) ) , & ctxt) . await ,
477
+ & compression,
447
478
) ) ,
448
479
"/perf/self-profile-raw" => Ok ( to_response (
449
480
request_handlers:: handle_self_profile_raw ( check ! ( parse_body( & body) ) , & ctxt) . await ,
481
+ & compression,
450
482
) ) ,
451
483
"/perf/bootstrap" => Ok (
452
484
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> {
594
626
Some ( false )
595
627
}
596
628
597
- fn to_response < S > ( result : ServerResult < S > ) -> Response
629
+ fn to_response < S > ( result : ServerResult < S > , compression : & Option < BrotliEncoderParams > ) -> Response
598
630
where
599
631
S : Serialize ,
600
632
{
@@ -604,7 +636,7 @@ where
604
636
. header_typed ( ContentType :: octet_stream ( ) )
605
637
. header_typed ( CacheControl :: new ( ) . with_no_cache ( ) . with_no_store ( ) ) ;
606
638
let body = rmp_serde:: to_vec_named ( & result) . unwrap ( ) ;
607
- response. body ( hyper :: Body :: from ( body ) ) . unwrap ( )
639
+ maybe_compressed_response ( response, body, compression )
608
640
}
609
641
Err ( err) => http:: Response :: builder ( )
610
642
. status ( StatusCode :: INTERNAL_SERVER_ERROR )
@@ -615,6 +647,30 @@ where
615
647
}
616
648
}
617
649
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
+
618
674
fn to_triage_response ( result : ServerResult < api:: triage:: Response > ) -> Response {
619
675
match result {
620
676
Ok ( result) => {
0 commit comments