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)] 44 pub enum Error { 45 /// The provided buffer is too short. 46 BufferTooShort, 47 48 /// The QPACK header block's huffman encoding is invalid. 49 InvalidHuffmanEncoding, 50 51 /// The QPACK static table index provided doesn't exist. 52 InvalidStaticTableIndex, 53 54 /// The decoded QPACK header name or value is not valid. 55 InvalidHeaderValue, 56 57 /// The decoded header list exceeded the size limit. 58 HeaderListTooLarge, 59 } 60 61 impl std::fmt::Display for Error { fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result62 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 63 write!(f, "{:?}", self) 64 } 65 } 66 67 impl std::error::Error for Error { source(&self) -> Option<&(dyn std::error::Error + 'static)>68 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { 69 None 70 } 71 } 72 73 impl std::convert::From<crate::octets::BufferTooShortError> for Error { from(_err: crate::octets::BufferTooShortError) -> Self74 fn from(_err: crate::octets::BufferTooShortError) -> Self { 75 Error::BufferTooShort 76 } 77 } 78 79 #[cfg(test)] 80 mod tests { 81 use crate::*; 82 83 use super::*; 84 85 #[test] encode_decode()86 fn encode_decode() { 87 let mut encoded = [0u8; 240]; 88 89 let headers = vec![ 90 h3::Header::new(":path", "/rsrc.php/v3/yn/r/rIPZ9Qkrdd9.png"), 91 h3::Header::new("accept-encoding", "gzip, deflate, br"), 92 h3::Header::new("accept-language", "en-US,en;q=0.9"), 93 h3::Header::new("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.70 Safari/537.36"), 94 h3::Header::new("accept", "image/webp,image/apng,image/*,*/*;q=0.8"), 95 h3::Header::new("referer", "https://static.xx.fbcdn.net/rsrc.php/v3/yT/l/0,cross/dzXGESIlGQQ.css"), 96 h3::Header::new(":authority", "static.xx.fbcdn.net"), 97 h3::Header::new(":scheme", "https"), 98 h3::Header::new(":method", "GET"), 99 ]; 100 101 let mut enc = Encoder::new(); 102 assert_eq!(enc.encode(&headers, &mut encoded), Ok(240)); 103 104 let mut dec = Decoder::new(); 105 assert_eq!(dec.decode(&mut encoded, std::u64::MAX), Ok(headers)); 106 } 107 108 #[test] lower_case()109 fn lower_case() { 110 let mut encoded = [0u8; 35]; 111 112 let headers_expected = vec![ 113 crate::h3::Header::new(":status", "200"), 114 crate::h3::Header::new(":path", "/HeLlO"), 115 crate::h3::Header::new("woot", "woot"), 116 crate::h3::Header::new("hello", "WorlD"), 117 crate::h3::Header::new("foo", "BaR"), 118 ]; 119 120 // Header. 121 let headers_in = vec![ 122 crate::h3::Header::new(":StAtUs", "200"), 123 crate::h3::Header::new(":PaTh", "/HeLlO"), 124 crate::h3::Header::new("WooT", "woot"), 125 crate::h3::Header::new("hello", "WorlD"), 126 crate::h3::Header::new("fOo", "BaR"), 127 ]; 128 129 let mut enc = Encoder::new(); 130 assert_eq!(enc.encode(&headers_in, &mut encoded), Ok(35)); 131 132 let mut dec = Decoder::new(); 133 let headers_out = dec.decode(&mut encoded, std::u64::MAX).unwrap(); 134 135 assert_eq!(headers_expected, headers_out); 136 137 // HeaderRef. 138 let headers_in = vec![ 139 crate::h3::HeaderRef::new(":StAtUs", "200"), 140 crate::h3::HeaderRef::new(":PaTh", "/HeLlO"), 141 crate::h3::HeaderRef::new("WooT", "woot"), 142 crate::h3::HeaderRef::new("hello", "WorlD"), 143 crate::h3::HeaderRef::new("fOo", "BaR"), 144 ]; 145 146 let mut enc = Encoder::new(); 147 assert_eq!(enc.encode(&headers_in, &mut encoded), Ok(35)); 148 149 let mut dec = Decoder::new(); 150 let headers_out = dec.decode(&mut encoded, std::u64::MAX).unwrap(); 151 152 assert_eq!(headers_expected, headers_out); 153 } 154 } 155 156 pub use decoder::Decoder; 157 pub use encoder::Encoder; 158 159 mod decoder; 160 mod encoder; 161 mod huffman; 162 mod static_table; 163