From 52fd183495b95654fb88b67f28c5154a018b016a Mon Sep 17 00:00:00 2001 From: Kylee Tilley Date: Sat, 19 Apr 2025 04:51:23 -0500 Subject: [PATCH 01/10] Forking --- .github/FUNDING.yml | 1 - Cargo.toml | 15 +- LICENSE-MIT | 2 +- README.md | 5 +- benches/parse.rs | 205 +++++--- build.rs | 30 +- fuzz/fuzz_targets/parse_chunk_size.rs | 2 +- fuzz/fuzz_targets/parse_headers.rs | 4 +- fuzz/fuzz_targets/parse_request.rs | 4 +- fuzz/fuzz_targets/parse_request_multspaces.rs | 6 +- fuzz/fuzz_targets/parse_response.rs | 4 +- .../fuzz_targets/parse_response_multspaces.rs | 6 +- src/lib.rs | 463 +++++++++++------- tests/uri.rs | 295 +---------- 14 files changed, 464 insertions(+), 578 deletions(-) delete mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index a6b3376..0000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1 +0,0 @@ -github: [seanmonstar] diff --git a/Cargo.toml b/Cargo.toml index 4aee710..7e5d698 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,14 +1,19 @@ [package] -name = "httparse" +name = "httparser" version = "1.10.1" -authors = ["Sean McArthur "] +authors = ["Kylee Tilley "] license = "MIT OR Apache-2.0" -description = "A tiny, safe, speedy, zero-copy HTTP/1.x parser." -repository = "https://github.com/seanmonstar/httparse" +description = "A parser for partially correct HTTP messages" +repository = "https://github.com/testingrequired/httparser" documentation = "https://docs.rs/httparse" readme = "README.md" keywords = ["http", "parser", "no_std"] -categories = ["network-programming", "no-std", "parser-implementations", "web-programming"] +categories = [ + "network-programming", + "no-std", + "parser-implementations", + "web-programming", +] edition = "2018" build = "build.rs" diff --git a/LICENSE-MIT b/LICENSE-MIT index 5e854c5..7bc4977 100644 --- a/LICENSE-MIT +++ b/LICENSE-MIT @@ -1,3 +1,4 @@ +Copyright (c) 2025 Kylee Tilley Copyright (c) 2015-2025 Sean McArthur Permission is hereby granted, free of charge, to any person obtaining a copy @@ -17,4 +18,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - diff --git a/README.md b/README.md index ce79aa6..3bbc450 100644 --- a/README.md +++ b/README.md @@ -12,15 +12,14 @@ Works with `no_std`, simply disable the `std` Cargo feature. [Changelog](https://github.com/seanmonstar/httparse/releases) - [discord-badge]: https://img.shields.io/discord/500028886025895936.svg?logo=discord [discord-url]: https://discord.gg/kkwpueZ ## Usage ```rust -let mut headers = [httparse::EMPTY_HEADER; 64]; -let mut req = httparse::Request::new(&mut headers); +let mut headers = [httparser::EMPTY_HEADER; 64]; +let mut req = httparser::Request::new(&mut headers); let buf = b"GET /index.html HTTP/1.1\r\nHost"; assert!(req.parse(buf)?.is_partial()); diff --git a/benches/parse.rs b/benches/parse.rs index 7794d31..dfb7c0b 100644 --- a/benches/parse.rs +++ b/benches/parse.rs @@ -1,6 +1,6 @@ use std::time::Duration; -use criterion::{black_box, criterion_group, criterion_main, Criterion, Throughput, BatchSize}; +use criterion::{black_box, criterion_group, criterion_main, BatchSize, Criterion, Throughput}; const REQ_SHORT: &[u8] = b"\ GET / HTTP/1.0\r\n\ @@ -22,35 +22,47 @@ Cookie: wp_ozh_wsa_visits=2; wp_ozh_wsa_visit_lasttime=xxxxxxxxxx; __utma=xxxxxx fn req(c: &mut Criterion) { c.benchmark_group("req") .throughput(Throughput::Bytes(REQ.len() as u64)) - .bench_function("req", |b| b.iter_batched_ref(|| { - [httparse::Header { - name: "", - value: &[], - }; 16] - },|headers| { - let mut req = httparse::Request::new(headers); - assert_eq!( - black_box(req.parse(REQ).unwrap()), - httparse::Status::Complete(REQ.len()) - ); - }, BatchSize::SmallInput)); + .bench_function("req", |b| { + b.iter_batched_ref( + || { + [httparser::Header { + name: "", + value: &[], + }; 16] + }, + |headers| { + let mut req = httparser::Request::new(headers); + assert_eq!( + black_box(req.parse(REQ).unwrap()), + httparser::Status::Complete(REQ.len()) + ); + }, + BatchSize::SmallInput, + ) + }); } fn req_short(c: &mut Criterion) { c.benchmark_group("req_short") .throughput(Throughput::Bytes(REQ_SHORT.len() as u64)) - .bench_function("req_short", |b| b.iter_batched_ref(|| { - [httparse::Header { - name: "", - value: &[], - }; 16] - },|headers| { - let mut req = httparse::Request::new(headers); - assert_eq!( - req.parse(black_box(REQ_SHORT)).unwrap(), - httparse::Status::Complete(REQ_SHORT.len()) - ); - }, BatchSize::SmallInput)); + .bench_function("req_short", |b| { + b.iter_batched_ref( + || { + [httparser::Header { + name: "", + value: &[], + }; 16] + }, + |headers| { + let mut req = httparser::Request::new(headers); + assert_eq!( + req.parse(black_box(REQ_SHORT)).unwrap(), + httparser::Status::Complete(REQ_SHORT.len()) + ); + }, + BatchSize::SmallInput, + ) + }); } const RESP_SHORT: &[u8] = b"\ @@ -75,46 +87,59 @@ Cookie: wp_ozh_wsa_visits=2; wp_ozh_wsa_visit_lasttime=xxxxxxxxxx; __utma=xxxxxx fn resp(c: &mut Criterion) { c.benchmark_group("resp") .throughput(Throughput::Bytes(RESP.len() as u64)) - .bench_function("resp", |b| b.iter_batched_ref(|| { - [httparse::Header { - name: "", - value: &[], - }; 16] - }, |headers| { - let mut resp = httparse::Response::new(headers); - assert_eq!( - resp.parse(black_box(RESP)).unwrap(), - httparse::Status::Complete(RESP.len()) - ); - }, BatchSize::SmallInput)); + .bench_function("resp", |b| { + b.iter_batched_ref( + || { + [httparser::Header { + name: "", + value: &[], + }; 16] + }, + |headers| { + let mut resp = httparser::Response::new(headers); + assert_eq!( + resp.parse(black_box(RESP)).unwrap(), + httparser::Status::Complete(RESP.len()) + ); + }, + BatchSize::SmallInput, + ) + }); } fn resp_short(c: &mut Criterion) { c.benchmark_group("resp_short") .throughput(Throughput::Bytes(RESP_SHORT.len() as u64)) - .bench_function("resp_short", |b| b.iter_batched_ref(|| { - [httparse::Header { - name: "", - value: &[], - }; 16] - }, - |headers| { - let mut resp = httparse::Response::new(headers); - assert_eq!( - resp.parse(black_box(RESP_SHORT)).unwrap(), - httparse::Status::Complete(RESP_SHORT.len()) - ); - }, BatchSize::SmallInput)); + .bench_function("resp_short", |b| { + b.iter_batched_ref( + || { + [httparser::Header { + name: "", + value: &[], + }; 16] + }, + |headers| { + let mut resp = httparser::Response::new(headers); + assert_eq!( + resp.parse(black_box(RESP_SHORT)).unwrap(), + httparser::Status::Complete(RESP_SHORT.len()) + ); + }, + BatchSize::SmallInput, + ) + }); } fn uri(c: &mut Criterion) { fn _uri(c: &mut Criterion, name: &str, input: &'static [u8]) { c.benchmark_group("uri") - .throughput(Throughput::Bytes(input.len() as u64)) - .bench_function(name, |b| b.iter(|| { - let mut b = httparse::_benchable::Bytes::new(black_box(input)); - httparse::_benchable::parse_uri(&mut b).unwrap() - })); + .throughput(Throughput::Bytes(input.len() as u64)) + .bench_function(name, |b| { + b.iter(|| { + let mut b = httparser::_benchable::Bytes::new(black_box(input)); + httparser::_benchable::parse_uri(&mut b).unwrap() + }) + }); } const S: &[u8] = b" "; @@ -124,25 +149,35 @@ fn uri(c: &mut Criterion) { // 1b to 4096b for p in 0..=12 { let n = 1 << p; - _uri(c, &format!("uri_{:04}b", n), [chunk_4k[..n].to_vec(), S.into()].concat().leak()); + _uri( + c, + &format!("uri_{:04}b", n), + [chunk_4k[..n].to_vec(), S.into()].concat().leak(), + ); } } fn header(c: &mut Criterion) { fn _header(c: &mut Criterion, name: &str, input: &'static [u8]) { c.benchmark_group("header") - .throughput(Throughput::Bytes(input.len() as u64)) - .bench_function(name, |b| b.iter_batched_ref(|| [httparse::EMPTY_HEADER; 128],|headers| { - let status = httparse::parse_headers(black_box(input), headers).unwrap(); - black_box(status.unwrap()).0 - }, BatchSize::SmallInput)); + .throughput(Throughput::Bytes(input.len() as u64)) + .bench_function(name, |b| { + b.iter_batched_ref( + || [httparser::EMPTY_HEADER; 128], + |headers| { + let status = httparser::parse_headers(black_box(input), headers).unwrap(); + black_box(status.unwrap()).0 + }, + BatchSize::SmallInput, + ) + }); } const RN: &[u8] = b"\r\n"; const RNRN: &[u8] = b"\r\n\r\n"; const TINY_RN: &[u8] = b"a: b\r\n"; // minimal header line const XFOOBAR: &[u8] = b"X-Foobar"; - let xfoobar_4k = XFOOBAR.repeat(4096/XFOOBAR.len()); + let xfoobar_4k = XFOOBAR.repeat(4096 / XFOOBAR.len()); // header names 1b to 4096b for p in 0..=12 { @@ -161,18 +196,24 @@ fn header(c: &mut Criterion) { // 1 to 128 for p in 0..=7 { let n = 1 << p; - _header(c, &format!("count_{:03}", n), [TINY_RN.repeat(n), RN.into()].concat().leak()); + _header( + c, + &format!("count_{:03}", n), + [TINY_RN.repeat(n), RN.into()].concat().leak(), + ); } } fn version(c: &mut Criterion) { fn _version(c: &mut Criterion, name: &str, input: &'static [u8]) { c.benchmark_group("version") - .throughput(Throughput::Bytes(input.len() as u64)) - .bench_function(name, |b| b.iter(|| { - let mut b = httparse::_benchable::Bytes::new(black_box(input)); - httparse::_benchable::parse_version(&mut b).unwrap() - })); + .throughput(Throughput::Bytes(input.len() as u64)) + .bench_function(name, |b| { + b.iter(|| { + let mut b = httparser::_benchable::Bytes::new(black_box(input)); + httparser::_benchable::parse_version(&mut b).unwrap() + }) + }); } _version(c, "http10", b"HTTP/1.0\r\n"); @@ -183,17 +224,25 @@ fn version(c: &mut Criterion) { fn method(c: &mut Criterion) { fn _method(c: &mut Criterion, name: &str, input: &[u8]) { c.benchmark_group("method") - .throughput(Throughput::Bytes(input.len() as u64)) - .bench_function(name, |b| b.iter(|| { - let mut b = httparse::_benchable::Bytes::new(black_box(input)); - httparse::_benchable::parse_method(&mut b).unwrap() - })); + .throughput(Throughput::Bytes(input.len() as u64)) + .bench_function(name, |b| { + b.iter(|| { + let mut b = httparser::_benchable::Bytes::new(black_box(input)); + httparser::_benchable::parse_method(&mut b).unwrap() + }) + }); } // Common methods should be fast-pathed - const COMMON_METHODS: &[&str] = &["GET", "HEAD", "POST", "PUT", "DELETE", "CONNECT", "OPTIONS", "TRACE", "PATCH"]; + const COMMON_METHODS: &[&str] = &[ + "GET", "HEAD", "POST", "PUT", "DELETE", "CONNECT", "OPTIONS", "TRACE", "PATCH", + ]; for method in COMMON_METHODS { - _method(c, &method.to_lowercase(), format!("{} / HTTP/1.1\r\n", method).as_bytes()); + _method( + c, + &method.to_lowercase(), + format!("{} / HTTP/1.1\r\n", method).as_bytes(), + ); } // Custom methods should be infrequent and thus not worth optimizing _method(c, "custom", b"CUSTOM / HTTP/1.1\r\n"); @@ -224,8 +273,8 @@ fn many_requests(c: &mut Criterion) { .bench_function("_", |b| { b.iter(|| { requests.iter().for_each(|req| { - let mut b = httparse::_benchable::Bytes::new(black_box(req.as_bytes())); - httparse::_benchable::parse_method(&mut b).unwrap(); + let mut b = httparser::_benchable::Bytes::new(black_box(req.as_bytes())); + httparser::_benchable::parse_method(&mut b).unwrap(); }); }) }); @@ -234,7 +283,7 @@ fn many_requests(c: &mut Criterion) { const WARMUP: Duration = Duration::from_millis(100); const MTIME: Duration = Duration::from_millis(100); const SAMPLES: usize = 200; -criterion_group!{ +criterion_group! { name = benches; config = Criterion::default().sample_size(SAMPLES).warm_up_time(WARMUP).measurement_time(MTIME); targets = req, req_short, resp, resp_short, uri, header, version, method, many_requests diff --git a/build.rs b/build.rs index 6c45358..8462ae1 100644 --- a/build.rs +++ b/build.rs @@ -12,9 +12,8 @@ fn main() { .expect("failed to check 'rustc --version'") .stdout; - let raw_version = String::from_utf8(output) - .expect("rustc version output should be utf-8"); - + let raw_version = String::from_utf8(output).expect("rustc version output should be utf-8"); + let version = match Version::parse(&raw_version) { Ok(version) => version, Err(err) => { @@ -42,7 +41,10 @@ fn enable_simd(version: Version) { let env_disable = "CARGO_CFG_HTTPARSE_DISABLE_SIMD"; if var_is(env_disable, "1") { - println!("cargo:warning=detected {} environment variable, disabling SIMD", env_disable); + println!( + "cargo:warning=detected {} environment variable, disabling SIMD", + env_disable + ); return; } @@ -65,10 +67,12 @@ fn enable_simd(version: Version) { // since the compiler will eliminate several branches with its internal // cfg(target_feature) usage. - let env_runtime_only = "CARGO_CFG_HTTPARSE_DISABLE_SIMD_COMPILETIME"; if var_is(env_runtime_only, "1") { - println!("cargo:warning=detected {} environment variable, using runtime SIMD detection only", env_runtime_only); + println!( + "cargo:warning=detected {} environment variable, using runtime SIMD detection only", + env_runtime_only + ); return; } let feature_list = match env::var_os("CARGO_CFG_TARGET_FEATURE") { @@ -77,14 +81,14 @@ fn enable_simd(version: Version) { Err(_) => { println!("cargo:warning=CARGO_CFG_TARGET_FEATURE was not valid utf-8"); return; - }, + } }, None => { println!("cargo:warning=CARGO_CFG_TARGET_FEATURE was not set"); - return - }, + return; + } }; - + let features = feature_list.split(',').map(|s| s.trim()); if features.clone().any(|f| f == "sse4.2") { println!("cargo:rustc-cfg=httparse_simd_target_feature_sse42"); @@ -95,7 +99,7 @@ fn enable_simd(version: Version) { } #[derive(Debug, Clone, Copy, PartialEq, PartialOrd)] -struct Version (u32, u32, u32); +struct Version(u32, u32, u32); impl Version { fn parse(s: &str) -> Result { @@ -112,11 +116,11 @@ impl Version { None => s, }) .map(|s| s.parse::().map_err(|e| e.to_string())); - + if iter.clone().count() != 3 { return Err(format!("not enough version parts: {:?}", s)); } - + let major = iter.next().unwrap()?; let minor = iter.next().unwrap()?; let patch = iter.next().unwrap()?; diff --git a/fuzz/fuzz_targets/parse_chunk_size.rs b/fuzz/fuzz_targets/parse_chunk_size.rs index b543c61..807a514 100644 --- a/fuzz/fuzz_targets/parse_chunk_size.rs +++ b/fuzz/fuzz_targets/parse_chunk_size.rs @@ -2,5 +2,5 @@ use libfuzzer_sys::fuzz_target; fuzz_target!(|data: &[u8]| { - httparse::parse_chunk_size(data); + httparser::parse_chunk_size(data); }); diff --git a/fuzz/fuzz_targets/parse_headers.rs b/fuzz/fuzz_targets/parse_headers.rs index 2e0309d..5f63e15 100644 --- a/fuzz/fuzz_targets/parse_headers.rs +++ b/fuzz/fuzz_targets/parse_headers.rs @@ -2,6 +2,6 @@ use libfuzzer_sys::fuzz_target; fuzz_target!(|data: &[u8]| { - let mut headers = [httparse::EMPTY_HEADER; 16]; - httparse::parse_headers(data, &mut headers); + let mut headers = [httparser::EMPTY_HEADER; 16]; + httparser::parse_headers(data, &mut headers); }); diff --git a/fuzz/fuzz_targets/parse_request.rs b/fuzz/fuzz_targets/parse_request.rs index 37654b9..1694554 100644 --- a/fuzz/fuzz_targets/parse_request.rs +++ b/fuzz/fuzz_targets/parse_request.rs @@ -2,7 +2,7 @@ use libfuzzer_sys::fuzz_target; fuzz_target!(|data: &[u8]| { - let mut headers = [httparse::EMPTY_HEADER; 16]; - let mut req = httparse::Request::new(&mut headers); + let mut headers = [httparser::EMPTY_HEADER; 16]; + let mut req = httparser::Request::new(&mut headers); req.parse(data); }); diff --git a/fuzz/fuzz_targets/parse_request_multspaces.rs b/fuzz/fuzz_targets/parse_request_multspaces.rs index dfa8395..e7174d7 100644 --- a/fuzz/fuzz_targets/parse_request_multspaces.rs +++ b/fuzz/fuzz_targets/parse_request_multspaces.rs @@ -3,9 +3,9 @@ use libfuzzer_sys::fuzz_target; fuzz_target!(|data: &[u8]| { - let mut headers = [httparse::EMPTY_HEADER; 16]; - let mut resp = httparse::Request::new(&mut headers); - let _ = httparse::ParserConfig::default() + let mut headers = [httparser::EMPTY_HEADER; 16]; + let mut resp = httparser::Request::new(&mut headers); + let _ = httparser::ParserConfig::default() .allow_multiple_spaces_in_request_line_delimiters(true) .parse_request(&mut resp, data); }); diff --git a/fuzz/fuzz_targets/parse_response.rs b/fuzz/fuzz_targets/parse_response.rs index 757723f..2578c8e 100644 --- a/fuzz/fuzz_targets/parse_response.rs +++ b/fuzz/fuzz_targets/parse_response.rs @@ -2,7 +2,7 @@ use libfuzzer_sys::fuzz_target; fuzz_target!(|data: &[u8]| { - let mut headers = [httparse::EMPTY_HEADER; 16]; - let mut resp = httparse::Response::new(&mut headers); + let mut headers = [httparser::EMPTY_HEADER; 16]; + let mut resp = httparser::Response::new(&mut headers); let _ = resp.parse(data); }); diff --git a/fuzz/fuzz_targets/parse_response_multspaces.rs b/fuzz/fuzz_targets/parse_response_multspaces.rs index ceba2f4..e44c1dc 100644 --- a/fuzz/fuzz_targets/parse_response_multspaces.rs +++ b/fuzz/fuzz_targets/parse_response_multspaces.rs @@ -3,9 +3,9 @@ use libfuzzer_sys::fuzz_target; fuzz_target!(|data: &[u8]| { - let mut headers = [httparse::EMPTY_HEADER; 16]; - let mut resp = httparse::Response::new(&mut headers); - let _ = httparse::ParserConfig::default() + let mut headers = [httparser::EMPTY_HEADER; 16]; + let mut resp = httparser::Response::new(&mut headers); + let _ = httparser::ParserConfig::default() .allow_multiple_spaces_in_response_status_delimiters(true) .parse_response(&mut resp, data); }); diff --git a/src/lib.rs b/src/lib.rs index 1a99bc9..3e73e15 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,23 +25,24 @@ //! `-C target_cpu=native` allows the detection to become compile time checks, //! making it *even* faster. -use core::{fmt, mem, result, str}; use core::mem::MaybeUninit; +use core::{fmt, mem, result, str}; use crate::iter::Bytes; mod iter; -#[macro_use] mod macros; +#[macro_use] +mod macros; mod simd; #[doc(hidden)] // Expose some internal functions so we can bench them individually // WARNING: Exported for internal benchmarks, not fit for public consumption pub mod _benchable { + pub use super::iter::Bytes; + pub use super::parse_method; pub use super::parse_uri; pub use super::parse_version; - pub use super::parse_method; - pub use super::iter::Bytes; } /// Determines if byte is a method token char. @@ -86,12 +87,10 @@ pub(crate) fn is_header_name_token(b: u8) -> bool { TOKEN_MAP[b as usize] } - static HEADER_VALUE_MAP: [bool; 256] = byte_map!( b'\t' | b' '..=0x7e | 0x80..=0xFF ); - #[inline] pub(crate) fn is_header_value_token(b: u8) -> bool { HEADER_VALUE_MAP[b as usize] @@ -172,7 +171,7 @@ pub enum Status { /// The completed result. Complete(T), /// A partial result. - Partial + Partial, } impl Status { @@ -181,7 +180,7 @@ impl Status { pub fn is_complete(&self) -> bool { match *self { Status::Complete(..) => true, - Status::Partial => false + Status::Partial => false, } } @@ -190,7 +189,7 @@ impl Status { pub fn is_partial(&self) -> bool { match *self { Status::Complete(..) => false, - Status::Partial => true + Status::Partial => true, } } @@ -200,7 +199,7 @@ impl Status { pub fn unwrap(self) -> T { match self { Status::Complete(t) => t, - Status::Partial => panic!("Tried to unwrap Status::Partial") + Status::Partial => panic!("Tried to unwrap Status::Partial"), } } } @@ -219,10 +218,7 @@ pub struct ParserConfig { impl ParserConfig { /// Sets whether spaces and tabs should be allowed after header names in responses. - pub fn allow_spaces_after_header_name_in_responses( - &mut self, - value: bool, - ) -> &mut Self { + pub fn allow_spaces_after_header_name_in_responses(&mut self, value: bool) -> &mut Self { self.allow_spaces_after_header_name_in_responses = value; self } @@ -264,7 +260,10 @@ impl ParserConfig { /// line to contain the other mentioned whitespace characters. /// /// [spec]: https://httpwg.org/http-core/draft-ietf-httpbis-messaging-latest.html#rfc.section.4.p.3 - pub fn allow_multiple_spaces_in_response_status_delimiters(&mut self, value: bool) -> &mut Self { + pub fn allow_multiple_spaces_in_response_status_delimiters( + &mut self, + value: bool, + ) -> &mut Self { self.allow_multiple_spaces_in_response_status_delimiters = value; self } @@ -284,23 +283,20 @@ impl ParserConfig { /// /// ```rust /// let buf = b"HTTP/1.1 200 OK\r\nFolded-Header: hello\r\n there \r\n\r\n"; - /// let mut headers = [httparse::EMPTY_HEADER; 16]; - /// let mut response = httparse::Response::new(&mut headers); + /// let mut headers = [httparser::EMPTY_HEADER; 16]; + /// let mut response = httparser::Response::new(&mut headers); /// - /// let res = httparse::ParserConfig::default() + /// let res = httparser::ParserConfig::default() /// .allow_obsolete_multiline_headers_in_responses(true) /// .parse_response(&mut response, buf); /// - /// assert_eq!(res, Ok(httparse::Status::Complete(buf.len()))); + /// assert_eq!(res, Ok(httparser::Status::Complete(buf.len()))); /// /// assert_eq!(response.headers.len(), 1); /// assert_eq!(response.headers[0].name, "Folded-Header"); /// assert_eq!(response.headers[0].value, b"hello\r\n there"); /// ``` - pub fn allow_obsolete_multiline_headers_in_responses( - &mut self, - value: bool, - ) -> &mut Self { + pub fn allow_obsolete_multiline_headers_in_responses(&mut self, value: bool) -> &mut Self { self.allow_obsolete_multiline_headers_in_responses = value; self } @@ -319,13 +315,13 @@ impl ParserConfig { /// /// ```rust /// let buf = b"HTTP/1.1 200 OK\r\n Space-Before-Header: hello there\r\n\r\n"; - /// let mut headers = [httparse::EMPTY_HEADER; 1]; - /// let mut response = httparse::Response::new(&mut headers[..]); - /// let result = httparse::ParserConfig::default() + /// let mut headers = [httparser::EMPTY_HEADER; 1]; + /// let mut response = httparser::Response::new(&mut headers[..]); + /// let result = httparser::ParserConfig::default() /// .allow_space_before_first_header_name(true) /// .parse_response(&mut response, buf); /// - /// assert_eq!(result, Ok(httparse::Status::Complete(buf.len()))); + /// assert_eq!(result, Ok(httparser::Status::Complete(buf.len()))); /// assert_eq!(response.version.unwrap(), 1); /// assert_eq!(response.code.unwrap(), 200); /// assert_eq!(response.reason.unwrap(), "OK"); @@ -392,19 +388,13 @@ impl ParserConfig { /// with whitespace, those will be ignored too. An error will be emitted /// nonetheless if it finds `\0` or a lone `\r` while looking for the /// next line. - pub fn ignore_invalid_headers_in_responses( - &mut self, - value: bool, - ) -> &mut Self { + pub fn ignore_invalid_headers_in_responses(&mut self, value: bool) -> &mut Self { self.ignore_invalid_headers_in_responses = value; self } /// Sets whether invalid header lines should be silently ignored in requests. - pub fn ignore_invalid_headers_in_requests( - &mut self, - value: bool, - ) -> &mut Self { + pub fn ignore_invalid_headers_in_requests(&mut self, value: bool) -> &mut Self { self.ignore_invalid_headers_in_requests = value; self } @@ -439,8 +429,8 @@ impl ParserConfig { /// /// ```no_run /// let buf = b"GET /404 HTTP/1.1\r\nHost:"; -/// let mut headers = [httparse::EMPTY_HEADER; 16]; -/// let mut req = httparse::Request::new(&mut headers); +/// let mut headers = [httparser::EMPTY_HEADER; 16]; +/// let mut req = httparser::Request::new(&mut headers); /// let res = req.parse(buf).unwrap(); /// if res.is_partial() { /// match req.path { @@ -463,7 +453,7 @@ pub struct Request<'headers, 'buf> { /// The request minor version, such as `1` for `HTTP/1.1`. pub version: Option, /// The request headers. - pub headers: &'headers mut [Header<'buf>] + pub headers: &'headers mut [Header<'buf>], } impl<'h, 'b> Request<'h, 'b> { @@ -541,7 +531,7 @@ impl<'h, 'b> Request<'h, 'b> { // put the original headers back self.headers = &mut *(headers as *mut [Header<'_>]); other - }, + } } } } @@ -611,7 +601,7 @@ pub struct Response<'headers, 'buf> { /// Contains an empty string if the reason-phrase was missing or contained invalid characters. pub reason: Option<&'buf str>, /// The response headers. - pub headers: &'headers mut [Header<'buf>] + pub headers: &'headers mut [Header<'buf>], } impl<'h, 'b> Response<'h, 'b> { @@ -645,7 +635,7 @@ impl<'h, 'b> Response<'h, 'b> { // put the original headers back self.headers = &mut *(headers as *mut [Header<'_>]); other - }, + } } } } @@ -683,12 +673,12 @@ impl<'h, 'b> Response<'h, 'b> { } bytes.slice(); self.reason = Some(complete!(parse_reason(&mut bytes))); - }, + } b'\r' => { expect!(bytes.next() == b'\n' => Err(Error::Status)); bytes.slice(); self.reason = Some(""); - }, + } b'\n' => { bytes.slice(); self.reason = Some(""); @@ -696,14 +686,14 @@ impl<'h, 'b> Response<'h, 'b> { _ => return Err(Error::Status), } - let len = orig_len - bytes.len(); let headers_len = complete!(parse_headers_iter_uninit( &mut headers, &mut bytes, &HeaderParserConfig { allow_spaces_after_header_name: config.allow_spaces_after_header_name_in_responses, - allow_obsolete_multiline_headers: config.allow_obsolete_multiline_headers_in_responses, + allow_obsolete_multiline_headers: config + .allow_obsolete_multiline_headers_in_responses, allow_space_before_first_header_name: config.allow_space_before_first_header_name, ignore_invalid_headers: config.ignore_invalid_headers_in_responses } @@ -747,9 +737,12 @@ impl fmt::Debug for Header<'_> { /// # Example /// /// ``` -/// let headers = [httparse::EMPTY_HEADER; 64]; +/// let headers = [httparser::EMPTY_HEADER; 64]; /// ``` -pub const EMPTY_HEADER: Header<'static> = Header { name: "", value: b"" }; +pub const EMPTY_HEADER: Header<'static> = Header { + name: "", + value: b"", +}; #[inline] #[doc(hidden)] @@ -930,9 +923,9 @@ fn parse_code(bytes: &mut Bytes<'_>) -> Result { let tens = expect!(bytes.next() == b'0'..=b'9' => Err(Error::Status)); let ones = expect!(bytes.next() == b'0'..=b'9' => Err(Error::Status)); - Ok(Status::Complete((hundreds - b'0') as u16 * 100 + - (tens - b'0') as u16 * 10 + - (ones - b'0') as u16)) + Ok(Status::Complete( + (hundreds - b'0') as u16 * 100 + (tens - b'0') as u16 * 10 + (ones - b'0') as u16, + )) } /// Parse a buffer of bytes as headers. @@ -946,11 +939,11 @@ fn parse_code(bytes: &mut Bytes<'_>) -> Result { /// /// ``` /// let buf = b"Host: foo.bar\nAccept: */*\n\nblah blah"; -/// let mut headers = [httparse::EMPTY_HEADER; 4]; -/// assert_eq!(httparse::parse_headers(buf, &mut headers), -/// Ok(httparse::Status::Complete((27, &[ -/// httparse::Header { name: "Host", value: b"foo.bar" }, -/// httparse::Header { name: "Accept", value: b"*/*" } +/// let mut headers = [httparser::EMPTY_HEADER; 4]; +/// assert_eq!(httparser::parse_headers(buf, &mut headers), +/// Ok(httparser::Status::Complete((27, &[ +/// httparser::Header { name: "Host", value: b"foo.bar" }, +/// httparser::Header { name: "Accept", value: b"*/*" } /// ][..])))); /// ``` pub fn parse_headers<'b: 'h, 'h>( @@ -958,7 +951,11 @@ pub fn parse_headers<'b: 'h, 'h>( mut dst: &'h mut [Header<'b>], ) -> Result<(usize, &'h [Header<'b>])> { let mut iter = Bytes::new(src); - let pos = complete!(parse_headers_iter(&mut dst, &mut iter, &HeaderParserConfig::default())); + let pos = complete!(parse_headers_iter( + &mut dst, + &mut iter, + &HeaderParserConfig::default() + )); Ok(Status::Complete((pos, dst))) } @@ -1007,9 +1004,8 @@ struct HeaderParserConfig { fn parse_headers_iter_uninit<'a>( headers: &mut &mut [MaybeUninit>], bytes: &mut Bytes<'a>, - config: &HeaderParserConfig + config: &HeaderParserConfig, ) -> Result { - /* Flow of this function is pretty complex, especially with macros, * so this struct makes sure we shrink `headers` to only parsed ones. * Comparing to previous code, this only may introduce some additional @@ -1217,7 +1213,7 @@ fn parse_headers_iter_uninit<'a>( let uninit_header = match iter.next() { Some(header) => header, - None => break 'headers + None => break 'headers, }; // trim trailing whitespace in the header @@ -1226,7 +1222,7 @@ fn parse_headers_iter_uninit<'a>( .rposition(|b| *b != b' ' && *b != b'\t' && *b != b'\r' && *b != b'\n') { // There is at least one non-whitespace character. - &value_slice[0..last_visible+1] + &value_slice[0..last_visible + 1] } else { // There is no non-whitespace character. This can only happen when value_slice is // empty. @@ -1252,11 +1248,10 @@ fn parse_headers_iter_uninit<'a>( /// /// ``` /// let buf = b"4\r\nRust\r\n0\r\n\r\n"; -/// assert_eq!(httparse::parse_chunk_size(buf), -/// Ok(httparse::Status::Complete((3, 4)))); +/// assert_eq!(httparser::parse_chunk_size(buf), +/// Ok(httparser::Status::Complete((3, 4)))); /// ``` -pub fn parse_chunk_size(buf: &[u8]) - -> result::Result, InvalidChunkSize> { +pub fn parse_chunk_size(buf: &[u8]) -> result::Result, InvalidChunkSize> { const RADIX: u64 = 16; let mut bytes = Bytes::new(buf); let mut size = 0; @@ -1266,7 +1261,7 @@ pub fn parse_chunk_size(buf: &[u8]) loop { let b = next!(bytes); match b { - b'0' ..= b'9' if in_chunk_size => { + b'0'..=b'9' if in_chunk_size => { if count > 15 { return Err(InvalidChunkSize); } @@ -1280,8 +1275,8 @@ pub fn parse_chunk_size(buf: &[u8]) } size *= RADIX; size += (b - b'0') as u64; - }, - b'a' ..= b'f' if in_chunk_size => { + } + b'a'..=b'f' if in_chunk_size => { if count > 15 { return Err(InvalidChunkSize); } @@ -1292,7 +1287,7 @@ pub fn parse_chunk_size(buf: &[u8]) size *= RADIX; size += (b + 10 - b'a') as u64; } - b'A' ..= b'F' if in_chunk_size => { + b'A'..=b'F' if in_chunk_size => { if count > 15 { return Err(InvalidChunkSize); } @@ -1303,12 +1298,10 @@ pub fn parse_chunk_size(buf: &[u8]) size *= RADIX; size += (b + 10 - b'A') as u64; } - b'\r' => { - match next!(bytes) { - b'\n' => break, - _ => return Err(InvalidChunkSize), - } - } + b'\r' => match next!(bytes) { + b'\n' => break, + _ => return Err(InvalidChunkSize), + }, // If we weren't in the extension yet, the ";" signals its start b';' if !in_ext => { in_ext = true; @@ -1335,28 +1328,28 @@ pub fn parse_chunk_size(buf: &[u8]) #[cfg(test)] mod tests { - use super::{Error, Request, Response, Status, EMPTY_HEADER, parse_chunk_size}; + use super::{parse_chunk_size, Error, Request, Response, Status, EMPTY_HEADER}; const NUM_OF_HEADERS: usize = 4; macro_rules! req { - ($name:ident, $buf:expr, |$arg:ident| $body:expr) => ( + ($name:ident, $buf:expr, |$arg:ident| $body:expr) => { req! {$name, $buf, Ok(Status::Complete($buf.len())), |$arg| $body } - ); - ($name:ident, $buf:expr, $len:expr, |$arg:ident| $body:expr) => ( - #[test] - fn $name() { - let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS]; - let mut req = Request::new(&mut headers[..]); - let status = req.parse($buf.as_ref()); - assert_eq!(status, $len); - closure(req); - - fn closure($arg: Request) { - $body + }; + ($name:ident, $buf:expr, $len:expr, |$arg:ident| $body:expr) => { + #[test] + fn $name() { + let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS]; + let mut req = Request::new(&mut headers[..]); + let status = req.parse($buf.as_ref()); + assert_eq!(status, $len); + closure(req); + + fn closure($arg: Request) { + $body + } } - } - ) + }; } req! { @@ -1614,7 +1607,6 @@ mod tests { |_r| {} } - req! { test_request_with_invalid_but_short_version, b"GET / HTTP/1!", @@ -1644,23 +1636,23 @@ mod tests { } macro_rules! res { - ($name:ident, $buf:expr, |$arg:ident| $body:expr) => ( + ($name:ident, $buf:expr, |$arg:ident| $body:expr) => { res! {$name, $buf, Ok(Status::Complete($buf.len())), |$arg| $body } - ); - ($name:ident, $buf:expr, $len:expr, |$arg:ident| $body:expr) => ( - #[test] - fn $name() { - let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS]; - let mut res = Response::new(&mut headers[..]); - let status = res.parse($buf.as_ref()); - assert_eq!(status, $len); - closure(res); - - fn closure($arg: Response) { - $body + }; + ($name:ident, $buf:expr, $len:expr, |$arg:ident| $body:expr) => { + #[test] + fn $name() { + let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS]; + let mut res = Response::new(&mut headers[..]); + let status = res.parse($buf.as_ref()); + assert_eq!(status, $len); + closure(res); + + fn closure($arg: Response) { + $body + } } - } - ) + }; } res! { @@ -1824,7 +1816,10 @@ mod tests { let mut response = Response::new(&mut headers[..]); let result = crate::ParserConfig::default() .allow_spaces_after_header_name_in_responses(true) - .parse_response(&mut response, RESPONSE_WITH_WHITESPACE_BETWEEN_HEADER_NAME_AND_COLON); + .parse_response( + &mut response, + RESPONSE_WITH_WHITESPACE_BETWEEN_HEADER_NAME_AND_COLON, + ); assert_eq!(result, Ok(Status::Complete(77))); assert_eq!(response.version.unwrap(), 1); @@ -1843,7 +1838,10 @@ mod tests { let mut response = Response::new(&mut headers[..]); let result = crate::ParserConfig::default() .ignore_invalid_headers_in_responses(true) - .parse_response(&mut response, RESPONSE_WITH_WHITESPACE_BETWEEN_HEADER_NAME_AND_COLON); + .parse_response( + &mut response, + RESPONSE_WITH_WHITESPACE_BETWEEN_HEADER_NAME_AND_COLON, + ); assert_eq!(result, Ok(Status::Complete(77))); assert_eq!(response.version.unwrap(), 1); @@ -1872,7 +1870,10 @@ mod tests { let mut request = Request::new(&mut headers[..]); let result = crate::ParserConfig::default() .ignore_invalid_headers_in_requests(true) - .parse_request(&mut request, REQUEST_WITH_WHITESPACE_BETWEEN_HEADER_NAME_AND_COLON); + .parse_request( + &mut request, + REQUEST_WITH_WHITESPACE_BETWEEN_HEADER_NAME_AND_COLON, + ); assert_eq!(result, Ok(Status::Complete(36))); } @@ -1897,7 +1898,12 @@ mod tests { .allow_obsolete_multiline_headers_in_responses(true) .parse_response(&mut response, RESPONSE_WITH_OBSOLETE_LINE_FOLDING_AT_START); - assert_eq!(result, Ok(Status::Complete(RESPONSE_WITH_OBSOLETE_LINE_FOLDING_AT_START.len()))); + assert_eq!( + result, + Ok(Status::Complete( + RESPONSE_WITH_OBSOLETE_LINE_FOLDING_AT_START.len() + )) + ); assert_eq!(response.version.unwrap(), 1); assert_eq!(response.code.unwrap(), 200); assert_eq!(response.reason.unwrap(), "OK"); @@ -1926,7 +1932,12 @@ mod tests { .allow_obsolete_multiline_headers_in_responses(true) .parse_response(&mut response, RESPONSE_WITH_OBSOLETE_LINE_FOLDING_AT_END); - assert_eq!(result, Ok(Status::Complete(RESPONSE_WITH_OBSOLETE_LINE_FOLDING_AT_END.len()))); + assert_eq!( + result, + Ok(Status::Complete( + RESPONSE_WITH_OBSOLETE_LINE_FOLDING_AT_END.len() + )) + ); assert_eq!(response.version.unwrap(), 1); assert_eq!(response.code.unwrap(), 200); assert_eq!(response.reason.unwrap(), "OK"); @@ -1955,7 +1966,12 @@ mod tests { .allow_obsolete_multiline_headers_in_responses(true) .parse_response(&mut response, RESPONSE_WITH_OBSOLETE_LINE_FOLDING_IN_MIDDLE); - assert_eq!(result, Ok(Status::Complete(RESPONSE_WITH_OBSOLETE_LINE_FOLDING_IN_MIDDLE.len()))); + assert_eq!( + result, + Ok(Status::Complete( + RESPONSE_WITH_OBSOLETE_LINE_FOLDING_IN_MIDDLE.len() + )) + ); assert_eq!(response.version.unwrap(), 1); assert_eq!(response.code.unwrap(), 200); assert_eq!(response.reason.unwrap(), "OK"); @@ -1982,9 +1998,17 @@ mod tests { let mut response = Response::new(&mut headers[..]); let result = crate::ParserConfig::default() .allow_obsolete_multiline_headers_in_responses(true) - .parse_response(&mut response, RESPONSE_WITH_OBSOLETE_LINE_FOLDING_IN_EMPTY_HEADER); + .parse_response( + &mut response, + RESPONSE_WITH_OBSOLETE_LINE_FOLDING_IN_EMPTY_HEADER, + ); - assert_eq!(result, Ok(Status::Complete(RESPONSE_WITH_OBSOLETE_LINE_FOLDING_IN_EMPTY_HEADER.len()))); + assert_eq!( + result, + Ok(Status::Complete( + RESPONSE_WITH_OBSOLETE_LINE_FOLDING_IN_EMPTY_HEADER.len() + )) + ); assert_eq!(response.version.unwrap(), 1); assert_eq!(response.code.unwrap(), 200); assert_eq!(response.reason.unwrap(), "OK"); @@ -1996,23 +2020,55 @@ mod tests { #[test] fn test_chunk_size() { assert_eq!(parse_chunk_size(b"0\r\n"), Ok(Status::Complete((3, 0)))); - assert_eq!(parse_chunk_size(b"12\r\nchunk"), Ok(Status::Complete((4, 18)))); - assert_eq!(parse_chunk_size(b"3086d\r\n"), Ok(Status::Complete((7, 198765)))); - assert_eq!(parse_chunk_size(b"3735AB1;foo bar*\r\n"), Ok(Status::Complete((18, 57891505)))); - assert_eq!(parse_chunk_size(b"3735ab1 ; baz \r\n"), Ok(Status::Complete((16, 57891505)))); + assert_eq!( + parse_chunk_size(b"12\r\nchunk"), + Ok(Status::Complete((4, 18))) + ); + assert_eq!( + parse_chunk_size(b"3086d\r\n"), + Ok(Status::Complete((7, 198765))) + ); + assert_eq!( + parse_chunk_size(b"3735AB1;foo bar*\r\n"), + Ok(Status::Complete((18, 57891505))) + ); + assert_eq!( + parse_chunk_size(b"3735ab1 ; baz \r\n"), + Ok(Status::Complete((16, 57891505))) + ); assert_eq!(parse_chunk_size(b"77a65\r"), Ok(Status::Partial)); assert_eq!(parse_chunk_size(b"ab"), Ok(Status::Partial)); - assert_eq!(parse_chunk_size(b"567f8a\rfoo"), Err(crate::InvalidChunkSize)); - assert_eq!(parse_chunk_size(b"567f8a\rfoo"), Err(crate::InvalidChunkSize)); - assert_eq!(parse_chunk_size(b"567xf8a\r\n"), Err(crate::InvalidChunkSize)); - assert_eq!(parse_chunk_size(b"ffffffffffffffff\r\n"), Ok(Status::Complete((18, u64::MAX)))); - assert_eq!(parse_chunk_size(b"1ffffffffffffffff\r\n"), Err(crate::InvalidChunkSize)); - assert_eq!(parse_chunk_size(b"Affffffffffffffff\r\n"), Err(crate::InvalidChunkSize)); - assert_eq!(parse_chunk_size(b"fffffffffffffffff\r\n"), Err(crate::InvalidChunkSize)); + assert_eq!( + parse_chunk_size(b"567f8a\rfoo"), + Err(crate::InvalidChunkSize) + ); + assert_eq!( + parse_chunk_size(b"567f8a\rfoo"), + Err(crate::InvalidChunkSize) + ); + assert_eq!( + parse_chunk_size(b"567xf8a\r\n"), + Err(crate::InvalidChunkSize) + ); + assert_eq!( + parse_chunk_size(b"ffffffffffffffff\r\n"), + Ok(Status::Complete((18, u64::MAX))) + ); + assert_eq!( + parse_chunk_size(b"1ffffffffffffffff\r\n"), + Err(crate::InvalidChunkSize) + ); + assert_eq!( + parse_chunk_size(b"Affffffffffffffff\r\n"), + Err(crate::InvalidChunkSize) + ); + assert_eq!( + parse_chunk_size(b"fffffffffffffffff\r\n"), + Err(crate::InvalidChunkSize) + ); } - static RESPONSE_WITH_MULTIPLE_SPACE_DELIMITERS: &[u8] = - b"HTTP/1.1 200 OK\r\n\r\n"; + static RESPONSE_WITH_MULTIPLE_SPACE_DELIMITERS: &[u8] = b"HTTP/1.1 200 OK\r\n\r\n"; #[test] fn test_forbid_response_with_multiple_space_delimiters() { @@ -2031,7 +2087,12 @@ mod tests { .allow_multiple_spaces_in_response_status_delimiters(true) .parse_response(&mut response, RESPONSE_WITH_MULTIPLE_SPACE_DELIMITERS); - assert_eq!(result, Ok(Status::Complete(RESPONSE_WITH_MULTIPLE_SPACE_DELIMITERS.len()))); + assert_eq!( + result, + Ok(Status::Complete( + RESPONSE_WITH_MULTIPLE_SPACE_DELIMITERS.len() + )) + ); assert_eq!(response.version.unwrap(), 1); assert_eq!(response.code.unwrap(), 200); assert_eq!(response.reason.unwrap(), "OK"); @@ -2040,8 +2101,7 @@ mod tests { /// This is technically allowed by the spec, but we only support multiple spaces as an option, /// not stray `\r`s. - static RESPONSE_WITH_WEIRD_WHITESPACE_DELIMITERS: &[u8] = - b"HTTP/1.1 200\rOK\r\n\r\n"; + static RESPONSE_WITH_WEIRD_WHITESPACE_DELIMITERS: &[u8] = b"HTTP/1.1 200\rOK\r\n\r\n"; #[test] fn test_forbid_response_with_weird_whitespace_delimiters() { @@ -2062,8 +2122,7 @@ mod tests { assert_eq!(result, Err(crate::Error::Status)); } - static REQUEST_WITH_MULTIPLE_SPACE_DELIMITERS: &[u8] = - b"GET / HTTP/1.1\r\n\r\n"; + static REQUEST_WITH_MULTIPLE_SPACE_DELIMITERS: &[u8] = b"GET / HTTP/1.1\r\n\r\n"; #[test] fn test_forbid_request_with_multiple_space_delimiters() { @@ -2082,7 +2141,12 @@ mod tests { .allow_multiple_spaces_in_request_line_delimiters(true) .parse_request(&mut request, REQUEST_WITH_MULTIPLE_SPACE_DELIMITERS); - assert_eq!(result, Ok(Status::Complete(REQUEST_WITH_MULTIPLE_SPACE_DELIMITERS.len()))); + assert_eq!( + result, + Ok(Status::Complete( + REQUEST_WITH_MULTIPLE_SPACE_DELIMITERS.len() + )) + ); assert_eq!(request.method.unwrap(), "GET"); assert_eq!(request.path.unwrap(), "/"); assert_eq!(request.version.unwrap(), 1); @@ -2091,8 +2155,7 @@ mod tests { /// This is technically allowed by the spec, but we only support multiple spaces as an option, /// not stray `\r`s. - static REQUEST_WITH_WEIRD_WHITESPACE_DELIMITERS: &[u8] = - b"GET\r/\rHTTP/1.1\r\n\r\n"; + static REQUEST_WITH_WEIRD_WHITESPACE_DELIMITERS: &[u8] = b"GET\r/\rHTTP/1.1\r\n\r\n"; #[test] fn test_forbid_request_with_weird_whitespace_delimiters() { @@ -2157,8 +2220,14 @@ mod tests { .allow_multiple_spaces_in_request_line_delimiters(true) .parse_request(&mut request, first_line.as_bytes()); - assert_eq!(result, Ok(Status::Complete(20)), "failed for utf8 char i: {}, j: {}", i, j); - }, + assert_eq!( + result, + Ok(Status::Complete(20)), + "failed for utf8 char i: {}, j: {}", + i, + j + ); + } Err(_) => { let mut first_line = b"GET /".to_vec(); first_line.extend(&bytes); @@ -2168,8 +2237,14 @@ mod tests { .allow_multiple_spaces_in_request_line_delimiters(true) .parse_request(&mut request, first_line.as_slice()); - assert_eq!(result, Err(crate::Error::Token), "failed for utf8 char i: {}, j: {}", i, j); - }, + assert_eq!( + result, + Err(crate::Error::Token), + "failed for utf8 char i: {}, j: {}", + i, + j + ); + } }; // three code points starting from 0xe0 @@ -2189,8 +2264,15 @@ mod tests { .allow_multiple_spaces_in_request_line_delimiters(true) .parse_request(&mut request, first_line.as_bytes()); - assert_eq!(result, Ok(Status::Complete(21)), "failed for utf8 char i: {}, j: {}, k: {}", i, j, k); - }, + assert_eq!( + result, + Ok(Status::Complete(21)), + "failed for utf8 char i: {}, j: {}, k: {}", + i, + j, + k + ); + } Err(_) => { let mut first_line = b"GET /".to_vec(); first_line.extend(&bytes); @@ -2200,8 +2282,15 @@ mod tests { .allow_multiple_spaces_in_request_line_delimiters(true) .parse_request(&mut request, first_line.as_slice()); - assert_eq!(result, Err(crate::Error::Token), "failed for utf8 char i: {}, j: {}, k: {}", i, j, k); - }, + assert_eq!( + result, + Err(crate::Error::Token), + "failed for utf8 char i: {}, j: {}, k: {}", + i, + j, + k + ); + } }; // four code points starting from 0xf0 @@ -2221,8 +2310,16 @@ mod tests { .allow_multiple_spaces_in_request_line_delimiters(true) .parse_request(&mut request, first_line.as_bytes()); - assert_eq!(result, Ok(Status::Complete(22)), "failed for utf8 char i: {}, j: {}, k: {}, l: {}", i, j, k, l); - }, + assert_eq!( + result, + Ok(Status::Complete(22)), + "failed for utf8 char i: {}, j: {}, k: {}, l: {}", + i, + j, + k, + l + ); + } Err(_) => { let mut first_line = b"GET /".to_vec(); first_line.extend(&bytes); @@ -2232,8 +2329,16 @@ mod tests { .allow_multiple_spaces_in_request_line_delimiters(true) .parse_request(&mut request, first_line.as_slice()); - assert_eq!(result, Err(crate::Error::Token), "failed for utf8 char i: {}, j: {}, k: {}, l: {}", i, j, k, l); - }, + assert_eq!( + result, + Err(crate::Error::Token), + "failed for utf8 char i: {}, j: {}, k: {}, l: {}", + i, + j, + k, + l + ); + } }; } } @@ -2255,8 +2360,7 @@ mod tests { #[test] fn test_response_with_empty_header_name() { - const RESPONSE: &[u8] = - b"HTTP/1.1 200 OK\r\n: hello\r\nBread: baguette\r\n\r\n"; + const RESPONSE: &[u8] = b"HTTP/1.1 200 OK\r\n: hello\r\nBread: baguette\r\n\r\n"; let mut headers = [EMPTY_HEADER; 2]; let mut response = Response::new(&mut headers[..]); @@ -2281,14 +2385,12 @@ mod tests { #[test] fn test_request_with_empty_header_name() { - const RESPONSE: &[u8] = - b"GET / HTTP/1.1\r\n: hello\r\nBread: baguette\r\n\r\n"; + const RESPONSE: &[u8] = b"GET / HTTP/1.1\r\n: hello\r\nBread: baguette\r\n\r\n"; let mut headers = [EMPTY_HEADER; 2]; let mut request = Request::new(&mut headers[..]); - let result = crate::ParserConfig::default() - .parse_request(&mut request, RESPONSE); + let result = crate::ParserConfig::default().parse_request(&mut request, RESPONSE); assert_eq!(result, Err(crate::Error::HeaderName)); let result = crate::ParserConfig::default() @@ -2311,7 +2413,6 @@ mod tests { assert_eq!(result, Err(crate::Error::HeaderName)); let result = crate::ParserConfig::default() - .ignore_invalid_headers_in_responses(true) .parse_request(&mut request, REQUEST); assert_eq!(result, Err(crate::Error::HeaderName)); @@ -2351,8 +2452,7 @@ mod tests { let mut headers = [EMPTY_HEADER; 2]; let mut request = Request::new(&mut headers[..]); - let result = crate::ParserConfig::default() - .parse_request(&mut request, REQUEST); + let result = crate::ParserConfig::default().parse_request(&mut request, REQUEST); assert_eq!(result, Err(crate::Error::HeaderName)); let result = crate::ParserConfig::default() @@ -2369,8 +2469,7 @@ mod tests { let mut headers = [EMPTY_HEADER; 2]; let mut response = Response::new(&mut headers[..]); - let result = crate::ParserConfig::default() - .parse_response(&mut response, RESPONSE); + let result = crate::ParserConfig::default().parse_response(&mut response, RESPONSE); assert_eq!(result, Err(crate::Error::HeaderName)); let result = crate::ParserConfig::default() @@ -2394,8 +2493,7 @@ mod tests { let mut headers = [EMPTY_HEADER; 2]; let mut request = Request::new(&mut headers[..]); - let result = crate::ParserConfig::default() - .parse_request(&mut request, REQUEST); + let result = crate::ParserConfig::default().parse_request(&mut request, REQUEST); assert_eq!(result, Err(crate::Error::HeaderName)); let result = crate::ParserConfig::default() @@ -2439,8 +2537,7 @@ mod tests { let mut headers = [EMPTY_HEADER; 2]; let mut request = Request::new(&mut headers[..]); - let result = crate::ParserConfig::default() - .parse_request(&mut request, REQUEST); + let result = crate::ParserConfig::default().parse_request(&mut request, REQUEST); assert_eq!(result, Err(crate::Error::HeaderName)); let result = crate::ParserConfig::default() @@ -2457,8 +2554,7 @@ mod tests { let mut headers = [EMPTY_HEADER; 2]; let mut response = Response::new(&mut headers[..]); - let result = crate::ParserConfig::default() - .parse_response(&mut response, RESPONSE); + let result = crate::ParserConfig::default().parse_response(&mut response, RESPONSE); assert_eq!(result, Err(crate::Error::HeaderName)); let result = crate::ParserConfig::default() @@ -2475,8 +2571,7 @@ mod tests { let mut headers = [EMPTY_HEADER; 2]; let mut request = Request::new(&mut headers[..]); - let result = crate::ParserConfig::default() - .parse_request(&mut request, REQUEST); + let result = crate::ParserConfig::default().parse_request(&mut request, REQUEST); assert_eq!(result, Err(crate::Error::HeaderName)); let result = crate::ParserConfig::default() @@ -2493,8 +2588,7 @@ mod tests { let mut headers = [EMPTY_HEADER; 2]; let mut response = Response::new(&mut headers[..]); - let result = crate::ParserConfig::default() - .parse_response(&mut response, RESPONSE); + let result = crate::ParserConfig::default().parse_response(&mut response, RESPONSE); assert_eq!(result, Err(crate::Error::HeaderName)); let result = crate::ParserConfig::default() @@ -2508,8 +2602,7 @@ mod tests { let mut headers = [EMPTY_HEADER; 2]; let mut request = Request::new(&mut headers[..]); - let result = crate::ParserConfig::default() - .parse_request(&mut request, REQUEST); + let result = crate::ParserConfig::default().parse_request(&mut request, REQUEST); assert_eq!(result, Err(crate::Error::HeaderName)); let result = crate::ParserConfig::default() @@ -2557,8 +2650,7 @@ mod tests { let mut headers = [EMPTY_HEADER; 2]; let mut response = Response::new(&mut headers[..]); - let result = crate::ParserConfig::default() - .parse_response(&mut response, RESPONSE); + let result = crate::ParserConfig::default().parse_response(&mut response, RESPONSE); assert_eq!(result, Err(crate::Error::HeaderValue)); let result = crate::ParserConfig::default() @@ -2572,8 +2664,7 @@ mod tests { let mut headers = [EMPTY_HEADER; 2]; let mut request = Request::new(&mut headers[..]); - let result = crate::ParserConfig::default() - .parse_request(&mut request, REQUEST); + let result = crate::ParserConfig::default().parse_request(&mut request, REQUEST); assert_eq!(result, Err(crate::Error::HeaderValue)); let result = crate::ParserConfig::default() @@ -2590,8 +2681,7 @@ mod tests { let mut headers = [EMPTY_HEADER; 2]; let mut response = Response::new(&mut headers[..]); - let result = crate::ParserConfig::default() - .parse_response(&mut response, RESPONSE); + let result = crate::ParserConfig::default().parse_response(&mut response, RESPONSE); assert_eq!(result, Err(crate::Error::HeaderValue)); let result = crate::ParserConfig::default() @@ -2612,8 +2702,7 @@ mod tests { let mut headers = [EMPTY_HEADER; 2]; let mut request = Request::new(&mut headers[..]); - let result = crate::ParserConfig::default() - .parse_request(&mut request, REQUEST); + let result = crate::ParserConfig::default().parse_request(&mut request, REQUEST); assert_eq!(result, Err(crate::Error::HeaderValue)); let result = crate::ParserConfig::default() @@ -2637,8 +2726,7 @@ mod tests { let mut headers = [EMPTY_HEADER; 2]; let mut response = Response::new(&mut headers[..]); - let result = crate::ParserConfig::default() - .parse_response(&mut response, RESPONSE); + let result = crate::ParserConfig::default().parse_response(&mut response, RESPONSE); assert_eq!(result, Err(crate::Error::HeaderValue)); let result = crate::ParserConfig::default() @@ -2659,8 +2747,7 @@ mod tests { let mut headers = [EMPTY_HEADER; 2]; let mut request = Request::new(&mut headers[..]); - let result = crate::ParserConfig::default() - .parse_request(&mut request, REQUEST); + let result = crate::ParserConfig::default().parse_request(&mut request, REQUEST); assert_eq!(result, Err(crate::Error::HeaderValue)); let result = crate::ParserConfig::default() @@ -2695,7 +2782,7 @@ mod tests { assert!(method.as_ptr() <= buf_end); } - static RESPONSE_WITH_SPACE_BEFORE_FIRST_HEADER: &[u8] = + static RESPONSE_WITH_SPACE_BEFORE_FIRST_HEADER: &[u8] = b"HTTP/1.1 200 OK\r\n Space-Before-Header: hello there\r\n\r\n"; #[test] @@ -2770,7 +2857,10 @@ mod tests { let mut headers = [EMPTY_HEADER; 1]; let mut request = Request::new(&mut headers[..]); - let result = crate::ParserConfig::default().parse_request(&mut request, b"GET /test?post=I\xE2\x80\x99msorryIforkedyou HTTP/1.1\r\nHost: example.org\r\n\r\n"); + let result = crate::ParserConfig::default().parse_request( + &mut request, + b"GET /test?post=I\xE2\x80\x99msorryIforkedyou HTTP/1.1\r\nHost: example.org\r\n\r\n", + ); assert_eq!(result, Ok(Status::Complete(67))); assert_eq!(request.version.unwrap(), 1); @@ -2786,7 +2876,10 @@ mod tests { let mut headers = [EMPTY_HEADER; 1]; let mut request = Request::new(&mut headers[..]); - let result = crate::ParserConfig::default().parse_request(&mut request, b"GET /test?post=I\xE2msorryIforkedyou HTTP/1.1\r\nHost: example.org\r\n\r\n"); + let result = crate::ParserConfig::default().parse_request( + &mut request, + b"GET /test?post=I\xE2msorryIforkedyou HTTP/1.1\r\nHost: example.org\r\n\r\n", + ); assert_eq!(result, Err(crate::Error::Token)); } diff --git a/tests/uri.rs b/tests/uri.rs index e0ceab4..7982289 100644 --- a/tests/uri.rs +++ b/tests/uri.rs @@ -1,26 +1,25 @@ - -use httparse::{Error, Request, Status, EMPTY_HEADER}; +use httparser::{Error, Request, Status, EMPTY_HEADER}; const NUM_OF_HEADERS: usize = 4; macro_rules! req { - ($name:ident, $buf:expr, |$arg:ident| $body:expr) => ( + ($name:ident, $buf:expr, |$arg:ident| $body:expr) => { req! {$name, $buf, Ok(Status::Complete($buf.len())), |$arg| $body } - ); - ($name:ident, $buf:expr, $len:expr, |$arg:ident| $body:expr) => ( - #[test] - fn $name() { - let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS]; - let mut req = Request::new(&mut headers[..]); - let status = req.parse($buf.as_ref()); - assert_eq!(status, $len); - closure(req); - - fn closure($arg: Request<'_, '_>) { - $body + }; + ($name:ident, $buf:expr, $len:expr, |$arg:ident| $body:expr) => { + #[test] + fn $name() { + let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS]; + let mut req = Request::new(&mut headers[..]); + let status = req.parse($buf.as_ref()); + assert_eq!(status, $len); + closure(req); + + fn closure($arg: Request<'_, '_>) { + $body + } } - } - ) + }; } req! { @@ -36,7 +35,6 @@ req! { } } - req! { urltest_002, b"GET /x HTTP/1.1\r\nHost: test\r\n\r\n", @@ -50,7 +48,6 @@ req! { } } - req! { urltest_003, b"GET /x HTTP/1.1\r\nHost: test\r\n\r\n", @@ -64,7 +61,6 @@ req! { } } - req! { urltest_004, b"GET /foo/foo.com HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -78,7 +74,6 @@ req! { } } - req! { urltest_005, b"GET /foo/:foo.com HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -92,7 +87,6 @@ req! { } } - req! { urltest_006, b"GET /foo/foo.com HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -106,7 +100,6 @@ req! { } } - req! { urltest_007, b"GET foo.com HTTP/1.1\r\nHost: \r\n\r\n", @@ -114,7 +107,6 @@ req! { |_r| {} } - req! { urltest_008, b"GET /%20b%20?%20d%20 HTTP/1.1\r\nHost: f\r\n\r\n", @@ -128,7 +120,6 @@ req! { } } - req! { urltest_009, b"GET x x HTTP/1.1\r\nHost: \r\n\r\n", @@ -136,7 +127,6 @@ req! { |_r| {} } - req! { urltest_010, b"GET /c HTTP/1.1\r\nHost: f\r\n\r\n", @@ -150,7 +140,6 @@ req! { } } - req! { urltest_011, b"GET /c HTTP/1.1\r\nHost: f\r\n\r\n", @@ -164,7 +153,6 @@ req! { } } - req! { urltest_012, b"GET /c HTTP/1.1\r\nHost: f\r\n\r\n", @@ -178,7 +166,6 @@ req! { } } - req! { urltest_013, b"GET /c HTTP/1.1\r\nHost: f\r\n\r\n", @@ -192,7 +179,6 @@ req! { } } - req! { urltest_014, b"GET /c HTTP/1.1\r\nHost: f\r\n\r\n", @@ -206,7 +192,6 @@ req! { } } - req! { urltest_015, b"GET /foo/bar HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -220,7 +205,6 @@ req! { } } - req! { urltest_016, b"GET /foo/bar HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -234,7 +218,6 @@ req! { } } - req! { urltest_017, b"GET /foo/:foo.com/ HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -248,7 +231,6 @@ req! { } } - req! { urltest_018, b"GET /foo/:foo.com/ HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -262,7 +244,6 @@ req! { } } - req! { urltest_019, b"GET /foo/: HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -276,7 +257,6 @@ req! { } } - req! { urltest_020, b"GET /foo/:a HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -290,7 +270,6 @@ req! { } } - req! { urltest_021, b"GET /foo/:/ HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -304,7 +283,6 @@ req! { } } - req! { urltest_022, b"GET /foo/:/ HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -318,7 +296,6 @@ req! { } } - req! { urltest_023, b"GET /foo/: HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -332,7 +309,6 @@ req! { } } - req! { urltest_024, b"GET /foo/bar HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -346,7 +322,6 @@ req! { } } - req! { urltest_025, b"GET /foo/bar HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -360,7 +335,6 @@ req! { } } - req! { urltest_026, b"GET /foo/bar HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -374,7 +348,6 @@ req! { } } - req! { urltest_027, b"GET /foo/bar HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -388,7 +361,6 @@ req! { } } - req! { urltest_028, b"GET /foo/bar HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -402,7 +374,6 @@ req! { } } - req! { urltest_029, b"GET /foo/:23 HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -416,7 +387,6 @@ req! { } } - req! { urltest_030, b"GET /:23 HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -430,7 +400,6 @@ req! { } } - req! { urltest_031, b"GET /foo/:: HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -444,7 +413,6 @@ req! { } } - req! { urltest_032, b"GET /foo/::23 HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -458,7 +426,6 @@ req! { } } - req! { urltest_033, b"GET /d HTTP/1.1\r\nHost: c\r\n\r\n", @@ -472,7 +439,6 @@ req! { } } - req! { urltest_034, b"GET /foo/:@c:29 HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -486,7 +452,6 @@ req! { } } - req! { urltest_035, b"GET //@ HTTP/1.1\r\nHost: foo.com\r\n\r\n", @@ -500,7 +465,6 @@ req! { } } - req! { urltest_036, b"GET /b:c/d@foo.com/ HTTP/1.1\r\nHost: a\r\n\r\n", @@ -514,7 +478,6 @@ req! { } } - req! { urltest_037, b"GET /bar.com/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -528,7 +491,6 @@ req! { } } - req! { urltest_038, b"GET /////// HTTP/1.1\r\nHost: \r\n\r\n", @@ -542,7 +504,6 @@ req! { } } - req! { urltest_039, b"GET ///////bar.com/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -556,7 +517,6 @@ req! { } } - req! { urltest_040, b"GET //:///// HTTP/1.1\r\nHost: \r\n\r\n", @@ -570,7 +530,6 @@ req! { } } - req! { urltest_041, b"GET /foo HTTP/1.1\r\nHost: \r\n\r\n", @@ -584,7 +543,6 @@ req! { } } - req! { urltest_042, b"GET /bar HTTP/1.1\r\nHost: foo\r\n\r\n", @@ -598,7 +556,6 @@ req! { } } - req! { urltest_043, b"GET /path;a??e HTTP/1.1\r\nHost: foo\r\n\r\n", @@ -612,7 +569,6 @@ req! { } } - req! { urltest_044, b"GET /abcd?efgh?ijkl HTTP/1.1\r\nHost: foo\r\n\r\n", @@ -626,7 +582,6 @@ req! { } } - req! { urltest_045, b"GET /abcd HTTP/1.1\r\nHost: foo\r\n\r\n", @@ -640,7 +595,6 @@ req! { } } - req! { urltest_046, b"GET /foo/[61:24:74]:98 HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -654,7 +608,6 @@ req! { } } - req! { urltest_047, b"GET /foo/[61:27]/:foo HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -668,7 +621,6 @@ req! { } } - req! { urltest_048, b"GET /example.com/ HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -682,7 +634,6 @@ req! { } } - req! { urltest_049, b"GET /example.com/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -696,7 +647,6 @@ req! { } } - req! { urltest_050, b"GET /example.com/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -710,7 +660,6 @@ req! { } } - req! { urltest_051, b"GET /example.com/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -724,7 +673,6 @@ req! { } } - req! { urltest_052, b"GET /example.com/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -738,7 +686,6 @@ req! { } } - req! { urltest_053, b"GET /example.com/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -752,7 +699,6 @@ req! { } } - req! { urltest_054, b"GET /example.com/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -766,7 +712,6 @@ req! { } } - req! { urltest_055, b"GET /foo/example.com/ HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -780,7 +725,6 @@ req! { } } - req! { urltest_056, b"GET example.com/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -794,7 +738,6 @@ req! { } } - req! { urltest_057, b"GET example.com/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -808,7 +751,6 @@ req! { } } - req! { urltest_058, b"GET example.com/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -822,7 +764,6 @@ req! { } } - req! { urltest_059, b"GET example.com/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -836,7 +777,6 @@ req! { } } - req! { urltest_060, b"GET example.com/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -850,7 +790,6 @@ req! { } } - req! { urltest_061, b"GET /a/b/c HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -864,7 +803,6 @@ req! { } } - req! { urltest_062, b"GET /a/%20/c HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -878,7 +816,6 @@ req! { } } - req! { urltest_063, b"GET /a%2fc HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -892,7 +829,6 @@ req! { } } - req! { urltest_064, b"GET /a/%2f/c HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -906,7 +842,6 @@ req! { } } - req! { urltest_065, b"GET /foo/bar HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -920,7 +855,6 @@ req! { } } - req! { urltest_066, b"GET text/html,test HTTP/1.1\r\nHost: \r\n\r\n", @@ -934,7 +868,6 @@ req! { } } - req! { urltest_067, b"GET 1234567890 HTTP/1.1\r\nHost: \r\n\r\n", @@ -948,7 +881,6 @@ req! { } } - req! { urltest_068, b"GET /c:/foo/bar.html HTTP/1.1\r\nHost: \r\n\r\n", @@ -962,7 +894,6 @@ req! { } } - req! { urltest_069, b"GET /c:////foo/bar.html HTTP/1.1\r\nHost: \r\n\r\n", @@ -976,7 +907,6 @@ req! { } } - req! { urltest_070, b"GET /C:/foo/bar HTTP/1.1\r\nHost: \r\n\r\n", @@ -990,7 +920,6 @@ req! { } } - req! { urltest_071, b"GET /C:/foo/bar HTTP/1.1\r\nHost: \r\n\r\n", @@ -1004,7 +933,6 @@ req! { } } - req! { urltest_072, b"GET /C:/foo/bar HTTP/1.1\r\nHost: \r\n\r\n", @@ -1018,7 +946,6 @@ req! { } } - req! { urltest_073, b"GET /file HTTP/1.1\r\nHost: server\r\n\r\n", @@ -1032,7 +959,6 @@ req! { } } - req! { urltest_074, b"GET /file HTTP/1.1\r\nHost: server\r\n\r\n", @@ -1046,7 +972,6 @@ req! { } } - req! { urltest_075, b"GET /file HTTP/1.1\r\nHost: server\r\n\r\n", @@ -1060,7 +985,6 @@ req! { } } - req! { urltest_076, b"GET /foo/bar.txt HTTP/1.1\r\nHost: \r\n\r\n", @@ -1074,7 +998,6 @@ req! { } } - req! { urltest_077, b"GET /home/me HTTP/1.1\r\nHost: \r\n\r\n", @@ -1088,7 +1011,6 @@ req! { } } - req! { urltest_078, b"GET /test HTTP/1.1\r\nHost: \r\n\r\n", @@ -1102,7 +1024,6 @@ req! { } } - req! { urltest_079, b"GET /test HTTP/1.1\r\nHost: \r\n\r\n", @@ -1116,7 +1037,6 @@ req! { } } - req! { urltest_080, b"GET /tmp/mock/test HTTP/1.1\r\nHost: \r\n\r\n", @@ -1130,7 +1050,6 @@ req! { } } - req! { urltest_081, b"GET /tmp/mock/test HTTP/1.1\r\nHost: \r\n\r\n", @@ -1144,7 +1063,6 @@ req! { } } - req! { urltest_082, b"GET /foo HTTP/1.1\r\nHost: example.com\r\n\r\n", @@ -1158,7 +1076,6 @@ req! { } } - req! { urltest_083, b"GET /.foo HTTP/1.1\r\nHost: example.com\r\n\r\n", @@ -1172,7 +1089,6 @@ req! { } } - req! { urltest_084, b"GET /foo/ HTTP/1.1\r\nHost: example.com\r\n\r\n", @@ -1186,7 +1102,6 @@ req! { } } - req! { urltest_085, b"GET /foo/ HTTP/1.1\r\nHost: example.com\r\n\r\n", @@ -1200,7 +1115,6 @@ req! { } } - req! { urltest_086, b"GET /foo/ HTTP/1.1\r\nHost: example.com\r\n\r\n", @@ -1214,7 +1128,6 @@ req! { } } - req! { urltest_087, b"GET /foo/ HTTP/1.1\r\nHost: example.com\r\n\r\n", @@ -1228,7 +1141,6 @@ req! { } } - req! { urltest_088, b"GET /foo/..bar HTTP/1.1\r\nHost: example.com\r\n\r\n", @@ -1242,7 +1154,6 @@ req! { } } - req! { urltest_089, b"GET /foo/ton HTTP/1.1\r\nHost: example.com\r\n\r\n", @@ -1256,7 +1167,6 @@ req! { } } - req! { urltest_090, b"GET /a HTTP/1.1\r\nHost: example.com\r\n\r\n", @@ -1270,7 +1180,6 @@ req! { } } - req! { urltest_091, b"GET /ton HTTP/1.1\r\nHost: example.com\r\n\r\n", @@ -1284,7 +1193,6 @@ req! { } } - req! { urltest_092, b"GET /foo/ HTTP/1.1\r\nHost: example.com\r\n\r\n", @@ -1298,7 +1206,6 @@ req! { } } - req! { urltest_093, b"GET /foo/%2e%2 HTTP/1.1\r\nHost: example.com\r\n\r\n", @@ -1312,7 +1219,6 @@ req! { } } - req! { urltest_094, b"GET /%2e.bar HTTP/1.1\r\nHost: example.com\r\n\r\n", @@ -1326,7 +1232,6 @@ req! { } } - req! { urltest_095, b"GET // HTTP/1.1\r\nHost: example.com\r\n\r\n", @@ -1340,7 +1245,6 @@ req! { } } - req! { urltest_096, b"GET /foo/ HTTP/1.1\r\nHost: example.com\r\n\r\n", @@ -1354,7 +1258,6 @@ req! { } } - req! { urltest_097, b"GET /foo/bar/ HTTP/1.1\r\nHost: example.com\r\n\r\n", @@ -1368,7 +1271,6 @@ req! { } } - req! { urltest_098, b"GET /foo HTTP/1.1\r\nHost: example.com\r\n\r\n", @@ -1382,7 +1284,6 @@ req! { } } - req! { urltest_099, b"GET /%20foo HTTP/1.1\r\nHost: example.com\r\n\r\n", @@ -1396,7 +1297,6 @@ req! { } } - req! { urltest_100, b"GET /foo% HTTP/1.1\r\nHost: example.com\r\n\r\n", @@ -1410,7 +1310,6 @@ req! { } } - req! { urltest_101, b"GET /foo%2 HTTP/1.1\r\nHost: example.com\r\n\r\n", @@ -1424,7 +1323,6 @@ req! { } } - req! { urltest_102, b"GET /foo%2zbar HTTP/1.1\r\nHost: example.com\r\n\r\n", @@ -1438,7 +1336,6 @@ req! { } } - req! { urltest_103, b"GET /foo%2%C3%82%C2%A9zbar HTTP/1.1\r\nHost: example.com\r\n\r\n", @@ -1452,7 +1349,6 @@ req! { } } - req! { urltest_104, b"GET /foo%41%7a HTTP/1.1\r\nHost: example.com\r\n\r\n", @@ -1466,7 +1362,6 @@ req! { } } - req! { urltest_105, b"GET /foo%C2%91%91 HTTP/1.1\r\nHost: example.com\r\n\r\n", @@ -1480,7 +1375,6 @@ req! { } } - req! { urltest_106, b"GET /foo%00%51 HTTP/1.1\r\nHost: example.com\r\n\r\n", @@ -1494,7 +1388,6 @@ req! { } } - req! { urltest_107, b"GET /(%28:%3A%29) HTTP/1.1\r\nHost: example.com\r\n\r\n", @@ -1508,7 +1401,6 @@ req! { } } - req! { urltest_108, b"GET /%3A%3a%3C%3c HTTP/1.1\r\nHost: example.com\r\n\r\n", @@ -1522,7 +1414,6 @@ req! { } } - req! { urltest_109, b"GET /foobar HTTP/1.1\r\nHost: example.com\r\n\r\n", @@ -1536,7 +1427,6 @@ req! { } } - req! { urltest_110, b"GET //foo//bar HTTP/1.1\r\nHost: example.com\r\n\r\n", @@ -1550,7 +1440,6 @@ req! { } } - req! { urltest_111, b"GET /%7Ffp3%3Eju%3Dduvgw%3Dd HTTP/1.1\r\nHost: example.com\r\n\r\n", @@ -1564,7 +1453,6 @@ req! { } } - req! { urltest_112, b"GET /@asdf%40 HTTP/1.1\r\nHost: example.com\r\n\r\n", @@ -1578,7 +1466,6 @@ req! { } } - req! { urltest_113, b"GET /%E4%BD%A0%E5%A5%BD%E4%BD%A0%E5%A5%BD HTTP/1.1\r\nHost: example.com\r\n\r\n", @@ -1592,7 +1479,6 @@ req! { } } - req! { urltest_114, b"GET /%E2%80%A5/foo HTTP/1.1\r\nHost: example.com\r\n\r\n", @@ -1606,7 +1492,6 @@ req! { } } - req! { urltest_115, b"GET /%EF%BB%BF/foo HTTP/1.1\r\nHost: example.com\r\n\r\n", @@ -1620,7 +1505,6 @@ req! { } } - req! { urltest_116, b"GET /%E2%80%AE/foo/%E2%80%AD/bar HTTP/1.1\r\nHost: example.com\r\n\r\n", @@ -1634,7 +1518,6 @@ req! { } } - req! { urltest_117, b"GET /foo?bar=baz HTTP/1.1\r\nHost: www.google.com\r\n\r\n", @@ -1648,7 +1531,6 @@ req! { } } - req! { urltest_118, b"GET /foo?bar=baz HTTP/1.1\r\nHost: www.google.com\r\n\r\n", @@ -1662,7 +1544,6 @@ req! { } } - req! { urltest_119, b"GET test HTTP/1.1\r\nHost: \r\n\r\n", @@ -1676,7 +1557,6 @@ req! { } } - req! { urltest_120, b"GET /foo%2Ehtml HTTP/1.1\r\nHost: www\r\n\r\n", @@ -1690,7 +1570,6 @@ req! { } } - req! { urltest_121, b"GET /foo/html HTTP/1.1\r\nHost: www\r\n\r\n", @@ -1704,7 +1583,6 @@ req! { } } - req! { urltest_122, b"GET /foo HTTP/1.1\r\nHost: www.google.com\r\n\r\n", @@ -1718,7 +1596,6 @@ req! { } } - req! { urltest_123, b"GET /example.com/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -1732,7 +1609,6 @@ req! { } } - req! { urltest_124, b"GET /example.com/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -1746,7 +1622,6 @@ req! { } } - req! { urltest_125, b"GET /example.com/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -1760,7 +1635,6 @@ req! { } } - req! { urltest_126, b"GET /example.com/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -1774,7 +1648,6 @@ req! { } } - req! { urltest_127, b"GET /example.com/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -1788,7 +1661,6 @@ req! { } } - req! { urltest_128, b"GET /example.com/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -1802,7 +1674,6 @@ req! { } } - req! { urltest_129, b"GET example.com/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -1816,7 +1687,6 @@ req! { } } - req! { urltest_130, b"GET example.com/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -1830,7 +1700,6 @@ req! { } } - req! { urltest_131, b"GET example.com/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -1844,7 +1713,6 @@ req! { } } - req! { urltest_132, b"GET example.com/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -1858,7 +1726,6 @@ req! { } } - req! { urltest_133, b"GET example.com/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -1872,7 +1739,6 @@ req! { } } - req! { urltest_134, b"GET /test.txt HTTP/1.1\r\nHost: www.example.com\r\n\r\n", @@ -1886,7 +1752,6 @@ req! { } } - req! { urltest_135, b"GET /test.txt HTTP/1.1\r\nHost: www.example.com\r\n\r\n", @@ -1900,7 +1765,6 @@ req! { } } - req! { urltest_136, b"GET /test.txt HTTP/1.1\r\nHost: www.example.com\r\n\r\n", @@ -1914,7 +1778,6 @@ req! { } } - req! { urltest_137, b"GET /test.txt HTTP/1.1\r\nHost: www.example.com\r\n\r\n", @@ -1928,7 +1791,6 @@ req! { } } - req! { urltest_138, b"GET /aaa/test.txt HTTP/1.1\r\nHost: www.example.com\r\n\r\n", @@ -1942,7 +1804,6 @@ req! { } } - req! { urltest_139, b"GET /test.txt HTTP/1.1\r\nHost: www.example.com\r\n\r\n", @@ -1956,7 +1817,6 @@ req! { } } - req! { urltest_140, b"GET /%E4%B8%AD/test.txt HTTP/1.1\r\nHost: www.example.com\r\n\r\n", @@ -1970,7 +1830,6 @@ req! { } } - req! { urltest_141, b"GET /... HTTP/1.1\r\nHost: \r\n\r\n", @@ -1984,7 +1843,6 @@ req! { } } - req! { urltest_142, b"GET /a HTTP/1.1\r\nHost: \r\n\r\n", @@ -1998,7 +1856,6 @@ req! { } } - req! { urltest_143, b"GET /%EF%BF%BD?%EF%BF%BD HTTP/1.1\r\nHost: x\r\n\r\n", @@ -2012,7 +1869,6 @@ req! { } } - req! { urltest_144, b"GET /bar HTTP/1.1\r\nHost: example.com\r\n\r\n", @@ -2026,7 +1882,6 @@ req! { } } - req! { urltest_145, b"GET test HTTP/1.1\r\nHost: \r\n\r\n", @@ -2040,7 +1895,6 @@ req! { } } - req! { urltest_146, b"GET x@x.com HTTP/1.1\r\nHost: \r\n\r\n", @@ -2054,7 +1908,6 @@ req! { } } - req! { urltest_147, b"GET , HTTP/1.1\r\nHost: \r\n\r\n", @@ -2068,7 +1921,6 @@ req! { } } - req! { urltest_148, b"GET blank HTTP/1.1\r\nHost: \r\n\r\n", @@ -2082,7 +1934,6 @@ req! { } } - req! { urltest_149, b"GET test?test HTTP/1.1\r\nHost: \r\n\r\n", @@ -2096,7 +1947,6 @@ req! { } } - req! { urltest_150, b"GET /%60%7B%7D?`{} HTTP/1.1\r\nHost: h\r\n\r\n", @@ -2111,7 +1961,6 @@ req! { } - req! { urltest_151, b"GET /?%27 HTTP/1.1\r\nHost: host\r\n\r\n", @@ -2125,7 +1974,6 @@ req! { } } - req! { urltest_152, b"GET /?' HTTP/1.1\r\nHost: host\r\n\r\n", @@ -2139,7 +1987,6 @@ req! { } } - req! { urltest_153, b"GET /some/path HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -2153,7 +2000,6 @@ req! { } } - req! { urltest_154, b"GET /smth HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -2167,7 +2013,6 @@ req! { } } - req! { urltest_155, b"GET /some/path HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -2181,7 +2026,6 @@ req! { } } - req! { urltest_156, b"GET /pa/i HTTP/1.1\r\nHost: \r\n\r\n", @@ -2195,7 +2039,6 @@ req! { } } - req! { urltest_157, b"GET /i HTTP/1.1\r\nHost: ho\r\n\r\n", @@ -2209,7 +2052,6 @@ req! { } } - req! { urltest_158, b"GET /pa/i HTTP/1.1\r\nHost: \r\n\r\n", @@ -2223,7 +2065,6 @@ req! { } } - req! { urltest_159, b"GET /i HTTP/1.1\r\nHost: \r\n\r\n", @@ -2237,7 +2078,6 @@ req! { } } - req! { urltest_160, b"GET /i HTTP/1.1\r\nHost: ho\r\n\r\n", @@ -2251,7 +2091,6 @@ req! { } } - req! { urltest_161, b"GET /i HTTP/1.1\r\nHost: \r\n\r\n", @@ -2265,7 +2104,6 @@ req! { } } - req! { urltest_162, b"GET /i HTTP/1.1\r\nHost: \r\n\r\n", @@ -2279,7 +2117,6 @@ req! { } } - req! { urltest_163, b"GET /i HTTP/1.1\r\nHost: ho\r\n\r\n", @@ -2293,7 +2130,6 @@ req! { } } - req! { urltest_164, b"GET /i HTTP/1.1\r\nHost: \r\n\r\n", @@ -2307,7 +2143,6 @@ req! { } } - req! { urltest_165, b"GET /pa/pa?i HTTP/1.1\r\nHost: \r\n\r\n", @@ -2321,7 +2156,6 @@ req! { } } - req! { urltest_166, b"GET /pa?i HTTP/1.1\r\nHost: ho\r\n\r\n", @@ -2335,7 +2169,6 @@ req! { } } - req! { urltest_167, b"GET /pa/pa?i HTTP/1.1\r\nHost: \r\n\r\n", @@ -2349,7 +2182,6 @@ req! { } } - req! { urltest_168, b"GET sd HTTP/1.1\r\nHost: \r\n\r\n", @@ -2363,7 +2195,6 @@ req! { } } - req! { urltest_169, b"GET sd/sd HTTP/1.1\r\nHost: \r\n\r\n", @@ -2377,7 +2208,6 @@ req! { } } - req! { urltest_170, b"GET /pa/pa HTTP/1.1\r\nHost: \r\n\r\n", @@ -2391,7 +2221,6 @@ req! { } } - req! { urltest_171, b"GET /pa HTTP/1.1\r\nHost: ho\r\n\r\n", @@ -2405,7 +2234,6 @@ req! { } } - req! { urltest_172, b"GET /pa/pa HTTP/1.1\r\nHost: \r\n\r\n", @@ -2419,7 +2247,6 @@ req! { } } - req! { urltest_173, b"GET /x HTTP/1.1\r\nHost: %C3%B1\r\n\r\n", @@ -2433,7 +2260,6 @@ req! { } } - req! { urltest_174, b"GET \\.\\./ HTTP/1.1\r\n\r\n", @@ -2445,7 +2271,6 @@ req! { } } - req! { urltest_175, b"GET :a@example.net HTTP/1.1\r\nHost: \r\n\r\n", @@ -2459,7 +2284,6 @@ req! { } } - req! { urltest_176, b"GET %NBD HTTP/1.1\r\nHost: \r\n\r\n", @@ -2473,7 +2297,6 @@ req! { } } - req! { urltest_177, b"GET %1G HTTP/1.1\r\nHost: \r\n\r\n", @@ -2487,7 +2310,6 @@ req! { } } - req! { urltest_178, b"GET /relative_import.html HTTP/1.1\r\nHost: 127.0.0.1\r\n\r\n", @@ -2501,7 +2323,6 @@ req! { } } - req! { urltest_179, b"GET /?foo=%7B%22abc%22 HTTP/1.1\r\nHost: facebook.com\r\n\r\n", @@ -2515,7 +2336,6 @@ req! { } } - req! { urltest_180, b"GET /jqueryui@1.2.3 HTTP/1.1\r\nHost: localhost\r\n\r\n", @@ -2529,7 +2349,6 @@ req! { } } - req! { urltest_181, b"GET /path?query HTTP/1.1\r\nHost: host\r\n\r\n", @@ -2543,7 +2362,6 @@ req! { } } - req! { urltest_182, b"GET /foo/bar?a=b&c=d HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -2557,7 +2375,6 @@ req! { } } - req! { urltest_183, b"GET /foo/bar??a=b&c=d HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -2571,7 +2388,6 @@ req! { } } - req! { urltest_184, b"GET /foo/bar HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -2585,7 +2401,6 @@ req! { } } - req! { urltest_185, b"GET /baz?qux HTTP/1.1\r\nHost: foo.bar\r\n\r\n", @@ -2599,7 +2414,6 @@ req! { } } - req! { urltest_186, b"GET /baz?qux HTTP/1.1\r\nHost: foo.bar\r\n\r\n", @@ -2613,7 +2427,6 @@ req! { } } - req! { urltest_187, b"GET /baz?qux HTTP/1.1\r\nHost: foo.bar\r\n\r\n", @@ -2627,7 +2440,6 @@ req! { } } - req! { urltest_188, b"GET /baz?qux HTTP/1.1\r\nHost: foo.bar\r\n\r\n", @@ -2641,7 +2453,6 @@ req! { } } - req! { urltest_189, b"GET /baz?qux HTTP/1.1\r\nHost: foo.bar\r\n\r\n", @@ -2655,7 +2466,6 @@ req! { } } - req! { urltest_190, b"GET /C%3A/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -2669,7 +2479,6 @@ req! { } } - req! { urltest_191, b"GET /C%7C/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -2683,7 +2492,6 @@ req! { } } - req! { urltest_192, b"GET /C:/Users/Domenic/Dropbox/GitHub/tmpvar/jsdom/test/level2/html/files/pix/submit.gif HTTP/1.1\r\nHost: \r\n\r\n", @@ -2697,7 +2505,6 @@ req! { } } - req! { urltest_193, b"GET /C:/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -2711,7 +2518,6 @@ req! { } } - req! { urltest_194, b"GET /C:/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -2725,7 +2531,6 @@ req! { } } - req! { urltest_195, b"GET /d: HTTP/1.1\r\nHost: \r\n\r\n", @@ -2739,7 +2544,6 @@ req! { } } - req! { urltest_196, b"GET /d:/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -2753,7 +2557,6 @@ req! { } } - req! { urltest_197, b"GET /test?test HTTP/1.1\r\nHost: \r\n\r\n", @@ -2767,7 +2570,6 @@ req! { } } - req! { urltest_198, b"GET /test?test HTTP/1.1\r\nHost: \r\n\r\n", @@ -2781,7 +2583,6 @@ req! { } } - req! { urltest_199, b"GET /test?x HTTP/1.1\r\nHost: \r\n\r\n", @@ -2795,7 +2596,6 @@ req! { } } - req! { urltest_200, b"GET /test?x HTTP/1.1\r\nHost: \r\n\r\n", @@ -2809,7 +2609,6 @@ req! { } } - req! { urltest_201, b"GET /test?test HTTP/1.1\r\nHost: \r\n\r\n", @@ -2823,7 +2622,6 @@ req! { } } - req! { urltest_202, b"GET /test?test HTTP/1.1\r\nHost: \r\n\r\n", @@ -2837,7 +2635,6 @@ req! { } } - req! { urltest_203, b"GET /?fox HTTP/1.1\r\nHost: \r\n\r\n", @@ -2851,7 +2648,6 @@ req! { } } - req! { urltest_204, b"GET /localhost//cat HTTP/1.1\r\nHost: \r\n\r\n", @@ -2865,7 +2661,6 @@ req! { } } - req! { urltest_205, b"GET /localhost//cat HTTP/1.1\r\nHost: \r\n\r\n", @@ -2879,7 +2674,6 @@ req! { } } - req! { urltest_206, b"GET /mouse HTTP/1.1\r\nHost: \r\n\r\n", @@ -2893,7 +2687,6 @@ req! { } } - req! { urltest_207, b"GET /pig HTTP/1.1\r\nHost: \r\n\r\n", @@ -2907,7 +2700,6 @@ req! { } } - req! { urltest_208, b"GET /pig HTTP/1.1\r\nHost: \r\n\r\n", @@ -2921,7 +2713,6 @@ req! { } } - req! { urltest_209, b"GET /pig HTTP/1.1\r\nHost: \r\n\r\n", @@ -2935,7 +2726,6 @@ req! { } } - req! { urltest_210, b"GET /localhost//pig HTTP/1.1\r\nHost: lion\r\n\r\n", @@ -2949,7 +2739,6 @@ req! { } } - req! { urltest_211, b"GET /rooibos HTTP/1.1\r\nHost: tea\r\n\r\n", @@ -2963,7 +2752,6 @@ req! { } } - req! { urltest_212, b"GET /?chai HTTP/1.1\r\nHost: tea\r\n\r\n", @@ -2977,7 +2765,6 @@ req! { } } - req! { urltest_213, b"GET /C: HTTP/1.1\r\nHost: \r\n\r\n", @@ -2991,7 +2778,6 @@ req! { } } - req! { urltest_214, b"GET /C: HTTP/1.1\r\nHost: \r\n\r\n", @@ -3005,7 +2791,6 @@ req! { } } - req! { urltest_215, b"GET /C: HTTP/1.1\r\nHost: \r\n\r\n", @@ -3019,7 +2804,6 @@ req! { } } - req! { urltest_216, b"GET /C:/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -3033,7 +2817,6 @@ req! { } } - req! { urltest_217, b"GET /C:/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -3047,7 +2830,6 @@ req! { } } - req! { urltest_218, b"GET /C:/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -3061,7 +2843,6 @@ req! { } } - req! { urltest_219, b"GET /dir/C HTTP/1.1\r\nHost: host\r\n\r\n", @@ -3075,7 +2856,6 @@ req! { } } - req! { urltest_220, b"GET /dir/C|a HTTP/1.1\r\nHost: host\r\n\r\n", @@ -3089,7 +2869,6 @@ req! { } } - req! { urltest_221, b"GET /c:/foo/bar HTTP/1.1\r\nHost: \r\n\r\n", @@ -3103,7 +2882,6 @@ req! { } } - req! { urltest_222, b"GET /c:/foo/bar HTTP/1.1\r\nHost: \r\n\r\n", @@ -3117,7 +2895,6 @@ req! { } } - req! { urltest_223, b"GET /c:/foo/bar HTTP/1.1\r\nHost: \r\n\r\n", @@ -3131,7 +2908,6 @@ req! { } } - req! { urltest_224, b"GET /c:/foo/bar HTTP/1.1\r\nHost: \r\n\r\n", @@ -3145,7 +2921,6 @@ req! { } } - req! { urltest_225, b"GET /C:/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -3159,7 +2934,6 @@ req! { } } - req! { urltest_226, b"GET /C:/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -3173,7 +2947,6 @@ req! { } } - req! { urltest_227, b"GET /C:/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -3187,7 +2960,6 @@ req! { } } - req! { urltest_228, b"GET /C:/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -3201,7 +2973,6 @@ req! { } } - req! { urltest_229, b"GET /C:/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -3215,7 +2986,6 @@ req! { } } - req! { urltest_230, b"GET /?q=v HTTP/1.1\r\nHost: \r\n\r\n", @@ -3229,7 +2999,6 @@ req! { } } - req! { urltest_231, b"GET ?x HTTP/1.1\r\nHost: %C3%B1\r\n\r\n", @@ -3243,7 +3012,6 @@ req! { } } - req! { urltest_232, b"GET ?x HTTP/1.1\r\nHost: %C3%B1\r\n\r\n", @@ -3257,7 +3025,6 @@ req! { } } - req! { urltest_233, b"GET // HTTP/1.1\r\nHost: \r\n\r\n", @@ -3271,7 +3038,6 @@ req! { } } - req! { urltest_234, b"GET //x/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -3285,7 +3051,6 @@ req! { } } - req! { urltest_235, b"GET /someconfig;mode=netascii HTTP/1.1\r\nHost: foobar.com\r\n\r\n", @@ -3299,7 +3064,6 @@ req! { } } - req! { urltest_236, b"GET /Index.ut2 HTTP/1.1\r\nHost: 10.10.10.10\r\n\r\n", @@ -3313,7 +3077,6 @@ req! { } } - req! { urltest_237, b"GET /0?baz=bam&qux=baz HTTP/1.1\r\nHost: somehost\r\n\r\n", @@ -3327,7 +3090,6 @@ req! { } } - req! { urltest_238, b"GET /sup HTTP/1.1\r\nHost: host\r\n\r\n", @@ -3341,7 +3103,6 @@ req! { } } - req! { urltest_239, b"GET /foo/bar.git HTTP/1.1\r\nHost: github.com\r\n\r\n", @@ -3355,7 +3116,6 @@ req! { } } - req! { urltest_240, b"GET /channel?passwd HTTP/1.1\r\nHost: myserver.com\r\n\r\n", @@ -3369,7 +3129,6 @@ req! { } } - req! { urltest_241, b"GET /foo.bar.org?type=TXT HTTP/1.1\r\nHost: fw.example.org\r\n\r\n", @@ -3383,7 +3142,6 @@ req! { } } - req! { urltest_242, b"GET /ou=People,o=JNDITutorial HTTP/1.1\r\nHost: localhost\r\n\r\n", @@ -3397,7 +3155,6 @@ req! { } } - req! { urltest_243, b"GET /foo/bar HTTP/1.1\r\nHost: github.com\r\n\r\n", @@ -3411,7 +3168,6 @@ req! { } } - req! { urltest_244, b"GET ietf:rfc:2648 HTTP/1.1\r\nHost: \r\n\r\n", @@ -3425,7 +3181,6 @@ req! { } } - req! { urltest_245, b"GET joe@example.org,2001:foo/bar HTTP/1.1\r\nHost: \r\n\r\n", @@ -3439,7 +3194,6 @@ req! { } } - req! { urltest_246, b"GET /path HTTP/1.1\r\nHost: H%4fSt\r\n\r\n", @@ -3453,7 +3207,6 @@ req! { } } - req! { urltest_247, b"GET https://example.com:443/ HTTP/1.1\r\nHost: \r\n\r\n", @@ -3467,7 +3220,6 @@ req! { } } - req! { urltest_248, b"GET d3958f5c-0777-0845-9dcf-2cb28783acaf HTTP/1.1\r\nHost: \r\n\r\n", @@ -3481,7 +3233,6 @@ req! { } } - req! { urltest_249, b"GET /test?%22 HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -3495,7 +3246,6 @@ req! { } } - req! { urltest_250, b"GET /test HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -3509,7 +3259,6 @@ req! { } } - req! { urltest_251, b"GET /test?%3C HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -3523,7 +3272,6 @@ req! { } } - req! { urltest_252, b"GET /test?%3E HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -3537,7 +3285,6 @@ req! { } } - req! { urltest_253, b"GET /test?%E2%8C%A3 HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -3551,7 +3298,6 @@ req! { } } - req! { urltest_254, b"GET /test?%23%23 HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -3565,7 +3311,6 @@ req! { } } - req! { urltest_255, b"GET /test?%GH HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -3579,7 +3324,6 @@ req! { } } - req! { urltest_256, b"GET /test?a HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -3593,7 +3337,6 @@ req! { } } - req! { urltest_257, b"GET /test?a HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -3607,7 +3350,6 @@ req! { } } - req! { urltest_258, b"GET /test-a-colon-slash.html HTTP/1.1\r\nHost: \r\n\r\n", @@ -3621,7 +3363,6 @@ req! { } } - req! { urltest_259, b"GET /test-a-colon-slash-slash.html HTTP/1.1\r\nHost: \r\n\r\n", @@ -3635,7 +3376,6 @@ req! { } } - req! { urltest_260, b"GET /test-a-colon-slash-b.html HTTP/1.1\r\nHost: \r\n\r\n", @@ -3649,7 +3389,6 @@ req! { } } - req! { urltest_261, b"GET /test-a-colon-slash-slash-b.html HTTP/1.1\r\nHost: b\r\n\r\n", @@ -3663,7 +3402,6 @@ req! { } } - req! { urltest_262, b"GET /test?a HTTP/1.1\r\nHost: example.org\r\n\r\n", @@ -3677,7 +3415,6 @@ req! { } } - req! { urltest_nvidia, b"GET /nvidia_web_services/controller.gfeclientcontent.php/com.nvidia.services.GFEClientContent.getShieldReady/{\"gcV\":\"2.2.2.0\",\"dID\":\"1341\",\"osC\":\"6.20\",\"is6\":\"1\",\"lg\":\"1033\",\"GFPV\":\"389.08\",\"isO\":\"1\",\"sM\":\"16777216\"} HTTP/1.0\r\nHost: gfwsl.geforce.com\r\n\r\n", From a6b774a9624b849cbfc9ce0d81db803681863f2b Mon Sep 17 00:00:00 2001 From: Kylee Tilley Date: Sat, 19 Apr 2025 16:37:38 -0500 Subject: [PATCH 02/10] Refactor --- src/lib.rs | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 3e73e15..e1ad7f8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -655,7 +655,7 @@ impl<'h, 'b> Response<'h, 'b> { if config.allow_multiple_spaces_in_response_status_delimiters { complete!(skip_spaces(&mut bytes)); } - self.code = Some(complete!(parse_code(&mut bytes))); + self.code = Some(complete!(parse_status_code(&mut bytes))); // RFC7230 says there must be 'SP' and then reason-phrase, but admits // its only for legacy reasons. With the reason-phrase completely @@ -917,15 +917,34 @@ pub fn parse_uri<'a>(bytes: &mut Bytes<'a>) -> Result<&'a str> { } } +/// Parse a response status code from a byte slice. #[inline] -fn parse_code(bytes: &mut Bytes<'_>) -> Result { - let hundreds = expect!(bytes.next() == b'0'..=b'9' => Err(Error::Status)); - let tens = expect!(bytes.next() == b'0'..=b'9' => Err(Error::Status)); - let ones = expect!(bytes.next() == b'0'..=b'9' => Err(Error::Status)); - - Ok(Status::Complete( - (hundreds - b'0') as u16 * 100 + (tens - b'0') as u16 * 10 + (ones - b'0') as u16, - )) +fn parse_status_code(bytes: &mut Bytes<'_>) -> Result { + // Extract the hundreds place X00 + let hundreds = { + let hundreds = expect!(bytes.next() == b'0'..=b'9' => Err(Error::Status)); + let hundreds = (hundreds - b'0') as u16; + hundreds * 100 + }; + + // Extract the tens place 0X0 + let tens = { + let tens = expect!(bytes.next() == b'0'..=b'9' => Err(Error::Status)); + let tens = (tens - b'0') as u16; + tens * 10 + }; + + // Extract the ones place 00X + let ones = { + let ones = expect!(bytes.next() == b'0'..=b'9' => Err(Error::Status)); + let ones = (ones - b'0') as u16; + ones + }; + + // Calculate the status code + let status_code = hundreds + tens + ones; + + Ok(Status::Complete(status_code)) } /// Parse a buffer of bytes as headers. From 9d7b0267ca20c1312dad1ae7d2c314c6986b95ac Mon Sep 17 00:00:00 2001 From: Kylee Tilley Date: Wed, 23 Apr 2025 21:20:17 -0500 Subject: [PATCH 03/10] Removing the apache license --- LICENSE-APACHE | 201 ------------------------------------------------- README.md | 3 +- 2 files changed, 1 insertion(+), 203 deletions(-) delete mode 100644 LICENSE-APACHE diff --git a/LICENSE-APACHE b/LICENSE-APACHE deleted file mode 100644 index 16fe87b..0000000 --- a/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/README.md b/README.md index 3bbc450..0c42b7f 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# httparse +# httparser [![crates.io](https://img.shields.io/crates/v/httparse.svg)](https://crates.io/crates/httparse) [![Released API docs](https://docs.rs/httparse/badge.svg)](https://docs.rs/httparse) @@ -34,7 +34,6 @@ assert!(req.parse(buf)?.is_complete()); Licensed under either of -- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or https://apache.org/licenses/LICENSE-2.0) - MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT) ### Contribution From 807065856ccf8e8a3dd08be7c3bb09043429000e Mon Sep 17 00:00:00 2001 From: Kylee Tilley Date: Fri, 25 Apr 2025 08:23:43 -0500 Subject: [PATCH 04/10] Add tracing/logging --- Cargo.toml | 5 +++++ src/iter.rs | 10 ++++++++++ src/lib.rs | 33 +++++++++++++++++++++++++++++++++ src/macros.rs | 9 +++++++++ src/simd/neon.rs | 1 + 5 files changed, 58 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 7e5d698..5317d20 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,9 +21,14 @@ build = "build.rs" default = ["std"] std = [] +[dependencies] +log = "0.4.27" +tracing = { version = "0.1", features = ["log"] } + [dev-dependencies] criterion = "0.3.5" rand = "0.8.5" +env_logger = "0.11.8" [lib] bench = false diff --git a/src/iter.rs b/src/iter.rs index 06f78c3..37b4e5b 100644 --- a/src/iter.rs +++ b/src/iter.rs @@ -87,6 +87,8 @@ impl<'a> Bytes<'a> { /// Caller must ensure that Bytes hasn't been advanced/bumped by more than [`Bytes::len()`]. #[inline] pub unsafe fn advance(&mut self, n: usize) { + log::debug!("Advancing by {}", n); + self.cursor = self.cursor.add(n); debug_assert!(self.cursor <= self.end, "overflow"); } @@ -103,6 +105,7 @@ impl<'a> Bytes<'a> { #[inline] pub fn slice(&mut self) -> &'a [u8] { + log::debug!("Slicing bytes"); // SAFETY: not moving position at all, so it's safe let slice = unsafe { slice_from_ptr_range(self.start, self.cursor) }; self.commit(); @@ -125,6 +128,11 @@ impl<'a> Bytes<'a> { #[inline] pub fn commit(&mut self) { + log::debug!( + "Committing! start: {:?}, cursor: {:?}", + self.start, + self.cursor + ); self.start = self.cursor } @@ -157,6 +165,8 @@ impl<'a> Bytes<'a> { /// Must ensure invariant `bytes.start() <= ptr && ptr <= bytes.end()`. #[inline] pub unsafe fn set_cursor(&mut self, ptr: *const u8) { + log::info!("Set cursor! {:?}", ptr); + debug_assert!(ptr >= self.start); debug_assert!(ptr <= self.end); self.cursor = ptr; diff --git a/src/lib.rs b/src/lib.rs index e1ad7f8..605fcea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -468,6 +468,7 @@ impl<'h, 'b> Request<'h, 'b> { } } + #[tracing::instrument(skip_all)] fn parse_with_config_and_uninit_headers( &mut self, buf: &'b [u8], @@ -510,6 +511,7 @@ impl<'h, 'b> Request<'h, 'b> { /// except use an uninitialized slice of `Header`s. /// /// For more information, see `parse` + #[tracing::instrument(skip_all)] pub fn parse_with_uninit_headers( &mut self, buf: &'b [u8], @@ -518,6 +520,7 @@ impl<'h, 'b> Request<'h, 'b> { self.parse_with_config_and_uninit_headers(buf, &Default::default(), headers) } + #[tracing::instrument(skip_all)] fn parse_with_config(&mut self, buf: &'b [u8], config: &ParserConfig) -> Result { let headers = mem::take(&mut self.headers); @@ -539,12 +542,15 @@ impl<'h, 'b> Request<'h, 'b> { /// Try to parse a buffer of bytes into the Request. /// /// Returns byte offset in `buf` to start of HTTP body. + #[tracing::instrument(skip_all)] pub fn parse(&mut self, buf: &'b [u8]) -> Result { + log::info!("Parsing!"); self.parse_with_config(buf, &Default::default()) } } #[inline] +#[tracing::instrument(skip_all)] fn skip_empty_lines(bytes: &mut Bytes<'_>) -> Result<()> { loop { let b = bytes.peek(); @@ -570,6 +576,7 @@ fn skip_empty_lines(bytes: &mut Bytes<'_>) -> Result<()> { } #[inline] +#[tracing::instrument(skip_all)] fn skip_spaces(bytes: &mut Bytes<'_>) -> Result<()> { loop { let b = bytes.peek(); @@ -747,9 +754,12 @@ pub const EMPTY_HEADER: Header<'static> = Header { #[inline] #[doc(hidden)] #[allow(missing_docs)] +#[tracing::instrument(skip_all)] // WARNING: Exported for internal benchmarks, not fit for public consumption pub fn parse_version(bytes: &mut Bytes) -> Result { if let Some(eight) = bytes.peek_n::<[u8; 8]>(8) { + tracing::debug!("Skipping past HTTP version string e.g. HTTP/1.1"); + const H10: u64 = u64::from_ne_bytes(*b"HTTP/1.0"); const H11: u64 = u64::from_ne_bytes(*b"HTTP/1.1"); // SAFETY: peek_n(8) before ensure within bounds @@ -780,6 +790,7 @@ pub fn parse_version(bytes: &mut Bytes) -> Result { #[inline] #[doc(hidden)] #[allow(missing_docs)] +#[tracing::instrument(skip_all)] // WARNING: Exported for internal benchmarks, not fit for public consumption pub fn parse_method<'a>(bytes: &mut Bytes<'a>) -> Result<&'a str> { const GET: [u8; 4] = *b"GET "; @@ -788,6 +799,8 @@ pub fn parse_method<'a>(bytes: &mut Bytes<'a>) -> Result<&'a str> { Some(GET) => { // SAFETY: we matched "GET " which has 4 bytes and is ASCII let method = unsafe { + tracing::debug!("Skipping past HTTP method characters 'GET '"); + bytes.advance(4); // advance cursor past "GET " str::from_utf8_unchecked(bytes.slice_skip(1)) // "GET" without space }; @@ -824,6 +837,7 @@ pub fn parse_method<'a>(bytes: &mut Bytes<'a>) -> Result<&'a str> { /// > Non-US-ASCII content in header fields and the reason phrase /// > has been obsoleted and made opaque (the TEXT rule was removed). #[inline] +#[tracing::instrument(skip_all)] fn parse_reason<'a>(bytes: &mut Bytes<'a>) -> Result<&'a str> { let mut seen_obs_text = false; loop { @@ -872,6 +886,7 @@ fn parse_reason<'a>(bytes: &mut Bytes<'a>) -> Result<&'a str> { } #[inline] +#[tracing::instrument(skip_all)] fn parse_token<'a>(bytes: &mut Bytes<'a>) -> Result<&'a str> { let b = next!(bytes); if !is_method_token(b) { @@ -895,6 +910,7 @@ fn parse_token<'a>(bytes: &mut Bytes<'a>) -> Result<&'a str> { #[inline] #[doc(hidden)] #[allow(missing_docs)] +#[tracing::instrument(skip_all)] // WARNING: Exported for internal benchmarks, not fit for public consumption pub fn parse_uri<'a>(bytes: &mut Bytes<'a>) -> Result<&'a str> { let start = bytes.pos(); @@ -965,6 +981,7 @@ fn parse_status_code(bytes: &mut Bytes<'_>) -> Result { /// httparser::Header { name: "Accept", value: b"*/*" } /// ][..])))); /// ``` +#[tracing::instrument(skip_all)] pub fn parse_headers<'b: 'h, 'h>( src: &'b [u8], mut dst: &'h mut [Header<'b>], @@ -979,6 +996,7 @@ pub fn parse_headers<'b: 'h, 'h>( } #[inline] +#[tracing::instrument(skip_all)] fn parse_headers_iter<'a>( headers: &mut &mut [Header<'a>], bytes: &mut Bytes<'a>, @@ -1020,6 +1038,7 @@ struct HeaderParserConfig { * Also it promises `headers` get shrunk to number of initialized headers, * so casting the other way around after calling this function is safe */ +#[tracing::instrument(skip_all)] fn parse_headers_iter_uninit<'a>( headers: &mut &mut [MaybeUninit>], bytes: &mut Bytes<'a>, @@ -1125,6 +1144,13 @@ fn parse_headers_iter_uninit<'a>( result = Ok(Status::Complete(end - start)); break; } + + log::debug!( + "Handling char: {} @ {:?}", + char::from_u32(b.into()).unwrap(), + bytes.pos() + ); + if !is_header_name_token(b) { if config.allow_space_before_first_header_name && autoshrink.num_headers == 0 @@ -1141,6 +1167,11 @@ fn parse_headers_iter_uninit<'a>( bytes.slice(); continue 'headers; } else { + log::error!( + "Handling invalid header name! char: {} @ {:?}", + char::from_u32(b.into()).unwrap(), + bytes.pos() + ); handle_invalid_char!(bytes, b, HeaderName); } } @@ -1158,6 +1189,7 @@ fn parse_headers_iter_uninit<'a>( let name = unsafe { str::from_utf8_unchecked(bslice) }; if b == b':' { + log::debug!("Header name found: {}", name); break 'name name; } @@ -1270,6 +1302,7 @@ fn parse_headers_iter_uninit<'a>( /// assert_eq!(httparser::parse_chunk_size(buf), /// Ok(httparser::Status::Complete((3, 4)))); /// ``` +#[tracing::instrument(skip_all)] pub fn parse_chunk_size(buf: &[u8]) -> result::Result, InvalidChunkSize> { const RADIX: u64 = 16; let mut bytes = Bytes::new(buf); diff --git a/src/macros.rs b/src/macros.rs index 751f60b..fb1e904 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -54,12 +54,21 @@ macro_rules! space { macro_rules! newline { ($bytes:ident) => ({ + log::debug!( + "Skipping newline for \\r" + ); match next!($bytes) { b'\r' => { + log::debug!( + "Skipping newline for \\n" + ); expect!($bytes.next() == b'\n' => Err(Error::NewLine)); $bytes.slice(); }, b'\n' => { + log::debug!( + "Skipping newlines for \\n" + ); $bytes.slice(); }, _ => return Err(Error::NewLine) diff --git a/src/simd/neon.rs b/src/simd/neon.rs index b059efb..a6f26d8 100644 --- a/src/simd/neon.rs +++ b/src/simd/neon.rs @@ -81,6 +81,7 @@ const BITMAPS: ([u8; 16], [u8; 16]) = build_bitmap(); // NOTE: adapted from 256-bit version, with upper 128-bit ops commented out #[inline] +#[tracing::instrument] unsafe fn match_header_name_char_16_neon(ptr: *const u8) -> usize { let bitmaps = BITMAPS; // NOTE: ideally compile-time constants From 7e22e6961355c6e75ce3d59842a55bba635f5096 Mon Sep 17 00:00:00 2001 From: Kylee Tilley Date: Fri, 25 Apr 2025 08:25:46 -0500 Subject: [PATCH 05/10] Allow {{}} template references in header keys --- examples/example.rs | 9 ++++++++ src/lib.rs | 53 ++++++++++++++++++++++++++++++++++++++++++++- src/simd/neon.rs | 2 +- 3 files changed, 62 insertions(+), 2 deletions(-) create mode 100644 examples/example.rs diff --git a/examples/example.rs b/examples/example.rs new file mode 100644 index 0000000..090af66 --- /dev/null +++ b/examples/example.rs @@ -0,0 +1,9 @@ +fn main() { + env_logger::init(); + + let mut headers = [httparser::EMPTY_HEADER; 1]; + let mut req = httparser::Request::new(&mut headers); + + let buf = b"GET /index.html HTTP/1.1\r\n{{key}}: example.com\r\n\r\n"; + assert!(req.parse(buf).unwrap().is_complete()); +} diff --git a/src/lib.rs b/src/lib.rs index 605fcea..66fcd19 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -79,7 +79,7 @@ pub(crate) fn is_uri_token(b: u8) -> bool { static TOKEN_MAP: [bool; 256] = byte_map!( b'A'..=b'Z' | b'a'..=b'z' | b'0'..=b'9' | b'!' | b'#' | b'$' | b'%' | b'&' | b'\'' | b'*' | b'+' | - b'-' | b'.' | b'^' | b'_' | b'`' | b'|' | b'~' + b'-' | b'.' | b'^' | b'_' | b'`' | b'|' | b'~' | b'{' | b'}' ); #[inline] @@ -1452,6 +1452,57 @@ mod tests { } } + req! { + test_request_headers_templated_key, + b"GET / HTTP/1.1\r\nHost: foo.com\r\nCookie: \r\n{{key}}: value\r\n\r\n", + |req| { + assert_eq!(req.method.unwrap(), "GET"); + assert_eq!(req.path.unwrap(), "/"); + assert_eq!(req.version.unwrap(), 1); + assert_eq!(req.headers.len(), 3); + assert_eq!(req.headers[0].name, "Host"); + assert_eq!(req.headers[0].value, b"foo.com"); + assert_eq!(req.headers[1].name, "Cookie"); + assert_eq!(req.headers[1].value, b""); + assert_eq!(req.headers[2].name, "{{key}}"); + assert_eq!(req.headers[2].value, b"value"); + } + } + + req! { + test_request_headers_templated_key_optional_whitespace, + b"GET / HTTP/1.1\r\nHost: foo.com\r\nCookie: \r\n{{key}}: \t \r\n\r\n", + |req| { + assert_eq!(req.method.unwrap(), "GET"); + assert_eq!(req.path.unwrap(), "/"); + assert_eq!(req.version.unwrap(), 1); + assert_eq!(req.headers.len(), 3); + assert_eq!(req.headers[0].name, "Host"); + assert_eq!(req.headers[0].value, b"foo.com"); + assert_eq!(req.headers[1].name, "Cookie"); + assert_eq!(req.headers[1].value, b""); + assert_eq!(req.headers[2].name, "{{key}}"); + assert_eq!(req.headers[2].value, b""); + } + } + + req! { + test_request_headers_templated_key_and_value, + b"GET / HTTP/1.1\r\nHost: foo.com\r\nCookie: \r\n{{key}}: {{value}}\r\n\r\n", + |req| { + assert_eq!(req.method.unwrap(), "GET"); + assert_eq!(req.path.unwrap(), "/"); + assert_eq!(req.version.unwrap(), 1); + assert_eq!(req.headers.len(), 3); + assert_eq!(req.headers[0].name, "Host"); + assert_eq!(req.headers[0].value, b"foo.com"); + assert_eq!(req.headers[1].name, "Cookie"); + assert_eq!(req.headers[1].value, b""); + assert_eq!(req.headers[2].name, "{{key}}"); + assert_eq!(req.headers[2].value, b"{{value}}"); + } + } + req! { test_request_headers_optional_whitespace, b"GET / HTTP/1.1\r\nHost: \tfoo.com\t \r\nCookie: \t \r\n\r\n", diff --git a/src/simd/neon.rs b/src/simd/neon.rs index a6f26d8..44adf6e 100644 --- a/src/simd/neon.rs +++ b/src/simd/neon.rs @@ -52,7 +52,7 @@ pub fn match_uri_vectored(bytes: &mut Bytes) { const fn bit_set(x: u8) -> bool { // Validates if a byte is a valid header name character // https://tools.ietf.org/html/rfc7230#section-3.2.6 - matches!(x, b'0'..=b'9' | b'a'..=b'z' | b'A'..=b'Z' | b'!' | b'#' | b'$' | b'%' | b'&' | b'\'' | b'*' | b'+' | b'-' | b'.' | b'^' | b'_' | b'`' | b'|' | b'~') + matches!(x, b'0'..=b'9' | b'a'..=b'z' | b'A'..=b'Z' | b'!' | b'#' | b'$' | b'%' | b'&' | b'\'' | b'*' | b'+' | b'-' | b'.' | b'^' | b'_' | b'`' | b'|' | b'~' | b'{' | b'}') } // A 256-bit bitmap, split into two halves From 55c762d0eb5ae8c2a5f9e96341405c088f59eb54 Mon Sep 17 00:00:00 2001 From: Kylee Tilley Date: Fri, 25 Apr 2025 08:28:00 -0500 Subject: [PATCH 06/10] Format code --- src/macros.rs | 10 +++---- src/simd/avx2.rs | 26 ++++++++--------- src/simd/mod.rs | 71 ++++++++++----------------------------------- src/simd/neon.rs | 6 ++-- src/simd/runtime.rs | 4 +-- src/simd/sse42.rs | 24 ++++++++------- src/simd/swar.rs | 7 +++-- 7 files changed, 57 insertions(+), 91 deletions(-) diff --git a/src/macros.rs b/src/macros.rs index fb1e904..d29eb15 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -1,12 +1,12 @@ //! Utility macros macro_rules! next { - ($bytes:ident) => ({ + ($bytes:ident) => {{ match $bytes.next() { Some(b) => b, - None => return Ok(Status::Partial) + None => return Ok(Status::Partial), } - }) + }}; } macro_rules! expect { @@ -25,9 +25,9 @@ macro_rules! complete { ($e:expr) => { match $e? { Status::Complete(v) => v, - Status::Partial => return Ok(Status::Partial) + Status::Partial => return Ok(Status::Partial), } - } + }; } macro_rules! byte_map { diff --git a/src/simd/avx2.rs b/src/simd/avx2.rs index 8cb292c..99264b6 100644 --- a/src/simd/avx2.rs +++ b/src/simd/avx2.rs @@ -4,7 +4,6 @@ use crate::iter::Bytes; #[target_feature(enable = "avx2")] pub unsafe fn match_uri_vectored(bytes: &mut Bytes) { while bytes.as_ref().len() >= 32 { - let advance = match_url_char_32_avx(bytes.as_ref()); bytes.advance(advance); @@ -152,8 +151,11 @@ fn avx2_code_matches_uri_chars_table() { for (b, allowed) in crate::URI_MAP.iter().cloned().enumerate() { assert_eq!( - byte_is_allowed(b as u8, match_uri_vectored), allowed, - "byte_is_allowed({:?}) should be {:?}", b, allowed, + byte_is_allowed(b as u8, match_uri_vectored), + allowed, + "byte_is_allowed({:?}) should be {:?}", + b, + allowed, ); } } @@ -171,8 +173,11 @@ fn avx2_code_matches_header_value_chars_table() { for (b, allowed) in crate::HEADER_VALUE_MAP.iter().cloned().enumerate() { assert_eq!( - byte_is_allowed(b as u8, match_header_value_vectored), allowed, - "byte_is_allowed({:?}) should be {:?}", b, allowed, + byte_is_allowed(b as u8, match_header_value_vectored), + allowed, + "byte_is_allowed({:?}) should be {:?}", + b, + allowed, ); } } @@ -181,14 +186,9 @@ fn avx2_code_matches_header_value_chars_table() { #[cfg(test)] unsafe fn byte_is_allowed(byte: u8, f: unsafe fn(bytes: &mut Bytes<'_>)) -> bool { let slice = [ - b'_', b'_', b'_', b'_', - b'_', b'_', b'_', b'_', - b'_', b'_', b'_', b'_', - b'_', b'_', b'_', b'_', - b'_', b'_', b'_', b'_', - b'_', b'_', b'_', b'_', - b'_', b'_', byte, b'_', - b'_', b'_', b'_', b'_', + b'_', b'_', b'_', b'_', b'_', b'_', b'_', b'_', b'_', b'_', b'_', b'_', b'_', b'_', b'_', + b'_', b'_', b'_', b'_', b'_', b'_', b'_', b'_', b'_', b'_', b'_', byte, b'_', b'_', b'_', + b'_', b'_', ]; let mut bytes = Bytes::new(&slice); diff --git a/src/simd/mod.rs b/src/simd/mod.rs index 0e4493a..ef346eb 100644 --- a/src/simd/mod.rs +++ b/src/simd/mod.rs @@ -5,10 +5,7 @@ mod swar; any( target_arch = "x86", target_arch = "x86_64", - all( - target_arch = "aarch64", - httparse_simd_neon_intrinsics, - ) + all(target_arch = "aarch64", httparse_simd_neon_intrinsics,) ), )))] pub use self::swar::*; @@ -16,10 +13,7 @@ pub use self::swar::*; #[cfg(all( httparse_simd, not(httparse_simd_target_feature_avx2), - any( - target_arch = "x86", - target_arch = "x86_64", - ), + any(target_arch = "x86", target_arch = "x86_64",), ))] mod sse42; @@ -29,36 +23,21 @@ mod sse42; httparse_simd_target_feature_avx2, not(httparse_simd_target_feature_sse42), ), - any( - target_arch = "x86", - target_arch = "x86_64", - ), + any(target_arch = "x86", target_arch = "x86_64",), ))] mod avx2; #[cfg(all( httparse_simd, - not(any( - httparse_simd_target_feature_sse42, - httparse_simd_target_feature_avx2, - )), - any( - target_arch = "x86", - target_arch = "x86_64", - ), + not(any(httparse_simd_target_feature_sse42, httparse_simd_target_feature_avx2,)), + any(target_arch = "x86", target_arch = "x86_64",), ))] mod runtime; #[cfg(all( httparse_simd, - not(any( - httparse_simd_target_feature_sse42, - httparse_simd_target_feature_avx2, - )), - any( - target_arch = "x86", - target_arch = "x86_64", - ), + not(any(httparse_simd_target_feature_sse42, httparse_simd_target_feature_avx2,)), + any(target_arch = "x86", target_arch = "x86_64",), ))] pub use self::runtime::*; @@ -66,10 +45,7 @@ pub use self::runtime::*; httparse_simd, httparse_simd_target_feature_sse42, not(httparse_simd_target_feature_avx2), - any( - target_arch = "x86", - target_arch = "x86_64", - ), + any(target_arch = "x86", target_arch = "x86_64",), ))] mod sse42_compile_time { #[inline(always)] @@ -82,7 +58,7 @@ mod sse42_compile_time { // SAFETY: calls are guarded by a compile time feature check unsafe { crate::simd::sse42::match_uri_vectored(b) } } - + #[inline(always)] pub fn match_header_value_vectored(b: &mut crate::iter::Bytes<'_>) { // SAFETY: calls are guarded by a compile time feature check @@ -94,20 +70,14 @@ mod sse42_compile_time { httparse_simd, httparse_simd_target_feature_sse42, not(httparse_simd_target_feature_avx2), - any( - target_arch = "x86", - target_arch = "x86_64", - ), + any(target_arch = "x86", target_arch = "x86_64",), ))] pub use self::sse42_compile_time::*; #[cfg(all( httparse_simd, httparse_simd_target_feature_avx2, - any( - target_arch = "x86", - target_arch = "x86_64", - ), + any(target_arch = "x86", target_arch = "x86_64",), ))] mod avx2_compile_time { #[inline(always)] @@ -120,7 +90,7 @@ mod avx2_compile_time { // SAFETY: calls are guarded by a compile time feature check unsafe { crate::simd::avx2::match_uri_vectored(b) } } - + #[inline(always)] pub fn match_header_value_vectored(b: &mut crate::iter::Bytes<'_>) { // SAFETY: calls are guarded by a compile time feature check @@ -131,23 +101,12 @@ mod avx2_compile_time { #[cfg(all( httparse_simd, httparse_simd_target_feature_avx2, - any( - target_arch = "x86", - target_arch = "x86_64", - ), + any(target_arch = "x86", target_arch = "x86_64",), ))] pub use self::avx2_compile_time::*; -#[cfg(all( - httparse_simd, - target_arch = "aarch64", - httparse_simd_neon_intrinsics, -))] +#[cfg(all(httparse_simd, target_arch = "aarch64", httparse_simd_neon_intrinsics,))] mod neon; -#[cfg(all( - httparse_simd, - target_arch = "aarch64", - httparse_simd_neon_intrinsics, -))] +#[cfg(all(httparse_simd, target_arch = "aarch64", httparse_simd_neon_intrinsics,))] pub use self::neon::*; diff --git a/src/simd/neon.rs b/src/simd/neon.rs index 44adf6e..9cbade5 100644 --- a/src/simd/neon.rs +++ b/src/simd/neon.rs @@ -4,7 +4,7 @@ use core::arch::aarch64::*; #[inline] pub fn match_header_name_vectored(bytes: &mut Bytes) { while bytes.as_ref().len() >= 16 { - // SAFETY: ensured that there are at least 16 bytes remaining + // SAFETY: ensured that there are at least 16 bytes remaining unsafe { let advance = match_header_name_char_16_neon(bytes.as_ref().as_ptr()); bytes.advance(advance); @@ -20,7 +20,7 @@ pub fn match_header_name_vectored(bytes: &mut Bytes) { #[inline] pub fn match_header_value_vectored(bytes: &mut Bytes) { while bytes.as_ref().len() >= 16 { - // SAFETY: ensured that there are at least 16 bytes remaining + // SAFETY: ensured that there are at least 16 bytes remaining unsafe { let advance = match_header_value_char_16_neon(bytes.as_ref().as_ptr()); bytes.advance(advance); @@ -36,7 +36,7 @@ pub fn match_header_value_vectored(bytes: &mut Bytes) { #[inline] pub fn match_uri_vectored(bytes: &mut Bytes) { while bytes.as_ref().len() >= 16 { - // SAFETY: ensured that there are at least 16 bytes remaining + // SAFETY: ensured that there are at least 16 bytes remaining unsafe { let advance = match_url_char_16_neon(bytes.as_ref().as_ptr()); bytes.advance(advance); diff --git a/src/simd/runtime.rs b/src/simd/runtime.rs index c523a92..68b17e2 100644 --- a/src/simd/runtime.rs +++ b/src/simd/runtime.rs @@ -1,7 +1,7 @@ -use std::sync::atomic::{AtomicU8, Ordering}; -use crate::iter::Bytes; use super::avx2; use super::sse42; +use crate::iter::Bytes; +use std::sync::atomic::{AtomicU8, Ordering}; const AVX2: u8 = 1; const SSE42: u8 = 2; diff --git a/src/simd/sse42.rs b/src/simd/sse42.rs index 8dd438b..d436dcc 100644 --- a/src/simd/sse42.rs +++ b/src/simd/sse42.rs @@ -46,9 +46,9 @@ pub unsafe fn match_header_value_vectored(bytes: &mut Bytes) { let advance = match_header_value_char_16_sse(bytes.as_ref()); bytes.advance(advance); - if advance != 16 { + if advance != 16 { return; - } + } } super::swar::match_header_value_vectored(bytes); } @@ -93,8 +93,11 @@ fn sse_code_matches_uri_chars_table() { for (b, allowed) in crate::URI_MAP.iter().cloned().enumerate() { assert_eq!( - byte_is_allowed(b as u8, match_uri_vectored), allowed, - "byte_is_allowed({:?}) should be {:?}", b, allowed, + byte_is_allowed(b as u8, match_uri_vectored), + allowed, + "byte_is_allowed({:?}) should be {:?}", + b, + allowed, ); } } @@ -112,8 +115,11 @@ fn sse_code_matches_header_value_chars_table() { for (b, allowed) in crate::HEADER_VALUE_MAP.iter().cloned().enumerate() { assert_eq!( - byte_is_allowed(b as u8, match_header_value_vectored), allowed, - "byte_is_allowed({:?}) should be {:?}", b, allowed, + byte_is_allowed(b as u8, match_header_value_vectored), + allowed, + "byte_is_allowed({:?}) should be {:?}", + b, + allowed, ); } } @@ -123,10 +129,8 @@ fn sse_code_matches_header_value_chars_table() { #[cfg(test)] unsafe fn byte_is_allowed(byte: u8, f: unsafe fn(bytes: &mut Bytes<'_>)) -> bool { let slice = [ - b'_', b'_', b'_', b'_', - b'_', b'_', b'_', b'_', - b'_', b'_', byte, b'_', - b'_', b'_', b'_', b'_', + b'_', b'_', b'_', b'_', b'_', b'_', b'_', b'_', b'_', b'_', byte, b'_', b'_', b'_', b'_', + b'_', ]; let mut bytes = Bytes::new(&slice); diff --git a/src/simd/swar.rs b/src/simd/swar.rs index ef7435f..5533050 100644 --- a/src/simd/swar.rs +++ b/src/simd/swar.rs @@ -187,13 +187,16 @@ fn test_is_header_value_block() { assert!(is_header_value_block([b; BLOCK_SIZE]), "b={}", b); } // 127 => false - assert!(!is_header_value_block([b'\x7F'; BLOCK_SIZE]), "b={}", b'\x7F'); + assert!( + !is_header_value_block([b'\x7F'; BLOCK_SIZE]), + "b={}", + b'\x7F' + ); // 128..=255 => true for b in 128..=255_u8 { assert!(is_header_value_block([b; BLOCK_SIZE]), "b={}", b); } - #[cfg(target_pointer_width = "64")] { // A few sanity checks on non-uniform bytes for safe-measure From 8c905716cd9f4da20e2f7e8dbf53587b7083c3c0 Mon Sep 17 00:00:00 2001 From: Kylee Tilley Date: Fri, 25 Apr 2025 08:35:22 -0500 Subject: [PATCH 07/10] Make parse_with_config public --- src/lib.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 66fcd19..10ca88a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -520,8 +520,11 @@ impl<'h, 'b> Request<'h, 'b> { self.parse_with_config_and_uninit_headers(buf, &Default::default(), headers) } + /// Configure and try to parse a buffer of bytes into the Request. + /// + /// Returns byte offset in `buf` to start of HTTP body. #[tracing::instrument(skip_all)] - fn parse_with_config(&mut self, buf: &'b [u8], config: &ParserConfig) -> Result { + pub fn parse_with_config(&mut self, buf: &'b [u8], config: &ParserConfig) -> Result { let headers = mem::take(&mut self.headers); /* SAFETY: see `parse_headers_iter_uninit` guarantees */ From bce1ad924e3e4e2adb1444635c5f38a4ae3ac60a Mon Sep 17 00:00:00 2001 From: Kylee Tilley Date: Fri, 25 Apr 2025 09:01:12 -0500 Subject: [PATCH 08/10] Bump rust version to 1.85 --- .github/workflows/ci.yml | 8 ++++---- clippy.toml | 2 +- src/lib.rs | 3 +-- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0ea7bac..9c5d44e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -154,10 +154,10 @@ jobs: uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: 1.47.0 + toolchain: 1.85.0 override: true - # Only build, dev-dependencies don't compile on 1.47.0 + # Only build, dev-dependencies don't compile on 1.85.0 - name: Build uses: actions-rs/cargo@v1 with: @@ -180,11 +180,11 @@ jobs: uses: actions-rs/toolchain@v1 with: profile: minimal - toolchain: 1.47.0 + toolchain: 1.85.0 override: true target: aarch64-unknown-linux-gnu - # Only build, dev-dependencies don't compile on 1.47.0 + # Only build, dev-dependencies don't compile on 1.85.0 - name: Build uses: actions-rs/cargo@v1 with: diff --git a/clippy.toml b/clippy.toml index 7846a3e..b339f7c 100644 --- a/clippy.toml +++ b/clippy.toml @@ -1 +1 @@ -msrv = "1.47" +msrv = "1.85" diff --git a/src/lib.rs b/src/lib.rs index 10ca88a..26e94d8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -956,8 +956,7 @@ fn parse_status_code(bytes: &mut Bytes<'_>) -> Result { // Extract the ones place 00X let ones = { let ones = expect!(bytes.next() == b'0'..=b'9' => Err(Error::Status)); - let ones = (ones - b'0') as u16; - ones + (ones - b'0') as u16 }; // Calculate the status code From 1391fa74bebe5f902abccc203d80370e09e75424 Mon Sep 17 00:00:00 2001 From: Kylee Tilley Date: Fri, 25 Apr 2025 09:12:49 -0500 Subject: [PATCH 09/10] Fix linting issues --- benches/parse.rs | 2 +- src/lib.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/benches/parse.rs b/benches/parse.rs index dfb7c0b..99545ac 100644 --- a/benches/parse.rs +++ b/benches/parse.rs @@ -259,7 +259,7 @@ fn many_requests(c: &mut Criterion) { ("w3!r`d", 20), ] .iter() - .flat_map(|&(method, count)| std::iter::repeat(method).take(count)) + .flat_map(|&(method, count)| std::iter::repeat_n(method, count)) .map(|method| format!("{method} / HTTP/1.1\r\n\r\n")) .collect::>(); SliceRandom::shuffle(&mut *requests, &mut StdRng::seed_from_u64(0)); diff --git a/src/lib.rs b/src/lib.rs index 26e94d8..c30fd05 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1892,7 +1892,7 @@ mod tests { let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS]; let mut req = Request::new(&mut headers[..]); for i in 0..req_str.len() { - let status = req.parse(req_str[..i].as_bytes()); + let status = req.parse(&req_str.as_bytes()[..i]); assert_eq!( status, Ok(Status::Partial), From 87ab25ccdb9824d1d5be4f73346b001ce72868b6 Mon Sep 17 00:00:00 2001 From: Kylee Tilley Date: Fri, 25 Apr 2025 10:47:06 -0500 Subject: [PATCH 10/10] Update readme --- README.md | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/README.md b/README.md index 0c42b7f..ae457cf 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,12 @@ # httparser -[![crates.io](https://img.shields.io/crates/v/httparse.svg)](https://crates.io/crates/httparse) -[![Released API docs](https://docs.rs/httparse/badge.svg)](https://docs.rs/httparse) [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE-MIT) -[![CI](https://github.com/seanmonstar/httparse/workflows/CI/badge.svg)](https://github.com/seanmonstar/httparse/actions?query=workflow%3ACI) -[![Discord chat][discord-badge]][discord-url] +[![CI](https://github.com/testingrequired/httparser/workflows/CI/badge.svg)](https://github.com/testingrequired/httparser/actions?query=workflow%3ACI) A push parser for the HTTP 1.x protocol. Avoids allocations. No copy. **Fast.** Works with `no_std`, simply disable the `std` Cargo feature. -[Changelog](https://github.com/seanmonstar/httparse/releases) - -[discord-badge]: https://img.shields.io/discord/500028886025895936.svg?logo=discord -[discord-url]: https://discord.gg/kkwpueZ - ## Usage ```rust @@ -35,7 +27,3 @@ assert!(req.parse(buf)?.is_complete()); Licensed under either of - MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT) - -### Contribution - -Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.