11//! WebSocket extensions.
22// Only `permessage-deflate` is supported at the moment.
3- use http:: HeaderValue ;
43
54#[ cfg( feature = "deflate" ) ]
65mod compression;
@@ -17,128 +16,3 @@ pub struct Extensions {
1716 #[ cfg( feature = "deflate" ) ]
1817 pub ( crate ) compression : Option < DeflateContext > ,
1918}
20-
21- /// Iterator of all extension offers/responses in `Sec-WebSocket-Extensions` values.
22- pub ( crate ) fn iter_all < ' a > (
23- values : impl Iterator < Item = & ' a HeaderValue > ,
24- ) -> impl Iterator < Item = ( & ' a str , impl Iterator < Item = ( & ' a str , Option < & ' a str > ) > ) > {
25- values
26- . filter_map ( |h| h. to_str ( ) . ok ( ) )
27- . map ( |value_str| {
28- split_iter ( value_str, ',' ) . filter_map ( |offer| {
29- // Parameters are separted by semicolons.
30- // The first element is the name of the extension.
31- let mut iter = split_iter ( offer. trim ( ) , ';' ) . map ( str:: trim) ;
32- let name = iter. next ( ) ?;
33- let params = iter. filter_map ( |kv| {
34- let mut it = kv. splitn ( 2 , '=' ) ;
35- let key = it. next ( ) ?. trim ( ) ;
36- let val = it. next ( ) . map ( |v| v. trim ( ) . trim_matches ( '"' ) ) ;
37- Some ( ( key, val) )
38- } ) ;
39- Some ( ( name, params) )
40- } )
41- } )
42- . flatten ( )
43- }
44-
45- fn split_iter ( input : & str , sep : char ) -> impl Iterator < Item = & str > {
46- let mut in_quotes = false ;
47- let mut prev = None ;
48- input. split ( move |c| {
49- if in_quotes {
50- if c == '"' && prev != Some ( '\\' ) {
51- in_quotes = false ;
52- }
53- prev = Some ( c) ;
54- false
55- } else if c == sep {
56- prev = Some ( c) ;
57- true
58- } else {
59- if c == '"' {
60- in_quotes = true ;
61- }
62- prev = Some ( c) ;
63- false
64- }
65- } )
66- }
67-
68- #[ cfg( test) ]
69- mod tests {
70- use http:: { header:: SEC_WEBSOCKET_EXTENSIONS , HeaderMap } ;
71-
72- use super :: * ;
73-
74- // Make sure comma separated offers and multiple headers are equivalent
75- fn test_iteration < ' a > (
76- mut iter : impl Iterator < Item = ( & ' a str , impl Iterator < Item = ( & ' a str , Option < & ' a str > ) > ) > ,
77- ) {
78- let ( name, mut params) = iter. next ( ) . unwrap ( ) ;
79- assert_eq ! ( name, "permessage-deflate" ) ;
80- assert_eq ! ( params. next( ) , Some ( ( "client_max_window_bits" , None ) ) ) ;
81- assert_eq ! ( params. next( ) , Some ( ( "server_max_window_bits" , Some ( "10" ) ) ) ) ;
82- assert ! ( params. next( ) . is_none( ) ) ;
83-
84- let ( name, mut params) = iter. next ( ) . unwrap ( ) ;
85- assert_eq ! ( name, "permessage-deflate" ) ;
86- assert_eq ! ( params. next( ) , Some ( ( "client_max_window_bits" , None ) ) ) ;
87- assert ! ( params. next( ) . is_none( ) ) ;
88-
89- assert ! ( iter. next( ) . is_none( ) ) ;
90- }
91-
92- #[ test]
93- fn iter_single ( ) {
94- let mut hm = HeaderMap :: new ( ) ;
95- hm. append (
96- SEC_WEBSOCKET_EXTENSIONS ,
97- HeaderValue :: from_static (
98- "permessage-deflate; client_max_window_bits; server_max_window_bits=10, permessage-deflate; client_max_window_bits" ,
99- ) ,
100- ) ;
101- test_iteration ( iter_all ( std:: iter:: once ( hm. get ( SEC_WEBSOCKET_EXTENSIONS ) . unwrap ( ) ) ) ) ;
102- }
103-
104- #[ test]
105- fn iter_multiple ( ) {
106- let mut hm = HeaderMap :: new ( ) ;
107- hm. append (
108- SEC_WEBSOCKET_EXTENSIONS ,
109- HeaderValue :: from_static (
110- "permessage-deflate; client_max_window_bits; server_max_window_bits=10" ,
111- ) ,
112- ) ;
113- hm. append (
114- SEC_WEBSOCKET_EXTENSIONS ,
115- HeaderValue :: from_static ( "permessage-deflate; client_max_window_bits" ) ,
116- ) ;
117- test_iteration ( iter_all ( hm. get_all ( SEC_WEBSOCKET_EXTENSIONS ) . iter ( ) ) ) ;
118- }
119- }
120-
121- // TODO More strict parsing
122- // https://datatracker.ietf.org/doc/html/rfc6455#section-4.3
123- // Sec-WebSocket-Extensions = extension-list
124- // extension-list = 1#extension
125- // extension = extension-token *( ";" extension-param )
126- // extension-token = registered-token
127- // registered-token = token
128- // extension-param = token [ "=" (token | quoted-string) ]
129- // ;When using the quoted-string syntax variant, the value
130- // ;after quoted-string unescaping MUST conform to the
131- // ;'token' ABNF.
132- //
133- // token = 1*<any CHAR except CTLs or separators>
134- // CHAR = <any US-ASCII character (octets 0 - 127)>
135- // CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
136- // separators = "(" | ")" | "<" | ">" | "@"
137- // | "," | ";" | ":" | "\" | <">
138- // | "/" | "[" | "]" | "?" | "="
139- // | "{" | "}" | SP | HT
140- // SP = <US-ASCII SP, space (32)>
141- // HT = <US-ASCII HT, horizontal-tab (9)>
142- // quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
143- // qdtext = <any TEXT except <">>
144- // quoted-pair = "\" CHAR
0 commit comments