1 // Copyright (C) 2019, Cloudflare, Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 //     * Redistributions of source code must retain the above copyright notice,
9 //       this list of conditions and the following disclaimer.
10 //
11 //     * Redistributions in binary form must reproduce the above copyright
12 //       notice, this list of conditions and the following disclaimer in the
13 //       documentation and/or other materials provided with the distribution.
14 //
15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
16 // IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17 // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
19 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 
27 //! HTTP/3 header compression (QPACK).
28 
29 const INDEXED: u8 = 0b1000_0000;
30 const INDEXED_WITH_POST_BASE: u8 = 0b0001_0000;
31 const LITERAL: u8 = 0b0010_0000;
32 const LITERAL_WITH_NAME_REF: u8 = 0b0100_0000;
33 
34 /// A specialized [`Result`] type for quiche QPACK operations.
35 ///
36 /// This type is used throughout quiche's QPACK public API for any operation
37 /// that can produce an error.
38 ///
39 /// [`Result`]: https://doc.rust-lang.org/std/result/enum.Result.html
40 pub type Result<T> = std::result::Result<T, Error>;
41 
42 /// A QPACK error.
43 #[derive(Clone, Copy, Debug, PartialEq, Eq)]
44 pub enum Error {
45     /// The provided buffer is too short.
46     BufferTooShort,
47 
48     /// The provided string would be larger after huffman encoding.
49     InflatedHuffmanEncoding,
50 
51     /// The QPACK header block's huffman encoding is invalid.
52     InvalidHuffmanEncoding,
53 
54     /// The QPACK static table index provided doesn't exist.
55     InvalidStaticTableIndex,
56 
57     /// The decoded QPACK header name or value is not valid.
58     InvalidHeaderValue,
59 
60     /// The decoded header list exceeded the size limit.
61     HeaderListTooLarge,
62 }
63 
64 impl std::fmt::Display for Error {
fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result65     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
66         write!(f, "{self:?}")
67     }
68 }
69 
70 impl std::error::Error for Error {
source(&self) -> Option<&(dyn std::error::Error + 'static)>71     fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
72         None
73     }
74 }
75 
76 impl std::convert::From<octets::BufferTooShortError> for Error {
from(_err: octets::BufferTooShortError) -> Self77     fn from(_err: octets::BufferTooShortError) -> Self {
78         Error::BufferTooShort
79     }
80 }
81 
82 #[cfg(test)]
83 mod tests {
84     use crate::*;
85 
86     use super::*;
87 
88     #[test]
encode_decode()89     fn encode_decode() {
90         let mut encoded = [0u8; 240];
91 
92         let headers = vec![
93             h3::Header::new(b":path", b"/rsrc.php/v3/yn/r/rIPZ9Qkrdd9.png"),
94             h3::Header::new(b"accept-encoding", b"gzip, deflate, br"),
95             h3::Header::new(b"accept-language", b"en-US,en;q=0.9"),
96             h3::Header::new(b"user-agent", b"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.70 Safari/537.36"),
97             h3::Header::new(b"accept", b"image/webp,image/apng,image/*,*/*;q=0.8"),
98             h3::Header::new(b"referer", b"https://static.xx.fbcdn.net/rsrc.php/v3/yT/l/0,cross/dzXGESIlGQQ.css"),
99             h3::Header::new(b":authority", b"static.xx.fbcdn.net"),
100             h3::Header::new(b":scheme", b"https"),
101             h3::Header::new(b":method", b"GET"),
102         ];
103 
104         let mut enc = Encoder::new();
105         assert_eq!(enc.encode(&headers, &mut encoded), Ok(240));
106 
107         let mut dec = Decoder::new();
108         assert_eq!(dec.decode(&mut encoded, u64::MAX), Ok(headers));
109     }
110 
111     #[test]
lower_case()112     fn lower_case() {
113         let mut encoded = [0u8; 35];
114 
115         let headers_expected = vec![
116             crate::h3::Header::new(b":status", b"200"),
117             crate::h3::Header::new(b":path", b"/HeLlO"),
118             crate::h3::Header::new(b"woot", b"woot"),
119             crate::h3::Header::new(b"hello", b"WorlD"),
120             crate::h3::Header::new(b"foo", b"BaR"),
121         ];
122 
123         // Header.
124         let headers_in = vec![
125             crate::h3::Header::new(b":StAtUs", b"200"),
126             crate::h3::Header::new(b":PaTh", b"/HeLlO"),
127             crate::h3::Header::new(b"WooT", b"woot"),
128             crate::h3::Header::new(b"hello", b"WorlD"),
129             crate::h3::Header::new(b"fOo", b"BaR"),
130         ];
131 
132         let mut enc = Encoder::new();
133         assert_eq!(enc.encode(&headers_in, &mut encoded), Ok(35));
134 
135         let mut dec = Decoder::new();
136         let headers_out = dec.decode(&mut encoded, u64::MAX).unwrap();
137 
138         assert_eq!(headers_expected, headers_out);
139 
140         // HeaderRef.
141         let headers_in = vec![
142             crate::h3::HeaderRef::new(b":StAtUs", b"200"),
143             crate::h3::HeaderRef::new(b":PaTh", b"/HeLlO"),
144             crate::h3::HeaderRef::new(b"WooT", b"woot"),
145             crate::h3::HeaderRef::new(b"hello", b"WorlD"),
146             crate::h3::HeaderRef::new(b"fOo", b"BaR"),
147         ];
148 
149         let mut enc = Encoder::new();
150         assert_eq!(enc.encode(&headers_in, &mut encoded), Ok(35));
151 
152         let mut dec = Decoder::new();
153         let headers_out = dec.decode(&mut encoded, u64::MAX).unwrap();
154 
155         assert_eq!(headers_expected, headers_out);
156     }
157 
158     #[test]
lower_ascii_range()159     fn lower_ascii_range() {
160         let mut encoded = [0u8; 50];
161         let mut enc = Encoder::new();
162 
163         // Indexed name with literal value
164         let headers1 = vec![crate::h3::Header::new(b"location", b"															")];
165         assert_eq!(enc.encode(&headers1, &mut encoded), Ok(19));
166 
167         // Literal name and value
168         let headers2 = vec![crate::h3::Header::new(b"a", b"")];
169         assert_eq!(enc.encode(&headers2, &mut encoded), Ok(20));
170 
171         let headers3 = vec![crate::h3::Header::new(b"															", b"hello")];
172         assert_eq!(enc.encode(&headers3, &mut encoded), Ok(24));
173     }
174 
175     #[test]
extended_ascii_range()176     fn extended_ascii_range() {
177         let mut encoded = [0u8; 50];
178         let mut enc = Encoder::new();
179 
180         let name = b"location";
181         let value = "£££££££££££££££";
182 
183         // Indexed name with literal value
184         let headers1 = vec![crate::h3::Header::new(name, value.as_bytes())];
185         assert_eq!(enc.encode(&headers1, &mut encoded), Ok(34));
186 
187         // Literal name and value
188         let value = "ððððððððððððððð";
189         let headers2 = vec![crate::h3::Header::new(b"a", value.as_bytes())];
190         assert_eq!(enc.encode(&headers2, &mut encoded), Ok(35));
191 
192         let headers3 = vec![crate::h3::Header::new(value.as_bytes(), b"hello")];
193         assert_eq!(enc.encode(&headers3, &mut encoded), Ok(39));
194     }
195 }
196 
197 pub use decoder::Decoder;
198 pub use encoder::Encoder;
199 
200 mod decoder;
201 mod encoder;
202 mod huffman;
203 mod static_table;
204