diff options
author | 2021-06-01 10:48:29 +0100 | |
---|---|---|
committer | 2021-06-03 13:40:05 +0100 | |
commit | 985bd890b2a2e766cc194bc5fb23a45020627682 (patch) | |
tree | 7bcffffb74464f23193cb4f2edd6b25207de2206 | |
parent | a9116d8ca6539b02212573352a96867c41afa5c2 (diff) | |
download | quiche-985bd890b2a2e766cc194bc5fb23a45020627682.tar.gz quiche-985bd890b2a2e766cc194bc5fb23a45020627682.tar.zst quiche-985bd890b2a2e766cc194bc5fb23a45020627682.zip |
h3: don't use UTF-8 strings to represent headers
There is not requirement for HTTP/3 header names and values to be valid
UTF-8, so don't require it.
-rw-r--r-- | examples/http3-client.rs | 13 | ||||
-rw-r--r-- | examples/http3-server.rs | 24 | ||||
-rw-r--r-- | examples/qpack-decode.rs | 4 | ||||
-rw-r--r-- | examples/qpack-encode.rs | 2 | ||||
-rw-r--r-- | src/h3/ffi.rs | 12 | ||||
-rw-r--r-- | src/h3/mod.rs | 119 | ||||
-rw-r--r-- | src/h3/qpack/decoder.rs | 11 | ||||
-rw-r--r-- | src/h3/qpack/encoder.rs | 14 | ||||
-rw-r--r-- | src/h3/qpack/mod.rs | 48 | ||||
-rw-r--r-- | src/h3/qpack/static_table.rs | 206 | ||||
-rw-r--r-- | tools/apps/src/common.rs | 86 | ||||
-rw-r--r-- | tools/http3_test/src/lib.rs | 46 | ||||
-rw-r--r-- | tools/http3_test/tests/httpbin_tests.rs | 290 |
13 files changed, 450 insertions, 425 deletions
diff --git a/examples/http3-client.rs b/examples/http3-client.rs index 07a5a1a1..2acb2ca6 100644 --- a/examples/http3-client.rs +++ b/examples/http3-client.rs @@ -141,11 +141,14 @@ fn main() { } let req = vec![ - quiche::h3::Header::new(":method", "GET"), - quiche::h3::Header::new(":scheme", url.scheme()), - quiche::h3::Header::new(":authority", url.host_str().unwrap()), - quiche::h3::Header::new(":path", &path), - quiche::h3::Header::new("user-agent", "quiche"), + quiche::h3::Header::new(b":method", b"GET"), + quiche::h3::Header::new(b":scheme", url.scheme().as_bytes()), + quiche::h3::Header::new( + b":authority", + url.host_str().unwrap().as_bytes(), + ), + quiche::h3::Header::new(b":path", path.as_bytes()), + quiche::h3::Header::new(b"user-agent", b"quiche"), ]; let req_start = std::time::Instant::now(); diff --git a/examples/http3-server.rs b/examples/http3-server.rs index b1fc1edc..e84d1ea2 100644 --- a/examples/http3-server.rs +++ b/examples/http3-server.rs @@ -560,25 +560,24 @@ fn build_response( ) -> (Vec<quiche::h3::Header>, Vec<u8>) { let mut file_path = std::path::PathBuf::from(root); let mut path = std::path::Path::new(""); - let mut method = ""; + let mut method = None; // Look for the request's path and method. for hdr in request { match hdr.name() { - ":path" => { - path = std::path::Path::new(hdr.value()); - }, + b":path" => + path = std::path::Path::new( + std::str::from_utf8(hdr.value()).unwrap(), + ), - ":method" => { - method = hdr.value(); - }, + b":method" => method = Some(hdr.value()), _ => (), } } let (status, body) = match method { - "GET" => { + Some(b"GET") => { for c in path.components() { if let std::path::Component::Normal(v) = c { file_path.push(v) @@ -596,9 +595,12 @@ fn build_response( }; let headers = vec![ - quiche::h3::Header::new(":status", &status.to_string()), - quiche::h3::Header::new("server", "quiche"), - quiche::h3::Header::new("content-length", &body.len().to_string()), + quiche::h3::Header::new(b":status", status.to_string().as_bytes()), + quiche::h3::Header::new(b"server", b"quiche"), + quiche::h3::Header::new( + b"content-length", + body.len().to_string().as_bytes(), + ), ]; (headers, body) diff --git a/examples/qpack-decode.rs b/examples/qpack-decode.rs index 8468a850..d2aaaa5e 100644 --- a/examples/qpack-decode.rs +++ b/examples/qpack-decode.rs @@ -77,7 +77,9 @@ fn main() { } for hdr in dec.decode(&data[..len], std::u64::MAX).unwrap() { - println!("{}\t{}", hdr.name(), hdr.value()); + let name = std::str::from_utf8(hdr.name()).unwrap(); + let value = std::str::from_utf8(hdr.value()).unwrap(); + println!("{}\t{}", name, value); } println!(); diff --git a/examples/qpack-encode.rs b/examples/qpack-encode.rs index e3812279..5215fe4e 100644 --- a/examples/qpack-encode.rs +++ b/examples/qpack-encode.rs @@ -83,6 +83,6 @@ fn main() { let name = line.split('\t').next().unwrap(); let value = line.split('\t').last().unwrap(); - headers.push(h3::Header::new(name, value)); + headers.push(h3::Header::new(name.as_bytes(), value.as_bytes())); } } diff --git a/src/h3/ffi.rs b/src/h3/ffi.rs index 66cd9c55..fc254b08 100644 --- a/src/h3/ffi.rs +++ b/src/h3/ffi.rs @@ -27,7 +27,6 @@ use std::ffi; use std::ptr; use std::slice; -use std::str; use libc::c_char; use libc::c_int; @@ -324,15 +323,8 @@ fn headers_from_ptr<'a>( for h in headers { out.push({ - let name = unsafe { - let slice = slice::from_raw_parts(h.name, h.name_len); - str::from_utf8_unchecked(slice) - }; - - let value = unsafe { - let slice = slice::from_raw_parts(h.value, h.value_len); - str::from_utf8_unchecked(slice) - }; + let name = unsafe { slice::from_raw_parts(h.name, h.name_len) }; + let value = unsafe { slice::from_raw_parts(h.value, h.value_len) }; h3::HeaderRef::new(name, value) }); diff --git a/src/h3/mod.rs b/src/h3/mod.rs index 60fd3af8..24b84b0a 100644 --- a/src/h3/mod.rs +++ b/src/h3/mod.rs @@ -81,11 +81,11 @@ //! # let h3_config = quiche::h3::Config::new()?; //! # let mut h3_conn = quiche::h3::Connection::with_transport(&mut conn, &h3_config)?; //! let req = vec![ -//! quiche::h3::Header::new(":method", "GET"), -//! quiche::h3::Header::new(":scheme", "https"), -//! quiche::h3::Header::new(":authority", "quic.tech"), -//! quiche::h3::Header::new(":path", "/"), -//! quiche::h3::Header::new("user-agent", "quiche"), +//! quiche::h3::Header::new(b":method", b"GET"), +//! quiche::h3::Header::new(b":scheme", b"https"), +//! quiche::h3::Header::new(b":authority", b"quic.tech"), +//! quiche::h3::Header::new(b":path", b"/"), +//! quiche::h3::Header::new(b"user-agent", b"quiche"), //! ]; //! //! h3_conn.send_request(&mut conn, &req, true)?; @@ -103,11 +103,11 @@ //! # let h3_config = quiche::h3::Config::new()?; //! # let mut h3_conn = quiche::h3::Connection::with_transport(&mut conn, &h3_config)?; //! let req = vec![ -//! quiche::h3::Header::new(":method", "GET"), -//! quiche::h3::Header::new(":scheme", "https"), -//! quiche::h3::Header::new(":authority", "quic.tech"), -//! quiche::h3::Header::new(":path", "/"), -//! quiche::h3::Header::new("user-agent", "quiche"), +//! quiche::h3::Header::new(b":method", b"GET"), +//! quiche::h3::Header::new(b":scheme", b"https"), +//! quiche::h3::Header::new(b":authority", b"quic.tech"), +//! quiche::h3::Header::new(b":path", b"/"), +//! quiche::h3::Header::new(b"user-agent", b"quiche"), //! ]; //! //! let stream_id = h3_conn.send_request(&mut conn, &req, false)?; @@ -139,15 +139,15 @@ //! let mut headers = list.into_iter(); //! //! // Look for the request's method. -//! let method = headers.find(|h| h.name() == ":method").unwrap(); +//! let method = headers.find(|h| h.name() == b":method").unwrap(); //! //! // Look for the request's path. -//! let path = headers.find(|h| h.name() == ":path").unwrap(); +//! let path = headers.find(|h| h.name() == b":path").unwrap(); //! -//! if method.value() == "GET" && path.value() == "/" { +//! if method.value() == b"GET" && path.value() == b"/" { //! let resp = vec![ -//! quiche::h3::Header::new(":status", &200.to_string()), -//! quiche::h3::Header::new("server", "quiche"), +//! quiche::h3::Header::new(b":status", 200.to_string().as_bytes()), +//! quiche::h3::Header::new(b"server", b"quiche"), //! ]; //! //! h3_conn.send_response(&mut conn, stream_id, &resp, false)?; @@ -198,9 +198,10 @@ //! loop { //! match h3_conn.poll(&mut conn) { //! Ok((stream_id, quiche::h3::Event::Headers{list, has_body})) => { -//! let status = list.iter().find(|h| h.name() == ":status").unwrap(); +//! let status = list.iter().find(|h| h.name() == b":status").unwrap(); //! println!("Received {} response on stream {}", -//! status.value(), stream_id); +//! std::str::from_utf8(status.value()).unwrap(), +//! stream_id); //! }, //! //! Ok((stream_id, quiche::h3::Event::Data)) => { @@ -494,52 +495,52 @@ impl Config { /// A trait for types with associated string name and value. pub trait NameValue { /// Returns the object's name. - fn name(&self) -> &str; + fn name(&self) -> &[u8]; /// Returns the object's value. - fn value(&self) -> &str; + fn value(&self) -> &[u8]; } /// An owned name-value pair representing a raw HTTP header. #[derive(Clone, Debug, PartialEq)] -pub struct Header(String, String); +pub struct Header(Vec<u8>, Vec<u8>); impl Header { /// Creates a new header. /// /// Both `name` and `value` will be cloned. - pub fn new(name: &str, value: &str) -> Self { - Self(String::from(name), String::from(value)) + pub fn new(name: &[u8], value: &[u8]) -> Self { + Self(name.to_vec(), value.to_vec()) } } impl NameValue for Header { - fn name(&self) -> &str { + fn name(&self) -> &[u8] { &self.0 } - fn value(&self) -> &str { + fn value(&self) -> &[u8] { &self.1 } } /// A non-owned name-value pair representing a raw HTTP header. #[derive(Clone, Debug, PartialEq)] -pub struct HeaderRef<'a>(&'a str, &'a str); +pub struct HeaderRef<'a>(&'a [u8], &'a [u8]); impl<'a> HeaderRef<'a> { /// Creates a new header. - pub fn new(name: &'a str, value: &'a str) -> Self { + pub fn new(name: &'a [u8], value: &'a [u8]) -> Self { Self(name, value) } } impl<'a> NameValue for HeaderRef<'a> { - fn name(&self) -> &str { + fn name(&self) -> &[u8] { self.0 } - fn value(&self) -> &str { + fn value(&self) -> &[u8] { self.1 } } @@ -2201,11 +2202,11 @@ pub mod testing { /// On success it returns the newly allocated stream and the headers. pub fn send_request(&mut self, fin: bool) -> Result<(u64, Vec<Header>)> { let req = vec![ - Header::new(":method", "GET"), - Header::new(":scheme", "https"), - Header::new(":authority", "quic.tech"), - Header::new(":path", "/test"), - Header::new("user-agent", "quiche-test"), + Header::new(b":method", b"GET"), + Header::new(b":scheme", b"https"), + Header::new(b":authority", b"quic.tech"), + Header::new(b":path", b"/test"), + Header::new(b"user-agent", b"quiche-test"), ]; let stream = @@ -2223,8 +2224,8 @@ pub mod testing { &mut self, stream: u64, fin: bool, ) -> Result<Vec<Header>> { let resp = vec![ - Header::new(":status", "200"), - Header::new("server", "quiche-test"), + Header::new(b":status", b"200"), + Header::new(b"server", b"quiche-test"), ]; self.server.send_response( @@ -3242,11 +3243,11 @@ mod tests { s.handshake().unwrap(); let req = vec![ - Header::new(":method", "GET"), - Header::new(":scheme", "https"), - Header::new(":authority", "quic.tech"), - Header::new(":path", "/test"), - Header::new("user-agent", "quiche-test"), + Header::new(b":method", b"GET"), + Header::new(b":scheme", b"https"), + Header::new(b":authority", b"quic.tech"), + Header::new(b":path", b"/test"), + Header::new(b"user-agent", b"quiche-test"), ]; assert_eq!( @@ -3384,11 +3385,11 @@ mod tests { s.handshake().unwrap(); let req = vec![ - Header::new(":method", "GET"), - Header::new(":scheme", "https"), - Header::new(":authority", "quic.tech"), - Header::new(":path", "/test"), - Header::new("aaaaaaa", "aaaaaaaa"), + Header::new(b":method", b"GET"), + Header::new(b":scheme", b"https"), + Header::new(b":authority", b"quic.tech"), + Header::new(b":path", b"/test"), + Header::new(b"aaaaaaa", b"aaaaaaaa"), ]; let stream = s @@ -3415,11 +3416,11 @@ mod tests { s.handshake().unwrap(); let req = vec![ - Header::new(":method", "GET"), - Header::new(":scheme", "https"), - Header::new(":authority", "quic.tech"), - Header::new(":path", "/test"), - Header::new("user-agent", "quiche-test"), + Header::new(b":method", b"GET"), + Header::new(b":scheme", b"https"), + Header::new(b":authority", b"quic.tech"), + Header::new(b":path", b"/test"), + Header::new(b"user-agent", b"quiche-test"), ]; // We need to open all streams in the same flight, so we can't use the @@ -3520,10 +3521,10 @@ mod tests { s.handshake().unwrap(); let req = vec![ - Header::new(":method", "GET"), - Header::new(":scheme", "https"), - Header::new(":authority", "quic.tech"), - Header::new(":path", "/test"), + Header::new(b":method", b"GET"), + Header::new(b":scheme", b"https"), + Header::new(b":authority", b"quic.tech"), + Header::new(b":path", b"/test"), ]; assert_eq!(s.client.send_request(&mut s.pipe.client, &req, true), Ok(0)); @@ -3621,10 +3622,10 @@ mod tests { s.handshake().unwrap(); let req = vec![ - Header::new(":method", "GET"), - Header::new(":scheme", "https"), - Header::new(":authority", "quic.tech"), - Header::new(":path", "/test"), + Header::new(b":method", b"GET"), + Header::new(b":scheme", b"https"), + Header::new(b":authority", b"quic.tech"), + Header::new(b":path", b"/test"), ]; assert_eq!( @@ -4295,7 +4296,7 @@ mod tests { // Send more data, then HEADERS, then more data. let body = s.send_body_client(stream, false).unwrap(); - let trailers = vec![Header::new("hello", "world")]; + let trailers = vec![Header::new(b"hello", b"world")]; s.client .send_headers(&mut s.pipe.client, stream, &trailers, false) diff --git a/src/h3/qpack/decoder.rs b/src/h3/qpack/decoder.rs index 3f787c7d..1bc5755e 100644 --- a/src/h3/qpack/decoder.rs +++ b/src/h3/qpack/decoder.rs @@ -149,9 +149,7 @@ impl Decoder { name.to_vec() }; - let name = String::from_utf8(name) - .map_err(|_| Error::InvalidHeaderValue)?; - + let name = name.to_vec(); let value = decode_str(&mut b)?; trace!( @@ -198,7 +196,7 @@ impl Decoder { // 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_string(), value); + let hdr = Header(name.to_vec(), value); out.push(hdr); }, @@ -215,7 +213,7 @@ impl Decoder { } } -fn lookup_static(idx: u64) -> Result<(&'static str, &'static str)> { +fn lookup_static(idx: u64) -> Result<(&'static [u8], &'static [u8])> { if idx >= super::static_table::STATIC_TABLE.len() as u64 { return Err(Error::InvalidStaticTableIndex); } @@ -254,7 +252,7 @@ fn decode_int(b: &mut octets::Octets, prefix: usize) -> Result<u64> { Err(Error::BufferTooShort) } -fn decode_str(b: &mut octets::Octets) -> Result<String> { +fn decode_str(b: &mut octets::Octets) -> Result<Vec<u8>> { let first = b.peek_u8()?; let huff = first & 0x80 == 0x80; @@ -269,7 +267,6 @@ fn decode_str(b: &mut octets::Octets) -> Result<String> { val.to_vec() }; - let val = String::from_utf8(val).map_err(|_| Error::InvalidHeaderValue)?; Ok(val) } diff --git a/src/h3/qpack/encoder.rs b/src/h3/qpack/encoder.rs index 24a28c21..09c8b083 100644 --- a/src/h3/qpack/encoder.rs +++ b/src/h3/qpack/encoder.rs @@ -80,14 +80,12 @@ impl Encoder { None => { // Encode as fully literal. - let name_len = super::huffman::encode_output_length( - h.name().as_bytes(), - true, - )?; + let name_len = + super::huffman::encode_output_length(h.name(), true)?; encode_int(name_len as u64, LITERAL | 0x08, 3, &mut b)?; - super::huffman::encode(h.name().as_bytes(), &mut b, true)?; + super::huffman::encode(h.name(), &mut b, true)?; encode_str(h.value(), 7, &mut b)?; }, @@ -151,12 +149,12 @@ fn encode_int( Ok(()) } -fn encode_str(v: &str, prefix: usize, b: &mut octets::OctetsMut) -> Result<()> { - let len = super::huffman::encode_output_length(v.as_bytes(), false)?; +fn encode_str(v: &[u8], prefix: usize, b: &mut octets::OctetsMut) -> Result<()> { + let len = super::huffman::encode_output_length(v, false)?; encode_int(len as u64, 0x80, prefix, b)?; - super::huffman::encode(v.as_bytes(), b, false)?; + super::huffman::encode(v, b, false)?; Ok(()) } diff --git a/src/h3/qpack/mod.rs b/src/h3/qpack/mod.rs index 6f2bdda4..0a23306b 100644 --- a/src/h3/qpack/mod.rs +++ b/src/h3/qpack/mod.rs @@ -87,15 +87,15 @@ mod tests { let mut encoded = [0u8; 240]; let headers = vec![ - h3::Header::new(":path", "/rsrc.php/v3/yn/r/rIPZ9Qkrdd9.png"), - h3::Header::new("accept-encoding", "gzip, deflate, br"), - h3::Header::new("accept-language", "en-US,en;q=0.9"), - 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"), - h3::Header::new("accept", "image/webp,image/apng,image/*,*/*;q=0.8"), - h3::Header::new("referer", "https://static.xx.fbcdn.net/rsrc.php/v3/yT/l/0,cross/dzXGESIlGQQ.css"), - h3::Header::new(":authority", "static.xx.fbcdn.net"), - h3::Header::new(":scheme", "https"), - h3::Header::new(":method", "GET"), + h3::Header::new(b":path", b"/rsrc.php/v3/yn/r/rIPZ9Qkrdd9.png"), + h3::Header::new(b"accept-encoding", b"gzip, deflate, br"), + h3::Header::new(b"accept-language", b"en-US,en;q=0.9"), + 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"), + h3::Header::new(b"accept", b"image/webp,image/apng,image/*,*/*;q=0.8"), + h3::Header::new(b"referer", b"https://static.xx.fbcdn.net/rsrc.php/v3/yT/l/0,cross/dzXGESIlGQQ.css"), + h3::Header::new(b":authority", b"static.xx.fbcdn.net"), + h3::Header::new(b":scheme", b"https"), + h3::Header::new(b":method", b"GET"), ]; let mut enc = Encoder::new(); @@ -110,20 +110,20 @@ mod tests { let mut encoded = [0u8; 35]; let headers_expected = vec![ - crate::h3::Header::new(":status", "200"), - crate::h3::Header::new(":path", "/HeLlO"), - crate::h3::Header::new("woot", "woot"), - crate::h3::Header::new("hello", "WorlD"), - crate::h3::Header::new("foo", "BaR"), + crate::h3::Header::new(b":status", b"200"), + crate::h3::Header::new(b":path", b"/HeLlO"), + crate::h3::Header::new(b"woot", b"woot"), + crate::h3::Header::new(b"hello", b"WorlD"), + crate::h3::Header::new(b"foo", b"BaR"), ]; // Header. let headers_in = vec![ - crate::h3::Header::new(":StAtUs", "200"), - crate::h3::Header::new(":PaTh", "/HeLlO"), - crate::h3::Header::new("WooT", "woot"), - crate::h3::Header::new("hello", "WorlD"), - crate::h3::Header::new("fOo", "BaR"), + crate::h3::Header::new(b":StAtUs", b"200"), + crate::h3::Header::new(b":PaTh", b"/HeLlO"), + crate::h3::Header::new(b"WooT", b"woot"), + crate::h3::Header::new(b"hello", b"WorlD"), + crate::h3::Header::new(b"fOo", b"BaR"), ]; let mut enc = Encoder::new(); @@ -136,11 +136,11 @@ mod tests { // HeaderRef. let headers_in = vec![ - crate::h3::HeaderRef::new(":StAtUs", "200"), - crate::h3::HeaderRef::new(":PaTh", "/HeLlO"), - crate::h3::HeaderRef::new("WooT", "woot"), - crate::h3::HeaderRef::new("hello", "WorlD"), - crate::h3::HeaderRef::new("fOo", "BaR"), + crate::h3::HeaderRef::new(b":StAtUs", b"200"), + crate::h3::HeaderRef::new(b":PaTh", b"/HeLlO"), + crate::h3::HeaderRef::new(b"WooT", b"woot"), + crate::h3::HeaderRef::new(b"hello", b"WorlD"), + crate::h3::HeaderRef::new(b"fOo", b"BaR"), ]; let mut enc = Encoder::new(); diff --git a/src/h3/qpack/static_table.rs b/src/h3/qpack/static_table.rs index 4010d12d..3cc10d49 100644 --- a/src/h3/qpack/static_table.rs +++ b/src/h3/qpack/static_table.rs @@ -24,113 +24,113 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -pub const STATIC_TABLE: [(&str, &str); 99] = [ - (":authority", ""), - (":path", "/"), - ("age", "0"), - ("content-disposition", ""), - ("content-length", "0"), - ("cookie", ""), - ("date", ""), - ("etag", ""), - ("if-modified-since", ""), - ("if-none-match", ""), - ("last-modified", ""), - ("link", ""), - ("location", ""), - ("referer", ""), - ("set-cookie", ""), - (":method", "CONNECT"), - (":method", "DELETE"), - (":method", "GET"), - (":method", "HEAD"), - (":method", "OPTIONS"), - (":method", "POST"), - (":method", "PUT"), - (":scheme", "http"), - (":scheme", "https"), - (":status", "103"), - (":status", "200"), - (":status", "304"), - (":status", "404"), - (":status", "503"), - ("accept", "*/*"), - ("accept", "application/dns-message"), - ("accept-encoding", "gzip, deflate, br"), - ("accept-ranges", "bytes"), - ("access-control-allow-headers", "cache-control"), - ("access-control-allow-headers", "content-type"), - ("access-control-allow-origin", "*"), - ("cache-control", "max-age=0"), - ("cache-control", "max-age=2592000"), - ("cache-control", "max-age=604800"), - ("cache-control", "no-cache"), - ("cache-control", "no-store"), - ("cache-control", "public, max-age=31536000"), - ("content-encoding", "br"), - ("content-encoding", "gzip"), - ("content-type", "application/dns-message"), - ("content-type", "application/javascript"), - ("content-type", "application/json"), - ("content-type", "application/x-www-form-urlencoded"), - ("content-type", "image/gif"), - ("content-type", "image/jpeg"), - ("content-type", "image/png"), - ("content-type", "text/css"), - ("content-type", "text/html; charset=utf-8"), - ("content-type", "text/plain"), - ("content-type", "text/plain;charset=utf-8"), - ("range", "bytes=0-"), - ("strict-transport-security", "max-age=31536000"), +pub const STATIC_TABLE: [(&[u8], &[u8]); 99] = [ + (b":authority", b""), + (b":path", b"/"), + (b"age", b"0"), + (b"content-disposition", b""), + (b"content-length", b"0"), + (b"cookie", b""), + (b"date", b""), + (b"etag", b""), + (b"if-modified-since", b""), + (b"if-none-match", b""), + (b"last-modified", b""), + (b"link", b""), + (b"location", b""), + (b"referer", b""), + (b"set-cookie", b""), + (b":method", b"CONNECT"), + (b":method", b"DELETE"), + (b":method", b"GET"), + (b":method", b"HEAD"), + (b":method", b"OPTIONS"), + (b":method", b"POST"), + (b":method", b"PUT"), + (b":scheme", b"http"), + (b":scheme", b"https"), + (b":status", b"103"), + (b":status", b"200"), + (b":status", b"304"), + (b":status", b"404"), + (b":status", b"503"), + (b"accept", b"*/*"), + (b"accept", b"application/dns-message"), + (b"accept-encoding", b"gzip, deflate, br"), + (b"accept-ranges", b"bytes"), + (b"access-control-allow-headers", b"cache-control"), + (b"access-control-allow-headers", b"content-type"), + (b"access-control-allow-origin", b"*"), + (b"cache-control", b"max-age=0"), + (b"cache-control", b"max-age=2592000"), + (b"cache-control", b"max-age=604800"), + (b"cache-control", b"no-cache"), + (b"cache-control", b"no-store"), + (b"cache-control", b"public, max-age=31536000"), + (b"content-encoding", b"br"), + (b"content-encoding", b"gzip"), + (b"content-type", b"application/dns-message"), + (b"content-type", b"application/javascript"), + (b"content-type", b"application/json"), + (b"content-type", b"application/x-www-form-urlencoded"), + (b"content-type", b"image/gif"), + (b"content-type", b"image/jpeg"), + (b"content-type", b"image/png"), + (b"content-type", b"text/css"), + (b"content-type", b"text/html; charset=utf-8"), + (b"content-type", b"text/plain"), + (b"content-type", b"text/plain;charset=utf-8"), + (b"range", b"bytes=0-"), + (b"strict-transport-security", b"max-age=31536000"), ( - "strict-transport-security", - "max-age=31536000; includesubdomains", + b"strict-transport-security", + b"max-age=31536000; includesubdomains", ), ( - "strict-transport-security", - "max-age=31536000; includesubdomains; preload", + b"strict-transport-security", + b"max-age=31536000; includesubdomains; preload", ), - ("vary", "accept-encoding"), - ("vary", "origin"), - ("x-content-type-options", "nosniff"), - ("x-xss-protection", "1; mode=block"), - (":status", "100"), - (":status", "204"), - (":status", "206"), - (":status", "302"), - (":status", "400"), - (":status", "403"), - (":status", "421"), - (":status", "425"), - (":status", "500"), - ("accept-language", ""), - ("access-control-allow-credentials", "FALSE"), - ("access-control-allow-credentials", "TRUE"), - ("access-control-allow-headers", "*"), - ("access-control-allow-methods", "get"), - ("access-control-allow-methods", "get, post, options"), - ("access-control-allow-methods", "options"), - ("access-control-expose-headers", "content-length"), - ("access-control-request-headers", "content-type"), - ("access-control-request-method", "get"), - ("access-control-request-method", "post"), - ("alt-svc", "clear"), - ("authorization", ""), + (b"vary", b"accept-encoding"), + (b"vary", b"origin"), + (b"x-content-type-options", b"nosniff"), + (b"x-xss-protection", b"1; mode=block"), + (b":status", b"100"), + (b":status", b"204"), + (b":status", b"206"), + (b":status", b"302"), + (b":status", b"400"), + (b":status", b"403"), + (b":status", b"421"), + (b":status", b"425"), + (b":status", b"500"), + (b"accept-language", b""), + (b"access-control-allow-credentials", b"FALSE"), + (b"access-control-allow-credentials", b"TRUE"), + (b"access-control-allow-headers", b"*"), + (b"access-control-allow-methods", b"get"), + (b"access-control-allow-methods", b"get, post, options"), + (b"access-control-allow-methods", b"options"), + (b"access-control-expose-headers", b"content-length"), + (b"access-control-request-headers", b"content-type"), + (b"access-control-request-method", b"get"), + (b"access-control-request-method", b"post"), + (b"alt-svc", b"clear"), + (b"authorization", b""), ( - "content-security-policy", - "script-src 'none'; object-src 'none'; base-uri 'none'", + b"content-security-policy", + b"script-src 'none'; object-src 'none'; base-uri 'none'", ), - ("early-data", "1"), - ("expect-ct", ""), - ("forwarded", ""), - ("if-range", ""), - ("origin", ""), - ("purpose", "prefetch"), - ("server", ""), - ("timing-allow-origin", "*"), - ("upgrade-insecure-requests", "1"), - ("user-agent", ""), - ("x-forwarded-for", ""), - ("x-frame-options", "deny"), - ("x-frame-options", "sameorigin"), + (b"early-data", b"1"), + (b"expect-ct", b""), + (b"forwarded", b""), + (b"if-range", b""), + (b"origin", b""), + (b"purpose", b"prefetch"), + (b"server", b""), + (b"timing-allow-origin", b"*"), + (b"upgrade-insecure-requests", b"1"), + (b"user-agent", b""), + (b"x-forwarded-for", b""), + (b"x-frame-options", b"deny"), + (b"x-frame-options", b"sameorigin"), ]; diff --git a/tools/apps/src/common.rs b/tools/apps/src/common.rs index fa1d7d80..c97274fb 100644 --- a/tools/apps/src/common.rs +++ b/tools/apps/src/common.rs @@ -174,8 +174,20 @@ fn dump_json(reqs: &[Http3Request], output_sink: &mut dyn FnMut(String)) { let mut req_hdrs = req.hdrs.iter().peekable(); while let Some(h) = req_hdrs.next() { writeln!(out, " {{").unwrap(); - writeln!(out, " \"name\": \"{}\",", h.name()).unwrap(); - writeln!(out, " \"value\": \"{}\"", h.value()).unwrap(); + writeln!( + out, + " \"name\": \"{}\",", + std::str::from_utf8(h.name()).unwrap() + ) + .unwrap(); + writeln!( + out, + " \"value\": \"{}\"", + std::str::from_utf8(h.value()) + .unwrap() + .replace("\"", "\\\"") + ) + .unwrap(); if req_hdrs.peek().is_some() { writeln!(out, " }},").unwrap(); @@ -191,11 +203,18 @@ fn dump_json(reqs: &[Http3Request], output_sink: &mut dyn FnMut(String)) { let mut response_hdrs = req.response_hdrs.iter().peekable(); while let Some(h) = response_hdrs.next() { writeln!(out, " {{").unwrap(); - writeln!(out, " \"name\": \"{}\",", h.name()).unwrap(); + writeln!( + out, + " \"name\": \"{}\",", + std::str::from_utf8(h.name()).unwrap() + ) + .unwrap(); writeln!( out, " \"value\": \"{}\"", - h.value().replace("\"", "\\\"") + std::str::from_utf8(h.value()) + .unwrap() + .replace("\"", "\\\"") ) .unwrap(); @@ -790,34 +809,35 @@ impl Http3Conn { }; let mut hdrs = vec![ - quiche::h3::Header::new(":method", &method), - quiche::h3::Header::new(":scheme", url.scheme()), - quiche::h3::Header::new(":authority", &authority), + quiche::h3::Header::new(b":method", method.as_bytes()), + quiche::h3::Header::new(b":scheme", url.scheme().as_bytes()), + quiche::h3::Header::new(b":authority", authority.as_bytes()), quiche::h3::Header::new( - ":path", - &url[url::Position::BeforePath..], + b":path", + &url[url::Position::BeforePath..].as_bytes(), ), - quiche::h3::Header::new("user-agent", "quiche"), + quiche::h3::Header::new(b"user-agent", b"quiche"), ]; // Add custom headers to the request. for header in req_headers { let header_split: Vec<&str> = header.splitn(2, ": ").collect(); + if header_split.len() != 2 { panic!("malformed header provided - \"{}\"", header); } hdrs.push(quiche::h3::Header::new( - header_split[0], - header_split[1], + header_split[0].as_bytes(), + header_split[1].as_bytes(), )); } if body.is_some() { hdrs.push(quiche::h3::Header::new( - "content-length", - &body.as_ref().unwrap().len().to_string(), + b"content-length", + body.as_ref().unwrap().len().to_string().as_bytes(), )); } @@ -892,25 +912,17 @@ impl Http3Conn { // Parse some of the request headers. for hdr in request { match hdr.name() { - ":scheme" => { - scheme = hdr.value(); - }, + b":scheme" => scheme = std::str::from_utf8(hdr.value()).unwrap(), - ":authority" | "host" => { - host = hdr.value(); - }, + b":authority" | b"host" => + host = std::str::from_utf8(hdr.value()).unwrap(), - ":path" => { - path = hdr.value(); - }, + b":path" => path = std::str::from_utf8(hdr.value()).unwrap(), - ":method" => { - method = hdr.value(); - }, + b":method" => method = std::str::from_utf8(hdr.value()).unwrap(), - "priority" => { - priority = hdr.value(); - }, + b"priority" => + priority = std::str::from_utf8(hdr.value()).unwrap(), _ => (), } @@ -918,8 +930,8 @@ impl Http3Conn { if scheme != "http" && scheme != "https" { let headers = vec![ - quiche::h3::Header::new(":status", &"400".to_string()), - quiche::h3::Header::new("server", "quiche"), + quiche::h3::Header::new(b":status", "400".to_string().as_bytes()), + quiche::h3::Header::new(b"server", b"quiche"), ]; return (headers, b"Invalid scheme".to_vec(), priority.to_string()); @@ -967,13 +979,17 @@ impl Http3Conn { }; let mut headers = vec![ - quiche::h3::Header::new(":status", &status.to_string()), - quiche::h3::Header::new("server", "quiche"), - quiche::h3::Header::new("content-length", &body.len().to_string()), + quiche::h3::Header::new(b":status", status.to_string().as_bytes()), + quiche::h3::Header::new(b"server", b"quiche"), + quiche::h3::Header::new( + b"content-length", + body.len().to_string().as_bytes(), + ), ]; if !priority.is_empty() { - headers.push(quiche::h3::Header::new("priority", &priority)); + headers + .push(quiche::h3::Header::new(b"priority", priority.as_bytes())); } (headers, body, priority.to_string()) diff --git a/tools/http3_test/src/lib.rs b/tools/http3_test/src/lib.rs index ea36b991..15937c94 100644 --- a/tools/http3_test/src/lib.rs +++ b/tools/http3_test/src/lib.rs @@ -43,7 +43,7 @@ //! let mut url = url::Url::parse("https://cloudflare-quic.com/b/get").unwrap(); //! let mut reqs = Vec::new(); //! -//! reqs.push(http3_test::Http3Req::new("GET", &url, None, None)); +//! reqs.push(http3_test::Http3Req::new(b"GET", &url, None, None)); //! ``` //! //! Assertions are used to check the received response headers and body @@ -69,8 +69,8 @@ //! let mut url = url::Url::parse("https://cloudflare-quic.com/b/get").unwrap(); //! let mut reqs = Vec::new(); //! -//! let expect_hdrs = Some(vec![quiche::h3::Header::new(":status", "200")]); -//! reqs.push(http3_test::Http3Req::new("GET", &url, None, expect_hdrs)); +//! let expect_hdrs = Some(vec![quiche::h3::Header::new(b":status", "200")]); +//! reqs.push(http3_test::Http3Req::new(b"GET", &url, None, expect_hdrs)); //! ``` //! //! The [`assert_headers!`] macro can be used to validate the received headers, @@ -89,8 +89,8 @@ //! let mut url = url::Url::parse("https://cloudflare-quic.com/b/get").unwrap(); //! let mut reqs = Vec::new(); //! -//! let expect_hdrs = Some(vec![quiche::h3::Header::new(":status", "200")]); -//! reqs.push(http3_test::Http3Req::new("GET", &url, None, expect_hdrs)); +//! let expect_hdrs = Some(vec![quiche::h3::Header::new(b":status", "200")]); +//! reqs.push(http3_test::Http3Req::new(b"GET", &url, None, expect_hdrs)); //! //! // Using a closure... //! let assert = @@ -113,8 +113,8 @@ //! ```no_run //! # let mut url = url::Url::parse("https://cloudflare-quic.com/b/get").unwrap(); //! # let mut reqs = Vec::new(); -//! # let expect_hdrs = Some(vec![quiche::h3::Header::new(":status", "200")]); -//! # reqs.push(http3_test::Http3Req::new("GET", &url, None, expect_hdrs)); +//! # let expect_hdrs = Some(vec![quiche::h3::Header::new(b":status", "200")]); +//! # reqs.push(http3_test::Http3Req::new(b"GET", &url, None, expect_hdrs)); //! # // Using a closure... //! # let assert = |reqs: &[http3_test::Http3Req]| { //! # http3_test::assert_headers!(reqs[0]); @@ -144,8 +144,8 @@ //! ```no_run //! # let mut url = url::Url::parse("https://cloudflare-quic.com/b/get").unwrap(); //! # let mut reqs = Vec::new(); -//! # let expect_hdrs = Some(vec![quiche::h3::Header::new(":status", "200")]); -//! # reqs.push(http3_test::Http3Req::new("GET", &url, None, expect_hdrs)); +//! # let expect_hdrs = Some(vec![quiche::h3::Header::new(b":status", "200")]); +//! # reqs.push(http3_test::Http3Req::new(b"GET", &url, None, expect_hdrs)); //! # // Using a closure... //! # let assert = |reqs: &[http3_test::Http3Req]| { //! # http3_test::assert_headers!(reqs[0]); @@ -184,8 +184,8 @@ //! ```no_run //! # let mut url = url::Url::parse("https://cloudflare-quic.com/b/get").unwrap(); //! # let mut reqs = Vec::new(); -//! # let expect_hdrs = Some(vec![quiche::h3::Header::new(":status", "200")]); -//! # reqs.push(http3_test::Http3Req::new("GET", &url, None, expect_hdrs)); +//! # let expect_hdrs = Some(vec![quiche::h3::Header::new(b":status", "200")]); +//! # reqs.push(http3_test::Http3Req::new(b"GET", &url, None, expect_hdrs)); //! # // Using a closure... //! # let assert = |reqs: &[http3_test::Http3Req]| { //! # http3_test::assert_headers!(reqs[0]); @@ -231,7 +231,7 @@ use std::collections::HashMap; use quiche::h3::Header; -pub const USER_AGENT: &str = "quiche-http3-integration-client"; +pub const USER_AGENT: &[u8] = b"quiche-http3-integration-client"; /// Stores the request, the expected response headers, and the actual response. /// @@ -259,15 +259,18 @@ impl Http3Req { } let mut hdrs = vec![ - Header::new(":method", method), - Header::new(":scheme", url.scheme()), - Header::new(":authority", url.host_str().unwrap()), - Header::new(":path", &path), - Header::new("user-agent", USER_AGENT), + Header::new(b":method", method.as_bytes()), + Header::new(b":scheme", url.scheme().as_bytes()), + Header::new(b":authority", url.host_str().unwrap().as_bytes()), + Header::new(b":path", path.as_bytes()), + Header::new(b"user-agent", USER_AGENT), ]; if let Some(body) = &body { - hdrs.push(Header::new("content-length", &body.len().to_string())); + hdrs.push(Header::new( + b"content-length", + body.len().to_string().as_bytes(), + )); } Http3Req { @@ -297,11 +300,10 @@ macro_rules! assert_headers { if let Some(expect_hdrs) = &$req.expect_resp_hdrs { for hdr in expect_hdrs { match $req.resp_hdrs.iter().find(|&x| x.name() == hdr.name()) { - Some(h) => { assert_eq!(hdr.value(), h.value());}, + Some(h) => assert_eq!(hdr.value(), h.value()), - None => { - panic!("assertion failed: expected response header field {} not present!", hdr.name()); - } + None => + panic!("assertion failed: expected response header field {} not present!", std::str::from_utf8(hdr.name()).unwrap()), } } } diff --git a/tools/http3_test/tests/httpbin_tests.rs b/tools/http3_test/tests/httpbin_tests.rs index 49d200e6..ff53ebbb 100644 --- a/tools/http3_test/tests/httpbin_tests.rs +++ b/tools/http3_test/tests/httpbin_tests.rs @@ -226,7 +226,8 @@ mod httpbin_tests { // Build a single request and expected response with status code fn request_check_status(testpoint: &str, status: usize) -> Vec<Http3Req> { - let expect_hdrs = Some(vec![Header::new(":status", &status.to_string())]); + let expect_hdrs = + Some(vec![Header::new(b":status", status.to_string().as_bytes())]); let url = endpoint(Some(testpoint)); @@ -235,7 +236,7 @@ mod httpbin_tests { // Build a single request with a simple JSON body using the provided method fn request_with_body(method: &str) -> Vec<Http3Req> { - let expect_hdrs = Some(vec![Header::new(":status", "200")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"200")]); let url = endpoint(Some(&method.to_ascii_lowercase())); @@ -249,7 +250,7 @@ mod httpbin_tests { ); req.hdrs - .push(Header::new("content-type", "application/json")); + .push(Header::new(b"content-type", b"application/json")); vec![req] } @@ -272,7 +273,7 @@ mod httpbin_tests { #[test] fn get() { let mut reqs = Vec::new(); - let expect_hdrs = Some(vec![Header::new(":status", "200")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"200")]); let mut url = endpoint(Some("get")); @@ -300,16 +301,16 @@ mod httpbin_tests { #[test] fn req_no_method() { let mut reqs = Vec::new(); - let expect_hdrs = Some(vec![Header::new(":status", "400")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"400")]); let url = endpoint(Some("get")); let path = String::from(url.path()); let hdrs = vec![ - Header::new(":scheme", url.scheme()), - Header::new(":authority", url.host_str().unwrap()), - Header::new(":path", &path), - Header::new("user-agent", USER_AGENT), + Header::new(b":scheme", url.scheme().as_bytes()), + Header::new(b":authority", url.host_str().unwrap().as_bytes()), + Header::new(b":path", path.as_bytes()), + Header::new(b"user-agent", USER_AGENT), ]; let req = Http3Req { @@ -333,17 +334,17 @@ mod httpbin_tests { #[test] fn req_empty_method() { let mut reqs = Vec::new(); - let expect_hdrs = Some(vec![Header::new(":status", "400")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"400")]); let url = endpoint(Some("get")); let path = String::from(url.path()); let hdrs = vec![ - Header::new(":method", ""), - Header::new(":scheme", url.scheme()), - Header::new(":authority", url.host_str().unwrap()), - Header::new(":path", &path), - Header::new("user-agent", USER_AGENT), + Header::new(b":method", b""), + Header::new(b":scheme", url.scheme().as_bytes()), + Header::new(b":authority", url.host_str().unwrap().as_bytes()), + Header::new(b":path", path.as_bytes()), + Header::new(b"user-agent", USER_AGENT), ]; let req = Http3Req { @@ -367,17 +368,17 @@ mod httpbin_tests { #[test] fn req_invalid_method() { let mut reqs = Vec::new(); - let expect_hdrs = Some(vec![Header::new(":status", "400")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"400")]); let url = endpoint(Some("get")); let path = String::from(url.path()); let hdrs = vec![ - Header::new(":method", "$GET"), - Header::new(":scheme", url.scheme()), - Header::new(":path", &path), - Header::new(":authority", url.host_str().unwrap()), - Header::new("user-agent", USER_AGENT), + Header::new(b":method", b"$GET"), + Header::new(b":scheme", url.scheme().as_bytes()), + Header::new(b":path", path.as_bytes()), + Header::new(b":authority", url.host_str().unwrap().as_bytes()), + Header::new(b"user-agent", USER_AGENT), ]; let req = Http3Req { @@ -401,16 +402,16 @@ mod httpbin_tests { #[test] fn req_no_scheme() { let mut reqs = Vec::new(); - let expect_hdrs = Some(vec![Header::new(":status", "400")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"400")]); let url = endpoint(Some("get")); let path = String::from(url.path()); let hdrs = vec![ - Header::new(":method", "GET"), - Header::new(":authority", url.host_str().unwrap()), - Header::new(":path", &path), - Header::new("user-agent", USER_AGENT), + Header::new(b":method", b"GET"), + Header::new(b":authority", url.host_str().unwrap().as_bytes()), + Header::new(b":path", path.as_bytes()), + Header::new(b"user-agent", USER_AGENT), ]; let req = Http3Req { @@ -434,17 +435,17 @@ mod httpbin_tests { #[test] fn req_empty_scheme() { let mut reqs = Vec::new(); - let expect_hdrs = Some(vec![Header::new(":status", "400")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"400")]); let url = endpoint(Some("get")); let path = String::from(url.path()); let hdrs = vec![ - Header::new(":method", "GET"), - Header::new(":scheme", ""), - Header::new(":authority", url.host_str().unwrap()), - Header::new(":path", &path), - Header::new("user-agent", USER_AGENT), + Header::new(b":method", b"GET"), + Header::new(b":scheme", b""), + Header::new(b":authority", url.host_str().unwrap().as_bytes()), + Header::new(b":path", path.as_bytes()), + Header::new(b"user-agent", USER_AGENT), ]; let req = Http3Req { @@ -468,17 +469,17 @@ mod httpbin_tests { #[test] fn req_invalid_scheme() { let mut reqs = Vec::new(); - let expect_hdrs = Some(vec![Header::new(":status", "400")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"400")]); let url = endpoint(Some("get")); let path = String::from(url.path()); let hdrs = vec![ - Header::new(":method", "GET"), - Header::new(":scheme", "$fail"), - Header::new(":path", &path), - Header::new(":authority", url.host_str().unwrap()), - Header::new("user-agent", USER_AGENT), + Header::new(b":method", b"GET"), + Header::new(b":scheme", b"$fail"), + Header::new(b":path", path.as_bytes()), + Header::new(b":authority", url.host_str().unwrap().as_bytes()), + Header::new(b"user-agent", USER_AGENT), ]; let req = Http3Req { @@ -502,16 +503,16 @@ mod httpbin_tests { #[test] fn req_no_authority() { let mut reqs = Vec::new(); - let expect_hdrs = Some(vec![Header::new(":status", "400")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"400")]); let url = endpoint(Some("get")); let path = String::from(url.path()); let hdrs = vec![ - Header::new(":method", "GET"), - Header::new(":scheme", url.scheme()), - Header::new(":path", &path), - Header::new("user-agent", USER_AGENT), + Header::new(b":method", b"GET"), + Header::new(b":scheme", url.scheme().as_bytes()), + Header::new(b":path", path.as_bytes()), + Header::new(b"user-agent", USER_AGENT), ]; let req = Http3Req { @@ -535,17 +536,17 @@ mod httpbin_tests { #[test] fn req_empty_authority() { let mut reqs = Vec::new(); - let expect_hdrs = Some(vec![Header::new(":status", "400")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"400")]); let url = endpoint(Some("get")); let path = String::from(url.path()); let hdrs = vec![ - Header::new(":method", "GET"), - Header::new(":scheme", url.scheme()), - Header::new(":authority", ""), - Header::new(":path", &path), - Header::new("user-agent", USER_AGENT), + Header::new(b":method", b"GET"), + Header::new(b":scheme", url.scheme().as_bytes()), + Header::new(b":authority", b""), + Header::new(b":path", path.as_bytes()), + Header::new(b"user-agent", USER_AGENT), ]; let req = Http3Req { @@ -569,15 +570,15 @@ mod httpbin_tests { #[test] fn req_no_path() { let mut reqs = Vec::new(); - let expect_hdrs = Some(vec![Header::new(":status", "400")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"400")]); let url = endpoint(Some("get")); let hdrs = vec![ - Header::new(":method", "GET"), - Header::new(":scheme", url.scheme()), - Header::new(":authority", url.host_str().unwrap()), - Header::new("user-agent", USER_AGENT), + Header::new(b":method", b"GET"), + Header::new(b":scheme", url.scheme().as_bytes()), + Header::new(b":authority", url.host_str().unwrap().as_bytes()), + Header::new(b"user-agent", USER_AGENT), ]; let req = Http3Req { @@ -601,16 +602,16 @@ mod httpbin_tests { #[test] fn req_empty_path() { let mut reqs = Vec::new(); - let expect_hdrs = Some(vec![Header::new(":status", "400")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"400")]); let url = endpoint(Some("get")); let hdrs = vec![ - Header::new(":method", "GET"), - Header::new(":scheme", url.scheme()), - Header::new(":path", ""), - Header::new(":authority", url.host_str().unwrap()), - Header::new("user-agent", USER_AGENT), + Header::new(b":method", b"GET"), + Header::new(b":scheme", url.scheme().as_bytes()), + Header::new(b":path", b""), + Header::new(b":authority", url.host_str().unwrap().as_bytes()), + Header::new(b"user-agent", USER_AGENT), ]; let req = Http3Req { @@ -634,17 +635,17 @@ mod httpbin_tests { #[test] fn req_invalid_pseudoheader_name() { let mut reqs = Vec::new(); - let expect_hdrs = Some(vec![Header::new(":status", "400")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"400")]); let url = endpoint(Some("get")); let path = String::from(url.path()); let hdrs = vec![ - Header::new(":$method", "GET"), - Header::new(":scheme", url.scheme()), - Header::new(":path", &path), - Header::new(":authority", url.host_str().unwrap()), - Header::new("user-agent", USER_AGENT), + Header::new(b":$method", b"GET"), + Header::new(b":scheme", url.scheme().as_bytes()), + Header::new(b":path", path.as_bytes()), + Header::new(b":authority", url.host_str().unwrap().as_bytes()), + Header::new(b"user-agent", USER_AGENT), ]; let req = Http3Req { @@ -668,18 +669,18 @@ mod httpbin_tests { #[test] fn req_duplicate_pseudoheader_bad_order() { let mut reqs = Vec::new(); - let expect_hdrs = Some(vec![Header::new(":status", "400")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"400")]); let url = endpoint(Some("get")); let path = String::from(url.path()); let hdrs = vec![ - Header::new(":method", "GET"), - Header::new(":scheme", url.scheme()), - Header::new(":path", &path), - Header::new(":authority", url.host_str().unwrap()), - Header::new("user-agent", USER_AGENT), - Header::new(":method", "GET"), + Header::new(b":method", b"GET"), + Header::new(b":scheme", url.scheme().as_bytes()), + Header::new(b":path", path.as_bytes()), + Header::new(b":authority", url.host_str().unwrap().as_bytes()), + Header::new(b"user-agent", USER_AGENT), + Header::new(b":method", b"GET"), ]; let req = Http3Req { @@ -703,7 +704,7 @@ mod httpbin_tests { #[test] fn req_too_large_headers() { let mut reqs = Vec::new(); - let expect_hdrs = Some(vec![Header::new(":status", "200")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"200")]); let url = endpoint(Some("get")); reqs.push(Http3Req::new("GET", &url, None, expect_hdrs.clone())); @@ -714,7 +715,10 @@ mod httpbin_tests { if let Some(headers) = &extra_headers() { for (name, val) in headers { println!("{}: {}", name, val); - reqs[0].hdrs.push(Header::new(&name, val.as_str().unwrap())); + reqs[0].hdrs.push(Header::new( + name.as_bytes(), + val.as_str().unwrap().as_bytes(), + )); } }; @@ -728,7 +732,7 @@ mod httpbin_tests { #[test] fn frames_duplicate_settings() { let mut reqs = Vec::new(); - let expect_hdrs = Some(vec![Header::new(":status", "200")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"200")]); let url = endpoint(Some("get")); reqs.push(Http3Req::new("GET", &url, None, expect_hdrs.clone())); @@ -761,7 +765,7 @@ mod httpbin_tests { #[test] fn frames_max_push_on_request() { let mut reqs = Vec::new(); - let expect_hdrs = Some(vec![Header::new(":status", "200")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"200")]); let url = endpoint(Some("get")); reqs.push(Http3Req::new("GET", &url, None, expect_hdrs.clone())); @@ -791,7 +795,7 @@ mod httpbin_tests { #[test] fn frames_data_on_control() { let mut reqs = Vec::new(); - let expect_hdrs = Some(vec![Header::new(":status", "200")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"200")]); let url = endpoint(Some("get")); reqs.push(Http3Req::new("GET", &url, None, expect_hdrs.clone())); @@ -821,7 +825,7 @@ mod httpbin_tests { #[test] fn frames_data_before_headers() { let mut reqs = Vec::new(); - let expect_hdrs = Some(vec![Header::new(":status", "200")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"200")]); let url = endpoint(Some("get")); reqs.push(Http3Req::new("GET", &url, None, expect_hdrs.clone())); @@ -851,7 +855,7 @@ mod httpbin_tests { #[test] fn frames_too_small_headers() { let mut reqs = Vec::new(); - let expect_hdrs = Some(vec![Header::new(":status", "200")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"200")]); let url = endpoint(Some("get")); reqs.push(Http3Req::new("GET", &url, None, expect_hdrs.clone())); @@ -881,7 +885,7 @@ mod httpbin_tests { #[test] fn stream_close_control() { let mut reqs = Vec::new(); - let expect_hdrs = Some(vec![Header::new(":status", "200")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"200")]); let url = endpoint(Some("get")); reqs.push(Http3Req::new("GET", &url, None, expect_hdrs.clone())); @@ -904,7 +908,7 @@ mod httpbin_tests { #[test] fn stream_close_qpack_enc() { let mut reqs = Vec::new(); - let expect_hdrs = Some(vec![Header::new(":status", "200")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"200")]); let url = endpoint(Some("get")); reqs.push(Http3Req::new("GET", &url, None, expect_hdrs.clone())); @@ -927,7 +931,7 @@ mod httpbin_tests { #[test] fn stream_close_qpack_dec() { let mut reqs = Vec::new(); - let expect_hdrs = Some(vec![Header::new(":status", "200")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"200")]); let url = endpoint(Some("get")); reqs.push(Http3Req::new("GET", &url, None, expect_hdrs.clone())); @@ -969,7 +973,10 @@ mod httpbin_tests { assert_headers!(reqs[0]); let json = jsonify(&reqs[0].resp_body); - assert_eq!(json.user_agent, Some(USER_AGENT.to_string())); + assert_eq!( + json.user_agent, + String::from_utf8(USER_AGENT.to_vec()).ok() + ); }; assert_eq!(Ok(()), do_test(reqs, assert, true)); @@ -978,7 +985,7 @@ mod httpbin_tests { #[test] fn headers() { let mut reqs = Vec::new(); - let expect_hdrs = Some(vec![Header::new(":status", "200")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"200")]); let mut url = endpoint(Some("headers")); url.set_query(Some("show_env=1")); // reveal X-Forwarded-* headers @@ -986,7 +993,10 @@ mod httpbin_tests { if let Some(headers) = &extra_headers() { for (name, val) in headers { - reqs[0].hdrs.push(Header::new(&name, val.as_str().unwrap())); + reqs[0].hdrs.push(Header::new( + name.as_bytes(), + val.as_str().unwrap().as_bytes(), + )); } }; @@ -1050,8 +1060,8 @@ mod httpbin_tests { let mut reqs = Vec::new(); let expect_hdrs = Some(vec![ - Header::new(":status", "200"), - Header::new("content-type", "text/html; charset=utf-8"), + Header::new(b":status", b"200"), + Header::new(b"content-type", b"text/html; charset=utf-8"), ]); let url = endpoint(Some("encoding/utf8")); @@ -1066,14 +1076,14 @@ mod httpbin_tests { let mut reqs = Vec::new(); let expect_hdrs = Some(vec![ - Header::new(":status", "200"), - Header::new("content-encoding", "gzip"), + Header::new(b":status", b"200"), + Header::new(b"content-encoding", b"gzip"), ]); let url = endpoint(Some("gzip")); let mut req = Http3Req::new("GET", &url, None, expect_hdrs); - req.hdrs.push(Header::new("accept-encoding", "gzip")); + req.hdrs.push(Header::new(b"accept-encoding", b"gzip")); reqs.push(req); @@ -1086,12 +1096,12 @@ mod httpbin_tests { // Not all servers actually take up the deflate option, // so don't check content-type response header. - let expect_hdrs = Some(vec![Header::new(":status", "200")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"200")]); let url = endpoint(Some("deflate")); let mut req = Http3Req::new("GET", &url, None, expect_hdrs); - req.hdrs.push(Header::new("accept-encoding", "deflate")); + req.hdrs.push(Header::new(b"accept-encoding", b"deflate")); reqs.push(req); assert_eq!(Ok(()), do_test(reqs, assert_headers_only, true)); @@ -1103,8 +1113,10 @@ mod httpbin_tests { for i in (200..600).step_by(100) { for j in 0..5 { - let expect_hdrs = - Some(vec![Header::new(":status", &(i + j).to_string())]); + let expect_hdrs = Some(vec![Header::new( + b":status", + (i + j).to_string().as_bytes(), + )]); let testpoint = format!("{}/{}", "status", i + j); let url = endpoint(Some(&testpoint)); @@ -1119,7 +1131,7 @@ mod httpbin_tests { #[test] fn response_headers() { let mut reqs = Vec::new(); - let expect_hdrs = Some(vec![Header::new(":status", "200")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"200")]); let mut url = endpoint(Some("response-headers")); url.set_query(Some( @@ -1149,8 +1161,8 @@ mod httpbin_tests { // Request 1 let expect_hdrs = Some(vec![ - Header::new(":status", "302"), - Header::new("location", "https://example.com"), + Header::new(b":status", b"302"), + Header::new(b"location", b"https://example.com"), ]); url.set_query(Some("url=https://example.com")); @@ -1159,15 +1171,15 @@ mod httpbin_tests { // Request 2 let expect_hdrs = Some(vec![ - Header::new(":status", "307"), - Header::new("location", "https://example.com"), + Header::new(b":status", b"307"), + Header::new(b"location", b"https://example.com"), ]); url.set_query(Some("url=https://example.com&status_code=307")); reqs.push(Http3Req::new("GET", &url, None, expect_hdrs)); // Request 3 - let expect_hdrs = Some(vec![Header::new(":status", "302")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"302")]); let url = endpoint(Some("relative-redirect/3")); reqs.push(Http3Req::new("GET", &url, None, expect_hdrs)); @@ -1180,14 +1192,14 @@ mod httpbin_tests { let mut reqs = Vec::new(); // Request 1 - let expect_hdrs = Some(vec![Header::new(":status", "302")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"302")]); let mut url = endpoint(Some("cookies/set")); url.set_query(Some("k1=v1")); reqs.push(Http3Req::new("GET", &url, None, expect_hdrs)); // Request 2 - let expect_hdrs = Some(vec![Header::new(":status", "302")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"302")]); let mut url = endpoint(Some("cookies/set")); url.set_query(Some("k1=v1")); @@ -1203,8 +1215,8 @@ mod httpbin_tests { let url = endpoint(Some("basic-auth/user/passwd")); let expect_hdrs = Some(vec![ - Header::new(":status", "401"), - Header::new("www-authenticate", "Basic realm=\"Fake Realm\""), + Header::new(b":status", b"401"), + Header::new(b"www-authenticate", b"Basic realm=\"Fake Realm\""), ]); reqs.push(Http3Req::new("GET", &url, None, expect_hdrs)); @@ -1219,7 +1231,7 @@ mod httpbin_tests { let sizes = [1, 50, 100]; for size in &sizes { - let expect_hdrs = Some(vec![Header::new(":status", "200")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"200")]); let testpoint = format!("{}/{}", "stream", size.to_string()); @@ -1262,7 +1274,7 @@ mod httpbin_tests { let delays = [1, 10, 30]; for delay in &delays { - let expect_hdrs = Some(vec![Header::new(":status", "200")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"200")]); let testpoint = format!("{}/{}", "delay", delay); let url = endpoint(Some(&testpoint)); @@ -1280,7 +1292,7 @@ mod httpbin_tests { let durations = [1, 10, 30]; for duration in &durations { - let expect_hdrs = Some(vec![Header::new(":status", "200")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"200")]); let mut url = endpoint(Some("drip")); url.set_query(Some(&format!( @@ -1299,7 +1311,7 @@ mod httpbin_tests { let mut reqs = Vec::new(); // Request 1 - let expect_hdrs = Some(vec![Header::new(":status", "200")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"200")]); let url = endpoint(Some("range/102400")); @@ -1308,21 +1320,21 @@ mod httpbin_tests { // Request 2 let expect_hdrs = Some(vec![ - Header::new(":status", "206"), - Header::new("content-range", "bytes 0-49/102400"), + Header::new(b":status", b"206"), + Header::new(b"content-range", b"bytes 0-49/102400"), ]); let mut req = Http3Req::new("GET", &url, None, expect_hdrs); - req.hdrs.push(Header::new("range", "bytes=0-49")); + req.hdrs.push(Header::new(b"range", b"bytes=0-49")); reqs.push(req); // Request 3 let expect_hdrs = Some(vec![ - Header::new(":status", "206"), - Header::new("content-range", "bytes 100-10000/102400"), + Header::new(b":status", b"206"), + Header::new(b"content-range", b"bytes 100-10000/102400"), ]); let mut req = Http3Req::new("GET", &url, None, expect_hdrs); - req.hdrs.push(Header::new("range", "bytes=100-10000")); + req.hdrs.push(Header::new(b"range", b"bytes=100-10000")); reqs.push(req); assert_eq!(Ok(()), do_test(reqs, assert_headers_only, true)); @@ -1333,7 +1345,7 @@ mod httpbin_tests { let mut reqs = Vec::new(); // Request 1 - let expect_hdrs = Some(vec![Header::new(":status", "200")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"200")]); let url = endpoint(Some("cache")); @@ -1341,19 +1353,19 @@ mod httpbin_tests { reqs.push(req); // Request 2 - let expect_hdrs = Some(vec![Header::new(":status", "304")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"304")]); let mut req = Http3Req::new("GET", &url, None, expect_hdrs); req.hdrs.push(Header::new( - "if-modified-since", - "Wed, 21 Oct 2015 07:28:00 GMT", + b"if-modified-since", + b"Wed, 21 Oct 2015 07:28:00 GMT", )); reqs.push(req); // Request 3 - let expect_hdrs = Some(vec![Header::new(":status", "304")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"304")]); let mut req = Http3Req::new("GET", &url, None, expect_hdrs); - req.hdrs.push(Header::new("if-none-match", "*")); + req.hdrs.push(Header::new(b"if-none-match", b"*")); reqs.push(req); assert_eq!(Ok(()), do_test(reqs, assert_headers_only, true)); @@ -1367,8 +1379,8 @@ mod httpbin_tests { for size in &sizes { let expect_hdrs = Some(vec![ - Header::new(":status", "200"), - Header::new("content-length", &size.to_string()), + Header::new(b":status", b"200"), + Header::new(b"content-length", size.to_string().as_bytes()), ]); let testpoint = format!("{}/{}", "bytes", size.to_string()); @@ -1387,7 +1399,7 @@ mod httpbin_tests { let sizes = [10, 100, 1000, 10000, 100_000]; for size in &sizes { - let expect_hdrs = Some(vec![Header::new(":status", "200")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"200")]); let testpoint = format!("{}/{}", "stream-bytes", size.to_string()); let url = endpoint(Some(&testpoint)); @@ -1403,41 +1415,41 @@ mod httpbin_tests { let mut reqs = Vec::new(); // Request 1 - let expect_hdrs = Some(vec![Header::new(":status", "406")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"406")]); let url = endpoint(Some("image")); let mut req = Http3Req::new("GET", &url, None, expect_hdrs); - req.hdrs.push(Header::new("accept", "*/*")); + req.hdrs.push(Header::new(b"accept", b"*/*")); reqs.push(req); // Request 2 let expect_hdrs = Some(vec![ - Header::new(":status", "200"), - Header::new("content-type", "image/png"), + Header::new(b":status", b"200"), + Header::new(b"content-type", b"image/png"), ]); let mut req = Http3Req::new("GET", &url, None, expect_hdrs); - req.hdrs.push(Header::new("accept", "image/*")); + req.hdrs.push(Header::new(b"accept", b"image/*")); reqs.push(req); // Multiple requests based on accept let formats = ["image/webp", "image/svg+xml", "image/jpeg", "image/png"]; for format in &formats { let expect_hdrs = Some(vec![ - Header::new(":status", "200"), - Header::new("content-type", &format), + Header::new(b":status", b"200"), + Header::new(b"content-type", format.as_bytes()), ]); let mut req = Http3Req::new("GET", &url, None, expect_hdrs); - req.hdrs.push(Header::new("accept", &format)); + req.hdrs.push(Header::new(b"accept", format.as_bytes())); reqs.push(req); } // Multiple requests based on path for format in &formats { let expect_hdrs = Some(vec![ - Header::new(":status", "200"), - Header::new("content-type", &format), + Header::new(b":status", b"200"), + Header::new(b"content-type", format.as_bytes()), ]); let testpoint = if format == &"image/svg+xml" { @@ -1448,7 +1460,7 @@ mod httpbin_tests { let url = endpoint(Some(&testpoint)); let mut req = Http3Req::new("GET", &url, None, expect_hdrs); - req.hdrs.push(Header::new("accept", &format)); + req.hdrs.push(Header::new(b"accept", format.as_bytes())); reqs.push(req); } @@ -1458,7 +1470,7 @@ mod httpbin_tests { #[test] fn form() { let mut reqs = Vec::new(); - let expect_hdrs = Some(vec![Header::new(":status", "200")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"200")]); let url = endpoint(Some("post")); @@ -1472,8 +1484,8 @@ mod httpbin_tests { ); req.hdrs.push(Header::new( - "content-type", - "application/x-www-form-urlencoded", + b"content-type", + b"application/x-www-form-urlencoded", )); reqs.push(req); @@ -1523,7 +1535,7 @@ mod httpbin_tests { fn zero_length_body() { let mut reqs = Vec::new(); - let expect_hdrs = Some(vec![Header::new(":status", "200")]); + let expect_hdrs = Some(vec![Header::new(b":status", b"200")]); let url = endpoint(Some("stream/0")); |