From 8afa1ab32e9b2d8edad48d4e6cf549f23ec981fc Mon Sep 17 00:00:00 2001 From: Eloi DEMOLIS Date: Mon, 28 Aug 2023 18:23:02 +0200 Subject: [PATCH] H2 header fixes: - Separate hpack encoder and decoder for each ConnectionH2 - Filter out forbidden H2 headers in converter - Remove kawa namespace prefix in pkawa Signed-off-by: Eloi DEMOLIS --- lib/src/protocol/mux/converter.rs | 35 +++++++++-------- lib/src/protocol/mux/h2.rs | 3 +- lib/src/protocol/mux/mod.rs | 4 +- lib/src/protocol/mux/pkawa.rs | 63 ++++++++++++++++--------------- 4 files changed, 58 insertions(+), 47 deletions(-) diff --git a/lib/src/protocol/mux/converter.rs b/lib/src/protocol/mux/converter.rs index d81770f7e..1e1f8b4f3 100644 --- a/lib/src/protocol/mux/converter.rs +++ b/lib/src/protocol/mux/converter.rs @@ -1,5 +1,9 @@ +use std::str::from_utf8_unchecked; + use kawa::{AsBuffer, Block, BlockConverter, Chunk, Flags, Kawa, Pair, StatusLine, Store}; +use crate::protocol::http::parser::compare_no_case; + use super::{ parser::{FrameHeader, FrameType}, serializer::gen_frame_header, @@ -14,7 +18,7 @@ pub struct H2BlockConverter<'a> { impl<'a, T: AsBuffer> BlockConverter for H2BlockConverter<'a> { fn call(&mut self, block: Block, kawa: &mut Kawa) { - let buffer = kawa.storage.mut_buffer(); + let buffer = kawa.storage.buffer(); match block { Block::StatusLine => match kawa.detached.status_line.pop() { StatusLine::Request { @@ -66,20 +70,21 @@ impl<'a, T: AsBuffer> BlockConverter for H2BlockConverter<'a> { } Block::Header(Pair { key, val }) => { { - // let key = key.data(kawa.storage.buffer()); - // let val = val.data(kawa.storage.buffer()); - // if compare_no_case(key, b"connection") - // || compare_no_case(key, b"host") - // || compare_no_case(key, b"http2-settings") - // || compare_no_case(key, b"keep-alive") - // || compare_no_case(key, b"proxy-connection") - // || compare_no_case(key, b"te") && !compare_no_case(val, b"trailers") - // || compare_no_case(key, b"trailer") - // || compare_no_case(key, b"transfer-encoding") - // || compare_no_case(key, b"upgrade") - // { - // return; - // } + let key = key.data(buffer); + let val = val.data(buffer); + if compare_no_case(key, b"connection") + || compare_no_case(key, b"host") + || compare_no_case(key, b"http2-settings") + || compare_no_case(key, b"keep-alive") + || compare_no_case(key, b"proxy-connection") + || compare_no_case(key, b"te") && !compare_no_case(val, b"trailers") + || compare_no_case(key, b"trailer") + || compare_no_case(key, b"transfer-encoding") + || compare_no_case(key, b"upgrade") + { + println!("Elided H2 header: {}", unsafe { from_utf8_unchecked(key) }); + return; + } } self.encoder .encode_header_into( diff --git a/lib/src/protocol/mux/h2.rs b/lib/src/protocol/mux/h2.rs index 7797bbb8b..4d6f0a8e8 100644 --- a/lib/src/protocol/mux/h2.rs +++ b/lib/src/protocol/mux/h2.rs @@ -50,6 +50,7 @@ impl Default for H2Settings { pub struct ConnectionH2 { pub decoder: hpack::Decoder<'static>, + pub encoder: hpack::Encoder<'static>, pub expect: Option<(H2StreamId, usize)>, pub position: Position, pub readiness: Readiness, @@ -263,7 +264,7 @@ impl ConnectionH2 { (_, _) => { let mut converter = converter::H2BlockConverter { stream_id: 0, - encoder: unsafe { std::mem::transmute(&mut self.decoder) }, + encoder: &mut self.encoder, out: Vec::new(), }; let mut want_write = false; diff --git a/lib/src/protocol/mux/mod.rs b/lib/src/protocol/mux/mod.rs index c5988c458..9234080a8 100644 --- a/lib/src/protocol/mux/mod.rs +++ b/lib/src/protocol/mux/mod.rs @@ -107,6 +107,7 @@ impl Connection { zero: kawa::Kawa::new(kawa::Kind::Request, kawa::Buffer::new(buffer)), window: 1 << 16, decoder: hpack::Decoder::new(), + encoder: hpack::Encoder::new(), })) } pub fn new_h2_client( @@ -130,6 +131,7 @@ impl Connection { zero: kawa::Kawa::new(kawa::Kind::Request, kawa::Buffer::new(buffer)), window: 1 << 16, decoder: hpack::Decoder::new(), + encoder: hpack::Encoder::new(), })) } @@ -590,7 +592,7 @@ impl SessionState for Mux { || backend.readiness().filter_interest().is_error() { println!("{:?} {:?}", backend.readiness(), backend.socket()); - return SessionResult::Close; + // return SessionResult::Close; } } diff --git a/lib/src/protocol/mux/pkawa.rs b/lib/src/protocol/mux/pkawa.rs index d44412abe..f4ec9280d 100644 --- a/lib/src/protocol/mux/pkawa.rs +++ b/lib/src/protocol/mux/pkawa.rs @@ -1,6 +1,9 @@ use std::{io::Write, str::from_utf8_unchecked}; -use kawa::h1::ParserCallbacks; +use kawa::{ + h1::ParserCallbacks, repr::Slice, Block, BodySize, Flags, Kind, Pair, ParsingPhase, StatusLine, + Store, Version, +}; use crate::{pool::Checkout, protocol::http::parser::compare_no_case}; @@ -15,20 +18,20 @@ pub fn handle_header( ) where C: ParserCallbacks, { - kawa.push_block(kawa::Block::StatusLine); + kawa.push_block(Block::StatusLine); kawa.detached.status_line = match kawa.kind { - kawa::Kind::Request => { - let mut method = kawa::Store::Empty; - let mut authority = kawa::Store::Empty; - let mut path = kawa::Store::Empty; - let mut scheme = kawa::Store::Empty; + Kind::Request => { + let mut method = Store::Empty; + let mut authority = Store::Empty; + let mut path = Store::Empty; + let mut scheme = Store::Empty; decoder .decode_with_cb(input, |k, v| { let start = kawa.storage.end as u32; kawa.storage.write_all(&v).unwrap(); let len_key = k.len() as u32; let len_val = v.len() as u32; - let val = kawa::Store::Slice(kawa::repr::Slice { + let val = Store::Slice(Slice { start, len: len_val, }); @@ -45,14 +48,14 @@ pub fn handle_header( if compare_no_case(&k, b"content-length") { let length = unsafe { from_utf8_unchecked(&v).parse::().unwrap() }; - kawa.body_size = kawa::BodySize::Length(length); + kawa.body_size = BodySize::Length(length); } kawa.storage.write_all(&k).unwrap(); - let key = kawa::Store::Slice(kawa::repr::Slice { + let key = Store::Slice(Slice { start: start + len_val, len: len_key, }); - kawa.push_block(kawa::Block::Header(kawa::Pair { key, val })); + kawa.push_block(Block::Header(Pair { key, val })); } }) .unwrap(); @@ -68,24 +71,24 @@ pub fn handle_header( // ) // }; // println!("Reconstructed URI: {uri}"); - kawa::StatusLine::Request { - version: kawa::Version::V20, + StatusLine::Request { + version: Version::V20, method, - uri: path.clone(), //kawa::Store::from_string(uri), + uri: path.clone(), //Store::from_string(uri), authority, path, } } - kawa::Kind::Response => { + Kind::Response => { let mut code = 0; - let mut status = kawa::Store::Empty; + let mut status = Store::Empty; decoder .decode_with_cb(input, |k, v| { let start = kawa.storage.end as u32; kawa.storage.write_all(&v).unwrap(); let len_key = k.len() as u32; let len_val = v.len() as u32; - let val = kawa::Store::Slice(kawa::repr::Slice { + let val = Store::Slice(Slice { start, len: len_val, }); @@ -100,19 +103,19 @@ pub fn handle_header( } } else { kawa.storage.write_all(&k).unwrap(); - let key = kawa::Store::Slice(kawa::repr::Slice { + let key = Store::Slice(Slice { start: start + len_val, len: len_key, }); - kawa.push_block(kawa::Block::Header(kawa::Pair { key, val })); + kawa.push_block(Block::Header(Pair { key, val })); } }) .unwrap(); - kawa::StatusLine::Response { - version: kawa::Version::V20, + StatusLine::Response { + version: Version::V20, code, status, - reason: kawa::Store::Empty, + reason: Store::Empty, } } }; @@ -122,7 +125,7 @@ pub fn handle_header( callbacks.on_headers(kawa); - kawa.push_block(kawa::Block::Flags(kawa::Flags { + kawa.push_block(Block::Flags(Flags { end_body: false, end_chunk: false, end_header: true, @@ -130,21 +133,21 @@ pub fn handle_header( })); if end_stream { - kawa.push_block(kawa::Block::Flags(kawa::Flags { + kawa.push_block(Block::Flags(Flags { end_body: true, end_chunk: false, end_header: false, end_stream: true, })); - kawa.body_size = kawa::BodySize::Length(0); + kawa.body_size = BodySize::Length(0); } kawa.parsing_phase = match kawa.body_size { - kawa::BodySize::Chunked => kawa::ParsingPhase::Chunks { first: true }, - kawa::BodySize::Length(0) => kawa::ParsingPhase::Terminated, - kawa::BodySize::Length(_) => kawa::ParsingPhase::Body, - kawa::BodySize::Empty => { + BodySize::Chunked => ParsingPhase::Chunks { first: true }, + BodySize::Length(0) => ParsingPhase::Terminated, + BodySize::Length(_) => ParsingPhase::Body, + BodySize::Empty => { println!("HTTP is just the worst..."); - kawa::ParsingPhase::Body + ParsingPhase::Body } }; }