11//! Client header advertising available compression algorithms.
22
3- use crate :: content:: EncodingProposal ;
3+ use crate :: content:: { ContentEncoding , Encoding , EncodingProposal } ;
44use crate :: headers:: { HeaderName , HeaderValue , Headers , ToHeaderValues , ACCEPT_ENCODING } ;
55use crate :: utils:: sort_by_weight;
6+ use crate :: { Error , StatusCode } ;
67
78use std:: fmt:: { self , Debug , Write } ;
89use std:: option;
@@ -71,11 +72,43 @@ impl AcceptEncoding {
7172 self . wildcard = wildcard
7273 }
7374
74- /// Sort the entries in-place.
75+ /// Sort the header directives by weight.
76+ ///
77+ /// Headers with a higher `q=` value will be returned first. If two
78+ /// directives have the same weight, the directive that was declared later
79+ /// will be returned first.
7580 pub fn sort ( & mut self ) {
7681 sort_by_weight ( & mut self . entries ) ;
7782 }
7883
84+ /// Determine the most suitable `Content-Type` encoding.
85+ ///
86+ /// # Errors
87+ ///
88+ /// If no suitable encoding is found, an error with the status of `406` will be returned.
89+ pub fn negotiate ( & mut self , encodings : & [ Encoding ] ) -> crate :: Result < ContentEncoding > {
90+ // Start by ordering the encodings.
91+ self . sort ( ) ;
92+
93+ // Try and find the first encoding that matches.
94+ for encoding in & self . entries {
95+ if encodings. contains ( & encoding) {
96+ return Ok ( encoding. into ( ) ) ;
97+ }
98+ }
99+
100+ // If no encoding matches and wildcard is set, send whichever encoding we got.
101+ if self . wildcard {
102+ if let Some ( encoding) = encodings. iter ( ) . next ( ) {
103+ return Ok ( encoding. into ( ) ) ;
104+ }
105+ }
106+
107+ let mut err = Error :: new_adhoc ( "No suitable ContentEncoding found" ) ;
108+ err. set_status ( StatusCode :: NotAcceptable ) ;
109+ Err ( err)
110+ }
111+
79112 /// Insert a `HeaderName` + `HeaderValue` pair into a `Headers` instance.
80113 pub fn apply ( & self , mut headers : impl AsMut < Headers > ) {
81114 headers. as_mut ( ) . insert ( ACCEPT_ENCODING , self . value ( ) ) ;
@@ -294,7 +327,7 @@ mod test {
294327 }
295328
296329 #[ test]
297- fn reorder_iter_based_on_weight ( ) -> crate :: Result < ( ) > {
330+ fn reorder_based_on_weight ( ) -> crate :: Result < ( ) > {
298331 let mut accept = AcceptEncoding :: new ( ) ;
299332 accept. push ( EncodingProposal :: new ( Encoding :: Gzip , Some ( 0.4 ) ) ?) ;
300333 accept. push ( EncodingProposal :: new ( Encoding :: Identity , None ) ?) ;
@@ -311,4 +344,23 @@ mod test {
311344 assert_eq ! ( accept. next( ) . unwrap( ) , Encoding :: Identity ) ;
312345 Ok ( ( ) )
313346 }
347+
348+ #[ test]
349+ fn reorder_based_on_weight_and_location ( ) -> crate :: Result < ( ) > {
350+ let mut accept = AcceptEncoding :: new ( ) ;
351+ accept. push ( EncodingProposal :: new ( Encoding :: Identity , None ) ?) ;
352+ accept. push ( EncodingProposal :: new ( Encoding :: Gzip , None ) ?) ;
353+ accept. push ( EncodingProposal :: new ( Encoding :: Brotli , Some ( 0.8 ) ) ?) ;
354+
355+ let mut headers = Response :: new ( 200 ) ;
356+ accept. apply ( & mut headers) ;
357+
358+ let mut accept = AcceptEncoding :: from_headers ( headers) ?. unwrap ( ) ;
359+ accept. sort ( ) ;
360+ let mut accept = accept. iter ( ) ;
361+ assert_eq ! ( accept. next( ) . unwrap( ) , Encoding :: Brotli ) ;
362+ assert_eq ! ( accept. next( ) . unwrap( ) , Encoding :: Gzip ) ;
363+ assert_eq ! ( accept. next( ) . unwrap( ) , Encoding :: Identity ) ;
364+ Ok ( ( ) )
365+ }
314366}
0 commit comments