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