| // Copyright (C) 2019, Cloudflare, Inc. |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright notice, |
| // this list of conditions and the following disclaimer. |
| // |
| // * Redistributions in binary form must reproduce the above copyright |
| // notice, this list of conditions and the following disclaimer in the |
| // documentation and/or other materials provided with the distribution. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS |
| // IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR |
| // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
| // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
| // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| use super::Error; |
| use super::Result; |
| |
| use crate::h3::Header; |
| |
| use super::INDEXED; |
| use super::INDEXED_WITH_POST_BASE; |
| use super::LITERAL; |
| use super::LITERAL_WITH_NAME_REF; |
| |
| #[derive(Clone, Copy, Debug, PartialEq)] |
| enum Representation { |
| Indexed, |
| IndexedWithPostBase, |
| Literal, |
| LiteralWithNameRef, |
| LiteralWithPostBase, |
| } |
| |
| impl Representation { |
| pub fn from_byte(b: u8) -> Representation { |
| if b & INDEXED == INDEXED { |
| return Representation::Indexed; |
| } |
| |
| if b & LITERAL_WITH_NAME_REF == LITERAL_WITH_NAME_REF { |
| return Representation::LiteralWithNameRef; |
| } |
| |
| if b & LITERAL == LITERAL { |
| return Representation::Literal; |
| } |
| |
| if b & INDEXED_WITH_POST_BASE == INDEXED_WITH_POST_BASE { |
| return Representation::IndexedWithPostBase; |
| } |
| |
| Representation::LiteralWithPostBase |
| } |
| } |
| |
| /// A QPACK decoder. |
| #[derive(Default)] |
| pub struct Decoder {} |
| |
| impl Decoder { |
| /// Creates a new QPACK decoder. |
| pub fn new() -> Decoder { |
| Decoder::default() |
| } |
| |
| /// Processes control instructions from the encoder. |
| pub fn control(&mut self, _buf: &mut [u8]) -> Result<()> { |
| // TODO: process control instructions |
| Ok(()) |
| } |
| |
| /// Decodes a QPACK header block into a list of headers. |
| pub fn decode(&mut self, buf: &[u8], max_size: u64) -> Result<Vec<Header>> { |
| let mut b = octets::Octets::with_slice(buf); |
| |
| let mut out = Vec::new(); |
| |
| let mut left = max_size; |
| |
| let req_insert_count = decode_int(&mut b, 8)?; |
| let base = decode_int(&mut b, 7)?; |
| |
| trace!("Header count={} base={}", req_insert_count, base); |
| |
| while b.cap() > 0 { |
| let first = b.peek_u8()?; |
| |
| match Representation::from_byte(first) { |
| Representation::Indexed => { |
| const STATIC: u8 = 0x40; |
| |
| let s = first & STATIC == STATIC; |
| let index = decode_int(&mut b, 6)?; |
| |
| trace!("Indexed index={} static={}", index, s); |
| |
| if !s { |
| // TODO: implement dynamic table |
| return Err(Error::InvalidHeaderValue); |
| } |
| |
| let (name, value) = lookup_static(index)?; |
| |
| left = left |
| .checked_sub((name.len() + value.len()) as u64) |
| .ok_or(Error::HeaderListTooLarge)?; |
| |
| let hdr = Header::new(name, value); |
| out.push(hdr); |
| }, |
| |
| Representation::IndexedWithPostBase => { |
| let index = decode_int(&mut b, 4)?; |
| |
| trace!("Indexed With Post Base index={}", index); |
| |
| // TODO: implement dynamic table |
| return Err(Error::InvalidHeaderValue); |
| }, |
| |
| Representation::Literal => { |
| let name_huff = b.as_ref()[0] & 0x08 == 0x08; |
| let name_len = decode_int(&mut b, 3)? as usize; |
| |
| let mut name = b.get_bytes(name_len)?; |
| |
| let name = if name_huff { |
| super::huffman::decode(&mut name)? |
| } else { |
| name.to_vec() |
| }; |
| |
| let name = name.to_vec(); |
| let value = decode_str(&mut b)?; |
| |
| trace!( |
| "Literal Without Name Reference name={:?} value={:?}", |
| name, |
| value, |
| ); |
| |
| left = left |
| .checked_sub((name.len() + value.len()) as u64) |
| .ok_or(Error::HeaderListTooLarge)?; |
| |
| // Instead of calling Header::new(), create Header directly |
| // from `name` and `value`, which are already String. |
| let hdr = Header(name, value); |
| out.push(hdr); |
| }, |
| |
| Representation::LiteralWithNameRef => { |
| const STATIC: u8 = 0x10; |
| |
| let s = first & STATIC == STATIC; |
| let name_idx = decode_int(&mut b, 4)?; |
| let value = decode_str(&mut b)?; |
| |
| trace!( |
| "Literal name_idx={} static={} value={:?}", |
| name_idx, |
| s, |
| value |
| ); |
| |
| if !s { |
| // TODO: implement dynamic table |
| return Err(Error::InvalidHeaderValue); |
| } |
| |
| let (name, _) = lookup_static(name_idx)?; |
| |
| left = left |
| .checked_sub((name.len() + value.len()) as u64) |
| .ok_or(Error::HeaderListTooLarge)?; |
| |
| // Instead of calling Header::new(), create Header directly |
| // from `value`, which is already String, but clone `name` |
| // as it is just a reference. |
| let hdr = Header(name.to_vec(), value); |
| out.push(hdr); |
| }, |
| |
| Representation::LiteralWithPostBase => { |
| trace!("Literal With Post Base"); |
| |
| // TODO: implement dynamic table |
| return Err(Error::InvalidHeaderValue); |
| }, |
| } |
| } |
| |
| Ok(out) |
| } |
| } |
| |
| fn lookup_static(idx: u64) -> Result<(&'static [u8], &'static [u8])> { |
| if idx >= super::static_table::STATIC_TABLE.len() as u64 { |
| return Err(Error::InvalidStaticTableIndex); |
| } |
| |
| Ok(super::static_table::STATIC_TABLE[idx as usize]) |
| } |
| |
| fn decode_int(b: &mut octets::Octets, prefix: usize) -> Result<u64> { |
| let mask = 2u64.pow(prefix as u32) - 1; |
| |
| let mut val = u64::from(b.get_u8()?); |
| val &= mask; |
| |
| if val < mask { |
| return Ok(val); |
| } |
| |
| let mut shift = 0; |
| |
| while b.cap() > 0 { |
| let byte = b.get_u8()?; |
| |
| let inc = u64::from(byte & 0x7f) |
| .checked_shl(shift) |
| .ok_or(Error::BufferTooShort)?; |
| |
| val = val.checked_add(inc).ok_or(Error::BufferTooShort)?; |
| |
| shift += 7; |
| |
| if byte & 0x80 == 0 { |
| return Ok(val); |
| } |
| } |
| |
| Err(Error::BufferTooShort) |
| } |
| |
| fn decode_str(b: &mut octets::Octets) -> Result<Vec<u8>> { |
| let first = b.peek_u8()?; |
| |
| let huff = first & 0x80 == 0x80; |
| |
| let len = decode_int(b, 7)? as usize; |
| |
| let mut val = b.get_bytes(len)?; |
| |
| let val = if huff { |
| super::huffman::decode(&mut val)? |
| } else { |
| val.to_vec() |
| }; |
| |
| Ok(val) |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| |
| #[test] |
| fn decode_int1() { |
| let mut encoded = [0b01010, 0x02]; |
| let mut b = octets::Octets::with_slice(&mut encoded); |
| |
| assert_eq!(decode_int(&mut b, 5), Ok(10)); |
| } |
| |
| #[test] |
| fn decode_int2() { |
| let mut encoded = [0b11111, 0b10011010, 0b00001010]; |
| let mut b = octets::Octets::with_slice(&mut encoded); |
| |
| assert_eq!(decode_int(&mut b, 5), Ok(1337)); |
| } |
| |
| #[test] |
| fn decode_int3() { |
| let mut encoded = [0b101010]; |
| let mut b = octets::Octets::with_slice(&mut encoded); |
| |
| assert_eq!(decode_int(&mut b, 8), Ok(42)); |
| } |
| } |